feat: publish workspace trash lifecycle

This commit is contained in:
2026-07-12 17:11:00 +08:00
parent 5280d245ee
commit e1a6b2af64
6 changed files with 262 additions and 4 deletions
+51 -2
View File
@@ -52,6 +52,8 @@ const maxBrowserInboxCaptures = 100
const workspaceCreatedEventName = "workspace.created"
const workspaceRenamedEventName = "workspace.renamed"
const workspaceTrashedEventName = "workspace.trashed"
const workspaceRestoredEventName = "workspace.restored"
const workspacePurgedEventName = "workspace.purged"
const workspaceSelectedEventName = "workspace.selected"
// App is the main application struct exposed to the Wails frontend.
@@ -2036,6 +2038,47 @@ func (a *App) TrashWorkspace(name string) (workspace.TrashResult, string) {
return result, ""
}
// RestoreWorkspaceTrash restores a trashed workspace and publishes its durable identity.
func (a *App) RestoreWorkspaceTrash(trashID, targetName string) (workspace.Workspace, string) {
if a.workspace == nil {
return workspace.Workspace{}, "workspace not initialized"
}
restored, err := a.workspace.RestoreWorkspaceTrash(trashID, targetName)
if err != nil {
return workspace.Workspace{}, err.Error()
}
a.publishWorkspaceLifecycleEvent(workspaceRestoredEventName, map[string]interface{}{
"operation": "restore",
"workspaceId": restored.ID,
"workspaceRootPath": restored.RootPath,
"workspaceName": restored.Name,
"trashId": trashID,
})
return restored, ""
}
// PurgeWorkspaceTrash permanently removes a trashed workspace and publishes its former identity.
func (a *App) PurgeWorkspaceTrash(trashID string) string {
if a.workspace == nil {
return "workspace not initialized"
}
identity, err := a.workspace.GetWorkspaceTrashIdentity(trashID)
if err != nil {
return err.Error()
}
if err := a.workspace.PurgeWorkspaceTrash(trashID); err != nil {
return err.Error()
}
a.publishWorkspaceLifecycleEvent(workspacePurgedEventName, map[string]interface{}{
"operation": "purge",
"workspaceId": identity.WorkspaceID,
"workspaceRootPath": identity.RootPath,
"workspaceName": identity.RootPath,
"trashId": trashID,
})
return ""
}
// GetWorkspaceMetadata returns metadata or a generic fallback for a workspace.
func (a *App) GetWorkspaceMetadata(name string) (workspace.Metadata, string) {
if a.workspace == nil {
@@ -2069,9 +2112,15 @@ func (a *App) GetCurrentWorkspace() map[string]interface{} {
if err != nil {
return map[string]interface{}{"error": err.Error()}
}
identity, err := a.workspace.GetWorkspaceIdentity(node.Name)
if err != nil {
return map[string]interface{}{"error": err.Error()}
}
return map[string]interface{}{
"name": node.Name,
"rootPath": node.RootPath,
"id": identity.WorkspaceID,
"workspaceId": identity.WorkspaceID,
"name": node.Name,
"rootPath": node.RootPath,
}
}
+44
View File
@@ -1826,6 +1826,10 @@ func TestWorkspaceAPIPublishesLifecycleEvents(t *testing.T) {
if errStr := app.SetCurrentWorkspace("Project"); errStr != "" {
t.Fatalf("SetCurrentWorkspace: %s", errStr)
}
currentID, ok := app.GetCurrentWorkspace()["workspaceId"].(string)
if !ok || currentID == "" {
t.Fatalf("GetCurrentWorkspace workspaceId = %#v", app.GetCurrentWorkspace()["workspaceId"])
}
if errStr := app.RenameWorkspace("Project", "Renamed"); errStr != "" {
t.Fatalf("RenameWorkspace: %s", errStr)
}
@@ -1891,6 +1895,46 @@ func TestWorkspaceIdentityAPIListsAndRepairsDuplicates(t *testing.T) {
}
}
func TestWorkspaceTrashRestoreAndPurgePublishIdentityLifecycle(t *testing.T) {
app, vaultDir := newFilesTestApp(t, []string{"files.read"})
app.workspace = workspace.NewManager(vaultDir)
app.eventBus = events.NewBus()
if err := app.workspace.Load(); err != nil {
t.Fatal(err)
}
received := map[string]map[string]interface{}{}
for _, eventName := range []string{"workspace.restored", "workspace.purged"} {
name := eventName
app.eventBus.Subscribe(name, func(event events.Event) {
received[name], _ = event.Payload.(map[string]interface{})
})
}
if _, errStr := app.CreateWorkspace("Client", "default"); errStr != "" {
t.Fatalf("CreateWorkspace: %s", errStr)
}
trashed, errStr := app.TrashWorkspace("Client")
if errStr != "" {
t.Fatalf("TrashWorkspace: %s", errStr)
}
restored, errStr := app.RestoreWorkspaceTrash(trashed.TrashID, "Client-Restored")
if errStr != "" {
t.Fatalf("RestoreWorkspaceTrash: %s", errStr)
}
if got := received["workspace.restored"]["workspaceId"]; got != restored.ID {
t.Fatalf("restored workspaceId = %#v, want %q", got, restored.ID)
}
trashed, errStr = app.TrashWorkspace("Client-Restored")
if errStr != "" {
t.Fatalf("TrashWorkspace restored: %s", errStr)
}
if errStr := app.PurgeWorkspaceTrash(trashed.TrashID); errStr != "" {
t.Fatalf("PurgeWorkspaceTrash: %s", errStr)
}
if got := received["workspace.purged"]["workspaceId"]; got != restored.ID {
t.Fatalf("purged workspaceId = %#v, want %q", got, restored.ID)
}
}
func TestMoveWorkspaceNodeCompatibilityIsUnsupported(t *testing.T) {
app, vaultDir := newFilesTestApp(t, []string{"files.read"})
app.workspace = workspace.NewManager(vaultDir)