Digital ACT192 story ONEAPP-8912- YIR | Year in review Prototype Updates

This commit is contained in:
Nowfal E Salam 2024-07-01 00:05:21 +05:30
commit d3bf1d320f
60 changed files with 245 additions and 199 deletions

View File

@ -293,6 +293,7 @@
AF1C336F2885A16A006B1001 /* ActionCollapseNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C336E2885A16A006B1001 /* ActionCollapseNotificationHandler.swift */; }; AF1C336F2885A16A006B1001 /* ActionCollapseNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C336E2885A16A006B1001 /* ActionCollapseNotificationHandler.swift */; };
AF1C33712885AE76006B1001 /* MVMCoreUIActionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C33702885AE76006B1001 /* MVMCoreUIActionHandler.swift */; }; AF1C33712885AE76006B1001 /* MVMCoreUIActionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C33702885AE76006B1001 /* MVMCoreUIActionHandler.swift */; };
AF1C33732885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C33722885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift */; }; AF1C33732885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C33722885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift */; };
AF1CEFE82BEA73890001F9A5 /* VDSCoreTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF1CEFE72BEA73890001F9A5 /* VDSCoreTokens.xcframework */; };
AF60A7F62892D2E300919EEB /* ActionDismissNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F52892D2E300919EEB /* ActionDismissNotificationModel.swift */; }; AF60A7F62892D2E300919EEB /* ActionDismissNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F52892D2E300919EEB /* ActionDismissNotificationModel.swift */; };
AF60A7F82892D34D00919EEB /* ActionDismissNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F72892D34D00919EEB /* ActionDismissNotificationHandler.swift */; }; AF60A7F82892D34D00919EEB /* ActionDismissNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F72892D34D00919EEB /* ActionDismissNotificationHandler.swift */; };
AF766D262A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF766D252A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift */; }; AF766D262A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF766D252A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift */; };
@ -607,7 +608,6 @@
EABFC1412763BB8D00E78B40 /* FormLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC1402763BB8D00E78B40 /* FormLabel.swift */; }; EABFC1412763BB8D00E78B40 /* FormLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC1402763BB8D00E78B40 /* FormLabel.swift */; };
EABFC152276913E800E78B40 /* FormLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC151276913E800E78B40 /* FormLabelModel.swift */; }; EABFC152276913E800E78B40 /* FormLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC151276913E800E78B40 /* FormLabelModel.swift */; };
EACCF38C2ABB346700E0F104 /* VDS-Interpreters.swift in Sources */ = {isa = PBXBuildFile; fileRef = EACCF38B2ABB346700E0F104 /* VDS-Interpreters.swift */; }; EACCF38C2ABB346700E0F104 /* VDS-Interpreters.swift in Sources */ = {isa = PBXBuildFile; fileRef = EACCF38B2ABB346700E0F104 /* VDS-Interpreters.swift */; };
EAD715AA2BBC8FAF00DEDA6A /* VDSTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAD715A92BBC8FAF00DEDA6A /* VDSTokens.xcframework */; };
FD99130028E21E4900542CC3 /* RuleNotEqualsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9912FF28E21E4900542CC3 /* RuleNotEqualsModel.swift */; }; FD99130028E21E4900542CC3 /* RuleNotEqualsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9912FF28E21E4900542CC3 /* RuleNotEqualsModel.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@ -911,6 +911,7 @@
AF1C336E2885A16A006B1001 /* ActionCollapseNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCollapseNotificationHandler.swift; sourceTree = "<group>"; }; AF1C336E2885A16A006B1001 /* ActionCollapseNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCollapseNotificationHandler.swift; sourceTree = "<group>"; };
AF1C33702885AE76006B1001 /* MVMCoreUIActionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIActionHandler.swift; sourceTree = "<group>"; }; AF1C33702885AE76006B1001 /* MVMCoreUIActionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIActionHandler.swift; sourceTree = "<group>"; };
AF1C33722885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIActionOpenPageHandler.swift; sourceTree = "<group>"; }; AF1C33722885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIActionOpenPageHandler.swift; sourceTree = "<group>"; };
AF1CEFE72BEA73890001F9A5 /* VDSCoreTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSCoreTokens.xcframework; path = ../SharedFrameworks/VDSCoreTokens.xcframework; sourceTree = "<group>"; };
AF60A7F52892D2E300919EEB /* ActionDismissNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionDismissNotificationModel.swift; sourceTree = "<group>"; }; AF60A7F52892D2E300919EEB /* ActionDismissNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionDismissNotificationModel.swift; sourceTree = "<group>"; };
AF60A7F72892D34D00919EEB /* ActionDismissNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionDismissNotificationHandler.swift; sourceTree = "<group>"; }; AF60A7F72892D34D00919EEB /* ActionDismissNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionDismissNotificationHandler.swift; sourceTree = "<group>"; };
AF766D252A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAccessibilityTraits+Codable.swift"; sourceTree = "<group>"; }; AF766D252A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAccessibilityTraits+Codable.swift"; sourceTree = "<group>"; };
@ -1244,9 +1245,9 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
AF1CEFE82BEA73890001F9A5 /* VDSCoreTokens.xcframework in Frameworks */,
D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */, D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */,
EA985C602970A3F000F2FF2E /* VDS.framework in Frameworks */, EA985C602970A3F000F2FF2E /* VDS.framework in Frameworks */,
EAD715AA2BBC8FAF00DEDA6A /* VDSTokens.xcframework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -2138,6 +2139,7 @@
D29DF0E421E4F3C7003B2FB9 /* Frameworks */ = { D29DF0E421E4F3C7003B2FB9 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
AF1CEFE72BEA73890001F9A5 /* VDSCoreTokens.xcframework */,
EAD715A92BBC8FAF00DEDA6A /* VDSTokens.xcframework */, EAD715A92BBC8FAF00DEDA6A /* VDSTokens.xcframework */,
EA985C5F2970A3F000F2FF2E /* VDS.framework */, EA985C5F2970A3F000F2FF2E /* VDS.framework */,
D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */, D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */,

View File

@ -7,7 +7,7 @@
// //
import UIKit import UIKit
import VDSTokens import VDSCoreTokens
import VDS import VDS
import MVMCore import MVMCore

View File

@ -7,7 +7,7 @@
// //
import UIKit import UIKit
import VDSTokens import VDSCoreTokens
import VDS import VDS
open class Link: VDS.TextLink, VDSMoleculeViewProtocol { open class Link: VDS.TextLink, VDSMoleculeViewProtocol {

View File

@ -7,7 +7,7 @@
// //
import UIKit import UIKit
import VDSTokens import VDSCoreTokens
import VDS import VDS
import MVMCore import MVMCore
import Combine import Combine

View File

@ -7,7 +7,7 @@
// //
import UIKit import UIKit
import VDSTokens import VDSCoreTokens
@objcMembers open class RadioButton: Control, MFButtonProtocol { @objcMembers open class RadioButton: Control, MFButtonProtocol {
//-------------------------------------------------- //--------------------------------------------------

View File

@ -41,7 +41,9 @@ open class ButtonIcon: VDS.ButtonIcon, VDSMoleculeViewProtocol {
iconName = viewModel.iconName iconName = viewModel.iconName
selectedIconName = viewModel.selectedIconName selectedIconName = viewModel.selectedIconName
size = viewModel.size size = viewModel.size
customSize = viewModel.customSize customContainerSize = viewModel.customContainerSize
customIconSize = viewModel.customIconSize
customBadgeIndicatorOffset = viewModel.customBadgeIndicatorOffSet
floating = viewModel.floating floating = viewModel.floating
fitToIcon = viewModel.fitToIcon fitToIcon = viewModel.fitToIcon
hideBorder = viewModel.hideBorder hideBorder = viewModel.hideBorder

View File

@ -31,7 +31,9 @@ open class ButtonIconModel: ButtonModelProtocol, MoleculeModelProtocol {
public var iconName: Icon.Name = .info public var iconName: Icon.Name = .info
public var selectedIconName: Icon.Name? public var selectedIconName: Icon.Name?
public var size = ButtonIcon.Size.large public var size = ButtonIcon.Size.large
public var customSize : Int? public var customContainerSize : Int?
public var customIconSize : Int?
public var customBadgeIndicatorOffSet : CGPoint?
public var floating: Bool = false public var floating: Bool = false
public var fitToIcon: Bool = false public var fitToIcon: Bool = false
public var hideBorder: Bool = true public var hideBorder: Bool = true
@ -85,7 +87,9 @@ open class ButtonIconModel: ButtonModelProtocol, MoleculeModelProtocol {
case iconName case iconName
case selectedIconName case selectedIconName
case size case size
case customSize case customContainerSize
case customIconSize
case customBadgeIndicatorOffSet
case floating case floating
case fitToIcon case fitToIcon
case hideBorder case hideBorder
@ -110,7 +114,9 @@ open class ButtonIconModel: ButtonModelProtocol, MoleculeModelProtocol {
iconName = try container.decode(Icon.Name.self, forKey: .iconName) iconName = try container.decode(Icon.Name.self, forKey: .iconName)
selectedIconName = try container.decodeIfPresent(Icon.Name.self, forKey: .selectedIconName) selectedIconName = try container.decodeIfPresent(Icon.Name.self, forKey: .selectedIconName)
size = try container.decodeIfPresent(ButtonIcon.Size.self, forKey: .size) ?? .large size = try container.decodeIfPresent(ButtonIcon.Size.self, forKey: .size) ?? .large
customSize = try container.decodeIfPresent(Int.self, forKey: .customSize) customContainerSize = try container.decodeIfPresent(Int.self, forKey: .customContainerSize)
customIconSize = try container.decodeIfPresent(Int.self, forKey: .customIconSize)
customBadgeIndicatorOffSet = try container.decodeIfPresent(CGPoint.self, forKey: .customBadgeIndicatorOffSet)
floating = try container.decodeIfPresent(Bool.self, forKey: .floating) ?? false floating = try container.decodeIfPresent(Bool.self, forKey: .floating) ?? false
fitToIcon = try container.decodeIfPresent(Bool.self, forKey: .fitToIcon) ?? false fitToIcon = try container.decodeIfPresent(Bool.self, forKey: .fitToIcon) ?? false
hideBorder = try container.decodeIfPresent(Bool.self, forKey: .hideBorder) ?? false hideBorder = try container.decodeIfPresent(Bool.self, forKey: .hideBorder) ?? false
@ -131,7 +137,9 @@ open class ButtonIconModel: ButtonModelProtocol, MoleculeModelProtocol {
try container.encode(iconName, forKey: .iconName) try container.encode(iconName, forKey: .iconName)
try container.encodeIfPresent(selectedIconName, forKey: .selectedIconName) try container.encodeIfPresent(selectedIconName, forKey: .selectedIconName)
try container.encodeIfPresent(size, forKey: .size) try container.encodeIfPresent(size, forKey: .size)
try container.encodeIfPresent(customSize, forKey: .customSize) try container.encodeIfPresent(customContainerSize, forKey: .customContainerSize)
try container.encodeIfPresent(customIconSize, forKey: .customIconSize)
try container.encodeIfPresent(customBadgeIndicatorOffSet, forKey: .customBadgeIndicatorOffSet)
try container.encodeIfPresent(floating, forKey: .floating) try container.encodeIfPresent(floating, forKey: .floating)
try container.encodeIfPresent(fitToIcon, forKey: .fitToIcon) try container.encodeIfPresent(fitToIcon, forKey: .fitToIcon)
try container.encodeIfPresent(hideBorder, forKey: .hideBorder) try container.encodeIfPresent(hideBorder, forKey: .hideBorder)

View File

@ -5,7 +5,7 @@
// Created by Kevin Christiano on 1/30/20. // Created by Kevin Christiano on 1/30/20.
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import VDSTokens import VDSCoreTokens
open class CarouselIndicator: Control, CarouselPageControlProtocol { open class CarouselIndicator: Control, CarouselPageControlProtocol {
//-------------------------------------------------- //--------------------------------------------------

View File

@ -7,7 +7,7 @@
// //
import Foundation import Foundation
import VDSTokens import VDSCoreTokens
open class CarouselIndicatorModel: CarouselPagingModelProtocol, MoleculeModelProtocol, EnableableModelProtocol { open class CarouselIndicatorModel: CarouselPagingModelProtocol, MoleculeModelProtocol, EnableableModelProtocol {
//-------------------------------------------------- //--------------------------------------------------

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
import VDS import VDS
import VDSTokens import VDSCoreTokens
open class IconModel: MoleculeModelProtocol { open class IconModel: MoleculeModelProtocol {

View File

@ -375,8 +375,8 @@ extension Label {
public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect { public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect {
guard let abstractContainer = label.abstractTextContainer() else { return CGRect() } guard let abstractContainer = label.abstractTextContainer() else { return CGRect() }
let textContainer = abstractContainer.0 let textContainer = abstractContainer.textContainer
let layoutManager = abstractContainer.1 let layoutManager = abstractContainer.layoutManager
var glyphRange = NSRange() var glyphRange = NSRange()
@ -385,36 +385,6 @@ extension Label {
return layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer) return layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer)
} }
/**
Provides a text container and layout manager of how the text would appear on screen.
They are used in tandem to derive low-level TextKit results of the label.
*/
public func abstractTextContainer() -> (NSTextContainer, NSLayoutManager, NSTextStorage)? {
// Must configure the attributed string to translate what would appear on screen to accurately analyze.
guard let attributedText = attributedText else { return nil }
let paragraph = NSMutableParagraphStyle()
paragraph.alignment = textAlignment
let stagedAttributedString = NSMutableAttributedString(attributedString: attributedText)
stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count))
let textStorage = NSTextStorage(attributedString: stagedAttributedString)
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: .zero)
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = lineBreakMode
textContainer.maximumNumberOfLines = numberOfLines
textContainer.size = bounds.size
return (textContainer, layoutManager, textStorage)
}
} }
// MARK: - Atomization // MARK: - Atomization

View File

@ -7,7 +7,7 @@
// //
import UIKit import UIKit
import VDSTokens import VDSCoreTokens
import VDS import VDS
public class LineModel: MoleculeModelProtocol, Invertable { public class LineModel: MoleculeModelProtocol, Invertable {

View File

@ -34,6 +34,7 @@ open class TileContainerModel: TileContainerBaseModel<TileContainer.Padding, Til
case moleculeName case moleculeName
case molecule case molecule
} }
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString id = try container.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
@ -84,6 +85,7 @@ open class TileContainerBaseModel<PaddingType: DefaultValuing & Codable, TileCon
case aspectRatio case aspectRatio
case backgroundEffect case backgroundEffect
} }
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
inverted = try container.decodeIfPresent(Bool.self, forKey: .inverted) ?? false inverted = try container.decodeIfPresent(Bool.self, forKey: .inverted) ?? false
@ -98,7 +100,6 @@ open class TileContainerBaseModel<PaddingType: DefaultValuing & Codable, TileCon
color = try container.decodeIfPresent(TileContainerType.BackgroundColor.self, forKey: .color) ?? .black color = try container.decodeIfPresent(TileContainerType.BackgroundColor.self, forKey: .color) ?? .black
aspectRatio = try container.decodeIfPresent(TileContainerType.AspectRatio.self, forKey: .aspectRatio) ?? .ratio1x1 aspectRatio = try container.decodeIfPresent(TileContainerType.AspectRatio.self, forKey: .aspectRatio) ?? .ratio1x1
backgroundEffect = try container.decodeIfPresent(TileContainerType.BackgroundEffect.self, forKey: .backgroundEffect) ?? .none backgroundEffect = try container.decodeIfPresent(TileContainerType.BackgroundEffect.self, forKey: .backgroundEffect) ?? .none
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
import VDS import VDS
import VDSTokens import VDSCoreTokens
import MVMCore import MVMCore
open class TooltipModel: MoleculeModelProtocol { open class TooltipModel: MoleculeModelProtocol {

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
import VDS import VDS
import VDSTokens import VDSCoreTokens
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Codable Extensions // MARK: - Codable Extensions

View File

@ -6,7 +6,7 @@
// Copyright © 2022 Verizon Wireless. All rights reserved. // Copyright © 2022 Verizon Wireless. All rights reserved.
// //
import VDSTokens import VDSCoreTokens
import VDS import VDS
public class TitleLockupModel: ParentMoleculeModelProtocol { public class TitleLockupModel: ParentMoleculeModelProtocol {

View File

@ -5,7 +5,7 @@
// Created by Scott Pfeil on 5/28/20. // Created by Scott Pfeil on 5/28/20.
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import VDSTokens import VDSCoreTokens
@objcMembers open class TabBar: UITabBar, MoleculeViewProtocol, TabBarProtocol, UITabBarDelegate { @objcMembers open class TabBar: UITabBar, MoleculeViewProtocol, TabBarProtocol, UITabBarDelegate {

View File

@ -7,7 +7,7 @@
// //
import Foundation import Foundation
import VDSTokens import VDSCoreTokens
open class TabBarModel: MoleculeModelProtocol { open class TabBarModel: MoleculeModelProtocol {
public static var identifier: String = "tabBar" public static var identifier: String = "tabBar"
@ -34,7 +34,7 @@ open class TabBarModel: MoleculeModelProtocol {
if let selectedColor = _selectedColor { return selectedColor } if let selectedColor = _selectedColor { return selectedColor }
if let style = style, if let style = style,
style == .dark { return Color(uiColor: VDSColor.elementsPrimaryOndark) } style == .dark { return Color(uiColor: VDSColor.elementsPrimaryOndark) }
return Color(uiColor: VDSColor.elementsPrimaryOnlight) return Color(uiColor: UIColor.mvmRed)
} }
set { set {
_selectedColor = newValue _selectedColor = newValue
@ -54,7 +54,7 @@ open class TabBarModel: MoleculeModelProtocol {
} }
} }
open var style: NavigationItemStyle? = .dark open var style: NavigationItemStyle? = .light
// Must be capped to 0...(tabs.count - 1) // Must be capped to 0...(tabs.count - 1)
open var selectedTab: Int = 0 open var selectedTab: Int = 0

View File

@ -7,7 +7,7 @@
// //
import UIKit import UIKit
import VDSTokens import VDSCoreTokens
import VDS import VDS
@objc public protocol TabsDelegate { @objc public protocol TabsDelegate {

View File

@ -7,7 +7,7 @@
// //
import UIKit import UIKit
import VDSTokens import VDSCoreTokens
import VDS import VDS
open class TabsModel: MoleculeModelProtocol { open class TabsModel: MoleculeModelProtocol {

View File

@ -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
} }
} }

View File

@ -6,7 +6,7 @@
// Copyright © 2020 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
import VDSTokens import VDSCoreTokens
public enum NavigationItemStyle: String, Codable { public enum NavigationItemStyle: String, Codable {
case light case light

View File

@ -10,7 +10,7 @@ import Foundation
import Combine import Combine
import Dispatch import Dispatch
import MVMCore import MVMCore
import VDSTokens import VDSCoreTokens
@objcMembers open class CollapsableNotification: View { @objcMembers open class CollapsableNotification: View {
//-------------------------------------------------- //--------------------------------------------------

View File

@ -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,9 +174,12 @@ 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)
FormValidator.setupValidation(for: carouselModel, delegate: delegateObject?.formHolderDelegate) FormValidator.setupValidation(for: carouselModel, delegate: delegateObject?.formHolderDelegate)
updateModelIndex() // Ensure the new model indexing matches the old. updateModelIndex() // Ensure the new model indexing matches the old.
pagingView?.currentIndex = pageIndex // Trigger a paging view render. pagingView?.currentIndex = pageIndex // Trigger a paging view render.
@ -228,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
@ -304,6 +307,7 @@ open class Carousel: View {
pagingView?.removeFromSuperview() pagingView?.removeFromSuperview()
bottomPin?.isActive = false bottomPin?.isActive = false
pagingView = nil
guard var pagingView = view else { guard var pagingView = view else {
bottomPin = bottomAnchor.constraint(equalTo: collectionView.bottomAnchor) bottomPin = bottomAnchor.constraint(equalTo: collectionView.bottomAnchor)

View File

@ -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
} }

View File

@ -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 { }
}
} }

View File

@ -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) {

View File

@ -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
}
}

View File

@ -83,6 +83,9 @@ import Foundation
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
if let defaultPageType = Self.defaultPageType() { if let defaultPageType = Self.defaultPageType() {
pageType = try typeContainer.decodeIfPresent(String.self, forKey: .pageType) ?? defaultPageType pageType = try typeContainer.decodeIfPresent(String.self, forKey: .pageType) ?? defaultPageType
if pageType.isEmpty {
pageType = defaultPageType
}
} else { } else {
pageType = try typeContainer.decode(String.self, forKey: .pageType) pageType = try typeContainer.decode(String.self, forKey: .pageType)
} }

View File

@ -79,10 +79,10 @@
} }
open override func handleNewData(_ pageModel: PageModelProtocol? = nil) { open override func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) {
setup() setup()
registerCells() registerCells()
super.handleNewData(pageModel) super.handleNewData(pageModel, shouldTriggerRender: shouldTriggerRender)
} }
open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) { open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) {

View File

@ -25,8 +25,8 @@ open class ModalMoleculeListTemplate: MoleculeListTemplate {
try decoder.decode(ModalListPageTemplateModel.self, from: data) try decoder.decode(ModalListPageTemplateModel.self, from: data)
} }
override open func handleNewData(_ pageModel: PageModelProtocol? = nil) { override open func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) {
super.handleNewData(pageModel) super.handleNewData(pageModel, shouldTriggerRender: shouldTriggerRender)
closeButton = MVMCoreUICommonViewsUtility.addCloseButton(to: view, tintColor: self.pageModel?.navigationBar?.tintColor.uiColor, action: { [weak self] _ in closeButton = MVMCoreUICommonViewsUtility.addCloseButton(to: view, tintColor: self.pageModel?.navigationBar?.tintColor.uiColor, action: { [weak self] _ in
guard let self = self else { return } guard let self = self else { return }

View File

@ -23,8 +23,8 @@ open class ModalMoleculeStackTemplate: MoleculeStackTemplate {
// MARK: - Lifecycle // MARK: - Lifecycle
//-------------------------------------------------- //--------------------------------------------------
override open func handleNewData(_ pageModel: PageModelProtocol? = nil) { override open func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) {
super.handleNewData(pageModel) super.handleNewData(pageModel, shouldTriggerRender: shouldTriggerRender)
_ = MVMCoreUICommonViewsUtility.addCloseButton(to: view, tintColor: self.pageModel?.navigationBar?.tintColor.uiColor, action: { [weak self] _ in _ = MVMCoreUICommonViewsUtility.addCloseButton(to: view, tintColor: self.pageModel?.navigationBar?.tintColor.uiColor, action: { [weak self] _ in
guard let self = self else { return } guard let self = self else { return }
let closeAction = (self.templateModel as? ModalStackPageTemplateModel)?.closeAction ?? let closeAction = (self.templateModel as? ModalStackPageTemplateModel)?.closeAction ??

View File

@ -85,8 +85,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
return view return view
} }
open override func handleNewData(_ pageModel: PageModelProtocol? = nil) { open override func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) {
super.handleNewData(pageModel) super.handleNewData(pageModel, shouldTriggerRender: shouldTriggerRender)
if pageModel != nil { if pageModel != nil {
setup() setup()

View File

@ -20,10 +20,10 @@ open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol {
// MARK: - Lifecycle // MARK: - Lifecycle
//-------------------------------------------------- //--------------------------------------------------
open override func handleNewData(_ pageModel: PageModelProtocol? = nil) { open override func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) {
topViewOutsideOfScroll = templateModel?.anchorHeader ?? false topViewOutsideOfScroll = templateModel?.anchorHeader ?? false
bottomViewOutsideOfScroll = templateModel?.anchorFooter ?? false bottomViewOutsideOfScroll = templateModel?.anchorFooter ?? false
super.handleNewData(pageModel) super.handleNewData(pageModel, shouldTriggerRender: shouldTriggerRender)
} }
// For subclassing the model. // For subclassing the model.

View File

@ -52,17 +52,20 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController, Rotor
bottomView.updateView(width) bottomView.updateView(width)
showFooter(width) showFooter(width)
} }
tableView.reloadData() tableView.visibleCells.forEach { cell in
(cell as? MVMCoreViewProtocol)?.updateView(width)
}
} }
open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) { open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) {
super.updateUI(for: molecules) super.updateUI(for: molecules)
guard molecules == nil else { return } guard molecules == nil else { return }
createViewForTableHeader() createViewForTableHeader()
createViewForTableFooter() createViewForTableFooter()
tableView?.reloadData() // Reloading the table is handled in updateViews, however, update views is on a separate rendering task than the current thread. The table render needs to be bound and settled to the new model before others put in additional update requests.
tableView.reloadData()
} }
override open func viewDidLoad() { override open func viewDidLoad() {

View File

@ -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))
@ -225,16 +230,16 @@ import MVMCore
return navigationModel return navigationModel
} }
/// Processes any new data. Called after the page is loaded the first time and on response updates for this page. Triggers a render refresh. /// Processes any new data. Called after the page is loaded the first time and on response updates for this page. Triggers a render refresh unless specified otherwise.
@MainActor @MainActor
open func handleNewData(_ pageModel: PageModelProtocol? = nil) { 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,15 +282,20 @@ 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)
if allUpdatedMolecules.isEmpty || isFirstRender { guard shouldTriggerRender else { return }
debugLog("Performing full page render...")
updateUI() // Dispatch to decouple execution. First massage data through template classes, then render.
} else { Task { @MainActor in
debugLog("Performing partial render of \(allUpdatedMolecules) molecules...")
updateUI(for: allUpdatedMolecules) if allUpdatedMolecules.isEmpty || isFirstRender {
debugLog("Performing full page render...")
updateUI()
} else {
debugLog("Performing partial render of \(allUpdatedMolecules) molecules...")
updateUI(for: allUpdatedMolecules)
}
} }
} }
@ -399,7 +409,8 @@ import MVMCore
initialLoad() initialLoad()
} }
handleNewData(pageModel) // Set outside shouldFinishProcessingLoad. handleNewData(pageModel, shouldTriggerRender: false) // Set outside shouldFinishProcessingLoad.
updateUI() // Force the rendering on the same main UI thread.
} }
open override func viewDidLayoutSubviews() { open override func viewDidLayoutSubviews() {

View File

@ -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)
} }

View File

@ -20,7 +20,7 @@ public class ReplaceableMoleculeBehaviorModel: PageBehaviorModelProtocol {
} }
} }
public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, CoreLogging { public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, PageVisibilityBehavior, CoreLogging {
public var loggingPrefix: String { public var loggingPrefix: String {
"\(self) \(ObjectIdentifier(self).hashValue) \(moleculeIds.prefix(3)) \(moleculeIds.count > 3 ? "+ \(moleculeIds.count - 3) more" : ""):\n" "\(self) \(ObjectIdentifier(self).hashValue) \(moleculeIds.prefix(3)) \(moleculeIds.count > 3 ? "+ \(moleculeIds.count - 3) more" : ""):\n"
@ -30,6 +30,8 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
String(describing: Self.self) String(describing: Self.self)
} }
var isPageShowing = false
var previouslyReplacedIds = Set<String>()
var moleculeIds: [String] var moleculeIds: [String]
public var modulesToListenFor: [String] public var modulesToListenFor: [String]
private var observingForResponses: NSObjectProtocol? private var observingForResponses: NSObjectProtocol?
@ -41,7 +43,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]? {
@ -75,7 +77,7 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
return rootMolecule return rootMolecule
} }
debugLog("top replacing \(rootMolecule) with \(updatedMolecule)") debugLog("top replacing \(rootMolecule) with \(updatedMolecule)")
logUpdated(molecule: updatedMolecule) logUpdated(moleculeId: updatedMolecule.id)
changeList.append(updatedMolecule) changeList.append(updatedMolecule)
return updatedMolecule return updatedMolecule
} }
@ -85,14 +87,14 @@ 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...")
return return
} }
debugLog("deep replacing \(replacedMolecule) with \(newMolecule)") debugLog("deep replacing \(replacedMolecule) with \(newMolecule)")
logUpdated(molecule: newMolecule) logUpdated(moleculeId: newMolecule.id)
changeList.append(newMolecule) changeList.append(newMolecule)
} }
} catch { } catch {
@ -111,15 +113,29 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
return hasReplacement ? updatedRootMolecules : nil return hasReplacement ? updatedRootMolecules : nil
} }
private func logUpdated(molecule: MoleculeModelProtocol) { private func logUpdated(moleculeId: String) {
guard let module: [AnyHashable: Any] = delegateObject?.moleculeDelegate?.getModuleWithName(molecule.id), guard let module: [AnyHashable: Any] = delegateObject?.moleculeDelegate?.getModuleWithName(moleculeId),
let viewController = delegateObject?.moleculeDelegate as? MVMCoreViewControllerProtocol else { let viewController = delegateObject?.moleculeDelegate as? MVMCoreViewControllerProtocol else {
debugLog("Missing the originating module \(molecule.id) creating this molecule!") debugLog("Missing the originating module \(moleculeId) creating this molecule!")
return return
} }
previouslyReplacedIds.insert(moleculeId)
guard isPageShowing else { return } // Page has not been made visible yet. (Pulled and replaced from cache on load or background update.) Hold reporting until onPageShown.
MVMCoreUILoggingHandler.shared()?.defaultLogPageUpdate(forController: viewController, from: module) MVMCoreUILoggingHandler.shared()?.defaultLogPageUpdate(forController: viewController, from: module)
} }
public func onPageShown(_ delegateObject: MVMCoreUIDelegateObject?) {
isPageShowing = true
debugLog("Page shown. Send molecule analytics for: \(previouslyReplacedIds)")
previouslyReplacedIds.forEach { id in
logUpdated(moleculeId: id)
}
}
public func onPageHidden(_ delegateObject: MVMCoreUIDelegateObject?) {
isPageShowing = false
}
deinit { deinit {
debugLog("deinit") debugLog("deinit")
} }

