Merge branch 'main' into pattern_selection
This commit is contained in:
commit
106772e480
50 changed files with 1924 additions and 544 deletions
|
|
@ -1,6 +1,13 @@
|
|||
import { DocumentDuplicateIcon, PencilSquareIcon, TrashIcon } from '@heroicons/react/20/solid';
|
||||
|
||||
import {
|
||||
|
||||
$featuredPatterns,
|
||||
$publicPatterns,
|
||||
clearUserPatterns,
|
||||
deleteActivePattern,
|
||||
duplicateActivePattern,
|
||||
|
||||
exportPatterns,
|
||||
importPatterns,
|
||||
useActivePattern,
|
||||
|
|
@ -9,8 +16,14 @@ import {
|
|||
userPattern,
|
||||
examplePattern,
|
||||
} from '../../settings.mjs';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import * as tunes from '../tunes.mjs';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { getMetadata } from '../../metadata_parser';
|
||||
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ');
|
||||
}
|
||||
|
|
@ -50,6 +63,7 @@ export function PatternsTab({ context }) {
|
|||
const { userPatterns } = useSettings();
|
||||
const examplePatterns = useMemo(() => examplePattern.getAll(), []);
|
||||
const activePattern = useActivePattern();
|
||||
|
||||
const viewingPattern = useViewingPattern();
|
||||
const updateCodeWindow = (id, code, reset = false) => {
|
||||
context.handleUpdate(id, code, reset);
|
||||
|
|
@ -62,6 +76,11 @@ export function PatternsTab({ context }) {
|
|||
};
|
||||
|
||||
const isExample = examplePatterns[viewingPattern] != null;
|
||||
|
||||
const featuredPatterns = useStore($featuredPatterns);
|
||||
const publicPatterns = useStore($publicPatterns);
|
||||
const isExample = useMemo(() => activePattern && !!tunes[activePattern], [activePattern]);
|
||||
|
||||
return (
|
||||
<div className="px-4 w-full dark:text-white text-stone-900 space-y-4 pb-4">
|
||||
<section>
|
||||
|
|
@ -147,7 +166,52 @@ export function PatternsTab({ context }) {
|
|||
</button>
|
||||
</div>
|
||||
</section>
|
||||
{featuredPatterns && (
|
||||
<section>
|
||||
<h2 className="text-xl mb-2">Featured Patterns</h2>
|
||||
<div className="font-mono text-sm">
|
||||
{featuredPatterns.map((pattern) => (
|
||||
<a
|
||||
key={pattern.id}
|
||||
className={classNames(
|
||||
'mr-4 hover:opacity-50 cursor-pointer block',
|
||||
pattern.hash === activePattern ? 'outline outline-1' : '',
|
||||
)}
|
||||
onClick={() => {
|
||||
setActivePattern(pattern.hash);
|
||||
context.handleUpdate(pattern.code, true);
|
||||
}}
|
||||
>
|
||||
<PatternLabel pattern={pattern} />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
{publicPatterns && (
|
||||
<section>
|
||||
<h2 className="text-xl mb-2">Last Creations</h2>
|
||||
<div className="font-mono text-sm">
|
||||
{publicPatterns.map((pattern) => (
|
||||
<a
|
||||
key={'public-' + pattern.id}
|
||||
className={classNames(
|
||||
'mr-4 hover:opacity-50 cursor-pointer block', // inline-block
|
||||
pattern.hash === activePattern ? 'outline outline-1' : '',
|
||||
)}
|
||||
onClick={() => {
|
||||
setActivePattern(pattern.hash);
|
||||
context.handleUpdate(pattern.code, true);
|
||||
}}
|
||||
>
|
||||
<PatternLabel pattern={pattern} />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
<section>
|
||||
|
||||
<h2 className="text-xl mb-2">Examples</h2>
|
||||
<PatternButtons
|
||||
onClick={(id) => onPatternBtnClick(id, true)}
|
||||
|
|
@ -156,7 +220,36 @@ export function PatternsTab({ context }) {
|
|||
activePattern={activePattern}
|
||||
viewingPattern={viewingPattern}
|
||||
/>
|
||||
|
||||
<h2 className="text-xl mb-2">Stock Examples</h2>
|
||||
<div className="font-mono text-sm">
|
||||
{Object.entries(tunes).map(([key, tune]) => (
|
||||
<a
|
||||
key={key}
|
||||
className={classNames(
|
||||
'mr-4 hover:opacity-50 cursor-pointer inline-block',
|
||||
key === activePattern ? 'outline outline-1' : '',
|
||||
)}
|
||||
onClick={() => {
|
||||
setActivePattern(key);
|
||||
context.handleUpdate(tune, true);
|
||||
}}
|
||||
>
|
||||
{key}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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'}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,32 +57,36 @@ export function SoundsTab() {
|
|||
<ImportSoundsButton onComplete={() => settingsMap.setKey('soundsFilter', 'user')} />
|
||||
</div>
|
||||
<div className="min-h-0 max-h-full grow overflow-auto font-mono text-sm break-normal">
|
||||
{soundEntries.map(([name, { data, onTrigger }]) => (
|
||||
<span
|
||||
key={name}
|
||||
className="cursor-pointer hover:opacity-50"
|
||||
onMouseDown={async () => {
|
||||
const ctx = getAudioContext();
|
||||
const params = {
|
||||
note: ['synth', 'soundfont'].includes(data.type) ? 'a3' : undefined,
|
||||
s: name,
|
||||
clip: 1,
|
||||
release: 0.5,
|
||||
};
|
||||
const time = ctx.currentTime + 0.05;
|
||||
const onended = () => trigRef.current?.node?.disconnect();
|
||||
trigRef.current = Promise.resolve(onTrigger(time, params, onended));
|
||||
trigRef.current.then((ref) => {
|
||||
connectToDestination(ref?.node);
|
||||
});
|
||||
}}
|
||||
>
|
||||
{' '}
|
||||
{name}
|
||||
{data?.type === 'sample' ? `(${getSamples(data.samples)})` : ''}
|
||||
{data?.type === 'soundfont' ? `(${data.fonts.length})` : ''}
|
||||
</span>
|
||||
))}
|
||||
{soundEntries.map(([name, { data, onTrigger }]) => {
|
||||
return (
|
||||
<span
|
||||
key={name}
|
||||
className="cursor-pointer hover:opacity-50"
|
||||
onMouseDown={async () => {
|
||||
const ctx = getAudioContext();
|
||||
const params = {
|
||||
note: ['synth', 'soundfont'].includes(data.type) ? 'a3' : undefined,
|
||||
s: name,
|
||||
clip: 1,
|
||||
release: 0.5,
|
||||
sustain: 1,
|
||||
duration: 0.5,
|
||||
};
|
||||
const time = ctx.currentTime + 0.05;
|
||||
const onended = () => trigRef.current?.node?.disconnect();
|
||||
trigRef.current = Promise.resolve(onTrigger(time, params, onended));
|
||||
trigRef.current.then((ref) => {
|
||||
connectToDestination(ref?.node);
|
||||
});
|
||||
}}
|
||||
>
|
||||
{' '}
|
||||
{name}
|
||||
{data?.type === 'sample' ? `(${getSamples(data.samples)})` : ''}
|
||||
{data?.type === 'soundfont' ? `(${data.fonts.length})` : ''}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
{!soundEntries.length ? 'No custom sounds loaded in this pattern (yet).' : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,13 +9,37 @@ import { createClient } from '@supabase/supabase-js';
|
|||
import { nanoid } from 'nanoid';
|
||||
import { writeText } from '@tauri-apps/api/clipboard';
|
||||
import { createContext } from 'react';
|
||||
import { $publicPatterns, $featuredPatterns } from '../settings.mjs';
|
||||
|
||||
// Create a single supabase client for interacting with your database
|
||||
const supabase = createClient(
|
||||
export const supabase = createClient(
|
||||
'https://pidxdsxphlhzjnzmifth.supabase.co',
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM',
|
||||
);
|
||||
|
||||
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 });
|
||||
}
|
||||
|
||||
async function loadDBPatterns() {
|
||||
try {
|
||||
const { data: publicPatterns } = await loadPublicPatterns();
|
||||
const { data: featuredPatterns } = await loadFeaturedPatterns();
|
||||
$publicPatterns.set(publicPatterns);
|
||||
$featuredPatterns.set(featuredPatterns);
|
||||
} catch (err) {
|
||||
console.error('error loading patterns');
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
loadDBPatterns();
|
||||
}
|
||||
|
||||
export async function initCode() {
|
||||
// load code from url hash (either short hash from database or decode long hash)
|
||||
try {
|
||||
|
|
@ -90,10 +114,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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue