Watch: review fixes for action errors that vanished, feed refreshes dropped or overwritten around a change, a fresh thread screen per route, errors cleared on sign out, notification actions only while paired, cache writes off the main thread and capped at 30 threads, Ask before acting and Load earlier races, two-letter search, C# headings, and strictly encoded query values

This commit is contained in:
gib committed 2026-11-02 15:29:15 -05:00
1 parent 99b300855e
commit 25be69fa30
14 files changed
+591 -94

No files matched your search

+19 -3
View File
@@ -1,11 +1,13 @@
// The last good feed, and the last detail of each thread opened, saved as JSON in the app's caches
// folder. Launch shows them at once while the first refresh runs. Signing out clears them, and demo
// mode never reads or writes them.
// The last good feed, and the last detail of the 30 threads saved most recently, saved as JSON in
// the app's caches folder. Launch shows them at once while the first refresh runs. Signing out clears
// them, and demo mode never reads or writes them.
import Foundation
struct FeedCache: Sendable {
let directory: URL
/// How many thread details are kept. Saving one more deletes the oldest.
var threadLimit = 30
static let standard = FeedCache(directory: .cachesDirectory.appending(path: "WristCode", directoryHint: .isDirectory))
@@ -23,6 +25,20 @@ struct FeedCache: Sendable {
func save(_ detail: ThreadDetail) {
write(detail, Self.threadFile(detail.item.hostId, detail.item.threadId))
pruneThreads()
}
/// Deletes the thread details saved longest ago, past `threadLimit`.
private func pruneThreads() {
let fileManager = FileManager.default
let key = URLResourceKey.contentModificationDateKey
guard let files = try? fileManager.contentsOfDirectory(at: directory, includingPropertiesForKeys: [key]) else { return }
let threads = files.filter { $0.lastPathComponent.hasPrefix("thread-") }
guard threads.count > threadLimit else { return }
let modified = { (url: URL) in (try? url.resourceValues(forKeys: [key]).contentModificationDate) ?? .distantPast }
for url in threads.sorted(by: { modified($0) > modified($1) }).dropFirst(threadLimit) {
try? fileManager.removeItem(at: url)
}
}
/// Deletes everything saved, as when the watch unpairs.