@@ -250,6 +250,7 @@ struct StreamTextView: View {
250250
251251@available ( iOS 15 , * )
252252public struct LinkDetectionTextView : View {
253+ @Environment ( \. layoutDirection) var layoutDirection
253254
254255 @Injected ( \. colors) var colors
255256 @Injected ( \. fonts) var fonts
@@ -271,16 +272,10 @@ public struct LinkDetectionTextView: View {
271272 self . message = message
272273 }
273274
274- private var markdownEnabled : Bool {
275- utils. messageListConfig. markdownSupportEnabled
276- }
277-
278275 public var body : some View {
279276 Group {
280277 if let displayedText {
281278 Text ( displayedText)
282- } else if markdownEnabled {
283- Text ( text)
284279 } else {
285280 Text ( message. adjustedText)
286281 }
@@ -289,72 +284,63 @@ public struct LinkDetectionTextView: View {
289284 . font ( fonts. body)
290285 . tint ( tintColor)
291286 . onAppear {
292- detectLinks ( for: message)
287+ displayedText = attributedString ( for: message)
293288 }
294289 . onChange ( of: message, perform: { updated in
295- detectLinks ( for: updated)
290+ displayedText = attributedString ( for: updated)
296291 } )
297292 }
298293
299- func detectLinks( for message: ChatMessage ) {
300- guard utils. messageListConfig. localLinkDetectionEnabled else { return }
301- var attributes : [ NSAttributedString . Key : Any ] = [
302- . foregroundColor: textColor ( for: message) ,
303- . font: fonts. body
304- ]
294+ private func attributedString( for message: ChatMessage ) -> AttributedString {
295+ let text = message. adjustedText
305296
306- let additional = utils. messageListConfig. messageDisplayOptions. messageLinkDisplayResolver ( message)
307- for (key, value) in additional {
308- if key == . foregroundColor, let value = value as? UIColor {
309- tintColor = Color ( value)
310- } else {
311- attributes [ key] = value
312- }
297+ // Markdown
298+ let attributes = AttributeContainer ( )
299+ . foregroundColor ( textColor ( for: message) )
300+ . font ( fonts. body)
301+ var attributedString : AttributedString
302+ if utils. messageListConfig. markdownSupportEnabled {
303+ attributedString = utils. markdownFormatter. format (
304+ text,
305+ attributes: attributes,
306+ layoutDirection: layoutDirection
307+ )
308+ } else {
309+ attributedString = AttributedString ( message. adjustedText, attributes: attributes)
313310 }
314-
315- let attributedText = NSMutableAttributedString (
316- string: message. adjustedText,
317- attributes: attributes
318- )
319- let attributedTextString = attributedText. string
320- var containsLinks = false
321-
322- message. mentionedUsers. forEach { user in
323- containsLinks = true
324- let mention = " @ \( user. name ?? user. id) "
325- attributedTextString
326- . ranges ( of: mention, options: [ . caseInsensitive] )
327- . map { NSRange ( $0, in: attributedTextString) }
328- . forEach {
329- let messageId = message. messageId. addingPercentEncoding ( withAllowedCharacters: . urlPathAllowed)
330- if let messageId {
331- attributedText. addAttribute ( . link, value: " getstream://mention/ \( messageId) / \( user. id) " , range: $0)
311+ // Links and mentions
312+ if utils. messageListConfig. localLinkDetectionEnabled {
313+ for user in message. mentionedUsers {
314+ let mention = " @ \( user. name ?? user. id) "
315+ let ranges = attributedString. ranges ( of: mention, options: [ . caseInsensitive] )
316+ for range in ranges {
317+ if let messageId = message. messageId. addingPercentEncoding ( withAllowedCharacters: . urlPathAllowed) ,
318+ let url = URL ( string: " getstream://mention/ \( messageId) / \( user. id) " ) {
319+ attributedString [ range] . link = url
332320 }
333321 }
334- }
335-
336- let range = NSRange ( location: 0 , length: message. adjustedText. utf16. count)
337- linkDetector. links ( in: message. adjustedText) . forEach { textLink in
338- let escapedOriginalText = NSRegularExpression . escapedPattern ( for: textLink. originalText)
339- let pattern = " \\ [([^ \\ ]]+) \\ ] \\ ( \( escapedOriginalText) \\ ) "
340- if let regex = try ? NSRegularExpression ( pattern: pattern) {
341- containsLinks = ( regex. firstMatch (
342- in: message. adjustedText,
343- options: [ ] ,
344- range: range
345- ) == nil ) || !markdownEnabled
346- } else {
347- containsLinks = true
348322 }
349-
350- if !message. adjustedText. contains ( " ]( \( textLink. originalText) ) " ) {
351- containsLinks = true
323+ for link in linkDetector. links ( in: String ( attributedString. characters) ) {
324+ if let attributedStringRange = Range ( link. range, in: attributedString) {
325+ attributedString [ attributedStringRange] . link = link. url
326+ }
352327 }
353- attributedText. addAttribute ( . link, value: textLink. url, range: textLink. range)
354328 }
355-
356- if containsLinks {
357- displayedText = AttributedString ( attributedText)
329+ // Finally change attributes for links (markdown links, text links, mentions)
330+ var linkAttributes = utils. messageListConfig. messageDisplayOptions. messageLinkDisplayResolver ( message)
331+ if !linkAttributes. isEmpty {
332+ var linkAttributeContainer = AttributeContainer ( )
333+ if let uiColor = linkAttributes [ . foregroundColor] as? UIColor {
334+ linkAttributeContainer = linkAttributeContainer. foregroundColor ( Color ( uiColor: uiColor) )
335+ linkAttributes. removeValue ( forKey: . foregroundColor)
336+ }
337+ linkAttributeContainer. merge ( AttributeContainer ( linkAttributes) )
338+ for (value, range) in attributedString. runs [ \. link] {
339+ guard value != nil else { continue }
340+ attributedString [ range] . mergeAttributes ( linkAttributeContainer)
341+ }
358342 }
343+
344+ return attributedString
359345 }
360346}
0 commit comments