feat: expose trash in gui
This commit is contained in:
parent
035f877280
commit
cc83cd3476
|
|
@ -19,6 +19,7 @@ func (a *App) ListSystemViews() []SystemViewDTO {
|
||||||
return []SystemViewDTO{
|
return []SystemViewDTO{
|
||||||
{ID: "today", Label: i18n.TF("ru", "nav.today")},
|
{ID: "today", Label: i18n.TF("ru", "nav.today")},
|
||||||
{ID: "inbox", Label: i18n.TF("ru", "nav.inbox")},
|
{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: "journal", Label: i18n.TF("ru", "nav.journal")},
|
||||||
{ID: "activity", Label: i18n.TF("ru", "nav.activity")},
|
{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;
|
background: #13131f;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script type="module" crossorigin src="/assets/main-Wpp0QvRu.js"></script>
|
<script type="module" crossorigin src="/assets/main-4y6wyoK9.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/main-xwXesvcm.css">
|
<link rel="stylesheet" crossorigin href="/assets/main-es_E5H-H.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -92,6 +92,7 @@
|
||||||
let suggestions = []
|
let suggestions = []
|
||||||
let suggestionCount = 0
|
let suggestionCount = 0
|
||||||
let inboxNodes = []
|
let inboxNodes = []
|
||||||
|
let trashInfo = null
|
||||||
let showCreateNode = false
|
let showCreateNode = false
|
||||||
let newNodeTitle = ''
|
let newNodeTitle = ''
|
||||||
let createInNode = null
|
let createInNode = null
|
||||||
|
|
@ -230,6 +231,7 @@
|
||||||
worklog = []
|
worklog = []
|
||||||
suggestions = []
|
suggestions = []
|
||||||
inboxNodes = []
|
inboxNodes = []
|
||||||
|
trashInfo = null
|
||||||
showCreateNode = false
|
showCreateNode = false
|
||||||
error = ''
|
error = ''
|
||||||
todayDashboard = null
|
todayDashboard = null
|
||||||
|
|
@ -245,6 +247,8 @@
|
||||||
suggestionCount = suggestions.length
|
suggestionCount = suggestions.length
|
||||||
} else if (id === 'inbox') {
|
} else if (id === 'inbox') {
|
||||||
inboxNodes = await wailsCall('ListInboxNodes') || []
|
inboxNodes = await wailsCall('ListInboxNodes') || []
|
||||||
|
} else if (id === 'trash') {
|
||||||
|
trashInfo = await wailsCall('ListTrash') || { nodes: [], entries: [], trashPath: '' }
|
||||||
} else if (id === 'journal') {
|
} else if (id === 'journal') {
|
||||||
await loadJournal()
|
await loadJournal()
|
||||||
} else if (id === 'activity') {
|
} else if (id === 'activity') {
|
||||||
|
|
@ -256,6 +260,7 @@
|
||||||
error = String(e)
|
error = String(e)
|
||||||
todayDashboard = { cases: [] }
|
todayDashboard = { cases: [] }
|
||||||
inboxNodes = []
|
inboxNodes = []
|
||||||
|
trashInfo = null
|
||||||
activityFeed = []
|
activityFeed = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2023,6 +2028,57 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{:else if selectedSection === 'trash'}
|
||||||
|
<div class="trash-screen">
|
||||||
|
<div class="trash-header">
|
||||||
|
<div>
|
||||||
|
<h2>{t('nav.trash')}</h2>
|
||||||
|
<p>{trashInfo?.trashPath || ''}</p>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-sm" on:click={() => wailsCall('OpenTrashFolder')}>{t('trash.openFolder')}</button>
|
||||||
|
</div>
|
||||||
|
{#if !trashInfo || ((trashInfo.nodes || []).length === 0 && (trashInfo.entries || []).length === 0)}
|
||||||
|
<div class="empty-state">
|
||||||
|
<p>{t('trash.empty')}</p>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="trash-grid">
|
||||||
|
<section class="trash-section">
|
||||||
|
<h3>{t('trash.deletedNodes')}</h3>
|
||||||
|
{#if (trashInfo.nodes || []).length === 0}
|
||||||
|
<p class="trash-empty-line">{t('common.empty')}</p>
|
||||||
|
{:else}
|
||||||
|
{#each trashInfo.nodes as node}
|
||||||
|
<div class="trash-row">
|
||||||
|
<div class="trash-row-main">
|
||||||
|
<span class="trash-row-title">{node.title}</span>
|
||||||
|
<span class="trash-row-meta">{nodeKindLabel(node.type)} · {formatDate(node.deletedAt)}</span>
|
||||||
|
</div>
|
||||||
|
{#if node.fsPath}<span class="trash-row-path">{node.fsPath}</span>{/if}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</section>
|
||||||
|
<section class="trash-section">
|
||||||
|
<h3>{t('trash.physicalEntries')}</h3>
|
||||||
|
{#if (trashInfo.entries || []).length === 0}
|
||||||
|
<p class="trash-empty-line">{t('common.empty')}</p>
|
||||||
|
{:else}
|
||||||
|
{#each trashInfo.entries as entry}
|
||||||
|
<div class="trash-row">
|
||||||
|
<div class="trash-row-main">
|
||||||
|
<span class="trash-row-title">{entry.name}</span>
|
||||||
|
<span class="trash-row-meta">{entry.isDir ? t('mime.folder') : t('mime.file')} · {formatDate(entry.modifiedAt)}</span>
|
||||||
|
</div>
|
||||||
|
<span class="trash-row-path">{entry.path}</span>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
{:else if selectedSection === 'journal'}
|
{:else if selectedSection === 'journal'}
|
||||||
<div class="journal-screen">
|
<div class="journal-screen">
|
||||||
<div class="journal-header">
|
<div class="journal-header">
|
||||||
|
|
@ -2742,6 +2798,21 @@
|
||||||
.inbox-item-meta { color: #8888a0; font-size: 12px; }
|
.inbox-item-meta { color: #8888a0; font-size: 12px; }
|
||||||
.inbox-item-actions { display: flex; gap: 8px; flex-shrink: 0; }
|
.inbox-item-actions { display: flex; gap: 8px; flex-shrink: 0; }
|
||||||
|
|
||||||
|
/* Trash screen */
|
||||||
|
.trash-screen { padding: 24px; overflow-y: auto; flex: 1; }
|
||||||
|
.trash-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; margin-bottom: 20px; }
|
||||||
|
.trash-header h2 { margin: 0 0 6px; }
|
||||||
|
.trash-header p { margin: 0; color: #8888a0; font-size: 12px; }
|
||||||
|
.trash-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; }
|
||||||
|
.trash-section { min-width: 0; }
|
||||||
|
.trash-section h3 { margin: 0 0 10px; font-size: 13px; color: #a5b4fc; text-transform: uppercase; letter-spacing: 0.3px; }
|
||||||
|
.trash-row { padding: 10px 12px; border: 1px solid #2a2a3c; border-radius: 8px; background: #1a1a28; margin-bottom: 8px; }
|
||||||
|
.trash-row-main { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; }
|
||||||
|
.trash-row-title { min-width: 0; color: #e4e4ef; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.trash-row-meta { flex-shrink: 0; color: #8888a0; font-size: 12px; }
|
||||||
|
.trash-row-path { display: block; margin-top: 4px; color: #707088; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.trash-empty-line { color: #8888a0; font-size: 13px; margin: 0; }
|
||||||
|
|
||||||
/* Journal screen */
|
/* Journal screen */
|
||||||
.journal-screen { padding: 24px; overflow-y: auto; flex: 1; }
|
.journal-screen { padding: 24px; overflow-y: auto; flex: 1; }
|
||||||
.journal-header { margin-bottom: 24px; }
|
.journal-header { margin-bottom: 24px; }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
export default {
|
export default {
|
||||||
'nav.today': 'Today',
|
'nav.today': 'Today',
|
||||||
'nav.inbox': 'Inbox',
|
'nav.inbox': 'Inbox',
|
||||||
|
'nav.trash': 'Trash',
|
||||||
'nav.activity': 'Activity',
|
'nav.activity': 'Activity',
|
||||||
'nav.journal': 'Journal',
|
'nav.journal': 'Journal',
|
||||||
'nav.clients': 'Clients',
|
'nav.clients': 'Clients',
|
||||||
|
|
@ -25,6 +26,10 @@ export default {
|
||||||
'nav.moveToRoot': 'Move to root',
|
'nav.moveToRoot': 'Move to root',
|
||||||
'inbox.subtitle': 'Root items without an assigned section',
|
'inbox.subtitle': 'Root items without an assigned section',
|
||||||
'inbox.empty': 'No unprocessed items',
|
'inbox.empty': 'No unprocessed items',
|
||||||
|
'trash.openFolder': 'Open trash folder',
|
||||||
|
'trash.empty': 'Trash is empty',
|
||||||
|
'trash.deletedNodes': 'Deleted items',
|
||||||
|
'trash.physicalEntries': 'Files in .verstak/trash',
|
||||||
'tab.overview': 'Overview',
|
'tab.overview': 'Overview',
|
||||||
'tab.notes': 'Notes',
|
'tab.notes': 'Notes',
|
||||||
'tab.files': 'Files',
|
'tab.files': 'Files',
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
export default {
|
export default {
|
||||||
'nav.today': 'Сегодня',
|
'nav.today': 'Сегодня',
|
||||||
'nav.inbox': 'Неразобранное',
|
'nav.inbox': 'Неразобранное',
|
||||||
|
'nav.trash': 'Корзина',
|
||||||
'nav.activity': 'Активность',
|
'nav.activity': 'Активность',
|
||||||
'nav.journal': 'Журнал',
|
'nav.journal': 'Журнал',
|
||||||
'nav.clients': 'Клиенты',
|
'nav.clients': 'Клиенты',
|
||||||
|
|
@ -27,6 +28,11 @@ export default {
|
||||||
'inbox.subtitle': 'Корневые элементы без назначенного раздела',
|
'inbox.subtitle': 'Корневые элементы без назначенного раздела',
|
||||||
'inbox.empty': 'Неразобранных элементов нет',
|
'inbox.empty': 'Неразобранных элементов нет',
|
||||||
|
|
||||||
|
'trash.openFolder': 'Открыть папку корзины',
|
||||||
|
'trash.empty': 'Корзина пуста',
|
||||||
|
'trash.deletedNodes': 'Удаленные элементы',
|
||||||
|
'trash.physicalEntries': 'Файлы в .verstak/trash',
|
||||||
|
|
||||||
'tab.overview': 'Обзор',
|
'tab.overview': 'Обзор',
|
||||||
'tab.notes': 'Заметки',
|
'tab.notes': 'Заметки',
|
||||||
'tab.files': 'Файлы',
|
'tab.files': 'Файлы',
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,14 @@ export function ListInboxNodes() {
|
||||||
return window['go']['main']['App']['ListInboxNodes']();
|
return window['go']['main']['App']['ListInboxNodes']();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ListTrash() {
|
||||||
|
return window['go']['main']['App']['ListTrash']();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function OpenTrashFolder() {
|
||||||
|
return window['go']['main']['App']['OpenTrashFolder']();
|
||||||
|
}
|
||||||
|
|
||||||
export function CreateWorklog(arg1, arg2, arg3) {
|
export function CreateWorklog(arg1, arg2, arg3) {
|
||||||
return window['go']['main']['App']['CreateWorklog'](arg1, arg2, arg3);
|
return window['go']['main']['App']['CreateWorklog'](arg1, arg2, arg3);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,19 @@ func (r *Repository) ListInboxRoots(includeDeleted bool) ([]Node, error) {
|
||||||
return scanNodes(rows)
|
return scanNodes(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListDeleted returns soft-deleted nodes, newest deleted first.
|
||||||
|
func (r *Repository) ListDeleted() ([]Node, error) {
|
||||||
|
rows, err := r.db.Query(
|
||||||
|
`SELECT ` + nodeColumns + ` FROM nodes
|
||||||
|
WHERE deleted_at IS NOT NULL
|
||||||
|
ORDER BY deleted_at DESC, updated_at DESC, title`)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
return scanNodes(rows)
|
||||||
|
}
|
||||||
|
|
||||||
// CountChildren returns the number of non-deleted children for a parent,
|
// CountChildren returns the number of non-deleted children for a parent,
|
||||||
// optionally filtered by one or more types.
|
// optionally filtered by one or more types.
|
||||||
func (r *Repository) CountChildren(parentID string, types ...string) (int, error) {
|
func (r *Repository) CountChildren(parentID string, types ...string) (int, error) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"nav.today": "Today",
|
"nav.today": "Today",
|
||||||
"nav.inbox": "Inbox",
|
"nav.inbox": "Inbox",
|
||||||
|
"nav.trash": "Trash",
|
||||||
"nav.activity": "Activity",
|
"nav.activity": "Activity",
|
||||||
"nav.clients": "Clients",
|
"nav.clients": "Clients",
|
||||||
"nav.projects": "Projects",
|
"nav.projects": "Projects",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"nav.today": "Сегодня",
|
"nav.today": "Сегодня",
|
||||||
"nav.inbox": "Неразобранное",
|
"nav.inbox": "Неразобранное",
|
||||||
|
"nav.trash": "Корзина",
|
||||||
"nav.activity": "Активность",
|
"nav.activity": "Активность",
|
||||||
"nav.clients": "Клиенты",
|
"nav.clients": "Клиенты",
|
||||||
"nav.projects": "Проекты",
|
"nav.projects": "Проекты",
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,10 @@ async function runReadyScenario(cdp, url) {
|
||||||
await clickText(cdp, '.inbox-item-actions .btn', 'Открыть')
|
await clickText(cdp, '.inbox-item-actions .btn', 'Открыть')
|
||||||
await assertText(cdp, 'Inbox Smoke Item', 'inbox: item opens from list')
|
await assertText(cdp, 'Inbox Smoke Item', 'inbox: item opens from list')
|
||||||
|
|
||||||
|
await clickText(cdp, '.nav-item', 'Корзина')
|
||||||
|
await assertText(cdp, 'Trash Smoke Folder', 'trash: deleted node visible')
|
||||||
|
await assertText(cdp, 'node-trash_Trash-Smoke-Folder', 'trash: physical entry visible')
|
||||||
|
|
||||||
await clickText(cdp, '.tree-label', 'Smoke Project')
|
await clickText(cdp, '.tree-label', 'Smoke Project')
|
||||||
await waitForSelector(cdp, '.tabs')
|
await waitForSelector(cdp, '.tabs')
|
||||||
await assertText(cdp, 'Smoke Project', 'node: selected project visible')
|
await assertText(cdp, 'Smoke Project', 'node: selected project visible')
|
||||||
|
|
@ -673,12 +677,19 @@ function wailsMockSource() {
|
||||||
ListSystemViews: async () => [
|
ListSystemViews: async () => [
|
||||||
{ id: 'today', label: 'Сегодня' },
|
{ id: 'today', label: 'Сегодня' },
|
||||||
{ id: 'inbox', label: 'Неразобранное' },
|
{ id: 'inbox', label: 'Неразобранное' },
|
||||||
|
{ id: 'trash', label: 'Корзина' },
|
||||||
{ id: 'activity', label: 'Активность' },
|
{ id: 'activity', label: 'Активность' },
|
||||||
{ id: 'journal', label: 'Журнал' },
|
{ id: 'journal', label: 'Журнал' },
|
||||||
],
|
],
|
||||||
ListWorkspaceTree: async () => clone(state.nodes),
|
ListWorkspaceTree: async () => clone(state.nodes),
|
||||||
ListWorkspaceChildren: async (id) => clone(childrenOf(id)),
|
ListWorkspaceChildren: async (id) => clone(childrenOf(id)),
|
||||||
ListInboxNodes: async () => clone(state.nodes.filter((node) => !node.parent_id && (!node.section || node.section === 'inbox'))),
|
ListInboxNodes: async () => clone(state.nodes.filter((node) => !node.parent_id && (!node.section || node.section === 'inbox'))),
|
||||||
|
ListTrash: async () => clone({
|
||||||
|
trashPath: '/tmp/verstak-smoke-vault/.verstak/trash',
|
||||||
|
nodes: [{ id: 'node-trash', title: 'Trash Smoke Folder', type: 'folder', fsPath: 'Trash Smoke Folder', deletedAt: now }],
|
||||||
|
entries: [{ name: 'node-trash_Trash-Smoke-Folder', path: '/tmp/verstak-smoke-vault/.verstak/trash/node-trash_Trash-Smoke-Folder', isDir: true, size: 0, modifiedAt: now }],
|
||||||
|
}),
|
||||||
|
OpenTrashFolder: async () => true,
|
||||||
ListEnabledTemplates: async () => clone(templates),
|
ListEnabledTemplates: async () => clone(templates),
|
||||||
AllTemplates: async () => clone(templates),
|
AllTemplates: async () => clone(templates),
|
||||||
SetTemplateEnabled: async () => true,
|
SetTemplateEnabled: async () => true,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue