Digital PCT265 story PCT-135: More isEquals. Fix replaceChildMolecule signature for TabsListItemModel.

This commit is contained in:
Hedden, Kyle Matthew 2024-05-15 22:24:03 -04:00
parent cd5d9b0c4a
commit ffc36c309f
9 changed files with 129 additions and 15 deletions

View File

@ -162,7 +162,7 @@ import VDS
&& fontName == model.fontName && fontName == model.fontName
&& fontSize == model.fontSize && fontSize == model.fontSize
&& textAlignment == model.textAlignment && textAlignment == model.textAlignment
&& attributes.isEqual(to: model.attributes) && attributes.isVisuallyEquivalent(to: model.attributes)
&& html == model.html && html == model.html
&& hero == model.hero && hero == model.hero
&& makeWholeViewClickable == model.makeWholeViewClickable && makeWholeViewClickable == model.makeWholeViewClickable

View File

@ -106,9 +106,28 @@ open class TabBarModel: MoleculeModelProtocol {
try container.encode(selectedTab, forKey: .selectedTab) try container.encode(selectedTab, forKey: .selectedTab)
try container.encodeIfPresent(style, forKey: .style) try container.encodeIfPresent(style, forKey: .style)
} }
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return backgroundColor == model.backgroundColor
&& selectedColor == model.selectedColor
&& selectedTab == model.selectedTab
&& style == model.style
&& tabs == model.tabs
}
public func isVisuallyEquivalent(to model: any MoleculeModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return backgroundColor == model.backgroundColor
&& selectedColor == model.selectedColor
&& selectedTab == model.selectedTab
&& style == model.style
&& tabs.isVisuallyEquivalent(to: model.tabs)
}
} }
open class TabBarItemModel: Codable { open class TabBarItemModel: Codable, Equatable, MoleculeModelComparisonProtocol {
open var title: String? open var title: String?
open var image: String open var image: String
open var action: ActionModelProtocol open var action: ActionModelProtocol
@ -142,4 +161,18 @@ open class TabBarItemModel: Codable {
try container.encodeModel(action, forKey: .action) try container.encodeModel(action, forKey: .action)
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText) try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
} }
public static func == (lhs: TabBarItemModel, rhs: TabBarItemModel) -> Bool {
return lhs.title == rhs.title
&& lhs.image == rhs.image
&& lhs.accessibilityText == rhs.accessibilityText
&& lhs.action.isEqual(to: rhs.action)
}
public func isVisuallyEquivalent(to model: any MoleculeModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return image == model.image
&& accessibilityText == model.accessibilityText
&& title == model.title
}
} }

View File

@ -105,11 +105,39 @@ open class TabsModel: MoleculeModelProtocol {
try container.encode(borderLine, forKey: .borderLine) try container.encode(borderLine, forKey: .borderLine)
try container.encodeIfPresent(minWidth, forKey: .minWidth) try container.encodeIfPresent(minWidth, forKey: .minWidth)
} }
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return backgroundColor == model.backgroundColor
&& style == model.style
&& orientation == model.orientation
&& indicatorPosition == model.indicatorPosition
&& overflow == model.overflow
&& fillContainer == model.fillContainer
&& size == model.size
&& borderLine == model.borderLine
&& minWidth == model.minWidth
&& selectedIndex == model.selectedIndex
&& tabs.isEqual(to: model.tabs)
}
public func isVisuallyEquivalent(to model: any MoleculeModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return backgroundColor == model.backgroundColor
&& style == model.style
&& orientation == model.orientation
&& indicatorPosition == model.indicatorPosition
&& overflow == model.overflow
&& fillContainer == model.fillContainer
&& size == model.size
&& borderLine == model.borderLine
&& minWidth == model.minWidth
//&& selectedIndex == model.selectedIndex // Selected index could have been either reset locally or by server. For now ignore.c
&& tabs.isVisuallyEquivalent(to: model.tabs)
}
} }
open class TabItemModel: Codable, Equatable, MoleculeModelComparisonProtocol {
open class TabItemModel: Codable {
open var label: LabelModel open var label: LabelModel
open var action: ActionModelProtocol? open var action: ActionModelProtocol?
@ -146,4 +174,14 @@ open class TabItemModel: Codable {
try container.encodeModel(label, forKey: .label) try container.encodeModel(label, forKey: .label)
try container.encodeModelIfPresent(action, forKey: .action) try container.encodeModelIfPresent(action, forKey: .action)
} }
public static func == (lhs: TabItemModel, rhs: TabItemModel) -> Bool {
return lhs.label.isEqual(to: rhs.label)
&& lhs.action.isEqual(to: rhs.action)
}
public func isVisuallyEquivalent(to model: any MoleculeModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return label.isVisuallyEquivalent(to: model.label)
}
} }

