verstak/cmd/verstak-gui/bindings_notes.go

143 lines
3.6 KiB
Go

package main
import (
"time"
"verstak/internal/core/activity"
"verstak/internal/core/notes"
"verstak/internal/core/nodes"
syncsvc "verstak/internal/core/sync"
)
func (a *App) ListNotes(nodeID string) ([]NodeDTO, error) {
if err := a.requireVault(); err != nil {
return nil, err
}
// Return empty for non-notes-capable parents.
if !a.notes.SupportsNotes(nodeID) {
return []NodeDTO{}, nil
}
// Try the canonical layout: notes live under a "Notes" folder.
// Also fall back to direct TypeNote children so that notes placed
// directly by AssignInboxNode / old layout are still visible.
notesFolder := a.notes.FindNotesFolder(nodeID)
// Direct children (old layout / inbox-assigned notes)
directChildren, err := a.nodes.ListChildren(nodeID, false)
if err != nil {
return nil, err
}
seen := map[string]bool{}
var result []NodeDTO
processNote := func(n nodes.Node) {
if n.Type == nodes.TypeNote && !seen[n.ID] {
seen[n.ID] = true
result = append(result, toNodeDTO(&n))
}
}
if notesFolder != nil {
// Canonical layout: notes inside the Notes folder
notesChildren, err := a.nodes.ListChildren(notesFolder.ID, false)
if err != nil {
return nil, err
}
for i := range notesChildren {
processNote(notesChildren[i])
}
}
// Also include direct TypeNote children (old / inbox-assigned notes)
for i := range directChildren {
processNote(directChildren[i])
}
// Trigger repair in background for old-layout notes
go func() {
if _, err := a.notes.RepairNotesLayout(); err != nil {
// log only
}
}()
return result, nil
}
func (a *App) RepairNotesLayout() (*notes.RepairResult, error) {
if err := a.requireVault(); err != nil {
return nil, err
}
return a.notes.RepairNotesLayout()
}
func (a *App) CreateNote(parentID, title string) (*NodeDTO, error) {
if err := a.requireVault(); err != nil {
return nil, err
}
node, fileRec, err := a.notes.Create(parentID, title, "")
if err != nil {
return nil, err
}
_ = a.activity.Record(parentID, activity.TargetNote, node.ID, "", activity.TypeNoteCreated, title, "")
_ = a.sync.RecordOp(syncsvc.EntityNote, node.ID, syncsvc.OpCreate, notePayload(node, fileRec, ""))
dto := toNodeDTO(node)
return &dto, nil
}
func (a *App) ReadNote(noteID string) (string, error) {
if err := a.requireVault(); err != nil {
return "", err
}
return a.notes.Read(noteID)
}
func (a *App) SaveNote(noteID, content string) error {
if err := a.requireVault(); err != nil {
return err
}
if err := a.notes.Save(noteID, content); err != nil {
return err
}
if n, err := a.nodes.GetActive(noteID); err == nil {
pid := ""
if n.ParentID != nil {
pid = *n.ParentID
}
_ = a.activity.Record(pid, activity.TargetNote, noteID, "", activity.TypeNoteUpdated, n.Title, "")
_ = a.sync.RecordOp(syncsvc.EntityNote, noteID, syncsvc.OpUpdate, map[string]interface{}{
"node_id": noteID,
"content": content,
"updated_at": time.Now().UTC().Format(time.RFC3339),
})
}
return nil
}
func (a *App) RenameNote(noteID, newTitle string) error {
if err := a.requireVault(); err != nil {
return err
}
return a.notes.Rename(noteID, newTitle)
}
func (a *App) DeleteNote(noteID string) error {
if err := a.requireVault(); err != nil {
return err
}
// Record activity and sync op before delete (need node info).
n, _ := a.nodes.GetActive(noteID)
pid := ""
if n != nil && n.ParentID != nil {
pid = *n.ParentID
}
title := ""
if n != nil {
title = n.Title
}
_ = a.activity.Record(pid, activity.TargetNote, noteID, "", activity.TypeNoteDeleted, title, "")
_ = a.sync.RecordOp(syncsvc.EntityNote, noteID, syncsvc.OpDelete, nil)
return a.notes.Delete(noteID)
}