From d16fdedd3db0b5d590c1719338f43b40c8dae09d Mon Sep 17 00:00:00 2001 From: Jamie Pine Date: Sun, 4 Sep 2022 15:58:16 -0700 Subject: [PATCH] valtio --- .vscode/settings.json | 3 +- packages/client/package.json | 4 +- packages/client/src/stores/explorerStore.ts | 48 +++++++++++++++ packages/client/src/stores/index.ts | 2 +- .../client/src/stores/useExplorerStore.ts | 57 ------------------ packages/client/src/stores/useLibraryStore.ts | 4 +- packages/interface/package.json | 2 + .../src/components/explorer/Explorer.tsx | 10 +-- .../explorer/ExplorerContextMenu.tsx | 5 +- .../src/components/explorer/FileItem.tsx | 17 +++--- .../src/components/explorer/FileRow.tsx | 2 +- .../src/components/explorer/FileThumb.tsx | 5 +- .../components/explorer/VirtualizedList.tsx | 27 +++++---- .../src/components/layout/TopBar.tsx | 11 ++-- .../src/screens/LocationExplorer.tsx | 6 +- pnpm-lock.yaml | Bin 675031 -> 677479 bytes 16 files changed, 99 insertions(+), 104 deletions(-) create mode 100644 packages/client/src/stores/explorerStore.ts delete mode 100644 packages/client/src/stores/useExplorerStore.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index a21f04485..1f767097e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -27,7 +27,8 @@ "trivago", "tsparticles", "unlisten", - "upsert" + "upsert", + "valtio" ], "[rust]": { "editor.defaultFormatter": "rust-lang.rust-analyzer" diff --git a/packages/client/package.json b/packages/client/package.json index 1c9df3845..2c16230fc 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -18,13 +18,15 @@ }, "dependencies": { "@rspc/client": "^0.0.5", - "@sd/core": "workspace:*", "@sd/config": "workspace:*", + "@sd/core": "workspace:*", "@sd/interface": "workspace:*", "@tanstack/react-query": "^4.0.10", "eventemitter3": "^4.0.7", "immer": "^9.0.15", "lodash": "^4.17.21", + "valtio": "^1.6.4", + "valtio-persist": "^1.0.2", "zustand": "4.0.0" }, "devDependencies": { diff --git a/packages/client/src/stores/explorerStore.ts b/packages/client/src/stores/explorerStore.ts new file mode 100644 index 000000000..e1679fe32 --- /dev/null +++ b/packages/client/src/stores/explorerStore.ts @@ -0,0 +1,48 @@ +import { proxy, ref } from 'valtio'; + +export type ExplorerLayoutMode = 'list' | 'grid'; + +export enum ExplorerKind { + Location, + Tag, + Space +} + +const state = { + locationId: null as number | null, + layoutMode: 'grid' as ExplorerLayoutMode, + gridItemSize: 100, + listItemSize: 40, + selectedRowIndex: 1, + showInspector: true, + multiSelectIndexes: [] as number[], + contextMenuObjectId: null as number | null, + newThumbnails: {} as Record +}; + +export const explorerStore = proxy({ + ...state, + reset: () => resetStore(explorerStore, state), + addNewThumbnail: (cas_id: string) => { + explorerStore.newThumbnails[cas_id] = true; + }, + selectMore: (indexes: number[]) => { + if (!explorerStore.multiSelectIndexes.length && indexes.length) { + explorerStore.multiSelectIndexes = [explorerStore.selectedRowIndex, ...indexes]; + } else { + explorerStore.multiSelectIndexes = [ + ...new Set([...explorerStore.multiSelectIndexes, ...indexes]) + ]; + } + } +}); + +export function resetStore, E extends Record>( + store: T, + defaults: E +) { + for (const key in defaults) { + // @ts-ignore + store[key] = defaults[key]; + } +} diff --git a/packages/client/src/stores/index.ts b/packages/client/src/stores/index.ts index 96babfd41..a44e3a69c 100644 --- a/packages/client/src/stores/index.ts +++ b/packages/client/src/stores/index.ts @@ -1,2 +1,2 @@ export * from './useLibraryStore'; -export * from './useExplorerStore'; +export * from './explorerStore'; diff --git a/packages/client/src/stores/useExplorerStore.ts b/packages/client/src/stores/useExplorerStore.ts deleted file mode 100644 index 2c956216b..000000000 --- a/packages/client/src/stores/useExplorerStore.ts +++ /dev/null @@ -1,57 +0,0 @@ -import produce from 'immer'; -import create from 'zustand'; - -export type ExplorerLayoutMode = 'list' | 'grid'; - -export enum ExplorerKind { - Location, - Tag, - Space -} - -type ExplorerStore = { - layoutMode: ExplorerLayoutMode; - locationId: number | null; // used by top bar - gridItemSize: number; - listItemSize: number; - showInspector: boolean; - selectedRowIndex: number; - multiSelectIndexes: number[]; - contextMenuObjectId: number | null; - newThumbnails: Record; - addNewThumbnail: (cas_id: string) => void; - selectMore: (indexes: number[]) => void; - reset: () => void; - set: (changes: Partial) => void; -}; - -export const useExplorerStore = create((set) => ({ - layoutMode: 'grid', - locationId: null, - gridItemSize: 100, - listItemSize: 40, - showInspector: true, - selectedRowIndex: 1, - multiSelectIndexes: [], - contextMenuObjectId: -1, - newThumbnails: {}, - addNewThumbnail: (cas_id) => - set((state) => - produce(state, (draft) => { - draft.newThumbnails[cas_id] = true; - }) - ), - selectMore: (indexes) => { - set((state) => - produce(state, (draft) => { - if (!draft.multiSelectIndexes.length && indexes.length) { - draft.multiSelectIndexes = [draft.selectedRowIndex, ...indexes]; - } else { - draft.multiSelectIndexes = [...new Set([...draft.multiSelectIndexes, ...indexes])]; - } - }) - ); - }, - reset: () => set(() => ({})), - set: (changes) => set((state) => ({ ...state, ...changes })) -})); diff --git a/packages/client/src/stores/useLibraryStore.ts b/packages/client/src/stores/useLibraryStore.ts index 5e348d658..4e736391e 100644 --- a/packages/client/src/stores/useLibraryStore.ts +++ b/packages/client/src/stores/useLibraryStore.ts @@ -5,7 +5,7 @@ import create from 'zustand'; import { devtools, persist } from 'zustand/middleware'; import { useBridgeQuery } from '../index'; -import { useExplorerStore } from './useExplorerStore'; +import { explorerStore } from './explorerStore'; type LibraryStore = { // the uuid of the currently active library @@ -28,7 +28,7 @@ export const useLibraryStore = create()( }) ); // reset other stores - useExplorerStore().reset(); + explorerStore.reset(); }, init: async (libraries) => { set((state) => diff --git a/packages/interface/package.json b/packages/interface/package.json index 3df99a7eb..dc49dce1f 100644 --- a/packages/interface/package.json +++ b/packages/interface/package.json @@ -68,6 +68,8 @@ "tailwindcss": "^3.1.6", "use-count-up": "^3.0.1", "use-debounce": "^8.0.3", + "valtio": "^1.6.4", + "valtio-persist": "^1.0.2", "zod": "^3.18.0", "zustand": "4.0.0" }, diff --git a/packages/interface/src/components/explorer/Explorer.tsx b/packages/interface/src/components/explorer/Explorer.tsx index aa70d89cd..d96aafec1 100644 --- a/packages/interface/src/components/explorer/Explorer.tsx +++ b/packages/interface/src/components/explorer/Explorer.tsx @@ -1,6 +1,6 @@ import { + explorerStore, rspc, - useExplorerStore, useLibraryMutation, useLibraryQuery, useLibraryStore @@ -17,6 +17,7 @@ import { TrashSimple } from 'phosphor-react'; import React, { memo, useLayoutEffect, useMemo, useRef, useState } from 'react'; +import { useSnapshot } from 'valtio'; import { Inspector } from '../explorer/Inspector'; import { WithContextMenu } from '../layout/MenuOverlay'; @@ -29,7 +30,7 @@ interface Props { } export default function Explorer(props: Props) { - const addNewThumbnail = useExplorerStore((store) => store.addNewThumbnail); + const { addNewThumbnail, selectedRowIndex, showInspector } = useSnapshot(explorerStore); const currentLibraryUuid = useLibraryStore((store) => store.currentLibraryUuid); @@ -39,11 +40,6 @@ export default function Explorer(props: Props) { } }); - const { selectedRowIndex, showInspector } = useExplorerStore((store) => ({ - selectedRowIndex: store.selectedRowIndex, - showInspector: store.showInspector - })); - return (
diff --git a/packages/interface/src/components/explorer/ExplorerContextMenu.tsx b/packages/interface/src/components/explorer/ExplorerContextMenu.tsx index 942ede4c0..04c4578f5 100644 --- a/packages/interface/src/components/explorer/ExplorerContextMenu.tsx +++ b/packages/interface/src/components/explorer/ExplorerContextMenu.tsx @@ -1,4 +1,4 @@ -import { useExplorerStore, useLibraryMutation, useLibraryQuery } from '@sd/client'; +import { explorerStore, useLibraryMutation, useLibraryQuery } from '@sd/client'; import { ExplorerData } from '@sd/core'; import { ArrowBendUpRight, @@ -11,6 +11,7 @@ import { TrashSimple } from 'phosphor-react'; import React from 'react'; +import { useSnapshot } from 'valtio'; import { WithContextMenu } from '../layout/MenuOverlay'; @@ -19,7 +20,7 @@ interface Props { } export default function ExplorerContextMenu(props: Props) { - const contextMenuObjectId = useExplorerStore((store) => store.contextMenuObjectId); + const contextMenuObjectId = useSnapshot(explorerStore).contextMenuObjectId; const { data: tags } = useLibraryQuery(['tags.getAll'], {}); diff --git a/packages/interface/src/components/explorer/FileItem.tsx b/packages/interface/src/components/explorer/FileItem.tsx index 7ed656f28..496632392 100644 --- a/packages/interface/src/components/explorer/FileItem.tsx +++ b/packages/interface/src/components/explorer/FileItem.tsx @@ -1,7 +1,8 @@ -import { useExplorerStore } from '@sd/client'; +import { explorerStore } from '@sd/client'; import { ExplorerItem } from '@sd/core'; import clsx from 'clsx'; import React from 'react'; +import { useSnapshot } from 'valtio'; import FileThumb from './FileThumb'; import { isObject } from './utils'; @@ -13,17 +14,17 @@ interface Props extends React.HTMLAttributes { } function FileItem(props: Props) { - const set = useExplorerStore.getState().set; - - const size = useExplorerStore((state) => state.gridItemSize) || 100; + const { gridItemSize } = useSnapshot(explorerStore); return (
{ const objectId = isObject(props.data) ? props.data.id : props.data.file?.id; if (objectId != undefined) { - set({ contextMenuObjectId: objectId }); - if (props.index != undefined) set({ selectedRowIndex: props.index }); + explorerStore.contextMenuObjectId = objectId; + if (props.index != undefined) { + explorerStore.selectedRowIndex = props.index; + } } }} draggable @@ -31,7 +32,7 @@ function FileItem(props: Props) { className={clsx('inline-block w-[100px] mb-3', props.className)} >
diff --git a/packages/interface/src/components/explorer/FileRow.tsx b/packages/interface/src/components/explorer/FileRow.tsx index 8a4df7411..4276c6a51 100644 --- a/packages/interface/src/components/explorer/FileRow.tsx +++ b/packages/interface/src/components/explorer/FileRow.tsx @@ -1,5 +1,5 @@ import { EllipsisHorizontalIcon } from '@heroicons/react/24/solid'; -import { LocationContext, useBridgeQuery, useExplorerStore, useLibraryQuery } from '@sd/client'; +import { LocationContext, explorerStore, useBridgeQuery, useLibraryQuery } from '@sd/client'; import { ExplorerContext, ExplorerItem, FilePath } from '@sd/core'; import { useVirtualizer } from '@tanstack/react-virtual'; import clsx from 'clsx'; diff --git a/packages/interface/src/components/explorer/FileThumb.tsx b/packages/interface/src/components/explorer/FileThumb.tsx index 63487d301..833810f6f 100644 --- a/packages/interface/src/components/explorer/FileThumb.tsx +++ b/packages/interface/src/components/explorer/FileThumb.tsx @@ -1,7 +1,8 @@ -import { AppPropsContext, useExplorerStore } from '@sd/client'; +import { AppPropsContext, explorerStore } from '@sd/client'; import { ExplorerItem } from '@sd/core'; import clsx from 'clsx'; import React, { useContext } from 'react'; +import { useSnapshot } from 'valtio'; import icons from '../../assets/icons'; import { Folder } from '../icons/Folder'; @@ -16,7 +17,7 @@ interface Props { export default function FileThumb({ data, ...props }: Props) { const appProps = useContext(AppPropsContext); - const newThumbnails = useExplorerStore((store) => store.newThumbnails); + const { newThumbnails } = useSnapshot(explorerStore); if (isPath(data) && data.is_dir) return ; diff --git a/packages/interface/src/components/explorer/VirtualizedList.tsx b/packages/interface/src/components/explorer/VirtualizedList.tsx index 52acfea46..f472b8501 100644 --- a/packages/interface/src/components/explorer/VirtualizedList.tsx +++ b/packages/interface/src/components/explorer/VirtualizedList.tsx @@ -1,9 +1,10 @@ -import { ExplorerLayoutMode, useExplorerStore } from '@sd/client'; +import { ExplorerLayoutMode, explorerStore } from '@sd/client'; import { ExplorerContext, ExplorerItem, FilePath } from '@sd/core'; import { useVirtualizer } from '@tanstack/react-virtual'; import React, { memo, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; import { useKey, useWindowSize } from 'rooks'; +import { useSnapshot } from 'valtio'; import FileItem from './FileItem'; import FileRow from './FileRow'; @@ -23,16 +24,16 @@ export const VirtualizedList: React.FC = ({ data, context }) => { const [goingUp, setGoingUp] = useState(false); const [width, setWidth] = useState(0); - const { gridItemSize, layoutMode, listItemSize, selectedRowIndex } = useExplorerStore( - (state) => ({ - selectedRowIndex: state.selectedRowIndex, - gridItemSize: state.gridItemSize, - layoutMode: state.layoutMode, - listItemSize: state.listItemSize - }) - ); + // const { gridItemSize, layoutMode, listItemSize, selectedRowIndex } = useExplorerStore( + // (state) => ({ + // selectedRowIndex: state.selectedRowIndex, + // gridItemSize: state.gridItemSize, + // layoutMode: state.layoutMode, + // listItemSize: state.listItemSize + // }) + // ); - const set = useExplorerStore.getState().set; + const { gridItemSize, layoutMode, listItemSize, selectedRowIndex } = useSnapshot(explorerStore); useLayoutEffect(() => { setWidth(innerRef.current?.offsetWidth || 0); @@ -61,14 +62,14 @@ export const VirtualizedList: React.FC = ({ data, context }) => { e.preventDefault(); setGoingUp(true); if (selectedRowIndex !== -1 && selectedRowIndex !== 0) - set({ selectedRowIndex: selectedRowIndex - 1 }); + explorerStore.selectedRowIndex = selectedRowIndex - 1; }); useKey('ArrowDown', (e) => { e.preventDefault(); setGoingUp(false); if (selectedRowIndex !== -1 && selectedRowIndex !== (data.length ?? 1) - 1) - set({ selectedRowIndex: selectedRowIndex + 1 }); + explorerStore.selectedRowIndex = selectedRowIndex + 1; }); // const Header = () => ( @@ -164,7 +165,7 @@ const WrappedItem: React.FC = memo(({ item, index, isSelected, }, [item, setSearchParams]); const onClick = useCallback(() => { - useExplorerStore.getState().set({ selectedRowIndex: isSelected ? -1 : index }); + explorerStore.selectedRowIndex = isSelected ? -1 : index; }, [isSelected, index]); if (kind === 'list') { diff --git a/packages/interface/src/components/layout/TopBar.tsx b/packages/interface/src/components/layout/TopBar.tsx index b5804840d..18a2ae252 100644 --- a/packages/interface/src/components/layout/TopBar.tsx +++ b/packages/interface/src/components/layout/TopBar.tsx @@ -1,5 +1,5 @@ import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline'; -import { AppPropsContext, useExplorerStore, useLibraryMutation } from '@sd/client'; +import { AppPropsContext, explorerStore, useLibraryMutation } from '@sd/client'; import { Dropdown } from '@sd/ui'; import clsx from 'clsx'; import { @@ -13,6 +13,7 @@ import { } from 'phosphor-react'; import React, { DetailedHTMLProps, HTMLAttributes, RefAttributes, useContext } from 'react'; import { useNavigate } from 'react-router-dom'; +import { useSnapshot } from 'valtio'; import { Shortcut } from '../primitive/Shortcut'; import { DefaultProps } from '../primitive/types'; @@ -80,7 +81,7 @@ const SearchBar = React.forwardRef((props, ref) }); export const TopBar: React.FC = (props) => { - const { layoutMode, set, locationId, showInspector } = useExplorerStore(); + const { layoutMode, locationId, showInspector } = useSnapshot(explorerStore); const { mutate: generateThumbsForLocation } = useLibraryMutation( 'jobs.generateThumbsForLocation', { @@ -159,7 +160,7 @@ export const TopBar: React.FC = (props) => { left active={layoutMode === 'list'} icon={Rows} - onClick={() => set({ layoutMode: 'list' })} + onClick={() => (explorerStore.layoutMode = 'list')} /> @@ -168,7 +169,7 @@ export const TopBar: React.FC = (props) => { right active={layoutMode === 'grid'} icon={SquaresFour} - onClick={() => set({ layoutMode: 'grid' })} + onClick={() => (explorerStore.layoutMode = 'grid')} />
@@ -194,7 +195,7 @@ export const TopBar: React.FC = (props) => {
set({ showInspector: !showInspector })} + onClick={() => (explorerStore.showInspector = !showInspector)} className="my-2" icon={SidebarSimple} /> diff --git a/packages/interface/src/screens/LocationExplorer.tsx b/packages/interface/src/screens/LocationExplorer.tsx index 05f2033d8..bec544be2 100644 --- a/packages/interface/src/screens/LocationExplorer.tsx +++ b/packages/interface/src/screens/LocationExplorer.tsx @@ -1,5 +1,5 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { useExplorerStore, useLibraryQuery, useLibraryStore } from '@sd/client'; +import { explorerStore, useLibraryQuery, useLibraryStore } from '@sd/client'; import React, { useEffect } from 'react'; import { useParams, useSearchParams } from 'react-router-dom'; import z from 'zod'; @@ -20,10 +20,8 @@ export function useExplorerParams() { export const LocationExplorer: React.FC = () => { const { location_id, path } = useExplorerParams(); - // for top bar location context, could be replaced with react context as it is child component - const set = useExplorerStore((state) => state.set); useEffect(() => { - set({ locationId: location_id }); + explorerStore.locationId = location_id; }, [location_id]); const library_id = useLibraryStore((state) => state.currentLibraryUuid); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cad990b964c6834c3acc08f9231cf5dee6b523ea..18ff20608ad33be6b5d376515605246278c72efb 100644 GIT binary patch delta 1444 zcmeHG&rcIU6b`#kN{v5*pjfb?P_cHqJG=d%iArfJErHtdqqfzE%XZr??3Qh}-IiXA z@qmd3q8l?Y(WnQbCLA=7^rqq9$rB#b=tVu~iNrs^M5kb*XfS$rl6RQ*-Z%5j_r7^` zqvz9n&!ZDYi^__5Vfv`)97Q+?#$Z}qYe}oj=M+K7uNFxHXm`QRg(2hSI!6ukCk#a( z$oN`k?`k8$cP6S&!A`YT4-`*ic{%3ED!~WH!g! z09?A#&=cY4LEz!sf-~uqRnbd&oh9X@1$e@N(=HF~XJaK0N~W?d-p%;kBHi!yJ8X$a zOquDMh@G^N(E$aNh5o@@St{F;QaL0!+@u_a)GfSqR_oGNw5}%K^Y*GGYSXvMv{&h_W(5`YvJ*ITWUffLjuAMm%H%2F!L1Kwy9P?UV_^% zG_@Gk5{RDzE^)Ydtmsq3AT@D{64C_~L?@ZaKxwL<^`?|eaYT(LI5x%&IM|UQ8;p&S z@gd&A#|1g*D^HAO6O5;r87er1w6`zFD`Y%m_XI>Ln1%b=@OJpcXE>m}yR5%9wM%b4 ze#}Wwz^pBev>spE(B|)6Vil2>SV>|N)~e5}vRFt78LPx{IrPU+b5%^z%8GKDB`7mA zE@H+;ltz9uLpG$}*-|^H3SUJ4!i66A_zK<$$u~O0pcP&^gPGvDH@ZfR4C$)74&$FL zhRYx7_QL&(So3w<&;dV(u>+VY!1IKG-XZ6HGTBiRAZYlNz|HXfVZ6ERhrk50{u9Br lJGkw?;2TW(9|gZ?#rD8?zX52^m>t^3^w!GD&rNk_zX3x-(?9?K delta 130 zcmaF9M)Uei%?ZLXK$Wt=?USbFml*(&bI<*ag>*J&qB zy^So*y~gdm#*9GB1jNih%mT!$+k1`KPA>&2m7MOY%%r+~`b{=_=IIMKnfTiU@3L(d fyvx4AbNa>YU=Iw`cs~{2vVfv^+6>