Merge 77646fbae916473523e75d97707d7eb4b29aa6cf into c127eb83ab94c069c32d37530d2faecd381cd2a8

This commit is contained in:
Taksh Kothari 2026-09-14 09:43:25 +05:30 committed by GitHub
commit e80fd844c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 77 additions and 7 deletions

View File

@ -1675,6 +1675,11 @@ class ChatViewModel(
geohashViewModel.blockUserInGeohash(targetNickname)
}
/** True when this Nostr pubkey is blocked for geohash/location chats. */
fun isGeohashPubkeyBlocked(pubkeyHex: String): Boolean {
return dataManager.isGeohashUserBlocked(pubkeyHex.lowercase())
}
// MARK: - Navigation Management
fun showAppInfo() {

View File

@ -562,14 +562,20 @@ class CommandProcessor(
when (val selectedChannel = viewModel.selectedLocationChannel.value) {
is com.bitchat.android.geohash.ChannelID.Mesh,
null -> {
// Mesh channel: use Bluetooth mesh peer nicknames
val peerNicknames = meshService.getPeerNicknames()
peerNicknames.values.filter { it != peerNicknames[meshService.myPeerID] }
meshMentionCandidates(
peerNicknames = meshService.getPeerNicknames(),
myPeerID = meshService.myPeerID.orEmpty(),
isPeerBlocked = privateChatManager::isPeerBlocked
)
}
is com.bitchat.android.geohash.ChannelID.Location -> {
// Location channel: use geohash participants with collision-resistant suffixes
val geohashPeople = viewModel.geohashPeople.value
val geohashPeople = excludeBlockedGeohashPeople(
people = viewModel.geohashPeople.value,
pubkeyOf = { it.id },
isBlocked = viewModel::isGeohashPubkeyBlocked
)
val currentNickname = state.getNicknameValue()
val duplicateNames = duplicateGeohashBaseNames(geohashPeople)
@ -593,9 +599,11 @@ class CommandProcessor(
}
}
} else {
// Fallback to mesh peers if no viewModel available
val peerNicknames = meshService.getPeerNicknames()
peerNicknames.values.filter { it != peerNicknames[meshService.myPeerID] }
meshMentionCandidates(
peerNicknames = meshService.getPeerNicknames(),
myPeerID = meshService.myPeerID.orEmpty(),
isPeerBlocked = privateChatManager::isPeerBlocked
)
}
val filteredNicknames = filterMentionCandidates(peerCandidates, textAfterAt)
@ -719,3 +727,30 @@ internal fun filterMentionCandidates(
.sortedWith(String.CASE_INSENSITIVE_ORDER)
.toList()
}
/**
* Mesh @-mention candidates excluding self and blocked peers (iOS ChatComposerCoordinator parity).
*/
internal fun meshMentionCandidates(
peerNicknames: Map<String, String>,
myPeerID: String,
isPeerBlocked: (String) -> Boolean
): List<String> {
val myNickname = peerNicknames[myPeerID]
return peerNicknames
.filter { (peerID, nick) ->
nick != myNickname && !isPeerBlocked(peerID)
}
.values
.toList()
}
/**
* Drop geohash people whose Nostr pubkey is blocked before building mention tokens.
*/
internal fun <T> excludeBlockedGeohashPeople(
people: List<T>,
pubkeyOf: (T) -> String,
isBlocked: (String) -> Boolean
): List<T> = people.filterNot { isBlocked(pubkeyOf(it)) }

View File

@ -51,4 +51,34 @@ class MentionSuggestionsTest {
fun `mention popup viewport is capped at five rows`() {
assertEquals(5, MaxVisibleMentionSuggestions)
}
@Test
fun `mesh mention candidates exclude self and blocked peers`() {
val nicknames = mapOf(
"aaaa" to "me",
"bbbb" to "alice",
"cccc" to "eve"
)
val candidates = meshMentionCandidates(
peerNicknames = nicknames,
myPeerID = "aaaa",
isPeerBlocked = { it == "cccc" }
)
assertEquals(listOf("alice"), candidates)
}
@Test
fun `geohash mention candidates exclude blocked pubkeys`() {
data class Person(val id: String, val name: String)
val people = listOf(
Person("aaaabbbb", "carol"),
Person("bbbbcccc", "blocked")
)
val kept = excludeBlockedGeohashPeople(
people = people,
pubkeyOf = { it.id },
isBlocked = { it == "bbbbcccc" }
)
assertEquals(listOf("carol"), kept.map { it.name })
}
}