app v1.1.0: delete-after-upload, duplicate status, clear history, open-with

- Delete the source file once an upload is confirmed (best-effort: SAF
  picks get a persisted read+write grant and are removed; read-only
  shared streams are left in place). Cache copy always removed. A removed
  source shows as 'removed from phone'.
- Distinguish duplicates: a HEAD (or the server's 200 existed) marks the
  item 'Duplicate · already on server' — content is never re-sent or
  overwritten, since the name IS the sha256. Different content = different
  hash = a new file, not a duplicate.
- 'Clear history' app-bar button clears finished/duplicate/failed rows.
- Register as an 'Open with' target (ACTION_VIEW, content/file) in
  addition to the existing share-sheet target.
- Room schema v2 (destructive fallback; queue is ephemeral).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-29 00:07:30 +01:00
parent 2fd80cfd7d
commit 4c6c897b10
11 changed files with 203 additions and 24 deletions

View File

@@ -8,6 +8,28 @@ Every file is stored as `<sha256-of-content>.<original extension>`, so names
carry nothing, re-uploads are idempotent, and a retried upload can be verified
server-side before it is accepted.
## Behaviour
- **Integrity is structural.** The remote name *is* the SHA-256 of the content.
The server recomputes the hash of the bytes it receives and stores the file
only if it matches (mismatch → 409, nothing written). So a success response
is proof the server holds byte-for-byte-intact content — which is what makes
deleting the local copy safe.
- **Delete after confirmed upload.** Once an upload is confirmed, the app
removes its working copy and, best-effort, the original file. Files picked
in-app (SAF) are removed via the persisted read+write grant; a file that
arrives as a read-only share/`open with` stream cannot be deleted and is left
in place (not a failure). A removed source shows as "removed from phone".
- **Duplicates are never rewritten.** Identical content (same hash) already on
the server is detected by a `HEAD` before any bytes are sent; the item is
marked *Duplicate · already on server* and nothing is re-uploaded or
overwritten. Different content is a different hash, i.e. a different file —
not a duplicate.
- **Entry points.** Share sheet (`ACTION_SEND`), "Open with" on a downloaded
file (`ACTION_VIEW`), and an in-app file picker. "Clear history" (app-bar
button) drops finished, duplicate and failed rows; in-flight uploads keep
running.
## Parts
- `server/` — Node 22 / TypeScript. Runs as a docker container **on nas001**