mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
Delete a tab when it's out of blocks, cascade to window (#1374)
Updates `DeleteBlock` to close its parent tab if the tab has no more blocks. This will also cascade to close the workspace if it no longer has any tabs, same for window. I had to move some block-related functionality around on the backend.
This commit is contained in:
parent
04c4f0a203
commit
90e31dfa48
@ -39,6 +39,7 @@ import {
|
|||||||
getAllWaveWindows,
|
getAllWaveWindows,
|
||||||
getWaveWindowById,
|
getWaveWindowById,
|
||||||
getWaveWindowByWebContentsId,
|
getWaveWindowByWebContentsId,
|
||||||
|
getWaveWindowByWorkspaceId,
|
||||||
WaveBrowserWindow,
|
WaveBrowserWindow,
|
||||||
} from "./emain-window";
|
} from "./emain-window";
|
||||||
import { ElectronWshClient, initElectronWshClient } from "./emain-wsh";
|
import { ElectronWshClient, initElectronWshClient } from "./emain-wsh";
|
||||||
@ -125,6 +126,14 @@ function handleWSEvent(evtMsg: WSEventType) {
|
|||||||
if (ww != null) {
|
if (ww != null) {
|
||||||
ww.destroy(); // bypass the "are you sure?" dialog
|
ww.destroy(); // bypass the "are you sure?" dialog
|
||||||
}
|
}
|
||||||
|
} else if (evtMsg.eventtype == "electron:updateactivetab") {
|
||||||
|
const activeTabUpdate: { workspaceid: string; newactivetabid: string } = evtMsg.data;
|
||||||
|
console.log("electron:updateactivetab", activeTabUpdate);
|
||||||
|
const ww = getWaveWindowByWorkspaceId(activeTabUpdate.workspaceid);
|
||||||
|
if (ww == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await ww.setActiveTab(activeTabUpdate.newactivetabid, false);
|
||||||
} else {
|
} else {
|
||||||
console.log("unhandled electron ws eventtype", evtMsg.eventtype);
|
console.log("unhandled electron ws eventtype", evtMsg.eventtype);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
WSEvent_ElectronNewWindow = "electron:newwindow"
|
WSEvent_ElectronNewWindow = "electron:newwindow"
|
||||||
WSEvent_ElectronCloseWindow = "electron:closewindow"
|
WSEvent_ElectronCloseWindow = "electron:closewindow"
|
||||||
|
WSEvent_ElectronUpdateActiveTab = "electron:updateactivetab"
|
||||||
WSEvent_Rpc = "rpc"
|
WSEvent_Rpc = "rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -175,15 +175,7 @@ func (svc *WorkspaceService) CloseTab(ctx context.Context, workspaceId string, t
|
|||||||
}
|
}
|
||||||
rtn := &CloseTabRtnType{}
|
rtn := &CloseTabRtnType{}
|
||||||
if newActiveTabId == "" {
|
if newActiveTabId == "" {
|
||||||
windowId, err := wstore.DBFindWindowForWorkspaceId(ctx, workspaceId)
|
|
||||||
if err != nil {
|
|
||||||
return rtn, nil, fmt.Errorf("unable to find window for workspace id %v: %w", workspaceId, err)
|
|
||||||
}
|
|
||||||
rtn.CloseWindow = true
|
rtn.CloseWindow = true
|
||||||
err = wcore.CloseWindow(ctx, windowId, fromElectron)
|
|
||||||
if err != nil {
|
|
||||||
return rtn, nil, err
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
rtn.NewActiveTabId = newActiveTabId
|
rtn.NewActiveTabId = newActiveTabId
|
||||||
}
|
}
|
||||||
|
@ -159,6 +159,11 @@ type WorkspaceListEntry struct {
|
|||||||
|
|
||||||
type WorkspaceList []*WorkspaceListEntry
|
type WorkspaceList []*WorkspaceListEntry
|
||||||
|
|
||||||
|
type ActiveTabUpdate struct {
|
||||||
|
WorkspaceId string `json:"workspaceid"`
|
||||||
|
NewActiveTabId string `json:"newactivetabid"`
|
||||||
|
}
|
||||||
|
|
||||||
type Workspace struct {
|
type Workspace struct {
|
||||||
OID string `json:"oid"`
|
OID string `json:"oid"`
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
|
@ -3,11 +3,14 @@ package wcore
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/wavetermdev/waveterm/pkg/blockcontroller"
|
"github.com/wavetermdev/waveterm/pkg/blockcontroller"
|
||||||
"github.com/wavetermdev/waveterm/pkg/panichandler"
|
"github.com/wavetermdev/waveterm/pkg/panichandler"
|
||||||
"github.com/wavetermdev/waveterm/pkg/telemetry"
|
"github.com/wavetermdev/waveterm/pkg/telemetry"
|
||||||
|
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
|
||||||
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
||||||
"github.com/wavetermdev/waveterm/pkg/wps"
|
"github.com/wavetermdev/waveterm/pkg/wps"
|
||||||
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
||||||
@ -21,13 +24,34 @@ func CreateSubBlock(ctx context.Context, blockId string, blockDef *waveobj.Block
|
|||||||
if blockDef.Meta == nil || blockDef.Meta.GetString(waveobj.MetaKey_View, "") == "" {
|
if blockDef.Meta == nil || blockDef.Meta.GetString(waveobj.MetaKey_View, "") == "" {
|
||||||
return nil, fmt.Errorf("no view provided for new block")
|
return nil, fmt.Errorf("no view provided for new block")
|
||||||
}
|
}
|
||||||
blockData, err := wstore.CreateSubBlock(ctx, blockId, blockDef)
|
blockData, err := createSubBlockObj(ctx, blockId, blockDef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error creating sub block: %w", err)
|
return nil, fmt.Errorf("error creating sub block: %w", err)
|
||||||
}
|
}
|
||||||
return blockData, nil
|
return blockData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createSubBlockObj(ctx context.Context, parentBlockId string, blockDef *waveobj.BlockDef) (*waveobj.Block, error) {
|
||||||
|
return wstore.WithTxRtn(ctx, func(tx *wstore.TxWrap) (*waveobj.Block, error) {
|
||||||
|
parentBlock, _ := wstore.DBGet[*waveobj.Block](tx.Context(), parentBlockId)
|
||||||
|
if parentBlock == nil {
|
||||||
|
return nil, fmt.Errorf("parent block not found: %q", parentBlockId)
|
||||||
|
}
|
||||||
|
blockId := uuid.NewString()
|
||||||
|
blockData := &waveobj.Block{
|
||||||
|
OID: blockId,
|
||||||
|
ParentORef: waveobj.MakeORef(waveobj.OType_Block, parentBlockId).String(),
|
||||||
|
BlockDef: blockDef,
|
||||||
|
RuntimeOpts: nil,
|
||||||
|
Meta: blockDef.Meta,
|
||||||
|
}
|
||||||
|
wstore.DBInsert(tx.Context(), blockData)
|
||||||
|
parentBlock.SubBlockIds = append(parentBlock.SubBlockIds, blockId)
|
||||||
|
wstore.DBUpdate(tx.Context(), parentBlock)
|
||||||
|
return blockData, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func CreateBlock(ctx context.Context, tabId string, blockDef *waveobj.BlockDef, rtOpts *waveobj.RuntimeOpts) (*waveobj.Block, error) {
|
func CreateBlock(ctx context.Context, tabId string, blockDef *waveobj.BlockDef, rtOpts *waveobj.RuntimeOpts) (*waveobj.Block, error) {
|
||||||
if blockDef == nil {
|
if blockDef == nil {
|
||||||
return nil, fmt.Errorf("blockDef is nil")
|
return nil, fmt.Errorf("blockDef is nil")
|
||||||
@ -35,7 +59,7 @@ func CreateBlock(ctx context.Context, tabId string, blockDef *waveobj.BlockDef,
|
|||||||
if blockDef.Meta == nil || blockDef.Meta.GetString(waveobj.MetaKey_View, "") == "" {
|
if blockDef.Meta == nil || blockDef.Meta.GetString(waveobj.MetaKey_View, "") == "" {
|
||||||
return nil, fmt.Errorf("no view provided for new block")
|
return nil, fmt.Errorf("no view provided for new block")
|
||||||
}
|
}
|
||||||
blockData, err := wstore.CreateBlock(ctx, tabId, blockDef, rtOpts)
|
blockData, err := createBlockObj(ctx, tabId, blockDef, rtOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error creating block: %w", err)
|
return nil, fmt.Errorf("error creating block: %w", err)
|
||||||
}
|
}
|
||||||
@ -54,6 +78,27 @@ func CreateBlock(ctx context.Context, tabId string, blockDef *waveobj.BlockDef,
|
|||||||
return blockData, nil
|
return blockData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createBlockObj(ctx context.Context, tabId string, blockDef *waveobj.BlockDef, rtOpts *waveobj.RuntimeOpts) (*waveobj.Block, error) {
|
||||||
|
return wstore.WithTxRtn(ctx, func(tx *wstore.TxWrap) (*waveobj.Block, error) {
|
||||||
|
tab, _ := wstore.DBGet[*waveobj.Tab](tx.Context(), tabId)
|
||||||
|
if tab == nil {
|
||||||
|
return nil, fmt.Errorf("tab not found: %q", tabId)
|
||||||
|
}
|
||||||
|
blockId := uuid.NewString()
|
||||||
|
blockData := &waveobj.Block{
|
||||||
|
OID: blockId,
|
||||||
|
ParentORef: waveobj.MakeORef(waveobj.OType_Tab, tabId).String(),
|
||||||
|
BlockDef: blockDef,
|
||||||
|
RuntimeOpts: rtOpts,
|
||||||
|
Meta: blockDef.Meta,
|
||||||
|
}
|
||||||
|
wstore.DBInsert(tx.Context(), blockData)
|
||||||
|
tab.BlockIds = append(tab.BlockIds, blockId)
|
||||||
|
wstore.DBUpdate(tx.Context(), tab)
|
||||||
|
return blockData, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func DeleteBlock(ctx context.Context, blockId string) error {
|
func DeleteBlock(ctx context.Context, blockId string) error {
|
||||||
block, err := wstore.DBMustGet[*waveobj.Block](ctx, blockId)
|
block, err := wstore.DBMustGet[*waveobj.Block](ctx, blockId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -70,15 +115,71 @@ func DeleteBlock(ctx context.Context, blockId string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = wstore.DeleteBlock(ctx, blockId)
|
parentBlockCount, err := deleteBlockObj(ctx, blockId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error deleting block: %w", err)
|
return fmt.Errorf("error deleting block: %w", err)
|
||||||
}
|
}
|
||||||
|
log.Printf("DeleteBlock: parentBlockCount: %d", parentBlockCount)
|
||||||
|
parentORef := waveobj.ParseORefNoErr(block.ParentORef)
|
||||||
|
|
||||||
|
if parentORef.OType == waveobj.OType_Tab {
|
||||||
|
if parentBlockCount == 0 {
|
||||||
|
// if parent tab has no blocks, delete the tab
|
||||||
|
log.Printf("DeleteBlock: parent tab has no blocks, deleting tab %s", parentORef.OID)
|
||||||
|
parentWorkspaceId, err := wstore.DBFindWorkspaceForTabId(ctx, parentORef.OID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error finding workspace for tab to delete %s: %w", parentORef.OID, err)
|
||||||
|
}
|
||||||
|
newActiveTabId, err := DeleteTab(ctx, parentWorkspaceId, parentORef.OID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error deleting tab %s: %w", parentORef.OID, err)
|
||||||
|
}
|
||||||
|
SendActiveTabUpdate(ctx, parentWorkspaceId, newActiveTabId)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
go blockcontroller.StopBlockController(blockId)
|
go blockcontroller.StopBlockController(blockId)
|
||||||
sendBlockCloseEvent(blockId)
|
sendBlockCloseEvent(blockId)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns the updated block count for the parent object
|
||||||
|
func deleteBlockObj(ctx context.Context, blockId string) (int, error) {
|
||||||
|
return wstore.WithTxRtn(ctx, func(tx *wstore.TxWrap) (int, error) {
|
||||||
|
block, err := wstore.DBGet[*waveobj.Block](tx.Context(), blockId)
|
||||||
|
if err != nil {
|
||||||
|
return -1, fmt.Errorf("error getting block: %w", err)
|
||||||
|
}
|
||||||
|
if block == nil {
|
||||||
|
return -1, fmt.Errorf("block not found: %q", blockId)
|
||||||
|
}
|
||||||
|
if len(block.SubBlockIds) > 0 {
|
||||||
|
return -1, fmt.Errorf("block has subblocks, must delete subblocks first")
|
||||||
|
}
|
||||||
|
parentORef := waveobj.ParseORefNoErr(block.ParentORef)
|
||||||
|
parentBlockCount := -1
|
||||||
|
if parentORef != nil {
|
||||||
|
if parentORef.OType == waveobj.OType_Tab {
|
||||||
|
tab, _ := wstore.DBGet[*waveobj.Tab](tx.Context(), parentORef.OID)
|
||||||
|
if tab != nil {
|
||||||
|
tab.BlockIds = utilfn.RemoveElemFromSlice(tab.BlockIds, blockId)
|
||||||
|
wstore.DBUpdate(tx.Context(), tab)
|
||||||
|
parentBlockCount = len(tab.BlockIds)
|
||||||
|
}
|
||||||
|
} else if parentORef.OType == waveobj.OType_Block {
|
||||||
|
parentBlock, _ := wstore.DBGet[*waveobj.Block](tx.Context(), parentORef.OID)
|
||||||
|
if parentBlock != nil {
|
||||||
|
parentBlock.SubBlockIds = utilfn.RemoveElemFromSlice(parentBlock.SubBlockIds, blockId)
|
||||||
|
wstore.DBUpdate(tx.Context(), parentBlock)
|
||||||
|
parentBlockCount = len(parentBlock.SubBlockIds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wstore.DBDelete(tx.Context(), waveobj.OType_Block, blockId)
|
||||||
|
return parentBlockCount, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func sendBlockCloseEvent(blockId string) {
|
func sendBlockCloseEvent(blockId string) {
|
||||||
waveEvent := wps.WaveEvent{
|
waveEvent := wps.WaveEvent{
|
||||||
Event: wps.Event_BlockClose,
|
Event: wps.Event_BlockClose,
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/wavetermdev/waveterm/pkg/eventbus"
|
||||||
"github.com/wavetermdev/waveterm/pkg/telemetry"
|
"github.com/wavetermdev/waveterm/pkg/telemetry"
|
||||||
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
|
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
|
||||||
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
||||||
@ -139,6 +140,17 @@ func DeleteTab(ctx context.Context, workspaceId string, tabId string) (string, e
|
|||||||
wstore.DBUpdate(ctx, ws)
|
wstore.DBUpdate(ctx, ws)
|
||||||
wstore.DBDelete(ctx, waveobj.OType_Tab, tabId)
|
wstore.DBDelete(ctx, waveobj.OType_Tab, tabId)
|
||||||
wstore.DBDelete(ctx, waveobj.OType_LayoutState, tab.LayoutState)
|
wstore.DBDelete(ctx, waveobj.OType_LayoutState, tab.LayoutState)
|
||||||
|
|
||||||
|
if newActiveTabId == "" {
|
||||||
|
windowId, err := wstore.DBFindWindowForWorkspaceId(ctx, workspaceId)
|
||||||
|
if err != nil {
|
||||||
|
return newActiveTabId, fmt.Errorf("unable to find window for workspace id %v: %w", workspaceId, err)
|
||||||
|
}
|
||||||
|
err = CloseWindow(ctx, windowId, false)
|
||||||
|
if err != nil {
|
||||||
|
return newActiveTabId, err
|
||||||
|
}
|
||||||
|
}
|
||||||
return newActiveTabId, nil
|
return newActiveTabId, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,6 +170,13 @@ func SetActiveTab(ctx context.Context, workspaceId string, tabId string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SendActiveTabUpdate(ctx context.Context, workspaceId string, newActiveTabId string) {
|
||||||
|
eventbus.SendEventToElectron(eventbus.WSEventType{
|
||||||
|
EventType: eventbus.WSEvent_ElectronUpdateActiveTab,
|
||||||
|
Data: &waveobj.ActiveTabUpdate{WorkspaceId: workspaceId, NewActiveTabId: newActiveTabId},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func UpdateWorkspaceTabIds(ctx context.Context, workspaceId string, tabIds []string) error {
|
func UpdateWorkspaceTabIds(ctx context.Context, workspaceId string, tabIds []string) error {
|
||||||
ws, _ := wstore.DBGet[*waveobj.Workspace](ctx, workspaceId)
|
ws, _ := wstore.DBGet[*waveobj.Workspace](ctx, workspaceId)
|
||||||
if ws == nil {
|
if ws == nil {
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
|
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
|
||||||
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
||||||
)
|
)
|
||||||
@ -32,81 +31,6 @@ func UpdateTabName(ctx context.Context, tabId, name string) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateSubBlock(ctx context.Context, parentBlockId string, blockDef *waveobj.BlockDef) (*waveobj.Block, error) {
|
|
||||||
return WithTxRtn(ctx, func(tx *TxWrap) (*waveobj.Block, error) {
|
|
||||||
parentBlock, _ := DBGet[*waveobj.Block](tx.Context(), parentBlockId)
|
|
||||||
if parentBlock == nil {
|
|
||||||
return nil, fmt.Errorf("parent block not found: %q", parentBlockId)
|
|
||||||
}
|
|
||||||
blockId := uuid.NewString()
|
|
||||||
blockData := &waveobj.Block{
|
|
||||||
OID: blockId,
|
|
||||||
ParentORef: waveobj.MakeORef(waveobj.OType_Block, parentBlockId).String(),
|
|
||||||
BlockDef: blockDef,
|
|
||||||
RuntimeOpts: nil,
|
|
||||||
Meta: blockDef.Meta,
|
|
||||||
}
|
|
||||||
DBInsert(tx.Context(), blockData)
|
|
||||||
parentBlock.SubBlockIds = append(parentBlock.SubBlockIds, blockId)
|
|
||||||
DBUpdate(tx.Context(), parentBlock)
|
|
||||||
return blockData, nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateBlock(ctx context.Context, tabId string, blockDef *waveobj.BlockDef, rtOpts *waveobj.RuntimeOpts) (*waveobj.Block, error) {
|
|
||||||
return WithTxRtn(ctx, func(tx *TxWrap) (*waveobj.Block, error) {
|
|
||||||
tab, _ := DBGet[*waveobj.Tab](tx.Context(), tabId)
|
|
||||||
if tab == nil {
|
|
||||||
return nil, fmt.Errorf("tab not found: %q", tabId)
|
|
||||||
}
|
|
||||||
blockId := uuid.NewString()
|
|
||||||
blockData := &waveobj.Block{
|
|
||||||
OID: blockId,
|
|
||||||
ParentORef: waveobj.MakeORef(waveobj.OType_Tab, tabId).String(),
|
|
||||||
BlockDef: blockDef,
|
|
||||||
RuntimeOpts: rtOpts,
|
|
||||||
Meta: blockDef.Meta,
|
|
||||||
}
|
|
||||||
DBInsert(tx.Context(), blockData)
|
|
||||||
tab.BlockIds = append(tab.BlockIds, blockId)
|
|
||||||
DBUpdate(tx.Context(), tab)
|
|
||||||
return blockData, nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteBlock(ctx context.Context, blockId string) error {
|
|
||||||
return WithTx(ctx, func(tx *TxWrap) error {
|
|
||||||
block, err := DBGet[*waveobj.Block](tx.Context(), blockId)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error getting block: %w", err)
|
|
||||||
}
|
|
||||||
if block == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if len(block.SubBlockIds) > 0 {
|
|
||||||
return fmt.Errorf("block has subblocks, must delete subblocks first")
|
|
||||||
}
|
|
||||||
parentORef := waveobj.ParseORefNoErr(block.ParentORef)
|
|
||||||
if parentORef != nil {
|
|
||||||
if parentORef.OType == waveobj.OType_Tab {
|
|
||||||
tab, _ := DBGet[*waveobj.Tab](tx.Context(), parentORef.OID)
|
|
||||||
if tab != nil {
|
|
||||||
tab.BlockIds = utilfn.RemoveElemFromSlice(tab.BlockIds, blockId)
|
|
||||||
DBUpdate(tx.Context(), tab)
|
|
||||||
}
|
|
||||||
} else if parentORef.OType == waveobj.OType_Block {
|
|
||||||
parentBlock, _ := DBGet[*waveobj.Block](tx.Context(), parentORef.OID)
|
|
||||||
if parentBlock != nil {
|
|
||||||
parentBlock.SubBlockIds = utilfn.RemoveElemFromSlice(parentBlock.SubBlockIds, blockId)
|
|
||||||
DBUpdate(tx.Context(), parentBlock)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DBDelete(tx.Context(), waveobj.OType_Block, blockId)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// must delete all blocks individually first
|
// must delete all blocks individually first
|
||||||
// also deletes LayoutState
|
// also deletes LayoutState
|
||||||
func DeleteTab(ctx context.Context, workspaceId string, tabId string) error {
|
func DeleteTab(ctx context.Context, workspaceId string, tabId string) error {
|
||||||
|
Loading…
Reference in New Issue
Block a user