View File

@ -23,17 +23,18 @@ public class TabsListItemModel: ListItemModel, ParentMoleculeModelProtocol {
return molecules.flatMap { $0 } return molecules.flatMap { $0 }
} }
public func replaceChildMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> Bool { public func replaceChildMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> MoleculeModelProtocol? {
guard let replacementMolecule = replacementMolecule as? ListItemModelProtocol & MoleculeModelProtocol else { return false } guard let replacementMolecule = replacementMolecule as? ListItemModelProtocol & MoleculeModelProtocol else { return nil }
for (tabIndex, _) in molecules.enumerated() { for (tabIndex, _) in molecules.enumerated() {
for (elementIndex, _) in molecules[tabIndex].enumerated() { for (elementIndex, _) in molecules[tabIndex].enumerated() {
if molecules[tabIndex][elementIndex].id == replacementMolecule.id { if molecules[tabIndex][elementIndex].id == replacementMolecule.id {
let replacedMolecule = molecules[tabIndex][elementIndex]
molecules[tabIndex][elementIndex] = replacementMolecule molecules[tabIndex][elementIndex] = replacementMolecule
return true return replacedMolecule
} }
} }
} }
return false return nil
} }
//-------------------------------------------------- //--------------------------------------------------
@ -90,6 +91,20 @@ public class TabsListItemModel: ListItemModel, ParentMoleculeModelProtocol {
try container.encode(tabs, forKey: .tabs) try container.encode(tabs, forKey: .tabs)
try container.encodeModels2D(molecules, forKey: .molecules) try container.encodeModels2D(molecules, forKey: .molecules)
} }
public override func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard super.isEqual(to: model), let model = model as? Self else { return false }
return tabs.isEqual(to: model.tabs)
&& molecules.count == model.molecules.count
&& zip(molecules, model.molecules).allSatisfy({ $0.0.isEqual(to: $0.1) })
}
public override func isVisuallyEquivalent(to model: any MoleculeModelComparisonProtocol) -> Bool {
guard super.isVisuallyEquivalent(to: model), let model = model as? Self else { return false }
return tabs.isVisuallyEquivalent(to: model.tabs)
&& molecules.count == model.molecules.count
&& zip(molecules, model.molecules).allSatisfy({ $0.0.isVisuallyEquivalent(to: $0.1) })
}
} }
extension TabsListItemModel: PageBehaviorProtocolRequirer { extension TabsListItemModel: PageBehaviorProtocolRequirer {

View File

@ -141,6 +141,17 @@ open class NavigationItemModel: NavigationItemModelProtocol, MoleculeModelProtoc
try container.encodeIfPresent(style, forKey: .style) try container.encodeIfPresent(style, forKey: .style)
try container.encodeIfPresent(titleOffset, forKey: .titleOffset) try container.encodeIfPresent(titleOffset, forKey: .titleOffset)
} }
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return backgroundColor == model.backgroundColor
&& title == model.title
&& hidden == model.hidden
&& tintColor == model.tintColor
&& line.isEqual(to: model.line)
&& hidesSystemBackButton == model.hidesSystemBackButton
&& style == model.style
}
} }
extension NavigationItemModel: ParentMoleculeModelProtocol { extension NavigationItemModel: ParentMoleculeModelProtocol {

View File

@ -101,6 +101,14 @@ open class HeadlineBodyModel: ParentMoleculeModelProtocol {
&& style == style && style == style
&& backgroundColor == backgroundColor && backgroundColor == backgroundColor
} }
public func isVisuallyEquivalent(to model: any MoleculeModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return headline.isVisuallyEquivalent(to: model.headline)
&& body.isVisuallyEquivalent(to: model.body)
&& style == style
&& backgroundColor == backgroundColor
}
} }
public extension HeadlineBodyModel { public extension HeadlineBodyModel {

View File

@ -86,4 +86,12 @@
&& axis == model.axis && axis == model.axis
&& spacing == model.spacing && spacing == model.spacing
} }
public func isVisuallyEquivalent(to model: any MoleculeModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return backgroundColor == model.backgroundColor
&& molecules.isVisuallyEquivalent(to: model.molecules)
&& axis == model.axis
&& spacing == model.spacing
}
} }

