mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
Break layout node into its own Wave Object (#21)
I am updating the layout node setup to write to its own wave object. The existing setup requires me to plumb the layout updates through every time the tab gets updated, which produces a lot of annoying and unintuitive design patterns. With this new setup, the tab object doesn't get written to when the layout changes, only the layout object will get written to. This prevents collisions when both the tab object and the layout node object are getting updated, such as when a new block is added or deleted.
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
|
||||
// WaveObjectStore
|
||||
|
||||
import { LayoutNode } from "@/faraday/index";
|
||||
import { Call as $Call, Events } from "@wailsio/runtime";
|
||||
import * as jotai from "jotai";
|
||||
import * as React from "react";
|
||||
@@ -297,7 +296,7 @@ function getObjectValue<T>(oref: string, getFn?: jotai.Getter): T {
|
||||
// sets the value of a WaveObject in the cache.
|
||||
// should provide setFn if it is available (e.g. inside of a jotai atom)
|
||||
// otherwise it will use the globalStore.set function
|
||||
function setObjectValue<T>(value: WaveObj, setFn?: jotai.Setter, pushToServer?: boolean) {
|
||||
function setObjectValue<T extends WaveObj>(value: T, setFn?: jotai.Setter, pushToServer?: boolean) {
|
||||
const oref = makeORef(value.otype, value.oid);
|
||||
let wov = waveObjectValueCache.get(oref);
|
||||
if (wov == null) {
|
||||
@@ -309,7 +308,9 @@ function setObjectValue<T>(value: WaveObj, setFn?: jotai.Setter, pushToServer?:
|
||||
}
|
||||
console.log("Setting", oref, "to", value);
|
||||
setFn(wov.dataAtom, { value: value, loading: false });
|
||||
console.log("Setting", oref, "to", value, "done");
|
||||
if (pushToServer) {
|
||||
console.log("pushToServer", oref, value);
|
||||
UpdateObject(value, false);
|
||||
}
|
||||
}
|
||||
@@ -326,8 +327,8 @@ export function CreateBlock(blockDef: BlockDef, rtOpts: RuntimeOpts): Promise<{
|
||||
return wrapObjectServiceCall("CreateBlock", blockDef, rtOpts);
|
||||
}
|
||||
|
||||
export function DeleteBlock(blockId: string, newLayout?: LayoutNode<any>): Promise<void> {
|
||||
return wrapObjectServiceCall("DeleteBlock", blockId, newLayout);
|
||||
export function DeleteBlock(blockId: string): Promise<void> {
|
||||
return wrapObjectServiceCall("DeleteBlock", blockId);
|
||||
}
|
||||
|
||||
export function CloseTab(tabId: string): Promise<void> {
|
||||
@@ -339,9 +340,9 @@ export function UpdateObjectMeta(blockId: string, meta: MetadataType): Promise<v
|
||||
}
|
||||
|
||||
export function UpdateObject(waveObj: WaveObj, returnUpdates: boolean): Promise<WaveObjUpdate[]> {
|
||||
console.log("UpdateObject", waveObj, returnUpdates);
|
||||
return wrapObjectServiceCall("UpdateObject", waveObj, returnUpdates);
|
||||
}
|
||||
|
||||
export {
|
||||
cleanWaveObjectCache,
|
||||
clearWaveObjectCache,
|
||||
|
||||
@@ -27,13 +27,10 @@ const TabContent = ({ tabId }: { tabId: string }) => {
|
||||
return <Block blockId={tabData.blockId} onClose={onClose} />;
|
||||
}, []);
|
||||
|
||||
const onNodeDelete = useCallback(
|
||||
(data: TabLayoutData) => {
|
||||
console.log("onNodeDelete", data, tabData);
|
||||
WOS.DeleteBlock(data.blockId, tabData.layout);
|
||||
},
|
||||
[tabData]
|
||||
);
|
||||
const onNodeDelete = useCallback((data: TabLayoutData) => {
|
||||
console.log("onNodeDelete", data);
|
||||
return WOS.DeleteBlock(data.blockId);
|
||||
}, []);
|
||||
|
||||
if (tabLoading) {
|
||||
return <CenteredLoadingDiv />;
|
||||
|
||||
@@ -77,7 +77,7 @@ function Widgets() {
|
||||
};
|
||||
dispatchLayoutStateAction(insertNodeAction);
|
||||
},
|
||||
[activeTabAtom]
|
||||
[activeTabAtom, dispatchLayoutStateAction]
|
||||
);
|
||||
|
||||
async function createBlock(blockDef: BlockDef) {
|
||||
|
||||
@@ -22,7 +22,7 @@ import { setTransform as createTransform, debounce, determineDropDirection } fro
|
||||
export interface TileLayoutProps<T> {
|
||||
layoutTreeStateAtom: WritableLayoutTreeStateAtom<T>;
|
||||
renderContent: ContentRenderer<T>;
|
||||
onNodeDelete?: (data: T) => void;
|
||||
onNodeDelete?: (data: T) => Promise<void>;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ export const TileLayout = <T,>({ layoutTreeStateAtom, className, renderContent,
|
||||
);
|
||||
}
|
||||
}, 30),
|
||||
[activeDrag, overlayContainerRef, displayContainerRef, layoutTreeState, nodeRefs]
|
||||
[activeDrag, overlayContainerRef, displayContainerRef, layoutTreeState.leafs, nodeRefs]
|
||||
);
|
||||
|
||||
// Update the transforms whenever we drag something and whenever the layout updates.
|
||||
@@ -133,6 +133,7 @@ export const TileLayout = <T,>({ layoutTreeStateAtom, className, renderContent,
|
||||
// reattach the new callback.
|
||||
const [prevUpdateTransforms, setPrevUpdateTransforms] = useState<() => void>(undefined);
|
||||
useEffect(() => {
|
||||
console.log("replace resize listener");
|
||||
if (prevUpdateTransforms) window.removeEventListener("resize", prevUpdateTransforms);
|
||||
window.addEventListener("resize", updateTransforms);
|
||||
setPrevUpdateTransforms(updateTransforms);
|
||||
@@ -156,7 +157,7 @@ export const TileLayout = <T,>({ layoutTreeStateAtom, className, renderContent,
|
||||
}, []);
|
||||
|
||||
const onLeafClose = useCallback(
|
||||
(node: LayoutNode<T>) => {
|
||||
async (node: LayoutNode<T>) => {
|
||||
console.log("onLeafClose", node);
|
||||
const deleteAction: LayoutTreeDeleteNodeAction = {
|
||||
type: LayoutTreeActionType.DeleteNode,
|
||||
@@ -165,7 +166,7 @@ export const TileLayout = <T,>({ layoutTreeStateAtom, className, renderContent,
|
||||
console.log("calling dispatch", deleteAction);
|
||||
dispatch(deleteAction);
|
||||
console.log("calling onNodeDelete", node);
|
||||
onNodeDelete?.(node.data);
|
||||
await onNodeDelete?.(node.data);
|
||||
console.log("node deleted");
|
||||
},
|
||||
[onNodeDelete, dispatch]
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { PrimitiveAtom, WritableAtom, atom, useAtom } from "jotai";
|
||||
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,
|
||||
@@ -29,27 +31,16 @@ export function newLayoutTreeStateAtom<T>(rootNode: LayoutNode<T>): PrimitiveAto
|
||||
* @returns The derived WritableLayoutTreeStateAtom.
|
||||
*/
|
||||
export function withLayoutTreeState<T>(layoutNodeAtom: WritableLayoutNodeAtom<T>): WritableLayoutTreeStateAtom<T> {
|
||||
return atom(
|
||||
(get) => newLayoutTreeState(get(layoutNodeAtom)),
|
||||
(_get, set, value) => set(layoutNodeAtom, value.rootNode)
|
||||
);
|
||||
}
|
||||
|
||||
export function withLayoutStateFromTab(
|
||||
tabAtom: WritableAtom<Tab, [value: Tab], void>
|
||||
): WritableLayoutTreeStateAtom<TabLayoutData> {
|
||||
const pendingActionAtom = atom<LayoutTreeAction>(null) as PrimitiveAtom<LayoutTreeAction>;
|
||||
return atom(
|
||||
(get) => {
|
||||
const tabData = get(tabAtom);
|
||||
console.log("get layout state from tab", tabData);
|
||||
return newLayoutTreeState(tabData?.layout);
|
||||
const layoutState = newLayoutTreeState(get(layoutNodeAtom));
|
||||
layoutState.pendingAction = get(pendingActionAtom);
|
||||
return layoutState;
|
||||
},
|
||||
(get, set, value) => {
|
||||
const tabValue = get(tabAtom);
|
||||
const newTabValue = { ...tabValue };
|
||||
newTabValue.layout = value.rootNode;
|
||||
console.log("set tab", tabValue, value);
|
||||
set(tabAtom, newTabValue);
|
||||
(_get, set, value) => {
|
||||
set(pendingActionAtom, value.pendingAction);
|
||||
set(layoutNodeAtom, value.rootNode);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -72,6 +63,38 @@ export function useLayoutTreeStateReducerAtom<T>(
|
||||
|
||||
const tabLayoutAtomCache = new Map<string, WritableLayoutTreeStateAtom<TabLayoutData>>();
|
||||
|
||||
function getLayoutNodeWaveObjAtomFromTab<T>(
|
||||
tabAtom: Atom<Tab>,
|
||||
get: Getter
|
||||
): WritableAtom<LayoutNodeWaveObj<T>, [value: LayoutNodeWaveObj<T>], 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<LayoutNodeWaveObj<T>>(layoutNodeOref);
|
||||
}
|
||||
|
||||
export function withLayoutNodeAtomFromTab<T>(tabAtom: Atom<Tab>): WritableLayoutNodeAtom<T> {
|
||||
return atom(
|
||||
(get) => {
|
||||
console.log("get withLayoutNodeAtomFromTab", tabAtom);
|
||||
const atom = getLayoutNodeWaveObjAtomFromTab<T>(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<T>(tabAtom, get);
|
||||
if (!waveObjAtom) return;
|
||||
const newWaveObjAtom = { ...get(waveObjAtom), node: value };
|
||||
set(waveObjAtom, newWaveObjAtom);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getLayoutStateAtomForTab(
|
||||
tabId: string,
|
||||
tabAtom: WritableAtom<Tab, [value: Tab], void>
|
||||
@@ -82,7 +105,7 @@ export function getLayoutStateAtomForTab(
|
||||
return atom;
|
||||
}
|
||||
console.log("Creating new atom for tab", tabId);
|
||||
atom = withLayoutStateFromTab(tabAtom);
|
||||
atom = withLayoutTreeState(withLayoutNodeAtomFromTab<TabLayoutData>(tabAtom));
|
||||
tabLayoutAtomCache.set(tabId, atom);
|
||||
return atom;
|
||||
}
|
||||
|
||||
@@ -131,3 +131,7 @@ export type WritableLayoutNodeAtom<T> = WritableAtom<LayoutNode<T>, [value: Layo
|
||||
export type WritableLayoutTreeStateAtom<T> = WritableAtom<LayoutTreeState<T>, [value: LayoutTreeState<T>], void>;
|
||||
|
||||
export type ContentRenderer<T> = (data: T, ready: boolean, onClose?: () => void) => React.ReactNode;
|
||||
|
||||
export interface LayoutNodeWaveObj<T> extends WaveObj {
|
||||
node: LayoutNode<T>;
|
||||
}
|
||||
|
||||
Vendored
+1
-3
@@ -1,8 +1,6 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { LayoutNode } from "../faraday";
|
||||
|
||||
declare global {
|
||||
type UIContext = {
|
||||
windowid: string;
|
||||
@@ -70,7 +68,7 @@ declare global {
|
||||
version: number;
|
||||
name: string;
|
||||
blockids: string[];
|
||||
layout: LayoutNode<TabLayoutData>;
|
||||
layoutNode: string;
|
||||
};
|
||||
|
||||
type Point = {
|
||||
|
||||
Reference in New Issue
Block a user