Merge pull request 'feat: add delete user samples button for convenience' (#1556) from delete_samples_button into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1556
This commit is contained in:
commit
e82629de2e
5 changed files with 61 additions and 25 deletions
10
website/src/repl/components/button/action-button.jsx
Normal file
10
website/src/repl/components/button/action-button.jsx
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import cx from '@src/cx.mjs';
|
||||||
|
|
||||||
|
export function ActionButton({ children, label, labelIsHidden, className, ...buttonProps }) {
|
||||||
|
return (
|
||||||
|
<button className={cx('hover:opacity-50 text-nowrap w-fit', className)} title={label} {...buttonProps}>
|
||||||
|
{labelIsHidden !== true && label}
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -12,8 +12,8 @@ import { useMemo } from 'react';
|
||||||
import { getMetadata } from '../../../metadata_parser.js';
|
import { getMetadata } from '../../../metadata_parser.js';
|
||||||
import { useExamplePatterns } from '../../useExamplePatterns.jsx';
|
import { useExamplePatterns } from '../../useExamplePatterns.jsx';
|
||||||
import { parseJSON, isUdels } from '../../util.mjs';
|
import { parseJSON, isUdels } from '../../util.mjs';
|
||||||
import { ButtonGroup } from './Forms.jsx';
|
import { useSettings } from '../../../settings.mjs';
|
||||||
import { settingsMap, useSettings } from '../../../settings.mjs';
|
import { ActionButton } from '../button/action-button.jsx';
|
||||||
import { Pagination } from '../pagination/Pagination.jsx';
|
import { Pagination } from '../pagination/Pagination.jsx';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useDebounce } from '../usedebounce.jsx';
|
import { useDebounce } from '../usedebounce.jsx';
|
||||||
|
|
@ -75,15 +75,6 @@ function PatternButtons({ patterns, activePattern, onClick, started }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ActionButton({ children, onClick, label, labelIsHidden }) {
|
|
||||||
return (
|
|
||||||
<button className="hover:opacity-50 text-nowrap" onClick={onClick} title={label}>
|
|
||||||
{labelIsHidden !== true && label}
|
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateCodeWindow = (context, patternData, reset = false) => {
|
const updateCodeWindow = (context, patternData, reset = false) => {
|
||||||
context.handleUpdate(patternData, reset);
|
context.handleUpdate(patternData, reset);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,21 @@ import useEvent from '@src/useEvent.mjs';
|
||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { getAudioContext, soundMap, connectToDestination } from '@strudel/webaudio';
|
import { getAudioContext, soundMap, connectToDestination } from '@strudel/webaudio';
|
||||||
import { useMemo, useRef, useState } from 'react';
|
import { useMemo, useRef, useState } from 'react';
|
||||||
import { settingsMap, useSettings } from '../../../settings.mjs';
|
import { settingsMap, soundFilterType, useSettings } from '../../../settings.mjs';
|
||||||
import { ButtonGroup } from './Forms.jsx';
|
import { ButtonGroup } from './Forms.jsx';
|
||||||
import ImportSoundsButton from './ImportSoundsButton.jsx';
|
import ImportSoundsButton from './ImportSoundsButton.jsx';
|
||||||
import { Textbox } from '../textbox/Textbox.jsx';
|
import { Textbox } from '../textbox/Textbox.jsx';
|
||||||
|
import { ActionButton } from '../button/action-button.jsx';
|
||||||
|
import { confirmDialog } from '@src/repl/util.mjs';
|
||||||
|
import { clearIDB, userSamplesDBConfig } from '@src/repl/idbutils.mjs';
|
||||||
|
import { prebake } from '@src/repl/prebake.mjs';
|
||||||
|
|
||||||
const getSamples = (samples) =>
|
const getSamples = (samples) =>
|
||||||
Array.isArray(samples) ? samples.length : typeof samples === 'object' ? Object.values(samples).length : 1;
|
Array.isArray(samples) ? samples.length : typeof samples === 'object' ? Object.values(samples).length : 1;
|
||||||
|
|
||||||
export function SoundsTab() {
|
export function SoundsTab() {
|
||||||
const sounds = useStore(soundMap);
|
const sounds = useStore(soundMap);
|
||||||
|
|
||||||
const { soundsFilter } = useSettings();
|
const { soundsFilter } = useSettings();
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const { BASE_URL } = import.meta.env;
|
const { BASE_URL } = import.meta.env;
|
||||||
|
|
@ -27,18 +32,19 @@ export function SoundsTab() {
|
||||||
.sort((a, b) => a[0].localeCompare(b[0]))
|
.sort((a, b) => a[0].localeCompare(b[0]))
|
||||||
.filter(([name]) => name.toLowerCase().includes(search.toLowerCase()));
|
.filter(([name]) => name.toLowerCase().includes(search.toLowerCase()));
|
||||||
|
|
||||||
if (soundsFilter === 'user') {
|
if (soundsFilter === soundFilterType.USER) {
|
||||||
return filtered.filter(([_, { data }]) => !data.prebake);
|
return filtered.filter(([_, { data }]) => !data.prebake);
|
||||||
}
|
}
|
||||||
if (soundsFilter === 'drums') {
|
if (soundsFilter === soundFilterType.DRUMS) {
|
||||||
return filtered.filter(([_, { data }]) => data.type === 'sample' && data.tag === 'drum-machines');
|
return filtered.filter(([_, { data }]) => data.type === 'sample' && data.tag === 'drum-machines');
|
||||||
}
|
}
|
||||||
if (soundsFilter === 'samples') {
|
if (soundsFilter === soundFilterType.SAMPLES) {
|
||||||
return filtered.filter(([_, { data }]) => data.type === 'sample' && data.tag !== 'drum-machines');
|
return filtered.filter(([_, { data }]) => data.type === 'sample' && data.tag !== 'drum-machines');
|
||||||
}
|
}
|
||||||
if (soundsFilter === 'synths') {
|
if (soundsFilter === soundFilterType.SYNTHS) {
|
||||||
return filtered.filter(([_, { data }]) => ['synth', 'soundfont'].includes(data.type));
|
return filtered.filter(([_, { data }]) => ['synth', 'soundfont'].includes(data.type));
|
||||||
}
|
}
|
||||||
|
//TODO: tidy this up, it does not need to be saved in settings
|
||||||
if (soundsFilter === 'importSounds') {
|
if (soundsFilter === 'importSounds') {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
@ -57,10 +63,10 @@ export function SoundsTab() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<div id="sounds-tab" className="px-4 flex flex-col w-full h-full text-foreground">
|
<div id="sounds-tab" className="px-4 flex gap-2 flex-col w-full h-full text-foreground">
|
||||||
<Textbox placeholder="Search" value={search} onChange={(v) => setSearch(v)} />
|
<Textbox placeholder="Search" value={search} onChange={(v) => setSearch(v)} />
|
||||||
|
|
||||||
<div className="pb-2 flex shrink-0 flex-wrap">
|
<div className=" flex shrink-0 flex-wrap">
|
||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
value={soundsFilter}
|
value={soundsFilter}
|
||||||
onChange={(value) => settingsMap.setKey('soundsFilter', value)}
|
onChange={(value) => settingsMap.setKey('soundsFilter', value)}
|
||||||
|
|
@ -74,7 +80,26 @@ export function SoundsTab() {
|
||||||
></ButtonGroup>
|
></ButtonGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="min-h-0 max-h-full grow overflow-auto text-sm break-normal pb-2">
|
{soundsFilter === soundFilterType.USER && soundEntries.length > 0 && (
|
||||||
|
<ActionButton
|
||||||
|
className="pl-2"
|
||||||
|
label="delete-all"
|
||||||
|
onClick={async () => {
|
||||||
|
try {
|
||||||
|
const confirmed = await confirmDialog('Delete all imported user samples?');
|
||||||
|
if (confirmed) {
|
||||||
|
clearIDB(userSamplesDBConfig.dbName);
|
||||||
|
soundMap.set({});
|
||||||
|
await prebake();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="min-h-0 max-h-full grow overflow-auto text-sm break-normal bg-background p-2 rounded-md">
|
||||||
{soundEntries.map(([name, { data, onTrigger }]) => {
|
{soundEntries.map(([name, { data, onTrigger }]) => {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
|
|
@ -151,9 +176,7 @@ export function SoundsTab() {
|
||||||
) : (
|
) : (
|
||||||
''
|
''
|
||||||
)}
|
)}
|
||||||
{!soundEntries.length && soundsFilter !== 'importSounds'
|
{!soundEntries.length && soundsFilter !== 'importSounds' ? 'No sounds loaded' : ''}
|
||||||
? 'No custom sounds loaded in this pattern (yet).'
|
|
||||||
: ''}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -12,17 +12,21 @@ export const userSamplesDBConfig = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// deletes all of the databases, useful for debugging
|
// deletes all of the databases, useful for debugging
|
||||||
function clearIDB() {
|
function clearAllIDB() {
|
||||||
window.indexedDB
|
window.indexedDB
|
||||||
.databases()
|
.databases()
|
||||||
.then((r) => {
|
.then((r) => {
|
||||||
for (var i = 0; i < r.length; i++) window.indexedDB.deleteDatabase(r[i].name);
|
for (var i = 0; i < r.length; i++) clearIDB(r[i].name);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
alert('All data cleared.');
|
alert('All data cleared.');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function clearIDB(dbName) {
|
||||||
|
return window.indexedDB.deleteDatabase(dbName);
|
||||||
|
}
|
||||||
|
|
||||||
// queries the DB, and registers the sounds so they can be played
|
// queries the DB, and registers the sounds so they can be played
|
||||||
export function registerSamplesFromDB(config = userSamplesDBConfig, onComplete = () => {}) {
|
export function registerSamplesFromDB(config = userSamplesDBConfig, onComplete = () => {}) {
|
||||||
openDB(config, (objectStore) => {
|
openDB(config, (objectStore) => {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,14 @@ export const audioEngineTargets = {
|
||||||
osc: 'osc',
|
osc: 'osc',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const soundFilterType = {
|
||||||
|
USER: 'user',
|
||||||
|
DRUMS: 'drums',
|
||||||
|
SAMPLES: 'samples',
|
||||||
|
SYNTHS: 'synths',
|
||||||
|
ALL: 'all',
|
||||||
|
};
|
||||||
|
|
||||||
export const defaultSettings = {
|
export const defaultSettings = {
|
||||||
activeFooter: 'intro',
|
activeFooter: 'intro',
|
||||||
keybindings: 'codemirror',
|
keybindings: 'codemirror',
|
||||||
|
|
@ -28,7 +36,7 @@ export const defaultSettings = {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
latestCode: '',
|
latestCode: '',
|
||||||
isZen: false,
|
isZen: false,
|
||||||
soundsFilter: 'all',
|
soundsFilter: soundFilterType.ALL,
|
||||||
patternFilter: 'community',
|
patternFilter: 'community',
|
||||||
// panelPosition: window.innerWidth > 1000 ? 'right' : 'bottom', //FIX: does not work on astro
|
// panelPosition: window.innerWidth > 1000 ? 'right' : 'bottom', //FIX: does not work on astro
|
||||||
panelPosition: 'right',
|
panelPosition: 'right',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue