feat: Implement backups initializing wizard

This commit is contained in:
NaiJi 2023-09-26 23:44:46 -03:00
parent 8f6cb77cc4
commit a19f0dc2ef
8 changed files with 277 additions and 37 deletions

View file

@ -25,27 +25,33 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
@override
Future<void> load() async {
if (serverInstallationCubit.state is ServerInstallationFinished) {
final BackblazeBucket? bucket = getIt<ApiConfigModel>().backblazeBucket;
final BackupConfiguration? backupConfig =
await api.getBackupsConfiguration();
final BackupsCredential? backupsCredential =
getIt<ApiConfigModel>().backblazeCredential;
final List<Backup> 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<ApiConfigModel>().backblazeBucket;
final BackupConfiguration? backupConfig =
await api.getBackupsConfiguration();
final BackupsCredential? backupsCredential =
getIt<ApiConfigModel>().backblazeCredential;
final List<Backup> 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<void> setBackupsKey(
@ -58,7 +64,20 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
provider: BackupsProviderType.backblaze,
);
await getIt<ApiConfigModel>().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<void> initializeBackups() async {
@ -290,6 +309,6 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
@override
void clear() async {
emit(const BackupsState());
emit(BackupsNotFinishedState.fromBackupsState(const BackupsState()));
}
}

View file

@ -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<Backup>? 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,
}

View file

@ -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(),
),
],

View file

@ -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,
});

View file

@ -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<BackupsCubit>();
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<String> titles = [
'backup.steps.hosting',
'backup.steps.period',
'backup.steps.rotation',
];
return BlocListener<BackupsCubit, BackupsState>(
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,
),
),
],
),
),
),
],
),
),
),
);
}
}

View file

@ -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',

View file

@ -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),

View file

@ -186,10 +186,10 @@ abstract class _$RootRouter extends RootStackRouter {
child: const RecoveryRouting(),
);
},
BackupProviderPickerRoute.name: (routeData) {
BackupsInitializingRoute.name: (routeData) {
return AutoRoutePage<dynamic>(
routeData: routeData,
child: const BackupProviderPickerPage(),
child: const BackupsInitializingPage(),
);
},
};
@ -691,15 +691,15 @@ class RecoveryRoute extends PageRouteInfo<void> {
}
/// generated route for
/// [BackupProviderPickerPage]
class BackupProviderPickerRoute extends PageRouteInfo<void> {
const BackupProviderPickerRoute({List<PageRouteInfo>? children})
/// [BackupsInitializingPage]
class BackupsInitializingRoute extends PageRouteInfo<void> {
const BackupsInitializingRoute({List<PageRouteInfo>? children})
: super(
BackupProviderPickerRoute.name,
BackupsInitializingRoute.name,
initialChildren: children,
);
static const String name = 'BackupProviderPickerRoute';
static const String name = 'BackupsInitializingRoute';
static const PageInfo<void> page = PageInfo<void>(name);
}