~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/knotless

git that lives in your Atmosphere account

knotless.dev
1 collaborator

knotless 0.13.0 Latest 2 packages, 1 image

1
10

The iOS app's inbox keeps your place when you come back to it

Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:ios/inbox-keeps-place
ios/Knotless/Views/InboxView.swift+18−5
@@ -11,6 +11,8 @@ struct InboxView: View {
@State private var items: [InboxItem] = []
@State private var cursor: String?
@State private var phase = Phase.loading
/// What `items` are of.
@State private var read: ReloadKey?
enum Phase { case loading, ready, signedOut, failed(String) }
private struct ReloadKey: Hashable { let filter: String?; let did: String? }
@@ -24,7 +26,12 @@ struct InboxView: View {
content.frame(maxHeight: .infinity)
}
.navigationTitle(signedOut ? "" : "Inbox")
.task(id: ReloadKey(filter: filter, did: account.person?.did)) { await reload() }
.task(id: ReloadKey(filter: filter, did: account.person?.did)) {
// Shown again, it keeps the pages read of it. Another filter or account starts anew.
let key = ReloadKey(filter: filter, did: account.person?.did)
// Only once it's read: with none, `items` are still of what came before.
if await reload(keepingPages: key == read) { read = key }
}
}
private var signedOut: Bool {
@@ -91,11 +98,13 @@ struct InboxView: View {
try await Client.shared.query("listNotifications", ["limit": "30", "kind": filter, "cursor": cursor])
}
private func reload() async {
/// Whether it was read.
@discardableResult private func reload(keepingPages: Bool = false) async -> Bool {
do {
let answer = try await fetch(cursor: nil)
items = answer.notifications
cursor = answer.cursor
let old = keepingPages ? items : []
cursor = old.count > answer.notifications.count ? cursor : answer.cursor
items = rereading(answer.notifications, over: old)
phase = .ready
// Seen, once all of them were on offer. The dots stay until the next
// look, so someone can see what was new this time.
@@ -103,16 +112,20 @@ struct InboxView: View {
try? await Client.shared.send(Writes.seen(at: newest))
Pushes.shared.clearBadge()
}
return true
} catch let error as APIError where error.needsSignIn {
phase = .signedOut
} catch is CancellationError {
} catch let error as URLError where error.code == .cancelled {
} catch {
phase = .failed(error.localizedDescription)
}
return false
}
private func more() async {
guard let cursor, let answer = try? await fetch(cursor: cursor) else { return }
let asked = filter
guard let cursor, let answer = try? await fetch(cursor: cursor), filter == asked else { return }
self.cursor = answer.cursor
items += answer.notifications.filter { !items.contains($0) }
}