From 025107949ebb898853cb03003a40cf5b0de44c0c Mon Sep 17 00:00:00 2001 From: James Gentes Date: Tue, 21 May 2024 19:27:02 -0700 Subject: [PATCH] fix for stemhandler --- app/api/handlers/fileHandlers.ts | 13 +++---- app/api/models/appModels.ts | 58 +++++++++++++++---------------- app/api/models/appState.client.ts | 20 ++++++----- 3 files changed, 48 insertions(+), 43 deletions(-) diff --git a/app/api/handlers/fileHandlers.ts b/app/api/handlers/fileHandlers.ts index 5552b82..7db2da4 100644 --- a/app/api/handlers/fileHandlers.ts +++ b/app/api/handlers/fileHandlers.ts @@ -1,17 +1,18 @@ +import { ref } from 'valtio' import { type Stem, type Track, type TrackCache, addToMix, db, - storeTrackCache + storeTrackCache, } from '~/api/handlers/dbHandlers' import type { StemState } from '~/api/models/appModels' import { audioState, mixState, uiState, - userState + userState, } from '~/api/models/appState.client' import { errorHandler } from '~/utils/notifications' import { processTracks } from './audioHandlers.client' @@ -33,7 +34,7 @@ function showOpenFilePickerPolyfill(options: OpenFilePickerOptions) { getFile: async () => new Promise(resolve => { resolve(file) - }) + }), } }) ) @@ -159,14 +160,14 @@ const getStemsDirHandle = async (): Promise< const newStemsDirHandle = await window.showDirectoryPicker({ startIn: stemsDirHandle, id: 'stemsDir', - mode: 'readwrite' + mode: 'readwrite', }) if ( (await newStemsDirHandle.queryPermission({ mode: 'readwrite' })) === 'granted' ) { - userState.stemsDirHandle = newStemsDirHandle + userState.stemsDirHandle = ref(newStemsDirHandle) return newStemsDirHandle } } @@ -190,7 +191,7 @@ const validateTrackStemAccess = async ( // do we have access to the stem dir? try { const stemDirAccess = await stemsDirHandle.queryPermission({ - mode: 'readwrite' + mode: 'readwrite', }) if (stemDirAccess !== 'granted') return 'grantStemDirAccess' } catch (e) { diff --git a/app/api/models/appModels.ts b/app/api/models/appModels.ts index 843b1f4..f05df62 100644 --- a/app/api/models/appModels.ts +++ b/app/api/models/appModels.ts @@ -23,7 +23,7 @@ class MixpointDb extends Dexie { mixes: '++id, tracks', sets: '++id, mixes', trackCache: 'id', - appState: '' + appState: '', }) // example migration: // @@ -120,12 +120,6 @@ type TrackCache = { }> } -// AppState provides persistence for Valtio state, which has much simpler get/set than Dexie -type AppState = Partial<{ - userState: UserState - mixState: MixState -}> - type TrackState = Partial<{ adjustedBpm: Track['bpm'] beatResolution: '1:1' | '1:2' | '1:4' @@ -133,27 +127,13 @@ type TrackState = Partial<{ mixpointTime: number // seconds }> -type MixState = { - tracks: Track['id'][] - trackState: { - [trackId: Track['id']]: TrackState - } -} - -type UserState = Partial<{ - sortDirection: 'ascending' | 'descending' - sortColumn: Key - visibleColumns: Set // track table visible columns - stemsDirHandle: FileSystemDirectoryHandle // local folder on file system to store stems -}> - -// AudioState is the working state of the mix, limited to the # of tracks in use, thereby not storing waveforms for all tracks +// AudioState is the working state of the mix, should not be persisted due to time/volumeMeter! type AudioState = Partial<{ - waveform: WaveSurfer // must be a valtio ref() + waveform: WaveSurfer // ref() playing: boolean time: number - gainNode?: GainNode // gain controls actual loudness of track, must be a ref() - analyserNode?: AnalyserNode // analyzerNode is used for volumeMeter, must be a ref() + gainNode?: GainNode // ref() gain controls actual loudness of track + analyserNode?: AnalyserNode // ref() analyzerNode is used for volumeMeter volume: number // volume is the crossfader value volumeMeter?: number // value between 0 and 1 stems: Stems @@ -163,9 +143,9 @@ type AudioState = Partial<{ type Stems = { [key in Stem]: Partial<{ - waveform: WaveSurfer // must be a valtio ref() - gainNode?: GainNode // gain controls actual loudness of stem, must be a ref() - analyserNode?: AnalyserNode // analyzerNode is used for volumeMeter, must be a ref() + waveform: WaveSurfer // ref() + gainNode?: GainNode // ref() gain controls actual loudness of stem + analyserNode?: AnalyserNode // ref() analyzerNode is used for volumeMeter volume: number // volume is the crossfader value volumeMeter: number mute: boolean @@ -211,6 +191,26 @@ type UiState = { modal: ModalState } +// AppState provides persistence for Valtio state, which has much simpler get/set than Dexie +type AppState = Partial<{ + userState: UserState + mixState: MixState +}> + +type MixState = { + tracks: Track['id'][] + trackState: { + [trackId: Track['id']]: TrackState + } +} + +type UserState = { + sortDirection: 'ascending' | 'descending' + sortColumn: Key + visibleColumns: Set // track table visible columns + stemsDirHandle: FileSystemDirectoryHandle // ref() local folder on file system to store stems +} + // Avoid having two files export same type names export type { Track, @@ -226,6 +226,6 @@ export type { MixState, TrackState, AudioState, - UiState + UiState, } export { db, STEMS, EFFECTS } diff --git a/app/api/models/appState.client.ts b/app/api/models/appState.client.ts index ddf346e..ffe2a71 100644 --- a/app/api/models/appState.client.ts +++ b/app/api/models/appState.client.ts @@ -1,5 +1,5 @@ // This file handles application state that may be persisted to local storage. -import { proxy, snapshot } from 'valtio' +import { proxy, ref, snapshot } from 'valtio' import { devtools, proxySet, watch } from 'valtio/utils' import { db } from '~/api/handlers/dbHandlers' import type { @@ -7,7 +7,7 @@ import type { MixState, Track, UiState, - UserState + UserState, } from '~/api/models/appModels' import { Env } from '~/utils/env' @@ -29,15 +29,13 @@ const uiState = proxy({ stemsAnalyzing: proxySet(), syncTimer: undefined, userEmail: '', - modal: { openState: false } + modal: { openState: false }, }) // Pull latest persistent state from Dexie and populate Valtio store let seeded = false const initialMixState = (await db.appState.get('mixState')) as MixState -const mixState = proxy( - initialMixState || { tracks: [], trackState: {} } -) +const mixState = proxy(initialMixState) watch(async get => { get(mixState) @@ -45,8 +43,14 @@ watch(async get => { if (seeded) db.appState.put(snapshot(mixState), 'mixState') }) -const initialUserState = (await db.appState.get('userState')) as UserState -const userState = proxy(initialUserState || {}) +let initialUserState = (await db.appState.get('userState')) as UserState +// ensure that we ref the stemsDirHandle +if (initialUserState?.stemsDirHandle) + initialUserState = { + ...initialUserState, + stemsDirHandle: ref(initialUserState.stemsDirHandle), + } +const userState = proxy(initialUserState) watch(async get => { get(userState)