From d6fe2d7848a3d107d947d165b4f87670e4ba12e8 Mon Sep 17 00:00:00 2001 From: Egor Egorov Date: Wed, 16 Aug 2023 15:20:16 +0300 Subject: [PATCH] Reworked styles for chat message views Added supporting for borders and gradiented backgrounds --- GliaWidgets.xcodeproj/project.pbxproj | 36 ++++ GliaWidgets/ColorType.swift | 9 + .../Sources/Extensions/UIColor+Hex.swift | 8 +- .../Theme/Chat/Theme.ChatMessageStyle.swift | 67 ++++++ .../Chat/Theme.ChatTextContentStyle.swift | 58 ++++++ .../Theme/Chat/Theme.ChoiceCardStyle.swift | 191 ++++++++++++++++++ .../Chat/Theme.OperatorChatMessageStyle.swift | 117 +++++++++++ .../Theme/Chat/Theme.SystemMessageStyle.swift | 107 ++++++++++ .../Chat/Theme.VisitorChatMessageStyle.swift | 129 ++++++++++++ .../Sources/Theme/Survey/Theme+Survey.swift | 13 +- .../Survey/Theme.Button.Accessibility.swift | 15 +- .../Survey/Theme.Survey.BooleanQuestion.swift | 12 +- .../Survey/Theme.Survey.InputQuestion.swift | 15 +- .../Survey/Theme.Survey.ScaleQuestion.swift | 12 +- .../Survey/Theme.Survey.SingleQuestion.swift | 6 +- GliaWidgets/Sources/Theme/Theme+Button.swift | 3 +- GliaWidgets/Sources/Theme/Theme+Chat.swift | 146 +++++++------ GliaWidgets/Sources/Theme/Theme+Gva.swift | 16 +- GliaWidgets/Sources/Theme/Theme+Text.swift | 24 ++- .../View/Chat/ChatStyle.Deprecated.swift | 117 +++++++++++ GliaWidgets/Sources/View/Chat/ChatStyle.swift | 36 ++-- GliaWidgets/Sources/View/Chat/ChatView.swift | 12 +- .../GalleryCard/GvaGalleryCardView.swift | 40 ++-- .../Chat/GVA/GvaPersistentButtonStyle.swift | 11 +- .../Chat/GVA/GvaPersistentButtonView.swift | 2 +- .../View/Chat/GVA/GvaResponseTextView.swift | 15 +- .../GVA/QuickReply/QuickReplyButtonCell.swift | 19 +- .../View/Chat/Message/ChatMessageStyle.swift | 6 + .../View/Chat/Message/ChatMessageView.swift | 16 +- .../ChoiceCard/ChoiceCardOptionView.swift | 38 ++-- .../Content/ChoiceCard/ChoiceCardStyle.swift | 72 +++++++ .../Content/ChoiceCard/ChoiceCardView.swift | 38 +++- .../Content/Text/ChatTextContentView.swift | 53 +++-- .../Message/OperatorChatMessageStyle.swift | 25 +++ .../Message/OperatorChatMessageView.swift | 15 +- .../Chat/Message/SystemMessageStyle.swift | 25 +++ .../View/Chat/Message/SystemMessageView.swift | 15 +- .../Message/VisitorChatMessageStyle.swift | 31 +++ .../Chat/Message/VisitorChatMessageView.swift | 19 +- 39 files changed, 1399 insertions(+), 190 deletions(-) create mode 100644 GliaWidgets/Sources/Theme/Chat/Theme.ChatMessageStyle.swift create mode 100644 GliaWidgets/Sources/Theme/Chat/Theme.ChatTextContentStyle.swift create mode 100644 GliaWidgets/Sources/Theme/Chat/Theme.ChoiceCardStyle.swift create mode 100644 GliaWidgets/Sources/Theme/Chat/Theme.OperatorChatMessageStyle.swift create mode 100644 GliaWidgets/Sources/Theme/Chat/Theme.SystemMessageStyle.swift create mode 100644 GliaWidgets/Sources/Theme/Chat/Theme.VisitorChatMessageStyle.swift create mode 100644 GliaWidgets/Sources/View/Chat/ChatStyle.Deprecated.swift diff --git a/GliaWidgets.xcodeproj/project.pbxproj b/GliaWidgets.xcodeproj/project.pbxproj index fd960d95b..0fa2bd116 100644 --- a/GliaWidgets.xcodeproj/project.pbxproj +++ b/GliaWidgets.xcodeproj/project.pbxproj @@ -377,6 +377,13 @@ 8491AF0A2A78FED700CC3E72 /* GvaGalleryListViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8491AF092A78FED700CC3E72 /* GvaGalleryListViewStyle.swift */; }; 8491AF192A7C140F00CC3E72 /* GvaGalleryCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8491AF182A7C140F00CC3E72 /* GvaGalleryCardView.swift */; }; 8491AF212A7D1F7900CC3E72 /* ChatView.GvaGallery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8491AF202A7D1F7900CC3E72 /* ChatView.GvaGallery.swift */; }; + 8491AF0D2A7A9CB900CC3E72 /* Theme.VisitorChatMessageStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8491AF0C2A7A9CB900CC3E72 /* Theme.VisitorChatMessageStyle.swift */; }; + 8491AF132A7ACC5400CC3E72 /* Theme.OperatorChatMessageStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8491AF122A7ACC5400CC3E72 /* Theme.OperatorChatMessageStyle.swift */; }; + 8491AF152A7ACDB000CC3E72 /* Theme.SystemMessageStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8491AF142A7ACDB000CC3E72 /* Theme.SystemMessageStyle.swift */; }; + 8491AF172A7AD1D300CC3E72 /* Theme.ChoiceCardStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8491AF162A7AD1D300CC3E72 /* Theme.ChoiceCardStyle.swift */; }; + 8491AF1D2A7D01BF00CC3E72 /* Theme.ChatMessageStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8491AF1C2A7D01BF00CC3E72 /* Theme.ChatMessageStyle.swift */; }; + 8491AF1F2A7D027D00CC3E72 /* Theme.ChatTextContentStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8491AF1E2A7D027D00CC3E72 /* Theme.ChatTextContentStyle.swift */; }; + 8491AF232A8A3EA800CC3E72 /* ChatStyle.Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8491AF222A8A3EA800CC3E72 /* ChatStyle.Deprecated.swift */; }; 84A318A12869ECFC00CA1DE5 /* Unavailable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A318A02869ECFC00CA1DE5 /* Unavailable.swift */; }; 84CFB7732822700000167258 /* Theme.Button.Accessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CFB7722822700000167258 /* Theme.Button.Accessibility.swift */; }; 84D2292B28C61C8D00F64FE7 /* WKNavigationPolicyProvider.CustomResponseCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D2292A28C61C8D00F64FE7 /* WKNavigationPolicyProvider.CustomResponseCard.swift */; }; @@ -1073,6 +1080,13 @@ 8491AF092A78FED700CC3E72 /* GvaGalleryListViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GvaGalleryListViewStyle.swift; sourceTree = ""; }; 8491AF182A7C140F00CC3E72 /* GvaGalleryCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GvaGalleryCardView.swift; sourceTree = ""; }; 8491AF202A7D1F7900CC3E72 /* ChatView.GvaGallery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.GvaGallery.swift; sourceTree = ""; }; + 8491AF0C2A7A9CB900CC3E72 /* Theme.VisitorChatMessageStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.VisitorChatMessageStyle.swift; sourceTree = ""; }; + 8491AF122A7ACC5400CC3E72 /* Theme.OperatorChatMessageStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.OperatorChatMessageStyle.swift; sourceTree = ""; }; + 8491AF142A7ACDB000CC3E72 /* Theme.SystemMessageStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.SystemMessageStyle.swift; sourceTree = ""; }; + 8491AF162A7AD1D300CC3E72 /* Theme.ChoiceCardStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.ChoiceCardStyle.swift; sourceTree = ""; }; + 8491AF1C2A7D01BF00CC3E72 /* Theme.ChatMessageStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.ChatMessageStyle.swift; sourceTree = ""; }; + 8491AF1E2A7D027D00CC3E72 /* Theme.ChatTextContentStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.ChatTextContentStyle.swift; sourceTree = ""; }; + 8491AF222A8A3EA800CC3E72 /* ChatStyle.Deprecated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatStyle.Deprecated.swift; sourceTree = ""; }; 84A318A02869ECFC00CA1DE5 /* Unavailable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Unavailable.swift; sourceTree = ""; }; 84CFB7722822700000167258 /* Theme.Button.Accessibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.Button.Accessibility.swift; sourceTree = ""; }; 84D2292A28C61C8D00F64FE7 /* WKNavigationPolicyProvider.CustomResponseCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKNavigationPolicyProvider.CustomResponseCard.swift; sourceTree = ""; }; @@ -1938,6 +1952,7 @@ isa = PBXGroup; children = ( 1AC7A7AB2587849200567FF8 /* Alert */, + 8491AF0B2A7A9C9200CC3E72 /* Chat */, 75FF151527F4E11000FE7BE2 /* Survey */, 1A60AF7925656E0500E53F53 /* Theme.swift */, 9A19926D27D3BB7800161AAE /* Theme.Mock.swift */, @@ -2053,6 +2068,7 @@ C0175A262A67D431001FACDE /* GVA */, 1AC7A7822583B65B00567FF8 /* Cells */, 9AB3402627FCDD92006E0FE2 /* ChatStyle.Accessibility.swift */, + 8491AF222A8A3EA800CC3E72 /* ChatStyle.Deprecated.swift */, 1A60AFE725669C5000E53F53 /* ChatStyle.swift */, 1A60AFDC25669A4200E53F53 /* ChatView.swift */, C0175A1C2A5D4226001FACDE /* ChatView.TableView.swift */, @@ -3152,6 +3168,19 @@ path = GalleryCard; sourceTree = ""; }; + 8491AF0B2A7A9C9200CC3E72 /* Chat */ = { + isa = PBXGroup; + children = ( + 8491AF1C2A7D01BF00CC3E72 /* Theme.ChatMessageStyle.swift */, + 8491AF0C2A7A9CB900CC3E72 /* Theme.VisitorChatMessageStyle.swift */, + 8491AF122A7ACC5400CC3E72 /* Theme.OperatorChatMessageStyle.swift */, + 8491AF142A7ACDB000CC3E72 /* Theme.SystemMessageStyle.swift */, + 8491AF162A7AD1D300CC3E72 /* Theme.ChoiceCardStyle.swift */, + 8491AF1E2A7D027D00CC3E72 /* Theme.ChatTextContentStyle.swift */, + ); + path = Chat; + sourceTree = ""; + }; 84D2292C28C61DE000F64FE7 /* WKNavigationPolicyProvider */ = { isa = PBXGroup; children = ( @@ -4050,6 +4079,7 @@ 9A19926A27D3BA8700161AAE /* ViewFactory.Environment.Interface.swift in Sources */, 1A0EDF8225E8E0150076D1AD /* PopoverViewController.swift in Sources */, 84265E4B298D7B1900D65842 /* CallVisualizer.Coordinator.swift in Sources */, + 8491AF172A7AD1D300CC3E72 /* Theme.ChoiceCardStyle.swift in Sources */, C0D2F07629A4E2CE00803B47 /* ConnectOperatorStyle.Mock.swift in Sources */, 75940987298D38C2008B173A /* VisitorCodeViewModel+Environment.swift in Sources */, 1A60AFBC256682C800E53F53 /* SubFlowCoordinator.swift in Sources */, @@ -4089,6 +4119,7 @@ 1A0C9A7925C16F9700815406 /* Theme+AlertConfiguration.swift in Sources */, AF10ED8F29BF849A00E85309 /* UnreadMessageDividerView.swift in Sources */, 1AC8AADD25D419CE00DAFE50 /* VideoStreamView.swift in Sources */, + 8491AF152A7ACDB000CC3E72 /* Theme.SystemMessageStyle.swift in Sources */, 1A4AD3C7256E863900468BFB /* ThemeColor.swift in Sources */, 756B8B1E2996BAEA001D2BB2 /* HeaderButton.Props.swift in Sources */, 9A1992DB27D6241400161AAE /* ImageView.Cache.Interface.swift in Sources */, @@ -4103,6 +4134,7 @@ 846429802A45A1F200943BD6 /* OfferPresenter.swift in Sources */, 1A4AF5D325AEF543002CD0F4 /* AlertViewController+Confirmation.swift in Sources */, 755D187B29A6A7180009F5E8 /* WelcomeStyle+TitleImageStyle.swift in Sources */, + 8491AF232A8A3EA800CC3E72 /* ChatStyle.Deprecated.swift in Sources */, 1AFB1E7825F8B26800CA460D /* ChatTextContentStyle.swift in Sources */, 1AFB1E7425F8B00B00CA460D /* ChatTextContentView.swift in Sources */, 845876AE282A95DE007AC3DF /* InputQuestionView.Props.Accessibility.swift in Sources */, @@ -4124,6 +4156,7 @@ 3100D92C296E946600DEC9CE /* SecureConversations.ConfirmationViewController.swift in Sources */, 9A8130BB27D7A41000220BBD /* FileUpload.Environment.Interface.swift in Sources */, 3100D92B296E946600DEC9CE /* SecureConversations+ConfirmationStyle.swift in Sources */, + 8491AF132A7ACC5400CC3E72 /* Theme.OperatorChatMessageStyle.swift in Sources */, 845E2F93283FB6D000C04D56 /* Theme.Survey.Accessibility.swift in Sources */, 75940959298D386F008B173A /* UIStackView.Extensions.swift in Sources */, 1A0C9A9125C41AB900815406 /* CallButtonBar.swift in Sources */, @@ -4379,6 +4412,7 @@ 1AA738AE2578E0D500E1120F /* ConnectAnimationView.swift in Sources */, 754CC61627E2816F005676E9 /* Survey.InputQuestionView.swift in Sources */, 9A1992DF27D62C2E00161AAE /* ImageView.Cache.Mock.swift in Sources */, + 8491AF0D2A7A9CB900CC3E72 /* Theme.VisitorChatMessageStyle.swift in Sources */, 1A2DA72D25EF9DD900032611 /* FileUpload.swift in Sources */, C499A57E26382FAA009962AC /* UnreadMessageIndicatorView.swift in Sources */, 31DB0C01287C2EFC00FB288E /* StaticValues.swift in Sources */, @@ -4429,6 +4463,7 @@ 75B7BD802A39D5A70060794D /* Layoutable.swift in Sources */, 845876AB282A959C007AC3DF /* SingleChoiceQuestionView.Props.Accessibility.swift in Sources */, 9AB3402327FC859E006E0FE2 /* CallButtonStyle.StateStyle.Accessibility.swift in Sources */, + 8491AF1F2A7D027D00CC3E72 /* Theme.ChatTextContentStyle.swift in Sources */, 84D2293C28D1FC4B00F64FE7 /* ChatViewModel+CustomCard.swift in Sources */, 84265DF02983E62100D65842 /* Theme+ScreenSharing.swift in Sources */, 6E9C01AD26D3B8B500EBE1D4 /* OperatorTypingIndicatorView.swift in Sources */, @@ -4440,6 +4475,7 @@ 7529F2B427E1D503004D3581 /* Survey.swift in Sources */, 84681AA12A66B9F100DD7406 /* SelfSizingCollectionView.swift in Sources */, 1A5F494B25CA86CA003E3678 /* Call.swift in Sources */, + 8491AF1D2A7D01BF00CC3E72 /* Theme.ChatMessageStyle.swift in Sources */, 84265E65298D7B2900D65842 /* ScreenSharingView.swift in Sources */, 9A1992ED27D6C19E00161AAE /* FileSystemStorage.Environment.Mock.swift in Sources */, 1AFB1E6825F7AE3C00CA460D /* ChatAttachment.swift in Sources */, diff --git a/GliaWidgets/ColorType.swift b/GliaWidgets/ColorType.swift index f0a3634ff..8eebc2a38 100644 --- a/GliaWidgets/ColorType.swift +++ b/GliaWidgets/ColorType.swift @@ -16,4 +16,13 @@ public enum ColorType: Equatable { return false } } + + var color: UIColor { + switch self { + case let .fill(color): + return color + case let .gradient(colors): + return UIColor(cgColor: colors.first ?? .clear) + } + } } diff --git a/GliaWidgets/Sources/Extensions/UIColor+Hex.swift b/GliaWidgets/Sources/Extensions/UIColor+Hex.swift index 02e34b592..a41017836 100644 --- a/GliaWidgets/Sources/Extensions/UIColor+Hex.swift +++ b/GliaWidgets/Sources/Extensions/UIColor+Hex.swift @@ -27,7 +27,7 @@ extension UIColor { self.init(red: components.R, green: components.G, blue: components.B, alpha: components.a) } - func toHex(alpha: Bool = false) -> String? { + func toHex(alpha: Bool = true) -> String? { guard let components = cgColor.components, components.count >= 3 else { return nil } @@ -48,3 +48,9 @@ extension UIColor { } } } + +extension CGColor { + static var clear: CGColor { + UIColor.clear.cgColor + } +} diff --git a/GliaWidgets/Sources/Theme/Chat/Theme.ChatMessageStyle.swift b/GliaWidgets/Sources/Theme/Chat/Theme.ChatMessageStyle.swift new file mode 100644 index 000000000..0b8eeb9bb --- /dev/null +++ b/GliaWidgets/Sources/Theme/Chat/Theme.ChatMessageStyle.swift @@ -0,0 +1,67 @@ +import UIKit + +extension Theme { + /// Internal base style of a chat message. + struct ChatMessageStyle { + /// Style of the message text. + let text: Text + /// Style of the message background. + let background: Layer + /// Style of the image content. + let imageFile: ChatImageFileContentStyle + /// Style of the image content. + let fileDownload: ChatFileDownloadStyle + /// Accessibility related properties. + let accessibility: Accessibility + + /// + /// - Parameters: + /// - text: Style of the message text. + /// - background: Style of the message background. + /// - imageFile: Style of the image content. + /// - fileDownload: Style of the downloadable file content. + /// - accessibility: Accessibility related properties. + /// + init( + text: Text, + background: Layer, + imageFile: ChatImageFileContentStyle, + fileDownload: ChatFileDownloadStyle, + accessibility: Accessibility = .unsupported + ) { + self.text = text + self.background = background + self.imageFile = imageFile + self.fileDownload = fileDownload + self.accessibility = accessibility + } + } +} + +extension Theme.ChatMessageStyle { + /// Accessibility properties for ChatMessageStyle. + struct Accessibility: Equatable { + /// Accessibility value. + var value: String + /// Flag that provides font dynamic type by setting `adjustsFontForContentSizeCategory` for component that supports it. + var isFontScalingEnabled: Bool + + /// + /// - Parameters: + /// - value: Accessibility value. + /// - isFontScalingEnabled: Flag that provides font dynamic type by setting `adjustsFontForContentSizeCategory` for component that supports it. + init( + value: String = "", + isFontScalingEnabled: Bool + ) { + self.value = value + self.isFontScalingEnabled = isFontScalingEnabled + } + + /// Accessibility is not supported intentionally. + static let unsupported = Self( + value: "", + isFontScalingEnabled: false + ) + } +} diff --git a/GliaWidgets/Sources/Theme/Chat/Theme.ChatTextContentStyle.swift b/GliaWidgets/Sources/Theme/Chat/Theme.ChatTextContentStyle.swift new file mode 100644 index 000000000..b70111f22 --- /dev/null +++ b/GliaWidgets/Sources/Theme/Chat/Theme.ChatTextContentStyle.swift @@ -0,0 +1,58 @@ +import UIKit + +public extension Theme { + /// Style of a text message. + struct ChatTextContentStyle { + /// Style of the message text. + public var text: Text + /// Style of the content view. + public var background: Layer + /// Accessibility related properties. + public var accessibility: Accessibility + + /// + /// - Parameters: + /// - text: Style of the message text. + /// - background: Style of the content view. + /// - accessibility: Accessibility related properties. + /// + public init( + text: Text, + background: Layer, + accessibility: Accessibility = .unsupported + ) { + self.text = text + self.background = background + self.accessibility = accessibility + } + } +} + +extension Theme.ChatTextContentStyle { + /// Accessibility properties for ChatTextContentStyle. + public struct Accessibility: Equatable { + /// Accessibility value. + public var value: String + + /// Flag that provides font dynamic type by setting `adjustsFontForContentSizeCategory` for component that supports it. + public var isFontScalingEnabled: Bool + + /// + /// - Parameters: + /// - value: Accessibility value. + /// - isFontScalingEnabled: Flag that provides font dynamic type by setting `adjustsFontForContentSizeCategory` for component that supports it. + public init( + value: String = "", + isFontScalingEnabled: Bool + ) { + self.value = value + self.isFontScalingEnabled = isFontScalingEnabled + } + + /// Accessibility is not supported intentionally. + public static let unsupported = Self( + value: "", + isFontScalingEnabled: false + ) + } +} diff --git a/GliaWidgets/Sources/Theme/Chat/Theme.ChoiceCardStyle.swift b/GliaWidgets/Sources/Theme/Chat/Theme.ChoiceCardStyle.swift new file mode 100644 index 000000000..7b834fde8 --- /dev/null +++ b/GliaWidgets/Sources/Theme/Chat/Theme.ChoiceCardStyle.swift @@ -0,0 +1,191 @@ +import UIKit + +public extension Theme { + /// Style of the choice card sent to the visitor by the AI engine. + struct ChoiceCardStyle { + /// Style of the message text. + public var text: Text + /// Style of the message background. + public var background: Layer + /// Style of the image content. + public var imageFile: ChatImageFileContentStyle + /// Style of the image content. + public var fileDownload: ChatFileDownloadStyle + /// Style of the operator's image. + public var operatorImage: UserImageStyle + /// Styles of the choice card's answer options. + public var choiceOption: Option + /// Accessibility related properties. + public var accessibility: Accessibility + + /// + /// - Parameters: + /// - text: Style of the message text. + /// - background: Style of the message background. + /// - imageFile: Style of the image content. + /// - fileDownload: Style of the downloadable file content. + /// - operatorImage: Style of the operator's image. + /// - choiceOption: Styles of the choice card's answer options. + /// - accessibility: Accessibility related properties. + /// + public init( + text: Text, + background: Layer, + imageFile: ChatImageFileContentStyle, + fileDownload: ChatFileDownloadStyle, + operatorImage: UserImageStyle, + choiceOption: Option, + accessibility: Accessibility = .unsupported + ) { + self.text = text + self.background = background + self.imageFile = imageFile + self.fileDownload = fileDownload + self.operatorImage = operatorImage + self.choiceOption = choiceOption + self.accessibility = accessibility + } + + mutating func apply( + configuration: RemoteConfiguration.ResponseCard?, + assetsBuilder: RemoteConfiguration.AssetsBuilder + ) { + background.apply(configuration: configuration?.background) + + choiceOption.apply( + configuration: configuration?.option, + assetsBuilder: assetsBuilder + ) + + text.apply( + configuration: configuration?.text, + assetsBuilder: assetsBuilder + ) + + operatorImage.apply(configuration: configuration?.userImage) + } + } +} + +extension Theme.ChoiceCardStyle { + /// Accessibility properties for ChoiceCardStyle. + public struct Accessibility: Equatable { + /// Accessibility label for image. + public var imageLabel: String + + /// + /// - Parameters imageLabel: Accessibility label for image. + public init(imageLabel: String) { + self.imageLabel = imageLabel + } + + /// Accessibility is not supported intentionally. + public static let unsupported = Self(imageLabel: "") + } +} + +public extension Theme.ChoiceCardStyle { + struct Option { + /// Style of an option in an active state - choice card has not been answered by the visitor yet. + public var normal: Theme.Button + /// Style of an option in a selected state - choice card has already been answered by the visitor. + public var selected: Theme.Button + /// Style of an option in a disabled state - choice card has already been answered by the visitor or the choice card became inactive (e.g. engagement ended). + public var disabled: Theme.Button + + /// + /// - Parameters: + /// - normal: Style of an option in an active state - choice card has not been answered by the visitor yet. + /// - selected: Style of an option in a selected state - choice card has already been answered by the visitor. + /// - disabled: Style of an option in a disabled state - choice card has already been answered by the visitor or the choice card became inactive (e.g. engagement ended). + /// + public init( + normal: Theme.Button, + selected: Theme.Button, + disabled: Theme.Button + ) { + self.normal = normal + self.selected = selected + self.disabled = disabled + } + + mutating func apply( + configuration: RemoteConfiguration.ResponseCardOption?, + assetsBuilder: RemoteConfiguration.AssetsBuilder + ) { + normal.apply( + configuration: configuration?.normal, + assetsBuilder: assetsBuilder + ) + selected.apply( + configuration: configuration?.selected, + assetsBuilder: assetsBuilder + ) + disabled.apply( + configuration: configuration?.disabled, + assetsBuilder: assetsBuilder + ) + } + } +} + +extension Theme.ChoiceCardStyle { + func toOldChoiceCardStyle() -> ChoiceCardStyle { + return ChoiceCardStyle( + mainText: .init( + textFont: text.font, + textColor: UIColor(hex: text.color), + textStyle: text.textStyle, + backgroundColor: background.background?.color ?? .clear, + cornerRadius: background.cornerRadius, + accessibility: .init(isFontScalingEnabled: text.accessibility.isFontScalingEnabled) + ), + frameColor: UIColor(cgColor: background.borderColor), + borderWidth: background.borderWidth, + cornerRadius: background.cornerRadius, + backgroundColor: background.background?.color ?? .clear, + imageFile: imageFile, + fileDownload: fileDownload, + operatorImage: operatorImage, + choiceOption: .init( + normal: .init( + textFont: choiceOption.normal.title.font, + textColor: UIColor(hex: choiceOption.normal.title.color), + textStyle: choiceOption.normal.title.textStyle, + backgroundColor: choiceOption.normal.background.color ?? .clear, + borderColor: UIColor(hex: choiceOption.normal.borderColor ?? ""), + borderWidth: choiceOption.normal.borderWidth, + accessibility: .init( + value: choiceOption.normal.accessibility.label, + isFontScalingEnabled: choiceOption.normal.accessibility.isFontScalingEnabled + ) + ), + selected: .init( + textFont: choiceOption.selected.title.font, + textColor: UIColor(hex: choiceOption.selected.title.color), + textStyle: choiceOption.selected.title.textStyle, + backgroundColor: choiceOption.selected.background.color ?? .clear, + borderColor: UIColor(hex: choiceOption.selected.borderColor ?? ""), + borderWidth: choiceOption.selected.borderWidth, + accessibility: .init( + value: choiceOption.selected.accessibility.label, + isFontScalingEnabled: choiceOption.selected.accessibility.isFontScalingEnabled + ) + ), + disabled: .init( + textFont: choiceOption.disabled.title.font, + textColor: UIColor(hex: choiceOption.disabled.title.color), + textStyle: choiceOption.disabled.title.textStyle, + backgroundColor: choiceOption.disabled.background.color ?? .clear, + borderColor: UIColor(hex: choiceOption.disabled.borderColor ?? ""), + borderWidth: choiceOption.disabled.borderWidth, + accessibility: .init( + value: choiceOption.disabled.accessibility.label, + isFontScalingEnabled: choiceOption.disabled.accessibility.isFontScalingEnabled + ) + ) + ), + accessibility: .init(imageLabel: accessibility.imageLabel) + ) + } +} diff --git a/GliaWidgets/Sources/Theme/Chat/Theme.OperatorChatMessageStyle.swift b/GliaWidgets/Sources/Theme/Chat/Theme.OperatorChatMessageStyle.swift new file mode 100644 index 000000000..41b7e7093 --- /dev/null +++ b/GliaWidgets/Sources/Theme/Chat/Theme.OperatorChatMessageStyle.swift @@ -0,0 +1,117 @@ +import UIKit + +public extension Theme { + /// Style of an operator's message. + struct OperatorMessageStyle { + /// Style of the message text. + public var text: Text + /// Style of the message background. + public var background: Layer + /// Style of the image content. + public var imageFile: ChatImageFileContentStyle + /// Style of the image content. + public var fileDownload: ChatFileDownloadStyle + /// Style of the operator's image. + public var operatorImage: UserImageStyle + /// Accessibility related properties. + public var accessibility: Accessibility + + /// + /// - Parameters: + /// - text: Style of the message text. + /// - background: Style of the message background. + /// - imageFile: Style of the image content. + /// - fileDownload: Style of the downloadable file content. + /// - operatorImage: Style of the operator's image. + /// - accessibility: Accessibility related properties. + /// + public init( + text: Text, + background: Layer, + imageFile: ChatImageFileContentStyle, + fileDownload: ChatFileDownloadStyle, + operatorImage: UserImageStyle, + accessibility: Accessibility = .unsupported + ) { + self.text = text + self.background = background + self.imageFile = imageFile + self.fileDownload = fileDownload + self.operatorImage = operatorImage + self.accessibility = accessibility + } + + mutating func apply( + configuration: RemoteConfiguration.MessageBalloon?, + assetsBuilder: RemoteConfiguration.AssetsBuilder + ) { + background.apply(configuration: configuration?.background) + + configuration?.background?.color?.value + .map { UIColor(hex: $0) } + .first + .unwrap { imageFile.backgroundColor = $0 } + + text.apply( + configuration: configuration?.text, + assetsBuilder: assetsBuilder + ) + + fileDownload.apply( + configuration: configuration?.file, + assetsBuilder: assetsBuilder + ) + operatorImage.apply(configuration: configuration?.userImage) + } + } +} + +extension Theme.OperatorMessageStyle { + /// Accessibility properties for OperatorMessageStyle. + public struct Accessibility: Equatable { + /// Accessibility value. + public var value: String + + /// Flag that provides font dynamic type by setting `adjustsFontForContentSizeCategory` for component that supports it. + public var isFontScalingEnabled: Bool + + /// + /// - Parameters: + /// - value: Accessibility value. + /// - isFontScalingEnabled: Flag that provides font dynamic type by setting `adjustsFontForContentSizeCategory` for component that supports it. + public init( + value: String = "", + isFontScalingEnabled: Bool + ) { + self.value = value + self.isFontScalingEnabled = isFontScalingEnabled + } + + /// Accessibility is not supported intentionally. + public static let unsupported = Self( + value: "", + isFontScalingEnabled: false + ) + } +} + +extension Theme.OperatorMessageStyle { + func toOldOperatorMessageStyle() -> OperatorChatMessageStyle { + .init( + text: .init( + textFont: text.font, + textColor: UIColor(hex: text.color), + textStyle: text.textStyle, + backgroundColor: background.background?.color ?? .clear, + cornerRadius: background.cornerRadius, + accessibility: .init( + value: accessibility.value, + isFontScalingEnabled: accessibility.isFontScalingEnabled + ) + ), + imageFile: imageFile, + fileDownload: fileDownload, + operatorImage: operatorImage + ) + } +} diff --git a/GliaWidgets/Sources/Theme/Chat/Theme.SystemMessageStyle.swift b/GliaWidgets/Sources/Theme/Chat/Theme.SystemMessageStyle.swift new file mode 100644 index 000000000..0395e429e --- /dev/null +++ b/GliaWidgets/Sources/Theme/Chat/Theme.SystemMessageStyle.swift @@ -0,0 +1,107 @@ +import UIKit + +public extension Theme { + /// Style of a visitor's message. + struct SystemMessageStyle { + /// Style of the message text. + public var text: Text + /// Style of the message background. + public var background: Layer + /// Style of the image content. + public var imageFile: ChatImageFileContentStyle + /// Style of the image content. + public var fileDownload: ChatFileDownloadStyle + /// Accessibility related properties. + public var accessibility: Accessibility + + /// + /// - Parameters: + /// - text: Style of the message text. + /// - background: Style of the message background. + /// - imageFile: Style of the image content. + /// - fileDownload: Style of the downloadable file content. + /// - accessibility: Accessibility related properties. + /// + public init( + text: Text, + background: Layer, + imageFile: ChatImageFileContentStyle, + fileDownload: ChatFileDownloadStyle, + accessibility: Accessibility = .unsupported + ) { + self.text = text + self.background = background + self.imageFile = imageFile + self.fileDownload = fileDownload + self.accessibility = accessibility + } + + mutating func apply( + configuration: RemoteConfiguration.MessageBalloon?, + assetsBuilder: RemoteConfiguration.AssetsBuilder + ) { + background.apply(configuration: configuration?.background) + text.apply( + configuration: configuration?.text, + assetsBuilder: assetsBuilder + ) + fileDownload.apply( + configuration: configuration?.file, + assetsBuilder: assetsBuilder + ) + configuration?.background?.color?.value + .map { UIColor(hex: $0) } + .first + .unwrap { imageFile.backgroundColor = $0 } + } + } +} + +extension Theme.SystemMessageStyle { + /// Accessibility properties for SystemMessageStyle. + public struct Accessibility: Equatable { + /// Accessibility value. + public var value: String + + /// Flag that provides font dynamic type by setting `adjustsFontForContentSizeCategory` for component that supports it. + public var isFontScalingEnabled: Bool + + /// + /// - Parameters: + /// - value: Accessibility value. + /// - isFontScalingEnabled: Flag that provides font dynamic type by setting `adjustsFontForContentSizeCategory` for component that supports it. + public init( + value: String = "", + isFontScalingEnabled: Bool + ) { + self.value = value + self.isFontScalingEnabled = isFontScalingEnabled + } + + /// Accessibility is not supported intentionally. + public static let unsupported = Self( + value: "", + isFontScalingEnabled: false + ) + } +} + +extension Theme.SystemMessageStyle { + func toOldSystemMessageStyle() -> SystemMessageStyle { + .init( + text: .init( + textFont: text.font, + textColor: UIColor(hex: text.color), + textStyle: text.textStyle, + backgroundColor: background.background?.color ?? .clear, + cornerRadius: background.cornerRadius, + accessibility: .init( + value: accessibility.value, + isFontScalingEnabled: accessibility.isFontScalingEnabled + ) + ), + imageFile: imageFile, + fileDownload: fileDownload + ) + } +} diff --git a/GliaWidgets/Sources/Theme/Chat/Theme.VisitorChatMessageStyle.swift b/GliaWidgets/Sources/Theme/Chat/Theme.VisitorChatMessageStyle.swift new file mode 100644 index 000000000..080b42365 --- /dev/null +++ b/GliaWidgets/Sources/Theme/Chat/Theme.VisitorChatMessageStyle.swift @@ -0,0 +1,129 @@ +import UIKit + +public extension Theme { + /// Style of a visitor's message. + struct VisitorMessageStyle { + /// Style of the message text. + public var text: Text + /// Style of the message background. + public var background: Layer + /// Style of the image content. + public var imageFile: ChatImageFileContentStyle + /// Style of the image content. + public var fileDownload: ChatFileDownloadStyle + /// Style of the message status text. + public var status: Text + /// Text of the message delivered status. + public var delivered: String + /// Accessibility related properties. + public var accessibility: Accessibility + + /// + /// - Parameters: + /// - text: Style of the message text. + /// - background: Style of the message background. + /// - imageFile: Style of the image content. + /// - fileDownload: Style of the downloadable file content. + /// - status: Style of the message status text. + /// - delivered: Text of the message delivered status. + /// - accessibility: Accessibility related properties. + /// + public init( + text: Text, + background: Layer, + imageFile: ChatImageFileContentStyle, + fileDownload: ChatFileDownloadStyle, + status: Text, + delivered: String, + accessibility: Accessibility = .unsupported + ) { + self.text = text + self.background = background + self.imageFile = imageFile + self.fileDownload = fileDownload + self.status = status + self.delivered = delivered + self.accessibility = accessibility + } + + mutating func apply( + configuration: RemoteConfiguration.MessageBalloon?, + assetsBuilder: RemoteConfiguration.AssetsBuilder + ) { + background.apply(configuration: configuration?.background) + + configuration?.background?.color?.value + .map { UIColor(hex: $0) } + .first + .unwrap { imageFile.backgroundColor = $0 } + + text.apply( + configuration: configuration?.text, + assetsBuilder: assetsBuilder + ) + status.apply( + configuration: configuration?.status, + assetsBuilder: assetsBuilder + ) + + fileDownload.apply( + configuration: configuration?.file, + assetsBuilder: assetsBuilder + ) + } + } +} + +extension Theme.VisitorMessageStyle { + /// Accessibility properties for VisitorMessageStyle. + public struct Accessibility: Equatable { + /// Accessibility value. + public var value: String + + /// Flag that provides font dynamic type by setting `adjustsFontForContentSizeCategory` for component that supports it. + public var isFontScalingEnabled: Bool + + /// + /// - Parameters: + /// - value: Accessibility value. + /// - isFontScalingEnabled: Flag that provides font dynamic type by setting `adjustsFontForContentSizeCategory` for component that supports it. + public init( + value: String = "", + isFontScalingEnabled: Bool + ) { + self.value = value + self.isFontScalingEnabled = isFontScalingEnabled + } + + /// Accessibility is not supported intentionally. + public static let unsupported = Self( + value: "", + isFontScalingEnabled: false + ) + } +} + +extension Theme.VisitorMessageStyle { + func toOldVisitorMessageStyle() -> VisitorChatMessageStyle { + .init( + text: .init( + textFont: text.font, + textColor: UIColor(hex: text.color), + textStyle: text.textStyle, + backgroundColor: background.background?.color ?? .clear, + cornerRadius: background.cornerRadius, + accessibility: .init( + value: accessibility.value, + isFontScalingEnabled: accessibility.isFontScalingEnabled + ) + ), + imageFile: imageFile, + fileDownload: fileDownload, + statusFont: status.font, + statusColor: UIColor(hex: status.color), + statusTextStyle: status.textStyle, + delivered: delivered, + accessibility: .init(isFontScalingEnabled: accessibility.isFontScalingEnabled) + ) + } +} diff --git a/GliaWidgets/Sources/Theme/Survey/Theme+Survey.swift b/GliaWidgets/Sources/Theme/Survey/Theme+Survey.swift index 6d65e8c7e..03b8ec48b 100644 --- a/GliaWidgets/Sources/Theme/Survey/Theme+Survey.swift +++ b/GliaWidgets/Sources/Theme/Survey/Theme+Survey.swift @@ -65,15 +65,22 @@ extension Theme.SurveyStyle { title: .init( color: color.baseNormal.hex, font: font.header2, - textStyle: .title2 + textStyle: .title2, + accessibility: .init(isFontScalingEnabled: true) ), submitButton: .init( actionButtonStyle: alertStyle.positiveAction, - accessibility: .init(label: L10n.Survey.Accessibility.Footer.SubmitButton.label) + accessibility: .init( + label: L10n.Survey.Accessibility.Footer.SubmitButton.label, + isFontScalingEnabled: true + ) ), cancellButton: .init( actionButtonStyle: alertStyle.negativeAction, - accessibility: .init(label: L10n.Survey.Accessibility.Footer.CancelButton.label) + accessibility: .init( + label: L10n.Survey.Accessibility.Footer.CancelButton.label, + isFontScalingEnabled: true + ) ), booleanQuestion: .default(color: color, font: font), scaleQuestion: .default(color: color, font: font), diff --git a/GliaWidgets/Sources/Theme/Survey/Theme.Button.Accessibility.swift b/GliaWidgets/Sources/Theme/Survey/Theme.Button.Accessibility.swift index 8b6d4c8c8..e1d071d14 100644 --- a/GliaWidgets/Sources/Theme/Survey/Theme.Button.Accessibility.swift +++ b/GliaWidgets/Sources/Theme/Survey/Theme.Button.Accessibility.swift @@ -3,14 +3,21 @@ extension Theme.Button { public struct Accessibility: Equatable { /// Accessibility label. public var label: String + /// Flag that provides font dynamic type by setting `adjustsFontForContentSizeCategory` for component that supports it. + public var isFontScalingEnabled: Bool - /// - /// - Parameter label: Accessibility label. - public init(label: String) { + /// - Parameters: + /// - label: Accessibility label. + /// - isFontScalingEnabled: Flag that provides font dynamic type by setting `adjustsFontForContentSizeCategory` for component that supports it. + public init( + label: String, + isFontScalingEnabled: Bool + ) { self.label = label + self.isFontScalingEnabled = isFontScalingEnabled } /// Accessibility is not supported intentionally. - public static let unsupported = Self(label: "") + public static let unsupported = Self(label: "", isFontScalingEnabled: false) } } diff --git a/GliaWidgets/Sources/Theme/Survey/Theme.Survey.BooleanQuestion.swift b/GliaWidgets/Sources/Theme/Survey/Theme.Survey.BooleanQuestion.swift index d5807ab77..b0a5c0772 100644 --- a/GliaWidgets/Sources/Theme/Survey/Theme.Survey.BooleanQuestion.swift +++ b/GliaWidgets/Sources/Theme/Survey/Theme.Survey.BooleanQuestion.swift @@ -20,13 +20,15 @@ public extension Theme.SurveyStyle { title: .init( color: color.baseDark.hex, font: font.mediumSubtitle1, - textStyle: .subheadline + textStyle: .subheadline, + accessibility: .init(isFontScalingEnabled: true) ), option: .init( normalText: .init( color: color.baseDark.hex, font: font.bodyText, - textStyle: .body + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) ), normalLayer: .init( borderColor: color.baseNormal.cgColor, @@ -36,7 +38,8 @@ public extension Theme.SurveyStyle { selectedText: .init( color: color.baseLight.hex, font: font.bodyText, - textStyle: .body + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) ), selectedLayer: .init( background: .fill(color: color.primary), @@ -47,7 +50,8 @@ public extension Theme.SurveyStyle { highlightedText: .init( color: color.systemNegative.hex, font: font.bodyText, - textStyle: .body + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) ), highlightedLayer: .init( borderColor: color.systemNegative.cgColor, diff --git a/GliaWidgets/Sources/Theme/Survey/Theme.Survey.InputQuestion.swift b/GliaWidgets/Sources/Theme/Survey/Theme.Survey.InputQuestion.swift index 6f5252c1f..964cf9b8c 100644 --- a/GliaWidgets/Sources/Theme/Survey/Theme.Survey.InputQuestion.swift +++ b/GliaWidgets/Sources/Theme/Survey/Theme.Survey.InputQuestion.swift @@ -22,13 +22,15 @@ public extension Theme.SurveyStyle { title: .init( color: color.baseDark.hex, font: font.mediumSubtitle1, - textStyle: .subheadline + textStyle: .subheadline, + accessibility: .init(isFontScalingEnabled: true) ), option: .init( normalText: .init( color: color.baseDark.hex, font: font.bodyText, - textStyle: .body + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) ), normalLayer: .init( borderColor: color.baseNormal.cgColor, @@ -38,7 +40,8 @@ public extension Theme.SurveyStyle { selectedText: .init( color: color.baseLight.hex, font: font.bodyText, - textStyle: .body + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) ), selectedLayer: .init( background: .fill(color: color.primary), @@ -49,7 +52,8 @@ public extension Theme.SurveyStyle { highlightedText: .init( color: color.systemNegative.hex, font: font.bodyText, - textStyle: .body + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) ), highlightedLayer: .init( borderColor: color.systemNegative.cgColor, @@ -63,7 +67,8 @@ public extension Theme.SurveyStyle { text: .init( color: color.baseDark.hex, font: font.subtitle, - textStyle: .footnote + textStyle: .footnote, + accessibility: .init(isFontScalingEnabled: true) ), error: .default(color: color, font: font), accessibility: .init(isFontScalingEnabled: true) diff --git a/GliaWidgets/Sources/Theme/Survey/Theme.Survey.ScaleQuestion.swift b/GliaWidgets/Sources/Theme/Survey/Theme.Survey.ScaleQuestion.swift index 0e259f700..4f07c1952 100644 --- a/GliaWidgets/Sources/Theme/Survey/Theme.Survey.ScaleQuestion.swift +++ b/GliaWidgets/Sources/Theme/Survey/Theme.Survey.ScaleQuestion.swift @@ -20,13 +20,15 @@ public extension Theme.SurveyStyle { title: .init( color: color.baseDark.hex, font: font.mediumSubtitle1, - textStyle: .subheadline + textStyle: .subheadline, + accessibility: .init(isFontScalingEnabled: true) ), option: .init( normalText: .init( color: color.baseDark.hex, font: font.bodyText, - textStyle: .body + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) ), normalLayer: .init( borderColor: color.baseNormal.cgColor, @@ -36,7 +38,8 @@ public extension Theme.SurveyStyle { selectedText: .init( color: color.baseLight.hex, font: font.bodyText, - textStyle: .body + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) ), selectedLayer: .init( background: .fill(color: color.primary), @@ -47,7 +50,8 @@ public extension Theme.SurveyStyle { highlightedText: .init( color: color.systemNegative.hex, font: font.bodyText, - textStyle: .body + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) ), highlightedLayer: .init( borderColor: color.systemNegative.cgColor, diff --git a/GliaWidgets/Sources/Theme/Survey/Theme.Survey.SingleQuestion.swift b/GliaWidgets/Sources/Theme/Survey/Theme.Survey.SingleQuestion.swift index 3c655ed24..2b197bff8 100644 --- a/GliaWidgets/Sources/Theme/Survey/Theme.Survey.SingleQuestion.swift +++ b/GliaWidgets/Sources/Theme/Survey/Theme.Survey.SingleQuestion.swift @@ -22,14 +22,16 @@ public extension Theme.SurveyStyle { title: .init( color: color.baseDark.hex, font: font.mediumSubtitle1, - textStyle: .subheadline + textStyle: .subheadline, + accessibility: .init(isFontScalingEnabled: true) ), tintColor: color.primary.hex, option: .init( title: .init( color: color.baseDark.hex, font: font.bodyText, - textStyle: .body + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) ), accessibility: .init(isFontScalingEnabled: true)), error: .default(color: color, font: font), diff --git a/GliaWidgets/Sources/Theme/Theme+Button.swift b/GliaWidgets/Sources/Theme/Theme+Button.swift index b3e92dcc7..dafde0cf5 100644 --- a/GliaWidgets/Sources/Theme/Theme+Button.swift +++ b/GliaWidgets/Sources/Theme/Theme+Button.swift @@ -47,7 +47,8 @@ extension Theme { self.title = .init( color: actionButtonStyle.titleColor.hex, font: actionButtonStyle.titleFont, - textStyle: actionButtonStyle.textStyle + textStyle: actionButtonStyle.textStyle, + accessibility: .init(isFontScalingEnabled: accessibility.isFontScalingEnabled) ) self.cornerRadius = actionButtonStyle.cornerRaidus ?? 0 self.borderWidth = actionButtonStyle.borderWidth ?? 0 diff --git a/GliaWidgets/Sources/Theme/Theme+Chat.swift b/GliaWidgets/Sources/Theme/Theme+Chat.swift index 5e3730be5..3b4204bec 100644 --- a/GliaWidgets/Sources/Theme/Theme+Chat.swift +++ b/GliaWidgets/Sources/Theme/Theme+Chat.swift @@ -150,13 +150,6 @@ extension Theme { transferring: transferring, onHold: onHold ) - let visitorText = ChatTextContentStyle( - textFont: font.bodyText, - textColor: color.baseLight, - textStyle: .body, - backgroundColor: color.primary, - accessibility: .init(isFontScalingEnabled: true) - ) let visitorImageFile = ChatImageFileContentStyle( backgroundColor: color.primary, accessibility: .init( @@ -165,23 +158,30 @@ extension Theme { isFontScalingEnabled: true ) ) - let visitorMessage = VisitorChatMessageStyle( - text: visitorText, + let visitorMessage = VisitorMessageStyle( + text: .init( + color: color.baseLight.hex, + font: font.bodyText, + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) + ), + background: .init( + background: .fill(color: color.primary), + borderColor: .clear, + borderWidth: .zero, + cornerRadius: 8.49 + ), imageFile: visitorImageFile, fileDownload: fileDownload, - statusFont: font.caption, - statusColor: color.baseNormal, - statusTextStyle: .caption1, + status: .init( + color: color.baseNormal.hex, + font: font.caption, + textStyle: .caption1, + accessibility: .init(isFontScalingEnabled: true) + ), delivered: Chat.Message.Status.delivered, accessibility: .init(isFontScalingEnabled: true) ) - let operatorText = ChatTextContentStyle( - textFont: font.bodyText, - textColor: color.baseDark, - textStyle: .body, - backgroundColor: Color.lightGrey, - accessibility: .init(isFontScalingEnabled: true) - ) let operatorImageFile = ChatImageFileContentStyle( backgroundColor: Color.lightGrey, accessibility: .init( @@ -190,23 +190,29 @@ extension Theme { isFontScalingEnabled: true ) ) - let operatorMessage = OperatorChatMessageStyle( + let operatorText = Text( + color: color.baseDark.hex, + font: font.bodyText, + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) + ) + let operatorMessage = OperatorMessageStyle( text: operatorText, + background: .init( + background: .fill(color: Color.lightGrey), + borderColor: .clear, + borderWidth: .zero, + cornerRadius: 8.49 + ), imageFile: operatorImageFile, fileDownload: fileDownload, - operatorImage: operatorImage + operatorImage: operatorImage, + accessibility: .init(isFontScalingEnabled: true) ) let operatorTypingIndicator = OperatorTypingIndicatorStyle( color: color.primary, accessibility: .init(label: Accessibility.Message.Operator.TypingIndicator.label) ) - let choiceCardText = ChatTextContentStyle( - textFont: font.bodyText, - textColor: color.baseDark, - textStyle: .body, - backgroundColor: color.baseLight, - accessibility: .init(isFontScalingEnabled: true) - ) let choiceCardImageFile = ChatImageFileContentStyle( backgroundColor: color.baseLight, accessibility: .init( @@ -215,48 +221,68 @@ extension Theme { isFontScalingEnabled: true ) ) - let choiceCardOptionNormalState = ChoiceCardOptionStateStyle( - textFont: font.bodyText, - textColor: color.baseDark, - textStyle: .body, - backgroundColor: Color.lightGrey, - borderColor: nil, + let choiceCardOptionNormalState = Button( + background: .fill(color: Color.lightGrey), + title: .init( + color: color.baseDark.hex, + font: font.bodyText, + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) + ), + cornerRadius: 4, accessibility: .init( - value: Accessibility.Message.ChoiceCard.ButtonState.normal, + label: Accessibility.Message.ChoiceCard.ButtonState.normal, isFontScalingEnabled: true ) ) - let choiceCardOptionSelectedState = ChoiceCardOptionStateStyle( - textFont: font.bodyText, - textColor: color.baseLight, - textStyle: .body, - backgroundColor: color.primary, - borderColor: nil, + let choiceCardOptionSelectedState = Button( + background: .fill(color: Color.primary), + title: .init( + color: color.baseLight.hex, + font: font.bodyText, + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) + ), + cornerRadius: 4, accessibility: .init( - value: Accessibility.Message.ChoiceCard.ButtonState.selected, + label: Accessibility.Message.ChoiceCard.ButtonState.selected, isFontScalingEnabled: true ) ) - let choiceCardOptionDisabledState = ChoiceCardOptionStateStyle( - textFont: font.bodyText, - textColor: Color.grey, - textStyle: .body, - backgroundColor: Color.lightGrey, - borderColor: Color.baseShade, + let choiceCardOptionDisabledState = Button( + background: .fill(color: Color.lightGrey), + title: .init( + color: Color.grey.hex, + font: font.bodyText, + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) + ), + cornerRadius: 4, + borderWidth: 1, + borderColor: Color.baseShade.toHex(), accessibility: .init( - value: Accessibility.Message.ChoiceCard.ButtonState.disabled, + label: Accessibility.Message.ChoiceCard.ButtonState.disabled, isFontScalingEnabled: true ) ) - let choiceCardOption = ChoiceCardOptionStyle( + let choiceCardOption = ChoiceCardStyle.Option( normal: choiceCardOptionNormalState, selected: choiceCardOptionSelectedState, disabled: choiceCardOptionDisabledState ) let choiceCard = ChoiceCardStyle( - mainText: choiceCardText, - frameColor: color.primary, - backgroundColor: color.baseLight, + text: .init( + color: color.baseDark.hex, + font: font.bodyText, + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) + ), + background: .init( + background: .fill(color: color.baseLight), + borderColor: color.primary.cgColor, + borderWidth: 1, + cornerRadius: 8 + ), imageFile: choiceCardImageFile, fileDownload: fileDownload, operatorImage: operatorImage, @@ -355,6 +381,12 @@ extension Theme { let systemMessage = SystemMessageStyle( text: operatorText, + background: Theme.Layer( + background: .fill(color: Color.lightGrey), + borderColor: .clear, + borderWidth: .zero, + cornerRadius: 8.49 + ), imageFile: operatorImageFile, fileDownload: fileDownload ) @@ -365,9 +397,9 @@ extension Theme { backgroundColor: .fill(color: color.background), preferredStatusBarStyle: .lightContent, title: Chat.title, - visitorMessage: visitorMessage, - operatorMessage: operatorMessage, - choiceCard: choiceCard, + visitorMessageStyle: visitorMessage, + operatorMessageStyle: operatorMessage, + choiceCardStyle: choiceCard, messageEntry: messageEntry, audioUpgrade: audioUpgrade, videoUpgrade: videoUpgrade, @@ -383,7 +415,7 @@ extension Theme { secureTranscriptTitle: Chat.SecureTranscript.headerTitle, secureTranscriptHeader: secureTranscriptHeader, unreadMessageDivider: unreadMessageDivider, - systemMessage: systemMessage, + systemMessageStyle: systemMessage, gliaVirtualAssistant: gliaVirtualAssistantStyle ) } diff --git a/GliaWidgets/Sources/Theme/Theme+Gva.swift b/GliaWidgets/Sources/Theme/Theme+Gva.swift index f1b4ffd99..1d458b9ea 100644 --- a/GliaWidgets/Sources/Theme/Theme+Gva.swift +++ b/GliaWidgets/Sources/Theme/Theme+Gva.swift @@ -6,10 +6,18 @@ extension Theme { let persistentButton: GvaPersistentButtonStyle = .init( title: .init( - textFont: font.bodyText, - textColor: .black, - textStyle: .body, - backgroundColor: .clear, + text: .init( + color: UIColor.black.hex, + font: font.bodyText, + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) + ), + background: .init( + background: .fill(color: .clear), + borderColor: .clear, + borderWidth: .zero, + cornerRadius: 8.49 + ), accessibility: .init(isFontScalingEnabled: true) ), backgroundColor: .fill(color: color.lightGrey), diff --git a/GliaWidgets/Sources/Theme/Theme+Text.swift b/GliaWidgets/Sources/Theme/Theme+Text.swift index 708a40fcc..aec8a9822 100644 --- a/GliaWidgets/Sources/Theme/Theme+Text.swift +++ b/GliaWidgets/Sources/Theme/Theme+Text.swift @@ -10,21 +10,24 @@ extension Theme { public var font: UIFont /// Text style. public var textStyle: UIFont.TextStyle - /// Text aligmment. public var alignment: NSTextAlignment + /// Accessibility related properties. + public var accessibility: Accessibility /// Initializes `Text` style instance. public init( color: String, font: UIFont, textStyle: UIFont.TextStyle, - alignment: NSTextAlignment = .center + alignment: NSTextAlignment = .center, + accessibility: Accessibility ) { self.color = color self.font = font self.textStyle = textStyle self.alignment = alignment + self.accessibility = accessibility } /// Apply text from remote configuration @@ -58,3 +61,20 @@ extension Theme { } } } + +extension Theme.Text { + /// Accessibility properties for Text. + public struct Accessibility: Equatable { + /// Flag that provides font dynamic type by setting `adjustsFontForContentSizeCategory` for component that supports it. + public var isFontScalingEnabled: Bool + + /// + /// - Parameter isFontScalingEnabled: Flag that provides font dynamic type by setting `adjustsFontForContentSizeCategory` for component that supports it. + public init(isFontScalingEnabled: Bool) { + self.isFontScalingEnabled = isFontScalingEnabled + } + + /// Accessibility is not supported intentionally. + public static let unsupported = Self(isFontScalingEnabled: false) + } +} diff --git a/GliaWidgets/Sources/View/Chat/ChatStyle.Deprecated.swift b/GliaWidgets/Sources/View/Chat/ChatStyle.Deprecated.swift new file mode 100644 index 000000000..dd3347f08 --- /dev/null +++ b/GliaWidgets/Sources/View/Chat/ChatStyle.Deprecated.swift @@ -0,0 +1,117 @@ +import UIKit + +extension ChatStyle { + /// Style of the message sent by the operator. + @available(*, deprecated, message: "Deprecated, use ``ChatStyle.operatorMessageStyle`` instead.") + public var operatorMessage: OperatorChatMessageStyle { + get { + operatorMessageStyle.toOldOperatorMessageStyle() + } + set { + operatorMessageStyle = newValue.toNewOperatorMessageStyle() + } + } + + /// Style of the system message + @available(*, deprecated, message: "Deprecated, use ``ChatStyle.systemMessageStyle`` instead.") + public var systemMessage: SystemMessageStyle { + get { + systemMessageStyle.toOldSystemMessageStyle() + } + set { + systemMessageStyle = newValue.toNewSystemMessageStyle() + } + } + + /// Style of the message sent by the visitor. + @available(*, deprecated, message: "Deprecated, use ``ChatStyle.visitorMessageStyle`` instead.") + public var visitorMessage: VisitorChatMessageStyle { + get { + visitorMessageStyle.toOldVisitorMessageStyle() + } + set { + visitorMessageStyle = newValue.toNewVisitorMessageStyle() + } + } + + /// Style of the choice card sent to the visitor by the AI engine. + @available(*, deprecated, message: "Deprecated, use ``ChatStyle.choiceCardStyle`` instead.") + public var choiceCard: ChoiceCardStyle { + get { + choiceCardStyle.toOldChoiceCardStyle() + } + set { + choiceCardStyle = newValue.toNewChoiceCardStyle() + } + } + + /// + /// - Parameters: + /// - header: Style of the view's header (navigation bar area) when the screen is displaying live chat. + /// - connect: Styles for different engagement connection states. + /// - backgroundColor: View's background color. + /// - preferredStatusBarStyle: Preferred style of the status bar. + /// - title: Title shown in the header. + /// - visitorMessage: Style of the message sent by the visitor. + /// - operatorMessage: Style of the message sent by the operator. + /// - choiceCard: Style of the choice card sent to the visitor by the AI engine. + /// - messageEntry: Style of the message entry area on the bottom of the view. + /// - audioUpgrade: Style of the audio upgrade view. + /// - videoUpgrade: Style of the video upgrade view. + /// - callBubble: Style of the call bubble in chat (shown after upgrade to call). + /// - pickMedia: Style of the attachment media type picker. + /// - unreadMessageIndicator: Style of the unread message indicator. + /// - operatorTypingIndicator: Style of the view that indicates that the operator is currently typing. + /// - accessibility: Accessibility related properties. + /// - secureTranscriptTitle: Header title for secure messaging transcript. + /// - secureTranscriptHeader: Style of the view's header (navigation bar area) when the screen is displaying secure conversations. + /// - unreadMessageDivider: Style for divider of unread messages in secure messaging transcript. + @available(*, deprecated, message: "Deprecated, use designated initializer instead.") + public convenience init( + header: HeaderStyle, + connect: ConnectStyle, + backgroundColor: ColorType, + preferredStatusBarStyle: UIStatusBarStyle, + title: String, + visitorMessage: VisitorChatMessageStyle, + operatorMessage: OperatorChatMessageStyle, + choiceCard: ChoiceCardStyle, + messageEntry: ChatMessageEntryStyle, + audioUpgrade: ChatCallUpgradeStyle, + videoUpgrade: ChatCallUpgradeStyle, + callBubble: BubbleStyle, + pickMedia: AttachmentSourceListStyle, + unreadMessageIndicator: UnreadMessageIndicatorStyle, + operatorTypingIndicator: OperatorTypingIndicatorStyle, + accessibility: Accessibility = .unsupported, + secureTranscriptTitle: String, + secureTranscriptHeader: HeaderStyle, + unreadMessageDivider: UnreadMessageDividerStyle, + systemMessage: SystemMessageStyle, + gliaVirtualAssistant: GliaVirtualAssistantStyle + ) { + self.init( + header: header, + connect: connect, + backgroundColor: backgroundColor, + preferredStatusBarStyle: preferredStatusBarStyle, + title: title, + visitorMessageStyle: visitorMessage.toNewVisitorMessageStyle(), + operatorMessageStyle: operatorMessage.toNewOperatorMessageStyle(), + choiceCardStyle: choiceCard.toNewChoiceCardStyle(), + messageEntry: messageEntry, + audioUpgrade: audioUpgrade, + videoUpgrade: videoUpgrade, + callBubble: callBubble, + pickMedia: pickMedia, + unreadMessageIndicator: unreadMessageIndicator, + operatorTypingIndicator: operatorTypingIndicator, + accessibility: accessibility, + secureTranscriptTitle: secureTranscriptTitle, + secureTranscriptHeader: secureTranscriptHeader, + unreadMessageDivider: unreadMessageDivider, + systemMessageStyle: systemMessage.toNewSystemMessageStyle(), + gliaVirtualAssistant: gliaVirtualAssistant + ) + } +} diff --git a/GliaWidgets/Sources/View/Chat/ChatStyle.swift b/GliaWidgets/Sources/View/Chat/ChatStyle.swift index f42a531df..964e895e7 100644 --- a/GliaWidgets/Sources/View/Chat/ChatStyle.swift +++ b/GliaWidgets/Sources/View/Chat/ChatStyle.swift @@ -6,13 +6,13 @@ public class ChatStyle: EngagementStyle { public var title: String /// Style of the message sent by the visitor. - public var visitorMessage: VisitorChatMessageStyle + public var visitorMessageStyle: Theme.VisitorMessageStyle /// Style of the message sent by the operator. - public var operatorMessage: OperatorChatMessageStyle + public var operatorMessageStyle: Theme.OperatorMessageStyle /// Style of the choice card sent to the visitor by the AI engine. - public var choiceCard: ChoiceCardStyle + public var choiceCardStyle: Theme.ChoiceCardStyle /// Style of the message entry area on the bottom of the view. public var messageEntry: ChatMessageEntryStyle @@ -48,7 +48,7 @@ public class ChatStyle: EngagementStyle { public var unreadMessageDivider: UnreadMessageDividerStyle /// Style of the system message - public var systemMessage: SystemMessageStyle + public var systemMessageStyle: Theme.SystemMessageStyle /// Style of the Glia Virtual Assistant public var gliaVirtualAssistant: GliaVirtualAssistantStyle @@ -60,8 +60,8 @@ public class ChatStyle: EngagementStyle { /// - backgroundColor: View's background color. /// - preferredStatusBarStyle: Preferred style of the status bar. /// - title: Title shown in the header. - /// - visitorMessage: Style of the message sent by the visitor. - /// - operatorMessage: Style of the message sent by the operator. + /// - visitorMessageStyle: Style of the message sent by the visitor. + /// - operatorMessageStyle: Style of the message sent by the operator. /// - choiceCard: Style of the choice card sent to the visitor by the AI engine. /// - messageEntry: Style of the message entry area on the bottom of the view. /// - audioUpgrade: Style of the audio upgrade view. @@ -80,9 +80,9 @@ public class ChatStyle: EngagementStyle { backgroundColor: ColorType, preferredStatusBarStyle: UIStatusBarStyle, title: String, - visitorMessage: VisitorChatMessageStyle, - operatorMessage: OperatorChatMessageStyle, - choiceCard: ChoiceCardStyle, + visitorMessageStyle: Theme.VisitorMessageStyle, + operatorMessageStyle: Theme.OperatorMessageStyle, + choiceCardStyle: Theme.ChoiceCardStyle, messageEntry: ChatMessageEntryStyle, audioUpgrade: ChatCallUpgradeStyle, videoUpgrade: ChatCallUpgradeStyle, @@ -94,13 +94,13 @@ public class ChatStyle: EngagementStyle { secureTranscriptTitle: String, secureTranscriptHeader: HeaderStyle, unreadMessageDivider: UnreadMessageDividerStyle, - systemMessage: SystemMessageStyle, + systemMessageStyle: Theme.SystemMessageStyle, gliaVirtualAssistant: GliaVirtualAssistantStyle ) { self.title = title - self.visitorMessage = visitorMessage - self.operatorMessage = operatorMessage - self.choiceCard = choiceCard + self.visitorMessageStyle = visitorMessageStyle + self.operatorMessageStyle = operatorMessageStyle + self.choiceCardStyle = choiceCardStyle self.messageEntry = messageEntry self.audioUpgrade = audioUpgrade self.videoUpgrade = videoUpgrade @@ -112,7 +112,7 @@ public class ChatStyle: EngagementStyle { self.secureTranscriptTitle = secureTranscriptTitle self.secureTranscriptHeader = secureTranscriptHeader self.unreadMessageDivider = unreadMessageDivider - self.systemMessage = systemMessage + self.systemMessageStyle = systemMessageStyle self.gliaVirtualAssistant = gliaVirtualAssistant super.init( @@ -136,11 +136,11 @@ public class ChatStyle: EngagementStyle { configuration: configuration?.connect, assetsBuilder: assetsBuilder ) - visitorMessage.apply( + visitorMessageStyle.apply( configuration: configuration?.visitorMessage, assetsBuilder: assetsBuilder ) - operatorMessage.apply( + operatorMessageStyle.apply( configuration: configuration?.operatorMessage, assetsBuilder: assetsBuilder ) @@ -148,7 +148,7 @@ public class ChatStyle: EngagementStyle { configuration: configuration?.input, assetsBuilder: assetsBuilder ) - choiceCard.apply( + choiceCardStyle.apply( configuration: configuration?.responseCard, assetsBuilder: assetsBuilder ) @@ -178,7 +178,7 @@ public class ChatStyle: EngagementStyle { text: configuration?.newMessagesDividerText, assetBuilder: assetsBuilder ) - systemMessage.apply( + systemMessageStyle.apply( configuration: configuration?.systemMessage, assetsBuilder: assetsBuilder ) diff --git a/GliaWidgets/Sources/View/Chat/ChatView.swift b/GliaWidgets/Sources/View/Chat/ChatView.swift index 529bae1b7..b19d2987e 100644 --- a/GliaWidgets/Sources/View/Chat/ChatView.swift +++ b/GliaWidgets/Sources/View/Chat/ChatView.swift @@ -612,7 +612,7 @@ extension ChatView { imageUrl: String? ) -> ChatItemCell.Content { let view = OperatorChatMessageView( - with: style.operatorMessage, + with: style.operatorMessageStyle, environment: .init( data: environment.data, uuid: environment.uuid, @@ -652,7 +652,7 @@ extension ChatView { isActive: Bool ) -> ChatItemCell.Content { let view = ChoiceCardView( - with: style.choiceCard, + with: style.choiceCardStyle, environment: .init( data: environment.data, uuid: environment.uuid, @@ -671,7 +671,7 @@ extension ChatView { private func systemMessageContent(_ message: ChatMessage) -> ChatItemCell.Content { let view = SystemMessageView( - with: style.systemMessage, + with: style.systemMessageStyle, environment: .init( uiScreen: environment.uiScreen ) @@ -694,7 +694,7 @@ extension ChatView { private func outgoingMessageContent(_ message: OutgoingMessage) -> ChatItemCell.Content { let view = VisitorChatMessageView( - with: style.visitorMessage, + with: style.visitorMessageStyle, environment: .init(uiScreen: environment.uiScreen) ) view.appendContent( @@ -725,7 +725,7 @@ extension ChatView { status: String? ) -> ChatItemCell.Content { let view = VisitorChatMessageView( - with: style.visitorMessage, + with: style.visitorMessageStyle, environment: .init(uiScreen: environment.uiScreen) ) view.appendContent( @@ -868,7 +868,7 @@ extension ChatView { imageUrl: String? ) -> GvaResponseTextView { let view = GvaResponseTextView( - with: style.operatorMessage, + with: style.operatorMessageStyle, environment: .init( data: environment.data, uuid: environment.uuid, diff --git a/GliaWidgets/Sources/View/Chat/GVA/Gallery/GalleryCard/GvaGalleryCardView.swift b/GliaWidgets/Sources/View/Chat/GVA/Gallery/GalleryCard/GvaGalleryCardView.swift index ed273003a..2f79c7ddc 100644 --- a/GliaWidgets/Sources/View/Chat/GVA/Gallery/GalleryCard/GvaGalleryCardView.swift +++ b/GliaWidgets/Sources/View/Chat/GVA/Gallery/GalleryCard/GvaGalleryCardView.swift @@ -64,6 +64,30 @@ final class GvaGalleryCardView: BaseView { return .init(width: .contentWidth, height: height) } + + override func layoutSubviews() { + super.layoutSubviews() + + switch props.style.cardContainer.backgroundColor { + case let .fill(color): + backgroundColor = color + case let .gradient(colors): + makeGradientBackground( + colors: colors, + cornerRadius: props.style.cardContainer.cornerRadius + ) + } + + switch props.style.imageView.backgroundColor { + case let .fill(color): + imageView?.backgroundColor = color + case let .gradient(colors): + imageView?.makeGradientBackground( + colors: colors, + cornerRadius: props.style.imageView.cornerRadius + ) + } + } } // MARK: - Private @@ -74,12 +98,8 @@ private extension GvaGalleryCardView { // Container layer.cornerRadius = style.cardContainer.cornerRadius - switch style.cardContainer.backgroundColor { - case let .fill(color): - backgroundColor = color - case let .gradient(colors): - makeGradientBackground(colors: colors) - } + layer.borderWidth = style.cardContainer.borderWidth + layer.borderColor = style.cardContainer.borderColor.cgColor // Labels titleLabel.text = props.title @@ -102,12 +122,8 @@ private extension GvaGalleryCardView { imageView.setImage(from: image.url.absoluteString, animated: false) imageView.layer.cornerRadius = style.imageView.cornerRadius - switch style.imageView.backgroundColor { - case let .fill(color): - imageView.backgroundColor = color - case let .gradient(colors): - imageView.makeGradientBackground(colors: colors) - } + imageView.layer.borderColor = style.imageView.borderColor.cgColor + imageView.layer.borderWidth = style.imageView.borderWidth } else { imageView?.removeFromSuperview() } diff --git a/GliaWidgets/Sources/View/Chat/GVA/GvaPersistentButtonStyle.swift b/GliaWidgets/Sources/View/Chat/GVA/GvaPersistentButtonStyle.swift index ecde87d78..60ac8d0a6 100644 --- a/GliaWidgets/Sources/View/Chat/GVA/GvaPersistentButtonStyle.swift +++ b/GliaWidgets/Sources/View/Chat/GVA/GvaPersistentButtonStyle.swift @@ -3,7 +3,7 @@ import UIKit /// Style of the GVA Persistent Button container public struct GvaPersistentButtonStyle { /// Title of the container - public var title: ChatTextContentStyle + public var title: Theme.ChatTextContentStyle /// Background of the container public var backgroundColor: ColorType @@ -22,7 +22,7 @@ public struct GvaPersistentButtonStyle { /// Initialization of the object public init( - title: ChatTextContentStyle, + title: Theme.ChatTextContentStyle, backgroundColor: ColorType, cornerRadius: CGFloat, borderWidth: CGFloat, @@ -52,13 +52,12 @@ public struct GvaPersistentButtonStyle { ) { UIFont.convertToFont( uiFont: assetBuilder.fontBuilder(configuration?.font), - textStyle: title.textStyle - ).unwrap { title.textFont = $0 } + textStyle: title.text.textStyle + ).unwrap { title.text.font = $0 } configuration?.foreground?.value - .map { UIColor(hex: $0) } .first - .unwrap { title.textColor = $0 } + .unwrap { title.text.color = $0 } } mutating private func applyBackgroundConfiguration(_ configuration: RemoteConfiguration.Layer?) { diff --git a/GliaWidgets/Sources/View/Chat/GVA/GvaPersistentButtonView.swift b/GliaWidgets/Sources/View/Chat/GVA/GvaPersistentButtonView.swift index e66814247..36d51e0e6 100644 --- a/GliaWidgets/Sources/View/Chat/GVA/GvaPersistentButtonView.swift +++ b/GliaWidgets/Sources/View/Chat/GVA/GvaPersistentButtonView.swift @@ -12,7 +12,7 @@ final class GvaPersistentButtonView: OperatorChatMessageView { self.viewStyle = style.gliaVirtualAssistant.persistentButton self.environment = environment super.init( - with: style.operatorMessage, + with: style.operatorMessageStyle, environment: .init( data: environment.data, uuid: environment.uuid, diff --git a/GliaWidgets/Sources/View/Chat/GVA/GvaResponseTextView.swift b/GliaWidgets/Sources/View/Chat/GVA/GvaResponseTextView.swift index e0254480c..ea39e8482 100644 --- a/GliaWidgets/Sources/View/Chat/GVA/GvaResponseTextView.swift +++ b/GliaWidgets/Sources/View/Chat/GVA/GvaResponseTextView.swift @@ -24,7 +24,7 @@ final class GvaResponseTextView: ChatMessageView { } } - private let viewStyle: OperatorChatMessageStyle + private let viewStyle: Theme.OperatorMessageStyle private var operatorImageView: UserImageView? private let operatorImageViewContainer = UIView().makeView() private let imageViewInsets = UIEdgeInsets(top: 4, left: 8, bottom: 2, right: 60) @@ -32,13 +32,22 @@ final class GvaResponseTextView: ChatMessageView { private let environment: Environment init( - with style: OperatorChatMessageStyle, + with style: Theme.OperatorMessageStyle, environment: Environment ) { viewStyle = style self.environment = environment super.init( - with: style, + with: .init( + text: style.text, + background: style.background, + imageFile: style.imageFile, + fileDownload: style.fileDownload, + accessibility: .init( + value: style.accessibility.value, + isFontScalingEnabled: style.accessibility.isFontScalingEnabled + ) + ), contentAlignment: .left, environment: .init(uiScreen: environment.uiScreen) ) diff --git a/GliaWidgets/Sources/View/Chat/GVA/QuickReply/QuickReplyButtonCell.swift b/GliaWidgets/Sources/View/Chat/GVA/QuickReply/QuickReplyButtonCell.swift index ae540e3ca..e0df66248 100644 --- a/GliaWidgets/Sources/View/Chat/GVA/QuickReply/QuickReplyButtonCell.swift +++ b/GliaWidgets/Sources/View/Chat/GVA/QuickReply/QuickReplyButtonCell.swift @@ -44,18 +44,27 @@ final class QuickReplyButtonCell: UICollectionViewCell { @objc private func tap() { props.action() } -} -// MARK: - Private + override func layoutSubviews() { + super.layoutSubviews() -private extension QuickReplyButtonCell { - func applyStyle(_ style: GvaQuickReplyButtonStyle) { + guard let style else { return } switch style.backgroundColor { case .fill(let color): button.backgroundColor = color case .gradient(let colors): - button.makeGradientBackground(colors: colors) + button.makeGradientBackground( + colors: colors, + cornerRadius: style.cornerRadius + ) } + } +} + +// MARK: - Private + +private extension QuickReplyButtonCell { + func applyStyle(_ style: GvaQuickReplyButtonStyle) { button.setTitleColor(style.textColor, for: .normal) button.titleLabel?.font = style.textFont button.layer.cornerRadius = style.cornerRadius diff --git a/GliaWidgets/Sources/View/Chat/Message/ChatMessageStyle.swift b/GliaWidgets/Sources/View/Chat/Message/ChatMessageStyle.swift index ac4cf0cfc..22e796d45 100644 --- a/GliaWidgets/Sources/View/Chat/Message/ChatMessageStyle.swift +++ b/GliaWidgets/Sources/View/Chat/Message/ChatMessageStyle.swift @@ -1,6 +1,12 @@ import UIKit /// Base style of a chat message. +@available(*, deprecated, message: """ +Deprecated, use +``Theme.VisitorMessageStyle``, +``Theme.OperatorMessageStyle`` and +``Theme.SystemMessageStyle`` instead. +""") public class ChatMessageStyle { /// Style of the text content. public var text: ChatTextContentStyle diff --git a/GliaWidgets/Sources/View/Chat/Message/ChatMessageView.swift b/GliaWidgets/Sources/View/Chat/Message/ChatMessageView.swift index 4ceb394a6..dc4921ee0 100644 --- a/GliaWidgets/Sources/View/Chat/Message/ChatMessageView.swift +++ b/GliaWidgets/Sources/View/Chat/Message/ChatMessageView.swift @@ -1,7 +1,7 @@ import UIKit class ChatMessageView: BaseView { - let style: ChatMessageStyle + let style: Theme.ChatMessageStyle let contentViews = UIStackView().makeView() var fileTapped: ((LocalFile) -> Void)? var downloadTapped: ((FileDownload) -> Void)? @@ -11,7 +11,7 @@ class ChatMessageView: BaseView { private let environment: Environment init( - with style: ChatMessageStyle, + with style: Theme.ChatMessageStyle, contentAlignment: ChatMessageContentAlignment, environment: Environment ) { @@ -31,10 +31,18 @@ class ChatMessageView: BaseView { } func appendContent(_ content: ChatMessageContent, animated: Bool) { + let style = Theme.ChatTextContentStyle( + text: style.text, + background: style.background, + accessibility: .init( + value: style.accessibility.value, + isFontScalingEnabled: style.accessibility.isFontScalingEnabled + ) + ) switch content { case let .text(text, accProperties): let contentView = ChatTextContentView( - with: style.text, + with: style, contentAlignment: contentAlignment ) contentView.text = text @@ -58,7 +66,7 @@ class ChatMessageView: BaseView { appendContentViews(contentViews, animated: animated) case let .attributedText(text, accProperties): let contentView = ChatTextContentView( - with: style.text, + with: style, contentAlignment: contentAlignment ) contentView.attributedText = text diff --git a/GliaWidgets/Sources/View/Chat/Message/Content/ChoiceCard/ChoiceCardOptionView.swift b/GliaWidgets/Sources/View/Chat/Message/Content/ChoiceCard/ChoiceCardOptionView.swift index 4569ff8e2..48b50a16e 100644 --- a/GliaWidgets/Sources/View/Chat/Message/Content/ChoiceCard/ChoiceCardOptionView.swift +++ b/GliaWidgets/Sources/View/Chat/Message/Content/ChoiceCard/ChoiceCardOptionView.swift @@ -1,6 +1,6 @@ import UIKit -class ChoiceCardOptionView: UIView { +final class ChoiceCardOptionView: UIView { enum OptionState { case normal case selected @@ -16,10 +16,10 @@ class ChoiceCardOptionView: UIView { private let text: String? private let textLabel = UILabel() private let choiceButton = UIButton() - private let style: ChoiceCardOptionStyle + private let style: Theme.ChoiceCardStyle.Option private let viewInsets = UIEdgeInsets(top: 10, left: 12, bottom: 10, right: 12) - init(with style: ChoiceCardOptionStyle, text: String?) { + init(with style: Theme.ChoiceCardStyle.Option, text: String?) { self.style = style self.state = .normal self.text = text @@ -34,13 +34,12 @@ class ChoiceCardOptionView: UIView { } private func setup() { - backgroundColor = .clear - layer.backgroundColor = style.normal.backgroundColor.cgColor - layer.cornerRadius = 4 + backgroundColor = style.normal.background.color + layer.cornerRadius = style.normal.cornerRadius textLabel.text = text - textLabel.font = style.normal.textFont - textLabel.textColor = style.normal.textColor + textLabel.font = style.normal.title.font + textLabel.textColor = UIColor(hex: style.normal.title.color) textLabel.textAlignment = .center textLabel.numberOfLines = 0 textLabel.isAccessibilityElement = false @@ -72,17 +71,26 @@ class ChoiceCardOptionView: UIView { updateAccessibilityProperties() } - private func applyStyle(_ style: ChoiceCardOptionStateStyle) { + private func applyStyle(_ style: Theme.Button) { setFontScalingEnabled( style.accessibility.isFontScalingEnabled, for: textLabel ) UIView.transition(with: textLabel, duration: 0.2, options: .transitionCrossDissolve) { - self.layer.backgroundColor = style.backgroundColor.cgColor - self.textLabel.textColor = style.textColor + switch style.background { + case let .fill(color): + self.backgroundColor = color + case let .gradient(colors): + self.makeGradientBackground( + colors: colors, + cornerRadius: style.cornerRadius + ) + } + self.layer.cornerRadius = style.cornerRadius + self.textLabel.textColor = UIColor(hex: style.title.color) if let borderColor = style.borderColor { - self.layer.borderColor = borderColor.cgColor + self.layer.borderColor = UIColor(hex: borderColor).cgColor self.layer.borderWidth = style.borderWidth } } @@ -98,11 +106,11 @@ extension ChoiceCardOptionView { choiceButton.accessibilityLabel = text switch state { case .normal: - choiceButton.accessibilityValue = style.normal.accessibility.value + choiceButton.accessibilityValue = style.normal.accessibility.label case .selected: - choiceButton.accessibilityValue = style.selected.accessibility.value + choiceButton.accessibilityValue = style.selected.accessibility.label case .disabled: - choiceButton.accessibilityValue = style.disabled.accessibility.value + choiceButton.accessibilityValue = style.disabled.accessibility.label } } } diff --git a/GliaWidgets/Sources/View/Chat/Message/Content/ChoiceCard/ChoiceCardStyle.swift b/GliaWidgets/Sources/View/Chat/Message/Content/ChoiceCard/ChoiceCardStyle.swift index 2175b68aa..03e624412 100644 --- a/GliaWidgets/Sources/View/Chat/Message/Content/ChoiceCard/ChoiceCardStyle.swift +++ b/GliaWidgets/Sources/View/Chat/Message/Content/ChoiceCard/ChoiceCardStyle.swift @@ -1,6 +1,7 @@ import UIKit /// Style of the choice card sent to the visitor by the AI engine. +@available(*, deprecated, message: "Deprecated, use ``Theme.ChoiceCardStyle`` instead.") public final class ChoiceCardStyle: OperatorChatMessageStyle { /// Color of the choice card's border. public var frameColor: UIColor @@ -92,4 +93,75 @@ public final class ChoiceCardStyle: OperatorChatMessageStyle { operatorImage.apply(configuration: configuration?.userImage) } + + func toNewChoiceCardStyle() -> Theme.ChoiceCardStyle { + .init( + text: .init( + color: text.textColor.hex, + font: text.textFont, + textStyle: text.textStyle, + accessibility: .init(isFontScalingEnabled: text.accessibility.isFontScalingEnabled) + ), + background: .init( + background: .fill(color: backgroundColor), + borderColor: .clear, + borderWidth: .zero, + cornerRadius: cornerRadius + ), + imageFile: imageFile, + fileDownload: fileDownload, + operatorImage: operatorImage, + choiceOption: .init( + normal: .init( + background: .fill(color: choiceOption.normal.backgroundColor), + title: .init( + color: choiceOption.normal.textColor.hex, + font: choiceOption.normal.textFont, + textStyle: choiceOption.normal.textStyle, + accessibility: .init(isFontScalingEnabled: choiceOption.normal.accessibility.isFontScalingEnabled) + ), + cornerRadius: choiceOption.normal.cornerRadius, + borderWidth: choiceOption.normal.borderWidth, + borderColor: choiceOption.normal.borderColor?.hex, + accessibility: .init( + label: choiceOption.normal.accessibility.value, + isFontScalingEnabled: choiceOption.normal.accessibility.isFontScalingEnabled + ) + ), + selected: .init( + background: .fill(color: choiceOption.selected.backgroundColor), + title: .init( + color: choiceOption.selected.textColor.hex, + font: choiceOption.selected.textFont, + textStyle: choiceOption.selected.textStyle, + accessibility: .init(isFontScalingEnabled: choiceOption.selected.accessibility.isFontScalingEnabled) + ), + cornerRadius: choiceOption.selected.cornerRadius, + borderWidth: choiceOption.selected.borderWidth, + borderColor: choiceOption.selected.borderColor?.hex, + accessibility: .init( + label: choiceOption.selected.accessibility.value, + isFontScalingEnabled: choiceOption.selected.accessibility.isFontScalingEnabled + ) + ), + disabled: .init( + background: .fill(color: choiceOption.disabled.backgroundColor), + title: .init( + color: choiceOption.disabled.textColor.hex, + font: choiceOption.disabled.textFont, + textStyle: choiceOption.disabled.textStyle, + accessibility: .init(isFontScalingEnabled: choiceOption.disabled.accessibility.isFontScalingEnabled) + ), + cornerRadius: choiceOption.disabled.cornerRadius, + borderWidth: choiceOption.disabled.borderWidth, + borderColor: choiceOption.disabled.borderColor?.hex, + accessibility: .init( + label: choiceOption.disabled.accessibility.value, + isFontScalingEnabled: choiceOption.disabled.accessibility.isFontScalingEnabled + ) + ) + ), + accessibility: .init(imageLabel: accessibility.imageLabel) + ) + } } diff --git a/GliaWidgets/Sources/View/Chat/Message/Content/ChoiceCard/ChoiceCardView.swift b/GliaWidgets/Sources/View/Chat/Message/Content/ChoiceCard/ChoiceCardView.swift index 21d84d740..7a6589ca7 100644 --- a/GliaWidgets/Sources/View/Chat/Message/Content/ChoiceCard/ChoiceCardView.swift +++ b/GliaWidgets/Sources/View/Chat/Message/Content/ChoiceCard/ChoiceCardView.swift @@ -3,17 +3,24 @@ import UIKit final class ChoiceCardView: OperatorChatMessageView { var onOptionTapped: ((ChatChoiceCardOption) -> Void)! - private let viewStyle: ChoiceCardStyle + private let viewStyle: Theme.ChoiceCardStyle private let kLayoutMargins = UIEdgeInsets(top: 12, left: 16, bottom: 16, right: 16) private let kImageHeight: CGFloat = 200.0 private let environment: Environment - init(with style: ChoiceCardStyle, environment: Environment) { + init(with style: Theme.ChoiceCardStyle, environment: Environment) { viewStyle = style self.environment = environment super.init( - with: style, + with: .init( + text: style.text, + background: style.background, + imageFile: style.imageFile, + fileDownload: style.fileDownload, + operatorImage: style.operatorImage, + accessibility: .init(isFontScalingEnabled: style.text.accessibility.isFontScalingEnabled) + ), environment: .init( data: environment.data, uuid: environment.uuid, @@ -40,10 +47,17 @@ final class ChoiceCardView: OperatorChatMessageView { private func contentView(for choiceCard: ChoiceCard) -> UIView { let containerView = UIView() - containerView.backgroundColor = viewStyle.backgroundColor - containerView.layer.cornerRadius = viewStyle.cornerRadius - containerView.layer.borderWidth = viewStyle.borderWidth - containerView.layer.borderColor = viewStyle.frameColor.cgColor + viewStyle.background.background.unwrap { + switch $0 { + case let .fill(color): + containerView.backgroundColor = color + case let .gradient(colors): + containerView.makeGradientBackground(colors: colors) + } + } + containerView.layer.cornerRadius = viewStyle.background.cornerRadius + containerView.layer.borderWidth = viewStyle.background.borderWidth + containerView.layer.borderColor = viewStyle.background.borderColor let stackView = UIStackView() stackView.axis = .vertical @@ -72,7 +86,15 @@ final class ChoiceCardView: OperatorChatMessageView { } let textView = ChatTextContentView( - with: style.text, + with: .init( + text: viewStyle.text, + background: .init( + borderColor: .clear, + borderWidth: .zero, + cornerRadius: .zero + ), + accessibility: .init(isFontScalingEnabled: viewStyle.text.accessibility.isFontScalingEnabled) + ), contentAlignment: .left, insets: .zero ) diff --git a/GliaWidgets/Sources/View/Chat/Message/Content/Text/ChatTextContentView.swift b/GliaWidgets/Sources/View/Chat/Message/Content/Text/ChatTextContentView.swift index 335495147..cec6a9ceb 100644 --- a/GliaWidgets/Sources/View/Chat/Message/Content/Text/ChatTextContentView.swift +++ b/GliaWidgets/Sources/View/Chat/Message/Content/Text/ChatTextContentView.swift @@ -32,13 +32,13 @@ class ChatTextContentView: BaseView { var linkTapped: ((URL) -> Void)? private let textView = UITextView() - private let style: ChatTextContentStyle + private let style: Theme.ChatTextContentStyle private let contentAlignment: ChatMessageContentAlignment private let contentView = UIView() private let kTextInsets: UIEdgeInsets init( - with style: ChatTextContentStyle, + with style: Theme.ChatTextContentStyle, contentAlignment: ChatMessageContentAlignment, insets: UIEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16) ) { @@ -52,10 +52,28 @@ class ChatTextContentView: BaseView { fatalError("init() has not been implemented") } + override func layoutSubviews() { + super.layoutSubviews() + + style.background.background.unwrap { + switch $0 { + case let .fill(color): + contentView.backgroundColor = color + case let .gradient(colors): + contentView.makeGradientBackground( + colors: colors, + cornerRadius: style.background.cornerRadius + ) + } + } + } + override func setup() { super.setup() - contentView.backgroundColor = style.backgroundColor - contentView.layer.cornerRadius = style.cornerRadius + + contentView.layer.cornerRadius = style.background.cornerRadius + contentView.layer.borderWidth = style.background.borderWidth + contentView.layer.borderColor = style.background.borderColor textView.delegate = self textView.isScrollEnabled = false @@ -65,9 +83,9 @@ class ChatTextContentView: BaseView { textView.textContainer.lineFragmentPadding = 0 textView.dataDetectorTypes = [.link, .phoneNumber] textView.linkTextAttributes = [.underlineStyle: NSUnderlineStyle.single.rawValue] - textView.font = style.textFont + textView.font = style.text.font textView.backgroundColor = .clear - textView.textColor = style.textColor + textView.textColor = UIColor(hex: style.text.color) setFontScalingEnabled( style.accessibility.isFontScalingEnabled, @@ -121,10 +139,9 @@ class ChatTextContentView: BaseView { var constraints = [NSLayoutConstraint](); defer { constraints.activate() } constraints += textView.layoutInSuperview(insets: kTextInsets) } - let attributes: [NSAttributedString.Key: Any] = [ - .font: UIFont.preferredFont(forTextStyle: style.textStyle), - .foregroundColor: style.textColor + .font: UIFont.preferredFont(forTextStyle: style.text.textStyle), + .foregroundColor: UIColor(hex: style.text.color) ] text.addAttributes( @@ -163,11 +180,19 @@ extension ChatTextContentView { extension ChatTextContentView { static func mock(environment: Environment) -> ChatTextContentView { ChatTextContentView( - with: ChatTextContentStyle( - textFont: .systemFont(ofSize: 10), - textColor: .black, - textStyle: .body, - backgroundColor: .black, + with: Theme.ChatTextContentStyle( + text: .init( + color: UIColor.black.hex, + font: .systemFont(ofSize: 10), + textStyle: .body, + accessibility: .init(isFontScalingEnabled: true) + ), + background: .init( + background: .fill(color: .black), + borderColor: UIColor.clear.cgColor, + borderWidth: 0, + cornerRadius: 8.49 + ), accessibility: .init(isFontScalingEnabled: true) ), contentAlignment: .left, diff --git a/GliaWidgets/Sources/View/Chat/Message/OperatorChatMessageStyle.swift b/GliaWidgets/Sources/View/Chat/Message/OperatorChatMessageStyle.swift index f6e6794b2..7082d719e 100644 --- a/GliaWidgets/Sources/View/Chat/Message/OperatorChatMessageStyle.swift +++ b/GliaWidgets/Sources/View/Chat/Message/OperatorChatMessageStyle.swift @@ -1,6 +1,7 @@ import UIKit /// Style of an operator's message. +@available(*, deprecated, message: "Deprecated, use ``Theme.OperatorMessageStyle`` instead.") public class OperatorChatMessageStyle: ChatMessageStyle { /// Style of the operator's image. public var operatorImage: UserImageStyle @@ -57,4 +58,28 @@ public class OperatorChatMessageStyle: ChatMessageStyle { ) operatorImage.apply(configuration: configuration?.userImage) } + + func toNewOperatorMessageStyle() -> Theme.OperatorMessageStyle { + .init( + text: .init( + color: text.textColor.hex, + font: text.textFont, + textStyle: text.textStyle, + accessibility: .init(isFontScalingEnabled: text.accessibility.isFontScalingEnabled) + ), + background: .init( + background: .fill(color: text.backgroundColor), + borderColor: .clear, // <- Because OperatorChatMessageStyle does not have `borderColor` + borderWidth: .zero, // <- Because OperatorChatMessageStyle does not have `borderWidth` + cornerRadius: text.cornerRadius + ), + imageFile: imageFile, + fileDownload: fileDownload, + operatorImage: operatorImage, + accessibility: .init( + value: text.accessibility.value, + isFontScalingEnabled: text.accessibility.isFontScalingEnabled + ) + ) + } } diff --git a/GliaWidgets/Sources/View/Chat/Message/OperatorChatMessageView.swift b/GliaWidgets/Sources/View/Chat/Message/OperatorChatMessageView.swift index b86d34310..02eff23a7 100644 --- a/GliaWidgets/Sources/View/Chat/Message/OperatorChatMessageView.swift +++ b/GliaWidgets/Sources/View/Chat/Message/OperatorChatMessageView.swift @@ -24,7 +24,7 @@ class OperatorChatMessageView: ChatMessageView { } } - private let viewStyle: OperatorChatMessageStyle + private let viewStyle: Theme.OperatorMessageStyle private var operatorImageView: UserImageView? private var operatorImageViewContainer = UIView().makeView() private let imageViewInsets = UIEdgeInsets(top: 2, left: 10, bottom: 2, right: 60) @@ -32,13 +32,22 @@ class OperatorChatMessageView: ChatMessageView { private let environment: Environment init( - with style: OperatorChatMessageStyle, + with style: Theme.OperatorMessageStyle, environment: Environment ) { viewStyle = style self.environment = environment super.init( - with: style, + with: .init( + text: style.text, + background: style.background, + imageFile: style.imageFile, + fileDownload: style.fileDownload, + accessibility: .init( + value: style.accessibility.value, + isFontScalingEnabled: style.accessibility.isFontScalingEnabled + ) + ), contentAlignment: .left, environment: .init(uiScreen: environment.uiScreen) ) diff --git a/GliaWidgets/Sources/View/Chat/Message/SystemMessageStyle.swift b/GliaWidgets/Sources/View/Chat/Message/SystemMessageStyle.swift index 9e2fa5f48..99591b1b3 100644 --- a/GliaWidgets/Sources/View/Chat/Message/SystemMessageStyle.swift +++ b/GliaWidgets/Sources/View/Chat/Message/SystemMessageStyle.swift @@ -1,6 +1,7 @@ import UIKit /// Style of a system message. +@available(*, deprecated, message: "Deprecated, use ``Theme.SystemMessageStyle`` instead.") final public class SystemMessageStyle: ChatMessageStyle { /// - Parameters: /// - text: Style of the text content. @@ -42,4 +43,28 @@ final public class SystemMessageStyle: ChatMessageStyle { .first .unwrap { text.textColor = $0 } } + + func toNewSystemMessageStyle() -> Theme.SystemMessageStyle { + .init( + text: .init( + color: text.textColor.hex, + font: text.textFont, + textStyle: text.textStyle, + alignment: .center, + accessibility: .init(isFontScalingEnabled: text.accessibility.isFontScalingEnabled) + ), + background: .init( + background: .fill(color: text.backgroundColor), + borderColor: .clear, // <- Because SystemMessageStyle does not have `borderColor` + borderWidth: .zero, // <- Because SystemMessageStyle does not have `borderWidth` + cornerRadius: text.cornerRadius + ), + imageFile: imageFile, + fileDownload: fileDownload, + accessibility: .init( + value: text.accessibility.value, + isFontScalingEnabled: text.accessibility.isFontScalingEnabled + ) + ) + } } diff --git a/GliaWidgets/Sources/View/Chat/Message/SystemMessageView.swift b/GliaWidgets/Sources/View/Chat/Message/SystemMessageView.swift index 7a431501d..4b81df78a 100644 --- a/GliaWidgets/Sources/View/Chat/Message/SystemMessageView.swift +++ b/GliaWidgets/Sources/View/Chat/Message/SystemMessageView.swift @@ -1,19 +1,28 @@ import UIKit final class SystemMessageView: ChatMessageView { - private let viewStyle: SystemMessageStyle + private let viewStyle: Theme.SystemMessageStyle private let kInsets = UIEdgeInsets(top: 2, left: 10, bottom: 2, right: 96) private let kOperatorImageViewSize = CGSize(width: 28, height: 28) private let environment: Environment init( - with style: SystemMessageStyle, + with style: Theme.SystemMessageStyle, environment: Environment ) { viewStyle = style self.environment = environment super.init( - with: style, + with: .init( + text: style.text, + background: style.background, + imageFile: style.imageFile, + fileDownload: style.fileDownload, + accessibility: .init( + value: style.accessibility.value, + isFontScalingEnabled: style.accessibility.isFontScalingEnabled + ) + ), contentAlignment: .left, environment: .init(uiScreen: environment.uiScreen) ) diff --git a/GliaWidgets/Sources/View/Chat/Message/VisitorChatMessageStyle.swift b/GliaWidgets/Sources/View/Chat/Message/VisitorChatMessageStyle.swift index f3350011a..bb46cdb85 100644 --- a/GliaWidgets/Sources/View/Chat/Message/VisitorChatMessageStyle.swift +++ b/GliaWidgets/Sources/View/Chat/Message/VisitorChatMessageStyle.swift @@ -1,6 +1,7 @@ import UIKit /// Style of a visitor's message. +@available(*, deprecated, message: "Deprecated, use ``Theme.VisitorMessageStyle`` instead.") public class VisitorChatMessageStyle: ChatMessageStyle { /// Font of the message status text. public var statusFont: UIFont @@ -90,4 +91,34 @@ public class VisitorChatMessageStyle: ChatMessageStyle { assetsBuilder: assetsBuilder ) } + + func toNewVisitorMessageStyle() -> Theme.VisitorMessageStyle { + .init( + text: .init( + color: text.textColor.hex, + font: text.textFont, + textStyle: text.textStyle, + accessibility: .init(isFontScalingEnabled: text.accessibility.isFontScalingEnabled) + ), + background: .init( + background: .fill(color: text.backgroundColor), + borderColor: .clear, // <- Because OperatorChatMessageStyle does not have `borderColor` + borderWidth: .zero, // <- Because OperatorChatMessageStyle does not have `borderWidth` + cornerRadius: text.cornerRadius + ), + imageFile: imageFile, + fileDownload: fileDownload, + status: .init( + color: statusColor.hex, + font: statusFont, + textStyle: statusTextStyle, + accessibility: .init(isFontScalingEnabled: text.accessibility.isFontScalingEnabled) + ), + delivered: delivered, + accessibility: .init( + value: text.accessibility.value, + isFontScalingEnabled: text.accessibility.isFontScalingEnabled + ) + ) + } } diff --git a/GliaWidgets/Sources/View/Chat/Message/VisitorChatMessageView.swift b/GliaWidgets/Sources/View/Chat/Message/VisitorChatMessageView.swift index 296293974..9cab0cd3c 100644 --- a/GliaWidgets/Sources/View/Chat/Message/VisitorChatMessageView.swift +++ b/GliaWidgets/Sources/View/Chat/Message/VisitorChatMessageView.swift @@ -10,11 +10,20 @@ class VisitorChatMessageView: ChatMessageView { private let contentInsets = UIEdgeInsets(top: 2, left: 88, bottom: 2, right: 16) init( - with style: VisitorChatMessageStyle, + with style: Theme.VisitorMessageStyle, environment: Environment ) { super.init( - with: style, + with: .init( + text: style.text, + background: style.background, + imageFile: style.imageFile, + fileDownload: style.fileDownload, + accessibility: .init( + value: style.accessibility.value, + isFontScalingEnabled: style.accessibility.isFontScalingEnabled + ) + ), contentAlignment: .right, environment: .init(uiScreen: environment.uiScreen) ) @@ -26,9 +35,9 @@ class VisitorChatMessageView: ChatMessageView { fatalError("init() has not been implemented") } - private func setup(style: VisitorChatMessageStyle) { - statusLabel.font = style.statusFont - statusLabel.textColor = style.statusColor + private func setup(style: Theme.VisitorMessageStyle) { + statusLabel.font = style.status.font + statusLabel.textColor = UIColor(hex: style.status.color) setFontScalingEnabled( style.accessibility.isFontScalingEnabled, for: statusLabel