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: "\(tagName)>", 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 {