View File

@ -38,7 +38,7 @@ public extension MoleculeTreeTraversalProtocol {
func printMolecules(options: TreeTraversalOptions = .parentFirst) { func printMolecules(options: TreeTraversalOptions = .parentFirst) {
depthFirstTraverse(options: options, depth: 1) { depth, molecule, stop in depthFirstTraverse(options: options, depth: 1) { depth, molecule, stop in
print("\(String(repeating: ">>", count: depth)) \"\(molecule.moleculeName)\" [\(molecule): \(molecule.id)]") print("\(String(repeating: ">>", count: depth)) \"\(molecule.moleculeName)\" [\(molecule)]")
} }
} }

View File

@ -65,17 +65,17 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
} }
fileprivate func findAndReplace(_ moleculeModels: [any MoleculeModelProtocol], in rootMolecules: [any MoleculeModelProtocol]) -> [any MoleculeModelProtocol]? { fileprivate func findAndReplace(_ moleculeModels: [any MoleculeModelProtocol], in rootMolecules: [any MoleculeModelProtocol]) -> [any MoleculeModelProtocol]? {
debugLog("onPageNew replacing \(moleculeModels.map { $0.id })") debugLog("attempting to replace \(moleculeModels.map { $0.id }) in \(rootMolecules)")
var hasReplacement = false var hasReplacement = false
let updatedRootMolecules = rootMolecules.map { rootMolecule in let updatedRootMolecules = rootMolecules.map { rootMolecule in
// Top level check to return a new root molecule. // Top level check to return a new root molecule.
if let updatedMolecule = moleculeModels.first(where: { rootMolecule.id == $0.id }) { if let updatedMolecule = moleculeModels.first(where: { rootMolecule.id == $0.id }) {
guard !updatedMolecule.isEqual(to: rootMolecule) else { guard !updatedMolecule.isEqual(to: rootMolecule) else {
debugLog("onPageNew molecule \(updatedMolecule) is the same as \(rootMolecule). skipping...") debugLog("molecule \(updatedMolecule) is the same as \(rootMolecule). skipping...")
return rootMolecule return rootMolecule
} }
debugLog("onPageNew replacing \(rootMolecule) with \(updatedMolecule)") debugLog("replacing \(rootMolecule) with \(updatedMolecule)")
logUpdated(molecule: updatedMolecule) logUpdated(molecule: updatedMolecule)
hasReplacement = true hasReplacement = true
return updatedMolecule return updatedMolecule
@ -89,10 +89,10 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
if let replacedMolecule = try parentMolecule.replaceChildMolecule(with: newMolecule) { if let replacedMolecule = try parentMolecule.replaceChildMolecule(with: newMolecule) {
guard !replacedMolecule.isEqual(to: newMolecule) else { guard !replacedMolecule.isEqual(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("onPageNew molecule \(newMolecule) is the same as \(replacedMolecule). skipping...") debugLog("molecule \(newMolecule) is the same as \(replacedMolecule). skipping...")
return return
} }
debugLog("onPageNew replacing \(replacedMolecule) with \(newMolecule)") debugLog("replacing \(replacedMolecule) with \(newMolecule)")
logUpdated(molecule: newMolecule) logUpdated(molecule: newMolecule)
hasReplacement = true hasReplacement = true
} }
@ -106,6 +106,7 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
} }
return parentMolecule return parentMolecule
} }
debugLog("replacing \(hasReplacement ? updatedRootMolecules.count : 0) molecules")
return hasReplacement ? updatedRootMolecules : nil return hasReplacement ? updatedRootMolecules : nil
} }