feat: expose trash in gui
This commit is contained in:
@@ -19,6 +19,7 @@ func (a *App) ListSystemViews() []SystemViewDTO {
|
||||
return []SystemViewDTO{
|
||||
{ID: "today", Label: i18n.TF("ru", "nav.today")},
|
||||
{ID: "inbox", Label: i18n.TF("ru", "nav.inbox")},
|
||||
{ID: "trash", Label: i18n.TF("ru", "nav.trash")},
|
||||
{ID: "journal", Label: i18n.TF("ru", "nav.journal")},
|
||||
{ID: "activity", Label: i18n.TF("ru", "nav.activity")},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TrashDTO struct {
|
||||
TrashPath string `json:"trashPath"`
|
||||
Nodes []TrashNodeDTO `json:"nodes"`
|
||||
Entries []TrashEntryDTO `json:"entries"`
|
||||
}
|
||||
|
||||
type TrashNodeDTO struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Type string `json:"type"`
|
||||
FsPath string `json:"fsPath"`
|
||||
DeletedAt string `json:"deletedAt"`
|
||||
}
|
||||
|
||||
type TrashEntryDTO struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
IsDir bool `json:"isDir"`
|
||||
Size int64 `json:"size"`
|
||||
ModifiedAt string `json:"modifiedAt"`
|
||||
}
|
||||
|
||||
func (a *App) ListTrash() (*TrashDTO, error) {
|
||||
if err := a.requireVault(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
trashPath := filepath.Join(a.vault, ".verstak", "trash")
|
||||
|
||||
deleted, err := a.nodes.ListDeleted()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes := make([]TrashNodeDTO, 0, len(deleted))
|
||||
for _, n := range deleted {
|
||||
deletedAt := ""
|
||||
if n.DeletedAt != nil {
|
||||
deletedAt = n.DeletedAt.Format(time.RFC3339)
|
||||
}
|
||||
nodes = append(nodes, TrashNodeDTO{
|
||||
ID: n.ID,
|
||||
Title: n.Title,
|
||||
Type: n.Type,
|
||||
FsPath: n.FsPath,
|
||||
DeletedAt: deletedAt,
|
||||
})
|
||||
}
|
||||
|
||||
entries, err := listTrashEntries(trashPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &TrashDTO{TrashPath: trashPath, Nodes: nodes, Entries: entries}, nil
|
||||
}
|
||||
|
||||
func listTrashEntries(trashPath string) ([]TrashEntryDTO, error) {
|
||||
if err := os.MkdirAll(trashPath, 0o750); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dirEntries, err := os.ReadDir(trashPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]TrashEntryDTO, 0, len(dirEntries))
|
||||
for _, entry := range dirEntries {
|
||||
info, err := entry.Info()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
out = append(out, TrashEntryDTO{
|
||||
Name: entry.Name(),
|
||||
Path: filepath.Join(trashPath, entry.Name()),
|
||||
IsDir: entry.IsDir(),
|
||||
Size: info.Size(),
|
||||
ModifiedAt: info.ModTime().UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (a *App) OpenTrashFolder() error {
|
||||
if err := a.requireVault(); err != nil {
|
||||
return err
|
||||
}
|
||||
trashPath := filepath.Join(a.vault, ".verstak", "trash")
|
||||
if err := os.MkdirAll(trashPath, 0o750); err != nil {
|
||||
return err
|
||||
}
|
||||
return exec.Command("xdg-open", trashPath).Run()
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -16,8 +16,8 @@
|
||||
background: #13131f;
|
||||
}
|
||||
</style>
|
||||
<script type="module" crossorigin src="/assets/main-Wpp0QvRu.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/main-xwXesvcm.css">
|
||||
<script type="module" crossorigin src="/assets/main-4y6wyoK9.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/main-es_E5H-H.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestListTrashShowsDeletedNodesAndPhysicalEntries(t *testing.T) {
|
||||
app, _ := setupTestApp(t)
|
||||
|
||||
n, err := app.CreateNodeFromTemplate("", "Trash Me", "folder.default")
|
||||
if err != nil {
|
||||
t.Fatalf("create node: %v", err)
|
||||
}
|
||||
if err := app.DeleteNode(n.ID); err != nil {
|
||||
t.Fatalf("DeleteNode: %v", err)
|
||||
}
|
||||
|
||||
trash, err := app.ListTrash()
|
||||
if err != nil {
|
||||
t.Fatalf("ListTrash: %v", err)
|
||||
}
|
||||
|
||||
var foundNode bool
|
||||
for _, node := range trash.Nodes {
|
||||
if node.ID == n.ID && node.Title == "Trash Me" && node.DeletedAt != "" {
|
||||
foundNode = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundNode {
|
||||
t.Fatalf("deleted node %s missing from trash nodes: %#v", n.ID, trash.Nodes)
|
||||
}
|
||||
|
||||
var foundPhysical bool
|
||||
for _, entry := range trash.Entries {
|
||||
if strings.Contains(entry.Name, n.ID) && entry.IsDir {
|
||||
foundPhysical = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundPhysical {
|
||||
t.Fatalf("physical trash entry for %s missing: %#v", n.ID, trash.Entries)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user