diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index bf618b4d..a02893b3 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -80,6 +80,7 @@ export function Repl({ embedded = false }) { const data = { code }; let id = getViewingPattern(); window.location.hash = '#' + code2hash(code); + const examplePatternData = examplePattern.getPatternData(id); const isExamplePattern = examplePatternData != null; diff --git a/website/src/repl/panel/PatternsTab.jsx b/website/src/repl/panel/PatternsTab.jsx index e2d9cd1d..3276a16b 100644 --- a/website/src/repl/panel/PatternsTab.jsx +++ b/website/src/repl/panel/PatternsTab.jsx @@ -1,13 +1,8 @@ import { DocumentDuplicateIcon, PencilSquareIcon, TrashIcon } from '@heroicons/react/20/solid'; import { - $featuredPatterns, $publicPatterns, - clearUserPatterns, - deleteActivePattern, - duplicateActivePattern, - exportPatterns, importPatterns, useActivePattern, @@ -23,72 +18,113 @@ import * as tunes from '../tunes.mjs'; import { useStore } from '@nanostores/react'; import { getMetadata } from '../../metadata_parser'; - function classNames(...classes) { return classes.filter(Boolean).join(' '); } -function PatternButton({ showOutline, onClick, label, showHiglight }) { +function PatternLabel({ pattern } /* : { pattern: Tables<'code'> } */) { + const meta = useMemo(() => getMetadata(pattern.code), [pattern]); + return ( + <>{`${pattern.id}: ${meta.title ?? pattern.hash ?? 'unnamed'} by ${ + Array.isArray(meta.by) ? meta.by.join(',') : 'Anonymous' + }`} + ); +} + +const getPatternLabel = (pattern) => { + return `${pattern.id}: ${meta.title ?? pattern.hash ?? 'unnamed'} by ${ + Array.isArray(meta.by) ? meta.by.join(',') : 'Anonymous' + }`; +}; + +function PatternButton({ showOutline, onClick, pattern, showHiglight }) { return ( - {label} + ); } +// function ListButton() { +// return ( +// { +// setActivePattern(pattern.hash); +// context.handleUpdate(pattern.code, true); +// }} +// > +// +// +// ); +// } + function PatternButtons({ patterns, activePattern, onClick, viewingPattern, started }) { return (
- {Object.entries(patterns).map(([id]) => ( - onClick(id)} - /> - ))} + {Object.values(patterns).map((pattern) => { + const id = pattern.id; + return ( + onClick(id)} + /> + ); + })}
); } export function PatternsTab({ context }) { const { userPatterns } = useSettings(); - const examplePatterns = useMemo(() => examplePattern.getAll(), []); const activePattern = useActivePattern(); + const featuredPatterns = useStore($featuredPatterns); + const publicPatterns = useStore($publicPatterns); + + // const otherPatterns = [ + // {source: 'Stock Examples', patterns: examplePatterns } + // ] + + const otherPatterns = useMemo(() => { + const pats = new Map(); + pats.set('Featured', featuredPatterns); + pats.set('Last Creations', publicPatterns); + pats.set('Stock Examples', examplePattern.getAll()); + return pats; + }, [featuredPatterns, publicPatterns]); + const viewingPattern = useViewingPattern(); const updateCodeWindow = (id, code, reset = false) => { context.handleUpdate(id, code, reset); }; - const onPatternBtnClick = (id, isExample = false) => { - const code = isExample ? examplePatterns[id].code : userPatterns[id].code; - // display selected pattern code in the window - updateCodeWindow(id, code, isExample); - }; + const isUserPattern = userPatterns[viewingPattern] != null; - const isExample = examplePatterns[viewingPattern] != null; - - const featuredPatterns = useStore($featuredPatterns); - const publicPatterns = useStore($publicPatterns); - const isExample = useMemo(() => activePattern && !!tunes[activePattern], [activePattern]); + // const isExample = useMemo(() => activePattern && !!tunes[activePattern], [activePattern]); return (
{viewingPattern && (
-

{viewingPattern}

+

{`${viewingPattern}`}

- {!isExample && ( + {/* {!isExample && ( - )} + )} */} - {!isExample && ( + {isUserPattern && (
)} updateCodeWindow(id, userPatterns[id]?.code, false)} patterns={userPatterns} started={context.started} activePattern={activePattern} @@ -166,90 +202,24 @@ export function PatternsTab({ context }) {
- {featuredPatterns && ( -
-

Featured Patterns

-
- {featuredPatterns.map((pattern) => ( - { - setActivePattern(pattern.hash); - context.handleUpdate(pattern.code, true); - }} - > - - - ))} -
-
- )} - {publicPatterns && ( -
-

Last Creations

-
- {publicPatterns.map((pattern) => ( - { - setActivePattern(pattern.hash); - context.handleUpdate(pattern.code, true); - }} - > - - - ))} -
-
- )} -
+ {Array.from(otherPatterns.keys()).map((key) => { + const patterns = otherPatterns.get(key); -

Examples

- onPatternBtnClick(id, true)} - started={context.started} - patterns={examplePatterns} - activePattern={activePattern} - viewingPattern={viewingPattern} - /> - -

Stock Examples

-
- {Object.entries(tunes).map(([key, tune]) => ( - { - setActivePattern(key); - context.handleUpdate(tune, true); - }} - > - {key} - - ))} -
- -
+ return ( +
+

{key}

+
+ updateCodeWindow(id, patterns[id]?.code, true)} + started={context.started} + patterns={patterns} + activePattern={activePattern} + viewingPattern={viewingPattern} + /> +
+
+ ); + })}
); } - -export function PatternLabel({ pattern } /* : { pattern: Tables<'code'> } */) { - const meta = useMemo(() => getMetadata(pattern.code), [pattern]); - return ( - <> - {pattern.id}. {meta.title || pattern.hash} by {Array.isArray(meta.by) ? meta.by.join(',') : 'Anonymous'} - - ); -} diff --git a/website/src/repl/util.mjs b/website/src/repl/util.mjs index 3405e93d..1da4fc65 100644 --- a/website/src/repl/util.mjs +++ b/website/src/repl/util.mjs @@ -29,8 +29,12 @@ async function loadDBPatterns() { try { const { data: publicPatterns } = await loadPublicPatterns(); const { data: featuredPatterns } = await loadFeaturedPatterns(); - $publicPatterns.set(publicPatterns); - $featuredPatterns.set(featuredPatterns); + const featured = {}; + const pub = {}; + publicPatterns?.forEach((data, key) => (pub[data.id ?? key] = data)); + featuredPatterns?.forEach((data, key) => (featured[data.id ?? key] = data)); + $publicPatterns.set(pub); + $featuredPatterns.set(featured); } catch (err) { console.error('error loading patterns'); } diff --git a/website/src/settings.mjs b/website/src/settings.mjs index f500b92c..868a2ba8 100644 --- a/website/src/settings.mjs +++ b/website/src/settings.mjs @@ -4,7 +4,7 @@ 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 { nanoid } from 'nanoid'; export let $publicPatterns = atom([]); export let $featuredPatterns = atom([]); @@ -70,6 +70,14 @@ export function initUserCode(code) { export function useSettings() { const state = useStore(settingsMap); + + const userPatterns = JSON.parse(state.userPatterns); + Object.keys(userPatterns).forEach((key) => { + const data = userPatterns[key]; + data.id = data.id ?? key; + data.date = data.date ?? 0; + userPatterns[key] = data; + }); return { ...state, isZen: [true, 'true'].includes(state.isZen) ? true : false, @@ -82,7 +90,7 @@ export function useSettings() { isFlashEnabled: [true, 'true'].includes(state.isFlashEnabled) ? true : false, fontSize: Number(state.fontSize), panelPosition: state.activeFooter !== '' ? state.panelPosition : 'bottom', // <-- keep this 'bottom' where it is! - userPatterns: JSON.parse(state.userPatterns), + userPatterns: userPatterns, }; } @@ -108,35 +116,23 @@ export const fontSize = patternSetting('fontSize'); export const settingPatterns = { theme, fontFamily, fontSize }; -export function getUserPatterns() { +function getUserPatterns() { return JSON.parse(settingsMap.get().userPatterns); } -function getSetting(key) { - return settingsMap.get()[key]; -} -export function setUserPatterns(obj) { +function setUserPatterns(obj) { return settingsMap.setKey('userPatterns', JSON.stringify(obj)); } export const createPatternID = () => { - const userPatterns = getUserPatterns(); - const date = new Date().toISOString().split('T')[0]; - const todays = Object.entries(userPatterns).filter(([name]) => name.startsWith(date)); - const num = String(todays.length + 1).padStart(3, '0'); - const id = date + '_' + num; - return id; + return nanoid(12); }; export const getNextCloneID = (id) => { - const patterns = { ...userPattern.getAll(), ...examplePattern.getAll() }; - const clones = Object.entries(patterns).filter(([patID]) => patID.startsWith(id)); - const num = String(clones.length + 1).padStart(3, '0'); - const newID = id + '_' + num; - return newID; + return createPatternID(); }; -const examplePatterns = Object.fromEntries(Object.entries(tunes).map(([id, code]) => [id, { code }])); +const examplePatterns = Object.fromEntries(Object.entries(tunes).map(([key, code], i) => [i, { id: i, code }])); export const examplePattern = { getAll() { @@ -154,7 +150,8 @@ export const examplePattern = { // break export const userPattern = { getAll() { - return JSON.parse(settingsMap.get().userPatterns); + const patterns = JSON.parse(settingsMap.get().userPatterns); + return patterns; }, getPatternData(id) { const userPatterns = this.getAll(); @@ -163,10 +160,13 @@ export const userPattern = { exists(id) { return this.getPatternData(id) != null; }, + create() { const newID = createPatternID(); const code = defaultCode; - const data = { code }; + + // const meta = getMetadata + const data = { code, created_at: Date.now(), id: newID }; this.update(newID, data); return { id: newID, data }; }, @@ -221,44 +221,6 @@ export const userPattern = { alert('Name already taken!'); return { id, data }; } -// break -export function updateUserCode(code) { - const userPatterns = getUserPatterns(); - let activePattern = getActivePattern(); - // check if code is that of an example tune - const [example] = Object.entries(tunes).find(([_, tune]) => tune === code) || []; - if (example && (!activePattern || activePattern === example)) { - // select example - 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]) || // 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); - } - setUserPatterns({ ...userPatterns, [activePattern]: { code } }); -} -// break - userPatterns[newID] = data; // copy code delete userPatterns[id]; @@ -270,6 +232,42 @@ export function updateUserCode(code) { }, }; +// export function updateUserCode(code) { +// const userPatterns = getUserPatterns(); +// let activePattern = getActivePattern(); +// // check if code is that of an example tune +// const [example] = Object.entries(tunes).find(([_, tune]) => tune === code) || []; +// if (example && (!activePattern || activePattern === example)) { +// // select example +// 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]) || // 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); +// } +// setUserPatterns({ ...userPatterns, [activePattern]: { code } }); +// } + export async function importPatterns(fileList) { const files = Array.from(fileList); await Promise.all(