diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index facdb93f..3e7cca54 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -277,6 +277,16 @@ AAE7270E24AC8B9300A3ED0E /* HeadersH2CaretLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE7270D24AC8B9300A3ED0E /* HeadersH2CaretLink.swift */; }; AAE96FA225341F6A0037A989 /* ListStoreLocatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE96FA125341F6A0037A989 /* ListStoreLocatorModel.swift */; }; AAE96FA525341F7D0037A989 /* ListStoreLocator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE96FA425341F7D0037A989 /* ListStoreLocator.swift */; }; + AF1C33652883B5A4006B1001 /* ActionTopNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C33642883B5A4006B1001 /* ActionTopNotificationHandler.swift */; }; + AF1C33672883B712006B1001 /* ActionPopupHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C33662883B712006B1001 /* ActionPopupHandler.swift */; }; + AF1C336928859778006B1001 /* ActionAlertHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C336828859778006B1001 /* ActionAlertHandler.swift */; }; + AF1C336B28859C73006B1001 /* ActionTopAlertHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C336A28859C73006B1001 /* ActionTopAlertHandler.swift */; }; + AF1C336D28859EE1006B1001 /* ActionOpenPanelHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C336C28859EE1006B1001 /* ActionOpenPanelHandler.swift */; }; + AF1C336F2885A16A006B1001 /* ActionCollapseNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C336E2885A16A006B1001 /* ActionCollapseNotificationHandler.swift */; }; + AF1C33712885AE76006B1001 /* MVMCoreUIActionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C33702885AE76006B1001 /* MVMCoreUIActionHandler.swift */; }; + AF1C33732885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C33722885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift */; }; + AF60A7F62892D2E300919EEB /* ActionDismissNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F52892D2E300919EEB /* ActionDismissNotificationModel.swift */; }; + AF60A7F82892D34D00919EEB /* ActionDismissNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F72892D34D00919EEB /* ActionDismissNotificationHandler.swift */; }; AFE4A1D127DFB5EE00C458D0 /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = AFE4A1D027DFB5EE00C458D0 /* VDSColorTokens.xcframework */; }; AFE4A1D627DFBB6F00C458D0 /* UINavigationController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFE4A1D527DFBB6F00C458D0 /* UINavigationController+Extension.swift */; }; BB105859248DEFF70069D008 /* UICollectionViewLeftAlignedLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB105858248DEFF60069D008 /* UICollectionViewLeftAlignedLayout.swift */; }; @@ -567,8 +577,6 @@ D2ED2812254B0EB800A1C293 /* MVMCoreTopAlertObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2ED280B254B0EB800A1C293 /* MVMCoreTopAlertObject.m */; }; D2ED2815254B0EE400A1C293 /* MVMCoreGlobalTopAlertDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D2ED2814254B0EE400A1C293 /* MVMCoreGlobalTopAlertDelegateProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; D2ED2818254B115400A1C293 /* MVMCoreUIActionDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D2ED2817254B112900A1C293 /* MVMCoreUIActionDelegateProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D2ED281D254B119D00A1C293 /* MVMCoreUIActionHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = D2ED281B254B119D00A1C293 /* MVMCoreUIActionHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D2ED281E254B119D00A1C293 /* MVMCoreUIActionHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = D2ED281C254B119D00A1C293 /* MVMCoreUIActionHandler.m */; }; D2FA83D22513EA6900564112 /* NotificationXButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D12513EA6900564112 /* NotificationXButton.swift */; }; D2FA83D42514F80C00564112 /* CollapsableNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D32514F80C00564112 /* CollapsableNotification.swift */; }; D2FA83D62515021F00564112 /* CollapsableNotificationTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */; }; @@ -869,6 +877,16 @@ AAE7270D24AC8B9300A3ED0E /* HeadersH2CaretLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2CaretLink.swift; sourceTree = ""; }; AAE96FA125341F6A0037A989 /* ListStoreLocatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListStoreLocatorModel.swift; sourceTree = ""; }; AAE96FA425341F7D0037A989 /* ListStoreLocator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListStoreLocator.swift; sourceTree = ""; }; + AF1C33642883B5A4006B1001 /* ActionTopNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionTopNotificationHandler.swift; sourceTree = ""; }; + AF1C33662883B712006B1001 /* ActionPopupHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionPopupHandler.swift; sourceTree = ""; }; + AF1C336828859778006B1001 /* ActionAlertHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionAlertHandler.swift; sourceTree = ""; }; + AF1C336A28859C73006B1001 /* ActionTopAlertHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionTopAlertHandler.swift; sourceTree = ""; }; + AF1C336C28859EE1006B1001 /* ActionOpenPanelHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionOpenPanelHandler.swift; sourceTree = ""; }; + AF1C336E2885A16A006B1001 /* ActionCollapseNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCollapseNotificationHandler.swift; sourceTree = ""; }; + AF1C33702885AE76006B1001 /* MVMCoreUIActionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIActionHandler.swift; sourceTree = ""; }; + AF1C33722885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIActionOpenPageHandler.swift; sourceTree = ""; }; + AF60A7F52892D2E300919EEB /* ActionDismissNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionDismissNotificationModel.swift; sourceTree = ""; }; + AF60A7F72892D34D00919EEB /* ActionDismissNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionDismissNotificationHandler.swift; sourceTree = ""; }; AFE4A1D027DFB5EE00C458D0 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = ""; }; AFE4A1D527DFBB6F00C458D0 /* UINavigationController+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationController+Extension.swift"; sourceTree = ""; }; BB105858248DEFF60069D008 /* UICollectionViewLeftAlignedLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionViewLeftAlignedLayout.swift; sourceTree = ""; }; @@ -1160,8 +1178,6 @@ D2ED280B254B0EB800A1C293 /* MVMCoreTopAlertObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreTopAlertObject.m; sourceTree = ""; }; D2ED2814254B0EE400A1C293 /* MVMCoreGlobalTopAlertDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreGlobalTopAlertDelegateProtocol.h; sourceTree = ""; }; D2ED2817254B112900A1C293 /* MVMCoreUIActionDelegateProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIActionDelegateProtocol.h; sourceTree = ""; }; - D2ED281B254B119D00A1C293 /* MVMCoreUIActionHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIActionHandler.h; sourceTree = ""; }; - D2ED281C254B119D00A1C293 /* MVMCoreUIActionHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIActionHandler.m; sourceTree = ""; }; D2FA83D12513EA6900564112 /* NotificationXButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationXButton.swift; sourceTree = ""; }; D2FA83D32514F80C00564112 /* CollapsableNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotification.swift; sourceTree = ""; }; D2FA83D52515021F00564112 /* CollapsableNotificationTopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsableNotificationTopView.swift; sourceTree = ""; }; @@ -1480,12 +1496,21 @@ isa = PBXGroup; children = ( 94C0150924215643005811A9 /* ActionTopAlertModel.swift */, + AF1C336A28859C73006B1001 /* ActionTopAlertHandler.swift */, 94C0150B2421564A005811A9 /* ActionCollapseNotificationModel.swift */, + AF1C336E2885A16A006B1001 /* ActionCollapseNotificationHandler.swift */, D2C78CD124228BBD00B69FDE /* ActionOpenPanelModel.swift */, + AF1C336C28859EE1006B1001 /* ActionOpenPanelHandler.swift */, D2ED27E9254B0CE600A1C293 /* ActionAlertModel.swift */, D2ED27EA254B0CE700A1C293 /* AlertModel.swift */, + AF1C336828859778006B1001 /* ActionAlertHandler.swift */, D2ED27E8254B0CE600A1C293 /* ActionPopupModel.swift */, + AF1C33662883B712006B1001 /* ActionPopupHandler.swift */, C6687440259D92D400F32D13 /* ActionTopNotificationModel.swift */, + AF1C33642883B5A4006B1001 /* ActionTopNotificationHandler.swift */, + AF1C33722885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift */, + AF60A7F52892D2E300919EEB /* ActionDismissNotificationModel.swift */, + AF60A7F72892D34D00919EEB /* ActionDismissNotificationHandler.swift */, ); path = Actions; sourceTree = ""; @@ -2283,8 +2308,7 @@ D2092352244F7D630044AD09 /* MVMCoreUIViewControllerMappingObject+Extension.swift */, D296E14622A597490051EBE7 /* MVMCoreUIViewConstrainingProtocol.h */, D2ED2817254B112900A1C293 /* MVMCoreUIActionDelegateProtocol.h */, - D2ED281B254B119D00A1C293 /* MVMCoreUIActionHandler.h */, - D2ED281C254B119D00A1C293 /* MVMCoreUIActionHandler.m */, + AF1C33702885AE76006B1001 /* MVMCoreUIActionHandler.swift */, D23A90672614B0B4007E14CE /* CoreUIModelMapping.swift */, ); path = OtherHandlers; @@ -2551,7 +2575,6 @@ D29DF2C821E7BFC1003B2FB9 /* MFSizeObject.h in Headers */, D29DF2E121E9240B003B2FB9 /* MVMCoreUIPanelProtocol.h in Headers */, D29DF12921E6851E003B2FB9 /* MVMCoreUITopAlertMainView.h in Headers */, - D2ED281D254B119D00A1C293 /* MVMCoreUIActionHandler.h in Headers */, D29DF12C21E6851E003B2FB9 /* MVMCoreUITopAlertShortView.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2738,6 +2761,7 @@ AAB9C10A243496DD00151545 /* RadioSwatch.swift in Sources */, D29DF2B421E7B76D003B2FB9 /* MFLoadingSpinner.m in Sources */, 011D9602240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift in Sources */, + AF1C33672883B712006B1001 /* ActionPopupHandler.swift in Sources */, D23A900926125FFB007E14CE /* GetContactBehavior.swift in Sources */, D264FAA1243CF66B00D98315 /* ContainerCollectionReusableView.swift in Sources */, AA617AB22453012400910B8F /* ListDeviceComplexLinkSmallModel.swift in Sources */, @@ -2802,6 +2826,7 @@ 94C2D9A723872DA90006CF46 /* LabelAttributeColorModel.swift in Sources */, 943820842432382400B43AF3 /* WebView.swift in Sources */, 0103B84E23D7E33A009C315C /* HeadlineBodyToggleModel.swift in Sources */, + AF1C336F2885A16A006B1001 /* ActionCollapseNotificationHandler.swift in Sources */, D2755D7B23689C7500485468 /* TableViewCell.swift in Sources */, 0A25209624645AFD000FA9F6 /* TextViewEntryField.swift in Sources */, 014AA72623C501E2006F3E93 /* ContainerModelProtocol.swift in Sources */, @@ -2834,6 +2859,7 @@ 0A9D091D2433796500D2E6C0 /* BarsCarouselIndicatorModel.swift in Sources */, DBEFFA04225A829700230692 /* Label.swift in Sources */, D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */, + AF1C33712885AE76006B1001 /* MVMCoreUIActionHandler.swift in Sources */, 0A7ECC5D243CE85300C828E8 /* DoughnutChartItemModel.swift in Sources */, 0A7EF85F23D8ABC500B2AAD1 /* MdnEntryFieldModel.swift in Sources */, 011D959B240451E3000E3791 /* RuleRequiredModel.swift in Sources */, @@ -2936,6 +2962,7 @@ D2E2A99623D8CF85000B42E6 /* HeadlineBodyLinkToggleModel.swift in Sources */, C6FA7D5323C77A4A00A3614A /* StringAndMoleculeStack.swift in Sources */, 32F8804624765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift in Sources */, + AF1C336B28859C73006B1001 /* ActionTopAlertHandler.swift in Sources */, 011D958524042432000E3791 /* RulesProtocol.swift in Sources */, 4457904E27ECE989002B1E1E /* UIImageRenderingMode+Extension.swift in Sources */, D23118B325124E18001C8440 /* Notification.swift in Sources */, @@ -2956,8 +2983,10 @@ 012A88B1238C880100FE3DA1 /* CarouselPagingModelProtocol.swift in Sources */, 0A9D091E2433796500D2E6C0 /* NumericCarouselIndicatorModel.swift in Sources */, D29DF2C921E7BFC6003B2FB9 /* MFSizeObject.m in Sources */, + AF1C336928859778006B1001 /* ActionAlertHandler.swift in Sources */, 9445890E2385C3F800DE9FD4 /* MultiProgressModel.swift in Sources */, 011D95A5240455DC000E3791 /* FormGroupRule.swift in Sources */, + AF1C33732885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift in Sources */, D2A6390522CBCE160052ED1F /* MoleculeCollectionViewCell.swift in Sources */, D2A6390122CBB1820052ED1F /* Carousel.swift in Sources */, C7F8012123E8303200396FBD /* ListRVWheel.swift in Sources */, @@ -2976,6 +3005,7 @@ 522679C223FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift in Sources */, AA7F32AB246C0F7900C965BA /* ListLeftVariableRadioButtonAllTextAndLinksModel.swift in Sources */, 8D084AD02410BF4800951227 /* ListOneColumnFullWidthTextBodyTextModel.swift in Sources */, + AF60A7F82892D34D00919EEB /* ActionDismissNotificationHandler.swift in Sources */, D253BB9E2458751F002DE544 /* BGImageMoleculeModel.swift in Sources */, AA104AC924472DC7004D2810 /* HeadersH1ButtonModel.swift in Sources */, 0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */, @@ -2985,7 +3015,6 @@ BB2FB3BD247E7EF200DF73CD /* Tags.swift in Sources */, AA104ADC244734EA004D2810 /* HeadersH1LandingPageHeaderModel.swift in Sources */, BBAA4F03243D8E3B005AAD5F /* RadioBoxes.swift in Sources */, - D2ED281E254B119D00A1C293 /* MVMCoreUIActionHandler.m in Sources */, 323AC96A24C837F000F8E4C4 /* ListThreeColumnBillChangesModel.swift in Sources */, D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */, 525019E72406853600EED91C /* ListFourColumnDataUsageDivider.swift in Sources */, @@ -3019,6 +3048,7 @@ D23A8FD9260CE004007E14CE /* MFStyler+PaddingExtension.swift in Sources */, 1D6D258926899B0C00DEBB08 /* ImageButton.swift in Sources */, C695A68123C9830D00BFB94E /* NumberedListModel.swift in Sources */, + AF60A7F62892D2E300919EEB /* ActionDismissNotificationModel.swift in Sources */, 01EB3684236097C0006832FA /* MoleculeModelProtocol.swift in Sources */, D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */, AAB7EDF1246ADA2A00E54929 /* ListProgressBarThin.swift in Sources */, @@ -3062,6 +3092,7 @@ 52B201D224081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift in Sources */, D26C5A6B23F4A40D007AEECE /* ListItemModel.swift in Sources */, D270E5672642F77300CDBED2 /* AddRemoveMoleculeBehavior.swift in Sources */, + AF1C33652883B5A4006B1001 /* ActionTopNotificationHandler.swift in Sources */, BB2BF0EC2452A9D5001D0FC2 /* ListDeviceComplexButtonSmallModel.swift in Sources */, 943784F6236B77BB006A1E82 /* WheelAnimationHandler.swift in Sources */, 011D95A1240453D0000E3791 /* RuleEqualsModel.swift in Sources */, @@ -3156,6 +3187,7 @@ AAB7EDEF246ADA1600E54929 /* ListProgressBarThinModel.swift in Sources */, D21B7F75243BAC8900051ABF /* CarouselItem.swift in Sources */, C695A69823C990C200BFB94E /* DoughnutChartView.swift in Sources */, + AF1C336D28859EE1006B1001 /* ActionOpenPanelHandler.swift in Sources */, 8D3BA9BD2433787000D341BA /* ListThreeColumnInternationalDataDividerModel.swift in Sources */, D29DF2CB21E7BFCC003B2FB9 /* MFSizeThreshold.m in Sources */, 011D959F240453A1000E3791 /* RuleAllValueChangedModel.swift in Sources */, @@ -3337,7 +3369,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.0; + MARKETING_VERSION = 2.1; PRODUCT_BUNDLE_IDENTIFIER = com.vzw.MVMCoreUI; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -3368,7 +3400,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.0; + MARKETING_VERSION = 2.1; PRODUCT_BUNDLE_IDENTIFIER = com.vzw.MVMCoreUI; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; diff --git a/MVMCoreUI/Alerts/MVMCoreAlertObject.m b/MVMCoreUI/Alerts/MVMCoreAlertObject.m index 666745e2..b99a5870 100644 --- a/MVMCoreUI/Alerts/MVMCoreAlertObject.m +++ b/MVMCoreUI/Alerts/MVMCoreAlertObject.m @@ -17,7 +17,6 @@ @import MVMCore.NSDictionary_MFConvenience; @import MVMCore.MVMCoreHardcodedStringsConstants; @import MVMCore.MVMCoreJSONConstants; -@import MVMCore.MVMCoreActionHandler; @import MVMCore.Swift; #import @@ -151,9 +150,9 @@ + (nullable instancetype)alertObjectWithPage:(nullable NSDictionary *)page isGreedy:(BOOL)isGreedy additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject error:(MVMCoreErrorObject *_Nullable *_Nullable)error { MVMCoreAlertObject *alert = [[MVMCoreAlertObject alloc] init]; - alert.title = [page string:KeyTitle] ?: [MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle]; + alert.title = [page string:KeyTitle]; alert.pageJson = page; - alert.message = [page string:KeyMessage] ?: [MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess]; + alert.message = [page string:KeyMessage]; alert.isGreedy = isGreedy; alert.type = MFAlertTypePopup; alert.alertStyle = UIAlertControllerStyleAlert; @@ -162,7 +161,7 @@ NSMutableArray *actionsForAlert = [NSMutableArray array]; for (NSDictionary *actionMap in actions) { [actionsForAlert addObject:[UIAlertAction actionWithTitle:[actionMap stringForKey:KeyTitle] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { - [[MVMCoreActionHandler sharedActionHandler] handleActionWithDictionary:actionMap additionalData:additionalData delegateObject:delegateObject]; + [[MVMCoreUIActionHandler sharedActionHandler] handleActionWithDictionary:actionMap additionalData:additionalData delegateObject:delegateObject]; }]]; } alert.actions = actionsForAlert; diff --git a/MVMCoreUI/Atomic/Actions/ActionAlertHandler.swift b/MVMCoreUI/Atomic/Actions/ActionAlertHandler.swift new file mode 100644 index 00000000..0f4a11fa --- /dev/null +++ b/MVMCoreUI/Atomic/Actions/ActionAlertHandler.swift @@ -0,0 +1,31 @@ +// +// ActionAlertHandler.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 7/18/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore + +/// Shows an alert using the model. +open class ActionAlertHandler: MVMCoreJSONActionHandlerProtocol { + required public init() {} + + open func performAction(with JSON: [AnyHashable : Any], model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws { + var error: MVMCoreErrorObject? = nil + guard let alertObject = MVMCoreAlertObject.alertObjectWith(action: JSON, additionalData: additionalData, delegateObject: delegateObject, error: &error) else { + throw MVMCoreError.errorObject(error!) + } + (delegateObject?.actionDelegate as? MVMCoreUIActionDelegateProtocol)?.willShowPopup(with: alertObject, alertJson: JSON) + _ = await MainActor.run { + MVMCoreAlertHandler.shared()?.showAlert(with: alertObject) + } + } + + open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws { + let json = try MVMCoreActionHandler.convertActionToJSON(model) + try await performAction(with: json, model: model, delegateObject: delegateObject, additionalData: additionalData) + } +} diff --git a/MVMCoreUI/Atomic/Actions/ActionAlertModel.swift b/MVMCoreUI/Atomic/Actions/ActionAlertModel.swift index 67748a4d..f657304b 100644 --- a/MVMCoreUI/Atomic/Actions/ActionAlertModel.swift +++ b/MVMCoreUI/Atomic/Actions/ActionAlertModel.swift @@ -7,7 +7,7 @@ // import MVMCore -@objcMembers public class ActionAlertModel: ActionModelProtocol { +public struct ActionAlertModel: ActionModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Actions/ActionCollapseNotificationHandler.swift b/MVMCoreUI/Atomic/Actions/ActionCollapseNotificationHandler.swift new file mode 100644 index 00000000..069ee74f --- /dev/null +++ b/MVMCoreUI/Atomic/Actions/ActionCollapseNotificationHandler.swift @@ -0,0 +1,19 @@ +// +// ActionCollapseNotificationHandler.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 7/18/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore + +/// Collapse the current top notification. +open class ActionCollapseNotificationHandler: MVMCoreActionHandlerProtocol { + required public init() {} + + open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws { + CoreUIObject.sharedInstance()?.globalTopAlertDelegate?.getTopAlertView?().collapseNotification?() + } +} diff --git a/MVMCoreUI/Atomic/Actions/ActionCollapseNotificationModel.swift b/MVMCoreUI/Atomic/Actions/ActionCollapseNotificationModel.swift index 4663d539..94e84744 100644 --- a/MVMCoreUI/Atomic/Actions/ActionCollapseNotificationModel.swift +++ b/MVMCoreUI/Atomic/Actions/ActionCollapseNotificationModel.swift @@ -7,8 +7,9 @@ // import UIKit +import MVMCore -@objcMembers public class ActionCollapseNotificationModel: ActionModelProtocol { +public struct ActionCollapseNotificationModel: ActionModelProtocol { public static var identifier: String = "collapseNotification" public var actionType: String = ActionCollapseNotificationModel.identifier diff --git a/MVMCoreUI/Atomic/Actions/ActionDismissNotificationHandler.swift b/MVMCoreUI/Atomic/Actions/ActionDismissNotificationHandler.swift new file mode 100644 index 00000000..a2e1f279 --- /dev/null +++ b/MVMCoreUI/Atomic/Actions/ActionDismissNotificationHandler.swift @@ -0,0 +1,23 @@ +// +// ActionDismissNotificationHandler.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 7/28/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore + +/// Collapse the current top notification. +open class ActionDismissNotificationHandler: MVMCoreActionHandlerProtocol { + required public init() {} + + open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws { + await withCheckedContinuation { continuation in + CoreUIObject.sharedInstance()?.globalTopAlertDelegate?.getTopAlertView?().hideAlertView?(true, completionHandler: { finished in + continuation.resume() + }) ?? continuation.resume() + } + } +} diff --git a/MVMCoreUI/Atomic/Actions/ActionDismissNotificationModel.swift b/MVMCoreUI/Atomic/Actions/ActionDismissNotificationModel.swift new file mode 100644 index 00000000..13816f37 --- /dev/null +++ b/MVMCoreUI/Atomic/Actions/ActionDismissNotificationModel.swift @@ -0,0 +1,23 @@ +// +// ActionDismissNotificationModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 7/28/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore + +public struct ActionDismissNotificationModel: ActionModelProtocol { + + public static var identifier: String = "dismissNotification" + public var actionType: String = ActionDismissNotificationModel.identifier + public var extraParameters: JSONValueDictionary? + public var analyticsData: JSONValueDictionary? + + public init(_ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) { + self.extraParameters = extraParameters + self.analyticsData = analyticsData + } +} diff --git a/MVMCoreUI/Atomic/Actions/ActionOpenPanelHandler.swift b/MVMCoreUI/Atomic/Actions/ActionOpenPanelHandler.swift new file mode 100644 index 00000000..98287b27 --- /dev/null +++ b/MVMCoreUI/Atomic/Actions/ActionOpenPanelHandler.swift @@ -0,0 +1,33 @@ +// +// ActionOpenPanelHandler.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 7/18/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore + +/// Shows the panel. +open class ActionOpenPanelHandler: MVMCoreJSONActionHandlerProtocol { + required public init() {} + + open func performAction(with JSON: [AnyHashable : Any], model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws { + guard let model = model as? ActionOpenPanelModel else { return } + switch model.panel { + case .left, .menu: + await MVMCoreUISplitViewController.main()?.leftPanel?.willOpen?(withActionInformation: JSON, additionalData: additionalData) + await MVMCoreUISplitViewController.main()?.showLeftPanel(animated: true) + case .right, .support: + await MVMCoreUISplitViewController.main()?.rightPanel?.willOpen?(withActionInformation: JSON, additionalData: additionalData) + await MVMCoreUISplitViewController.main()?.showRightPanel(animated: true) + } + } + + open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws { + guard let model = model as? ActionOpenPanelModel else { return } + let json = try MVMCoreActionHandler.convertActionToJSON(model) + try await performAction(with: json, model: model, delegateObject: delegateObject, additionalData: additionalData) + } +} diff --git a/MVMCoreUI/Atomic/Actions/ActionOpenPanelModel.swift b/MVMCoreUI/Atomic/Actions/ActionOpenPanelModel.swift index eaad4746..ec526e10 100644 --- a/MVMCoreUI/Atomic/Actions/ActionOpenPanelModel.swift +++ b/MVMCoreUI/Atomic/Actions/ActionOpenPanelModel.swift @@ -7,8 +7,9 @@ // import Foundation +import MVMCore -public class ActionOpenPanelModel: ActionModelProtocol { +public struct ActionOpenPanelModel: ActionModelProtocol { public enum Panel: String, Codable { case left diff --git a/MVMCoreUI/Atomic/Actions/ActionPopupHandler.swift b/MVMCoreUI/Atomic/Actions/ActionPopupHandler.swift new file mode 100644 index 00000000..554a000a --- /dev/null +++ b/MVMCoreUI/Atomic/Actions/ActionPopupHandler.swift @@ -0,0 +1,33 @@ +// +// ActionPopupHandler.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 7/16/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore + +/// Shows a popup alert by grabbing the content from a Page in the cache using the pageType. +open class ActionPopupHandler: MVMCoreActionHandlerProtocol { + required public init() {} + + open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws { + guard let model = model as? ActionPopupModel else { return } + try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in + MVMCoreCache.shared()?.fetchJSON(forPageType: model.pageType, queue: nil, waitUntilFinished: true, completionHandler: { json in + var error: MVMCoreErrorObject? = nil + guard let alertObject = MVMCoreAlertObject(page: json, isGreedy: false, additionalData: additionalData, delegateObject: delegateObject, error: &error) else { + continuation.resume(throwing: MVMCoreError.errorObject(error!)) + return + } + (delegateObject?.actionDelegate as? MVMCoreUIActionDelegateProtocol)?.willShowPopup(with: alertObject, alertJson: json!) + Task { @MainActor in + MVMCoreAlertHandler.shared()?.showAlert(with: alertObject) + continuation.resume() + } + }) + } + } +} diff --git a/MVMCoreUI/Atomic/Actions/ActionPopupModel.swift b/MVMCoreUI/Atomic/Actions/ActionPopupModel.swift index 8cdbd83c..cbade308 100644 --- a/MVMCoreUI/Atomic/Actions/ActionPopupModel.swift +++ b/MVMCoreUI/Atomic/Actions/ActionPopupModel.swift @@ -6,8 +6,9 @@ // Copyright © 2019 myverizon. All rights reserved. // +import MVMCore -@objcMembers public class ActionPopupModel: ActionModelProtocol { +public struct ActionPopupModel: ActionModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Actions/ActionTopAlertHandler.swift b/MVMCoreUI/Atomic/Actions/ActionTopAlertHandler.swift new file mode 100644 index 00000000..49ca8561 --- /dev/null +++ b/MVMCoreUI/Atomic/Actions/ActionTopAlertHandler.swift @@ -0,0 +1,35 @@ +// +// ActionTopAlertHandler.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 7/18/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore + +/// Creates and shows an alert using the ResponseInfo of a Page found in the cache. +open class ActionTopAlertHandler: MVMCoreActionHandlerProtocol { + required public init() {} + + open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws { + guard let model = model as? ActionTopAlertModel else { return } + try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in + MVMCoreCache.shared()?.fetchJSON(forPageType: model.pageType, queue: nil, waitUntilFinished: true, completionHandler: { json in + guard let responseInfo = json?.optionalDictionaryForKey(KeyResponseInfo) else { + continuation.resume(throwing: ModelRegistry.Error.decoderOther(message: "Alert Page \(model.pageType) missing ResponseInfo")) + return + } + + var alertObject = MVMCoreAlertObject(forPageType: model.pageType, responseInfo: responseInfo, additionalData: additionalData, delegateObject: delegateObject) + if let object = alertObject, + let closure = (delegateObject?.actionDelegate as? MVMCoreUIActionDelegateProtocol)?.willShowTopAlert { + alertObject = closure(object, json!) + } + alertObject?.showAlert() + continuation.resume() + }) + } + } +} diff --git a/MVMCoreUI/Atomic/Actions/ActionTopAlertModel.swift b/MVMCoreUI/Atomic/Actions/ActionTopAlertModel.swift index 62e496c2..440e318c 100644 --- a/MVMCoreUI/Atomic/Actions/ActionTopAlertModel.swift +++ b/MVMCoreUI/Atomic/Actions/ActionTopAlertModel.swift @@ -8,7 +8,7 @@ import Foundation -@objcMembers public class ActionTopAlertModel: ActionModelProtocol { +public struct ActionTopAlertModel: ActionModelProtocol { public static var identifier: String = "topAlert" public var actionType: String = ActionTopAlertModel.identifier diff --git a/MVMCoreUI/Atomic/Actions/ActionTopNotificationHandler.swift b/MVMCoreUI/Atomic/Actions/ActionTopNotificationHandler.swift new file mode 100644 index 00000000..858c1d09 --- /dev/null +++ b/MVMCoreUI/Atomic/Actions/ActionTopNotificationHandler.swift @@ -0,0 +1,20 @@ +// +// ActionTopNotificationHandler.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 7/16/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore + +/// Shows a top notification/alert with the model. +open class ActionTopNotificationHandler: MVMCoreActionHandlerProtocol { + required public init() {} + + open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws { + guard let model = model as? ActionTopNotificationModel else { return } + await MVMCoreUITopAlertView.sharedGlobal()?.showTopAlert(with: model.topNotification) + } +} diff --git a/MVMCoreUI/Atomic/Actions/ActionTopNotificationModel.swift b/MVMCoreUI/Atomic/Actions/ActionTopNotificationModel.swift index 7ca1d6aa..9850e8d0 100644 --- a/MVMCoreUI/Atomic/Actions/ActionTopNotificationModel.swift +++ b/MVMCoreUI/Atomic/Actions/ActionTopNotificationModel.swift @@ -7,8 +7,9 @@ // import UIKit +import MVMCore -@objcMembers public class ActionTopNotificationModel: ActionModelProtocol { +public struct ActionTopNotificationModel: ActionModelProtocol { public static var identifier: String = "topNotification" public var actionType: String = ActionTopNotificationModel.identifier diff --git a/MVMCoreUI/Atomic/Actions/MVMCoreUIActionOpenPageHandler.swift b/MVMCoreUI/Atomic/Actions/MVMCoreUIActionOpenPageHandler.swift new file mode 100644 index 00000000..9e0187e6 --- /dev/null +++ b/MVMCoreUI/Atomic/Actions/MVMCoreUIActionOpenPageHandler.swift @@ -0,0 +1,17 @@ +// +// MVMCoreUIActionOpenPageHandler.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 7/18/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore + +open class MVMCoreUIActionOpenPageHandler: ActionOpenPageHandler { + open override func performAction(with JSON: [AnyHashable : Any], model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws { + // Cleanup the source model data to prevent it from being accidentally auto-forwarded in openPage network requests by blind additionalData insertions. (https://onejira.verizon.com/browse/CXTDT-135642, https://onejira.verizon.com/browse/CXTDT-136001). + try await super.performAction(with: JSON, model: model, delegateObject: delegateObject, additionalData: MVMCoreUIActionHandler.removeSourceModel(from: additionalData)) + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift index 4f4801c6..8818107b 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift @@ -210,7 +210,7 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat if let style = try typeContainer.decodeIfPresent(Styler.Button.Style.self, forKey: .style){ self.style = style setFacade(by: style) - } else if let style = decoder.context?.value(forKey: CodingKeys.style.stringValue) as? Styler.Button.Style { ///Reading the style param from context which is set is molecules, ex: TwoButtonView + } else if let style = decoder.context?.value(forKey: CodingKeys.style.stringValue) as? Styler.Button.Style { ///Reading the style param from context which is set is molecules, ex: TwoButtonView self.style = style setFacade(by: style) } else { ///Default style diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/Tags/Tags.swift b/MVMCoreUI/Atomic/Atoms/FormFields/Tags/Tags.swift index 4b1fa4f9..a112a723 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/Tags/Tags.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/Tags/Tags.swift @@ -100,6 +100,8 @@ extension Tags: UICollectionViewDelegate { open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { guard let tagModel = tags?[indexPath.row], let tagAction = tagModel.action else { return } - Button.performButtonAction(with: tagAction, button: self, delegateObject: delegateObject, additionalData: nil) + Task(priority: .userInitiated) { + try await Button.performButtonAction(with: tagAction, button: self, delegateObject: delegateObject, additionalData: nil) + } } } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/Dropdown Fields/BaseDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/Dropdown Fields/BaseDropdownEntryField.swift index 6e8be5f1..172f304d 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/Dropdown Fields/BaseDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/Dropdown Fields/BaseDropdownEntryField.swift @@ -7,7 +7,7 @@ // import UIKit - +import MVMCore /** This class is intended to be subclassed. See ItemDropdownEntryField and DateDropdownEntryField. @@ -98,8 +98,6 @@ import UIKit guard let baseDropdownEntryFieldModel = baseDropdownEntryFieldModel, let actionModel = baseDropdownEntryFieldModel.action else { return } - - let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: baseDropdownEntryFieldModel) - MVMCoreActionHandler.shared()?.asyncHandleAction(with: actionModel, additionalData: additionalDataWithSource, delegateObject: delegateObject) + MVMCoreUIActionHandler.performActionUnstructured(with: actionModel, sourceModel: baseDropdownEntryFieldModel, additionalData: additionalData, delegateObject: delegateObject) } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index 4bd64c7c..9efc554a 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -389,9 +389,7 @@ import MVMCore } private func performCheckboxAction(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - var additionalDataToUpdate = additionalData ?? [:] - additionalDataToUpdate[KeySourceModel] = checkboxModel - MVMCoreActionHandler.shared()?.asyncHandleAction(with: actionModel, additionalData: additionalDataToUpdate, delegateObject: delegateObject) + MVMCoreUIActionHandler.performActionUnstructured(with: actionModel, sourceModel: checkboxModel, additionalData: additionalData, delegateObject: delegateObject) } public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift index eb0b0b4c..1f22e328 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Heart.swift @@ -106,7 +106,9 @@ import UIKit guard isEnabled else { return } isSelected = !isSelected if let heartModel = heartModel { - Button.performButtonAction(with: heartModel.action, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: heartModel) + Task(priority: .userInitiated) { + try await Button.performButtonAction(with: heartModel.action, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: heartModel) + } } setNeedsDisplay() } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift index 27323048..9fb6b354 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioBox.swift @@ -142,7 +142,9 @@ open class RadioBox: Control, MFButtonProtocol { isSelected = true radioBoxModel?.selected = isSelected if let radioBoxModel = radioBoxModel, let actionModel = radioBoxModel.action { - Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioBoxModel) + Task(priority: .userInitiated) { + try await Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioBoxModel) + } } layer.setNeedsDisplay() } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift index 1f82a63c..ddd155be 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioButton.swift @@ -104,7 +104,9 @@ import VDSFormControlsTokens } if let radioModel = radioModel, let actionModel = radioModel.action, isSelected, !wasPreviouslySelected { - Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioModel) + Task(priority: .userInitiated) { + try await Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioModel) + } } _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift index eb569810..50d9d9c5 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift @@ -124,7 +124,9 @@ open class RadioSwatch: Control, MFButtonProtocol { isSelected = true radioSwatchModel?.selected = isSelected if let radioSwatchModel = radioSwatchModel, let actionModel = radioSwatchModel.action { - Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioSwatchModel) + Task(priority: .userInitiated) { + try await Button.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: radioSwatchModel) + } } layer.setNeedsDisplay() } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift index 04c34da2..85ad8e9c 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Toggle.swift @@ -390,17 +390,16 @@ public typealias ActionBlockConfirmation = () -> (Bool) accessibilityLabel = accessibileString } - let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: model) if model.action != nil || model.alternateAction != nil { didToggleAction = { [weak self] in guard let self = self else { return } if self.isOn { if let action = model.action { - MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: additionalDataWithSource, delegateObject: delegateObject) + MVMCoreUIActionHandler.performActionUnstructured(with: action, sourceModel: model, additionalData: additionalData, delegateObject: delegateObject) } } else { if let action = model.alternateAction ?? model.action { - MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: additionalDataWithSource, delegateObject: delegateObject) + MVMCoreUIActionHandler.performActionUnstructured(with: action, sourceModel: model, additionalData: additionalData, delegateObject: delegateObject) } } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 2ad66ede..1beb6069 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -230,7 +230,7 @@ public typealias ActionBlock = () -> () documentAttributes: nil) } catch { if let coreErrorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "LabelHTMLParse") { - MVMCoreUILoggingHandler.shared()?.addError(toLog: coreErrorObject) + MVMCoreUILoggingHandler.addError(toLog: coreErrorObject) } } } @@ -387,7 +387,7 @@ public typealias ActionBlock = () -> () } case let actionAtt as LabelAttributeActionModel: addTappableLinkAttribute(range: NSRange(location: range.location, length: range.length)) { - MVMCoreActionHandler.shared()?.asyncHandleAction(with: actionAtt.action, additionalData: additionalData, delegateObject: delegateObject) + MVMCoreUIActionHandler.performActionUnstructured(with: actionAtt.action, sourceModel: model, additionalData: additionalData, delegateObject: delegateObject) } addActionAttributes(range: range, string: attributedString) diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift index 527ed054..e49006fa 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TabBar.swift @@ -69,7 +69,10 @@ import VDSColorTokens // MARK: - UITabBarDelegate public func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) { model.selectedTab = item.tag - Button.performButtonAction(with: model.tabs[item.tag].action, button: item, delegateObject: delegateObject, additionalData: nil) + let action = model.tabs[item.tag].action + Task(priority: .userInitiated) { + try await Button.performButtonAction(with: action, button: item, delegateObject: delegateObject, additionalData: nil) + } } // MARK: - TabBarProtocol diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/Tabs.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/Tabs.swift index f8e00ae6..de9ef49f 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/Tabs.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/Tabs.swift @@ -259,7 +259,7 @@ extension Tabs: UICollectionViewDelegateFlowLayout { if let delegate = delegate { delegate.didSelectItem(indexPath, tabs: self) } else if let action = tabsModel.tabs[selectedIndex].action { - MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: [KeySourceModel: tabsModel], delegateObject:delegateObject) + MVMCoreUIActionHandler.performActionUnstructured(with: action, sourceModel: tabsModel, additionalData: nil, delegateObject: delegateObject) } if UIAccessibility.isVoiceOverRunning { UIAccessibility.post(notification: .layoutChanged, argument: tabCell) diff --git a/MVMCoreUI/Atomic/Molecules/Items/AccordionMoleculeTableViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/AccordionMoleculeTableViewCell.swift index 6975f8cb..f8832ad5 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/AccordionMoleculeTableViewCell.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/AccordionMoleculeTableViewCell.swift @@ -6,8 +6,8 @@ // Copyright © 2019 Verizon Wireless. All rights reserved. // - -@objcMembers public class AccordionMoleculeTableViewCell: MoleculeTableViewCell { +import MVMCore +public class AccordionMoleculeTableViewCell: MoleculeTableViewCell { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -46,9 +46,9 @@ model.selected = accordionButton.isSelected if accordionButton.isSelected { - MVMCoreActionHandler.shared()?.asyncHandleAction(with: AddMoleculesActionModel(.automatic), additionalData: [KeySourceModel: model], delegateObject: delegateObject) + MVMCoreUIActionHandler.performActionUnstructured(with: AddMoleculesActionModel(.automatic), sourceModel: model, additionalData: additionalData, delegateObject: delegateObject) } else { - MVMCoreActionHandler.shared()?.asyncHandleAction(with: RemoveMoleculesActionModel(.automatic), additionalData: [KeySourceModel: model], delegateObject: delegateObject) + MVMCoreUIActionHandler.performActionUnstructured(with: RemoveMoleculesActionModel(.automatic), sourceModel: model, additionalData: additionalData, delegateObject: delegateObject) } if (accordionListItemModel?.hideLineWhenExpanded ?? false) && (self.bottomSeparatorView?.shouldBeVisible() ?? false) { @@ -56,7 +56,7 @@ } if let actionModel = accordionButton.isSelected ? accordionListItemModel?.expandAction : accordionListItemModel?.collapseAction { - MVMCoreActionHandler.shared()?.asyncHandleAction(with: actionModel, additionalData: [KeySourceModel: model], delegateObject: delegateObject) + MVMCoreUIActionHandler.performActionUnstructured(with: actionModel, sourceModel: model, additionalData: additionalData, delegateObject: delegateObject) } } diff --git a/MVMCoreUI/Atomic/Molecules/Items/DropDownFilterTableViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/DropDownFilterTableViewCell.swift index b6de93d1..663a53fa 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/DropDownFilterTableViewCell.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/DropDownFilterTableViewCell.swift @@ -24,18 +24,19 @@ import UIKit addMolecule(dropDown) dropDown.observeDropdownChange = { [weak self] oldValue, newValue in - guard newValue != oldValue, - let self = self, - let model = self.listItemModel as? DropDownListItemModel - else { return } - MVMCoreDispatchUtility.performBlock(inBackground: { - if let oldValue = oldValue, - oldValue.count > 0 { - MVMCoreActionHandler.shared()?.syncHandleAction(with: RemoveMoleculesActionModel(.fade), additionalData: [KeySourceModel: model], delegateObject: self.delegateObject) - } - MVMCoreActionHandler.shared()?.syncHandleAction(with: AddMoleculesActionModel(.fade), additionalData: [KeySourceModel: model], delegateObject: self.delegateObject) - }) + let self = self, + let model = self.listItemModel as? DropDownListItemModel else { return } + + var actions: [ActionModelProtocol] = [] + if let oldValue = oldValue, + oldValue.count > 0 { + actions.append(RemoveMoleculesActionModel(.fade)) + } + actions.append(AddMoleculesActionModel(.fade)) + var actionsModel = ActionActionsModel(actions: actions) + actionsModel.concurrent = false + MVMCoreUIActionHandler.performActionUnstructured(with: actionsModel, sourceModel: model, additionalData: nil, delegateObject: self.delegateObject) } } diff --git a/MVMCoreUI/Atomic/Molecules/Items/TabsTableViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/TabsTableViewCell.swift index d3b91b11..6a941881 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/TabsTableViewCell.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/TabsTableViewCell.swift @@ -57,7 +57,7 @@ extension TabsTableViewCell: TabsDelegate { public func shouldSelectItem(_ indexPath: IndexPath, tabs: Tabs) -> Bool { guard indexPath.row != tabs.selectedIndex else { return false } if let model = tabsListItemModel { - MVMCoreActionHandler.shared()?.asyncHandleAction(with: RemoveMoleculesActionModel(indexPath.row < tabs.selectedIndex ? .right : .left), additionalData: [KeySourceModel: model], delegateObject: delegateObject) + MVMCoreUIActionHandler.performActionUnstructured(with: RemoveMoleculesActionModel(indexPath.row < tabs.selectedIndex ? .right : .left), sourceModel: model, additionalData: nil, delegateObject: delegateObject) } previousTabIndex = tabs.selectedIndex return true @@ -68,9 +68,9 @@ extension TabsTableViewCell: TabsDelegate { guard let model = tabsListItemModel, index < model.molecules.count else { return } if let action = model.tabs.tabs[index].action { - MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: [KeySourceModel: model.tabs], delegateObject:delegateObject) + MVMCoreUIActionHandler.performActionUnstructured(with: action, sourceModel: model.tabs, additionalData: nil, delegateObject: delegateObject) } - MVMCoreActionHandler.shared()?.asyncHandleAction(with: AddMoleculesActionModel(index < previousTabIndex ? .left : .right), additionalData: [KeySourceModel: model], delegateObject: delegateObject) + MVMCoreUIActionHandler.performActionUnstructured(with: AddMoleculesActionModel(index < previousTabIndex ? .left : .right), sourceModel: model, additionalData: nil, delegateObject: delegateObject) } } diff --git a/MVMCoreUI/Atomic/Molecules/OtherContainers/ModuleMolecule.swift b/MVMCoreUI/Atomic/Molecules/OtherContainers/ModuleMolecule.swift index ce76d379..6083ac5e 100644 --- a/MVMCoreUI/Atomic/Molecules/OtherContainers/ModuleMolecule.swift +++ b/MVMCoreUI/Atomic/Molecules/OtherContainers/ModuleMolecule.swift @@ -75,7 +75,7 @@ open class ModuleMolecule: Container { let _ = delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) else { if let errorObject = MVMCoreErrorObject(title: nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess), code: CoreUIErrorCode.ErrorCodeModuleMolecule.rawValue, domain: ErrorDomainNative, location: String(describing: self)) { error?.pointee = errorObject - MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject) + MVMCoreUILoggingHandler.addError(toLog: errorObject) } return nil } diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift index 82983f4d..8c9416c0 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/CollapsableNotification.swift @@ -60,7 +60,9 @@ import Foundation if let topAction = model.topAction, topAction.actionType == ActionNoopModel.identifier { topView.button.addActionBlock(event: .touchUpInside) { [weak self] (button) in - Button.performButtonAction(with: topAction, button: button, delegateObject: delegateObject, additionalData: additionalData) + Task(priority: .userInitiated) { + try await Button.performButtonAction(with: topAction, button: button, delegateObject: delegateObject, additionalData: additionalData) + } self?.expand(topViewShowing: model.alwaysShowTopLabel) } } diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift index bd84b7fd..fe40a022 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationModel.swift @@ -85,24 +85,14 @@ open class NotificationModel: ContainerModel, MoleculeModelProtocol { } button?.size = .tiny - if button?.enabledTextColor == nil { - switch style { - case .error, .warning: - button?.enabledTextColor = Color(uiColor: .mvmBlack) - default: - button?.enabledTextColor = Color(uiColor: .mvmWhite) - } - } - if button?.enabledBorderColor == nil { - switch style { - case .error, .warning: - button?.enabledBorderColor = Color(uiColor: .mvmBlack) - default: - button?.enabledBorderColor = Color(uiColor: .mvmWhite) - } - } - if button?.style == nil { - button?.style = .secondary + button?.style = .secondary + switch style { + case .error, .warning: + button?.enabledTextColor = Color(uiColor: .mvmBlack) + button?.enabledBorderColor = Color(uiColor: .mvmBlack) + default: + button?.enabledTextColor = Color(uiColor: .mvmWhite) + button?.enabledBorderColor = Color(uiColor: .mvmWhite) } if closeButton?.color == nil { diff --git a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButton.swift b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButton.swift index 204785cb..d0071668 100644 --- a/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButton.swift +++ b/MVMCoreUI/Atomic/Molecules/TopNotification/NotificationXButton.swift @@ -7,15 +7,12 @@ // import Foundation +import MVMCore @objcMembers open class NotificationXButton: Button { - open func closeTopAlert() { - if let delegate = MVMCoreUITopAlertView.sharedGlobal()?.animationDelegate { - delegate.topAlertCloseButtonPressed() - } else { - MVMCoreUISession.sharedGlobal()?.topAlertView?.hideAlertView(true, completionHandler: nil) - } + open func closeTopAlert(with delegateObject: MVMCoreUIDelegateObject?) { + MVMCoreUIActionHandler.performActionUnstructured(with: ActionDismissNotificationModel(), sourceModel: model, additionalData: nil, delegateObject: delegateObject) } open override func setupView() { @@ -38,7 +35,7 @@ import Foundation // TODO: Temporary, consider action for dismissing top alert if model.action.actionType == ActionNoopModel.identifier { addActionBlock(event: .touchUpInside) { (button) in - (button as? NotificationXButton)?.closeTopAlert() + (button as? NotificationXButton)?.closeTopAlert(with: delegateObject) } } } diff --git a/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift b/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift index 71c23c16..656d5086 100644 --- a/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/ModalMoleculeListTemplate.swift @@ -7,7 +7,7 @@ // import UIKit - +import MVMCore open class ModalMoleculeListTemplate: MoleculeListTemplate { //-------------------------------------------------- @@ -32,7 +32,7 @@ open class ModalMoleculeListTemplate: MoleculeListTemplate { guard let self = self else { return } let closeAction = (self.templateModel as? ModalListPageTemplateModel)?.closeAction ?? ActionBackModel() - MVMCoreActionHandler.shared()?.asyncHandleAction(with: closeAction, additionalData: nil, delegateObject: self.delegateObjectIVar) + MVMCoreUIActionHandler.performActionUnstructured(with: closeAction, additionalData: nil, delegateObject: self.delegateObject()) }) } diff --git a/MVMCoreUI/Atomic/Templates/ModalMoleculeStackTemplate.swift b/MVMCoreUI/Atomic/Templates/ModalMoleculeStackTemplate.swift index 1c76b467..97aced2a 100644 --- a/MVMCoreUI/Atomic/Templates/ModalMoleculeStackTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/ModalMoleculeStackTemplate.swift @@ -7,6 +7,7 @@ // import UIKit +import MVMCore open class ModalMoleculeStackTemplate: MoleculeStackTemplate { //-------------------------------------------------- @@ -28,7 +29,7 @@ open class ModalMoleculeStackTemplate: MoleculeStackTemplate { guard let self = self else { return } let closeAction = (self.templateModel as? ModalStackPageTemplateModel)?.closeAction ?? ActionBackModel() - MVMCoreActionHandler.shared()?.asyncHandleAction(with: closeAction, additionalData: nil, delegateObject: self.delegateObjectIVar) + MVMCoreUIActionHandler.performActionUnstructured(with: closeAction, additionalData: nil, delegateObject: self.delegateObject()) }) } } diff --git a/MVMCoreUI/Atomic/Templates/ModalSectionListTemplate.swift b/MVMCoreUI/Atomic/Templates/ModalSectionListTemplate.swift index 3e4f3fc2..baf8d78a 100644 --- a/MVMCoreUI/Atomic/Templates/ModalSectionListTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/ModalSectionListTemplate.swift @@ -5,7 +5,7 @@ // Created by Scott Pfeil on 10/8/20. // Copyright © 2020 Verizon Wireless. All rights reserved. // - +import MVMCore open class ModalSectionListTemplate: SectionListTemplate { //-------------------------------------------------- @@ -27,7 +27,7 @@ open class ModalSectionListTemplate: SectionListTemplate { guard let self = self else { return } let closeAction = (self.templateModel as? ModalSectionListTemplateModel)?.closeAction ?? ActionBackModel() - MVMCoreActionHandler.shared()?.asyncHandleAction(with: closeAction, additionalData: nil, delegateObject: self.delegateObjectIVar) + MVMCoreUIActionHandler.performActionUnstructured(with: closeAction, additionalData: nil, delegateObject: self.delegateObject()) }) } } diff --git a/MVMCoreUI/BaseClasses/BarButtonItem.swift b/MVMCoreUI/BaseClasses/BarButtonItem.swift index b46647f2..d759b147 100644 --- a/MVMCoreUI/BaseClasses/BarButtonItem.swift +++ b/MVMCoreUI/BaseClasses/BarButtonItem.swift @@ -32,8 +32,9 @@ public typealias BarButtonAction = (BarButtonItem) -> () self.model = model buttonDelegate = delegateObject?.buttonDelegate actionDelegate?.buttonAction = { sender in - let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: model) - Button.performButtonAction(with: model.action, button: sender, delegateObject: delegateObject, additionalData: additionalDataWithSource) + Task(priority: .userInitiated) { + try await Button.performButtonAction(with: model.action, button: sender, delegateObject: delegateObject, additionalData: additionalData, sourceModel: model) + } } } } diff --git a/MVMCoreUI/BaseClasses/Button.swift b/MVMCoreUI/BaseClasses/Button.swift index a94cafe8..15ea1e03 100644 --- a/MVMCoreUI/BaseClasses/Button.swift +++ b/MVMCoreUI/BaseClasses/Button.swift @@ -6,8 +6,9 @@ // Copyright © 2019 Verizon Wireless. All rights reserved. // -public typealias ButtonAction = (Button) -> () +import MVMCore +public typealias ButtonAction = (Button) -> () @objcMembers open class Button: UIButton, MFButtonProtocol, MoleculeViewProtocol { //-------------------------------------------------- @@ -79,21 +80,15 @@ public typealias ButtonAction = (Button) -> () addActionBlock(event: .touchUpInside) { [weak self] sender in guard let self = self, let actionModel = actionModel else { return } - Self.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: self.model) + Task(priority: .userInitiated) { + try await Self.performButtonAction(with: actionModel, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: self.model) + } } } - open class func performButtonAction(with model: ActionModelProtocol, button: MFButtonProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?, sourceModel: MoleculeModelProtocol? = nil) { - if let data = try? model.encode(using: JSONEncoder()), - let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any], - delegateObject?.buttonDelegate?.button?(button, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { - if let sourceModel = sourceModel { - let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: sourceModel) - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalDataWithSource, delegateObject: delegateObject) - } else { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) - } - } + open class func performButtonAction(with model: ActionModelProtocol, button: MFButtonProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?, sourceModel: MoleculeModelProtocol? = nil) async throws { + guard delegateObject?.buttonDelegate?.button?(button, shouldPerformActionWithMap: model.toJSON(), additionalData: additionalData) ?? true else { return } + try await (delegateObject?.actionDelegate as? ActionDelegateProtocol)?.performAction(with: model, additionalData: MVMCoreUIActionHandler.add(sourceModel: sourceModel, to: additionalData), delegateObject: delegateObject) } // MARK:- MoleculeViewProtocol diff --git a/MVMCoreUI/BaseClasses/CollectionViewCell.swift b/MVMCoreUI/BaseClasses/CollectionViewCell.swift index cfbe4ac7..c57afd41 100644 --- a/MVMCoreUI/BaseClasses/CollectionViewCell.swift +++ b/MVMCoreUI/BaseClasses/CollectionViewCell.swift @@ -118,7 +118,9 @@ open class CollectionViewCell: UICollectionViewCell, MoleculeViewProtocol, MVMCo open func shouldSelect(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Bool { if let action = model?.action { - Button.performButtonAction(with: action, button: self, delegateObject: delegateObject, additionalData: additionalData) + Task(priority: .userInitiated) { + try await Button.performButtonAction(with: action, button: self, delegateObject: delegateObject, additionalData: additionalData) + } } return false } diff --git a/MVMCoreUI/BaseClasses/TableViewCell.swift b/MVMCoreUI/BaseClasses/TableViewCell.swift index d90d0338..c21e2c49 100644 --- a/MVMCoreUI/BaseClasses/TableViewCell.swift +++ b/MVMCoreUI/BaseClasses/TableViewCell.swift @@ -256,7 +256,9 @@ import UIKit open func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { guard let action = listItemModel?.action else { return } - Button.performButtonAction(with: action, button: self, delegateObject: delegateObject, additionalData: additionalData) + Task(priority: .userInitiated) { + try await Button.performButtonAction(with: action, button: self, delegateObject: delegateObject, additionalData: additionalData) + } } open func didDeselectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { } diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index 97846891..c6c64f3a 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -7,8 +7,9 @@ // import UIKit +import MVMCore -@objc open class ViewController: UIViewController, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MoleculeDelegateProtocol, FormHolderProtocol, MVMCoreActionDelegateProtocol, MVMCoreLoadDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, ObservingTextFieldDelegate, MVMCoreUIDetailViewProtocol, PageProtocol, PageBehaviorHandlerProtocol { +@objc open class ViewController: UIViewController, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MoleculeDelegateProtocol, FormHolderProtocol, MVMCoreActionDelegateProtocol, ActionDelegateProtocol, MVMCoreLoadDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, ObservingTextFieldDelegate, MVMCoreUIDetailViewProtocol, PageProtocol, PageBehaviorHandlerProtocol { //-------------------------------------------------- // MARK: - Properties @@ -26,7 +27,7 @@ import UIKit public weak var manager: (UIViewController & MVMCoreViewManagerProtocol)? /// A temporary iVar backer for delegateObject() until we change the protocol - public lazy var delegateObjectIVar: MVMCoreUIDelegateObject = { + open lazy var delegateObjectIVar: MVMCoreUIDelegateObject = { MVMCoreUIDelegateObject.create(withDelegateForAll: self) }() @@ -417,8 +418,8 @@ import UIKit } /// Override this method to avoid adding form params. - open func addFormParams(requestParameters: MVMCoreRequestParameters, actionInformation: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?) { - formValidator?.addFormParams(requestParameters: requestParameters, model: additionalData?[KeySourceModel] as? MoleculeModelProtocol & FormItemProtocol) + open func addFormParams(requestParameters: MVMCoreRequestParameters, additionalData: [AnyHashable: Any]?) { + formValidator?.addFormParams(requestParameters: requestParameters, model: MVMCoreUIActionHandler.getSourceModel(from: additionalData) as? MoleculeModelProtocol & FormItemProtocol) } public func handleFieldErrors(_ fieldErrors: [Any]?, loadObject: MVMCoreLoadObject) { @@ -439,37 +440,42 @@ import UIKit // MARK: - MVMCoreActionDelegateProtocol //-------------------------------------------------- - open func handleOpenPage(for requestParameters: MVMCoreRequestParameters, actionInformation: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?) { - addFormParams(requestParameters: requestParameters, actionInformation: actionInformation, additionalData: additionalData) - requestParameters.parentPageType = loadObject?.pageJSON?.optionalStringForKey("parentPageType") - var pageForwardedData = additionalData ?? [:] - - executeBehaviors { (behavior: PageLocalDataShareBehavior) in - let dataMap = behavior.compileLocalPageDataForTransfer(delegateObjectIVar) - pageForwardedData.merge(dataMap) { current, _ in current } - } - - MVMCoreActionHandler.defaultHandleOpenPage(for: requestParameters, actionInformation: actionInformation, additionalData: pageForwardedData, delegateObject: delegateObject()) - } - open func logAction(withActionInformation actionInformation: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?) { MVMCoreUILoggingHandler.shared()?.defaultLogAction(forController: self, actionInformation: actionInformation, additionalData: additionalData) } - open func handleUnknownActionType(_ actionType: String?, actionInformation: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?) { - var handled = false - - executeBehaviors { (behavior: PageCustomActionHandlerBehavior) in - if !handled { - handled = behavior.handleAction(type: actionType, information: actionInformation, additionalData: additionalData) + open func performAction(with model: ActionModelProtocol, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) async throws { + var additionalData = additionalData + if let model = model as? ActionOpenPageModel { + addFormParams(requestParameters: model.requestParameters, additionalData: additionalData) + model.requestParameters.parentPageType = loadObject?.pageJSON?.optionalStringForKey("parentPageType") + + var pageForwardedData = additionalData ?? [:] + executeBehaviors { (behavior: PageLocalDataShareBehavior) in + let dataMap = behavior.compileLocalPageDataForTransfer(delegateObjectIVar) + pageForwardedData.merge(dataMap) { current, _ in current } } + additionalData = pageForwardedData } - if !handled { - MVMCoreUIActionHandler.defaultHandleUnknownActionType(actionType, actionInformation: actionInformation, additionalData: additionalData, delegateObject: delegateObjectIVar) + do { + if let behavior = behaviors?.compactMap({ $0 as? PageCustomActionHandlerBehavior }).first(where: { $0.canHandleAction(with: model, additionalData: additionalData) }) { + logAction(withActionInformation: model.toJSON(), additionalData: additionalData) + try await behavior.handleAction(with: model, additionalData: additionalData) + } else { + try await MVMCoreUIActionHandler.shared()?.handleAction(with: model, additionalData: additionalData, delegateObject: delegateObject) + } + } catch { + handleAction(error: error, model: model, additionalData: additionalData, delegateObject: delegateObject) + throw error } } + open func handleAction(error: Error, model: ActionModelProtocol, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) { + let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: MVMCoreActionHandler.getErrorLocation(with: delegateObject?.actionDelegate, actionType: model.actionType))! + MVMCoreUIActionHandler.shared()?.defaultHandleActionError(errorObject, additionalData: additionalData) + } + //-------------------------------------------------- // MARK: - MoleculeDelegateProtocol //-------------------------------------------------- diff --git a/MVMCoreUI/Behaviors/AddRemoveMoleculeBehavior.swift b/MVMCoreUI/Behaviors/AddRemoveMoleculeBehavior.swift index a3be96f5..ed9b8196 100644 --- a/MVMCoreUI/Behaviors/AddRemoveMoleculeBehavior.swift +++ b/MVMCoreUI/Behaviors/AddRemoveMoleculeBehavior.swift @@ -7,6 +7,7 @@ // import Foundation +import MVMCore public enum AddMoleculesPosition { case above @@ -85,28 +86,27 @@ public class AddRemoveMoleculesBehavior: PageCustomActionHandlerBehavior, PageMo } } - public func handleAction(type actionType: String?, information: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) -> Bool { - if actionType == AddMoleculesActionModel.identifier { - guard let list = delegate?.moleculeListDelegate, - let model = additionalData?[KeySourceModel] as? (ListItemModelProtocol & MoleculeModelProtocol & AddMolecules), - let moleculesToAdd = model.getRecursiveMoleculesToAdd(), - let indexPath = list.getAdjustedIndexPath(for: model, position: moleculesToAdd.1), - let animation = information?["animation"] as? Int else { return true } - DispatchQueue.main.async { - list.addMolecules(moleculesToAdd.0, indexPath: indexPath, animation: UITableView.RowAnimation(rawValue: animation)) + public func canHandleAction(with model: ActionModelProtocol, additionalData: [AnyHashable : Any]?) -> Bool { + return model.actionType == AddMoleculesActionModel.identifier || model.actionType == RemoveMoleculesActionModel.identifier + } + + public func handleAction(with model: ActionModelProtocol, additionalData: [AnyHashable : Any]?) async throws { + if let model = model as? AddMoleculesActionModel, + let list = delegate?.moleculeListDelegate, + let sourceModel = MVMCoreUIActionHandler.getSourceModel(from: additionalData) as? (ListItemModelProtocol & MoleculeModelProtocol & AddMolecules), + let moleculesToAdd = sourceModel.getRecursiveMoleculesToAdd(), + let indexPath = list.getAdjustedIndexPath(for: sourceModel, position: moleculesToAdd.1) { + await MainActor.run { + list.addMolecules(moleculesToAdd.0, indexPath: indexPath, animation: model.animation) } - return true - } else if actionType == RemoveMoleculesActionModel.identifier { - guard let list = delegate?.moleculeListDelegate, - let model = additionalData?[KeySourceModel] as? (ListItemModelProtocol & MoleculeModelProtocol & RemoveMolecules), - let moleculesToRemove = model.getRecursiveMoleculesToRemove(), - let animation = information?["animation"] as? Int else { return true } - DispatchQueue.main.async { - list.removeMolecules(moleculesToRemove, animation: UITableView.RowAnimation(rawValue: animation)) + } else if let model = model as? RemoveMoleculesActionModel, + let list = delegate?.moleculeListDelegate, + let sourceModel = MVMCoreUIActionHandler.getSourceModel(from: additionalData) as? (ListItemModelProtocol & MoleculeModelProtocol & RemoveMolecules), + let moleculesToRemove = sourceModel.getRecursiveMoleculesToRemove() { + await MainActor.run { + list.removeMolecules(moleculesToRemove, animation: model.animation) } - return true } - return false } } diff --git a/MVMCoreUI/Behaviors/Protocols/PageBehaviorProtocol.swift b/MVMCoreUI/Behaviors/Protocols/PageBehaviorProtocol.swift index 453041c2..3420299d 100644 --- a/MVMCoreUI/Behaviors/Protocols/PageBehaviorProtocol.swift +++ b/MVMCoreUI/Behaviors/Protocols/PageBehaviorProtocol.swift @@ -6,6 +6,8 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // +import MVMCore + public protocol PageBehaviorProtocol: ModelHandlerProtocol { @@ -65,13 +67,14 @@ public protocol PageLocalDataShareBehavior: PageBehaviorProtocol { } public protocol PageCustomActionHandlerBehavior: PageBehaviorProtocol { + + /// Returns if this behavior can handle the action. + func canHandleAction(with model: ActionModelProtocol, additionalData: [AnyHashable : Any]?) -> Bool /// - Parameters: - /// - actionType: The action type of the passed action model. - /// - information: information of the passed action model. + /// - model: The action model. /// - additionalData: Additional information of the - /// - Returns: Boolean determines if the action has been handled. - func handleAction(type actionType: String?, information: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) -> Bool + func handleAction(with model: ActionModelProtocol, additionalData: [AnyHashable : Any]?) async throws } public extension MVMCoreUIDelegateObject { diff --git a/MVMCoreUI/FormUIHelpers/FormValidator.swift b/MVMCoreUI/FormUIHelpers/FormValidator.swift index 3aa50030..ec06542c 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator.swift @@ -95,7 +95,7 @@ import MVMCore groupValid = try validateGroup(group) } catch { if let err = MVMCoreErrorObject.createErrorObject(for: error, location: "FormValidator"){ - MVMCoreLoggingHandler.shared()?.addError(toLog: err) + MVMCoreLoggingHandler.addError(toLog: err) fatalError(err.description) } } diff --git a/MVMCoreUI/Legacy/Adapters/ActionModelAdapter.swift b/MVMCoreUI/Legacy/Adapters/ActionModelAdapter.swift index f88ca5ec..01a7d0e8 100644 --- a/MVMCoreUI/Legacy/Adapters/ActionModelAdapter.swift +++ b/MVMCoreUI/Legacy/Adapters/ActionModelAdapter.swift @@ -9,16 +9,7 @@ public extension Dictionary { func asActionModel() throws -> ActionModelProtocol { - guard let castedSelf = self as? [String: Any] else { - throw ModelRegistry.Error.decoderOther(message: "Dictionary is not of type [String: Any]") - } - guard let actionType = ModelRegistry.getType(for: castedSelf.stringForkey(KeyActionType), with: ActionModelProtocol.self) else { - throw ModelRegistry.Error.decoderErrorModelNotMapped() - } - guard let actionModel = try actionType.decode(jsonDict: castedSelf) as? ActionModelProtocol else { - throw ModelRegistry.Error.decoderOther(message: "Could not decode to ActionModelProtocol") - } - return actionModel + return try MVMCoreActionHandler.createModel(with: self) } - + } diff --git a/MVMCoreUI/MVMCoreUI.h b/MVMCoreUI/MVMCoreUI.h index ce663709..37083fd0 100644 --- a/MVMCoreUI/MVMCoreUI.h +++ b/MVMCoreUI/MVMCoreUI.h @@ -21,7 +21,6 @@ FOUNDATION_EXPORT const unsigned char MVMCoreUIVersionString[]; #import #import #import -#import // Alert Handling #import diff --git a/MVMCoreUI/Managers/SubNav/SubNavManagerController.swift b/MVMCoreUI/Managers/SubNav/SubNavManagerController.swift index f5c4023a..db339cf8 100644 --- a/MVMCoreUI/Managers/SubNav/SubNavManagerController.swift +++ b/MVMCoreUI/Managers/SubNav/SubNavManagerController.swift @@ -145,7 +145,9 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol, open func shouldContinue(toErrorPage loadObject: MVMCoreLoadObject, error: MVMCoreErrorObject?) -> Bool { // Push error screens so they do not replace the tab page. - loadObject.requestParameters?.navigationController = navigationController + MVMCoreDispatchUtility.performSyncBlock(onMainThread: { + loadObject.requestParameters?.navigationController = self.navigationController + }) loadObject.requestParameters?.loadStyle = .push return true } @@ -154,37 +156,32 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol, /// Logs the action for the selected tab. open func trackSelectTab() { - guard let action = tabs.tabsModel?.tabs[tabs.selectedIndex].action, - let json = MVMCoreUIActionHandler.shared()?.convertActionToJSON(action, delegateObject: delegateObjectIVar) else { return } - MVMCoreUIActionHandler.shared()?.logAction(json, additionalData: getAdditionalDataForNewTabLoad(indexPath: IndexPath(row: tabs.selectedIndex, section: 0)), delegateObject: delegateObjectIVar) + guard let action = tabs.tabsModel?.tabs[tabs.selectedIndex].action else { return } + MVMCoreUIActionHandler.shared()?.logAction(with: action.toJSON(), additionalData: getAdditionalDataForNewTabLoad(indexPath: IndexPath(row: tabs.selectedIndex, section: 0)), delegateObject: delegateObjectIVar) } /// Allow override of additioonal data for tab press action open func getAdditionalDataForNewTabLoad(indexPath: IndexPath) -> [AnyHashable: Any]? { - return ["tabBarPressed": true, KeySourceModel: tabsModel] + return MVMCoreUIActionHandler.add(sourceModel: tabsModel, to: ["tabBarPressed": true]) } /// Allows modification of requestParameters object for openPage request - open func getRequestParametersForNewTabLoad(requestParameters: MVMCoreRequestParameters, actionInformation: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) -> MVMCoreRequestParameters { + open func getRequestParametersForNewTabLoad(requestParameters: MVMCoreRequestParameters, model: ActionOpenPageModel, additionalData: [AnyHashable : Any]?) -> MVMCoreRequestParameters { requestParameters.navigationController = subNavigationController requestParameters.loadStyle = .replaceCurrent requestParameters.tabWasPressed = true return requestParameters } - - open override func handleOpenPage(for requestParameters: MVMCoreRequestParameters, actionInformation: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) { - var requestParameters = requestParameters - guard let additionalData = additionalData, + + open override func performAction(with model: ActionModelProtocol, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) async throws { + guard var model = model as? ActionOpenPageModel, + let additionalData = additionalData, additionalData.boolForKey("tabBarPressed") else { - // Pass to delegate - if (viewController as? MVMCoreActionDelegateProtocol)?.handleOpenPage?(for: requestParameters, actionInformation: actionInformation, additionalData: additionalData) == nil { - super.handleOpenPage(for: requestParameters, actionInformation: actionInformation, additionalData: additionalData) - } - return - } + return try await super.performAction(with: model, additionalData: additionalData, delegateObject: delegateObject) + } // Allow opportunity to modify parameters. - requestParameters = getRequestParametersForNewTabLoad(requestParameters: requestParameters, actionInformation: actionInformation, additionalData: additionalData) - super.handleOpenPage(for: requestParameters, actionInformation: actionInformation, additionalData: additionalData) + model.requestParameters = getRequestParametersForNewTabLoad(requestParameters: model.requestParameters, model: model, additionalData: additionalData) + try await super.performAction(with: model, additionalData: additionalData, delegateObject: delegateObject) } // MARK: - MVMCorePresentationDelegateProtocol @@ -279,7 +276,7 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol, } else if let tabsModel = tabs.tabsModel, let action = tabsModel.tabs[indexPath.row].action { // Perform the tab action - MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: getAdditionalDataForNewTabLoad(indexPath: indexPath), delegateObject: delegateObjectIVar) + MVMCoreUIActionHandler.performActionUnstructured(with: action, sourceModel: tabsModel, additionalData: getAdditionalDataForNewTabLoad(indexPath: indexPath), delegateObject: delegateObject()) } return false } diff --git a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift index ef051b88..061c67fb 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift @@ -228,12 +228,14 @@ open class CoreUIModelMapping: ModelMapping { open override class func registerActions() { super.registerActions() - ModelRegistry.register(ActionPopupModel.self) - ModelRegistry.register(ActionAlertModel.self) - ModelRegistry.register(ActionTopAlertModel.self) - ModelRegistry.register(ActionCollapseNotificationModel.self) - ModelRegistry.register(ActionOpenPanelModel.self) - ModelRegistry.register(ActionTopNotificationModel.self) + ModelRegistry.register(handler: ActionPopupHandler.self, for: ActionPopupModel.self) + ModelRegistry.register(handler: ActionAlertHandler.self, for: ActionAlertModel.self) + ModelRegistry.register(handler: ActionTopAlertHandler.self, for: ActionTopAlertModel.self) + ModelRegistry.register(handler: ActionCollapseNotificationHandler.self, for: ActionCollapseNotificationModel.self) + ModelRegistry.register(handler: ActionDismissNotificationHandler.self, for: ActionDismissNotificationModel.self) + ModelRegistry.register(handler: ActionOpenPanelHandler.self, for: ActionOpenPanelModel.self) + ModelRegistry.register(handler: ActionTopNotificationHandler.self, for: ActionTopNotificationModel.self) + ModelRegistry.register(handler: MVMCoreUIActionOpenPageHandler.self, for: ActionOpenPageModel.self, allowsReplace: true) } open class func registerRules() { diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.h b/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.h deleted file mode 100644 index 60a24646..00000000 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// MVMCoreUIActionHandler.h -// MVMCoreUI -// -// Created by Scott Pfeil on 10/28/20. -// Copyright © 2020 Verizon Wireless. All rights reserved. -// - -@import MVMCore.MVMCoreActionHandler; - -NS_ASSUME_NONNULL_BEGIN - -@interface MVMCoreUIActionHandler : MVMCoreActionHandler - -// Shows a popup alert by grabbing the content from the page map. -- (void)popupAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; - -// Shows popup alert from the alert object in the action map. The actionType of the action is 'alert' -- (void)showAlert:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; - -// Shows a top alert -- (void)topAlertAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; - -// Collapses the current top notification -- (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; - -/// Legacy in app safari webview load -- (void)openURLInSafariWebView:(nonnull NSURL *)url; - -@end - -NS_ASSUME_NONNULL_END diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.m b/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.m deleted file mode 100644 index 9425547c..00000000 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.m +++ /dev/null @@ -1,131 +0,0 @@ -// -// MVMCoreUIActionHandler.m -// MVMCoreUI -// -// Created by Scott Pfeil on 10/28/20. -// Copyright © 2020 Verizon Wireless. All rights reserved. -// - -#import "MVMCoreUIActionHandler.h" -#import "MVMCoreUIConstants.h" -#import "MVMCoreAlertObject.h" -#import -#import -@import MVMCore.MVMCoreActionHandler; -@import MVMCore.NSDictionary_MFConvenience; -@import MVMCore.MVMCoreJSONConstants; -@import MVMCore.MVMCoreCache; -@import SafariServices; - -@implementation MVMCoreUIActionHandler - -- (BOOL)handleOtherActions:(NSString *)actionType actionInformation:(NSDictionary *)actionInformation additionalData:(NSDictionary *)additionalData delegateObject:(DelegateObject *)delegateObject { - if ([actionType isEqualToString:KeyActionTypePopup]) { - [self popupAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; - return YES; - } else if ([actionType isEqualToString:KeyActionTypeTopAlert]) { - [self topAlertAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; - return YES; - } else if ([actionType isEqualToString:KeyActionTypeCollapseNotification]) { - [self collapseNotificationAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; - return YES; - } else if ([actionType isEqualToString:KeyActionTypeAlert]) { - [self showAlert:actionInformation additionalData:additionalData delegateObject:delegateObject]; - return YES; - } else if ([actionType isEqualToString:KeyActionTypeTopNotification]) { - [self topNotificationAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; - return YES; - } - return [super handleOtherActions:actionType actionInformation:actionInformation additionalData:additionalData delegateObject:delegateObject]; -} - -- (void)openPageAction:(NSDictionary *)actionInformation additionalData:(NSDictionary *)additionalData delegateObject:(DelegateObject *)delegateObject { - // Cleanup the source model data to prevent it from being accidentally auto-forwarded in openPage network requests by blind additionalData insertions. (https://onejira.verizon.com/browse/CXTDT-135642, https://onejira.verizon.com/browse/CXTDT-136001). - if (additionalData[KeySourceModel]) { - NSMutableDictionary *cleanedData = [additionalData mutableCopy]; - [cleanedData removeObjectForKey:KeySourceModel]; - additionalData = cleanedData; - } - [super openPageAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; -} - -- (void)popupAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { - // Perform a popup. - NSString *pageTypeForPopup = [actionInformation stringForKey:KeyPageType]; - [[MVMCoreCache sharedCache] fetchJSONForPageType:pageTypeForPopup queue:nil waitUntilFinished:YES completionHandler:^(NSDictionary * _Nullable jsonDictionary) { - - MVMCoreErrorObject *error = nil; - MVMCoreAlertObject *alertObject = [MVMCoreAlertObject alertObjectWithPage:jsonDictionary isGreedy:NO additionalData:additionalData delegateObject:delegateObject error:&error]; - if ([delegateObject.actionDelegate respondsToSelector:@selector(willShowPopupWithAlertObject:alertJson:)]) { - [((id )delegateObject.actionDelegate) willShowPopupWithAlertObject:alertObject alertJson:jsonDictionary]; - } - - if (alertObject) { - [[MVMCoreAlertHandler sharedAlertHandler] showAlertWithAlertObject:alertObject]; - } else { - [self handleActionError:error actionInformation:actionInformation additionalData:additionalData delegateObject:delegateObject ]; - } - }]; -} - -- (void)showAlert:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { - - MVMCoreErrorObject *error = nil; - MVMCoreAlertObject *alertObject = [MVMCoreAlertObject alertObjectWithAction:actionInformation additionalData:additionalData delegateObject:delegateObject error:&error]; - if ([delegateObject.actionDelegate respondsToSelector:@selector(willShowPopupWithAlertObject:alertJson:)]) { - [((id )delegateObject.actionDelegate) willShowPopupWithAlertObject:alertObject alertJson:actionInformation]; - } - - if (alertObject) { - [[MVMCoreAlertHandler sharedAlertHandler] showAlertWithAlertObject:alertObject]; - } else { - [self handleActionError:error actionInformation:actionInformation additionalData:additionalData delegateObject:delegateObject]; - } -} - -- (void)topAlertAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { - // Perform a top alert. - NSString *pageTypeForTopAlert = [actionInformation stringForKey:KeyPageType]; - [[MVMCoreCache sharedCache] fetchJSONForPageType:pageTypeForTopAlert queue:nil waitUntilFinished:YES completionHandler:^(NSDictionary * _Nullable jsonDictionary) { - - NSDictionary *responseInfo = [jsonDictionary dict:KeyResponseInfo]; - if (responseInfo) { - MVMCoreAlertObject *alertObject = [MVMCoreAlertObject alertObjectForPageType:pageTypeForTopAlert responseInfo:responseInfo additionalData:additionalData delegateObject:delegateObject]; - if ([delegateObject.actionDelegate respondsToSelector:@selector(willShowTopAlertWithAlertObject:alertJson:)]) { - [((id )delegateObject.actionDelegate) willShowTopAlertWithAlertObject:alertObject alertJson:actionInformation]; - } - [alertObject showAlert]; - } - }]; -} - -- (void)collapseNotificationAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { - // Collapse the current notification. - if ([[CoreUIObject sharedInstance].globalTopAlertDelegate respondsToSelector:@selector(getTopAlertView)]) { - [[[CoreUIObject sharedInstance].globalTopAlertDelegate getTopAlertView] collapseNotification]; - } -} - -- (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 { - [super defaultHandleActionError:error additionalData:additionalData]; - if (!error.silentError) { - - // Show alert - [MVMCoreDispatchUtility performBlockOnMainThread:^{ - MVMCoreAlertObject *alertObject = [[MVMCoreAlertObject alloc] initPopupAlertWithError:error isGreedy:NO]; - [[MVMCoreAlertHandler sharedAlertHandler] showAlertWithAlertObject:alertObject]; - }]; - } -} - -- (void)openURLInSafariWebView:(nonnull NSURL *)url { - SFSafariViewController *safariViewController = [[SFSafariViewController alloc] initWithURL:url]; - [[MVMCoreNavigationHandler sharedNavigationHandler] presentViewController:safariViewController animated:YES]; -} - -@end diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.swift b/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.swift new file mode 100644 index 00000000..24d57f8d --- /dev/null +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.swift @@ -0,0 +1,61 @@ +// +// MVMCoreUIActionHandler.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 7/18/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore +import SafariServices + +@objc open class MVMCoreUIActionHandler: MVMCoreActionHandler { + + /// Adds the source model to the additionalData + public static func add(sourceModel: MoleculeModelProtocol?, to additionalData: [AnyHashable: Any]?) -> [AnyHashable: Any]? { + guard let sourceModel = sourceModel else { return additionalData } + return additionalData.dictionaryAdding(key: KeySourceModel, value: sourceModel) + } + + /// Gets the source model from the additionalData + public static func getSourceModel(from additionalData: [AnyHashable: Any]?) -> MoleculeModelProtocol? { + return additionalData?[KeySourceModel] as? MoleculeModelProtocol + } + + /// Removes the source model to the additionalData + public static func removeSourceModel(from additionalData: [AnyHashable: Any]?) -> [AnyHashable: Any]? { + guard var additionalData = additionalData else { return nil } + additionalData.removeValue(forKey: KeySourceModel) + return additionalData + } + + /// Starts the action in a task, adding the source model. + public static func performActionUnstructured(with model: ActionModelProtocol, sourceModel: MoleculeModelProtocol? = nil, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { + Task(priority: .userInitiated) { + try await (delegateObject?.actionDelegate as? ActionDelegateProtocol)?.performAction(with: model, additionalData: MVMCoreUIActionHandler.add(sourceModel: sourceModel, to: additionalData), delegateObject: delegateObject) + } + } + + /// Logs the error and shows a popup if the error is not silent. + open override func defaultHandleActionError(_ error: MVMCoreErrorObject, additionalData: [AnyHashable : Any]?) { + super.defaultHandleActionError(error, additionalData: additionalData) + guard !error.silentError else { return } + Task(priority: .userInitiated) { @MainActor in + let alertObject = MVMCoreAlertObject.init(popupAlertWithError: error, isGreedy: false)! + MVMCoreAlertHandler.shared()?.showAlert(with: alertObject) + } + } + + open override func logAction(with JSON: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { + // Legacy logging. + delegateObject?.actionDelegate?.logAction?(withActionInformation: JSON, additionalData: additionalData) ?? MVMCoreUILoggingHandler.shared()?.defaultLogAction(forController: nil, actionInformation: JSON, additionalData: additionalData) + } + + @objc(openURLInSafariWebView:) + @MainActor + open func openURL(inSafariWebView url: URL) { + let safariViewController = SFSafariViewController(url: url) + MVMCoreNavigationHandler.shared()?.present(safariViewController, animated: true) + } +} diff --git a/MVMCoreUI/TopAlert/MVMCoreTopAlertAnimationDelegateProtocol.h b/MVMCoreUI/TopAlert/MVMCoreTopAlertAnimationDelegateProtocol.h index bb9f6dfc..52922df4 100644 --- a/MVMCoreUI/TopAlert/MVMCoreTopAlertAnimationDelegateProtocol.h +++ b/MVMCoreUI/TopAlert/MVMCoreTopAlertAnimationDelegateProtocol.h @@ -16,7 +16,10 @@ // Called when the top alert is ending an animation - (void)topAlertViewFinishAnimation; -// Called when the top alert close button was pressed by the user -- (void)topAlertCloseButtonPressed; +// Called when the top alert is starting to dismiss. +- (void)topAlertWillDismiss; + +// Called when the top alert is dismissed. +- (void)topAlertDismissed; @end diff --git a/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m b/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m index 7a57f989..f0011719 100644 --- a/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m +++ b/MVMCoreUI/TopAlert/MVMCoreTopAlertOperation.m @@ -183,11 +183,11 @@ if (self.isCancelled) { // Cancelled, dismiss immediately. - [self dismissAlertView:YES forceful:YES]; + [self dismissAlertView:YES]; } else if (self.isPaused) { // Paused, dismiss for the time being if persistent. - [self dismissAlertView:!self.topAlertObject.persistent forceful:YES]; + [self dismissAlertView:YES]; } else { [self updateDismissTimer]; } @@ -223,7 +223,7 @@ if (weakSelf.isFinished || [weakSelf checkAndHandleForCancellation]) { return; } - [weakSelf dismissAlertView:YES forceful:NO]; + [weakSelf dismissAlertView:NO]; }); dispatch_resume(self.timerSource); } @@ -235,27 +235,21 @@ if (!self.isAnimating) { if (self.isDisplayed) { - [self dismissAlertView:YES forceful:YES]; + [self dismissAlertView:YES]; } else if (self.isExecuting) { [self markAsFinished]; } } } -- (void)dismissAlertView:(BOOL)andFinish forceful:(BOOL)forceful { +- (void)dismissAlertView:(BOOL)forceful { if (self.timerSource) { dispatch_source_cancel(self.timerSource); } if (self.isDisplayed && !self.isAnimating) { // Dismisses. - [[[CoreUIObject sharedInstance].globalTopAlertDelegate getTopAlertView] hideAlertView:forceful completionHandler:^(BOOL finished) { - - self.displayed = NO; - if (andFinish) { - [self markAsFinished]; - } - }]; + [[[CoreUIObject sharedInstance].globalTopAlertDelegate getTopAlertView] hideAlertView:forceful completionHandler:NULL]; } } @@ -266,7 +260,7 @@ self.paused = YES; [self didChangeValueForKey:@"isPaused"]; } - [self dismissAlertView:!self.topAlertObject.persistent forceful:YES]; + [self dismissAlertView:YES]; } - (void)unpause { @@ -291,8 +285,20 @@ self.animating = NO; } +- (void)topAlertDismissed { + self.displayed = NO; + if (!self.isCancelled && self.isPaused && self.topAlertObject.persistent) { return; } + [self markAsFinished]; +} + +- (void)topAlertWillDismiss { + if (self.timerSource) { + dispatch_source_cancel(self.timerSource); + } +} + - (void)topAlertCloseButtonPressed { - [self dismissAlertView:YES forceful:YES]; + [self dismissAlertView:YES]; } - (id)copyWithZone:(nullable NSZone *)zone { diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertBaseView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertBaseView.m index 174b1bfb..862e4b5b 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertBaseView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertBaseView.m @@ -7,7 +7,6 @@ // #import "MVMCoreUITopAlertBaseView.h" -@import MVMCore.MVMCoreActionHandler; #import "MVMCoreUISplitViewController.h" @import MVMCore.MVMCoreLoadObject; @import MVMCore.MVMCoreRequestParameters; @@ -33,7 +32,7 @@ } if (performAction) { - [[MVMCoreActionHandler sharedActionHandler] handleActionWithDictionary:actionMap additionalData:additionalData delegateObject:[MVMCoreUIDelegateObject createWithDelegateForAll:[MVMCoreUISession sharedGlobal].topAlertView]]; + [[MVMCoreUIActionHandler sharedActionHandler] handleActionWithDictionary:actionMap additionalData:additionalData delegateObject:[MVMCoreUIDelegateObject createWithDelegateForAll:[MVMCoreUISession sharedGlobal].topAlertView]]; } }]; } @@ -71,11 +70,7 @@ - (nonnull Button *)addCloseButtonWithAnimationDelegate:(nullable id )animationDelegate { Button *closeButton = [MVMCoreUICommonViewsUtility addCloseButtonTo:self action:^(Button * _Nonnull button) { - if (animationDelegate) { - [animationDelegate topAlertCloseButtonPressed]; - } else { - [[MVMCoreUISession sharedGlobal].topAlertView hideAlertView:YES completionHandler:nil]; - } + [[MVMCoreUIActionHandler sharedActionHandler] handleActionWithDictionary:@{KeyActionType: @"dismissNotification"} additionalData:nil delegateObject:nil]; } centeredVertically:YES]; [closeButton.heightAnchor constraintEqualToConstant:16.0].active = YES; [closeButton.widthAnchor constraintEqualToConstant:16.0].active = YES; diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift index 46765385..0ae1e879 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView+Extension.swift @@ -7,6 +7,7 @@ // import Foundation +import MVMCore /// Allows top alerts to determine the status bar color and style. protocol StatusBarUI { @@ -47,7 +48,7 @@ public extension MVMCoreUITopAlertView { return try TopNotificationModel.decode(json: json, delegateObject: delegateObject) } catch { if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") { - MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject) + MVMCoreUILoggingHandler.addError(toLog: errorObject) } return nil } @@ -96,7 +97,7 @@ public extension MVMCoreUITopAlertView { let molecule = currentAlert as? MoleculeViewProtocol else { // Log that we couldn't update. if let errorObject = MVMCoreErrorObject(title: nil, message: nil, messageToLog: nil, code: ErrorCode.parsingJSON.rawValue, domain: ErrorDomainNative, location: "TopNotification update \(String(describing: topAlertObject.type))") { - MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject) + MVMCoreUILoggingHandler.addError(toLog: errorObject) } return } @@ -132,9 +133,11 @@ public extension MVMCoreUITopAlertView { return molecule } catch { if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") { - MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject) + MVMCoreUILoggingHandler.addError(toLog: errorObject) } return nil } } } + +extension MVMCoreUITopAlertView: ActionDelegateProtocol {} diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m index c0835541..292d9132 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertView.m @@ -22,7 +22,6 @@ @import MVMCore.MVMCoreNavigationHandler; @import MVMCore.MVMCoreBlockOperation; #import -@import MVMCore.MVMCoreActionHandler; #import @import MVMCore.NSDictionary_MFConvenience; @import MVMCore.MVMCoreRequestParameters; @@ -227,6 +226,7 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; __weak typeof(self) weakSelf = self; MVMCoreBlockOperation *operation = [MVMCoreBlockOperation blockOperationWithBlock:^(MVMCoreBlockOperation * _Nonnull operation) { [MVMCoreDispatchUtility performBlockOnMainThread:^{ + [weakSelf.animationDelegate topAlertWillDismiss]; [weakSelf.animationDelegate topAlertViewBeginAnimation]; // accessibility - below line added to notify VI user through voiceover user when the top alert is closed @@ -252,6 +252,7 @@ NSString * const MFAccTopAlertClosed = @"Top alert notification is closed."; if (completionHandler) { completionHandler(finished); } + [weakSelf.animationDelegate topAlertDismissed]; weakSelf.topAlertObject = nil; if (weakSelf.currentAlertOverridingStatusBar) { weakSelf.currentAlertOverridingStatusBar = NO;