The watch had the reactivity bug too, on all four screens

Checked the wear module for the same mistake review had just caught twice on
the phone, and it was there.

`WearPeerIdentityState.snapshot` now reads the announced nickname to decide
whether the seal still applies, but all four screens cache it with
`remember(peerID, revision)` — and `revision` is bumped by favourites and
verification changes, never by a peer renaming. So the name repainted while the
cached `isVerified`, and its seal, survived. Same defect Codex found in
`PeopleSection` and `PrivateChatSheet`, in a module neither reviewer looked at.

All four now key on the announced nickname: DmScreen, PeerDebugScreen,
VerificationCodeScreen and UserDetailScreen. The last of those I had missed
even after deciding to audit this module — grepping for the snapshot call found
three, and the fourth only turned up when I grepped for the remember key
instead. It draws a seal like the others.

Nothing else here needs gating: every wear screen reads `identity.isVerified`
straight from the snapshot, so there is no second independent source the way
the phone's SecurityVerificationSheet had one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Shubham Bhandari 2026-09-15 11:39:37 +08:00
parent d637b5bc1b
commit 077c82b63d
4 changed files with 16 additions and 5 deletions

View File

@ -75,7 +75,12 @@ fun DmScreen(
val nickname = mesh?.getPeerNickname(peerID) ?: peerID.take(8)
val identityRevision by WearPeerIdentityState.revision.collectAsState()
val identity = remember(peerID, identityRevision) {
// Keyed on the announced nickname too: the snapshot now reads it to
// decide whether the seal still applies, and `revision` is bumped by
// favourites and verification changes, never by a peer renaming. Without
// this the name repaints while the cached "verified" — and its seal —
// survives, which is the live impersonation the binding exists to stop.
val identity = remember(peerID, identityRevision, nickname) {
WearPeerIdentityState.snapshot(peerID, mesh)
}
var sessionEstablished by remember {

View File

@ -76,7 +76,8 @@ fun PeerDebugScreen() {
items(peers) { peerID ->
val nick = nicknames[peerID] ?: peerID.take(8)
val encrypted = mesh?.hasEstablishedSession(peerID) == true
val identity = androidx.compose.runtime.remember(peerID, identityRevision) {
// Keyed on the nickname too — see DmScreen.
val identity = androidx.compose.runtime.remember(peerID, identityRevision, nick) {
WearPeerIdentityState.snapshot(peerID, mesh)
}
Row(

View File

@ -40,10 +40,13 @@ fun UserDetailScreen(
) {
val mesh = WearMeshService.peek()
val revision by WearPeerIdentityState.revision.collectAsState()
val identity = androidx.compose.runtime.remember(peerID, revision) {
val nickname = mesh?.getPeerNickname(peerID) ?: peerID.take(8)
// Keyed on the announced nickname too — see DmScreen. This screen draws the
// seal as well, so without it a rename repaints the name here and leaves
// the checkmark sitting beside the new one.
val identity = androidx.compose.runtime.remember(peerID, revision, nickname) {
WearPeerIdentityState.snapshot(peerID, mesh)
}
val nickname = mesh?.getPeerNickname(peerID) ?: peerID.take(8)
val listState = rememberScalingLazyListState(initialCenterItemIndex = 0)
val palette = LocalBitchatPalette.current

View File

@ -36,7 +36,9 @@ import com.bitchat.watch.ui.theme.LocalBitchatPalette
fun VerificationCodeScreen(peerID: String) {
val mesh = WearMeshService.peek()
val revision by WearPeerIdentityState.revision.collectAsState()
val identity = androidx.compose.runtime.remember(peerID, revision) {
// Keyed on the announced nickname too — see DmScreen.
val announcedNickname = mesh?.getPeerNickname(peerID)
val identity = androidx.compose.runtime.remember(peerID, revision, announcedNickname) {
WearPeerIdentityState.snapshot(peerID, mesh)
}
val myFingerprint = WearPeerIdentityState.myFingerprint(mesh)