feat(todo,app): files attached to tasks and comments

Halkyon's ADR 0043, D1, D3, D5 and D8: any file up to 100 MB, attached to
a task's description or to a comment, sealed as everything of Todo's.

Server: the attachment table, synced and sealed as the others, with the
sealed file's hash readable for the store; todoFiles hands halkyon_base
its rows, readable by their space's members, and takes sealed files
alone; the files no attachment names go daily. Its tests hold downloads
to the row's space, refuse a readable file and prune what no live row
names. Migration attachments: CREATE TABLE "attachment" with its indexes
and the foreign key to its space, cascading as every synced table's.

App: AttachmentRepository seals each file for its row, keeps it sealed
and opened in the person's folder, uploads it after each sync and fetches
it as it shows, both held back by Without files on mobile data but for a
file tapped. Under the description and a comment's text, a row of them:
pictures as thumbnails opening full-size, other files as chips opening in
the system's app, else saved; the paperclip beside the description and in the comment's
composer adds them, the composer holding them until it is sent. A task deleted takes its files
and its comments' along, a comment its own.

halkyon_app opens a file in the system's app (FileOpener): on Android
through its plugin and a FileProvider of its own class, sharing the
cache's halkyon_opened/ folder alone; on the desktop as a file: URI.

Whats-New: Attach files to a task's description or a comment with the paperclip: pictures show as thumbnails, other files open in their app.
Whats-New-De: Hänge mit der Büroklammer Dateien an die Beschreibung einer Aufgabe oder an einen Kommentar: Bilder erscheinen als Vorschau, andere Dateien öffnen sich in ihrer App.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-11 01:17:22 +01:00
commit 3ca4b4308d
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -2,8 +2,8 @@
- **Status:** decided 10 Nov 2026 by the developer: D1 and D2 are their own requests,
D3 their choice over the agent's recommendation (20 MB), D4 and D5 the agent's
recommendations, which they chose. D2, D4, D6 and D7 built 10 Nov 2026 (below);
Todo's attachments come next.
recommendations, which they chose. D2, D4, D6 and D7 built 10 Nov 2026, Todo's
attachments (D1, D3, D5, D8) 11 Nov 2026 (below).
- **Builds on:** ADR 0004 (Learn's assets, rule 5), ADR 0014 (the module), ADR 0017
(the account's keys), ADR 0019 (sealed files), ADR 0033 (Todo), ADR 0042 (mobile data).
@ -92,3 +92,35 @@ module's pruning removes a file no row names any more.
20 MB only as a PDF); `AssetEndpoint` a forwarder in Learn's models for released apps;
the app calls the module (`client.modules.base.files`).
## As built: Todo's attachments (11 Nov 2026)
- Server: the `attachment` table (`lib/src/attachment/attachment.spy.yaml`), synced and
sealed as Todo's other tables, with the sealed file's hash readable (`sha256`, indexed
by space and hash); `todoFiles` (`todo_files.dart`): its rows, their spaces' members,
sealed files alone; `todoBase(files: todoFiles)`; the pruning daily
(`FilePruningFutureCall`, `schedulePruning`). Migration `attachments`: `CREATE TABLE
"attachment"` with its two indexes and the foreign key to the space, cascading on its
deletion, as every synced table; nothing else changes.
- Shared in halkyon_app, moved from Learn, which uses them since: `FileCache`
(`DirectoryFileCache`), `FileServerGateway` (`ServerpodFileServerGateway` on the
module's `files`, `FileLimitReached`, `FileRoom`), their fakes (`MemoryFileCache`,
`FakeFileServerGateway`), and the sealed file's format (`SealedFile`, the recommendation
above: halkyon_app rather than halkyon_keys, as it seals with halkyon_app's
`ContentCipher`; Learn's `Privacy` seals with it, its context unchanged).
- Opening a file in the system's app (D1): halkyon_app's `FileOpener`,
`SystemFileOpener` opening a copy under its name in the cache's `halkyon_opened/`, on
Android through halkyon_app's plugin (a FileProvider of its own class,
`HalkyonFileProvider`, sharing that folder alone, so that it never clashes with an app's
own, Train's), on the desktop as a `file:` URI; where none opens it (iOS), a copy is
saved where the person chooses.
- Todo's app (`lib/src/feature/attachment/`): `AttachmentRepository` sealing each file for
its row (`attachment|<id>|file`), keeping the sealed and the opened file in the person's
folder (`files/`), uploading after each sync and downloading as shown, both held back by
*Without files* on mobile data but for a file the person taps; `AttachmentRow` under the
description (`TaskAttachments`, shown once there is one, the paperclip beside the
description, `TaskAttachButton`, so a sheet without files keeps its height) and under a
comment's text, the comment's composer holding the files picked until it is sent, a
comment of files alone allowed. A task deleted takes its comments' and its own attachments along, a comment
its own.
- Not built: the room used, in Todo's Settings; a file's upload or download progress.