This commit is contained in:
Kevin G Christiano 2021-01-12 10:33:06 -05:00
commit 5826b39872
20 changed files with 194 additions and 89 deletions

View File

@ -290,6 +290,7 @@
BBC0C4FF24811DCA0087C44F /* TagModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBC0C4FE24811DCA0087C44F /* TagModel.swift */; }; BBC0C4FF24811DCA0087C44F /* TagModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBC0C4FE24811DCA0087C44F /* TagModel.swift */; };
C003506123AA94CD00B6AC29 /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = C003506023AA94CD00B6AC29 /* Button.swift */; }; C003506123AA94CD00B6AC29 /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = C003506023AA94CD00B6AC29 /* Button.swift */; };
C07065C42395677300FBF997 /* Link.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07065C32395677300FBF997 /* Link.swift */; }; C07065C42395677300FBF997 /* Link.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07065C32395677300FBF997 /* Link.swift */; };
C6687441259D92D400F32D13 /* ActionTopNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6687440259D92D400F32D13 /* ActionTopNotificationModel.swift */; };
C695A67F23C9830600BFB94E /* UnOrderedListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A67E23C9830600BFB94E /* UnOrderedListModel.swift */; }; C695A67F23C9830600BFB94E /* UnOrderedListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A67E23C9830600BFB94E /* UnOrderedListModel.swift */; };
C695A68123C9830D00BFB94E /* NumberedListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A68023C9830D00BFB94E /* NumberedListModel.swift */; }; C695A68123C9830D00BFB94E /* NumberedListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A68023C9830D00BFB94E /* NumberedListModel.swift */; };
C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A69323C9909000BFB94E /* DoughnutChartModel.swift */; }; C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C695A69323C9909000BFB94E /* DoughnutChartModel.swift */; };
@ -833,6 +834,7 @@
BBC0C4FE24811DCA0087C44F /* TagModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagModel.swift; sourceTree = "<group>"; }; BBC0C4FE24811DCA0087C44F /* TagModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagModel.swift; sourceTree = "<group>"; };
C003506023AA94CD00B6AC29 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; }; C003506023AA94CD00B6AC29 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
C07065C32395677300FBF997 /* Link.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Link.swift; sourceTree = "<group>"; }; C07065C32395677300FBF997 /* Link.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Link.swift; sourceTree = "<group>"; };
C6687440259D92D400F32D13 /* ActionTopNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionTopNotificationModel.swift; sourceTree = "<group>"; };
C695A67E23C9830600BFB94E /* UnOrderedListModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnOrderedListModel.swift; sourceTree = "<group>"; }; C695A67E23C9830600BFB94E /* UnOrderedListModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnOrderedListModel.swift; sourceTree = "<group>"; };
C695A68023C9830D00BFB94E /* NumberedListModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberedListModel.swift; sourceTree = "<group>"; }; C695A68023C9830D00BFB94E /* NumberedListModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberedListModel.swift; sourceTree = "<group>"; };
C695A69323C9909000BFB94E /* DoughnutChartModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoughnutChartModel.swift; sourceTree = "<group>"; }; C695A69323C9909000BFB94E /* DoughnutChartModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoughnutChartModel.swift; sourceTree = "<group>"; };
@ -1308,6 +1310,7 @@
D2ED27E9254B0CE600A1C293 /* ActionAlertModel.swift */, D2ED27E9254B0CE600A1C293 /* ActionAlertModel.swift */,
D2ED27EA254B0CE700A1C293 /* AlertModel.swift */, D2ED27EA254B0CE700A1C293 /* AlertModel.swift */,
D2ED27E8254B0CE600A1C293 /* ActionPopupModel.swift */, D2ED27E8254B0CE600A1C293 /* ActionPopupModel.swift */,
C6687440259D92D400F32D13 /* ActionTopNotificationModel.swift */,
); );
path = Actions; path = Actions;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2432,6 +2435,7 @@
32F8804824765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift in Sources */, 32F8804824765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift in Sources */,
D2CAC7CF2511052300C75681 /* CollapsableNotificationModel.swift in Sources */, D2CAC7CF2511052300C75681 /* CollapsableNotificationModel.swift in Sources */,
DBC4391822442197001AB423 /* CaretView.swift in Sources */, DBC4391822442197001AB423 /* CaretView.swift in Sources */,
C6687441259D92D400F32D13 /* ActionTopNotificationModel.swift in Sources */,
C07065C42395677300FBF997 /* Link.swift in Sources */, C07065C42395677300FBF997 /* Link.swift in Sources */,
0A69F611241BDEA700F7231B /* RuleAnyRequiredModel.swift in Sources */, 0A69F611241BDEA700F7231B /* RuleAnyRequiredModel.swift in Sources */,
D29B771022C281F400D6ACE0 /* ModuleMolecule.swift in Sources */, D29B771022C281F400D6ACE0 /* ModuleMolecule.swift in Sources */,

