diff --git a/novawallet.xcodeproj/project.pbxproj b/novawallet.xcodeproj/project.pbxproj index 41607172e8..06e508e781 100644 --- a/novawallet.xcodeproj/project.pbxproj +++ b/novawallet.xcodeproj/project.pbxproj @@ -1439,7 +1439,6 @@ 0F5539A29F404F98DF6B2463 /* DAppAuthConfirmViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 301287CBBF23EF58186A7BB5 /* DAppAuthConfirmViewLayout.swift */; }; 0FB3F446C7DA3A1394EF94D5 /* BannersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A00DD977BD9CE066B1B11569 /* BannersViewController.swift */; }; 0FB6781AB0186A1ED474CAD6 /* StakingUnbondConfirmProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD348E749EC6A7E3BB069DE /* StakingUnbondConfirmProtocols.swift */; }; - 0FD7DB6BDCD695E9D9B51615 /* AHMInfoWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A133C3748C74C128BA7846 /* AHMInfoWireframe.swift */; }; 103394F7AC51A1F1768ED8C7 /* ManualBackupWalletListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF28F01EC3319611F744863 /* ManualBackupWalletListViewController.swift */; }; 106CC4BFC48B6BFFF31434A9 /* LedgerWalletConfirmPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BC1402B34E341312ABB378 /* LedgerWalletConfirmPresenter.swift */; }; 109512489F8CB32C2430808E /* ManualBackupKeyListViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29CD41F03830F4967EF06F91 /* ManualBackupKeyListViewLayout.swift */; }; @@ -1726,7 +1725,6 @@ 2D21BD3F2D675D2800B1C480 /* AssetPriceChartDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D21BD3E2D675D2800B1C480 /* AssetPriceChartDataSource.swift */; }; 2D21BD412D6760CC00B1C480 /* AssetPriceChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D21BD402D6760CC00B1C480 /* AssetPriceChart.swift */; }; 2D21BD432D67964400B1C480 /* PriceChartDataOperationFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D21BD422D67964400B1C480 /* PriceChartDataOperationFactory.swift */; }; - 2D259A9AA172D8DDADE58FBD /* AHMInfoInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D65767C939FE2ACAF80E2B5 /* AHMInfoInteractor.swift */; }; 2D2B6AEB2D6C7581002F8C77 /* AssetDetailsBalanceWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D2B6AEA2D6C7581002F8C77 /* AssetDetailsBalanceWidget.swift */; }; 2D2B6AED2D6C8597002F8C77 /* StackTitleValueIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D2B6AEC2D6C8597002F8C77 /* StackTitleValueIconView.swift */; }; 2D2B6AEF2D6DB77E002F8C77 /* BrowserNavigationProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D2B6AEE2D6DB77E002F8C77 /* BrowserNavigationProtocols.swift */; }; @@ -2086,6 +2084,25 @@ 2D96D86F2DF04FF700159E91 /* DelegatedAccountsUpdateViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D96D8672DF04FF700159E91 /* DelegatedAccountsUpdateViewLayout.swift */; }; 2D96D8702DF04FF700159E91 /* DelegatedAccountsUpdateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D96D8652DF04FF700159E91 /* DelegatedAccountsUpdateViewController.swift */; }; 2D96D8712DF04FF700159E91 /* DelegatedAccountsUpdateInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D96D8622DF04FF700159E91 /* DelegatedAccountsUpdateInteractor.swift */; }; + 2D98355B2F2773B200B184D4 /* InfoPopupProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D98355A2F2773B200B184D4 /* InfoPopupProtocols.swift */; }; + 2D9835632F2773CE00B184D4 /* InfoPopupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9835622F2773CE00B184D4 /* InfoPopupViewController.swift */; }; + 2D98356A2F2773E400B184D4 /* InfoPopupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9835692F2773E400B184D4 /* InfoPopupViewModel.swift */; }; + 2D9835712F27740800B184D4 /* InfoPopupViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9835702F27740800B184D4 /* InfoPopupViewLayout.swift */; }; + 2D9835782F27742600B184D4 /* InfoPopupPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9835772F27742600B184D4 /* InfoPopupPresenter.swift */; }; + 2D98357C2F27743800B184D4 /* InfoPopupInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D98357B2F27743800B184D4 /* InfoPopupInteractor.swift */; }; + 2D9835842F2774B500B184D4 /* InfoPopupContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9835832F2774B500B184D4 /* InfoPopupContent.swift */; }; + 2D9835892F2774D200B184D4 /* InfoPopupWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9835882F2774D200B184D4 /* InfoPopupWireframe.swift */; }; + 2D9835902F27750500B184D4 /* InfoPopupAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D98358F2F27750500B184D4 /* InfoPopupAction.swift */; }; + 2D98359C2F27754B00B184D4 /* InfoPopupContentBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D98359B2F27754B00B184D4 /* InfoPopupContentBuilder.swift */; }; + 2D9835AB2F28040F00B184D4 /* AHMInfoPopupInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9835AA2F28040F00B184D4 /* AHMInfoPopupInteractor.swift */; }; + 2D9835B52F2805FD00B184D4 /* AHMInfoPopupPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9835B42F2805FD00B184D4 /* AHMInfoPopupPresenter.swift */; }; + 2D9835BA2F28061200B184D4 /* AHMInfoPopupProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9835B92F28061200B184D4 /* AHMInfoPopupProtocols.swift */; }; + 2D9835BF2F28062700B184D4 /* AHMInfoPopupViewModelFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9835BE2F28062700B184D4 /* AHMInfoPopupViewModelFactory.swift */; }; + 2D9835C62F28067E00B184D4 /* AHMInfoPopupViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9835C52F28067E00B184D4 /* AHMInfoPopupViewFactory.swift */; }; + 2D9835CC2F280BDD00B184D4 /* ASMInfoPopupPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9835CB2F280BDD00B184D4 /* ASMInfoPopupPresenter.swift */; }; + 2D9835D42F280D3500B184D4 /* ASMInfoPopupInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9835D32F280D3500B184D4 /* ASMInfoPopupInteractor.swift */; }; + 2D9835D92F280D7100B184D4 /* ASMInfoPopupViewModelFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9835D82F280D7100B184D4 /* ASMInfoPopupViewModelFactory.swift */; }; + 2D9835E02F28113800B184D4 /* ASMInfoViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9835DF2F28113800B184D4 /* ASMInfoViewFactory.swift */; }; 2D9835F42F28139F00B184D4 /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = 2D9835F32F28139F00B184D4 /* AppIcon.icon */; }; 2D9837A02F310CE000B184D4 /* WOScamAlertSheetProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D98379B2F310CE000B184D4 /* WOScamAlertSheetProtocols.swift */; }; 2D9837A12F310CE000B184D4 /* WOScamAlertSheetViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D98379E2F310CE000B184D4 /* WOScamAlertSheetViewLayout.swift */; }; @@ -2210,8 +2227,6 @@ 2DC002C52E840DC700D49A42 /* ImageSecureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC002C42E840DC700D49A42 /* ImageSecureView.swift */; }; 2DC002C72E840DF600D49A42 /* AssetListNftSecureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC002C62E840DF600D49A42 /* AssetListNftSecureView.swift */; }; 2DC002C92E840E2D00D49A42 /* HideSecureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC002C82E840E2D00D49A42 /* HideSecureView.swift */; }; - 2DC002CF2E867BB500D49A42 /* AHMInfoViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC002CE2E867BB500D49A42 /* AHMInfoViewModel.swift */; }; - 2DC002D22E867C1600D49A42 /* AHMInfoViewModelFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC002D12E867C1600D49A42 /* AHMInfoViewModelFactory.swift */; }; 2DC002D52E867CCD00D49A42 /* AHMRemoteData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC002D42E867CCD00D49A42 /* AHMRemoteData.swift */; }; 2DC002D72E869D2000D49A42 /* AHMNavigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC002D62E869D2000D49A42 /* AHMNavigation.swift */; }; 2DC002D92E869FD700D49A42 /* OpenAHMUrlParsingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC002D82E869FD700D49A42 /* OpenAHMUrlParsingService.swift */; }; @@ -2219,7 +2234,7 @@ 2DC002DE2E86D27D00D49A42 /* ObservableInMemoryCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC002DD2E86D27D00D49A42 /* ObservableInMemoryCache.swift */; }; 2DC002DF2E86D27D00D49A42 /* ObservableInMemoryCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC002DD2E86D27D00D49A42 /* ObservableInMemoryCache.swift */; }; 2DC002E12E86D59900D49A42 /* AHMInfoRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC002E02E86D59900D49A42 /* AHMInfoRepository.swift */; }; - 2DC002E32E895DB200D49A42 /* AHMInfoFetchOeprationFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC002E22E895DB200D49A42 /* AHMInfoFetchOeprationFactory.swift */; }; + 2DC002E32E895DB200D49A42 /* AHMInfoFetchOperationFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC002E22E895DB200D49A42 /* AHMInfoFetchOperationFactory.swift */; }; 2DC002E52E8A56AC00D49A42 /* PreSyncServiceCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC002E42E8A56AC00D49A42 /* PreSyncServiceCoordinator.swift */; }; 2DC002E72E8A5A0000D49A42 /* AHMInfoService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC002E62E8A5A0000D49A42 /* AHMInfoService.swift */; }; 2DC002E92E8A6E4500D49A42 /* AHMInfoShownChains.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC002E82E8A6E4500D49A42 /* AHMInfoShownChains.swift */; }; @@ -2839,7 +2854,6 @@ 57E20F0723C4748D576C4882 /* StakingUnbondSetupViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A82E373FFFBF708D7CF0973E /* StakingUnbondSetupViewFactory.swift */; }; 58647872EA51556AC0791A3E /* ExportInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F83F91EC9B29C21D087D869 /* ExportInteractor.swift */; }; 5869563D0EA593FBD02C169C /* StakingPayoutConfirmationProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52F8D055D0481469073AA859 /* StakingPayoutConfirmationProtocols.swift */; }; - 58BAD91930AB3BEC2C4121AD /* AHMInfoViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8487FC52E560180302B91222 /* AHMInfoViewLayout.swift */; }; 58D5B4F17DA37C241FF96A5F /* ParaStkRebondViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FDF5963FA924F8C815F3BCF /* ParaStkRebondViewController.swift */; }; 58F385F41D42CC96373EDA42 /* TokensManageProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05AA59AFC801F52B79DDBBCF /* TokensManageProtocols.swift */; }; 58F693958EF69F59D7C9760E /* StakingRewardPayoutsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4191E0055768541F6A3D8A61 /* StakingRewardPayoutsInteractor.swift */; }; @@ -5506,7 +5520,6 @@ 85600C63BB1BEC76EDFA04CB /* ReferendumFullDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C535E8504943299B3E4A8EB /* ReferendumFullDetailsViewController.swift */; }; 8582395FEF296771447439FF /* AssetsSearchWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6746DDB8F277A968E6B25332 /* AssetsSearchWireframe.swift */; }; 85A093F6055DDD2E2E9253F2 /* ControllerAccountProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = F829E7F8B39EE7D977001510 /* ControllerAccountProtocols.swift */; }; - 85E6DA35DF2D2C4C73FBE988 /* AHMInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DA7291E053BF3B6F78990C /* AHMInfoViewController.swift */; }; 8614F070CD3DEEA3D41B3635 /* DAppBrowserTabListViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3861E52C532B8825313B7F9 /* DAppBrowserTabListViewLayout.swift */; }; 86EB789787B731691B36C827 /* OnChainTransferSetupPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1A2F7E5E278FDCC89FE097 /* OnChainTransferSetupPresenter.swift */; }; 873FAB6E5CAD1FD4D02737D0 /* NominationPoolBondMoreSetupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0C9473AB7D1FE4A27403078 /* NominationPoolBondMoreSetupViewController.swift */; }; @@ -5846,7 +5859,6 @@ 94B234EE404088B077DB6411 /* DAppListProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10F3CCA5BA57C68D5AE2B42F /* DAppListProtocols.swift */; }; 94C5557889830B223206A937 /* GenericLedgerWalletInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCDB2256863350F2236C2C0 /* GenericLedgerWalletInteractor.swift */; }; 9550165DF9EF3C32A63FC581 /* NetworkNodeWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D7528CB09D28F392C10EBC /* NetworkNodeWireframe.swift */; }; - 955209300EF9643EC1241B12 /* AHMInfoProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FCC7D490906498F89DAE535 /* AHMInfoProtocols.swift */; }; 9565BEB636E6D386B0C0FBE5 /* StakingPayoutConfirmationViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BE0492B98AB9C1540846B39 /* StakingPayoutConfirmationViewFactory.swift */; }; 957A4EA7C87AEC2CF24B53D2 /* CrowdloanUnlockInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25847C6FB20218256A38D488 /* CrowdloanUnlockInteractor.swift */; }; 95AF91994555227D52FCDA24 /* ParitySignerTxQrPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F309CF5A47A66E0D55B2CCD /* ParitySignerTxQrPresenter.swift */; }; @@ -5898,7 +5910,6 @@ 9F2AC903E85C0C63FBFD22B9 /* GiftPrepareShareViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53E1F934970CFFAB1FE9F4E /* GiftPrepareShareViewLayout.swift */; }; 9F3E2D64D77BF89B474BF1E3 /* DAppOperationConfirmViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FBC2EA83A121CEBD25549D /* DAppOperationConfirmViewController.swift */; }; A0128EBC581EFB57B7014422 /* WalletMigrateAcceptViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = D99E687F7D5A3862643BF533 /* WalletMigrateAcceptViewLayout.swift */; }; - A0345DBFA840D7355DB20171 /* AHMInfoViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377CD0A9ABDBDFCA07B6DB5C /* AHMInfoViewFactory.swift */; }; A05C2B3458F12EFE1633D917 /* MarkdownDescriptionPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAEF44ADECD66B49E3430365 /* MarkdownDescriptionPresenter.swift */; }; A05E466D47CCB956E318A39F /* StakingDashboardViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E807E9E12A130C50E8FFDF /* StakingDashboardViewFactory.swift */; }; A07A987DE3047AF1A786D511 /* DAppListViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11BB49F165F64A7EF6418EB4 /* DAppListViewLayout.swift */; }; @@ -6178,7 +6189,6 @@ C39621E998D1DE526DF3C20A /* AssetOperationNetworkListProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 699926F12D454980392BAF03 /* AssetOperationNetworkListProtocols.swift */; }; C39CB534B2EE7ABD6D6085BA /* TransactionHistoryViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A937F85FE35340EDA131C2EC /* TransactionHistoryViewFactory.swift */; }; C3D21C269EC7B9A2C8A5E5BE /* BackupMnemonicCardWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC95296F1B2515142FEA822 /* BackupMnemonicCardWireframe.swift */; }; - C4196AA0781D276DA03C1042 /* AHMInfoPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BC3ECEE9326CFA2A15D2457 /* AHMInfoPresenter.swift */; }; C49AC521056CBDB5451B1CDC /* NominationPoolBondMoreBaseInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84033496FC259BCA5420D52B /* NominationPoolBondMoreBaseInteractor.swift */; }; C4A4D40A08DAB4A71C21C1A8 /* StakingRedeemInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 560C48D7A83F51F001622D71 /* StakingRedeemInteractor.swift */; }; C4D962BDC211A38CA3536425 /* StakingProxyManagementProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE5C4D40180946CA35A0E2F8 /* StakingProxyManagementProtocols.swift */; }; @@ -8025,7 +8035,6 @@ 0E52C5919A1DA8A18297325B /* StakingConfirmProxyViewFactory.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = StakingConfirmProxyViewFactory.swift; sourceTree = ""; }; 0EC18369BDAF9076681B6E3F /* InAppUpdatesWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = InAppUpdatesWireframe.swift; sourceTree = ""; }; 0FA94E5B74194AABA482F2FD /* WalletConnectSessionDetailsProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = WalletConnectSessionDetailsProtocols.swift; sourceTree = ""; }; - 0FCC7D490906498F89DAE535 /* AHMInfoProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AHMInfoProtocols.swift; sourceTree = ""; }; 1039AA3654461114FBB86844 /* DAppTxDetailsPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DAppTxDetailsPresenter.swift; sourceTree = ""; }; 1090AF9D9F18846E5A73F73C /* NominationPoolSearchPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = NominationPoolSearchPresenter.swift; sourceTree = ""; }; 10B808DF32AF89255DD5B0CB /* AppearanceSettingsPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AppearanceSettingsPresenter.swift; sourceTree = ""; }; @@ -8678,6 +8687,25 @@ 2D96D8662DF04FF700159E91 /* DelegatedAccountsUpdateViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelegatedAccountsUpdateViewFactory.swift; sourceTree = ""; }; 2D96D8672DF04FF700159E91 /* DelegatedAccountsUpdateViewLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelegatedAccountsUpdateViewLayout.swift; sourceTree = ""; }; 2D96D8682DF04FF700159E91 /* DelegatedAccountsUpdateWireframe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelegatedAccountsUpdateWireframe.swift; sourceTree = ""; }; + 2D98355A2F2773B200B184D4 /* InfoPopupProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPopupProtocols.swift; sourceTree = ""; }; + 2D9835622F2773CE00B184D4 /* InfoPopupViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPopupViewController.swift; sourceTree = ""; }; + 2D9835692F2773E400B184D4 /* InfoPopupViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPopupViewModel.swift; sourceTree = ""; }; + 2D9835702F27740800B184D4 /* InfoPopupViewLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPopupViewLayout.swift; sourceTree = ""; }; + 2D9835772F27742600B184D4 /* InfoPopupPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPopupPresenter.swift; sourceTree = ""; }; + 2D98357B2F27743800B184D4 /* InfoPopupInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPopupInteractor.swift; sourceTree = ""; }; + 2D9835832F2774B500B184D4 /* InfoPopupContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPopupContent.swift; sourceTree = ""; }; + 2D9835882F2774D200B184D4 /* InfoPopupWireframe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPopupWireframe.swift; sourceTree = ""; }; + 2D98358F2F27750500B184D4 /* InfoPopupAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPopupAction.swift; sourceTree = ""; }; + 2D98359B2F27754B00B184D4 /* InfoPopupContentBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPopupContentBuilder.swift; sourceTree = ""; }; + 2D9835AA2F28040F00B184D4 /* AHMInfoPopupInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AHMInfoPopupInteractor.swift; sourceTree = ""; }; + 2D9835B42F2805FD00B184D4 /* AHMInfoPopupPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AHMInfoPopupPresenter.swift; sourceTree = ""; }; + 2D9835B92F28061200B184D4 /* AHMInfoPopupProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AHMInfoPopupProtocols.swift; sourceTree = ""; }; + 2D9835BE2F28062700B184D4 /* AHMInfoPopupViewModelFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AHMInfoPopupViewModelFactory.swift; sourceTree = ""; }; + 2D9835C52F28067E00B184D4 /* AHMInfoPopupViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AHMInfoPopupViewFactory.swift; sourceTree = ""; }; + 2D9835CB2F280BDD00B184D4 /* ASMInfoPopupPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASMInfoPopupPresenter.swift; sourceTree = ""; }; + 2D9835D32F280D3500B184D4 /* ASMInfoPopupInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASMInfoPopupInteractor.swift; sourceTree = ""; }; + 2D9835D82F280D7100B184D4 /* ASMInfoPopupViewModelFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASMInfoPopupViewModelFactory.swift; sourceTree = ""; }; + 2D9835DF2F28113800B184D4 /* ASMInfoViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASMInfoViewFactory.swift; sourceTree = ""; }; 2D9835F32F28139F00B184D4 /* AppIcon.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = AppIcon.icon; sourceTree = ""; }; 2D9837992F310CE000B184D4 /* TimerButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimerButton.swift; sourceTree = ""; }; 2D98379A2F310CE000B184D4 /* WOScamAlertSheetPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WOScamAlertSheetPresenter.swift; sourceTree = ""; }; @@ -8790,15 +8818,13 @@ 2DC002C42E840DC700D49A42 /* ImageSecureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSecureView.swift; sourceTree = ""; }; 2DC002C62E840DF600D49A42 /* AssetListNftSecureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetListNftSecureView.swift; sourceTree = ""; }; 2DC002C82E840E2D00D49A42 /* HideSecureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HideSecureView.swift; sourceTree = ""; }; - 2DC002CE2E867BB500D49A42 /* AHMInfoViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AHMInfoViewModel.swift; sourceTree = ""; }; - 2DC002D12E867C1600D49A42 /* AHMInfoViewModelFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AHMInfoViewModelFactory.swift; sourceTree = ""; }; 2DC002D42E867CCD00D49A42 /* AHMRemoteData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AHMRemoteData.swift; sourceTree = ""; }; 2DC002D62E869D2000D49A42 /* AHMNavigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AHMNavigation.swift; sourceTree = ""; }; 2DC002D82E869FD700D49A42 /* OpenAHMUrlParsingService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenAHMUrlParsingService.swift; sourceTree = ""; }; 2DC002DB2E86D25D00D49A42 /* ExpiringInMemoryCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpiringInMemoryCache.swift; sourceTree = ""; }; 2DC002DD2E86D27D00D49A42 /* ObservableInMemoryCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservableInMemoryCache.swift; sourceTree = ""; }; 2DC002E02E86D59900D49A42 /* AHMInfoRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AHMInfoRepository.swift; sourceTree = ""; }; - 2DC002E22E895DB200D49A42 /* AHMInfoFetchOeprationFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AHMInfoFetchOeprationFactory.swift; sourceTree = ""; }; + 2DC002E22E895DB200D49A42 /* AHMInfoFetchOperationFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AHMInfoFetchOperationFactory.swift; sourceTree = ""; }; 2DC002E42E8A56AC00D49A42 /* PreSyncServiceCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreSyncServiceCoordinator.swift; sourceTree = ""; }; 2DC002E62E8A5A0000D49A42 /* AHMInfoService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AHMInfoService.swift; sourceTree = ""; }; 2DC002E82E8A6E4500D49A42 /* AHMInfoShownChains.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AHMInfoShownChains.swift; sourceTree = ""; }; @@ -9022,7 +9048,6 @@ 374AF5133A7FD39B961B9C84 /* GovernanceUnavailableTracksWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GovernanceUnavailableTracksWireframe.swift; sourceTree = ""; }; 37513C1D98A2D9583348BD66 /* GiftClaimWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GiftClaimWireframe.swift; sourceTree = ""; }; 376F2C0E94A454FBBBB903F6 /* ParaStkYieldBoostStopInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ParaStkYieldBoostStopInteractor.swift; sourceTree = ""; }; - 377CD0A9ABDBDFCA07B6DB5C /* AHMInfoViewFactory.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AHMInfoViewFactory.swift; sourceTree = ""; }; 3789A955202DA7CCADE13E68 /* DAppSettingsProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DAppSettingsProtocols.swift; sourceTree = ""; }; 37C7955BBDC93BC07D069B8F /* ParaStkStakeSetupViewFactory.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ParaStkStakeSetupViewFactory.swift; sourceTree = ""; }; 37D17FFDACA017C3460DE280 /* LedgerNetworkSelectionPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LedgerNetworkSelectionPresenter.swift; sourceTree = ""; }; @@ -9052,7 +9077,6 @@ 3B6244A9538B39AFCD3A6F3A /* SelectValidatorsStartPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SelectValidatorsStartPresenter.swift; sourceTree = ""; }; 3B8473AA386E1AD6F0F0C964 /* ControllerAccountConfirmationInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ControllerAccountConfirmationInteractor.swift; sourceTree = ""; }; 3BB7BFB2477E9F3893126931 /* DAppOperationConfirmViewFactory.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DAppOperationConfirmViewFactory.swift; sourceTree = ""; }; - 3BC3ECEE9326CFA2A15D2457 /* AHMInfoPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AHMInfoPresenter.swift; sourceTree = ""; }; 3BEEFB03BBA45F5143484398 /* ParaStkRedeemViewFactory.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ParaStkRedeemViewFactory.swift; sourceTree = ""; }; 3C3ADD92DEC7B0142C5C6696 /* AssetOperationNetworkListInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AssetOperationNetworkListInteractor.swift; sourceTree = ""; }; 3C402A458F6B80E4CD6DBFE3 /* AssetOperationNetworkListWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AssetOperationNetworkListWireframe.swift; sourceTree = ""; }; @@ -9280,7 +9304,6 @@ 6C972C86D5104BEB79598AC8 /* SwipeGovProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SwipeGovProtocols.swift; sourceTree = ""; }; 6CDC898FE48131F4FB8E64B8 /* DAppWalletAuthProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DAppWalletAuthProtocols.swift; sourceTree = ""; }; 6CF47A33418B791076057967 /* GenericLedgerAddEvmWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GenericLedgerAddEvmWireframe.swift; sourceTree = ""; }; - 6D65767C939FE2ACAF80E2B5 /* AHMInfoInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AHMInfoInteractor.swift; sourceTree = ""; }; 6D9A2CF1016F4D5A7F075B69 /* GovernanceSelectTracksInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GovernanceSelectTracksInteractor.swift; sourceTree = ""; }; 6DA49EE64DC450755353E482 /* CloudBackupAddWalletViewFactory.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudBackupAddWalletViewFactory.swift; sourceTree = ""; }; 6DEC8CCF0671304A658AD606 /* LedgerWalletAccountConfirmationWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LedgerWalletAccountConfirmationWireframe.swift; sourceTree = ""; }; @@ -9484,7 +9507,6 @@ 77A0B2F22A3CA37E00CBF653 /* StakingMoreOptionsViewModelFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StakingMoreOptionsViewModelFactory.swift; sourceTree = ""; }; 77A0B2F42A3CA39D00CBF653 /* StakingMoreOptionCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StakingMoreOptionCollectionViewCell.swift; sourceTree = ""; }; 77A0B2F82A3CA40E00CBF653 /* StakingMoreOptionsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StakingMoreOptionsSection.swift; sourceTree = ""; }; - 77A133C3748C74C128BA7846 /* AHMInfoWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AHMInfoWireframe.swift; sourceTree = ""; }; 77A4F4002B035027006294BC /* AssetOperationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetOperationState.swift; sourceTree = ""; }; 77A4F4022B036615006294BC /* Optional+Result.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Result.swift"; sourceTree = ""; }; 77A502F02B63E3830062FA51 /* ProxyAccountSubscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProxyAccountSubscription.swift; sourceTree = ""; }; @@ -10735,7 +10757,6 @@ 8487584627F1816200495306 /* QRExtractionService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRExtractionService.swift; sourceTree = ""; }; 8487584827F1830D00495306 /* QRImageUploadDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRImageUploadDelegate.swift; sourceTree = ""; }; 8487584A27F1834E00495306 /* ImageGalleryPresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageGalleryPresentable.swift; sourceTree = ""; }; - 8487FC52E560180302B91222 /* AHMInfoViewLayout.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AHMInfoViewLayout.swift; sourceTree = ""; }; 84880C3F29016F1500CADB06 /* ReferendumLockChangeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReferendumLockChangeViewModel.swift; sourceTree = ""; }; 84880C41290172C300CADB06 /* ReferendumLockChangeViewModelFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReferendumLockChangeViewModelFactory.swift; sourceTree = ""; }; 84880C4329026C3E00CADB06 /* ReferendumDelegatingLocal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReferendumDelegatingLocal.swift; sourceTree = ""; }; @@ -12551,7 +12572,6 @@ D087F5710630FCC968B65FB5 /* MarkdownDescriptionViewLayout.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = MarkdownDescriptionViewLayout.swift; sourceTree = ""; }; D0C2C6656744FA89FA45B45E /* OnboardingImportOptionsWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = OnboardingImportOptionsWireframe.swift; sourceTree = ""; }; D0D4E719EB5AC6CDB97BAB5C /* ParaStkUnstakeWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ParaStkUnstakeWireframe.swift; sourceTree = ""; }; - D0DA7291E053BF3B6F78990C /* AHMInfoViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AHMInfoViewController.swift; sourceTree = ""; }; D101339CC1292531CC4DB0AC /* StakingUnbondSetupInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = StakingUnbondSetupInteractor.swift; sourceTree = ""; }; D1852EB0DD9E0C39E0AAEE68 /* AssetListPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AssetListPresenter.swift; sourceTree = ""; }; D19485BAFCB2056BDC135441 /* TokenManageSingleViewFactory.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TokenManageSingleViewFactory.swift; sourceTree = ""; }; @@ -18247,6 +18267,79 @@ path = DelegatedAccountsUpdate; sourceTree = ""; }; + 2D9835592F2773A300B184D4 /* InfoPopup */ = { + isa = PBXGroup; + children = ( + 2D9835CA2F280BD000B184D4 /* ASM */, + 2D9835A92F28040800B184D4 /* AHM */, + 2D9835A22F2775BA00B184D4 /* Model */, + 2D98355A2F2773B200B184D4 /* InfoPopupProtocols.swift */, + 2D9835772F27742600B184D4 /* InfoPopupPresenter.swift */, + 2D98357B2F27743800B184D4 /* InfoPopupInteractor.swift */, + 2D9835882F2774D200B184D4 /* InfoPopupWireframe.swift */, + 2D9835622F2773CE00B184D4 /* InfoPopupViewController.swift */, + 2D9835702F27740800B184D4 /* InfoPopupViewLayout.swift */, + ); + path = InfoPopup; + sourceTree = ""; + }; + 2D9835A22F2775BA00B184D4 /* Model */ = { + isa = PBXGroup; + children = ( + 2D9835692F2773E400B184D4 /* InfoPopupViewModel.swift */, + 2D9835832F2774B500B184D4 /* InfoPopupContent.swift */, + 2D98358F2F27750500B184D4 /* InfoPopupAction.swift */, + 2D98359B2F27754B00B184D4 /* InfoPopupContentBuilder.swift */, + ); + path = Model; + sourceTree = ""; + }; + 2D9835A92F28040800B184D4 /* AHM */ = { + isa = PBXGroup; + children = ( + 2D98364D2F28AA5700B184D4 /* Factory */, + 2D9836442F28AA3700B184D4 /* Model */, + 2DC002E82E8A6E4500D49A42 /* AHMInfoShownChains.swift */, + 2D9835AA2F28040F00B184D4 /* AHMInfoPopupInteractor.swift */, + 2D9835B42F2805FD00B184D4 /* AHMInfoPopupPresenter.swift */, + 2D9835B92F28061200B184D4 /* AHMInfoPopupProtocols.swift */, + ); + path = AHM; + sourceTree = ""; + }; + 2D9835CA2F280BD000B184D4 /* ASM */ = { + isa = PBXGroup; + children = ( + 2D9835CB2F280BDD00B184D4 /* ASMInfoPopupPresenter.swift */, + 2D9835D32F280D3500B184D4 /* ASMInfoPopupInteractor.swift */, + 2D9835D82F280D7100B184D4 /* ASMInfoPopupViewModelFactory.swift */, + 2D9835DF2F28113800B184D4 /* ASMInfoViewFactory.swift */, + ); + path = ASM; + sourceTree = ""; + }; + 2D9836442F28AA3700B184D4 /* Model */ = { + isa = PBXGroup; + children = ( + 2DC002D42E867CCD00D49A42 /* AHMRemoteData.swift */, + 2DC002D62E869D2000D49A42 /* AHMNavigation.swift */, + 2DC002E02E86D59900D49A42 /* AHMInfoRepository.swift */, + 2DC002F32E8BB65900D49A42 /* AHMFullInfoFactory.swift */, + 2DC002F52E8BB6BB00D49A42 /* AHMFullInfo.swift */, + ); + path = Model; + sourceTree = ""; + }; + 2D98364D2F28AA5700B184D4 /* Factory */ = { + isa = PBXGroup; + children = ( + 2D9835BE2F28062700B184D4 /* AHMInfoPopupViewModelFactory.swift */, + 2D9835C52F28067E00B184D4 /* AHMInfoPopupViewFactory.swift */, + 2DC002E22E895DB200D49A42 /* AHMInfoFetchOperationFactory.swift */, + ); + path = Factory; + sourceTree = ""; + }; 2D9837982F310CD000B184D4 /* ScamAlert */ = { isa = PBXGroup; children = ( @@ -18640,30 +18733,6 @@ path = PrivacyMode; sourceTree = ""; }; - 2DC002D02E867C0400D49A42 /* Model */ = { - isa = PBXGroup; - children = ( - 2DC002E02E86D59900D49A42 /* AHMInfoRepository.swift */, - 2DC002F32E8BB65900D49A42 /* AHMFullInfoFactory.swift */, - 2DC002F52E8BB6BB00D49A42 /* AHMFullInfo.swift */, - 2DC002E82E8A6E4500D49A42 /* AHMInfoShownChains.swift */, - 2DC002CE2E867BB500D49A42 /* AHMInfoViewModel.swift */, - 2DC002D42E867CCD00D49A42 /* AHMRemoteData.swift */, - 2DC002D62E869D2000D49A42 /* AHMNavigation.swift */, - ); - path = Model; - sourceTree = ""; - }; - 2DC002D32E867C1E00D49A42 /* Factory */ = { - isa = PBXGroup; - children = ( - 377CD0A9ABDBDFCA07B6DB5C /* AHMInfoViewFactory.swift */, - 2DC002D12E867C1600D49A42 /* AHMInfoViewModelFactory.swift */, - 2DC002E22E895DB200D49A42 /* AHMInfoFetchOeprationFactory.swift */, - ); - path = Factory; - sourceTree = ""; - }; 2DC002DA2E86D24D00D49A42 /* InMemoryCache */ = { isa = PBXGroup; children = ( @@ -23952,6 +24021,7 @@ 849013D224A9268D008F705E /* Modules */ = { isa = PBXGroup; children = ( + 2D9835592F2773A300B184D4 /* InfoPopup */, 2DE37C402EA0D824007C43A0 /* Gifts */, 0C3B126E2DDC663400A61597 /* WalletMigration */, 2D6CE43A2DE4C3FD00B94DBA /* DelegatedAccounts */, @@ -24004,7 +24074,6 @@ 525901B46946ED08CB25F6C3 /* Banners */, 0274F684DCFA6D2AFEA43CF5 /* AssetPriceChart */, 028384EC1D6B48710C2236A2 /* UnifiedAddressPopup */, - F969E89D4A7D83173147E818 /* AHMInfo */, ); path = Modules; sourceTree = ""; @@ -30125,21 +30194,6 @@ path = ReferendumsFilters; sourceTree = ""; }; - F969E89D4A7D83173147E818 /* AHMInfo */ = { - isa = PBXGroup; - children = ( - 2DC002D32E867C1E00D49A42 /* Factory */, - 2DC002D02E867C0400D49A42 /* Model */, - 0FCC7D490906498F89DAE535 /* AHMInfoProtocols.swift */, - 77A133C3748C74C128BA7846 /* AHMInfoWireframe.swift */, - 3BC3ECEE9326CFA2A15D2457 /* AHMInfoPresenter.swift */, - 6D65767C939FE2ACAF80E2B5 /* AHMInfoInteractor.swift */, - D0DA7291E053BF3B6F78990C /* AHMInfoViewController.swift */, - 8487FC52E560180302B91222 /* AHMInfoViewLayout.swift */, - ); - path = AHMInfo; - sourceTree = ""; - }; F9C6DF4BF106A135BD3BB1CF /* CustomNetwork */ = { isa = PBXGroup; children = ( @@ -31040,6 +31094,7 @@ 0C2D1E3D2D9708350030E43C /* LedgerDeviceDisplayInfo.swift in Sources */, 8425D0E328FE766B003B782A /* ReferendumVoteProtocols.swift in Sources */, 0C3976A52C33050600998DD6 /* HydraXYKSwapQuoteFactory.swift in Sources */, + 2D9835D42F280D3500B184D4 /* ASMInfoPopupInteractor.swift in Sources */, F462B364260C88050005AB01 /* UITableView+Reuse.swift in Sources */, 0CBF5DE72AB1A60500087EBF /* SharedOperationStatus.swift in Sources */, 0C8FC21F2E60BD8C00034ED3 /* OffchainMultisigOperation.swift in Sources */, @@ -31363,6 +31418,7 @@ 77DB70EE2B709E4300288F26 /* Web3AlertRemoteModel.swift in Sources */, 0C0F6DBA2D3EE9C200943A7C /* MythosStakingDetails.swift in Sources */, 8471538D2653B29100CB91D8 /* ChangeRewardDestinationViewModelFactory.swift in Sources */, + 2D9835902F27750500B184D4 /* InfoPopupAction.swift in Sources */, 84216FDA2827B25F00479375 /* StorageItemSyncService.swift in Sources */, 8499FEDE27C0AB0200712589 /* NftSource.swift in Sources */, 84D2F1B12775999F0040C680 /* DAppParsedExtrinsic.swift in Sources */, @@ -31479,7 +31535,7 @@ 0C37AFB72B555F1400009ECA /* StakingValidatorExposureFacade.swift in Sources */, 0CE1A0522BE9E995008206ED /* CloudBackupSettingsView.swift in Sources */, 84E9A05028F000AB00551DC4 /* ReferendumMetadataLocal.swift in Sources */, - 2DC002E32E895DB200D49A42 /* AHMInfoFetchOeprationFactory.swift in Sources */, + 2DC002E32E895DB200D49A42 /* AHMInfoFetchOperationFactory.swift in Sources */, 84821E84275F93C700ADC8D2 /* TitleMultiValueView+Style.swift in Sources */, 2D20F1BE2D351936003E9CF2 /* DAppListBannerViewModel.swift in Sources */, 84DA03D12758AA6800E8B326 /* BaseAccountImportWireframe.swift in Sources */, @@ -31560,6 +31616,7 @@ 8430AAE126022CA1005B1066 /* BaseStakingState.swift in Sources */, 8490141124A92F6D008F705E /* OnboardingMainViewController.swift in Sources */, 0CE94FF42D04969100E31D0C /* SwapInterEDNotMet.swift in Sources */, + 2D9835892F2774D200B184D4 /* InfoPopupWireframe.swift in Sources */, 0CCCDF842B64C63D00473D42 /* HydraDxTokenConverter.swift in Sources */, 0CEBAD4E2D5A2BD9000EBA45 /* SwapsDifferenceModelFactory.swift in Sources */, 2D4431E02D01096300017951 /* DAppItemViewCollectionCell.swift in Sources */, @@ -31630,6 +31687,7 @@ 84E4897329A79017008726FA /* SkeletonDecoration+View.swift in Sources */, 2AD0A19525D3D3EC00312428 /* GitHubOperationFactory.swift in Sources */, 2D039DE42BFBB2FE00A928E5 /* ExportRowView.swift in Sources */, + 2D9835D92F280D7100B184D4 /* ASMInfoPopupViewModelFactory.swift in Sources */, 2D74CA3F2CF7262E004EAD8C /* BrowserOpening.swift in Sources */, 845B07FF2916F529005785D3 /* Gov1SubscriptionFactory.swift in Sources */, 888A3B6528F73DC300E15BD2 /* ReferendumVotingStatusView.swift in Sources */, @@ -31872,6 +31930,7 @@ 2D3A3A7E2EC466F00069FD31 /* ReferendumLinkFactory.swift in Sources */, AEF7404E25E6DC9400407D41 /* InflationCurveRewardEngine.swift in Sources */, 2DEDEF572E4CF3B7008F07B2 /* RuntimeDefaultTypesSnapshotFactory.swift in Sources */, + 2D9835CC2F280BDD00B184D4 /* ASMInfoPopupPresenter.swift in Sources */, 848CCB522833D29F00A1FD00 /* StakingParachainStatics.swift in Sources */, 84CA68D926BE9E7F003B9453 /* SpecVersionSubscription.swift in Sources */, 0C13DFCB2AF6182500E5F355 /* SwapModel.swift in Sources */, @@ -32107,6 +32166,7 @@ 842898CC265A8EC3002D5D65 /* RemoteImageViewModel.swift in Sources */, 84DA3B1424C6D7C700B5E27F /* RuntimeDispatchInfo.swift in Sources */, 840D92A7278EE8690007B979 /* DAppSignBytesConfirmInteractor.swift in Sources */, + 2D9835C62F28067E00B184D4 /* AHMInfoPopupViewFactory.swift in Sources */, 84EC2D1A276C6600009B0BE1 /* PolkadotExtensionExtrinsic.swift in Sources */, 0C59E8E32AA61252001E11F3 /* NominationPoolExternalServiceFactory.swift in Sources */, 0CEBAD652D64560C000EBA45 /* DispatchCallError+Display.swift in Sources */, @@ -32402,6 +32462,7 @@ AEF507F226259DF00098574D /* ValidationViewModel.swift in Sources */, 84FD91B229B08F8F007851D3 /* BaseTableSearchViewLayout.swift in Sources */, 2DC002D92E869FD700D49A42 /* OpenAHMUrlParsingService.swift in Sources */, + 2D9835BA2F28061200B184D4 /* AHMInfoPopupProtocols.swift in Sources */, 847F2D4827A9DDC700AFD476 /* MultigradientView+Default.swift in Sources */, 8448F7A6288314250080CEA9 /* AssetListAssetsViewModelFactory.swift in Sources */, 0C59E8F42AA7649E001E11F3 /* OperationDetailsBaseProvider.swift in Sources */, @@ -32974,7 +33035,6 @@ 0CED3A082BD12DFD00B1E994 /* CloudBackupSecretsExporting.swift in Sources */, 2D6861CB2DDEFBFD004DCD2D /* Multisig+Call.swift in Sources */, F41C728326428A9400E8BB41 /* ControllerAccountViewLayout.swift in Sources */, - 2DC002CF2E867BB500D49A42 /* AHMInfoViewModel.swift in Sources */, 84981EEE29D4608800948306 /* TransactionHistoryFetcherFactory.swift in Sources */, 2D62ED322D5CE621001EF17D /* BannersViewController+UICollectionView.swift in Sources */, 886A278629E93A84003B269F /* EquilibriumReservedData.swift in Sources */, @@ -33204,6 +33264,7 @@ 849976CC27B3905E00B14A6C /* DAppMetamaskDeniedState.swift in Sources */, 849976B327B2430300B14A6C /* DAppMetamaskTransport.swift in Sources */, 0C56B4FD2A4B0CA90030F9C9 /* AssetListBuilderResult.swift in Sources */, + 2D98355B2F2773B200B184D4 /* InfoPopupProtocols.swift in Sources */, 8425D0EC28FE9ACB003B782A /* Gov2ExtrinsicFactory.swift in Sources */, 0CCE3AAF2BF5BE4300D55F03 /* IdentityProxyFactory.swift in Sources */, 84E1CCFA260DCBF9001E81B5 /* SwitchAccount+UsernameSetupWireframe.swift in Sources */, @@ -33265,6 +33326,7 @@ 77EFFC8A2A6E7A24009E28F8 /* AccountExistense.swift in Sources */, 8466BB472640152A00E065A8 /* StakingUnbondConfirmViewModelFactory.swift in Sources */, 84CE6A312566800000559427 /* ByteLengthProcessor+Username.swift in Sources */, + 2D9835712F27740800B184D4 /* InfoPopupViewLayout.swift in Sources */, 0C3976AD2C33A64700998DD6 /* HydraXYKQuoteParamsService.swift in Sources */, 84B24F9C2A2E297000F9BF59 /* StakingDashboardViewModel.swift in Sources */, 8482F633281024B80006C3A0 /* Data+EthereumSign.swift in Sources */, @@ -33587,6 +33649,7 @@ 84B8AA7329F8EFC700347A37 /* DAppInteractionError.swift in Sources */, 8485035029FBC84300AE6909 /* WalletConnectSignModelFactory.swift in Sources */, 2D1C5D162C24553200E2DBDD /* NetworkNodeAddPresenter.swift in Sources */, + 2D98359C2F27754B00B184D4 /* InfoPopupContentBuilder.swift in Sources */, 0CE8621B2D64E86700245992 /* StakingRewardsLocalSubscriber.swift in Sources */, 849E17F02791909C002D1744 /* DAppSettings.swift in Sources */, 2DA8DAC42DC20CBE00AFE390 /* MultisigPallet+CodingPath.swift in Sources */, @@ -33664,6 +33727,7 @@ 06590486EED4050BADDD32C5 /* AccountManagementPresenter.swift in Sources */, 846A68292746234100D1A47A /* ChainConnection.swift in Sources */, 849A16452995229F00E235FE /* GovernanceDelegateConfirmPresenter+Updates.swift in Sources */, + 2D9835E02F28113800B184D4 /* ASMInfoViewFactory.swift in Sources */, 0C0F6E532D48934400943A7C /* MythosStakingDelegatorState.swift in Sources */, 88B1C33F29B9C86D00DCA101 /* BagListCalls.swift in Sources */, 8487584127F0842D00495306 /* AddressQRMatcher.swift in Sources */, @@ -33678,7 +33742,6 @@ 2D73AD4F2EC32EE8002DE18F /* GiftWalletChoosePresentable.swift in Sources */, 773A375C2B3B5901006AC4AA /* DelegatedMessageSheetPresenter.swift in Sources */, 84BC703F289DBE6C008A9758 /* QRWithLogoCreationOperation.swift in Sources */, - 2DC002D22E867C1600D49A42 /* AHMInfoViewModelFactory.swift in Sources */, 84452A6025D037AE00F47EC5 /* ChainStorage+Decodable.swift in Sources */, 8849AD6E29C4690F00F4F7FF /* String+CheckLength.swift in Sources */, 847C963525534E41002D288F /* UIFactory.swift in Sources */, @@ -34599,6 +34662,7 @@ 0CA72A842D4A2B1B00B3FD02 /* CollatorStkPartialUnstakeSetupLayout.swift in Sources */, 8480154E29BB6521008557F3 /* BorderedImageView.swift in Sources */, 2D3C71CF2E1151F500DF9E49 /* AssetListMultisigOperationsCell.swift in Sources */, + 2D9835AB2F28040F00B184D4 /* AHMInfoPopupInteractor.swift in Sources */, 84BFE89C28C23A2500140F1F /* AutomationTime.swift in Sources */, 2D6287402C91ED5300060814 /* ReferendumVoteSetupPresenter.swift in Sources */, 846DA5592A2098BE006CD6C1 /* StakingResolvedAccountMapper.swift in Sources */, @@ -34964,6 +35028,7 @@ 599A81A7D18BB70B6C650393 /* DAppAuthConfirmViewFactory.swift in Sources */, 84FBDBE628C8982B00CC1037 /* ParaStkYieldBoostSetupInteractorError.swift in Sources */, 84E25BF227E8FDC100290BF1 /* DecodedProviderTypes.swift in Sources */, + 2D9835B52F2805FD00B184D4 /* AHMInfoPopupPresenter.swift in Sources */, 841E5565282EA76C00C8438F /* ParachainStakingBaseState.swift in Sources */, 77CB33D72A3998FD00B6709A /* Array+Sort.swift in Sources */, 84B8AA8129F9097D00347A37 /* WalletConnectTransport.swift in Sources */, @@ -35250,6 +35315,7 @@ 413CCB7C7B22831147B8E815 /* ParaStkRedeemPresenter.swift in Sources */, 0C8FDBCF2CB25D7400775D7F /* MercuryoStatusChange.swift in Sources */, 35F9157CAA182493B2F0E1D3 /* ParaStkRedeemInteractor.swift in Sources */, + 2D98357C2F27743800B184D4 /* InfoPopupInteractor.swift in Sources */, 771901A22AE7E34D00D9C918 /* SwapBaseInteractor.swift in Sources */, 2DF95D2A2D785AF1001D8785 /* HapticEngine.swift in Sources */, C729BF3E60E6825AEED11383 /* CollatorStakingRedeemViewController.swift in Sources */, @@ -35396,6 +35462,7 @@ 012AE9F8BDA682C691B6F9FD /* PVWelcomeViewController.swift in Sources */, DC2867A7DC1415052D090C53 /* PVWelcomeViewLayout.swift in Sources */, 0C0E0AA02B3F013800865F10 /* BalancesPallet.swift in Sources */, + 2D9835842F2774B500B184D4 /* InfoPopupContent.swift in Sources */, 845B07F5291627A3005785D3 /* Gov1OperationFactory+Protocol.swift in Sources */, 97608590E8D770A86CCFBE86 /* PVWelcomeViewFactory.swift in Sources */, 8425D0E028FE738D003B782A /* ReferendumVoteSetupInteractorError.swift in Sources */, @@ -36411,6 +36478,7 @@ 4520C59F8C8CCB7669DDA4DF /* NotificationWalletListPresenter.swift in Sources */, 2D3286EE2DFFF910004D5D24 /* PendingMultisigChainSyncService.swift in Sources */, AD87A679F6E61D7AA20DF60C /* NotificationWalletListInteractor.swift in Sources */, + 2D98356A2F2773E400B184D4 /* InfoPopupViewModel.swift in Sources */, 6B1BDD1421916CCF59DF6F5F /* NotificationWalletListViewController.swift in Sources */, 0CA8AD622CE08FEE00ED9746 /* BlockEventsQueryFactory.swift in Sources */, 0CB618D22E70C3320018C766 /* Xcm+TransferType.swift in Sources */, @@ -36578,6 +36646,7 @@ 0CE94FF02D03AF9800E31D0C /* SwapPreferredFeeAssetModel.swift in Sources */, EECFBA0B881DBD2AE2BC73B7 /* CustomNetworkProtocols.swift in Sources */, 440436412294F3569F426552 /* CustomNetworkWireframe.swift in Sources */, + 2D9835782F27742600B184D4 /* InfoPopupPresenter.swift in Sources */, F08729B3361332AC9E017BED /* CustomNetworkViewController.swift in Sources */, A83FCCEF54DDA2C8CDA98AF5 /* CustomNetworkViewLayout.swift in Sources */, D71D861DF5798FF122845B86 /* CustomNetworkViewFactory.swift in Sources */, @@ -36641,6 +36710,7 @@ 0C06C48E2EC9E3D600E06744 /* BatchGenericSubscriptionHandler.swift in Sources */, 13C118A723E684D4272845FB /* SwipeGovReferendumDetailsViewFactory.swift in Sources */, 00AE6FF475338CEF6C65F3B0 /* SwipeGovVotingConfirmProtocols.swift in Sources */, + 2D9835632F2773CE00B184D4 /* InfoPopupViewController.swift in Sources */, 6919F96BC31D29A3FD1557AE /* SwipeGovVotingConfirmWireframe.swift in Sources */, DD5B5692C208F627E2597D31 /* SwipeGovVotingConfirmPresenter.swift in Sources */, 514A75EB56856362C411EB7F /* SwipeGovVotingConfirmInteractor.swift in Sources */, @@ -36665,6 +36735,7 @@ 14003DEC47A69E473B97B3E5 /* AppearanceSettingsViewController.swift in Sources */, 36EA1F22A0E7DDD3C987CF98 /* AppearanceSettingsViewLayout.swift in Sources */, BC927FA9FDA6F1AC16E0AAAE /* AppearanceSettingsViewFactory.swift in Sources */, + 2D9835BF2F28062700B184D4 /* AHMInfoPopupViewModelFactory.swift in Sources */, 0C0F6DEC2D427BE900943A7C /* CollatorStakingDelegationStatus.swift in Sources */, 076E4DC5984B431018CB9F65 /* SwapExecutionProtocols.swift in Sources */, 7BF57D772C522E7728585166 /* SwapExecutionWireframe.swift in Sources */, @@ -36802,13 +36873,6 @@ 852A62972D1940E698A4176F /* DelegatedSignValidationPresenter.swift in Sources */, 8EBA63725001CE2C640310D3 /* DelegatedSignValidationInteractor.swift in Sources */, 120A2EF278301252A6097C23 /* DelegatedSignValidationViewFactory.swift in Sources */, - 955209300EF9643EC1241B12 /* AHMInfoProtocols.swift in Sources */, - 0FD7DB6BDCD695E9D9B51615 /* AHMInfoWireframe.swift in Sources */, - C4196AA0781D276DA03C1042 /* AHMInfoPresenter.swift in Sources */, - 2D259A9AA172D8DDADE58FBD /* AHMInfoInteractor.swift in Sources */, - 85E6DA35DF2D2C4C73FBE988 /* AHMInfoViewController.swift in Sources */, - 58BAD91930AB3BEC2C4121AD /* AHMInfoViewLayout.swift in Sources */, - A0345DBFA840D7355DB20171 /* AHMInfoViewFactory.swift in Sources */, 56521E12400D021BF8FA22B4 /* GiftPrepareShareProtocols.swift in Sources */, 3883A36679199464BD299605 /* GiftPrepareShareWireframe.swift in Sources */, 7E8C10658FEAD469F5DFEDFA /* GiftPrepareSharePresenter.swift in Sources */, diff --git a/novawallet/Common/Extension/SettingsExtension.swift b/novawallet/Common/Extension/SettingsExtension.swift index 4ac996213e..67b9e5c37a 100644 --- a/novawallet/Common/Extension/SettingsExtension.swift +++ b/novawallet/Common/Extension/SettingsExtension.swift @@ -34,6 +34,7 @@ enum SettingsKey: String { case ahmAssetDetailsAlertClosedChains case ahmStakingAlertClosedChains case privacyModeSettings + case appStoreMigrationShown } extension SettingsManagerProtocol { @@ -425,4 +426,14 @@ extension SettingsManagerProtocol { ) } } + + var appStoreMigrationShown: Bool { + get { + bool(for: SettingsKey.appStoreMigrationShown.rawValue) ?? false + } + + set { + set(value: newValue, for: SettingsKey.appStoreMigrationShown.rawValue) + } + } } diff --git a/novawallet/Common/View/InlinableAlertView/InlinableAlertViewModelFactory.swift b/novawallet/Common/View/InlinableAlertView/InlinableAlertViewModelFactory.swift index cf420d1422..362eeea57c 100644 --- a/novawallet/Common/View/InlinableAlertView/InlinableAlertViewModelFactory.swift +++ b/novawallet/Common/View/InlinableAlertView/InlinableAlertViewModelFactory.swift @@ -1,10 +1,27 @@ import Foundation +import Foundation_iOS protocol InlinableAlertViewModelFactoryProtocol { func createWOAssetListAlertViewModel(for locale: Locale) -> InlinableAlertView.Model + + func createStakingDetailsAlertViewModel( + info: AHMFullInfo, + locale: Locale + ) -> InlinableAlertView.Model + + func createAssetDetailsAlertViewModel( + info: AHMFullInfo, + locale: Locale + ) -> InlinableAlertView.Model } -final class InlinableAlertViewModelFactory {} +final class InlinableAlertViewModelFactory { + private let dateFormatter: LocalizableResource + + init(dateFormatter: LocalizableResource = DateFormatter.fullDate) { + self.dateFormatter = dateFormatter + } +} extension InlinableAlertViewModelFactory: InlinableAlertViewModelFactoryProtocol { func createWOAssetListAlertViewModel(for locale: Locale) -> InlinableAlertView.Model { @@ -29,4 +46,93 @@ extension InlinableAlertViewModelFactory: InlinableAlertViewModelFactoryProtocol showCloseButton: false ) } + + func createStakingDetailsAlertViewModel( + info: AHMFullInfo, + locale: Locale + ) -> InlinableAlertView.Model { + let sourceChainAsset = ChainAsset( + chain: info.sourceChain, + asset: info.asset + ) + let languages = locale.rLanguages + + let date = Date(timeIntervalSince1970: TimeInterval(info.info.timestamp)) + + let formattedDate = dateFormatter + .value(for: locale) + .string(from: date) + + let title = R.string( + preferredLanguages: locale.rLanguages + ).localizable.ahmInfoAlertStakingDetailsMessage( + sourceChainAsset.chainAssetName, + info.destinationChain.name, + formattedDate + ) + let learnMoreModel = LearnMoreViewModel( + iconViewModel: nil, + title: R.string( + preferredLanguages: locale.rLanguages + ).localizable.commonLearnMore() + ) + + return InlinableAlertView.Model( + type: .ahmStakingDetails, + title: title, + message: nil, + learnMore: learnMoreModel, + actionTitle: nil, + icon: R.image.iconInfoAccent()!, + showCloseButton: true + ) + } + + func createAssetDetailsAlertViewModel( + info: AHMFullInfo, + locale: Locale + ) -> InlinableAlertView.Model { + let languages = locale.rLanguages + + let date = Date(timeIntervalSince1970: TimeInterval(info.info.timestamp)) + + let formattedDate = dateFormatter + .value(for: locale) + .string(from: date) + + let title = R.string( + preferredLanguages: locale.rLanguages + ).localizable.ahmInfoAlertAssetDetailsTitle( + info.asset.symbol, + info.destinationChain.name + ) + let message = R.string( + preferredLanguages: locale.rLanguages + ).localizable.ahmInfoAlertAssetDetailsMessage( + formattedDate, + info.asset.symbol, + info.destinationChain.name + ) + let learnMoreModel = LearnMoreViewModel( + iconViewModel: nil, + title: R.string( + preferredLanguages: locale.rLanguages + ).localizable.commonLearnMore() + ) + let actionTitle = R.string( + preferredLanguages: locale.rLanguages + ).localizable.ahmInfoAlertAssetDetailsAction( + info.destinationChain.name + ) + + return InlinableAlertView.Model( + type: .ahmAssetDetails, + title: title, + message: message, + learnMore: learnMoreModel, + actionTitle: actionTitle, + icon: R.image.iconInfoAccent()!, + showCloseButton: true + ) + } } diff --git a/novawallet/Modules/AHMInfo/AHMInfoInteractor.swift b/novawallet/Modules/AHMInfo/AHMInfoInteractor.swift deleted file mode 100644 index 36585ba7ab..0000000000 --- a/novawallet/Modules/AHMInfo/AHMInfoInteractor.swift +++ /dev/null @@ -1,38 +0,0 @@ -import Foundation -import Keystore_iOS - -final class AHMInfoInteractor { - weak var presenter: AHMInfoInteractorOutputProtocol? - - private let info: AHMRemoteData - private let chainRegistry: ChainRegistryProtocol - private let settingsManager: SettingsManagerProtocol - - init( - info: AHMRemoteData, - chainRegistry: ChainRegistryProtocol, - settingsManager: SettingsManagerProtocol - ) { - self.info = info - self.chainRegistry = chainRegistry - self.settingsManager = settingsManager - } -} - -// MARK: - AHMInfoInteractorInputProtocol - -extension AHMInfoInteractor: AHMInfoInteractorInputProtocol { - func setup() { - if let sourceChain = chainRegistry.getChain(for: info.sourceData.chainId) { - presenter?.didReceive(sourceChain: sourceChain) - } - - if let destinationChain = chainRegistry.getChain(for: info.destinationData.chainId) { - presenter?.didReceive(destinationChain: destinationChain) - } - } - - func setShown() { - settingsManager.ahmInfoShownChains.add(info.sourceData.chainId) - } -} diff --git a/novawallet/Modules/AHMInfo/AHMInfoPresenter.swift b/novawallet/Modules/AHMInfo/AHMInfoPresenter.swift deleted file mode 100644 index 7fc7e36e0d..0000000000 --- a/novawallet/Modules/AHMInfo/AHMInfoPresenter.swift +++ /dev/null @@ -1,120 +0,0 @@ -import Foundation -import Foundation_iOS -import BigInt - -final class AHMInfoPresenter: BannersModuleInputOwnerProtocol { - weak var view: AHMInfoViewProtocol? - weak var bannersModule: BannersModuleInputProtocol? - - private let wireframe: AHMInfoWireframeProtocol - private let interactor: AHMInfoInteractorInputProtocol - private let viewModelFactory: AHMInfoViewModelFactoryProtocol - private let info: AHMRemoteData - private let localizationManager: LocalizationManagerProtocol - - private var sourceChain: ChainModel? - private var destinationChain: ChainModel? - - init( - interactor: AHMInfoInteractorInputProtocol, - wireframe: AHMInfoWireframeProtocol, - viewModelFactory: AHMInfoViewModelFactoryProtocol, - info: AHMRemoteData, - localizationManager: LocalizationManagerProtocol - ) { - self.interactor = interactor - self.wireframe = wireframe - self.viewModelFactory = viewModelFactory - self.info = info - self.localizationManager = localizationManager - } - - private func provideViewModel() { - guard - let sourceChain = sourceChain, - let destinationChain = destinationChain - else { return } - - let viewModel = viewModelFactory.createViewModel( - from: info, - sourceChain: sourceChain, - destinationChain: destinationChain, - bannerState: bannersModule?.bannersState ?? .unavailable, - locale: localizationManager.selectedLocale - ) - - view?.didReceive(viewModel: viewModel) - } -} - -// MARK: - AHMInfoPresenterProtocol - -extension AHMInfoPresenter: AHMInfoPresenterProtocol { - func setup() { - interactor.setup() - - guard bannersModule?.locale != localizationManager.selectedLocale else { return } - - bannersModule?.updateLocale(localizationManager.selectedLocale) - } - - func actionGotIt() { - interactor.setShown() - wireframe.complete(from: view) - } - - func actionLearnMore() { - guard let view else { return } - - wireframe.showWeb( - url: info.wikiURL, - from: view, - style: .automatic - ) - } -} - -// MARK: - AHMInfoInteractorOutputProtocol - -extension AHMInfoPresenter: AHMInfoInteractorOutputProtocol { - func didReceive(sourceChain: ChainModel) { - self.sourceChain = sourceChain - provideViewModel() - } - - func didReceive(destinationChain: ChainModel) { - self.destinationChain = destinationChain - provideViewModel() - } -} - -// MARK: - BannersModuleOutputProtocol - -extension AHMInfoPresenter: BannersModuleOutputProtocol { - func didReceiveBanners(state _: BannersState) { - provideViewModel() - } - - func didUpdateContent(state _: BannersState) { - provideViewModel() - } - - func didReceive(_ error: Error) { - wireframe.present( - error: error, - from: view, - locale: localizationManager.selectedLocale - ) - } -} - -// MARK: - Localizable - -extension AHMInfoPresenter: Localizable { - func applyLocalization() { - if let view = view, view.isSetup { - provideViewModel() - bannersModule?.updateLocale(localizationManager.selectedLocale) - } - } -} diff --git a/novawallet/Modules/AHMInfo/AHMInfoProtocols.swift b/novawallet/Modules/AHMInfo/AHMInfoProtocols.swift deleted file mode 100644 index 87bc4d17d3..0000000000 --- a/novawallet/Modules/AHMInfo/AHMInfoProtocols.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Foundation - -protocol AHMInfoViewProtocol: ControllerBackedProtocol { - func didReceive(viewModel: AHMInfoViewModel) -} - -protocol AHMInfoPresenterProtocol: AnyObject { - func setup() - func actionGotIt() - func actionLearnMore() -} - -protocol AHMInfoInteractorInputProtocol: AnyObject { - func setup() - func setShown() -} - -protocol AHMInfoInteractorOutputProtocol: AnyObject { - func didReceive(sourceChain: ChainModel) - func didReceive(destinationChain: ChainModel) -} - -protocol AHMInfoWireframeProtocol: WebPresentable, AlertPresentable, ErrorPresentable { - func complete(from view: AHMInfoViewProtocol?) -} diff --git a/novawallet/Modules/AHMInfo/AHMInfoWireframe.swift b/novawallet/Modules/AHMInfo/AHMInfoWireframe.swift deleted file mode 100644 index 8258a7edde..0000000000 --- a/novawallet/Modules/AHMInfo/AHMInfoWireframe.swift +++ /dev/null @@ -1,10 +0,0 @@ -import Foundation - -final class AHMInfoWireframe: AHMInfoWireframeProtocol { - func complete(from view: AHMInfoViewProtocol?) { - view?.controller.dismiss( - animated: true, - completion: nil - ) - } -} diff --git a/novawallet/Modules/AHMInfo/Model/AHMInfoViewModel.swift b/novawallet/Modules/AHMInfo/Model/AHMInfoViewModel.swift deleted file mode 100644 index 5f2b652f7f..0000000000 --- a/novawallet/Modules/AHMInfo/Model/AHMInfoViewModel.swift +++ /dev/null @@ -1,27 +0,0 @@ -import UIKit - -struct AHMInfoViewModel { - let bannerState: BannersState - let title: String - let subtitle: String - let features: [Feature] - let info: [Info] - let actionButtonTitle: String - - struct Feature { - let emoji: String - let text: String - } - - struct Info { - let type: InfoType - let text: String - } -} - -extension AHMInfoViewModel.Info { - enum InfoType { - case history - case migration - } -} diff --git a/novawallet/Modules/AssetDetails/AssetDetailsPresenter.swift b/novawallet/Modules/AssetDetails/AssetDetailsPresenter.swift index 4c75ea30d6..c0e11e063a 100644 --- a/novawallet/Modules/AssetDetails/AssetDetailsPresenter.swift +++ b/novawallet/Modules/AssetDetails/AssetDetailsPresenter.swift @@ -8,7 +8,7 @@ final class AssetDetailsPresenter: RampFlowManaging, AssetPriceChartInputOwnerPr weak var assetPriceChartModule: AssetPriceChartModuleInputProtocol? let wireframe: AssetDetailsWireframeProtocol - let ahmViewModelFactory: AHMInfoViewModelFactoryProtocol + let inlinableAlertViewModelFactory: InlinableAlertViewModelFactoryProtocol let viewModelFactory: AssetDetailsViewModelFactoryProtocol let interactor: AssetDetailsInteractorInputProtocol let chainAsset: ChainAsset @@ -29,7 +29,7 @@ final class AssetDetailsPresenter: RampFlowManaging, AssetPriceChartInputOwnerPr localizableManager: LocalizationManagerProtocol, chainAsset: ChainAsset, selectedAccount: MetaAccountModel, - ahmViewModelFactory: AHMInfoViewModelFactoryProtocol, + inlinableAlertViewModelFactory: InlinableAlertViewModelFactoryProtocol, viewModelFactory: AssetDetailsViewModelFactoryProtocol, wireframe: AssetDetailsWireframeProtocol, logger: LoggerProtocol? @@ -38,7 +38,7 @@ final class AssetDetailsPresenter: RampFlowManaging, AssetPriceChartInputOwnerPr self.wireframe = wireframe self.chainAsset = chainAsset self.selectedAccount = selectedAccount - self.ahmViewModelFactory = ahmViewModelFactory + self.inlinableAlertViewModelFactory = inlinableAlertViewModelFactory self.viewModelFactory = viewModelFactory self.logger = logger localizationManager = localizableManager @@ -79,7 +79,7 @@ private extension AssetDetailsPresenter { view.didReceive(availableOperations: availableOperations) let ahmAlertModel: InlinableAlertView.Model? = if let ahmInfo { - ahmViewModelFactory.createAssetDetailsAlertViewModel( + inlinableAlertViewModelFactory.createAssetDetailsAlertViewModel( info: ahmInfo, locale: selectedLocale ) diff --git a/novawallet/Modules/AssetDetails/AssetDetailsViewFactory.swift b/novawallet/Modules/AssetDetails/AssetDetailsViewFactory.swift index 835dac5aad..44a49ce695 100644 --- a/novawallet/Modules/AssetDetails/AssetDetailsViewFactory.swift +++ b/novawallet/Modules/AssetDetails/AssetDetailsViewFactory.swift @@ -55,7 +55,7 @@ struct AssetDetailsViewFactory { localizableManager: localizationManager, chainAsset: chainAsset, selectedAccount: selectedAccount, - ahmViewModelFactory: AHMInfoViewModelFactory(), + inlinableAlertViewModelFactory: InlinableAlertViewModelFactory(), viewModelFactory: viewModelFactory, wireframe: wireframe, logger: Logger.shared diff --git a/novawallet/Modules/Banners/Factory/BannersViewFactory.swift b/novawallet/Modules/Banners/Factory/BannersViewFactory.swift index bb63f945ef..9d48c3d296 100644 --- a/novawallet/Modules/Banners/Factory/BannersViewFactory.swift +++ b/novawallet/Modules/Banners/Factory/BannersViewFactory.swift @@ -72,7 +72,7 @@ struct BannersViewFactory { private static func closeFeatureAvailability(for domain: Banners.Domain) -> Bool { switch domain { - case .dApps, .ahmKusama, .ahmPolkadot: + case .dApps, .ahmKusama, .ahmPolkadot, .appStoreMigration: false case .assets: true diff --git a/novawallet/Modules/Banners/Model/BannersModuleConfiguration.swift b/novawallet/Modules/Banners/Model/BannersModuleConfiguration.swift index cc035e300d..6e50036e8b 100644 --- a/novawallet/Modules/Banners/Model/BannersModuleConfiguration.swift +++ b/novawallet/Modules/Banners/Model/BannersModuleConfiguration.swift @@ -11,6 +11,7 @@ enum Banners { case assets case ahmKusama = "ahm_kusama" case ahmPolkadot = "ahm_polkadot" + case appStoreMigration = "appstore_migration" } } diff --git a/novawallet/Modules/InfoPopup/AHM/AHMInfoPopupInteractor.swift b/novawallet/Modules/InfoPopup/AHM/AHMInfoPopupInteractor.swift new file mode 100644 index 0000000000..dba83ae29d --- /dev/null +++ b/novawallet/Modules/InfoPopup/AHM/AHMInfoPopupInteractor.swift @@ -0,0 +1,36 @@ +import Foundation +import Keystore_iOS + +final class AHMInfoPopupInteractor: InfoPopupInteractor { + weak var ahmPresenter: AHMInfoPopupInteractorOutputProtocol? + + private let info: AHMRemoteData + private let chainRegistry: ChainRegistryProtocol + private let settingsManager: SettingsManagerProtocol + + init( + info: AHMRemoteData, + chainRegistry: ChainRegistryProtocol, + settingsManager: SettingsManagerProtocol + ) { + self.info = info + self.chainRegistry = chainRegistry + self.settingsManager = settingsManager + } + + override func setup() { + let sourceChain = chainRegistry.getChain(for: info.sourceData.chainId) + let destinationChain = chainRegistry.getChain(for: info.destinationData.chainId) + + ahmPresenter?.didReceive( + info: info, + sourceChain: sourceChain, + destinationChain: destinationChain + ) + } + + override func performMainAction() { + settingsManager.ahmInfoShownChains.add(info.sourceData.chainId) + presenter?.didCompleteMainAction() + } +} diff --git a/novawallet/Modules/InfoPopup/AHM/AHMInfoPopupPresenter.swift b/novawallet/Modules/InfoPopup/AHM/AHMInfoPopupPresenter.swift new file mode 100644 index 0000000000..dbb550d2a7 --- /dev/null +++ b/novawallet/Modules/InfoPopup/AHM/AHMInfoPopupPresenter.swift @@ -0,0 +1,67 @@ +import Foundation +import Foundation_iOS + +final class AHMInfoPopupPresenter: InfoPopupPresenter { + private let viewModelFactory: AHMInfoPopupViewModelFactoryProtocol + + private var info: AHMRemoteData? + private var sourceChain: ChainModel? + private var destinationChain: ChainModel? + + init( + interactor: AHMInfoPopupInteractor, + wireframe: InfoPopupWireframeProtocol, + viewModelFactory: AHMInfoPopupViewModelFactoryProtocol, + localizationManager: LocalizationManagerProtocol + ) { + self.viewModelFactory = viewModelFactory + + super.init( + interactor: interactor, + wireframe: wireframe, + mainAction: nil, + skipAction: nil, + learnMoreURL: nil, + localizationManager: localizationManager + ) + + interactor.ahmPresenter = self + } + + override func createViewModel( + bannerState: BannersState, + locale: Locale + ) -> InfoPopupViewModel? { + guard + let info, + let sourceChain, + let destinationChain + else { + return nil + } + + return viewModelFactory.createViewModel( + from: info, + sourceChain: sourceChain, + destinationChain: destinationChain, + bannerState: bannerState, + locale: locale + ) + } +} + +// MARK: - AHMInfoPopupInteractorOutputProtocol + +extension AHMInfoPopupPresenter: AHMInfoPopupInteractorOutputProtocol { + func didReceive( + info: AHMRemoteData, + sourceChain: ChainModel?, + destinationChain: ChainModel? + ) { + self.info = info + self.sourceChain = sourceChain + self.destinationChain = destinationChain + + provideViewModel() + } +} diff --git a/novawallet/Modules/InfoPopup/AHM/AHMInfoPopupProtocols.swift b/novawallet/Modules/InfoPopup/AHM/AHMInfoPopupProtocols.swift new file mode 100644 index 0000000000..7754183b72 --- /dev/null +++ b/novawallet/Modules/InfoPopup/AHM/AHMInfoPopupProtocols.swift @@ -0,0 +1,12 @@ +import Foundation + +protocol AHMInfoPopupInteractorOutputProtocol: AnyObject { + func didReceive( + info: AHMRemoteData, + sourceChain: ChainModel?, + destinationChain: ChainModel? + ) + func didCompleteMainAction() + func didCompleteSkipAction() + func didReceive(error: Error) +} diff --git a/novawallet/Modules/AHMInfo/Model/AHMInfoShownChains.swift b/novawallet/Modules/InfoPopup/AHM/AHMInfoShownChains.swift similarity index 100% rename from novawallet/Modules/AHMInfo/Model/AHMInfoShownChains.swift rename to novawallet/Modules/InfoPopup/AHM/AHMInfoShownChains.swift diff --git a/novawallet/Modules/AHMInfo/Factory/AHMInfoFetchOeprationFactory.swift b/novawallet/Modules/InfoPopup/AHM/Factory/AHMInfoFetchOperationFactory.swift similarity index 100% rename from novawallet/Modules/AHMInfo/Factory/AHMInfoFetchOeprationFactory.swift rename to novawallet/Modules/InfoPopup/AHM/Factory/AHMInfoFetchOperationFactory.swift diff --git a/novawallet/Modules/AHMInfo/Factory/AHMInfoViewFactory.swift b/novawallet/Modules/InfoPopup/AHM/Factory/AHMInfoPopupViewFactory.swift similarity index 77% rename from novawallet/Modules/AHMInfo/Factory/AHMInfoViewFactory.swift rename to novawallet/Modules/InfoPopup/AHM/Factory/AHMInfoPopupViewFactory.swift index 21bb631150..a05e046e93 100644 --- a/novawallet/Modules/AHMInfo/Factory/AHMInfoViewFactory.swift +++ b/novawallet/Modules/InfoPopup/AHM/Factory/AHMInfoPopupViewFactory.swift @@ -2,29 +2,26 @@ import Foundation import Foundation_iOS import Keystore_iOS -final class AHMInfoViewFactory { +struct AHMInfoPopupViewFactory { static func createView( info: AHMRemoteData - ) -> AHMInfoViewProtocol? { + ) -> InfoPopupViewProtocol? { let chainRegistry = ChainRegistryFacade.sharedRegistry + let localizationManager = LocalizationManager.shared - let interactor = AHMInfoInteractor( + let interactor = AHMInfoPopupInteractor( info: info, chainRegistry: chainRegistry, settingsManager: SettingsManager.shared ) - let wireframe = AHMInfoWireframe() - - let viewModelFactory = AHMInfoViewModelFactory() + let wireframe = InfoPopupWireframe() + let viewModelFactory = AHMInfoPopupViewModelFactory() - let localizationManager = LocalizationManager.shared - - let presenter = AHMInfoPresenter( + let presenter = AHMInfoPopupPresenter( interactor: interactor, wireframe: wireframe, viewModelFactory: viewModelFactory, - info: info, localizationManager: localizationManager ) @@ -37,7 +34,7 @@ final class AHMInfoViewFactory { return nil } - let view = AHMInfoViewController( + let view = InfoPopupViewController( presenter: presenter, bannersViewProvider: bannersModule, localizationManager: localizationManager diff --git a/novawallet/Modules/InfoPopup/AHM/Factory/AHMInfoPopupViewModelFactory.swift b/novawallet/Modules/InfoPopup/AHM/Factory/AHMInfoPopupViewModelFactory.swift new file mode 100644 index 0000000000..d369c4f11a --- /dev/null +++ b/novawallet/Modules/InfoPopup/AHM/Factory/AHMInfoPopupViewModelFactory.swift @@ -0,0 +1,244 @@ +import Foundation +import BigInt + +protocol AHMInfoPopupViewModelFactoryProtocol { + func createViewModel( + from info: AHMRemoteData, + sourceChain: ChainModel, + destinationChain: ChainModel, + bannerState: BannersState, + locale: Locale + ) -> InfoPopupViewModel +} + +final class AHMInfoPopupViewModelFactory { + private let assetFormatterFactory: AssetBalanceFormatterFactoryProtocol + private let dateFormatter = DateFormatter.fullDate + + init(assetFormatterFactory: AssetBalanceFormatterFactoryProtocol = AssetBalanceFormatterFactory()) { + self.assetFormatterFactory = assetFormatterFactory + } +} + +// MARK: - Private + +private extension AHMInfoPopupViewModelFactory { + func createTitle( + from info: AHMRemoteData, + sourceChain: ChainModel, + destinationChain: ChainModel, + locale: Locale + ) -> String { + let date = Date(timeIntervalSince1970: TimeInterval(info.timestamp)) + let sourceAsset = sourceChain.asset(for: info.sourceData.assetId) + let tokenSymbol = sourceAsset?.symbol ?? "" + + if info.migrationInProgress { + return R.string(preferredLanguages: locale.rLanguages) + .localizable.ahmInfoInProgressTitle( + dateFormatter.value(for: locale).string(from: date), + tokenSymbol, + destinationChain.name + ) + } else { + return R.string(preferredLanguages: locale.rLanguages) + .localizable.ahmInfoTitle( + dateFormatter.value(for: locale).string(from: date), + tokenSymbol, + destinationChain.name + ) + } + } + + func createFeatures( + from info: AHMRemoteData, + sourceChain: ChainModel, + destinationChain: ChainModel, + locale: Locale + ) -> [InfoPopupViewModel.Feature] { + var features: [InfoPopupViewModel.Feature] = [] + + guard + let sourceAsset = sourceChain.asset(for: info.sourceData.assetId), + let destinationAsset = destinationChain.asset(for: info.destinationData.assetId) + else { + return features + } + + // Min balance reduction + let minBalanceReduction = calculateReduction( + from: info.sourceData.minBalance, + to: info.destinationData.minBalance + ) + + let sourceMinBalance = formatBalance( + info.sourceData.minBalance, + asset: sourceAsset, + locale: locale + ) + let destMinBalance = formatBalance( + info.destinationData.minBalance, + asset: destinationAsset, + locale: locale + ) + + features.append( + InfoPopupViewModel.Feature( + emoji: "๐Ÿ‘›", + text: R.string(preferredLanguages: locale.rLanguages) + .localizable.ahmInfoFeatureMinBalance( + minBalanceReduction, + sourceMinBalance, + destMinBalance + ) + ) + ) + + // Fee reduction + let feeReduction = calculateReduction( + from: info.sourceData.averageFee, + to: info.destinationData.averageFee + ) + + let sourceFee = formatBalance( + info.sourceData.averageFee, + asset: sourceAsset, + locale: locale + ) + let destFee = formatBalance( + info.destinationData.averageFee, + asset: destinationAsset, + locale: locale + ) + + features.append( + InfoPopupViewModel.Feature( + emoji: "๐Ÿ’ธ", + text: R.string(preferredLanguages: locale.rLanguages) + .localizable.ahmInfoFeatureFees( + feeReduction, + sourceFee, + destFee + ) + ) + ) + + // More tokens + let tokensList = info.newTokenNames.joined(with: .commaSpace) + features.append( + InfoPopupViewModel.Feature( + emoji: "๐Ÿช™", + text: R.string(preferredLanguages: locale.rLanguages) + .localizable.ahmInfoFeatureTokens(tokensList) + ) + ) + + // Unified access + features.append( + InfoPopupViewModel.Feature( + emoji: "๐Ÿ—‚๏ธ", + text: R.string(preferredLanguages: locale.rLanguages) + .localizable.ahmInfoFeatureUnified(sourceAsset.symbol) + ) + ) + + // Pay fees in any token + features.append( + InfoPopupViewModel.Feature( + emoji: "๐Ÿงพ", + text: R.string(preferredLanguages: locale.rLanguages) + .localizable.ahmInfoFeaturePayFees() + ) + ) + + return features + } + + func createInfoItems( + sourceChain: ChainModel, + locale: Locale + ) -> [InfoPopupViewModel.InfoItem] { + [ + InfoPopupViewModel.InfoItem( + icon: .history, + text: R.string(preferredLanguages: locale.rLanguages) + .localizable.ahmInfoHistoryInfo(sourceChain.name) + ), + InfoPopupViewModel.InfoItem( + icon: .migration, + text: R.string(preferredLanguages: locale.rLanguages) + .localizable.ahmInfoMigrationInfo() + ) + ] + } + + func calculateReduction( + from source: BigUInt, + to destination: BigUInt + ) -> Int { + guard destination > 0 else { return 0 } + return Int(source / destination) + } + + func formatBalance( + _ value: BigUInt, + asset: AssetModel, + locale: Locale + ) -> String { + let assetInfo = asset.displayInfo + let formatter = assetFormatterFactory.createTokenFormatter( + for: assetInfo, + roundingMode: .down + ) + + return formatter + .value(for: locale) + .stringFromDecimal(value.decimal(assetInfo: assetInfo)) ?? "" + } +} + +// MARK: - AHMInfoPopupViewModelFactoryProtocol + +extension AHMInfoPopupViewModelFactory: AHMInfoPopupViewModelFactoryProtocol { + func createViewModel( + from info: AHMRemoteData, + sourceChain: ChainModel, + destinationChain: ChainModel, + bannerState: BannersState, + locale: Locale + ) -> InfoPopupViewModel { + let title = createTitle( + from: info, + sourceChain: sourceChain, + destinationChain: destinationChain, + locale: locale + ) + + let features = createFeatures( + from: info, + sourceChain: sourceChain, + destinationChain: destinationChain, + locale: locale + ) + + let infoItems = createInfoItems( + sourceChain: sourceChain, + locale: locale + ) + + return InfoPopupViewModel( + bannerState: bannerState, + title: title, + subtitle: R.string(preferredLanguages: locale.rLanguages) + .localizable.ahmInfoSubtitle(), + features: features, + infoItems: infoItems, + additionalInfo: nil, + mainActionTitle: R.string(preferredLanguages: locale.rLanguages) + .localizable.commonGotIt(), + skipActionTitle: nil, + learnMoreTitle: R.string(preferredLanguages: locale.rLanguages) + .localizable.commonLearnMore() + ) + } +} diff --git a/novawallet/Modules/AHMInfo/Model/AHMFullInfo.swift b/novawallet/Modules/InfoPopup/AHM/Model/AHMFullInfo.swift similarity index 100% rename from novawallet/Modules/AHMInfo/Model/AHMFullInfo.swift rename to novawallet/Modules/InfoPopup/AHM/Model/AHMFullInfo.swift diff --git a/novawallet/Modules/AHMInfo/Model/AHMFullInfoFactory.swift b/novawallet/Modules/InfoPopup/AHM/Model/AHMFullInfoFactory.swift similarity index 100% rename from novawallet/Modules/AHMInfo/Model/AHMFullInfoFactory.swift rename to novawallet/Modules/InfoPopup/AHM/Model/AHMFullInfoFactory.swift diff --git a/novawallet/Modules/AHMInfo/Model/AHMInfoRepository.swift b/novawallet/Modules/InfoPopup/AHM/Model/AHMInfoRepository.swift similarity index 100% rename from novawallet/Modules/AHMInfo/Model/AHMInfoRepository.swift rename to novawallet/Modules/InfoPopup/AHM/Model/AHMInfoRepository.swift diff --git a/novawallet/Modules/AHMInfo/Model/AHMNavigation.swift b/novawallet/Modules/InfoPopup/AHM/Model/AHMNavigation.swift similarity index 100% rename from novawallet/Modules/AHMInfo/Model/AHMNavigation.swift rename to novawallet/Modules/InfoPopup/AHM/Model/AHMNavigation.swift diff --git a/novawallet/Modules/AHMInfo/Model/AHMRemoteData.swift b/novawallet/Modules/InfoPopup/AHM/Model/AHMRemoteData.swift similarity index 100% rename from novawallet/Modules/AHMInfo/Model/AHMRemoteData.swift rename to novawallet/Modules/InfoPopup/AHM/Model/AHMRemoteData.swift diff --git a/novawallet/Modules/InfoPopup/ASM/ASMInfoPopupInteractor.swift b/novawallet/Modules/InfoPopup/ASM/ASMInfoPopupInteractor.swift new file mode 100644 index 0000000000..2a231ffd3d --- /dev/null +++ b/novawallet/Modules/InfoPopup/ASM/ASMInfoPopupInteractor.swift @@ -0,0 +1,20 @@ +import Foundation +import Keystore_iOS + +final class ASMInfoPopupInteractor: InfoPopupInteractor { + private let settingsManager: SettingsManagerProtocol + + init(settingsManager: SettingsManagerProtocol) { + self.settingsManager = settingsManager + } + + override func performMainAction() { + settingsManager.appStoreMigrationShown = true + presenter?.didCompleteMainAction() + } + + override func performSkipAction() { + settingsManager.appStoreMigrationShown = true + presenter?.didCompleteSkipAction() + } +} diff --git a/novawallet/Modules/InfoPopup/ASM/ASMInfoPopupPresenter.swift b/novawallet/Modules/InfoPopup/ASM/ASMInfoPopupPresenter.swift new file mode 100644 index 0000000000..81263e1579 --- /dev/null +++ b/novawallet/Modules/InfoPopup/ASM/ASMInfoPopupPresenter.swift @@ -0,0 +1,37 @@ +import Foundation +import Foundation_iOS + +final class ASMInfoPopupPresenter: InfoPopupPresenter { + private let viewModelFactory: ASMInfoPopupViewModelFactoryProtocol + + init( + interactor: InfoPopupInteractorInputProtocol, + wireframe: InfoPopupWireframeProtocol, + viewModelFactory: ASMInfoPopupViewModelFactoryProtocol, + learnMoreURL: URL?, + mainAction: InfoPopupAction?, + skipAction: InfoPopupAction?, + localizationManager: LocalizationManagerProtocol + ) { + self.viewModelFactory = viewModelFactory + + super.init( + interactor: interactor, + wireframe: wireframe, + mainAction: mainAction, + skipAction: skipAction, + learnMoreURL: learnMoreURL, + localizationManager: localizationManager + ) + } + + override func createViewModel( + bannerState: BannersState, + locale: Locale + ) -> InfoPopupViewModel? { + viewModelFactory.createViewModel( + bannerState: bannerState, + locale: locale + ) + } +} diff --git a/novawallet/Modules/InfoPopup/ASM/ASMInfoPopupViewModelFactory.swift b/novawallet/Modules/InfoPopup/ASM/ASMInfoPopupViewModelFactory.swift new file mode 100644 index 0000000000..e4bce36433 --- /dev/null +++ b/novawallet/Modules/InfoPopup/ASM/ASMInfoPopupViewModelFactory.swift @@ -0,0 +1,90 @@ +import Foundation + +protocol ASMInfoPopupViewModelFactoryProtocol { + func createViewModel( + bannerState: BannersState, + locale: Locale + ) -> InfoPopupViewModel +} + +final class ASMInfoPopupViewModelFactory {} + +private extension ASMInfoPopupViewModelFactory { + func createFeatures(locale: Locale) -> [InfoPopupViewModel.Feature] { + let languages = locale.rLanguages + + return [ + InfoPopupViewModel.Feature( + emoji: "๐Ÿคฉ", + text: R.string(preferredLanguages: languages) + .localizable.asmInfoFeatureUi() + ), + InfoPopupViewModel.Feature( + emoji: "๐ŸงŠ", + text: R.string(preferredLanguages: languages) + .localizable.asmInfoFeatureLiquidGlass() + ), + InfoPopupViewModel.Feature( + emoji: "๐Ÿ›ก๏ธ", + text: R.string(preferredLanguages: languages) + .localizable.asmInfoFeatureSecurity() + ), + InfoPopupViewModel.Feature( + emoji: "โœจ", + text: R.string(preferredLanguages: languages) + .localizable.asmInfoFeatureMore() + ) + ] + } + + func createInfoItems(locale: Locale) -> [InfoPopupViewModel.InfoItem] { + [ + InfoPopupViewModel.InfoItem( + icon: .migration, + text: R.string(preferredLanguages: locale.rLanguages) + .localizable.asmInfoAdditionalInfo() + ) + ] + } +} + +extension ASMInfoPopupViewModelFactory: ASMInfoPopupViewModelFactoryProtocol { + func createViewModel( + bannerState: BannersState, + locale: Locale + ) -> InfoPopupViewModel { + let languages = locale.rLanguages + + let title = R.string(preferredLanguages: languages) + .localizable.asmInfoTitle() + + let subtitle = R.string(preferredLanguages: languages) + .localizable.asmInfoSubtitle() + + let features = createFeatures(locale: locale) + + let additionalInfo = R.string(preferredLanguages: languages) + .localizable.asmInfoAdditionalInfo() + + let mainActionTitle = R.string(preferredLanguages: languages) + .localizable.asmInfoMainAction() + + let skipActionTitle = R.string(preferredLanguages: languages) + .localizable.commonSkip() + + let learnMoreTitle = R.string(preferredLanguages: languages) + .localizable.commonLearnMore() + + return InfoPopupViewModel( + bannerState: bannerState, + title: title, + subtitle: subtitle, + features: features, + infoItems: createInfoItems(locale: locale), + additionalInfo: nil, + mainActionTitle: mainActionTitle, + skipActionTitle: skipActionTitle, + learnMoreTitle: learnMoreTitle + ) + } +} diff --git a/novawallet/Modules/InfoPopup/ASM/ASMInfoViewFactory.swift b/novawallet/Modules/InfoPopup/ASM/ASMInfoViewFactory.swift new file mode 100644 index 0000000000..ea30f74975 --- /dev/null +++ b/novawallet/Modules/InfoPopup/ASM/ASMInfoViewFactory.swift @@ -0,0 +1,49 @@ +import Foundation +import Foundation_iOS +import Keystore_iOS + +struct ASMInfoPopupViewFactory { + static func createView( + learnMoreURL: URL?, + mainAction: InfoPopupAction?, + ) -> InfoPopupViewProtocol? { + let localizationManager = LocalizationManager.shared + + let interactor = ASMInfoPopupInteractor( + settingsManager: SettingsManager.shared + ) + + let wireframe = InfoPopupWireframe() + let viewModelFactory = ASMInfoPopupViewModelFactory() + + let presenter = ASMInfoPopupPresenter( + interactor: interactor, + wireframe: wireframe, + viewModelFactory: viewModelFactory, + learnMoreURL: learnMoreURL, + mainAction: mainAction, + skipAction: .custom {}, + localizationManager: localizationManager + ) + + guard let bannersModule = BannersViewFactory.createView( + domain: .appStoreMigration, + output: presenter, + inputOwner: presenter, + locale: localizationManager.selectedLocale + ) else { + return nil + } + + let view = InfoPopupViewController( + presenter: presenter, + bannersViewProvider: bannersModule, + localizationManager: localizationManager + ) + + presenter.view = view + interactor.presenter = presenter + + return view + } +} diff --git a/novawallet/Modules/InfoPopup/InfoPopupInteractor.swift b/novawallet/Modules/InfoPopup/InfoPopupInteractor.swift new file mode 100644 index 0000000000..d548acd596 --- /dev/null +++ b/novawallet/Modules/InfoPopup/InfoPopupInteractor.swift @@ -0,0 +1,21 @@ +import Foundation + +class InfoPopupInteractor { + weak var presenter: InfoPopupInteractorOutputProtocol? + + func setup() { + presenter?.didSetup() + } + + func performMainAction() { + presenter?.didCompleteMainAction() + } + + func performSkipAction() { + presenter?.didCompleteSkipAction() + } +} + +// MARK: - InfoPopupInteractorInputProtocol + +extension InfoPopupInteractor: InfoPopupInteractorInputProtocol {} diff --git a/novawallet/Modules/InfoPopup/InfoPopupPresenter.swift b/novawallet/Modules/InfoPopup/InfoPopupPresenter.swift new file mode 100644 index 0000000000..2917827fb7 --- /dev/null +++ b/novawallet/Modules/InfoPopup/InfoPopupPresenter.swift @@ -0,0 +1,134 @@ +import Foundation +import Foundation_iOS + +class InfoPopupPresenter: BannersModuleInputOwnerProtocol { + weak var view: InfoPopupViewProtocol? + weak var bannersModule: BannersModuleInputProtocol? + + let wireframe: InfoPopupWireframeProtocol + let interactor: InfoPopupInteractorInputProtocol + let localizationManager: LocalizationManagerProtocol + + var mainAction: InfoPopupAction? + var skipAction: InfoPopupAction? + var learnMoreURL: URL? + + init( + interactor: InfoPopupInteractorInputProtocol, + wireframe: InfoPopupWireframeProtocol, + mainAction: InfoPopupAction?, + skipAction: InfoPopupAction?, + learnMoreURL: URL?, + localizationManager: LocalizationManagerProtocol + ) { + self.interactor = interactor + self.wireframe = wireframe + self.mainAction = mainAction + self.skipAction = skipAction + self.learnMoreURL = learnMoreURL + self.localizationManager = localizationManager + } + + func createViewModel( + bannerState _: BannersState, + locale _: Locale + ) -> InfoPopupViewModel? { + fatalError("Subclasses must override createViewModel") + } + + func provideViewModel() { + guard let viewModel = createViewModel( + bannerState: bannersModule?.bannersState ?? .unavailable, + locale: localizationManager.selectedLocale + ) else { + return + } + + view?.didReceive(viewModel: viewModel) + } +} + +// MARK: - InfoPopupPresenterProtocol + +extension InfoPopupPresenter: InfoPopupPresenterProtocol { + func setup() { + interactor.setup() + + guard bannersModule?.locale != localizationManager.selectedLocale else { return } + + bannersModule?.updateLocale(localizationManager.selectedLocale) + } + + func actionMain() { + interactor.performMainAction() + } + + func actionSkip() { + interactor.performSkipAction() + } + + func actionLearnMore() { + guard let view, let learnMoreURL else { return } + + wireframe.showWeb( + url: learnMoreURL, + from: view, + style: .automatic + ) + } +} + +// MARK: - InfoPopupInteractorOutputProtocol + +extension InfoPopupPresenter: InfoPopupInteractorOutputProtocol { + func didSetup() { + provideViewModel() + } + + func didCompleteMainAction() { + wireframe.proceed(from: view, action: mainAction) + } + + func didCompleteSkipAction() { + wireframe.proceed(from: view, action: skipAction) + } + + func didReceive(error: Error) { + wireframe.present( + error: error, + from: view, + locale: localizationManager.selectedLocale + ) + } +} + +// MARK: - BannersModuleOutputProtocol + +extension InfoPopupPresenter: BannersModuleOutputProtocol { + func didReceiveBanners(state _: BannersState) { + provideViewModel() + } + + func didUpdateContent(state _: BannersState) { + provideViewModel() + } + + func didReceive(_ error: Error) { + wireframe.present( + error: error, + from: view, + locale: localizationManager.selectedLocale + ) + } +} + +// MARK: - Localizable + +extension InfoPopupPresenter: Localizable { + func applyLocalization() { + guard let view, view.isSetup else { return } + + provideViewModel() + bannersModule?.updateLocale(localizationManager.selectedLocale) + } +} diff --git a/novawallet/Modules/InfoPopup/InfoPopupProtocols.swift b/novawallet/Modules/InfoPopup/InfoPopupProtocols.swift new file mode 100644 index 0000000000..7be365d1e1 --- /dev/null +++ b/novawallet/Modules/InfoPopup/InfoPopupProtocols.swift @@ -0,0 +1,30 @@ +import Foundation + +protocol InfoPopupViewProtocol: ControllerBackedProtocol { + func didReceive(viewModel: InfoPopupViewModel) +} + +protocol InfoPopupPresenterProtocol: AnyObject { + func setup() + func actionMain() + func actionSkip() + func actionLearnMore() +} + +protocol InfoPopupInteractorInputProtocol: AnyObject { + func setup() + func performMainAction() + func performSkipAction() +} + +protocol InfoPopupInteractorOutputProtocol: AnyObject { + func didSetup() + func didCompleteMainAction() + func didCompleteSkipAction() + func didReceive(error: Error) +} + +protocol InfoPopupWireframeProtocol: WebPresentable, AlertPresentable, ErrorPresentable { + func complete(from view: InfoPopupViewProtocol?) + func proceed(from view: InfoPopupViewProtocol?, action: InfoPopupAction?) +} diff --git a/novawallet/Modules/AHMInfo/AHMInfoViewController.swift b/novawallet/Modules/InfoPopup/InfoPopupViewController.swift similarity index 53% rename from novawallet/Modules/AHMInfo/AHMInfoViewController.swift rename to novawallet/Modules/InfoPopup/InfoPopupViewController.swift index 53c3387496..c4eba17b30 100644 --- a/novawallet/Modules/AHMInfo/AHMInfoViewController.swift +++ b/novawallet/Modules/InfoPopup/InfoPopupViewController.swift @@ -1,15 +1,17 @@ import UIKit import Foundation_iOS -final class AHMInfoViewController: UIViewController, ViewHolder { - typealias RootViewType = AHMInfoViewLayout +final class InfoPopupViewController: UIViewController, ViewHolder { + typealias RootViewType = InfoPopupViewLayout - let presenter: AHMInfoPresenterProtocol - let bannersViewProvider: BannersViewProviderProtocol + let presenter: InfoPopupPresenterProtocol + let bannersViewProvider: BannersViewProviderProtocol? + + private var learnMoreTitle: String? init( - presenter: AHMInfoPresenterProtocol, - bannersViewProvider: BannersViewProviderProtocol, + presenter: InfoPopupPresenterProtocol, + bannersViewProvider: BannersViewProviderProtocol?, localizationManager: LocalizationManagerProtocol ) { self.presenter = presenter @@ -24,13 +26,12 @@ final class AHMInfoViewController: UIViewController, ViewHolder { } override func loadView() { - view = AHMInfoViewLayout() + view = InfoPopupViewLayout() } override func viewDidLoad() { super.viewDidLoad() - setupNavigation() setupBanner() setupHandlers() presenter.setup() @@ -39,12 +40,15 @@ final class AHMInfoViewController: UIViewController, ViewHolder { // MARK: - Private -private extension AHMInfoViewController { +private extension InfoPopupViewController { func setupNavigation() { + guard let learnMoreTitle else { + navigationController?.navigationBar.topItem?.rightBarButtonItem = nil + return + } + let barButtonItem = UIBarButtonItem( - title: R.string( - preferredLanguages: selectedLocale.rLanguages - ).localizable.commonLearnMore(), + title: learnMoreTitle, style: .plain, target: self, action: #selector(actionLearnMore) @@ -55,6 +59,11 @@ private extension AHMInfoViewController { } func setupBanner() { + guard let bannersViewProvider else { + rootView.bannerContainer.isHidden = true + return + } + bannersViewProvider.setupBanners( on: self, view: rootView.bannerContainer @@ -63,20 +72,30 @@ private extension AHMInfoViewController { } func setupHandlers() { - rootView.actionButton.addTarget( + rootView.mainActionButton.addTarget( + self, + action: #selector(actionMain), + for: .touchUpInside + ) + + rootView.skipButton.addTarget( self, - action: #selector(actionGotIt), + action: #selector(actionSkip), for: .touchUpInside ) } func updateBannerHeight() { - let bannerHeight = bannersViewProvider.getMaxBannerHeight() + let bannerHeight = bannersViewProvider?.getMaxBannerHeight() ?? 0 rootView.updateBannerHeight(bannerHeight) } - @objc func actionGotIt() { - presenter.actionGotIt() + @objc func actionMain() { + presenter.actionMain() + } + + @objc func actionSkip() { + presenter.actionSkip() } @objc func actionLearnMore() { @@ -84,18 +103,21 @@ private extension AHMInfoViewController { } } -// MARK: - AHMInfoViewProtocol +// MARK: - InfoPopupViewProtocol -extension AHMInfoViewController: AHMInfoViewProtocol { - func didReceive(viewModel: AHMInfoViewModel) { +extension InfoPopupViewController: InfoPopupViewProtocol { + func didReceive(viewModel: InfoPopupViewModel) { rootView.bind(viewModel) updateBannerHeight() + + learnMoreTitle = viewModel.learnMoreTitle + setupNavigation() } } // MARK: - Localizable -extension AHMInfoViewController: Localizable { +extension InfoPopupViewController: Localizable { func applyLocalization() { guard isViewLoaded else { return } diff --git a/novawallet/Modules/AHMInfo/AHMInfoViewLayout.swift b/novawallet/Modules/InfoPopup/InfoPopupViewLayout.swift similarity index 52% rename from novawallet/Modules/AHMInfo/AHMInfoViewLayout.swift rename to novawallet/Modules/InfoPopup/InfoPopupViewLayout.swift index 879375c191..077d703c6a 100644 --- a/novawallet/Modules/AHMInfo/AHMInfoViewLayout.swift +++ b/novawallet/Modules/InfoPopup/InfoPopupViewLayout.swift @@ -1,7 +1,7 @@ import UIKit import UIKit_iOS -final class AHMInfoViewLayout: SCSingleActionLayoutView { +final class InfoPopupViewLayout: SCSingleActionLayoutView { let bannerContainer: UIView = .create { view in view.backgroundColor = .clear } @@ -13,6 +13,7 @@ final class AHMInfoViewLayout: SCSingleActionLayoutView { let subtitleLabel: UILabel = .create { label in label.apply(style: .footnoteSecondary) + label.numberOfLines = 0 } let featuresStackView: UIStackView = .create { view in @@ -29,10 +30,24 @@ final class AHMInfoViewLayout: SCSingleActionLayoutView { view.alignment = .leading } - var actionButton: TriangularedButton { + let additionalInfoLabel: UILabel = .create { label in + label.apply(style: .footnoteSecondary) + label.numberOfLines = 0 + } + + let skipButton: TriangularedButton = .create { button in + button.applyAccessoryStyle() + button.triangularedView?.strokeWidth = .zero + button.imageWithTitleView?.titleColor = R.color.colorButtonTextAccent()! + button.imageWithTitleView?.titleFont = .semiBoldSubheadline + } + + var mainActionButton: TriangularedButton { genericActionView } + private var separatorView: UIView? + override func layoutSubviews() { super.layoutSubviews() @@ -52,24 +67,39 @@ final class AHMInfoViewLayout: SCSingleActionLayoutView { addArrangedSubview(titleLabel, spacingAfter: Constants.titleToSubtitle) addArrangedSubview(subtitleLabel, spacingAfter: Constants.subtitleToFeatures) - addArrangedSubview(featuresStackView, spacingAfter: Constants.featuresToSeparator) - addArrangedSubview(createSeparator(), spacingAfter: Constants.separatorToInfo) + separatorView = createSeparator() + addArrangedSubview(separatorView!, spacingAfter: Constants.separatorToInfo) + + addArrangedSubview(infoStackView, spacingAfter: Constants.infoToAdditional) + addArrangedSubview(additionalInfoLabel) - addArrangedSubview(infoStackView) + setupSkipButton() } override func setupStyle() { super.setupStyle() - actionButton.applyDefaultStyle() + mainActionButton.applyDefaultStyle() } } // MARK: - Private -private extension AHMInfoViewLayout { +private extension InfoPopupViewLayout { + func setupSkipButton() { + addSubview(skipButton) + + skipButton.snp.makeConstraints { make in + make.leading.trailing.equalToSuperview().inset(UIConstants.horizontalInset) + make.bottom.equalTo(safeAreaLayoutGuide).inset(UIConstants.actionBottomInset) + make.height.equalTo(UIConstants.actionHeight) + } + + skipButton.isHidden = true + } + func createSeparator() -> UIView { let separator = UIView.createSeparator() @@ -80,12 +110,11 @@ private extension AHMInfoViewLayout { return separator } - func addFeatureView(_ feature: AHMInfoViewModel.Feature) { + func addFeatureView(_ feature: InfoPopupViewModel.Feature) { let featureView: GenericPairValueView = .create { view in view.makeHorizontal() let emojiLabel = UILabel() - emojiLabel.apply(style: .title3Primary) emojiLabel.textAlignment = .left @@ -110,7 +139,7 @@ private extension AHMInfoViewLayout { featuresStackView.addArrangedSubview(featureView) } - func addInfoView(_ info: AHMInfoViewModel.Info) { + func addInfoItemView(_ infoItem: InfoPopupViewModel.InfoItem) { let infoView: IconDetailsView = .create { view in view.detailsLabel.apply(style: .footnoteSecondary) view.detailsLabel.numberOfLines = 0 @@ -118,40 +147,83 @@ private extension AHMInfoViewLayout { view.iconWidth = Constants.iconWidth } - let icon: UIImage? = switch info.type { + let icon: UIImage? = switch infoItem.icon { case .history: R.image.iconHistoryGray18() case .migration: R.image.iconStarGray18() + case let .custom(image): + image } infoView.imageView.image = icon - infoView.detailsLabel.text = info.text + infoView.detailsLabel.text = infoItem.text infoStackView.addArrangedSubview(infoView) } func clearStacks() { - [ - featuresStackView, - infoStackView - ].forEach { $0.arrangedSubviews.forEach { $0.removeFromSuperview() } } + featuresStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } + infoStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } + } + + func updateSkipButtonVisibility(hasSkipButton: Bool) { + guard hasSkipButton != !skipButton.isHidden else { return } + + skipButton.isHidden = !hasSkipButton + + if hasSkipButton { + genericActionView.snp.remakeConstraints { make in + make.leading.trailing.equalToSuperview().inset(UIConstants.horizontalInset) + make.bottom.equalTo(skipButton.snp.top).inset(-Constants.buttonsSpacing) + make.height.equalTo(UIConstants.actionHeight) + } + } else { + genericActionView.snp.remakeConstraints { make in + make.leading.trailing.equalToSuperview().inset(UIConstants.horizontalInset) + make.bottom.equalTo(safeAreaLayoutGuide).inset(UIConstants.actionBottomInset) + make.height.equalTo(UIConstants.actionHeight) + } + } } } // MARK: - Internal -extension AHMInfoViewLayout { - func bind(_ viewModel: AHMInfoViewModel) { +extension InfoPopupViewLayout { + func bind(_ viewModel: InfoPopupViewModel) { titleLabel.text = viewModel.title - subtitleLabel.text = viewModel.subtitle - clearStacks() + if let subtitle = viewModel.subtitle { + subtitleLabel.text = subtitle + subtitleLabel.isHidden = false + } else { + subtitleLabel.isHidden = true + } + clearStacks() viewModel.features.forEach { addFeatureView($0) } - viewModel.info.forEach { addInfoView($0) } + viewModel.infoItems.forEach { addInfoItemView($0) } - actionButton.imageWithTitleView?.title = viewModel.actionButtonTitle + let hasInfoItems = !viewModel.infoItems.isEmpty + separatorView?.isHidden = !hasInfoItems + infoStackView.isHidden = !hasInfoItems + + if let additionalInfo = viewModel.additionalInfo { + additionalInfoLabel.text = additionalInfo + additionalInfoLabel.isHidden = false + } else { + additionalInfoLabel.isHidden = true + } + + mainActionButton.imageWithTitleView?.title = viewModel.mainActionTitle + + if let skipTitle = viewModel.skipActionTitle { + skipButton.imageWithTitleView?.title = skipTitle + updateSkipButtonVisibility(hasSkipButton: true) + } else { + updateSkipButtonVisibility(hasSkipButton: false) + } } func updateBannerHeight(_ height: CGFloat) { @@ -164,7 +236,7 @@ extension AHMInfoViewLayout { // MARK: - Constants -private extension AHMInfoViewLayout { +private extension InfoPopupViewLayout { enum Constants { static let topMargin: CGFloat = 12 static let bannerToTitle: CGFloat = 16 @@ -172,6 +244,7 @@ private extension AHMInfoViewLayout { static let subtitleToFeatures: CGFloat = 17 static let featuresToSeparator: CGFloat = 13 static let separatorToInfo: CGFloat = 10 + static let infoToAdditional: CGFloat = 10 static let stackViewSpacing: CGFloat = 10 static let featureIconToText: CGFloat = 16 static let infoIconToText: CGFloat = 16 @@ -179,5 +252,6 @@ private extension AHMInfoViewLayout { static let separatorHeight: CGFloat = 1 static let emojiLabelHeight: CGFloat = 24 static let iconWidth: CGFloat = 18 + static let buttonsSpacing: CGFloat = 8.0 } } diff --git a/novawallet/Modules/InfoPopup/InfoPopupWireframe.swift b/novawallet/Modules/InfoPopup/InfoPopupWireframe.swift new file mode 100644 index 0000000000..80b7ef4f8c --- /dev/null +++ b/novawallet/Modules/InfoPopup/InfoPopupWireframe.swift @@ -0,0 +1,30 @@ +import UIKit + +final class InfoPopupWireframe: InfoPopupWireframeProtocol { + func complete(from view: InfoPopupViewProtocol?) { + view?.controller.dismiss(animated: true, completion: nil) + } + + func proceed(from view: InfoPopupViewProtocol?, action: InfoPopupAction?) { + guard let action else { + complete(from: view) + return + } + + switch action { + case let .url(url): + complete(from: view) + UIApplication.shared.open(url, options: [:], completionHandler: nil) + + case let .deepLink(deepLink): + complete(from: view) + if let url = URL(string: deepLink) { + UIApplication.shared.open(url, options: [:], completionHandler: nil) + } + + case let .custom(closure): + complete(from: view) + closure() + } + } +} diff --git a/novawallet/Modules/InfoPopup/Model/InfoPopupAction.swift b/novawallet/Modules/InfoPopup/Model/InfoPopupAction.swift new file mode 100644 index 0000000000..27e681490c --- /dev/null +++ b/novawallet/Modules/InfoPopup/Model/InfoPopupAction.swift @@ -0,0 +1,7 @@ +import Foundation + +enum InfoPopupAction { + case url(URL) + case deepLink(String) + case custom(() -> Void) +} diff --git a/novawallet/Modules/InfoPopup/Model/InfoPopupContent.swift b/novawallet/Modules/InfoPopup/Model/InfoPopupContent.swift new file mode 100644 index 0000000000..7d05af4c4a --- /dev/null +++ b/novawallet/Modules/InfoPopup/Model/InfoPopupContent.swift @@ -0,0 +1,43 @@ +import Foundation + +struct InfoPopupContent { + let bannerDomain: Banners.Domain? + let title: String + let subtitle: String? + let features: [Feature] + let additionalInfo: String? + let mainActionTitle: String + let skipActionTitle: String? + let learnMoreURL: URL? + + struct Feature { + let emoji: String + let text: String + } +} + +extension InfoPopupContent { + static func from(localized: InfoPopupLocalizedContent, locale: Locale) -> InfoPopupContent { + InfoPopupContent( + bannerDomain: localized.bannerDomain, + title: localized.title(locale), + subtitle: localized.subtitle?(locale), + features: localized.features(locale).map { Feature(emoji: $0.emoji, text: $0.text) }, + additionalInfo: localized.additionalInfo?(locale), + mainActionTitle: localized.mainActionTitle(locale), + skipActionTitle: localized.skipActionTitle?(locale), + learnMoreURL: localized.learnMoreURL + ) + } +} + +struct InfoPopupLocalizedContent { + let bannerDomain: Banners.Domain? + let title: (Locale) -> String + let subtitle: ((Locale) -> String)? + let features: (Locale) -> [InfoPopupContent.Feature] + let additionalInfo: ((Locale) -> String)? + let mainActionTitle: (Locale) -> String + let skipActionTitle: ((Locale) -> String)? + let learnMoreURL: URL? +} diff --git a/novawallet/Modules/InfoPopup/Model/InfoPopupContentBuilder.swift b/novawallet/Modules/InfoPopup/Model/InfoPopupContentBuilder.swift new file mode 100644 index 0000000000..32fa6f0000 --- /dev/null +++ b/novawallet/Modules/InfoPopup/Model/InfoPopupContentBuilder.swift @@ -0,0 +1,81 @@ +import Foundation + +final class InfoPopupContentBuilder { + private var bannerDomain: Banners.Domain? + private var title: ((Locale) -> String)? + private var subtitle: ((Locale) -> String)? + private var features: ((Locale) -> [InfoPopupContent.Feature])? + private var additionalInfo: ((Locale) -> String)? + private var mainActionTitle: ((Locale) -> String)? + private var skipActionTitle: ((Locale) -> String)? + private var learnMoreURL: URL? + + @discardableResult + func with(bannerDomain: Banners.Domain?) -> Self { + self.bannerDomain = bannerDomain + return self + } + + @discardableResult + func with(title: @escaping (Locale) -> String) -> Self { + self.title = title + return self + } + + @discardableResult + func with(subtitle: @escaping (Locale) -> String) -> Self { + self.subtitle = subtitle + return self + } + + @discardableResult + func with(features: @escaping (Locale) -> [InfoPopupContent.Feature]) -> Self { + self.features = features + return self + } + + @discardableResult + func with(additionalInfo: @escaping (Locale) -> String) -> Self { + self.additionalInfo = additionalInfo + return self + } + + @discardableResult + func with(mainActionTitle: @escaping (Locale) -> String) -> Self { + self.mainActionTitle = mainActionTitle + return self + } + + @discardableResult + func with(skipActionTitle: @escaping (Locale) -> String) -> Self { + self.skipActionTitle = skipActionTitle + return self + } + + @discardableResult + func with(learnMoreURL: URL?) -> Self { + self.learnMoreURL = learnMoreURL + return self + } + + func build() -> InfoPopupLocalizedContent? { + guard + let title, + let features, + let mainActionTitle + else { + return nil + } + + return InfoPopupLocalizedContent( + bannerDomain: bannerDomain, + title: title, + subtitle: subtitle, + features: features, + additionalInfo: additionalInfo, + mainActionTitle: mainActionTitle, + skipActionTitle: skipActionTitle, + learnMoreURL: learnMoreURL + ) + } +} diff --git a/novawallet/Modules/InfoPopup/Model/InfoPopupViewModel.swift b/novawallet/Modules/InfoPopup/Model/InfoPopupViewModel.swift new file mode 100644 index 0000000000..9b6bc6d674 --- /dev/null +++ b/novawallet/Modules/InfoPopup/Model/InfoPopupViewModel.swift @@ -0,0 +1,43 @@ +import UIKit + +struct InfoPopupViewModel { + let bannerState: BannersState + let title: String + let subtitle: String? + let features: [Feature] + let infoItems: [InfoItem] + let additionalInfo: String? + let mainActionTitle: String + let skipActionTitle: String? + let learnMoreTitle: String? + + struct Feature { + let emoji: String + let text: String + } + + struct InfoItem { + let icon: Icon + let text: String + + enum Icon { + case history + case migration + case custom(UIImage?) + } + } + + static func empty(with bannerState: BannersState) -> InfoPopupViewModel { + InfoPopupViewModel( + bannerState: bannerState, + title: "", + subtitle: nil, + features: [], + infoItems: [], + additionalInfo: nil, + mainActionTitle: "", + skipActionTitle: nil, + learnMoreTitle: nil + ) + } +} diff --git a/novawallet/Modules/MainTabBar/MainTabBarWireframe.swift b/novawallet/Modules/MainTabBar/MainTabBarWireframe.swift index 4683fabc7b..92714431ad 100644 --- a/novawallet/Modules/MainTabBar/MainTabBarWireframe.swift +++ b/novawallet/Modules/MainTabBar/MainTabBarWireframe.swift @@ -198,7 +198,7 @@ private extension MainTabBarWireframe { in view: MainTabBarViewProtocol?, with info: AHMRemoteData ) { - guard let ahmInfoView = AHMInfoViewFactory.createView(info: info) else { + guard let ahmInfoView = AHMInfoPopupViewFactory.createView(info: info) else { return } @@ -293,7 +293,7 @@ extension MainTabBarWireframe: MainTabBarWireframeProtocol { with info: [AHMRemoteData] ) { let ahmInfoControllers = info - .compactMap { AHMInfoViewFactory.createView(info: $0) } + .compactMap { AHMInfoPopupViewFactory.createView(info: $0) } .map { let navigationController = NovaNavigationController(rootViewController: $0.controller) diff --git a/novawallet/Modules/Staking/StakingMain/StakingMainPresenter.swift b/novawallet/Modules/Staking/StakingMain/StakingMainPresenter.swift index 7c02faeae8..7a44c06b73 100644 --- a/novawallet/Modules/Staking/StakingMain/StakingMainPresenter.swift +++ b/novawallet/Modules/Staking/StakingMain/StakingMainPresenter.swift @@ -9,7 +9,7 @@ final class StakingMainPresenter { let childPresenterFactory: StakingMainPresenterFactoryProtocol let viewModelFactory: StakingMainViewModelFactoryProtocol - let ahmViewModelFactory: AHMInfoViewModelFactoryProtocol + let inlinableAlertViewModelFactory: InlinableAlertViewModelFactoryProtocol let stakingOption: Multistaking.ChainAssetOption let localizationManager: LocalizationManagerProtocol let logger: LoggerProtocol? @@ -24,7 +24,7 @@ final class StakingMainPresenter { stakingOption: Multistaking.ChainAssetOption, childPresenterFactory: StakingMainPresenterFactoryProtocol, viewModelFactory: StakingMainViewModelFactoryProtocol, - ahmViewModelFactory: AHMInfoViewModelFactoryProtocol, + inlinableAlertViewModelFactory: InlinableAlertViewModelFactoryProtocol, localizationManager: LocalizationManagerProtocol, logger: LoggerProtocol? ) { @@ -33,7 +33,7 @@ final class StakingMainPresenter { self.stakingOption = stakingOption self.childPresenterFactory = childPresenterFactory self.viewModelFactory = viewModelFactory - self.ahmViewModelFactory = ahmViewModelFactory + self.inlinableAlertViewModelFactory = inlinableAlertViewModelFactory self.localizationManager = localizationManager self.logger = logger } @@ -50,7 +50,7 @@ private extension StakingMainPresenter { func provideAHMAlertModel() { let ahmAlertModel: InlinableAlertView.Model? = if let ahmInfo { - ahmViewModelFactory.createStakingDetailsAlertViewModel( + inlinableAlertViewModelFactory.createStakingDetailsAlertViewModel( info: ahmInfo, locale: localizationManager.selectedLocale ) diff --git a/novawallet/Modules/Staking/StakingMain/StakingMainViewFactory.swift b/novawallet/Modules/Staking/StakingMain/StakingMainViewFactory.swift index 773c80b94b..8371627012 100644 --- a/novawallet/Modules/Staking/StakingMain/StakingMainViewFactory.swift +++ b/novawallet/Modules/Staking/StakingMain/StakingMainViewFactory.swift @@ -44,7 +44,7 @@ enum StakingMainViewFactory { stakingOption: stakingOption, childPresenterFactory: childPresenterFactory, viewModelFactory: StakingMainViewModelFactory(), - ahmViewModelFactory: AHMInfoViewModelFactory(), + inlinableAlertViewModelFactory: InlinableAlertViewModelFactory(), localizationManager: localizationManager, logger: Logger.shared ) diff --git a/novawallet/en.lproj/Localizable.strings b/novawallet/en.lproj/Localizable.strings index 19876b5d90..23b1b40159 100644 --- a/novawallet/en.lproj/Localizable.strings +++ b/novawallet/en.lproj/Localizable.strings @@ -2042,3 +2042,11 @@ Do you want to continue?"; "inlinable.alert.wo.message" = "Beware of scammers. No one can unlock a watch-only wallet or its funds"; "create.watch.only.missing.any.address" = "Enter at least one address..."; "asset.list.watch.only.balance" = "Watch-only balance"; +"asm.info.title" = "Transfer all your data to the new Nova app โ€” smooth & easy"; +"asm.info.subtitle" = "Nova Wallet is moving to a new App Store account. The same team, vision, and top-level security remain. The new app already includes exciting improvements:"; +"asm.info.additional.info" = "All your wallets, notification settings, preferences, and more will be securely transferred"; +"asm.info.main.action" = "Download Nova & Migrate"; +"asm.info.feature.ui" = "Polished UI design"; +"asm.info.feature.liquidGlass" = "Liquid Glass (iOS 26) support"; +"asm.info.feature.security" = "Future updates for stability and security"; +"asm.info.feature.more" = "More features coming soon!";