From a19f0dc2efa342e230ac2a8f090e1677aceb7548 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Tue, 26 Sep 2023 23:44:46 -0300 Subject: [PATCH] feat: Implement backups initializing wizard --- lib/logic/cubit/backups/backups_cubit.dart | 63 +++++--- lib/logic/cubit/backups/backups_state.dart | 78 ++++++++++ lib/ui/pages/backups/backup_details.dart | 2 +- .../backup_provider_picker.dart} | 6 +- .../backups/setup/backups_initializing.dart | 146 ++++++++++++++++++ .../setup/initializing/initializing.dart | 1 - lib/ui/router/router.dart | 4 +- lib/ui/router/router.gr.dart | 14 +- 8 files changed, 277 insertions(+), 37 deletions(-) rename lib/ui/pages/backups/{backup_provider_picker_page.dart => setup/backup_provider_picker.dart} (94%) create mode 100644 lib/ui/pages/backups/setup/backups_initializing.dart diff --git a/lib/logic/cubit/backups/backups_cubit.dart b/lib/logic/cubit/backups/backups_cubit.dart index e6381542..361cda06 100644 --- a/lib/logic/cubit/backups/backups_cubit.dart +++ b/lib/logic/cubit/backups/backups_cubit.dart @@ -25,27 +25,33 @@ class BackupsCubit extends ServerInstallationDependendCubit { @override Future load() async { - if (serverInstallationCubit.state is ServerInstallationFinished) { - final BackblazeBucket? bucket = getIt().backblazeBucket; - final BackupConfiguration? backupConfig = - await api.getBackupsConfiguration(); - final BackupsCredential? backupsCredential = - getIt().backblazeCredential; - final List backups = await api.getBackups(); - backups.sort((final a, final b) => b.time.compareTo(a.time)); - emit( - state.copyWith( - backblazeBucket: bucket, - isInitialized: backupConfig?.isInitialized, - autobackupPeriod: backupConfig?.autobackupPeriod ?? Duration.zero, - autobackupQuotas: backupConfig?.autobackupQuotas, - backupsCredential: backupsCredential, - backups: backups, - preventActions: false, - refreshing: false, - ), - ); + if (serverInstallationCubit.state is! ServerInstallationFinished) { + return; } + + final BackblazeBucket? bucket = getIt().backblazeBucket; + final BackupConfiguration? backupConfig = + await api.getBackupsConfiguration(); + final BackupsCredential? backupsCredential = + getIt().backblazeCredential; + final List backups = await api.getBackups(); + backups.sort((final a, final b) => b.time.compareTo(a.time)); + final bool? initialized = backupConfig?.isInitialized; + final nextState = state.copyWith( + backblazeBucket: bucket, + isInitialized: initialized, + autobackupPeriod: backupConfig?.autobackupPeriod ?? Duration.zero, + autobackupQuotas: backupConfig?.autobackupQuotas, + backupsCredential: backupsCredential, + backups: backups, + preventActions: false, + refreshing: false, + ); + emit( + (initialized == null || initialized == false) + ? BackupsNotFinishedState.fromBackupsState(nextState) + : nextState, + ); } Future setBackupsKey( @@ -58,7 +64,20 @@ class BackupsCubit extends ServerInstallationDependendCubit { provider: BackupsProviderType.backblaze, ); await getIt().storeBackblazeCredential(backupsCredential); - emit(state.copyWith(backupsCredential: backupsCredential)); + if (state is BackupsNotFinishedState) { + emit( + (state as BackupsNotFinishedState).copyNotFinishedWith( + backupsCredential: backupsCredential, + step: BackupsInitializingStep.period, + ), + ); + return; + } + emit( + state.copyWith( + backupsCredential: backupsCredential, + ), + ); } Future initializeBackups() async { @@ -290,6 +309,6 @@ class BackupsCubit extends ServerInstallationDependendCubit { @override void clear() async { - emit(const BackupsState()); + emit(BackupsNotFinishedState.fromBackupsState(const BackupsState())); } } diff --git a/lib/logic/cubit/backups/backups_state.dart b/lib/logic/cubit/backups/backups_state.dart index b9d563eb..baf408b8 100644 --- a/lib/logic/cubit/backups/backups_state.dart +++ b/lib/logic/cubit/backups/backups_state.dart @@ -63,3 +63,81 @@ class BackupsState extends ServerInstallationDependendState { : autobackupPeriod ?? this.autobackupPeriod, ); } + +class BackupsNotFinishedState extends BackupsState { + BackupsNotFinishedState.fromBackupsState(final BackupsState backupsState) + : this( + step: BackupsInitializingStep.hosting, + isInitialized: false, + autobackupPeriod: backupsState.autobackupPeriod, + autobackupQuotas: backupsState.autobackupQuotas, + backblazeBucket: backupsState.backblazeBucket, + backups: backupsState.backups, + backupsCredential: backupsState.backupsCredential, + preventActions: backupsState.preventActions, + refreshTimer: backupsState.refreshTimer, + refreshing: backupsState.refreshing, + ); + + const BackupsNotFinishedState({ + required this.step, + super.isInitialized = false, + super.backups = const [], + super.preventActions = true, + super.refreshTimer = const Duration(seconds: 60), + super.refreshing = true, + super.autobackupPeriod, + super.backblazeBucket, + super.autobackupQuotas, + super.backupsCredential, + }); + + final BackupsInitializingStep step; + + BackupsNotFinishedState copyNotFinishedWith({ + required final BackupsInitializingStep step, + final bool? isInitialized, + final List? backups, + final bool? preventActions, + final Duration? refreshTimer, + final bool? refreshing, + final Duration? autobackupPeriod, + final BackblazeBucket? backblazeBucket, + final AutobackupQuotas? autobackupQuotas, + final BackupsCredential? backupsCredential, + }) => + BackupsNotFinishedState( + isInitialized: isInitialized ?? this.isInitialized, + backups: backups ?? this.backups, + preventActions: preventActions ?? this.preventActions, + refreshTimer: refreshTimer ?? this.refreshTimer, + refreshing: refreshing ?? this.refreshing, + backupsCredential: backupsCredential ?? this.backupsCredential, + backblazeBucket: backblazeBucket ?? this.backblazeBucket, + autobackupQuotas: autobackupQuotas ?? this.autobackupQuotas, + step: step, + // The autobackupPeriod might be null, so if the duration is set to 0, we + // set it to null. + autobackupPeriod: autobackupPeriod?.inSeconds == 0 + ? null + : autobackupPeriod ?? this.autobackupPeriod, + ); + + BackupsState finish() => BackupsState( + isInitialized: true, + autobackupPeriod: autobackupPeriod, + autobackupQuotas: autobackupQuotas, + backblazeBucket: backblazeBucket, + backups: backups, + backupsCredential: backupsCredential, + preventActions: preventActions, + refreshTimer: refreshTimer, + refreshing: refreshing, + ); +} + +enum BackupsInitializingStep { + hosting, + period, + rotation, +} diff --git a/lib/ui/pages/backups/backup_details.dart b/lib/ui/pages/backups/backup_details.dart index b10615bf..d3ef4ed8 100644 --- a/lib/ui/pages/backups/backup_details.dart +++ b/lib/ui/pages/backups/backup_details.dart @@ -75,7 +75,7 @@ class BackupDetailsPage extends StatelessWidget { BrandButton.rised( onPressed: preventActions ? null - : () => context.pushRoute(const BackupProviderPickerRoute()), + : () => context.pushRoute(const BackupsInitializingRoute()), text: 'backup.initialize'.tr(), ), ], diff --git a/lib/ui/pages/backups/backup_provider_picker_page.dart b/lib/ui/pages/backups/setup/backup_provider_picker.dart similarity index 94% rename from lib/ui/pages/backups/backup_provider_picker_page.dart rename to lib/ui/pages/backups/setup/backup_provider_picker.dart index c964e3e3..f54a35bd 100644 --- a/lib/ui/pages/backups/backup_provider_picker_page.dart +++ b/lib/ui/pages/backups/setup/backup_provider_picker.dart @@ -1,4 +1,3 @@ -import 'package:auto_route/auto_route.dart'; import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -7,9 +6,8 @@ import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; import 'package:selfprivacy/ui/layouts/responsive_layout_with_infobox.dart'; -@RoutePage() -class BackupProviderPickerPage extends StatelessWidget { - const BackupProviderPickerPage({ +class BackupProviderPicker extends StatelessWidget { + const BackupProviderPicker({ super.key, }); diff --git a/lib/ui/pages/backups/setup/backups_initializing.dart b/lib/ui/pages/backups/setup/backups_initializing.dart new file mode 100644 index 00000000..78ced1f1 --- /dev/null +++ b/lib/ui/pages/backups/setup/backups_initializing.dart @@ -0,0 +1,146 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:cubit_form/cubit_form.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/components/buttons/outlined_button.dart'; +import 'package:selfprivacy/ui/components/drawers/progress_drawer.dart'; +import 'package:selfprivacy/ui/components/progress_bar/progress_bar.dart'; +import 'package:selfprivacy/ui/components/drawers/support_drawer.dart'; +import 'package:selfprivacy/ui/pages/backups/setup/backup_provider_picker.dart'; +import 'package:selfprivacy/ui/router/router.dart'; +import 'package:selfprivacy/utils/breakpoints.dart'; + +@RoutePage() +class BackupsInitializingPage extends StatelessWidget { + const BackupsInitializingPage({super.key}); + + @override + Widget build(final BuildContext context) { + final Widget actualInitializingPage; + final cubit = context.watch(); + final currentStep = ((cubit.state) as BackupsNotFinishedState).step; + switch (currentStep) { + case BackupsInitializingStep.period: + actualInitializingPage = const BackupProviderPicker(); + break; + case BackupsInitializingStep.rotation: + actualInitializingPage = const BackupProviderPicker(); + break; + case BackupsInitializingStep.hosting: + default: + actualInitializingPage = const BackupProviderPicker(); + break; + } + + final List titles = [ + 'backup.steps.hosting', + 'backup.steps.period', + 'backup.steps.rotation', + ]; + + return BlocListener( + listener: (final context, final state) { + if (cubit.state is! BackupsNotFinishedState) { + context.router.pop(); + } + }, + child: Scaffold( + endDrawer: const SupportDrawer(), + endDrawerEnableOpenDragGesture: false, + appBar: Breakpoints.large.isActive(context) + ? null + : AppBar( + actions: [ + if (cubit.state is! BackupsNotFinishedState) + IconButton( + icon: const Icon(Icons.check), + onPressed: () { + context.router.pop(); + }, + ), + const SizedBox.shrink(), + ], + title: Text( + 'more_page.configuration_wizard'.tr(), + ), + bottom: PreferredSize( + preferredSize: const Size.fromHeight(28), + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), + child: ProgressBar( + steps: const [ + 'Hosting', + 'Automatic backups', + 'Rotation settings', + ], + activeIndex: currentStep.index, + ), + ), + ), + ), + body: LayoutBuilder( + builder: (final context, final constraints) => Row( + children: [ + if (Breakpoints.large.isActive(context)) + ProgressDrawer( + steps: titles, + currentStep: currentStep.index, + title: 'more_page.configuration_wizard'.tr(), + constraints: constraints, + trailing: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (cubit.state is ServerInstallationEmpty || + cubit.state is ServerInstallationNotFinished) + Container( + alignment: Alignment.center, + child: BrandButton.filled( + text: 'basis.connect_to_existing'.tr(), + onPressed: () { + context.router.replace(const RecoveryRoute()); + }, + ), + ), + // const SizedBox(height: 8), + BrandOutlinedButton( + child: Text( + 'basis.later'.tr(), + ), + onPressed: () { + context.router.pop(); + }, + ), + ], + ), + ), + SizedBox( + width: constraints.maxWidth - + (Breakpoints.large.isActive(context) ? 300 : 0), + height: constraints.maxHeight, + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: Breakpoints.large.isActive(context) + ? const EdgeInsets.all(16.0) + : const EdgeInsets.fromLTRB(16.0, 0, 16.0, 0.0), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: actualInitializingPage, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/pages/setup/initializing/initializing.dart b/lib/ui/pages/setup/initializing/initializing.dart index 14851024..cad8241a 100644 --- a/lib/ui/pages/setup/initializing/initializing.dart +++ b/lib/ui/pages/setup/initializing/initializing.dart @@ -54,7 +54,6 @@ class InitializingPage extends StatelessWidget { 'initializing.steps.hosting', 'initializing.steps.server_type', 'initializing.steps.dns_provider', - 'initializing.steps.backups_provider', 'initializing.steps.domain', 'initializing.steps.master_account', 'initializing.steps.server', diff --git a/lib/ui/router/router.dart b/lib/ui/router/router.dart index 1433e1c3..2610ca86 100644 --- a/lib/ui/router/router.dart +++ b/lib/ui/router/router.dart @@ -5,6 +5,7 @@ import 'package:selfprivacy/logic/models/disk_status.dart'; import 'package:selfprivacy/logic/models/service.dart'; import 'package:selfprivacy/ui/pages/backups/backup_details.dart'; import 'package:selfprivacy/ui/pages/backups/backups_list.dart'; +import 'package:selfprivacy/ui/pages/backups/setup/backups_initializing.dart'; import 'package:selfprivacy/ui/pages/devices/devices.dart'; import 'package:selfprivacy/ui/pages/dns_details/dns_details.dart'; import 'package:selfprivacy/ui/pages/more/about_application.dart'; @@ -25,7 +26,6 @@ import 'package:selfprivacy/ui/pages/services/services.dart'; import 'package:selfprivacy/ui/pages/setup/initializing/initializing.dart'; import 'package:selfprivacy/ui/pages/setup/recovering/recovery_routing.dart'; import 'package:selfprivacy/ui/pages/users/users.dart'; -import 'package:selfprivacy/ui/pages/backups/backup_provider_picker_page.dart'; part 'router.gr.dart'; @@ -101,7 +101,7 @@ class RootRouter extends _$RootRouter { AutoRoute(page: BackupsListRoute.page), AutoRoute(page: ServerStorageRoute.page), AutoRoute(page: ExtendingVolumeRoute.page), - AutoRoute(page: BackupProviderPickerRoute.page), + AutoRoute(page: BackupsInitializingRoute.page), ], ), AutoRoute(page: ServicesMigrationRoute.page), diff --git a/lib/ui/router/router.gr.dart b/lib/ui/router/router.gr.dart index 80802db2..42b96d22 100644 --- a/lib/ui/router/router.gr.dart +++ b/lib/ui/router/router.gr.dart @@ -186,10 +186,10 @@ abstract class _$RootRouter extends RootStackRouter { child: const RecoveryRouting(), ); }, - BackupProviderPickerRoute.name: (routeData) { + BackupsInitializingRoute.name: (routeData) { return AutoRoutePage( routeData: routeData, - child: const BackupProviderPickerPage(), + child: const BackupsInitializingPage(), ); }, }; @@ -691,15 +691,15 @@ class RecoveryRoute extends PageRouteInfo { } /// generated route for -/// [BackupProviderPickerPage] -class BackupProviderPickerRoute extends PageRouteInfo { - const BackupProviderPickerRoute({List? children}) +/// [BackupsInitializingPage] +class BackupsInitializingRoute extends PageRouteInfo { + const BackupsInitializingRoute({List? children}) : super( - BackupProviderPickerRoute.name, + BackupsInitializingRoute.name, initialChildren: children, ); - static const String name = 'BackupProviderPickerRoute'; + static const String name = 'BackupsInitializingRoute'; static const PageInfo page = PageInfo(name); }