Merge remote-tracking branch 'origin/develop' into feature/monarch
This commit is contained in:
commit
2e36423fae
@ -24,6 +24,7 @@
|
|||||||
public var fieldKey: String?
|
public var fieldKey: String?
|
||||||
public var groupName: String = FormValidator.defaultGroupName
|
public var groupName: String = FormValidator.defaultGroupName
|
||||||
public var baseValue: AnyHashable?
|
public var baseValue: AnyHashable?
|
||||||
|
public var gone: Bool = false
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Validation
|
// MARK: - Validation
|
||||||
@ -53,6 +54,7 @@
|
|||||||
case groupName
|
case groupName
|
||||||
case enabled
|
case enabled
|
||||||
case readOnly
|
case readOnly
|
||||||
|
case gone
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -76,6 +78,7 @@
|
|||||||
if let readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) {
|
if let readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) {
|
||||||
self.readOnly = readOnly
|
self.readOnly = readOnly
|
||||||
}
|
}
|
||||||
|
gone = try typeContainer.decodeIfPresent(Bool.self, forKey: .gone) ?? false
|
||||||
try super.init(from: decoder)
|
try super.init(from: decoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,6 +102,7 @@
|
|||||||
&& fieldValue == model.fieldValue
|
&& fieldValue == model.fieldValue
|
||||||
&& enabled == model.enabled
|
&& enabled == model.enabled
|
||||||
&& readOnly == model.readOnly
|
&& readOnly == model.readOnly
|
||||||
|
&& gone == model.gone
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func isVisuallyEquivalent(to model: any MoleculeModelComparisonProtocol) -> Bool {
|
public override func isVisuallyEquivalent(to model: any MoleculeModelComparisonProtocol) -> Bool {
|
||||||
@ -107,5 +111,6 @@
|
|||||||
&& peakingArrowColor == model.peakingArrowColor
|
&& peakingArrowColor == model.peakingArrowColor
|
||||||
&& enabled == model.enabled
|
&& enabled == model.enabled
|
||||||
&& readOnly == model.readOnly
|
&& readOnly == model.readOnly
|
||||||
|
&& gone == model.gone
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,7 +90,7 @@ open class Carousel: View {
|
|||||||
showPeaking(false)
|
showPeaking(false)
|
||||||
|
|
||||||
// Go to current cell. layoutIfNeeded is needed otherwise cellForItem returns nil for peaking logic. The dispatch is a sad way to ensure the collection view is ready to be scrolled.
|
// Go to current cell. layoutIfNeeded is needed otherwise cellForItem returns nil for peaking logic. The dispatch is a sad way to ensure the collection view is ready to be scrolled.
|
||||||
guard let model = model as? CarouselModel, !model.molecules.isEmpty else { return }
|
guard let model = model as? CarouselModel, !model.visibleMolecules.isEmpty else { return }
|
||||||
guard (model.paging == true || loop == true) else {
|
guard (model.paging == true || loop == true) else {
|
||||||
DispatchQueue.main.async { [self] in
|
DispatchQueue.main.async { [self] in
|
||||||
updatePagerVisibility()
|
updatePagerVisibility()
|
||||||
@ -174,7 +174,9 @@ open class Carousel: View {
|
|||||||
MVMCoreLoggingHandler.shared()?.handleDebugMessage("[\(Self.self)] [\(ObjectIdentifier(self).hashValue)]\noriginal model: \(originalModel?.debugDescription ?? "none")\nnew model: \(model)")
|
MVMCoreLoggingHandler.shared()?.handleDebugMessage("[\(Self.self)] [\(ObjectIdentifier(self).hashValue)]\noriginal model: \(originalModel?.debugDescription ?? "none")\nnew model: \(model)")
|
||||||
|
|
||||||
if #available(iOS 15.0, *) {
|
if #available(iOS 15.0, *) {
|
||||||
if let originalModel, carouselModel.isDeeplyVisuallyEquivalent(to: originalModel) {
|
if let originalModel, carouselModel.isDeeplyVisuallyEquivalent(to: originalModel),
|
||||||
|
originalModel.visibleMolecules.isVisuallyEquivalent(to: molecules ?? []) // Since the carousel model's children are in place replaced and we do not have a deep copy of this model tree, add in this hack to check if the prior captured carousel items match the newly visible ones.
|
||||||
|
{
|
||||||
// Prevents a carousel reset while still updating the cell backing data through reconfigureItems.
|
// Prevents a carousel reset while still updating the cell backing data through reconfigureItems.
|
||||||
MVMCoreLoggingHandler.shared()?.handleDebugMessage("[\(Self.self)] Model is visually equivalent. Skipping rebuild...")
|
MVMCoreLoggingHandler.shared()?.handleDebugMessage("[\(Self.self)] Model is visually equivalent. Skipping rebuild...")
|
||||||
prepareMolecules(with: carouselModel)
|
prepareMolecules(with: carouselModel)
|
||||||
@ -229,7 +231,7 @@ open class Carousel: View {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
func prepareMolecules(with carouselModel: CarouselModel?) {
|
func prepareMolecules(with carouselModel: CarouselModel?) {
|
||||||
guard let newMolecules = carouselModel?.molecules else {
|
guard let newMolecules = carouselModel?.visibleMolecules else {
|
||||||
numberOfPages = 0
|
numberOfPages = 0
|
||||||
molecules = nil
|
molecules = nil
|
||||||
return
|
return
|
||||||
|
|||||||
@ -44,6 +44,10 @@ import UIKit
|
|||||||
public var selectable = false
|
public var selectable = false
|
||||||
public var selectedIndex: Int?
|
public var selectedIndex: Int?
|
||||||
|
|
||||||
|
public var visibleMolecules: [MoleculeModelProtocol & CarouselItemModelProtocol] {
|
||||||
|
molecules.filter { !$0.gone }
|
||||||
|
}
|
||||||
|
|
||||||
public init(molecules: [MoleculeModelProtocol & CarouselItemModelProtocol]) {
|
public init(molecules: [MoleculeModelProtocol & CarouselItemModelProtocol]) {
|
||||||
self.molecules = molecules
|
self.molecules = molecules
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
public protocol CarouselItemModelProtocol: FormFieldProtocol, ContainerModelProtocol {
|
public protocol CarouselItemModelProtocol: FormFieldProtocol, ContainerModelProtocol {
|
||||||
var analyticsData: JSONValueDictionary? { get set }
|
var analyticsData: JSONValueDictionary? { get set }
|
||||||
|
var gone: Bool { get set }
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension CarouselItemModelProtocol {
|
public extension CarouselItemModelProtocol {
|
||||||
@ -16,4 +17,9 @@ public extension CarouselItemModelProtocol {
|
|||||||
get { nil }
|
get { nil }
|
||||||
set { analyticsData = newValue }
|
set { analyticsData = newValue }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var gone: Bool{
|
||||||
|
get { false }
|
||||||
|
set { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,7 +43,7 @@ public extension ParentModelProtocol where Self: AnyObject {
|
|||||||
|
|
||||||
func replaceChildMolecule<T>(in molecules: inout [T], with replacementMolecule: MoleculeModelProtocol) throws -> MoleculeModelProtocol? {
|
func replaceChildMolecule<T>(in molecules: inout [T], with replacementMolecule: MoleculeModelProtocol) throws -> MoleculeModelProtocol? {
|
||||||
var replacedMolecule: MoleculeModelProtocol?
|
var replacedMolecule: MoleculeModelProtocol?
|
||||||
return try replaceChildMolecule(at: &molecules, with: replacementMolecule, replaced: &replacedMolecule) ? replacedMolecule : nil
|
return try replaceChildMolecule(in: &molecules, with: replacementMolecule, replaced: &replacedMolecule) ? replacedMolecule : nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper for replacing a molecule in place within an array. Note the "in".
|
/// Helper for replacing a molecule in place within an array. Note the "in".
|
||||||
@ -67,6 +67,26 @@ public protocol ParentMoleculeModelProtocol: ParentModelProtocol, MoleculeModelP
|
|||||||
|
|
||||||
public extension ParentMoleculeModelProtocol {
|
public extension ParentMoleculeModelProtocol {
|
||||||
|
|
||||||
|
/// Recursively finds and replaces the first child matching the replacement molecule id property.
|
||||||
|
mutating func deepReplaceMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> MoleculeModelProtocol? {
|
||||||
|
var replacedMolecule: MoleculeModelProtocol?
|
||||||
|
var possibleError: Error?
|
||||||
|
// Dive into each root.
|
||||||
|
depthFirstTraverse(options: .parentFirst, depth: 0) { depth, molecule, stop in
|
||||||
|
guard var parentMolecule = molecule as? ParentMoleculeModelProtocol else { return }
|
||||||
|
do {
|
||||||
|
replacedMolecule = try parentMolecule.replaceChildMolecule(with: replacementMolecule)
|
||||||
|
} catch {
|
||||||
|
possibleError = error
|
||||||
|
}
|
||||||
|
stop = replacedMolecule != nil || possibleError != nil
|
||||||
|
}
|
||||||
|
if let error = possibleError {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
return replacedMolecule
|
||||||
|
}
|
||||||
|
|
||||||
func reduceDepthFirstTraverse<Result>(options: TreeTraversalOptions, depth: Int, initialResult: Result, nextPartialResult: (Result, MoleculeModelProtocol, Int) -> Result) -> Result {
|
func reduceDepthFirstTraverse<Result>(options: TreeTraversalOptions, depth: Int, initialResult: Result, nextPartialResult: (Result, MoleculeModelProtocol, Int) -> Result) -> Result {
|
||||||
var result = initialResult
|
var result = initialResult
|
||||||
if (options == .parentFirst) {
|
if (options == .parentFirst) {
|
||||||
|
|||||||
@ -40,31 +40,3 @@ public extension TemplateModelProtocol {
|
|||||||
return rootMolecules.depthFirstTraverse(options: options, depth: depth, onVisit: onVisit)
|
return rootMolecules.depthFirstTraverse(options: options, depth: depth, onVisit: onVisit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TemplateModelProtocol {
|
|
||||||
|
|
||||||
/// Recursively finds and replaces the first child matching the replacement molecule id property.
|
|
||||||
mutating func replaceMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> MoleculeModelProtocol? {
|
|
||||||
// Attempt root level replacement on the template model first.
|
|
||||||
if let replacedMolecule = try replaceChildMolecule(with: replacementMolecule) {
|
|
||||||
return replacedMolecule
|
|
||||||
}
|
|
||||||
|
|
||||||
var replacedMolecule: MoleculeModelProtocol?
|
|
||||||
var possibleError: Error?
|
|
||||||
// Dive into each root thereafter.
|
|
||||||
depthFirstTraverse(options: .parentFirst, depth: 0) { depth, molecule, stop in
|
|
||||||
guard var parentMolecule = molecule as? ParentMoleculeModelProtocol else { return }
|
|
||||||
do {
|
|
||||||
replacedMolecule = try parentMolecule.replaceChildMolecule(with: replacementMolecule)
|
|
||||||
} catch {
|
|
||||||
possibleError = error
|
|
||||||
}
|
|
||||||
stop = replacedMolecule != nil || possibleError != nil
|
|
||||||
}
|
|
||||||
if let error = possibleError {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
return replacedMolecule
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -146,6 +146,11 @@ import MVMCore
|
|||||||
do {
|
do {
|
||||||
let template = try parsePageJSON(loadObject: loadObject)
|
let template = try parsePageJSON(loadObject: loadObject)
|
||||||
pageModel = template // TODO: Eventually this page parsing should be done outside of this class and then set by the caller. For now, double duty.
|
pageModel = template // TODO: Eventually this page parsing should be done outside of this class and then set by the caller. For now, double duty.
|
||||||
|
// Needed for PageMoleculeTransformationBehavior + PageLocalDataShareBehavior behaviors.
|
||||||
|
if let behaviorContainer = template as? (PageBehaviorContainerModelProtocol & TemplateModelProtocol) {
|
||||||
|
var behaviorHandler = self
|
||||||
|
behaviorHandler.applyBehaviors(pageBehaviorModel: behaviorContainer)
|
||||||
|
}
|
||||||
isFirstRender = true // Assuming this is only on the first page load from the handler. Might need to revist later.
|
isFirstRender = true // Assuming this is only on the first page load from the handler. Might need to revist later.
|
||||||
if let backgroundRequest = loadObject.requestParameters?.backgroundRequest, !backgroundRequest, let pageType, let identifier = loadObject.identifier {
|
if let backgroundRequest = loadObject.requestParameters?.backgroundRequest, !backgroundRequest, let pageType, let identifier = loadObject.identifier {
|
||||||
MVMCoreLoggingHandler.shared()?.logCoreEvent(.pageProcessingComplete(pageType: pageType, requestUUID: identifier, webUrl: nil))
|
MVMCoreLoggingHandler.shared()?.logCoreEvent(.pageProcessingComplete(pageType: pageType, requestUUID: identifier, webUrl: nil))
|
||||||
@ -230,11 +235,11 @@ import MVMCore
|
|||||||
open func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) {
|
open func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) {
|
||||||
|
|
||||||
guard var newPageModel = pageModel ?? self.pageModel else { return }
|
guard var newPageModel = pageModel ?? self.pageModel else { return }
|
||||||
let originalModel = isFirstRender ? nil : self.pageModel as? MVMControllerModelProtocol
|
let originalModel = self.pageModel as? MVMControllerModelProtocol
|
||||||
|
|
||||||
// Refresh our behaviors if there is a page change.
|
// Refresh our behaviors if there is a page change. Originally set up in shouldFinishProcessingLoad.
|
||||||
if let behaviorContainer = newPageModel as? (PageBehaviorContainerModelProtocol & TemplateModelProtocol),
|
if let behaviorContainer = newPageModel as? (PageBehaviorContainerModelProtocol & TemplateModelProtocol),
|
||||||
(originalModel == nil || originalModel!.id != behaviorContainer.id) {
|
(originalModel == nil || originalModel!.id != behaviorContainer.id) {
|
||||||
var behaviorHandler = self
|
var behaviorHandler = self
|
||||||
behaviorHandler.applyBehaviors(pageBehaviorModel: behaviorContainer)
|
behaviorHandler.applyBehaviors(pageBehaviorModel: behaviorContainer)
|
||||||
}
|
}
|
||||||
@ -277,7 +282,6 @@ import MVMCore
|
|||||||
let allUpdatedMolecules = behaviorUpdatedModels //+ pageUpdatedModels
|
let allUpdatedMolecules = behaviorUpdatedModels //+ pageUpdatedModels
|
||||||
|
|
||||||
// Notify the manager of new data.
|
// Notify the manager of new data.
|
||||||
// Warning: Some flows cause table reloads. Until the UI update is decoupled, should be after the updateUI.
|
|
||||||
manager?.newDataReceived?(in: self)
|
manager?.newDataReceived?(in: self)
|
||||||
|
|
||||||
guard shouldTriggerRender else { return }
|
guard shouldTriggerRender else { return }
|
||||||
|
|||||||
@ -41,7 +41,7 @@ public extension PageBehaviorHandlerProtocol {
|
|||||||
// Apply them to the page.
|
// Apply them to the page.
|
||||||
self.behaviors = behaviors.count > 0 ? behaviors : nil
|
self.behaviors = behaviors.count > 0 ? behaviors : nil
|
||||||
|
|
||||||
// Ask the session to apply any more. (Curently inverted contol due to Swift <--> Obj-C conflict.
|
// Ask the session to apply any more. (Currently inverted contol due to Swift <--> Obj-C conflict.)
|
||||||
if let viewController = self as? UIViewController {
|
if let viewController = self as? UIViewController {
|
||||||
MVMCoreUISession.sharedGlobal()?.applyGlobalBehaviors(to: viewController)
|
MVMCoreUISession.sharedGlobal()?.applyGlobalBehaviors(to: viewController)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,7 +41,7 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
|
|||||||
self.delegateObject = delegateObject
|
self.delegateObject = delegateObject
|
||||||
guard let pageType = delegateObject?.moleculeDelegate?.getTemplateModel()?.pageType else { return }
|
guard let pageType = delegateObject?.moleculeDelegate?.getTemplateModel()?.pageType else { return }
|
||||||
MVMCoreViewControllerMappingObject.shared()?.addOptionalModules(toMapping: moleculeIds, forPageType: pageType)
|
MVMCoreViewControllerMappingObject.shared()?.addOptionalModules(toMapping: moleculeIds, forPageType: pageType)
|
||||||
Self.debugLog("Initializing for \((model as! ReplaceableMoleculeBehaviorModel).moleculeIds)")
|
Self.debugLog("Initializing for \(moleculeIds)")
|
||||||
}
|
}
|
||||||
|
|
||||||
public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?, changes: inout [MoleculeModelProtocol]) -> [MoleculeModelProtocol]? {
|
public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?, changes: inout [MoleculeModelProtocol]) -> [MoleculeModelProtocol]? {
|
||||||
@ -85,7 +85,7 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
|
|||||||
|
|
||||||
moleculeModels.forEach { newMolecule in
|
moleculeModels.forEach { newMolecule in
|
||||||
do {
|
do {
|
||||||
if let replacedMolecule = try parentMolecule.replaceChildMolecule(with: newMolecule) {
|
if let replacedMolecule = try parentMolecule.deepReplaceMolecule(with: newMolecule) {
|
||||||
guard !replacedMolecule.deepEquals(to: newMolecule) else {
|
guard !replacedMolecule.deepEquals(to: newMolecule) else {
|
||||||
// Note: Slight risk here of replacing the something in the original tree and misreporting that is it not replaced based on equality.
|
// Note: Slight risk here of replacing the something in the original tree and misreporting that is it not replaced based on equality.
|
||||||
debugLog("deep molecule \(newMolecule) is the same as \(replacedMolecule). skipping...")
|
debugLog("deep molecule \(newMolecule) is the same as \(replacedMolecule). skipping...")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user