View File

@ -0,0 +1,24 @@
//
// ActionTopNotificationModel.swift
// MVMCoreUI
//
// Created by Murugan, Vimal on 31/12/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import UIKit
@objcMembers public class ActionTopNotificationModel: ActionModelProtocol {
public static var identifier: String = "topNotification"
public var actionType: String = ActionTopNotificationModel.identifier
public var topNotification: TopNotificationModel
public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary?
public init(topNotification: TopNotificationModel, _ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) {
self.topNotification = topNotification
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
}

View File

@ -71,13 +71,17 @@ import UIKit
public var showError: Bool { public var showError: Bool {
get { return entryFieldContainer.showError } get { return entryFieldContainer.showError }
set (error) { set (error) {
self.feedback = error ? entryFieldModel?.errorMessage : entryFieldModel?.feedback self.feedback = error ? errorMessage : entryFieldModel?.feedback
self.feedbackLabel.textColor = error ? entryFieldModel?.errorTextColor?.uiColor ?? .mvmBlack : .mvmBlack self.feedbackLabel.textColor = error ? entryFieldModel?.errorTextColor?.uiColor ?? .mvmBlack : .mvmBlack
self.entryFieldContainer.showError = error self.entryFieldContainer.showError = error
self.entryFieldModel?.showError = error self.entryFieldModel?.showError = error
} }
} }
var errorMessage: String? {
entryFieldModel?.dynamicErrorMessage ?? entryFieldModel?.errorMessage
}
/// Toggles original or locked UI. /// Toggles original or locked UI.
public var isLocked: Bool { public var isLocked: Bool {
get { return entryFieldContainer.isLocked } get { return entryFieldContainer.isLocked }
@ -306,16 +310,30 @@ import UIKit
if self.isSelected { if self.isSelected {
self.updateValidation(model.isValid ?? true) self.updateValidation(model.isValid ?? true)
} else if model.isValid ?? true && self.showError { } else if model.isValid ?? true && self.showError {
self.showError = false self.showError = false
} }
}) })
} }
model.updateUIDynamicError = { [weak self] in
MVMCoreDispatchUtility.performBlock(onMainThread: {
guard let self = self else { return }
let validState = model.isValid ?? false
self.updateValidation(validState)
if !validState && model.shouldClearText {
self.text = ""
model.shouldClearText = false
}
})
}
title = model.title title = model.title
feedback = model.feedback feedback = model.feedback
isEnabled = model.enabled isEnabled = model.enabled
entryFieldContainer.disableAllBorders = model.hideBorders entryFieldContainer.disableAllBorders = model.hideBorders
accessibilityIdentifier = model.accessibilityIdentifier ?? model.fieldKey
if let isLocked = model.locked { if let isLocked = model.locked {
self.isLocked = isLocked self.isLocked = isLocked

View File

@ -20,6 +20,13 @@ import Foundation
public var accessibilityIdentifier: String? public var accessibilityIdentifier: String?
public var title: String? public var title: String?
public var feedback: String? public var feedback: String?
public var shouldClearText: Bool = false
public var dynamicErrorMessage: String? {
didSet {
isValid = dynamicErrorMessage?.isEmpty ?? true
updateUIDynamicError?()
}
}
public var errorMessage: String? public var errorMessage: String?
public var errorTextColor: Color? public var errorTextColor: Color?
public var enabled: Bool = true public var enabled: Bool = true
@ -40,6 +47,9 @@ import Foundation
/// Temporary binding mechanism for the view to update on enable changes. /// Temporary binding mechanism for the view to update on enable changes.
public var updateUI: ActionBlock? public var updateUI: ActionBlock?
// TODO: Remove once updateUI is fixed with isSelected
public var updateUIDynamicError: ActionBlock?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
//-------------------------------------------------- //--------------------------------------------------
@ -66,7 +76,12 @@ import Foundation
// MARK: - Validation Methods // MARK: - Validation Methods
//-------------------------------------------------- //--------------------------------------------------
public func formFieldValue() -> AnyHashable? { text } public func formFieldValue() -> AnyHashable? {
if dynamicErrorMessage != nil {
dynamicErrorMessage = nil
}
return text
}
public func setValidity(_ valid: Bool, rule: RulesProtocol) { public func setValidity(_ valid: Bool, rule: RulesProtocol) {

View File

@ -259,6 +259,7 @@ import Foundation
try? ModelRegistry.register(ActionTopAlertModel.self) try? ModelRegistry.register(ActionTopAlertModel.self)
try? ModelRegistry.register(ActionCollapseNotificationModel.self) try? ModelRegistry.register(ActionCollapseNotificationModel.self)
try? ModelRegistry.register(ActionOpenPanelModel.self) try? ModelRegistry.register(ActionOpenPanelModel.self)
try? ModelRegistry.register(ActionTopNotificationModel.self)
// MARK:- Behaviors // MARK:- Behaviors
try? ModelRegistry.register(ScreenBrightnessModifierBehavior.self) try? ModelRegistry.register(ScreenBrightnessModifierBehavior.self)

View File

@ -9,12 +9,12 @@
import Foundation import Foundation
@objcMembers public class CarouselItemModel: MoleculeCollectionItemModel, CarouselItemModelProtocol { @objcMembers open class CarouselItemModel: MoleculeCollectionItemModel, CarouselItemModelProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public override class var identifier: String { open override class var identifier: String {
return "carouselItem" return "carouselItem"
} }
@ -44,7 +44,7 @@ import Foundation
try super.init(from: decoder) try super.init(from: decoder)
} }
public override func encode(to encoder: Encoder) throws { open override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder) try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(peakingUI, forKey: .peakingUI) try container.encodeIfPresent(peakingUI, forKey: .peakingUI)

View File

@ -9,7 +9,7 @@
import Foundation import Foundation
/// A model for a collection item that is a container for any molecule. /// A model for a collection item that is a container for any molecule.
@objcMembers public class MoleculeCollectionItemModel: MoleculeContainerModel, CollectionItemModelProtocol { @objcMembers open class MoleculeCollectionItemModel: MoleculeContainerModel, CollectionItemModelProtocol {
open override class var identifier: String { open override class var identifier: String {
return "collectionItem" return "collectionItem"
} }

View File

@ -26,6 +26,8 @@ import Foundation
adjustsImageWhenHighlighted = false adjustsImageWhenHighlighted = false
accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "AccCloseButton") accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "AccCloseButton")
setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
heightAnchor.constraint(equalToConstant: 16.0).isActive = true
widthAnchor.constraint(equalToConstant: 16.0).isActive = true
} }
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {

View File

@ -54,8 +54,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
open override func viewForTop() -> UIView { open override func viewForTop() -> UIView {
guard let headerModel = templateModel?.header, guard let headerModel = templateModel?.header,
let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar) let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar)
else { return super.viewForTop() } else { return super.viewForTop() }
// Temporary, Default the horizontal padding // Temporary, Default the horizontal padding
if var container = templateModel?.header as? ContainerModelProtocol, container.useHorizontalMargins == nil { if var container = templateModel?.header as? ContainerModelProtocol, container.useHorizontalMargins == nil {
@ -67,8 +67,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
override open func viewForBottom() -> UIView { override open func viewForBottom() -> UIView {
guard let footerModel = templateModel?.footer, guard let footerModel = templateModel?.footer,
let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar) let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar)
else { return super.viewForBottom() } else { return super.viewForBottom() }
return molecule return molecule
} }
@ -117,8 +117,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
guard let moleculeInfo = getMoleculeInfo(for: indexPath), guard let moleculeInfo = getMoleculeInfo(for: indexPath),
let estimatedHeight = (moleculeInfo.class as? MoleculeViewProtocol.Type)?.estimatedHeight(with: moleculeInfo.molecule, delegateObject() as? MVMCoreUIDelegateObject) let estimatedHeight = (moleculeInfo.class as? MoleculeViewProtocol.Type)?.estimatedHeight(with: moleculeInfo.molecule, delegateObject() as? MVMCoreUIDelegateObject)
else { return 0 } else { return 0 }
return estimatedHeight return estimatedHeight
} }
@ -130,8 +130,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
open override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { open override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let moleculeInfo = getMoleculeInfo(for: indexPath), guard let moleculeInfo = getMoleculeInfo(for: indexPath),
let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.identifier) let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.identifier)
else { return UITableViewCell() } else { return UITableViewCell() }
(cell as? MoleculeViewProtocol)?.reset() (cell as? MoleculeViewProtocol)?.reset()
(cell as? MoleculeListCellProtocol)?.setLines(with: templateModel?.line, delegateObject: delegateObjectIVar, additionalData: nil, indexPath: indexPath) (cell as? MoleculeListCellProtocol)?.setLines(with: templateModel?.line, delegateObject: delegateObjectIVar, additionalData: nil, indexPath: indexPath)
@ -222,8 +222,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
func createMoleculeInfo(with listItem: MoleculeModelProtocol?) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? { func createMoleculeInfo(with listItem: MoleculeModelProtocol?) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? {
guard let listItem = listItem, guard let listItem = listItem,
let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(listItem) let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(listItem)
else { return nil } else { return nil }
let moleculeName = moleculeClass.nameForReuse(with: listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName let moleculeName = moleculeClass.nameForReuse(with: listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName

View File

@ -9,8 +9,8 @@
import Foundation import Foundation
class UICollectionViewLeftAlignedLayout: UICollectionViewFlowLayout { public class UICollectionViewLeftAlignedLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { public override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let attributes = super.layoutAttributesForElements(in: rect) else { return nil } guard let attributes = super.layoutAttributesForElements(in: rect) else { return nil }
var newAttributesForElementsInRect = [UICollectionViewLayoutAttributes]() var newAttributesForElementsInRect = [UICollectionViewLayoutAttributes]()
for attribute in attributes { for attribute in attributes {

View File

@ -18,12 +18,8 @@ import UIKit
@objc public var loadObject: MVMCoreLoadObject? @objc public var loadObject: MVMCoreLoadObject?
public var model: MVMControllerModelProtocol? public var model: MVMControllerModelProtocol?
public var pageModel: PageModelProtocol? { public var pageModel: PageModelProtocol? {
get { get { model }
return model set { model = newValue as? MVMControllerModelProtocol }
}
set {
model = newValue as? MVMControllerModelProtocol
}
} }
/// Set if this page is containted in a manager. /// Set if this page is containted in a manager.
@ -31,19 +27,17 @@ import UIKit
/// A temporary iVar backer for delegateObject() until we change the protocol /// A temporary iVar backer for delegateObject() until we change the protocol
public lazy var delegateObjectIVar: MVMCoreUIDelegateObject = { public lazy var delegateObjectIVar: MVMCoreUIDelegateObject = {
return MVMCoreUIDelegateObject.create(withDelegateForAll: self) MVMCoreUIDelegateObject.create(withDelegateForAll: self)
}() }()
public func delegateObject() -> DelegateObject? { public func delegateObject() -> DelegateObject? { delegateObjectIVar }
return delegateObjectIVar
}
public var formValidator: FormValidator? public var formValidator: FormValidator?
public var needsUpdateUI = false public var needsUpdateUI = false
private var observingForResponses = false private var observingForResponses = false
private var initialLoadFinished = false private var initialLoadFinished = false
private var previousScreenSize = CGSize.zero public var previousScreenSize = CGSize.zero
public var selectedField: UIView? public var selectedField: UIView?
@ -52,7 +46,7 @@ import UIKit
/// Checks if the screen width has changed /// Checks if the screen width has changed
open func screenSizeChanged() -> Bool { open func screenSizeChanged() -> Bool {
return !MVMCoreGetterUtility.cgfequalwiththreshold(previousScreenSize.width, view.bounds.size.width, 0.1) !MVMCoreGetterUtility.cgfequalwiththreshold(previousScreenSize.width, view.bounds.size.width, 0.1)
} }
//-------------------------------------------------- //--------------------------------------------------
@ -61,8 +55,8 @@ import UIKit
open func observeForResponseJSONUpdates() { open func observeForResponseJSONUpdates() {
guard !observingForResponses, guard !observingForResponses,
(pagesToListenFor()?.count ?? 0 > 0 || modulesToListenFor()?.count ?? 0 > 0) (pagesToListenFor()?.count ?? 0 > 0 || modulesToListenFor()?.count ?? 0 > 0)
else { return } else { return }
observingForResponses = true observingForResponses = true
NotificationCenter.default.addObserver(self, selector: #selector(responseJSONUpdated(notification:)), name: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(responseJSONUpdated(notification:)), name: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil)
@ -80,28 +74,28 @@ import UIKit
} }
open func modulesToListenFor() -> [String]? { open func modulesToListenFor() -> [String]? {
return loadObject?.requestParameters?.modules as? [String] loadObject?.requestParameters?.modules as? [String]
} }
@objc open func responseJSONUpdated(notification: Notification) { @objc open func responseJSONUpdated(notification: Notification) {
// Checks for a page we are listening for. // Checks for a page we are listening for.
var newData = false var newData = false
if let pagesLoaded = notification.userInfo?.optionalDictionaryForKey(KeyPageMap), if let pagesLoaded = notification.userInfo?.optionalDictionaryForKey(KeyPageMap),
let pageType = pagesToListenFor()?.first(where: { (pageTypeListened) -> Bool in let pageType = pagesToListenFor()?.first(where: { (pageTypeListened) -> Bool in
guard let page = pagesLoaded.optionalDictionaryForKey(pageTypeListened), guard let page = pagesLoaded.optionalDictionaryForKey(pageTypeListened),
let pageType = page.optionalStringForKey(KeyPageType), let pageType = page.optionalStringForKey(KeyPageType),
pageType == pageTypeListened pageType == pageTypeListened
else { return false } else { return false }
return true return true
}) { }) {
newData = true newData = true
loadObject?.pageJSON = pagesLoaded.optionalDictionaryForKey(pageType) loadObject?.pageJSON = pagesLoaded.optionalDictionaryForKey(pageType)
} }
// Checks for modules we are listening for. // Checks for modules we are listening for.
if let modulesLoaded = notification.userInfo?.optionalDictionaryForKey(KeyModuleMap), if let modulesLoaded = notification.userInfo?.optionalDictionaryForKey(KeyModuleMap),
let modulesListened = modulesToListenFor() { let modulesListened = modulesToListenFor() {
for moduleName in modulesListened { for moduleName in modulesListened {
if let module = modulesLoaded.optionalDictionaryForKey(moduleName) { if let module = modulesLoaded.optionalDictionaryForKey(moduleName) {
newData = true newData = true
@ -196,9 +190,9 @@ import UIKit
open class func verifyRequiredModulesLoaded(for loadObject: MVMCoreLoadObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject>) -> Bool { open class func verifyRequiredModulesLoaded(for loadObject: MVMCoreLoadObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject>) -> Bool {
guard let pageType = loadObject?.pageType, guard let pageType = loadObject?.pageType,
var modulesRequired = MVMCoreUIViewControllerMappingObject.shared()?.modulesRequired(forPageType: pageType), var modulesRequired = MVMCoreUIViewControllerMappingObject.shared()?.modulesRequired(forPageType: pageType),
!modulesRequired.isEmpty !modulesRequired.isEmpty
else { return true } else { return true }
guard let loadedModules = loadObject?.modulesJSON else { return false } guard let loadedModules = loadObject?.modulesJSON else { return false }
@ -263,8 +257,8 @@ import UIKit
/// Sets the navigation item for this view controller. /// Sets the navigation item for this view controller.
open func setNavigationItem() { open func setNavigationItem() {
guard let navigationItemModel = getNavigationModel(), guard let navigationItemModel = getNavigationModel(),
let navigationController = navigationController let navigationController = navigationController
else { return } else { return }
// Utilize helper function to set the navigation item state. // Utilize helper function to set the navigation item state.
NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: self) NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: self)
@ -273,9 +267,9 @@ import UIKit
/// Sets the appearance of the navigation bar based on the model. /// Sets the appearance of the navigation bar based on the model.
open func setNavigationBar() { open func setNavigationBar() {
guard let navigationItemModel = getNavigationModel(), guard let navigationItemModel = getNavigationModel(),
let navigationController = navigationController else { let navigationController = navigationController else {
MVMCoreUISession.sharedGlobal()?.splitViewController?.parent?.setNeedsStatusBarAppearanceUpdate() MVMCoreUISession.sharedGlobal()?.splitViewController?.parent?.setNeedsStatusBarAppearanceUpdate()
return return
} }
// Utilize helper function to set the split view and navigation item state. // Utilize helper function to set the split view and navigation item state.
@ -405,7 +399,7 @@ import UIKit
} }
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask { open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return MVMCoreGetterUtility.isOnIPad() ? UIInterfaceOrientationMask.all : UIInterfaceOrientationMask.portrait MVMCoreGetterUtility.isOnIPad() ? UIInterfaceOrientationMask.all : UIInterfaceOrientationMask.portrait
} }
open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
@ -424,6 +418,7 @@ import UIKit
open func viewControllerReady(inManager manager: UIViewController & MVMCoreViewManagerProtocol) { open func viewControllerReady(inManager manager: UIViewController & MVMCoreViewManagerProtocol) {
pageShown() pageShown()
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - MVMCoreLoadDelegateProtocol // MARK: - MVMCoreLoadDelegateProtocol
//-------------------------------------------------- //--------------------------------------------------
@ -435,7 +430,7 @@ import UIKit
// Open the support panel // Open the support panel
if error == nil, if error == nil,
loadObject?.requestParameters?.openSupportPanel ?? (loadObject?.systemParametersJSON?.boolForKey(KeyOpenSupport) ?? false) == true { loadObject?.requestParameters?.openSupportPanel ?? (loadObject?.systemParametersJSON?.boolForKey(KeyOpenSupport) ?? false) == true {
MVMCoreUISession.sharedGlobal()?.splitViewController?.showRightPanel(animated: true) MVMCoreUISession.sharedGlobal()?.splitViewController?.showRightPanel(animated: true)
} }
} }
@ -444,6 +439,21 @@ import UIKit
open func addFormParams(_ requestParameters: MVMCoreRequestParameters) { open func addFormParams(_ requestParameters: MVMCoreRequestParameters) {
formValidator?.addFormParams(requestParameters: requestParameters) formValidator?.addFormParams(requestParameters: requestParameters)
} }
public func handleFieldErrors(_ fieldErrors: [Any]?, loadObject: MVMCoreLoadObject) {
for case let fieldError as [AnyHashable: Any] in fieldErrors ?? [] {
guard let fieldName = fieldError["fieldName"] as? String,
let userError = fieldError["userMessage"] as? String,
let entryFieldModel = formValidator?.fields[fieldName] as? EntryFieldModel
else { continue }
entryFieldModel.shouldClearText = fieldError["clearText"] as? Bool ?? true
entryFieldModel.dynamicErrorMessage = userError
}
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - MVMCoreActionDelegateProtocol // MARK: - MVMCoreActionDelegateProtocol
//-------------------------------------------------- //--------------------------------------------------
@ -469,9 +479,9 @@ import UIKit
open func getModuleWithName(_ moleculeName: String) -> MoleculeModelProtocol? { open func getModuleWithName(_ moleculeName: String) -> MoleculeModelProtocol? {
guard let moduleJSON = loadObject?.modulesJSON?.optionalDictionaryForKey(moleculeName), guard let moduleJSON = loadObject?.modulesJSON?.optionalDictionaryForKey(moleculeName),
let moleculeName = moduleJSON.optionalStringForKey("moleculeName"), let moleculeName = moduleJSON.optionalStringForKey("moleculeName"),
let modelType = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self) let modelType = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self)
else { return nil } else { return nil }
do { do {
return try modelType.decode(jsonDict: moduleJSON) as? MoleculeModelProtocol return try modelType.decode(jsonDict: moduleJSON) as? MoleculeModelProtocol
@ -483,8 +493,8 @@ import UIKit
} }
// Needed otherwise when subclassed, the extension gets called. // Needed otherwise when subclassed, the extension gets called.
open func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) {} open func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { }
open func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? { return nil } open func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? { nil }
open func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) { } open func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) { }
open func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) { } open func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) { }
@ -514,13 +524,13 @@ import UIKit
} }
open func showRightPanelForScreenBeforeLaunchApp() -> Bool { open func showRightPanelForScreenBeforeLaunchApp() -> Bool {
return loadObject?.pageJSON?.lenientBoolForKey("showRightPanel") ?? false loadObject?.pageJSON?.lenientBoolForKey("showRightPanel") ?? false
} }
// TODO: make molecular // TODO: make molecular
open func isOverridingRightButton() -> Bool { open func isOverridingRightButton() -> Bool {
guard let rightPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("rightPanelButtonLink") guard let rightPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("rightPanelButtonLink")
else { return false } else { return false }
MVMCoreActionHandler.shared()?.handleAction(with: rightPanelLink, additionalData: nil, delegateObject: delegateObject()) MVMCoreActionHandler.shared()?.handleAction(with: rightPanelLink, additionalData: nil, delegateObject: delegateObject())
return true return true
} }
@ -528,7 +538,7 @@ import UIKit
// TODO: make molecular // TODO: make molecular
open func isOverridingLeftButton() -> Bool { open func isOverridingLeftButton() -> Bool {
guard let leftPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("leftPanelButtonLink") guard let leftPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("leftPanelButtonLink")
else { return false } else { return false }
MVMCoreActionHandler.shared()?.handleAction(with: leftPanelLink, additionalData: nil, delegateObject: delegateObject()) MVMCoreActionHandler.shared()?.handleAction(with: leftPanelLink, additionalData: nil, delegateObject: delegateObject())
return true return true
} }
@ -536,8 +546,8 @@ import UIKit
// Eventually will be moved to Model // Eventually will be moved to Model
open func bottomProgress() -> Float? { open func bottomProgress() -> Float? {
guard let progressString = loadObject?.pageJSON?.optionalStringForKey(KeyProgressPercent), guard let progressString = loadObject?.pageJSON?.optionalStringForKey(KeyProgressPercent),
let progress = Float(progressString) let progress = Float(progressString)
else { return nil } else { return nil }
return progress / Float(100) return progress / Float(100)
} }
@ -558,8 +568,8 @@ import UIKit
// TODO: Make this into a protocol // TODO: Make this into a protocol
if UIAccessibility.isVoiceOverRunning { if UIAccessibility.isVoiceOverRunning {
if let toolBar = textField.inputAccessoryView as? UIToolbar, if let toolBar = textField.inputAccessoryView as? UIToolbar,
let _ = toolBar.items?.last, let _ = toolBar.items?.last,
let pickerView = textField.inputView as? UIPickerView { let pickerView = textField.inputView as? UIPickerView {
view.accessibilityElements = [pickerView, toolBar] view.accessibilityElements = [pickerView, toolBar]
} }
@ -610,6 +620,6 @@ import UIKit
//-------------------------------------------------- //--------------------------------------------------
func executeBehaviors<T>(_ behaviorBlock:(_ behavior:T)->Void) { func executeBehaviors<T>(_ behaviorBlock:(_ behavior:T)->Void) {
model?.behaviors?.compactMap({ $0 as? T }).forEach { behaviorBlock($0) } model?.behaviors?.compactMap { $0 as? T }.forEach { behaviorBlock($0) }
} }
} }

View File

@ -15,9 +15,9 @@ open class Container: View, ContainerProtocol {
//-------------------------------------------------- //--------------------------------------------------
public var view: UIView? public var view: UIView?
let containerHelper = ContainerHelper() public let containerHelper = ContainerHelper()
var containerModel: ContainerModelProtocol? { public var containerModel: ContainerModelProtocol? {
get { return model as? ContainerModelProtocol } get { return model as? ContainerModelProtocol }
} }

View File

@ -15,23 +15,23 @@ open class ContainerHelper: NSObject {
// MARK: - Constraints // MARK: - Constraints
//-------------------------------------------------- //--------------------------------------------------
var leftConstraint: NSLayoutConstraint? open var leftConstraint: NSLayoutConstraint?
var topConstraint: NSLayoutConstraint? open var topConstraint: NSLayoutConstraint?
var bottomConstraint: NSLayoutConstraint? open var bottomConstraint: NSLayoutConstraint?
var rightConstraint: NSLayoutConstraint? open var rightConstraint: NSLayoutConstraint?
var alignCenterHorizontalConstraint: NSLayoutConstraint? open var alignCenterHorizontalConstraint: NSLayoutConstraint?
var alignCenterLeftConstraint: NSLayoutConstraint? open var alignCenterLeftConstraint: NSLayoutConstraint?
var alignCenterRightConstraint: NSLayoutConstraint? open var alignCenterRightConstraint: NSLayoutConstraint?
var alignCenterVerticalConstraint: NSLayoutConstraint? open var alignCenterVerticalConstraint: NSLayoutConstraint?
var alignCenterTopConstraint: NSLayoutConstraint? open var alignCenterTopConstraint: NSLayoutConstraint?
var alignCenterBottomConstraint: NSLayoutConstraint? open var alignCenterBottomConstraint: NSLayoutConstraint?
var leftLowConstraint: NSLayoutConstraint? open var leftLowConstraint: NSLayoutConstraint?
var topLowConstraint: NSLayoutConstraint? open var topLowConstraint: NSLayoutConstraint?
var bottomLowConstraint: NSLayoutConstraint? open var bottomLowConstraint: NSLayoutConstraint?
var rightLowConstraint: NSLayoutConstraint? open var rightLowConstraint: NSLayoutConstraint?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Methods // MARK: - Methods

View File

@ -22,7 +22,6 @@ public protocol FormFieldProtocol: FormItemProtocol {
} }
extension FormFieldProtocol { extension FormFieldProtocol {
var baseValue: AnyHashable? {
return nil var baseValue: AnyHashable? { nil }
}
} }

View File

@ -24,6 +24,9 @@ NS_ASSUME_NONNULL_BEGIN
// Collapses the current top notification // Collapses the current top notification
- (void)collapseNotificationAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; - (void)collapseNotificationAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
// Shows a topnotification new molecular
- (void)topNotificationAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
#pragma mark - Deprecated #pragma mark - Deprecated
// Shows a popup // Shows a popup
@ -32,6 +35,9 @@ NS_ASSUME_NONNULL_BEGIN
// Shows a top alert // Shows a top alert
- (void)topAlertAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated; - (void)topAlertAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
// Shows a molecular top alert
- (void)topNotificationAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
// Collapses the current top notification // Collapses the current top notification
- (void)collapseNotificationAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated; - (void)collapseNotificationAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;

View File

@ -31,6 +31,9 @@
} else if ([actionType isEqualToString:KeyActionTypeAlert]) { } else if ([actionType isEqualToString:KeyActionTypeAlert]) {
[self showAlert:actionInformation additionalData:additionalData delegateObject:delegateObject]; [self showAlert:actionInformation additionalData:additionalData delegateObject:delegateObject];
return YES; return YES;
} else if ([actionType isEqualToString:KeyActionTypeTopNotification]) {
[self topNotificationAction:actionInformation additionalData:additionalData delegateObject:delegateObject];
return YES;
} }
return NO; return NO;
} }
@ -102,6 +105,11 @@
} }
} }
- (void)topNotificationAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
//Handle molecular topnotification
[[MVMCoreUITopAlertView sharedGlobal] showTopAlertWith:[actionInformation dict:@"topNotification"] ?: @{}];
}
- (void)defaultHandleActionError:(nonnull MVMCoreErrorObject *)error additionalData:(nullable NSDictionary *)additionalData { - (void)defaultHandleActionError:(nonnull MVMCoreErrorObject *)error additionalData:(nullable NSDictionary *)additionalData {
[super defaultHandleActionError:error additionalData:additionalData]; [super defaultHandleActionError:error additionalData:additionalData];
if (!error.silentError) { if (!error.silentError) {
@ -126,6 +134,9 @@
} else if ([actionType isEqualToString:KeyActionTypeCollapseNotification]) { } else if ([actionType isEqualToString:KeyActionTypeCollapseNotification]) {
[self collapseNotificationAction:actionInformation additionalData:additionalData delegate:delegate]; [self collapseNotificationAction:actionInformation additionalData:additionalData delegate:delegate];
return YES; return YES;
} else if ([actionType isEqualToString:KeyActionTypeTopNotification]) {
[self topNotificationAction:actionInformation additionalData:additionalData delegate:delegate];
return YES;
} }
return NO; return NO;
} }
@ -172,4 +183,9 @@
} }
} }
- (void)topNotificationAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate {
//Handle molecular topnotification
[[MVMCoreUITopAlertView sharedGlobal] showTopAlertWith:[actionInformation dict:@"topNotification"] ?: @{}];
}
@end @end

