Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MOB-2119 - Maintenance mode #616

Merged
merged 16 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ final class CoreAppCoordinator: CoreAppCoordinatorProtocol {

}

func showFullMaintenanceModeOn(maintenanceData: MaintenanceModeData) { }

func setKeyWindow() {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@
//

import Foundation
import Combine

final class UDFeatureFlagsService: UDFeatureFlagsServiceProtocol {
private(set) var featureFlagPublisher = PassthroughSubject<UDFeatureFlag, Never>()

func entityValueFor<T: Codable>(flag: UDFeatureFlag) -> T? {
nil
}

func valueFor(flag: UDFeatureFlag) -> Bool {
true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

}
Expand All @@ -56,7 +60,6 @@ extension FB_UD_MPC {
""
}


func fetchCryptoPortfolioForMPC(wallet: String, accessToken: String) async throws -> [WalletTokenPortfolio] {
MockEntitiesFabric.Wallet.mockEntities()[0].balance
}
Expand Down
60 changes: 60 additions & 0 deletions unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
}




Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 }

Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 }

Expand All @@ -22,7 +23,7 @@ struct HomeExploreView: View, ViewAnalyticsLogger {
if viewModel.isSearchActive {
domainSearchTypeSelector()
}
contentList()
contentView()
}
.animation(.default, value: UUID())
.background(Color.backgroundDefault)
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
}))
Expand Down Expand Up @@ -267,7 +272,6 @@ extension HomeTabRouter {
completion(Void())
}))
}
//await view.dismissPullUpMenu()
await finishSetupPurchasedProfileIfNeeded(domains: domains, requests: requests)
}
}
Expand Down Expand Up @@ -337,6 +341,12 @@ extension HomeTabRouter {
}
}
}

func showSigningMessagesInMaintenancePullUp() {
guard let topVC else { return }

appContext.pullUpViewService.showMessageSigningInMaintenancePullUp(in: topVC)
}
}

// MARK: - Pull up related
Expand Down Expand Up @@ -416,7 +426,6 @@ extension HomeTabRouter: PublicProfileViewDelegate {

UDRouter().showTransferInProgressScreen(domain: domain, in: topVC)
}

}

// MARK: - Private methods
Expand Down Expand Up @@ -501,7 +510,6 @@ private extension HomeTabRouter {
}
}))
}
// await view.dismissPullUpMenu()
await finishSetupPurchasedProfileIfNeeded(domains: domains, requests: requests)
} catch {
PurchasedDomainsStorage.setPendingNonEmptyProfiles(pendingProfilesLeft)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand All @@ -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])))
Expand Down
Loading