diff --git a/Yattee/Core/AccountsManager.swift b/Yattee/Core/AccountsManager.swift index b9481b12..972a8d86 100644 --- a/Yattee/Core/AccountsManager.swift +++ b/Yattee/Core/AccountsManager.swift @@ -892,5 +892,9 @@ extension Notification.Name { static let accountsDidChange = Notification.Name("stream.yattee.accountsDidChange") /// An account's usability changed — not the list of accounts. static let accountStatusesDidChange = Notification.Name("stream.yattee.accountStatusesDidChange") + /// An account's stored credential was replaced. The account itself is + /// unchanged, so anything holding data fetched with the old one has to be + /// told: it may belong to an entirely different person on that server. + static let accountCredentialDidChange = Notification.Name("stream.yattee.accountCredentialDidChange") static let dataContextDidChange = Notification.Name("stream.yattee.dataContextDidChange") } diff --git a/Yattee/Core/AppEnvironment.swift b/Yattee/Core/AppEnvironment.swift index 494b477c..41dfb511 100644 --- a/Yattee/Core/AppEnvironment.swift +++ b/Yattee/Core/AppEnvironment.swift @@ -441,6 +441,17 @@ final class AppEnvironment { guard let account, let credentials = self.accountCredentialsManager(for: account) else { self.invidiousCredentialsManager.deleteCredential(forAccountID: accountID) self.pipedCredentialsManager.deleteCredential(forAccountID: accountID) + + // The unscoped legacy item needs the instance to be identified, + // but not to be *checked*: the adoption marker is specific to + // service, account, scope and the exact secret, so asking both + // managers retires it if either owns it and does nothing + // otherwise. Worth asking — this account was explicitly deleted, + // and the instance may simply have gone first. + if let account { + self.invidiousCredentialsManager.deleteLegacyCredentialIfOwned(for: account) + self.pipedCredentialsManager.deleteLegacyCredentialIfOwned(for: account) + } return } @@ -696,6 +707,12 @@ final class AppEnvironment { if accountsManager.currentContext == .account(account.id) { SubscriptionFeedCache.shared.handleAccountChange() } + + // Views hold their own copies of this account's data, and signing in + // again as somebody else changes neither the account ID, the context, + // nor usually the status — so nothing they observe would move. Told + // explicitly instead. + NotificationCenter.default.post(name: .accountCredentialDidChange, object: nil) } /// Drops everything cached for whichever account was being shown. diff --git a/Yattee/Views/Instances/InstanceBrowseView.swift b/Yattee/Views/Instances/InstanceBrowseView.swift index 1349c86d..f563a27a 100644 --- a/Yattee/Views/Instances/InstanceBrowseView.swift +++ b/Yattee/Views/Instances/InstanceBrowseView.swift @@ -455,6 +455,13 @@ struct InstanceBrowseView: View { refreshLoginState() Task { await startContentLoad() } } + // Same reason, without the context moving: the credential behind this + // instance's account was replaced. + .onReceive(NotificationCenter.default.publisher(for: .accountCredentialDidChange)) { _ in + discardAccountScopedContent() + refreshLoginState() + Task { await startContentLoad() } + } .onChange(of: selectedTab) { _, _ in isLoading = true errorMessage = nil diff --git a/Yattee/Views/Subscriptions/SubscriptionsView.swift b/Yattee/Views/Subscriptions/SubscriptionsView.swift index 194b0a25..d2eb14c0 100644 --- a/Yattee/Views/Subscriptions/SubscriptionsView.swift +++ b/Yattee/Views/Subscriptions/SubscriptionsView.swift @@ -527,6 +527,12 @@ struct SubscriptionsView: View { .onReceive(NotificationCenter.default.publisher(for: .instancesDidChange)) { _ in reloadIfEffectiveContextMoved() } + // Signing in again keeps the same account and context, so + // nothing above would fire — but the data on screen may now + // belong to a different person on that server. + .onReceive(NotificationCenter.default.publisher(for: .accountCredentialDidChange)) { _ in + reloadForContextChange() + } .task(id: instanceConfigurationID) { LoggingService.shared.debug("SubscriptionsView task triggered, instanceConfigurationID: \(instanceConfigurationID)", category: .general) await loadSubscriptionsAsync()