zustand part 3 (#2878)

* zustand part 3

* clean up

* yarn lock
This commit is contained in:
Kurt Hutten
2024-07-03 09:22:46 +10:00
committed by GitHub
parent c5150468a2
commit 4c6e8633f7
11 changed files with 81 additions and 92 deletions

View File

@ -56,8 +56,7 @@
"vscode-languageserver-protocol": "^3.17.5", "vscode-languageserver-protocol": "^3.17.5",
"vscode-uri": "^3.0.8", "vscode-uri": "^3.0.8",
"web-vitals": "^3.5.2", "web-vitals": "^3.5.2",
"xstate": "^4.38.2", "xstate": "^4.38.2"
"zustand": "^4.5.2"
}, },
"scripts": { "scripts": {
"start": "vite", "start": "vite",

View File

@ -1,4 +1,4 @@
import { MouseEventHandler, useEffect, useRef } from 'react' import { MouseEventHandler, useEffect, useMemo, useRef } from 'react'
import { uuidv4 } from 'lib/utils' import { uuidv4 } from 'lib/utils'
import { useHotKeyListener } from './hooks/useHotKeyListener' import { useHotKeyListener } from './hooks/useHotKeyListener'
import { Stream } from './components/Stream' import { Stream } from './components/Stream'
@ -25,7 +25,7 @@ import ModalContainer from 'react-modal-promise'
import useHotkeyWrapper from 'lib/hotkeyWrapper' import useHotkeyWrapper from 'lib/hotkeyWrapper'
import Gizmo from 'components/Gizmo' import Gizmo from 'components/Gizmo'
import { CoreDumpManager } from 'lib/coredump' import { CoreDumpManager } from 'lib/coredump'
import { useStore } from 'useStore' import { useAppState } from 'AppState'
export function App() { export function App() {
useRefreshSettings(paths.FILE + 'SETTINGS') useRefreshSettings(paths.FILE + 'SETTINGS')
@ -45,18 +45,19 @@ export function App() {
useHotKeyListener() useHotKeyListener()
const { context } = useModelingContext() const { context } = useModelingContext()
const { setHtmlRef } = useStore((s) => ({ const { setAppState } = useAppState()
setHtmlRef: s.setHtmlRef,
}))
useEffect(() => { useEffect(() => {
setHtmlRef(ref) setAppState({ htmlRef: ref })
}, [ref]) }, [ref])
const { auth, settings } = useSettingsAuthContext() const { auth, settings } = useSettingsAuthContext()
const token = auth?.context?.token const token = auth?.context?.token
const coreDumpManager = new CoreDumpManager(engineCommandManager, ref, token) const coreDumpManager = useMemo(
() => new CoreDumpManager(engineCommandManager, ref, token),
[]
)
const { const {
app: { onboardingStatus }, app: { onboardingStatus },

45
src/AppState.tsx Normal file
View File

@ -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<HTMLDivElement> | null
setAppState: (newAppState: Partial<AppState>) => void
}
const AppStateContext = createContext<AppState>({
htmlRef: null,
isStreamReady: false,
setAppState: () => {},
})
export const useAppState = () => useContext(AppStateContext)
export const AppStateProvider = ({ children }: { children: ReactNode }) => {
const [appState, _setAppState] = useState<AppState>({
htmlRef: null,
isStreamReady: false,
setAppState: () => {},
})
const setAppState = (newAppState: Partial<AppState>) =>
_setAppState({ ...appState, ...newAppState })
return (
<AppStateContext.Provider
value={{
htmlRef: appState.htmlRef,
isStreamReady: appState.isStreamReady,
setAppState,
}}
>
{children}
</AppStateContext.Provider>
)
}

View File

@ -40,7 +40,7 @@ import useHotkeyWrapper from 'lib/hotkeyWrapper'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import { coreDump } from 'lang/wasm' import { coreDump } from 'lang/wasm'
import { useMemo } from 'react' import { useMemo } from 'react'
import { useStore } from 'useStore' import { AppStateProvider, useAppState } from 'AppState'
const router = createBrowserRouter([ const router = createBrowserRouter([
{ {
@ -53,7 +53,9 @@ const router = createBrowserRouter([
<SettingsAuthProvider> <SettingsAuthProvider>
<LspProvider> <LspProvider>
<KclContextProvider> <KclContextProvider>
<Outlet /> <AppStateProvider>
<Outlet />
</AppStateProvider>
</KclContextProvider> </KclContextProvider>
</LspProvider> </LspProvider>
</SettingsAuthProvider> </SettingsAuthProvider>
@ -178,9 +180,7 @@ export const Router = () => {
function CoreDump() { function CoreDump() {
const { auth } = useSettingsAuthContext() const { auth } = useSettingsAuthContext()
const token = auth?.context?.token const token = auth?.context?.token
const { htmlRef } = useStore((s) => ({ const { htmlRef } = useAppState()
htmlRef: s.htmlRef,
}))
const coreDumpManager = useMemo( const coreDumpManager = useMemo(
() => new CoreDumpManager(engineCommandManager, htmlRef, token), () => new CoreDumpManager(engineCommandManager, htmlRef, token),
[] []

View File

@ -11,7 +11,7 @@ import { useKclContext } from 'lang/KclProvider'
import { ActionButtonDropdown } from 'components/ActionButtonDropdown' import { ActionButtonDropdown } from 'components/ActionButtonDropdown'
import { useHotkeys } from 'react-hotkeys-hook' import { useHotkeys } from 'react-hotkeys-hook'
import Tooltip from 'components/Tooltip' import Tooltip from 'components/Tooltip'
import { useStore } from 'useStore' import { useAppState } from 'AppState'
export function Toolbar({ export function Toolbar({
className = '', className = '',
@ -38,9 +38,8 @@ export function Toolbar({
const toolbarButtonsRef = useRef<HTMLUListElement>(null) const toolbarButtonsRef = useRef<HTMLUListElement>(null)
const { overallState } = useNetworkContext() const { overallState } = useNetworkContext()
const { isExecuting } = useKclContext() const { isExecuting } = useKclContext()
const { isStreamReady } = useStore((s) => ({ const { isStreamReady } = useAppState()
isStreamReady: s.isStreamReady,
}))
const disableAllButtons = const disableAllButtons =
(overallState !== NetworkHealthState.Ok && (overallState !== NetworkHealthState.Ok &&
overallState !== NetworkHealthState.Weak) || overallState !== NetworkHealthState.Weak) ||

View File

@ -6,14 +6,12 @@ import React, { useMemo } from 'react'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import Tooltip from './Tooltip' import Tooltip from './Tooltip'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { useStore } from 'useStore' import { useAppState } from 'AppState'
export const RefreshButton = ({ children }: React.PropsWithChildren) => { export const RefreshButton = ({ children }: React.PropsWithChildren) => {
const { auth } = useSettingsAuthContext() const { auth } = useSettingsAuthContext()
const token = auth?.context?.token const token = auth?.context?.token
const { htmlRef } = useStore((s) => ({ const { htmlRef } = useAppState()
htmlRef: s.htmlRef,
}))
const coreDumpManager = useMemo( const coreDumpManager = useMemo(
() => new CoreDumpManager(engineCommandManager, htmlRef, token), () => new CoreDumpManager(engineCommandManager, htmlRef, token),
[] []

View File

@ -4,7 +4,7 @@ import { deferExecution } from 'lib/utils'
import { Themes } from 'lib/theme' import { Themes } from 'lib/theme'
import { makeDefaultPlanes, modifyGrid } from 'lang/wasm' import { makeDefaultPlanes, modifyGrid } from 'lang/wasm'
import { useModelingContext } from './useModelingContext' import { useModelingContext } from './useModelingContext'
import { useStore } from 'useStore' import { useAppState } from 'AppState'
export function useSetupEngineManager( export function useSetupEngineManager(
streamRef: React.RefObject<HTMLDivElement>, streamRef: React.RefObject<HTMLDivElement>,
@ -27,9 +27,7 @@ export function useSetupEngineManager(
showScaleGrid: boolean showScaleGrid: boolean
} }
) { ) {
const { setIsStreamReady } = useStore((s) => ({ const { setAppState } = useAppState()
setIsStreamReady: s.setIsStreamReady,
}))
const streamWidth = streamRef?.current?.offsetWidth const streamWidth = streamRef?.current?.offsetWidth
const streamHeight = streamRef?.current?.offsetHeight const streamHeight = streamRef?.current?.offsetHeight
@ -61,7 +59,7 @@ export function useSetupEngineManager(
type: 'Set context', type: 'Set context',
data: { mediaStream }, data: { mediaStream },
}), }),
setIsStreamReady, setIsStreamReady: (isStreamReady) => setAppState({ isStreamReady }),
width: quadWidth, width: quadWidth,
height: quadHeight, height: quadHeight,
executeCode: () => { executeCode: () => {

View File

@ -10,7 +10,7 @@ import { Command, CommandSetConfig, CommandSetSchema } from 'lib/commandTypes'
import { useKclContext } from 'lang/KclProvider' import { useKclContext } from 'lang/KclProvider'
import { useNetworkContext } from 'hooks/useNetworkContext' import { useNetworkContext } from 'hooks/useNetworkContext'
import { NetworkHealthState } from 'hooks/useNetworkStatus' import { NetworkHealthState } from 'hooks/useNetworkStatus'
import { useStore } from 'useStore' import { useAppState } from 'AppState'
// This might not be necessary, AnyStateMachine from xstate is working // This might not be necessary, AnyStateMachine from xstate is working
export type AllMachines = export type AllMachines =
@ -47,9 +47,7 @@ export default function useStateMachineCommands<
const { commandBarSend } = useCommandsContext() const { commandBarSend } = useCommandsContext()
const { overallState } = useNetworkContext() const { overallState } = useNetworkContext()
const { isExecuting } = useKclContext() const { isExecuting } = useKclContext()
const { isStreamReady } = useStore((s) => ({ const { isStreamReady } = useAppState()
isStreamReady: s.isStreamReady,
}))
useEffect(() => { useEffect(() => {
const disableAllButtons = const disableAllButtons =

View File

@ -1,4 +1,4 @@
import { FormEvent, useEffect } from 'react' import { FormEvent, useEffect, useRef } from 'react'
import { remove } from '@tauri-apps/plugin-fs' import { remove } from '@tauri-apps/plugin-fs'
import { import {
getNextProjectIndex, getNextProjectIndex,
@ -39,6 +39,7 @@ import {
listProjects, listProjects,
renameProjectDirectory, renameProjectDirectory,
} from 'lib/tauri' } from 'lib/tauri'
import { useAppState } from 'AppState'
// This route only opens in the Tauri desktop context for now, // 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. // as defined in Router.tsx, so we can use the Tauri APIs and types.
@ -64,6 +65,12 @@ const Home = () => {
splitKey: '|', splitKey: '|',
} }
) )
const ref = useRef<HTMLDivElement>(null)
const { setAppState } = useAppState()
useEffect(() => {
setAppState({ htmlRef: ref })
}, [ref])
const [state, send, actor] = useMachine(homeMachine, { const [state, send, actor] = useMachine(homeMachine, {
context: { context: {
@ -198,7 +205,7 @@ const Home = () => {
} }
return ( return (
<div className="relative flex flex-col h-screen overflow-hidden"> <div className="relative flex flex-col h-screen overflow-hidden" ref={ref}>
<AppHeader showToolbar={false} /> <AppHeader showToolbar={false} />
<div className="w-full flex flex-col overflow-hidden max-w-5xl px-4 mx-auto mt-24 lg:px-2"> <div className="w-full flex flex-col overflow-hidden max-w-5xl px-4 mx-auto mt-24 lg:px-2">
<section> <section>

View File

@ -1,19 +0,0 @@
import { create } from 'zustand'
export interface StoreState {
isStreamReady: boolean
setIsStreamReady: (isStreamReady: boolean) => void
setHtmlRef: (ref: React.RefObject<HTMLDivElement>) => void
htmlRef: React.RefObject<HTMLDivElement> | null
}
export const useStore = create<StoreState>()((set, get) => {
return {
setIsStreamReady: (isStreamReady) => set({ isStreamReady }),
setHtmlRef: (htmlRef) => {
set({ htmlRef })
},
htmlRef: null,
isStreamReady: false,
}
})

View File

@ -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" resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
"string-width-cjs@npm:string-width@^4.2.0": "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==
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:
version "4.2.3" version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -7652,14 +7643,7 @@ string_decoder@~1.1.1:
dependencies: dependencies:
safe-buffer "~5.1.0" safe-buffer "~5.1.0"
"strip-ansi-cjs@npm: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==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1" version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@ -8159,11 +8143,6 @@ use-latest@^1.2.1:
dependencies: dependencies:
use-isomorphic-layout-effect "^1.1.1" 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: use-sync-external-store@^1.0.0:
version "1.2.2" version "1.2.2"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" 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" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== 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" version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@ -8550,15 +8529,6 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0" string-width "^4.1.0"
strip-ansi "^6.0.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: wrap-ansi@^8.1.0:
version "8.1.0" version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" 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" archiver-utils "^5.0.0"
compress-commons "^6.0.2" compress-commons "^6.0.2"
readable-stream "^4.0.0" 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"