diff --git a/DashSyncCurrentCommit b/DashSyncCurrentCommit index 86f98cd8a..9a1806aff 100644 --- a/DashSyncCurrentCommit +++ b/DashSyncCurrentCommit @@ -1 +1 @@ -8e211b3d6c33781c1cad47ec58f66ad5914e8ae6 +751595434308e0761cceb15b428a41ca5eefdb8b diff --git a/DashWallet.xcodeproj/project.pbxproj b/DashWallet.xcodeproj/project.pbxproj index 5f2322732..449f9e512 100644 --- a/DashWallet.xcodeproj/project.pbxproj +++ b/DashWallet.xcodeproj/project.pbxproj @@ -8274,13 +8274,23 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-dashpay/Pods-dashpay-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/DashSync/DashSync.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport/GoogleDataTransport_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/Protobuf/Protobuf_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Alamofire.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/DashSync.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleDataTransport_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleUtilities_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Protobuf_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/TOCropViewControllerBundle.bundle", ); @@ -8332,12 +8342,22 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-dashwallet/Pods-dashwallet-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/DashSync/DashSync.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport/GoogleDataTransport_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/Protobuf/Protobuf_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Alamofire.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/DashSync.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleDataTransport_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleUtilities_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Protobuf_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", ); runOnlyForDeploymentPostprocessing = 0; @@ -10159,7 +10179,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = dashwallet/dashwallet.entitlements; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 44RJ69WHFF; EXCLUDED_ARCHS = ""; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; @@ -10267,7 +10287,8 @@ "\"UIKit\"", "-weak_framework", "\"WebKit\"", - "-ld64", + "-ld_classic", + "-Wl,-no_compact_unwind", ); PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -10289,7 +10310,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = dashwallet/dashwallet.entitlements; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 44RJ69WHFF; EXCLUDED_ARCHS = ""; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -10406,7 +10427,8 @@ "\"UIKit\"", "-weak_framework", "\"WebKit\"", - "-ld64", + "-ld_classic", + "-Wl,-no_compact_unwind", ); PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -10495,7 +10517,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 44RJ69WHFF; EXCLUDED_ARCHS = ""; IBSC_MODULE = WatchApp_Extension; @@ -10517,7 +10539,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 44RJ69WHFF; EXCLUDED_ARCHS = ""; IBSC_MODULE = WatchApp_Extension; @@ -10537,7 +10559,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = C98AA93FF5283EC6405BCE4B /* Pods-WatchApp Extension.debug.xcconfig */; buildSettings = { - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 44RJ69WHFF; ENABLE_BITCODE = NO; EXCLUDED_ARCHS = ""; @@ -10564,7 +10586,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = CE02413EF0C60B1D1EDE6457 /* Pods-WatchApp Extension.release.xcconfig */; buildSettings = { - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 44RJ69WHFF; ENABLE_BITCODE = NO; EXCLUDED_ARCHS = ""; @@ -10606,7 +10628,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 8.0.0; + MARKETING_VERSION = 8.2.0; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.TodayExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -10635,7 +10657,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 8.0.0; + MARKETING_VERSION = 8.2.0; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.TodayExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -11276,7 +11298,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = dashwallet/dashwallet.entitlements; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 44RJ69WHFF; EXCLUDED_ARCHS = ""; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -11393,7 +11415,8 @@ "\"UIKit\"", "-weak_framework", "\"WebKit\"", - "-ld64", + "-ld_classic", + "-Wl,-no_compact_unwind", ); PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -11426,7 +11449,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 8.0.0; + MARKETING_VERSION = 8.2.0; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.TodayExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -11477,7 +11500,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 44RJ69WHFF; EXCLUDED_ARCHS = ""; IBSC_MODULE = WatchApp_Extension; @@ -11497,7 +11520,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 556B5EBEBAEA571D74FF69A3 /* Pods-WatchApp Extension.testflight.xcconfig */; buildSettings = { - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 44RJ69WHFF; ENABLE_BITCODE = NO; EXCLUDED_ARCHS = ""; @@ -11596,7 +11619,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = dashwallet/dashwallet.entitlements; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 44RJ69WHFF; EXCLUDED_ARCHS = ""; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -11703,7 +11726,8 @@ "\"UIKit\"", "-weak_framework", "\"WebKit\"", - "-ld64", + "-ld_classic", + "-Wl,-no_compact_unwind", ); PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -11735,7 +11759,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 8.0.0; + MARKETING_VERSION = 8.2.0; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.TodayExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -11785,7 +11809,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 44RJ69WHFF; EXCLUDED_ARCHS = ""; IBSC_MODULE = WatchApp_Extension; @@ -11805,7 +11829,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 29B232FD70BA2EDF87F86A56 /* Pods-WatchApp Extension.testnet.xcconfig */; buildSettings = { - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 44RJ69WHFF; ENABLE_BITCODE = NO; EXCLUDED_ARCHS = ""; diff --git a/DashWallet/Sources/Application/Syncyng Activity Monitor/SyncingActivityMonitor.swift b/DashWallet/Sources/Application/Syncyng Activity Monitor/SyncingActivityMonitor.swift index 7b80c5f2a..c9f1eb9b6 100644 --- a/DashWallet/Sources/Application/Syncyng Activity Monitor/SyncingActivityMonitor.swift +++ b/DashWallet/Sources/Application/Syncyng Activity Monitor/SyncingActivityMonitor.swift @@ -18,6 +18,7 @@ import Foundation private let kChainManagerNotificationChainKey = "DSChainManagerNotificationChainKey" +private let kChainManagerNotificationSyncStateKey = "DSChainManagerNotificationSyncStateKey" private let kSyncingCompleteProgress = 1.0 private let kMaxProgressDelta = 0.1 // 10% @@ -53,6 +54,7 @@ class SyncingActivityMonitor: NSObject, NetworkReachabilityHandling { case noConnection case unknown } + public lazy var model: DSSyncState = DSSyncState(syncPhase: .offline) @objc public var progress: Double = 0 { @@ -122,14 +124,6 @@ class SyncingActivityMonitor: NSObject, NetworkReachabilityHandling { @objc func chainManagerSyncStartedNotification(notification: Notification) { guard shouldAcceptSyncNotification(notification) else { return } - - startSyncingActivity() - } - - @objc - func chainManagerParametersUpdatedNotification(notification: Notification) { - guard shouldAcceptSyncNotification(notification) else { return } - startSyncingActivity() } @@ -147,20 +141,20 @@ class SyncingActivityMonitor: NSObject, NetworkReachabilityHandling { stopSyncingActivity(failed: true) } - + @objc - func chainManagerChainBlocksDidChangeNotification(notification: Notification) { - guard !isSyncing, chainSyncProgress < kSyncingCompleteProgress else { return } - - startSyncingActivity() + func chainManagerSyncStateChangedNotification(notification: Notification) { + guard shouldAcceptSyncNotification(notification) else { return } + + if let model = notification.userInfo?[kChainManagerNotificationSyncStateKey] as? DSSyncState { + self.model = model + } } - + @objc func peerManagerConnectedPeersDidChangeNotification(notification: Notification) { - let isConnected = DWEnvironment.sharedInstance().currentChainManager.peerManager.connected - - if isConnected { - NotificationCenter.default.removeObserver(self, name: .peerManagerConnectedPeersDidChange, object: nil) + if model.peerManagerConnected { + removeChainObserver(.peerManagerConnectedPeersDidChange) startSyncingIfNeeded() } } @@ -173,14 +167,13 @@ class SyncingActivityMonitor: NSObject, NetworkReachabilityHandling { @objc public static let shared = SyncingActivityMonitor() } -// MARK: Suncing +// MARK: Syncing extension SyncingActivityMonitor { + private func startSyncingIfNeeded() { - guard DWEnvironment.sharedInstance().currentChainManager.peerManager.connected else { - NotificationCenter.default.addObserver(self, selector: #selector(peerManagerConnectedPeersDidChangeNotification(notification:)), - name: .peerManagerConnectedPeersDidChange, object: nil) - + guard model.peerManagerConnected else { + addChainObserver(.peerManagerConnectedPeersDidChange, #selector(peerManagerConnectedPeersDidChangeNotification(notification:))) return } @@ -190,7 +183,7 @@ extension SyncingActivityMonitor { private func startSyncingActivity() { guard !isSyncing else { return } - progress = chainSyncProgress + progress = model.combinedSyncProgress lastPeakDate = nil NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(syncLoop), object: nil) @@ -205,16 +198,18 @@ extension SyncingActivityMonitor { isSyncing = false state = failed ? .syncFailed : .syncDone } - + @objc private func syncLoop() { guard reachability.networkReachabilityStatus != .notReachable else { state = .noConnection return } + updateProgress() + } - let progress = chainSyncProgress - + private func updateProgress() { + let progress = model.combinedSyncProgress if progress < kSyncingCompleteProgress { isSyncing = true @@ -261,18 +256,18 @@ extension SyncingActivityMonitor { syncLoop() } + private func addChainObserver(_ aName: NSNotification.Name?, _ aSelector: Selector) { + NotificationCenter.default.addObserver(self, selector: aSelector, name: aName, object: nil) + } + private func removeChainObserver(_ aName: NSNotification.Name?) { + NotificationCenter.default.removeObserver(self, name: aName, object: nil) + } + private func configureObserver() { - let notificationCenter = NotificationCenter.default - notificationCenter.addObserver(self, selector: #selector(chainManagerSyncStartedNotification(notification:)), - name: .chainManagerSyncStarted, object: nil) - notificationCenter.addObserver(self, selector: #selector(chainManagerParametersUpdatedNotification(notification:)), - name: .chainManagerParametersUpdated, object: nil) - notificationCenter.addObserver(self, selector: #selector(chainManagerSyncFinishedNotification(notification:)), - name: .chainManagerSyncFinished, object: nil) - notificationCenter.addObserver(self, selector: #selector(chainManagerSyncFailedNotification(notification:)), - name: .chainManagerSyncFailed, object: nil) - notificationCenter.addObserver(self, selector: #selector(chainManagerChainBlocksDidChangeNotification(notification:)), - name: .chainManagerChainSyncBlocksDidChange, object: nil) + addChainObserver(.chainManagerSyncStarted, #selector(chainManagerSyncStartedNotification(notification:))) + addChainObserver(.chainManagerSyncFinished, #selector(chainManagerSyncFinishedNotification(notification:))) + addChainObserver(.chainManagerSyncFailed, #selector(chainManagerSyncFailedNotification(notification:))) + addChainObserver(.chainManagerSyncStateChanged, #selector(chainManagerSyncStateChangedNotification(notification:))) } } @@ -280,7 +275,7 @@ extension SyncingActivityMonitor { extension SyncingActivityMonitor { private var chainSyncProgress: Double { - DWEnvironment.sharedInstance().currentChainManager.combinedSyncProgress + model.combinedSyncProgress } private var shouldStopSyncing: Bool { @@ -305,16 +300,12 @@ extension SyncingActivityMonitor { } extension Notification.Name { + // TODO: unused? static let syncStateChangedNotification: Notification.Name = .init(rawValue: "DWSyncStateChangedNotification") static let chainManagerSyncStarted: Notification.Name = .init(rawValue: "DSChainManagerSyncWillStartNotification") - static let chainManagerParametersUpdated: Notification - .Name = .init(rawValue: "DSChainManagerSyncParametersUpdatedNotification") static let chainManagerSyncFinished: Notification.Name = .init(rawValue: "DSChainManagerSyncFinishedNotification") static let chainManagerSyncFailed: Notification.Name = .init(rawValue: "DSChainManagerSyncFailedNotification") - static let chainManagerChainSyncBlocksDidChange: Notification - .Name = .init(rawValue: "DSChainChainSyncBlocksDidChangeNotification") - static let peerManagerConnectedPeersDidChange: Notification - .Name = .init(rawValue: "DSPeerManagerConnectedPeersDidChangeNotification") - + static let peerManagerConnectedPeersDidChange: Notification.Name = .init(rawValue: "DSPeerManagerConnectedPeersDidChangeNotification") + static let chainManagerSyncStateChanged: Notification.Name = .init(rawValue: "DSChainManagerSyncStateDidChangeNotification") } diff --git a/DashWallet/Sources/Categories/DSTransaction+DashWallet.swift b/DashWallet/Sources/Categories/DSTransaction+DashWallet.swift index 4a5159265..804a7edcc 100644 --- a/DashWallet/Sources/Categories/DSTransaction+DashWallet.swift +++ b/DashWallet/Sources/Categories/DSTransaction+DashWallet.swift @@ -40,7 +40,7 @@ extension DSTransaction { let direction = direction switch direction { - case .moved: + case .notAccountFunds, .moved: amount = 0 case .sent: let amountSent = chain.amountSent(by: self) @@ -54,9 +54,7 @@ extension DSTransaction { let fee = feeUsed == UInt64.max ? 0 : feeUsed amount = amountSent - amountReceived - fee case .received: - amount = account!.amountReceived(from: self) - case .notAccountFunds: - amount = 0 + amount = account?.amountReceived(from: self) ?? 0 @unknown default: fatalError() } @@ -96,7 +94,7 @@ extension DSTransaction { switch direction { case .moved, .sent, .received: - outputReceiveAddresses = account!.externalAddresses(of: self) + outputReceiveAddresses = account?.externalAddresses(of: self) ?? [] default: break } @@ -229,10 +227,10 @@ extension DSTransactionDirection { return .dw_quaternaryText() case .sent: return .dw_darkTitle() - case .received: - return .dw_dashBlue() - case .notAccountFunds: + case .received, .notAccountFunds: return .dw_dashBlue() + @unknown default: + fatalError() } } } diff --git a/DashWallet/Sources/Models/Transactions/Model/Transaction.swift b/DashWallet/Sources/Models/Transactions/Model/Transaction.swift index a748d3abb..c3885496b 100644 --- a/DashWallet/Sources/Models/Transactions/Model/Transaction.swift +++ b/DashWallet/Sources/Models/Transactions/Model/Transaction.swift @@ -91,7 +91,9 @@ class Transaction: TransactionDataItem, Identifiable { let chain = DWEnvironment.sharedInstance().currentChain let currentAccount = DWEnvironment.sharedInstance().currentAccount; let account = tx.accounts.contains(where: { ($0 as! DSAccount) == currentAccount }) ? currentAccount : nil - + if account == nil { + return .invalid + } let blockHeight = chain.lastTerminalBlockHeight let instantSendReceived = tx.instantSendReceived let processingInstantSend = tx.hasUnverifiedInstantSendLock diff --git a/DashWallet/Sources/UI/Home/Syncing Views/Alert/SyncingAlertContentView.swift b/DashWallet/Sources/UI/Home/Syncing Views/Alert/SyncingAlertContentView.swift index eae517abe..4380ec856 100644 --- a/DashWallet/Sources/UI/Home/Syncing Views/Alert/SyncingAlertContentView.swift +++ b/DashWallet/Sources/UI/Home/Syncing Views/Alert/SyncingAlertContentView.swift @@ -17,6 +17,9 @@ import UIKit +private let kChainManagerNotificationChainKey = "DSChainManagerNotificationChainKey" +private let kChainManagerNotificationSyncStateKey = "DSChainManagerNotificationSyncStateKey" + // MARK: - SyncingAlertContentViewDelegate protocol SyncingAlertContentViewDelegate: AnyObject { @@ -95,6 +98,10 @@ final class SyncingAlertContentView: UIView { required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + deinit { + NotificationCenter.default.removeObserver(self) + } @objc func okButtonAction(sender: UIButton) { @@ -104,30 +111,25 @@ final class SyncingAlertContentView: UIView { func update(with syncState: SyncingActivityMonitor.State) { switch syncState { case .syncing, .syncDone: - let environment = DWEnvironment.sharedInstance() - let chain = environment.currentChain - let chainManager = environment.currentChainManager - // We give a 6 block window, just in case a new block comes in - let atTheEndOfInitialTerminalBlocksAndSyncingMasternodeList = chain.lastTerminalBlockHeight >= chain.estimatedBlockHeight - 6 && chainManager.masternodeManager - .masternodeListRetrievalQueueCount > 0 && chainManager.syncPhase == .initialTerminalBlocks - let atTheEndOfSyncBlocksAndSyncingMasternodeList = chain.lastSyncBlockHeight >= chain.estimatedBlockHeight - 6 && chainManager.masternodeManager - .masternodeListRetrievalQueueCount > 0 && chainManager.syncPhase == .synced - if atTheEndOfInitialTerminalBlocksAndSyncingMasternodeList || atTheEndOfSyncBlocksAndSyncingMasternodeList { - var remaining: UInt = 0 - - if chainManager.masternodeManager.masternodeListRetrievalQueueCount > chainManager.masternodeManager.masternodeListRetrievalQueueMaxAmount { - remaining = 0 - } else { - remaining = chainManager.masternodeManager.masternodeListRetrievalQueueMaxAmount - chainManager.masternodeManager.masternodeListRetrievalQueueCount - } - - subtitleLabel.text = String(format: NSLocalizedString("masternode list #%d of %d", comment: ""), remaining, chainManager.masternodeManager.masternodeListRetrievalQueueMaxAmount) + let model = SyncingActivityMonitor.shared.model; + let kind = model.kind; + if kind == .headers { + subtitleLabel.text = localized( + template: "header #%d of %d", + model.lastTerminalBlockHeight, + model.estimatedBlockHeight) + } else if kind == .masternodes { + let masternodeListsReceived = model.masternodeListSyncInfo.retrievalQueueCount + let masternodeListsTotal = model.masternodeListSyncInfo.retrievalQueueMaxAmount + subtitleLabel.text = localized( + template: "masternode list #%d of %d", + masternodeListsReceived > masternodeListsTotal ? 0 : masternodeListsTotal - masternodeListsTotal, + masternodeListsTotal) } else { - if chainManager.syncPhase == .initialTerminalBlocks { - subtitleLabel.text = String(format: NSLocalizedString("header #%d of %d", comment: ""), chain.lastTerminalBlockHeight, chain.estimatedBlockHeight) - } else { - subtitleLabel.text = String(format: NSLocalizedString("block #%d of %d", comment: ""), chain.lastSyncBlockHeight, chain.estimatedBlockHeight) - } + subtitleLabel.text = localized( + template: "block #%d of %d", + model.lastSyncBlockHeight, + model.estimatedBlockHeight) } case .syncFailed: @@ -166,5 +168,32 @@ final class SyncingAlertContentView: UIView { func hideAnimation() { syncingImageView.layer.removeAllAnimations() } -} + + private func localized( + template: String, + _ arguments: any CVarArg... + ) -> String { + String( + format: NSLocalizedString( + template, + comment: ""), + arguments) + } + + private func addChainObserver(_ aName: NSNotification.Name?, _ aSelector: Selector) { + NotificationCenter.default.addObserver(self, selector: aSelector, name: aName, object: nil) + } + + private func configureObserver() { + addChainObserver(.chainManagerSyncStateChanged, #selector(chainManagerSyncStateChangedNotification(notification:))) + } + @objc + func chainManagerSyncStateChangedNotification(notification: Notification) { + guard let chain = notification.userInfo?[kChainManagerNotificationChainKey], DWEnvironment.sharedInstance().currentChain.isEqual(chain), + let model = notification.userInfo?[kChainManagerNotificationSyncStateKey] as? DSSyncState else { + return + } + self.update(with: model.combinedSyncProgress) + } +} diff --git a/DashWallet/Sources/UI/Home/Syncing Views/Sync View/SyncView.swift b/DashWallet/Sources/UI/Home/Syncing Views/Sync View/SyncView.swift index 271431bba..d6c44eba3 100644 --- a/DashWallet/Sources/UI/Home/Syncing Views/Sync View/SyncView.swift +++ b/DashWallet/Sources/UI/Home/Syncing Views/Sync View/SyncView.swift @@ -92,26 +92,22 @@ final class SyncView: UIView { // TODO: Don't access DashSync directly if syncState == .syncing || syncState == .syncDone { if viewStateSeeingBlocks { - let environment = DWEnvironment.sharedInstance() - let chain = environment.currentChain - let chainManager = environment.currentChainManager - if chainManager.syncPhase == .initialTerminalBlocks { - if chain.lastTerminalBlockHeight >= chain.estimatedBlockHeight && chainManager.masternodeManager.masternodeListRetrievalQueueCount != 0 { - descriptionLabel.text = String(format: NSLocalizedString("masternode list #%d of %d", comment: ""), - chainManager.masternodeManager.masternodeListRetrievalQueueMaxAmount - chainManager.masternodeManager - .masternodeListRetrievalQueueCount, - chainManager.masternodeManager.masternodeListRetrievalQueueMaxAmount) - } - else { - descriptionLabel.text = String(format: NSLocalizedString("header #%d of %d", comment: ""), - chain.lastTerminalBlockHeight, - chain.estimatedBlockHeight) - } - } - else { + let model = SyncingActivityMonitor.shared.model; + let kind = model.kind; + if kind == .headers { + descriptionLabel.text = String(format: NSLocalizedString("header #%d of %d", comment: ""), + model.lastTerminalBlockHeight, + model.estimatedBlockHeight) + } else if kind == .masternodes { + let masternodeListsReceived = model.masternodeListSyncInfo.retrievalQueueCount + let masternodeListsTotal = model.masternodeListSyncInfo.retrievalQueueMaxAmount + descriptionLabel.text = String(format: NSLocalizedString("masternode list #%d of %d", comment: ""), + masternodeListsReceived > masternodeListsTotal ? 0 : masternodeListsTotal - masternodeListsTotal, + masternodeListsTotal) + } else { descriptionLabel.text = String(format: NSLocalizedString("block #%d of %d", comment: ""), - chain.lastSyncBlockHeight, - chain.estimatedBlockHeight) + model.lastSyncBlockHeight, + model.estimatedBlockHeight) } } else { diff --git a/DashWallet/Sources/UI/Home/Views/HomeView.swift b/DashWallet/Sources/UI/Home/Views/HomeView.swift index db92641c8..95b028243 100644 --- a/DashWallet/Sources/UI/Home/Views/HomeView.swift +++ b/DashWallet/Sources/UI/Home/Views/HomeView.swift @@ -274,60 +274,56 @@ struct TransactionList: View { @State private var selectedTxDataItem: TransactionListDataItem? = nil @StateObject var viewModel: HomeViewModel - @ViewBuilder var balanceHeader: () -> Content @ViewBuilder var syncingHeader: () -> Content + private let topOverscrollSize: CGFloat = 1000 // Fixed value for top overscroll area var body: some View { - GeometryReader { geometry in - let topOverscrollSize = geometry.size.height * 1.5 - - ScrollView { - ZStack { Color.dashBlue } // Top overscroll area + ScrollView { + ZStack { Color.navigationBarColor } // Top overscroll area .frame(height: topOverscrollSize) .padding(EdgeInsets(top: -topOverscrollSize, leading: 0, bottom: 0, trailing: 0)) + + LazyVStack(pinnedViews: [.sectionHeaders]) { + balanceHeader() + .frame(height: viewModel.hasNetwork ? 250 : 335) - LazyVStack(pinnedViews: [.sectionHeaders]) { - balanceHeader() - .frame(height: 250) - - syncingHeader() - .frame(height: 50) - - if viewModel.txItems.isEmpty { - Text(NSLocalizedString("There are no transactions to display", comment: "")) - .font(.caption) - .foregroundStyle(Color.primaryText.opacity(0.5)) - .padding(.top, 20) - } else { - ForEach(viewModel.txItems, id: \.0.key) { key, value in - Section(header: SectionHeader(key) - .padding(.bottom, -24) - ) { - VStack(spacing: 0) { - ForEach(value, id: \.id) { txItem in - TransactionPreviewFrom(txItem: txItem) - .padding(.horizontal, 5) - } + syncingHeader() + .frame(height: 50) + + if viewModel.txItems.isEmpty { + Text(NSLocalizedString("There are no transactions to display", comment: "")) + .font(.caption) + .foregroundColor(Color.primary.opacity(0.5)) + .padding(.top, 20) + } else { + ForEach(viewModel.txItems, id: \.0.key) { key, value in + Section(header: SectionHeader(key) + .padding(.bottom, -24) + ) { + VStack(spacing: 0) { + ForEach(value, id: \.id) { txItem in + TransactionPreviewFrom(txItem: txItem) + .padding(.horizontal, 5) } - .padding(.bottom, 4) - .background(Color.secondaryBackground) - .clipShape(RoundedShape(corners: [.bottomLeft, .bottomRight], radii: 10)) - .padding(15) - .shadow(color: .shadow, radius: 10, x: 0, y: 5) } + .padding(.bottom, 4) + .background(Color.secondaryBackground) + .clipShape(RoundedShape(corners: [.bottomLeft, .bottomRight], radii: 10)) + .padding(15) + .shadow(color: .shadow, radius: 10, x: 0, y: 5) } } } - .padding(EdgeInsets(top: -20, leading: 0, bottom: 0, trailing: 0)) } + .padding(EdgeInsets(top: -20, leading: 0, bottom: 0, trailing: 0)) } .sheet(item: $selectedTxDataItem) { item in TransactionDetailsSheet(item: item) } } - + @ViewBuilder private func SectionHeader(_ dateKey: DateKey) -> some View { VStack { diff --git a/DashWallet/Sources/UI/Home/Views/HomeViewModel.swift b/DashWallet/Sources/UI/Home/Views/HomeViewModel.swift index 84dcb8d2d..f6b3df166 100644 --- a/DashWallet/Sources/UI/Home/Views/HomeViewModel.swift +++ b/DashWallet/Sources/UI/Home/Views/HomeViewModel.swift @@ -19,6 +19,15 @@ import Foundation class HomeViewModel: ObservableObject { @Published var txItems: Array<(DateKey, [TransactionListDataItem])> = [] + @Published var hasNetwork: Bool = true + private var model: SyncModel = SyncModelImpl() + + init() { + model.networkStatusDidChange = { status in + self.hasNetwork = status == .online + } + self.hasNetwork = model.networkStatus == .online + } func updateItems(transactions: [DSTransaction]) { Task.detached { diff --git a/DashWallet/Sources/UI/Menu/Settings/About/DWAboutViewController.m b/DashWallet/Sources/UI/Menu/Settings/About/DWAboutViewController.m index fa8994630..1191e0c7b 100644 --- a/DashWallet/Sources/UI/Menu/Settings/About/DWAboutViewController.m +++ b/DashWallet/Sources/UI/Menu/Settings/About/DWAboutViewController.m @@ -98,7 +98,7 @@ - (void)viewDidLoad { object:nil]; [notificationCenter addObserver:self selector:@selector(updateStatusNotification:) - name:DSChainNewChainTipBlockNotification + name:DSChainManagerSyncStateDidChangeNotification object:nil]; [notificationCenter addObserver:self selector:@selector(updateStatusNotification:) diff --git a/DashWallet/Sources/UI/SwiftUI Components/Color+DWStyle.swift b/DashWallet/Sources/UI/SwiftUI Components/Color+DWStyle.swift index c424f8a2a..1ac7c567b 100644 --- a/DashWallet/Sources/UI/SwiftUI Components/Color+DWStyle.swift +++ b/DashWallet/Sources/UI/SwiftUI Components/Color+DWStyle.swift @@ -34,6 +34,10 @@ extension Color { Color("DashBlueColor") } + static var navigationBarColor: Color { + Color("DashNavigationBarBlueColor") + } + static var buttonRed: Color { Color("ButtonRedColor") } diff --git a/DashWallet/dashwallet.entitlements b/DashWallet/dashwallet.entitlements index 120550dd4..26eff9d3e 100644 --- a/DashWallet/dashwallet.entitlements +++ b/DashWallet/dashwallet.entitlements @@ -22,7 +22,7 @@ com.apple.security.application-groups - group.org.dashfoundation.dash + group.M62AAKG43G.org.dashfoundation.dash diff --git a/DashWallet/dashwalletTodayExtension.entitlements b/DashWallet/dashwalletTodayExtension.entitlements index 44fb864e4..b1a7f9394 100644 --- a/DashWallet/dashwalletTodayExtension.entitlements +++ b/DashWallet/dashwalletTodayExtension.entitlements @@ -6,7 +6,7 @@ development com.apple.security.application-groups - group.org.dashfoundation.dash + group.M62AAKG43G.org.dashfoundation.dash diff --git a/Podfile.lock b/Podfile.lock index cf61c83e4..1cd160baa 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -565,7 +565,7 @@ PODS: - abseil/base/base_internal - abseil/base/config - abseil/meta/type_traits - - Alamofire (5.8.1) + - Alamofire (5.9.1) - BlueCryptor (1.0.32) - BlueECC (1.2.5) - BlueRSA (1.0.200) @@ -620,14 +620,17 @@ PODS: - FirebaseStorage (8.15.0): - FirebaseCore (~> 8.0) - GTMSessionFetcher/Core (~> 1.5) - - GoogleDataTransport (9.3.0): + - GoogleDataTransport (9.4.1): - GoogleUtilities/Environment (~> 7.7) - - nanopb (< 2.30910.0, >= 2.30908.0) + - nanopb (< 2.30911.0, >= 2.30908.0) - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/Environment (7.12.0): + - GoogleUtilities/Environment (7.13.3): + - GoogleUtilities/Privacy - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/Logger (7.12.0): + - GoogleUtilities/Logger (7.13.3): - GoogleUtilities/Environment + - GoogleUtilities/Privacy + - GoogleUtilities/Privacy (7.13.3) - gRPC-Core (1.49.0): - gRPC-Core/Implementation (= 1.49.0) - gRPC-Core/Interface (= 1.49.0) @@ -706,8 +709,8 @@ PODS: - nanopb/encode (= 2.30908.0) - nanopb/decode (2.30908.0) - nanopb/encode (2.30908.0) - - PromisesObjC (2.3.1) - - Protobuf (3.25.1) + - PromisesObjC (2.4.0) + - Protobuf (3.27.2) - SDWebImage (5.13.2): - SDWebImage/Core (= 5.13.2) - SDWebImage/Core (5.13.2) @@ -819,7 +822,7 @@ SPEC CHECKSUMS: "!ProtoCompiler": e9c09244955a8565817aa59a4787b6bb849a63c6 "!ProtoCompiler-gRPCPlugin": 755f0ee414a0d5f0028e0dcfe98c23bdbc3e6fa3 abseil: 926fb7a82dc6d2b8e1f2ed7f3a718bce691d1e46 - Alamofire: 3ca42e259043ee0dc5c0cdd76c4bc568b8e42af7 + Alamofire: f36a35757af4587d8e4f4bfa223ad10be2422b8c BlueCryptor: b0aee3d9b8f367b49b30de11cda90e1735571c24 BlueECC: 0d18e93347d3ec6d41416de21c1ffa4d4cd3c2cc BlueRSA: dfeef51db96bcc4edec654956c1581adbda4e6a3 @@ -837,8 +840,8 @@ SPEC CHECKSUMS: FirebaseCoreDiagnostics: 92e07a649aeb66352b319d43bdd2ee3942af84cb FirebaseDynamicLinks: 1dc816ef789c5adac6fede0b46d11478175c70e4 FirebaseStorage: 8019af461599b2c3bc61c6a5dbdfa3d2de66a4d9 - GoogleDataTransport: 57c22343ab29bc686febbf7cbb13bad167c2d8fe - GoogleUtilities: 0759d1a57ebb953965c2dfe0ba4c82e95ccc2e34 + GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a + GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 gRPC: 64f36d689b2ecd99c4351f74e6f91347cdc65d9f gRPC-Core: 3a9fdb5967d42211e875826f3f6fc163ea02c2a1 gRPC-ProtoRPC: 1c223e0f1732bb8d0b9e9e0ea60cc0fe995b8e2d @@ -852,8 +855,8 @@ SPEC CHECKSUMS: MMSegmentSlider: db7ee0d6eb2eefbd511179195bf288ff2cebac0a Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 - PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 - Protobuf: d94761c33f1239c0a43a0817ca1a5f7f7c900241 + PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 + Protobuf: fb2c13674723f76ff6eede14f78847a776455fa2 SDWebImage: 72f86271a6f3139cc7e4a89220946489d4b9a866 SQLite.swift: 903bfa3bc9ab06345fdfbb578e34f47cfcf417da SQLiteMigrationManager.swift: 5383578f5bc8955c06695e8bf04835ee0e6673a8