diff --git a/app/src/main/java/com/bitchat/android/nostr/GeohashRepository.kt b/app/src/main/java/com/bitchat/android/nostr/GeohashRepository.kt index 822606c2a..f6fbca78d 100644 --- a/app/src/main/java/com/bitchat/android/nostr/GeohashRepository.kt +++ b/app/src/main/java/com/bitchat/android/nostr/GeohashRepository.kt @@ -43,6 +43,20 @@ class GeohashRepository( }?.key } + fun findPubkeyByShortId(shortId: String): String? { + // First check cached nicknames (fastest) + var found = geoNicknames.keys.firstOrNull { it.startsWith(shortId, ignoreCase = true) } + if (found != null) return found + + // If not found in nicknames (e.g. anon user), check all known participants across all geohashes + for (participants in geohashParticipants.values) { + found = participants.keys.firstOrNull { it.startsWith(shortId, ignoreCase = true) } + if (found != null) return found + } + + return null + } + // peerID alias -> nostr pubkey mapping for geohash DMs and temp aliases private val nostrKeyMapping: MutableMap = mutableMapOf() diff --git a/app/src/main/java/com/bitchat/android/ui/ChatUserSheet.kt b/app/src/main/java/com/bitchat/android/ui/ChatUserSheet.kt index 36ff15a6a..0cfa91ba9 100644 --- a/app/src/main/java/com/bitchat/android/ui/ChatUserSheet.kt +++ b/app/src/main/java/com/bitchat/android/ui/ChatUserSheet.kt @@ -40,6 +40,7 @@ fun ChatUserSheet( val isDark = colorScheme.background.red + colorScheme.background.green + colorScheme.background.blue < 1.5f val standardGreen = if (isDark) Color(0xFF32D74B) else Color(0xFF248A3D) // iOS green val standardBlue = Color(0xFF007AFF) // iOS blue + val standardPurple = if (isDark) Color(0xFFBF5AF2) else Color(0xFFAF52DE) // iOS purple val standardRed = Color(0xFFFF3B30) // iOS red val standardGrey = if (isDark) Color(0xFF8E8E93) else Color(0xFF6D6D70) // iOS grey @@ -92,6 +93,33 @@ fun ChatUserSheet( // Only show user actions for other users' messages or when no message is selected if (selectedMessage?.sender != viewModel.nickname.value) { + // Send private message action + item { + UserActionRow( + title = stringResource(R.string.action_private_message_title, targetNickname), + subtitle = stringResource(R.string.action_private_message_subtitle), + titleColor = standardPurple, + onClick = { + val selectedLocationChannel = viewModel.selectedLocationChannel.value + if (selectedLocationChannel is com.bitchat.android.geohash.ChannelID.Location) { + if (selectedMessage?.senderPeerID?.startsWith("nostr:") == true) { + val shortId = selectedMessage.senderPeerID!!.substring(6) + viewModel.startGeohashDMByShortId(shortId) + } else { + viewModel.startGeohashDMByNickname(targetNickname) + } + } else { + // Mesh chat + val peerID = selectedMessage?.senderPeerID ?: viewModel.getPeerIDForNickname(targetNickname) + if (peerID != null) { + viewModel.showPrivateChatSheet(peerID) + } + } + onDismiss() + } + ) + } + // Slap action item { UserActionRow( diff --git a/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt b/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt index 5456ca4d6..12fa88c50 100644 --- a/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt +++ b/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt @@ -1065,6 +1065,18 @@ class ChatViewModel( } } + fun startGeohashDMByNickname(nickname: String) { + geohashViewModel.startGeohashDMByNickname(nickname) { convKey -> + showPrivateChatSheet(convKey) + } + } + + fun startGeohashDMByShortId(shortId: String) { + geohashViewModel.startGeohashDMByShortId(shortId) { convKey -> + showPrivateChatSheet(convKey) + } + } + fun selectLocationChannel(channel: com.bitchat.android.geohash.ChannelID) { geohashViewModel.selectLocationChannel(channel) } diff --git a/app/src/main/java/com/bitchat/android/ui/GeohashViewModel.kt b/app/src/main/java/com/bitchat/android/ui/GeohashViewModel.kt index 0db0a5800..da0f10aef 100644 --- a/app/src/main/java/com/bitchat/android/ui/GeohashViewModel.kt +++ b/app/src/main/java/com/bitchat/android/ui/GeohashViewModel.kt @@ -273,6 +273,25 @@ class GeohashViewModel( Log.d(TAG, "🗨️ Started geohash DM with ${pubkeyHex} -> ${convKey} (geohash=${gh})") } + fun startGeohashDMByNickname(nickname: String, onStartPrivateChat: (String) -> Unit) { + val pubkey = repo.findPubkeyByNickname(nickname) + if (pubkey != null) { + startGeohashDM(pubkey, onStartPrivateChat) + } else { + Log.w(TAG, "Cannot start geohash DM: nickname '$nickname' not found in repo") + // Optionally notify user + } + } + + fun startGeohashDMByShortId(shortId: String, onStartPrivateChat: (String) -> Unit) { + val pubkey = repo.findPubkeyByShortId(shortId) + if (pubkey != null) { + startGeohashDM(pubkey, onStartPrivateChat) + } else { + Log.w(TAG, "Cannot start geohash DM: shortId '$shortId' not found in repo") + } + } + fun getNostrKeyMapping(): Map = repo.getNostrKeyMapping() fun blockUserInGeohash(targetNickname: String) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9845fd85b..d34dce0a3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -201,7 +201,10 @@ hug %1$s send a friendly hug message block %1$s - block all messages from this user + block all messages from this user + message %1$s + send a private message + #location channels