From f12e246c150cd6b452b4fc5a5f820a9bfc6d0c41 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Wed, 5 Jun 2024 17:21:40 -0700 Subject: [PATCH] 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. --- db/migrations-wstore/000001_init.down.sql | 1 - db/migrations-wstore/000001_init.up.sql | 1 + db/migrations-wstore/000002_init.down.sql | 1 + db/migrations-wstore/000002_init.up.sql | 5 ++ frontend/app/store/wos.ts | 11 ++-- frontend/app/tab/tab.tsx | 11 ++-- frontend/app/workspace/workspace.tsx | 2 +- frontend/faraday/lib/TileLayout.tsx | 9 ++-- frontend/faraday/lib/layoutAtom.ts | 63 +++++++++++++++------- frontend/faraday/lib/model.ts | 4 ++ frontend/types/custom.d.ts | 4 +- pkg/filestore/blockstore_test.go | 18 +++---- pkg/service/objectservice/objectservice.go | 4 +- pkg/wshrpc/rpc_client.go | 4 +- pkg/wstore/wstore.go | 52 +++++++++++------- pkg/wstore/wstore_types.go | 43 +++++++++++---- 16 files changed, 149 insertions(+), 84 deletions(-) create mode 100644 db/migrations-wstore/000002_init.down.sql create mode 100644 db/migrations-wstore/000002_init.up.sql diff --git a/db/migrations-wstore/000001_init.down.sql b/db/migrations-wstore/000001_init.down.sql index 9e6fc6d60..177ce0860 100644 --- a/db/migrations-wstore/000001_init.down.sql +++ b/db/migrations-wstore/000001_init.down.sql @@ -5,4 +5,3 @@ DROP TABLE db_workspace; DROP TABLE db_tab; DROP TABLE db_block; - diff --git a/db/migrations-wstore/000001_init.up.sql b/db/migrations-wstore/000001_init.up.sql index 7fa0c3bd8..34c3b8832 100644 --- a/db/migrations-wstore/000001_init.up.sql +++ b/db/migrations-wstore/000001_init.up.sql @@ -27,3 +27,4 @@ CREATE TABLE db_block ( version int NOT NULL, data json NOT NULL ); + diff --git a/db/migrations-wstore/000002_init.down.sql b/db/migrations-wstore/000002_init.down.sql new file mode 100644 index 000000000..f7ef05cd1 --- /dev/null +++ b/db/migrations-wstore/000002_init.down.sql @@ -0,0 +1 @@ +DROP TABLE db_layout; diff --git a/db/migrations-wstore/000002_init.up.sql b/db/migrations-wstore/000002_init.up.sql new file mode 100644 index 000000000..b0a05456c --- /dev/null +++ b/db/migrations-wstore/000002_init.up.sql @@ -0,0 +1,5 @@ +CREATE TABLE db_layout ( + oid varchar(36) PRIMARY KEY, + version int NOT NULL, + data json NOT NULL +); diff --git a/frontend/app/store/wos.ts b/frontend/app/store/wos.ts index 5ab78133d..da3f05304 100644 --- a/frontend/app/store/wos.ts +++ b/frontend/app/store/wos.ts @@ -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(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(value: WaveObj, setFn?: jotai.Setter, pushToServer?: boolean) { +function setObjectValue(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(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): Promise { - return wrapObjectServiceCall("DeleteBlock", blockId, newLayout); +export function DeleteBlock(blockId: string): Promise { + return wrapObjectServiceCall("DeleteBlock", blockId); } export function CloseTab(tabId: string): Promise { @@ -339,9 +340,9 @@ export function UpdateObjectMeta(blockId: string, meta: MetadataType): Promise { + console.log("UpdateObject", waveObj, returnUpdates); return wrapObjectServiceCall("UpdateObject", waveObj, returnUpdates); } - export { cleanWaveObjectCache, clearWaveObjectCache, diff --git a/frontend/app/tab/tab.tsx b/frontend/app/tab/tab.tsx index 4da6fe350..6b3e25cb9 100644 --- a/frontend/app/tab/tab.tsx +++ b/frontend/app/tab/tab.tsx @@ -27,13 +27,10 @@ const TabContent = ({ tabId }: { tabId: string }) => { return ; }, []); - 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 ; diff --git a/frontend/app/workspace/workspace.tsx b/frontend/app/workspace/workspace.tsx index 87d794e11..816cfe555 100644 --- a/frontend/app/workspace/workspace.tsx +++ b/frontend/app/workspace/workspace.tsx @@ -77,7 +77,7 @@ function Widgets() { }; dispatchLayoutStateAction(insertNodeAction); }, - [activeTabAtom] + [activeTabAtom, dispatchLayoutStateAction] ); async function createBlock(blockDef: BlockDef) { diff --git a/frontend/faraday/lib/TileLayout.tsx b/frontend/faraday/lib/TileLayout.tsx index 8e336c569..f41b7f5c1 100644 --- a/frontend/faraday/lib/TileLayout.tsx +++ b/frontend/faraday/lib/TileLayout.tsx @@ -22,7 +22,7 @@ import { setTransform as createTransform, debounce, determineDropDirection } fro export interface TileLayoutProps { layoutTreeStateAtom: WritableLayoutTreeStateAtom; renderContent: ContentRenderer; - onNodeDelete?: (data: T) => void; + onNodeDelete?: (data: T) => Promise; className?: string; } @@ -120,7 +120,7 @@ export const TileLayout = ({ 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 = ({ 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 = ({ layoutTreeStateAtom, className, renderContent, }, []); const onLeafClose = useCallback( - (node: LayoutNode) => { + async (node: LayoutNode) => { console.log("onLeafClose", node); const deleteAction: LayoutTreeDeleteNodeAction = { type: LayoutTreeActionType.DeleteNode, @@ -165,7 +166,7 @@ export const TileLayout = ({ 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] diff --git a/frontend/faraday/lib/layoutAtom.ts b/frontend/faraday/lib/layoutAtom.ts index b5ed97f97..65e74bc0b 100644 --- a/frontend/faraday/lib/layoutAtom.ts +++ b/frontend/faraday/lib/layoutAtom.ts @@ -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(rootNode: LayoutNode): PrimitiveAto * @returns The derived WritableLayoutTreeStateAtom. */ export function withLayoutTreeState(layoutNodeAtom: WritableLayoutNodeAtom): WritableLayoutTreeStateAtom { - return atom( - (get) => newLayoutTreeState(get(layoutNodeAtom)), - (_get, set, value) => set(layoutNodeAtom, value.rootNode) - ); -} - -export function withLayoutStateFromTab( - tabAtom: WritableAtom -): WritableLayoutTreeStateAtom { + const pendingActionAtom = atom(null) as PrimitiveAtom; 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( 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 @@ -82,7 +105,7 @@ export function getLayoutStateAtomForTab( return atom; } console.log("Creating new atom for tab", tabId); - atom = withLayoutStateFromTab(tabAtom); + atom = withLayoutTreeState(withLayoutNodeAtomFromTab(tabAtom)); tabLayoutAtomCache.set(tabId, atom); return atom; } diff --git a/frontend/faraday/lib/model.ts b/frontend/faraday/lib/model.ts index dda6a7042..861effba4 100644 --- a/frontend/faraday/lib/model.ts +++ b/frontend/faraday/lib/model.ts @@ -131,3 +131,7 @@ export type WritableLayoutNodeAtom = WritableAtom, [value: Layo export type WritableLayoutTreeStateAtom = WritableAtom, [value: LayoutTreeState], void>; export type ContentRenderer = (data: T, ready: boolean, onClose?: () => void) => React.ReactNode; + +export interface LayoutNodeWaveObj extends WaveObj { + node: LayoutNode; +} diff --git a/frontend/types/custom.d.ts b/frontend/types/custom.d.ts index 78f1dea92..cfa74d359 100644 --- a/frontend/types/custom.d.ts +++ b/frontend/types/custom.d.ts @@ -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; + layoutNode: string; }; type Point = { diff --git a/pkg/filestore/blockstore_test.go b/pkg/filestore/blockstore_test.go index 1fda2e009..e5ba928ad 100644 --- a/pkg/filestore/blockstore_test.go +++ b/pkg/filestore/blockstore_test.go @@ -79,7 +79,7 @@ func TestCreate(t *testing.T) { ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) defer cancelFn() - zoneId := uuid.New().String() + zoneId := uuid.NewString() err := WFS.MakeFile(ctx, zoneId, "testfile", nil, FileOptsType{}) if err != nil { 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) defer cancelFn() - zoneId := uuid.New().String() + zoneId := uuid.NewString() err := WFS.MakeFile(ctx, zoneId, "testfile", nil, FileOptsType{}) if err != nil { 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) defer cancelFn() - zoneId := uuid.New().String() + zoneId := uuid.NewString() err := WFS.MakeFile(ctx, zoneId, "testfile", nil, FileOptsType{}) if err != nil { 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) defer cancelFn() - zoneId := uuid.New().String() + zoneId := uuid.NewString() fileName := "t2" err := WFS.MakeFile(ctx, zoneId, fileName, nil, FileOptsType{}) if err != nil { @@ -347,7 +347,7 @@ func TestWriteFile(t *testing.T) { ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) defer cancelFn() - zoneId := uuid.New().String() + zoneId := uuid.NewString() fileName := "t3" err := WFS.MakeFile(ctx, zoneId, fileName, nil, FileOptsType{}) if err != nil { @@ -391,7 +391,7 @@ func TestCircularWrites(t *testing.T) { defer cleanupDb(t) ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) defer cancelFn() - zoneId := uuid.New().String() + zoneId := uuid.NewString() err := WFS.MakeFile(ctx, zoneId, "c1", nil, FileOptsType{Circular: true, MaxSize: 50}) if err != nil { 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) defer cancelFn() - zoneId := uuid.New().String() + zoneId := uuid.NewString() fileName := "m2" data := makeText(80) 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) defer cancelFn() - zoneId := uuid.New().String() + zoneId := uuid.NewString() fileName := "t1" err := WFS.MakeFile(ctx, zoneId, fileName, nil, FileOptsType{}) if err != nil { @@ -586,7 +586,7 @@ func TestConcurrentAppend(t *testing.T) { defer cleanupDb(t) ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) defer cancelFn() - zoneId := uuid.New().String() + zoneId := uuid.NewString() fileName := "t1" err := WFS.MakeFile(ctx, zoneId, fileName, nil, FileOptsType{}) if err != nil { diff --git a/pkg/service/objectservice/objectservice.go b/pkg/service/objectservice/objectservice.go index 3f569cab2..fcffe6e45 100644 --- a/pkg/service/objectservice/objectservice.go +++ b/pkg/service/objectservice/objectservice.go @@ -147,11 +147,11 @@ func (svc *ObjectService) CreateBlock(uiContext wstore.UIContext, blockDef *wsto 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) defer cancelFn() ctx = wstore.ContextWithUpdates(ctx) - err := wstore.DeleteBlock(ctx, uiContext.ActiveTabId, blockId, newLayout) + err := wstore.DeleteBlock(ctx, uiContext.ActiveTabId, blockId) if err != nil { return nil, fmt.Errorf("error deleting block: %w", err) } diff --git a/pkg/wshrpc/rpc_client.go b/pkg/wshrpc/rpc_client.go index 425fae7c6..9495b9c6c 100644 --- a/pkg/wshrpc/rpc_client.go +++ b/pkg/wshrpc/rpc_client.go @@ -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) { - rpcId := uuid.New().String() + rpcId := uuid.NewString() seqNum := c.NextSeqNum.Add(1) var timeoutInfo *TimeoutInfo 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) { - rpcId := uuid.New().String() + rpcId := uuid.NewString() seqNum := c.NextSeqNum.Add(1) var timeoutInfo *TimeoutInfo = &TimeoutInfo{RespPacketTimeout: respTimeout.Milliseconds()} deadline, ok := ctx.Deadline() diff --git a/pkg/wstore/wstore.go b/pkg/wstore/wstore.go index a9d95e486..24747dd37 100644 --- a/pkg/wstore/wstore.go +++ b/pkg/wstore/wstore.go @@ -140,13 +140,19 @@ func CreateTab(ctx context.Context, workspaceId string, name string) (*Tab, erro if ws == nil { return nil, fmt.Errorf("workspace not found: %q", workspaceId) } + layoutNodeId := uuid.NewString() tab := &Tab{ - OID: uuid.New().String(), - Name: name, - BlockIds: []string{}, + OID: uuid.NewString(), + Name: name, + BlockIds: []string{}, + LayoutNode: layoutNodeId, + } + layoutNode := &LayoutNode{ + OID: layoutNodeId, } ws.TabIds = append(ws.TabIds, tab.OID) DBInsert(tx.Context(), tab) + DBInsert(tx.Context(), layoutNode) DBUpdate(tx.Context(), ws) 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) { ws := &Workspace{ - OID: uuid.New().String(), + OID: uuid.NewString(), TabIds: []string{}, } DBInsert(ctx, ws) @@ -185,7 +191,7 @@ func CreateBlock(ctx context.Context, tabId string, blockDef *BlockDef, rtOpts * if tab == nil { return nil, fmt.Errorf("tab not found: %q", tabId) } - blockId := uuid.New().String() + blockId := uuid.NewString() blockData := &Block{ OID: blockId, BlockDef: blockDef, @@ -210,7 +216,7 @@ func findStringInSlice(slice []string, val string) int { 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 { tab, _ := DBGet[*Tab](tx.Context(), tabId) if tab == nil { @@ -221,11 +227,8 @@ func DeleteBlock(ctx context.Context, tabId string, blockId string, newLayout an return nil } tab.BlockIds = append(tab.BlockIds[:blockIdx], tab.BlockIds[blockIdx+1:]...) - if newLayout != nil { - tab.Layout = newLayout - } DBUpdate(tx.Context(), tab) - DBDelete(tx.Context(), "block", blockId) + DBDelete(tx.Context(), OType_Block, blockId) 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:]...) 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 { - DBDelete(tx.Context(), "block", blockId) + DBDelete(tx.Context(), OType_Block, blockId) } return nil }) @@ -300,11 +304,12 @@ func EnsureInitialData() error { if clientCount > 0 { return nil } - windowId := uuid.New().String() - workspaceId := uuid.New().String() - tabId := uuid.New().String() + windowId := uuid.NewString() + workspaceId := uuid.NewString() + tabId := uuid.NewString() + layoutNodeId := uuid.NewString() client := &Client{ - OID: uuid.New().String(), + OID: uuid.NewString(), MainWindowId: windowId, } err = DBInsert(ctx, client) @@ -339,13 +344,22 @@ func EnsureInitialData() error { return fmt.Errorf("error inserting workspace: %w", err) } tab := &Tab{ - OID: tabId, - Name: "Tab-1", - BlockIds: []string{}, + OID: tabId, + Name: "Tab-1", + BlockIds: []string{}, + LayoutNode: layoutNodeId, } err = DBInsert(ctx, tab) if err != nil { 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 } diff --git a/pkg/wstore/wstore_types.go b/pkg/wstore/wstore_types.go index 036d16ce7..356e5626b 100644 --- a/pkg/wstore/wstore_types.go +++ b/pkg/wstore/wstore_types.go @@ -21,6 +21,15 @@ const ( 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 { UpdateType string `json:"updatetype"` OType string `json:"otype"` @@ -51,7 +60,7 @@ type Client struct { } func (*Client) GetOType() string { - return "client" + return OType_Client } // stores the ui-context of the window @@ -69,7 +78,7 @@ type Window struct { } func (*Window) GetOType() string { - return "window" + return OType_Window } type Workspace struct { @@ -81,20 +90,31 @@ type Workspace struct { } func (*Workspace) GetOType() string { - return "workspace" + return OType_Workspace } type Tab struct { - OID string `json:"oid"` - Version int `json:"version"` - Name string `json:"name"` - Layout any `json:"layout,omitempty"` - BlockIds []string `json:"blockids"` - Meta map[string]any `json:"meta"` + OID string `json:"oid"` + Version int `json:"version"` + Name string `json:"name"` + LayoutNode string `json:"layoutNode"` + BlockIds []string `json:"blockids"` + Meta map[string]any `json:"meta"` } 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 { @@ -138,7 +158,7 @@ type Block struct { } func (*Block) GetOType() string { - return "block" + return OType_Block } func AllWaveObjTypes() []reflect.Type { @@ -148,5 +168,6 @@ func AllWaveObjTypes() []reflect.Type { reflect.TypeOf(&Workspace{}), reflect.TypeOf(&Tab{}), reflect.TypeOf(&Block{}), + reflect.TypeOf(&LayoutNode{}), } }