@@ -250,6 +250,7 @@ struct StreamTextView: View {
250
250
251
251
@available ( iOS 15 , * )
252
252
public struct LinkDetectionTextView : View {
253
+ @Environment ( \. layoutDirection) var layoutDirection
253
254
254
255
@Injected ( \. colors) var colors
255
256
@Injected ( \. fonts) var fonts
@@ -271,16 +272,10 @@ public struct LinkDetectionTextView: View {
271
272
self . message = message
272
273
}
273
274
274
- private var markdownEnabled : Bool {
275
- utils. messageListConfig. markdownSupportEnabled
276
- }
277
-
278
275
public var body : some View {
279
276
Group {
280
277
if let displayedText {
281
278
Text ( displayedText)
282
- } else if markdownEnabled {
283
- Text ( text)
284
279
} else {
285
280
Text ( message. adjustedText)
286
281
}
@@ -289,72 +284,63 @@ public struct LinkDetectionTextView: View {
289
284
. font ( fonts. body)
290
285
. tint ( tintColor)
291
286
. onAppear {
292
- detectLinks ( for: message)
287
+ displayedText = attributedString ( for: message)
293
288
}
294
289
. onChange ( of: message, perform: { updated in
295
- detectLinks ( for: updated)
290
+ displayedText = attributedString ( for: updated)
296
291
} )
297
292
}
298
293
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
305
296
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)
313
310
}
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
332
320
}
333
321
}
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
348
322
}
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
+ }
352
327
}
353
- attributedText. addAttribute ( . link, value: textLink. url, range: textLink. range)
354
328
}
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
+ }
358
342
}
343
+
344
+ return attributedString
359
345
}
360
346
}
0 commit comments