mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 02:25:20 +00:00
Fix crash in formatMessageAsText: use NSString length for NSRanges and guard slicing before matches to avoid invalid ranges with multi-byte content
This commit is contained in:
@@ -3161,9 +3161,12 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
|
|
||||||
// For extremely long content, render as plain text to avoid heavy regex/layout work,
|
// For extremely long content, render as plain text to avoid heavy regex/layout work,
|
||||||
// unless the content includes Cashu tokens we want to chip-render below
|
// unless the content includes Cashu tokens we want to chip-render below
|
||||||
|
// Compute NSString-backed length for regex/nsrange correctness with multi-byte characters
|
||||||
|
let nsContent = content as NSString
|
||||||
|
let nsLen = nsContent.length
|
||||||
let containsCashuEarly: Bool = {
|
let containsCashuEarly: Bool = {
|
||||||
let rx = Regexes.quickCashuPresence
|
let rx = Regexes.quickCashuPresence
|
||||||
return rx.numberOfMatches(in: content, options: [], range: NSRange(location: 0, length: content.count)) > 0
|
return rx.numberOfMatches(in: content, options: [], range: NSRange(location: 0, length: nsLen)) > 0
|
||||||
}()
|
}()
|
||||||
if (content.count > 4000 || content.hasVeryLongToken(threshold: 1024)) && !containsCashuEarly {
|
if (content.count > 4000 || content.hasVeryLongToken(threshold: 1024)) && !containsCashuEarly {
|
||||||
var plainStyle = AttributeContainer()
|
var plainStyle = AttributeContainer()
|
||||||
@@ -3181,8 +3184,6 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
let lnurlRegex = Regexes.lnurl
|
let lnurlRegex = Regexes.lnurl
|
||||||
let lightningSchemeRegex = Regexes.lightningScheme
|
let lightningSchemeRegex = Regexes.lightningScheme
|
||||||
let detector = Regexes.linkDetector
|
let detector = Regexes.linkDetector
|
||||||
|
|
||||||
let nsLen = content.count
|
|
||||||
let hasMentionsHint = content.contains("@")
|
let hasMentionsHint = content.contains("@")
|
||||||
let hasHashtagsHint = content.contains("#")
|
let hasHashtagsHint = content.contains("#")
|
||||||
let hasURLHint = content.contains("://") || content.contains("www.") || content.contains("http")
|
let hasURLHint = content.contains("://") || content.contains("www.") || content.contains("http")
|
||||||
@@ -3262,17 +3263,19 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
for (range, type) in allMatches {
|
for (range, type) in allMatches {
|
||||||
// Add text before match
|
// Add text before match
|
||||||
if let nsRange = Range(range, in: content) {
|
if let nsRange = Range(range, in: content) {
|
||||||
let beforeText = String(content[lastEnd..<nsRange.lowerBound])
|
if lastEnd < nsRange.lowerBound {
|
||||||
if !beforeText.isEmpty {
|
let beforeText = String(content[lastEnd..<nsRange.lowerBound])
|
||||||
var beforeStyle = AttributeContainer()
|
if !beforeText.isEmpty {
|
||||||
beforeStyle.foregroundColor = baseColor
|
var beforeStyle = AttributeContainer()
|
||||||
beforeStyle.font = isSelf
|
beforeStyle.foregroundColor = baseColor
|
||||||
? .system(size: 14, weight: .bold, design: .monospaced)
|
beforeStyle.font = isSelf
|
||||||
: .system(size: 14, design: .monospaced)
|
? .system(size: 14, weight: .bold, design: .monospaced)
|
||||||
if isMentioned {
|
: .system(size: 14, design: .monospaced)
|
||||||
beforeStyle.font = beforeStyle.font?.bold()
|
if isMentioned {
|
||||||
|
beforeStyle.font = beforeStyle.font?.bold()
|
||||||
|
}
|
||||||
|
result.append(AttributedString(beforeText).mergingAttributes(beforeStyle))
|
||||||
}
|
}
|
||||||
result.append(AttributedString(beforeText).mergingAttributes(beforeStyle))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add styled match
|
// Add styled match
|
||||||
@@ -3380,7 +3383,10 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
result.append(AttributedString(matchText).mergingAttributes(matchStyle))
|
result.append(AttributedString(matchText).mergingAttributes(matchStyle))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastEnd = nsRange.upperBound
|
// Advance lastEnd safely in case of overlaps
|
||||||
|
if lastEnd < nsRange.upperBound {
|
||||||
|
lastEnd = nsRange.upperBound
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user