diff --git a/unstoppable-ios-app/domains-manager-ios-preview/AppContext/PreviewCoreAppCoordinator.swift b/unstoppable-ios-app/domains-manager-ios-preview/AppContext/PreviewCoreAppCoordinator.swift index 00da40cfa..67727a28e 100644 --- a/unstoppable-ios-app/domains-manager-ios-preview/AppContext/PreviewCoreAppCoordinator.swift +++ b/unstoppable-ios-app/domains-manager-ios-preview/AppContext/PreviewCoreAppCoordinator.swift @@ -35,6 +35,8 @@ final class CoreAppCoordinator: CoreAppCoordinatorProtocol { } + func showFullMaintenanceModeOn(maintenanceData: MaintenanceModeData) { } + func setKeyWindow() { } diff --git a/unstoppable-ios-app/domains-manager-ios-preview/AppContext/PreviewUDFeatureFlagsService.swift b/unstoppable-ios-app/domains-manager-ios-preview/AppContext/PreviewUDFeatureFlagsService.swift index aa9e08c12..238e41507 100644 --- a/unstoppable-ios-app/domains-manager-ios-preview/AppContext/PreviewUDFeatureFlagsService.swift +++ b/unstoppable-ios-app/domains-manager-ios-preview/AppContext/PreviewUDFeatureFlagsService.swift @@ -6,8 +6,15 @@ // import Foundation +import Combine final class UDFeatureFlagsService: UDFeatureFlagsServiceProtocol { + private(set) var featureFlagPublisher = PassthroughSubject() + + func entityValueFor(flag: UDFeatureFlag) -> T? { + nil + } + func valueFor(flag: UDFeatureFlag) -> Bool { true } diff --git a/unstoppable-ios-app/domains-manager-ios-preview/Entities/PreviewMPCConnector.swift b/unstoppable-ios-app/domains-manager-ios-preview/Entities/PreviewMPCConnector.swift index 97ab5a51c..891fd2068 100644 --- a/unstoppable-ios-app/domains-manager-ios-preview/Entities/PreviewMPCConnector.swift +++ b/unstoppable-ios-app/domains-manager-ios-preview/Entities/PreviewMPCConnector.swift @@ -48,6 +48,10 @@ extension FB_UD_MPC { .init(id: "1", status: "", type: "") } + func startSendETHTransaction(accessToken: String, accountId: String, assetId: String, destinationAddress: String, data: String, value: String) async throws -> FB_UD_MPC.OperationDetails { + .init(id: "1", status: "", type: "") + } + func waitForOperationCompleted(accessToken: String, operationId: String) async throws { } @@ -56,7 +60,6 @@ extension FB_UD_MPC { "" } - func fetchCryptoPortfolioForMPC(wallet: String, accessToken: String) async throws -> [WalletTokenPortfolio] { MockEntitiesFabric.Wallet.mockEntities()[0].balance } diff --git a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj index db1f6252d..547f01556 100644 --- a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj +++ b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj @@ -848,6 +848,23 @@ C64BCB7B2A6FB5B700EF8B47 /* MessagingChatMessageImageDataTypeDisplayInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64BCB7A2A6FB5B700EF8B47 /* MessagingChatMessageImageDataTypeDisplayInfo.swift */; }; C64F6C4F2C4E72A900D89FEF /* EthereumSendTransactionPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C4E2C4E72A900D89FEF /* EthereumSendTransactionPayload.swift */; }; C64F6C502C4E72A900D89FEF /* EthereumSendTransactionPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C4E2C4E72A900D89FEF /* EthereumSendTransactionPayload.swift */; }; + C64F6C532C4FAF2200D89FEF /* FullMaintenanceModeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C522C4FAF2200D89FEF /* FullMaintenanceModeView.swift */; }; + C64F6C542C4FAF2200D89FEF /* FullMaintenanceModeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C522C4FAF2200D89FEF /* FullMaintenanceModeView.swift */; }; + C64F6C562C4FD48000D89FEF /* MaintenanceModeData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C552C4FD48000D89FEF /* MaintenanceModeData.swift */; }; + C64F6C572C4FD48000D89FEF /* MaintenanceModeData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C552C4FD48000D89FEF /* MaintenanceModeData.swift */; }; + C64F6C592C50F0FE00D89FEF /* UDMaintenanceModeFeatureFlagTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C582C50F0FE00D89FEF /* UDMaintenanceModeFeatureFlagTracker.swift */; }; + C64F6C5A2C50F0FE00D89FEF /* UDMaintenanceModeFeatureFlagTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C582C50F0FE00D89FEF /* UDMaintenanceModeFeatureFlagTracker.swift */; }; + C64F6C5D2C50FCA200D89FEF /* MaintenanceDetailsEmbeddedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C5C2C50FCA200D89FEF /* MaintenanceDetailsEmbeddedView.swift */; }; + C64F6C5E2C50FCA200D89FEF /* MaintenanceDetailsEmbeddedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C5C2C50FCA200D89FEF /* MaintenanceDetailsEmbeddedView.swift */; }; + C64F6C602C50FCB100D89FEF /* MaintenanceServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C5F2C50FCB100D89FEF /* MaintenanceServiceType.swift */; }; + C64F6C612C50FCB100D89FEF /* MaintenanceServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C5F2C50FCB100D89FEF /* MaintenanceServiceType.swift */; }; + C64F6C632C50FE0400D89FEF /* MaintenanceLinkButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C622C50FE0400D89FEF /* MaintenanceLinkButtonView.swift */; }; + C64F6C642C50FE0400D89FEF /* MaintenanceLinkButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C622C50FE0400D89FEF /* MaintenanceLinkButtonView.swift */; }; + C64F6C662C51086400D89FEF /* MaintenanceDetailsFullView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C652C51086400D89FEF /* MaintenanceDetailsFullView.swift */; }; + C64F6C672C51086400D89FEF /* MaintenanceDetailsFullView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C652C51086400D89FEF /* MaintenanceDetailsFullView.swift */; }; + C64F6C692C51192600D89FEF /* MaintenanceDetailsPullUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C682C51192600D89FEF /* MaintenanceDetailsPullUpView.swift */; }; + C64F6C6A2C51192600D89FEF /* MaintenanceDetailsPullUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C682C51192600D89FEF /* MaintenanceDetailsPullUpView.swift */; }; + C64F6C6C2C5139B900D89FEF /* MaintenanceModeDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F6C6B2C5139B900D89FEF /* MaintenanceModeDataTests.swift */; }; C64F8DFE2B60F2140075D37F /* ViewPullUpDefaultConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F8DFD2B60F2140075D37F /* ViewPullUpDefaultConfiguration.swift */; }; C64F8DFF2B60F2140075D37F /* ViewPullUpDefaultConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F8DFD2B60F2140075D37F /* ViewPullUpDefaultConfiguration.swift */; }; C64F8E012B60F2590075D37F /* ViewPullUpConfigurationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64F8E002B60F2590075D37F /* ViewPullUpConfigurationType.swift */; }; @@ -3304,6 +3321,15 @@ C64BCB752A6FA61F00EF8B47 /* XMTPServiceHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XMTPServiceHelper.swift; sourceTree = ""; }; C64BCB7A2A6FB5B700EF8B47 /* MessagingChatMessageImageDataTypeDisplayInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagingChatMessageImageDataTypeDisplayInfo.swift; sourceTree = ""; }; C64F6C4E2C4E72A900D89FEF /* EthereumSendTransactionPayload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumSendTransactionPayload.swift; sourceTree = ""; }; + C64F6C522C4FAF2200D89FEF /* FullMaintenanceModeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullMaintenanceModeView.swift; sourceTree = ""; }; + C64F6C552C4FD48000D89FEF /* MaintenanceModeData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaintenanceModeData.swift; sourceTree = ""; }; + C64F6C582C50F0FE00D89FEF /* UDMaintenanceModeFeatureFlagTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UDMaintenanceModeFeatureFlagTracker.swift; sourceTree = ""; }; + C64F6C5C2C50FCA200D89FEF /* MaintenanceDetailsEmbeddedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaintenanceDetailsEmbeddedView.swift; sourceTree = ""; }; + C64F6C5F2C50FCB100D89FEF /* MaintenanceServiceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaintenanceServiceType.swift; sourceTree = ""; }; + C64F6C622C50FE0400D89FEF /* MaintenanceLinkButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaintenanceLinkButtonView.swift; sourceTree = ""; }; + C64F6C652C51086400D89FEF /* MaintenanceDetailsFullView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaintenanceDetailsFullView.swift; sourceTree = ""; }; + C64F6C682C51192600D89FEF /* MaintenanceDetailsPullUpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaintenanceDetailsPullUpView.swift; sourceTree = ""; }; + C64F6C6B2C5139B900D89FEF /* MaintenanceModeDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaintenanceModeDataTests.swift; sourceTree = ""; }; C64F8DFD2B60F2140075D37F /* ViewPullUpDefaultConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewPullUpDefaultConfiguration.swift; sourceTree = ""; }; C64F8E002B60F2590075D37F /* ViewPullUpConfigurationType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewPullUpConfigurationType.swift; sourceTree = ""; }; C64F8E032B60F4AF0075D37F /* ViewPullUpCustomContentConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewPullUpCustomContentConfiguration.swift; sourceTree = ""; }; @@ -4393,6 +4419,7 @@ C640F3572C06FCB6009EB0F9 /* MPCWalletPasswordValidatorTests.swift */, C63391772A86819600623188 /* XMTPMessagingAPIServiceTests.swift */, 30C0F94927D8CB250060D283 /* PrivateKeyStorageTests.swift */, + C64F6C6B2C5139B900D89FEF /* MaintenanceModeDataTests.swift */, C62900FD2BAAD126008B35A2 /* NumberPadInputInterpreterTests.swift */, 29EDB622290A94E700A0BD08 /* ProfilesTests.swift */, C6BEC9502C004B0700F21FB6 /* RequestsLimitControllerTests.swift */, @@ -5831,6 +5858,19 @@ path = ReverseResolution; sourceTree = ""; }; + C64F6C5B2C50FC5B00D89FEF /* Maintenance */ = { + isa = PBXGroup; + children = ( + C64F6C522C4FAF2200D89FEF /* FullMaintenanceModeView.swift */, + C64F6C5C2C50FCA200D89FEF /* MaintenanceDetailsEmbeddedView.swift */, + C64F6C652C51086400D89FEF /* MaintenanceDetailsFullView.swift */, + C64F6C682C51192600D89FEF /* MaintenanceDetailsPullUpView.swift */, + C64F6C622C50FE0400D89FEF /* MaintenanceLinkButtonView.swift */, + C64F6C5F2C50FCB100D89FEF /* MaintenanceServiceType.swift */, + ); + path = Maintenance; + sourceTree = ""; + }; C64F8DFC2B60F2010075D37F /* ViewPullUp */ = { isa = PBXGroup; children = ( @@ -6975,6 +7015,7 @@ C689C1792ADE6D2500AA0186 /* UDFeatureFlagsServiceProtocol.swift */, C689C17B2ADE6D3400AA0186 /* UDFeatureFlag.swift */, C689C1762ADE693900AA0186 /* LaunchDarklyService.swift */, + C64F6C582C50F0FE00D89FEF /* UDMaintenanceModeFeatureFlagTracker.swift */, ); path = FeatureFlags; sourceTree = ""; @@ -7592,6 +7633,7 @@ C63414D92A7D335100C80B77 /* GlobalRR.swift */, 30BA2B35252914100097817E /* Helpers.swift */, C653304D2BC3941000A705AE /* JWToken.swift */, + C64F6C552C4FD48000D89FEF /* MaintenanceModeData.swift */, C6B65F962B566B37006D1812 /* NFTDisplayInfo.swift */, C62900FF2BAAD3C3008B35A2 /* NumberPadInputInterpreter.swift */, C6ED320C295E8EDE00BC6919 /* NonEmptyArray.swift */, @@ -8097,6 +8139,7 @@ C685A9782840BF4D00E54044 /* InfoScreen */, C6637A6E28100D180000AE56 /* LaunchViewController */, C63DD35D29C89FCB002D45B2 /* LoginViewController */, + C64F6C5B2C50FC5B00D89FEF /* Maintenance */, C65271DD2A148D74001A084C /* Messaging */, C61B3E68283E249800500B6D /* MintDomains */, 30BFA4CE27DE001B00644CD5 /* Onboarding */, @@ -9055,6 +9098,7 @@ 29BECD0E2979BD7B00662FC1 /* CloudStorage.swift in Sources */, C6D0F438283F7DFF00444921 /* MintDomainsConfigurationSelectionCell.swift in Sources */, C6B65F722B550F6B006D1812 /* DomainNFTsStorage.swift in Sources */, + C64F6C632C50FE0400D89FEF /* MaintenanceLinkButtonView.swift in Sources */, 307B1D392603C0C4007B14C3 /* UDWalletEthereum.swift in Sources */, C60EE80F2ACD203000D699C7 /* NetworkService+MessagingApi.swift in Sources */, C6D9E28928533783002CDAC2 /* UDDomainSharingCardView.swift in Sources */, @@ -9134,6 +9178,7 @@ C6B659242B68E49500CA6A68 /* HomeWalletTokenNotMatchingRowView.swift in Sources */, C6124CCC29223066005E6537 /* DomainAvatarImageView.swift in Sources */, C69F99292A9F1264004B1958 /* View.swift in Sources */, + C64F6C532C4FAF2200D89FEF /* FullMaintenanceModeView.swift in Sources */, C671CD062BC66D6E005DA2FB /* EcomPurchaseMPCWalletService.swift in Sources */, C6952A372BC500D200F4B475 /* FB_UD_MPCAccountAsset.swift in Sources */, C639F3992C32D10C008CC1CB /* ConnectCurveLine.swift in Sources */, @@ -9242,6 +9287,7 @@ C6D8FF1B2B82E7A80094A21E /* MessageRowView.swift in Sources */, C6D647682B1EDF3400D724AC /* EthereumTransaction.swift in Sources */, C61B3E99283E70C100500B6D /* EnterEmailVerificationCodeToMintDomainsPresenter.swift in Sources */, + C64F6C562C4FD48000D89FEF /* MaintenanceModeData.swift in Sources */, C6F3FD632AAA11E700208679 /* PullUpNamespace.swift in Sources */, C6C99582289D313D00367362 /* CNavigationBarContentView.swift in Sources */, C6D5DAE928374C7300379C38 /* WalletAddressFieldCollectionCell.swift in Sources */, @@ -9442,6 +9488,7 @@ C62BDA412B5E4104008E21AD /* MessagingBlockUserInChatType.swift in Sources */, C62900DF2BAAB1A8008B35A2 /* UDTabsPickerView.swift in Sources */, C61B3E72283E43D000500B6D /* EnterEmailViewPresenter.swift in Sources */, + C64F6C602C50FCB100D89FEF /* MaintenanceServiceType.swift in Sources */, C64F6C4F2C4E72A900D89FEF /* EthereumSendTransactionPayload.swift in Sources */, C6ED320D295E8EDE00BC6919 /* NonEmptyArray.swift in Sources */, C63DBCCB2BE49376008F3D2C /* NetworkService+Common.swift in Sources */, @@ -9632,6 +9679,7 @@ C67213D62BAA8DB60075B9C7 /* SendCryptoAssetSelectReceiverFollowingRowView.swift in Sources */, C669C3A52912AD4200837F21 /* DomainProfileBadgeCell.swift in Sources */, C61ECD742A20E87100E97D70 /* PushGroupChatDTO.swift in Sources */, + C64F6C662C51086400D89FEF /* MaintenanceDetailsFullView.swift in Sources */, C673BA3B2A82789B001FD763 /* MessagingChannelsWebSocketsServiceProtocol.swift in Sources */, C66804DD280D9EC8007E6390 /* VerifyPasscodeViewController.swift in Sources */, 309FDFBC265E7C0C00AE53D3 /* StripePaymentHelper.swift in Sources */, @@ -9675,6 +9723,7 @@ C696989C2ACC043A0000738C /* MessagingCommunitiesChatDetails.swift in Sources */, C6C1E9342B74C33B00030447 /* AnalyticAppearanceTrackerModifier.swift in Sources */, C665FC1F29B06896009699D2 /* NotificationsServiceProtocol.swift in Sources */, + C64F6C692C51192600D89FEF /* MaintenanceDetailsPullUpView.swift in Sources */, C64528D32AA0CDFC00298121 /* PublicProfileViewDelegate.swift in Sources */, C6D6E52C28193611008C66BB /* BasePresenterProtocol.swift in Sources */, C6D6E4D42817BD4B008C66BB /* TutorialViewPresenter.swift in Sources */, @@ -10102,6 +10151,7 @@ C68BEC142A397D9B00A0B809 /* MessagingChatUserProfileDisplayInfo.swift in Sources */, C6C9958A289D313D00367362 /* CNavigationBarBackButton.swift in Sources */, C6E6A001288AEAC6000A8346 /* ExternalEventsStorage.swift in Sources */, + C64F6C5D2C50FCA200D89FEF /* MaintenanceDetailsEmbeddedView.swift in Sources */, C6A3592A2BB5586700B1209A /* NavigationTrackerViewModifier.swift in Sources */, C666E0A129CB3FEA0003DECB /* ParkedDomainCell.swift in Sources */, 29AEA04B292F7D60003BB5B4 /* SecurePersistedStorage.swift in Sources */, @@ -10114,6 +10164,7 @@ C6FECF46282D0694008DAA49 /* ResizableRoundedImageView.swift in Sources */, C61ECD792A20E8BD00E97D70 /* PushGroupChatMember.swift in Sources */, C6EECE972833A3E400978ED5 /* CoinRecord.swift in Sources */, + C64F6C592C50F0FE00D89FEF /* UDMaintenanceModeFeatureFlagTracker.swift in Sources */, 29DE3B1328913DF500655675 /* Env.swift in Sources */, C664B6492914BE3D00A76154 /* DomainProfileMetadataSection.swift in Sources */, C62396952A288AD100363F60 /* MessagingChatType.swift in Sources */, @@ -10172,6 +10223,7 @@ C6DF9876290F83020098733A /* ArrayExtensionsTests.swift in Sources */, C617CFA02B9ED9A400663516 /* TestableUDDomainsService.swift in Sources */, C6DF9873290F83020098733A /* UDWalletsServiceTests.swift in Sources */, + C64F6C6C2C5139B900D89FEF /* MaintenanceModeDataTests.swift in Sources */, C6DF9872290F83020098733A /* TransactionsStorageTests.swift in Sources */, C66FFD052B01E2EE00988A6F /* CoreDataMessagingStorageServiceTests.swift in Sources */, C617CFA82B9EDA2F00663516 /* TestableWalletNFTsService.swift in Sources */, @@ -10794,6 +10846,7 @@ C6C8F8272B217D3500A9834D /* WCCompatibleWallet.swift in Sources */, C65CEB8C2B674FC700A13B34 /* UDWalletsServiceEnvironmentKey.swift in Sources */, C6C8F83D2B217E9600A9834D /* OnboardingBackupWalletPresenter.swift in Sources */, + C64F6C5E2C50FCA200D89FEF /* MaintenanceDetailsEmbeddedView.swift in Sources */, C6D645C82B1DBD3900D724AC /* CNavigationBar.swift in Sources */, C618085B2B19BBFE0032E543 /* UDTextFieldView.swift in Sources */, C68F15682BD645CE0049BFA2 /* MPCActivateWalletView.swift in Sources */, @@ -10825,12 +10878,14 @@ C6FAED842B8C5C1200CC1844 /* ChatMentionSuggestionsView.swift in Sources */, C61807BF2B19A2CC0032E543 /* AppContextProtocol.swift in Sources */, C61807D62B19A4580032E543 /* ExternalEventsServiceProtocol.swift in Sources */, + C64F6C612C50FCB100D89FEF /* MaintenanceServiceType.swift in Sources */, C650E2942B9E97F2002C120A /* UDButtonStyle+Large.swift in Sources */, C618084E2B19B9FD0032E543 /* PreviewFirebasePurchaseDomainsService.swift in Sources */, C6B65F952B5665F0006D1812 /* HomeTabView.swift in Sources */, C62900EF2BAAC5C0008B35A2 /* BalanceTokenIconsView.swift in Sources */, C67684A42BCEB9AC0062474F /* FB_UD_MPCRefreshBootstrapTokenResponse.swift in Sources */, C6D647212B1ED91F00D724AC /* PullUpViewService.swift in Sources */, + C64F6C672C51086400D89FEF /* MaintenanceDetailsFullView.swift in Sources */, C665566E2BF70D6A00F0BD7A /* PurchaseMPCWalletUDAuthView.swift in Sources */, C6D8FF1F2B82EB740094A21E /* TextMessageRowView.swift in Sources */, C6D645DA2B1DBD4D00D724AC /* NavBarBackButtonTransitionPerformer.swift in Sources */, @@ -10851,6 +10906,7 @@ C6C8F94D2B21853900A9834D /* AppReviewServiceProtocol.swift in Sources */, C661DC2B2C06DB6A00844AF5 /* MPCOnboardingPurchaseAlreadyHaveWalletViewController.swift in Sources */, C6960C3B2B1997DC00B79E28 /* UnsConfigManager.swift in Sources */, + C64F6C642C50FE0400D89FEF /* MaintenanceLinkButtonView.swift in Sources */, C62900EC2BAAC135008B35A2 /* NavigationTopSafeAreaOffset.swift in Sources */, C6C8F8792B21827700A9834D /* LoginInAppViewPresenter.swift in Sources */, C6C8F8342B217E9600A9834D /* CreateLocalWalletRecoveryPhrasePresenter.swift in Sources */, @@ -10859,6 +10915,7 @@ C63AD09B2B95657C00BF8C83 /* LineView.swift in Sources */, C6D6463B2B1DC50600D724AC /* AppReviewActionEvent.swift in Sources */, C6534A8E2BBFBA10008EEBB5 /* HomeExploreFollowerCellView.swift in Sources */, + C64F6C572C4FD48000D89FEF /* MaintenanceModeData.swift in Sources */, C6C8F8B32B2182CF00A9834D /* EnterEmailVerificationCodeToMintDomainsPresenter.swift in Sources */, C671CD192BC68083005DA2FB /* PurchaseDomainsCheckoutData.swift in Sources */, C67684A72BD21E0C0062474F /* FB_UD_MPCWalletAccountWithAssets.swift in Sources */, @@ -11019,6 +11076,7 @@ C6F433522BB27AEF000C5E46 /* SendCryptoAssetSuccessView.swift in Sources */, C6C8F8462B217E9600A9834D /* EnterBackupCreateLocalWalletPresenter.swift in Sources */, C6D646FD2B1ED7B900D724AC /* MessagingChatDisplayInfo.swift in Sources */, + C64F6C6A2C51192600D89FEF /* MaintenanceDetailsPullUpView.swift in Sources */, C6D646A32B1ED15A00D724AC /* PublicProfileFollowersView.swift in Sources */, C61807EF2B19A70F0032E543 /* PreviewPaymentConfirmationDelegate.swift in Sources */, C67684AA2BD2200E0062474F /* FB_UD_MPCSignMessageEncoding.swift in Sources */, @@ -11194,6 +11252,7 @@ C6D6473A2B1ED9EF00D724AC /* BaseSignTransactionView.swift in Sources */, C6B761F42BB3FD7500773943 /* WalletTransactionDisplayInfo.swift in Sources */, C6BF6BDE2B8F3602006CC2BD /* PassViewAnalyticsDetailsViewModifier.swift in Sources */, + C64F6C5A2C50F0FE00D89FEF /* UDMaintenanceModeFeatureFlagTracker.swift in Sources */, C6C8F85C2B217FED00A9834D /* BackedUpWallet.swift in Sources */, C6170EC52B7A0075008E9C93 /* ShowingWalletSelectionModifier.swift in Sources */, C6C8F82F2B217E7F00A9834D /* AppUpdatedRequired.swift in Sources */, @@ -11278,6 +11337,7 @@ C6D645762B1D721D00D724AC /* PurchaseDomainsSelectDiscountsView.swift in Sources */, C6D646552B1ED10100D724AC /* DomainProfileWeb3WebsiteCell.swift in Sources */, C6D6462E2B1DC31700D724AC /* CollectionDashesHeaderReusableView.swift in Sources */, + C64F6C542C4FAF2200D89FEF /* FullMaintenanceModeView.swift in Sources */, C6D647492B1EDAD000D724AC /* CopyWalletAddressPullUpHandler.swift in Sources */, C6534AC82BBFBAF5008EEBB5 /* UserProfilesServiceProtocol.swift in Sources */, C6D646AE2B1ED16900D724AC /* DomainProfileTutorialItemViewController.swift in Sources */, diff --git a/unstoppable-ios-app/domains-manager-ios/AppContext/GeneralAppContext.swift b/unstoppable-ios-app/domains-manager-ios/AppContext/GeneralAppContext.swift index 02eaa59e9..d8d2c61a6 100644 --- a/unstoppable-ios-app/domains-manager-ios/AppContext/GeneralAppContext.swift +++ b/unstoppable-ios-app/domains-manager-ios/AppContext/GeneralAppContext.swift @@ -50,7 +50,8 @@ final class GeneralAppContext: AppContextProtocol { private(set) lazy var appLaunchService: AppLaunchServiceProtocol = { AppLaunchService(coreAppCoordinator: coreAppCoordinator, udWalletsService: udWalletsService, - userProfilesService: userProfilesService) + userProfilesService: userProfilesService, + udFeatureFlagsService: udFeatureFlagsService) }() private(set) lazy var domainRecordsService: DomainRecordsServiceProtocol = DomainRecordsService() private(set) lazy var qrCodeService: QRCodeServiceProtocol = QRCodeService() diff --git a/unstoppable-ios-app/domains-manager-ios/Entities/MaintenanceModeData.swift b/unstoppable-ios-app/domains-manager-ios/Entities/MaintenanceModeData.swift new file mode 100644 index 000000000..9051a42c6 --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios/Entities/MaintenanceModeData.swift @@ -0,0 +1,75 @@ +// +// MaintenanceModeData.swift +// domains-manager-ios +// +// Created by Oleg Kuplin on 23.07.2024. +// + +import Foundation + +struct MaintenanceModeData: Codable { + private let isOn: Bool + var link: String? + var title: String? + var message: String? + var startDate: Date? + var endDate: Date? + + init(isOn: Bool, + link: String? = nil, + title: String? = nil, + message: String? = nil, + startDate: Date? = nil, + endDate: Date? = nil) { + self.isOn = isOn + self.link = link + self.title = title + self.message = message + self.startDate = startDate + self.endDate = endDate + } + + var isCurrentlyEnabled: Bool { + if isOn { + /// If there's a startDate set, we check if it is already started,. Otherwise return true + if let startDate { + if startDate > Date() { + return false + } else if let endDate { /// If there's end date, we check if it is already ended. Otherwise return true + return endDate >= Date() + } + return true + } else if let endDate { + return endDate >= Date() + } + + return true + } + return false + } + + var linkURL: URL? { URL(string: link ?? "") } + + func onMaintenanceStatusUpdate(callback: @escaping EmptyCallback) { + let now = Date() + + func scheduleUpdatedAfter(date: Date) { + let timeInterval = date.timeIntervalSince(now) + 1 + DispatchQueue.main.asyncAfter(deadline: .now() + timeInterval) { + callback() + } + } + + if let startDate, + startDate > now { + scheduleUpdatedAfter(date: startDate) + } else if let endDate, + endDate > now { + scheduleUpdatedAfter(date: endDate) + } + } +} + + + + diff --git a/unstoppable-ios-app/domains-manager-ios/Extensions/Extension-String+Preview.swift b/unstoppable-ios-app/domains-manager-ios/Extensions/Extension-String+Preview.swift index e354db710..52078933e 100644 --- a/unstoppable-ios-app/domains-manager-ios/Extensions/Extension-String+Preview.swift +++ b/unstoppable-ios-app/domains-manager-ios/Extensions/Extension-String+Preview.swift @@ -1283,6 +1283,26 @@ extension String { static let backedUp = "BACKED_UP" static let backUp = "BACK_UP" static let setAsPrimaryDomain = "SET_AS_PRIMARY_DOMAIN" + + // Maintenance + static let fullMaintenanceMessageTitle = "FULL_MAINTENANCE_MESSAGE_TITLE" + static let fullMaintenanceMessageSubtitle = "FULL_MAINTENANCE_MESSAGE_SUBTITLE" + static let activityMaintenanceMessageTitle = "ACTIVITY_MAINTENANCE_MESSAGE_TITLE" + static let activityMaintenanceMessageSubtitle = "ACTIVITY_MAINTENANCE_MESSAGE_SUBTITLE" + static let exploreMaintenanceMessageTitle = "EXPLORE_MAINTENANCE_MESSAGE_TITLE" + static let exploreMaintenanceMessageSubtitle = "EXPLORE_MAINTENANCE_MESSAGE_SUBTITLE" + static let homeMaintenanceMessageTitle = "HOME_MAINTENANCE_MESSAGE_TITLE" + static let homeMaintenanceMessageSubtitle = "HOME_MAINTENANCE_MESSAGE_SUBTITLE" + static let purchaseDomainsMaintenanceMessageTitle = "PURCHASE_DOMAINS_MAINTENANCE_MESSAGE_TITLE" + static let purchaseDomainsMaintenanceMessageSubtitle = "PURCHASE_DOMAINS_MAINTENANCE_MESSAGE_SUBTITLE" + static let vaultedDomainsMaintenanceMessageTitle = "VAULTED_DOMAINS_MAINTENANCE_MESSAGE_TITLE" + static let vaultedDomainsMaintenanceMessageSubtitle = "VAULTED_DOMAINS_MAINTENANCE_MESSAGE_SUBTITLE" + static let domainProfileMaintenanceMessageTitle = "DOMAIN_PROFILE_MAINTENANCE_MESSAGE_TITLE" + static let domainProfileMaintenanceMessageSubtitle = "DOMAIN_PROFILE_MAINTENANCE_MESSAGE_SUBTITLE" + static let sendCryptoMaintenanceMessageTitle = "SEND_CRYPTO_MAINTENANCE_MESSAGE_TITLE" + static let sendCryptoMaintenanceMessageSubtitle = "SEND_CRYPTO_MAINTENANCE_MESSAGE_SUBTITLE" + static let signMessagesMaintenanceMessageTitle = "SIGN_MESSAGES_MAINTENANCE_MESSAGE_TITLE" + static let signMessagesMaintenanceMessageSubtitle = "SIGN_MESSAGES_MAINTENANCE_MESSAGE_SUBTITLE" } enum SystemImage: String { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/HomeActivityView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/HomeActivityView.swift index 89aa0924f..2ff07ba5c 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/HomeActivityView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/HomeActivityView.swift @@ -11,8 +11,10 @@ struct HomeActivityView: View, ViewAnalyticsLogger { @EnvironmentObject var tabRouter: HomeTabRouter @State private var navigationState: NavigationStateManager? + @StateObject private var okLinkFlagTracker = UDMaintenanceModeFeatureFlagTracker(featureFlag: .isMaintenanceOKLinkEnabled) + @StateObject private var profilesAPIFlagTracker = UDMaintenanceModeFeatureFlagTracker(featureFlag: .isMaintenanceProfilesAPIEnabled) @StateObject var viewModel: HomeActivityViewModel - + var isOtherScreenPushed: Bool { !tabRouter.activityTabNavPath.isEmpty } var analyticsName: Analytics.ViewName { .homeActivity } @@ -94,7 +96,15 @@ private extension HomeActivityView { private extension HomeActivityView { @ViewBuilder func contentList() -> some View { - if viewModel.groupedTxs.isEmpty, + if okLinkFlagTracker.maintenanceData?.isCurrentlyEnabled == true { + MaintenanceDetailsEmbeddedView(serviceType: .activity, + maintenanceData: okLinkFlagTracker.maintenanceData) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else if profilesAPIFlagTracker.maintenanceData?.isCurrentlyEnabled == true { + MaintenanceDetailsEmbeddedView(serviceType: .activity, + maintenanceData: profilesAPIFlagTracker.maintenanceData) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else if viewModel.groupedTxs.isEmpty, !viewModel.isLoadingMore { GeometryReader { geometry in /// ScrollView needed to keep PTR functionality diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Explore/HomeExploreView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Explore/HomeExploreView.swift index d35db9ca6..d42fe9ca7 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Explore/HomeExploreView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Explore/HomeExploreView.swift @@ -11,8 +11,9 @@ struct HomeExploreView: View, ViewAnalyticsLogger { @EnvironmentObject var tabRouter: HomeTabRouter @State private var navigationState: NavigationStateManager? + @StateObject private var profilesAPIFlagTracker = UDMaintenanceModeFeatureFlagTracker(featureFlag: .isMaintenanceProfilesAPIEnabled) @StateObject var viewModel: HomeExploreViewModel - + var isOtherScreenPushed: Bool { !tabRouter.exploreTabNavPath.isEmpty } var analyticsName: Analytics.ViewName { .homeExplore } @@ -22,7 +23,7 @@ struct HomeExploreView: View, ViewAnalyticsLogger { if viewModel.isSearchActive { domainSearchTypeSelector() } - contentList() + contentView() } .animation(.default, value: UUID()) .background(Color.backgroundDefault) @@ -122,6 +123,17 @@ private extension HomeExploreView { .background(.regularMaterial) } + @ViewBuilder + func contentView() -> some View { + if profilesAPIFlagTracker.maintenanceData?.isCurrentlyEnabled == true { + MaintenanceDetailsEmbeddedView(serviceType: .explore, + maintenanceData: profilesAPIFlagTracker.maintenanceData) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else { + contentList() + } + } + @ViewBuilder func contentList() -> some View { List { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/HomeTabRouter.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/HomeTabRouter.swift index 93713bfd3..61d025f4b 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/HomeTabRouter.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/HomeTabRouter.swift @@ -22,6 +22,7 @@ final class HomeTabRouter: ObservableObject { @Published var exploreTabNavPath: [HomeExploreNavigationDestination] = [] @Published var activityTabNavPath: [HomeActivityNavigationDestination] = [] @Published var presentedNFT: NFTDisplayInfo? + @Published var presentedDomain: DomainPresentationDetails? @Published var presentedPublicDomain: PublicProfileViewConfiguration? @Published var presentedUBTSearch: UBTSearchPresentationDetails? @@ -162,6 +163,10 @@ extension HomeTabRouter { guard !domain.isTransferring else { showDomainTransferringInProgress(domain) return } + let flagTracker = UDMaintenanceModeFeatureFlagTracker(featureFlag: .isMaintenanceProfilesAPIEnabled) + guard flagTracker.maintenanceData?.isCurrentlyEnabled != true else { + appContext.pullUpViewService.showDomainProfileInMaintenancePullUp(in: topVC) + return } presentedDomain = .init(domain: domain, wallet: wallet, @@ -213,8 +218,8 @@ extension HomeTabRouter { let mintedDomains = domains.interactableItems() walletViewNavPath.append(HomeWalletNavigationDestination.minting(mode: mode, - mintedDomains: mintedDomains, - domainsMintedCallback: { result in + mintedDomains: mintedDomains, + domainsMintedCallback: { result in }, mintingNavProvider: { [weak self] mintingNav in self?.mintingNav = mintingNav })) @@ -267,7 +272,6 @@ extension HomeTabRouter { completion(Void()) })) } - //await view.dismissPullUpMenu() await finishSetupPurchasedProfileIfNeeded(domains: domains, requests: requests) } } @@ -337,6 +341,12 @@ extension HomeTabRouter { } } } + + func showSigningMessagesInMaintenancePullUp() { + guard let topVC else { return } + + appContext.pullUpViewService.showMessageSigningInMaintenancePullUp(in: topVC) + } } // MARK: - Pull up related @@ -416,7 +426,6 @@ extension HomeTabRouter: PublicProfileViewDelegate { UDRouter().showTransferInProgressScreen(domain: domain, in: topVC) } - } // MARK: - Private methods @@ -501,7 +510,6 @@ private extension HomeTabRouter { } })) } - // await view.dismissPullUpMenu() await finishSetupPurchasedProfileIfNeeded(domains: domains, requests: requests) } catch { PurchasedDomainsStorage.setPendingNonEmptyProfiles(pendingProfilesLeft) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetRootView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetRootView.swift index c91a52021..a4db6be5c 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetRootView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetRootView.swift @@ -11,23 +11,13 @@ struct SendCryptoAssetRootView: View { @Environment(\.presentationMode) private var presentationMode @StateObject var viewModel: SendCryptoAssetViewModel + @StateObject private var infuraFlagTracker = UDMaintenanceModeFeatureFlagTracker(featureFlag: .isMaintenanceInfuraEnabled) + @StateObject private var mpcFlagTracker = UDMaintenanceModeFeatureFlagTracker(featureFlag: .isMaintenanceMPCEnabled) var body: some View { NavigationViewWithCustomTitle(content: { ZStack { - SendCryptoAssetSelectReceiverView() - .environmentObject(viewModel) - .navigationBarTitleDisplayMode(.inline) - .navigationDestination(for: SendCryptoAsset.NavigationDestination.self) { destination in - SendCryptoAsset.LinkNavigationDestination.viewFor(navigationDestination: destination) - .ignoresSafeArea() - .environmentObject(viewModel) - } - .onChange(of: viewModel.navPath) { _ in - updateTitleView() - } - .trackNavigationControllerEvents(onDidNotFinishNavigationBack: updateTitleView) - + contentView() if viewModel.isLoading { ProgressView() } @@ -52,6 +42,63 @@ private extension SendCryptoAssetRootView { } } +// MARK: - Private methods +private extension SendCryptoAssetRootView { + var isMaintenanceOnForSelectedWallet: Bool { + affectedServiceMaintenanceData?.isCurrentlyEnabled == true + } + + var affectedServiceMaintenanceData: MaintenanceModeData? { + switch viewModel.sourceWallet.udWallet.type { + case .mpc: + return mpcFlagTracker.maintenanceData + case .externalLinked: + return nil + default: + return infuraFlagTracker.maintenanceData + } + } + + @ViewBuilder + func contentView() -> some View { + if isMaintenanceOnForSelectedWallet { + MaintenanceDetailsFullView(serviceType: .sendCrypto, + maintenanceData: affectedServiceMaintenanceData) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .toolbar { + ToolbarItem(placement: .topBarLeading) { + closeButton() + } + } + } else { + sendCryptoFlowView() + } + } + + @ViewBuilder + func sendCryptoFlowView() -> some View { + SendCryptoAssetSelectReceiverView() + .environmentObject(viewModel) + .navigationBarTitleDisplayMode(.inline) + .navigationDestination(for: SendCryptoAsset.NavigationDestination.self) { destination in + SendCryptoAsset.LinkNavigationDestination.viewFor(navigationDestination: destination) + .ignoresSafeArea() + .environmentObject(viewModel) + } + .onChange(of: viewModel.navPath) { _ in + updateTitleView() + } + .trackNavigationControllerEvents(onDidNotFinishNavigationBack: updateTitleView) + } + + @ViewBuilder + func closeButton() -> some View { + CloseButtonView { + presentationMode.wrappedValue.dismiss() + } + } +} + #Preview { PresentAsModalPreviewView { SendCryptoAssetRootView(viewModel: SendCryptoAssetViewModel(initialData: .init(sourceWallet: MockEntitiesFabric.Wallet.mockEntities()[0]))) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Wallet/HomeWalletView/HomeWalletView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Wallet/HomeWalletView/HomeWalletView.swift index 39321945d..a7f825a4b 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Wallet/HomeWalletView/HomeWalletView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Wallet/HomeWalletView/HomeWalletView.swift @@ -14,6 +14,8 @@ struct HomeWalletView: View, ViewAnalyticsLogger { @EnvironmentObject var tabRouter: HomeTabRouter @StateObject var viewModel: HomeWalletViewModel + @StateObject private var profilesAPIFlagTracker = UDMaintenanceModeFeatureFlagTracker(featureFlag: .isMaintenanceProfilesAPIEnabled) + @StateObject private var mpcFlagTracker = UDMaintenanceModeFeatureFlagTracker(featureFlag: .isMaintenanceMPCEnabled) @State private var isOtherScreenPresented: Bool = false @Binding var navigationState: NavigationStateManager? @Binding var isTabBarVisible: Bool @@ -39,22 +41,8 @@ struct HomeWalletView: View, ViewAnalyticsLogger { .listRowSeparator(.hidden) .unstoppableListRowInset() - contentTypeSelector() - .listRowBackground(Color.clear) - .listRowSeparator(.hidden) - .padding(.vertical) - .unstoppableListRowInset() + userDataContentViewsIfAvailable() - sortingOptionsForSelectedType() - .environment(\.analyticsAdditionalProperties, [.homeContentType : viewModel.selectedContentType.rawValue]) - .listRowBackground(Color.clear) - .listRowSeparator(.hidden) - .listRowInsets(EdgeInsets(top: 10, leading: 16, bottom: 0, trailing: 16)) - - contentForSelectedType() - .listRowBackground(Color.clear) - .listRowSeparator(.hidden) - .listRowInsets(.init(horizontal: 16)) }.environment(\.defaultMinListRowHeight, 28) .onChange(of: tabRouter.walletViewNavPath) { _ in updateNavTitleVisibility() @@ -90,12 +78,16 @@ struct HomeWalletView: View, ViewAnalyticsLogger { // MARK: - Private methods private extension HomeWalletView { + var isProfileButtonEnabled: Bool { + viewModel.isProfileButtonEnabled && !isHomeInMaintenance + } + func walletActions() -> [WalletAction] { var actions: [WalletAction] = [.buy] if viewModel.isSendCryptoEnabled { actions.append(.send) } - actions.append(contentsOf: [.receive, .profile(enabled: viewModel.isProfileButtonEnabled)]) + actions.append(contentsOf: [.receive, .profile(enabled: isProfileButtonEnabled)]) return actions } @@ -118,6 +110,44 @@ private extension HomeWalletView { navigationState?.isTitleVisible = !isOtherScreenPushed && tabRouter.tabViewSelection == .wallets } } + + var isHomeInMaintenance: Bool { + profilesAPIFlagTracker.maintenanceData?.isCurrentlyEnabled == true + } + + @ViewBuilder + func userDataContentViewsIfAvailable() -> some View { + if isHomeInMaintenance { + MaintenanceDetailsEmbeddedView(serviceType: .home, + maintenanceData: profilesAPIFlagTracker.maintenanceData) + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) + .padding(.top, 16) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else { + userDataContentViews() + } + } + + @ViewBuilder + func userDataContentViews() -> some View { + contentTypeSelector() + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) + .padding(.vertical) + .unstoppableListRowInset() + + sortingOptionsForSelectedType() + .environment(\.analyticsAdditionalProperties, [.homeContentType : viewModel.selectedContentType.rawValue]) + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) + .listRowInsets(EdgeInsets(top: 10, leading: 16, bottom: 0, trailing: 16)) + + contentForSelectedType() + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) + .listRowInsets(.init(horizontal: 16)) + } @ViewBuilder func contentTypeSelector() -> some View { @@ -250,15 +280,20 @@ private extension HomeWalletView { @ViewBuilder func qrNavButtonView() -> some View { - NavigationLink(value: HomeWalletNavigationDestination.qrScanner(selectedWallet: viewModel.selectedWallet)) { + Button { + if case .mpc = viewModel.selectedWallet.udWallet.type, + mpcFlagTracker.maintenanceData?.isCurrentlyEnabled == true { + tabRouter.showSigningMessagesInMaintenancePullUp() + } else { + tabRouter.walletViewNavPath.append(.qrScanner(selectedWallet: viewModel.selectedWallet)) + logButtonPressedAnalyticEvents(button: .qrCode) + } + } label: { Image.qrBarCodeIcon .resizable() .squareFrame(24) .foregroundStyle(Color.foregroundDefault) } - .onButtonTap { - logButtonPressedAnalyticEvents(button: .qrCode) - } } @ViewBuilder diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Wallet/HomeWalletView/Subviews/HomeWalletActionsView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Wallet/HomeWalletView/Subviews/HomeWalletActionsView.swift index b39e74113..dbee7c34e 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Wallet/HomeWalletView/Subviews/HomeWalletActionsView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Wallet/HomeWalletView/Subviews/HomeWalletActionsView.swift @@ -96,6 +96,7 @@ private extension HomeWalletActionsView { } .buttonStyle(.plain) .withoutAnimation() + .allowsHitTesting(!isDimmed) } } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Wallet/HomeWebAccountView/HomeWebAccountView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Wallet/HomeWebAccountView/HomeWebAccountView.swift index 3c7e0d23a..bc549c599 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Wallet/HomeWebAccountView/HomeWebAccountView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Wallet/HomeWebAccountView/HomeWebAccountView.swift @@ -15,6 +15,8 @@ struct HomeWebAccountView: View, ViewAnalyticsLogger { let user: FirebaseUser @EnvironmentObject var tabRouter: HomeTabRouter + @StateObject private var ecommFlagTracker = UDMaintenanceModeFeatureFlagTracker(featureFlag: .isMaintenanceEcommEnabled) + @Binding var navigationState: NavigationStateManager? @Binding var isTabBarVisible: Bool @@ -37,7 +39,7 @@ struct HomeWebAccountView: View, ViewAnalyticsLogger { .listRowSeparator(.hidden) .unstoppableListRowInset() - HomeWalletActionsView(actions: WebAction.allCases, + HomeWalletActionsView(actions: walletActions(), actionCallback: { action in handleAction(action) }, subActionCallback: { subAction in @@ -47,10 +49,7 @@ struct HomeWebAccountView: View, ViewAnalyticsLogger { .listRowSeparator(.hidden) .listRowInsets(EdgeInsets(top: 24, leading: 16, bottom: 16, trailing: 16)) - domainsListView() - .listRowBackground(Color.clear) - .listRowSeparator(.hidden) - .unstoppableListRowInset() + userDataContentViewsIfAvailable() } .onChange(of: tabRouter.walletViewNavPath) { _ in updateNavTitleVisibility() @@ -107,10 +106,42 @@ private extension HomeWebAccountView { navigationState?.isTitleVisible = !isOtherScreenPushed && tabRouter.tabViewSelection == .wallets } } + + func walletActions() -> [WebAction] { + [.addWallet, + .claim(isEnabled: !isVaultedDomainsInMaintenance), + .more] + } + + var isVaultedDomainsInMaintenance: Bool { + ecommFlagTracker.maintenanceData?.isCurrentlyEnabled == true + } } // MARK: - Private methods private extension HomeWebAccountView { + @ViewBuilder + func userDataContentViewsIfAvailable() -> some View { + if isVaultedDomainsInMaintenance { + MaintenanceDetailsEmbeddedView(serviceType: .vaultedDomains, + maintenanceData: ecommFlagTracker.maintenanceData) + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) + .padding(.top, 16) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else { + userDataContentViews() + } + } + + @ViewBuilder + func userDataContentViews() -> some View { + domainsListView() + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) + .unstoppableListRowInset() + } + @ViewBuilder func headerIconView(size: CGFloat) -> some View { ZStack { @@ -222,10 +253,19 @@ private extension HomeWebAccountView { // MARK: - Actions private extension HomeWebAccountView { - enum WebAction: String, CaseIterable, HomeWalletActionItem { - var id: String { rawValue } + enum WebAction: HomeWalletActionItem { + var id: String { + switch self { + case .addWallet: + "addWallet" + case .claim(let isEnabled): + "claim_\(isEnabled)" + case .more: + "more" + } + } - case addWallet, claim, more + case addWallet, claim(isEnabled: Bool), more var title: String { switch self { @@ -268,7 +308,14 @@ private extension HomeWebAccountView { return .more } } - var isDimmed: Bool { false } + var isDimmed: Bool { + switch self { + case .addWallet, .more: + return false + case .claim(let isEnabled): + return !isEnabled + } + } } enum WebSubAction: String, CaseIterable, HomeWalletSubActionItem { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/FullMaintenanceModeView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/FullMaintenanceModeView.swift new file mode 100644 index 000000000..f71ddd255 --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/FullMaintenanceModeView.swift @@ -0,0 +1,30 @@ +// +// FullMaintenanceModeView.swift +// domains-manager-ios +// +// Created by Oleg Kuplin on 23.07.2024. +// + +import SwiftUI + +struct FullMaintenanceModeView: View, ViewAnalyticsLogger { + + @Environment(\.udFeatureFlagsService) var udFeatureFlagsService + var analyticsName: Analytics.ViewName { .fullMaintenance } + @StateObject private var flagTracker = UDMaintenanceModeFeatureFlagTracker(featureFlag: .isMaintenanceFullEnabled) + + static func instance(maintenanceData: MaintenanceModeData) -> UIViewController { + let view = FullMaintenanceModeView() + + return UIHostingController(rootView: view) + } + + var body: some View { + MaintenanceDetailsFullView(serviceType: .full, maintenanceData: flagTracker.maintenanceData) + .trackAppearanceAnalytics(analyticsLogger: self) + } +} + +#Preview { + FullMaintenanceModeView() +} diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/MaintenanceDetailsEmbeddedView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/MaintenanceDetailsEmbeddedView.swift new file mode 100644 index 000000000..42c20d767 --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/MaintenanceDetailsEmbeddedView.swift @@ -0,0 +1,54 @@ +// +// MaintenanceDetailsEmbeddedView.swift +// domains-manager-ios +// +// Created by Oleg Kuplin on 24.07.2024. +// + +import SwiftUI + +struct MaintenanceDetailsEmbeddedView: View { + + let serviceType: MaintenanceServiceType + let maintenanceData: MaintenanceModeData? + + var body: some View { + VStack(spacing: 16) { + serviceType.icon + .resizable() + .renderingMode(.template) + .squareFrame(48) + .foregroundStyle(Color.foregroundSecondary) + VStack(spacing: 8) { + Text(title) + .textAttributes(color: .foregroundSecondary, + fontSize: 22, + fontWeight: .bold) + Text(subtitle) + .textAttributes(color: .foregroundSecondary, + fontSize: 16, + fontWeight: .regular) + } + .multilineTextAlignment(.center) + + MaintenanceLinkButtonView(maintenanceData: maintenanceData) + } + .animation(.default, value: UUID()) + } +} + +// MARK: - Private methods +private extension MaintenanceDetailsEmbeddedView { + var title: String { + maintenanceData?.title ?? serviceType.title + } + + var subtitle: String { + maintenanceData?.message ?? serviceType.message + } +} + +#Preview { + MaintenanceDetailsEmbeddedView(serviceType: .activity, + maintenanceData: nil) +} diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/MaintenanceDetailsFullView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/MaintenanceDetailsFullView.swift new file mode 100644 index 000000000..3f6745fac --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/MaintenanceDetailsFullView.swift @@ -0,0 +1,50 @@ +// +// MaintenanceDetailsFullView.swift +// domains-manager-ios +// +// Created by Oleg Kuplin on 24.07.2024. +// + +import SwiftUI + +struct MaintenanceDetailsFullView: View { + + let serviceType: MaintenanceServiceType + let maintenanceData: MaintenanceModeData? + + var body: some View { + VStack(spacing: 24) { + serviceType.icon + .resizable() + .renderingMode(.template) + .squareFrame(56) + .foregroundStyle(Color.foregroundSecondary) + VStack(spacing: 16) { + Text(title) + .titleText() + Text(subtitle) + .subtitleText() + } + .multilineTextAlignment(.center) + + MaintenanceLinkButtonView(maintenanceData: maintenanceData) + } + .animation(.default, value: UUID()) + } +} + +// MARK: - Private methods +private extension MaintenanceDetailsFullView { + var title: String { + maintenanceData?.title ?? serviceType.title + } + + var subtitle: String { + maintenanceData?.message ?? serviceType.message + } +} + +#Preview { + MaintenanceDetailsFullView(serviceType: .activity, + maintenanceData: nil) +} diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/MaintenanceDetailsPullUpView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/MaintenanceDetailsPullUpView.swift new file mode 100644 index 000000000..bc05a3fc3 --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/MaintenanceDetailsPullUpView.swift @@ -0,0 +1,93 @@ +// +// MaintenanceDetailsPullUpView.swift +// domains-manager-ios +// +// Created by Oleg Kuplin on 24.07.2024. +// + +import SwiftUI + +struct MaintenanceDetailsPullUpView: View { + + @Environment(\.udFeatureFlagsService) var udFeatureFlagsService + + let serviceType: MaintenanceServiceType + let featureFlag: UDFeatureFlag + @State private var maintenanceData: MaintenanceModeData? = nil + + var body: some View { + VStack(spacing: 16) { + serviceType.icon + .resizable() + .renderingMode(.template) + .squareFrame(48) + .foregroundStyle(Color.foregroundSecondary) + VStack(spacing: 8) { + Text(title) + .textAttributes(color: .foregroundDefault, + fontSize: 22, + fontWeight: .bold) + Text(subtitle) + .textAttributes(color: .foregroundSecondary, + fontSize: 16, + fontWeight: .regular) + } + .multilineTextAlignment(.center) + + linkButtonView() + .frame(height: 48) + + gotItButton() + Spacer() + } + .padding(.top, 8) + .padding(.horizontal, 16) + .background(Color.backgroundDefault) + .animation(.default, value: UUID()) + .onAppear(perform: fetchMaintenanceData) + } +} + +// MARK: - Private methods +private extension MaintenanceDetailsPullUpView { + var title: String { + maintenanceData?.title ?? serviceType.title + } + + var subtitle: String { + maintenanceData?.message ?? serviceType.message + } + + func fetchMaintenanceData() { + let maintenanceData: MaintenanceModeData? = udFeatureFlagsService.entityValueFor(flag: featureFlag) + self.maintenanceData = maintenanceData + } + + @ViewBuilder + func linkButtonView() -> some View { + MaintenanceLinkButtonView(maintenanceData: maintenanceData) + if maintenanceData?.linkURL == nil { + // Placeholder for the button + Rectangle() + .foregroundStyle(Color.clear) + } + } + + @ViewBuilder + func gotItButton() -> some View { + UDButtonView(text: String.Constants.gotIt.localized(), + style: .large(.raisedPrimary), + callback: gotItButtonPressed) + } + + @MainActor + func gotItButtonPressed() { + appContext.coreAppCoordinator.topVC?.dismiss(animated: true) + } +} + +#Preview { + MaintenanceDetailsPullUpView(serviceType: .domainProfile, + featureFlag: .isMaintenanceProfilesAPIEnabled) + .frame(height: 280) +} diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/MaintenanceLinkButtonView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/MaintenanceLinkButtonView.swift new file mode 100644 index 000000000..5ea03c627 --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/MaintenanceLinkButtonView.swift @@ -0,0 +1,26 @@ +// +// MaintenanceLinkButtonView.swift +// domains-manager-ios +// +// Created by Oleg Kuplin on 24.07.2024. +// + +import SwiftUI + +struct MaintenanceLinkButtonView: View, ViewAnalyticsLogger { + + @Environment(\.analyticsViewName) var analyticsName + @Environment(\.analyticsAdditionalProperties) var additionalAppearAnalyticParameters + let maintenanceData: MaintenanceModeData? + + var body: some View { + if let url = maintenanceData?.linkURL { + UDButtonView(text: String.Constants.learnMore.localized(), + style: .medium(.ghostPrimary)) { + logButtonPressedAnalyticEvents(button: .learnMore) + openLinkExternally(.generic(url: url.absoluteString)) + } + } + } + +} diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/MaintenanceServiceType.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/MaintenanceServiceType.swift new file mode 100644 index 000000000..af516d456 --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Maintenance/MaintenanceServiceType.swift @@ -0,0 +1,80 @@ +// +// MaintenanceServiceType.swift +// domains-manager-ios +// +// Created by Oleg Kuplin on 24.07.2024. +// + +import Foundation +import SwiftUI + +enum MaintenanceServiceType { + case full + case activity + case explore + case home + case purchaseDomains + case vaultedDomains + case domainProfile + case sendCrypto + case signMessages + + var title: String { + switch self { + case .full: + return String.Constants.fullMaintenanceMessageTitle.localized() + case .activity: + return String.Constants.activityMaintenanceMessageTitle.localized() + case .explore: + return String.Constants.exploreMaintenanceMessageTitle.localized() + case .home: + return String.Constants.homeMaintenanceMessageTitle.localized() + case .purchaseDomains: + return String.Constants.purchaseDomainsMaintenanceMessageTitle.localized() + case .vaultedDomains: + return String.Constants.vaultedDomainsMaintenanceMessageTitle.localized() + case .domainProfile: + return String.Constants.domainProfileMaintenanceMessageTitle.localized() + case .sendCrypto: + return String.Constants.sendCryptoMaintenanceMessageTitle.localized() + case .signMessages: + return String.Constants.signMessagesMaintenanceMessageTitle.localized() + } + } + + var message: String { + switch self { + case .full: + return String.Constants.fullMaintenanceMessageSubtitle.localized() + case .activity: + return String.Constants.activityMaintenanceMessageSubtitle.localized() + case .explore: + return String.Constants.exploreMaintenanceMessageSubtitle.localized() + case .home: + return String.Constants.homeMaintenanceMessageSubtitle.localized() + case .purchaseDomains: + return String.Constants.purchaseDomainsMaintenanceMessageSubtitle.localized() + case .vaultedDomains: + return String.Constants.vaultedDomainsMaintenanceMessageSubtitle.localized() + case .domainProfile: + return String.Constants.domainProfileMaintenanceMessageSubtitle.localized() + case .sendCrypto: + return String.Constants.sendCryptoMaintenanceMessageSubtitle.localized() + case .signMessages: + return String.Constants.signMessagesMaintenanceMessageSubtitle.localized() + } + } + + var icon: Image { + switch self { + case .full: + return .udCartLogoRaster + case .activity, .explore, .home, .vaultedDomains, .domainProfile, .signMessages: + return .infoIcon + case .purchaseDomains: + return .exploreFilledIcon + case .sendCrypto: + return .paperPlaneTopRightSend + } + } +} diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Onboarding/Tutorial/TutorialViewPresenter.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Onboarding/Tutorial/TutorialViewPresenter.swift index 4a678d69f..b0220dbc7 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Onboarding/Tutorial/TutorialViewPresenter.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Onboarding/Tutorial/TutorialViewPresenter.swift @@ -31,11 +31,18 @@ extension TutorialViewPresenter: TutorialViewPresenterProtocol { } + private var isEcommMaintenanceEnabled: Bool { + let maintenanceData: MaintenanceModeData? = appContext.udFeatureFlagsService.entityValueFor(flag: .isMaintenanceEcommEnabled) + + return maintenanceData?.isCurrentlyEnabled == true + } + func didPressCreateNewWalletButton() { let udFeatureFlagsService = appContext.udFeatureFlagsService if udFeatureFlagsService.valueFor(flag: .isMPCWalletEnabled), - udFeatureFlagsService.valueFor(flag: .isMPCPurchaseEnabled) { + udFeatureFlagsService.valueFor(flag: .isMPCPurchaseEnabled), + !isEcommMaintenanceEnabled { onboardingFlowManager?.moveToStep(.createNewSelection) } else { onboardingFlowManager?.moveToStep(.createWallet) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/PurchaseDomains/Search/PurchaseSearchDomainsView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/PurchaseDomains/Search/PurchaseSearchDomainsView.swift index 8110c994d..c05842ac4 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/PurchaseDomains/Search/PurchaseSearchDomainsView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/PurchaseDomains/Search/PurchaseSearchDomainsView.swift @@ -12,6 +12,7 @@ struct PurchaseSearchDomainsView: View, ViewAnalyticsLogger { @Environment(\.purchaseDomainsService) private var purchaseDomainsService @StateObject private var debounceObject = DebounceObject() + @StateObject private var ecommFlagTracker = UDMaintenanceModeFeatureFlagTracker(featureFlag: .isMaintenanceEcommEnabled) @State private var suggestions: [DomainToPurchaseSuggestion] = [] @State private var searchResult: [DomainToPurchase] = [] @State private var isLoading = false @@ -28,6 +29,32 @@ struct PurchaseSearchDomainsView: View, ViewAnalyticsLogger { var analyticsName: Analytics.ViewName { .purchaseDomainsSearch } var body: some View { + currentContentView() + .animation(.default, value: UUID()) + .background(Color.backgroundDefault) + .onChange(of: scrollOffset) { newValue in + scrollOffsetCallback?(newValue) + } + .viewPullUp($pullUp) + .onAppear(perform: onAppear) + } +} + +// MARK: - Views +private extension PurchaseSearchDomainsView { + @ViewBuilder + func currentContentView() -> some View { + if ecommFlagTracker.maintenanceData?.isCurrentlyEnabled == true { + MaintenanceDetailsFullView(serviceType: .purchaseDomains, + maintenanceData: ecommFlagTracker.maintenanceData) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else { + contentView() + } + } + + @ViewBuilder + func contentView() -> some View { GeometryReader { geom in ZStack { OffsetObservingScrollView(offset: $scrollOffset) { @@ -41,18 +68,8 @@ struct PurchaseSearchDomainsView: View, ViewAnalyticsLogger { .offset(y: (geom.size.height / 2) - searchInspirationButtonOffsetToKeyboard) } } - .animation(.default, value: UUID()) - .background(Color.backgroundDefault) - .onChange(of: scrollOffset) { newValue in - scrollOffsetCallback?(newValue) - } - .viewPullUp($pullUp) - .onAppear(perform: onAppear) } -} - -// MARK: - Views -private extension PurchaseSearchDomainsView { + @ViewBuilder func headerView() -> some View { VStack(spacing: 16) { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Settings/SettingsView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Settings/SettingsView.swift index 02db0bd78..387970b0a 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Settings/SettingsView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Settings/SettingsView.swift @@ -12,7 +12,8 @@ struct SettingsView: View, ViewAnalyticsLogger { @Environment(\.udFeatureFlagsService) var udFeatureFlagsService @Environment(\.userProfilesService) var userProfilesService @EnvironmentObject private var tabRouter: HomeTabRouter - + @StateObject private var ecommFlagTracker = UDMaintenanceModeFeatureFlagTracker(featureFlag: .isMaintenanceEcommEnabled) + @State var initialAction: InitialAction @State private var profiles: [UserProfile] = [] @@ -327,9 +328,13 @@ private extension SettingsView { otherItemsList() } + var isEcommMaintenanceEnabled: Bool { + ecommFlagTracker.maintenanceData?.isCurrentlyEnabled == true + } + var settingsItemsToShow: [SettingsItems] { var items = SettingsItems.allCases - if webUser != nil { + if webUser != nil || isEcommMaintenanceEnabled { items.removeAll(where: { $0 == .viewVaulted }) } return items @@ -482,7 +487,8 @@ private extension SettingsView { switch action { case .create: if udFeatureFlagsService.valueFor(flag: .isMPCWalletEnabled), - udFeatureFlagsService.valueFor(flag: .isMPCPurchaseEnabled) { + udFeatureFlagsService.valueFor(flag: .isMPCPurchaseEnabled), + !isEcommMaintenanceEnabled { showAddWalletSelection() } else { createNewWallet() diff --git a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift index dd40e13e3..d36fceb6f 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift @@ -259,6 +259,8 @@ extension Analytics { case inAppAddWallet case mpcActivateEnterCode, mpcActivateEnterPassword case reconnectMPCWalletPrompt + + case fullMaintenance } } @@ -508,6 +510,7 @@ extension Analytics { case copyMultiChainAddresses case removeMPCWalletConfirmation case transactionDetails + case domainProfileMaintenance, signMessagesMaintenance // Disabled case walletTransactionsSelection, copyWalletAddressSelection diff --git a/unstoppable-ios-app/domains-manager-ios/Services/AppLaunchService/AppLaunchService.swift b/unstoppable-ios-app/domains-manager-ios/Services/AppLaunchService/AppLaunchService.swift index 13f489805..eb1462f5a 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/AppLaunchService/AppLaunchService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/AppLaunchService/AppLaunchService.swift @@ -14,16 +14,21 @@ final class AppLaunchService { private let coreAppCoordinator: CoreAppCoordinatorProtocol private let udWalletsService: UDWalletsServiceProtocol private let userProfilesService: UserProfilesServiceProtocol + private let udFeatureFlagsService: UDFeatureFlagsServiceProtocol private var sceneDelegate: SceneDelegateProtocol? private var completion: EmptyAsyncCallback? private var listeners: [AppLaunchListenerHolder] = [] + private var isInFullMaintenanceMode = false init(coreAppCoordinator: CoreAppCoordinatorProtocol, udWalletsService: UDWalletsServiceProtocol, - userProfilesService: UserProfilesServiceProtocol) { + userProfilesService: UserProfilesServiceProtocol, + udFeatureFlagsService: UDFeatureFlagsServiceProtocol) { self.coreAppCoordinator = coreAppCoordinator self.udWalletsService = udWalletsService self.userProfilesService = userProfilesService + self.udFeatureFlagsService = udFeatureFlagsService + udFeatureFlagsService.addListener(self) } } @@ -54,11 +59,45 @@ extension AppLaunchService: AppLaunchServiceProtocol { } } +// MARK: - UDFeatureFlagsListener +extension AppLaunchService: UDFeatureFlagsListener { + func didUpdatedUDFeatureFlag(_ flag: UDFeatureFlag, withValue newValue: Bool) { + if case .isMaintenanceFullEnabled = flag { + updateFullMaintenanceState() + } + } + + private func getFullMaintenanceModeData() -> MaintenanceModeData? { + let fullMaintenanceModeData: MaintenanceModeData? = udFeatureFlagsService.entityValueFor(flag: .isMaintenanceFullEnabled) + return fullMaintenanceModeData + } + + private func updateFullMaintenanceState() { + let fullMaintenanceModeData = getFullMaintenanceModeData() + if let fullMaintenanceModeData, + fullMaintenanceModeData.isCurrentlyEnabled != self.isInFullMaintenanceMode { + self.isInFullMaintenanceMode = fullMaintenanceModeData.isCurrentlyEnabled + resolveInitialViewController() + } + fullMaintenanceModeData?.onMaintenanceStatusUpdate { [weak self] in + self?.updateFullMaintenanceState() + } + } +} + // MARK: - Private methods private extension AppLaunchService { func resolveInitialViewController() { let startTime = Date() + Task { + updateFullMaintenanceState() + guard !isInFullMaintenanceMode else { + let maintenanceData: MaintenanceModeData = getFullMaintenanceModeData() ?? .init(isOn: true) + await coreAppCoordinator.showFullMaintenanceModeOn(maintenanceData: maintenanceData) + return + } + do { try await initialWalletsCheck() @@ -102,50 +141,50 @@ private extension AppLaunchService { func resolveInitialMintingState(startTime: Date, profile: UserProfile) { - Task.detached(priority: .medium) { [weak self] in - guard let self else { return } - - let appVersion = await appContext.userDataService.getLatestAppVersion() - appContext.coinRecordsService.refreshCurrencies(version: appVersion.mobileUnsReleaseVersion ?? Constants.defaultUNSReleaseVersion) - await self.appVersionUpdated(appVersion) - await self.stateMachine.set(appVersionInfo: appVersion) - - let state = await self.stateMachine.state + Task { + await stateMachine.reset() - switch state { - case .dataLoadedLate, .dataLoadedInTime, .maxIntervalPassed: - if !self.isAppVersionSupported(info: appVersion) { - await self.coreAppCoordinator.showAppUpdateRequired() - } else { + Task.detached(priority: .medium) { [weak self] in + guard let self else { return } + + let appVersion = await appContext.userDataService.getLatestAppVersion() + appContext.coinRecordsService.refreshCurrencies(version: appVersion.mobileUnsReleaseVersion ?? Constants.defaultUNSReleaseVersion) + await self.appVersionUpdated(appVersion) + await self.stateMachine.set(appVersionInfo: appVersion) + + let state = await self.stateMachine.state + + switch state { + case .dataLoadedLate, .dataLoadedInTime, .maxIntervalPassed: self.listeners.forEach { holder in holder.listener?.appLaunchServiceDidUpdateAppVersion() } + case .loading: + return } - case .loading: - return } - } - - Task.detached(priority: .background) { [weak self] in - await Task.sleep(seconds: 0.05) - guard let self else { return } - try? await self.sceneDelegate?.authorizeUserOnAppOpening() - await self.handleInitialState(await self.stateMachine.stateAfter(event: .didAuthorise), - profile: profile) - } - - Task { - await handleInitialState(await stateMachine.stateAfter(event: .didLoadData), - profile: profile) - } - - Task { - let timePassed = Date().timeIntervalSince(startTime) - let timeLeft: TimeInterval = max(0, maximumWaitingTime - timePassed) - await Task.sleep(seconds: timeLeft) - - await handleInitialState(await stateMachine.stateAfter(event: .didPassMaxWaitingTime), - profile: profile) + + Task.detached(priority: .background) { [weak self] in + await Task.sleep(seconds: 0.05) + guard let self else { return } + try? await self.sceneDelegate?.authorizeUserOnAppOpening() + await self.handleInitialState(await self.stateMachine.stateAfter(event: .didAuthorise), + profile: profile) + } + + Task { + await handleInitialState(await stateMachine.stateAfter(event: .didLoadData), + profile: profile) + } + + Task { + let timePassed = Date().timeIntervalSince(startTime) + let timeLeft: TimeInterval = max(0, maximumWaitingTime - timePassed) + await Task.sleep(seconds: timeLeft) + + await handleInitialState(await stateMachine.stateAfter(event: .didPassMaxWaitingTime), + profile: profile) + } } } @@ -158,8 +197,10 @@ private extension AppLaunchService { case .loading: return case .maxIntervalPassed, .dataLoadedInTime: - if let newAppVersionInfo = await stateMachine.appVersionInfo, - !isAppVersionSupported(info: newAppVersionInfo) { + if isInFullMaintenanceMode { + coreAppCoordinator.showAppUpdateRequired() + } else if let newAppVersionInfo = await stateMachine.appVersionInfo, + !isAppVersionSupported(info: newAppVersionInfo) { coreAppCoordinator.showAppUpdateRequired() } else { coreAppCoordinator.showHome(profile: profile) @@ -281,5 +322,13 @@ private extension AppLaunchService { func set(appVersionInfo: AppVersionInfo) { self.appVersionInfo = appVersionInfo } + + func reset() { + didAuthorise = false + didLoadData = false + didPassMaxWaitingTime = false + state = .loading + appVersionInfo = nil + } } } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/CoreAppCoordinator/CoreAppCoordinator.swift b/unstoppable-ios-app/domains-manager-ios/Services/CoreAppCoordinator/CoreAppCoordinator.swift index a378bca81..5a4abd0de 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/CoreAppCoordinator/CoreAppCoordinator.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/CoreAppCoordinator/CoreAppCoordinator.swift @@ -55,6 +55,12 @@ extension CoreAppCoordinator: CoreAppCoordinatorProtocol { currentRoot = .appUpdate } + func showFullMaintenanceModeOn(maintenanceData: MaintenanceModeData) { + let appUpdateRequiredVC = FullMaintenanceModeView.instance(maintenanceData: maintenanceData) + setRootViewController(appUpdateRequiredVC) + currentRoot = .fullMaintenance + } + func setKeyWindow() { window?.makeKeyAndVisible() topInfoWindow?.makeKeyAndVisible() @@ -395,7 +401,7 @@ extension CoreAppCoordinator { // MARK: - CurrentRoot private extension CoreAppCoordinator { enum CurrentRoot { - case none, onboarding, appUpdate + case none, onboarding, appUpdate, fullMaintenance case home(router: HomeTabRouter) } } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/CoreAppCoordinator/CoreAppCoordinatorProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Services/CoreAppCoordinator/CoreAppCoordinatorProtocol.swift index 076bab812..fee99abed 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/CoreAppCoordinator/CoreAppCoordinatorProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/CoreAppCoordinator/CoreAppCoordinatorProtocol.swift @@ -15,6 +15,7 @@ protocol CoreAppCoordinatorProtocol: WalletConnectClientUIHandler, ExternalEvent func showOnboarding(_ flow: OnboardingNavigationController.OnboardingFlow) func showHome(profile: UserProfile) func showAppUpdateRequired() + func showFullMaintenanceModeOn(maintenanceData: MaintenanceModeData) func setKeyWindow() @discardableResult func goBackToPreviousApp() -> Bool diff --git a/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/LaunchDarklyService.swift b/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/LaunchDarklyService.swift index 0c73fec67..cb059cea6 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/LaunchDarklyService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/LaunchDarklyService.swift @@ -50,9 +50,26 @@ extension LaunchDarklyService { func valueFor(key: String, defaultValue: Bool) -> Bool { + if let jsonValue = ldClient?.jsonVariation(forKey: key, defaultValue: ""), + case .object(let dictionary) = jsonValue, + let ldValue = dictionary["isOn"], + case .bool(let bool) = ldValue { + return bool + } let ldValue = ldClient?.boolVariation(forKey: key, defaultValue: defaultValue) return ldValue ?? defaultValue } + + func entityValueFor(key: String) -> T? { + if let jsonLDValue = ldClient?.jsonVariation(forKey: key, defaultValue: ""), + case .object(let objectData) = jsonLDValue, + let jsonData = objectData.jsonData() { + + return T.objectFromData(jsonData) + } + + return nil + } } // MARK: - Private methods diff --git a/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/MockUDFeatureFlagsService.swift b/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/MockUDFeatureFlagsService.swift index 218a274b5..9ecc5352a 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/MockUDFeatureFlagsService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/MockUDFeatureFlagsService.swift @@ -6,11 +6,13 @@ // import Foundation +import Combine final class MockUDFeatureFlagsService { private var listenerHolders: [UDFeatureFlagListenerHolder] = [] private var isMocking = false - + private(set) var featureFlagPublisher = PassthroughSubject() + init() { start() } @@ -25,6 +27,10 @@ extension MockUDFeatureFlagsService: UDFeatureFlagsServiceProtocol { return flag.defaultValue } + func entityValueFor(flag: UDFeatureFlag) -> T? { + nil + } + func addListener(_ listener: UDFeatureFlagsListener) { if !listenerHolders.contains(where: { $0.listener === listener }) { listenerHolders.append(.init(listener: listener)) diff --git a/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/UDFeatureFlag.swift b/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/UDFeatureFlag.swift index b754ffb77..7dbb30213 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/UDFeatureFlag.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/UDFeatureFlag.swift @@ -19,12 +19,29 @@ enum UDFeatureFlag: String, CaseIterable { case isMPCSignatureEnabled = "mobile-mpc-signature-enabled" case isMPCPurchaseEnabled = "mobile-mpc-purchase-enabled" + case isMaintenanceFullEnabled = "mobile-maintenance-full" + case isMaintenanceOKLinkEnabled = "mobile-maintenance-oklink" + case isMaintenanceProfilesAPIEnabled = "mobile-maintenance-profiles-api" + case isMaintenanceEcommEnabled = "mobile-maintenance-ecomm" + case isMaintenanceInfuraEnabled = "mobile-maintenance-infura" + case isMaintenanceMPCEnabled = "mobile-maintenance-mpc" + var defaultValue: Bool { switch self { - case .communityMediaEnabled, .isBuyCryptoEnabled, .isMPCMessagingEnabled, .isMPCWCNativeEnabled: + case .communityMediaEnabled, .isBuyCryptoEnabled, .isMPCMessagingEnabled, .isMPCWCNativeEnabled, .isMaintenanceFullEnabled, .isMaintenanceOKLinkEnabled, .isMaintenanceProfilesAPIEnabled, .isMaintenanceEcommEnabled, .isMaintenanceInfuraEnabled, .isMaintenanceMPCEnabled: return false case .isSendCryptoEnabled, .isMPCWalletEnabled, .isMPCSendCryptoEnabled, .isMPCSignatureEnabled, .isMPCPurchaseEnabled: return true } } + + /// This flag has JSON structure + var isStructuredFlag: Bool { + switch self { + case .isMaintenanceFullEnabled, .isMaintenanceOKLinkEnabled, .isMaintenanceProfilesAPIEnabled, .isMaintenanceEcommEnabled, .isMaintenanceInfuraEnabled, .isMaintenanceMPCEnabled: + return true + case .isSendCryptoEnabled, .isMPCWalletEnabled, .isMPCSendCryptoEnabled, .isMPCSignatureEnabled, .isMPCPurchaseEnabled, .communityMediaEnabled, .isBuyCryptoEnabled, .isMPCMessagingEnabled, .isMPCWCNativeEnabled: + return false + } + } } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/UDFeatureFlagsService.swift b/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/UDFeatureFlagsService.swift index 63aefdf93..36fdb049c 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/UDFeatureFlagsService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/UDFeatureFlagsService.swift @@ -6,12 +6,14 @@ // import Foundation +import Combine final class UDFeatureFlagsService { private let ldService: LaunchDarklyService private var listenerHolders: [UDFeatureFlagListenerHolder] = [] - + private(set) var featureFlagPublisher = PassthroughSubject() + init() { #if DEBUG let ldMobileKey = LaunchDarkly.stagingMobileKey @@ -32,6 +34,10 @@ extension UDFeatureFlagsService: UDFeatureFlagsServiceProtocol { return ldValue } + func entityValueFor(flag: UDFeatureFlag) -> T? { + ldService.entityValueFor(key: flag.rawValue) + } + func addListener(_ listener: UDFeatureFlagsListener) { if !listenerHolders.contains(where: { $0.listener === listener }) { listenerHolders.append(.init(listener: listener)) @@ -57,7 +63,7 @@ private extension UDFeatureFlagsService { let defaultValue = getDefaultValueFor(featureFlag: flag) let value = valueFor(flag: flag) - if value != defaultValue { + if flag.isStructuredFlag || value != defaultValue { storeValue(value, forFeatureFlag: flag) notifyListenersUpdated(flag: flag, withValue: value) } @@ -65,6 +71,7 @@ private extension UDFeatureFlagsService { func notifyListenersUpdated(flag: UDFeatureFlag, withValue value: Bool) { listenerHolders.forEach { $0.listener?.didUpdatedUDFeatureFlag(flag, withValue: value) } + featureFlagPublisher.send(flag) } } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/UDFeatureFlagsServiceProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/UDFeatureFlagsServiceProtocol.swift index a631e5cf7..055bff59b 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/UDFeatureFlagsServiceProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/UDFeatureFlagsServiceProtocol.swift @@ -6,9 +6,13 @@ // import Foundation +import Combine protocol UDFeatureFlagsServiceProtocol { + var featureFlagPublisher: PassthroughSubject { get } + func valueFor(flag: UDFeatureFlag) -> Bool + func entityValueFor(flag: UDFeatureFlag) -> T? func addListener(_ listener: UDFeatureFlagsListener) func removeListener(_ listener: UDFeatureFlagsListener) } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/UDMaintenanceModeFeatureFlagTracker.swift b/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/UDMaintenanceModeFeatureFlagTracker.swift new file mode 100644 index 000000000..8daf4a877 --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios/Services/FeatureFlags/UDMaintenanceModeFeatureFlagTracker.swift @@ -0,0 +1,43 @@ +// +// UDMaintenanceModeFeatureFlagTracker.swift +// domains-manager-ios +// +// Created by Oleg Kuplin on 24.07.2024. +// + +import Foundation +import Combine + +@MainActor +final class UDMaintenanceModeFeatureFlagTracker: ObservableObject { + + let featureFlag: UDFeatureFlag + @Published var maintenanceData: MaintenanceModeData? + private var cancellables: Set = [] + + init(featureFlag: UDFeatureFlag) { + self.featureFlag = featureFlag + updateMaintenanceData() + + appContext.udFeatureFlagsService.featureFlagPublisher.receive(on: DispatchQueue.main).sink { [weak self] flag in + self?.didReceiveChangeFlagNotification(flag) + }.store(in: &cancellables) + } + + private func updateMaintenanceData() { + let maintenanceData: MaintenanceModeData? = appContext.udFeatureFlagsService.entityValueFor(flag: featureFlag) + if let maintenanceData { + self.maintenanceData = maintenanceData + maintenanceData.onMaintenanceStatusUpdate { [weak self] in + self?.updateMaintenanceData() + } + } + } + + private func didReceiveChangeFlagNotification(_ flag: UDFeatureFlag) { + if flag == featureFlag { + updateMaintenanceData() + } + } + +} diff --git a/unstoppable-ios-app/domains-manager-ios/Services/PullUp/PullUpViewService.swift b/unstoppable-ios-app/domains-manager-ios/Services/PullUp/PullUpViewService.swift index dc1ea47bb..c17d7c90a 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/PullUp/PullUpViewService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/PullUp/PullUpViewService.swift @@ -534,6 +534,35 @@ extension PullUpViewService: PullUpViewServiceProtocol { presentPullUpView(in: viewController, pullUp: .walletsMaxNumberLimitReachedAlready, contentView: selectionView, isDismissAble: true, height: selectionViewHeight) } + + func showDomainProfileInMaintenancePullUp(in viewController: UIViewController) { + showMaintenanceInProgressPullUp(in: viewController, + pullUp: .domainProfileMaintenance, + serviceType: .domainProfile, + featureFlag: .isMaintenanceProfilesAPIEnabled) + } + + func showMessageSigningInMaintenancePullUp(in viewController: UIViewController) { + showMaintenanceInProgressPullUp(in: viewController, + pullUp: .signMessagesMaintenance, + serviceType: .signMessages, + featureFlag: .isMaintenanceMPCEnabled) + } + + private func showMaintenanceInProgressPullUp(in viewController: UIViewController, + pullUp: Analytics.PullUp, + serviceType: MaintenanceServiceType, + featureFlag: UDFeatureFlag) { + let view = MaintenanceDetailsPullUpView(serviceType: serviceType, featureFlag: featureFlag) + .frame(height: 380) + let vc = UIHostingController(rootView: view) + + showIfNotPresent(in: viewController, + pullUp: pullUp, + contentView: vc.view, + isDismissAble: true, + height: 380) + } } import SwiftUI diff --git a/unstoppable-ios-app/domains-manager-ios/Services/PullUp/PullUpViewServiceProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Services/PullUp/PullUpViewServiceProtocol.swift index e41d1ae59..2fb174a8b 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/PullUp/PullUpViewServiceProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/PullUp/PullUpViewServiceProtocol.swift @@ -47,6 +47,8 @@ protocol PullUpViewServiceProtocol { maxNumberOfWallets: Int) func showCopyMultichainWalletAddressesPullUp(in viewController: UIViewController, tokens: [BalanceTokenUIDescription]) + func showDomainProfileInMaintenancePullUp(in viewController: UIViewController) + func showMessageSigningInMaintenancePullUp(in viewController: UIViewController) // MARK: - External wallet func showConnectedWalletInfoPullUp(in viewController: UIViewController) diff --git a/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Assets.xcassets/Settings/settingsIconPrivacy.imageset/Contents.json b/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Assets.xcassets/Settings/settingsIconPrivacy.imageset/Contents.json index 8397c4271..f9841fc94 100644 --- a/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Assets.xcassets/Settings/settingsIconPrivacy.imageset/Contents.json +++ b/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Assets.xcassets/Settings/settingsIconPrivacy.imageset/Contents.json @@ -10,6 +10,7 @@ "version" : 1 }, "properties" : { + "preserves-vector-representation" : true, "template-rendering-intent" : "template" } } diff --git a/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Localization/en.lproj/Localizable.strings b/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Localization/en.lproj/Localizable.strings index d9dc52b44..92495ee02 100644 --- a/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Localization/en.lproj/Localizable.strings +++ b/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Localization/en.lproj/Localizable.strings @@ -1177,3 +1177,23 @@ More tabs are coming in the next updates."; "BACKED_UP" = "Backed up"; "BACK_UP" = "Back up"; "SET_AS_PRIMARY_DOMAIN" = "Set as primary domain"; + +// Maintenance +"FULL_MAINTENANCE_MESSAGE_TITLE" = "Our services are in a maintenance mode"; +"FULL_MAINTENANCE_MESSAGE_SUBTITLE" = "Do not worry, we'll be back shortly.\nThank you for your patience!"; +"ACTIVITY_MAINTENANCE_MESSAGE_TITLE" = "Activity is currently unavailable"; +"ACTIVITY_MAINTENANCE_MESSAGE_SUBTITLE" = "We're working to make it available soon."; +"EXPLORE_MAINTENANCE_MESSAGE_TITLE" = "Profiles search is currently unavailable"; +"EXPLORE_MAINTENANCE_MESSAGE_SUBTITLE" = "We're working to make it available soon."; +"HOME_MAINTENANCE_MESSAGE_TITLE" = "Displaying your tokens, collectibles and domains in the app is currently unavailable"; +"HOME_MAINTENANCE_MESSAGE_SUBTITLE" = "All your assets are safe. It is only the problem with API that display your assets in the app. We're working to make it available soon."; +"PURCHASE_DOMAINS_MAINTENANCE_MESSAGE_TITLE" = "Searching for and purchasing domains is currently unavailable"; +"PURCHASE_DOMAINS_MAINTENANCE_MESSAGE_SUBTITLE" = "We're working to make it available soon."; +"VAULTED_DOMAINS_MAINTENANCE_MESSAGE_TITLE" = "Displaying domains in imported vaults is currently unavailable"; +"VAULTED_DOMAINS_MAINTENANCE_MESSAGE_SUBTITLE" = "If you have imported vaults previously and do not see them now, don’t worry. We’re working to make them available soon."; +"DOMAIN_PROFILE_MAINTENANCE_MESSAGE_TITLE" = "Domain profile is currently unavailable"; +"DOMAIN_PROFILE_MAINTENANCE_MESSAGE_SUBTITLE" = "We're working to make it available soon."; +"SEND_CRYPTO_MAINTENANCE_MESSAGE_TITLE" = "Sending crypto is currently unavailable\nusing this wallet"; +"SEND_CRYPTO_MAINTENANCE_MESSAGE_SUBTITLE" = "We're working to make it available soon.\nMeantime try to switch to another wallet."; +"SIGN_MESSAGES_MAINTENANCE_MESSAGE_TITLE" = "Signing messages is currently unavailable"; +"SIGN_MESSAGES_MAINTENANCE_MESSAGE_SUBTITLE" = "We're working to make it available soon."; diff --git a/unstoppable-ios-app/domains-manager-iosTests/DeepLinksServiceTests.swift b/unstoppable-ios-app/domains-manager-iosTests/DeepLinksServiceTests.swift index 74603ecbd..2eaa54b9e 100644 --- a/unstoppable-ios-app/domains-manager-iosTests/DeepLinksServiceTests.swift +++ b/unstoppable-ios-app/domains-manager-iosTests/DeepLinksServiceTests.swift @@ -203,6 +203,8 @@ private final class MockCoreAppCoordinator: CoreAppCoordinatorProtocol { } + func showFullMaintenanceModeOn(maintenanceData: MaintenanceModeData) { } + func setKeyWindow() { } diff --git a/unstoppable-ios-app/domains-manager-iosTests/Helpers/TestableMPCWalletsService.swift b/unstoppable-ios-app/domains-manager-iosTests/Helpers/TestableMPCWalletsService.swift index ad64ded0e..8fbef3114 100644 --- a/unstoppable-ios-app/domains-manager-iosTests/Helpers/TestableMPCWalletsService.swift +++ b/unstoppable-ios-app/domains-manager-iosTests/Helpers/TestableMPCWalletsService.swift @@ -22,6 +22,14 @@ final class TestableMPCWalletsService: MPCWalletsServiceProtocol { "" } + func sendETHTransaction(data: String, + value: String, + chain: BlockchainType, + destinationAddress: String, + by walletMetadata: MPCWalletMetadata) async throws -> String { + "" + } + func getTokens(for walletMetadata: MPCWalletMetadata) throws -> [BalanceTokenUIDescription] { [] } diff --git a/unstoppable-ios-app/domains-manager-iosTests/MaintenanceModeDataTests.swift b/unstoppable-ios-app/domains-manager-iosTests/MaintenanceModeDataTests.swift new file mode 100644 index 000000000..c777a0148 --- /dev/null +++ b/unstoppable-ios-app/domains-manager-iosTests/MaintenanceModeDataTests.swift @@ -0,0 +1,91 @@ +// +// MaintenanceModeDataTests.swift +// domains-manager-iosTests +// +// Created by Oleg Kuplin on 24.07.2024. +// + +import XCTest +@testable import domains_manager_ios + +class MaintenanceModeDataTests: XCTestCase { + func testIsCurrentlyEnabledWhenIsOnAndNoDates() { + let maintenanceData = MaintenanceModeData(isOn: true) + + XCTAssertTrue(maintenanceData.isCurrentlyEnabled) + } + + func testIsCurrentlyEnabledWhenIsOff() { + let maintenanceData = MaintenanceModeData(isOn: false) + + XCTAssertFalse(maintenanceData.isCurrentlyEnabled) + } + + func testIsCurrentlyEnabledWithinDateRange() { + let startDate = Date().addingTimeInterval(-3600) // 1 hour ago + let endDate = Date().addingTimeInterval(3600) // 1 hour later + let maintenanceData = MaintenanceModeData(isOn: true, startDate: startDate, endDate: endDate) + + XCTAssertTrue(maintenanceData.isCurrentlyEnabled) + } + + func testIsCurrentlyEnabledBeforeStartDateTrue() { + let startDate = Date().addingTimeInterval(-3600) // 1 hour later + let maintenanceData = MaintenanceModeData(isOn: true, startDate: startDate) + + XCTAssertTrue(maintenanceData.isCurrentlyEnabled) + } + + func testIsCurrentlyEnabledBeforeStartDateFalse() { + let startDate = Date().addingTimeInterval(3600) // 1 hour later + let maintenanceData = MaintenanceModeData(isOn: true, startDate: startDate) + + XCTAssertFalse(maintenanceData.isCurrentlyEnabled) + } + + func testIsCurrentlyEnabledBeforeStartDateAndAfterEndDateCorrect() { + let startDate = Date().addingTimeInterval(3600) // 1 hour ago + let endDate = Date().addingTimeInterval(3900) // 1 hour later + let maintenanceData = MaintenanceModeData(isOn: true, startDate: startDate, endDate: endDate) + + XCTAssertFalse(maintenanceData.isCurrentlyEnabled) + } + + func testIsCurrentlyEnabledBeforeStartDateAndAfterEndDateIncorrect() { + let startDate = Date().addingTimeInterval(3600) // 1 hour ago + let endDate = Date().addingTimeInterval(-3600) // 1 hour later + let maintenanceData = MaintenanceModeData(isOn: true, startDate: startDate, endDate: endDate) + + XCTAssertFalse(maintenanceData.isCurrentlyEnabled) + } + + func testIsCurrentlyEnabledAfterEndDateFalse() { + let endDate = Date().addingTimeInterval(-3600) // 1 hour ago + let maintenanceData = MaintenanceModeData(isOn: true, endDate: endDate) + + XCTAssertFalse(maintenanceData.isCurrentlyEnabled) + } + + func testIsCurrentlyEnabledAfterEndDateTrue() { + let endDate = Date().addingTimeInterval(3600) // 1 hour ago + let maintenanceData = MaintenanceModeData(isOn: true, endDate: endDate) + + XCTAssertTrue(maintenanceData.isCurrentlyEnabled) + } + + func testIsCurrentlyEnabledAfterEndDateAndStartDateCorrect() { + let startDate = Date().addingTimeInterval(-3900) // 1 hour ago + let endDate = Date().addingTimeInterval(-3600) // 1 hour later + let maintenanceData = MaintenanceModeData(isOn: true, startDate: startDate, endDate: endDate) + + XCTAssertFalse(maintenanceData.isCurrentlyEnabled) + } + + func testIsCurrentlyEnabledAfterEndDateAndStartDateIncorrect() { + let startDate = Date().addingTimeInterval(-3000) // 1 hour ago + let endDate = Date().addingTimeInterval(-3600) // 1 hour later + let maintenanceData = MaintenanceModeData(isOn: true, startDate: startDate, endDate: endDate) + + XCTAssertFalse(maintenanceData.isCurrentlyEnabled) + } +}