mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-26 07:45:21 +00:00
render links normally (#331)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package com.bitchat.android.ui
|
||||
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import android.util.Patterns
|
||||
|
||||
/**
|
||||
* Utilities for parsing special tokens in chat messages (geohashes, etc.).
|
||||
@@ -11,6 +12,7 @@ object MessageSpecialParser {
|
||||
private val standaloneGeohashRegex = Regex("(^|[^A-Za-z0-9_#])#([0-9bcdefghjkmnpqrstuvwxyz]{2,})($|[^A-Za-z0-9_])", RegexOption.IGNORE_CASE)
|
||||
|
||||
data class GeohashMatch(val start: Int, val endExclusive: Int, val geohash: String)
|
||||
data class UrlMatch(val start: Int, val endExclusive: Int, val url: String)
|
||||
|
||||
/**
|
||||
* Finds standalone geohashes within [text]. A match is returned only when
|
||||
@@ -48,6 +50,61 @@ object MessageSpecialParser {
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect URLs in text. Supports http(s) schemes, www.* domains, and bare domains with TLDs.
|
||||
* Trailing punctuation like '.', ',', ')', '!' is trimmed from the match.
|
||||
*/
|
||||
fun findUrls(text: String): List<UrlMatch> {
|
||||
if (text.isEmpty()) return emptyList()
|
||||
val results = mutableListOf<UrlMatch>()
|
||||
|
||||
// 1) Use Android's WEB_URL for robust detection of http(s) and www.*
|
||||
val webUrl = Patterns.WEB_URL
|
||||
val matcher = webUrl.matcher(text)
|
||||
while (matcher.find()) {
|
||||
var start = matcher.start()
|
||||
var endExclusive = matcher.end()
|
||||
var token = text.substring(start, endExclusive)
|
||||
|
||||
// Trim trailing punctuation
|
||||
while (token.isNotEmpty() && token.last() in setOf('.', ',', ';', ':', '!', '?', '\'', '"')) {
|
||||
endExclusive -= 1
|
||||
token = text.substring(start, endExclusive)
|
||||
}
|
||||
results.add(UrlMatch(start, endExclusive, token))
|
||||
}
|
||||
|
||||
// 2) Bare-domain fallback for things WEB_URL can miss (e.g., google.com)
|
||||
val bare = Regex("(?<!@)(?<![A-Za-z0-9_-])([A-Za-z0-9-]+\\.[A-Za-z]{2,}(?:/[A-Za-z0-9@:%._+~#=/?&!$'()*,-]*)?)")
|
||||
for (m in bare.findAll(text)) {
|
||||
val start = m.range.first
|
||||
var endExclusive = m.range.last + 1
|
||||
var token = text.substring(start, endExclusive)
|
||||
|
||||
// Exclude if overlaps any already-found match
|
||||
val overlapsExisting = results.any { start < it.endExclusive && endExclusive > it.start }
|
||||
if (overlapsExisting) continue
|
||||
|
||||
// Skip emails
|
||||
if (token.contains('@')) continue
|
||||
|
||||
// Trim trailing punctuation
|
||||
while (token.isNotEmpty() && token.last() in setOf('.', ',', ';', ':', '!', '?', '\'', '"')) {
|
||||
endExclusive -= 1
|
||||
token = text.substring(start, endExclusive)
|
||||
}
|
||||
|
||||
// Require at least one dot
|
||||
if (!token.contains('.')) continue
|
||||
|
||||
results.add(UrlMatch(start, endExclusive, token))
|
||||
}
|
||||
|
||||
// Sort by start index
|
||||
results.sortBy { it.start }
|
||||
return results
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user