merge:
This commit is contained in:
commit
fc318a03cc
@ -160,7 +160,7 @@ import Foundation
|
||||
|
||||
/// Collapse if focus is no longer on this top alert.
|
||||
@objc func accessibilityFocusChanged(notification: Notification) {
|
||||
if !MVMCoreUIUtility.viewContainsAccessiblityFocus(self) {
|
||||
if (notification.userInfo?[UIAccessibility.focusedElementUserInfoKey] != nil) && !MVMCoreUIUtility.viewContainsAccessiblityFocus(self) {
|
||||
NotificationCenter.default.removeObserver(self, name: UIAccessibility.elementFocusedNotification, object: nil)
|
||||
collapse()
|
||||
}
|
||||
|
||||
@ -8,6 +8,21 @@
|
||||
|
||||
|
||||
open class NotificationModel: ContainerModel, MoleculeModelProtocol {
|
||||
|
||||
/**
|
||||
The style of the notification:
|
||||
- success, green background, white content
|
||||
- error, orange background, black content
|
||||
- \warning, yellow background, black content
|
||||
- information, blue background, white content
|
||||
*/
|
||||
public enum Style: String, Codable {
|
||||
case success
|
||||
case error
|
||||
case warning
|
||||
case information
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
@ -19,6 +34,7 @@ open class NotificationModel: ContainerModel, MoleculeModelProtocol {
|
||||
public var body: LabelModel?
|
||||
public var button: ButtonModel?
|
||||
public var closeButton: NotificationXButtonModel?
|
||||
public var style: NotificationModel.Style = .success
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializer
|
||||
@ -40,20 +56,63 @@ open class NotificationModel: ContainerModel, MoleculeModelProtocol {
|
||||
bottomPadding = PaddingTwo
|
||||
|
||||
if backgroundColor == nil {
|
||||
backgroundColor = Color(uiColor: .mvmGreen)
|
||||
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 {
|
||||
headline.textColor = Color(uiColor: .mvmWhite)
|
||||
switch style {
|
||||
case .error, .warning:
|
||||
headline.textColor = Color(uiColor: .mvmBlack)
|
||||
default:
|
||||
headline.textColor = Color(uiColor: .mvmWhite)
|
||||
}
|
||||
}
|
||||
if body?.textColor == nil {
|
||||
body?.textColor = Color(uiColor: .mvmWhite)
|
||||
switch style {
|
||||
case .error, .warning:
|
||||
body?.textColor = Color(uiColor: .mvmBlack)
|
||||
default:
|
||||
body?.textColor = Color(uiColor: .mvmWhite)
|
||||
}
|
||||
}
|
||||
|
||||
button?.size = .tiny
|
||||
if button?.enabledTextColor == nil {
|
||||
switch style {
|
||||
case .error, .warning:
|
||||
button?.enabledTextColor = Color(uiColor: .mvmBlack)
|
||||
default:
|
||||
button?.enabledTextColor = Color(uiColor: .mvmWhite)
|
||||
}
|
||||
}
|
||||
if button?.enabledBorderColor == nil {
|
||||
switch style {
|
||||
case .error, .warning:
|
||||
button?.enabledBorderColor = Color(uiColor: .mvmBlack)
|
||||
default:
|
||||
button?.enabledBorderColor = Color(uiColor: .mvmWhite)
|
||||
}
|
||||
}
|
||||
if button?.style == nil {
|
||||
button?.style = .secondary
|
||||
}
|
||||
button?.size = .tiny
|
||||
button?.enabledTextColor = Color(uiColor: .mvmWhite)
|
||||
button?.enabledBorderColor = Color(uiColor: .mvmWhite)
|
||||
|
||||
if closeButton?.color == nil {
|
||||
switch style {
|
||||
case .error, .warning:
|
||||
closeButton?.color = Color(uiColor: .mvmBlack)
|
||||
default:
|
||||
closeButton?.color = Color(uiColor: .mvmWhite)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -68,6 +127,7 @@ open class NotificationModel: ContainerModel, MoleculeModelProtocol {
|
||||
case body
|
||||
case button
|
||||
case closeButton
|
||||
case style
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -82,9 +142,12 @@ open class NotificationModel: ContainerModel, MoleculeModelProtocol {
|
||||
body = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .body)
|
||||
button = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .button)
|
||||
closeButton = try typeContainer.decodeIfPresent(NotificationXButtonModel.self, forKey: .closeButton)
|
||||
if let style = try typeContainer.decodeIfPresent(NotificationModel.Style.self, forKey: .style) {
|
||||
self.style = style
|
||||
}
|
||||
super.init()
|
||||
}
|
||||
|
||||
|
||||
open override func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
@ -94,5 +157,6 @@ open class NotificationModel: ContainerModel, MoleculeModelProtocol {
|
||||
try container.encodeIfPresent(body, forKey: .body)
|
||||
try container.encodeIfPresent(button, forKey: .button)
|
||||
try container.encodeIfPresent(closeButton, forKey: .closeButton)
|
||||
try container.encode(style, forKey: .style)
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ import Foundation
|
||||
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
|
||||
super.set(with: model, delegateObject, additionalData)
|
||||
guard let model = model as? NotificationXButtonModel else { return }
|
||||
tintColor = model.color.uiColor
|
||||
tintColor = model.color?.uiColor ?? .white
|
||||
|
||||
// TODO: Temporary, consider action for dismissing top alert
|
||||
if model.action.actionType == ActionNoopModel.identifier {
|
||||
|
||||
@ -11,7 +11,7 @@ import Foundation
|
||||
public class NotificationXButtonModel: ButtonModelProtocol, MoleculeModelProtocol {
|
||||
public static var identifier: String = "notificationXButton"
|
||||
public var backgroundColor: Color?
|
||||
public var color = Color(uiColor: .white)
|
||||
public var color: Color?
|
||||
public var action: ActionModelProtocol = ActionNoopModel()
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
@ -24,7 +24,7 @@ public class NotificationXButtonModel: ButtonModelProtocol, MoleculeModelProtoco
|
||||
|
||||
public required init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) ?? Color(uiColor: .white)
|
||||
color = try typeContainer.decodeIfPresent(Color.self, forKey: .color)
|
||||
if let action: ActionModelProtocol = try typeContainer.decodeModelIfPresent(codingKey: .action) {
|
||||
self.action = action
|
||||
}
|
||||
@ -33,7 +33,7 @@ public class NotificationXButtonModel: ButtonModelProtocol, MoleculeModelProtoco
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
try container.encode(color, forKey: .color)
|
||||
try container.encodeIfPresent(color, forKey: .color)
|
||||
try container.encodeModel(action, forKey: .action)
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,8 +96,13 @@ import MVMCore
|
||||
public func validateGroup(_ group: FormGroupRule) -> Bool {
|
||||
// Validate each rule.
|
||||
var valid = true
|
||||
var previousValidity: [String: Bool] = [:]
|
||||
for rule in group.rules {
|
||||
valid = valid && rule.validate(fields)
|
||||
let tuple = rule.validate(fields, previousValidity)
|
||||
let isValidRule = tuple.valid
|
||||
let returnedValidity = tuple.fieldValidity
|
||||
previousValidity = previousValidity.merging(returnedValidity) { (_, new) in new }
|
||||
valid = valid && isValidRule
|
||||
}
|
||||
|
||||
// Notify the group watchers of validity.
|
||||
|
||||
@ -36,15 +36,23 @@ public class RuleAnyRequiredModel: RulesProtocol {
|
||||
return false
|
||||
}
|
||||
|
||||
public func validate(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool {
|
||||
|
||||
public func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: Bool]) -> (valid: Bool, fieldValidity: [String: Bool]) {
|
||||
|
||||
var previousValidity: [String: Bool] = [:]
|
||||
for formKey in fields {
|
||||
guard let formField = fieldMolecules[formKey] else { continue }
|
||||
|
||||
if isValid(formField) {
|
||||
return true
|
||||
|
||||
var fieldValidity = isValid(formField)
|
||||
// If past rule is invalid for a field, the current rule should not flip the validity of a field
|
||||
if let validity = previousFieldValidity[formKey], !validity, fieldValidity {
|
||||
fieldValidity = false
|
||||
}
|
||||
|
||||
if fieldValidity {
|
||||
return (fieldValidity, previousValidity)
|
||||
}
|
||||
previousValidity[formKey] = false
|
||||
}
|
||||
return false
|
||||
return (valid: false, fieldValidity: previousValidity)
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,13 +27,21 @@ public class RuleAnyValueChangedModel: RulesProtocol {
|
||||
return formField.baseValue != formField.formFieldValue()
|
||||
}
|
||||
|
||||
public func validate(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool {
|
||||
public func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: Bool]) -> (valid: Bool, fieldValidity: [String: Bool]) {
|
||||
var previousValidity: [String: Bool] = [:]
|
||||
for formKey in fields {
|
||||
guard let formField = fieldMolecules[formKey] else { continue }
|
||||
if isValid(formField) {
|
||||
return true
|
||||
var fieldValidity = isValid(formField)
|
||||
// If past rule is invalid forr a field, the current rule should not flip the validity of a field
|
||||
if let validity = previousFieldValidity[formKey], !validity, fieldValidity {
|
||||
fieldValidity = false
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
if fieldValidity {
|
||||
return (true, previousValidity)
|
||||
}
|
||||
previousValidity[formKey] = false
|
||||
}
|
||||
return (valid: false, fieldValidity: previousValidity)
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,10 +27,11 @@ public class RuleEqualsIgnoreCaseModel: RulesProtocol {
|
||||
return false
|
||||
}
|
||||
|
||||
public func validate(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool {
|
||||
var valid = false
|
||||
var compareText: String?
|
||||
public func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: Bool]) -> (valid: Bool, fieldValidity: [String: Bool]) {
|
||||
var valid = false
|
||||
var compareText: String?
|
||||
|
||||
var previousValidity: [String: Bool] = [:]
|
||||
for formKey in fields {
|
||||
guard let formField = fieldMolecules[formKey] else { continue }
|
||||
|
||||
@ -42,16 +43,23 @@ public class RuleEqualsIgnoreCaseModel: RulesProtocol {
|
||||
if let fieldValue = formField.formFieldValue() as? String,
|
||||
compareString.caseInsensitiveCompare(fieldValue) == .orderedSame {
|
||||
valid = true
|
||||
|
||||
var fieldValidity = valid
|
||||
// If past rule is invalid for a field, the current rule should not flip the validity of a field
|
||||
if let validity = previousFieldValidity[formKey], !validity, fieldValidity {
|
||||
fieldValidity = false
|
||||
}
|
||||
|
||||
for formKey in fields {
|
||||
guard let formField = fieldMolecules[formKey] else { continue }
|
||||
(formField as? FormRuleWatcherFieldProtocol)?.setValidity(true, rule: self)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
previousValidity[formKey] = valid
|
||||
(formField as? FormRuleWatcherFieldProtocol)?.setValidity(valid, rule: self)
|
||||
}
|
||||
|
||||
return valid
|
||||
return (valid: valid, fieldValidity: previousValidity)
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,9 +27,10 @@ public class RuleEqualsModel: RulesProtocol {
|
||||
return false
|
||||
}
|
||||
|
||||
public func validate(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool {
|
||||
var valid = true
|
||||
var compareValue: AnyHashable?
|
||||
public func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: Bool]) -> (valid: Bool, fieldValidity: [String: Bool]) {
|
||||
var valid = true
|
||||
var compareValue: AnyHashable?
|
||||
var previousValidity: [String: Bool] = [:]
|
||||
|
||||
for formKey in fields {
|
||||
guard let formField = fieldMolecules[formKey] else { continue }
|
||||
@ -41,13 +42,18 @@ public class RuleEqualsModel: RulesProtocol {
|
||||
|
||||
if compareValue != formField.formFieldValue() {
|
||||
valid = false
|
||||
previousValidity[formKey] = valid
|
||||
(formField as? FormRuleWatcherFieldProtocol)?.setValidity(valid, rule: self)
|
||||
break
|
||||
} else {
|
||||
(formField as? FormRuleWatcherFieldProtocol)?.setValidity(valid, rule: self)
|
||||
var fieldValidity = valid
|
||||
// If past rule is invalid for a field, the current rule should not flip the validity of a field
|
||||
if let validity = previousFieldValidity[formKey], !validity, fieldValidity {
|
||||
fieldValidity = false
|
||||
}
|
||||
(formField as? FormRuleWatcherFieldProtocol)?.setValidity(fieldValidity, rule: self)
|
||||
}
|
||||
}
|
||||
|
||||
return valid
|
||||
return (valid: valid, fieldValidity: previousValidity)
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ public protocol RulesProtocol: ModelProtocol {
|
||||
func isValid(_ formField: FormFieldProtocol) -> Bool
|
||||
|
||||
// Validates the rule and returns the result.
|
||||
func validate(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool
|
||||
func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: Bool]) -> (valid: Bool, fieldValidity: [String: Bool])
|
||||
}
|
||||
|
||||
public extension RulesProtocol {
|
||||
@ -38,14 +38,21 @@ public extension RulesProtocol {
|
||||
static var categoryName: String { "\(RulesProtocol.self)" }
|
||||
|
||||
// Individual rule can override the function to validate based on the rule type.
|
||||
func validate(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool {
|
||||
var valid = true
|
||||
for formKey in fields {
|
||||
guard let formField = fieldMolecules[formKey] else { continue }
|
||||
let fieldValidity = isValid(formField)
|
||||
(formField as? FormRuleWatcherFieldProtocol)?.setValidity(fieldValidity, rule: self)
|
||||
valid = valid && fieldValidity
|
||||
}
|
||||
return valid
|
||||
func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: Bool]) -> (valid: Bool, fieldValidity: [String: Bool]) {
|
||||
var valid = true
|
||||
var previousValidity: [String: Bool] = [:]
|
||||
for formKey in fields {
|
||||
guard let formField = fieldMolecules[formKey] else { continue }
|
||||
|
||||
var fieldValidity = isValid(formField)
|
||||
// If past rule is invalid for a field, the current rule should not flip the validity of a field
|
||||
if let validity = previousFieldValidity[formKey], !validity, fieldValidity {
|
||||
fieldValidity = false
|
||||
}
|
||||
(formField as? FormRuleWatcherFieldProtocol)?.setValidity(fieldValidity, rule: self)
|
||||
valid = valid && fieldValidity
|
||||
previousValidity[formKey] = fieldValidity
|
||||
}
|
||||
return (valid: valid, fieldValidity: previousValidity)
|
||||
}
|
||||
}
|
||||
|
||||
@ -423,7 +423,7 @@
|
||||
}
|
||||
|
||||
- (void)accessibilityFocusChanged:(NSNotification *)notification {
|
||||
if (![MVMCoreUIUtility viewContainsAccessiblityFocus:self]) {
|
||||
if (notification.userInfo[UIAccessibilityFocusedElementKey] && ![MVMCoreUIUtility viewContainsAccessiblityFocus:self]) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIAccessibilityElementFocusedNotification object:nil];
|
||||
[self collapse];
|
||||
}
|
||||
|
||||
@ -335,7 +335,7 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed.";
|
||||
|
||||
/// If the voice over user leaves top alert focus, hide.
|
||||
- (void)accessibilityFocusChanged:(NSNotification *)notification {
|
||||
if (![MVMCoreUIUtility viewContainsAccessiblityFocus:self]) {
|
||||
if (notification.userInfo[UIAccessibilityFocusedElementKey] && ![MVMCoreUIUtility viewContainsAccessiblityFocus:self]) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIAccessibilityElementFocusedNotification object:nil];
|
||||
[self hideAlertView:YES completionHandler:self.hideCompletionHandler];
|
||||
self.hideCompletionHandler = nil;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user