diff --git a/package.json b/package.json index 803081aad..96e109783 100644 --- a/package.json +++ b/package.json @@ -56,8 +56,7 @@ "vscode-languageserver-protocol": "^3.17.5", "vscode-uri": "^3.0.8", "web-vitals": "^3.5.2", - "xstate": "^4.38.2", - "zustand": "^4.5.2" + "xstate": "^4.38.2" }, "scripts": { "start": "vite", diff --git a/src/App.tsx b/src/App.tsx index a67d95dbb..b43bc898a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, useEffect, useRef } from 'react' +import { MouseEventHandler, useEffect, useMemo, useRef } from 'react' import { uuidv4 } from 'lib/utils' import { useHotKeyListener } from './hooks/useHotKeyListener' import { Stream } from './components/Stream' @@ -25,7 +25,7 @@ import ModalContainer from 'react-modal-promise' import useHotkeyWrapper from 'lib/hotkeyWrapper' import Gizmo from 'components/Gizmo' import { CoreDumpManager } from 'lib/coredump' -import { useStore } from 'useStore' +import { useAppState } from 'AppState' export function App() { useRefreshSettings(paths.FILE + 'SETTINGS') @@ -45,18 +45,19 @@ export function App() { useHotKeyListener() const { context } = useModelingContext() - const { setHtmlRef } = useStore((s) => ({ - setHtmlRef: s.setHtmlRef, - })) + const { setAppState } = useAppState() useEffect(() => { - setHtmlRef(ref) + setAppState({ htmlRef: ref }) }, [ref]) const { auth, settings } = useSettingsAuthContext() const token = auth?.context?.token - const coreDumpManager = new CoreDumpManager(engineCommandManager, ref, token) + const coreDumpManager = useMemo( + () => new CoreDumpManager(engineCommandManager, ref, token), + [] + ) const { app: { onboardingStatus }, diff --git a/src/AppState.tsx b/src/AppState.tsx new file mode 100644 index 000000000..580e5936c --- /dev/null +++ b/src/AppState.tsx @@ -0,0 +1,45 @@ +import { createContext, useContext, useState, ReactNode } from 'react' + +/* + +This is for a very small handful of global state we need that doesn't fit into +any of the Xstate machines. +Please do not fill this up with junk. + +*/ + +interface AppState { + isStreamReady: boolean + htmlRef: React.RefObject | null + setAppState: (newAppState: Partial) => void +} + +const AppStateContext = createContext({ + htmlRef: null, + isStreamReady: false, + setAppState: () => {}, +}) + +export const useAppState = () => useContext(AppStateContext) + +export const AppStateProvider = ({ children }: { children: ReactNode }) => { + const [appState, _setAppState] = useState({ + htmlRef: null, + isStreamReady: false, + setAppState: () => {}, + }) + const setAppState = (newAppState: Partial) => + _setAppState({ ...appState, ...newAppState }) + + return ( + + {children} + + ) +} diff --git a/src/Router.tsx b/src/Router.tsx index 201d8dd28..1c21314aa 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -40,7 +40,7 @@ import useHotkeyWrapper from 'lib/hotkeyWrapper' import toast from 'react-hot-toast' import { coreDump } from 'lang/wasm' import { useMemo } from 'react' -import { useStore } from 'useStore' +import { AppStateProvider, useAppState } from 'AppState' const router = createBrowserRouter([ { @@ -53,7 +53,9 @@ const router = createBrowserRouter([ - + + + @@ -178,9 +180,7 @@ export const Router = () => { function CoreDump() { const { auth } = useSettingsAuthContext() const token = auth?.context?.token - const { htmlRef } = useStore((s) => ({ - htmlRef: s.htmlRef, - })) + const { htmlRef } = useAppState() const coreDumpManager = useMemo( () => new CoreDumpManager(engineCommandManager, htmlRef, token), [] diff --git a/src/Toolbar.tsx b/src/Toolbar.tsx index 703f3e8ee..37694c427 100644 --- a/src/Toolbar.tsx +++ b/src/Toolbar.tsx @@ -11,7 +11,7 @@ import { useKclContext } from 'lang/KclProvider' import { ActionButtonDropdown } from 'components/ActionButtonDropdown' import { useHotkeys } from 'react-hotkeys-hook' import Tooltip from 'components/Tooltip' -import { useStore } from 'useStore' +import { useAppState } from 'AppState' export function Toolbar({ className = '', @@ -38,9 +38,8 @@ export function Toolbar({ const toolbarButtonsRef = useRef(null) const { overallState } = useNetworkContext() const { isExecuting } = useKclContext() - const { isStreamReady } = useStore((s) => ({ - isStreamReady: s.isStreamReady, - })) + const { isStreamReady } = useAppState() + const disableAllButtons = (overallState !== NetworkHealthState.Ok && overallState !== NetworkHealthState.Weak) || diff --git a/src/components/RefreshButton.tsx b/src/components/RefreshButton.tsx index 631faeaf0..f70f17db6 100644 --- a/src/components/RefreshButton.tsx +++ b/src/components/RefreshButton.tsx @@ -6,14 +6,12 @@ import React, { useMemo } from 'react' import toast from 'react-hot-toast' import Tooltip from './Tooltip' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' -import { useStore } from 'useStore' +import { useAppState } from 'AppState' export const RefreshButton = ({ children }: React.PropsWithChildren) => { const { auth } = useSettingsAuthContext() const token = auth?.context?.token - const { htmlRef } = useStore((s) => ({ - htmlRef: s.htmlRef, - })) + const { htmlRef } = useAppState() const coreDumpManager = useMemo( () => new CoreDumpManager(engineCommandManager, htmlRef, token), [] diff --git a/src/hooks/useSetupEngineManager.ts b/src/hooks/useSetupEngineManager.ts index a14a5c621..34dd43fb3 100644 --- a/src/hooks/useSetupEngineManager.ts +++ b/src/hooks/useSetupEngineManager.ts @@ -4,7 +4,7 @@ import { deferExecution } from 'lib/utils' import { Themes } from 'lib/theme' import { makeDefaultPlanes, modifyGrid } from 'lang/wasm' import { useModelingContext } from './useModelingContext' -import { useStore } from 'useStore' +import { useAppState } from 'AppState' export function useSetupEngineManager( streamRef: React.RefObject, @@ -27,9 +27,7 @@ export function useSetupEngineManager( showScaleGrid: boolean } ) { - const { setIsStreamReady } = useStore((s) => ({ - setIsStreamReady: s.setIsStreamReady, - })) + const { setAppState } = useAppState() const streamWidth = streamRef?.current?.offsetWidth const streamHeight = streamRef?.current?.offsetHeight @@ -61,7 +59,7 @@ export function useSetupEngineManager( type: 'Set context', data: { mediaStream }, }), - setIsStreamReady, + setIsStreamReady: (isStreamReady) => setAppState({ isStreamReady }), width: quadWidth, height: quadHeight, executeCode: () => { diff --git a/src/hooks/useStateMachineCommands.ts b/src/hooks/useStateMachineCommands.ts index cc8c62261..83f53fa15 100644 --- a/src/hooks/useStateMachineCommands.ts +++ b/src/hooks/useStateMachineCommands.ts @@ -10,7 +10,7 @@ import { Command, CommandSetConfig, CommandSetSchema } from 'lib/commandTypes' import { useKclContext } from 'lang/KclProvider' import { useNetworkContext } from 'hooks/useNetworkContext' import { NetworkHealthState } from 'hooks/useNetworkStatus' -import { useStore } from 'useStore' +import { useAppState } from 'AppState' // This might not be necessary, AnyStateMachine from xstate is working export type AllMachines = @@ -47,9 +47,7 @@ export default function useStateMachineCommands< const { commandBarSend } = useCommandsContext() const { overallState } = useNetworkContext() const { isExecuting } = useKclContext() - const { isStreamReady } = useStore((s) => ({ - isStreamReady: s.isStreamReady, - })) + const { isStreamReady } = useAppState() useEffect(() => { const disableAllButtons = diff --git a/src/routes/Home.tsx b/src/routes/Home.tsx index b636f754d..90b9df82d 100644 --- a/src/routes/Home.tsx +++ b/src/routes/Home.tsx @@ -1,4 +1,4 @@ -import { FormEvent, useEffect } from 'react' +import { FormEvent, useEffect, useRef } from 'react' import { remove } from '@tauri-apps/plugin-fs' import { getNextProjectIndex, @@ -39,6 +39,7 @@ import { listProjects, renameProjectDirectory, } from 'lib/tauri' +import { useAppState } from 'AppState' // This route only opens in the Tauri desktop context for now, // as defined in Router.tsx, so we can use the Tauri APIs and types. @@ -64,6 +65,12 @@ const Home = () => { splitKey: '|', } ) + const ref = useRef(null) + const { setAppState } = useAppState() + + useEffect(() => { + setAppState({ htmlRef: ref }) + }, [ref]) const [state, send, actor] = useMachine(homeMachine, { context: { @@ -198,7 +205,7 @@ const Home = () => { } return ( -
+
diff --git a/src/useStore.ts b/src/useStore.ts deleted file mode 100644 index b730fcf67..000000000 --- a/src/useStore.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { create } from 'zustand' - -export interface StoreState { - isStreamReady: boolean - setIsStreamReady: (isStreamReady: boolean) => void - setHtmlRef: (ref: React.RefObject) => void - htmlRef: React.RefObject | null -} - -export const useStore = create()((set, get) => { - return { - setIsStreamReady: (isStreamReady) => set({ isStreamReady }), - setHtmlRef: (htmlRef) => { - set({ htmlRef }) - }, - htmlRef: null, - isStreamReady: false, - } -}) diff --git a/yarn.lock b/yarn.lock index 1921b786a..ac1a51a9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7565,16 +7565,7 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -7652,14 +7643,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -8159,11 +8143,6 @@ use-latest@^1.2.1: dependencies: use-isomorphic-layout-effect "^1.1.1" -use-sync-external-store@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" - integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== - use-sync-external-store@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" @@ -8532,7 +8511,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -8550,15 +8529,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -8718,10 +8688,3 @@ zip-stream@^6.0.1: archiver-utils "^5.0.0" compress-commons "^6.0.2" readable-stream "^4.0.0" - -zustand@^4.5.2: - version "4.5.2" - resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.2.tgz#fddbe7cac1e71d45413b3682cdb47b48034c3848" - integrity sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g== - dependencies: - use-sync-external-store "1.2.0"