mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-08-29 07:16:08 +00:00
Merge pull request #824 from permissionlesstech/codex/fix-peer-verification-status-badge
Fix peer verification status and private chat badge
This commit is contained in:
commit
b073dc2160
@ -475,6 +475,19 @@ fun ConversationHeaderAction(
|
||||
content = content
|
||||
)
|
||||
|
||||
/** A read-only status slot matching the footprint of [ConversationHeaderAction]. */
|
||||
@Composable
|
||||
fun ConversationHeaderStatus(
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier.size(HeaderTapTarget),
|
||||
contentAlignment = Alignment.Center,
|
||||
content = { content() }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NicknameEditor(
|
||||
value: String,
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
package com.bitchat.android.ui
|
||||
|
||||
/**
|
||||
* Resolves the Noise session shown for a private conversation.
|
||||
*
|
||||
* Persistent conversations use a canonical contact ID, while live Noise sessions are keyed by
|
||||
* the currently connected mesh peer ID. Prefer that live identity and retain the conversation ID
|
||||
* as a fallback for peers whose IDs are already identical.
|
||||
*/
|
||||
internal fun resolveConversationSessionState(
|
||||
conversationID: String,
|
||||
activeMeshPeerID: String?,
|
||||
peerSessionStates: Map<String, String>
|
||||
): String? {
|
||||
return activeMeshPeerID?.let(peerSessionStates::get)
|
||||
?: peerSessionStates[conversationID]
|
||||
}
|
||||
@ -1806,7 +1806,11 @@ fun PrivateChatSheet(
|
||||
|
||||
val conversationID = contactResolution.conversationID
|
||||
val messages = privateChats[conversationID] ?: privateChats[peerID] ?: emptyList()
|
||||
val sessionState = activeMeshPeerID?.let { peerSessionStates[it] } ?: peerSessionStates[peerID]
|
||||
val sessionState = resolveConversationSessionState(
|
||||
conversationID = peerID,
|
||||
activeMeshPeerID = activeMeshPeerID,
|
||||
peerSessionStates = peerSessionStates
|
||||
)
|
||||
val fingerprint = activeMeshPeerID?.let { peerFingerprints[it] }
|
||||
?: peerFingerprints[peerID]
|
||||
?: ContactIdentityResolver.fingerprintFromContactConversationId(peerID)
|
||||
@ -1998,8 +2002,21 @@ fun PrivateChatSheet(
|
||||
)
|
||||
}
|
||||
|
||||
// Encryption state, and the verification badge that qualifies it. Both are
|
||||
// read-only for Nostr peers, which have no Noise session at all.
|
||||
if (isVerified) {
|
||||
ConversationHeaderStatus {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Verified,
|
||||
contentDescription = stringResource(
|
||||
R.string.fingerprint_verified_label
|
||||
),
|
||||
modifier = Modifier.size(HeaderIconSize),
|
||||
tint = colorScheme.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the lock nearest the close action: from right to left the security
|
||||
// cluster reads close, encryption, verification, then favorite.
|
||||
if (!isNostrPeer && !isNostrReachableFavorite) {
|
||||
ConversationHeaderAction(
|
||||
onClick = { viewModel.showSecurityVerificationSheet() },
|
||||
@ -2014,20 +2031,6 @@ fun PrivateChatSheet(
|
||||
}
|
||||
}
|
||||
|
||||
if (isVerified) {
|
||||
Box(
|
||||
modifier = Modifier.size(HeaderIconSize),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_spec_check),
|
||||
contentDescription = stringResource(R.string.verify_title),
|
||||
modifier = Modifier.size(HeaderIconSize),
|
||||
tint = colorScheme.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val dismiss = LocalSheetDismiss.current
|
||||
CloseButton(onClick = { dismiss?.invoke() ?: onDismiss() })
|
||||
}
|
||||
|
||||
@ -48,6 +48,7 @@ import com.bitchat.android.R
|
||||
import com.bitchat.android.core.ui.component.button.CloseButton
|
||||
import com.bitchat.android.core.ui.component.sheet.LocalSheetDismiss
|
||||
import com.bitchat.android.core.ui.component.sheet.BitchatBottomSheet
|
||||
import com.bitchat.android.services.ContactDirectory
|
||||
|
||||
private data class SecurityStatusInfo(
|
||||
val text: String,
|
||||
@ -100,7 +101,12 @@ fun SecurityVerificationSheet(
|
||||
val displayName = viewModel.resolvePeerDisplayNameForFingerprint(selectedPeerID)
|
||||
val fingerprint = viewModel.getPeerFingerprintForDisplay(selectedPeerID)
|
||||
val isVerified = fingerprint != null && verifiedFingerprints.contains(fingerprint)
|
||||
val sessionState = peerSessionStates[selectedPeerID]
|
||||
val activeMeshPeerID = ContactDirectory.resolve(selectedPeerID).meshPeerID
|
||||
val sessionState = resolveConversationSessionState(
|
||||
conversationID = selectedPeerID,
|
||||
activeMeshPeerID = activeMeshPeerID,
|
||||
peerSessionStates = peerSessionStates
|
||||
)
|
||||
val statusInfo = buildStatusInfo(
|
||||
isVerified = isVerified,
|
||||
sessionState = sessionState,
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
package com.bitchat.android.ui
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class ConversationSecurityStateTest {
|
||||
@Test
|
||||
fun `active mesh peer session wins for canonical conversation`() {
|
||||
val result = resolveConversationSessionState(
|
||||
conversationID = "contact-alice",
|
||||
activeMeshPeerID = "mesh-alice",
|
||||
peerSessionStates = mapOf(
|
||||
"contact-alice" to "uninitialized",
|
||||
"mesh-alice" to "established"
|
||||
)
|
||||
)
|
||||
|
||||
assertEquals("established", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `conversation session is fallback when live identity has no state`() {
|
||||
val result = resolveConversationSessionState(
|
||||
conversationID = "contact-alice",
|
||||
activeMeshPeerID = "mesh-alice",
|
||||
peerSessionStates = mapOf("contact-alice" to "handshaking")
|
||||
)
|
||||
|
||||
assertEquals("handshaking", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `conversation session resolves when mesh and conversation IDs are identical`() {
|
||||
val result = resolveConversationSessionState(
|
||||
conversationID = "mesh-alice",
|
||||
activeMeshPeerID = "mesh-alice",
|
||||
peerSessionStates = mapOf("mesh-alice" to "established")
|
||||
)
|
||||
|
||||
assertEquals("established", result)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user