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,
|
||||
getWaveWindowById,
|
||||
getWaveWindowByWebContentsId,
|
||||
getWaveWindowByWorkspaceId,
|
||||
WaveBrowserWindow,
|
||||
} from "./emain-window";
|
||||
import { ElectronWshClient, initElectronWshClient } from "./emain-wsh";
|
||||
@ -125,6 +126,14 @@ function handleWSEvent(evtMsg: WSEventType) {
|
||||
if (ww != null) {
|
||||
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 {
|
||||
console.log("unhandled electron ws eventtype", evtMsg.eventtype);
|
||||
}
|
||||
|
@ -13,9 +13,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
WSEvent_ElectronNewWindow = "electron:newwindow"
|
||||
WSEvent_ElectronCloseWindow = "electron:closewindow"
|
||||
WSEvent_Rpc = "rpc"
|
||||
WSEvent_ElectronNewWindow = "electron:newwindow"
|
||||
WSEvent_ElectronCloseWindow = "electron:closewindow"
|
||||
WSEvent_ElectronUpdateActiveTab = "electron:updateactivetab"
|
||||
WSEvent_Rpc = "rpc"
|
||||
)
|
||||
|
||||
type WSEventType struct {
|
||||
|
@ -175,15 +175,7 @@ func (svc *WorkspaceService) CloseTab(ctx context.Context, workspaceId string, t
|
||||
}
|
||||
rtn := &CloseTabRtnType{}
|
||||
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
|
||||
err = wcore.CloseWindow(ctx, windowId, fromElectron)
|
||||
if err != nil {
|
||||
return rtn, nil, err
|
||||
}
|
||||
} else {
|
||||
rtn.NewActiveTabId = newActiveTabId
|
||||
}
|
||||
|
@ -159,6 +159,11 @@ type WorkspaceListEntry struct {
|
||||
|
||||
type WorkspaceList []*WorkspaceListEntry
|
||||
|
||||
type ActiveTabUpdate struct {
|
||||
WorkspaceId string `json:"workspaceid"`
|
||||
NewActiveTabId string `json:"newactivetabid"`
|
||||
}
|
||||
|
||||
type Workspace struct {
|
||||
OID string `json:"oid"`
|
||||
Version int `json:"version"`
|
||||
|
@ -3,11 +3,14 @@ package wcore
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/wavetermdev/waveterm/pkg/blockcontroller"
|
||||
"github.com/wavetermdev/waveterm/pkg/panichandler"
|
||||
"github.com/wavetermdev/waveterm/pkg/telemetry"
|
||||
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
|
||||
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
||||
"github.com/wavetermdev/waveterm/pkg/wps"
|
||||
"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, "") == "" {
|
||||
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 {
|
||||
return nil, fmt.Errorf("error creating sub block: %w", err)
|
||||
}
|
||||
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) {
|
||||
if blockDef == 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, "") == "" {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
block, err := wstore.DBMustGet[*waveobj.Block](ctx, blockId)
|
||||
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 {
|
||||
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)
|
||||
sendBlockCloseEvent(blockId)
|
||||
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) {
|
||||
waveEvent := wps.WaveEvent{
|
||||
Event: wps.Event_BlockClose,
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/wavetermdev/waveterm/pkg/eventbus"
|
||||
"github.com/wavetermdev/waveterm/pkg/telemetry"
|
||||
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
|
||||
"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.DBDelete(ctx, waveobj.OType_Tab, tabId)
|
||||
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
|
||||
}
|
||||
|
||||
@ -158,6 +170,13 @@ func SetActiveTab(ctx context.Context, workspaceId string, tabId string) error {
|
||||
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 {
|
||||
ws, _ := wstore.DBGet[*waveobj.Workspace](ctx, workspaceId)
|
||||
if ws == nil {
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
|
||||
"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
|
||||
// also deletes LayoutState
|
||||
func DeleteTab(ctx context.Context, workspaceId string, tabId string) error {
|
||||
|
Loading…
Reference in New Issue
Block a user