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.
|
// Created by Scott Pfeil on 9/15/20.
|
||||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||||
//
|
//
|
||||||
|
import VDS
|
||||||
|
|
||||||
open class NotificationMoleculeModel: ContainerModel, MoleculeModelProtocol {
|
open class NotificationMoleculeModel: ContainerModel, MoleculeModelProtocol {
|
||||||
|
|
||||||
@ -21,6 +21,19 @@ open class NotificationMoleculeModel: ContainerModel, MoleculeModelProtocol {
|
|||||||
case error
|
case error
|
||||||
case warning
|
case warning
|
||||||
case information
|
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 headline: LabelModel
|
||||||
public var body: LabelModel?
|
public var body: LabelModel?
|
||||||
public var button: ButtonModel?
|
public var button: ButtonModel?
|
||||||
public var closeButton: NotificationXButtonModel?
|
public var secondaryButton: ButtonModel?
|
||||||
public var style: NotificationMoleculeModel.Style = .success
|
public var closeButton: ButtonModel?
|
||||||
|
public var style: Style = .success
|
||||||
|
public var inverted: Bool = false
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializer
|
// 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.headline = headline
|
||||||
self.style = style
|
self.style = style
|
||||||
self.backgroundColor = backgroundColor
|
self.backgroundColor = backgroundColor
|
||||||
self.body = body
|
self.body = body
|
||||||
self.button = button
|
self.button = button
|
||||||
|
self.secondaryButton = secondaryButton
|
||||||
self.closeButton = closeButton
|
self.closeButton = closeButton
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
@ -55,57 +71,7 @@ open class NotificationMoleculeModel: ContainerModel, MoleculeModelProtocol {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
open override func setDefaults() {
|
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 headline
|
||||||
case body
|
case body
|
||||||
case button
|
case button
|
||||||
|
case secondaryButton
|
||||||
case closeButton
|
case closeButton
|
||||||
|
case inverted
|
||||||
case style
|
case style
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +102,9 @@ open class NotificationMoleculeModel: ContainerModel, MoleculeModelProtocol {
|
|||||||
headline = try typeContainer.decode(LabelModel.self, forKey: .headline)
|
headline = try typeContainer.decode(LabelModel.self, forKey: .headline)
|
||||||
body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body)
|
body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body)
|
||||||
button = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .button)
|
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) {
|
if let style = try typeContainer.decodeIfPresent(NotificationMoleculeModel.Style.self, forKey: .style) {
|
||||||
self.style = style
|
self.style = style
|
||||||
}
|
}
|
||||||
@ -149,7 +119,29 @@ open class NotificationMoleculeModel: ContainerModel, MoleculeModelProtocol {
|
|||||||
try container.encode(headline, forKey: .headline)
|
try container.encode(headline, forKey: .headline)
|
||||||
try container.encodeIfPresent(body, forKey: .body)
|
try container.encodeIfPresent(body, forKey: .body)
|
||||||
try container.encodeIfPresent(button, forKey: .button)
|
try container.encodeIfPresent(button, forKey: .button)
|
||||||
|
try container.encodeIfPresent(secondaryButton, forKey: .secondaryButton)
|
||||||
try container.encodeIfPresent(closeButton, forKey: .closeButton)
|
try container.encodeIfPresent(closeButton, forKey: .closeButton)
|
||||||
try container.encode(style, forKey: .style)
|
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
|
import Foundation
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
@objcMembers open class NotificationMoleculeView: Container {
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Outlets
|
// 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
|
// Legacy constant
|
||||||
private static let viewHeight: CGFloat = 96.0
|
private static let viewHeight: CGFloat = 96.0
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Life Cycle
|
// MARK: - Life Cycle
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
open override func updateAccessibility() {
|
||||||
public override func setupView() {
|
super.updateAccessibility()
|
||||||
super.setupView()
|
Self.amendAccesibilityLabel(for: titleLabel)
|
||||||
reset()
|
Self.amendAccesibilityLabel(for: subTitleLabel)
|
||||||
|
Self.amendAccesibilityLabel(for: primaryButton)
|
||||||
// Buttons should have highest priority, then headline, then body
|
Self.amendAccesibilityLabel(for: secondaryButton)
|
||||||
headline.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 500), for: .horizontal)
|
Self.amendAccesibilityLabel(for: closeButton)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Molecule
|
// MARK: - Molecule
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
public func updateView(_ size: CGFloat) { }
|
||||||
|
|
||||||
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||||
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? {
|
|
||||||
return viewHeight
|
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.
|
/// Formats the accessibilityLabel so voice over users know it's in the notification.
|
||||||
static public func amendAccesibilityLabel(for view: UIView) {
|
public class func amendAccesibilityLabel(for view: UIView?) {
|
||||||
guard let amendment = MVMCoreUIUtility.hardcodedString(withKey: "top_alert_notification"),
|
guard let view,
|
||||||
let accessibilityLabel = view.accessibilityLabel,
|
let amendment = MVMCoreUIUtility.hardcodedString(withKey: "top_alert_notification")
|
||||||
!accessibilityLabel.hasPrefix(amendment) else { return }
|
else { return }
|
||||||
view.accessibilityLabel = "\(amendment) - \(accessibilityLabel)"
|
view.amendAccesibilityLabel(with: amendment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NotificationMoleculeView: AccessibilityProtocol {
|
extension NotificationMoleculeView: AccessibilityProtocol {
|
||||||
public func getAccessibilityLayoutChangedArgument() -> Any? {
|
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