From af5b75cde0edb3ad35da2e724c7eef03f4f8f81f Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Tue, 29 Mar 2022 14:52:21 +0300 Subject: [PATCH] Correct dark theme support with html rendering. --- ElementX.xcodeproj/project.pbxproj | 4 ++ ElementX/Sources/Other/Colors.swift | 37 +++++++++++ .../HTMLParsing/AttributedStringBuilder.swift | 66 ++++++++----------- .../AttributedStringBuilderProtocol.swift | 1 - .../Views/EventBasedTimelineView.swift | 2 +- .../Views/FormattedBodyText.swift | 1 + .../Contents.json | 38 +++++++++++ .../Assets.xcassets/Colors/Contents.json | 6 ++ .../ElementGreen.colorset/Contents.json | 38 +++++++++++ 9 files changed, 154 insertions(+), 39 deletions(-) create mode 100644 ElementX/Sources/Other/Colors.swift create mode 100644 ElementX/Supporting Files/Assets.xcassets/Colors/CodeBlockBackgroundColor.colorset/Contents.json create mode 100644 ElementX/Supporting Files/Assets.xcassets/Colors/Contents.json create mode 100644 ElementX/Supporting Files/Assets.xcassets/Colors/ElementGreen.colorset/Contents.json diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index f3b698bb6..e34b9e9f6 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 18920C1327F2347600A717B5 /* EmoteRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18920C1127F2347600A717B5 /* EmoteRoomTimelineItem.swift */; }; 18920C1627F2E3F400A717B5 /* NoticeRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18920C1427F2E3F400A717B5 /* NoticeRoomTimelineView.swift */; }; 18920C1727F2E3F400A717B5 /* EmoteRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18920C1527F2E3F400A717B5 /* EmoteRoomTimelineView.swift */; }; + 18920C1927F3222E00A717B5 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18920C1827F3222E00A717B5 /* Colors.swift */; }; 18ADC7D527E4B20300A8C953 /* PlaceholderAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18ADC7D427E4B20300A8C953 /* PlaceholderAvatarImage.swift */; }; 18ADC7D827E4B63C00A8C953 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 18ADC7D727E4B63C00A8C953 /* MatrixRustSDK */; }; 18ADC7FA27EB02D900A8C953 /* AttributedStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18ADC7F927EB02D900A8C953 /* AttributedStringBuilder.swift */; }; @@ -158,6 +159,7 @@ 18920C1127F2347600A717B5 /* EmoteRoomTimelineItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineItem.swift; sourceTree = ""; }; 18920C1427F2E3F400A717B5 /* NoticeRoomTimelineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineView.swift; sourceTree = ""; }; 18920C1527F2E3F400A717B5 /* EmoteRoomTimelineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineView.swift; sourceTree = ""; }; + 18920C1827F3222E00A717B5 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; 18ADC7D427E4B20300A8C953 /* PlaceholderAvatarImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlaceholderAvatarImage.swift; sourceTree = ""; }; 18ADC7F927EB02D900A8C953 /* AttributedStringBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttributedStringBuilder.swift; sourceTree = ""; }; 18ADC80427EB1ED100A8C953 /* UIFont+AttributedStringBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIFont+AttributedStringBuilder.m"; sourceTree = ""; }; @@ -509,6 +511,7 @@ 18F2BA7E27D25B4000DD1988 /* Routers */, 18F2BA9B27D25B4000DD1988 /* SwiftUI */, 18F2BA9527D25B4000DD1988 /* WeakDictionary */, + 18920C1827F3222E00A717B5 /* Colors.swift */, ); path = Other; sourceTree = ""; @@ -984,6 +987,7 @@ 18DF7C3127E3608100291672 /* MediaProviderProtocol.swift in Sources */, 18F2BB0127D25B4000DD1988 /* HomeScreenViewModel.swift in Sources */, 18F2BAF027D25B4000DD1988 /* ActivityDismissal.swift in Sources */, + 18920C1927F3222E00A717B5 /* Colors.swift in Sources */, 18F2BADD27D25B4000DD1988 /* KeychainController.swift in Sources */, 18920C1227F2347600A717B5 /* NoticeRoomTimelineItem.swift in Sources */, 18920C1727F2E3F400A717B5 /* EmoteRoomTimelineView.swift in Sources */, diff --git a/ElementX/Sources/Other/Colors.swift b/ElementX/Sources/Other/Colors.swift new file mode 100644 index 000000000..af1d3aa58 --- /dev/null +++ b/ElementX/Sources/Other/Colors.swift @@ -0,0 +1,37 @@ +// +// Colors.swift +// ElementX +// +// Created by Stefan Ceriu on 29/03/2022. +// Copyright © 2022 Element. All rights reserved. +// + +import Foundation +import UIKit +import SwiftUI + +//TODO: Switch this to SwiftGen + +extension Color { + static let elementGreen = Color(ColorNames.elementGreen.rawValue) + static let codeBlockBackgroundColor = Color(ColorNames.codeBlockBackgroundColor.rawValue) +} + +extension UIColor { + static let elementGreen = UIColor(named: ColorNames.elementGreen.rawValue) + static let codeBlockBackgroundColor = UIColor(named: ColorNames.codeBlockBackgroundColor.rawValue) +} + +private enum ColorNames: String { + case elementGreen + case codeBlockBackgroundColor + + var rawValue: String { + switch self { + case .elementGreen: + return "ElementGreen" + case .codeBlockBackgroundColor: + return "CodeBlockBackgroundColor" + } + } +} diff --git a/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilder.swift b/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilder.swift index bad9e616f..aaa510771 100644 --- a/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilder.swift +++ b/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilder.swift @@ -12,6 +12,7 @@ import DTCoreText struct AttributedStringBuilder: AttributedStringBuilderProtocol { private let temporaryBlockquoteMarkingColor = UIColor.magenta + private let temporaryCodeBlockMarkingColor = UIColor.cyan private let linkColor = UIColor.blue private let userIdDetector: NSRegularExpression @@ -40,22 +41,8 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol { } let mutableAttributedString = NSMutableAttributedString(string: string) - - addLinkAttributesToAttributedString(mutableAttributedString) - - return try? AttributedString(mutableAttributedString, including: \.elementX) - } - - func fromMarkdown(_ string: String?) -> AttributedString? { - guard let string = string else { - return nil - } - - guard let mutableAttributedString = try? NSMutableAttributedString(markdown: string) else { - return nil - } - - addLinkAttributesToAttributedString(mutableAttributedString) + addLinks(mutableAttributedString) + removeLinkColors(mutableAttributedString) return try? AttributedString(mutableAttributedString, including: \.elementX) } @@ -74,15 +61,13 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol { } let defaultFont = UIFont.preferredFont(forTextStyle: .body) - let defaultColor = UIColor.black let parsingOptions: [String: Any] = [ DTUseiOS6Attributes: true, DTDefaultFontFamily: defaultFont.familyName, DTDefaultFontName: defaultFont.fontName, DTDefaultFontSize: defaultFont.pointSize, - DTDefaultTextColor: defaultColor, - DTDefaultLinkColor: linkColor, + DTDefaultLinkDecoration: false, DTDefaultStyleSheet: DTCSSStylesheet(styleBlock: self.defaultCSS) as Any ] @@ -99,12 +84,11 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol { } let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString) - - removeDTCoreTextArtifactsFromAttributedString(mutableAttributedString) - + addLinks(mutableAttributedString) + removeLinkColors(mutableAttributedString) + removeDTCoreTextArtifacts(mutableAttributedString) replaceMarkedBlockquotes(mutableAttributedString) - - addLinkAttributesToAttributedString(mutableAttributedString) + replaceMarkedCodeBlocks(mutableAttributedString) return try? AttributedString(mutableAttributedString, including: \.elementX) } @@ -123,13 +107,9 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol { // MARK: - Private private func replaceMarkedBlockquotes(_ attributedString: NSMutableAttributedString) { - // Enumerate all sections marked thanks to `cssToMarkBlockquotes` - // and apply our own attribute instead. - // According to blockquotes in the string, DTCoreText can apply 2 policies: // - define a `DTTextBlocksAttribute` attribute on a
block // - or, just define a `NSBackgroundColorAttributeName` attribute - attributedString.enumerateAttribute(.DTTextBlocks, in: .init(location: 0, length: attributedString.length), options: []) { value, range, _ in guard let value = value as? NSArray, let dtTextBlock = value.firstObject as? DTTextBlock, @@ -151,7 +131,16 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol { } } - private func removeDTCoreTextArtifactsFromAttributedString(_ attributedString: NSMutableAttributedString) { + func replaceMarkedCodeBlocks(_ attributedString: NSMutableAttributedString) { + attributedString.enumerateAttribute(.backgroundColor, in: .init(location: 0, length: attributedString.length), options: []) { value, range, _ in + if let value = value as? UIColor, + value == temporaryCodeBlockMarkingColor { + attributedString.addAttribute(.backgroundColor, value: UIColor.codeBlockBackgroundColor as Any, range: range) + } + } + } + + private func removeDTCoreTextArtifacts(_ attributedString: NSMutableAttributedString) { guard attributedString.length > 0 else { return } @@ -164,7 +153,7 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol { } } - private func addLinkAttributesToAttributedString(_ attributedString: NSMutableAttributedString) { + private func addLinks(_ attributedString: NSMutableAttributedString) { let string = attributedString.string let range = NSRange(location: 0, length: attributedString.string.count) @@ -186,21 +175,24 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol { } let link = string[matchRange].addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) - attributedString.addAttributes([NSAttributedString.Key.link: link as Any, - NSAttributedString.Key.foregroundColor: linkColor], - range: match.range) + attributedString.addAttribute(.link, value: link as Any, range: match.range) } } - private var cssToMarkBlockquotes: String { - return "blockquote {background: \(temporaryBlockquoteMarkingColor.toHexString()); display: block;}" + private func removeLinkColors(_ attributedString: NSMutableAttributedString) { + attributedString.enumerateAttribute(.link, in: .init(location: 0, length: attributedString.length), options: []) { _, range, _ in + attributedString.removeAttribute(.foregroundColor, range: range) + } } private var defaultCSS: String { - cssToMarkBlockquotes + """ + blockquote { + background: \(temporaryBlockquoteMarkingColor.toHexString()); + display: block; + } pre,code { - background-color: #F5F7FA; + background-color: \(temporaryCodeBlockMarkingColor.toHexString()); display: inline; font-family: monospace; white-space: pre; diff --git a/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilderProtocol.swift b/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilderProtocol.swift index 4c253454a..56e12710a 100644 --- a/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilderProtocol.swift +++ b/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilderProtocol.swift @@ -16,7 +16,6 @@ struct AttributedStringBuilderComponent: Hashable { protocol AttributedStringBuilderProtocol { func fromPlain(_ string: String?) -> AttributedString? func fromHTML(_ htmlString: String?) -> AttributedString? - func fromMarkdown(_ string: String?) -> AttributedString? func blockquoteCoalescedComponentsFrom(_ attributedString: AttributedString?) -> [AttributedStringBuilderComponent]? } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Views/EventBasedTimelineView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Views/EventBasedTimelineView.swift index d50111fcb..bb2160a74 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Views/EventBasedTimelineView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Views/EventBasedTimelineView.swift @@ -33,7 +33,7 @@ struct EventBasedTimelineView: View { Image(uiImage: avatar) .resizable() .scaledToFill() - .overlay(Circle().stroke(Color(.sRGB, red: 0.05, green: 0.74, blue: 0.55, opacity: 1.0))) + .overlay(Circle().stroke(Color.elementGreen)) } else { PlaceholderAvatarImage(firstCharacter: String(firstLetter)) } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Views/FormattedBodyText.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Views/FormattedBodyText.swift index 74b87d005..4167ef811 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Views/FormattedBodyText.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Views/FormattedBodyText.swift @@ -29,5 +29,6 @@ struct FormattedBodyText: View { } } } + .tint(.elementGreen) } } diff --git a/ElementX/Supporting Files/Assets.xcassets/Colors/CodeBlockBackgroundColor.colorset/Contents.json b/ElementX/Supporting Files/Assets.xcassets/Colors/CodeBlockBackgroundColor.colorset/Contents.json new file mode 100644 index 000000000..2370ed419 --- /dev/null +++ b/ElementX/Supporting Files/Assets.xcassets/Colors/CodeBlockBackgroundColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "240", + "green" : "240", + "red" : "240" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "100", + "green" : "100", + "red" : "100" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ElementX/Supporting Files/Assets.xcassets/Colors/Contents.json b/ElementX/Supporting Files/Assets.xcassets/Colors/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/ElementX/Supporting Files/Assets.xcassets/Colors/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ElementX/Supporting Files/Assets.xcassets/Colors/ElementGreen.colorset/Contents.json b/ElementX/Supporting Files/Assets.xcassets/Colors/ElementGreen.colorset/Contents.json new file mode 100644 index 000000000..67097973b --- /dev/null +++ b/ElementX/Supporting Files/Assets.xcassets/Colors/ElementGreen.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "139", + "green" : "189", + "red" : "13" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "139", + "green" : "198", + "red" : "13" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +}