@@ -9,7 +9,10 @@ import SignalUI
99import StoreKit
1010import SwiftUI
1111
12- class BackupSettingsViewController : HostingController < BackupSettingsView > {
12+ class BackupSettingsViewController :
13+ HostingController < BackupSettingsView > ,
14+ BackupSettingsViewModel . ActionsDelegate
15+ {
1316 enum OnLoadAction {
1417 case none
1518 case presentWelcomeToBackupsSheet
@@ -95,11 +98,11 @@ class BackupSettingsViewController: HostingController<BackupSettingsView> {
9598 self . onLoadAction = onLoadAction
9699 self . viewModel = db. read { tx in
97100 let viewModel = BackupSettingsViewModel (
98- backupSubscriptionLoadingState: . loading, // Default, loaded after init
101+ backupSubscriptionLoadingState: . loading, // Default, loaded async
99102 backupPlan: backupPlanManager. backupPlan ( tx: tx) ,
100103 failedToDisableBackupsRemotely: backupDisablingManager. disableRemotelyFailed ( tx: tx) ,
101- latestBackupAttachmentDownloadUpdate: nil , // Default, loaded after init
102- latestBackupAttachmentUploadUpdate: nil , // Default, loaded after init
104+ latestBackupAttachmentDownloadUpdate: nil , // Default, loaded async
105+ latestBackupAttachmentUploadUpdate: nil , // Default, loaded async
103106 lastBackupDate: backupSettingsStore. lastBackupDate ( tx: tx) ,
104107 lastBackupSizeBytes: backupSettingsStore. lastBackupSizeBytes ( tx: tx) ,
105108 shouldAllowBackupUploadsOnCellular: backupSettingsStore. shouldAllowBackupUploadsOnCellular ( tx: tx)
@@ -117,8 +120,8 @@ class BackupSettingsViewController: HostingController<BackupSettingsView> {
117120 OWSTableViewController2 . removeBackButtonText ( viewController: self )
118121
119122 viewModel. actionsDelegate = self
120- // Run as soon as we've set the actionDelegate.
121- viewModel . loadBackupSubscription ( )
123+
124+ loadBackupSubscription ( )
122125
123126 eventObservationTasks = [
124127 Task { [ weak self, backupAttachmentDownloadTracker] in
@@ -176,13 +179,11 @@ class BackupSettingsViewController: HostingController<BackupSettingsView> {
176179 break
177180 }
178181
179- viewModel . loadBackupSubscription ( )
182+ loadBackupSubscription ( )
180183 }
181- }
182184
183- // MARK: - BackupSettingsViewModel.ActionsDelegate
185+ // MARK: - BackupSettingsViewModel.ActionsDelegate
184186
185- extension BackupSettingsViewController : BackupSettingsViewModel . ActionsDelegate {
186187 fileprivate func enableBackups(
187188 implicitPlanSelection: ChooseBackupPlanViewController . PlanSelection ?
188189 ) {
@@ -379,7 +380,31 @@ extension BackupSettingsViewController: BackupSettingsViewModel.ActionsDelegate
379380
380381 // MARK: -
381382
382- fileprivate func loadBackupSubscription( ) async throws -> BackupSettingsViewModel . BackupSubscriptionLoadingState . LoadedBackupSubscription {
383+ private lazy var loadBackupSubscriptionQueue = SerialTaskQueue ( )
384+
385+ fileprivate func loadBackupSubscription( ) {
386+ loadBackupSubscriptionQueue. enqueue { @MainActor [ self ] in
387+ withAnimation {
388+ viewModel. backupSubscriptionLoadingState = . loading
389+ }
390+
391+ let newLoadingState : BackupSettingsViewModel . BackupSubscriptionLoadingState
392+ do {
393+ let backupSubscription = try await _loadBackupSubscription ( )
394+ newLoadingState = . loaded( backupSubscription)
395+ } catch let error where error. isNetworkFailureOrTimeout {
396+ newLoadingState = . networkError
397+ } catch {
398+ newLoadingState = . genericError
399+ }
400+
401+ withAnimation {
402+ viewModel. backupSubscriptionLoadingState = newLoadingState
403+ }
404+ }
405+ }
406+
407+ private func _loadBackupSubscription( ) async throws -> BackupSettingsViewModel . BackupSubscriptionLoadingState . LoadedBackupSubscription {
383408 var currentBackupPlan = db. read { backupPlanManager. backupPlan ( tx: $0) }
384409
385410 switch currentBackupPlan {
@@ -447,7 +472,7 @@ extension BackupSettingsViewController: BackupSettingsViewModel.ActionsDelegate
447472
448473 // Reload the BackupPlan, since our subscription may now be in a
449474 // different state (e.g., set to not renew).
450- viewModel . loadBackupSubscription ( )
475+ loadBackupSubscription ( )
451476 }
452477 }
453478
@@ -723,7 +748,7 @@ private class BackupSettingsViewModel: ObservableObject {
723748
724749 func disableBackups( )
725750
726- func loadBackupSubscription( ) async throws -> BackupSubscriptionLoadingState . LoadedBackupSubscription
751+ func loadBackupSubscription( )
727752 func upgradeFromFreeToPaidPlan( )
728753 func manageOrCancelPaidPlan( )
729754 func managePaidPlanAsTester( )
@@ -767,8 +792,6 @@ private class BackupSettingsViewModel: ObservableObject {
767792
768793 weak var actionsDelegate : ActionsDelegate ?
769794
770- private let loadBackupSubscriptionQueue : SerialTaskQueue
771-
772795 init (
773796 backupSubscriptionLoadingState: BackupSubscriptionLoadingState ,
774797 backupPlan: BackupPlan ,
@@ -789,8 +812,6 @@ private class BackupSettingsViewModel: ObservableObject {
789812 self . lastBackupDate = lastBackupDate
790813 self . lastBackupSizeBytes = lastBackupSizeBytes
791814 self . shouldAllowBackupUploadsOnCellular = shouldAllowBackupUploadsOnCellular
792-
793- self . loadBackupSubscriptionQueue = SerialTaskQueue ( )
794815 }
795816
796817 // MARK: -
@@ -814,28 +835,10 @@ private class BackupSettingsViewModel: ObservableObject {
814835 }
815836 }
816837
817- func loadBackupSubscription( ) {
818- guard let actionsDelegate else { return }
819-
820- loadBackupSubscriptionQueue. enqueue { @MainActor [ self , actionsDelegate] in
821- withAnimation {
822- backupSubscriptionLoadingState = . loading
823- }
824-
825- let newLoadingState : BackupSubscriptionLoadingState
826- do {
827- let backupSubscription = try await actionsDelegate. loadBackupSubscription ( )
828- newLoadingState = . loaded( backupSubscription)
829- } catch let error where error. isNetworkFailureOrTimeout {
830- newLoadingState = . networkError
831- } catch {
832- newLoadingState = . genericError
833- }
838+ // MARK: -
834839
835- withAnimation {
836- backupSubscriptionLoadingState = newLoadingState
837- }
838- }
840+ func loadBackupSubscription( ) {
841+ actionsDelegate? . loadBackupSubscription ( )
839842 }
840843
841844 func upgradeFromFreeToPaidPlan( ) {
@@ -1678,21 +1681,13 @@ private extension BackupSettingsViewModel {
16781681 failedToDisableBackupsRemotely: Bool = false ,
16791682 latestBackupAttachmentDownloadUpdateState: BackupSettingsAttachmentDownloadTracker . DownloadUpdate . State ? = nil ,
16801683 latestBackupAttachmentUploadUpdateState: BackupSettingsAttachmentUploadTracker . UploadUpdate . State ? = nil ,
1681- backupPlanLoadResult : Result < BackupSubscriptionLoadingState . LoadedBackupSubscription , Error > ,
1684+ backupSubscriptionLoadingState : BackupSubscriptionLoadingState ,
16821685 ) -> BackupSettingsViewModel {
16831686 class PreviewActionsDelegate : ActionsDelegate {
1684- private let backupPlanLoadResult : Result < BackupSubscriptionLoadingState . LoadedBackupSubscription , Error >
1685- init ( backupPlanLoadResult: Result < BackupSubscriptionLoadingState . LoadedBackupSubscription , Error > ) {
1686- self . backupPlanLoadResult = backupPlanLoadResult
1687- }
1688-
16891687 func enableBackups( implicitPlanSelection: ChooseBackupPlanViewController . PlanSelection ? ) { print ( " Enabling! implicitPlanSelection: \( implicitPlanSelection as Any ) " ) }
16901688 func disableBackups( ) { print ( " Disabling! " ) }
16911689
1692- func loadBackupSubscription( ) async throws -> BackupSettingsViewModel . BackupSubscriptionLoadingState . LoadedBackupSubscription {
1693- try ! await Task . sleep ( nanoseconds: 2 . clampedNanoseconds)
1694- return try backupPlanLoadResult. get ( )
1695- }
1690+ func loadBackupSubscription( ) { print ( " Loading BackupSubscription! " ) }
16961691 func upgradeFromFreeToPaidPlan( ) { print ( " Upgrading! " ) }
16971692 func manageOrCancelPaidPlan( ) { print ( " Managing or canceling! " ) }
16981693 func managePaidPlanAsTester( ) { print ( " Managing as tester! " ) }
@@ -1709,7 +1704,7 @@ private extension BackupSettingsViewModel {
17091704 }
17101705
17111706 let viewModel = BackupSettingsViewModel (
1712- backupSubscriptionLoadingState: . loading ,
1707+ backupSubscriptionLoadingState: backupSubscriptionLoadingState ,
17131708 backupPlan: backupPlan,
17141709 failedToDisableBackupsRemotely: failedToDisableBackupsRemotely,
17151710 latestBackupAttachmentDownloadUpdate: latestBackupAttachmentDownloadUpdateState. map {
@@ -1730,43 +1725,42 @@ private extension BackupSettingsViewModel {
17301725 lastBackupSizeBytes: 2_400_000_000 ,
17311726 shouldAllowBackupUploadsOnCellular: false
17321727 )
1733- let actionsDelegate = PreviewActionsDelegate ( backupPlanLoadResult : backupPlanLoadResult )
1728+ let actionsDelegate = PreviewActionsDelegate ( )
17341729 viewModel. actionsDelegate = actionsDelegate
17351730 ObjectRetainer . retainObject ( actionsDelegate, forLifetimeOf: viewModel)
17361731
1737- viewModel. loadBackupSubscription ( )
17381732 return viewModel
17391733 }
17401734}
17411735
17421736#Preview( " Plan: Paid " ) {
17431737 BackupSettingsView ( viewModel: . forPreview(
17441738 backupPlan: . paid( optimizeLocalStorage: false ) ,
1745- backupPlanLoadResult : . success ( . paid(
1739+ backupSubscriptionLoadingState : . loaded ( . paid(
17461740 price: FiatMoney ( currencyCode: " USD " , value: 1.99 ) ,
17471741 renewalDate: Date ( ) . addingTimeInterval ( . week)
1748- ) )
1742+ ) ) ,
17491743 ) )
17501744}
17511745
17521746#Preview( " Plan: Free " ) {
17531747 BackupSettingsView ( viewModel: . forPreview(
17541748 backupPlan: . free,
1755- backupPlanLoadResult : . success ( . free)
1749+ backupSubscriptionLoadingState : . loaded ( . free)
17561750 ) )
17571751}
17581752
17591753#Preview( " Plan: Free For Testers " ) {
17601754 BackupSettingsView ( viewModel: . forPreview(
17611755 backupPlan: . paidAsTester( optimizeLocalStorage: false ) ,
1762- backupPlanLoadResult : . success ( . paidButFreeForTesters)
1756+ backupSubscriptionLoadingState : . loaded ( . paidButFreeForTesters)
17631757 ) )
17641758}
17651759
17661760#Preview( " Plan: Expiring " ) {
17671761 BackupSettingsView ( viewModel: . forPreview(
17681762 backupPlan: . paidExpiringSoon( optimizeLocalStorage: false ) ,
1769- backupPlanLoadResult : . success ( . paidButExpiring(
1763+ backupSubscriptionLoadingState : . loaded ( . paidButExpiring(
17701764 expirationDate: Date ( ) . addingTimeInterval ( . week)
17711765 ) )
17721766 ) )
@@ -1775,7 +1769,7 @@ private extension BackupSettingsViewModel {
17751769#Preview( " Plan: Expired " ) {
17761770 BackupSettingsView ( viewModel: . forPreview(
17771771 backupPlan: . paidExpiringSoon( optimizeLocalStorage: false ) ,
1778- backupPlanLoadResult : . success ( . paidButExpired(
1772+ backupSubscriptionLoadingState : . loaded ( . paidButExpired(
17791773 expirationDate: Date ( ) . addingTimeInterval ( - 1 * . week)
17801774 ) )
17811775 ) )
@@ -1784,22 +1778,22 @@ private extension BackupSettingsViewModel {
17841778#Preview( " Plan: Network Error " ) {
17851779 BackupSettingsView ( viewModel: . forPreview(
17861780 backupPlan: . paid( optimizeLocalStorage: false ) ,
1787- backupPlanLoadResult : . failure ( OWSHTTPError . networkFailure ( . genericTimeout ) )
1781+ backupSubscriptionLoadingState : . networkError
17881782 ) )
17891783}
17901784
17911785#Preview( " Plan: Generic Error " ) {
17921786 BackupSettingsView ( viewModel: . forPreview(
17931787 backupPlan: . paid( optimizeLocalStorage: false ) ,
1794- backupPlanLoadResult : . failure ( OWSGenericError ( " " ) )
1788+ backupSubscriptionLoadingState : . genericError
17951789 ) )
17961790}
17971791
17981792#Preview( " Downloads: Suspended " ) {
17991793 BackupSettingsView ( viewModel: . forPreview(
18001794 backupPlan: . paid( optimizeLocalStorage: false ) ,
18011795 latestBackupAttachmentDownloadUpdateState: . suspended,
1802- backupPlanLoadResult : . success ( . paid(
1796+ backupSubscriptionLoadingState : . loaded ( . paid(
18031797 price: FiatMoney ( currencyCode: " USD " , value: 1.99 ) ,
18041798 renewalDate: Date ( ) . addingTimeInterval ( . week)
18051799 ) )
@@ -1810,93 +1804,93 @@ private extension BackupSettingsViewModel {
18101804 BackupSettingsView ( viewModel: . forPreview(
18111805 backupPlan: . free,
18121806 latestBackupAttachmentDownloadUpdateState: . suspended,
1813- backupPlanLoadResult : . success ( . free)
1807+ backupSubscriptionLoadingState : . loaded ( . free)
18141808 ) )
18151809}
18161810
18171811#Preview( " Downloads: Running " ) {
18181812 BackupSettingsView ( viewModel: . forPreview(
18191813 backupPlan: . free,
18201814 latestBackupAttachmentDownloadUpdateState: . running,
1821- backupPlanLoadResult : . success ( . free)
1815+ backupSubscriptionLoadingState : . loaded ( . free)
18221816 ) )
18231817}
18241818
18251819#Preview( " Downloads: Paused (Battery) " ) {
18261820 BackupSettingsView ( viewModel: . forPreview(
18271821 backupPlan: . free,
18281822 latestBackupAttachmentDownloadUpdateState: . pausedLowBattery,
1829- backupPlanLoadResult : . success ( . free)
1823+ backupSubscriptionLoadingState : . loaded ( . free)
18301824 ) )
18311825}
18321826
18331827#Preview( " Downloads: Paused (WiFi) " ) {
18341828 BackupSettingsView ( viewModel: . forPreview(
18351829 backupPlan: . free,
18361830 latestBackupAttachmentDownloadUpdateState: . pausedNeedsWifi,
1837- backupPlanLoadResult : . success ( . free)
1831+ backupSubscriptionLoadingState : . loaded ( . free)
18381832 ) )
18391833}
18401834
18411835#Preview( " Downloads: Paused (Internet) " ) {
18421836 BackupSettingsView ( viewModel: . forPreview(
18431837 backupPlan: . free,
18441838 latestBackupAttachmentDownloadUpdateState: . pausedNeedsInternet,
1845- backupPlanLoadResult : . success ( . free)
1839+ backupSubscriptionLoadingState : . loaded ( . free)
18461840 ) )
18471841}
18481842
18491843#Preview( " Downloads: Disk Space Error " ) {
18501844 BackupSettingsView ( viewModel: . forPreview(
18511845 backupPlan: . free,
18521846 latestBackupAttachmentDownloadUpdateState: . outOfDiskSpace( bytesRequired: 200_000_000 ) ,
1853- backupPlanLoadResult : . success ( . free)
1847+ backupSubscriptionLoadingState : . loaded ( . free)
18541848 ) )
18551849}
18561850
18571851#Preview( " Uploads: Running " ) {
18581852 BackupSettingsView ( viewModel: . forPreview(
18591853 backupPlan: . free,
18601854 latestBackupAttachmentUploadUpdateState: . running,
1861- backupPlanLoadResult : . success ( . free)
1855+ backupSubscriptionLoadingState : . loaded ( . free)
18621856 ) )
18631857}
18641858
18651859#Preview( " Uploads: Paused (WiFi) " ) {
18661860 BackupSettingsView ( viewModel: . forPreview(
18671861 backupPlan: . free,
18681862 latestBackupAttachmentUploadUpdateState: . pausedNeedsWifi,
1869- backupPlanLoadResult : . success ( . free)
1863+ backupSubscriptionLoadingState : . loaded ( . free)
18701864 ) )
18711865}
18721866
18731867#Preview( " Uploads: Paused (Battery) " ) {
18741868 BackupSettingsView ( viewModel: . forPreview(
18751869 backupPlan: . free,
18761870 latestBackupAttachmentUploadUpdateState: . pausedLowBattery,
1877- backupPlanLoadResult : . success ( . free)
1871+ backupSubscriptionLoadingState : . loaded ( . free)
18781872 ) )
18791873}
18801874
18811875#Preview( " Disabling: Success " ) {
18821876 BackupSettingsView ( viewModel: . forPreview(
18831877 backupPlan: . disabled,
1884- backupPlanLoadResult : . success ( . free) ,
1878+ backupSubscriptionLoadingState : . loaded ( . free) ,
18851879 ) )
18861880}
18871881
18881882#Preview( " Disabling: Remotely " ) {
18891883 BackupSettingsView ( viewModel: . forPreview(
18901884 backupPlan: . disabling,
1891- backupPlanLoadResult : . success ( . free) ,
1885+ backupSubscriptionLoadingState : . loaded ( . free) ,
18921886 ) )
18931887}
18941888
18951889#Preview( " Disabling: Remotely Failed " ) {
18961890 BackupSettingsView ( viewModel: . forPreview(
18971891 backupPlan: . disabled,
18981892 failedToDisableBackupsRemotely: true ,
1899- backupPlanLoadResult : . success ( . free) ,
1893+ backupSubscriptionLoadingState : . loaded ( . free) ,
19001894 ) )
19011895}
19021896
0 commit comments