feat(core,app,android,ios): look at shares again on their own, and say when they were last looked at

No client crawled a share unless someone pressed Refresh, so a new
episode stayed out of the library until then, and nothing said how stale
the library was.

Catalog schema v11 adds share_crawls: when each share's last crawl
finished and was attempted, how many files it stored and how many were
new, and why the last attempt failed. A failure keeps the last good
crawl's numbers.

Every client now crawls, quietly, any share not looked at for six hours:
the desktop at launch and every half hour while open, the phones at
launch and on their existing catch-up timer. A quiet crawl says only
news ("anime: 3 new files") and leaves failures on the share's row
rather than in a toast.

Share rows on all three show "120 files - crawled 2 h ago", or the last
failure until a crawl succeeds.
This commit is contained in:
nvrl committed 2026-10-12 22:22:25 +02:00
1 parent a306d02cf1
commit 5807493a4a
13 files changed
+552 -75

No files matched your search

+10 -4
View File
@@ -242,7 +242,9 @@ final class AppModel {
}
}
await reloadNow()
if !failed.isEmpty {
// A catch-up is the app's doing, not the viewer's: a failure goes on
// the share's row rather than in their face, and only news is said.
if !failed.isEmpty, announce {
message = failed.joined(separator: "\n")
return
}
@@ -250,6 +252,9 @@ final class AppModel {
if announce {
let added = Set(titles.map(\.key)).subtracting(before).count
message = added == 1 ? "1 title added" : "\(added) titles added"
} else {
let files = shares.filter { ids.contains($0.id) }.reduce(UInt64(0)) { $0 + ($1.crawlAdded ?? 0) }
if files > 0 { message = files == 1 ? "1 new file" : "\(files) new files" }
}
}
@@ -430,9 +435,10 @@ final class AppModel {
}
}
/// On launch and back on screen: crawl shares the catalog has nothing of —
/// brought by a sync that ran in the background, or an add whose crawl
/// never finished.
/// On launch and back on screen: crawl shares due a look — ones the
/// catalog has nothing of, brought by a sync that ran in the background or
/// an add whose crawl never finished, and ones not crawled for six hours,
/// so new episodes turn up without a pull to refresh.
func enteredForeground() {
guard !catchingUp else { return }
catchingUp = true