// Copyright 2024, Command Line Inc. // SPDX-License-Identifier: Apache-2.0 import { WOS } from "@/app/store/global.js"; import { Atom, Getter, PrimitiveAtom, WritableAtom, atom, useAtom } from "jotai"; import { useCallback } from "react"; import { layoutTreeStateReducer, newLayoutTreeState } from "./layoutState.js"; import { LayoutNode, LayoutNodeWaveObj, LayoutTreeAction, LayoutTreeState, WritableLayoutNodeAtom, WritableLayoutTreeStateAtom, } from "./model.js"; /** * Creates a new layout tree state wrapped as an atom. * @param rootNode The root node for the tree. * @returns The state wrapped as an atom. * * @template T The type of data associated with the nodes of the tree. */ export function newLayoutTreeStateAtom(rootNode: LayoutNode): PrimitiveAtom> { return atom(newLayoutTreeState(rootNode)) as PrimitiveAtom>; } /** * Derives a WritableLayoutTreeStateAtom from a WritableLayoutNodeAtom, initializing the tree state. * @param layoutNodeAtom The atom containing the root node for the LayoutTreeState. * @returns The derived WritableLayoutTreeStateAtom. */ export function withLayoutTreeState(layoutNodeAtom: WritableLayoutNodeAtom): WritableLayoutTreeStateAtom { const pendingActionAtom = atom(null) as PrimitiveAtom; const generationAtom = atom(0) as PrimitiveAtom; return atom( (get) => { const layoutState = newLayoutTreeState(get(layoutNodeAtom)); layoutState.pendingAction = get(pendingActionAtom); layoutState.generation = get(generationAtom); return layoutState; }, (get, set, value) => { set(pendingActionAtom, value.pendingAction); if (get(generationAtom) !== value.generation) { set(generationAtom, value.generation); set(layoutNodeAtom, value.rootNode); } } ); } /** * Hook to subscribe to the tree state and dispatch actions to its reducer functon. * @param layoutTreeStateAtom The atom holding the layout tree state. * @returns The current state of the tree and the dispatch function. */ export function useLayoutTreeStateReducerAtom( layoutTreeStateAtom: WritableLayoutTreeStateAtom ): readonly [LayoutTreeState, (action: LayoutTreeAction) => void] { const [state, setState] = useAtom(layoutTreeStateAtom); const dispatch = useCallback( (action: LayoutTreeAction) => setState(layoutTreeStateReducer(state, action)), [state, setState] ); return [state, dispatch]; } const tabLayoutAtomCache = new Map>(); function getLayoutNodeWaveObjAtomFromTab( tabAtom: Atom, get: Getter ): WritableAtom, [value: LayoutNodeWaveObj], void> { const tabValue = get(tabAtom); console.log("getLayoutNodeWaveObjAtomFromTab tabValue", tabValue); if (!tabValue) return; const layoutNodeOref = WOS.makeORef("layout", tabValue.layoutNode); console.log("getLayoutNodeWaveObjAtomFromTab oref", layoutNodeOref); return WOS.getWaveObjectAtom>(layoutNodeOref); } export function withLayoutNodeAtomFromTab(tabAtom: Atom): WritableLayoutNodeAtom { return atom( (get) => { console.log("get withLayoutNodeAtomFromTab", tabAtom); const atom = getLayoutNodeWaveObjAtomFromTab(tabAtom, get); if (!atom) return null; const retVal = get(atom)?.node; console.log("get withLayoutNodeAtomFromTab end", retVal); return get(atom)?.node; }, (get, set, value) => { console.log("set withLayoutNodeAtomFromTab", value); const waveObjAtom = getLayoutNodeWaveObjAtomFromTab(tabAtom, get); if (!waveObjAtom) return; const newWaveObjAtom = { ...get(waveObjAtom), node: value }; set(waveObjAtom, newWaveObjAtom); } ); } export function getLayoutStateAtomForTab( tabId: string, tabAtom: WritableAtom ): WritableLayoutTreeStateAtom { let atom = tabLayoutAtomCache.get(tabId); if (atom) { console.log("Reusing atom for tab", tabId); return atom; } console.log("Creating new atom for tab", tabId); atom = withLayoutTreeState(withLayoutNodeAtomFromTab(tabAtom)); tabLayoutAtomCache.set(tabId, atom); return atom; } export function deleteLayoutStateAtomForTab(tabId: string) { const atom = tabLayoutAtomCache.get(tabId); if (atom) { tabLayoutAtomCache.delete(tabId); } }