View File

@ -7,6 +7,7 @@
// //
import UIKit import UIKit
import VDSCoreTokens
public typealias ColorHexTuple = (uiColor: UIColor, hex: String) public typealias ColorHexTuple = (uiColor: UIColor, hex: String)
@ -19,7 +20,8 @@ extension UIColor {
/// Dictionary to access brand approved colors by name. /// Dictionary to access brand approved colors by name.
public static let names: [String: ColorHexTuple] = ["black": (.mvmBlack, "#000000"), public static let names: [String: ColorHexTuple] = ["black": (.mvmBlack, "#000000"),
"white": (.mvmWhite, "#FFFFFF"), "white": (.mvmWhite, "#FFFFFF"),
"red": (.mvmRed, "#D52B1E"), "red": (.mvmRed, "#EE0000"),
"monarchRed": (VDSCoreTokens.VDSColor.paletteMonarchred, "#f50a23"),
"pink": (.mvmPink, "#D90368"), "pink": (.mvmPink, "#D90368"),
"pink33": (.mvmPink33, "#F2ABCD"), "pink33": (.mvmPink33, "#F2ABCD"),
"pink66": (.mvmPink66, "#E6589B"), "pink66": (.mvmPink66, "#E6589B"),
@ -48,13 +50,20 @@ extension UIColor {
"blueShade2": (.mvmBlueShade2, "#0B4467"), "blueShade2": (.mvmBlueShade2, "#0B4467"),
"blueInverted": (.mvmBlueInverted, "#0088CE"), "blueInverted": (.mvmBlueInverted, "#0088CE"),
"yellow": (.mvmYellow, "#FFBC3D"), "yellow": (.mvmYellow, "#FFBC3D"),
"neonYellow": (VDSCoreTokens.VDSColor.paletteNeonyellow, "#f5ff1e"),
"coolGray1": (.mvmCoolGray1, "#F6F6F6"), "coolGray1": (.mvmCoolGray1, "#F6F6F6"),
"coolGray3": (.mvmCoolGray3, "#D8DADA"), "coolGray3": (.mvmCoolGray3, "#D8DADA"),
"coolGray6": (.mvmCoolGray6, "#747676"), "coolGray6": (.mvmCoolGray6, "#747676"),
"coolGray10": (.mvmCoolGray10, "#333333"), "coolGray10": (.mvmCoolGray10, "#333333"),
"upGold1": (.vzupGold1, "#F9D542"), "upGold1": (.vzupGold1, "#F9D542"),
"upGold2": (.vzupGold2, "#F4CA53"), "upGold2": (.vzupGold2, "#F4CA53"),
"upGold3": (.vzupGold3, "#CC9B2D")] "upGold3": (.vzupGold3, "#CC9B2D"),
"stone": (.stone, "#F3EDE0"),
"coral": (.coral, "#FF3C2D"),
"gray44": (.gray44, "#6F7171"),
"gray85": (.gray85, "#D8DADA"),
"gray95": (.gray95, "#F6F6F6")
]
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Helper // MARK: - Helper
@ -82,9 +91,10 @@ extension UIColor {
// MARK: - Red // MARK: - Red
//-------------------------------------------------- //--------------------------------------------------
/// HEX: #D52B1E /// HEX: #EE0000
@objc
public static let mvmRed = UIColor.assetColor(named: "red") public static let mvmRed = UIColor.assetColor(named: "red")
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Pink // MARK: - Pink
//-------------------------------------------------- //--------------------------------------------------
@ -190,7 +200,7 @@ extension UIColor {
// MARK: - Yellow // MARK: - Yellow
//-------------------------------------------------- //--------------------------------------------------
/// HEX: #FFBC3D /// HEX: ##F5FF1E
public static let mvmYellow = UIColor.assetColor(named: "yellow") public static let mvmYellow = UIColor.assetColor(named: "yellow")
//-------------------------------------------------- //--------------------------------------------------
@ -222,6 +232,25 @@ extension UIColor {
/// HEX: #CC9B2D /// HEX: #CC9B2D
public static let vzupGold3 = UIColor.assetColor(named: "upGold3") public static let vzupGold3 = UIColor.assetColor(named: "upGold3")
//--------------------------------------------------
// MARK: - Monarch
//--------------------------------------------------
/// HEX: #F3EDE0
@objc public static let stone = VDSCoreTokens.VDSColor.paletteStone
// HEX:#FF3C2D
@objc public static let coral = VDSCoreTokens.VDSColor.paletteCoral
// HEX:#6F7171
@objc public static let gray44 = VDSCoreTokens.VDSColor.paletteGray44
// HEX:#D8DADA
@objc public static let gray85 = VDSCoreTokens.VDSColor.paletteGray85
// HEX:#F6F6F6
@objc public static let gray95 = VDSCoreTokens.VDSColor.paletteGray95
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Functions // MARK: - Functions
//-------------------------------------------------- //--------------------------------------------------

View File

@ -77,7 +77,7 @@
#pragma mark - legacy #pragma mark - legacy
+ (nonnull UIColor *)mfRedColor { + (nonnull UIColor *)mfRedColor {
return [UIColor colorWithRed:.804 green:.016 blue:.043 alpha:1.0]; return [UIColor mvmRed];
} }
+ (nonnull UIColor *)mfDarkerRedColor { + (nonnull UIColor *)mfDarkerRedColor {
@ -307,8 +307,6 @@
static dispatch_once_t once; static dispatch_once_t once;
dispatch_once(&once, ^{ dispatch_once(&once, ^{
stringColorMapping = @{@"PrimaryRed":[UIColor mfRedColor], stringColorMapping = @{@"PrimaryRed":[UIColor mfRedColor],
@"black":[UIColor blackColor],
@"red":[UIColor mfRedColor],
@"greyish":[UIColor mfLightGrayColor], @"greyish":[UIColor mfLightGrayColor],
@"robinsEggBlue" : [UIColor mfRobinsEggBlue], @"robinsEggBlue" : [UIColor mfRobinsEggBlue],
@"lightSalmon" : [UIColor mfLightSalmon], @"lightSalmon" : [UIColor mfLightSalmon],

View File

@ -1,20 +1,20 @@
{ {
"info" : {
"version" : 1,
"author" : "xcode"
},
"colors" : [ "colors" : [
{ {
"idiom" : "universal",
"color" : { "color" : {
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"red" : "0xD5",
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "0x1E", "blue" : "0x00",
"green" : "0x2B" "green" : "0x00",
"red" : "0xEE"
} }
} },
"idiom" : "universal"
} }
] ],
} "info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,20 +1,20 @@
{ {
"info" : {
"version" : 1,
"author" : "xcode"
},
"colors" : [ "colors" : [
{ {
"idiom" : "universal",
"color" : { "color" : {
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"red" : "0xFF",
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "0x3D", "blue" : "0x3D",
"green" : "0xBC" "green" : "0xBC",
"red" : "0xFF"
} }
} },
"idiom" : "universal"
} }
] ],
} "info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
import MVMCore import MVMCore
import VDSTokens import VDSCoreTokens
open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol, TabsDelegate, MVMCorePresentationDelegateProtocol, SubNavSwipeNavigationProtocol { open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol, TabsDelegate, MVMCorePresentationDelegateProtocol, SubNavSwipeNavigationProtocol {
/// The number of tabs count or less that will turn on the fillContainer /// The number of tabs count or less that will turn on the fillContainer

View File

@ -1,23 +1,15 @@
{ {
"images" : [ "images" : [
{ {
"idiom" : "universal", "filename" : "Vector.svg",
"filename" : "exportBlack.png", "idiom" : "universal"
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "exportBlack@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "exportBlack@3x.png",
"scale" : "3x"
} }
], ],
"info" : { "info" : {
"version" : 1, "author" : "xcode",
"author" : "xcode" "version" : 1
},
"properties" : {
"preserves-vector-representation" : true
} }
} }

View File

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 -3.05176e-05V7.11108H18.7555V2.1333L11.9555 8.9333L11.0666 8.04441L17.8666 1.25553H12.8889V-3.05176e-05H20ZM15 18.7555H1.25553V4.99997H12.2222L13.3333 3.75552H-2.28882e-05V20H16.2555V6.66664L15 7.77775V18.7555Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 869 B

View File

@ -1,23 +1,15 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "nav_back.png", "filename" : "Vector.svg",
"idiom" : "universal", "idiom" : "universal"
"scale" : "1x"
},
{
"filename" : "nav_back@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "nav_back@3x.png",
"idiom" : "universal",
"scale" : "3x"
} }
], ],
"info" : { "info" : {
"author" : "xcode", "author" : "xcode",
"version" : 1 "version" : 1
},
"properties" : {
"preserves-vector-representation" : true
} }
} }

View File

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 10.6361H2.46073L10.9975 19.1001L10.0897 20L0 10L10.0897 0L10.9974 0.899867L2.46077 9.36388H20V10.6361Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 235 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 B

View File

@ -1,23 +1,15 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "Close.png", "filename" : "Vector.svg",
"idiom" : "universal", "idiom" : "universal"
"scale" : "1x"
},
{
"filename" : "Close-1.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Close-2.png",
"idiom" : "universal",
"scale" : "3x"
} }
], ],
"info" : { "info" : {
"author" : "xcode", "author" : "xcode",
"version" : 1 "version" : 1
},
"properties" : {
"preserves-vector-representation" : true
} }
} }

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.87772 9.00001L17.7777 16.8889L16.8888 17.7778L8.99995 9.87778L1.11106 17.7778L0.222168 16.8889L8.11106 9.00001L0.222168 1.11112L1.11106 0.222229L8.99995 8.11112L16.8888 0.222229L17.7777 1.11112L9.87772 9.00001Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 342 B

View File

@ -22,6 +22,9 @@ NS_ASSUME_NONNULL_BEGIN
// The bundle for this framework // The bundle for this framework
+ (nullable NSBundle *)bundleForMVMCoreUI; + (nullable NSBundle *)bundleForMVMCoreUI;
/// The bundle for the VDS frameowrk. Handy for accessing VDS resources such as fonts.
+ (nullable NSBundle *)bundleForFonts;
// Returns the hardcoded string from the string file. // Returns the hardcoded string from the string file.
+ (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key; + (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key;

View File

@ -22,6 +22,10 @@
return [NSBundle bundleWithIdentifier:@"com.vzw.MVMCoreUI"]; return [NSBundle bundleWithIdentifier:@"com.vzw.MVMCoreUI"];
} }
+ (nullable NSBundle *)bundleForFonts {
return [NSBundle bundleWithIdentifier:@"com.vzw.vds"];
}
+ (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key { + (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key {
// Redirect key with relevant module. // Redirect key with relevant module.
return [MVMCoreGetterUtility hardcodedStringWithKey:key bundle:[MVMCoreUIUtility bundleForMVMCoreUI]]; return [MVMCoreGetterUtility hardcodedStringWithKey:key bundle:[MVMCoreUIUtility bundleForMVMCoreUI]];