View File

@ -77,6 +77,8 @@
[[MVMCoreUISession sharedGlobal].topAlertView hideAlertView:YES completionHandler:nil]; [[MVMCoreUISession sharedGlobal].topAlertView hideAlertView:YES completionHandler:nil];
} }
} centeredVertically:YES]; } centeredVertically:YES];
[closeButton.heightAnchor constraintEqualToConstant:16.0].active = YES;
[closeButton.widthAnchor constraintEqualToConstant:16.0].active = YES;
[MVMCoreUITopAlertBaseView amendAccesibilityLabelForView:closeButton]; [MVMCoreUITopAlertBaseView amendAccesibilityLabelForView:closeButton];
return closeButton; return closeButton;
} }

View File

@ -61,6 +61,12 @@ public extension MVMCoreUITopAlertView {
MVMCoreAlertHandler.shared()?.add(operation) MVMCoreAlertHandler.shared()?.add(operation)
} }
/// Shows the top alert with the json.
@objc func showTopAlert(with json: [AnyHashable: Any]) {
guard let model = decodeTopNotification(with: json, delegateObject: getDelegateObject()) else { return }
showTopAlert(with: model)
}
/// Checks for existing top alert object of same type and updates it. Only happens for molecular top alerts. Returns true if we updated. /// Checks for existing top alert object of same type and updates it. Only happens for molecular top alerts. Returns true if we updated.
private func checkAndUpdateExisting(with topAlertObject: MVMCoreTopAlertObject) -> Bool { private func checkAndUpdateExisting(with topAlertObject: MVMCoreTopAlertObject) -> Bool {
guard let queue = MVMCoreAlertHandler.shared()?.topAlertQueue.operations else { return false } guard let queue = MVMCoreAlertHandler.shared()?.topAlertQueue.operations else { return false }

View File

@ -48,6 +48,7 @@ extern NSString * const KeyActionTypePopup;
extern NSString * const KeyActionTypeAlert; extern NSString * const KeyActionTypeAlert;
extern NSString * const KeyActionTypeTopAlert; extern NSString * const KeyActionTypeTopAlert;
extern NSString * const KeyActionTypeCollapseNotification; extern NSString * const KeyActionTypeCollapseNotification;
extern NSString * const KeyActionTypeTopNotification;
/// Key for molecular top notification architecture. /// Key for molecular top notification architecture.
extern NSString * const KeyTopAlert; extern NSString * const KeyTopAlert;

View File

@ -47,6 +47,7 @@ NSString * const KeyActionTypeAlert = @"alert";
NSString * const KeyActionTypeTopAlert = @"topAlert"; NSString * const KeyActionTypeTopAlert = @"topAlert";
NSString * const KeyActionTypeCollapseNotification = @"collapseNotification"; NSString * const KeyActionTypeCollapseNotification = @"collapseNotification";
NSString * const KeyActionTypeTopNotification = @"topNotification";
NSString * const KeyTopAlert = @"TopNotification"; NSString * const KeyTopAlert = @"TopNotification";
#pragma mark - Values #pragma mark - Values