mirror of
https://github.com/kuhyx/todo-app.git
synced 2026-11-11 13:27:46 +01:00
Some checks failed
📎 now picks any file (Android: Camera / Gallery / File; desktop: an
unfiltered dialog) and the desktop page accepts any dropped file. Photo and
screenshot formats up to 64 MB are still re-encoded and shown inline;
everything else is stored byte for byte on dufs beside the images and linked
as [name](url), shown as a row with its size that opens in the platform's
app (Android) or a new browser tab (desktop). BACKLOG.md rewrites file links
to ~/data/cloud/todo-images/ like image links.
There is no size cap, so no layer holds a file in memory: uploads and
downloads stream through DufsImageClient, writes land in a .tmp renamed when
whole (an interrupted download no longer leaves a truncated file that looks
done), the desktop page POSTs the browser's own Blob with fetch, and Android's
"File" uses file_picker, because file_selector's Android side reads the whole
file into memory on the UI thread. Verified with a 1 GB file end to end on
desktop (peak RSS +36 MB) and on an API 36 emulator.
The wrapper serves attachments from the origin that holds the GitHub token
and the notes, so only script-free types open inline (nosniff), the rest
download; writes carrying another site's Origin are refused (this also
closes cross-site overwrites of the backup files); and attachment routes run
concurrently so a long upload or a playing video cannot stall the page.
Also: cupertino_icons 2.0.0 (unused, but the freshness gate blocks the
pubspec otherwise), open_file 4.0.0 and file_picker 13.1.0 pinned.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
41 lines
984 B
Dart
41 lines
984 B
Dart
/// Counts from one `ImageCacheStore.reconcile` pass.
|
|
///
|
|
/// Pure Dart: the desktop wrapper returns it as JSON to the page.
|
|
library;
|
|
|
|
/// Counts from one reconcile pass.
|
|
class ReconcileReport {
|
|
/// Creates a report.
|
|
const ReconcileReport({
|
|
this.uploaded = 0,
|
|
this.downloaded = 0,
|
|
this.evicted = 0,
|
|
this.pending = 0,
|
|
this.missing = 0,
|
|
});
|
|
|
|
/// Queued attachments dufs accepted this pass.
|
|
final int uploaded;
|
|
|
|
/// Linked attachments fetched from dufs this pass.
|
|
final int downloaded;
|
|
|
|
/// Unlinked cached attachments deleted.
|
|
final int evicted;
|
|
|
|
/// Attachments still waiting to upload after the pass.
|
|
final int pending;
|
|
|
|
/// Linked attachments still not on this device after the pass.
|
|
final int missing;
|
|
|
|
/// JSON form, for the wrapper's `/images/reconcile` response.
|
|
Map<String, int> toJson() => {
|
|
'uploaded': uploaded,
|
|
'downloaded': downloaded,
|
|
'evicted': evicted,
|
|
'pending': pending,
|
|
'missing': missing,
|
|
};
|
|
}
|