mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01: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:
parent
28cef5f22f
commit
f12e246c15
@ -5,4 +5,3 @@ DROP TABLE db_workspace;
|
|||||||
DROP TABLE db_tab;
|
DROP TABLE db_tab;
|
||||||
|
|
||||||
DROP TABLE db_block;
|
DROP TABLE db_block;
|
||||||
|
|
||||||
|
@ -27,3 +27,4 @@ CREATE TABLE db_block (
|
|||||||
version int NOT NULL,
|
version int NOT NULL,
|
||||||
data json NOT NULL
|
data json NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
1
db/migrations-wstore/000002_init.down.sql
Normal file
1
db/migrations-wstore/000002_init.down.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
DROP TABLE db_layout;
|
5
db/migrations-wstore/000002_init.up.sql
Normal file
5
db/migrations-wstore/000002_init.up.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
CREATE TABLE db_layout (
|
||||||
|
oid varchar(36) PRIMARY KEY,
|
||||||
|
version int NOT NULL,
|
||||||
|
data json NOT NULL
|
||||||
|
);
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
// WaveObjectStore
|
// WaveObjectStore
|
||||||
|
|
||||||
import { LayoutNode } from "@/faraday/index";
|
|
||||||
import { Call as $Call, Events } from "@wailsio/runtime";
|
import { Call as $Call, Events } from "@wailsio/runtime";
|
||||||
import * as jotai from "jotai";
|
import * as jotai from "jotai";
|
||||||
import * as React from "react";
|
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.
|
// sets the value of a WaveObject in the cache.
|
||||||
// should provide setFn if it is available (e.g. inside of a jotai atom)
|
// should provide setFn if it is available (e.g. inside of a jotai atom)
|
||||||
// otherwise it will use the globalStore.set function
|
// 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);
|
const oref = makeORef(value.otype, value.oid);
|
||||||
let wov = waveObjectValueCache.get(oref);
|
let wov = waveObjectValueCache.get(oref);
|
||||||
if (wov == null) {
|
if (wov == null) {
|
||||||
@ -309,7 +308,9 @@ function setObjectValue<T>(value: WaveObj, setFn?: jotai.Setter, pushToServer?:
|
|||||||
}
|
}
|
||||||
console.log("Setting", oref, "to", value);
|
console.log("Setting", oref, "to", value);
|
||||||
setFn(wov.dataAtom, { value: value, loading: false });
|
setFn(wov.dataAtom, { value: value, loading: false });
|
||||||
|
console.log("Setting", oref, "to", value, "done");
|
||||||
if (pushToServer) {
|
if (pushToServer) {
|
||||||
|
console.log("pushToServer", oref, value);
|
||||||
UpdateObject(value, false);
|
UpdateObject(value, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -326,8 +327,8 @@ export function CreateBlock(blockDef: BlockDef, rtOpts: RuntimeOpts): Promise<{
|
|||||||
return wrapObjectServiceCall("CreateBlock", blockDef, rtOpts);
|
return wrapObjectServiceCall("CreateBlock", blockDef, rtOpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DeleteBlock(blockId: string, newLayout?: LayoutNode<any>): Promise<void> {
|
export function DeleteBlock(blockId: string): Promise<void> {
|
||||||
return wrapObjectServiceCall("DeleteBlock", blockId, newLayout);
|
return wrapObjectServiceCall("DeleteBlock", blockId);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CloseTab(tabId: string): Promise<void> {
|
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[]> {
|
export function UpdateObject(waveObj: WaveObj, returnUpdates: boolean): Promise<WaveObjUpdate[]> {
|
||||||
|
console.log("UpdateObject", waveObj, returnUpdates);
|
||||||
return wrapObjectServiceCall("UpdateObject", waveObj, returnUpdates);
|
return wrapObjectServiceCall("UpdateObject", waveObj, returnUpdates);
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
cleanWaveObjectCache,
|
cleanWaveObjectCache,
|
||||||
clearWaveObjectCache,
|
clearWaveObjectCache,
|
||||||
|
@ -27,13 +27,10 @@ const TabContent = ({ tabId }: { tabId: string }) => {
|
|||||||
return <Block blockId={tabData.blockId} onClose={onClose} />;
|
return <Block blockId={tabData.blockId} onClose={onClose} />;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onNodeDelete = useCallback(
|
const onNodeDelete = useCallback((data: TabLayoutData) => {
|
||||||
(data: TabLayoutData) => {
|
console.log("onNodeDelete", data);
|
||||||
console.log("onNodeDelete", data, tabData);
|
return WOS.DeleteBlock(data.blockId);
|
||||||
WOS.DeleteBlock(data.blockId, tabData.layout);
|
}, []);
|
||||||
},
|
|
||||||
[tabData]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tabLoading) {
|
if (tabLoading) {
|
||||||
return <CenteredLoadingDiv />;
|
return <CenteredLoadingDiv />;
|
||||||
|
@ -77,7 +77,7 @@ function Widgets() {
|
|||||||
};
|
};
|
||||||
dispatchLayoutStateAction(insertNodeAction);
|
dispatchLayoutStateAction(insertNodeAction);
|
||||||
},
|
},
|
||||||
[activeTabAtom]
|
[activeTabAtom, dispatchLayoutStateAction]
|
||||||
);
|
);
|
||||||
|
|
||||||
async function createBlock(blockDef: BlockDef) {
|
async function createBlock(blockDef: BlockDef) {
|
||||||
|
@ -22,7 +22,7 @@ import { setTransform as createTransform, debounce, determineDropDirection } fro
|
|||||||
export interface TileLayoutProps<T> {
|
export interface TileLayoutProps<T> {
|
||||||
layoutTreeStateAtom: WritableLayoutTreeStateAtom<T>;
|
layoutTreeStateAtom: WritableLayoutTreeStateAtom<T>;
|
||||||
renderContent: ContentRenderer<T>;
|
renderContent: ContentRenderer<T>;
|
||||||
onNodeDelete?: (data: T) => void;
|
onNodeDelete?: (data: T) => Promise<void>;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ export const TileLayout = <T,>({ layoutTreeStateAtom, className, renderContent,
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, 30),
|
}, 30),
|
||||||
[activeDrag, overlayContainerRef, displayContainerRef, layoutTreeState, nodeRefs]
|
[activeDrag, overlayContainerRef, displayContainerRef, layoutTreeState.leafs, nodeRefs]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update the transforms whenever we drag something and whenever the layout updates.
|
// 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.
|
// reattach the new callback.
|
||||||
const [prevUpdateTransforms, setPrevUpdateTransforms] = useState<() => void>(undefined);
|
const [prevUpdateTransforms, setPrevUpdateTransforms] = useState<() => void>(undefined);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
console.log("replace resize listener");
|
||||||
if (prevUpdateTransforms) window.removeEventListener("resize", prevUpdateTransforms);
|
if (prevUpdateTransforms) window.removeEventListener("resize", prevUpdateTransforms);
|
||||||
window.addEventListener("resize", updateTransforms);
|
window.addEventListener("resize", updateTransforms);
|
||||||
setPrevUpdateTransforms(updateTransforms);
|
setPrevUpdateTransforms(updateTransforms);
|
||||||
@ -156,7 +157,7 @@ export const TileLayout = <T,>({ layoutTreeStateAtom, className, renderContent,
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onLeafClose = useCallback(
|
const onLeafClose = useCallback(
|
||||||
(node: LayoutNode<T>) => {
|
async (node: LayoutNode<T>) => {
|
||||||
console.log("onLeafClose", node);
|
console.log("onLeafClose", node);
|
||||||
const deleteAction: LayoutTreeDeleteNodeAction = {
|
const deleteAction: LayoutTreeDeleteNodeAction = {
|
||||||
type: LayoutTreeActionType.DeleteNode,
|
type: LayoutTreeActionType.DeleteNode,
|
||||||
@ -165,7 +166,7 @@ export const TileLayout = <T,>({ layoutTreeStateAtom, className, renderContent,
|
|||||||
console.log("calling dispatch", deleteAction);
|
console.log("calling dispatch", deleteAction);
|
||||||
dispatch(deleteAction);
|
dispatch(deleteAction);
|
||||||
console.log("calling onNodeDelete", node);
|
console.log("calling onNodeDelete", node);
|
||||||
onNodeDelete?.(node.data);
|
await onNodeDelete?.(node.data);
|
||||||
console.log("node deleted");
|
console.log("node deleted");
|
||||||
},
|
},
|
||||||
[onNodeDelete, dispatch]
|
[onNodeDelete, dispatch]
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
// Copyright 2024, Command Line Inc.
|
// Copyright 2024, Command Line Inc.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// 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 { useCallback } from "react";
|
||||||
import { layoutTreeStateReducer, newLayoutTreeState } from "./layoutState.js";
|
import { layoutTreeStateReducer, newLayoutTreeState } from "./layoutState.js";
|
||||||
import {
|
import {
|
||||||
LayoutNode,
|
LayoutNode,
|
||||||
|
LayoutNodeWaveObj,
|
||||||
LayoutTreeAction,
|
LayoutTreeAction,
|
||||||
LayoutTreeState,
|
LayoutTreeState,
|
||||||
WritableLayoutNodeAtom,
|
WritableLayoutNodeAtom,
|
||||||
@ -29,27 +31,16 @@ export function newLayoutTreeStateAtom<T>(rootNode: LayoutNode<T>): PrimitiveAto
|
|||||||
* @returns The derived WritableLayoutTreeStateAtom.
|
* @returns The derived WritableLayoutTreeStateAtom.
|
||||||
*/
|
*/
|
||||||
export function withLayoutTreeState<T>(layoutNodeAtom: WritableLayoutNodeAtom<T>): WritableLayoutTreeStateAtom<T> {
|
export function withLayoutTreeState<T>(layoutNodeAtom: WritableLayoutNodeAtom<T>): WritableLayoutTreeStateAtom<T> {
|
||||||
return atom(
|
const pendingActionAtom = atom<LayoutTreeAction>(null) as PrimitiveAtom<LayoutTreeAction>;
|
||||||
(get) => newLayoutTreeState(get(layoutNodeAtom)),
|
|
||||||
(_get, set, value) => set(layoutNodeAtom, value.rootNode)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function withLayoutStateFromTab(
|
|
||||||
tabAtom: WritableAtom<Tab, [value: Tab], void>
|
|
||||||
): WritableLayoutTreeStateAtom<TabLayoutData> {
|
|
||||||
return atom(
|
return atom(
|
||||||
(get) => {
|
(get) => {
|
||||||
const tabData = get(tabAtom);
|
const layoutState = newLayoutTreeState(get(layoutNodeAtom));
|
||||||
console.log("get layout state from tab", tabData);
|
layoutState.pendingAction = get(pendingActionAtom);
|
||||||
return newLayoutTreeState(tabData?.layout);
|
return layoutState;
|
||||||
},
|
},
|
||||||
(get, set, value) => {
|
(_get, set, value) => {
|
||||||
const tabValue = get(tabAtom);
|
set(pendingActionAtom, value.pendingAction);
|
||||||
const newTabValue = { ...tabValue };
|
set(layoutNodeAtom, value.rootNode);
|
||||||
newTabValue.layout = value.rootNode;
|
|
||||||
console.log("set tab", tabValue, value);
|
|
||||||
set(tabAtom, newTabValue);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -72,6 +63,38 @@ export function useLayoutTreeStateReducerAtom<T>(
|
|||||||
|
|
||||||
const tabLayoutAtomCache = new Map<string, WritableLayoutTreeStateAtom<TabLayoutData>>();
|
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(
|
export function getLayoutStateAtomForTab(
|
||||||
tabId: string,
|
tabId: string,
|
||||||
tabAtom: WritableAtom<Tab, [value: Tab], void>
|
tabAtom: WritableAtom<Tab, [value: Tab], void>
|
||||||
@ -82,7 +105,7 @@ export function getLayoutStateAtomForTab(
|
|||||||
return atom;
|
return atom;
|
||||||
}
|
}
|
||||||
console.log("Creating new atom for tab", tabId);
|
console.log("Creating new atom for tab", tabId);
|
||||||
atom = withLayoutStateFromTab(tabAtom);
|
atom = withLayoutTreeState(withLayoutNodeAtomFromTab<TabLayoutData>(tabAtom));
|
||||||
tabLayoutAtomCache.set(tabId, atom);
|
tabLayoutAtomCache.set(tabId, atom);
|
||||||
return 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 WritableLayoutTreeStateAtom<T> = WritableAtom<LayoutTreeState<T>, [value: LayoutTreeState<T>], void>;
|
||||||
|
|
||||||
export type ContentRenderer<T> = (data: T, ready: boolean, onClose?: () => void) => React.ReactNode;
|
export type ContentRenderer<T> = (data: T, ready: boolean, onClose?: () => void) => React.ReactNode;
|
||||||
|
|
||||||
|
export interface LayoutNodeWaveObj<T> extends WaveObj {
|
||||||
|
node: LayoutNode<T>;
|
||||||
|
}
|
||||||
|
4
frontend/types/custom.d.ts
vendored
4
frontend/types/custom.d.ts
vendored
@ -1,8 +1,6 @@
|
|||||||
// Copyright 2024, Command Line Inc.
|
// Copyright 2024, Command Line Inc.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import { LayoutNode } from "../faraday";
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
type UIContext = {
|
type UIContext = {
|
||||||
windowid: string;
|
windowid: string;
|
||||||
@ -70,7 +68,7 @@ declare global {
|
|||||||
version: number;
|
version: number;
|
||||||
name: string;
|
name: string;
|
||||||
blockids: string[];
|
blockids: string[];
|
||||||
layout: LayoutNode<TabLayoutData>;
|
layoutNode: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Point = {
|
type Point = {
|
||||||
|
@ -79,7 +79,7 @@ func TestCreate(t *testing.T) {
|
|||||||
|
|
||||||
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancelFn()
|
defer cancelFn()
|
||||||
zoneId := uuid.New().String()
|
zoneId := uuid.NewString()
|
||||||
err := WFS.MakeFile(ctx, zoneId, "testfile", nil, FileOptsType{})
|
err := WFS.MakeFile(ctx, zoneId, "testfile", nil, FileOptsType{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error creating file: %v", err)
|
t.Fatalf("error creating file: %v", err)
|
||||||
@ -153,7 +153,7 @@ func TestDelete(t *testing.T) {
|
|||||||
|
|
||||||
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancelFn()
|
defer cancelFn()
|
||||||
zoneId := uuid.New().String()
|
zoneId := uuid.NewString()
|
||||||
err := WFS.MakeFile(ctx, zoneId, "testfile", nil, FileOptsType{})
|
err := WFS.MakeFile(ctx, zoneId, "testfile", nil, FileOptsType{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error creating file: %v", err)
|
t.Fatalf("error creating file: %v", err)
|
||||||
@ -216,7 +216,7 @@ func TestSetMeta(t *testing.T) {
|
|||||||
|
|
||||||
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancelFn()
|
defer cancelFn()
|
||||||
zoneId := uuid.New().String()
|
zoneId := uuid.NewString()
|
||||||
err := WFS.MakeFile(ctx, zoneId, "testfile", nil, FileOptsType{})
|
err := WFS.MakeFile(ctx, zoneId, "testfile", nil, FileOptsType{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error creating file: %v", err)
|
t.Fatalf("error creating file: %v", err)
|
||||||
@ -319,7 +319,7 @@ func TestAppend(t *testing.T) {
|
|||||||
|
|
||||||
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancelFn()
|
defer cancelFn()
|
||||||
zoneId := uuid.New().String()
|
zoneId := uuid.NewString()
|
||||||
fileName := "t2"
|
fileName := "t2"
|
||||||
err := WFS.MakeFile(ctx, zoneId, fileName, nil, FileOptsType{})
|
err := WFS.MakeFile(ctx, zoneId, fileName, nil, FileOptsType{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -347,7 +347,7 @@ func TestWriteFile(t *testing.T) {
|
|||||||
|
|
||||||
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancelFn()
|
defer cancelFn()
|
||||||
zoneId := uuid.New().String()
|
zoneId := uuid.NewString()
|
||||||
fileName := "t3"
|
fileName := "t3"
|
||||||
err := WFS.MakeFile(ctx, zoneId, fileName, nil, FileOptsType{})
|
err := WFS.MakeFile(ctx, zoneId, fileName, nil, FileOptsType{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -391,7 +391,7 @@ func TestCircularWrites(t *testing.T) {
|
|||||||
defer cleanupDb(t)
|
defer cleanupDb(t)
|
||||||
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancelFn()
|
defer cancelFn()
|
||||||
zoneId := uuid.New().String()
|
zoneId := uuid.NewString()
|
||||||
err := WFS.MakeFile(ctx, zoneId, "c1", nil, FileOptsType{Circular: true, MaxSize: 50})
|
err := WFS.MakeFile(ctx, zoneId, "c1", nil, FileOptsType{Circular: true, MaxSize: 50})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error creating file: %v", err)
|
t.Fatalf("error creating file: %v", err)
|
||||||
@ -478,7 +478,7 @@ func TestMultiPart(t *testing.T) {
|
|||||||
|
|
||||||
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancelFn()
|
defer cancelFn()
|
||||||
zoneId := uuid.New().String()
|
zoneId := uuid.NewString()
|
||||||
fileName := "m2"
|
fileName := "m2"
|
||||||
data := makeText(80)
|
data := makeText(80)
|
||||||
err := WFS.MakeFile(ctx, zoneId, fileName, nil, FileOptsType{})
|
err := WFS.MakeFile(ctx, zoneId, fileName, nil, FileOptsType{})
|
||||||
@ -554,7 +554,7 @@ func TestSimpleDBFlush(t *testing.T) {
|
|||||||
|
|
||||||
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancelFn()
|
defer cancelFn()
|
||||||
zoneId := uuid.New().String()
|
zoneId := uuid.NewString()
|
||||||
fileName := "t1"
|
fileName := "t1"
|
||||||
err := WFS.MakeFile(ctx, zoneId, fileName, nil, FileOptsType{})
|
err := WFS.MakeFile(ctx, zoneId, fileName, nil, FileOptsType{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -586,7 +586,7 @@ func TestConcurrentAppend(t *testing.T) {
|
|||||||
defer cleanupDb(t)
|
defer cleanupDb(t)
|
||||||
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancelFn()
|
defer cancelFn()
|
||||||
zoneId := uuid.New().String()
|
zoneId := uuid.NewString()
|
||||||
fileName := "t1"
|
fileName := "t1"
|
||||||
err := WFS.MakeFile(ctx, zoneId, fileName, nil, FileOptsType{})
|
err := WFS.MakeFile(ctx, zoneId, fileName, nil, FileOptsType{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -147,11 +147,11 @@ func (svc *ObjectService) CreateBlock(uiContext wstore.UIContext, blockDef *wsto
|
|||||||
return updatesRtn(ctx, rtn)
|
return updatesRtn(ctx, rtn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (svc *ObjectService) DeleteBlock(uiContext wstore.UIContext, blockId string, newLayout any) (any, error) {
|
func (svc *ObjectService) DeleteBlock(uiContext wstore.UIContext, blockId string) (any, error) {
|
||||||
ctx, cancelFn := context.WithTimeout(context.Background(), DefaultTimeout)
|
ctx, cancelFn := context.WithTimeout(context.Background(), DefaultTimeout)
|
||||||
defer cancelFn()
|
defer cancelFn()
|
||||||
ctx = wstore.ContextWithUpdates(ctx)
|
ctx = wstore.ContextWithUpdates(ctx)
|
||||||
err := wstore.DeleteBlock(ctx, uiContext.ActiveTabId, blockId, newLayout)
|
err := wstore.DeleteBlock(ctx, uiContext.ActiveTabId, blockId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error deleting block: %w", err)
|
return nil, fmt.Errorf("error deleting block: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ func (c *RpcClient) removeReqInfo(rpcId string, clearSend bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *RpcClient) SimpleReq(ctx context.Context, command string, data any) (any, error) {
|
func (c *RpcClient) SimpleReq(ctx context.Context, command string, data any) (any, error) {
|
||||||
rpcId := uuid.New().String()
|
rpcId := uuid.NewString()
|
||||||
seqNum := c.NextSeqNum.Add(1)
|
seqNum := c.NextSeqNum.Add(1)
|
||||||
var timeoutInfo *TimeoutInfo
|
var timeoutInfo *TimeoutInfo
|
||||||
deadline, ok := ctx.Deadline()
|
deadline, ok := ctx.Deadline()
|
||||||
@ -235,7 +235,7 @@ func (c *RpcClient) SimpleReq(ctx context.Context, command string, data any) (an
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *RpcClient) StreamReq(ctx context.Context, command string, data any, respTimeout time.Duration) (chan *RpcPacket, error) {
|
func (c *RpcClient) StreamReq(ctx context.Context, command string, data any, respTimeout time.Duration) (chan *RpcPacket, error) {
|
||||||
rpcId := uuid.New().String()
|
rpcId := uuid.NewString()
|
||||||
seqNum := c.NextSeqNum.Add(1)
|
seqNum := c.NextSeqNum.Add(1)
|
||||||
var timeoutInfo *TimeoutInfo = &TimeoutInfo{RespPacketTimeout: respTimeout.Milliseconds()}
|
var timeoutInfo *TimeoutInfo = &TimeoutInfo{RespPacketTimeout: respTimeout.Milliseconds()}
|
||||||
deadline, ok := ctx.Deadline()
|
deadline, ok := ctx.Deadline()
|
||||||
|
@ -140,13 +140,19 @@ func CreateTab(ctx context.Context, workspaceId string, name string) (*Tab, erro
|
|||||||
if ws == nil {
|
if ws == nil {
|
||||||
return nil, fmt.Errorf("workspace not found: %q", workspaceId)
|
return nil, fmt.Errorf("workspace not found: %q", workspaceId)
|
||||||
}
|
}
|
||||||
|
layoutNodeId := uuid.NewString()
|
||||||
tab := &Tab{
|
tab := &Tab{
|
||||||
OID: uuid.New().String(),
|
OID: uuid.NewString(),
|
||||||
Name: name,
|
Name: name,
|
||||||
BlockIds: []string{},
|
BlockIds: []string{},
|
||||||
|
LayoutNode: layoutNodeId,
|
||||||
|
}
|
||||||
|
layoutNode := &LayoutNode{
|
||||||
|
OID: layoutNodeId,
|
||||||
}
|
}
|
||||||
ws.TabIds = append(ws.TabIds, tab.OID)
|
ws.TabIds = append(ws.TabIds, tab.OID)
|
||||||
DBInsert(tx.Context(), tab)
|
DBInsert(tx.Context(), tab)
|
||||||
|
DBInsert(tx.Context(), layoutNode)
|
||||||
DBUpdate(tx.Context(), ws)
|
DBUpdate(tx.Context(), ws)
|
||||||
return tab, nil
|
return tab, nil
|
||||||
})
|
})
|
||||||
@ -154,7 +160,7 @@ func CreateTab(ctx context.Context, workspaceId string, name string) (*Tab, erro
|
|||||||
|
|
||||||
func CreateWorkspace(ctx context.Context) (*Workspace, error) {
|
func CreateWorkspace(ctx context.Context) (*Workspace, error) {
|
||||||
ws := &Workspace{
|
ws := &Workspace{
|
||||||
OID: uuid.New().String(),
|
OID: uuid.NewString(),
|
||||||
TabIds: []string{},
|
TabIds: []string{},
|
||||||
}
|
}
|
||||||
DBInsert(ctx, ws)
|
DBInsert(ctx, ws)
|
||||||
@ -185,7 +191,7 @@ func CreateBlock(ctx context.Context, tabId string, blockDef *BlockDef, rtOpts *
|
|||||||
if tab == nil {
|
if tab == nil {
|
||||||
return nil, fmt.Errorf("tab not found: %q", tabId)
|
return nil, fmt.Errorf("tab not found: %q", tabId)
|
||||||
}
|
}
|
||||||
blockId := uuid.New().String()
|
blockId := uuid.NewString()
|
||||||
blockData := &Block{
|
blockData := &Block{
|
||||||
OID: blockId,
|
OID: blockId,
|
||||||
BlockDef: blockDef,
|
BlockDef: blockDef,
|
||||||
@ -210,7 +216,7 @@ func findStringInSlice(slice []string, val string) int {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteBlock(ctx context.Context, tabId string, blockId string, newLayout any) error {
|
func DeleteBlock(ctx context.Context, tabId string, blockId string) error {
|
||||||
return WithTx(ctx, func(tx *TxWrap) error {
|
return WithTx(ctx, func(tx *TxWrap) error {
|
||||||
tab, _ := DBGet[*Tab](tx.Context(), tabId)
|
tab, _ := DBGet[*Tab](tx.Context(), tabId)
|
||||||
if tab == nil {
|
if tab == nil {
|
||||||
@ -221,11 +227,8 @@ func DeleteBlock(ctx context.Context, tabId string, blockId string, newLayout an
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
tab.BlockIds = append(tab.BlockIds[:blockIdx], tab.BlockIds[blockIdx+1:]...)
|
tab.BlockIds = append(tab.BlockIds[:blockIdx], tab.BlockIds[blockIdx+1:]...)
|
||||||
if newLayout != nil {
|
|
||||||
tab.Layout = newLayout
|
|
||||||
}
|
|
||||||
DBUpdate(tx.Context(), tab)
|
DBUpdate(tx.Context(), tab)
|
||||||
DBDelete(tx.Context(), "block", blockId)
|
DBDelete(tx.Context(), OType_Block, blockId)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -246,9 +249,10 @@ func CloseTab(ctx context.Context, workspaceId string, tabId string) error {
|
|||||||
}
|
}
|
||||||
ws.TabIds = append(ws.TabIds[:tabIdx], ws.TabIds[tabIdx+1:]...)
|
ws.TabIds = append(ws.TabIds[:tabIdx], ws.TabIds[tabIdx+1:]...)
|
||||||
DBUpdate(tx.Context(), ws)
|
DBUpdate(tx.Context(), ws)
|
||||||
DBDelete(tx.Context(), "tab", tabId)
|
DBDelete(tx.Context(), OType_Tab, tabId)
|
||||||
|
DBDelete(tx.Context(), OType_LayoutNode, tab.LayoutNode)
|
||||||
for _, blockId := range tab.BlockIds {
|
for _, blockId := range tab.BlockIds {
|
||||||
DBDelete(tx.Context(), "block", blockId)
|
DBDelete(tx.Context(), OType_Block, blockId)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -300,11 +304,12 @@ func EnsureInitialData() error {
|
|||||||
if clientCount > 0 {
|
if clientCount > 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
windowId := uuid.New().String()
|
windowId := uuid.NewString()
|
||||||
workspaceId := uuid.New().String()
|
workspaceId := uuid.NewString()
|
||||||
tabId := uuid.New().String()
|
tabId := uuid.NewString()
|
||||||
|
layoutNodeId := uuid.NewString()
|
||||||
client := &Client{
|
client := &Client{
|
||||||
OID: uuid.New().String(),
|
OID: uuid.NewString(),
|
||||||
MainWindowId: windowId,
|
MainWindowId: windowId,
|
||||||
}
|
}
|
||||||
err = DBInsert(ctx, client)
|
err = DBInsert(ctx, client)
|
||||||
@ -339,13 +344,22 @@ func EnsureInitialData() error {
|
|||||||
return fmt.Errorf("error inserting workspace: %w", err)
|
return fmt.Errorf("error inserting workspace: %w", err)
|
||||||
}
|
}
|
||||||
tab := &Tab{
|
tab := &Tab{
|
||||||
OID: tabId,
|
OID: tabId,
|
||||||
Name: "Tab-1",
|
Name: "Tab-1",
|
||||||
BlockIds: []string{},
|
BlockIds: []string{},
|
||||||
|
LayoutNode: layoutNodeId,
|
||||||
}
|
}
|
||||||
err = DBInsert(ctx, tab)
|
err = DBInsert(ctx, tab)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error inserting tab: %w", err)
|
return fmt.Errorf("error inserting tab: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
layoutNode := &LayoutNode{
|
||||||
|
OID: layoutNodeId,
|
||||||
|
}
|
||||||
|
err = DBInsert(ctx, layoutNode)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error inserting layout node: %w", err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,15 @@ const (
|
|||||||
UpdateType_Delete = "delete"
|
UpdateType_Delete = "delete"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
OType_Client = "client"
|
||||||
|
OType_Window = "window"
|
||||||
|
OType_Workspace = "workspace"
|
||||||
|
OType_Tab = "tab"
|
||||||
|
OType_LayoutNode = "layout"
|
||||||
|
OType_Block = "block"
|
||||||
|
)
|
||||||
|
|
||||||
type WaveObjUpdate struct {
|
type WaveObjUpdate struct {
|
||||||
UpdateType string `json:"updatetype"`
|
UpdateType string `json:"updatetype"`
|
||||||
OType string `json:"otype"`
|
OType string `json:"otype"`
|
||||||
@ -51,7 +60,7 @@ type Client struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*Client) GetOType() string {
|
func (*Client) GetOType() string {
|
||||||
return "client"
|
return OType_Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// stores the ui-context of the window
|
// stores the ui-context of the window
|
||||||
@ -69,7 +78,7 @@ type Window struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*Window) GetOType() string {
|
func (*Window) GetOType() string {
|
||||||
return "window"
|
return OType_Window
|
||||||
}
|
}
|
||||||
|
|
||||||
type Workspace struct {
|
type Workspace struct {
|
||||||
@ -81,20 +90,31 @@ type Workspace struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*Workspace) GetOType() string {
|
func (*Workspace) GetOType() string {
|
||||||
return "workspace"
|
return OType_Workspace
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tab struct {
|
type Tab struct {
|
||||||
OID string `json:"oid"`
|
OID string `json:"oid"`
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Layout any `json:"layout,omitempty"`
|
LayoutNode string `json:"layoutNode"`
|
||||||
BlockIds []string `json:"blockids"`
|
BlockIds []string `json:"blockids"`
|
||||||
Meta map[string]any `json:"meta"`
|
Meta map[string]any `json:"meta"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Tab) GetOType() string {
|
func (*Tab) GetOType() string {
|
||||||
return "tab"
|
return OType_Tab
|
||||||
|
}
|
||||||
|
|
||||||
|
type LayoutNode struct {
|
||||||
|
OID string `json:"oid"`
|
||||||
|
Version int `json:"version"`
|
||||||
|
Node any `json:"node,omitempty"`
|
||||||
|
Meta map[string]any `json:"meta,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*LayoutNode) GetOType() string {
|
||||||
|
return OType_LayoutNode
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileDef struct {
|
type FileDef struct {
|
||||||
@ -138,7 +158,7 @@ type Block struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*Block) GetOType() string {
|
func (*Block) GetOType() string {
|
||||||
return "block"
|
return OType_Block
|
||||||
}
|
}
|
||||||
|
|
||||||
func AllWaveObjTypes() []reflect.Type {
|
func AllWaveObjTypes() []reflect.Type {
|
||||||
@ -148,5 +168,6 @@ func AllWaveObjTypes() []reflect.Type {
|
|||||||
reflect.TypeOf(&Workspace{}),
|
reflect.TypeOf(&Workspace{}),
|
||||||
reflect.TypeOf(&Tab{}),
|
reflect.TypeOf(&Tab{}),
|
||||||
reflect.TypeOf(&Block{}),
|
reflect.TypeOf(&Block{}),
|
||||||
|
reflect.TypeOf(&LayoutNode{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user