Featured
{featuredPatterns?.map((pattern, i) => (
))}
@@ -55,7 +45,9 @@ export function SharedPatterns() {
{publicPatterns?.map((pattern, i) => (
))}
diff --git a/website/src/repl/panel/PatternsTab.jsx b/website/src/repl/panel/PatternsTab.jsx
index 4466b599..a3d67460 100644
--- a/website/src/repl/panel/PatternsTab.jsx
+++ b/website/src/repl/panel/PatternsTab.jsx
@@ -1,6 +1,8 @@
import { DocumentDuplicateIcon, PencilSquareIcon, TrashIcon } from '@heroicons/react/20/solid';
import { useMemo } from 'react';
import {
+ $featuredPatterns,
+ $publicPatterns,
clearUserPatterns,
deleteActivePattern,
duplicateActivePattern,
@@ -14,6 +16,8 @@ import {
useSettings,
} from '../../settings.mjs';
import * as tunes from '../tunes.mjs';
+import { PatternLabel } from '../../components/SharedPatterns';
+import { useStore } from '@nanostores/react';
function classNames(...classes) {
return classes.filter(Boolean).join(' ');
@@ -22,6 +26,8 @@ function classNames(...classes) {
export function PatternsTab({ context }) {
const { userPatterns } = useSettings();
const activePattern = useActivePattern();
+ const featuredPatterns = useStore($featuredPatterns);
+ const publicPatterns = useStore($publicPatterns);
const isExample = useMemo(() => activePattern && !!tunes[activePattern], [activePattern]);
return (
@@ -94,8 +100,52 @@ export function PatternsTab({ context }) {
+ {featuredPatterns && (
+
+ )}
+ {publicPatterns && (
+
+ )}
- Examples
+ Stock Examples
{Object.entries(tunes).map(([key, tune]) => (
{
}
initializeAudioOutput();
};
+
+export function loadPublicPatterns() {
+ return supabase.from('code').select().eq('public', true).limit(20).order('id', { ascending: false });
+}
+
+export function loadFeaturedPatterns() {
+ return supabase.from('code').select().eq('featured', true).limit(20).order('id', { ascending: false });
+}
diff --git a/website/src/settings.mjs b/website/src/settings.mjs
index 0e433806..2f973a2f 100644
--- a/website/src/settings.mjs
+++ b/website/src/settings.mjs
@@ -1,8 +1,22 @@
+import { atom } from 'nanostores';
import { persistentMap, persistentAtom } from '@nanostores/persistent';
import { useStore } from '@nanostores/react';
import { register } from '@strudel.cycles/core';
import * as tunes from './repl/tunes.mjs';
import { logger } from '@strudel.cycles/core';
+import { loadPublicPatterns, loadFeaturedPatterns } from './repl/util.mjs';
+
+export let $publicPatterns = atom([]);
+export let $featuredPatterns = atom([]);
+
+async function loadDBPatterns() {
+ const { data: publicPatterns } = await loadPublicPatterns();
+ $publicPatterns.set(publicPatterns);
+ const { data: featuredPatterns } = await loadFeaturedPatterns();
+ $featuredPatterns.set(featuredPatterns);
+}
+
+loadDBPatterns();
export const defaultAudioDeviceName = 'System Standard';
@@ -172,12 +186,25 @@ export function updateUserCode(code) {
setActivePattern(example);
return;
}
-
+ const publicPattern = $publicPatterns.get().find((pat) => pat.code === code);
+ if (publicPattern) {
+ setActivePattern(publicPattern.hash);
+ return;
+ }
+ const featuredPattern = $featuredPatterns.get().find((pat) => pat.code === code);
+ if (featuredPattern) {
+ setActivePattern(featuredPattern.hash);
+ return;
+ }
if (!activePattern) {
// create new user pattern
activePattern = newUserPattern();
setActivePattern(activePattern);
- } else if (!!tunes[activePattern] && code !== tunes[activePattern]) {
+ } else if (
+ (!!tunes[activePattern] && code !== tunes[activePattern]) || // fork example tune?
+ $publicPatterns.get().find((p) => p.hash === activePattern) || // fork public pattern?
+ $featuredPatterns.get().find((p) => p.hash === activePattern) // fork featured pattern?
+ ) {
// fork example
activePattern = getNextCloneName(activePattern);
setActivePattern(activePattern);