diff --git a/FlowCrypt/Controllers/Threads/ThreadDetailsViewController+TableView.swift b/FlowCrypt/Controllers/Threads/ThreadDetailsViewController+TableView.swift index a8ea7e780..39b058a31 100644 --- a/FlowCrypt/Controllers/Threads/ThreadDetailsViewController+TableView.swift +++ b/FlowCrypt/Controllers/Threads/ThreadDetailsViewController+TableView.swift @@ -78,7 +78,12 @@ extension ThreadDetailsViewController: ASTableDelegate, ASTableDataSource { guard row > 1 + securityWarningBlockCount else { if processedMessage.text.isHTMLString { return ThreadDetailWebNode( - input: .init(message: processedMessage.text, quote: processedMessage.quote, index: messageIndex) + input: .init( + message: processedMessage.text, + quote: processedMessage.quote, + index: messageIndex, + isEncrypted: processedMessage.type == .encrypted + ) ) } return MessageTextSubjectNode( diff --git a/FlowCryptCommon/Extensions/StringExtensions.swift b/FlowCryptCommon/Extensions/StringExtensions.swift index 9b480ab69..d9b4977c0 100644 --- a/FlowCryptCommon/Extensions/StringExtensions.swift +++ b/FlowCryptCommon/Extensions/StringExtensions.swift @@ -93,15 +93,35 @@ public extension String { return "tag.fill" } } - + + /// A regex that finds an opening HTML tag and captures its name. + private static let htmlTagRegex: NSRegularExpression? = { + try? NSRegularExpression( + pattern: "<([A-Za-z][A-Za-z0-9]*)\\b[^>]*>", + options: .caseInsensitive + ) + }() + + /// Returns `true` if this string contains at least one full HTML element + /// (an opening `` **and** a matching ``). + /// Email‑style `<…@…>` or other angle‑bracketed text won’t count. var isHTMLString: Bool { - do { - let regex = try NSRegularExpression(pattern: "<[a-z][\\s\\S]*>", options: .caseInsensitive) - let range = NSRange(startIndex..., in: self) - return regex.firstMatch(in: self, options: [], range: range) != nil - } catch { - return false - } + guard + let regex = Self.htmlTagRegex, + let match = regex.firstMatch( + in: self, + options: [], + range: NSRange(startIndex.. 1, + let nameRange = Range(match.range(at: 1), in: self) + else { + return false + } + + // Extract the tag name and look for its closing tag + let tagName = String(self[nameRange]) + return range(of: "", options: .caseInsensitive) != nil } func removingHtmlTags() -> String { diff --git a/FlowCryptUI/Cell Nodes/ThreadDetailWebNode.swift b/FlowCryptUI/Cell Nodes/ThreadDetailWebNode.swift index a29a3747e..0b56d31fe 100644 --- a/FlowCryptUI/Cell Nodes/ThreadDetailWebNode.swift +++ b/FlowCryptUI/Cell Nodes/ThreadDetailWebNode.swift @@ -13,11 +13,13 @@ public final class ThreadDetailWebNode: CellNode { let message: String? let quote: String? let index: Int + let isEncrypted: Bool - public init(message: String?, quote: String?, index: Int) { + public init(message: String?, quote: String?, index: Int, isEncrypted: Bool) { self.message = message self.quote = quote self.index = index + self.isEncrypted = isEncrypted } } @@ -50,7 +52,7 @@ public final class ThreadDetailWebNode: CellNode { self.input = input super.init() - addLeftBorder(width: .threadLeftBorderWidth, color: .plainTextBorder) + addLeftBorder(width: .threadLeftBorderWidth, color: input.isEncrypted ? .main : .plainTextBorder) } private func getFormattedHtml(html: String?) -> String {