Keep originals on this device, by pin and by use
Fills in `image_cache`, which the previous commit's "On this device" filter read but nothing wrote. Also carries in-flight work that shared these files: the Android TLS root store, the settings page, and a regenerated traceability report. # Two populations, deliberately separate An original is kept here for one of two reasons, and conflating them produces the exact failure the feature exists to prevent. **Pinned** originals were asked for. Pinning a collection before a trip is a promise, so pinned rows are never evicted and never counted against the budget — a cap that could silently delete a pinned trip would make pinning worthless, because it could not be relied on without checking. **Passively cached** originals are a side effect of working: develop already downloads the whole file, so keeping it costs no bandwidth and saves the entire transfer next time. This population is what the budget bounds, evicted least-recently-used, because it otherwise grows until a day of culling fills a disk. Sharing one budget would let a large pin starve the passive cache, or let browsing evict a pin. They are separate. # What was built `dr_catalog::cache` owns the bookkeeping — held tier, size, last use, pinned — and writes the bytes; deciding to download stays with the caller, which is what keeps a crate with no network out of the network's business. Files are written to a temporary and renamed, so a dropped connection cannot leave a truncated file recorded as a complete original. They are named by image id, not filename: `Photos/IMG_0001.CR2` and `Trips/IMG_0001.CR2` are different photographs, and a flat cache keyed on the name would serve one for the other. `spawn_full_fetch` became read-through. A hit is a disk read; a miss stores what it downloads and enforces the budget. A cache that cannot be opened is a miss, not a failure to open the photograph. Pinning writes intent — `tier_desired` — without downloading, so the button responds immediately, and `spawn_pin_fetch` fills it in sequentially afterwards. Sequential because these are tens of megabytes each: the lanes that make the thumbnail sweep fast buy little against one connection's bandwidth and cost a great deal of memory. A pin interrupted by a lost connection resumes from where it stopped. Schema v5 adds `pinned` and `path`. `pinned` is a column rather than something inferred from `pinned_by_rule`, which is ON DELETE SET NULL and so cannot answer for an image whose rule was deleted. A v4 catalog migrates in place; existing rows default to unpinned, the safe direction. The budget and "keep opened originals" come from the settings page rather than a constant, and are applied at startup rather than only on change — a cache capped at 2 GB last session would otherwise spend this one filling to the default. Turning off keeping leaves what is already cached readable: those bytes are paid for, and refusing them would re-download images sitting right there, including pinned ones. Also removes a doubled `#[test]` introduced in the previous commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+12
-4
@@ -64,10 +64,18 @@ pollster = "0.4"
|
||||
# cross-compile for Android — precisely the NDK pain D1 chose Rust to avoid.
|
||||
# ring is pure Rust apart from a small asm core that does build under the NDK.
|
||||
#
|
||||
# Note this still pulls rustls-platform-verifier, which crashes on Android
|
||||
# unless initialised from Kotlin (spike S3). D7 records `tls_certs_only` plus
|
||||
# webpki-roots as the escape hatch.
|
||||
reqwest = { version = "0.13", default-features = false, features = ["rustls-no-provider", "stream", "json"] }
|
||||
# `rustls-tls-webpki-roots-no-provider` rather than plain `rustls-no-provider`:
|
||||
# the latter verifies against rustls-platform-verifier, which reaches the
|
||||
# Android trust store over JNI and panics mid-handshake unless initialised from
|
||||
# Java first — the crash D7 predicted and spike S3 exists to resolve properly.
|
||||
# The panic surfaces inside tokio, which catches task panics itself, so it
|
||||
# reaches the UI as a worker that stopped rather than as an error.
|
||||
#
|
||||
# webpki-roots is the escape hatch D7 records: a root store compiled into the
|
||||
# binary, no JNI, identical on both platforms. The trade is real and belongs in
|
||||
# S3's scope — user-installed and enterprise CAs are not consulted, and the
|
||||
# roots go stale with the release rather than with the OS.
|
||||
reqwest = { version = "0.13", default-features = false, features = ["rustls-no-provider", "webpki-roots", "stream", "json"] }
|
||||
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] }
|
||||
quick-xml = "0.41"
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time"] }
|
||||
|
||||
Reference in New Issue
Block a user