diff --git a/interface/app/$libraryId/Explorer/useExplorerPreferences.ts b/interface/app/$libraryId/Explorer/useExplorerPreferences.ts new file mode 100644 index 000000000..6af94ec7b --- /dev/null +++ b/interface/app/$libraryId/Explorer/useExplorerPreferences.ts @@ -0,0 +1,69 @@ +import { useMemo } from 'react'; +import { + ExplorerSettings, + LibraryPreferences, + Ordering, + useExplorerLayoutStore, + useLibraryMutation, + useLibraryQuery, + useRspcLibraryContext +} from '@sd/client'; + +import { createDefaultExplorerSettings } from './store'; + +// preferences are settings persisted to the db and synced +export function useExplorerPreferences({ + data, + createDefaultSettings, + getSettings, + writeSettings +}: { + data: TData; + createDefaultSettings(): ReturnType>; + getSettings: (prefs: LibraryPreferences) => ExplorerSettings | undefined; + writeSettings: (settings: ExplorerSettings) => LibraryPreferences; +}) { + const rspc = useRspcLibraryContext(); + const explorerLayout = useExplorerLayoutStore(); + + const preferences = useLibraryQuery(['preferences.get']); + const updatePreferences = useLibraryMutation('preferences.update'); + + const settings = useMemo(() => { + const defaults = createDefaultSettings(); + + if (!location || !preferences.data) return defaults; + + const settings = getSettings(preferences.data); + + // Overwrite the default layout with the user's preference + Object.assign(defaults, { layoutMode: explorerLayout.defaultView }); + + if (!settings) return defaults; + + for (const [key, value] of Object.entries(settings)) { + if (value !== null) Object.assign(defaults, { [key]: value }); + } + + return defaults; + }, [preferences.data, getSettings, createDefaultSettings, explorerLayout.defaultView]); + + const onSettingsChanged = async (settings: ExplorerSettings) => { + if (preferences.isLoading) return; + + try { + await updatePreferences.mutateAsync(writeSettings(settings)); + rspc.queryClient.invalidateQueries(['preferences.get']); + } catch (e) { + alert('An error has occurred while updating your preferences.'); + } + }; + + return Object.assign(preferences, { + explorerSettingsProps: { + settings, + onSettingsChanged, + data + } + }); +} diff --git a/interface/app/$libraryId/location/$id.tsx b/interface/app/$libraryId/location/$id.tsx index e2c53e988..520ab8c89 100644 --- a/interface/app/$libraryId/location/$id.tsx +++ b/interface/app/$libraryId/location/$id.tsx @@ -3,7 +3,6 @@ import { useEffect, useMemo } from 'react'; import { stringify } from 'uuid'; import { arraysEqual, - ExplorerSettings, FilePathOrder, Location, useCache, @@ -12,8 +11,7 @@ import { useLibraryQuery, useLibrarySubscription, useNodes, - useOnlineLocations, - useRspcLibraryContext + useOnlineLocations } from '@sd/client'; import { Loader, Tooltip } from '@sd/ui'; import { LocationIdParamsSchema } from '~/app/route-schemas'; @@ -37,6 +35,7 @@ import { } from '../Explorer/store'; import { DefaultTopBarOptions } from '../Explorer/TopBarOptions'; import { useExplorer, useExplorerSettings } from '../Explorer/useExplorer'; +import { useExplorerPreferences } from '../Explorer/useExplorerPreferences'; import { useExplorerSearchParams } from '../Explorer/util'; import { EmptyNotice } from '../Explorer/View/EmptyNotice'; import { SearchContextProvider, SearchOptions, useSearchFromSearchParams } from '../search'; @@ -225,59 +224,22 @@ function getLastSectionOfPath(path: string): string | undefined { } function useLocationExplorerSettings(location: Location) { - const rspc = useRspcLibraryContext(); - - const preferences = useLibraryQuery(['preferences.get']); - const updatePreferences = useLibraryMutation('preferences.update'); - const explorerLayout = useExplorerLayoutStore(); - - const settings = useMemo(() => { - const defaults = createDefaultExplorerSettings({ - order: { field: 'name', value: 'Asc' } - }); - - if (!location) return defaults; - - const pubId = stringify(location.pub_id); - - const settings = preferences.data?.location?.[pubId]?.explorer; - - // Overwrite the default layout with the user's preference - Object.assign(defaults, { layoutMode: explorerLayout.defaultView }); - - if (!settings) return defaults; - - for (const [key, value] of Object.entries(settings)) { - if (value !== null) Object.assign(defaults, { [key]: value }); - } - - return defaults; - }, [explorerLayout.defaultView, location, preferences.data?.location]); - - const onSettingsChanged = async ( - settings: ExplorerSettings, - changedLocation: Location - ) => { - if (changedLocation.id === location.id && preferences.isLoading) return; - - const pubId = stringify(changedLocation.pub_id); - - try { - await updatePreferences.mutateAsync({ - location: { [pubId]: { explorer: settings } } - }); - rspc.queryClient.invalidateQueries(['preferences.get']); - } catch (e) { - alert('An error has occurred while updating your preferences.'); - } - }; + const preferences = useExplorerPreferences({ + data: location, + createDefaultSettings: () => + createDefaultExplorerSettings({ + order: { field: 'name', value: 'Asc' } + }), + getSettings: (prefs) => prefs.location?.[stringify(location.pub_id)]?.explorer, + writeSettings: (settings) => ({ + location: { [stringify(location.pub_id)]: { explorer: settings } } + }) + }); return { explorerSettings: useExplorerSettings({ - settings, - onSettingsChanged, - orderingKeys: filePathOrderingKeysSchema, - data: location + ...preferences.explorerSettingsProps, + orderingKeys: filePathOrderingKeysSchema }), preferences }; diff --git a/interface/app/$libraryId/tag/$id.tsx b/interface/app/$libraryId/tag/$id.tsx index e8921288e..3202376e0 100644 --- a/interface/app/$libraryId/tag/$id.tsx +++ b/interface/app/$libraryId/tag/$id.tsx @@ -1,14 +1,5 @@ import { useMemo } from 'react'; -import { - ExplorerSettings, - ObjectOrder, - Tag, - useCache, - useLibraryMutation, - useLibraryQuery, - useNodes, - useRspcLibraryContext -} from '@sd/client'; +import { ObjectOrder, Tag, useCache, useLibraryQuery, useNodes } from '@sd/client'; import { LocationIdParamsSchema } from '~/app/route-schemas'; import { Icon } from '~/components'; import { useLocale, useRouteTitle, useZodRouteParams } from '~/hooks'; @@ -19,6 +10,7 @@ import { ExplorerContextProvider } from '../Explorer/Context'; import { createDefaultExplorerSettings, objectOrderingKeysSchema } from '../Explorer/store'; import { DefaultTopBarOptions } from '../Explorer/TopBarOptions'; import { useExplorer, useExplorerSettings } from '../Explorer/useExplorer'; +import { useExplorerPreferences } from '../Explorer/useExplorerPreferences'; import { EmptyNotice } from '../Explorer/View/EmptyNotice'; import { SearchContextProvider, SearchOptions, useSearchFromSearchParams } from '../search'; import SearchBar from '../search/SearchBar'; @@ -97,51 +89,20 @@ export function Component() { } function useTagExplorerSettings(tag: Tag) { - const rspc = useRspcLibraryContext(); - - const preferences = useLibraryQuery(['preferences.get']); - const updatePreferences = useLibraryMutation('preferences.update'); - - const settings = useMemo(() => { - const defaults = createDefaultExplorerSettings({ order: null }); - - if (!location) return defaults; - - const pubId = stringify(tag.pub_id); - - const settings = preferences.data?.tag?.[pubId]?.explorer; - - if (!settings) return defaults; - - for (const [key, value] of Object.entries(settings)) { - if (value !== null) Object.assign(defaults, { [key]: value }); - } - - return defaults; - }, [tag, preferences.data?.tag]); - - const onSettingsChanged = async (settings: ExplorerSettings, changedTag: Tag) => { - if (changedTag.id === tag.id && preferences.isLoading) return; - - const pubId = stringify(changedTag.pub_id); - - try { - await updatePreferences.mutateAsync({ - tag: { [pubId]: { explorer: settings } } - }); - rspc.queryClient.invalidateQueries(['preferences.get']); - } catch (e) { - alert('An error has occurred while updating your preferences.'); - } - }; + const preferences = useExplorerPreferences({ + data: tag, + createDefaultSettings: () => createDefaultExplorerSettings({ order: null }), + getSettings: (prefs) => prefs.tag?.[stringify(tag.pub_id)]?.explorer, + writeSettings: (settings) => ({ + tag: { [stringify(tag.pub_id)]: { explorer: settings } } + }) + }); return { + preferences, explorerSettings: useExplorerSettings({ - settings, - onSettingsChanged, - orderingKeys: objectOrderingKeysSchema, - data: tag - }), - preferences + ...preferences.explorerSettingsProps, + orderingKeys: objectOrderingKeysSchema + }) }; }