mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-24 23:25:19 +00:00
works
This commit is contained in:
@@ -1302,11 +1302,29 @@ class NostrGeohashService(
|
||||
Log.d(TAG, "🔔 Triggering geohash notification - geohash: $geohash, mention: $isMention, first: $isFirstMessage")
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
// Sanitize mention for notifications: hide '#hash' from @username#hash if not needed
|
||||
val contentForNotification = if (isMention) {
|
||||
var sanitized = content
|
||||
try {
|
||||
val myGeoIdentity = NostrIdentityBridge.deriveIdentity(
|
||||
forGeohash = geohash,
|
||||
context = application
|
||||
)
|
||||
val myDisplay = displayNameForNostrPubkeyUI(myGeoIdentity.publicKeyHex)
|
||||
val needsDisambiguation = myDisplay.contains("#")
|
||||
if (!needsDisambiguation) {
|
||||
val pattern = ("@" + java.util.regex.Pattern.quote(currentNickname) + "#[a-fA-F0-9]{4}\\b").toRegex()
|
||||
sanitized = sanitized.replace(pattern, "@" + currentNickname)
|
||||
}
|
||||
} catch (_: Exception) { }
|
||||
sanitized
|
||||
} else content
|
||||
|
||||
val locationName = getLocationNameForGeohash(geohash)
|
||||
notificationManager.showGeohashNotification(
|
||||
geohash = geohash,
|
||||
senderNickname = senderName,
|
||||
messageContent = content,
|
||||
messageContent = contentForNotification,
|
||||
isMention = isMention,
|
||||
isFirstMessage = isFirstMessage,
|
||||
locationName = locationName
|
||||
|
||||
@@ -5,6 +5,7 @@ import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.bitchat.android.model.BitchatMessage
|
||||
import com.bitchat.android.mesh.BluetoothMeshService
|
||||
@@ -257,6 +258,31 @@ private fun appendIOSFormattedContent(
|
||||
for (match in mentionMatches) {
|
||||
allMatches.add(match.range to "mention")
|
||||
}
|
||||
|
||||
// Add standalone geohash matches (e.g., "#9q") that are not part of another word
|
||||
// We use MessageSpecialParser to find exact ranges; then merge with existing ranges avoiding overlaps
|
||||
val geoMatches = MessageSpecialParser.findStandaloneGeohashes(content)
|
||||
for (gm in geoMatches) {
|
||||
val range = gm.start until gm.endExclusive
|
||||
if (!overlapsMention(range)) {
|
||||
allMatches.add(range to "geohash")
|
||||
}
|
||||
}
|
||||
|
||||
// Remove generic hashtag matches that overlap with detected geohash ranges to avoid duplicate rendering
|
||||
fun rangesOverlap(a: IntRange, b: IntRange): Boolean {
|
||||
return a.first < b.last && a.last > b.first
|
||||
}
|
||||
val geoRanges = allMatches.filter { it.second == "geohash" }.map { it.first }
|
||||
if (geoRanges.isNotEmpty()) {
|
||||
val iterator = allMatches.listIterator()
|
||||
while (iterator.hasNext()) {
|
||||
val (range, type) = iterator.next()
|
||||
if (type == "hashtag" && geoRanges.any { rangesOverlap(range, it) }) {
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allMatches.sortBy { it.first.first }
|
||||
|
||||
@@ -327,7 +353,7 @@ private fun appendIOSFormattedContent(
|
||||
}
|
||||
}
|
||||
"hashtag" -> {
|
||||
// iOS-style: render hashtags like normal content (no special styling)
|
||||
// Render general hashtags like normal content
|
||||
builder.pushStyle(SpanStyle(
|
||||
color = baseColor,
|
||||
fontSize = BASE_FONT_SIZE.sp,
|
||||
@@ -342,6 +368,37 @@ private fun appendIOSFormattedContent(
|
||||
}
|
||||
builder.pop()
|
||||
}
|
||||
else -> {
|
||||
if (type == "geohash") {
|
||||
// Style geohash in blue, underlined, and add click annotation
|
||||
builder.pushStyle(SpanStyle(
|
||||
color = Color(0xFF007AFF),
|
||||
fontSize = BASE_FONT_SIZE.sp,
|
||||
fontWeight = if (isSelf) FontWeight.Bold else FontWeight.SemiBold,
|
||||
textDecoration = TextDecoration.Underline
|
||||
))
|
||||
val start = builder.length
|
||||
builder.append(matchText)
|
||||
val end = builder.length
|
||||
val geohash = matchText.removePrefix("#").lowercase()
|
||||
builder.addStringAnnotation(
|
||||
tag = "geohash_click",
|
||||
annotation = geohash,
|
||||
start = start,
|
||||
end = end
|
||||
)
|
||||
builder.pop()
|
||||
} else {
|
||||
// Fallback: treat as normal text
|
||||
builder.pushStyle(SpanStyle(
|
||||
color = baseColor,
|
||||
fontSize = BASE_FONT_SIZE.sp,
|
||||
fontWeight = if (isSelf) FontWeight.Bold else FontWeight.Normal
|
||||
))
|
||||
builder.append(matchText)
|
||||
builder.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastEnd = range.last + 1
|
||||
|
||||
@@ -21,6 +21,7 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import com.bitchat.android.model.BitchatMessage
|
||||
@@ -199,6 +200,7 @@ private fun MessageTextWithClickableNicknames(
|
||||
|
||||
if (!isSelf && (onNicknameClick != null || onMessageLongPress != null)) {
|
||||
val haptic = LocalHapticFeedback.current
|
||||
val context = LocalContext.current
|
||||
var textLayoutResult by remember { mutableStateOf<TextLayoutResult?>(null) }
|
||||
Text(
|
||||
text = annotatedText,
|
||||
@@ -216,6 +218,33 @@ private fun MessageTextWithClickableNicknames(
|
||||
val nickname = nicknameAnnotations.first().item
|
||||
haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove)
|
||||
onNicknameClick?.invoke(nickname)
|
||||
return@detectTapGestures
|
||||
}
|
||||
// Handle geohash click to teleport
|
||||
val geohashAnnotations = annotatedText.getStringAnnotations(
|
||||
tag = "geohash_click",
|
||||
start = offset,
|
||||
end = offset
|
||||
)
|
||||
if (geohashAnnotations.isNotEmpty()) {
|
||||
val geohash = geohashAnnotations.first().item
|
||||
try {
|
||||
val locationManager = com.bitchat.android.geohash.LocationChannelManager.getInstance(
|
||||
context
|
||||
)
|
||||
// Select appropriate precision based on length
|
||||
val level = when (geohash.length) {
|
||||
in 0..2 -> com.bitchat.android.geohash.GeohashChannelLevel.REGION
|
||||
in 3..4 -> com.bitchat.android.geohash.GeohashChannelLevel.PROVINCE
|
||||
5 -> com.bitchat.android.geohash.GeohashChannelLevel.CITY
|
||||
6 -> com.bitchat.android.geohash.GeohashChannelLevel.NEIGHBORHOOD
|
||||
else -> com.bitchat.android.geohash.GeohashChannelLevel.BLOCK
|
||||
}
|
||||
val channel = com.bitchat.android.geohash.GeohashChannel(level, geohash.lowercase())
|
||||
locationManager.setTeleported(true)
|
||||
locationManager.select(com.bitchat.android.geohash.ChannelID.Location(channel))
|
||||
} catch (_: Exception) { }
|
||||
haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove)
|
||||
}
|
||||
},
|
||||
onLongPress = {
|
||||
|
||||
Reference in New Issue
Block a user