diff --git a/DashSyncCurrentCommit b/DashSyncCurrentCommit index 913f2c8b1..4e0f41691 100644 --- a/DashSyncCurrentCommit +++ b/DashSyncCurrentCommit @@ -1 +1 @@ -bfa1376ac66ddcf02a3de83d3507732a6a1c50a5 +3bfe78b0722d5a0ca895c0bb24600ccd343d124e diff --git a/DashWallet.xcodeproj/project.pbxproj b/DashWallet.xcodeproj/project.pbxproj index 1ea2cd5ce..eed0363c7 100644 --- a/DashWallet.xcodeproj/project.pbxproj +++ b/DashWallet.xcodeproj/project.pbxproj @@ -119,9 +119,6 @@ 2A10EB412358D2A900C38B61 /* ResetWalletInfo.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2A10EB402358D2A900C38B61 /* ResetWalletInfo.storyboard */; }; 2A10EB442358D2CA00C38B61 /* DWResetWalletInfoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A10EB432358D2CA00C38B61 /* DWResetWalletInfoViewController.m */; }; 2A11F59F2194BD6200E7B563 /* DWDataMigrationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A11F59E2194BD6200E7B563 /* DWDataMigrationManager.m */; }; - 2A12E61923ABC7D3001CAF58 /* DWDemoMainTabbarViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A12E61823ABC7D3001CAF58 /* DWDemoMainTabbarViewController.m */; }; - 2A1A60BC2674084F003DAC65 /* DWSyncingAlertContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A1A60BB2674084F003DAC65 /* DWSyncingAlertContentView.m */; }; - 2A1A60BF26740E27003DAC65 /* DWSyncingAlertViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A1A60BE26740E27003DAC65 /* DWSyncingAlertViewController.m */; }; 2A1AE78E23F4668B00179A6E /* DWUsernameHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A1AE78D23F4668B00179A6E /* DWUsernameHeaderView.m */; }; 2A1AE79223F468CD00179A6E /* DWPlanetarySystemView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A1AE79123F468CD00179A6E /* DWPlanetarySystemView.m */; }; 2A1AF6DE23C7681B00442AF5 /* DWShortcutCollectionViewCell~ipad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2A1AF6DC23C7681B00442AF5 /* DWShortcutCollectionViewCell~ipad.xib */; }; @@ -143,10 +140,8 @@ 2A2CD71822F99CAE008C7BC9 /* ShortcutsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2A2CD71722F99CAE008C7BC9 /* ShortcutsView.xib */; }; 2A2CD72222F9B571008C7BC9 /* DWIntrinsicCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A2CD72122F9B571008C7BC9 /* DWIntrinsicCollectionView.m */; }; 2A2CD72522FA05DD008C7BC9 /* DWPressableButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A2CD72422FA05DD008C7BC9 /* DWPressableButton.m */; }; - 2A307CBC22E8925100A18347 /* DWHomeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A307CBB22E8925100A18347 /* DWHomeView.m */; }; 2A307CBF22E8A44200A18347 /* DWButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A307CBE22E8A44200A18347 /* DWButton.m */; }; 2A36A88B2350A05B0014DC60 /* DWBackupSeedPhraseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A36A88A2350A05B0014DC60 /* DWBackupSeedPhraseViewController.m */; }; - 2A392565234CD21300316EA6 /* DWTabBarButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A392564234CD21300316EA6 /* DWTabBarButton.m */; }; 2A392568234CFE9D00316EA6 /* NSAttributedString+DWHighlightText.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A392567234CFE9D00316EA6 /* NSAttributedString+DWHighlightText.m */; }; 2A3CCEF9242BB1B900300AF8 /* DWRegistrationCompletedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A3CCEF8242BB1B900300AF8 /* DWRegistrationCompletedViewController.m */; }; 2A3CCEFC242BB1DD00300AF8 /* DWDPAvatarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A3CCEFB242BB1DD00300AF8 /* DWDPAvatarView.m */; }; @@ -176,21 +171,16 @@ 2A4431E922D738C0009BAF7F /* DWSeedPhraseModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A4431E822D738C0009BAF7F /* DWSeedPhraseModel.m */; }; 2A4663032279DC2F0027533B /* DashWalletScreenshotsUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A4663022279DC2F0027533B /* DashWalletScreenshotsUITests.swift */; }; 2A46630D2279DD7D0027533B /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A46630C2279DD7D0027533B /* SnapshotHelper.swift */; }; - 2A4E1D5325056297008AC53F /* DWPaymentsButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A4E1D5225056297008AC53F /* DWPaymentsButton.m */; }; 2A4E531422E9F0A200E5168A /* DWStartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AB231D12196E25D00A6E7E6 /* DWStartViewController.m */; }; 2A4E531522E9F0A200E5168A /* DWStartModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AB231D62196E5CF00A6E7E6 /* DWStartModel.m */; }; - 2A4E531822EA381F00E5168A /* DWSyncView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A4E531722EA381F00E5168A /* DWSyncView.m */; }; - 2A4E531A22EA382B00E5168A /* DWSyncView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2A4E531922EA382B00E5168A /* DWSyncView.xib */; }; + 2A4E531A22EA382B00E5168A /* SyncView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2A4E531922EA382B00E5168A /* SyncView.xib */; }; 2A4E531D22EA49FE00E5168A /* DWProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A4E531C22EA49FE00E5168A /* DWProgressView.m */; }; - 2A4E532022EB2DF400E5168A /* DWHomeHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A4E531F22EB2DF400E5168A /* DWHomeHeaderView.m */; }; 2A4E533822F023AB00E5168A /* DWHomeModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A4E533722F023AB00E5168A /* DWHomeModel.m */; }; - 2A4E534022F025FE00E5168A /* DWTxListEmptyTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A4E533E22F025FE00E5168A /* DWTxListEmptyTableViewCell.m */; }; - 2A4E534122F025FE00E5168A /* DWTxListEmptyTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2A4E533F22F025FE00E5168A /* DWTxListEmptyTableViewCell.xib */; }; + 2A4E534122F025FE00E5168A /* TxListEmptyTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2A4E533F22F025FE00E5168A /* TxListEmptyTableViewCell.xib */; }; 2A4E534422F02BC300E5168A /* UIView+DWReuseHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A4E534322F02BC300E5168A /* UIView+DWReuseHelper.m */; }; 2A4E534B22F03A9E00E5168A /* DWFilterHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A4E534A22F03A9E00E5168A /* DWFilterHeaderView.m */; }; 2A4E534D22F03AAC00E5168A /* DWFilterHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2A4E534C22F03AAC00E5168A /* DWFilterHeaderView.xib */; }; - 2A4E535022F19BE300E5168A /* DWSyncModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A4E534F22F19BE300E5168A /* DWSyncModel.m */; }; - 2A4E535522F1D0D900E5168A /* DWTxListTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2A4E535322F1D0D900E5168A /* DWTxListTableViewCell.xib */; }; + 2A4E535522F1D0D900E5168A /* TxListTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2A4E535322F1D0D900E5168A /* TxListTableViewCell.xib */; }; 2A4E535C22F335C200E5168A /* DWTransactionListDataProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A4E535B22F335C200E5168A /* DWTransactionListDataProvider.m */; }; 2A5279BC23D994BC00F856D3 /* CoreNFC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A5279BB23D994BC00F856D3 /* CoreNFC.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 2A56EEFB2417E30F002C32F3 /* DWConfirmUsernameContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A56EEFA2417E30F002C32F3 /* DWConfirmUsernameContentView.m */; }; @@ -204,14 +194,12 @@ 2A5E4548243E06E7006BA067 /* DWDPRegistrationStatusTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A5E4546243E06E7006BA067 /* DWDPRegistrationStatusTableViewCell.m */; }; 2A5E4549243E06E7006BA067 /* DWDPRegistrationStatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2A5E4547243E06E7006BA067 /* DWDPRegistrationStatusTableViewCell.xib */; }; 2A60C9452444BF3A00AF72CF /* DWConfirmUsernameContentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2A60C9442444BF3900AF72CF /* DWConfirmUsernameContentView.xib */; }; - 2A612AB724409D6E0060CC77 /* DWDashPayProfileView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A612AB624409D6E0060CC77 /* DWDashPayProfileView.m */; }; 2A63003F2327B4BB00827825 /* DWPaymentOutput+DWView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A63003E2327B4BB00827825 /* DWPaymentOutput+DWView.m */; }; 2A6300422328CCE900827825 /* LockScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2A6300412328CCE900827825 /* LockScreen.storyboard */; }; 2A6300452328D07500827825 /* DWLockPinInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A6300442328D07500827825 /* DWLockPinInputView.m */; }; 2A6300492328EA8900827825 /* DWLockActionButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A6300482328EA8900827825 /* DWLockActionButton.m */; }; 2A63004E2328F37C00827825 /* DWLockScreenViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A63004D2328F37C00827825 /* DWLockScreenViewController.m */; }; 2A6688FD24BCB739008E10F0 /* DWDPTxItemView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A6688FC24BCB739008E10F0 /* DWDPTxItemView.m */; }; - 2A6B8E542387056200A2E5FA /* DWBalanceDisplayOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A6B8E532387056200A2E5FA /* DWBalanceDisplayOptions.m */; }; 2A741DC223639A9700840ADF /* TodayExtension.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2A741DC123639A9700840ADF /* TodayExtension.storyboard */; }; 2A741DC323639C7E00840ADF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 757E09971ADB8EEB006FD352 /* Localizable.strings */; }; 2A741DC62363A06000840ADF /* DWURLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A741DC52363A06000840ADF /* DWURLParser.m */; }; @@ -313,8 +301,6 @@ 2A913E7123A2667E006A2A59 /* Onboarding.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2A913E7023A2667E006A2A59 /* Onboarding.storyboard */; }; 2A913E7F23A30609006A2A59 /* DWRootModelStub.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A913E7E23A30609006A2A59 /* DWRootModelStub.m */; }; 2A913E8223A30623006A2A59 /* DWHomeModelStub.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A913E8123A30623006A2A59 /* DWHomeModelStub.m */; }; - 2A913E8523A309EA006A2A59 /* DWSyncModelStub.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A913E8423A309EA006A2A59 /* DWSyncModelStub.m */; }; - 2A913E8923A30DA8006A2A59 /* DWBalanceDisplayOptionsStub.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A913E8823A30DA8006A2A59 /* DWBalanceDisplayOptionsStub.m */; }; 2A913E8C23A3134E006A2A59 /* UIView+DWEmbedding.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A913E8B23A3134E006A2A59 /* UIView+DWEmbedding.m */; }; 2A913E9023A31713006A2A59 /* UIViewController+DWEmbedding.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A913E8F23A31713006A2A59 /* UIViewController+DWEmbedding.m */; }; 2A913E9523A3F75F006A2A59 /* DWInitialViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A913E9423A3F75F006A2A59 /* DWInitialViewController.m */; }; @@ -329,11 +315,8 @@ 2A919F9C24A4DCAD0018C9A3 /* DWDPSmallContactView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A919F9B24A4DCAD0018C9A3 /* DWDPSmallContactView.m */; }; 2A919F9F24A65CE00018C9A3 /* DWDPAmountContactView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A919F9E24A65CE00018C9A3 /* DWDPAmountContactView.m */; }; 2A951CE423D1B92C00602824 /* DWBaseContactsModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A951CE323D1B92C00602824 /* DWBaseContactsModel.m */; }; - 2A9CEBA822E1D5A200A50237 /* DWMainTabbarViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A9CEBA722E1D5A200A50237 /* DWMainTabbarViewController.m */; }; 2A9CEBAD22E1DA4000A50237 /* DWAppRootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A9CEBAC22E1DA4000A50237 /* DWAppRootViewController.m */; }; - 2A9CEBB522E1EAC900A50237 /* DWTabBarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A9CEBB422E1EAC900A50237 /* DWTabBarView.m */; }; 2A9CEBB922E1FA1000A50237 /* DWHomeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A9CEBB822E1FA1000A50237 /* DWHomeViewController.m */; }; - 2A9D72A82497C52E00F79CD8 /* DWBadgeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A9D72A72497C52E00F79CD8 /* DWBadgeView.m */; }; 2A9D72AC249A0EE000F79CD8 /* DWDPNewIncomingRequestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A9D72AB249A0EE000F79CD8 /* DWDPNewIncomingRequestObject.m */; }; 2A9E7DC723F6928C00CDA1EE /* DWTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A9E7DC623F6928C00CDA1EE /* DWTextField.m */; }; 2A9E7DCA23F6BD5200CDA1EE /* DWUsernameValidationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A9E7DC923F6BD5200CDA1EE /* DWUsernameValidationView.m */; }; @@ -431,7 +414,6 @@ 2AD6E5602487E13F00B52F14 /* DWRequestsContentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AD6E55F2487E13F00B52F14 /* DWRequestsContentViewController.m */; }; 2AD6E5632487E16400B52F14 /* DWRequestsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AD6E5622487E16400B52F14 /* DWRequestsViewController.m */; }; 2AD6E56A2487E1DB00B52F14 /* DWRequestsModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AD6E5692487E1DB00B52F14 /* DWRequestsModel.m */; }; - 2AD7BCBE2673BDE9008FF133 /* DWSyncingHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AD7BCBD2673BDE9008FF133 /* DWSyncingHeaderView.m */; }; 2AD85A9F245881740045B480 /* DWQRScanStatusView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AD85A9E245881740045B480 /* DWQRScanStatusView.m */; }; 2ADB396924223D9B00A6F898 /* DWDashPayAnimationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2ADB396824223D9B00A6F898 /* DWDashPayAnimationView.m */; }; 2ADB396C242615C200A6F898 /* CALayer+MBAnimationPersistence.m in Sources */ = {isa = PBXBuildFile; fileRef = 2ADB396B242615C200A6F898 /* CALayer+MBAnimationPersistence.m */; }; @@ -702,6 +684,7 @@ C917024129D462C6008C034D /* PayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C917024029D462C6008C034D /* PayViewController.swift */; }; C91E919729FBACE6003E7883 /* ExtendedPublicKeysModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91E919629FBACE6003E7883 /* ExtendedPublicKeysModel.swift */; }; C91E91AE29FFC8A1003E7883 /* ExtendedPublicKeysViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C91E91AD29FFC8A1003E7883 /* ExtendedPublicKeysViewController.swift */; }; + C94946E12A25F037008A678D /* DemoMainTabbarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94946E02A25F037008A678D /* DemoMainTabbarViewController.swift */; }; C94F5E8829D3E7E30034FD57 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C94F5E8729D3E7E30034FD57 /* GoogleService-Info.plist */; }; C94F5E8A29D3FCCF0034FD57 /* ShortcutAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94F5E8929D3FCCF0034FD57 /* ShortcutAction.swift */; }; C94F5E8C29D3FEC10034FD57 /* ShortcutsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94F5E8B29D3FEC10034FD57 /* ShortcutsModel.swift */; }; @@ -722,6 +705,21 @@ C9F42FB429DD86FB001BC549 /* BackupInfoItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F42FB329DD86FB001BC549 /* BackupInfoItemView.swift */; }; C9F42FB629DD8702001BC549 /* BackupInfoItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C9F42FB529DD8702001BC549 /* BackupInfoItemView.xib */; }; C9F42FB829DFC507001BC549 /* SpendableTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F42FB729DFC506001BC549 /* SpendableTransaction.swift */; }; + C9F451E52A0B986E00825057 /* MainTabbarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F451E42A0B986E00825057 /* MainTabbarController.swift */; }; + C9F451E72A0BA16400825057 /* PaymentButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F451E62A0BA16400825057 /* PaymentButton.swift */; }; + C9F451E92A0BDAE700825057 /* UIApplication+DashWallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F451E82A0BDAE700825057 /* UIApplication+DashWallet.swift */; }; + C9F451EB2A0BF10B00825057 /* SyncingAlertViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F451EA2A0BF10B00825057 /* SyncingAlertViewController.swift */; }; + C9F451EE2A0BF1F500825057 /* SyncingAlertContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F451ED2A0BF1F500825057 /* SyncingAlertContentView.swift */; }; + C9F451F32A0C933700825057 /* SyncingHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F451F22A0C933700825057 /* SyncingHeaderView.swift */; }; + C9F451F52A0CAC9400825057 /* HomeHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F451F42A0CAC9400825057 /* HomeHeaderView.swift */; }; + C9F451F72A0CAE1300825057 /* SyncView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F451F62A0CAE1300825057 /* SyncView.swift */; }; + C9F451F92A0CB08900825057 /* ProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F451F82A0CB08900825057 /* ProgressView.swift */; }; + C9F451FB2A0CC2A800825057 /* DashPayProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F451FA2A0CC2A800825057 /* DashPayProfileView.swift */; }; + C9F451FD2A0CC4A300825057 /* BadgeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F451FC2A0CC4A300825057 /* BadgeView.swift */; }; + C9F452012A0CE6C900825057 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F452002A0CE6C900825057 /* HomeView.swift */; }; + C9F452032A0CEB5800825057 /* TxListEmptyTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F452022A0CEB5800825057 /* TxListEmptyTableViewCell.swift */; }; + C9F452082A11F28600825057 /* SyncModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F452072A11F28600825057 /* SyncModel.swift */; }; + C9F4520B2A1209D100825057 /* HomeHeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F4520A2A1209D100825057 /* HomeHeaderModel.swift */; }; CC5F88E358330F8EE192D5BE /* libPods-DashWalletScreenshotsUITests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0CDD4C961516ED20BC9F01FA /* libPods-DashWalletScreenshotsUITests.a */; }; DE3A167A235B79D705C0A962 /* libPods-dashwallet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 982607F21196681DAC51A074 /* libPods-dashwallet.a */; }; FB248B5D1F73803100405AE0 /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB248B5C1F73803100405AE0 /* UserNotifications.framework */; }; @@ -959,13 +957,7 @@ 2A11F59D2194BD6200E7B563 /* DWDataMigrationManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWDataMigrationManager.h; sourceTree = ""; }; 2A11F59E2194BD6200E7B563 /* DWDataMigrationManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWDataMigrationManager.m; sourceTree = ""; }; 2A12E61623AB9B8D001CAF58 /* DWDemoDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWDemoDelegate.h; sourceTree = ""; }; - 2A12E61723ABC7D3001CAF58 /* DWDemoMainTabbarViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWDemoMainTabbarViewController.h; sourceTree = ""; }; - 2A12E61823ABC7D3001CAF58 /* DWDemoMainTabbarViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWDemoMainTabbarViewController.m; sourceTree = ""; }; 2A12E61A23ABC8E1001CAF58 /* DWExtendedContainerViewController+DWProtected.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "DWExtendedContainerViewController+DWProtected.h"; sourceTree = ""; }; - 2A1A60BA2674084F003DAC65 /* DWSyncingAlertContentView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWSyncingAlertContentView.h; sourceTree = ""; }; - 2A1A60BB2674084F003DAC65 /* DWSyncingAlertContentView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWSyncingAlertContentView.m; sourceTree = ""; }; - 2A1A60BD26740E27003DAC65 /* DWSyncingAlertViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWSyncingAlertViewController.h; sourceTree = ""; }; - 2A1A60BE26740E27003DAC65 /* DWSyncingAlertViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWSyncingAlertViewController.m; sourceTree = ""; }; 2A1AE78C23F4668B00179A6E /* DWUsernameHeaderView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWUsernameHeaderView.h; sourceTree = ""; }; 2A1AE78D23F4668B00179A6E /* DWUsernameHeaderView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWUsernameHeaderView.m; sourceTree = ""; }; 2A1AE79023F468CD00179A6E /* DWPlanetarySystemView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWPlanetarySystemView.h; sourceTree = ""; }; @@ -1006,16 +998,12 @@ 2A2CD72122F9B571008C7BC9 /* DWIntrinsicCollectionView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWIntrinsicCollectionView.m; sourceTree = ""; }; 2A2CD72322FA05DD008C7BC9 /* DWPressableButton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWPressableButton.h; sourceTree = ""; }; 2A2CD72422FA05DD008C7BC9 /* DWPressableButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWPressableButton.m; sourceTree = ""; }; - 2A307CBA22E8925100A18347 /* DWHomeView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWHomeView.h; sourceTree = ""; }; - 2A307CBB22E8925100A18347 /* DWHomeView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWHomeView.m; sourceTree = ""; }; 2A307CBD22E8A44200A18347 /* DWButton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWButton.h; sourceTree = ""; }; 2A307CBE22E8A44200A18347 /* DWButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWButton.m; sourceTree = ""; }; 2A36A887234F3F400014DC60 /* DWSelectorFormItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWSelectorFormItem.h; sourceTree = ""; }; 2A36A8892350A05B0014DC60 /* DWBackupSeedPhraseViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWBackupSeedPhraseViewController.h; sourceTree = ""; }; 2A36A88A2350A05B0014DC60 /* DWBackupSeedPhraseViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWBackupSeedPhraseViewController.m; sourceTree = ""; }; 2A36A88C2350A18A0014DC60 /* DWPreviewSeedPhraseViewController+DWProtected.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "DWPreviewSeedPhraseViewController+DWProtected.h"; sourceTree = ""; }; - 2A392563234CD21300316EA6 /* DWTabBarButton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWTabBarButton.h; sourceTree = ""; }; - 2A392564234CD21300316EA6 /* DWTabBarButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWTabBarButton.m; sourceTree = ""; }; 2A392566234CFE9D00316EA6 /* NSAttributedString+DWHighlightText.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSAttributedString+DWHighlightText.h"; sourceTree = ""; }; 2A392567234CFE9D00316EA6 /* NSAttributedString+DWHighlightText.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSAttributedString+DWHighlightText.m"; sourceTree = ""; }; 2A3CCEF7242BB1B900300AF8 /* DWRegistrationCompletedViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWRegistrationCompletedViewController.h; sourceTree = ""; }; @@ -1070,29 +1058,19 @@ 2A4663042279DC2F0027533B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 2A46630C2279DD7D0027533B /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SnapshotHelper.swift; path = fastlane/SnapshotHelper.swift; sourceTree = SOURCE_ROOT; }; 2A46630E2279DF490027533B /* DashWalletScreenshotsUITests-Briding-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "DashWalletScreenshotsUITests-Briding-Header.h"; sourceTree = ""; }; - 2A4E1D5125056297008AC53F /* DWPaymentsButton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWPaymentsButton.h; sourceTree = ""; }; - 2A4E1D5225056297008AC53F /* DWPaymentsButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWPaymentsButton.m; sourceTree = ""; }; - 2A4E531622EA381F00E5168A /* DWSyncView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWSyncView.h; sourceTree = ""; }; - 2A4E531722EA381F00E5168A /* DWSyncView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWSyncView.m; sourceTree = ""; }; - 2A4E531922EA382B00E5168A /* DWSyncView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DWSyncView.xib; sourceTree = ""; }; + 2A4E531922EA382B00E5168A /* SyncView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SyncView.xib; sourceTree = ""; }; 2A4E531B22EA49FE00E5168A /* DWProgressView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWProgressView.h; sourceTree = ""; }; 2A4E531C22EA49FE00E5168A /* DWProgressView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWProgressView.m; sourceTree = ""; }; - 2A4E531E22EB2DF400E5168A /* DWHomeHeaderView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWHomeHeaderView.h; sourceTree = ""; }; - 2A4E531F22EB2DF400E5168A /* DWHomeHeaderView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWHomeHeaderView.m; sourceTree = ""; }; 2A4E533622F023AB00E5168A /* DWHomeModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWHomeModel.h; sourceTree = ""; }; 2A4E533722F023AB00E5168A /* DWHomeModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWHomeModel.m; sourceTree = ""; }; - 2A4E533D22F025FE00E5168A /* DWTxListEmptyTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWTxListEmptyTableViewCell.h; sourceTree = ""; }; - 2A4E533E22F025FE00E5168A /* DWTxListEmptyTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWTxListEmptyTableViewCell.m; sourceTree = ""; }; - 2A4E533F22F025FE00E5168A /* DWTxListEmptyTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DWTxListEmptyTableViewCell.xib; sourceTree = ""; }; + 2A4E533F22F025FE00E5168A /* TxListEmptyTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TxListEmptyTableViewCell.xib; sourceTree = ""; }; 2A4E534222F02BC300E5168A /* UIView+DWReuseHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIView+DWReuseHelper.h"; sourceTree = ""; }; 2A4E534322F02BC300E5168A /* UIView+DWReuseHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIView+DWReuseHelper.m"; sourceTree = ""; }; 2A4E534522F02C6000E5168A /* DWUIKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWUIKit.h; sourceTree = ""; }; 2A4E534922F03A9E00E5168A /* DWFilterHeaderView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWFilterHeaderView.h; sourceTree = ""; }; 2A4E534A22F03A9E00E5168A /* DWFilterHeaderView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWFilterHeaderView.m; sourceTree = ""; }; 2A4E534C22F03AAC00E5168A /* DWFilterHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DWFilterHeaderView.xib; sourceTree = ""; }; - 2A4E534E22F19BE300E5168A /* DWSyncModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWSyncModel.h; sourceTree = ""; }; - 2A4E534F22F19BE300E5168A /* DWSyncModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWSyncModel.m; sourceTree = ""; }; - 2A4E535322F1D0D900E5168A /* DWTxListTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DWTxListTableViewCell.xib; sourceTree = ""; }; + 2A4E535322F1D0D900E5168A /* TxListTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TxListTableViewCell.xib; sourceTree = ""; }; 2A4E535A22F335C200E5168A /* DWTransactionListDataProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWTransactionListDataProvider.h; sourceTree = ""; }; 2A4E535B22F335C200E5168A /* DWTransactionListDataProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWTransactionListDataProvider.m; sourceTree = ""; }; 2A4E535D22F33BD200E5168A /* DWTransactionListDataProviderProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWTransactionListDataProviderProtocol.h; sourceTree = ""; }; @@ -1120,8 +1098,6 @@ 2A5E4546243E06E7006BA067 /* DWDPRegistrationStatusTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWDPRegistrationStatusTableViewCell.m; sourceTree = ""; }; 2A5E4547243E06E7006BA067 /* DWDPRegistrationStatusTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DWDPRegistrationStatusTableViewCell.xib; sourceTree = ""; }; 2A60C9442444BF3900AF72CF /* DWConfirmUsernameContentView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DWConfirmUsernameContentView.xib; sourceTree = ""; }; - 2A612AB524409D6E0060CC77 /* DWDashPayProfileView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWDashPayProfileView.h; sourceTree = ""; }; - 2A612AB624409D6E0060CC77 /* DWDashPayProfileView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWDashPayProfileView.m; sourceTree = ""; }; 2A63003D2327B4BB00827825 /* DWPaymentOutput+DWView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "DWPaymentOutput+DWView.h"; sourceTree = ""; }; 2A63003E2327B4BB00827825 /* DWPaymentOutput+DWView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "DWPaymentOutput+DWView.m"; sourceTree = ""; }; 2A6300412328CCE900827825 /* LockScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LockScreen.storyboard; sourceTree = ""; }; @@ -1134,8 +1110,6 @@ 2A6688FA24BCB3AC008E10F0 /* DWDPBasicItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWDPBasicItem.h; sourceTree = ""; }; 2A6688FB24BCB739008E10F0 /* DWDPTxItemView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWDPTxItemView.h; sourceTree = ""; }; 2A6688FC24BCB739008E10F0 /* DWDPTxItemView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWDPTxItemView.m; sourceTree = ""; }; - 2A6B8E522387056200A2E5FA /* DWBalanceDisplayOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWBalanceDisplayOptions.h; sourceTree = ""; }; - 2A6B8E532387056200A2E5FA /* DWBalanceDisplayOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWBalanceDisplayOptions.m; sourceTree = ""; }; 2A741DC02363993600840ADF /* DWTodayViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWTodayViewController.h; sourceTree = ""; }; 2A741DC123639A9700840ADF /* TodayExtension.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = TodayExtension.storyboard; sourceTree = ""; }; 2A741DC42363A06000840ADF /* DWURLParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWURLParser.h; sourceTree = ""; }; @@ -1339,22 +1313,13 @@ 2A913E6D23A26663006A2A59 /* DWOnboardingViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWOnboardingViewController.h; sourceTree = ""; }; 2A913E6E23A26663006A2A59 /* DWOnboardingViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWOnboardingViewController.m; sourceTree = ""; }; 2A913E7023A2667E006A2A59 /* Onboarding.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Onboarding.storyboard; sourceTree = ""; }; - 2A913E7323A2EB0E006A2A59 /* DWBalanceProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWBalanceProtocol.h; sourceTree = ""; }; - 2A913E7623A2EF06006A2A59 /* DWSyncProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWSyncProtocol.h; sourceTree = ""; }; 2A913E7723A2F2B6006A2A59 /* DWTxDisplayModeProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWTxDisplayModeProtocol.h; sourceTree = ""; }; - 2A913E7823A2F575006A2A59 /* DWShortcutsProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWShortcutsProtocol.h; sourceTree = ""; }; - 2A913E7923A2F637006A2A59 /* DWSyncContainerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWSyncContainerProtocol.h; sourceTree = ""; }; 2A913E7A23A2F7ED006A2A59 /* DWHomeProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWHomeProtocol.h; sourceTree = ""; }; 2A913E7B23A2FF4A006A2A59 /* DWRootProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWRootProtocol.h; sourceTree = ""; }; 2A913E7D23A30609006A2A59 /* DWRootModelStub.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWRootModelStub.h; sourceTree = ""; }; 2A913E7E23A30609006A2A59 /* DWRootModelStub.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWRootModelStub.m; sourceTree = ""; }; 2A913E8023A30623006A2A59 /* DWHomeModelStub.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWHomeModelStub.h; sourceTree = ""; }; 2A913E8123A30623006A2A59 /* DWHomeModelStub.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWHomeModelStub.m; sourceTree = ""; }; - 2A913E8323A309EA006A2A59 /* DWSyncModelStub.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWSyncModelStub.h; sourceTree = ""; }; - 2A913E8423A309EA006A2A59 /* DWSyncModelStub.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWSyncModelStub.m; sourceTree = ""; }; - 2A913E8623A30C2D006A2A59 /* DWBalanceDisplayOptionsProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWBalanceDisplayOptionsProtocol.h; sourceTree = ""; }; - 2A913E8723A30DA8006A2A59 /* DWBalanceDisplayOptionsStub.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWBalanceDisplayOptionsStub.h; sourceTree = ""; }; - 2A913E8823A30DA8006A2A59 /* DWBalanceDisplayOptionsStub.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWBalanceDisplayOptionsStub.m; sourceTree = ""; }; 2A913E8A23A3134E006A2A59 /* UIView+DWEmbedding.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIView+DWEmbedding.h"; sourceTree = ""; }; 2A913E8B23A3134E006A2A59 /* UIView+DWEmbedding.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIView+DWEmbedding.m"; sourceTree = ""; }; 2A913E8E23A31713006A2A59 /* UIViewController+DWEmbedding.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+DWEmbedding.h"; sourceTree = ""; }; @@ -1386,16 +1351,10 @@ 2A951CE223D1B92C00602824 /* DWBaseContactsModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWBaseContactsModel.h; sourceTree = ""; }; 2A951CE323D1B92C00602824 /* DWBaseContactsModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWBaseContactsModel.m; sourceTree = ""; }; 2A9CEBA422E1D26500A50237 /* DWSecureWalletDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWSecureWalletDelegate.h; sourceTree = ""; }; - 2A9CEBA622E1D5A200A50237 /* DWMainTabbarViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWMainTabbarViewController.h; sourceTree = ""; }; - 2A9CEBA722E1D5A200A50237 /* DWMainTabbarViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWMainTabbarViewController.m; sourceTree = ""; }; 2A9CEBAB22E1DA4000A50237 /* DWAppRootViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWAppRootViewController.h; sourceTree = ""; }; 2A9CEBAC22E1DA4000A50237 /* DWAppRootViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWAppRootViewController.m; sourceTree = ""; }; - 2A9CEBB322E1EAC900A50237 /* DWTabBarView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWTabBarView.h; sourceTree = ""; }; - 2A9CEBB422E1EAC900A50237 /* DWTabBarView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWTabBarView.m; sourceTree = ""; }; 2A9CEBB722E1FA1000A50237 /* DWHomeViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWHomeViewController.h; sourceTree = ""; }; 2A9CEBB822E1FA1000A50237 /* DWHomeViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWHomeViewController.m; sourceTree = ""; }; - 2A9D72A62497C52E00F79CD8 /* DWBadgeView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWBadgeView.h; sourceTree = ""; }; - 2A9D72A72497C52E00F79CD8 /* DWBadgeView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWBadgeView.m; sourceTree = ""; }; 2A9D72A9249A0E7800F79CD8 /* DWDPNewIncomingRequestItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWDPNewIncomingRequestItem.h; sourceTree = ""; }; 2A9D72AA249A0EE000F79CD8 /* DWDPNewIncomingRequestObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWDPNewIncomingRequestObject.h; sourceTree = ""; }; 2A9D72AB249A0EE000F79CD8 /* DWDPNewIncomingRequestObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWDPNewIncomingRequestObject.m; sourceTree = ""; }; @@ -1601,8 +1560,6 @@ 2AD6E5622487E16400B52F14 /* DWRequestsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWRequestsViewController.m; sourceTree = ""; }; 2AD6E5682487E1DB00B52F14 /* DWRequestsModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWRequestsModel.h; sourceTree = ""; }; 2AD6E5692487E1DB00B52F14 /* DWRequestsModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWRequestsModel.m; sourceTree = ""; }; - 2AD7BCBC2673BDE9008FF133 /* DWSyncingHeaderView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWSyncingHeaderView.h; sourceTree = ""; }; - 2AD7BCBD2673BDE9008FF133 /* DWSyncingHeaderView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWSyncingHeaderView.m; sourceTree = ""; }; 2AD85A9C245874B30045B480 /* DWCaptureSessionFrameDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWCaptureSessionFrameDelegate.h; sourceTree = ""; }; 2AD85A9D245881740045B480 /* DWQRScanStatusView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWQRScanStatusView.h; sourceTree = ""; }; 2AD85A9E245881740045B480 /* DWQRScanStatusView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DWQRScanStatusView.m; sourceTree = ""; }; @@ -1974,6 +1931,9 @@ C917024029D462C6008C034D /* PayViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayViewController.swift; sourceTree = ""; }; C91E919629FBACE6003E7883 /* ExtendedPublicKeysModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtendedPublicKeysModel.swift; sourceTree = ""; }; C91E91AD29FFC8A1003E7883 /* ExtendedPublicKeysViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtendedPublicKeysViewController.swift; sourceTree = ""; }; + C94946DE2A25EDA8008A678D /* DWHomeViewControllerDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWHomeViewControllerDelegate.h; sourceTree = ""; }; + C94946DF2A25EE24008A678D /* DWMainMenuViewControllerDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DWMainMenuViewControllerDelegate.h; sourceTree = ""; }; + C94946E02A25F037008A678D /* DemoMainTabbarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoMainTabbarViewController.swift; sourceTree = ""; }; C94F5E8729D3E7E30034FD57 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; C94F5E8929D3FCCF0034FD57 /* ShortcutAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutAction.swift; sourceTree = ""; }; C94F5E8B29D3FEC10034FD57 /* ShortcutsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsModel.swift; sourceTree = ""; }; @@ -1995,6 +1955,21 @@ C9F42FB329DD86FB001BC549 /* BackupInfoItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupInfoItemView.swift; sourceTree = ""; }; C9F42FB529DD8702001BC549 /* BackupInfoItemView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BackupInfoItemView.xib; sourceTree = ""; }; C9F42FB729DFC506001BC549 /* SpendableTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpendableTransaction.swift; sourceTree = ""; }; + C9F451E42A0B986E00825057 /* MainTabbarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabbarController.swift; sourceTree = ""; }; + C9F451E62A0BA16400825057 /* PaymentButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentButton.swift; sourceTree = ""; }; + C9F451E82A0BDAE700825057 /* UIApplication+DashWallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+DashWallet.swift"; sourceTree = ""; }; + C9F451EA2A0BF10B00825057 /* SyncingAlertViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncingAlertViewController.swift; sourceTree = ""; }; + C9F451ED2A0BF1F500825057 /* SyncingAlertContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncingAlertContentView.swift; sourceTree = ""; }; + C9F451F22A0C933700825057 /* SyncingHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncingHeaderView.swift; sourceTree = ""; }; + C9F451F42A0CAC9400825057 /* HomeHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeHeaderView.swift; sourceTree = ""; }; + C9F451F62A0CAE1300825057 /* SyncView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncView.swift; sourceTree = ""; }; + C9F451F82A0CB08900825057 /* ProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressView.swift; sourceTree = ""; }; + C9F451FA2A0CC2A800825057 /* DashPayProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashPayProfileView.swift; sourceTree = ""; }; + C9F451FC2A0CC4A300825057 /* BadgeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeView.swift; sourceTree = ""; }; + C9F452002A0CE6C900825057 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; + C9F452022A0CEB5800825057 /* TxListEmptyTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TxListEmptyTableViewCell.swift; sourceTree = ""; }; + C9F452072A11F28600825057 /* SyncModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncModel.swift; sourceTree = ""; }; + C9F4520A2A1209D100825057 /* HomeHeaderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeHeaderModel.swift; sourceTree = ""; }; CE02413EF0C60B1D1EDE6457 /* Pods-WatchApp Extension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WatchApp Extension.release.xcconfig"; path = "Pods/Target Support Files/Pods-WatchApp Extension/Pods-WatchApp Extension.release.xcconfig"; sourceTree = ""; }; D58B25CB4DC36975E05D3C0A /* Pods-dashwallet no watch.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-dashwallet no watch.debug.xcconfig"; path = "Pods/Target Support Files/Pods-dashwallet no watch/Pods-dashwallet no watch.debug.xcconfig"; sourceTree = ""; }; EA95ACF6CA2A73810B9BB451 /* Pods-DashWalletScreenshotsUITests.testflight.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DashWalletScreenshotsUITests.testflight.xcconfig"; path = "Pods/Target Support Files/Pods-DashWalletScreenshotsUITests/Pods-DashWalletScreenshotsUITests.testflight.xcconfig"; sourceTree = ""; }; @@ -2558,15 +2533,14 @@ path = DataMigration; sourceTree = ""; }; - 2A1A60B92674083B003DAC65 /* SyncingAlert */ = { + 2A1A60B92674083B003DAC65 /* Syncing Views */ = { isa = PBXGroup; children = ( - 2A1A60BA2674084F003DAC65 /* DWSyncingAlertContentView.h */, - 2A1A60BB2674084F003DAC65 /* DWSyncingAlertContentView.m */, - 2A1A60BD26740E27003DAC65 /* DWSyncingAlertViewController.h */, - 2A1A60BE26740E27003DAC65 /* DWSyncingAlertViewController.m */, + C9F4520C2A121EEF00825057 /* Model */, + C9F4520D2A121EF900825057 /* Sync View */, + C9F451EC2A0BF1EF00825057 /* Alert */, ); - path = SyncingAlert; + path = "Syncing Views"; sourceTree = ""; }; 2A1AE78B23F463FB00179A6E /* Views */ = { @@ -2653,14 +2627,9 @@ isa = PBXGroup; children = ( 2A913EA123A799D9006A2A59 /* Transactions */, - 472D13E7299E4EE7006903F1 /* BalanceModel.swift */, 2A4E533622F023AB00E5168A /* DWHomeModel.h */, 2A4E533722F023AB00E5168A /* DWHomeModel.m */, - 2A4E534E22F19BE300E5168A /* DWSyncModel.h */, - 2A4E534F22F19BE300E5168A /* DWSyncModel.m */, 47081198298CF57D003FCA3D /* TransactionListDataSource.swift */, - 2A6B8E522387056200A2E5FA /* DWBalanceDisplayOptions.h */, - 2A6B8E532387056200A2E5FA /* DWBalanceDisplayOptions.m */, 2A56EF0424193AEB002C32F3 /* DWDashPayModel.h */, 2A56EF0524193AEB002C32F3 /* DWDashPayModel.m */, 2AFF01D9243F4559003718DC /* DWDPRegistrationStatus.h */, @@ -2694,21 +2663,11 @@ 2A307CB322E6FA7F00A18347 /* Views */ = { isa = PBXGroup; children = ( + C9F451FF2A0CE63C00825057 /* Home Balance View */, + C9F451FE2A0CE60400825057 /* Home Header View */, 2A2CD70B22F97A9D008C7BC9 /* Shortcuts */, 2A4E533C22F025ED00E5168A /* Cells */, - C9F067F129E4576D0022D958 /* HomeBalanceView.swift */, - C9F067F329E457790022D958 /* HomeBalanceView.xib */, - 2A307CBA22E8925100A18347 /* DWHomeView.h */, - 2A307CBB22E8925100A18347 /* DWHomeView.m */, - 2A4E531622EA381F00E5168A /* DWSyncView.h */, - 2A4E531722EA381F00E5168A /* DWSyncView.m */, - 2A4E531922EA382B00E5168A /* DWSyncView.xib */, - 2A4E531E22EB2DF400E5168A /* DWHomeHeaderView.h */, - 2A4E531F22EB2DF400E5168A /* DWHomeHeaderView.m */, - 2A612AB524409D6E0060CC77 /* DWDashPayProfileView.h */, - 2A612AB624409D6E0060CC77 /* DWDashPayProfileView.m */, - 2AD7BCBC2673BDE9008FF133 /* DWSyncingHeaderView.h */, - 2AD7BCBD2673BDE9008FF133 /* DWSyncingHeaderView.m */, + C9F452002A0CE6C900825057 /* HomeView.swift */, ); path = Views; sourceTree = ""; @@ -2771,6 +2730,8 @@ 2ADC9D1B24603C4F001D7C0D /* UISearchBar+DWAdditions.m */, 47AE8C1428C6378E00490F5E /* HairlineView.swift */, C9F42FAF29DC27F4001BC549 /* EmptyView.swift */, + C9F451F82A0CB08900825057 /* ProgressView.swift */, + C9F451FC2A0CC4A300825057 /* BadgeView.swift */, ); path = Views; sourceTree = ""; @@ -2836,8 +2797,6 @@ 2A74EFF723053ECE00C475EB /* DWIntrinsicTextView.m */, 2A0C69B12312E8A0001B8C90 /* DWWindow.h */, 2A0C69B22312E8A0001B8C90 /* DWWindow.m */, - 2A9D72A62497C52E00F79CD8 /* DWBadgeView.h */, - 2A9D72A72497C52E00F79CD8 /* DWBadgeView.m */, ); path = SharedViews; sourceTree = ""; @@ -2873,10 +2832,10 @@ 2A44313C22CF631E009BAF7F /* UI */ = { isa = PBXGroup; children = ( + 4751137228DAF27300223B77 /* Assembly */, C9F42FA729DC09C6001BC549 /* Style */, 11C5F51128E5D0C500F6F135 /* CrowdNode */, 47A50F362912D9A800C70123 /* Payment Controller */, - 4751137228DAF27300223B77 /* Assembly */, 4751136A28D9A3BE00223B77 /* Portal */, 0F6EDFE028C8AE32000427E7 /* Coinbase */, 47AE8BB328C1305E00490F5E /* Explore Dash */, @@ -3002,15 +2961,15 @@ 2A4E533C22F025ED00E5168A /* Cells */ = { isa = PBXGroup; children = ( + C9F451F22A0C933700825057 /* SyncingHeaderView.swift */, 2A5E4544243E0595006BA067 /* RegistrationStatus */, - 2A4E533D22F025FE00E5168A /* DWTxListEmptyTableViewCell.h */, - 2A4E533E22F025FE00E5168A /* DWTxListEmptyTableViewCell.m */, - 2A4E533F22F025FE00E5168A /* DWTxListEmptyTableViewCell.xib */, + C9F452022A0CEB5800825057 /* TxListEmptyTableViewCell.swift */, + 2A4E533F22F025FE00E5168A /* TxListEmptyTableViewCell.xib */, 2A4E534922F03A9E00E5168A /* DWFilterHeaderView.h */, 2A4E534A22F03A9E00E5168A /* DWFilterHeaderView.m */, 2A4E534C22F03AAC00E5168A /* DWFilterHeaderView.xib */, 474C7219298A803200475CA6 /* TxListTableViewCell.swift */, - 2A4E535322F1D0D900E5168A /* DWTxListTableViewCell.xib */, + 2A4E535322F1D0D900E5168A /* TxListTableViewCell.xib */, ); path = Cells; sourceTree = ""; @@ -3144,6 +3103,7 @@ 2A7A7BB32347928B00451078 /* Views */, 2A7A7BB02347927700451078 /* DWMainMenuViewController.h */, 2A7A7BB12347927700451078 /* DWMainMenuViewController.m */, + C94946DF2A25EE24008A678D /* DWMainMenuViewControllerDelegate.h */, ); path = Main; sourceTree = ""; @@ -3610,12 +3570,7 @@ 2A913E7223A2EAEF006A2A59 /* Protocols */ = { isa = PBXGroup; children = ( - 2A913E8623A30C2D006A2A59 /* DWBalanceDisplayOptionsProtocol.h */, - 2A913E7323A2EB0E006A2A59 /* DWBalanceProtocol.h */, 2A913E7A23A2F7ED006A2A59 /* DWHomeProtocol.h */, - 2A913E7823A2F575006A2A59 /* DWShortcutsProtocol.h */, - 2A913E7923A2F637006A2A59 /* DWSyncContainerProtocol.h */, - 2A913E7623A2EF06006A2A59 /* DWSyncProtocol.h */, 2A913E7723A2F2B6006A2A59 /* DWTxDisplayModeProtocol.h */, 2A56EF0724193BA9002C32F3 /* DWDashPayProtocol.h */, ); @@ -3625,8 +3580,6 @@ 2A913E7C23A305E5006A2A59 /* Stubs */ = { isa = PBXGroup; children = ( - 2A913E8723A30DA8006A2A59 /* DWBalanceDisplayOptionsStub.h */, - 2A913E8823A30DA8006A2A59 /* DWBalanceDisplayOptionsStub.m */, 2A913E8023A30623006A2A59 /* DWHomeModelStub.h */, 2A913E8123A30623006A2A59 /* DWHomeModelStub.m */, 2AB3415C23A8133A004E37A7 /* DWPayModelStub.h */, @@ -3635,8 +3588,6 @@ 2AB3416123A81E8B004E37A7 /* DWReceiveModelStub.m */, 2A913E7D23A30609006A2A59 /* DWRootModelStub.h */, 2A913E7E23A30609006A2A59 /* DWRootModelStub.m */, - 2A913E8323A309EA006A2A59 /* DWSyncModelStub.h */, - 2A913E8423A309EA006A2A59 /* DWSyncModelStub.m */, 2A913EA623A79AD2006A2A59 /* DWTransactionListDataProviderStub.h */, 2A913EA723A79AD2006A2A59 /* DWTransactionListDataProviderStub.m */, 2A913EB423A7E145006A2A59 /* DWTransactionStub.h */, @@ -3726,8 +3677,7 @@ isa = PBXGroup; children = ( 2A9CEBB222E1EAAA00A50237 /* Views */, - 2A9CEBA622E1D5A200A50237 /* DWMainTabbarViewController.h */, - 2A9CEBA722E1D5A200A50237 /* DWMainTabbarViewController.m */, + C9F451E42A0B986E00825057 /* MainTabbarController.swift */, ); path = Main; sourceTree = ""; @@ -3745,12 +3695,7 @@ 2A9CEBB222E1EAAA00A50237 /* Views */ = { isa = PBXGroup; children = ( - 2A9CEBB322E1EAC900A50237 /* DWTabBarView.h */, - 2A9CEBB422E1EAC900A50237 /* DWTabBarView.m */, - 2A392563234CD21300316EA6 /* DWTabBarButton.h */, - 2A392564234CD21300316EA6 /* DWTabBarButton.m */, - 2A4E1D5125056297008AC53F /* DWPaymentsButton.h */, - 2A4E1D5225056297008AC53F /* DWPaymentsButton.m */, + C9F451E62A0BA16400825057 /* PaymentButton.swift */, ); path = Views; sourceTree = ""; @@ -3758,7 +3703,7 @@ 2A9CEBB622E1FA0000A50237 /* Home */ = { isa = PBXGroup; children = ( - 2A1A60B92674083B003DAC65 /* SyncingAlert */, + 2A1A60B92674083B003DAC65 /* Syncing Views */, 2A913E7223A2EAEF006A2A59 /* Protocols */, 2A2CD6E522F46D1A008C7BC9 /* Models */, 2A307CB322E6FA7F00A18347 /* Views */, @@ -3776,6 +3721,7 @@ 2A1B7DC223266C8400BA8C6A /* DWHomeViewController+DWSecureWalletDelegateImpl.m */, 2A3DC86F23972331004B3DBA /* DWHomeViewController+DWImportPrivateKeyDelegateImpl.h */, 2A3DC87023972331004B3DBA /* DWHomeViewController+DWImportPrivateKeyDelegateImpl.m */, + C94946DE2A25EDA8008A678D /* DWHomeViewControllerDelegate.h */, ); path = Home; sourceTree = ""; @@ -4053,8 +3999,7 @@ 2AB3417623A92978004E37A7 /* DWDemoAdvancedSecurityViewController.m */, 2AB3417123A926C9004E37A7 /* DWDemoAppRootViewController.h */, 2AB3417223A926C9004E37A7 /* DWDemoAppRootViewController.m */, - 2A12E61723ABC7D3001CAF58 /* DWDemoMainTabbarViewController.h */, - 2A12E61823ABC7D3001CAF58 /* DWDemoMainTabbarViewController.m */, + C94946E02A25F037008A678D /* DemoMainTabbarViewController.swift */, ); path = Controllers; sourceTree = ""; @@ -4288,6 +4233,7 @@ 47083B3129892D770010AF71 /* DSTransaction+DashWallet.swift */, 474C7210298A1A9500475CA6 /* UIView+Reuse.swift */, 472D13E9299E5396006903F1 /* NSAttributedString+Builder.swift */, + C9F451E82A0BDAE700825057 /* UIApplication+DashWallet.swift */, ); path = Categories; sourceTree = ""; @@ -5719,6 +5665,52 @@ path = Style; sourceTree = ""; }; + C9F451EC2A0BF1EF00825057 /* Alert */ = { + isa = PBXGroup; + children = ( + C9F451ED2A0BF1F500825057 /* SyncingAlertContentView.swift */, + C9F451EA2A0BF10B00825057 /* SyncingAlertViewController.swift */, + ); + path = Alert; + sourceTree = ""; + }; + C9F451FE2A0CE60400825057 /* Home Header View */ = { + isa = PBXGroup; + children = ( + C9F451FA2A0CC2A800825057 /* DashPayProfileView.swift */, + C9F451F42A0CAC9400825057 /* HomeHeaderView.swift */, + C9F4520A2A1209D100825057 /* HomeHeaderModel.swift */, + ); + path = "Home Header View"; + sourceTree = ""; + }; + C9F451FF2A0CE63C00825057 /* Home Balance View */ = { + isa = PBXGroup; + children = ( + 472D13E7299E4EE7006903F1 /* BalanceModel.swift */, + C9F067F129E4576D0022D958 /* HomeBalanceView.swift */, + C9F067F329E457790022D958 /* HomeBalanceView.xib */, + ); + path = "Home Balance View"; + sourceTree = ""; + }; + C9F4520C2A121EEF00825057 /* Model */ = { + isa = PBXGroup; + children = ( + C9F452072A11F28600825057 /* SyncModel.swift */, + ); + path = Model; + sourceTree = ""; + }; + C9F4520D2A121EF900825057 /* Sync View */ = { + isa = PBXGroup; + children = ( + C9F451F62A0CAE1300825057 /* SyncView.swift */, + 2A4E531922EA382B00E5168A /* SyncView.xib */, + ); + path = "Sync View"; + sourceTree = ""; + }; EBFC2EA47915CD4F5BA81564 /* Pods */ = { isa = PBXGroup; children = ( @@ -6014,7 +6006,7 @@ 2A1AF6DF23C7681B00442AF5 /* DWShortcutCollectionViewCell~iphone.xib in Resources */, 2A4431D622D52F67009BAF7F /* DWInfoTextCell.xib in Resources */, 2A44314922CF82EF009BAF7F /* BiometricAuth.storyboard in Resources */, - 2A4E534122F025FE00E5168A /* DWTxListEmptyTableViewCell.xib in Resources */, + 2A4E534122F025FE00E5168A /* TxListEmptyTableViewCell.xib in Resources */, 2A8B9E3D22FD71E100FF8653 /* Payments.storyboard in Resources */, 2A4E534D22F03AAC00E5168A /* DWFilterHeaderView.xib in Resources */, 757E09991ADB8EEB006FD352 /* Localizable.strings in Resources */, @@ -6033,8 +6025,8 @@ 75E83CF61B5F997A0038FB70 /* coinflip.aiff in Resources */, 2ADF83FF23633116008459A7 /* SharedAssets.xcassets in Resources */, 2A4431C522D4CAFA009BAF7F /* BackupInfo.storyboard in Resources */, - 2A4E531A22EA382B00E5168A /* DWSyncView.xib in Resources */, - 2A4E535522F1D0D900E5168A /* DWTxListTableViewCell.xib in Resources */, + 2A4E531A22EA382B00E5168A /* SyncView.xib in Resources */, + 2A4E535522F1D0D900E5168A /* TxListTableViewCell.xib in Resources */, C9F42FB629DD8702001BC549 /* BackupInfoItemView.xib in Resources */, 47AE8BB028BFF28700490F5E /* explore.db in Resources */, 2A0C69F423179C0F001B8C90 /* DWConfirmPaymentContentView.xib in Resources */, @@ -6390,12 +6382,12 @@ 4709C32128818D5400B4BD48 /* Foundation+Bitcoin.swift in Sources */, 47AE8BE928C1305F00490F5E /* AtmDetailsView.swift in Sources */, 2A9FFE872230FF4700956D5F /* DWPlaceholderFormTableViewCell.m in Sources */, - 2A4E532022EB2DF400E5168A /* DWHomeHeaderView.m in Sources */, 2A0C69F22316B93F001B8C90 /* DWTitleDetailCellView.m in Sources */, 47C661B828FE907300028A8D /* AmountInputControl.swift in Sources */, 2A1F640C238D5BBB00A9B505 /* DWSegmentSliderFormTableViewCell.m in Sources */, 11B8449428F27C470082770C /* ApiCode.swift in Sources */, 47838B92290701470003E8AB /* CoinbaseEntryPointModel.swift in Sources */, + C9F451EB2A0BF10B00825057 /* SyncingAlertViewController.swift in Sources */, 2A56EF002419310C002C32F3 /* DWDashPayConstants.m in Sources */, 11B8449628F5B9F80082770C /* CrowdNodeResponse.swift in Sources */, 47AE8BB228BFF61A00490F5E /* FileManager+DashWallet.swift in Sources */, @@ -6431,14 +6423,12 @@ 477F50102950A55A003C7508 /* Coinbase+Error.swift in Sources */, 2A63003F2327B4BB00827825 /* DWPaymentOutput+DWView.m in Sources */, 2ACD53EE234C9D8E00650AD3 /* UIView+DWRecursiveSubview.m in Sources */, - 2A9CEBA822E1D5A200A50237 /* DWMainTabbarViewController.m in Sources */, 11860923297598B400279FCC /* AddressStatus.swift in Sources */, C91E919729FBACE6003E7883 /* ExtendedPublicKeysModel.swift in Sources */, 4751CAD5297024EA00F63AC4 /* OrderPreviewViewController.swift in Sources */, 478C982F294305D800FAA0F0 /* ActivePaymentMethodView.swift in Sources */, 47CDEEC72949DC38008AE06D /* BasicInfoController.swift in Sources */, 2AD1CEA822E0C8C900C99324 /* DWPreviewSeedPhraseModel.m in Sources */, - 2A913E8923A30DA8006A2A59 /* DWBalanceDisplayOptionsStub.m in Sources */, 2A0C69CD23142F90001B8C90 /* DWModalPresentationAnimation.m in Sources */, 110C679A29227948006B580C /* UINavigationController+CrowdNode.swift in Sources */, 2A8B9E7A2302E67400FF8653 /* DWReceiveModel.m in Sources */, @@ -6465,7 +6455,6 @@ 11517C852949E6F0004FC7BF /* CrowdNodeEndpoint.swift in Sources */, 0F71317528F4365C0072F454 /* ServiceEntryPointModel.swift in Sources */, 47AE8B9528BFACA100490F5E /* ExploreDash.swift in Sources */, - 2A4E535022F19BE300E5168A /* DWSyncModel.m in Sources */, 2A7A7BE02348DC1900451078 /* DWToolsMenuViewController.m in Sources */, 2AE8B66B23CF0F390016F221 /* DWUsernamePendingViewController.m in Sources */, 4789D22F2981067C00BAFEFA /* UpholdAmountModel.swift in Sources */, @@ -6486,6 +6475,9 @@ C3DAD2CF247585C10001624F /* NSPredicate+DWFullTextSearch.m in Sources */, 47F2C6842860513900C2B774 /* TxReclassifyTransactionsWhereToChangeViewController.swift in Sources */, 2A7A7C16234B763600451078 /* DWLocalCurrencyViewController.m in Sources */, + C9F451FD2A0CC4A300825057 /* BadgeView.swift in Sources */, + C9F451F52A0CAC9400825057 /* HomeHeaderView.swift in Sources */, + C9F451F92A0CB08900825057 /* ProgressView.swift in Sources */, 2A2120EA2214566A009906DC /* DWAmountInputValidator.m in Sources */, 47AE8BF628C1306000490F5E /* ExplorePointOfUseListViewController.swift in Sources */, 117ECC7E29742A42003D798E /* CrowdNodeWebViewController.swift in Sources */, @@ -6498,17 +6490,16 @@ 2A8B9E4F22FED47E00FF8653 /* DWOverlapControl.m in Sources */, 119E8D062905200300D406C1 /* CoinsToAddressTxFilter.swift in Sources */, 2AB3417323A926C9004E37A7 /* DWDemoAppRootViewController.m in Sources */, - 2AD7BCBE2673BDE9008FF133 /* DWSyncingHeaderView.m in Sources */, 2A9FFE9B2230FF4700956D5F /* DWUpholdLogoutTutorialViewController.m in Sources */, 47CF46A529654E190067B6EE /* CBAccount.swift in Sources */, 47AF180529070B720025803E /* Types.swift in Sources */, 2A0C69EB2316B344001B8C90 /* DWConfirmPaymentViewController.m in Sources */, 2A7AF3402481A1B2001D74F9 /* DWDPGenericItemView.m in Sources */, 4774DCDB28F3FA9C008CF87D /* Coinbase.swift in Sources */, + C9F452082A11F28600825057 /* SyncModel.swift in Sources */, C9F42FAB29DC1098001BC549 /* ReceiveContentView.swift in Sources */, 47838B7528FFD1D10003E8AB /* AmountView.swift in Sources */, 4759D512292FD6F3002F20DC /* DWBasePayViewController.m in Sources */, - 2A12E61923ABC7D3001CAF58 /* DWDemoMainTabbarViewController.m in Sources */, 1193FF3629602835004EA8D7 /* CrowdNodeTransferModel.swift in Sources */, 2A44312622CCC14F009BAF7F /* DWSetPinViewController.m in Sources */, 47F4B6C7294842DF00AED4C9 /* ConfirmOrderController.swift in Sources */, @@ -6520,13 +6511,16 @@ 47A2A2E2293DCCEA00938DB7 /* Coinbase+Constants.swift in Sources */, 47AE8C0728C2274200490F5E /* PointOfUseListSearchCell.swift in Sources */, 4774DCE128F44BA4008CF87D /* ServiceDataProvider.swift in Sources */, + C9F451E72A0BA16400825057 /* PaymentButton.swift in Sources */, 2A4431DB22D675CD009BAF7F /* DWPreviewSeedPhraseViewController.m in Sources */, + C9F451FB2A0CC2A800825057 /* DashPayProfileView.swift in Sources */, 110D1781298BA9AF005BEB30 /* WKWebView+CrowdNode.swift in Sources */, 47FA3AFF29350929008D58DC /* SyncingActivityMonitor.swift in Sources */, C9F42FB029DC27F4001BC549 /* EmptyView.swift in Sources */, 2AFCB9BE23BE3C0800FF59A6 /* DWConfirmSendPaymentViewController.m in Sources */, 2AC92C8A1FEB0B8B008CAEE0 /* DWQRScanModel.m in Sources */, 2A3DC87123972331004B3DBA /* DWHomeViewController+DWImportPrivateKeyDelegateImpl.m in Sources */, + C9F451F72A0CAE1300825057 /* SyncView.swift in Sources */, 47522F502927CB9000EE143E /* SuccessfulOperationStatusViewController.swift in Sources */, 47E94B9A296D7F99000FE68E /* CustodialSwapsModel.swift in Sources */, 2A7A7C20234B79B700451078 /* DWLocalCurrencyTableViewCell.m in Sources */, @@ -6572,6 +6566,7 @@ 2A0C699C23104588001B8C90 /* DWPaymentInput.m in Sources */, 2AE9549D23D0C4F4003612B3 /* DWBaseContactsViewController.m in Sources */, 114D16B629812730009A124C /* OnlineAccountDetailsController.swift in Sources */, + C9F451E92A0BDAE700825057 /* UIApplication+DashWallet.swift in Sources */, 110D1784298E68A8005BEB30 /* OnlineAccountInfoController.swift in Sources */, 470AE1882926600A001A0514 /* PaymentController.swift in Sources */, 2ADB396C242615C200A6F898 /* CALayer+MBAnimationPersistence.m in Sources */, @@ -6675,6 +6670,7 @@ 2A8B9E2922FB1C5D00FF8653 /* DWSharedUIConstants.m in Sources */, 2A858A12237EE89C0097A7B5 /* BRAppleWatchTransactionData.m in Sources */, 47A50F3E29192F8D00C70123 /* TerritoriesListViewController.swift in Sources */, + C9F4520B2A1209D100825057 /* HomeHeaderModel.swift in Sources */, 47C6E6E02919578C003FEDF2 /* TerritoryListModel.swift in Sources */, 2A9FFE952230FF4700956D5F /* DWUpholdConfirmTransferModel.m in Sources */, 2AFF01DB243F4559003718DC /* DWDPRegistrationStatus.m in Sources */, @@ -6719,6 +6715,7 @@ 475AE2B92974348F009A1055 /* App.swift in Sources */, 2A0C69A3231048DA001B8C90 /* DWPaymentProcessor.m in Sources */, 2A7AF32824814A17001D74F9 /* DWNotificationsData.m in Sources */, + C9F451F32A0C933700825057 /* SyncingHeaderView.swift in Sources */, 114CFED2296489CD005F421B /* MinimumDepositBanner.swift in Sources */, 47838B8F2906D7340003E8AB /* BalanceView.swift in Sources */, 2AD85A9F245881740045B480 /* DWQRScanStatusView.m in Sources */, @@ -6730,14 +6727,12 @@ 2A3CCEF9242BB1B900300AF8 /* DWRegistrationCompletedViewController.m in Sources */, 2ADC9D712462D4AD001D7C0D /* DWUserProfileHeaderView.m in Sources */, 47305872295C971F004641DA /* UIViewController+AlertPresenting.swift in Sources */, - 2A9CEBB522E1EAC900A50237 /* DWTabBarView.m in Sources */, 2A858A0F237EE89C0097A7B5 /* DSWatchTransactionDataObject.m in Sources */, 47FA3B0229364991008D58DC /* HTTPClient.swift in Sources */, 2A8B9E6F2302A9C200FF8653 /* DWPasteboardAddressExtractor.m in Sources */, 477F501529531C07003C7508 /* ViewModel+Coinbase.swift in Sources */, 478A9297299242EC0008C43E /* CNCreateAccountTxDetailsModel.swift in Sources */, 47A2A2EC293E618600938DB7 /* CBUser.swift in Sources */, - 2A307CBC22E8925100A18347 /* DWHomeView.m in Sources */, 47A2A2EE293E622700938DB7 /* CBSecureTokenService.swift in Sources */, C9F42FA129DA95F5001BC549 /* PayTableViewCell.swift in Sources */, C3DAD2C8247538AA0001624F /* DWTitleActionHeaderView.m in Sources */, @@ -6752,6 +6747,7 @@ 2A7A7BB22347927700451078 /* DWMainMenuViewController.m in Sources */, 47838B7D290133610003E8AB /* PointOfUseListFiltersViewController.swift in Sources */, 47AE8C1A28C6A21A00490F5E /* AllMerchantLocationsDataProvider.swift in Sources */, + C9F451E52A0B986E00825057 /* MainTabbarController.swift in Sources */, 47B30D78290BFCA60080C326 /* NumberFormatter+DashWallet.swift in Sources */, 2AEC5CBB2494045C00F4A689 /* DWNotificationsFetchedDataSource.m in Sources */, 47AF18082907B7880025803E /* BaseAmountModel.swift in Sources */, @@ -6766,6 +6762,7 @@ 11E47BAD28EB3A7D0097CFA0 /* CrowdNode+Constants.swift in Sources */, 47F006022971B1FF0029EB10 /* CurrencyExchanger.swift in Sources */, 2A5BD5922451D68300688A8D /* DWMinLengthUsernameValidationRule.m in Sources */, + C94946E12A25F037008A678D /* DemoMainTabbarViewController.swift in Sources */, 11AE3DD62997AA36000856EE /* MessageStatus.swift in Sources */, 2AD1CEAB22E18F1800C99324 /* DWGlobalOptions.m in Sources */, 2A4430E722CBB6EC009BAF7F /* AppDelegate.m in Sources */, @@ -6823,7 +6820,6 @@ 2A8B9E5622FEDF2900FF8653 /* DWControllerCollectionView.m in Sources */, 2A9FFDF52230FF1A00956D5F /* SFSafariViewController+DashWallet.m in Sources */, 2AD6E54D2487CE0100B52F14 /* DWContactsDataSourceObject.m in Sources */, - 2A6B8E542387056200A2E5FA /* DWBalanceDisplayOptions.m in Sources */, 474C7218298A422500475CA6 /* TransactionItemView.swift in Sources */, 474C7211298A1A9500475CA6 /* UIView+Reuse.swift in Sources */, 2A9FFE072230FF2B00956D5F /* DWUpholdClient.m in Sources */, @@ -6831,9 +6827,7 @@ 47F4B6CA29484C9800AED4C9 /* ConfirmOrderModel.swift in Sources */, 472CEE012924AA6D00656B48 /* PointOfUseListEmptyResultsView.swift in Sources */, C94F5E8C29D3FEC10034FD57 /* ShortcutsModel.swift in Sources */, - 2A4E531822EA381F00E5168A /* DWSyncView.m in Sources */, 2A6300452328D07500827825 /* DWLockPinInputView.m in Sources */, - 2A392565234CD21300316EA6 /* DWTabBarButton.m in Sources */, 2A0C69AC23125074001B8C90 /* UIView+DWHUD.m in Sources */, 2A8B9E6822FFE4CC00FF8653 /* DWPayOptionModel.m in Sources */, 2A7AF36924826681001D74F9 /* DWDPRespondedIncomingRequestObject.m in Sources */, @@ -6896,16 +6890,17 @@ 2A307CBF22E8A44200A18347 /* DWButton.m in Sources */, 0F6EDFD128C896BD000427E7 /* CoinbaseCreateAddressesRequest.swift in Sources */, 2A10EB352358996700C38B61 /* DWImportWalletInfoViewController.m in Sources */, + C9F452032A0CEB5800825057 /* TxListEmptyTableViewCell.swift in Sources */, + C9F451EE2A0BF1F500825057 /* SyncingAlertContentView.swift in Sources */, 471A2605289ACD5C0056B7B2 /* AddressUserInfo.swift in Sources */, - 2A1A60BF26740E27003DAC65 /* DWSyncingAlertViewController.m in Sources */, 47AE8C0C28C53E4A00490F5E /* MerchantInfoViewController.swift in Sources */, 11E47BAB28EB38510097CFA0 /* SendCoinsService.swift in Sources */, C94F5E8A29D3FCCF0034FD57 /* ShortcutAction.swift in Sources */, 2A0C69E0231556D6001B8C90 /* DWModalChevronView.m in Sources */, 2A58815921A5906C00FD4D2C /* DWBaseLegacyViewController.m in Sources */, C3CA2030247E54C400158074 /* DWNoNotificationsCell.m in Sources */, + C9F452012A0CE6C900825057 /* HomeView.swift in Sources */, 47A2E3AC2972B1A60032A63B /* CoinbaseRatesProvider.swift in Sources */, - 2A4E534022F025FE00E5168A /* DWTxListEmptyTableViewCell.m in Sources */, 472D13DF299DF5C6006903F1 /* TaxReportGenerator.swift in Sources */, 119E8D122909513F00D406C1 /* CrowdNodeError.swift in Sources */, 111C3C4E296C52F800788E18 /* WithdrawalLimit.swift in Sources */, @@ -6927,7 +6922,6 @@ 2A9FFE8E2230FF4700956D5F /* DWUpholdAuthViewController.m in Sources */, 2A1F640F238D5C0900A9B505 /* DWSegmentSliderFormCellModel.m in Sources */, 2AD1CE6022D8E9D900C99324 /* DWSeedPhraseView.m in Sources */, - 2A612AB724409D6E0060CC77 /* DWDashPayProfileView.m in Sources */, 2A3CCEFC242BB1DD00300AF8 /* DWDPAvatarView.m in Sources */, 47B30D7C29100ABA0080C326 /* UIStackView+DashWallet.swift in Sources */, 2ADC9D7C24644E46001D7C0D /* DWUserProfileContactActionsCell.m in Sources */, @@ -6942,7 +6936,6 @@ C9F42FA429DBC6E5001BC549 /* PaymentsViewController.swift in Sources */, 2A7AF3832482954C001D74F9 /* DWDPContactsItemsFactory.m in Sources */, 4774DCDD28F43A68008CF87D /* ServiceDataSource.swift in Sources */, - 2A913E8523A309EA006A2A59 /* DWSyncModelStub.m in Sources */, 47838B7A2900196F0003E8AB /* ConverterView.swift in Sources */, 2A0C69CA23142E11001B8C90 /* DWModalBaseAnimation.m in Sources */, C9F42FB829DFC507001BC549 /* SpendableTransaction.swift in Sources */, @@ -6957,10 +6950,8 @@ 47CF46A1296540EF0067B6EE /* AccountService.swift in Sources */, 47C6E6E7291A90B3003FEDF2 /* ListHandlerView.swift in Sources */, 2A7A7BCD2347F01B00451078 /* DWSecurityMenuViewController.m in Sources */, - 2A4E1D5325056297008AC53F /* DWPaymentsButton.m in Sources */, 11BD738128E7356100A34022 /* CrowdNode.swift in Sources */, 47AE8B9F28BFAD8200490F5E /* SQLite+ExloreDash.swift in Sources */, - 2A1A60BC2674084F003DAC65 /* DWSyncingAlertContentView.m in Sources */, 2A4E534B22F03A9E00E5168A /* DWFilterHeaderView.m in Sources */, 2A951CE423D1B92C00602824 /* DWBaseContactsModel.m in Sources */, 2A1F6415238FEEA900A9B505 /* DWAdvancedSecurityModel.m in Sources */, @@ -6984,7 +6975,6 @@ 1141E4C5291FDC7A00ACDA9E /* WelcomeToCrowdNodeViewController.swift in Sources */, 47C6E6E329196D48003FEDF2 /* TerritoriesListCell.swift in Sources */, 2A4E531D22EA49FE00E5168A /* DWProgressView.m in Sources */, - 2A9D72A82497C52E00F79CD8 /* DWBadgeView.m in Sources */, 47A5146428491C60005A8E3E /* TxDetailCells.swift in Sources */, 477F501729543834003C7508 /* BaseViewController+NetworkReachability.swift in Sources */, 75D5F3CE191EC270004AB296 /* main.m in Sources */, @@ -7438,7 +7428,7 @@ INFOPLIST_KEY_CFBundleDisplayName = Dash; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 7.0.0; + MARKETING_VERSION = 7.0.1; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; @@ -7496,7 +7486,7 @@ INFOPLIST_KEY_CFBundleDisplayName = Dash; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 7.0.0; + MARKETING_VERSION = 7.0.1; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "DashWallet/dashwallet-Bridging-Header.h"; @@ -7581,7 +7571,7 @@ EXCLUDED_ARCHS = ""; IBSC_MODULE = WatchApp_Extension; INFOPLIST_FILE = WatchApp/Info.plist; - MARKETING_VERSION = 7.0.0; + MARKETING_VERSION = 7.0.1; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.watchkitapp; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; @@ -7603,7 +7593,7 @@ EXCLUDED_ARCHS = ""; IBSC_MODULE = WatchApp_Extension; INFOPLIST_FILE = WatchApp/Info.plist; - MARKETING_VERSION = 7.0.0; + MARKETING_VERSION = 7.0.1; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.watchkitapp; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; @@ -7624,7 +7614,7 @@ EXCLUDED_ARCHS = ""; INFOPLIST_FILE = "WatchApp Extension/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 7.0.0; + MARKETING_VERSION = 7.0.1; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.watchkitapp.watchkitextension; PRODUCT_NAME = "${TARGET_NAME}"; SDKROOT = watchos; @@ -7647,7 +7637,7 @@ EXCLUDED_ARCHS = ""; INFOPLIST_FILE = "WatchApp Extension/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 7.0.0; + MARKETING_VERSION = 7.0.1; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.watchkitapp.watchkitextension; PRODUCT_NAME = "${TARGET_NAME}"; SDKROOT = watchos; @@ -7675,7 +7665,7 @@ INFOPLIST_FILE = TodayExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 7.0.0; + MARKETING_VERSION = 7.0.1; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.TodayExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -7700,7 +7690,7 @@ INFOPLIST_FILE = TodayExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 7.0.0; + MARKETING_VERSION = 7.0.1; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.TodayExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -7817,7 +7807,7 @@ INFOPLIST_KEY_CFBundleDisplayName = Dash; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 7.0.0; + MARKETING_VERSION = 7.0.1; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "DashWallet/dashwallet-Bridging-Header.h"; @@ -7845,7 +7835,7 @@ INFOPLIST_FILE = TodayExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 7.0.0; + MARKETING_VERSION = 7.0.1; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.TodayExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -7897,7 +7887,7 @@ EXCLUDED_ARCHS = ""; IBSC_MODULE = WatchApp_Extension; INFOPLIST_FILE = WatchApp/Info.plist; - MARKETING_VERSION = 7.0.0; + MARKETING_VERSION = 7.0.1; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.watchkitapp; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; @@ -7918,7 +7908,7 @@ EXCLUDED_ARCHS = ""; INFOPLIST_FILE = "WatchApp Extension/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 7.0.0; + MARKETING_VERSION = 7.0.1; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.watchkitapp.watchkitextension; PRODUCT_NAME = "${TARGET_NAME}"; SDKROOT = watchos; @@ -8034,7 +8024,7 @@ INFOPLIST_KEY_CFBundleDisplayName = Dash; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 7.0.0; + MARKETING_VERSION = 7.0.1; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "DashWallet/dashwallet-Bridging-Header.h"; @@ -8061,7 +8051,7 @@ INFOPLIST_FILE = TodayExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 7.0.0; + MARKETING_VERSION = 7.0.1; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.TodayExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -8112,7 +8102,7 @@ EXCLUDED_ARCHS = ""; IBSC_MODULE = WatchApp_Extension; INFOPLIST_FILE = WatchApp/Info.plist; - MARKETING_VERSION = 7.0.0; + MARKETING_VERSION = 7.0.1; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.watchkitapp; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; @@ -8133,7 +8123,7 @@ EXCLUDED_ARCHS = ""; INFOPLIST_FILE = "WatchApp Extension/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 7.0.0; + MARKETING_VERSION = 7.0.1; PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.watchkitapp.watchkitextension; PRODUCT_NAME = "${TARGET_NAME}"; SDKROOT = watchos; diff --git a/DashWallet/Resources/AppAssets.xcassets/Explore Dash/image.explore.dash.atm.item.logo.empty.imageset/Contents.json b/DashWallet/Resources/AppAssets.xcassets/Explore Dash/image.explore.dash.atm.item.logo.empty.imageset/Contents.json new file mode 100644 index 000000000..920cbb155 --- /dev/null +++ b/DashWallet/Resources/AppAssets.xcassets/Explore Dash/image.explore.dash.atm.item.logo.empty.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Icons & images.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Icons & images@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Icons & images@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DashWallet/Resources/AppAssets.xcassets/Explore Dash/image.explore.dash.atm.item.logo.empty.imageset/Icons & images.png b/DashWallet/Resources/AppAssets.xcassets/Explore Dash/image.explore.dash.atm.item.logo.empty.imageset/Icons & images.png new file mode 100644 index 000000000..61f386ab9 Binary files /dev/null and b/DashWallet/Resources/AppAssets.xcassets/Explore Dash/image.explore.dash.atm.item.logo.empty.imageset/Icons & images.png differ diff --git a/DashWallet/Resources/AppAssets.xcassets/Explore Dash/image.explore.dash.atm.item.logo.empty.imageset/Icons & images@2x.png b/DashWallet/Resources/AppAssets.xcassets/Explore Dash/image.explore.dash.atm.item.logo.empty.imageset/Icons & images@2x.png new file mode 100644 index 000000000..92a01674d Binary files /dev/null and b/DashWallet/Resources/AppAssets.xcassets/Explore Dash/image.explore.dash.atm.item.logo.empty.imageset/Icons & images@2x.png differ diff --git a/DashWallet/Resources/AppAssets.xcassets/Explore Dash/image.explore.dash.atm.item.logo.empty.imageset/Icons & images@3x.png b/DashWallet/Resources/AppAssets.xcassets/Explore Dash/image.explore.dash.atm.item.logo.empty.imageset/Icons & images@3x.png new file mode 100644 index 000000000..e4e1f4cdd Binary files /dev/null and b/DashWallet/Resources/AppAssets.xcassets/Explore Dash/image.explore.dash.atm.item.logo.empty.imageset/Icons & images@3x.png differ diff --git a/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/Contents.json b/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/Contents.json index ea144946b..232e1ca6e 100644 --- a/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/Contents.json +++ b/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/Contents.json @@ -1,23 +1,23 @@ { "images" : [ { + "filename" : "logo.uphold.png", "idiom" : "universal", - "filename" : "uphold_logo.png", "scale" : "1x" }, { + "filename" : "logo.uphold@2x.png", "idiom" : "universal", - "filename" : "uphold_logo@2x.png", "scale" : "2x" }, { + "filename" : "logo.uphold@3x.png", "idiom" : "universal", - "filename" : "uphold_logo@3x.png", "scale" : "3x" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/logo.uphold.png b/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/logo.uphold.png new file mode 100644 index 000000000..798b60d7c Binary files /dev/null and b/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/logo.uphold.png differ diff --git a/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/logo.uphold@2x.png b/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/logo.uphold@2x.png new file mode 100644 index 000000000..42c9b349f Binary files /dev/null and b/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/logo.uphold@2x.png differ diff --git a/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/logo.uphold@3x.png b/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/logo.uphold@3x.png new file mode 100644 index 000000000..8c1723dcd Binary files /dev/null and b/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/logo.uphold@3x.png differ diff --git a/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/uphold_logo.png b/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/uphold_logo.png deleted file mode 100644 index bda2430ae..000000000 Binary files a/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/uphold_logo.png and /dev/null differ diff --git a/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/uphold_logo@2x.png b/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/uphold_logo@2x.png deleted file mode 100644 index b9f108949..000000000 Binary files a/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/uphold_logo@2x.png and /dev/null differ diff --git a/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/uphold_logo@3x.png b/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/uphold_logo@3x.png deleted file mode 100644 index f5c65d2c3..000000000 Binary files a/DashWallet/Resources/AppAssets.xcassets/Uphold/uphold_logo.imageset/uphold_logo@3x.png and /dev/null differ diff --git a/DashWallet/Sources/Application/App.swift b/DashWallet/Sources/Application/App.swift index feea49529..41e2f5235 100644 --- a/DashWallet/Sources/Application/App.swift +++ b/DashWallet/Sources/Application/App.swift @@ -92,6 +92,7 @@ final class App { func cleanUp() { TxUserInfoDAOImpl.shared.deleteAll() AddressUserInfoDAOImpl.shared.deleteAll() + Coinbase.shared.reset() } } diff --git a/DashWallet/Sources/Application/Syncyng Activity Monitor/SyncingActivityMonitor.swift b/DashWallet/Sources/Application/Syncyng Activity Monitor/SyncingActivityMonitor.swift index 9e3398128..7b80c5f2a 100644 --- a/DashWallet/Sources/Application/Syncyng Activity Monitor/SyncingActivityMonitor.swift +++ b/DashWallet/Sources/Application/Syncyng Activity Monitor/SyncingActivityMonitor.swift @@ -68,6 +68,10 @@ class SyncingActivityMonitor: NSObject, NetworkReachabilityHandling { DWGlobalOptions.sharedInstance().isResyncingWallet = false } + guard oldValue != state else { + return + } + NotificationCenter.default.post(name: .syncStateChangedNotification, object: nil, userInfo: [ kSyncStateChangedFromStateKey: oldValue, @@ -151,6 +155,16 @@ class SyncingActivityMonitor: NSObject, NetworkReachabilityHandling { startSyncingActivity() } + @objc + func peerManagerConnectedPeersDidChangeNotification(notification: Notification) { + let isConnected = DWEnvironment.sharedInstance().currentChainManager.peerManager.connected + + if isConnected { + NotificationCenter.default.removeObserver(self, name: .peerManagerConnectedPeersDidChange, object: nil) + startSyncingIfNeeded() + } + } + deinit { NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(reachabilityObserver!) @@ -164,6 +178,9 @@ class SyncingActivityMonitor: NSObject, NetworkReachabilityHandling { extension SyncingActivityMonitor { private func startSyncingIfNeeded() { guard DWEnvironment.sharedInstance().currentChainManager.peerManager.connected else { + NotificationCenter.default.addObserver(self, selector: #selector(peerManagerConnectedPeersDidChangeNotification(notification:)), + name: .peerManagerConnectedPeersDidChange, object: nil) + return } @@ -173,7 +190,7 @@ extension SyncingActivityMonitor { private func startSyncingActivity() { guard !isSyncing else { return } - progress = 0 + progress = chainSyncProgress lastPeakDate = nil NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(syncLoop), object: nil) @@ -224,9 +241,6 @@ extension SyncingActivityMonitor { perform(#selector(syncLoop), with: nil, afterDelay: kSyncLoopInterval) } else { - self.progress = 1.0 - state = .syncDone - stopSyncingActivity(failed: false) } } @@ -300,4 +314,7 @@ extension Notification.Name { static let chainManagerSyncFailed: Notification.Name = .init(rawValue: "DSChainManagerSyncFailedNotification") static let chainManagerChainSyncBlocksDidChange: Notification .Name = .init(rawValue: "DSChainChainSyncBlocksDidChangeNotification") + static let peerManagerConnectedPeersDidChange: Notification + .Name = .init(rawValue: "DSPeerManagerConnectedPeersDidChangeNotification") + } diff --git a/DashWallet/Sources/Categories/UIApplication+DashWallet.swift b/DashWallet/Sources/Categories/UIApplication+DashWallet.swift new file mode 100644 index 000000000..7929bf80f --- /dev/null +++ b/DashWallet/Sources/Categories/UIApplication+DashWallet.swift @@ -0,0 +1,43 @@ +// +// Created by PT +// Copyright © 2023 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Darwin +import MachO.dyld + +@objc +extension UIApplication { + @objc + static var isJailbroken: Bool { + var s = stat() + let jailbroken = stat("/bin/sh", &s) == 0 // if we can see /bin/sh, the app isn't sandboxed + + // some anti-jailbreak detection tools re-sandbox apps, so do a secondary check for any MobileSubstrate dyld images + let count = _dyld_image_count() + for i in 0.. 0 } + static var hasHomeIndicator: Bool { + (UIApplication.shared.delegate?.window??.safeAreaInsets.bottom ?? 0) > 0 + } } diff --git a/DashWallet/Sources/Categories/UITableView+DashWallet.swift b/DashWallet/Sources/Categories/UITableView+DashWallet.swift index f1c1b79ab..d560242fb 100644 --- a/DashWallet/Sources/Categories/UITableView+DashWallet.swift +++ b/DashWallet/Sources/Categories/UITableView+DashWallet.swift @@ -30,6 +30,10 @@ extension UITableView { dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T } + func registerNibForHeaderFooterView(for type: T.Type) { + register(UINib(nibName: T.reuseIdentifier, bundle: nil), forHeaderFooterViewReuseIdentifier: T.reuseIdentifier) + } + func registerClassforHeaderFooterView(for type: T.Type) { register(T.self, forHeaderFooterViewReuseIdentifier: T.reuseIdentifier) } diff --git a/DashWallet/Sources/Models/Coinbase/Coinbase.swift b/DashWallet/Sources/Models/Coinbase/Coinbase.swift index 0997b9576..953c66ded 100644 --- a/DashWallet/Sources/Models/Coinbase/Coinbase.swift +++ b/DashWallet/Sources/Models/Coinbase/Coinbase.swift @@ -40,6 +40,11 @@ class CoinbaseObjcWrapper: NSObject { static func start() { wrapped.initialize() } + + @objc + static func reset() { + wrapped.reset() + } } // MARK: - Coinbase @@ -59,11 +64,17 @@ class Coinbase { auth = CBAuth() accountService = AccountService(authInterop: auth) paymentMethodsService = PaymentMethods(authInterop: auth) - currencyExchanger.startExchangeRateFetching() + currencyExchanger.startExchangeRateFetching() prefetchData() } + func reset() { + Task { + try await signOut() + } + } + private func prefetchData() { Task { try await accountService.refreshAccount(kDashAccount) @@ -135,7 +146,7 @@ extension Coinbase { amount: UInt64) async throws -> CoinbaseTransaction { do { let tx = try await accountService.send(from: kDashAccount, amount: amount, verificationCode: verificationCode) - + if let address = tx.to?.address { Taxes.shared.mark(address: address, with: .transferIn) } diff --git a/DashWallet/Sources/Models/CrowdNode/CrowdNode.swift b/DashWallet/Sources/Models/CrowdNode/CrowdNode.swift index 80f89aeef..ee7f28436 100644 --- a/DashWallet/Sources/Models/CrowdNode/CrowdNode.swift +++ b/DashWallet/Sources/Models/CrowdNode/CrowdNode.swift @@ -170,7 +170,7 @@ public final class CrowdNode { let topUpTx = try await sendCoinsService.sendCoins(address: accountAddress, amount: amount) let filter = SpendableTransaction(transactionManager: transactionManager, txHashData: topUpTx.txHashData) - + if filter.matches(tx: topUpTx) { return topUpTx } else { @@ -276,11 +276,11 @@ extension CrowdNode { DSLogger.log("found signUp CrowdNode request, account: \(address)") signUpState = SignUpState.signingUp } - + private func validatePrefs() { if let accountAddress = prefs.accountAddress { let wallet = DWEnvironment.sharedInstance().currentWallet - + if !wallet.containsAddress(accountAddress) { DSLogger.log("Found alien address in CrowdNode prefs") reset() diff --git a/DashWallet/Sources/Models/CrowdNode/TxFilters/SpendableTransaction.swift b/DashWallet/Sources/Models/CrowdNode/TxFilters/SpendableTransaction.swift index 6bebb3032..4ad035083 100644 --- a/DashWallet/Sources/Models/CrowdNode/TxFilters/SpendableTransaction.swift +++ b/DashWallet/Sources/Models/CrowdNode/TxFilters/SpendableTransaction.swift @@ -27,14 +27,14 @@ public final class SpendableTransaction: TransactionFilter { func matches(tx: DSTransaction) -> Bool { let hashMatch = tx.txHashData == txHashData - + if hashMatch { let relayCount = transactionManager.relayCount(forTransaction: tx.txHash) DSLogger.log("CrowdNode: SpendableTransaction matched hash \(tx.txHashHexString); relayCount: \(relayCount)") - + return relayCount > 0 } - + return false } } diff --git a/DashWallet/Sources/Models/Explore Dash/Model/Entites/ExplorePointOfUse.swift b/DashWallet/Sources/Models/Explore Dash/Model/Entites/ExplorePointOfUse.swift index 5838c7503..3496fdf33 100644 --- a/DashWallet/Sources/Models/Explore Dash/Model/Entites/ExplorePointOfUse.swift +++ b/DashWallet/Sources/Models/Explore Dash/Model/Entites/ExplorePointOfUse.swift @@ -80,6 +80,15 @@ extension ExplorePointOfUse { return Int64.max } } + + var emptyLogoImageName: String { + switch category { + case .merchant(_), .unknown: + return "image.explore.dash.wts.item.logo.empty" + case .atm: + return "image.explore.dash.atm.item.logo.empty" + } + } } // MARK: - ExplorePointOfUse.Atm @@ -189,7 +198,7 @@ extension ExplorePointOfUse: RowDecodable { let longitude = row[ExplorePointOfUse.longitude] var website: String? - if var value = row[ExplorePointOfUse.website], !value.isEmpty { + if let value = row[ExplorePointOfUse.website], !value.isEmpty { if !value.hasPrefix("http") { website = "https://" + value } else { @@ -197,9 +206,9 @@ extension ExplorePointOfUse: RowDecodable { } } - let phone: String? = row[ExplorePointOfUse.phone]?.digits - let logoLocation = row[ExplorePointOfUse.logoLocation] - let coverImage: String? = row[ExplorePointOfUse.coverImage] + let phone: String? = try? row.get(ExplorePointOfUse.phone)?.digits + let logoLocation: String? = try? row.get(ExplorePointOfUse.logoLocation) + let coverImage: String? = try? row.get(ExplorePointOfUse.coverImage) let source: String? = row[ExplorePointOfUse.source] let category: Category diff --git a/DashWallet/Sources/Models/Uphold/DWUpholdAPIProvider.m b/DashWallet/Sources/Models/Uphold/DWUpholdAPIProvider.m index efeecdecf..5bbaa5fc5 100644 --- a/DashWallet/Sources/Models/Uphold/DWUpholdAPIProvider.m +++ b/DashWallet/Sources/Models/Uphold/DWUpholdAPIProvider.m @@ -84,7 +84,7 @@ - (DWUpholdCancellationToken)upholdAuthorizedRequest:(HTTPRequest *)httpRequest #pragma mark - API Provider -static NSSet *FiatCurrencyCodes() { +static NSSet *FiatCurrencyCodes(void) { return [NSSet setWithObjects: @"ARS", @"AUD", @"BRL", @"CAD", @"DKK", @"AED", @"EUR", @"HKD", @"INR", @"ILS", @"KES", @"MXN", @"NZD", @"NOK", @"PHP", @"PLN", @"GBP", @"SGD", @"SEK", @"CHF", @"USD", @"JPY", @"CNY", nil]; diff --git a/DashWallet/Sources/UI/Assembly/UIAssembly.swift b/DashWallet/Sources/UI/Assembly/UIAssembly.swift index 022dfcbf1..57b10eed2 100644 --- a/DashWallet/Sources/UI/Assembly/UIAssembly.swift +++ b/DashWallet/Sources/UI/Assembly/UIAssembly.swift @@ -41,7 +41,7 @@ extension UINib { guard let view = UINib(nibName: name.reuseIdentifier, bundle: nil).instantiate(withOwner: nil).first else { fatalError("Expect view") } - + return view as! T } } diff --git a/DashWallet/Sources/UI/Coinbase/Order Preview/OrderPreviewViewController.swift b/DashWallet/Sources/UI/Coinbase/Order Preview/OrderPreviewViewController.swift index d5542da60..52fd1ed87 100644 --- a/DashWallet/Sources/UI/Coinbase/Order Preview/OrderPreviewViewController.swift +++ b/DashWallet/Sources/UI/Coinbase/Order Preview/OrderPreviewViewController.swift @@ -79,7 +79,7 @@ class OrderPreviewViewController: BaseViewController, NetworkReachabilityHandlin Cryptocurrency markets are volatile, and this allows us to temporarily lock in a price for trade execution. """, comment: "Coinbase/Buy Dash/Fee Info") vc.actionButtonText = NSLocalizedString("Learn More...", comment: "Coinbase") - + let nvc = BaseNavigationController(rootViewController: vc) present(nvc, animated: true) } diff --git a/DashWallet/Sources/UI/Coinbase/ServiceOverview/ServiceEntryPointModel.swift b/DashWallet/Sources/UI/Coinbase/ServiceOverview/ServiceEntryPointModel.swift index f072bac2d..3b5dc52b8 100644 --- a/DashWallet/Sources/UI/Coinbase/ServiceOverview/ServiceEntryPointModel.swift +++ b/DashWallet/Sources/UI/Coinbase/ServiceOverview/ServiceEntryPointModel.swift @@ -77,7 +77,7 @@ extension Service { var entryIcon: String { switch self { case .coinbase: return "service.coinbase.square" - case .uphold: return "service.uphold.square" + case .uphold: return "uphold_logo" } } diff --git a/DashWallet/Sources/UI/Coinbase/Transfer Amount/TransferAmountViewController.swift b/DashWallet/Sources/UI/Coinbase/Transfer Amount/TransferAmountViewController.swift index 0fd34399b..4b0933ea5 100644 --- a/DashWallet/Sources/UI/Coinbase/Transfer Amount/TransferAmountViewController.swift +++ b/DashWallet/Sources/UI/Coinbase/Transfer Amount/TransferAmountViewController.swift @@ -43,7 +43,7 @@ final class TransferAmountViewController: CoinbaseAmountViewController, Converte override func actionButtonAction(sender: UIView) { showActivityIndicator() - + if transferModel.direction == .toCoinbase { checkLeftoverBalance { [weak self] canContinue in guard canContinue, let wSelf = self else { self?.hideActivityIndicator(); return } diff --git a/DashWallet/Sources/UI/Coinbase/Transfer Amount/Views/ConverterView.swift b/DashWallet/Sources/UI/Coinbase/Transfer Amount/Views/ConverterView.swift index 2a36de53b..76adcb670 100644 --- a/DashWallet/Sources/UI/Coinbase/Transfer Amount/Views/ConverterView.swift +++ b/DashWallet/Sources/UI/Coinbase/Transfer Amount/Views/ConverterView.swift @@ -350,9 +350,10 @@ final class SourceView: UIView { addSubview(stackView) imageView = UIImageView() + imageView.contentMode = .scaleAspectFit imageView.translatesAutoresizingMaskIntoConstraints = false imageView.layer.cornerRadius = 17 - imageView.backgroundColor = .dw_secondaryBackground() + imageView.backgroundColor = .clear stackView.addArrangedSubview(imageView) let labelStackView = UIStackView() diff --git a/DashWallet/Sources/UI/CrowdNode/CrowdNodeModel.swift b/DashWallet/Sources/UI/CrowdNode/CrowdNodeModel.swift index e7c09407e..ee02a2024 100644 --- a/DashWallet/Sources/UI/CrowdNode/CrowdNodeModel.swift +++ b/DashWallet/Sources/UI/CrowdNode/CrowdNodeModel.swift @@ -129,7 +129,7 @@ final class CrowdNodeModel { onlineAccountState = crowdNode.onlineAccountState observeState() observeBalances() - + crowdNode.restoreState() getAccountAddress() } diff --git a/DashWallet/Sources/UI/CrowdNode/Getting Started/GettingStartedViewController.swift b/DashWallet/Sources/UI/CrowdNode/Getting Started/GettingStartedViewController.swift index cdac8d49a..618586337 100644 --- a/DashWallet/Sources/UI/CrowdNode/Getting Started/GettingStartedViewController.swift +++ b/DashWallet/Sources/UI/CrowdNode/Getting Started/GettingStartedViewController.swift @@ -140,7 +140,7 @@ extension GettingStartedViewController { } } -// MARK: DWSecureWalletDelegate +// MARK: BackupInfoViewControllerDelegate extension GettingStartedViewController: BackupInfoViewControllerDelegate { private func backupPassphrase() { diff --git a/DashWallet/Sources/UI/Explore Dash/DWExploreTestnetViewController.h b/DashWallet/Sources/UI/Explore Dash/DWExploreTestnetViewController.h index f1c4c73c1..8d837471c 100644 --- a/DashWallet/Sources/UI/Explore Dash/DWExploreTestnetViewController.h +++ b/DashWallet/Sources/UI/Explore Dash/DWExploreTestnetViewController.h @@ -15,7 +15,6 @@ // limitations under the License. // -#import "DWSyncProtocol.h" #import "dashwallet-Swift.h" #import diff --git a/DashWallet/Sources/UI/Explore Dash/Merchants & ATMs/Details/Views/PointOfUseDetailsView.swift b/DashWallet/Sources/UI/Explore Dash/Merchants & ATMs/Details/Views/PointOfUseDetailsView.swift index 6aff41db1..bc3e18112 100644 --- a/DashWallet/Sources/UI/Explore Dash/Merchants & ATMs/Details/Views/PointOfUseDetailsView.swift +++ b/DashWallet/Sources/UI/Explore Dash/Merchants & ATMs/Details/Views/PointOfUseDetailsView.swift @@ -149,7 +149,7 @@ extension PointOfUseDetailsView { if let str = merchant.logoLocation, let url = URL(string: str) { logoImageView.sd_setImage(with: url, completed: nil) } else { - logoImageView.image = UIImage(named: "image.explore.dash.wts.item.logo.empty") + logoImageView.image = UIImage(named: merchant.emptyLogoImageName) } let subStackView = UIStackView() diff --git a/DashWallet/Sources/UI/Explore Dash/Merchants & ATMs/List/Cells/PointOfUseItemCell.swift b/DashWallet/Sources/UI/Explore Dash/Merchants & ATMs/List/Cells/PointOfUseItemCell.swift index ff1447a42..ed2037e66 100644 --- a/DashWallet/Sources/UI/Explore Dash/Merchants & ATMs/List/Cells/PointOfUseItemCell.swift +++ b/DashWallet/Sources/UI/Explore Dash/Merchants & ATMs/List/Cells/PointOfUseItemCell.swift @@ -44,7 +44,7 @@ class PointOfUseItemCell: UITableViewCell { if let urlString = pointOfUse.logoLocation, let url = URL(string: urlString) { logoImageView.sd_setImage(with: url) } else { - logoImageView.image = UIImage(named:"image.explore.dash.wts.item.logo.empty") + logoImageView.image = UIImage(named: pointOfUse.emptyLogoImageName) } } diff --git a/DashWallet/Sources/UI/Explore Dash/Merchants & ATMs/List/Views/ExploreMapAnnotationView.swift b/DashWallet/Sources/UI/Explore Dash/Merchants & ATMs/List/Views/ExploreMapAnnotationView.swift index 5c69d5c90..44cbe8afc 100644 --- a/DashWallet/Sources/UI/Explore Dash/Merchants & ATMs/List/Views/ExploreMapAnnotationView.swift +++ b/DashWallet/Sources/UI/Explore Dash/Merchants & ATMs/List/Views/ExploreMapAnnotationView.swift @@ -64,7 +64,7 @@ final class ExploreMapAnnotationView: MKAnnotationView { if let str = pointOfUse.logoLocation, let url = URL(string: str) { imageView.sd_setImage(with: url, completed: nil) } else { - imageView.image = UIImage(named: "image.explore.dash.wts.item.logo.empty") + imageView.image = UIImage(named: pointOfUse.emptyLogoImageName) } } @@ -77,6 +77,7 @@ final class ExploreMapAnnotationView: MKAnnotationView { imageView.layer.masksToBounds = true imageView.layer.borderColor = UIColor.white.cgColor imageView.layer.borderWidth = 2 + imageView.contentMode = .scaleAspectFit addSubview(imageView) imageView.frame = bounds diff --git a/DashWallet/Sources/UI/Home/DWHomeViewController+DWJailbreakCheck.m b/DashWallet/Sources/UI/Home/DWHomeViewController+DWJailbreakCheck.m index 570c16333..2a92b8507 100644 --- a/DashWallet/Sources/UI/Home/DWHomeViewController+DWJailbreakCheck.m +++ b/DashWallet/Sources/UI/Home/DWHomeViewController+DWJailbreakCheck.m @@ -31,7 +31,7 @@ @interface DWHomeViewController (DWJailbreakCheck_Internal) - -- (void)showPaymentsControllerWithActivePage:(DWPaymentsViewControllerIndex)pageIndex; - -@end +@protocol DWHomeViewControllerDelegate; @interface DWHomeViewController : DWBasePayViewController diff --git a/DashWallet/Sources/UI/Home/DWHomeViewController.m b/DashWallet/Sources/UI/Home/DWHomeViewController.m index 4f7e332cf..d1b7ce007 100644 --- a/DashWallet/Sources/UI/Home/DWHomeViewController.m +++ b/DashWallet/Sources/UI/Home/DWHomeViewController.m @@ -17,18 +17,14 @@ #import "DWHomeViewController.h" -#import "DWBalanceDisplayOptionsProtocol.h" #import "DWEnvironment.h" #import "DWGlobalOptions.h" #import "DWHomeModel.h" -#import "DWHomeView.h" #import "DWHomeViewController+DWBackupReminder.h" #import "DWHomeViewController+DWJailbreakCheck.h" #import "DWHomeViewController+DWShortcuts.h" #import "DWModalUserProfileViewController.h" #import "DWNotificationsViewController.h" -#import "DWSyncModel.h" -#import "DWSyncingAlertViewController.h" #import "DWWindow.h" #import "UIViewController+DWTxFilter.h" #import "UIWindow+DSUtils.h" @@ -39,7 +35,6 @@ @interface DWHomeViewController () @property (strong, nonatomic) DWHomeView *view; -@property (nullable, nonatomic, strong) id syncStateObserver; @end @@ -50,10 +45,6 @@ @implementation DWHomeViewController - (void)dealloc { DSLog(@"☠️ %@", NSStringFromClass(self.class)); - - if (self.syncStateObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:self.syncStateObserver]; - } } - (void)loadView { @@ -94,13 +85,13 @@ - (void)viewDidAppear:(BOOL)animated { [self.model registerForPushNotifications]; [self showReclassifyYourTransactionsIfPossibleWithTransaction:self.model.allDataSource.items.firstObject]; - [self checkCrowdNodeState]; + [self.model checkCrowdNodeState]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; - [self.model.balanceDisplayOptions hideBalanceIfNeeded]; + [self.view hideBalanceIfNeeded]; } #pragma mark - DWHomeViewDelegate @@ -111,7 +102,6 @@ - (void)homeView:(DWHomeView *)homeView showTxFilter:(UIView *)sender { - (void)homeView:(DWHomeView *)homeView showSyncingStatus:(UIView *)sender { DWSyncingAlertViewController *controller = [[DWSyncingAlertViewController alloc] init]; - controller.model = self.model; [self presentViewController:controller animated:YES completion:nil]; } @@ -223,35 +213,7 @@ - (void)presentTransactionDetails:(DSTransaction *)transaction { [self presentViewController:nvc animated:YES completion:nil]; } -- (void)checkCrowdNodeState { - if (self.model.syncModel.state == DWSyncModelState_SyncDone) { - [CrowdNodeObjcWrapper restoreState]; - if ([CrowdNodeObjcWrapper isInterrupted]) { - // Continue signup - [CrowdNodeObjcWrapper continueInterrupted]; - } - } - else { - NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; - self.syncStateObserver = [notificationCenter addObserverForName:DWSyncStateChangedNotification - object:nil - queue:nil - usingBlock:^(NSNotification *note) { - BOOL isSynced = self.model.syncModel.state == DWSyncModelState_SyncDone; - - if (isSynced) { - if (self.syncStateObserver) { - // Only need to observe once - [[NSNotificationCenter defaultCenter] removeObserver:self.syncStateObserver]; - self.syncStateObserver = nil; - } - - [self checkCrowdNodeState]; - } - }]; - } -} @end diff --git a/DashWallet/Sources/UI/Home/Models/DWBalanceDisplayOptions.h b/DashWallet/Sources/UI/Home/DWHomeViewControllerDelegate.h similarity index 64% rename from DashWallet/Sources/UI/Home/Models/DWBalanceDisplayOptions.h rename to DashWallet/Sources/UI/Home/DWHomeViewControllerDelegate.h index 637b0d44a..1506eee11 100644 --- a/DashWallet/Sources/UI/Home/Models/DWBalanceDisplayOptions.h +++ b/DashWallet/Sources/UI/Home/DWHomeViewControllerDelegate.h @@ -1,6 +1,6 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. +// +// Created by PT +// Copyright © 2023 Dash Core Group. All rights reserved. // // Licensed under the MIT License (the "License"); // you may not use this file except in compliance with the License. @@ -17,12 +17,13 @@ #import -#import "DWBalanceDisplayOptionsProtocol.h" +#ifndef DWHomeViewControllerDelegate_h +#define DWHomeViewControllerDelegate_h -NS_ASSUME_NONNULL_BEGIN +@protocol DWHomeViewControllerDelegate -@interface DWBalanceDisplayOptions : NSObject +- (void)showPaymentsControllerWithActivePage:(NSInteger)pageIndex; @end -NS_ASSUME_NONNULL_END +#endif /* DWHomeViewControllerDelegate_h */ diff --git a/DashWallet/Sources/UI/Home/Models/DWBalanceDisplayOptions.m b/DashWallet/Sources/UI/Home/Models/DWBalanceDisplayOptions.m deleted file mode 100644 index f632de859..000000000 --- a/DashWallet/Sources/UI/Home/Models/DWBalanceDisplayOptions.m +++ /dev/null @@ -1,44 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWBalanceDisplayOptions.h" - -#import "DWGlobalOptions.h" - -NS_ASSUME_NONNULL_BEGIN - -@implementation DWBalanceDisplayOptions - -@synthesize balanceHidden = _balanceHidden; - -- (instancetype)init { - self = [super init]; - if (self) { - _balanceHidden = [DWGlobalOptions sharedInstance].balanceHidden; - } - return self; -} - -- (void)hideBalanceIfNeeded { - if ([DWGlobalOptions sharedInstance].balanceHidden) { - self.balanceHidden = YES; - } -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Models/DWHomeModel.h b/DashWallet/Sources/UI/Home/Models/DWHomeModel.h index 7f434f105..404e6a8e7 100644 --- a/DashWallet/Sources/UI/Home/Models/DWHomeModel.h +++ b/DashWallet/Sources/UI/Home/Models/DWHomeModel.h @@ -23,7 +23,6 @@ NS_ASSUME_NONNULL_BEGIN @interface DWHomeModel : NSObject -- (void)forceStartSyncingActivity; @end diff --git a/DashWallet/Sources/UI/Home/Models/DWHomeModel.m b/DashWallet/Sources/UI/Home/Models/DWHomeModel.m index 54fb444e7..e901ca15f 100644 --- a/DashWallet/Sources/UI/Home/Models/DWHomeModel.m +++ b/DashWallet/Sources/UI/Home/Models/DWHomeModel.m @@ -23,7 +23,6 @@ #import #import "AppDelegate.h" -#import "DWBalanceDisplayOptions.h" #import "DWDashPayConstants.h" #import "DWDashPayContactsUpdater.h" #import "DWDashPayModel.h" @@ -31,7 +30,6 @@ #import "DWGlobalOptions.h" #import "DWPayModel.h" #import "DWReceiveModel.h" -#import "DWSyncModel.h" #import "DWTransactionListDataProvider.h" #import "DWVersionManager.h" #import "UIDevice+DashWallet.h" @@ -39,32 +37,16 @@ NS_ASSUME_NONNULL_BEGIN -static BOOL IsJailbroken(void) { - struct stat s; - BOOL jailbroken = (stat("/bin/sh", &s) == 0) ? YES : NO; // if we can see /bin/sh, the app isn't sandboxed - - // some anti-jailbreak detection tools re-sandbox apps, so do a secondary check for any MobileSubstrate dyld images - for (uint32_t count = _dyld_image_count(), i = 0; i < count && !jailbroken; i++) { - if (strstr(_dyld_get_image_name(i), "MobileSubstrate")) - jailbroken = YES; - } - -#if TARGET_IPHONE_SIMULATOR - jailbroken = NO; -#endif - - return jailbroken; -} - -@interface DWHomeModel () +@interface DWHomeModel () @property (nonatomic, strong) dispatch_queue_t queue; @property (strong, nonatomic) DSReachabilityManager *reachability; @property (readonly, nonatomic, strong) DWTransactionListDataProvider *dataProvider; -@property (nullable, nonatomic, strong) DWBalanceModel *balanceModel; @property (nonatomic, strong) id dashPayModel; +@property (nonatomic, strong) SyncingActivityMonitor *syncMonitor; + @property (readonly, nonatomic, strong) DWTransactionListDataSource *dataSource; @property (null_resettable, nonatomic, strong) DWTransactionListDataSource *receivedDataSource; @property (null_resettable, nonatomic, strong) DWTransactionListDataSource *sentDataSource; @@ -76,13 +58,10 @@ @interface DWHomeModel () 0); @@ -247,10 +218,6 @@ - (BOOL)shouldShowWalletBackupReminder { return (secondsSinceBalanceChanged > DAY_TIME_INTERVAL); } -- (void)reloadShortcuts { - [self.shortcutsModel reloadShortcuts]; -} - - (void)registerForPushNotifications { [[AppDelegate appDelegate] registerForPushNotifications]; } @@ -297,7 +264,7 @@ - (BOOL)performOnSetupUpgrades { if (needsCheck) { // Show backup reminder shortcut [DWGlobalOptions sharedInstance].walletNeedsBackup = YES; - [self reloadShortcuts]; + [self.updatesObserver homeModelWantToReloadShortcuts:self]; } }]; }]; @@ -305,18 +272,24 @@ - (BOOL)performOnSetupUpgrades { return YES; } -- (void)forceStartSyncingActivity { - DWSyncModel *syncModel = (DWSyncModel *)self.syncModel; - NSAssert([syncModel isKindOfClass:DWSyncModel.class], @"Internal inconsistency"); - [syncModel forceStartSyncingActivity]; -} - - (void)walletDidWipe { #if DASHPAY_ENABLED self.dashPayModel = [[DWDashPayModel alloc] init]; #endif /* DASHPAY_ENABLED */ } +- (void)checkCrowdNodeState { + if (SyncingActivityMonitor.shared.state == SyncingActivityMonitorStateSyncDone) { + [CrowdNodeObjcWrapper restoreState]; + + if ([CrowdNodeObjcWrapper isInterrupted]) { + // Continue signup + [CrowdNodeObjcWrapper continueInterrupted]; + } + } +} + + #pragma mark - DWShortcutsModelDataSource - (BOOL)shouldShowCreateUserNameButton { @@ -343,14 +316,14 @@ - (BOOL)shouldShowCreateUserNameButton { BOOL canRegisterUsername = YES; const uint64_t balanceValue = wallet.balance; BOOL isEnoughBalance = balanceValue >= DWDP_MIN_BALANCE_TO_CREATE_USERNAME; - BOOL isSynced = self.syncModel.state == DWSyncModelState_SyncDone; + BOOL isSynced = [SyncingActivityMonitor shared].state == SyncingActivityMonitorStateSyncDone; return canRegisterUsername && isSynced && isEnoughBalance; } #pragma mark - Notifications - (void)reachabilityDidChangeNotification { - [self reloadShortcuts]; + [self.updatesObserver homeModelWantToReloadShortcuts:self]; if (self.reachability.networkReachabilityStatus != DSReachabilityStatusNotReachable && [UIApplication sharedApplication].applicationState != UIApplicationStateBackground) { @@ -370,27 +343,12 @@ - (void)transactionManagerTransactionStatusDidChangeNotification { - (void)applicationWillEnterForegroundNotification { [self startSyncIfNeeded]; - [self.balanceDisplayOptions hideBalanceIfNeeded]; } - (void)fiatCurrencyDidChangeNotification { [self updateBalance]; [self reloadTxDataSource]; -} - -- (void)syncStateChangedNotification { - BOOL isSynced = self.syncModel.state == DWSyncModelState_SyncDone; - if (isSynced) { - [self.dashPayModel updateUsernameStatus]; - - if (self.dashPayModel.username != nil) { - [self.receiveModel updateReceivingInfo]; - [[DWDashPayContactsUpdater sharedInstance] beginUpdating]; - } - } - - [self updateBalance]; - [self reloadTxDataSource]; + [self.updatesObserver homeModelDidChangeInnerModels:self]; } - (void)chainWalletsDidChangeNotification:(NSNotification *)notification { @@ -541,26 +499,7 @@ - (void)reloadTxDataSource { - (void)updateBalance { [self.receiveModel updateReceivingInfo]; - - uint64_t balanceValue = [DWEnvironment sharedInstance].currentWallet.balance; - if (self.balanceModel && - balanceValue > self.balanceModel.value && - self.balanceModel.value > 0 && - [UIApplication sharedApplication].applicationState != UIApplicationStateBackground && - self.syncModel.progress > 0.995) { - [[UIDevice currentDevice] dw_playCoinSound]; - } - - self.balanceModel = [[DWBalanceModel alloc] initWith:balanceValue]; - - DWGlobalOptions *options = [DWGlobalOptions sharedInstance]; - if (balanceValue > 0 && options.walletNeedsBackup && !options.balanceChangedDate) { - options.balanceChangedDate = [NSDate date]; - } - - options.userHasBalance = balanceValue > 0; - - [self reloadShortcuts]; + [self.updatesObserver homeModelWantToReloadShortcuts:self]; } - (NSArray *)filterTransactions:(NSArray *)allTransactions @@ -589,16 +528,26 @@ - (void)updateBalance { return [mutableTransactions copy]; } -- (NSString *)supplementaryAmountString { - return [self.balanceModel fiatAmountString]; -} +#pragma mark SyncingActivityMonitorObserver -- (NSString *)mainAmountString { - return [self.balanceModel mainAmountString]; -} +- (void)syncingActivityMonitorProgressDidChange:(double)progress {} -- (BOOL)isBalanceHidden { - return self.balanceDisplayOptions.balanceHidden; +- (void)syncingActivityMonitorStateDidChangeWithPreviousState:(enum SyncingActivityMonitorState)previousState state:(enum SyncingActivityMonitorState)state { + BOOL isSynced = state == SyncingActivityMonitorStateSyncDone; + + if (isSynced) { + [self.dashPayModel updateUsernameStatus]; + + if (self.dashPayModel.username != nil) { + [self.receiveModel updateReceivingInfo]; + [[DWDashPayContactsUpdater sharedInstance] beginUpdating]; + } + + [self checkCrowdNodeState]; + } + + [self updateBalance]; + [self reloadTxDataSource]; } @end diff --git a/DashWallet/Sources/UI/Home/Models/DWSyncModel.h b/DashWallet/Sources/UI/Home/Models/DWSyncModel.h deleted file mode 100644 index e18d2b4d6..000000000 --- a/DashWallet/Sources/UI/Home/Models/DWSyncModel.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -#import "DWSyncProtocol.h" - -NS_ASSUME_NONNULL_BEGIN - -@class DSReachabilityManager; - -extern NSString *const DWSyncStateChangedNotification; -// `NSNumber` of previous state in notification `userInfo` dictionary -extern NSString *const DWSyncStateChangedFromStateKey; - -@interface DWSyncModel : NSObject - -- (void)forceStartSyncingActivity; - -- (instancetype)init; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Models/DWSyncModel.m b/DashWallet/Sources/UI/Home/Models/DWSyncModel.m deleted file mode 100644 index f86bb38db..000000000 --- a/DashWallet/Sources/UI/Home/Models/DWSyncModel.m +++ /dev/null @@ -1,122 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWSyncModel.h" - -#import - -#import "DWEnvironment.h" -#import "DWGlobalOptions.h" -#import "dashwallet-Swift.h" - -NS_ASSUME_NONNULL_BEGIN - -#define LOG_SYNCING 0 - -#if LOG_SYNCING -#define DWSyncLog(frmt, ...) DSLog(frmt, ##__VA_ARGS__) -#else -#define DWSyncLog(frmt, ...) -#endif /* LOG_SYNCING */ - -__unused static NSString *SyncStateToString(DWSyncModelState state) { - switch (state) { - case DWSyncModelState_Syncing: - return @"Syncing"; - case DWSyncModelState_SyncDone: - return @"Done"; - case DWSyncModelState_SyncFailed: - return @"Failed"; - case DWSyncModelState_NoConnection: - return @"NoConnection"; - } -} - -NSString *const DWSyncStateChangedNotification = @"DWSyncStateChangedNotification"; -NSString *const DWSyncStateChangedFromStateKey = @"DWSyncStateChangedFromStateKey"; - -static NSTimeInterval const SYNC_LOOP_INTERVAL = 0.2; - -// Wait for 2.5 seconds to update progress to the new peak value. -// Peak is considered to be a difference between progress values more than 10%. -static NSTimeInterval const PROGRESS_PEAK_DELAY = 3.25; // 3.25 sec -static float const MAX_PROGRESS_DELTA = 0.1; // 10% - -@interface DWSyncModel () - -@property (nonatomic, strong) SyncingActivityMonitor *syncMonitor; - -@property (nonatomic, assign) DWSyncModelState state; -@property (nonatomic, assign) float progress; - -@end - -@implementation DWSyncModel - -- (instancetype)init { - self = [super init]; - if (self) { - _syncMonitor = SyncingActivityMonitor.shared; - [_syncMonitor addObserver:self]; - - self.state = _syncMonitor.state; - self.progress = _syncMonitor.progress; - } - return self; -} - -- (void)dealloc { - [_syncMonitor removeObserver:self]; - - DSLog(@"☠️ %@", NSStringFromClass(self.class)); -} - -- (void)forceStartSyncingActivity { - DWSyncLog(@"[DW Sync] forceStartSyncingActivity"); - - [_syncMonitor forceStartSyncingActivity]; - - self.state = _syncMonitor.state; - self.progress = _syncMonitor.progress; -} - -#pragma mark Private - -- (void)setState:(DWSyncModelState)state { - NSAssert([NSThread isMainThread], @"Main thread is assumed here"); - - if (_state == state) { - return; - } - - DWSyncLog(@"[DW Sync] Sync state: %@ -> %@", SyncStateToString(previousState), SyncStateToString(state)); - _state = state; -} - -#pragma mark SyncingActivityMonitorObserver - -- (void)syncingActivityMonitorProgressDidChange:(double)progress { - self.progress = progress; -} - -- (void)syncingActivityMonitorStateDidChangeWithPreviousState:(enum SyncingActivityMonitorState)previousState state:(enum SyncingActivityMonitorState)state { - self.state = state; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Models/TransactionListDataSource.swift b/DashWallet/Sources/UI/Home/Models/TransactionListDataSource.swift index 2db05ebc4..078697a84 100644 --- a/DashWallet/Sources/UI/Home/Models/TransactionListDataSource.swift +++ b/DashWallet/Sources/UI/Home/Models/TransactionListDataSource.swift @@ -147,8 +147,7 @@ final class TransactionListDataSource: NSObject, UITableViewDataSource { cell.update(with: txs) return cell case .tx(let tx): - let cellId = TxListTableViewCell.dw_reuseIdentifier - let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! TxListTableViewCell + let cell = tableView.dequeueReusableCell(type: TxListTableViewCell.self, for: indexPath) cell.update(with: tx) return cell } @@ -166,7 +165,7 @@ enum TransactionListDataItemType: Int { extension TransactionListDataSource { @objc - func itemType(by indexPath: NSIndexPath) -> TransactionListDataItemType { + func itemType(by indexPath: IndexPath) -> TransactionListDataItemType { if case TransactionListDataItem.tx = _items[indexPath.row] { return .tx } diff --git a/DashWallet/Sources/UI/Home/Protocols/DWBalanceDisplayOptionsProtocol.h b/DashWallet/Sources/UI/Home/Protocols/DWBalanceDisplayOptionsProtocol.h deleted file mode 100644 index 306fb83e3..000000000 --- a/DashWallet/Sources/UI/Home/Protocols/DWBalanceDisplayOptionsProtocol.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@protocol DWBalanceDisplayOptionsProtocol - -@property (nonatomic, assign) BOOL balanceHidden; - -- (void)hideBalanceIfNeeded; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Protocols/DWHomeProtocol.h b/DashWallet/Sources/UI/Home/Protocols/DWHomeProtocol.h index 5e0442ce1..0d6497628 100644 --- a/DashWallet/Sources/UI/Home/Protocols/DWHomeProtocol.h +++ b/DashWallet/Sources/UI/Home/Protocols/DWHomeProtocol.h @@ -17,12 +17,8 @@ #import -#import "DWBalanceProtocol.h" #import "DWDashPayProtocol.h" -#import "DWShortcutsProtocol.h" -#import "DWSyncContainerProtocol.h" #import "DWTxDisplayModeProtocol.h" -#import "dashwallet-Swift.h" NS_ASSUME_NONNULL_BEGIN @@ -42,9 +38,11 @@ NS_ASSUME_NONNULL_BEGIN - (void)homeModel:(id)model didReceiveNewIncomingTransaction:(DSTransaction *)transaction; +- (void)homeModelDidChangeInnerModels:(id)model; +- (void)homeModelWantToReloadShortcuts:(id)model; @end -@protocol DWHomeProtocol +@protocol DWHomeProtocol @property (nullable, nonatomic, weak) id updatesObserver; @@ -56,12 +54,9 @@ NS_ASSUME_NONNULL_BEGIN @property (readonly, nonatomic, assign) BOOL shouldShowWalletBackupReminder; -@property (readonly, nonatomic, assign, getter=isJailbroken) BOOL jailbroken; @property (readonly, nonatomic, assign, getter=isWalletEmpty) BOOL walletEmpty; @property (readonly, nonatomic, assign, getter=isAllowedToShowReclassifyYourTransactions) BOOL allowedToShowReclassifyYourTransactions; -- (void)reloadShortcuts; - - (void)walletBackupReminderWasShown; - (void)registerForPushNotifications; @@ -73,6 +68,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)walletDidWipe; +- (void)retrySyncing; +- (void)checkCrowdNodeState; @end NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Protocols/DWShortcutsProtocol.h b/DashWallet/Sources/UI/Home/Protocols/DWShortcutsProtocol.h deleted file mode 100644 index 89d886906..000000000 --- a/DashWallet/Sources/UI/Home/Protocols/DWShortcutsProtocol.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@class DWShortcutsModel; - -@protocol DWShortcutsProtocol - -@property (readonly, nonatomic, strong) DWShortcutsModel *shortcutsModel; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Protocols/DWSyncContainerProtocol.h b/DashWallet/Sources/UI/Home/Protocols/DWSyncContainerProtocol.h deleted file mode 100644 index 3a74cf81f..000000000 --- a/DashWallet/Sources/UI/Home/Protocols/DWSyncContainerProtocol.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -#import "DWSyncProtocol.h" - -NS_ASSUME_NONNULL_BEGIN - -@protocol DWSyncContainerProtocol - -@property (readonly, nonatomic, strong) id syncModel; - -- (void)retrySyncing; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Protocols/DWSyncProtocol.h b/DashWallet/Sources/UI/Home/Protocols/DWSyncProtocol.h deleted file mode 100644 index 212083a20..000000000 --- a/DashWallet/Sources/UI/Home/Protocols/DWSyncProtocol.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -typedef NS_ENUM(NSUInteger, DWSyncModelState) { - DWSyncModelState_Syncing, - DWSyncModelState_SyncDone, - DWSyncModelState_SyncFailed, - DWSyncModelState_NoConnection, -}; - -@protocol DWSyncProtocol - -@property (readonly, nonatomic, assign) DWSyncModelState state; -@property (readonly, nonatomic, assign) float progress; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Syncing Views/Alert/SyncingAlertContentView.swift b/DashWallet/Sources/UI/Home/Syncing Views/Alert/SyncingAlertContentView.swift new file mode 100644 index 000000000..655d74ce8 --- /dev/null +++ b/DashWallet/Sources/UI/Home/Syncing Views/Alert/SyncingAlertContentView.swift @@ -0,0 +1,165 @@ +// +// Created by PT +// Copyright © 2023 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +// MARK: - SyncingAlertContentViewDelegate + +protocol SyncingAlertContentViewDelegate: AnyObject { + func syncingAlertContentView(_ view: SyncingAlertContentView, okButtonAction sender: UIButton) +} + +// MARK: - SyncingAlertContentView + +final class SyncingAlertContentView: UIView { + + private(set) var syncingImageView: UIImageView! + private(set) var titleLabel: UILabel! + private(set) var subtitleLabel: UILabel! + + weak var delegate: SyncingAlertContentViewDelegate? + + override init(frame: CGRect) { + super.init(frame: frame) + + backgroundColor = UIColor.dw_background() + + syncingImageView = UIImageView(image: UIImage(named: "icon_syncing_large")) + syncingImageView.translatesAutoresizingMaskIntoConstraints = false + addSubview(syncingImageView) + + titleLabel = UILabel() + titleLabel.translatesAutoresizingMaskIntoConstraints = false + titleLabel.font = UIFont.dw_font(forTextStyle: .title3) + titleLabel.textColor = UIColor.dw_darkTitle() + titleLabel.adjustsFontForContentSizeCategory = true + titleLabel.textAlignment = .center + titleLabel.numberOfLines = 0 + addSubview(titleLabel) + + subtitleLabel = UILabel() + subtitleLabel.translatesAutoresizingMaskIntoConstraints = false + subtitleLabel.font = UIFont.dw_font(forTextStyle: .callout) + subtitleLabel.textColor = UIColor.dw_secondaryText() + subtitleLabel.adjustsFontForContentSizeCategory = true + subtitleLabel.textAlignment = .center + subtitleLabel.numberOfLines = 0 + addSubview(subtitleLabel) + + let okButton = DWActionButton() + okButton.translatesAutoresizingMaskIntoConstraints = false + okButton.small = true + okButton.setTitle(NSLocalizedString("OK", comment: ""), for: .normal) + okButton.addTarget(self, action: #selector(okButtonAction(sender:)), for: .touchUpInside) + addSubview(okButton) + + syncingImageView.setContentCompressionResistancePriority(.required, for: .vertical) + syncingImageView.setContentCompressionResistancePriority(.required, for: .horizontal) + + titleLabel.setContentCompressionResistancePriority(.defaultHigh, for: .vertical) + subtitleLabel.setContentCompressionResistancePriority(.defaultLow, for: .vertical) + + NSLayoutConstraint.activate([ + syncingImageView.topAnchor.constraint(equalTo: topAnchor), + syncingImageView.centerXAnchor.constraint(equalTo: centerXAnchor), + + titleLabel.topAnchor.constraint(equalTo: syncingImageView.bottomAnchor, constant: 16.0), + titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor), + trailingAnchor.constraint(equalTo: titleLabel.trailingAnchor), + + subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8.0), + subtitleLabel.leadingAnchor.constraint(equalTo: leadingAnchor), + trailingAnchor.constraint(equalTo: subtitleLabel.trailingAnchor), + + okButton.topAnchor.constraint(equalTo: subtitleLabel.bottomAnchor, constant: 38.0), + okButton.centerXAnchor.constraint(equalTo: centerXAnchor), + bottomAnchor.constraint(equalTo: okButton.bottomAnchor), + okButton.heightAnchor.constraint(equalToConstant: 32.0), + okButton.widthAnchor.constraint(equalToConstant: 110.0), + ]) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc + func okButtonAction(sender: UIButton) { + delegate?.syncingAlertContentView(self, okButtonAction: sender) + } + + 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 { + subtitleLabel.text = String(format: NSLocalizedString("masternode list #%d of %d", comment: ""), + chainManager.masternodeManager.masternodeListRetrievalQueueMaxAmount - chainManager.masternodeManager.masternodeListRetrievalQueueCount, + chainManager.masternodeManager.masternodeListRetrievalQueueMaxAmount) + } 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) + } + } + + case .syncFailed: + subtitleLabel.text = NSLocalizedString("Sync Failed", comment: "") + + case .noConnection: + subtitleLabel.text = NSLocalizedString("Unable to connect", comment: "") + case .unknown: + break + } + + if syncState == .syncing { + showAnimation() + } else { + hideAnimation() + } + } + + func update(with progress: Double) { + titleLabel.text = String(format: "%@ %.1f%%", NSLocalizedString("Syncing", comment: ""), progress * 100.0) + } + + func showAnimation() { + if syncingImageView.layer.animation(forKey: "dw_rotationAnimation") != nil { + return + } + + let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z") + rotationAnimation.fromValue = 0 + rotationAnimation.toValue = Double.pi * 2.0 + rotationAnimation.duration = 1.75 + rotationAnimation.repeatCount = .infinity + syncingImageView.layer.add(rotationAnimation, forKey: "dw_rotationAnimation") + } + + func hideAnimation() { + syncingImageView.layer.removeAllAnimations() + } +} + diff --git a/DashWallet/Sources/UI/Home/Syncing Views/Alert/SyncingAlertViewController.swift b/DashWallet/Sources/UI/Home/Syncing Views/Alert/SyncingAlertViewController.swift new file mode 100644 index 000000000..faead6b56 --- /dev/null +++ b/DashWallet/Sources/UI/Home/Syncing Views/Alert/SyncingAlertViewController.swift @@ -0,0 +1,91 @@ +// +// Created by PT +// Copyright © 2023 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +// MARK: - SyncingAlertViewController + +@objc(DWSyncingAlertViewController) +final class SyncingAlertViewController: BaseViewController, SyncingAlertContentViewDelegate { + + var modalTransition = DWModalPopupTransition() + private lazy var childView: SyncingAlertContentView = { + let childView = SyncingAlertContentView() + childView.translatesAutoresizingMaskIntoConstraints = false + childView.delegate = self + childView.update(with: SyncingActivityMonitor.shared.progress) + childView.update(with: SyncingActivityMonitor.shared.state) + return childView + }() + + internal lazy var model: SyncModel = SyncModelImpl() + + init() { + super.init(nibName: nil, bundle: nil) + transitioningDelegate = modalTransition + modalPresentationStyle = .custom + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + model.networkStatusDidChange = { [weak self] _ in + // TODO: update view based on network connection instead of passing sync state + } + + model.progressDidChange = { [weak self] progress in + guard let self else { return } + self.childView.update(with: progress) + self.childView.update(with: self.model.state) + } + + model.stateDidChage = { [weak self] state in + self?.childView.update(with: state) + } + + view.backgroundColor = .clear + + let contentView = UIView() + contentView.translatesAutoresizingMaskIntoConstraints = false + contentView.backgroundColor = UIColor.dw_background() + contentView.layer.cornerRadius = 8.0 + contentView.layer.masksToBounds = true + view.addSubview(contentView) + + contentView.addSubview(childView) + + NSLayoutConstraint.activate([ + contentView.centerYAnchor.constraint(equalTo: view.centerYAnchor), + contentView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + childView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 32.0), + childView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + childView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + contentView.bottomAnchor.constraint(equalTo: childView.bottomAnchor, constant: 32.0), + ]) + } + + // MARK: - DWSyncingAlertContentViewDelegate + + func syncingAlertContentView(_ view: SyncingAlertContentView, okButtonAction sender: UIButton) { + dismiss(animated: true, completion: nil) + } +} diff --git a/DashWallet/Sources/UI/Home/Syncing Views/Model/SyncModel.swift b/DashWallet/Sources/UI/Home/Syncing Views/Model/SyncModel.swift new file mode 100644 index 000000000..ad77aba47 --- /dev/null +++ b/DashWallet/Sources/UI/Home/Syncing Views/Model/SyncModel.swift @@ -0,0 +1,85 @@ +// +// Created by PT +// Copyright © 2023 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +// MARK: - SyncModel + +protocol SyncModel { + var networkStatusDidChange: ((NetworkStatus) -> ())? { get set } + var networkStatus: NetworkStatus { get } + + var stateDidChage: ((SyncingActivityMonitor.State) -> ())? { get set } + var state: SyncingActivityMonitor.State { get } + + var progressDidChange: ((Double) -> ())? { get set } + var progress: Double { get } + + func forceStartSyncingActivity() +} + +// MARK: - SyncModelImpl + +final class SyncModelImpl: SyncModel { + var networkStatusDidChange: ((NetworkStatus) -> ())? + + var stateDidChage: ((SyncingActivityMonitor.State) -> ())? + private(set) var state: SyncingActivityMonitor.State + + var progressDidChange: ((Double) -> ())? + private(set) var progress: Double + + internal var reachabilityObserver: Any! + private let syncMonitor: SyncingActivityMonitor + + init() { + syncMonitor = SyncingActivityMonitor.shared + state = syncMonitor.state + progress = syncMonitor.progress + syncMonitor.add(observer: self) + + startNetworkMonitoring() + } + + func forceStartSyncingActivity() { + syncMonitor.forceStartSyncingActivity() + } + + deinit { + syncMonitor.remove(observer: self) + stopNetworkMonitoring() + } +} + +// MARK: NetworkReachabilityHandling + +extension SyncModelImpl: NetworkReachabilityHandling { } + +// MARK: SyncingActivityMonitorObserver + + +extension SyncModelImpl: SyncingActivityMonitorObserver { + func syncingActivityMonitorProgressDidChange(_ progress: Double) { + self.progress = progress + progressDidChange?(progress) + } + + func syncingActivityMonitorStateDidChange(previousState: SyncingActivityMonitor.State, state: SyncingActivityMonitor.State) { + self.state = state + stateDidChage?(state) + } +} diff --git a/DashWallet/Sources/UI/Home/Syncing Views/Sync View/SyncView.swift b/DashWallet/Sources/UI/Home/Syncing Views/Sync View/SyncView.swift new file mode 100644 index 000000000..271431bba --- /dev/null +++ b/DashWallet/Sources/UI/Home/Syncing Views/Sync View/SyncView.swift @@ -0,0 +1,183 @@ +// +// Created by PT +// Copyright © 2023 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +// MARK: - SyncViewDelegate + +protocol SyncViewDelegate: AnyObject { + func syncViewRetryButtonAction(_ view: SyncView) +} + +// MARK: - SyncView + +final class SyncView: UIView { + weak var delegate: SyncViewDelegate? + + var hasNetwork: Bool { + model.networkStatus == .online + } + + var syncState: SyncingActivityMonitor.State { + model.state + } + + var viewStateSeeingBlocks = false + + @IBOutlet var roundedView: UIView! + @IBOutlet var titleLabel: UILabel! + @IBOutlet var descriptionLabel: UILabel! + @IBOutlet var percentLabel: UILabel! + @IBOutlet var retryButton: UIButton! + @IBOutlet var progressView: ProgressView! + + internal lazy var model: SyncModel = SyncModelImpl() + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + private func commonInit() { + backgroundColor = UIColor.dw_secondaryBackground() + + titleLabel.font = UIFont.dw_font(forTextStyle: .subheadline) + descriptionLabel.font = UIFont.dw_font(forTextStyle: .footnote) + percentLabel.font = UIFont.dw_font(forTextStyle: .title1) + viewStateSeeingBlocks = false + + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(changeSeeBlocksStateAction(_:))) + roundedView.addGestureRecognizer(tapGestureRecognizer) + + model.networkStatusDidChange = { [weak self] _ in + self?.updateView() + } + + model.progressDidChange = { [weak self] progress in + self?.set(progress: Float(progress), animated: true) + } + + model.stateDidChage = { [weak self] _ in + self?.updateView() + } + } + + private func set(progress: Float, animated: Bool) { + percentLabel.text = String(format: "%.1f%%", progress * 100.0) + progressView.setProgress(progress, animated: animated) + + if viewStateSeeingBlocks && syncState == .syncing { + updateUIForViewStateSeeingBlocks() + } + } + + func updateUIForViewStateSeeingBlocks() { + // 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 { + descriptionLabel.text = String(format: NSLocalizedString("block #%d of %d", comment: ""), + chain.lastSyncBlockHeight, + chain.estimatedBlockHeight) + } + } + else { + descriptionLabel.text = NSLocalizedString("with Dash blockchain", comment: "") + } + } + } + + @objc + func changeSeeBlocksStateAction(_ sender: Any) { + viewStateSeeingBlocks.toggle() + updateUIForViewStateSeeingBlocks() + } + + @IBAction + func retryButtonAction(_ sender: Any) { + model.forceStartSyncingActivity() + + delegate?.syncViewRetryButtonAction(self) + } + + override func awakeFromNib() { + super.awakeFromNib() + + commonInit() + updateView() + } +} + +extension SyncView { + private func updateView() { + switch syncState { + case .syncing, .syncDone: + roundedView.backgroundColor = UIColor.dw_background() + percentLabel.isHidden = false + retryButton.isHidden = true + progressView.isHidden = false + titleLabel.text = NSLocalizedString("Syncing", comment: "") + updateUIForViewStateSeeingBlocks() + + case .syncFailed: + roundedView.backgroundColor = UIColor.dw_background() + percentLabel.isHidden = true + retryButton.tintColor = UIColor.dw_red() + retryButton.isHidden = false + progressView.isHidden = false + titleLabel.text = NSLocalizedString("Sync Failed", comment: "") + descriptionLabel.text = NSLocalizedString("Please try again", comment: "") + default: + break + } + + if hasNetwork { + titleLabel.textColor = UIColor.dw_secondaryText() + descriptionLabel.textColor = UIColor.dw_quaternaryText() + } else { + titleLabel.textColor = UIColor.dw_lightTitle() + descriptionLabel.textColor = UIColor.dw_lightTitle() + + roundedView.backgroundColor = UIColor.dw_red() + percentLabel.isHidden = true + retryButton.tintColor = UIColor.dw_background() + retryButton.isHidden = false + progressView.isHidden = true + titleLabel.text = NSLocalizedString("Unable to connect", comment: "") + descriptionLabel.text = NSLocalizedString("Check your connection", comment: "") + } + } +} diff --git a/DashWallet/Sources/UI/Home/Views/DWSyncView.xib b/DashWallet/Sources/UI/Home/Syncing Views/Sync View/SyncView.xib similarity index 94% rename from DashWallet/Sources/UI/Home/Views/DWSyncView.xib rename to DashWallet/Sources/UI/Home/Syncing Views/Sync View/SyncView.xib index 6c9c7111d..8939f06e9 100644 --- a/DashWallet/Sources/UI/Home/Views/DWSyncView.xib +++ b/DashWallet/Sources/UI/Home/Syncing Views/Sync View/SyncView.xib @@ -1,26 +1,16 @@ - + - + - - - - - - - - - - - + - + @@ -65,14 +55,14 @@ - + - @@ -133,13 +123,21 @@ + + + + + + + + - + diff --git a/DashWallet/Sources/UI/Home/SyncingAlert/DWSyncingAlertContentView.h b/DashWallet/Sources/UI/Home/SyncingAlert/DWSyncingAlertContentView.h deleted file mode 100644 index 2c08f2c83..000000000 --- a/DashWallet/Sources/UI/Home/SyncingAlert/DWSyncingAlertContentView.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2021 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -#import "DWSyncContainerProtocol.h" - -NS_ASSUME_NONNULL_BEGIN - -@class DWSyncingAlertContentView; - -@protocol DWSyncingAlertContentViewDelegate - -- (void)syncingAlertContentView:(DWSyncingAlertContentView *)view okButtonAction:(UIButton *)sender; - -@end - -@interface DWSyncingAlertContentView : KVOUIView - -@property (nullable, nonatomic, weak) id delegate; - -@property (nonatomic, strong) id model; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/SyncingAlert/DWSyncingAlertContentView.m b/DashWallet/Sources/UI/Home/SyncingAlert/DWSyncingAlertContentView.m deleted file mode 100644 index 18d720057..000000000 --- a/DashWallet/Sources/UI/Home/SyncingAlert/DWSyncingAlertContentView.m +++ /dev/null @@ -1,199 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2021 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWSyncingAlertContentView.h" - -#import "DWActionButton.h" -#import "DWEnvironment.h" -#import "DWUIKit.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface DWSyncingAlertContentView () - -@property (readonly, nonatomic, strong) UIImageView *syncingImageView; -@property (readonly, nonatomic, strong) UILabel *titleLabel; -@property (readonly, nonatomic, strong) UILabel *subtitleLabel; - -@end - -NS_ASSUME_NONNULL_END - -@implementation DWSyncingAlertContentView - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - self.backgroundColor = [UIColor dw_backgroundColor]; - - UIImageView *syncingImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"icon_syncing_large"]]; - syncingImageView.translatesAutoresizingMaskIntoConstraints = NO; - [self addSubview:syncingImageView]; - _syncingImageView = syncingImageView; - - UILabel *titleLabel = [[UILabel alloc] init]; - titleLabel.translatesAutoresizingMaskIntoConstraints = NO; - titleLabel.font = [UIFont dw_fontForTextStyle:UIFontTextStyleTitle3]; - titleLabel.textColor = [UIColor dw_darkTitleColor]; - titleLabel.adjustsFontForContentSizeCategory = YES; - titleLabel.textAlignment = NSTextAlignmentCenter; - titleLabel.numberOfLines = 0; - [self addSubview:titleLabel]; - _titleLabel = titleLabel; - - UILabel *subtitleLabel = [[UILabel alloc] init]; - subtitleLabel.translatesAutoresizingMaskIntoConstraints = NO; - subtitleLabel.font = [UIFont dw_fontForTextStyle:UIFontTextStyleCallout]; - subtitleLabel.textColor = [UIColor dw_secondaryTextColor]; - subtitleLabel.adjustsFontForContentSizeCategory = YES; - subtitleLabel.textAlignment = NSTextAlignmentCenter; - subtitleLabel.numberOfLines = 0; - [self addSubview:subtitleLabel]; - _subtitleLabel = subtitleLabel; - - DWActionButton *okButton = [[DWActionButton alloc] init]; - okButton.translatesAutoresizingMaskIntoConstraints = NO; - okButton.small = YES; - [okButton setTitle:NSLocalizedString(@"OK", nil) forState:UIControlStateNormal]; - [okButton addTarget:self action:@selector(okButtonAction:) forControlEvents:UIControlEventTouchUpInside]; - [self addSubview:okButton]; - - [syncingImageView setContentCompressionResistancePriority:UILayoutPriorityRequired - forAxis:UILayoutConstraintAxisVertical]; - [syncingImageView setContentCompressionResistancePriority:UILayoutPriorityRequired - forAxis:UILayoutConstraintAxisHorizontal]; - - [titleLabel setContentCompressionResistancePriority:UILayoutPriorityRequired - 1 - forAxis:UILayoutConstraintAxisVertical]; - [subtitleLabel setContentCompressionResistancePriority:UILayoutPriorityRequired - 2 - forAxis:UILayoutConstraintAxisVertical]; - - [NSLayoutConstraint activateConstraints:@[ - [syncingImageView.topAnchor constraintEqualToAnchor:self.topAnchor], - [syncingImageView.centerXAnchor constraintEqualToAnchor:self.centerXAnchor], - - [titleLabel.topAnchor constraintEqualToAnchor:syncingImageView.bottomAnchor - constant:16.0], - [titleLabel.leadingAnchor constraintEqualToAnchor:self.leadingAnchor], - [self.trailingAnchor constraintEqualToAnchor:titleLabel.trailingAnchor], - - [subtitleLabel.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor - constant:8.0], - [subtitleLabel.leadingAnchor constraintEqualToAnchor:self.leadingAnchor], - [self.trailingAnchor constraintEqualToAnchor:subtitleLabel.trailingAnchor], - - [okButton.topAnchor constraintEqualToAnchor:subtitleLabel.bottomAnchor - constant:38.0], - [okButton.centerXAnchor constraintEqualToAnchor:self.centerXAnchor], - [self.bottomAnchor constraintEqualToAnchor:okButton.bottomAnchor], - [okButton.heightAnchor constraintEqualToConstant:32.0], - [okButton.widthAnchor constraintEqualToConstant:110.0], - ]]; - - // KVO - - [self mvvm_observe:DW_KEYPATH(self, model.syncModel.state) - with:^(typeof(self) self, NSNumber *value) { - [self updateWithState:self.model.syncModel.state]; - }]; - - [self mvvm_observe:DW_KEYPATH(self, model.syncModel.progress) - with:^(typeof(self) self, NSNumber *value) { - const float progress = self.model.syncModel.progress; - self.titleLabel.text = [NSString stringWithFormat:@"%@ %0.1f%%", NSLocalizedString(@"Syncing", nil), - progress * 100.0]; - if (self.model.syncModel.state == DWSyncModelState_Syncing) { - [self updateWithState:self.model.syncModel.state]; - } - }]; - } - return self; -} - -- (void)okButtonAction:(UIButton *)sender { - [self.delegate syncingAlertContentView:self okButtonAction:sender]; -} - -- (void)updateWithState:(DWSyncModelState)syncState { - switch (syncState) { - case DWSyncModelState_Syncing: - case DWSyncModelState_SyncDone: { - DWEnvironment *environment = [DWEnvironment sharedInstance]; - DSChain *chain = environment.currentChain; - DSChainManager *chainManager = environment.currentChainManager; - // We give a 6 block window, just in case a new block comes in - BOOL atTheEndOfInitialTerminalBlocksAndSyncingMasternodeList = chain.lastTerminalBlockHeight >= chain.estimatedBlockHeight - 6 && chainManager.masternodeManager.masternodeListRetrievalQueueCount && - chainManager.syncPhase == DSChainSyncPhase_InitialTerminalBlocks; - BOOL atTheEndOfSyncBlocksAndSyncingMasternodeList = chain.lastSyncBlockHeight >= chain.estimatedBlockHeight - 6 && chainManager.masternodeManager.masternodeListRetrievalQueueCount && - chainManager.syncPhase == DSChainSyncPhase_Synced; - if (atTheEndOfInitialTerminalBlocksAndSyncingMasternodeList || atTheEndOfSyncBlocksAndSyncingMasternodeList) { - self.subtitleLabel.text = [NSString stringWithFormat:NSLocalizedString(@"masternode list #%d of %d", nil), - (int)(chainManager.masternodeManager.masternodeListRetrievalQueueMaxAmount - chainManager.masternodeManager.masternodeListRetrievalQueueCount), - (int)chainManager.masternodeManager.masternodeListRetrievalQueueMaxAmount]; - } - else { - if (chainManager.syncPhase == DSChainSyncPhase_InitialTerminalBlocks) { - - self.subtitleLabel.text = [NSString stringWithFormat:NSLocalizedString(@"header #%d of %d", nil), - chain.lastTerminalBlockHeight, - chain.estimatedBlockHeight]; - } - else { - self.subtitleLabel.text = [NSString stringWithFormat:NSLocalizedString(@"block #%d of %d", nil), - chain.lastSyncBlockHeight, - chain.estimatedBlockHeight]; - } - } - - break; - } - case DWSyncModelState_SyncFailed: - self.subtitleLabel.text = NSLocalizedString(@"Sync Failed", nil); - - break; - case DWSyncModelState_NoConnection: - self.subtitleLabel.text = NSLocalizedString(@"Unable to connect", nil); - - break; - } - - if (syncState == DWSyncModelState_Syncing) { - [self showAnimation]; - } - else { - [self hideAnimation]; - } -} - -- (void)showAnimation { - if ([self.syncingImageView.layer animationForKey:@"dw_rotationAnimation"]) { - return; - } - - CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; - rotationAnimation.fromValue = @0; - rotationAnimation.toValue = @(M_PI * 2.0); - rotationAnimation.duration = 1.75; - rotationAnimation.repeatCount = HUGE_VALF; - [self.syncingImageView.layer addAnimation:rotationAnimation forKey:@"dw_rotationAnimation"]; -} - -- (void)hideAnimation { - [self.syncingImageView.layer removeAllAnimations]; -} - -@end diff --git a/DashWallet/Sources/UI/Home/SyncingAlert/DWSyncingAlertViewController.h b/DashWallet/Sources/UI/Home/SyncingAlert/DWSyncingAlertViewController.h deleted file mode 100644 index ef73ac9da..000000000 --- a/DashWallet/Sources/UI/Home/SyncingAlert/DWSyncingAlertViewController.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2021 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -#import "DWSyncContainerProtocol.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface DWSyncingAlertViewController : UIViewController - -@property (nonatomic, strong) id model; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/SyncingAlert/DWSyncingAlertViewController.m b/DashWallet/Sources/UI/Home/SyncingAlert/DWSyncingAlertViewController.m deleted file mode 100644 index 727d60475..000000000 --- a/DashWallet/Sources/UI/Home/SyncingAlert/DWSyncingAlertViewController.m +++ /dev/null @@ -1,104 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2021 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWSyncingAlertViewController.h" - -#import "DWModalPopupTransition.h" -#import "DWSyncingAlertContentView.h" -#import "DWUIKit.h" - - -NS_ASSUME_NONNULL_BEGIN - -@interface DWSyncingAlertViewController () - -@property (nonatomic, strong) DWModalPopupTransition *modalTransition; -@property (null_resettable, nonatomic, strong) DWSyncingAlertContentView *childView; - -@end - -NS_ASSUME_NONNULL_END - -@implementation DWSyncingAlertViewController - -- (instancetype)init { - self = [super initWithNibName:nil bundle:nil]; - if (self) { - _modalTransition = [[DWModalPopupTransition alloc] init]; - - self.transitioningDelegate = self.modalTransition; - self.modalPresentationStyle = UIModalPresentationCustom; - } - return self; -} - -- (id)model { - return self.childView.model; -} - -- (void)setModel:(id)model { - self.childView.model = model; -} - -- (void)viewDidLoad { - [super viewDidLoad]; - - self.view.backgroundColor = [UIColor clearColor]; - - UIView *contentView = [[UIView alloc] init]; - contentView.translatesAutoresizingMaskIntoConstraints = NO; - contentView.backgroundColor = [UIColor dw_backgroundColor]; - contentView.layer.cornerRadius = 8.0; - contentView.layer.masksToBounds = YES; - [self.view addSubview:contentView]; - - [contentView addSubview:self.childView]; - - [NSLayoutConstraint activateConstraints:@[ - [contentView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor], - - [contentView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], - [contentView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor], - - [self.childView.topAnchor constraintEqualToAnchor:contentView.topAnchor - constant:32.0], - [self.childView.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor], - [self.childView.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor], - [contentView.bottomAnchor constraintEqualToAnchor:self.childView.bottomAnchor - constant:32.0], - ]]; -} - -#pragma mark - DWSyncingAlertContentViewDelegate - -- (void)syncingAlertContentView:(DWSyncingAlertContentView *)view okButtonAction:(UIButton *)sender { - [self dismissViewControllerAnimated:YES completion:nil]; -} - -#pragma mark - Private - -- (DWSyncingAlertContentView *)childView { - if (_childView == nil) { - DWSyncingAlertContentView *childView = [[DWSyncingAlertContentView alloc] init]; - childView.translatesAutoresizingMaskIntoConstraints = NO; - childView.delegate = self; - _childView = childView; - } - return _childView; -} - -@end diff --git a/DashWallet/Sources/UI/Home/Views/Cells/DWTxListEmptyTableViewCell.h b/DashWallet/Sources/UI/Home/Views/Cells/DWTxListEmptyTableViewCell.h deleted file mode 100644 index b3b41ff77..000000000 --- a/DashWallet/Sources/UI/Home/Views/Cells/DWTxListEmptyTableViewCell.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface DWTxListEmptyTableViewCell : UITableViewCell - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Views/Cells/DWTxListEmptyTableViewCell.m b/DashWallet/Sources/UI/Home/Views/Cells/DWTxListEmptyTableViewCell.m deleted file mode 100644 index b0535d96c..000000000 --- a/DashWallet/Sources/UI/Home/Views/Cells/DWTxListEmptyTableViewCell.m +++ /dev/null @@ -1,41 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWTxListEmptyTableViewCell.h" - -#import "DWUIKit.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface DWTxListEmptyTableViewCell () - -@property (strong, nonatomic) IBOutlet UILabel *placeholderLabel; - -@end - -@implementation DWTxListEmptyTableViewCell - -- (void)awakeFromNib { - [super awakeFromNib]; - - self.placeholderLabel.font = [UIFont dw_fontForTextStyle:UIFontTextStyleFootnote]; - self.placeholderLabel.text = NSLocalizedString(@"There are no transactions to display", nil); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Views/Cells/SyncingHeaderView.swift b/DashWallet/Sources/UI/Home/Views/Cells/SyncingHeaderView.swift new file mode 100644 index 000000000..28d688a7f --- /dev/null +++ b/DashWallet/Sources/UI/Home/Views/Cells/SyncingHeaderView.swift @@ -0,0 +1,158 @@ +// +// Created by PT +// Copyright © 2023 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +// MARK: - SyncingHeaderViewDelegate + +@objc(DWSyncingHeaderViewDelegate) +protocol SyncingHeaderViewDelegate: AnyObject { + func syncingHeaderView(_ view: SyncingHeaderView, filterButtonAction sender: UIButton) + func syncingHeaderView(_ view: SyncingHeaderView, syncingButtonAction sender: UIButton) +} + +// MARK: - SyncingHeaderView + +@objc(DWSyncingHeaderView) +final class SyncingHeaderView: UITableViewHeaderFooterView { + + @objc + weak var delegate: SyncingHeaderViewDelegate? + + @objc + var progress: Float = 0.0 { + didSet { + refreshView() + } + } + + @objc + var isSyncing = false { + didSet { + refreshView() + } + } + + private var syncingButton: UIButton! + + internal lazy var model: SyncModel = SyncModelImpl() + + override init(reuseIdentifier: String?) { + super.init(reuseIdentifier: reuseIdentifier) + + backgroundColor = UIColor.dw_secondaryBackground() + + let titleLabel = UILabel() + titleLabel.translatesAutoresizingMaskIntoConstraints = false + titleLabel.font = UIFont.dw_font(forTextStyle: .headline) + titleLabel.text = NSLocalizedString("History", comment: "") + titleLabel.textColor = UIColor.dw_darkTitle() + titleLabel.setContentHuggingPriority(.defaultHigh + 1, for: .horizontal) + addSubview(titleLabel) + + syncingButton = DWButton() + syncingButton.translatesAutoresizingMaskIntoConstraints = false + syncingButton.contentHorizontalAlignment = .right + syncingButton.setTitleColor(UIColor.dw_darkTitle(), for: .normal) + syncingButton.setContentHuggingPriority(.defaultHigh - 1, for: .horizontal) + syncingButton.setContentCompressionResistancePriority(.required - 1, for: .horizontal) + syncingButton.addTarget(self, action: #selector(syncingButtonAction(_:)), for: .touchUpInside) + addSubview(syncingButton) + + let filterButton = UIButton(type: .custom) + filterButton.translatesAutoresizingMaskIntoConstraints = false + filterButton.setImage(UIImage(named: "icon_filter_button"), for: .normal) + filterButton.addTarget(self, action: #selector(filterButtonAction(_:)), for: .touchUpInside) + addSubview(filterButton) + + let padding: CGFloat = 16.0 + NSLayoutConstraint.activate([ + titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: padding), + bottomAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: padding), + titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: padding), + + syncingButton.topAnchor.constraint(greaterThanOrEqualTo: topAnchor), + bottomAnchor.constraint(greaterThanOrEqualTo: syncingButton.bottomAnchor), + syncingButton.centerYAnchor.constraint(equalTo: centerYAnchor), + syncingButton.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: 8.0), + syncingButton.heightAnchor.constraint(equalToConstant: 44.0), + + filterButton.topAnchor.constraint(greaterThanOrEqualTo: topAnchor), + bottomAnchor.constraint(greaterThanOrEqualTo: filterButton.bottomAnchor), + filterButton.centerYAnchor.constraint(equalTo: centerYAnchor), + filterButton.leadingAnchor.constraint(equalTo: syncingButton.trailingAnchor), + trailingAnchor.constraint(equalTo: filterButton.trailingAnchor, constant: 10.0), + filterButton.heightAnchor.constraint(equalToConstant: 44.0), + filterButton.widthAnchor.constraint(equalToConstant: 44.0), + ]) + + model.networkStatusDidChange = { [weak self] _ in + } + + model.progressDidChange = { [weak self] progress in + self?.progress = Float(progress) + } + + model.stateDidChage = { [weak self] state in + self?.isSyncing = state == .syncing + } + + progress = Float(model.progress) + isSyncing = model.state == .syncing + + refreshView() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc + func filterButtonAction(_ sender: UIButton) { + delegate?.syncingHeaderView(self, filterButtonAction: sender) + } + + @objc + func syncingButtonAction(_ sender: UIButton) { + delegate?.syncingHeaderView(self, syncingButtonAction: sender) + } +} + +extension SyncingHeaderView { + private func refreshView() { + syncingButton.isHidden = !isSyncing + + guard isSyncing else { + return + } + + let string = NSMutableAttributedString() + + let syncingString = NSAttributedString(string: String(format: "%@ ", NSLocalizedString("Syncing", comment: "")), + attributes: [NSAttributedString.Key.font: UIFont.dw_font(forTextStyle: .body)]) + string.append(syncingString) + + if DWEnvironment.sharedInstance().currentChainManager.peerManager.connected || progress > 0 { + let percentString = String(format: "%0.1f%%", progress * 100.0) + let progressString = NSAttributedString(string: percentString, + attributes: [NSAttributedString.Key.font: UIFont.dw_font(forTextStyle: .headline)]) + string.append(progressString) + } + + syncingButton.setAttributedTitle(string, for: .normal) + } +} diff --git a/DashWallet/Sources/UI/Onboarding/Stubs/DWBalanceDisplayOptionsStub.m b/DashWallet/Sources/UI/Home/Views/Cells/TxListEmptyTableViewCell.swift similarity index 56% rename from DashWallet/Sources/UI/Onboarding/Stubs/DWBalanceDisplayOptionsStub.m rename to DashWallet/Sources/UI/Home/Views/Cells/TxListEmptyTableViewCell.swift index 122221534..e9daf1e57 100644 --- a/DashWallet/Sources/UI/Onboarding/Stubs/DWBalanceDisplayOptionsStub.m +++ b/DashWallet/Sources/UI/Home/Views/Cells/TxListEmptyTableViewCell.swift @@ -1,6 +1,6 @@ // -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. +// Created by PT +// Copyright © 2023 Dash Core Group. All rights reserved. // // Licensed under the MIT License (the "License"); // you may not use this file except in compliance with the License. @@ -15,25 +15,16 @@ // limitations under the License. // -#import "DWBalanceDisplayOptionsStub.h" +import UIKit -NS_ASSUME_NONNULL_BEGIN +class TxListEmptyTableViewCell: UITableViewCell { -@implementation DWBalanceDisplayOptionsStub + @IBOutlet var placeholderLabel: UILabel! -@synthesize balanceHidden = _balanceHidden; + override func awakeFromNib() { + super.awakeFromNib() -- (instancetype)init { - self = [super init]; - if (self) { - _balanceHidden = NO; + placeholderLabel?.font = UIFont.preferredFont(forTextStyle: .footnote) + placeholderLabel?.text = NSLocalizedString("There are no transactions to display", comment: "") } - return self; } - -- (void)hideBalanceIfNeeded { -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Views/Cells/DWTxListEmptyTableViewCell.xib b/DashWallet/Sources/UI/Home/Views/Cells/TxListEmptyTableViewCell.xib similarity index 87% rename from DashWallet/Sources/UI/Home/Views/Cells/DWTxListEmptyTableViewCell.xib rename to DashWallet/Sources/UI/Home/Views/Cells/TxListEmptyTableViewCell.xib index b7d4f67fa..a0e5b13e3 100644 --- a/DashWallet/Sources/UI/Home/Views/Cells/DWTxListEmptyTableViewCell.xib +++ b/DashWallet/Sources/UI/Home/Views/Cells/TxListEmptyTableViewCell.xib @@ -1,11 +1,9 @@ - - - - + + - + @@ -13,18 +11,18 @@ - + - + - + - + - + + diff --git a/DashWallet/Sources/UI/Home/Views/Cells/TxListTableViewCell.swift b/DashWallet/Sources/UI/Home/Views/Cells/TxListTableViewCell.swift index d082fe643..f6fe9f3ec 100644 --- a/DashWallet/Sources/UI/Home/Views/Cells/TxListTableViewCell.swift +++ b/DashWallet/Sources/UI/Home/Views/Cells/TxListTableViewCell.swift @@ -17,7 +17,6 @@ import UIKit -@objc(DWTxListTableViewCell) final class TxListTableViewCell: UITableViewCell { @IBOutlet var txItemView: TransactionItemView! @@ -30,6 +29,4 @@ final class TxListTableViewCell: UITableViewCell { dw_pressedAnimation(.light, pressed: highlighted) } - - override class var dw_reuseIdentifier: String { "DWTxListTableViewCell" } } diff --git a/DashWallet/Sources/UI/Home/Views/Cells/DWTxListTableViewCell.xib b/DashWallet/Sources/UI/Home/Views/Cells/TxListTableViewCell.xib similarity index 95% rename from DashWallet/Sources/UI/Home/Views/Cells/DWTxListTableViewCell.xib rename to DashWallet/Sources/UI/Home/Views/Cells/TxListTableViewCell.xib index d2617130e..c1ade7245 100644 --- a/DashWallet/Sources/UI/Home/Views/Cells/DWTxListTableViewCell.xib +++ b/DashWallet/Sources/UI/Home/Views/Cells/TxListTableViewCell.xib @@ -1,9 +1,9 @@ - + - + @@ -11,7 +11,7 @@ - + @@ -64,7 +64,7 @@ - + diff --git a/DashWallet/Sources/UI/Home/Views/DWDashPayProfileView.h b/DashWallet/Sources/UI/Home/Views/DWDashPayProfileView.h deleted file mode 100644 index 0f2533501..000000000 --- a/DashWallet/Sources/UI/Home/Views/DWDashPayProfileView.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2020 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface DWDashPayProfileView : UIControl - -@property (nullable, nonatomic, copy) NSString *username; -@property (nonatomic, assign) NSUInteger unreadCount; - -- (instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Views/DWDashPayProfileView.m b/DashWallet/Sources/UI/Home/Views/DWDashPayProfileView.m deleted file mode 100644 index d2e4824d9..000000000 --- a/DashWallet/Sources/UI/Home/Views/DWDashPayProfileView.m +++ /dev/null @@ -1,116 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2020 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWDashPayProfileView.h" - -#import "DWBadgeView.h" -#import "DWDPAvatarView.h" -#import "DWUIKit.h" - -NS_ASSUME_NONNULL_BEGIN - -static CGSize const AVATAR_SIZE = {72.0, 72.0}; - -@interface DWDashPayProfileView () - -@property (readonly, nonatomic, strong) UIView *contentView; -@property (readonly, nonatomic, strong) DWDPAvatarView *avatarView; -@property (readonly, nonatomic, strong) UIImageView *bellImageView; -@property (readonly, nonatomic, strong) DWBadgeView *badgeView; - -@end - -NS_ASSUME_NONNULL_END - -@implementation DWDashPayProfileView - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - self.backgroundColor = [UIColor dw_dashBlueColor]; - - UIView *contentView = [[UIView alloc] init]; - contentView.translatesAutoresizingMaskIntoConstraints = NO; - contentView.backgroundColor = self.backgroundColor; - contentView.userInteractionEnabled = NO; - [self addSubview:contentView]; - _contentView = contentView; - - DWDPAvatarView *avatarView = [[DWDPAvatarView alloc] init]; - avatarView.translatesAutoresizingMaskIntoConstraints = NO; - avatarView.backgroundMode = DWDPAvatarBackgroundMode_Random; - avatarView.userInteractionEnabled = NO; - [contentView addSubview:avatarView]; - _avatarView = avatarView; - - UIImageView *bellImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"icon_bell"]]; - bellImageView.translatesAutoresizingMaskIntoConstraints = NO; - bellImageView.userInteractionEnabled = NO; - [contentView addSubview:bellImageView]; - _bellImageView = bellImageView; - - DWBadgeView *badgeView = [[DWBadgeView alloc] initWithFrame:CGRectZero]; - badgeView.translatesAutoresizingMaskIntoConstraints = NO; - badgeView.hidden = YES; - [contentView addSubview:badgeView]; - _badgeView = badgeView; - - [NSLayoutConstraint activateConstraints:@[ - [contentView.topAnchor constraintEqualToAnchor:self.topAnchor], - [contentView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor], - [contentView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor], - [contentView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor], - - [avatarView.topAnchor constraintEqualToAnchor:contentView.topAnchor], - [avatarView.bottomAnchor constraintEqualToAnchor:contentView.bottomAnchor], - [avatarView.centerXAnchor constraintEqualToAnchor:contentView.centerXAnchor], - [avatarView.widthAnchor constraintEqualToConstant:AVATAR_SIZE.width], - [avatarView.heightAnchor constraintEqualToConstant:AVATAR_SIZE.height], - - [bellImageView.trailingAnchor constraintEqualToAnchor:avatarView.trailingAnchor], - [bellImageView.bottomAnchor constraintEqualToAnchor:contentView.bottomAnchor], - - [badgeView.centerXAnchor constraintEqualToAnchor:avatarView.trailingAnchor], - [badgeView.bottomAnchor constraintEqualToAnchor:contentView.bottomAnchor], - ]]; - } - return self; -} - - -- (void)setHighlighted:(BOOL)highlighted { - [super setHighlighted:highlighted]; - - [self.contentView dw_pressedAnimation:DWPressedAnimationStrength_Medium pressed:highlighted]; -} - -- (void)setUsername:(NSString *)username { - _username = username; - - self.avatarView.username = username; -} - -- (void)setUnreadCount:(NSUInteger)unreadCount { - _unreadCount = unreadCount; - - self.badgeView.text = [NSString stringWithFormat:@"%ld", unreadCount]; - - self.bellImageView.hidden = unreadCount > 0; - self.badgeView.hidden = unreadCount == 0; -} - -@end diff --git a/DashWallet/Sources/UI/Home/Views/DWHomeHeaderView.h b/DashWallet/Sources/UI/Home/Views/DWHomeHeaderView.h deleted file mode 100644 index 15fd59409..000000000 --- a/DashWallet/Sources/UI/Home/Views/DWHomeHeaderView.h +++ /dev/null @@ -1,48 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -#import "DWHomeProtocol.h" - - -NS_ASSUME_NONNULL_BEGIN - -@class DWHomeHeaderView; -@protocol DWShortcutsActionDelegate; - -@protocol DWHomeHeaderViewDelegate - -- (void)homeHeaderViewDidUpdateContents:(DWHomeHeaderView *)view; -- (void)homeHeaderView:(DWHomeHeaderView *)view profileButtonAction:(UIControl *)sender; - -@end - -@interface DWHomeHeaderView : KVOUIView - -@property (nullable, nonatomic, strong) id model; -@property (nullable, nonatomic, weak) id delegate; -@property (nullable, nonatomic, weak) id shortcutsDelegate; - -- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE; -- (instancetype)init NS_UNAVAILABLE; - -- (void)parentScrollViewDidScroll:(UIScrollView *)scrollView; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Views/DWHomeHeaderView.m b/DashWallet/Sources/UI/Home/Views/DWHomeHeaderView.m deleted file mode 100644 index 15e8b52ea..000000000 --- a/DashWallet/Sources/UI/Home/Views/DWHomeHeaderView.m +++ /dev/null @@ -1,206 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWHomeHeaderView.h" - -#import "DWDPRegistrationStatus.h" -#import "DWDashPayProfileView.h" -#import "DWSyncView.h" -#import "dashwallet-Swift.h" - -NS_ASSUME_NONNULL_BEGIN - -static CGSize const AVATAR_SIZE = {72.0, 72.0}; - -@interface DWHomeHeaderView () - -@property (readonly, nonatomic, strong) DWDashPayProfileView *profileView; -@property (readonly, nonatomic, strong) DWHomeBalanceView *balanceView; -@property (readonly, nonatomic, strong) DWSyncView *syncView; -@property (readonly, nonatomic, strong) ShortcutsView *shortcutsView; -@property (readonly, nonatomic, strong) UIStackView *stackView; - -@end - -@implementation DWHomeHeaderView - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - DWDashPayProfileView *profileView = [[DWDashPayProfileView alloc] initWithFrame:CGRectZero]; - profileView.translatesAutoresizingMaskIntoConstraints = NO; - [profileView addTarget:self action:@selector(profileViewAction:) forControlEvents:UIControlEventTouchUpInside]; - _profileView = profileView; - - DWHomeBalanceView *balanceView = [[DWHomeBalanceView alloc] initWithFrame:CGRectZero]; - balanceView.delegate = self; - _balanceView = balanceView; - - DWSyncView *syncView = [[DWSyncView alloc] initWithFrame:CGRectZero]; - syncView.delegate = self; - _syncView = syncView; - - ShortcutsView *shortcutsView = [[ShortcutsView alloc] initWithFrame:CGRectZero]; - shortcutsView.translatesAutoresizingMaskIntoConstraints = NO; - _shortcutsView = shortcutsView; - - NSArray *views = @[ profileView, balanceView, shortcutsView, syncView ]; - UIStackView *stackView = [[UIStackView alloc] initWithArrangedSubviews:views]; - stackView.translatesAutoresizingMaskIntoConstraints = NO; - stackView.axis = UILayoutConstraintAxisVertical; - [self addSubview:stackView]; - _stackView = stackView; - - [NSLayoutConstraint activateConstraints:@[ - [stackView.topAnchor constraintEqualToAnchor:self.topAnchor], - [stackView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor], - [stackView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor], - [stackView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor], - ]]; - - // KVO - - [self mvvm_observe:DW_KEYPATH(self, model.balanceModel) - with:^(typeof(self) self, id value) { - [self.balanceView reloadData]; - }]; - - [self mvvm_observe:DW_KEYPATH(self, model.balanceDisplayOptions.balanceHidden) - with:^(typeof(self) self, NSNumber *value) { - [self.balanceView hideBalance:self.model.balanceDisplayOptions.balanceHidden]; - }]; - - [self mvvm_observe:DW_KEYPATH(self, model.syncModel.state) - with:^(typeof(self) self, NSNumber *value) { - if (!value) { - return; - } - - const DWSyncModelState state = self.model.syncModel.state; - - self.balanceView.state = state == DWSyncModelState_Syncing ? DWHomeBalanceViewState_Syncing : DWHomeBalanceViewState_Default; - - [self.syncView setSyncState:state]; - - if (state == DWSyncModelState_SyncFailed || state == DWSyncModelState_NoConnection) { - [self showSyncView]; - } - else { - [self hideSyncView]; - } - }]; - - [self mvvm_observe:DW_KEYPATH(self, model.dashPayModel.registrationStatus) - with:^(typeof(self) self, id value) { - [self updateProfileView]; - }]; - - [self mvvm_observe:DW_KEYPATH(self, model.dashPayModel.username) - with:^(typeof(self) self, id value) { - [self updateProfileView]; - }]; - - [self mvvm_observe:DW_KEYPATH(self, model.dashPayModel.unreadNotificationsCount) - with:^(typeof(self) self, id value) { - self.profileView.unreadCount = self.model.dashPayModel.unreadNotificationsCount; - }]; - } - return self; -} - -- (void)setModel:(nullable id)model { - _model = model; - - self.shortcutsView.model = model.shortcutsModel; - [self updateProfileView]; - - self.balanceView.dataSource = model; -} - -- (nullable id)shortcutsDelegate { - return self.shortcutsView.actionDelegate; -} - -- (void)setShortcutsDelegate:(nullable id)shortcutsDelegate { - self.shortcutsView.actionDelegate = shortcutsDelegate; -} - -- (void)parentScrollViewDidScroll:(UIScrollView *)scrollView { -} - -#pragma mark - DWBalanceViewDelegate - -- (void)balanceView:(DWHomeBalanceView *)view balanceLongPressAction:(UIControl *)sender { - DWShortcutAction *action = [DWShortcutAction actionWithType:DWShortcutActionTypeLocalCurrency]; - [self.shortcutsDelegate shortcutsView:self.balanceView didSelectAction:action sender:sender]; -} - -- (void)balanceViewDidToggleBalanceVisibility:(DWHomeBalanceView *)view { - id balanceDisplayOptions = self.model.balanceDisplayOptions; - balanceDisplayOptions.balanceHidden = !balanceDisplayOptions.balanceHidden; -} - - -#pragma mark - DWShortcutsViewDelegate - -- (void)shortcutsViewDidUpdateContentSize:(ShortcutsView *)view { - [self.delegate homeHeaderViewDidUpdateContents:self]; -} - -#pragma mark - DWSyncViewDelegate - -- (void)syncViewRetryButtonAction:(DWSyncView *)view { - [self.model retrySyncing]; -} - -#pragma mark - Private - -- (void)profileViewAction:(UIControl *)sender { - [self.delegate homeHeaderView:self profileButtonAction:sender]; -} - -- (void)updateProfileView { - DWDPRegistrationStatus *status = self.model.dashPayModel.registrationStatus; - const BOOL completed = self.model.dashPayModel.registrationCompleted; - if (status.state == DWDPRegistrationState_Done || completed) { - self.profileView.username = self.model.dashPayModel.username; - self.profileView.hidden = NO; - } - else { - self.profileView.hidden = YES; - } - [self.delegate homeHeaderViewDidUpdateContents:self]; -} - -- (void)hideSyncView { - self.syncView.hidden = YES; - - [self.delegate homeHeaderViewDidUpdateContents:self]; -} - -- (void)showSyncView { - self.syncView.hidden = NO; - - [self.delegate homeHeaderViewDidUpdateContents:self]; -} - - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Views/DWHomeView.h b/DashWallet/Sources/UI/Home/Views/DWHomeView.h deleted file mode 100644 index 5a2cbfefd..000000000 --- a/DashWallet/Sources/UI/Home/Views/DWHomeView.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -#import "DWHomeProtocol.h" -#import "dashwallet-Swift.h" - -NS_ASSUME_NONNULL_BEGIN - -@class DWHomeView; -@class DSTransaction; - -@protocol DWHomeViewDelegate - -- (void)homeView:(DWHomeView *)homeView showTxFilter:(UIView *)sender; -- (void)homeView:(DWHomeView *)homeView showSyncingStatus:(UIView *)sender; -- (void)homeView:(DWHomeView *)homeView profileButtonAction:(UIControl *)sender; -- (void)homeView:(DWHomeView *)homeView didSelectTransaction:(DSTransaction *)transaction; -- (void)homeViewShowDashPayRegistrationFlow:(DWHomeView *)homeView; -- (void)homeView:(DWHomeView *)homeView showReclassifyYourTransactionsFlowWithTransaction:(DSTransaction *)transaction; -- (void)homeView:(DWHomeView *)homeView showCrowdNodeTxs:(NSArray *)transactions; - -@end - -@interface DWHomeView : KVOUIView - -@property (nonatomic, strong) id model; -@property (nullable, nonatomic, weak) id delegate; -@property (nullable, nonatomic, weak) id shortcutsDelegate; - -- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE; -- (instancetype)init NS_UNAVAILABLE; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Views/DWHomeView.m b/DashWallet/Sources/UI/Home/Views/DWHomeView.m deleted file mode 100644 index fe3386375..000000000 --- a/DashWallet/Sources/UI/Home/Views/DWHomeView.m +++ /dev/null @@ -1,262 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWHomeView.h" - -#import "DWDPRegistrationDoneTableViewCell.h" -#import "DWDPRegistrationErrorTableViewCell.h" -#import "DWDPRegistrationStatus.h" -#import "DWDPRegistrationStatusTableViewCell.h" -#import "DWDashPayProtocol.h" -#import "DWHomeHeaderView.h" -#import "DWSharedUIConstants.h" -#import "DWSyncingHeaderView.h" -#import "DWTxListEmptyTableViewCell.h" -#import "DWUIKit.h" -#import "dashwallet-Swift.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface DWHomeView () - -@property (readonly, nonatomic, strong) DWHomeHeaderView *headerView; -@property (readonly, nonatomic, strong) UIView *topOverscrollView; -@property (readonly, nonatomic, strong) UITableView *tableView; - -@property (nullable, nonatomic, weak) DWSyncingHeaderView *syncingHeaderView; - -// strong ref to current datasource to make sure it always exists while tableView uses it -@property (nonatomic, strong) DWTransactionListDataSource *currentDataSource; - -@end - -@implementation DWHomeView - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - self.backgroundColor = [UIColor dw_secondaryBackgroundColor]; - - DWHomeHeaderView *headerView = [[DWHomeHeaderView alloc] initWithFrame:CGRectZero]; - headerView.delegate = self; - _headerView = headerView; - - UIView *topOverscrollView = [[UIView alloc] initWithFrame:CGRectZero]; - topOverscrollView.backgroundColor = [UIColor dw_dashNavigationBlueColor]; - _topOverscrollView = topOverscrollView; - - UITableView *tableView = [[UITableView alloc] initWithFrame:self.bounds style:UITableViewStylePlain]; - tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - tableView.tableHeaderView = headerView; - tableView.backgroundColor = [UIColor dw_secondaryBackgroundColor]; - tableView.dataSource = self; - tableView.delegate = self; - tableView.rowHeight = UITableViewAutomaticDimension; - tableView.estimatedRowHeight = 74.0; - tableView.sectionHeaderHeight = UITableViewAutomaticDimension; - tableView.estimatedSectionHeaderHeight = 64.0; - tableView.separatorStyle = UITableViewCellSeparatorStyleNone; - tableView.contentInset = UIEdgeInsetsMake(0.0, 0.0, DW_TABBAR_NOTCH, 0.0); - [tableView addSubview:topOverscrollView]; - [self addSubview:tableView]; - _tableView = tableView; - - NSArray *cellIds = @[ - DWTxListEmptyTableViewCell.dw_reuseIdentifier, - DWTxListTableViewCell.dw_reuseIdentifier, - DWDPRegistrationStatusTableViewCell.dw_reuseIdentifier, - DWDPRegistrationErrorTableViewCell.dw_reuseIdentifier, - DWDPRegistrationDoneTableViewCell.dw_reuseIdentifier, - ]; - for (NSString *cellId in cellIds) { - UINib *nib = [UINib nibWithNibName:cellId bundle:nil]; - NSParameterAssert(nib); - [tableView registerNib:nib forCellReuseIdentifier:cellId]; - } - - - UINib *nib = [UINib nibWithNibName:@"CNCreateAccountCell" bundle:nil]; - [tableView registerNib:nib forCellReuseIdentifier:CNCreateAccountCell.dw_reuseIdentifier]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(setNeedsLayout) - name:UIContentSizeCategoryDidChangeNotification - object:nil]; - - [self mvvm_observe:DW_KEYPATH(self, model.syncModel.state) - with:^(typeof(self) self, NSNumber *value) { - if (!value) { - return; - } - - [self.syncingHeaderView setSyncState:self.model.syncModel.state]; - }]; - - [self mvvm_observe:DW_KEYPATH(self, model.syncModel.progress) - with:^(typeof(self) self, NSNumber *value) { - if (!value) { - return; - } - - [self.syncingHeaderView setProgress:self.model.syncModel.progress]; - }]; - } - return self; -} - -- (void)setModel:(id)model { - NSParameterAssert(model); - _model = model; - model.updatesObserver = self; - - self.headerView.model = model; -} - -- (nullable id)shortcutsDelegate { - return self.headerView.shortcutsDelegate; -} - -- (void)setShortcutsDelegate:(nullable id)shortcutsDelegate { - self.headerView.shortcutsDelegate = shortcutsDelegate; -} - -- (void)layoutSubviews { - [super layoutSubviews]; - - CGSize size = self.bounds.size; - self.topOverscrollView.frame = CGRectMake(0.0, -size.height, size.width, size.height); - - UIView *tableHeaderView = self.tableView.tableHeaderView; - if (tableHeaderView) { - CGSize headerSize = [tableHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; - if (CGRectGetHeight(tableHeaderView.frame) != headerSize.height) { - tableHeaderView.frame = CGRectMake(0.0, 0.0, headerSize.width, headerSize.height); - self.tableView.tableHeaderView = tableHeaderView; - } - } -} - -#pragma mark - DWHomeModelUpdatesObserver - -- (void)homeModel:(id)model didUpdateDataSource:(DWTransactionListDataSource *)dataSource shouldAnimate:(BOOL)shouldAnimate { - self.currentDataSource = dataSource; - dataSource.retryDelegate = self; - - if (dataSource.isEmpty) { - self.tableView.dataSource = self; - [self.tableView reloadData]; - } - else { - self.tableView.dataSource = dataSource; - [self.tableView reloadData]; - } -} - -- (void)homeModel:(id)model didReceiveNewIncomingTransaction:(DSTransaction *)transaction { - [self.delegate homeView:self showReclassifyYourTransactionsFlowWithTransaction:transaction]; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return 1; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - NSString *cellId = DWTxListEmptyTableViewCell.dw_reuseIdentifier; - DWTxListEmptyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId - forIndexPath:indexPath]; - return cell; -} - -#pragma mark - UITableViewDelegate - -- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { - DWSyncingHeaderView *headerView = [[DWSyncingHeaderView alloc] initWithFrame:CGRectZero]; - headerView.delegate = self; - [headerView setSyncState:self.model.syncModel.state]; - [headerView setProgress:self.model.syncModel.progress]; - self.syncingHeaderView = headerView; - return headerView; -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - [tableView deselectRowAtIndexPath:indexPath animated:YES]; - - if (self.currentDataSource.isEmpty) { - return; - } - - DWTransactionListDataItemType type = [self.currentDataSource itemTypeBy:indexPath]; - - if (type == DWTransactionListDataItemTypeCrowdnode) { - [self.delegate homeView:self showCrowdNodeTxs:[self.currentDataSource crowdnodeTxs]]; - return; - } - - DSTransaction *transaction = [self.currentDataSource transactionForIndexPath:indexPath]; - if (transaction) { - [self.delegate homeView:self didSelectTransaction:transaction]; - } - else { // registration status cell - [self.delegate homeViewShowDashPayRegistrationFlow:self]; - } -} - -#pragma mark - UIScrollViewDelegate - -- (void)scrollViewDidScroll:(UIScrollView *)scrollView { - [self.headerView parentScrollViewDidScroll:scrollView]; -} - -#pragma mark - DWSyncingHeaderViewDelegate - -- (void)syncingHeaderView:(DWSyncingHeaderView *)view syncingButtonAction:(UIButton *)sender { - [self.delegate homeView:self showSyncingStatus:sender]; -} - -- (void)syncingHeaderView:(DWSyncingHeaderView *)view filterButtonAction:(UIButton *)sender { - [self.delegate homeView:self showTxFilter:sender]; -} - -#pragma mark - DWHomeHeaderViewDelegate - -- (void)homeHeaderViewDidUpdateContents:(DWHomeHeaderView *)view { - [self setNeedsLayout]; -} - -- (void)homeHeaderView:(DWHomeHeaderView *)view profileButtonAction:(UIControl *)sender { - [self.delegate homeView:self profileButtonAction:sender]; -} - -#pragma mark - DWDPRegistrationErrorRetryDelegate - -- (void)registrationErrorRetryAction { - if ([self.model.dashPayModel canRetry]) { - [self.model.dashPayModel retry]; - } - else { - [self.delegate homeViewShowDashPayRegistrationFlow:self]; - } -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Views/DWSyncView.h b/DashWallet/Sources/UI/Home/Views/DWSyncView.h deleted file mode 100644 index f2106946f..000000000 --- a/DashWallet/Sources/UI/Home/Views/DWSyncView.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -#import "DWSyncModel.h" - -NS_ASSUME_NONNULL_BEGIN - -@class DWSyncView; - -@protocol DWSyncViewDelegate - -- (void)syncViewRetryButtonAction:(DWSyncView *)view; - -@end - -@interface DWSyncView : UIView - -@property (nullable, nonatomic, weak) id delegate; - -- (void)setSyncState:(DWSyncModelState)state; -- (void)setProgress:(float)progress animated:(BOOL)animated; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Views/DWSyncView.m b/DashWallet/Sources/UI/Home/Views/DWSyncView.m deleted file mode 100644 index ffd0581e5..000000000 --- a/DashWallet/Sources/UI/Home/Views/DWSyncView.m +++ /dev/null @@ -1,181 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWSyncView.h" - -#import "DWEnvironment.h" -#import "DWProgressView.h" -#import "DWUIKit.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface DWSyncView () - -@property (weak, nonatomic) IBOutlet UIView *contentView; -@property (strong, nonatomic) IBOutlet UIView *roundedView; -@property (strong, nonatomic) IBOutlet UILabel *titleLabel; -@property (strong, nonatomic) IBOutlet UILabel *descriptionLabel; -@property (strong, nonatomic) IBOutlet UILabel *percentLabel; -@property (strong, nonatomic) IBOutlet UIButton *retryButton; -@property (strong, nonatomic) IBOutlet DWProgressView *progressView; -@property (assign, nonatomic) BOOL viewStateSeeingBlocks; -@property (assign, nonatomic) DWSyncModelState syncState; - -@end - -@implementation DWSyncView - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - [self commonInit]; - } - return self; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self) { - [self commonInit]; - } - return self; -} - -- (void)commonInit { - [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil]; - [self addSubview:self.contentView]; - self.contentView.translatesAutoresizingMaskIntoConstraints = NO; - [NSLayoutConstraint activateConstraints:@[ - [self.contentView.topAnchor constraintEqualToAnchor:self.topAnchor], - [self.contentView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor], - [self.contentView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor], - [self.contentView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor], - [self.contentView.widthAnchor constraintEqualToAnchor:self.widthAnchor], - ]]; - - self.backgroundColor = [UIColor dw_secondaryBackgroundColor]; - - self.titleLabel.font = [UIFont dw_fontForTextStyle:UIFontTextStyleSubheadline]; - self.descriptionLabel.font = [UIFont dw_fontForTextStyle:UIFontTextStyleFootnote]; - self.percentLabel.font = [UIFont dw_fontForTextStyle:UIFontTextStyleTitle1]; - self.viewStateSeeingBlocks = NO; - - UITapGestureRecognizer *tapGestureRecognizer = - [[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(changeSeeBlocksStateAction:)]; - [self.roundedView addGestureRecognizer:tapGestureRecognizer]; -} - -- (void)setSyncState:(DWSyncModelState)state { - _syncState = state; - if (state == DWSyncModelState_NoConnection) { - self.titleLabel.textColor = [UIColor dw_lightTitleColor]; - self.descriptionLabel.textColor = [UIColor dw_lightTitleColor]; - } - else { - self.titleLabel.textColor = [UIColor dw_secondaryTextColor]; - self.descriptionLabel.textColor = [UIColor dw_quaternaryTextColor]; - } - - switch (state) { - case DWSyncModelState_Syncing: - case DWSyncModelState_SyncDone: { - self.roundedView.backgroundColor = [UIColor dw_backgroundColor]; - self.percentLabel.hidden = NO; - self.retryButton.hidden = YES; - self.progressView.hidden = NO; - self.titleLabel.text = NSLocalizedString(@"Syncing", nil); - [self updateUIForViewStateSeeingBlocks]; - break; - } - case DWSyncModelState_SyncFailed: { - self.roundedView.backgroundColor = [UIColor dw_backgroundColor]; - self.percentLabel.hidden = YES; - self.retryButton.tintColor = [UIColor dw_redColor]; - self.retryButton.hidden = NO; - self.progressView.hidden = NO; - self.titleLabel.text = NSLocalizedString(@"Sync Failed", nil); - self.descriptionLabel.text = NSLocalizedString(@"Please try again", nil); - - break; - } - case DWSyncModelState_NoConnection: { - self.roundedView.backgroundColor = [UIColor dw_redColor]; - self.percentLabel.hidden = YES; - self.retryButton.tintColor = [UIColor dw_backgroundColor]; - self.retryButton.hidden = NO; - self.progressView.hidden = YES; - self.titleLabel.text = NSLocalizedString(@"Unable to connect", nil); - self.descriptionLabel.text = NSLocalizedString(@"Check your connection", nil); - - break; - } - } -} - -- (void)setProgress:(float)progress animated:(BOOL)animated { - self.percentLabel.text = [NSString stringWithFormat:@"%0.1f%%", progress * 100.0]; - [self.progressView setProgress:progress animated:animated]; - if (self.viewStateSeeingBlocks && self.syncState == DWSyncModelState_Syncing) { - [self updateUIForViewStateSeeingBlocks]; - } -} - -- (void)updateUIForViewStateSeeingBlocks { - if (self.syncState == DWSyncModelState_Syncing || self.syncState == DWSyncModelState_SyncDone) { - if (self.viewStateSeeingBlocks) { - DWEnvironment *environment = [DWEnvironment sharedInstance]; - DSChain *chain = environment.currentChain; - DSChainManager *chainManager = environment.currentChainManager; - if (chainManager.syncPhase == DSChainSyncPhase_InitialTerminalBlocks) { - if (chain.lastTerminalBlockHeight >= chain.estimatedBlockHeight && chainManager.masternodeManager.masternodeListRetrievalQueueCount) { - self.descriptionLabel.text = [NSString stringWithFormat:NSLocalizedString(@"masternode list #%d of %d", nil), - (int)(chainManager.masternodeManager.masternodeListRetrievalQueueMaxAmount - chainManager.masternodeManager.masternodeListRetrievalQueueCount), - (int)chainManager.masternodeManager.masternodeListRetrievalQueueMaxAmount]; - } - else { - self.descriptionLabel.text = [NSString stringWithFormat:NSLocalizedString(@"header #%d of %d", nil), - chain.lastTerminalBlockHeight, - chain.estimatedBlockHeight]; - } - } - else { - self.descriptionLabel.text = [NSString stringWithFormat:NSLocalizedString(@"block #%d of %d", nil), - chain.lastSyncBlockHeight, - chain.estimatedBlockHeight]; - } - } - else { - self.descriptionLabel.text = NSLocalizedString(@"with Dash blockchain", nil); - } - } -} - -#pragma mark - Actions - -- (void)changeSeeBlocksStateAction:(id)sender { - self.viewStateSeeingBlocks = !self.viewStateSeeingBlocks; - [self updateUIForViewStateSeeingBlocks]; -} - -- (IBAction)retryButtonAction:(id)sender { - [self.delegate syncViewRetryButtonAction:self]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Views/DWSyncingHeaderView.h b/DashWallet/Sources/UI/Home/Views/DWSyncingHeaderView.h deleted file mode 100644 index 83653fc94..000000000 --- a/DashWallet/Sources/UI/Home/Views/DWSyncingHeaderView.h +++ /dev/null @@ -1,43 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2021 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -#import "DWSyncModel.h" - -NS_ASSUME_NONNULL_BEGIN - -@class DWSyncingHeaderView; - -@protocol DWSyncingHeaderViewDelegate - -- (void)syncingHeaderView:(DWSyncingHeaderView *)view filterButtonAction:(UIButton *)sender; -- (void)syncingHeaderView:(DWSyncingHeaderView *)view syncingButtonAction:(UIButton *)sender; - -@end - -@interface DWSyncingHeaderView : UIView - -@property (nullable, nonatomic, weak) id delegate; - -@property (assign, nonatomic) DWSyncModelState syncState; - -- (void)setProgress:(float)progress; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Views/DWSyncingHeaderView.m b/DashWallet/Sources/UI/Home/Views/DWSyncingHeaderView.m deleted file mode 100644 index d561cbb2c..000000000 --- a/DashWallet/Sources/UI/Home/Views/DWSyncingHeaderView.m +++ /dev/null @@ -1,131 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2021 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWSyncingHeaderView.h" - -#import "DWButton.h" -#import "DWUIKit.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface DWSyncingHeaderView () - -@property (strong, nonatomic) UIButton *syncingButton; - -@end - -NS_ASSUME_NONNULL_END - -@implementation DWSyncingHeaderView - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - self.backgroundColor = [UIColor dw_secondaryBackgroundColor]; - - UILabel *titleLabel = [[UILabel alloc] init]; - titleLabel.translatesAutoresizingMaskIntoConstraints = NO; - titleLabel.font = [UIFont dw_fontForTextStyle:UIFontTextStyleHeadline]; - titleLabel.text = NSLocalizedString(@"History", nil); - titleLabel.textColor = [UIColor dw_darkTitleColor]; - [titleLabel setContentHuggingPriority:UILayoutPriorityDefaultHigh + 1 - forAxis:UILayoutConstraintAxisHorizontal]; - [self addSubview:titleLabel]; - - DWButton *syncingButton = [[DWButton alloc] init]; - syncingButton.translatesAutoresizingMaskIntoConstraints = NO; - syncingButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight; - [syncingButton setTitleColor:[UIColor dw_darkTitleColor] forState:UIControlStateNormal]; - [syncingButton setContentHuggingPriority:UILayoutPriorityDefaultHigh - 1 - forAxis:UILayoutConstraintAxisHorizontal]; - [syncingButton setContentCompressionResistancePriority:UILayoutPriorityRequired - 1 - forAxis:UILayoutConstraintAxisHorizontal]; - [syncingButton addTarget:self action:@selector(syncingButtonAction:) forControlEvents:UIControlEventTouchUpInside]; - [self addSubview:syncingButton]; - _syncingButton = syncingButton; - - UIButton *filterButton = [UIButton buttonWithType:UIButtonTypeCustom]; - filterButton.translatesAutoresizingMaskIntoConstraints = NO; - [filterButton setImage:[UIImage imageNamed:@"icon_filter_button"] forState:UIControlStateNormal]; - [filterButton addTarget:self action:@selector(filterButtonAction:) forControlEvents:UIControlEventTouchUpInside]; - [self addSubview:filterButton]; - - - const CGFloat padding = 16.0; - [NSLayoutConstraint activateConstraints:@[ - [titleLabel.topAnchor constraintEqualToAnchor:self.topAnchor - constant:padding], - [self.bottomAnchor constraintEqualToAnchor:titleLabel.bottomAnchor - constant:padding], - [titleLabel.leadingAnchor constraintEqualToAnchor:self.leadingAnchor - constant:padding], - - [syncingButton.topAnchor constraintGreaterThanOrEqualToAnchor:self.topAnchor], - [self.bottomAnchor constraintGreaterThanOrEqualToAnchor:syncingButton.bottomAnchor], - [syncingButton.centerYAnchor constraintEqualToAnchor:self.centerYAnchor], - [syncingButton.leadingAnchor constraintEqualToAnchor:titleLabel.trailingAnchor - constant:8.0], - [syncingButton.heightAnchor constraintEqualToConstant:44.0], - - [filterButton.topAnchor constraintGreaterThanOrEqualToAnchor:self.topAnchor], - [self.bottomAnchor constraintGreaterThanOrEqualToAnchor:filterButton.bottomAnchor], - [filterButton.centerYAnchor constraintEqualToAnchor:self.centerYAnchor], - [filterButton.leadingAnchor constraintEqualToAnchor:syncingButton.trailingAnchor], - [self.trailingAnchor constraintEqualToAnchor:filterButton.trailingAnchor - constant:10.0], - [filterButton.heightAnchor constraintEqualToConstant:44.0], - [filterButton.widthAnchor constraintEqualToConstant:44.0], - ]]; - } - return self; -} - -- (void)setProgress:(float)progress { - NSString *percentString = [NSString stringWithFormat:@"%0.1f%%", progress * 100.0]; - - NSMutableAttributedString *result = [[NSMutableAttributedString alloc] init]; - [result beginEditing]; - - NSAttributedString *str1 = [[NSAttributedString alloc] - initWithString:[NSString stringWithFormat:@"%@ ", NSLocalizedString(@"Syncing", nil)] - attributes:@{ - NSFontAttributeName : [UIFont dw_fontForTextStyle:UIFontTextStyleBody], - }]; - [result appendAttributedString:str1]; - - NSAttributedString *str2 = [[NSAttributedString alloc] - initWithString:percentString - attributes:@{ - NSFontAttributeName : [UIFont dw_fontForTextStyle:UIFontTextStyleHeadline], - }]; - [result appendAttributedString:str2]; - - [result endEditing]; - - [self.syncingButton setAttributedTitle:result forState:UIControlStateNormal]; - self.syncingButton.hidden = self.syncState != DWSyncModelState_Syncing; -} - -- (void)filterButtonAction:(UIButton *)sender { - [self.delegate syncingHeaderView:self filterButtonAction:sender]; -} - -- (void)syncingButtonAction:(UIButton *)sender { - [self.delegate syncingHeaderView:self syncingButtonAction:sender]; -} - -@end diff --git a/DashWallet/Sources/UI/Home/Views/Home Balance View/BalanceModel.swift b/DashWallet/Sources/UI/Home/Views/Home Balance View/BalanceModel.swift new file mode 100644 index 000000000..1cd186bca --- /dev/null +++ b/DashWallet/Sources/UI/Home/Views/Home Balance View/BalanceModel.swift @@ -0,0 +1,116 @@ +// +// Created by tkhp +// Copyright © 2023 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +// MARK: - BalanceModel + +final class BalanceModel { + private(set) var state: SyncingActivityMonitor.State + + private(set) var value: UInt64 = 0 + var isBalanceHidden: Bool + + var balanceDidChange: (() -> ())? + + init() { + isBalanceHidden = DWGlobalOptions.sharedInstance().balanceHidden + state = SyncingActivityMonitor.shared.state + + SyncingActivityMonitor.shared.add(observer: self) + + reloadBalance() + + NotificationCenter.default.addObserver(self, selector: #selector(applicationWillEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil) + } + + func hideBalanceIfNeeded() { + if DWGlobalOptions.sharedInstance().balanceHidden { + isBalanceHidden = true + } + } + + func reloadBalance() { + let balanceValue = DWEnvironment.sharedInstance().currentWallet.balance + + if balanceValue > value && + value > 0 && + UIApplication.shared.applicationState != .background && + SyncingActivityMonitor.shared.progress > 0.995 { + UIDevice.current.dw_playCoinSound() + } + + value = balanceValue + + let options = DWGlobalOptions.sharedInstance() + if balanceValue > 0 + && options.walletNeedsBackup + && (options.balanceChangedDate == nil) { + options.balanceChangedDate = Date() + } + + options.userHasBalance = balanceValue > 0 + + balanceDidChange?() + } + + @objc + func applicationWillEnterForeground(_ notification: NSNotification) { + hideBalanceIfNeeded() + } + + deinit { + NotificationCenter.default.removeObserver(self) + SyncingActivityMonitor.shared.remove(observer: self) + } +} + +// MARK: BalanceViewDataSource + +extension BalanceModel: BalanceViewDataSource { + var mainAmountString: String { + value.formattedDashAmount + } + + var supplementaryAmountString: String { + fiatAmountString() + } +} + +// MARK: SyncingActivityMonitorObserver + +extension BalanceModel: SyncingActivityMonitorObserver { + func syncingActivityMonitorProgressDidChange(_ progress: Double) { + // NOP + } + + func syncingActivityMonitorStateDidChange(previousState: SyncingActivityMonitor.State, state: SyncingActivityMonitor.State) { + self.state = state + reloadBalance() + } +} + +extension BalanceModel { + func dashAmountStringWithFont(_ font: UIFont, tintColor: UIColor) -> NSAttributedString { + NSAttributedString.dashAttributedString(for: value, tintColor: tintColor, font: font) + } + + func fiatAmountString() -> String { + CurrencyExchanger.shared.fiatAmountString(for: value.dashAmount) + } +} + diff --git a/DashWallet/Sources/UI/Home/Views/HomeBalanceView.swift b/DashWallet/Sources/UI/Home/Views/Home Balance View/HomeBalanceView.swift similarity index 78% rename from DashWallet/Sources/UI/Home/Views/HomeBalanceView.swift rename to DashWallet/Sources/UI/Home/Views/Home Balance View/HomeBalanceView.swift index 477eb3704..1d784836a 100644 --- a/DashWallet/Sources/UI/Home/Views/HomeBalanceView.swift +++ b/DashWallet/Sources/UI/Home/Views/Home Balance View/HomeBalanceView.swift @@ -17,35 +17,22 @@ import Foundation -// MARK: - BalanceViewDelegate +// MARK: - HomeBalanceViewDelegate @objc(DWHomeBalanceViewDelegate) -protocol BalanceViewDelegate: AnyObject { +protocol HomeBalanceViewDelegate: AnyObject { func balanceView(_ view: HomeBalanceView, balanceLongPressAction sender: UIControl) - func balanceViewDidToggleBalanceVisibility(_ view: HomeBalanceView) -} - -// MARK: - HomeBalanceViewDataSource - -@objc(DWHomeBalanceViewDataSource) -protocol HomeBalanceViewDataSource: BalanceViewDataSource { - var isBalanceHidden: Bool { get } } // MARK: - HomeBalanceViewState -@objc(DWHomeBalanceViewState) enum HomeBalanceViewState: Int { - @objc(DWHomeBalanceViewState_Default) case `default` - - @objc(DWHomeBalanceViewState_Syncing) case syncing } // MARK: - HomeBalanceView -@objc(DWHomeBalanceView) final class HomeBalanceView: UIView { @IBOutlet private weak var contentView: UIView! @IBOutlet private var balanceButton: UIControl! @@ -56,63 +43,40 @@ final class HomeBalanceView: UIView { @IBOutlet private var amountsView: UIView! @IBOutlet private var balanceView: BalanceView! - @objc - weak var dataSource: HomeBalanceViewDataSource? { - didSet { - balanceView.dataSource = dataSource - reloadView() - reloadData() - } - } - - @objc - weak var delegate: BalanceViewDelegate? + weak var delegate: HomeBalanceViewDelegate? - @objc var state: HomeBalanceViewState = .default { didSet { reloadView() } } + private var isBalanceHidden: Bool { + model.isBalanceHidden + } + + private let model = BalanceModel() + override init(frame: CGRect) { super.init(frame: frame) + commonInit() } required init?(coder: NSCoder) { super.init(coder: coder) + commonInit() } - @objc - func reloadView() { - var titleString = "" - - let isBalanceHidden = dataSource?.isBalanceHidden ?? false - - if !isBalanceHidden && state == .syncing { - titleString = NSLocalizedString("Syncing Balance", comment: "") - - titleLabel.alpha = 0.8 - titleLabel.layer.removeAllAnimations() - - UIView.animate(withDuration: 0.8, - delay:0.0, - options:[.allowUserInteraction, .curveEaseInOut, .autoreverse, .repeat], - animations: { self.titleLabel.alpha = 0.3 }, - completion: nil) - - } else { - titleLabel.layer.removeAllAnimations() - } - - titleLabel.text = titleString + func hideBalanceIfNeeded() { + model.hideBalanceIfNeeded() + hideBalance(model.isBalanceHidden) } - @objc public func reloadData() { - balanceView.reloadData() + model.reloadBalance() + reloadView() } private func commonInit() { @@ -136,18 +100,58 @@ final class HomeBalanceView: UIView { eyeSlashImageView.tintColor = UIColor.dw_darkBlue() - tapToUnhideLabel.titleLabel?.font = UIFont.dw_font(forTextStyle: .caption1) tapToUnhideLabel.setTitle(NSLocalizedString("Tap to hide balance", comment: ""), for: .normal) tapToUnhideLabel.setTitleColor(UIColor.white.withAlphaComponent(0.5), for: .normal) + tapToUnhideLabel.isUserInteractionEnabled = true + + let tapRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(balanceButtonAction(_:))) + tapToUnhideLabel.addGestureRecognizer(tapRecognizer) let recognizer = UILongPressGestureRecognizer(target: self, action: #selector(balanceLongPressAction(_:))) balanceButton.addGestureRecognizer(recognizer) balanceView.tint = .white + balanceView.dataSource = model + + let isBalanceHidden = isBalanceHidden + hidingView.alpha = isBalanceHidden ? 1.0 : 0.0 + amountsView.alpha = isBalanceHidden ? 0.0 : 1.0 + tapToUnhideLabel.alpha = isBalanceHidden ? 0.0 : 1.0 NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChangeNotification(_:)), name: UIContentSizeCategory.didChangeNotification, object: nil) + + reloadView() + + model.balanceDidChange = { [weak self] in + self?.balanceView.reloadData() + } + } + + private func reloadView() { + var titleString = "" + + let isBalanceHidden = model.isBalanceHidden + + if !isBalanceHidden && state == .syncing { + titleString = NSLocalizedString("Syncing Balance", comment: "") + + titleLabel.alpha = 0.8 + titleLabel.layer.removeAllAnimations() + + UIView.animate(withDuration: 0.8, + delay:0.0, + options:[.allowUserInteraction, .curveEaseInOut, .autoreverse, .repeat], + animations: { self.titleLabel.alpha = 0.3 }, + completion: nil) + + } else { + titleLabel.layer.removeAllAnimations() + } + + titleLabel.text = titleString + hideBalance(isBalanceHidden) } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { @@ -158,7 +162,8 @@ final class HomeBalanceView: UIView { @IBAction private func balanceButtonAction(_ sender: UIControl) { - delegate?.balanceViewDidToggleBalanceVisibility(self) + model.isBalanceHidden.toggle() + reloadView() } @objc @@ -171,16 +176,17 @@ final class HomeBalanceView: UIView { reloadData() } - @objc func hideBalance(_ hidden: Bool) { let animated = window != nil + let isAlreadyHidden = amountsView.alpha == 0 + + guard isAlreadyHidden != hidden else { return } UIView.animate(withDuration: animated ? kAnimationDuration : 0.0) { self.hidingView.alpha = hidden ? 1.0 : 0.0 self.amountsView.alpha = hidden ? 0.0 : 1.0 self.tapToUnhideLabel.alpha = hidden ? 0.0 : 1.0 - self.reloadData() } } } diff --git a/DashWallet/Sources/UI/Home/Views/HomeBalanceView.xib b/DashWallet/Sources/UI/Home/Views/Home Balance View/HomeBalanceView.xib similarity index 100% rename from DashWallet/Sources/UI/Home/Views/HomeBalanceView.xib rename to DashWallet/Sources/UI/Home/Views/Home Balance View/HomeBalanceView.xib diff --git a/DashWallet/Sources/UI/Home/Views/Home Header View/DashPayProfileView.swift b/DashWallet/Sources/UI/Home/Views/Home Header View/DashPayProfileView.swift new file mode 100644 index 000000000..e6adcf621 --- /dev/null +++ b/DashWallet/Sources/UI/Home/Views/Home Header View/DashPayProfileView.swift @@ -0,0 +1,106 @@ +// +// Created by PT +// Copyright © 2023 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +import UIKit + +let AVATAR_SIZE = CGSize(width: 72.0, height: 72.0) + +// MARK: - DashPayProfileView + +class DashPayProfileView: UIControl { + private(set) var contentView: UIView + private(set) var avatarView: DWDPAvatarView + private(set) var bellImageView: UIImageView + private(set) var badgeView: BadgeView + + var username: String? { + didSet { + avatarView.username = username + } + } + + var unreadCount = 0 { + didSet { + badgeView.text = "\(unreadCount)" + bellImageView.isHidden = unreadCount > 0 + badgeView.isHidden = unreadCount == 0 + } + } + + override var isHighlighted: Bool { + didSet { + self.contentView.dw_pressedAnimation(.medium, pressed: isHighlighted) + } + } + + override init(frame: CGRect) { + contentView = UIView() + avatarView = DWDPAvatarView() + bellImageView = UIImageView(image: UIImage(named: "icon_bell")) + + badgeView = BadgeView() + + super.init(frame: frame) + + backgroundColor = UIColor.dw_dashBlue() + + contentView.translatesAutoresizingMaskIntoConstraints = false + contentView.backgroundColor = backgroundColor + contentView.isUserInteractionEnabled = false + addSubview(contentView) + + avatarView.translatesAutoresizingMaskIntoConstraints = false + avatarView.backgroundMode = .random + avatarView.isUserInteractionEnabled = false + contentView.addSubview(avatarView) + + bellImageView.translatesAutoresizingMaskIntoConstraints = false + bellImageView.isUserInteractionEnabled = false + contentView.addSubview(bellImageView) + + badgeView.translatesAutoresizingMaskIntoConstraints = false + badgeView.isHidden = true + contentView.addSubview(badgeView) + + NSLayoutConstraint.activate([ + contentView.topAnchor.constraint(equalTo: topAnchor), + contentView.leadingAnchor.constraint(equalTo: leadingAnchor), + contentView.trailingAnchor.constraint(equalTo: trailingAnchor), + contentView.bottomAnchor.constraint(equalTo: bottomAnchor), + + avatarView.topAnchor.constraint(equalTo: contentView.topAnchor), + avatarView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), + avatarView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor), + avatarView.widthAnchor.constraint(equalToConstant: AVATAR_SIZE.width), + avatarView.heightAnchor.constraint(equalToConstant: AVATAR_SIZE.height), + + bellImageView.trailingAnchor.constraint(equalTo: avatarView.trailingAnchor), + bellImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), + + badgeView.centerXAnchor.constraint(equalTo: avatarView.trailingAnchor), + badgeView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), + ]) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + +} diff --git a/DashWallet/Sources/UI/Home/Views/Home Header View/HomeHeaderModel.swift b/DashWallet/Sources/UI/Home/Views/Home Header View/HomeHeaderModel.swift new file mode 100644 index 000000000..b434e3c10 --- /dev/null +++ b/DashWallet/Sources/UI/Home/Views/Home Header View/HomeHeaderModel.swift @@ -0,0 +1,48 @@ +// +// Created by PT +// Copyright © 2023 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +// MARK: - HomeHeaderModel + +final class HomeHeaderModel { + var stateDidChage: ((SyncingActivityMonitor.State) -> ())? + private(set) var state: SyncingActivityMonitor.State + + init() { + state = SyncingActivityMonitor.shared.state + SyncingActivityMonitor.shared.add(observer: self) + } + + deinit { + SyncingActivityMonitor.shared.remove(observer: self) + } +} + +// MARK: SyncingActivityMonitorObserver + + +extension HomeHeaderModel: SyncingActivityMonitorObserver { + func syncingActivityMonitorProgressDidChange(_ progress: Double) { + // NOP + } + + func syncingActivityMonitorStateDidChange(previousState: SyncingActivityMonitor.State, state: SyncingActivityMonitor.State) { + self.state = state + stateDidChage?(state) + } +} diff --git a/DashWallet/Sources/UI/Home/Views/Home Header View/HomeHeaderView.swift b/DashWallet/Sources/UI/Home/Views/Home Header View/HomeHeaderView.swift new file mode 100644 index 000000000..45c211c44 --- /dev/null +++ b/DashWallet/Sources/UI/Home/Views/Home Header View/HomeHeaderView.swift @@ -0,0 +1,198 @@ +// +// Created by PT +// Copyright © 2023 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +private let kAvatarSize = CGSize(width: 72.0, height: 72.0) + +// MARK: - HomeHeaderViewDelegate + +protocol HomeHeaderViewDelegate: AnyObject { + func homeHeaderView(_ headerView: HomeHeaderView, profileButtonAction sender: UIControl) + func homeHeaderView(_ headerView: HomeHeaderView, retrySyncButtonAction sender: UIView) + func homeHeaderViewDidUpdateContents(_ headerView: HomeHeaderView) +} + +// MARK: - HomeHeaderView + +@objc(DWHomeHeaderView) +final class HomeHeaderView: UIView { + + public weak var delegate: HomeHeaderViewDelegate? + + private(set) var profileView: DashPayProfileView! + private(set) var balanceView: HomeBalanceView! + private(set) var syncView: SyncView! + private(set) var shortcutsView: ShortcutsView! + private(set) var stackView: UIStackView! + + weak var shortcutsDelegate: ShortcutsActionDelegate? { + get { + shortcutsView.actionDelegate + } + set { + shortcutsView.actionDelegate = newValue + } + } + + private let model: HomeHeaderModel + + override init(frame: CGRect) { + model = HomeHeaderModel() + + super.init(frame: frame) + + profileView = DashPayProfileView(frame: .zero) + profileView.translatesAutoresizingMaskIntoConstraints = false + profileView.addTarget(self, action: #selector(profileViewAction(_:)), for: .touchUpInside) + profileView.isHidden = true + + balanceView = HomeBalanceView(frame: .zero) + balanceView.delegate = self + + syncView = SyncView.view() + syncView.delegate = self + + shortcutsView = ShortcutsView(frame: .zero) + shortcutsView.translatesAutoresizingMaskIntoConstraints = false + + let views: [UIView] = [profileView, balanceView, shortcutsView, syncView] + let stackView = UIStackView(arrangedSubviews: views) + stackView.translatesAutoresizingMaskIntoConstraints = false + stackView.axis = .vertical + addSubview(stackView) + self.stackView = stackView + + NSLayoutConstraint.activate([ + stackView.topAnchor.constraint(equalTo: topAnchor), + stackView.leadingAnchor.constraint(equalTo: leadingAnchor), + stackView.bottomAnchor.constraint(equalTo: bottomAnchor), + stackView.trailingAnchor.constraint(equalTo: trailingAnchor), + ]) + + if model.state == .syncFailed || model.state == .noConnection { + showSyncView() + + } else { + hideSyncView() + } + + // TODO: Platform +// [self mvvm_observe:DW_KEYPATH(self, model.dashPayModel.registrationStatus) +// with:^(typeof(self) self, id value) { +// [self updateProfileView]; +// }]; +// +// [self mvvm_observe:DW_KEYPATH(self, model.dashPayModel.username) +// with:^(typeof(self) self, id value) { +// [self updateProfileView]; +// }]; +// +// [self mvvm_observe:DW_KEYPATH(self, model.dashPayModel.unreadNotificationsCount) +// with:^(typeof(self) self, id value) { +// self.profileView.unreadCount = self.model.dashPayModel.unreadNotificationsCount; +// }]; + + reloadBalance() + updateProfileView() + + model.stateDidChage = { [weak self] state in + self?.balanceView.state = state == .syncing ? .syncing : .default + + if state == .syncFailed || state == .noConnection { + self?.showSyncView() + } else { + self?.hideSyncView() + } + + self?.reloadBalance() + self?.reloadShortcuts() + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc + func profileViewAction(_ sender: UIControl) { + delegate?.homeHeaderView(self, profileButtonAction: sender) + } + + func parentScrollViewDidScroll(_ scrollView: UIScrollView) { } + + func reloadBalance() { + let isSyncing = SyncingActivityMonitor.shared.state == .syncing + + balanceView.reloadData() + balanceView.state = isSyncing ? .syncing : .`default` + } + + func reloadShortcuts() { + shortcutsView.reloadData() + } + + private func updateProfileView() { + profileView.isHidden = true + + // TODO: Platform +// let status = model?.dashPayModel.registrationStatus +// let completed = model?.dashPayModel.registrationCompleted ?? false +// if status?.state == .done || completed { +// profileView.username = model?.dashPay +// profileView.isHidden = false +// } else { +// profileView.isHidden = true +// } +// delegate?.homeHeaderViewDidUpdateContents(self) + } + + private func hideSyncView() { + syncView.isHidden = true + delegate?.homeHeaderViewDidUpdateContents(self) + } + + private func showSyncView() { + syncView.isHidden = false + delegate?.homeHeaderViewDidUpdateContents(self) + } +} + +// MARK: HomeBalanceViewDelegate + +extension HomeHeaderView: HomeBalanceViewDelegate { + func balanceView(_ view: HomeBalanceView, balanceLongPressAction sender: UIControl) { + let action = ShortcutAction(type: .localCurrency) + shortcutsDelegate?.shortcutsView(view, didSelectAction: action, sender: sender) + } +} + +// MARK: ShortcutsViewDelegate + +extension HomeHeaderView: ShortcutsViewDelegate { + func shortcutsViewDidUpdateContentSize(_ view: ShortcutsView) { + delegate?.homeHeaderViewDidUpdateContents(self) + } +} + +// MARK: SyncViewDelegate + +extension HomeHeaderView: SyncViewDelegate { + func syncViewRetryButtonAction(_ view: SyncView) { + delegate?.homeHeaderView(self, retrySyncButtonAction: view) + } +} diff --git a/DashWallet/Sources/UI/Home/Views/HomeView.swift b/DashWallet/Sources/UI/Home/Views/HomeView.swift new file mode 100644 index 000000000..8bbe43fe1 --- /dev/null +++ b/DashWallet/Sources/UI/Home/Views/HomeView.swift @@ -0,0 +1,267 @@ +// +// Created by PT +// Copyright © 2023 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +// MARK: - HomeViewDelegate + +@objc(DWHomeViewDelegate) +protocol HomeViewDelegate: AnyObject { + func homeView(_ homeView: HomeView, showTxFilter sender: UIView) + func homeView(_ homeView: HomeView, showSyncingStatus sender: UIView) + func homeView(_ homeView: HomeView, profileButtonAction sender: UIControl) + func homeView(_ homeView: HomeView, didSelectTransaction transaction: DSTransaction) + func homeViewShowDashPayRegistrationFlow(_ homeView: HomeView) + func homeView(_ homeView: HomeView, showReclassifyYourTransactionsFlowWithTransaction transaction: DSTransaction) + func homeView(_ homeView: HomeView, showCrowdNodeTxs transactions: [DSTransaction]) +} + +// MARK: - HomeView + +@objc(DWHomeView) +final class HomeView: UIView, DWHomeModelUpdatesObserver, DWDPRegistrationErrorRetryDelegate { + + @objc + weak var delegate: HomeViewDelegate? + + private(set) var headerView: HomeHeaderView! + private(set) var topOverscrollView: UIView! + private(set) var tableView: UITableView! + + weak var syncingHeaderView: SyncingHeaderView? + + // Strong ref to current dataSource to make sure it always exists while tableView uses it + var currentDataSource: TransactionListDataSource? + + @objc + var model: DWHomeProtocol? { + didSet { + model?.updatesObserver = self + } + } + + @objc + weak var shortcutsDelegate: ShortcutsActionDelegate? { + get { headerView.shortcutsDelegate } + set { headerView.shortcutsDelegate = newValue } + } + + override init(frame: CGRect) { + super.init(frame: frame) + setupView() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupView() + } + + @objc + func hideBalanceIfNeeded() { + headerView?.balanceView.hideBalanceIfNeeded() + } + + override func layoutSubviews() { + super.layoutSubviews() + + let size = bounds.size + topOverscrollView.frame = CGRect(x: 0.0, y: -size.height, width: size.width, height: size.height) + + if let tableHeaderView = tableView.tableHeaderView { + let headerSize = tableHeaderView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) + if tableHeaderView.frame.height != headerSize.height { + tableHeaderView.frame = CGRect(x: 0.0, y: 0.0, width: headerSize.width, height: headerSize.height) + tableView.tableHeaderView = tableHeaderView + } + } + } + + private func setupView() { + backgroundColor = UIColor.dw_secondaryBackground() + + headerView = HomeHeaderView(frame: CGRect.zero) + headerView.delegate = self + + topOverscrollView = UIView(frame: CGRect.zero) + topOverscrollView.backgroundColor = UIColor.dw_dashNavigationBlue() + + tableView = UITableView(frame: bounds, style: .plain) + tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + tableView.tableHeaderView = headerView + tableView.backgroundColor = UIColor.dw_secondaryBackground() + tableView.dataSource = self + tableView.delegate = self + tableView.rowHeight = UITableView.automaticDimension + tableView.estimatedRowHeight = 74.0 + tableView.sectionHeaderHeight = UITableView.automaticDimension + tableView.estimatedSectionHeaderHeight = 64.0 + tableView.separatorStyle = .none + // NOTE: tableView.contentInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: DW_TABBAR_NOTCH, right: 0.0) + tableView.addSubview(topOverscrollView) + addSubview(tableView) + + let cellIds = [ + TxListEmptyTableViewCell.reuseIdentifier, + TxListTableViewCell.reuseIdentifier, + DWDPRegistrationStatusTableViewCell.dw_reuseIdentifier, + DWDPRegistrationErrorTableViewCell.dw_reuseIdentifier, + DWDPRegistrationDoneTableViewCell.dw_reuseIdentifier, + ] + for cellId in cellIds { + let nib = UINib(nibName: cellId, bundle: nil) + tableView.register(nib, forCellReuseIdentifier: cellId) + } + + let nib = UINib(nibName: "CNCreateAccountCell", bundle: nil) + tableView.register(nib, forCellReuseIdentifier: CNCreateAccountCell.dw_reuseIdentifier) + tableView.registerClassforHeaderFooterView(for: SyncingHeaderView.self) + NotificationCenter.default.addObserver(self, + selector: #selector(setNeedsLayout), + name: UIContentSizeCategory.didChangeNotification, + object: nil) + } + + // MARK: - DWHomeModelUpdatesObserver + + func homeModel(_ model: DWHomeProtocol, didUpdate dataSource: TransactionListDataSource, shouldAnimate: Bool) { + currentDataSource = dataSource + dataSource.retryDelegate = self + + if dataSource.isEmpty { + tableView.dataSource = self + } else { + tableView.dataSource = dataSource + } + tableView.reloadData() + + headerView.reloadBalance() + reloadShortcuts() + } + + func homeModel(_ model: DWHomeProtocol, didReceiveNewIncomingTransaction transaction: DSTransaction) { + delegate?.homeView(self, showReclassifyYourTransactionsFlowWithTransaction: transaction) + } + + func homeModelDidChangeInnerModels(_ model: DWHomeProtocol) { + headerView.reloadBalance() + reloadShortcuts() + } + + func homeModelWant(toReloadShortcuts model: DWHomeProtocol) { + reloadShortcuts() + } + + + // MARK: - DWDPRegistrationErrorRetryDelegate + + func registrationErrorRetryAction() { + // TODO: Platform +// if model?.dashPayModel.canRetry ?? false { +// model?.dashPayModel.retry() +// } else { +// delegate?.homeViewShowDashPayRegistrationFlow(self) +// } + } + + @objc + func reloadShortcuts() { + headerView?.reloadShortcuts() + } +} + +// MARK: HomeHeaderViewDelegate + +extension HomeView: HomeHeaderViewDelegate { + func homeHeaderView(_ headerView: HomeHeaderView, retrySyncButtonAction sender: UIView) { + model?.retrySyncing() + } + + func homeHeaderViewDidUpdateContents(_ view: HomeHeaderView) { + setNeedsLayout() + } + + func homeHeaderView(_ view: HomeHeaderView, profileButtonAction sender: UIControl) { + delegate?.homeView(self, profileButtonAction: sender) + } +} + +// MARK: SyncingHeaderViewDelegate + +extension HomeView: SyncingHeaderViewDelegate { + func syncingHeaderView(_ view: SyncingHeaderView, syncingButtonAction sender: UIButton) { + delegate?.homeView(self, showSyncingStatus: sender) + } + + func syncingHeaderView(_ view: SyncingHeaderView, filterButtonAction sender: UIButton) { + delegate?.homeView(self, showTxFilter: sender) + } +} + +// MARK: UITableViewDataSource, UITableViewDelegate + +extension HomeView: UITableViewDataSource, UITableViewDelegate { + // MARK: - UITableViewDataSource + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + 1 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cellId = TxListEmptyTableViewCell.reuseIdentifier + let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) + return cell + } + + // MARK: - UITableViewDelegate + + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + let headerView = tableView.dequeueReusableHeaderFooterView(type: SyncingHeaderView.self) + headerView.delegate = self + syncingHeaderView = headerView + return headerView + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + + guard let currentDataSource, + !currentDataSource.isEmpty else { return } + + let type = currentDataSource.itemType(by: indexPath) + + if type == .crowdnode { + delegate?.homeView(self, showCrowdNodeTxs: currentDataSource.crowdnodeTxs()) + return + } + + if let transaction = currentDataSource.transactionForIndexPath(indexPath) { + delegate?.homeView(self, didSelectTransaction: transaction) + } else { // registration status cell + delegate?.homeViewShowDashPayRegistrationFlow(self) + } + } + + // MARK: - UIScrollViewDelegate + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + headerView.parentScrollViewDidScroll(scrollView) + } + + + +} + diff --git a/DashWallet/Sources/UI/Home/Views/Shortcuts/Models/ShortcutAction.swift b/DashWallet/Sources/UI/Home/Views/Shortcuts/Models/ShortcutAction.swift index 9a3f0b1dc..bb146356d 100644 --- a/DashWallet/Sources/UI/Home/Views/Shortcuts/Models/ShortcutAction.swift +++ b/DashWallet/Sources/UI/Home/Views/Shortcuts/Models/ShortcutAction.swift @@ -1,4 +1,4 @@ -// +// // Created by PT // Copyright © 2023 Dash Core Group. All rights reserved. // @@ -17,6 +17,8 @@ import Foundation +// MARK: - ShortcutActionType + @objc(DWShortcutActionType) enum ShortcutActionType: Int { case secureWallet = 1 @@ -103,9 +105,8 @@ extension ShortcutActionType { default: fatalError("Image not found for shortcut type: \(self)") } - } - + var title: String { switch self { case .secureWallet: @@ -148,31 +149,32 @@ extension ShortcutActionType { case .explore: return NSLocalizedString("Explore", comment: "Translate it as short as possible! (24 symbols max)") } - } } +// MARK: - ShortcutAction + @objc(DWShortcutAction) class ShortcutAction: NSObject { @objc let type: ShortcutActionType - + @objc let enabled: Bool - + init(type: ShortcutActionType, enabled: Bool = true) { self.type = type self.enabled = enabled } - + @objc static func action(type: ShortcutActionType) -> ShortcutAction { - return ShortcutAction(type: type, enabled: true) + ShortcutAction(type: type, enabled: true) } - + @objc static func action(type: ShortcutActionType, enabled: Bool) -> ShortcutAction { - return ShortcutAction(type: type, enabled: enabled) + ShortcutAction(type: type, enabled: enabled) } } @@ -180,19 +182,19 @@ extension ShortcutAction { var title: String { type.title } - + var icon: UIImage { type.icon } - + var alpha: CGFloat { enabled ? 1 : 0.4 } - + var showsGradientLayer: Bool { false } - + var textColor: UIColor { .dw_darkTitle() } diff --git a/DashWallet/Sources/UI/Home/Views/Shortcuts/Models/ShortcutsModel.swift b/DashWallet/Sources/UI/Home/Views/Shortcuts/Models/ShortcutsModel.swift index 7241fc881..76d8f17c9 100644 --- a/DashWallet/Sources/UI/Home/Views/Shortcuts/Models/ShortcutsModel.swift +++ b/DashWallet/Sources/UI/Home/Views/Shortcuts/Models/ShortcutsModel.swift @@ -1,4 +1,4 @@ -// +// // Created by PT // Copyright © 2023 Dash Core Group. All rights reserved. // @@ -17,55 +17,43 @@ import Foundation -@objc(DWShortcutsModelDataSource) -protocol ShortcutsModelDataSource: AnyObject { - func shouldShowCreateUserNameButton() -> Bool -} - -@objc(DWShortcutsModelDelegate) -protocol ShortcutsModelDelegate: AnyObject { - func shortcutItemsDidChange() -} let MAX_SHORTCUTS_COUNT = 4 -@objc(DWShortcutsModel) -class ShortcutsModel: NSObject { +// MARK: - ShortcutsModel + +final class ShortcutsModel { private var mutableItems: [ShortcutAction] = [] - - weak var dataSource: ShortcutsModelDataSource? - weak var delegate: ShortcutsModelDelegate? - + + var shortcutItemsDidChangeHandler: (() -> ())? + @objc - init(dataSource: ShortcutsModelDataSource) { - super.init() - - self.dataSource = dataSource - + init() { reloadShortcuts() } var items: [ShortcutAction] { - return mutableItems + mutableItems } @objc func reloadShortcuts() { mutableItems = Self.userShortcuts() - delegate?.shortcutItemsDidChange() + shortcutItemsDidChangeHandler?() } + // TODO: Move this to HomeModel static func userShortcuts() -> [ShortcutAction] { let options = DWGlobalOptions.sharedInstance() let walletNeedsBackup = options.walletNeedsBackup let userHasBalance = options.userHasBalance - + var mutableItems = [ShortcutAction]() - mutableItems.reserveCapacity(3) - + mutableItems.reserveCapacity(2) + if walletNeedsBackup { mutableItems.append(ShortcutAction(type: .secureWallet)) - + if userHasBalance { mutableItems.append(ShortcutAction(type: .receive)) mutableItems.append(ShortcutAction(type: .payToAddress)) @@ -73,7 +61,7 @@ class ShortcutsModel: NSObject { } else { mutableItems.append(ShortcutAction(type: .explore)) mutableItems.append(ShortcutAction(type: .receive)) - + if DWEnvironment.sharedInstance().currentChain.isMainnet() { mutableItems.append(ShortcutAction(type: .buySellDash)) } @@ -87,13 +75,13 @@ class ShortcutsModel: NSObject { } else { mutableItems.append(ShortcutAction(type: .explore)) mutableItems.append(ShortcutAction(type: .receive)) - + if DWEnvironment.sharedInstance().currentChain.isMainnet() { mutableItems.append(ShortcutAction(type: .buySellDash)) } } } - + return mutableItems } } diff --git a/DashWallet/Sources/UI/Home/Views/Shortcuts/ShortcutCell.swift b/DashWallet/Sources/UI/Home/Views/Shortcuts/ShortcutCell.swift index ea034df97..a26a82ecb 100644 --- a/DashWallet/Sources/UI/Home/Views/Shortcuts/ShortcutCell.swift +++ b/DashWallet/Sources/UI/Home/Views/Shortcuts/ShortcutCell.swift @@ -1,4 +1,4 @@ -// +// // Created by PT // Copyright © 2023 Dash Core Group. All rights reserved. // @@ -18,19 +18,19 @@ import UIKit class ShortcutCell: UICollectionViewCell { - + @IBOutlet private weak var centeredView: UIView! @IBOutlet private weak var iconImageView: UIImageView! @IBOutlet private weak var titleLabel: UILabel! - + private var gradientLayer: CAGradientLayer? - + override func awakeFromNib() { super.awakeFromNib() titleLabel.font = UIFont.dw_font(forTextStyle: .caption2) centeredView.backgroundColor = .clear } - + var model: ShortcutAction! { didSet { titleLabel.text = model.title @@ -42,7 +42,7 @@ class ShortcutCell: UICollectionViewCell { iconImageView.alpha = alpha } } - + override var isHighlighted: Bool { didSet { guard model.enabled == true else { return } diff --git a/DashWallet/Sources/UI/Home/Views/Shortcuts/ShortcutsView.swift b/DashWallet/Sources/UI/Home/Views/Shortcuts/ShortcutsView.swift index 043e16e76..41406b389 100644 --- a/DashWallet/Sources/UI/Home/Views/Shortcuts/ShortcutsView.swift +++ b/DashWallet/Sources/UI/Home/Views/Shortcuts/ShortcutsView.swift @@ -1,4 +1,4 @@ -// +// // Created by PT // Copyright © 2023 Dash Core Group. All rights reserved. // @@ -19,7 +19,7 @@ import UIKit func cellSize(for contentSizeCategory: UIContentSizeCategory) -> CGSize { var size = CGSize.zero - + if contentSizeCategory == .extraSmall || contentSizeCategory == .small || contentSizeCategory == .medium || @@ -32,7 +32,7 @@ func cellSize(for contentSizeCategory: UIContentSizeCategory) -> CGSize { } else { size = CGSize(width: 116.0, height: 116.0) } - + if UIDevice.isIphone6Plus || UIDevice.hasHomeIndicator { let width = UIScreen.main.bounds.size.width let margin: CGFloat = 16.0 @@ -43,103 +43,114 @@ func cellSize(for contentSizeCategory: UIContentSizeCategory) -> CGSize { return CGSize(width: cellWidth, height: cellWidth) } } - + if UIDevice.isIpad { return CGSize(width: size.width * 2.0, height: size.height) } - + return size } +// MARK: - ShortcutsActionDelegate + @objc(DWShortcutsActionDelegate) protocol ShortcutsActionDelegate: AnyObject { func shortcutsView(_ view: UIView, didSelectAction action: ShortcutAction, sender: UIView) } +// MARK: - ShortcutsViewDelegate + @objc(DWShortcutsViewDelegate) protocol ShortcutsViewDelegate: AnyObject { func shortcutsViewDidUpdateContentSize(_ shortcutsView: ShortcutsView) } +// MARK: - ShortcutsView + @objc class ShortcutsView: UIView { @objc weak var actionDelegate: ShortcutsActionDelegate? - + @objc weak var delegate: ShortcutsViewDelegate? - + @IBOutlet weak var contentView: UIView! - + @IBOutlet weak var collectionView: UICollectionView! - + @IBOutlet var collectionViewHeightConstraint: NSLayoutConstraint! - - @objc - var model: ShortcutsModel! { - didSet { - model.delegate = self - collectionView.reloadData() - } - } - + + var model = ShortcutsModel() + override init(frame: CGRect) { super.init(frame: frame) commonInit() } - + required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) commonInit() } - + + func reloadData() { + model.reloadShortcuts() + } + private func commonInit() { + model.shortcutItemsDidChangeHandler = { [weak self] in + self?.collectionView.reloadData() + } + Bundle.main.loadNibNamed(String(describing: type(of: self)), owner: self, options: nil) - + backgroundColor = .dw_secondaryBackground() - + contentView.translatesAutoresizingMaskIntoConstraints = false addSubview(contentView) - + NSLayoutConstraint.activate([ contentView.topAnchor.constraint(equalTo: topAnchor), contentView.leadingAnchor.constraint(equalTo: leadingAnchor), contentView.bottomAnchor.constraint(equalTo: bottomAnchor), contentView.trailingAnchor.constraint(equalTo: trailingAnchor), - contentView.widthAnchor.constraint(equalTo: widthAnchor) + contentView.widthAnchor.constraint(equalTo: widthAnchor), ]) - - + collectionView.layer.cornerRadius = 8 collectionView.layer.masksToBounds = true - + if UIDevice.current.userInterfaceIdiom == .pad { collectionView.contentInset = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 0) } - + collectionView.register(UINib(nibName: "DWShortcutCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: ShortcutCell.reuseIdentifier) - - NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChangeNotification(notification:)), name: UIContentSizeCategory.didChangeNotification, object: nil) - + + NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChangeNotification(notification:)), + name: UIContentSizeCategory.didChangeNotification, object: nil) + updateCellSizeForContentSizeCategory(UIApplication.shared.preferredContentSizeCategory, initialSetup: true) } - - @objc func contentSizeCategoryDidChangeNotification(notification: Notification) { + + @objc + func contentSizeCategoryDidChangeNotification(notification: Notification) { guard let category = notification.userInfo?[UIContentSizeCategory.newValueUserInfoKey] as? UIContentSizeCategory else { return } updateCellSizeForContentSizeCategory(category, initialSetup: false) } - + private func updateCellSizeForContentSizeCategory(_ contentSizeCategory: UIContentSizeCategory, initialSetup: Bool) { - let cellSize = cellSize(for: contentSizeCategory) + var cellSize = cellSize(for: contentSizeCategory) + cellSize.height = ceil(cellSize.height) // This fixes the autolayout issue when the size of the cell is higher than the collection view itself + collectionViewHeightConstraint.constant = cellSize.height setNeedsUpdateConstraints() - + if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout { layout.itemSize = cellSize if !initialSetup { @@ -147,49 +158,51 @@ class ShortcutsView: UIView { } } collectionView.reloadData() - + if !initialSetup { delegate?.shortcutsViewDidUpdateContentSize(self) } } } +// MARK: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout + extension ShortcutsView: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - let action = self.model.items[indexPath.item] - + let action = model.items[indexPath.item] + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ShortcutCell.reuseIdentifier, for: indexPath) as! ShortcutCell cell.model = action - #if SNAPSHOT - if (action.type == .secureWallet) { + #if SNAPSHOT + if action.type == .secureWallet { cell.accessibilityIdentifier = "shortcut_secure_wallet" } - #endif /* SNAPSHOT */ + #endif // SNAPSHOT return cell } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { model.items.count } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { collectionView.deselectItem(at: indexPath, animated: true) - - let action = self.model.items[indexPath.item] + + let action = model.items[indexPath.item] guard action.enabled else { return } guard let cell = collectionView.cellForItem(at: indexPath) else { return } - + actionDelegate?.shortcutsView(self, didSelectAction: action, sender: cell) } - + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { guard let collectionViewLayout = collectionViewLayout as? UICollectionViewFlowLayout else { return UIEdgeInsets.zero } - + let cellSpacing = collectionViewLayout.minimumLineSpacing let cellWidth = collectionViewLayout.itemSize.width - + let cellCount = CGFloat(collectionView.numberOfItems(inSection: section)) var inset = (collectionView.bounds.size.width - (cellCount * cellWidth) - ((cellCount - 1) * cellSpacing)) * 0.5 inset = max(inset, 0.0) @@ -197,12 +210,11 @@ extension ShortcutsView: UICollectionViewDataSource, UICollectionViewDelegate, U } } -extension ShortcutsView: ShortcutsModelDataSource, ShortcutsModelDelegate { +// MARK: ShortcutsModelDataSource, ShortcutsModelDelegate + +extension ShortcutsView { + // TODO: DashPay func shouldShowCreateUserNameButton() -> Bool { false } - - func shortcutItemsDidChange() { - collectionView.reloadData() - } } diff --git a/DashWallet/Sources/UI/Main/DWMainTabbarViewController.h b/DashWallet/Sources/UI/Main/DWMainTabbarViewController.h deleted file mode 100644 index 35c962be3..000000000 --- a/DashWallet/Sources/UI/Main/DWMainTabbarViewController.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWExtendedContainerViewController.h" - -#import "DWDemoDelegate.h" -#import "DWHomeProtocol.h" -#import "DWWipeDelegate.h" - -NS_ASSUME_NONNULL_BEGIN - -@class DWHomeModel; - -@interface DWMainTabbarViewController : DWExtendedContainerViewController - -@property (nonatomic, strong) id homeModel; -@property (nullable, nonatomic, weak) id delegate; - -@property (nonatomic, assign) BOOL demoMode; -@property (nullable, nonatomic, weak) id demoDelegate; - -- (void)performScanQRCodeAction; -- (void)performPayToURL:(NSURL *)url; - -- (void)handleFile:(NSData *)file; - -- (void)openPaymentsScreen; -- (void)closePaymentsScreen; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Main/DWMainTabbarViewController.m b/DashWallet/Sources/UI/Main/DWMainTabbarViewController.m deleted file mode 100644 index 09567782e..000000000 --- a/DashWallet/Sources/UI/Main/DWMainTabbarViewController.m +++ /dev/null @@ -1,377 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWMainTabbarViewController.h" - -#import "DWContactsViewController.h" -#import "DWHomeViewController.h" -#import "DWMainMenuViewController.h" -#import "DWModalUserProfileViewController.h" -#import "DWTabBarView.h" -#import "DWUIKit.h" -#import "dashwallet-Swift.h" - -NS_ASSUME_NONNULL_BEGIN - -static NSTimeInterval const ANIMATION_DURATION = 0.35; - -@interface DWMainTabbarViewController () - -@property (nullable, nonatomic, strong) UIView *contentView; -@property (nullable, nonatomic, strong) DWTabBarView *tabBarView; -@property (nullable, nonatomic, strong) NSLayoutConstraint *tabBarBottomConstraint; -@property (nullable, nonatomic, strong) NSLayoutConstraint *contentBottomConstraint; - -@property (null_resettable, nonatomic, strong) DWNavigationController *homeNavigationController; -@property (null_resettable, nonatomic, strong) DWNavigationController *contactsNavigationController; -@property (null_resettable, nonatomic, strong) DWNavigationController *menuNavigationController; -@property (nonatomic, weak) DWHomeViewController *homeController; - -@end - -@implementation DWMainTabbarViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - - [self setupView]; - [self setupControllers]; -} - -- (UIView *)containerView { - return self.contentView; -} - -#pragma mark - Public - -- (void)performScanQRCodeAction { - [self dismissViewControllerAnimated:false completion:nil]; - [self transitionToController:self.homeNavigationController - transitionType:DWContainerTransitionType_WithoutAnimation]; - [self.tabBarView updateSelectedTabButton:DWTabBarViewButtonType_Home]; - [self.homeController performScanQRCodeAction]; -} - -- (void)performPayToURL:(NSURL *)url { - [self dismissViewControllerAnimated:false completion:nil]; - [self transitionToController:self.homeNavigationController - transitionType:DWContainerTransitionType_WithoutAnimation]; - [self.tabBarView updateSelectedTabButton:DWTabBarViewButtonType_Home]; - [self.homeController performPayToURL:url]; -} - -- (void)handleFile:(NSData *)file { - [self dismissViewControllerAnimated:false completion:nil]; - [self transitionToController:self.homeNavigationController - transitionType:DWContainerTransitionType_WithoutAnimation]; - [self.tabBarView updateSelectedTabButton:DWTabBarViewButtonType_Home]; - [self.homeController handleFile:file]; -} - -- (void)openPaymentsScreen { - NSAssert(self.demoMode, @"Invalid usage. Should be used in Demo mode only"); - [self showPaymentsControllerWithActivePage:DWPaymentsViewControllerIndex_Pay]; -} - -- (void)closePaymentsScreen { - NSAssert(self.demoMode, @"Invalid usage. Should be used in Demo mode only"); - - [self tabBarViewDidClosePayments:self.tabBarView]; -} - -#pragma mark - DWTabBarViewDelegate - -- (void)tabBarView:(DWTabBarView *)tabBarView didTapButtonType:(DWTabBarViewButtonType)buttonType { - switch (buttonType) { - case DWTabBarViewButtonType_Home: { - if (self.currentController == self.homeNavigationController) { - return; - } - - [self transitionToController:self.homeNavigationController - transitionType:DWContainerTransitionType_WithoutAnimation]; - - break; - } - case DWTabBarViewButtonType_Contacts: { - if (self.currentController == self.contactsNavigationController) { - return; - } - - [self transitionToController:self.contactsNavigationController - transitionType:DWContainerTransitionType_WithoutAnimation]; - - break; - } - case DWTabBarViewButtonType_Others: { - if (self.currentController == self.menuNavigationController) { - return; - } - - [self transitionToController:self.menuNavigationController - transitionType:DWContainerTransitionType_WithoutAnimation]; - - break; - } - } - [tabBarView updateSelectedTabButton:buttonType]; -} - -- (void)tabBarViewDidOpenPayments:(DWTabBarView *)tabBarView { - [self showPaymentsControllerWithActivePage:DWPaymentsViewControllerIndex_None]; -} - -- (void)tabBarViewDidClosePayments:(DWTabBarView *)tabBarView { - - [self tabBarViewDidClosePayments:tabBarView completion:nil]; -} - -/// helper -- (void)tabBarViewDidClosePayments:(DWTabBarView *)tabBarView completion:(void (^_Nullable)(void))completion { - [tabBarView setPaymentsButtonOpened:NO]; - - if (![self.currentController.topController isKindOfClass:[DWPaymentsViewController class]]) { - self.tabBarView.userInteractionEnabled = YES; - - if (completion) - completion(); - return; - } - - tabBarView.userInteractionEnabled = NO; - - [self.currentController.topController dismissViewControllerAnimated:YES - completion:^{ - self.tabBarView.userInteractionEnabled = YES; - if (completion) { - completion(); - } - }]; -} - -#pragma mark - DWPaymentsViewControllerDelegate - -- (void)paymentsViewControllerWantsToImportPrivateKey:(DWPaymentsViewController *)controller { - // Make sure we enable tabbar before showing the scanner - _tabBarView.userInteractionEnabled = YES; - [_tabBarView setPaymentsButtonOpened:NO]; - - [controller dismissViewControllerAnimated:YES - completion:^{ - [self performScanQRCodeAction]; - }]; -} - -- (void)paymentsViewControllerDidCancel:(DWPaymentsViewController *)controller { - [self tabBarViewDidClosePayments:self.tabBarView]; -} - -- (void)paymentsViewControllerDidFinishPayment:(DWPaymentsViewController *)controller - contact:(nullable id)contact { - [self tabBarViewDidClosePayments:self.tabBarView - completion:^{ - if (!contact) { - return; - } - - DWModalUserProfileViewController *profile = - [[DWModalUserProfileViewController alloc] initWithItem:contact - payModel:self.homeModel.payModel - dataProvider:self.homeModel.getDataProvider]; - [self presentViewController:profile animated:YES completion:nil]; - }]; -} - -#pragma mark - DWHomeViewControllerDelegate - -- (void)homeViewControllerShowReceivePayment:(DWHomeViewController *)controller { - [self showPaymentsControllerWithActivePage:DWPaymentsViewControllerIndex_Receive]; -} - -#pragma mark - DWWipeDelegate - -- (void)didWipeWallet { - [self.delegate didWipeWallet]; -} - -#pragma mark - DWMainMenuViewControllerDelegate - -- (void)mainMenuViewControllerImportPrivateKey:(DWMainMenuViewController *)controller { - [self performScanQRCodeAction]; -} - -- (void)mainMenuViewControllerOpenHomeScreen:(DWMainMenuViewController *)controller { - [self transitionToController:self.homeNavigationController - transitionType:DWContainerTransitionType_WithoutAnimation]; - [self.tabBarView updateSelectedTabButton:DWTabBarViewButtonType_Home]; -} - -#pragma mark - UINavigationControllerDelegate - -- (void)navigationController:(UINavigationController *)navigationController - willShowViewController:(UIViewController *)viewController - animated:(BOOL)animated { - [self setTabBarHiddenAnimated:viewController.hidesBottomBarWhenPushed animated:YES]; -} - -- (void)navigationController:(UINavigationController *)navigationController - didShowViewController:(UIViewController *)viewController - animated:(BOOL)animated { - [self setTabBarHiddenAnimated:viewController.hidesBottomBarWhenPushed animated:NO]; -} - -#pragma mark - Private - -- (DWNavigationController *)homeNavigationController { - if (!_homeNavigationController) { - DWHomeViewController *homeController = [[DWHomeViewController alloc] init]; - homeController.model = self.homeModel; - homeController.delegate = self; - self.homeController = homeController; - - _homeNavigationController = [[DWNavigationController alloc] initWithRootViewController:homeController]; - _homeNavigationController.delegate = self; - } - - return _homeNavigationController; -} - -- (DWNavigationController *)contactsNavigationController { - if (!_contactsNavigationController) { - DWContactsViewController *contactsController = [[DWContactsViewController alloc] initWithPayModel:self.homeModel.payModel dataProvider:self.homeModel.getDataProvider]; - - _contactsNavigationController = [[DWNavigationController alloc] initWithRootViewController:contactsController]; - _contactsNavigationController.delegate = self; - } - - return _contactsNavigationController; -} - -- (DWNavigationController *)menuNavigationController { - if (!_menuNavigationController) { - DWMainMenuViewController *menuController = - [[DWMainMenuViewController alloc] initWithBalanceDisplayOptions:self.homeModel.balanceDisplayOptions]; - menuController.delegate = self; - - _menuNavigationController = [[DWNavigationController alloc] initWithRootViewController:menuController]; - _menuNavigationController.delegate = self; - } - - return _menuNavigationController; -} - -- (void)setupView { - self.view.backgroundColor = [UIColor dw_secondaryBackgroundColor]; - - UIView *contentView = [[UIView alloc] initWithFrame:CGRectZero]; - contentView.translatesAutoresizingMaskIntoConstraints = NO; - contentView.backgroundColor = self.view.backgroundColor; - [self.view addSubview:contentView]; - self.contentView = contentView; - - DWTabBarView *tabBarView = [[DWTabBarView alloc] initWithFrame:CGRectZero]; - tabBarView.translatesAutoresizingMaskIntoConstraints = NO; - tabBarView.delegate = self; - [self.view addSubview:tabBarView]; - self.tabBarView = tabBarView; - - self.contentBottomConstraint = [contentView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor]; - self.tabBarBottomConstraint = [tabBarView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor]; - - [NSLayoutConstraint activateConstraints:@[ - [contentView.topAnchor constraintEqualToAnchor:self.view.topAnchor], - [contentView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], - [contentView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor], - - [tabBarView.topAnchor constraintEqualToAnchor:contentView.bottomAnchor], - [tabBarView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], - [tabBarView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor], - self.tabBarBottomConstraint, - ]]; -} - -- (void)showPaymentsControllerWithActivePage:(DWPaymentsViewControllerIndex)pageIndex { - self.tabBarView.userInteractionEnabled = NO; - [self.tabBarView setPaymentsButtonOpened:YES]; - - id homeModel = self.homeModel; - NSParameterAssert(homeModel); - id receiveModel = homeModel.receiveModel; - id payModel = homeModel.payModel; - id dataProvider = [homeModel getDataProvider]; - - DWPaymentsViewController *controller = [DWPaymentsViewController controllerWithReceiveModel:receiveModel - payModel:payModel - dataProvider:dataProvider]; - controller.delegate = self; - controller.currentState = pageIndex; - controller.demoMode = self.demoMode; - controller.demoDelegate = self.demoDelegate; - DWNavigationController *navigationController = - [[DWNavigationController alloc] initWithRootViewController:controller]; - navigationController.delegate = self; - navigationController.modalInPresentation = YES; - - if (self.demoMode) { - [self.demoDelegate presentModalController:navigationController sender:self]; - } - else { - [self.currentController.topController presentViewController:navigationController - animated:YES - completion:^{ - self.tabBarView.userInteractionEnabled = YES; - }]; - } -} - -- (void)setupControllers { - DWNavigationController *navigationController = self.homeNavigationController; - [self transitionToController:navigationController]; -} - -- (void)setTabBarHiddenAnimated:(BOOL)hidden animated:(BOOL)animated { - if (hidden) { - self.tabBarBottomConstraint.active = NO; - self.contentBottomConstraint.active = YES; - } - else { - self.contentBottomConstraint.active = NO; - self.tabBarBottomConstraint.active = YES; - } - - const CGFloat alpha = hidden ? 0.0 : 1.0; - - if (self.tabBarView.alpha == alpha) { - return; - } - - [UIView animateWithDuration:animated ? ANIMATION_DURATION : 0.0 - animations:^{ - [self.view layoutIfNeeded]; - - self.tabBarView.alpha = alpha; - }]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Main/MainTabbarController.swift b/DashWallet/Sources/UI/Main/MainTabbarController.swift new file mode 100644 index 000000000..507290fc4 --- /dev/null +++ b/DashWallet/Sources/UI/Main/MainTabbarController.swift @@ -0,0 +1,335 @@ +// +// Created by PT +// Copyright © 2023 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +// MARK: - MainTabbarTabs + +private enum MainTabbarTabs: Int, CaseIterable { + case home + case payment + case more +} + +extension MainTabbarTabs { + var isEmpty: Bool { + self == .payment + } + + var icon: UIImage { + let name: String + + switch self { + case .home: + name = "tabbar_home_icon" + case .payment: + return UIImage() + case .more: + name = "tabbar_other_icon" + } + + return UIImage(named: name)! + } +} + +// MARK: - MainTabbarController + +@objc +class MainTabbarController: UITabBarController { + static let kAnimationDuration: TimeInterval = 0.35 + + weak var homeController: DWHomeViewController? + // weak var contactsNavigationController: DWContacts? + weak var menuNavigationController: DWMainMenuViewController? + + // TODO: Refactor this and send notification about wiped wallet instead of chaining the delegate + @objc + weak var wipeDelegate: DWWipeDelegate? + + private var paymentButton: PaymentButton! + + @objc + var isDemoMode = false + + @objc + weak var demoDelegate: DWDemoDelegate? + + // TODO: Move it out from here and initialize the model inside home view controller + @objc + var homeModel: DWHomeProtocol! + + @objc + init(homeModel: DWHomeProtocol) { + super.init(nibName: nil, bundle: nil) + + self.homeModel = homeModel + configureControllers() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Actions + + @objc + private func paymentButtonAction() { + showPaymentsController(withActivePage: .none) + } + + // MARK: Life Cycle + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + tabBar.addSubview(paymentButton) + } + + override func viewDidLoad() { + super.viewDidLoad() + + delegate = self + configureHierarchy() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + // Add Payment Button again to make sure it's at the top + tabBar.addSubview(paymentButton) + } +} + +// MARK: - Private +extension MainTabbarController { + private func configureControllers() { + var viewControllers: [UIViewController] = [] + + // Home + var item = UITabBarItem(title: nil, image: MainTabbarTabs.home.icon, tag: 0) + item.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0) + + let homeVC = DWHomeViewController() + homeVC.delegate = self + homeVC.model = homeModel + + var nvc = BaseNavigationController(rootViewController: homeVC) + nvc.tabBarItem = item + viewControllers.append(nvc) + + // Payment + item = UITabBarItem(title: "", image: UIImage(), tag: 1) + item.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0) + + let vc = EmptyController() + vc.tabBarItem = item + viewControllers.append(vc) + + // More + item = UITabBarItem(title: nil, image: MainTabbarTabs.more.icon, tag: 2) + item.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0) + let menuVC = DWMainMenuViewController() + menuVC.delegate = self + + nvc = BaseNavigationController(rootViewController: menuVC) + nvc.tabBarItem = item + viewControllers.append(nvc) + + self.viewControllers = viewControllers + } + + private func configureHierarchy() { + paymentButton = PaymentButton() + paymentButton.translatesAutoresizingMaskIntoConstraints = false + paymentButton.addTarget(self, action: #selector(paymentButtonAction), for: .touchUpInside) + tabBar.addSubview(paymentButton) + + NSLayoutConstraint.activate([ + paymentButton.centerXAnchor.constraint(equalTo: tabBar.centerXAnchor), + paymentButton.topAnchor.constraint(equalTo: tabBar.topAnchor, constant: UIDevice.hasHomeIndicator ? 4 : 1), + + paymentButton.widthAnchor.constraint(equalToConstant: PaymentButton.kCenterCircleSize), + paymentButton.heightAnchor.constraint(equalToConstant: PaymentButton.kCenterCircleSize), + ]) + + tabBar.barTintColor = .dw_background() + tabBar.tintColor = .dw_dashBlue() + tabBar.unselectedItemTintColor = .dw_tabbarInactiveButton() + } + + private func closePayments(completion: (() -> Void)? = nil) { + paymentButton.isOpened = false + + guard let currentController = selectedViewController, + currentController.topController() is PaymentsViewController else { + tabBar.isUserInteractionEnabled = true + completion?() + return + } + + tabBar.isUserInteractionEnabled = false + + currentController.topController().dismiss(animated: true) { + self.tabBar.isUserInteractionEnabled = true + completion?() + } + } + +// private func contactsNavigationController() -> DWNavigationController { +// if contactsNavigationController == nil { +// let contactsController = DWContactsViewController(payModel: homeModel.payModel, dataProvider: homeModel.getDataProvider) +// +// contactsNavigationController = DWNavigationController(rootViewController: contactsController) +// contactsNavigationController.delegate = self +// } +// +// return contactsNavigationController! +// } +} + +// MARK: - Public +extension MainTabbarController { + @objc + public func performScanQRCodeAction() { + dismiss(animated: false, completion: nil) + selectedIndex = MainTabbarTabs.home.rawValue + homeController?.performScanQRCodeAction() + } + + @objc + public func performPay(to url: URL) { + dismiss(animated: false, completion: nil) + selectedIndex = MainTabbarTabs.home.rawValue + homeController?.performPay(to: url) + } + + @objc + public func handleFile(_ file: Data) { + dismiss(animated: false, completion: nil) + selectedIndex = MainTabbarTabs.home.rawValue + homeController?.handleFile(file) + } + + @objc + public func openPaymentsScreen() { + assert(isDemoMode, "Invalid usage. Should be used in Demo mode only") + showPaymentsController(withActivePage: .pay) + } + + @objc + public func closePaymentsScreen() { + assert(isDemoMode, "Invalid usage. Should be used in Demo mode only") + closePayments() + } +} + +// MARK: DWMainMenuViewControllerDelegate + +extension MainTabbarController: DWMainMenuViewControllerDelegate { + func mainMenuViewControllerImportPrivateKey(_ controller: DWMainMenuViewController) { + performScanQRCodeAction() + } + + func mainMenuViewControllerOpenHomeScreen(_ controller: DWMainMenuViewController) { + selectedIndex = MainTabbarTabs.home.rawValue + } +} + +// MARK: DWWipeDelegate + +extension MainTabbarController: DWWipeDelegate { + func didWipeWallet() { + wipeDelegate?.didWipeWallet() + } +} + +// MARK: PaymentsViewControllerDelegate + +extension MainTabbarController: PaymentsViewControllerDelegate { + func paymentsViewControllerWantsToImportPrivateKey(_ controller: PaymentsViewController) { + // Make sure we enable tabbar before showing the scanner + tabBar.isUserInteractionEnabled = true + paymentButton.isOpened = false + + controller.dismiss(animated: true) { + self.performScanQRCodeAction() + } + } + + func paymentsViewControllerDidCancel(_ controller: PaymentsViewController) { + closePayments() + } + + func paymentsViewControllerDidFinishPayment(_ controller: PaymentsViewController, contact: DWDPBasicUserItem?) { + closePayments { + // TODO: DashPay +// guard let contact else { +// return +// } +// +// let profile = DWModalUserProfileViewController(item: contact, +// payModel: self.homeModel.payModel, +// dataProvider: self.homeModel.getDataProvider) +// self.present(profile, animated: true, completion: nil) + } + } +} + +// MARK: DWHomeViewControllerDelegate + +extension MainTabbarController: DWHomeViewControllerDelegate { + func showPaymentsController(withActivePage pageIndex: NSInteger) { + showPaymentsController(withActivePage: PaymentsViewControllerState(rawValue: pageIndex)!) + } + + func showPaymentsController(withActivePage pageIndex: PaymentsViewControllerState) { + tabBar.isUserInteractionEnabled = false + paymentButton.isOpened = true + + let receiveModel = DWReceiveModel() + let payModel = DWPayModel() + + let controller = PaymentsViewController.controller(withReceiveModel: receiveModel, + payModel: payModel) + + controller.delegate = self + controller.currentState = pageIndex + controller.demoMode = isDemoMode + controller.demoDelegate = demoDelegate + + let navigationController = BaseNavigationController(rootViewController: controller) + navigationController.isModalInPresentation = true + + if isDemoMode { + demoDelegate?.presentModalController(navigationController, sender: self) + } else { + selectedViewController?.topController().present(navigationController, animated: true) { + self.tabBar.isUserInteractionEnabled = true + } + } + } +} + +extension MainTabbarController: UITabBarControllerDelegate { + func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { + !(viewController is EmptyController) + } +} + +// MARK: - EmptyController + +private final class EmptyController: UIViewController { } diff --git a/DashWallet/Sources/UI/Main/Views/DWPaymentsButton.h b/DashWallet/Sources/UI/Main/Views/DWPaymentsButton.h deleted file mode 100644 index a6021b41f..000000000 --- a/DashWallet/Sources/UI/Main/Views/DWPaymentsButton.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2020 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface DWPaymentsButton : UIButton - -@property (nonatomic, assign) BOOL opened; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Main/Views/DWPaymentsButton.m b/DashWallet/Sources/UI/Main/Views/DWPaymentsButton.m deleted file mode 100644 index c3d45af64..000000000 --- a/DashWallet/Sources/UI/Main/Views/DWPaymentsButton.m +++ /dev/null @@ -1,22 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2020 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWPaymentsButton.h" - -@implementation DWPaymentsButton - -@end diff --git a/DashWallet/Sources/UI/Main/Views/DWTabBarButton.h b/DashWallet/Sources/UI/Main/Views/DWTabBarButton.h deleted file mode 100644 index afd7b940b..000000000 --- a/DashWallet/Sources/UI/Main/Views/DWTabBarButton.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -typedef NS_ENUM(NSUInteger, DWTabBarButtonType) { - DWTabBarButtonType_Home, - DWTabBarButtonType_Contacts, - DWTabBarButtonType_Discover, - DWTabBarButtonType_Others, -}; - -@interface DWTabBarButton : UIControl - -- (instancetype)initWithType:(DWTabBarButtonType)type; - -- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; -- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE; -- (instancetype)init NS_UNAVAILABLE; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Main/Views/DWTabBarButton.m b/DashWallet/Sources/UI/Main/Views/DWTabBarButton.m deleted file mode 100644 index dac5c8678..000000000 --- a/DashWallet/Sources/UI/Main/Views/DWTabBarButton.m +++ /dev/null @@ -1,84 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWTabBarButton.h" - -#import - -#import "DWUIKit.h" - -NS_ASSUME_NONNULL_BEGIN - -static UIColor *ActiveButtonColor(void) { - return [UIColor dw_dashBlueColor]; -} - -static UIColor *InactiveButtonColor(void) { - return [UIColor dw_tabbarInactiveButtonColor]; -} - - -@interface DWTabBarButton () - -@property (readonly, strong, nonatomic) UIImageView *iconImageView; - -@end - -@implementation DWTabBarButton - -- (instancetype)initWithType:(DWTabBarButtonType)type { - self = [super initWithFrame:CGRectZero]; - if (self) { - UIImage *image = nil; - switch (type) { - case DWTabBarButtonType_Home: - image = [UIImage imageNamed:@"tabbar_home_icon"]; - break; - case DWTabBarButtonType_Contacts: - image = [UIImage imageNamed:@"tabbar_contacts_icon"]; - break; - case DWTabBarButtonType_Discover: - image = [UIImage imageNamed:@"tabbar_discover_icon"]; - break; - case DWTabBarButtonType_Others: - image = [UIImage imageNamed:@"tabbar_other_icon"]; - break; - } - - UIImage *activeImage = [image ds_imageWithTintColor:ActiveButtonColor()]; - UIImage *inactiveImage = [image ds_imageWithTintColor:InactiveButtonColor()]; - - UIImageView *iconImageView = [[UIImageView alloc] initWithImage:inactiveImage - highlightedImage:activeImage]; - iconImageView.frame = self.bounds; - iconImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - iconImageView.contentMode = UIViewContentModeCenter; - [self addSubview:iconImageView]; - _iconImageView = iconImageView; - } - return self; -} - -- (void)setSelected:(BOOL)selected { - [super setSelected:selected]; - - self.iconImageView.highlighted = selected; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Main/Views/DWTabBarView.h b/DashWallet/Sources/UI/Main/Views/DWTabBarView.h deleted file mode 100644 index 9f061a6ba..000000000 --- a/DashWallet/Sources/UI/Main/Views/DWTabBarView.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -typedef NS_ENUM(NSUInteger, DWTabBarViewButtonType) { - DWTabBarViewButtonType_Home, - DWTabBarViewButtonType_Contacts, - DWTabBarViewButtonType_Others, -}; - -@class DWTabBarView; - -@protocol DWTabBarViewDelegate - -- (void)tabBarView:(DWTabBarView *)tabBarView didTapButtonType:(DWTabBarViewButtonType)buttonType; -- (void)tabBarViewDidOpenPayments:(DWTabBarView *)tabBarView; -- (void)tabBarViewDidClosePayments:(DWTabBarView *)tabBarView; - -@end - -@interface DWTabBarView : UIView - -@property (nullable, nonatomic, weak) id delegate; - -- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE; - -- (void)setPaymentsButtonOpened:(BOOL)opened; -- (void)updateSelectedTabButton:(DWTabBarViewButtonType)type; - -- (void)togglePaymentsOpenState; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Main/Views/DWTabBarView.m b/DashWallet/Sources/UI/Main/Views/DWTabBarView.m deleted file mode 100644 index 872ee4aee..000000000 --- a/DashWallet/Sources/UI/Main/Views/DWTabBarView.m +++ /dev/null @@ -1,246 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWTabBarView.h" - -#import "DWPaymentsButton.h" -#import "DWSharedUIConstants.h" -#import "DWTabBarButton.h" -#import "DWUIKit.h" - -NS_ASSUME_NONNULL_BEGIN - -static CGFloat const DW_TABBAR_HEIGHT = 64.0; -static CGFloat const TABBAR_HEIGHT_LARGE = 77.0; -static CGFloat const TABBAR_BORDER_WIDTH = 1.0; -static CGFloat const CENTER_CIRCLE_SIZE = 47.0; - -@interface DWTabBarView () - -@property (nonatomic, strong) CALayer *topLineLayer; - -@property (nonatomic, copy) NSArray *buttons; -@property (nonatomic, strong) DWTabBarButton *homeButton; -@property (nonatomic, strong) DWTabBarButton *contactsButton; -@property (nonatomic, strong) DWPaymentsButton *paymentsButton; -@property (nonatomic, strong) DWTabBarButton *discoverButton; -@property (nonatomic, strong) DWTabBarButton *othersButton; - -@end - -@implementation DWTabBarView - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - self.backgroundColor = [UIColor dw_backgroundColor]; - self.clipsToBounds = NO; - - CALayer *topLineLayer = [CALayer layer]; - topLineLayer.backgroundColor = [UIColor dw_tabbarBorderColor].CGColor; - [self.layer addSublayer:topLineLayer]; - _topLineLayer = topLineLayer; - - NSMutableArray *buttons = [NSMutableArray array]; - - { - DWTabBarButton *button = [[DWTabBarButton alloc] initWithType:DWTabBarButtonType_Home]; - [button addTarget:self - action:@selector(tabBarButtonAction:) - forControlEvents:UIControlEventTouchUpInside]; - [self addSubview:button]; - [buttons addObject:button]; - _homeButton = button; - -#if SNAPSHOT - button.accessibilityIdentifier = @"tabbar_home_button"; -#endif /* SNAPSHOT */ - } - - // { - // DWTabBarButton *button = [[DWTabBarButton alloc] initWithType:DWTabBarButtonType_Contacts]; - // [button addTarget:self - // action:@selector(tabBarButtonAction:) - // forControlEvents:UIControlEventTouchUpInside]; - // [self addSubview:button]; - // [buttons addObject:button]; - // _contactsButton = button; - // } - - { - DWPaymentsButton *button = [[DWPaymentsButton alloc] initWithFrame:CGRectZero]; - button.backgroundColor = [UIColor dw_dashBlueColor]; - [button setImage:[UIImage imageNamed:@"tabbar_pay_button"] forState:UIControlStateNormal]; - button.layer.cornerRadius = CENTER_CIRCLE_SIZE / 2.0; - button.layer.masksToBounds = YES; - [button addTarget:self - action:@selector(paymentsButtonAction:) - forControlEvents:UIControlEventTouchUpInside]; - [self addSubview:button]; - [buttons addObject:button]; - _paymentsButton = button; - -#if SNAPSHOT - button.accessibilityIdentifier = @"tabbar_payments_button"; -#endif /* SNAPSHOT */ - } - - // { - // DWTabBarButton *button = [[DWTabBarButton alloc] initWithType:DWTabBarButtonType_Discover]; - // [button addTarget:self - // action:@selector(tabBarButtonAction:) - // forControlEvents:UIControlEventTouchUpInside]; - // [self addSubview:button]; - // [buttons addObject:button]; - // _discoverButton = button; - // } - - { - DWTabBarButton *button = [[DWTabBarButton alloc] initWithType:DWTabBarButtonType_Others]; - [button addTarget:self - action:@selector(tabBarButtonAction:) - forControlEvents:UIControlEventTouchUpInside]; - [self addSubview:button]; - [buttons addObject:button]; - _othersButton = button; - -#if SNAPSHOT - button.accessibilityIdentifier = @"tabbar_menu_button"; -#endif /* SNAPSHOT */ - } - - _buttons = [buttons copy]; - - [self updateSelectedTabButton:DWTabBarViewButtonType_Home]; - } - return self; -} - -- (CGSize)intrinsicContentSize { - return CGSizeMake(UIViewNoIntrinsicMetric, - DEVICE_HAS_HOME_INDICATOR ? TABBAR_HEIGHT_LARGE : DW_TABBAR_HEIGHT); -} - -- (void)layoutSubviews { - [super layoutSubviews]; - - NSAssert(self.buttons.count > 0, @"Invalid state"); - - const CGSize size = self.bounds.size; - const CGFloat buttonWidth = size.width / self.buttons.count; - CGFloat x = 0.0; - for (UIButton *button in self.buttons) { - if (button != self.paymentsButton) { - button.frame = CGRectMake(x, 0.0, buttonWidth, MIN(DW_TABBAR_HEIGHT, size.height)); - } - - x += buttonWidth; - } - - self.topLineLayer.frame = CGRectMake(0, 0, size.width, TABBAR_BORDER_WIDTH); - - self.paymentsButton.frame = CGRectMake((size.width - CENTER_CIRCLE_SIZE) / 2.0, - (size.height - CENTER_CIRCLE_SIZE) / 2.0 - 5.0, - CENTER_CIRCLE_SIZE, - CENTER_CIRCLE_SIZE); -} - -- (void)traitCollectionDidChange:(nullable UITraitCollection *)previousTraitCollection { - [super traitCollectionDidChange:previousTraitCollection]; - - self.backgroundColor = [UIColor dw_backgroundColor]; - - UIColor *borderColor = [UIColor dw_tabbarBorderColor]; - self.topLineLayer.backgroundColor = borderColor.CGColor; -} - -- (void)setPaymentsButtonOpened:(BOOL)opened { - self.paymentsButton.opened = opened; -} - -- (void)togglePaymentsOpenState { - [self paymentsButtonAction:self.paymentsButton]; -} - -#pragma mark - Actions - -- (void)paymentsButtonAction:(DWPaymentsButton *)sender { - if (sender.opened == NO) { - [self.delegate tabBarViewDidOpenPayments:self]; - } - else { - [self.delegate tabBarViewDidClosePayments:self]; - } -} - -- (void)tabBarButtonAction:(UIView *)sender { - if (self.paymentsButton.opened) { - [self.delegate tabBarViewDidClosePayments:self]; - } - else { - DWTabBarViewButtonType type; - if (sender == self.homeButton) { - type = DWTabBarViewButtonType_Home; - } - else if (sender == self.contactsButton) { - type = DWTabBarViewButtonType_Contacts; - } - else if (sender == self.othersButton) { - type = DWTabBarViewButtonType_Others; - } - else if (sender == self.discoverButton) { - // TODO: DP fix me - type = DWTabBarViewButtonType_Contacts; - } - else { - type = DWTabBarViewButtonType_Home; - NSAssert(NO, @"Invalid sender"); - } - - [self.delegate tabBarView:self - didTapButtonType:type]; - } -} - -- (void)updateSelectedTabButton:(DWTabBarViewButtonType)type { - self.othersButton.selected = NO; - self.contactsButton.selected = NO; - self.discoverButton.selected = NO; - self.homeButton.selected = NO; - - switch (type) { - case DWTabBarViewButtonType_Home: { - self.homeButton.selected = YES; - - break; - } - case DWTabBarViewButtonType_Contacts: { - self.contactsButton.selected = YES; - - break; - } - case DWTabBarViewButtonType_Others: { - self.othersButton.selected = YES; - - break; - } - } -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Home/Models/BalanceModel.swift b/DashWallet/Sources/UI/Main/Views/PaymentButton.swift similarity index 50% rename from DashWallet/Sources/UI/Home/Models/BalanceModel.swift rename to DashWallet/Sources/UI/Main/Views/PaymentButton.swift index 951beb70c..538217221 100644 --- a/DashWallet/Sources/UI/Home/Models/BalanceModel.swift +++ b/DashWallet/Sources/UI/Main/Views/PaymentButton.swift @@ -1,5 +1,5 @@ // -// Created by tkhp +// Created by PT // Copyright © 2023 Dash Core Group. All rights reserved. // // Licensed under the MIT License (the "License"); @@ -15,32 +15,25 @@ // limitations under the License. // -import Foundation +import UIKit -@objc(DWBalanceModel) -final class BalanceModel: NSObject { - @objc - let value: UInt64 +@objc(DWPaymentsButton) +final class PaymentButton: UIButton { + static let kCenterCircleSize: CGFloat = 47.0 @objc - init(with value: UInt64) { - self.value = value + var isOpened = false - super.init() - } + override init(frame: CGRect) { + super.init(frame: frame) - @objc - func dashAmountStringWithFont(_ font: UIFont, tintColor: UIColor) -> NSAttributedString { - NSAttributedString.dashAttributedString(for: value, tintColor: tintColor, font: font) + backgroundColor = .dw_dashBlue() + setImage(UIImage(named: "tabbar_pay_button")!, for: .normal) + layer.cornerRadius = PaymentButton.kCenterCircleSize / 2.0 + layer.masksToBounds = true } - @objc - func mainAmountString() -> String { - value.formattedDashAmount - } - - @objc - func fiatAmountString() -> String { - CurrencyExchanger.shared.fiatAmountString(for: value.dashAmount) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") } } diff --git a/DashWallet/Sources/UI/Menu/Main/DWMainMenuViewController.h b/DashWallet/Sources/UI/Menu/Main/DWMainMenuViewController.h index 14e072a6a..8b3495960 100644 --- a/DashWallet/Sources/UI/Menu/Main/DWMainMenuViewController.h +++ b/DashWallet/Sources/UI/Menu/Main/DWMainMenuViewController.h @@ -17,32 +17,19 @@ #import -#import "DWSyncProtocol.h" #import "DWWipeDelegate.h" -#import "dashwallet-Swift.h" NS_ASSUME_NONNULL_BEGIN @class DWMainMenuViewController; -@protocol DWBalanceDisplayOptionsProtocol; - -@protocol DWMainMenuViewControllerDelegate - -- (void)mainMenuViewControllerImportPrivateKey:(DWMainMenuViewController *)controller; -- (void)mainMenuViewControllerOpenHomeScreen:(DWMainMenuViewController *)controller; -- (void)showPaymentsControllerWithActivePage:(DWPaymentsViewControllerIndex)pageIndex; - -@end +@protocol DWMainMenuViewControllerDelegate; @interface DWMainMenuViewController : UIViewController @property (nullable, nonatomic, weak) id delegate; -- (instancetype)initWithBalanceDisplayOptions:(id)balanceDisplayOptions; - - (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil NS_UNAVAILABLE; - (nullable instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE; -- (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; @end diff --git a/DashWallet/Sources/UI/Menu/Main/DWMainMenuViewController.m b/DashWallet/Sources/UI/Menu/Main/DWMainMenuViewController.m index 89b6e571a..0bf0baf2e 100644 --- a/DashWallet/Sources/UI/Menu/Main/DWMainMenuViewController.m +++ b/DashWallet/Sources/UI/Menu/Main/DWMainMenuViewController.m @@ -39,7 +39,6 @@ @interface DWMainMenuViewController () @property (nonatomic, strong) DWMainMenuContentView *view; -@property (nonatomic, strong) id balanceDisplayOptions; @end @@ -47,11 +46,9 @@ @implementation DWMainMenuViewController @dynamic view; -- (instancetype)initWithBalanceDisplayOptions:(id)balanceDisplayOptions { +- (instancetype)init { self = [super initWithNibName:nil bundle:nil]; if (self) { - _balanceDisplayOptions = balanceDisplayOptions; - self.title = NSLocalizedString(@"More", nil); } return self; @@ -108,7 +105,7 @@ - (void)mainMenuContentView:(DWMainMenuContentView *)view didSelectMenuItem:(id< break; } case DWMainMenuItemType_Security: { - DWSecurityMenuViewController *controller = [[DWSecurityMenuViewController alloc] initWithBalanceDisplayOptions:self.balanceDisplayOptions]; + DWSecurityMenuViewController *controller = [[DWSecurityMenuViewController alloc] init]; controller.delegate = self.delegate; [self.navigationController pushViewController:controller animated:YES]; diff --git a/DashWallet/Sources/UI/Home/Protocols/DWBalanceProtocol.h b/DashWallet/Sources/UI/Menu/Main/DWMainMenuViewControllerDelegate.h similarity index 53% rename from DashWallet/Sources/UI/Home/Protocols/DWBalanceProtocol.h rename to DashWallet/Sources/UI/Menu/Main/DWMainMenuViewControllerDelegate.h index ef399e84e..8b6ce5183 100644 --- a/DashWallet/Sources/UI/Home/Protocols/DWBalanceProtocol.h +++ b/DashWallet/Sources/UI/Menu/Main/DWMainMenuViewControllerDelegate.h @@ -1,6 +1,6 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. +// +// Created by PT +// Copyright © 2023 Dash Core Group. All rights reserved. // // Licensed under the MIT License (the "License"); // you may not use this file except in compliance with the License. @@ -17,18 +17,15 @@ #import -#import "DWBalanceDisplayOptionsProtocol.h" -#import "DWSyncContainerProtocol.h" - -NS_ASSUME_NONNULL_BEGIN - -@class DWBalanceModel; +#ifndef DWMainMenuViewControllerDelegate_h +#define DWMainMenuViewControllerDelegate_h -@protocol DWBalanceProtocol +@protocol DWMainMenuViewControllerDelegate -@property (readonly, nullable, nonatomic, strong) DWBalanceModel *balanceModel; -@property (readonly, nonatomic, strong) id balanceDisplayOptions; +- (void)mainMenuViewControllerImportPrivateKey:(DWMainMenuViewController *)controller; +- (void)mainMenuViewControllerOpenHomeScreen:(DWMainMenuViewController *)controller; +- (void)showPaymentsControllerWithActivePage:(NSInteger)pageIndex; @end -NS_ASSUME_NONNULL_END +#endif /* DWMainMenuViewControllerDelegate_h */ diff --git a/DashWallet/Sources/UI/Menu/Security/DWSecurityMenuModel.h b/DashWallet/Sources/UI/Menu/Security/DWSecurityMenuModel.h index 7fc283333..def739bc0 100644 --- a/DashWallet/Sources/UI/Menu/Security/DWSecurityMenuModel.h +++ b/DashWallet/Sources/UI/Menu/Security/DWSecurityMenuModel.h @@ -21,8 +21,6 @@ NS_ASSUME_NONNULL_BEGIN -@protocol DWBalanceDisplayOptionsProtocol; - @interface DWSecurityMenuModel : NSObject @property (readonly, assign, nonatomic) BOOL hasTouchID; @@ -35,8 +33,6 @@ NS_ASSUME_NONNULL_BEGIN - (void)setBiometricsEnabled:(BOOL)enabled completion:(void (^)(BOOL success))completion; -- (instancetype)initWithBalanceDisplayOptions:(id)balanceDisplayOptions; -- (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; @end diff --git a/DashWallet/Sources/UI/Menu/Security/DWSecurityMenuModel.m b/DashWallet/Sources/UI/Menu/Security/DWSecurityMenuModel.m index 0713b851b..b687f0b87 100644 --- a/DashWallet/Sources/UI/Menu/Security/DWSecurityMenuModel.m +++ b/DashWallet/Sources/UI/Menu/Security/DWSecurityMenuModel.m @@ -21,7 +21,6 @@ #import #import "DWAdvancedSecurityModel.h" -#import "DWBalanceDisplayOptionsProtocol.h" #import "DWBiometricAuthModel.h" #import "DWGlobalOptions.h" @@ -53,17 +52,15 @@ - (instancetype)initWithTitle:(NSString *)title value:(uint64_t)value { @interface DWSecurityMenuModel () @property (assign, nonatomic) BOOL biometricsEnabled; -@property (readonly, strong, nonatomic) id balanceDisplayOptions; @property (readonly, nonatomic, strong) DWBiometricAuthModel *biometricAuthModel; @end @implementation DWSecurityMenuModel -- (instancetype)initWithBalanceDisplayOptions:(id)balanceDisplayOptions { +- (instancetype)init { self = [super init]; if (self) { - _balanceDisplayOptions = balanceDisplayOptions; _biometricAuthModel = [[DWBiometricAuthModel alloc] init]; @@ -91,7 +88,6 @@ - (BOOL)balanceHidden { - (void)setBalanceHidden:(BOOL)balanceHidden { [DWGlobalOptions sharedInstance].balanceHidden = balanceHidden; - self.balanceDisplayOptions.balanceHidden = balanceHidden; } - (void)changePinContinueBlock:(void (^)(BOOL allowed))continueBlock { diff --git a/DashWallet/Sources/UI/Menu/Security/DWSecurityMenuViewController.h b/DashWallet/Sources/UI/Menu/Security/DWSecurityMenuViewController.h index b1bec840a..90e86e7ad 100644 --- a/DashWallet/Sources/UI/Menu/Security/DWSecurityMenuViewController.h +++ b/DashWallet/Sources/UI/Menu/Security/DWSecurityMenuViewController.h @@ -21,17 +21,15 @@ NS_ASSUME_NONNULL_BEGIN -@protocol DWBalanceDisplayOptionsProtocol; @interface DWSecurityMenuViewController : UIViewController @property (nullable, nonatomic, weak) id delegate; -- (instancetype)initWithBalanceDisplayOptions:(id)balanceDisplayOptions; +- (instancetype)init; - (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil NS_UNAVAILABLE; - (nullable instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE; -- (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; @end diff --git a/DashWallet/Sources/UI/Menu/Security/DWSecurityMenuViewController.m b/DashWallet/Sources/UI/Menu/Security/DWSecurityMenuViewController.m index 6fd8024c9..bcb78e8cc 100644 --- a/DashWallet/Sources/UI/Menu/Security/DWSecurityMenuViewController.m +++ b/DashWallet/Sources/UI/Menu/Security/DWSecurityMenuViewController.m @@ -37,7 +37,6 @@ @interface DWSecurityMenuViewController () -@property (readonly, nonatomic, strong) id balanceDisplayOptions; @property (null_resettable, nonatomic, strong) DWSecurityMenuModel *model; @property (nonatomic, strong) DWFormTableViewController *formController; @@ -45,11 +44,10 @@ @interface DWSecurityMenuViewController () )balanceDisplayOptions { +- (instancetype)init { self = [super initWithNibName:nil bundle:nil]; if (self) { - _balanceDisplayOptions = balanceDisplayOptions; - + self.title = NSLocalizedString(@"Security", nil); self.hidesBottomBarWhenPushed = YES; } @@ -58,7 +56,7 @@ - (instancetype)initWithBalanceDisplayOptions:(id Void)?) { +// guard let modalController = self.modalController else { +// completion?() +// return +// } +// +// self.modalController = nil +// self.currentController.beginAppearanceTransition(true, animated: true) +// +// let childView = modalController.view +// modalController.willMove(toParentViewController: nil) +// +// UIView.animate(withDuration: self.transitionAnimationDuration, animations: { +// childView.alpha = 0.0 +// self.setNeedsStatusBarAppearanceUpdate() +// }) { finished in +// childView.removeFromSuperview() +// modalController.removeFromParentViewController() +// +// self.currentController.endAppearanceTransition() +// +// completion?() +// } +// } + +} diff --git a/DashWallet/Sources/UI/Onboarding/Stubs/DWBalanceDisplayOptionsStub.h b/DashWallet/Sources/UI/Onboarding/Stubs/DWBalanceDisplayOptionsStub.h deleted file mode 100644 index 7fe1367d4..000000000 --- a/DashWallet/Sources/UI/Onboarding/Stubs/DWBalanceDisplayOptionsStub.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -#import "DWBalanceDisplayOptionsProtocol.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface DWBalanceDisplayOptionsStub : NSObject - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Onboarding/Stubs/DWHomeModelStub.m b/DashWallet/Sources/UI/Onboarding/Stubs/DWHomeModelStub.m index dc01ec941..f9d322b62 100644 --- a/DashWallet/Sources/UI/Onboarding/Stubs/DWHomeModelStub.m +++ b/DashWallet/Sources/UI/Onboarding/Stubs/DWHomeModelStub.m @@ -17,39 +17,32 @@ #import "DWHomeModelStub.h" -#import "DWBalanceDisplayOptionsStub.h" #import "DWDashPayModel.h" #import "DWEnvironment.h" #import "DWPayModelStub.h" #import "DWReceiveModelStub.h" -#import "DWSyncModelStub.h" #import "DWTransactionListDataProviderStub.h" #import "DWTransactionStub.h" #import "dashwallet-Swift.h" NS_ASSUME_NONNULL_BEGIN -@interface DWHomeModelStub () +@interface DWHomeModelStub () @property (readonly, nonatomic, copy) NSArray *stubTxs; @property (readonly, nonatomic, strong) DWTransactionListDataProviderStub *dataProvider; -@property (nullable, nonatomic, strong) DWBalanceModel *balanceModel; - @property (readonly, nonatomic, strong) DWTransactionListDataSource *dataSource; @end @implementation DWHomeModelStub -@synthesize balanceDisplayOptions = _balanceDisplayOptions; @synthesize displayMode = _displayMode; @synthesize payModel = _payModel; @synthesize receiveModel = _receiveModel; @synthesize dashPayModel = _dashPayModel; -@synthesize shortcutsModel = _shortcutsModel; -@synthesize syncModel = _syncModel; @synthesize updatesObserver = _updatesObserver; @synthesize allDataSource = _allDataSource; @synthesize allowedToShowReclassifyYourTransactions = _allowedToShowReclassifyYourTransactions; @@ -57,7 +50,6 @@ @implementation DWHomeModelStub - (instancetype)init { self = [super init]; if (self) { - _syncModel = [[DWSyncModelStub alloc] init]; _dataProvider = [[DWTransactionListDataProviderStub alloc] init]; _stubTxs = [DWTransactionStub stubs]; @@ -66,9 +58,7 @@ - (instancetype)init { #if DASHPAY_ENABLED _dashPayModel = [[DWDashPayModel alloc] init]; // TODO: DP consider using stub #endif /* DASHPAY_ENABLED */ - _shortcutsModel = [[DWShortcutsModel alloc] initWithDataSource:self]; _payModel = [[DWPayModelStub alloc] init]; - _balanceDisplayOptions = [[DWBalanceDisplayOptionsStub alloc] init]; _allowedToShowReclassifyYourTransactions = NO; [self updateBalance]; @@ -108,19 +98,14 @@ - (BOOL)shouldShowWalletBackupReminder { return NO; } -- (BOOL)isJailbroken { - return NO; -} - - (BOOL)isWalletEmpty { return NO; } -- (void)reloadShortcuts { - [self.shortcutsModel reloadShortcuts]; +- (void)retrySyncing { } -- (void)retrySyncing { +- (void)checkCrowdNodeState { } - (void)registerForPushNotifications { @@ -155,7 +140,6 @@ - (void)walletBalanceDidChangeNotification { } - (void)updateBalance { - self.balanceModel = [[DWBalanceModel alloc] initWith:42 * DUFFS]; } - (void)reloadTxDataSource { @@ -165,18 +149,6 @@ - (void)reloadTxDataSource { [self.updatesObserver homeModel:self didUpdateDataSource:self.dataSource shouldAnimate:NO]; } -- (NSString *)supplementaryAmountString { - return [self.balanceModel fiatAmountString]; -} - -- (NSString *)mainAmountString { - return [self.balanceModel mainAmountString]; -} - -- (BOOL)isBalanceHidden { - return self.balanceDisplayOptions.balanceHidden; -} - @end NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Onboarding/Stubs/DWSyncModelStub.h b/DashWallet/Sources/UI/Onboarding/Stubs/DWSyncModelStub.h deleted file mode 100644 index 441b112a8..000000000 --- a/DashWallet/Sources/UI/Onboarding/Stubs/DWSyncModelStub.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -#import "DWSyncProtocol.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface DWSyncModelStub : NSObject - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Onboarding/Stubs/DWSyncModelStub.m b/DashWallet/Sources/UI/Onboarding/Stubs/DWSyncModelStub.m deleted file mode 100644 index 6ead90c57..000000000 --- a/DashWallet/Sources/UI/Onboarding/Stubs/DWSyncModelStub.m +++ /dev/null @@ -1,34 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWSyncModelStub.h" - -NS_ASSUME_NONNULL_BEGIN - -@implementation DWSyncModelStub - -- (DWSyncModelState)state { - return DWSyncModelState_SyncDone; -} - -- (float)progress { - return 1.0; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Onboarding/Stubs/DWTransactionStub.m b/DashWallet/Sources/UI/Onboarding/Stubs/DWTransactionStub.m index ba1df11db..7cd1b8b0c 100644 --- a/DashWallet/Sources/UI/Onboarding/Stubs/DWTransactionStub.m +++ b/DashWallet/Sources/UI/Onboarding/Stubs/DWTransactionStub.m @@ -21,7 +21,7 @@ NS_ASSUME_NONNULL_BEGIN -static UInt256 RandomUInt256() { +static UInt256 RandomUInt256(void) { return ((UInt256){.u64 = { ((uint64_t)arc4random() << 32) | (uint64_t)arc4random(), ((uint64_t)arc4random() << 32) | (uint64_t)arc4random(), diff --git a/DashWallet/Sources/UI/Operation Status/FailedOperationStatusViewController.swift b/DashWallet/Sources/UI/Operation Status/FailedOperationStatusViewController.swift index 77f971be3..75d49a515 100644 --- a/DashWallet/Sources/UI/Operation Status/FailedOperationStatusViewController.swift +++ b/DashWallet/Sources/UI/Operation Status/FailedOperationStatusViewController.swift @@ -29,7 +29,7 @@ final class FailedOperationStatusViewController: BaseViewController, NavigationB var cancelHandler: (() -> ())? var retryHandler: (() -> ())? var supportHandler: (() -> ())? - + var headerText: String! { didSet { titleLabel?.text = headerText @@ -41,7 +41,7 @@ final class FailedOperationStatusViewController: BaseViewController, NavigationB descriptionLabel?.text = descriptionText } } - + var supportButtonText: String! { didSet { contactSupportButton?.setTitle(supportButtonText, for: .normal) @@ -53,7 +53,7 @@ final class FailedOperationStatusViewController: BaseViewController, NavigationB retryHandler?() } - @IBAction + @IBAction func supportAction() { supportHandler?() } diff --git a/DashWallet/Sources/UI/Payment Controller/Enter Amount/ProvideAmountViewController.swift b/DashWallet/Sources/UI/Payment Controller/Enter Amount/ProvideAmountViewController.swift index bcf68620d..a0eac121a 100644 --- a/DashWallet/Sources/UI/Payment Controller/Enter Amount/ProvideAmountViewController.swift +++ b/DashWallet/Sources/UI/Payment Controller/Enter Amount/ProvideAmountViewController.swift @@ -45,10 +45,10 @@ final class ProvideAmountViewController: SendAmountViewController { override func actionButtonAction(sender: UIView) { guard validateInputAmount() else { return } - + checkLeftoverBalance { [weak self] canContinue in guard canContinue, let wSelf = self else { return } - + wSelf.showActivityIndicator() let paymentCurrency: DWPaymentCurrency = wSelf.sendAmountModel.activeAmountType == .main ? .dash : .fiat DWGlobalOptions.sharedInstance().selectedPaymentCurrency = paymentCurrency diff --git a/DashWallet/Sources/UI/Payments/Amount/SendAmountViewController.swift b/DashWallet/Sources/UI/Payments/Amount/SendAmountViewController.swift index 5934a794a..f3edffe76 100644 --- a/DashWallet/Sources/UI/Payments/Amount/SendAmountViewController.swift +++ b/DashWallet/Sources/UI/Payments/Amount/SendAmountViewController.swift @@ -44,23 +44,26 @@ class SendAmountViewController: BaseAmountViewController { override func maxButtonAction() { sendAmountModel.selectAllFunds() } - + internal func checkLeftoverBalance(isCrowdNodeTransfer: Bool = false, completion: @escaping ((Bool) -> Void)) { if CrowdNodeDefaults.shared.lastKnownBalance <= 0 && !isCrowdNodeTransfer { // If CrowdNode balance is 0, then there is no need to check the leftover balance completion(true) return } - + // If CrowdNode balance isn't empty and the user sends DASH somewhere, // or if the user is making a CrowdNode deposit, then we need to check the leftover balance - + let account = DWEnvironment.sharedInstance().currentAccount let allAvailableFunds = account.maxOutputAmount - + if model.amount.plainAmount + CrowdNode.minimumLeftoverBalance > allAvailableFunds { let title = NSLocalizedString("Looks like you are emptying your Dash Wallet", comment: "Leftover balance warning") - let message = String.localizedStringWithFormat(NSLocalizedString("Please note, you will not be able to withdraw your funds from CowdNode to this wallet until you increase your balance to %@ Dash.", comment: "Leftover balance warning"), CrowdNode.minimumLeftoverBalance.formattedDashAmountWithoutCurrencySymbol) + let message = String + .localizedStringWithFormat(NSLocalizedString("Please note, you will not be able to withdraw your funds from CowdNode to this wallet until you increase your balance to %@ Dash.", + comment: "Leftover balance warning"), + CrowdNode.minimumLeftoverBalance.formattedDashAmountWithoutCurrencySymbol) let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: NSLocalizedString("Continue", comment: "Leftover balance warning"), style: .default, handler: { _ in completion(true) @@ -68,7 +71,7 @@ class SendAmountViewController: BaseAmountViewController { alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "Leftover balance warning"), style: .cancel, handler: { _ in completion(false) })) - self.present(alert, animated: true, completion: nil) + present(alert, animated: true, completion: nil) } else { completion(true) } diff --git a/DashWallet/Sources/UI/Payments/Pay/Cells/PayTableViewCell.swift b/DashWallet/Sources/UI/Payments/Pay/Cells/PayTableViewCell.swift index 3d1adb385..013046d19 100644 --- a/DashWallet/Sources/UI/Payments/Pay/Cells/PayTableViewCell.swift +++ b/DashWallet/Sources/UI/Payments/Pay/Cells/PayTableViewCell.swift @@ -1,4 +1,4 @@ -// +// // Created by PT // Copyright © 2023 Dash Core Group. All rights reserved. // @@ -19,10 +19,12 @@ import UIKit let MAX_ALLOWED_BUTTON_WIDTH: CGFloat = 108.0 +// MARK: - PayTableViewCell + class PayTableViewCell: UITableViewCell { @IBOutlet private weak var iconImageView: UIImageView! @IBOutlet private weak var titleLabel: UILabel! - + override func awakeFromNib() { super.awakeFromNib() titleLabel.font = UIFont.dw_font(forTextStyle: .subheadline) @@ -30,7 +32,7 @@ class PayTableViewCell: UITableViewCell { var model: DWPayOptionModel? { didSet { - guard let model = model else { return } + guard let model else { return } let type = model.type titleLabel.text = model.title diff --git a/DashWallet/Sources/UI/Payments/Pay/PayViewController.swift b/DashWallet/Sources/UI/Payments/Pay/PayViewController.swift index d8566a69e..710eed97f 100644 --- a/DashWallet/Sources/UI/Payments/Pay/PayViewController.swift +++ b/DashWallet/Sources/UI/Payments/Pay/PayViewController.swift @@ -1,4 +1,4 @@ -// +// // Created by PT // Copyright © 2023 Dash Core Group. All rights reserved. // @@ -17,26 +17,30 @@ import UIKit +// MARK: - PayViewControllerDelegate + @objc(DWPayViewControllerDelegate) protocol PayViewControllerDelegate: AnyObject { func payViewControllerDidFinishPayment(_ controller: PayViewController, contact: DWDPBasicUserItem?) } +// MARK: - PayViewController + @objc(DWPayViewController) class PayViewController: BaseViewController, PayableViewController { @IBOutlet weak var tableView: UITableView! - + @objc var paymentController: PaymentController! - + @objc var payModel: DWPayModelProtocol! var maxActionButtonWidth: CGFloat = 0 - + @objc - var demoMode: Bool = false - + var demoMode = false + @objc var delegate: PayViewControllerDelegate? @@ -51,7 +55,7 @@ class PayViewController: BaseViewController, PayableViewController { override func viewDidLoad() { super.viewDidLoad() - + configurePaymentController() configureHierarchy() } @@ -67,14 +71,14 @@ class PayViewController: BaseViewController, PayableViewController { } } -private extension PayViewController { - func configurePaymentController() { +extension PayViewController { + private func configurePaymentController() { paymentController = PaymentController() paymentController.delegate = self paymentController.presentationContextProvider = self } - - func configureHierarchy() { + + private func configureHierarchy() { let cellId = PayTableViewCell.reuseIdentifier let nib = UINib(nibName: cellId, bundle: nil) tableView.register(nib, forCellReuseIdentifier: cellId) @@ -89,10 +93,12 @@ private extension PayViewController { } } +// MARK: UITableViewDataSource, UITableViewDelegate + extension PayViewController: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return payModel.options.count + payModel.options.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { @@ -105,7 +111,7 @@ extension PayViewController: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let payOption = payModel.options[indexPath.row] - + switch payOption.type { case .scanQR: performScanQRCodeAction(delegate: self) @@ -121,43 +127,45 @@ extension PayViewController: UITableViewDataSource, UITableViewDelegate { +// MARK: DWQRScanModelDelegate + extension PayViewController: DWQRScanModelDelegate { func qrScanModel(_ viewModel: DWQRScanModel, didScanPaymentInput paymentInput: DWPaymentInput) { dismiss(animated: true) { [weak self] in self?.paymentController.performPayment(with: paymentInput) } } - + func qrScanModelDidCancel(_ viewModel: DWQRScanModel) { dismiss(animated: true) } } +// MARK: PaymentControllerDelegate, PaymentControllerPresentationContextProviding + extension PayViewController: PaymentControllerDelegate, PaymentControllerPresentationContextProviding { func presentationAnchorForPaymentController(_ controller: PaymentController) -> PaymentControllerPresentationAnchor { self } - + func paymentControllerDidFinishTransaction(_ controller: PaymentController, transaction: DSTransaction) { let model = TxDetailModel(transaction: transaction) let vc = SuccessTxDetailViewController(model: model) vc.modalPresentationStyle = .fullScreen vc.contactItem = paymentController.contactItem vc.delegate = self - self.present(vc, animated: true) - } - - func paymentControllerDidCancelTransaction(_ controller: PaymentController) { - - } - - func paymentControllerDidFailTransaction(_ controller: PaymentController) { - + present(vc, animated: true) } + + func paymentControllerDidCancelTransaction(_ controller: PaymentController) { } + + func paymentControllerDidFailTransaction(_ controller: PaymentController) { } } +// MARK: SuccessTxDetailViewControllerDelegate + extension PayViewController: SuccessTxDetailViewControllerDelegate { func txDetailViewControllerDidFinish(controller: SuccessTxDetailViewController) { - self.delegate?.payViewControllerDidFinishPayment(self, contact: paymentController.contactItem) + delegate?.payViewControllerDidFinishPayment(self, contact: paymentController.contactItem) } } diff --git a/DashWallet/Sources/UI/Payments/Pay/PayableViewController.swift b/DashWallet/Sources/UI/Payments/Pay/PayableViewController.swift index 67c8cd92a..6ae3f0fc4 100644 --- a/DashWallet/Sources/UI/Payments/Pay/PayableViewController.swift +++ b/DashWallet/Sources/UI/Payments/Pay/PayableViewController.swift @@ -1,4 +1,4 @@ -// +// // Created by PT // Copyright © 2023 Dash Core Group. All rights reserved. // @@ -17,6 +17,8 @@ import Foundation +// MARK: - PayableViewController + protocol PayableViewController: DWQRScanModelDelegate { var payModel: DWPayModelProtocol! { get } var paymentController: PaymentController! { get } @@ -24,7 +26,7 @@ protocol PayableViewController: DWQRScanModelDelegate { extension PayableViewController where Self: UIViewController { func payToAddressAction() { - guard let payModel = payModel else { return } + guard let payModel else { return } payModel.payToAddress { [weak self] success in guard let strongSelf = self else { return } @@ -42,34 +44,32 @@ extension PayableViewController where Self: UIViewController { } } } - + func performScanQRCodeAction(delegate: DWQRScanModelDelegate) { if let vc = presentedViewController, vc is DWQRScanViewController { return; } - + let controller = DWQRScanViewController() controller.model.delegate = delegate present(controller, animated: true, completion: nil) } - + func performNFCReadingAction() { payModel?.performNFCReading(completion: { [weak self] paymentInput in guard let strongSelf = self else { return } strongSelf.processPaymentInput(paymentInput) }) } - + func performPayToPasteboardAction() { guard let paymentInput = payModel?.pasteboardPaymentInput else { return } processPaymentInput(paymentInput) } - + func processPaymentInput(_ input: DWPaymentInput) { paymentController.performPayment(with: input) } } -extension PayableViewController where Self: UIViewController { - -} +extension PayableViewController where Self: UIViewController { } diff --git a/DashWallet/Sources/UI/Payments/PaymentsViewController.swift b/DashWallet/Sources/UI/Payments/PaymentsViewController.swift index 5b0ea3658..5fdacf819 100644 --- a/DashWallet/Sources/UI/Payments/PaymentsViewController.swift +++ b/DashWallet/Sources/UI/Payments/PaymentsViewController.swift @@ -1,4 +1,4 @@ -// +// // Created by PT // Copyright © 2023 Dash Core Group. All rights reserved. // @@ -17,18 +17,22 @@ import UIKit +// MARK: - PaymentsViewControllerState + @objc(DWPaymentsViewControllerIndex) enum PaymentsViewControllerState: Int { @objc(DWPaymentsViewControllerIndex_None) case none = -1 - + @objc(DWPaymentsViewControllerIndex_Receive) case receive = 0 - + @objc(DWPaymentsViewControllerIndex_Pay) case pay = 1 } +// MARK: - PaymentsViewControllerDelegate + @objc(DWPaymentsViewControllerDelegate) protocol PaymentsViewControllerDelegate: AnyObject { func paymentsViewControllerWantsToImportPrivateKey(_ controller: PaymentsViewController) @@ -36,73 +40,78 @@ protocol PaymentsViewControllerDelegate: AnyObject { func paymentsViewControllerDidFinishPayment(_ controller: PaymentsViewController, contact: DWDPBasicUserItem?) } +// MARK: - PaymentsViewController + @objc(DWPaymentsViewController) class PaymentsViewController: BaseViewController { @IBOutlet var segmentedControl: UISegmentedControl! @IBOutlet var containerView: UIView! @IBOutlet var closeButton: UIButton! - + @objc weak var delegate: PaymentsViewControllerDelegate? - + @objc - var demoMode: Bool = false - + var demoMode = false + @objc weak var demoDelegate: DWDemoDelegate? - + @objc var currentState: PaymentsViewControllerState = .pay { didSet { if currentState == .none { currentState = PaymentsViewControllerState(rawValue: DWGlobalOptions.sharedInstance().paymentsScreenCurrentTab)! } - + let idx = currentState.rawValue - + segmentedControl?.selectedSegmentIndex = idx pageController?.selectedIndex = idx } } - + private var receiveModel: DWReceiveModelProtocol! private var payModel: DWPayModelProtocol! - private var dataProvider: DWTransactionListDataProviderProtocol? - + private var payViewController: PayViewController! private var receiveViewController: ReceiveViewController! - + private var pageController: SendReceivePageController! - - @IBAction func segmentedControlAction() { + + @IBAction + func segmentedControlAction() { let idx = segmentedControl.selectedSegmentIndex pageController.setSelectedIndex(idx, animated: true) - + DWGlobalOptions.sharedInstance().paymentsScreenCurrentTab = idx } - - @IBAction func closeButtonAction() { + + @IBAction + func closeButtonAction() { delegate?.paymentsViewControllerDidCancel(self) } - + override func viewDidLoad() { super.viewDidLoad() - + configureHierarchy() } - + override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) } - + + class func controller() -> PaymentsViewController { + sb("Payments").vc(PaymentsViewController.self) + } + @objc class func controller(withReceiveModel receiveModel: DWReceiveModelProtocol?, - payModel: DWPayModelProtocol?, - dataProvider: DWTransactionListDataProviderProtocol?) -> PaymentsViewController { - let controller = sb("Payments").vc(PaymentsViewController.self) + payModel: DWPayModelProtocol?) -> PaymentsViewController { + let controller = controller() controller.receiveModel = receiveModel controller.payModel = payModel - controller.dataProvider = dataProvider return controller } } @@ -110,17 +119,17 @@ class PaymentsViewController: BaseViewController { extension PaymentsViewController { func configureHierarchy() { view.backgroundColor = .dw_secondaryBackground() - + segmentedControl.setTitle(NSLocalizedString("Receive", comment: "Receive/Send"), forSegmentAt: 0) segmentedControl.setTitle(NSLocalizedString("Send", comment: "Receive/Send"), forSegmentAt: 1) segmentedControl.selectedSegmentIndex = currentState.rawValue - + payViewController = PayViewController.controller(with: payModel) payViewController.delegate = self - + receiveViewController = ReceiveViewController(model: receiveModel) receiveViewController.delegate = self - + pageController = SendReceivePageController() pageController.helperDelegate = self addChild(pageController) @@ -129,9 +138,9 @@ extension PaymentsViewController { pageController.didMove(toParent: self) pageController.controllers = [receiveViewController, payViewController] pageController.selectedIndex = currentState.rawValue - + closeButton.layer.cornerRadius = 24 - + NSLayoutConstraint.activate([ pageController.view.topAnchor.constraint(equalTo: containerView.topAnchor), pageController.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor), @@ -141,29 +150,37 @@ extension PaymentsViewController { } } +// MARK: NavigationBarDisplayable + extension PaymentsViewController: NavigationBarDisplayable { var isNavigationBarHidden: Bool { true } } +// MARK: SendReceivePageControllerDelegate + extension PaymentsViewController: SendReceivePageControllerDelegate { func sendReceivePageControllerWillChangeSelectedIndex(to index: Int) { segmentedControl.selectedSegmentIndex = index - + DWGlobalOptions.sharedInstance().paymentsScreenCurrentTab = index } } +// MARK: PayViewControllerDelegate + extension PaymentsViewController: PayViewControllerDelegate { func payViewControllerDidFinishPayment(_ controller: PayViewController, contact: DWDPBasicUserItem?) { delegate?.paymentsViewControllerDidFinishPayment(self, contact: contact) } } +// MARK: ReceiveViewControllerDelegate + extension PaymentsViewController: ReceiveViewControllerDelegate { func receiveViewControllerExitButtonAction(_ controller: ReceiveViewController) { - //NOP + // NOP } - + func importPrivateKeyButtonAction(_ controller: ReceiveViewController) { delegate?.paymentsViewControllerWantsToImportPrivateKey(self) } diff --git a/DashWallet/Sources/UI/Payments/Receive/ReceiveViewController.swift b/DashWallet/Sources/UI/Payments/Receive/ReceiveViewController.swift index c0add69ae..075d43f94 100644 --- a/DashWallet/Sources/UI/Payments/Receive/ReceiveViewController.swift +++ b/DashWallet/Sources/UI/Payments/Receive/ReceiveViewController.swift @@ -1,4 +1,4 @@ -// +// // Created by PT // Copyright © 2023 Dash Core Group. All rights reserved. // @@ -17,85 +17,91 @@ import Foundation +// MARK: - ReceiveViewType + @objc(DWReceiveViewType) enum ReceiveViewType: Int { @objc(DWReceiveViewType_Default) case `default` - + @objc(DWReceiveViewType_QuickReceive) case quick } - + extension ReceiveViewType { var secondButtonTitle: String { if self == .default { return NSLocalizedString("Share address", comment: "Receive screen") - }else{ + } else { return NSLocalizedString("Exit", comment: "Receive screen") } } } +// MARK: - ReceiveViewControllerDelegate + @objc(DWReceiveViewControllerDelegate) protocol ReceiveViewControllerDelegate: AnyObject { func receiveViewControllerExitButtonAction(_ controller: ReceiveViewController) func importPrivateKeyButtonAction(_ controller: ReceiveViewController) } +// MARK: - ReceiveViewController + @objc(DWReceiveViewController) class ReceiveViewController: BaseViewController { var model: DWReceiveModelProtocol! - + @objc var viewType: ReceiveViewType = .default - + @objc weak var delegate: ReceiveViewControllerDelegate? - + @objc - var allowedToImportPrivateKey: Bool = true - + var allowedToImportPrivateKey = true + @objc init(model: DWReceiveModelProtocol) { self.model = model super.init(nibName: nil, bundle: nil) } - + @objc func importPrivateKeyButtonAction() { let controller = sb("ImportWalletInfo").instantiateInitialViewController() as! DWImportWalletInfoViewController controller.delegate = self - + let nvc = BaseNavigationController(rootViewController: controller) present(nvc, animated: true) - + nvc.setCancelButtonHidden(false) } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override func viewDidLoad() { super.viewDidLoad() - + configureHierarchy() } } -private extension ReceiveViewController { +extension ReceiveViewController { private func configureHierarchy() { let mainStackView = UIStackView() mainStackView.translatesAutoresizingMaskIntoConstraints = false mainStackView.axis = .vertical mainStackView.spacing = stackSpacing view.addSubview(mainStackView) - + let receiveContentView = ReceiveContentView.view(with: model) receiveContentView.viewType = viewType receiveContentView.specifyAmountHandler = { [weak self] in guard let self else { return } - + let vc = SpecifyAmountViewController.controller() vc.delegate = self self.navigationController?.pushViewController(vc, animated: true) @@ -103,17 +109,16 @@ private extension ReceiveViewController { receiveContentView.shareHandler = { [weak self] sender in guard let self else { return } self.dw_shareReceiveInfo(self.model, sender: sender) - } receiveContentView.exitHandler = { [weak self] in guard let self else { return } self.delegate?.receiveViewControllerExitButtonAction(self) } - + receiveContentView.backgroundColor = .dw_background() receiveContentView.layer.cornerRadius = radius mainStackView.addArrangedSubview(receiveContentView) - + let importPrivateKeyButton = UIButton(type: .custom) importPrivateKeyButton.addTarget(self, action: #selector(importPrivateKeyButtonAction), for: .touchUpInside) importPrivateKeyButton.backgroundColor = .dw_background() @@ -127,36 +132,40 @@ private extension ReceiveViewController { importPrivateKeyButton.setTitle(NSLocalizedString("Import Private Key", comment: "Import Private Key"), for: .normal) importPrivateKeyButton.isHidden = !allowedToImportPrivateKey mainStackView.addArrangedSubview(importPrivateKeyButton) - + mainStackView.addArrangedSubview(EmptyView()) - + NSLayoutConstraint.activate([ mainStackView.topAnchor.constraint(equalTo: view.topAnchor), mainStackView.bottomAnchor.constraint(equalTo: view.bottomAnchor), mainStackView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor), mainStackView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor), - + receiveContentView.heightAnchor.constraint(equalToConstant: 373), importPrivateKeyButton.heightAnchor.constraint(equalToConstant: 64), ]) } } +// MARK: SpecifyAmountViewControllerDelegate + extension ReceiveViewController: SpecifyAmountViewControllerDelegate { func specifyAmountViewController(_ vc: SpecifyAmountViewController, didInput amount: UInt64) { let model = DWReceiveModel(amount: amount) - + let requestController = DWRequestAmountViewController(model: model) requestController.delegate = self - self.present(requestController, animated: true) + present(requestController, animated: true) } } +// MARK: DWRequestAmountViewControllerDelegate + extension ReceiveViewController: DWRequestAmountViewControllerDelegate { func requestAmountViewController(_ controller: DWRequestAmountViewController, didReceiveAmountWithInfo info: String) { controller.dismiss(animated: true) { self.navigationController?.popViewController(animated: true) - + let popAnimationDuration = 300 DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .milliseconds(popAnimationDuration)) { self.navigationController?.view.dw_showInfoHUD(withText: info) @@ -165,11 +174,12 @@ extension ReceiveViewController: DWRequestAmountViewControllerDelegate { } } +// MARK: DWImportWalletInfoViewControllerDelegate + extension ReceiveViewController: DWImportWalletInfoViewControllerDelegate { func importWalletInfoViewControllerScanPrivateKeyAction(_ controller: DWImportWalletInfoViewController) { controller.dismiss(animated: true) { self.delegate?.importPrivateKeyButtonAction(self) } - } } diff --git a/DashWallet/Sources/UI/Payments/Receive/Views/ReceiveContentView.swift b/DashWallet/Sources/UI/Payments/Receive/Views/ReceiveContentView.swift index 2b7671d50..2748be387 100644 --- a/DashWallet/Sources/UI/Payments/Receive/Views/ReceiveContentView.swift +++ b/DashWallet/Sources/UI/Payments/Receive/Views/ReceiveContentView.swift @@ -1,4 +1,4 @@ -// +// // Created by PT // Copyright © 2023 Dash Core Group. All rights reserved. // @@ -17,6 +17,8 @@ import UIKit +// MARK: - ReceiveContentView + @objc(DWReceiveContentView) final class ReceiveContentView: UIView { @IBOutlet var qrCodeButton: UIButton! @@ -24,39 +26,43 @@ final class ReceiveContentView: UIView { @IBOutlet var addressButton: UIButton! @IBOutlet var specifyAmountButton: UIButton! @IBOutlet var secondButton: UIButton! - + private var model: DWReceiveModelProtocol! - private var feedbackGenerator: UINotificationFeedbackGenerator = UINotificationFeedbackGenerator() - + private var feedbackGenerator = UINotificationFeedbackGenerator() + @objc public var viewType: ReceiveViewType = .default - + public var specifyAmountHandler: (() -> Void)? - + @objc public var shareHandler: ((UIButton) -> Void)? - + @objc public var exitHandler: (() -> Void)? - - @IBAction func addressButtonAction() { + + @IBAction + func addressButtonAction() { feedbackGenerator.notificationOccurred(.success) model.copyAddressToPasteboard() } - - @IBAction func qrButtonAction() { + + @IBAction + func qrButtonAction() { feedbackGenerator.notificationOccurred(.success) model.copyQRImageToPasteboard() } - - @IBAction func specifyAmountButtonAction() { + + @IBAction + func specifyAmountButtonAction() { specifyAmountHandler?() } - - @IBAction func secondButtonAction() { + + @IBAction + func secondButtonAction() { if viewType == .default { shareHandler?(secondButton) - }else{ + } else { exitHandler?() } } @@ -65,49 +71,51 @@ final class ReceiveContentView: UIView { func setSpecifyAmountButtonHidden(_ hidden: Bool) { specifyAmountButton.isHidden = hidden } - + @objc func setSecondButtonHidden(_ hidden: Bool) { secondButton.isHidden = hidden } - + @objc static func view(with model: DWReceiveModelProtocol) -> ReceiveContentView { let view = UINib.view(Self.self) view.model = model - + model.delegate = view - + view.configureHierarchy() view.reloadView() - + return view } } -private extension ReceiveContentView { +extension ReceiveContentView { private func configureHierarchy() { specifyAmountButton.setTitle(NSLocalizedString("Specify Amount", comment: "Receive screen"), for: .normal) secondButton.setTitle(viewType.secondButtonTitle, for: .normal) } - + private func reloadView() { let hasValue = model.paymentAddress != nil - + addressButton.setTitle(model.paymentAddress, for: .normal) addressButton.isHidden = !hasValue - + qrCodeButton.setImage(model.qrCodeImage, for: .normal) qrCodeButton.isHidden = model.qrCodeImage == nil - + specifyAmountButton.isEnabled = hasValue - + if viewType == .default { secondButton.isEnabled = hasValue } } } +// MARK: DWReceiveModelDelegate + extension ReceiveContentView: DWReceiveModelDelegate { func receivingInfoDidUpdate() { reloadView() diff --git a/DashWallet/Sources/UI/Payments/SendReceivePageController.swift b/DashWallet/Sources/UI/Payments/SendReceivePageController.swift index 78da21246..27a473cc8 100644 --- a/DashWallet/Sources/UI/Payments/SendReceivePageController.swift +++ b/DashWallet/Sources/UI/Payments/SendReceivePageController.swift @@ -1,4 +1,4 @@ -// +// // Created by PT // Copyright © 2023 Dash Core Group. All rights reserved. // @@ -17,72 +17,79 @@ import UIKit +// MARK: - SendReceivePageControllerDelegate + protocol SendReceivePageControllerDelegate: AnyObject { func sendReceivePageControllerWillChangeSelectedIndex(to index: Int) } +// MARK: - SendReceivePageController + class SendReceivePageController: UIPageViewController { weak var helperDelegate: SendReceivePageControllerDelegate? - - private var isControllerReady: Bool = false - + + private var isControllerReady = false + init() { super.init(transitionStyle: .scroll, navigationOrientation: .horizontal) } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + var controllers: [UIViewController]! { didSet { guard isControllerReady else { return } - + let vc = controllers.first! setViewControllers([vc], direction: .forward, animated: false) } } - - var selectedIndex: Int = 0 - + + var selectedIndex = 0 + func setSelectedIndex(_ idx: Int, animated: Bool) { selectedIndex = idx let vc = controllers[selectedIndex] let direction = direction(for: vc) setViewControllers([vc], direction: direction, animated: animated) } - + override func viewDidLoad() { super.viewDidLoad() - + delegate = self dataSource = self } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - + guard selectedIndex < controllers.count else { return } let vc = controllers[selectedIndex] setViewControllers([vc], direction: .forward, animated: false) - + isControllerReady = true } } +// MARK: UIPageViewControllerDelegate + extension SendReceivePageController: UIPageViewControllerDelegate { func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) { guard let vc = pendingViewControllers.first else { return } - + let idx = index(of: vc) selectedIndex = idx helperDelegate?.sendReceivePageControllerWillChangeSelectedIndex(to: idx) } - - func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { + + func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], + transitionCompleted completed: Bool) { print("pageViewController", finished, completed, previousViewControllers) guard finished else { return } - + if let vc = previousViewControllers.first, !completed { let idx = index(of: vc) selectedIndex = idx @@ -91,20 +98,22 @@ extension SendReceivePageController: UIPageViewControllerDelegate { } } +// MARK: UIPageViewControllerDataSource + extension SendReceivePageController: UIPageViewControllerDataSource { func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { if controllers.last == viewController { return controllers.first } - + return nil } - + func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { if controllers.first == viewController { return controllers.last } - + return nil } } @@ -114,7 +123,7 @@ extension SendReceivePageController { func index(of controller: UIViewController) -> Int { controllers.first == controller ? 0 : 1 } - + func direction(for controller: UIViewController) -> UIPageViewController.NavigationDirection { let idx = index(of: controller) return idx == 0 ? .reverse : .forward diff --git a/DashWallet/Sources/UI/RootNavigation/DWAppRootViewController.m b/DashWallet/Sources/UI/RootNavigation/DWAppRootViewController.m index 3239dcac4..b1d5b18aa 100644 --- a/DashWallet/Sources/UI/RootNavigation/DWAppRootViewController.m +++ b/DashWallet/Sources/UI/RootNavigation/DWAppRootViewController.m @@ -21,7 +21,6 @@ #import #import "DWLockScreenViewController.h" -#import "DWMainTabbarViewController.h" #import "DWRootModel.h" #import "DWSetupViewController.h" #import "DWUIKit.h" @@ -40,7 +39,7 @@ @interface DWAppRootViewController () model; -@property (null_resettable, nonatomic, strong) DWMainTabbarViewController *mainController; +@property (null_resettable, nonatomic, strong) MainTabbarController *mainController; @property (nullable, nonatomic, strong) UIImageView *overlayImageView; @property (nonatomic, strong) UIWindow *lockWindow; @@ -70,7 +69,7 @@ - (instancetype)initWithModel:(id)model { #pragma mark - Public + (Class)mainControllerClass { - return [DWMainTabbarViewController class]; + return [MainTabbarController class]; } - (void)setLaunchingAsDeferredController { @@ -120,7 +119,7 @@ - (void)handleURL:(NSURL *)url { } else if ([action isKindOfClass:DWURLPayAction.class]) { NSURL *paymentURL = [(DWURLPayAction *)action paymentURL]; - [self.mainController performPayToURL:paymentURL]; + [self.mainController performPayTo:paymentURL]; } else { NSAssert(NO, @"Unhandled action", action); @@ -397,14 +396,13 @@ - (UIViewController *)setupController { return navigationController; } -- (DWMainTabbarViewController *)mainController { +- (MainTabbarController *)mainController { if (_mainController == nil) { id homeModel = self.model.homeModel; Class klass = [self.class mainControllerClass]; - DWMainTabbarViewController *controller = [[klass alloc] init]; - controller.homeModel = homeModel; - controller.delegate = self; - controller.demoMode = self.demoMode; + MainTabbarController *controller = [[MainTabbarController alloc] initWithHomeModel:self.model.homeModel]; + controller.wipeDelegate = self; + controller.isDemoMode = self.demoMode; controller.demoDelegate = self.demoDelegate; _mainController = controller; diff --git a/DashWallet/Sources/UI/RootNavigation/DWRootModel.m b/DashWallet/Sources/UI/RootNavigation/DWRootModel.m index 2cf774caa..5ebf272df 100644 --- a/DashWallet/Sources/UI/RootNavigation/DWRootModel.m +++ b/DashWallet/Sources/UI/RootNavigation/DWRootModel.m @@ -21,7 +21,6 @@ #import "DWEnvironment.h" #import "DWGlobalOptions.h" #import "DWHomeModel.h" -#import "DWSyncModel.h" #import "dashwallet-Swift.h" #import @@ -98,7 +97,8 @@ - (BOOL)shouldShowLockScreen { } - (void)setupDidFinish { - [self.homeModel.shortcutsModel reloadShortcuts]; + //TODO: check whether we really need to do that here + //[self.homeModel.shortcutsModel reloadShortcuts]; } - (void)wipeWallet { @@ -119,7 +119,7 @@ - (void)currentNetworkDidChangeNotification:(NSNotification *)notification { self.currentNetworkDidChangeBlock(); } - [homeModel forceStartSyncingActivity]; + [SyncingActivityMonitor.shared forceStartSyncingActivity]; } @end diff --git a/DashWallet/Sources/UI/Setup/DWSetupViewController.m b/DashWallet/Sources/UI/Setup/DWSetupViewController.m index 8b7639a6d..97d81a752 100644 --- a/DashWallet/Sources/UI/Setup/DWSetupViewController.m +++ b/DashWallet/Sources/UI/Setup/DWSetupViewController.m @@ -20,7 +20,6 @@ #import "DWBiometricAuthModel.h" #import "DWBiometricAuthViewController.h" #import "DWGlobalOptions.h" -#import "DWMainTabbarViewController.h" #import "DWPreviewSeedPhraseModel.h" #import "DWRecoverViewController.h" #import "DWSetPinModel.h" @@ -34,7 +33,8 @@ @interface DWSetupViewController () + DWRecoverViewControllerDelegate, + DWBackupInfoViewControllerDelegate> @property (nonatomic, assign) BOOL initialAnimationCompleted; diff --git a/DashWallet/Sources/UI/Setup/RecoverWallet/DWRecoverModel.m b/DashWallet/Sources/UI/Setup/RecoverWallet/DWRecoverModel.m index af284b75d..843ba31d0 100644 --- a/DashWallet/Sources/UI/Setup/RecoverWallet/DWRecoverModel.m +++ b/DashWallet/Sources/UI/Setup/RecoverWallet/DWRecoverModel.m @@ -80,7 +80,7 @@ - (BOOL)phraseIsValid:(NSString *)phrase { } - (void)wipeWallet { - [DWApp cleanUp]; + [DWApp cleanUp]; //Send notificaiton [[DWEnvironment sharedInstance] clearAllWallets]; [[DWGlobalOptions sharedInstance] restoreToDefaults]; [[DWAppGroupOptions sharedInstance] restoreToDefaults]; diff --git a/DashWallet/Sources/UI/Setup/SecureWallet/BackupInfo/BackupInfoViewController.swift b/DashWallet/Sources/UI/Setup/SecureWallet/BackupInfo/BackupInfoViewController.swift index 5ef4c8583..a989de9c5 100644 --- a/DashWallet/Sources/UI/Setup/SecureWallet/BackupInfo/BackupInfoViewController.swift +++ b/DashWallet/Sources/UI/Setup/SecureWallet/BackupInfo/BackupInfoViewController.swift @@ -1,4 +1,4 @@ -// +// // Created by PT // Copyright © 2023 Dash Core Group. All rights reserved. // @@ -17,6 +17,8 @@ import Foundation +// MARK: - BackupInfoItem + private enum BackupInfoItem { case notStoredByDash case unableToRestore @@ -29,7 +31,7 @@ extension BackupInfoItem { case .unableToRestore: return "You will NOT be able to restore the wallet without a recovery phrase" } } - + var description: String { switch self { case .notStoredByDash: return "Anyone that has your recovery phrase can access your funds." @@ -45,98 +47,117 @@ extension BackupInfoItem { } } +// MARK: - SecureWalletInfoType + @objc(DWSecureWalletInfoType) enum SecureWalletInfoType: Int { @objc(DWSecureWalletInfoType_Setup) case setup - + @objc(DWSecureWalletInfoType_Reminder) case reminder } +// MARK: - BackupInfoViewControllerDelegate + @objc(DWBackupInfoViewControllerDelegate) -protocol BackupInfoViewControllerDelegate: DWSecureWalletDelegate { -} +protocol BackupInfoViewControllerDelegate: DWSecureWalletDelegate { } + +// MARK: - BackupInfoViewController @objc(DWBackupInfoViewController) final class BackupInfoViewController: BaseViewController { @IBOutlet private var titleLabel: UILabel! @IBOutlet private var subtitleLabel: UILabel! - + @IBOutlet private var contentView: UIStackView! - + @IBOutlet private var bottomButtonStack: UIStackView! @IBOutlet private var showRecoveryPhraseButton: UIButton! @IBOutlet private var skipButton: UIButton! - + private var closeButton: UIBarButtonItem! private var seedPhraseModel: DWPreviewSeedPhraseModel! - + @objc public weak var delegate: BackupInfoViewControllerDelegate? - + public var type: SecureWalletInfoType = .setup - + @objc - var isSkipButtonHidden: Bool = true { + var isSkipButtonHidden = true { didSet { if isViewLoaded { skipButton?.isHidden = isSkipButtonHidden } } } - + @objc - var isCloseButtonHidden: Bool = false { + var isCloseButtonHidden = false { didSet { if isViewLoaded { reloadCloseButton() } } } - + @objc - var isAllActionHidden: Bool = false { + var isAllActionHidden = false { didSet { if isViewLoaded { reloadView() } } } - + @objc private func closeAction() { delegate?.secureWalletRoutineDidCanceled(self) } - - @IBAction func skipButtonAction() { + + @IBAction + func skipButtonAction() { delegate?.secureWalletRoutineDidCanceled(self) } - - @IBAction func backupButtonAction() { - let controller = DWBackupSeedPhraseViewController(model: seedPhraseModel) - controller.shouldCreateNewWalletOnScreenshot = shouldCreateNewWalletOnScreenshot - controller.delegate = delegate - navigationController?.pushViewController(controller, animated: true) - } - - @IBAction func closeButtonAction() { - delegate?.secureWalletRoutineDidCanceled(self) + + @IBAction + func backupButtonAction() { + if type == .setup { + showSeedPhraseViewController() + } else { + DSAuthenticationManager.sharedInstance() + .authenticate(withPrompt: nil, + usingBiometricAuthentication: false, + alertIfLockout: true) { [weak self] authenticated, _, _ in + guard authenticated else { + return + } + + guard let self else { + return + } + + self.seedPhraseModel = DWPreviewSeedPhraseModel() + self.seedPhraseModel.getOrCreateNewWallet() + self.showSeedPhraseViewController() + } + } } - + override func viewDidLoad() { super.viewDidLoad() - + if type == .setup { // Create wallet entry point - self.seedPhraseModel = DWPreviewSeedPhraseModel() + seedPhraseModel = DWPreviewSeedPhraseModel() seedPhraseModel.getOrCreateNewWallet() } - + configureHierarchy() } - + @objc static func controller(with type: SecureWalletInfoType) -> BackupInfoViewController { let controller = vc(BackupInfoViewController.self, from: sb("BackupInfo")) @@ -148,41 +169,50 @@ final class BackupInfoViewController: BaseViewController { extension BackupInfoViewController { private func configureHierarchy() { titleLabel.text = NSLocalizedString("Backup your recovery phrase", comment: "Back up wallet") - subtitleLabel.text = NSLocalizedString("You will need this recovery phrase to access your funds if this device is lost, damaged or if Dash Wallet is uninstalled from this device.", comment: "Back up wallet") + subtitleLabel + .text = NSLocalizedString("You will need this recovery phrase to access your funds if this device is lost, damaged or if Dash Wallet is uninstalled from this device.", + comment: "Back up wallet") showRecoveryPhraseButton.setTitle(NSLocalizedString("Show Recovery Phrase", comment: "Back up wallet"), for: .normal) skipButton.setTitle(NSLocalizedString("Skip", comment: "Back up wallet"), for: .normal) - + show(item: .notStoredByDash) show(item: .unableToRestore) - + skipButton.isHidden = isSkipButtonHidden reloadCloseButton() reloadView() } - + private func reloadView() { if isAllActionHidden { hideCloseButton() bottomButtonStack.isHidden = true - }else{ + } else { showCloseButtonIfNeeded() bottomButtonStack.isHidden = false } } - + + private func showSeedPhraseViewController() { + let controller = DWBackupSeedPhraseViewController(model: seedPhraseModel) + controller.shouldCreateNewWalletOnScreenshot = shouldCreateNewWalletOnScreenshot + controller.delegate = delegate + navigationController?.pushViewController(controller, animated: true) + } + private func reloadCloseButton() { if isCloseButtonHidden { hideCloseButton() - }else{ + } else { showCloseButton() } } - + private func show(item: BackupInfoItem) { let view = itemView(from: item) contentView.addArrangedSubview(view) } - + private func itemView(from item: BackupInfoItem) -> BackupInfoItemView { let view = BackupInfoItemView.view() view.titleLabel.text = item.title @@ -190,20 +220,20 @@ extension BackupInfoViewController { view.iconView.image = item.icon return view } - + private func showCloseButtonIfNeeded() { if !isCloseButtonHidden { showCloseButton() } } + private func showCloseButton() { let item = UIBarButtonItem(barButtonSystemItem: .close, target: self, action: #selector(closeAction)) navigationItem.rightBarButtonItem = item - + closeButton = item - } - + private func hideCloseButton() { navigationItem.rightBarButtonItem = nil closeButton = nil @@ -212,10 +242,12 @@ extension BackupInfoViewController { extension BackupInfoViewController { var shouldCreateNewWalletOnScreenshot: Bool { - return type == .reminder + type == .reminder } } +// MARK: NavigationBarDisplayable + extension BackupInfoViewController: NavigationBarDisplayable { var isBackButtonHidden: Bool { isAllActionHidden == false } } diff --git a/DashWallet/Sources/UI/Setup/SecureWallet/BackupInfo/Views/BackupInfoItemView.swift b/DashWallet/Sources/UI/Setup/SecureWallet/BackupInfo/Views/BackupInfoItemView.swift index 6dae281a1..4a386030f 100644 --- a/DashWallet/Sources/UI/Setup/SecureWallet/BackupInfo/Views/BackupInfoItemView.swift +++ b/DashWallet/Sources/UI/Setup/SecureWallet/BackupInfo/Views/BackupInfoItemView.swift @@ -1,4 +1,4 @@ -// +// // Created by PT // Copyright © 2023 Dash Core Group. All rights reserved. // @@ -21,10 +21,10 @@ final class BackupInfoItemView: UIView { @IBOutlet var iconView: UIImageView! @IBOutlet var titleLabel: UILabel! @IBOutlet var descriptionLabel: UILabel! - + override func awakeFromNib() { super.awakeFromNib() - + titleLabel.font = .dw_font(forTextStyle: .subheadline).withWeight(UIFont.Weight.semibold.rawValue) } } diff --git a/DashWallet/Sources/UI/Setup/SecureWallet/VerifiedSuccessfully/VerifiedSuccessfullyViewController.swift b/DashWallet/Sources/UI/Setup/SecureWallet/VerifiedSuccessfully/VerifiedSuccessfullyViewController.swift index 2dc65d674..51c7ce2bb 100644 --- a/DashWallet/Sources/UI/Setup/SecureWallet/VerifiedSuccessfully/VerifiedSuccessfullyViewController.swift +++ b/DashWallet/Sources/UI/Setup/SecureWallet/VerifiedSuccessfully/VerifiedSuccessfullyViewController.swift @@ -17,12 +17,14 @@ import Foundation +// MARK: - VerifiedSuccessfullyViewController + @objc(DWVerifiedSuccessfullyViewController) final class VerifiedSuccessfullyViewController : UIViewController, NavigationFullscreenable { let requiresNoNavigationBar = true override var preferredStatusBarStyle: UIStatusBarStyle { .default } @objc public weak var delegate: DWSecureWalletDelegate? = nil - + @IBOutlet var scrollView: UIScrollView! @IBOutlet var securityImageView: UIImageView! @IBOutlet var titleLabel: UILabel! @@ -39,12 +41,14 @@ final class VerifiedSuccessfullyViewController : UIViewController, NavigationFul scrollView.flashScrollIndicators() } - @objc static func controller() -> VerifiedSuccessfullyViewController { + @objc + static func controller() -> VerifiedSuccessfullyViewController { let storyboard = UIStoryboard(name: "VerifiedSuccessfully", bundle: nil) return storyboard.instantiateInitialViewController() as! VerifiedSuccessfullyViewController } - - @IBAction func continueButtonAction() { + + @IBAction + func continueButtonAction() { delegate?.secureWalletRoutineDidFinish(self) } } diff --git a/DashWallet/Sources/UI/Uphold/Auth/UpholdAuthStoryboard.storyboard b/DashWallet/Sources/UI/Uphold/Auth/UpholdAuthStoryboard.storyboard index 70739dc8a..ca5195f57 100644 --- a/DashWallet/Sources/UI/Uphold/Auth/UpholdAuthStoryboard.storyboard +++ b/DashWallet/Sources/UI/Uphold/Auth/UpholdAuthStoryboard.storyboard @@ -1,7 +1,9 @@ - + + - + + @@ -12,20 +14,23 @@ - + - + - - + + + + + - + - + @@ -35,7 +40,7 @@ + @@ -129,7 +135,6 @@ - @@ -144,9 +149,9 @@ + - diff --git a/DashWallet/Sources/UI/Uphold/Main/UpholdMainStoryboard.storyboard b/DashWallet/Sources/UI/Uphold/Main/UpholdMainStoryboard.storyboard index c732ca170..18846d1ad 100644 --- a/DashWallet/Sources/UI/Uphold/Main/UpholdMainStoryboard.storyboard +++ b/DashWallet/Sources/UI/Uphold/Main/UpholdMainStoryboard.storyboard @@ -1,7 +1,9 @@ - + + - + + @@ -17,17 +19,17 @@ - + - - + + - + @@ -125,7 +128,6 @@ - @@ -143,7 +145,7 @@ - + diff --git a/DashWallet/Sources/UI/Uphold/Transfer/Model/UpholdAmountModel.swift b/DashWallet/Sources/UI/Uphold/Transfer/Model/UpholdAmountModel.swift index fa3009a2f..f76c352d1 100644 --- a/DashWallet/Sources/UI/Uphold/Transfer/Model/UpholdAmountModel.swift +++ b/DashWallet/Sources/UI/Uphold/Transfer/Model/UpholdAmountModel.swift @@ -149,7 +149,7 @@ final class UpholdAmountModel: BaseAmountModel { extension UpholdAmountModel: ConverterViewDataSource { var fromItem: SourceViewDataProvider? { - ConverterViewSourceItem(image: .asset("service.uphold.square"), + ConverterViewSourceItem(image: .asset("uphold_logo"), title: "Uphold", balanceFormatted: card.formattedDashAmount, fiatBalanceFormatted: card.fiatBalanceFormatted) diff --git a/DashWallet/Sources/UI/Views/BadgeView.swift b/DashWallet/Sources/UI/Views/BadgeView.swift new file mode 100644 index 000000000..015893d6f --- /dev/null +++ b/DashWallet/Sources/UI/Views/BadgeView.swift @@ -0,0 +1,90 @@ +// +// Created by PT +// Copyright © 2023 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +// MARK: - BadgeView + +final class BadgeView: UIView { + + private(set) var label: UILabel! + var text: String? { + get { label.text } + set { + label.text = newValue + invalidateIntrinsicContentSize() + } + } + + var font: UIFont? { + get { label.font } + set { label.font = newValue } + } + + var textColor: UIColor? { + get { self.label.textColor } + set { self.label.textColor = newValue } + } + + override init(frame: CGRect) { + super.init(frame: frame) + setupBadgeView() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupBadgeView() + } + + override func layoutSubviews() { + super.layoutSubviews() + layer.cornerRadius = bounds.height / 2.0 + } + + override var backgroundColor: UIColor? { + didSet { + label?.backgroundColor = backgroundColor + } + } + + private func setupBadgeView() { + backgroundColor = .dw_tint() + layer.masksToBounds = true + isUserInteractionEnabled = false + + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.backgroundColor = backgroundColor + label.font = UIFont.dw_font(forTextStyle: UIFont.TextStyle.caption1) + label.adjustsFontForContentSizeCategory = true + label.textColor = UIColor.dw_dashBlue() + label.textAlignment = .center + addSubview(label) + self.label = label + + let verticalPadding: CGFloat = 5.0 + let horizontalPadding: CGFloat = 14.0 + + NSLayoutConstraint.activate([ + label.topAnchor.constraint(equalTo: topAnchor, constant: verticalPadding), + label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: horizontalPadding), + bottomAnchor.constraint(equalTo: label.bottomAnchor, constant: verticalPadding), + trailingAnchor.constraint(equalTo: label.trailingAnchor, constant: horizontalPadding), + ]) + } +} + diff --git a/DashWallet/Sources/UI/Views/EmptyView.swift b/DashWallet/Sources/UI/Views/EmptyView.swift index cd9702d39..b9ac2c6ea 100644 --- a/DashWallet/Sources/UI/Views/EmptyView.swift +++ b/DashWallet/Sources/UI/Views/EmptyView.swift @@ -1,4 +1,4 @@ -// +// // Created by PT // Copyright © 2023 Dash Core Group. All rights reserved. // @@ -20,13 +20,13 @@ import UIKit final class EmptyView: UIView { override init(frame: CGRect) { super.init(frame: frame) - + backgroundColor = .clear } - + required init?(coder: NSCoder) { super.init(coder: coder) - + backgroundColor = .clear } } diff --git a/DashWallet/Sources/UI/Views/ProgressView.swift b/DashWallet/Sources/UI/Views/ProgressView.swift new file mode 100644 index 000000000..74fe080a9 --- /dev/null +++ b/DashWallet/Sources/UI/Views/ProgressView.swift @@ -0,0 +1,123 @@ +// +// Created by PT +// Copyright © 2023 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +final class ProgressView: UIView { + private let kProgressAnimationKey = "DW_PROGRESS_ANIMATION_KEY" + private let kDelayBetweenPulse: CFTimeInterval = 4.0 + + private var greenLayer: CALayer! + private var blueLayer: CALayer! + private var animating = false + + var progress: Float = 0.0 + + required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + private func commonInit() { + backgroundColor = UIColor.dw_progressBackground() + layer.masksToBounds = true + + let greenLayer = CALayer() + greenLayer.backgroundColor = UIColor.dw_green().cgColor + layer.addSublayer(greenLayer) + self.greenLayer = greenLayer + + let blueLayer = CALayer() + blueLayer.backgroundColor = UIColor.dw_dashNavigationBlue().cgColor + layer.addSublayer(blueLayer) + self.blueLayer = blueLayer + } + + override func layoutSublayers(of layer: CALayer) { + super.layoutSublayers(of: layer) + if layer == self.layer { + greenLayer.frame = layer.bounds + blueLayer.frame = layer.bounds + let x = layer.bounds.width / 2.0 + let y = layer.bounds.height / 2.0 + // set 0 progress position + greenLayer.position = CGPoint(x: -x, y: y) + blueLayer.position = CGPoint(x: -x, y: y) + } + } + + func setProgress(_ progress: Float, animated: Bool) { + assert(progress >= 0.0 && progress <= 1.0, "Invalid progress") + self.progress = max(0.0, min(1.0, progress)) + + // use implicit animation + greenLayer.anchorPoint = CGPoint(x: 0.5 - Double(progress), y: 0.5) + + if progress == 1.0 { + NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(progressAnimationIteration), object: nil) + animating = false + } else if progress > 0.0 && !animating { + animating = true + perform(#selector(progressAnimationIteration), with: nil, afterDelay: kDelayBetweenPulse) + } + } + + @objc + private func progressAnimationIteration() { + if !animating { + blueLayer.removeAnimation(forKey: kProgressAnimationKey) + return + } + + let anchorAnimationDuration: CFTimeInterval = 0.4 + let delayBeforeFadingOut: CFTimeInterval = 0.4 + let colorAnimationDuration: CFTimeInterval = 1.0 + + let anchorAnimation = CABasicAnimation(keyPath: "anchorPoint") + anchorAnimation.fromValue = NSValue(cgPoint: CGPoint(x: 0.5, y: 0.5)) + anchorAnimation.toValue = NSValue(cgPoint: CGPoint(x: 0.5 - Double(progress), y: 0.5)) + anchorAnimation.duration = anchorAnimationDuration + anchorAnimation.beginTime = 0.0 + anchorAnimation.isRemovedOnCompletion = false + anchorAnimation.fillMode = .forwards + anchorAnimation.timingFunction = CAMediaTimingFunction(name: .easeIn) + + let colorAnimation = CABasicAnimation(keyPath: "backgroundColor") + colorAnimation.fromValue = UIColor.dw_dashNavigationBlue().cgColor + colorAnimation.toValue = UIColor.dw_green().cgColor + colorAnimation.duration = colorAnimationDuration + colorAnimation.beginTime = anchorAnimationDuration + delayBeforeFadingOut + colorAnimation.fillMode = .forwards + + let groupAnimation = CAAnimationGroup() + groupAnimation.animations = [anchorAnimation, colorAnimation] + groupAnimation.duration = anchorAnimationDuration + + delayBeforeFadingOut + + colorAnimationDuration + + Double(kDelayBetweenPulse) + + blueLayer.add(groupAnimation, forKey: kProgressAnimationKey) + + perform(#selector(progressAnimationIteration), with: nil, afterDelay: kDelayBetweenPulse) + } +} + diff --git a/DashWallet/Sources/UI/Views/SharedViews/DWBadgeView.h b/DashWallet/Sources/UI/Views/SharedViews/DWBadgeView.h deleted file mode 100644 index c6249fd8e..000000000 --- a/DashWallet/Sources/UI/Views/SharedViews/DWBadgeView.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2020 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface DWBadgeView : UIView - -@property (nullable, nonatomic, copy) NSString *text; -@property (nullable, nonatomic, strong) UIFont *font; -@property (nullable, nonatomic, strong) UIColor *textColor; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashWallet/Sources/UI/Views/SharedViews/DWBadgeView.m b/DashWallet/Sources/UI/Views/SharedViews/DWBadgeView.m deleted file mode 100644 index 19b412b31..000000000 --- a/DashWallet/Sources/UI/Views/SharedViews/DWBadgeView.m +++ /dev/null @@ -1,121 +0,0 @@ -// -// Created by Andrew Podkovyrin -// Copyright © 2020 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DWBadgeView.h" - -#import "DWUIKit.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface DWBadgeView () - -@property (readonly, nonatomic, strong) UILabel *label; - -@end - -NS_ASSUME_NONNULL_END - -@implementation DWBadgeView - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - [self setup_badgeView]; - } - return self; -} - -- (instancetype)initWithCoder:(NSCoder *)coder { - self = [super initWithCoder:coder]; - if (self) { - [self setup_badgeView]; - } - return self; -} - -- (void)layoutSubviews { - [super layoutSubviews]; - - self.layer.cornerRadius = floor(CGRectGetHeight(self.bounds) / 2.0); -} - -- (void)setBackgroundColor:(UIColor *)backgroundColor { - [super setBackgroundColor:backgroundColor]; - - self.label.backgroundColor = backgroundColor; -} - -- (NSString *)text { - return self.label.text; -} - -- (void)setText:(NSString *)text { - self.label.text = text; - [self invalidateIntrinsicContentSize]; -} - -- (UIFont *)font { - return self.label.font; -} - -- (void)setFont:(UIFont *)font { - self.label.font = font; -} - -- (UIColor *)textColor { - return self.label.textColor; -} - -- (void)setTextColor:(UIColor *)textColor { - self.label.textColor = textColor; -} - -#pragma mark - Private - -- (void)setup_badgeView { - self.backgroundColor = [UIColor dw_tintColor]; - - self.layer.masksToBounds = YES; - - self.userInteractionEnabled = NO; - - UILabel *label = [[UILabel alloc] init]; - label.translatesAutoresizingMaskIntoConstraints = NO; - label.backgroundColor = self.backgroundColor; - label.font = [UIFont dw_fontForTextStyle:UIFontTextStyleCaption1]; - label.adjustsFontForContentSizeCategory = YES; - label.textColor = [UIColor dw_dashBlueColor]; - label.textAlignment = NSTextAlignmentCenter; - [self addSubview:label]; - _label = label; - - const CGFloat verticalPadding = 5.0; - const CGFloat horizontalPadding = 14.0; - - [NSLayoutConstraint activateConstraints:@[ - [label.topAnchor constraintEqualToAnchor:self.topAnchor - constant:verticalPadding], - [label.leadingAnchor constraintEqualToAnchor:self.leadingAnchor - constant:horizontalPadding], - [self.bottomAnchor constraintEqualToAnchor:label.bottomAnchor - constant:verticalPadding], - [self.trailingAnchor constraintEqualToAnchor:label.trailingAnchor - constant:horizontalPadding], - ]]; -} - -@end diff --git a/DashWallet/Sources/UI/Views/SharedViews/Keyboard/NumberKeyboardButton.swift b/DashWallet/Sources/UI/Views/SharedViews/Keyboard/NumberKeyboardButton.swift index cb4285278..290442272 100644 --- a/DashWallet/Sources/UI/Views/SharedViews/Keyboard/NumberKeyboardButton.swift +++ b/DashWallet/Sources/UI/Views/SharedViews/Keyboard/NumberKeyboardButton.swift @@ -159,12 +159,12 @@ class NumberKeyboardButton: UIView { UIView.animate(withDuration: 0.075, delay: 0, options: [.curveEaseOut, .beginFromCurrentState]) { [unowned self] in - if self.isHighlighted { - self.backgroundColor = Styles.backgroundHighlightedColor - self.titleLabel.textColor = Styles.textHighlightedColor + if isHighlighted { + backgroundColor = Styles.backgroundHighlightedColor + titleLabel.textColor = Styles.textHighlightedColor } else { - self.backgroundColor = customBackgroundColor - self.titleLabel.textColor = Styles.textColor + backgroundColor = customBackgroundColor + titleLabel.textColor = Styles.textColor } } } diff --git a/DashWallet/Sources/UI/Views/Transitions/Item/Model/TransactionDataItem.swift b/DashWallet/Sources/UI/Views/Transitions/Item/Model/TransactionDataItem.swift index 1c1cd6344..4bc833f2a 100644 --- a/DashWallet/Sources/UI/Views/Transitions/Item/Model/TransactionDataItem.swift +++ b/DashWallet/Sources/UI/Views/Transitions/Item/Model/TransactionDataItem.swift @@ -51,7 +51,7 @@ extension TransactionDataItem { return NSAttributedString(string: NSLocalizedString("Syncing...", comment: "Transaction/Amount")) } - var formatted = formattedDashAmountWithDirectionalSymbol + let formatted = formattedDashAmountWithDirectionalSymbol return formatted.attributedAmountStringWithDashSymbol(tintColor: color, dashSymbolColor: color, font: font) } } diff --git a/DashWallet/dashwallet-Bridging-Header.h b/DashWallet/dashwallet-Bridging-Header.h index 777da49c3..a8d538531 100644 --- a/DashWallet/dashwallet-Bridging-Header.h +++ b/DashWallet/dashwallet-Bridging-Header.h @@ -26,42 +26,19 @@ static const bool _SNAPSHOT = 0; //MARK: DashWallet #import "DWActionButton.h" -#import "DWDPBasicUserItem.h" #import "DWEnvironment.h" #import "DWTitleDetailCellModel.h" #import "DWTitleDetailItem.h" #import "DWGlobalOptions.h" -#import "DWDPUserObject.h" #import "DWUIKit.h" #import "DWAboutModel.h" #import "DWDateFormatter.h" -#import "DWDPRegistrationStatus.h" -#import "DWDPRegistrationErrorTableViewCell.h" -#import "DWDPRegistrationDoneTableViewCell.h" -#import "DWDPRegistrationStatusTableViewCell.h" -#import "DWDPRegistrationErrorRetryDelegate.h" - -//MARK: CrowdNode -#import "DWCheckbox.h" -#import "DWPreviewSeedPhraseModel.h" -#import "DWSeedPhraseModel.h" -#import "DWSecureWalletDelegate.h" -#import "UIImage+Utils.h" -#import "NSData+Dash.h" -// end CrowdNode - - #import "DWBaseActionButtonViewController.h" #import "DWNumberKeyboardInputViewAudioFeedback.h" #import "DWInputValidator.h" #import "DWAmountInputValidator.h" -#import "DWPaymentProcessor.h" #import "DWConfirmSendPaymentViewController.h" -#import "DWPaymentOutput.h" -#import "DWPaymentInput.h" -#import "DWPaymentInputBuilder.h" #import "DWLocalCurrencyViewController.h" -#import "DWPayModelProtocol.h" #import "DWDemoDelegate.h" #import "DWModalPopupTransition.h" #import "DWModalTransition.h" @@ -75,7 +52,9 @@ static const bool _SNAPSHOT = 0; #import "CALayer+DWShadow.h" #import "DSTransaction+DashWallet.h" #import "DWAlertController.h" - +#import "DWHomeProtocol.h" +#import "DWDPRegistrationErrorRetryDelegate.h" +#import "UIDevice+DashWallet.h" //MARK: Backup Wallet #import "DWBackupSeedPhraseViewController.h" @@ -90,11 +69,13 @@ static const bool _SNAPSHOT = 0; #import "DWTransactionListDataProviderProtocol.h" #import "DWQuickReceiveViewController.h" #import "DWQRScanViewController.h" -#import "DWQRScanModel.h" #import "DWRequestAmountViewController.h" #import "UIViewController+DWShareReceiveInfo.h" #import "DWImportWalletInfoViewController.h" - +#import "DWPaymentProcessor.h" +#import "DWPaymentOutput.h" +#import "DWPaymentInput.h" +#import "DWPaymentInputBuilder.h" //MARK: Uphold #import "DWUpholdTransactionObject.h" @@ -111,5 +92,28 @@ static const bool _SNAPSHOT = 0; #import #import "DWPhoneWCSessionManager.h" -//MARK: Platform +//MARK: DashPay #import "DWDPBasicUserItem.h" +#import "DWDPAvatarView.h" +#import "DWDPRegistrationStatus.h" +#import "DWDPRegistrationErrorTableViewCell.h" +#import "DWDPRegistrationDoneTableViewCell.h" +#import "DWDPRegistrationStatusTableViewCell.h" +#import "DWDPRegistrationErrorRetryDelegate.h" +#import "DWDPUserObject.h" +#import "DWModalUserProfileViewController.h" + +//MARK: CrowdNode +#import "DWCheckbox.h" +#import "DWPreviewSeedPhraseModel.h" +#import "DWSeedPhraseModel.h" +#import "UIImage+Utils.h" +#import "NSData+Dash.h" + +//MARK: Tabbar +#import "DWHomeViewController.h" +#import "DWMainMenuViewController.h" +#import "DWWipeDelegate.h" +#import "DWPayModel.h" +#import "DWHomeViewControllerDelegate.h" +#import "DWMainMenuViewControllerDelegate.h" diff --git a/Podfile.lock b/Podfile.lock index 5dd312a11..0e5df03cc 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -588,11 +588,11 @@ PODS: - "!ProtoCompiler-gRPCPlugin (~> 1.0)" - DAPI-GRPC/Messages - gRPC-ProtoRPC - - DashSharedCore (0.4.0) + - DashSharedCore (0.4.5) - DashSync (0.1.0): - CocoaLumberjack (= 3.7.2) - DAPI-GRPC (= 0.22.0-dev.8) - - DashSharedCore (= 0.4.0) + - DashSharedCore (= 0.4.5) - DSDynamicOptions (= 0.1.2) - DWAlertController (= 0.2.1) - TinyCborObjc (= 0.4.6) @@ -699,7 +699,7 @@ PODS: - nanopb/decode (2.30908.0) - nanopb/encode (2.30908.0) - PromisesObjC (2.2.0) - - Protobuf (3.23.1) + - Protobuf (3.23.4) - SDWebImage (5.13.2): - SDWebImage/Core (= 5.13.2) - SDWebImage/Core (5.13.2) @@ -800,8 +800,8 @@ SPEC CHECKSUMS: CocoaImageHashing: 8656031d0899abe6c1c415827de43e9798189c53 CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da DAPI-GRPC: 138d62523bbfe7e88a39896f1053c0bc12390d9f - DashSharedCore: 4d583600745bc8b8ff32c3968d297fb1bd0e4218 - DashSync: 3ff50b1878685726b452638405d49d5802012732 + DashSharedCore: 6e9af4b23b9ec40ae33040fb9ae4d12b320c70ec + DashSync: a00e5103f25ea8f62632f9c763902df7353b0468 DSDynamicOptions: 347cc5d2c4e080eb3de6a86719ad3d861b82adfc DWAlertController: 5f4cd8adf90336331c054857f709f5f8d4b16a5b Firebase: 5f8193dff4b5b7c5d5ef72ae54bb76c08e2b841d @@ -822,7 +822,7 @@ SPEC CHECKSUMS: Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef - Protobuf: 3dd214698ddb7afd832da281c777b2e9facef401 + Protobuf: c6bc59bbab3d38a71c67f62d7cb7ca8f8ea4eca1 SDWebImage: 72f86271a6f3139cc7e4a89220946489d4b9a866 SQLite.swift: 903bfa3bc9ab06345fdfbb578e34f47cfcf417da SQLiteMigrationManager.swift: 5383578f5bc8955c06695e8bf04835ee0e6673a8