Skip to content

feat(documents): single-call tree, public docs by user, create-in-folder, move to folder - G24 #52

Description

@Adron

Summary

Four Documents routes the client does not build, plus two authoring affordances the web has and macOS lacks.

Routes

Route Auth What it unlocks
GET /api/documents/tree sync-token The whole sidebar in one call — today macOS assembles it from several
GET /api/users/{username}/documents public Public documents by user — the profile page has no documents tab
POST /api/documents/folders/{id}/documents sync-token, subscriber Create a document directly in a folder
GET /api/documents/invite/{token} public Invite landing (the accept half is session-only — see the caveat)

Live shapes (2026-09-07, Bearer)

GET /api/documents/tree:

{"folders":[{"id":"2b776197-…","name":"One-Folder","parentId":null,
             "documents":[{"id":"effbcb05-…","title":"a-single-doc",
                           "relativePath":"a-single-doc.md","isPublic":false}]},
            {"id":"b6a8f347-…","name":"_templates","parentId":null,
             "documents":[{"id":"c283ee0b-…","title":"Bespoke_Template!!!","…":"…"},
                          {"id":"c20f9ba6-…","title":"Recipe","…":"…"}]},
            {"id":"3775f95a-…","name":"emptiness","parentId":null,"documents":[]}],
 "rootDocuments":[{"id":"60668d66-…","title":"a-root-doc","…":"…"}]}

Note it returns the _templates folder inline — the same folder the template picker uses.

GET /api/users/adron/documents:

{"documents":[{"id":"026028c0-…","title":"Railroad Apps to Build for Fun",
               "folderId":null,"relativePath":"railroad-passenger-seating.md",
               "createdAt":"…","updatedAt":"…"}],
 "folders":[]}

⚠️ Backend constraint on the invite pair

POST /api/documents/invite/{token} and POST /api/documents/shared/{token} are both x-auth-type: session in the live spec — a Bearer-only client cannot claim. Deliver the landing view and hand the accept step to the browser; the backend ask is filed separately. Accepting is documented as always free, so no entitlement gate on the landing view.

Two authoring gaps

Move a document between folders. /help/documents: "Open the document, use the Move to folder option (or the document settings menu), and choose a destination; select No folder (root) to remove it from all folders." macOS has folder create / rename / delete (FolderTreeViewModel.swift:114/136/169) but no move-document action.

Template seed defaults. /help/documents: "Templates are stored in a special folder named _templates, created automatically the first time you open the template picker. A Seed defaults action fills it with a starter set (Recipe, Meeting Notes, Release Notes, and more)." macOS ships DocumentTemplatePickerView + ServerTemplatesViewModel — verify whether the seed-defaults action is present before building it; if it is, this bullet closes as already-done.

Deliberately deferred

POST / DELETE /api/documents/{id}/presence — the live-cursor heartbeat. It is a collaborative-editing feature with a continuous polling cost, and multi-user simultaneous editing is not currently a goal. Not in scope for this issue. (This was G13; it is no longer backend-blocked, just low-value.)

Division of labor

Kit

  • Documents.tree(), Documents.publicDocuments(username:), Documents.createInFolder(folderID:…), Documents.invite(token:) + DTOs
  • Contract tests for the tree shape (folders carry nested documents; root documents are a sibling array — do not flatten)

Domain

  • Fold the tree call into DocumentsService as the sidebar's single source, keeping the existing SWR cache semantics — the tree replaces several calls, so the cache key and TTL need revisiting, not just the fetch.
  • A move-document-to-folder method (including to root).
  • Public-documents-by-user projection, shared with the My Profile issue so it is built once.

App

  • DocumentsSidebarView paints from the single tree call.
  • Move to folder in the editor's settings menu and the document list's context menu, with "No folder (root)".
  • New Document inside a folder creates it there (today's flow depends on the selected folder — verify and use the explicit route).
  • A documents column on the profile page (shared with the My Profile issue).
  • Invite-landing view, ending in "Accept in your browser".

Tests

  • happy — tree renders nested folders and root documents; move relocates a document
  • invalid — move to a folder that no longer exists
  • upstream-failure — the tree call fails → the sidebar paints from cache and shows a revalidation error, not an empty tree
  • boundary — an empty folder; a folder holding only subfolders; the _templates folder is not offered as a normal destination

Acceptance criteria

  • The sidebar is built from one call.
  • A document can be moved between folders and to root from the Mac.
  • Full E2E gate green.

Notes

work-consolidation.md tracks this as G24. Size M.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestparityWeb-parity gap with the InterlinedList web app

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions