From 3c1844046a4ca677891cec3a5df9db65618efb7d Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 13 Jan 2024 23:01:16 +0100 Subject: [PATCH 1/4] basic browse page --- website/database.types.ts | 120 ++++++++++++++++++++++ website/src/components/SharedPatterns.tsx | 54 ++++++++++ website/src/pages/browse.astro | 14 +++ 3 files changed, 188 insertions(+) create mode 100644 website/database.types.ts create mode 100644 website/src/components/SharedPatterns.tsx create mode 100644 website/src/pages/browse.astro diff --git a/website/database.types.ts b/website/database.types.ts new file mode 100644 index 00000000..152742b4 --- /dev/null +++ b/website/database.types.ts @@ -0,0 +1,120 @@ +// generated with https://supabase.com/docs/reference/javascript/typescript-support#generating-typescript-types +export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[]; + +export interface Database { + public: { + Tables: { + code: { + Row: { + code: string | null; + created_at: string | null; + featured: boolean | null; + hash: string | null; + id: number; + public: boolean | null; + }; + Insert: { + code?: string | null; + created_at?: string | null; + featured?: boolean | null; + hash?: string | null; + id?: number; + public?: boolean | null; + }; + Update: { + code?: string | null; + created_at?: string | null; + featured?: boolean | null; + hash?: string | null; + id?: number; + public?: boolean | null; + }; + Relationships: []; + }; + }; + Views: { + [_ in never]: never; + }; + Functions: { + [_ in never]: never; + }; + Enums: { + [_ in never]: never; + }; + CompositeTypes: { + [_ in never]: never; + }; + }; +} + +export type Tables< + PublicTableNameOrOptions extends + | keyof (Database['public']['Tables'] & Database['public']['Views']) + | { schema: keyof Database }, + TableName extends PublicTableNameOrOptions extends { schema: keyof Database } + ? keyof (Database[PublicTableNameOrOptions['schema']]['Tables'] & + Database[PublicTableNameOrOptions['schema']]['Views']) + : never = never, +> = PublicTableNameOrOptions extends { schema: keyof Database } + ? (Database[PublicTableNameOrOptions['schema']]['Tables'] & + Database[PublicTableNameOrOptions['schema']]['Views'])[TableName] extends { + Row: infer R; + } + ? R + : never + : PublicTableNameOrOptions extends keyof (Database['public']['Tables'] & Database['public']['Views']) + ? (Database['public']['Tables'] & Database['public']['Views'])[PublicTableNameOrOptions] extends { + Row: infer R; + } + ? R + : never + : never; + +export type TablesInsert< + PublicTableNameOrOptions extends keyof Database['public']['Tables'] | { schema: keyof Database }, + TableName extends PublicTableNameOrOptions extends { schema: keyof Database } + ? keyof Database[PublicTableNameOrOptions['schema']]['Tables'] + : never = never, +> = PublicTableNameOrOptions extends { schema: keyof Database } + ? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends { + Insert: infer I; + } + ? I + : never + : PublicTableNameOrOptions extends keyof Database['public']['Tables'] + ? Database['public']['Tables'][PublicTableNameOrOptions] extends { + Insert: infer I; + } + ? I + : never + : never; + +export type TablesUpdate< + PublicTableNameOrOptions extends keyof Database['public']['Tables'] | { schema: keyof Database }, + TableName extends PublicTableNameOrOptions extends { schema: keyof Database } + ? keyof Database[PublicTableNameOrOptions['schema']]['Tables'] + : never = never, +> = PublicTableNameOrOptions extends { schema: keyof Database } + ? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends { + Update: infer U; + } + ? U + : never + : PublicTableNameOrOptions extends keyof Database['public']['Tables'] + ? Database['public']['Tables'][PublicTableNameOrOptions] extends { + Update: infer U; + } + ? U + : never + : never; + +export type Enums< + PublicEnumNameOrOptions extends keyof Database['public']['Enums'] | { schema: keyof Database }, + EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database } + ? keyof Database[PublicEnumNameOrOptions['schema']]['Enums'] + : never = never, +> = PublicEnumNameOrOptions extends { schema: keyof Database } + ? Database[PublicEnumNameOrOptions['schema']]['Enums'][EnumName] + : PublicEnumNameOrOptions extends keyof Database['public']['Enums'] + ? Database['public']['Enums'][PublicEnumNameOrOptions] + : never; diff --git a/website/src/components/SharedPatterns.tsx b/website/src/components/SharedPatterns.tsx new file mode 100644 index 00000000..4deab67e --- /dev/null +++ b/website/src/components/SharedPatterns.tsx @@ -0,0 +1,54 @@ +import { createClient } from '@supabase/supabase-js'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import type { Database, Tables } from '../../database.types'; +import { getMetadata } from '../metadata_parser'; + +function PatternLink({ pattern }: { pattern: Tables<'code'> }) { + const meta = useMemo(() => getMetadata(pattern.code), [pattern]); + // console.log('meta', meta); + return ( + + {meta.title || pattern.hash} by {meta.by.join(',') || 'Anonymous'} + + ); +} + +export function SharedPatterns() { + const [publicPatterns, setPublicPatterns] = useState[] | null>([]); + const [featuredPatterns, setFeaturedPatterns] = useState[] | null>([]); + const init = useCallback(async () => { + const supabase = createClient( + 'https://pidxdsxphlhzjnzmifth.supabase.co', + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM', + ); + const { data: _publicPatterns } = await supabase.from('code').select().eq('public', true).limit(20); + const { data: _featuredPatterns } = await supabase.from('code').select().eq('featured', true).limit(20); + setPublicPatterns(_publicPatterns); + setFeaturedPatterns(_featuredPatterns); + /* console.log('public', publicPatterns); + console.log('featured', featuredPatterns); */ + }, []); + useEffect(() => { + init(); + }, [useCallback]); + return ( +
+

