refactored NotificationMoleculeView/Model
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
parent
077ee85dc0
commit
5fbf076aa7
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)"
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user