mirror of
https://github.com/yattee/yattee.git
synced 2026-10-30 08:27:17 +00:00
Make a second login on an instance reachable and orderable
An additional login could be created but never selected: the legacy picker lists one entry per instance and always resolved to whichever account signed in first. Account rows gain a "Use This Account" action, and the subscriptions view reloads on the data context rather than on the legacy setting — two logins on the same instance map onto the same legacy value, so switching between them changed nothing on screen while writes went to the other account. Two devices migrating different legacy selections produced contexts with identical epoch timestamps, and each refused the other's: a permanent, silent divergence that both sides considered settled. Equal timestamps are now broken by the selected account's ID, on the incoming side and in the conflict resolver alike — a record that wins the upload must not be refused on arrival. Credential cleanup falls back to both managers whenever the backend cannot be resolved, not only when the account is unknown here: an instance deletion can arrive before the tombstone, and the credential was then left in the Keychain.
This commit is contained in:
@@ -478,8 +478,8 @@ final class AccountsManager {
|
||||
return false
|
||||
}
|
||||
|
||||
guard incoming.changedAt > storedContext.changedAt else {
|
||||
guard storedContext.changedAt > incoming.changedAt else { return false }
|
||||
guard storedContext.isOutranked(by: incoming) else {
|
||||
guard incoming.isOutranked(by: storedContext) else { return false }
|
||||
pendingContextUpload = true
|
||||
save()
|
||||
return true
|
||||
|
||||
@@ -417,21 +417,21 @@ final class AppEnvironment {
|
||||
accountSync.onAccountDeleted = { [weak self] accountID, account in
|
||||
guard let self else { return }
|
||||
|
||||
guard let account else {
|
||||
// No metadata for this account here, so its backend is unknown
|
||||
// and the right credential manager cannot be picked. The scoped
|
||||
// key is the account ID either way, so both are asked to drop
|
||||
// it; at most one of them holds anything. The unscoped legacy
|
||||
// item is left alone — deleting it needs the instance, and its
|
||||
// ownership check is bound to one.
|
||||
// The backend is unknown whenever the account itself never arrived
|
||||
// here, and equally when its instance is already gone — an instance
|
||||
// deletion can reach this device before the tombstone does. The
|
||||
// scoped key is the account ID either way, so both managers are
|
||||
// asked to drop it; at most one of them holds anything. The
|
||||
// unscoped legacy item is left alone in that case: deleting it
|
||||
// needs the instance, and its ownership check is bound to one.
|
||||
guard let account, let credentials = self.accountCredentialsManager(for: account) else {
|
||||
self.invidiousCredentialsManager.deleteCredential(forAccountID: accountID)
|
||||
self.pipedCredentialsManager.deleteCredential(forAccountID: accountID)
|
||||
return
|
||||
}
|
||||
|
||||
let credentials = self.accountCredentialsManager(for: account)
|
||||
credentials?.deleteCredential(for: account)
|
||||
credentials?.deleteLegacyCredentialIfOwned(for: account)
|
||||
credentials.deleteCredential(for: account)
|
||||
credentials.deleteLegacyCredentialIfOwned(for: account)
|
||||
}
|
||||
|
||||
// Statuses are cleared whenever the identity namespace reloads, so they
|
||||
|
||||
Reference in New Issue
Block a user