Featured

+
+ {featuredPatterns?.map((pattern, i) => ( +
+ +
+ ))} +
+

Last Creations

+
+ {publicPatterns?.map((pattern, i) => ( +
+ +
+ ))} +
+
+ ); +} diff --git a/website/src/pages/browse.astro b/website/src/pages/browse.astro new file mode 100644 index 00000000..3e63bf87 --- /dev/null +++ b/website/src/pages/browse.astro @@ -0,0 +1,14 @@ +--- +import HeadCommon from '../components/HeadCommon.astro'; +import { SharedPatterns } from '../components/SharedPatterns'; +--- + + + + + +
+

Browse

+ +
+ From 1ede99f71e93ab105925408b090330b90ffd97bb Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 13 Jan 2024 23:12:22 +0100 Subject: [PATCH 2/4] ask for public sharing (very cheap) + order browse list --- website/src/components/SharedPatterns.tsx | 16 +++++++++++++--- website/src/repl/util.mjs | 5 ++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/website/src/components/SharedPatterns.tsx b/website/src/components/SharedPatterns.tsx index 4deab67e..bbd5cc8d 100644 --- a/website/src/components/SharedPatterns.tsx +++ b/website/src/components/SharedPatterns.tsx @@ -8,7 +8,7 @@ function PatternLink({ pattern }: { pattern: Tables<'code'> }) { // console.log('meta', meta); return ( - {meta.title || pattern.hash} by {meta.by.join(',') || 'Anonymous'} + {pattern.id}. {meta.title || pattern.hash} by {meta.by.join(',') || 'Anonymous'} ); } @@ -21,8 +21,18 @@ export function SharedPatterns() { 'https://pidxdsxphlhzjnzmifth.supabase.co', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM', ); - const { data: _publicPatterns } = await supabase.from('code').select().eq('public', true).limit(20); - const { data: _featuredPatterns } = await supabase.from('code').select().eq('featured', true).limit(20); + const { data: _publicPatterns } = await supabase + .from('code') + .select() + .eq('public', true) + .limit(20) + .order('id', { ascending: false }); + const { data: _featuredPatterns } = await supabase + .from('code') + .select() + .eq('featured', true) + .limit(20) + .order('id', { ascending: false }); setPublicPatterns(_publicPatterns); setFeaturedPatterns(_featuredPatterns); /* console.log('public', publicPatterns); diff --git a/website/src/repl/util.mjs b/website/src/repl/util.mjs index 81c41e9d..325417d7 100644 --- a/website/src/repl/util.mjs +++ b/website/src/repl/util.mjs @@ -90,10 +90,13 @@ export async function shareCode(codeToShare) { logger(`Link already generated!`, 'error'); return; } + const isPublic = confirm( + 'Do you want your pattern to be public? If no, press cancel and you will get just a private link.', + ); // generate uuid in the browser const hash = nanoid(12); const shareUrl = window.location.origin + window.location.pathname + '?' + hash; - const { data, error } = await supabase.from('code').insert([{ code: codeToShare, hash }]); + const { error } = await supabase.from('code').insert([{ code: codeToShare, hash, ['public']: isPublic }]); if (!error) { lastShared = codeToShare; // copy shareUrl to clipboard From 204c9640503b9691fe0f7c2e736af7060ab96e0c Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 13 Jan 2024 23:53:46 +0100 Subject: [PATCH 3/4] integrate public patterns into patterns tab --- website/src/components/SharedPatterns.tsx | 46 +++++++++----------- website/src/repl/panel/PatternsTab.jsx | 52 ++++++++++++++++++++++- website/src/repl/util.mjs | 10 ++++- website/src/settings.mjs | 31 +++++++++++++- 4 files changed, 108 insertions(+), 31 deletions(-) diff --git a/website/src/components/SharedPatterns.tsx b/website/src/components/SharedPatterns.tsx index bbd5cc8d..1cdca792 100644 --- a/website/src/components/SharedPatterns.tsx +++ b/website/src/components/SharedPatterns.tsx @@ -1,53 +1,43 @@ -import { createClient } from '@supabase/supabase-js'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import type { Database, Tables } from '../../database.types'; +import type { Tables } from '../../database.types'; import { getMetadata } from '../metadata_parser'; +import { loadFeaturedPatterns, loadPublicPatterns } from '../repl/util.mjs'; -function PatternLink({ pattern }: { pattern: Tables<'code'> }) { +export function PatternLabel({ pattern }: { pattern: Tables<'code'> }) { const meta = useMemo(() => getMetadata(pattern.code), [pattern]); - // console.log('meta', meta); return ( - + <> {pattern.id}. {meta.title || pattern.hash} by {meta.by.join(',') || 'Anonymous'} - + ); } -export function SharedPatterns() { +export const usePublicPatterns = () => { const [publicPatterns, setPublicPatterns] = useState[] | null>([]); const [featuredPatterns, setFeaturedPatterns] = useState[] | null>([]); const init = useCallback(async () => { - const supabase = createClient( - 'https://pidxdsxphlhzjnzmifth.supabase.co', - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM', - ); - const { data: _publicPatterns } = await supabase - .from('code') - .select() - .eq('public', true) - .limit(20) - .order('id', { ascending: false }); - const { data: _featuredPatterns } = await supabase - .from('code') - .select() - .eq('featured', true) - .limit(20) - .order('id', { ascending: false }); + const { data: _publicPatterns } = await loadPublicPatterns(); + const { data: _featuredPatterns } = await loadFeaturedPatterns(); setPublicPatterns(_publicPatterns); setFeaturedPatterns(_featuredPatterns); - /* console.log('public', publicPatterns); - console.log('featured', featuredPatterns); */ }, []); useEffect(() => { init(); }, [useCallback]); + return { publicPatterns, featuredPatterns }; +}; + +export function SharedPatterns() { + const { publicPatterns, featuredPatterns } = usePublicPatterns(); return (

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 && ( +
+

Featured Patterns

+ +
+ )} + {publicPatterns && ( +
+

Last Creations

+ +
+ )}
-

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); From 5314c83534db1d28d158c274417a08eb15bec4b7 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 14 Jan 2024 00:02:27 +0100 Subject: [PATCH 4/4] delete browse page for now --- website/src/components/SharedPatterns.tsx | 56 ----------------------- website/src/pages/browse.astro | 14 ------ website/src/repl/panel/PatternsTab.jsx | 11 ++++- 3 files changed, 10 insertions(+), 71 deletions(-) delete mode 100644 website/src/components/SharedPatterns.tsx delete mode 100644 website/src/pages/browse.astro diff --git a/website/src/components/SharedPatterns.tsx b/website/src/components/SharedPatterns.tsx deleted file mode 100644 index 1cdca792..00000000 --- a/website/src/components/SharedPatterns.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { useCallback, useEffect, useMemo, useState } from 'react'; -import type { Tables } from '../../database.types'; -import { getMetadata } from '../metadata_parser'; -import { loadFeaturedPatterns, loadPublicPatterns } from '../repl/util.mjs'; - -export function PatternLabel({ pattern }: { pattern: Tables<'code'> }) { - const meta = useMemo(() => getMetadata(pattern.code), [pattern]); - return ( - <> - {pattern.id}. {meta.title || pattern.hash} by {meta.by.join(',') || 'Anonymous'} - - ); -} - -export const usePublicPatterns = () => { - const [publicPatterns, setPublicPatterns] = useState[] | null>([]); - const [featuredPatterns, setFeaturedPatterns] = useState[] | null>([]); - const init = useCallback(async () => { - const { data: _publicPatterns } = await loadPublicPatterns(); - const { data: _featuredPatterns } = await loadFeaturedPatterns(); - setPublicPatterns(_publicPatterns); - setFeaturedPatterns(_featuredPatterns); - }, []); - useEffect(() => { - init(); - }, [useCallback]); - return { publicPatterns, featuredPatterns }; -}; - -export function SharedPatterns() { - const { publicPatterns, featuredPatterns } = usePublicPatterns(); - return ( -
-

Featured

-
- {featuredPatterns?.map((pattern, i) => ( - - ))} -
-

Last Creations

-
- {publicPatterns?.map((pattern, i) => ( -
- - - -
- ))} -
-
- ); -} diff --git a/website/src/pages/browse.astro b/website/src/pages/browse.astro deleted file mode 100644 index 3e63bf87..00000000 --- a/website/src/pages/browse.astro +++ /dev/null @@ -1,14 +0,0 @@ ---- -import HeadCommon from '../components/HeadCommon.astro'; -import { SharedPatterns } from '../components/SharedPatterns'; ---- - - - - - -
-

Browse

- -
- diff --git a/website/src/repl/panel/PatternsTab.jsx b/website/src/repl/panel/PatternsTab.jsx index a3d67460..1b5ecef6 100644 --- a/website/src/repl/panel/PatternsTab.jsx +++ b/website/src/repl/panel/PatternsTab.jsx @@ -16,8 +16,8 @@ import { useSettings, } from '../../settings.mjs'; import * as tunes from '../tunes.mjs'; -import { PatternLabel } from '../../components/SharedPatterns'; import { useStore } from '@nanostores/react'; +import { getMetadata } from '../../metadata_parser'; function classNames(...classes) { return classes.filter(Boolean).join(' '); @@ -167,3 +167,12 @@ export function PatternsTab({ context }) {
); } + +export function PatternLabel({ pattern } /* : { pattern: Tables<'code'> } */) { + const meta = useMemo(() => getMetadata(pattern.code), [pattern]); + return ( + <> + {pattern.id}. {meta.title || pattern.hash} by {meta.by.join(',') || 'Anonymous'} + + ); +}