From 5fbf076aa703cb6318cdf8662d92ca6f307f66e0 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 10 Apr 2024 13:40:09 -0500 Subject: [PATCH] refactored NotificationMoleculeView/Model Signed-off-by: Matt Bruce --- .../NotificationMoleculeModel.swift | 102 +++++++------- .../NotificationMoleculeView.swift | 131 +++++++++--------- 2 files changed, 115 insertions(+), 118 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationMoleculeModel.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationMoleculeModel.swift index 9958b59a..f9387064 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationMoleculeModel.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationMoleculeModel.swift @@ -5,7 +5,7 @@ // Created by Scott Pfeil on 9/15/20. // Copyright © 2020 Verizon Wireless. All rights reserved. // - +import VDS open class NotificationMoleculeModel: ContainerModel, MoleculeModelProtocol { @@ -21,6 +21,19 @@ open class NotificationMoleculeModel: ContainerModel, MoleculeModelProtocol { case error case warning case information + + var toVDSStyle: VDS.Notification.Style { + switch self { + case .success: + .success + case .error: + .error + case .warning: + .warning + case .information: + .info + } + } } //-------------------------------------------------- @@ -33,19 +46,22 @@ open class NotificationMoleculeModel: ContainerModel, MoleculeModelProtocol { public var headline: LabelModel public var body: LabelModel? public var button: ButtonModel? - public var closeButton: NotificationXButtonModel? - public var style: NotificationMoleculeModel.Style = .success + public var secondaryButton: ButtonModel? + public var closeButton: ButtonModel? + public var style: Style = .success + public var inverted: Bool = false //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- - public init(with headline: LabelModel, style: NotificationMoleculeModel.Style = .success, backgroundColor: Color? = nil, body: LabelModel? = nil, button: ButtonModel? = nil, closeButton: NotificationXButtonModel? = nil) { + public init(with headline: LabelModel, style: NotificationMoleculeModel.Style = .success, backgroundColor: Color? = nil, body: LabelModel? = nil, button: ButtonModel? = nil, secondaryButton: ButtonModel? = nil, closeButton: ButtonModel? = nil) { self.headline = headline self.style = style self.backgroundColor = backgroundColor self.body = body self.button = button + self.secondaryButton = secondaryButton self.closeButton = closeButton super.init() } @@ -55,57 +71,7 @@ open class NotificationMoleculeModel: ContainerModel, MoleculeModelProtocol { //-------------------------------------------------- open override func setDefaults() { - useHorizontalMargins = true - useVerticalMargins = true - topPadding = PaddingTwo - bottomPadding = PaddingTwo - if backgroundColor == nil { - switch style { - case .error: - backgroundColor = Color(uiColor: .mvmOrange) - case .warning: - backgroundColor = Color(uiColor: .mvmYellow) - case .information: - backgroundColor = Color(uiColor: .mvmBlue) - default: - backgroundColor = Color(uiColor: .mvmGreen) - } - } - if headline.textColor == nil { - switch style { - case .error, .warning: - headline.textColor = Color(uiColor: .mvmBlack) - default: - headline.textColor = Color(uiColor: .mvmWhite) - } - } - if body?.textColor == nil { - switch style { - case .error, .warning: - body?.textColor = Color(uiColor: .mvmBlack) - default: - body?.textColor = Color(uiColor: .mvmWhite) - } - } - - button?.size = .small - button?.style = .secondary - switch style { - case .error, .warning: - button?.inverted = false - default: - button?.inverted = true - } - - if closeButton?.color == nil { - switch style { - case .error, .warning: - closeButton?.color = Color(uiColor: .mvmBlack) - default: - closeButton?.color = Color(uiColor: .mvmWhite) - } - } } //-------------------------------------------------- @@ -119,7 +85,9 @@ open class NotificationMoleculeModel: ContainerModel, MoleculeModelProtocol { case headline case body case button + case secondaryButton case closeButton + case inverted case style } @@ -134,7 +102,9 @@ open class NotificationMoleculeModel: ContainerModel, MoleculeModelProtocol { headline = try typeContainer.decode(LabelModel.self, forKey: .headline) body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body) button = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .button) - closeButton = try typeContainer.decodeIfPresent(NotificationXButtonModel.self, forKey: .closeButton) + secondaryButton = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .secondaryButton) + closeButton = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .closeButton) + inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) ?? false if let style = try typeContainer.decodeIfPresent(NotificationMoleculeModel.Style.self, forKey: .style) { self.style = style } @@ -149,7 +119,29 @@ open class NotificationMoleculeModel: ContainerModel, MoleculeModelProtocol { try container.encode(headline, forKey: .headline) try container.encodeIfPresent(body, forKey: .body) try container.encodeIfPresent(button, forKey: .button) + try container.encodeIfPresent(secondaryButton, forKey: .secondaryButton) try container.encodeIfPresent(closeButton, forKey: .closeButton) try container.encode(style, forKey: .style) } } + +extension NotificationMoleculeModel { + public var surface: Surface { + inverted ? .dark : .light + } +} + +extension ButtonModel { + public func toNotficationButtonModel(delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) -> VDS.Notification.ButtonModel { + return .init(text: title, onClick: { [weak self] _ in + guard let self else { return } + self.onClick(delegateObject: delegateObject, additionalData) + }) + } + + public func onClick(delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + Task(priority: .userInitiated) { + try await (delegateObject?.actionDelegate as? ActionDelegateProtocol)?.performAction(with: action, additionalData: MVMCoreUIActionHandler.add(sourceModel: self, to: additionalData), delegateObject: delegateObject) + } + } +} diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationMoleculeView.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationMoleculeView.swift index 200dea16..7c4643bb 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationMoleculeView.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationMoleculeView.swift @@ -7,90 +7,95 @@ // import Foundation - -@objcMembers open class NotificationMoleculeView: Container { +import VDS +@objcMembers open class NotificationMoleculeView: VDS.Notification, VDSMoleculeViewProtocol { + + + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + open var viewModel: NotificationMoleculeModel! + public var delegateObject: MVMCoreUIDelegateObject? + public var additionalData: [AnyHashable: Any]? + + //-------------------------------------------------- + // MARK: - VDSMoleculeViewProtocol + //-------------------------------------------------- + + open func viewModelDidUpdate() { + surface = viewModel.surface + title = viewModel.headline.text + subTitle = viewModel.body?.text + + if let buttonModel = viewModel.button { + primaryButtonModel = buttonModel.toNotficationButtonModel(delegateObject: delegateObject, additionalData) + } + + if let buttonModel = viewModel.secondaryButton { + secondaryButtonModel = buttonModel.toNotficationButtonModel(delegateObject: delegateObject, additionalData) + } + + if let accessibilityIdentifier = viewModel.accessibilityIdentifier { + self.accessibilityIdentifier = accessibilityIdentifier + } + + if let closeButton = viewModel.closeButton { + onCloseClick = { [weak self] _ in + guard let self else { return } + closeButton.onClick(delegateObject: self.delegateObject, self.additionalData) + } + } + hideCloseButton = viewModel.closeButton == nil + style = viewModel.style.toVDSStyle + } + //-------------------------------------------------- // MARK: - Outlets //-------------------------------------------------- - - public let headline = Label(fontStyle: .BoldBodySmall) - public let body = Label(fontStyle: .RegularBodySmall) - public let button = PillButton() - public let closeButton = NotificationXButton() - public var labelStack: Stack! - public var horizontalStack: Stack! - // Legacy constant private static let viewHeight: CGFloat = 96.0 //-------------------------------------------------- // MARK: - Life Cycle //-------------------------------------------------- - - public override func setupView() { - super.setupView() - reset() - - // Buttons should have highest priority, then headline, then body - headline.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 500), for: .horizontal) - headline.setContentHuggingPriority(.required, for: .vertical) - body.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 500), for: .horizontal) - body.setContentHuggingPriority(.required, for: .vertical) - headline.setContentCompressionResistancePriority(UILayoutPriority(rawValue: body.contentCompressionResistancePriority(for: .vertical).rawValue + 40), for: .vertical) - headline.lineBreakMode = .byTruncatingTail - body.lineBreakMode = .byTruncatingTail - button.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) - - labelStack = Stack.createStack(with: [headline, body], spacing: 0) - horizontalStack = Stack.createStack(with: [(view: labelStack, model: StackItemModel()),(view: button, model: StackItemModel(horizontalAlignment: .fill)),(view: closeButton, model: StackItemModel(horizontalAlignment: .fill))], axis: .horizontal) - addAndContain(horizontalStack) - labelStack.restack() - horizontalStack.restack() - - heightAnchor.constraint(equalToConstant: Self.viewHeight).isActive = true - } - - open override func reset() { - super.reset() - backgroundColor = .mvmGreen() - headline.textColor = .white - body.textColor = .white + open override func updateAccessibility() { + super.updateAccessibility() + Self.amendAccesibilityLabel(for: titleLabel) + Self.amendAccesibilityLabel(for: subTitleLabel) + Self.amendAccesibilityLabel(for: primaryButton) + Self.amendAccesibilityLabel(for: secondaryButton) + Self.amendAccesibilityLabel(for: closeButton) } //-------------------------------------------------- // MARK: - Molecule //-------------------------------------------------- - - open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - super.set(with: model, delegateObject, additionalData) - guard let model = model as? NotificationMoleculeModel else { return } - labelStack.updateContainedMolecules(with: [model.headline, model.body], delegateObject, nil) - horizontalStack.updateContainedMolecules(with: [labelStack.stackModel, model.button, model.closeButton], delegateObject, nil) - updateAccessibility() - } - - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + public func updateView(_ size: CGFloat) { } + + open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return viewHeight } - - open func updateAccessibility() { - NotificationMoleculeView.amendAccesibilityLabel(for: headline) - NotificationMoleculeView.amendAccesibilityLabel(for: body) - NotificationMoleculeView.amendAccesibilityLabel(for: button) - NotificationMoleculeView.amendAccesibilityLabel(for: closeButton) - } - + /// Formats the accessibilityLabel so voice over users know it's in the notification. - static public func amendAccesibilityLabel(for view: UIView) { - guard let amendment = MVMCoreUIUtility.hardcodedString(withKey: "top_alert_notification"), - let accessibilityLabel = view.accessibilityLabel, - !accessibilityLabel.hasPrefix(amendment) else { return } - view.accessibilityLabel = "\(amendment) - \(accessibilityLabel)" + public class func amendAccesibilityLabel(for view: UIView?) { + guard let view, + let amendment = MVMCoreUIUtility.hardcodedString(withKey: "top_alert_notification") + else { return } + view.amendAccesibilityLabel(with: amendment) } } extension NotificationMoleculeView: AccessibilityProtocol { public func getAccessibilityLayoutChangedArgument() -> Any? { - return headline + return titleLabel + } +} + +extension UIView { + /// Formats the accessibilityLabel so voice over users know it's in the notification. + public func amendAccesibilityLabel(with amendment: String) { + guard let accessibilityLabel, !accessibilityLabel.hasPrefix(amendment) else { return } + self.accessibilityLabel = "\(amendment) - \(accessibilityLabel)" } }