From 83eb89e8f90d951d241765ef4f2a8d8981aca74f Mon Sep 17 00:00:00 2001 From: Shubham Bhandari Date: Tue, 15 Sep 2026 11:32:30 +0800 Subject: [PATCH] The sheet stops endorsing the rename, word as well as glyph MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @Chessing234 is right and my reasoning for the half-measure was wrong. I kept "✓ VERIFIED" in the fingerprint sheet on the grounds that the key genuinely is verified and only the glyph should demote. But that badge sits a few lines under `peerNickname`, so it is not a claim about a bare key — it is a claim about THIS NAME, and that claim is false. And this sheet is the screen someone opens *because* a seal vanished from a row, so the one surface they consult to resolve the doubt was the one endorsing the rename. The badge, the message and the verify button now all read `isVerified && !nameChangedSinceVerification`, which is what `nameChangedSinceVerification` was plumbed onto the presentation state for and then left unused. No new string, so no locale work is blocking it: the existing not-verified copy already names the current nickname and offers to verify it, and that offer IS the recovery — re-verifying re-pins the baseline to the name on screen. The stored verification is untouched underneath. One consequence worth stating rather than hiding: a renamed peer's sheet now offers "mark verified" instead of "remove verification", so unverify is one re-verification away rather than immediately to hand. I think that is the right trade — the alternative is a green checkmark next to a name nobody verified — but it is a real change to the affordance and I would rather it be argued with than found. 26 cases in the binding suite; 201 suites / 1977 tests, every difference from unmodified `main` an addition. Co-Authored-By: Claude Opus 5 --- bitchat/Views/FingerprintView.swift | 27 ++++++++++++++++--- ...tityStateManagerNicknameBindingTests.swift | 21 +++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/bitchat/Views/FingerprintView.swift b/bitchat/Views/FingerprintView.swift index bce7e89c..68b03d08 100644 --- a/bitchat/Views/FingerprintView.swift +++ b/bitchat/Views/FingerprintView.swift @@ -223,14 +223,33 @@ struct FingerprintView: View { // Verification status if fingerprintState.canToggleVerification { + // The seal is withheld when this key now presents a name it + // was not verified under — and the WORD goes with the glyph. + // + // An earlier revision kept "✓ VERIFIED" here on the grounds + // that the key genuinely is verified. Review put the + // objection better than I had: this badge sits a few lines + // under `peerNickname`, so it is not a claim about a bare + // key, it is a claim about THIS NAME — and that claim is + // false. Worse, this sheet is the screen someone opens + // *because* a seal vanished from a row, so the one surface + // they consult to resolve the doubt was the one endorsing + // the rename. + // + // No new string: the existing not-verified copy names the + // current nickname and offers to verify it, which is the + // recovery — re-verifying re-pins the baseline to the name + // on screen. The key's stored verified state is untouched. + let sealApplies = fingerprintState.isVerified + && !fingerprintState.nameChangedSinceVerification VStack(spacing: 12) { - Text(fingerprintState.isVerified ? Strings.verifiedBadge : Strings.notVerifiedBadge) + Text(sealApplies ? Strings.verifiedBadge : Strings.notVerifiedBadge) .bitchatFont(size: 14, weight: .bold) - .foregroundColor(fingerprintState.isVerified ? Color.green : Color.orange) + .foregroundColor(sealApplies ? Color.green : Color.orange) .frame(maxWidth: .infinity) Group { - if fingerprintState.isVerified { + if sealApplies { Text(Strings.verifiedMessage) } else { Text(Strings.verifyHint(fingerprintState.peerNickname)) @@ -243,7 +262,7 @@ struct FingerprintView: View { .fixedSize(horizontal: false, vertical: true) .frame(maxWidth: .infinity) - if !fingerprintState.isVerified { + if !sealApplies { Button(action: { verificationModel.verifyFingerprint(for: peerID) dismiss() diff --git a/bitchatTests/Services/SecureIdentityStateManagerNicknameBindingTests.swift b/bitchatTests/Services/SecureIdentityStateManagerNicknameBindingTests.swift index a844ea1a..5ac5da12 100644 --- a/bitchatTests/Services/SecureIdentityStateManagerNicknameBindingTests.swift +++ b/bitchatTests/Services/SecureIdentityStateManagerNicknameBindingTests.swift @@ -329,6 +329,27 @@ struct SecureIdentityStateManagerNicknameBindingTests { /// `PeerDisplayNameResolver` would give this peer and nothing else is. private var decorator: PeerID { PeerID(str: "a1b2c3d4") } + @Test + func theSheetStopsEndorsingAfterARename() { + // The fingerprint sheet drives its badge, its message and its verify + // button off `nameChangedSinceVerification`, so the word demotes with + // the glyph. Pinned here at the state level, which is what the view + // reads: the view itself needs an app bundle these tests cannot build. + let manager = makeManager() + announce(manager, vouchee, as: "ravi") + manager.setVerified(fingerprint: vouchee, verified: true) + #expect(!manager.trustedNicknameMismatch(fingerprint: vouchee), + "nothing to explain while the name holds") + + announce(manager, vouchee, as: "medic") + #expect(manager.trustedNicknameMismatch(fingerprint: vouchee), + "the sheet is told the name changed, so its copy can demote") + #expect(manager.trustedNickname(fingerprint: vouchee) == "ravi", + "and the old name survives, so a future revision can name it") + #expect(manager.isVerified(fingerprint: vouchee), + "while the stored verification is untouched — re-verifying is the recovery") + } + // MARK: - What counts as the same name @Test