mirror of
https://github.com/yattee/yattee.git
synced 2026-10-31 17:07:18 +00:00
Only a deletion the app can point at moves the selection
`correctIfContextRemoved` read a locally missing account as a deleted one and reset the context with `setContext(.local)` — a new timestamp and a pending upload. Now that foreground activation calls it, it ran before the fetch that delivers the account record: a perfectly good remote choice was reset and then published over everybody else's. It now asks `AccountsManager` for a repair keyed on the deletion tombstone, which is the same repair a deletion performs — a signed-in login on the same server when there is one, the replaced choice's timestamp, nothing uploaded. The toast names where the selection actually landed. Adopting a synced context that named a deleted account repaired it but kept whatever upload the previous choice still owed, so the sync engine published the automatic fallback under that choice's timestamp — exactly what not stamping it "now" exists to prevent. The adoption clears the flag, and only on adoption: a local choice that outranks the repair keeps its own upload. The channel button's subscription lookup is fenced to the channel and the round it was asked in, so an answer that lands after the user has walked on — or after the account behind it changed — no longer writes itself into the button on screen. Context and credential changes ask again. A destination toggle is compared against the effective context rather than the raw selection: the menu offers Local precisely when the selected account cannot be written to, and that toggle has to be the one the button then shows. The destination rows forget their checkmarks when the logins change. The answers describe credentials that have been replaced, and the tvOS picker deliberately stays open across exactly that. The subscriptions settings screen counts and exports what it is actually reading from. The fetch behind both already followed the effective context while the branch and the footer followed the selection, so an unavailable account meant local data described as the server's. The legacy `subscriptionAccount` migration refused to adopt a context whose account had not synced yet and was never retried, leaving a fresh install on local until the next launch. A synced account arriving now retries it; the migration's own guards keep it a one-way move that never overrides a choice made in the meantime. The remaining negative-able Piped counts — likes, channel subscribers and videos, playlist video counts — go through `PipedSentinel.count` like the views and upload dates already did.
This commit is contained in:
@@ -161,28 +161,30 @@ final class DataContextValidator {
|
||||
|
||||
/// Corrects the context only when the selected account is provably gone.
|
||||
///
|
||||
/// Called after deletions. A timeout, a failed fetch or a missing record
|
||||
/// must never reach here — those are all "unknown", and treating them as
|
||||
/// removal is how a user loses their selection during a slow sync.
|
||||
/// Called after deletions and on foreground activation. A timeout, a failed
|
||||
/// fetch or a record that simply has not arrived must never move anything —
|
||||
/// those are all "unknown", and treating them as removal is how a user
|
||||
/// loses their selection during a slow sync. Only the deletion tombstone
|
||||
/// counts, and the repair it triggers is never published.
|
||||
func correctIfContextRemoved() {
|
||||
// An unresolved identity has not loaded anybody's accounts yet, so an
|
||||
// account "missing" from that state proves nothing — least of all a
|
||||
// deletion, which is the only thing allowed to move the context here.
|
||||
// An unresolved identity has not loaded anybody's accounts yet, so
|
||||
// neither the selection nor the tombstones read here are the ones this
|
||||
// device will end up with.
|
||||
guard accountsManager.isIdentityResolved else { return }
|
||||
guard case let .account(id) = accountsManager.currentContext else { return }
|
||||
guard accountsManager.account(id: id) == nil else { return }
|
||||
|
||||
// Only an explicit deletion clears the account from the store, so
|
||||
// reaching here means the account really is gone.
|
||||
accountsManager.setContext(.local)
|
||||
// The manager owns the decision: it holds the tombstones, and it
|
||||
// repairs the same way an account deleted right now would be — onto a
|
||||
// signed-in login on the same server when there is one, keeping the
|
||||
// replaced choice's timestamp and publishing nothing.
|
||||
guard accountsManager.correctContextIfAccountDeleted() else { return }
|
||||
|
||||
toastManager.showInfo(
|
||||
String(localized: "subscriptions.accountSwitched.title"),
|
||||
subtitle: String(localized: "subscriptions.account.local")
|
||||
subtitle: displayName(for: accountsManager.currentContext)
|
||||
)
|
||||
|
||||
LoggingService.shared.info(
|
||||
"Selected account was deleted — data context reset to local",
|
||||
"Selected account was deleted — data context repaired",
|
||||
category: .general
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user