Stop a status refresh from erasing a rejected credential

The API layer marking an account `credentialRejected` posts the same
notification that recomputes statuses, which saw the Keychain item still
present and immediately reported "signed in" again. The rejection could
therefore never survive the moment it was recorded, and the user was
never asked to sign in again. Only the server can clear a rejection now;
storing a new credential resets it explicitly.

Cache invalidation follows the effective context rather than the stored
one. A credential arriving or disappearing flips reads between the
account and local without the stored context moving, so the global feed
caches kept whichever side had populated them.

The login sheet stays open when the credential could not be stored — an
unresolved identity scope or a Keychain failure — instead of dismissing
and discarding what the user typed.
This commit is contained in:
Arkadiusz Fal
2026-08-26 19:27:28 +02:00
parent 923e13565f
commit 4f617b52e1
3 changed files with 55 additions and 29 deletions

View File

@@ -389,8 +389,11 @@ private struct EditRemoteServerContent: View {
///
/// The identifier is stored beside the credential in the Keychain rather
/// than on the account record, so it never travels as synced metadata.
private func storeLogin(credential: String, username: String, target: AccountLoginTarget) {
guard let appEnvironment else { return }
/// - Returns: whether the credential was stored. A login the server
/// accepted is not the same as one this device could keep.
@discardableResult
private func storeLogin(credential: String, username: String, target: AccountLoginTarget) -> Bool {
guard let appEnvironment else { return false }
// Credential storage refuses to write under an unresolved identity
// scope, so creating the account first would leave one with no
@@ -400,13 +403,13 @@ private struct EditRemoteServerContent: View {
appEnvironment.toastManager.showError(
String(localized: "accounts.signIn.identityUnresolved")
)
return
return false
}
let account = target.account
?? appEnvironment.accountsManager.addAccount(instanceID: instance.id)
guard let credentials = appEnvironment.accountCredentialsManager(for: account) else { return }
guard let credentials = appEnvironment.accountCredentialsManager(for: account) else { return false }
credentials.setCredential(credential, for: account)
@@ -420,7 +423,7 @@ private struct EditRemoteServerContent: View {
appEnvironment.toastManager.showError(
String(localized: "accounts.signIn.storeFailed")
)
return
return false
}
if !username.isEmpty {
credentials.setUsername(username, for: account)
@@ -442,6 +445,7 @@ private struct EditRemoteServerContent: View {
appEnvironment.accountCredentialDidChange(for: account)
isLoggedIn = true
return true
}
private func loadServerInfo() async {