refactored NotificationMoleculeView/Model

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2024-04-10 13:40:09 -05:00
parent 077ee85dc0
commit 5fbf076aa7
2 changed files with 115 additions and 118 deletions

View File

@ -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)
}
}
}

View File

@ -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<StackModel>!
public var horizontalStack: Stack<StackModel>!
// 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<StackModel>.createStack(with: [headline, body], spacing: 0)
horizontalStack = Stack<StackModel>.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)"
}
}