From 10891881ae3c8bb605635f3112455edf94365ef3 Mon Sep 17 00:00:00 2001 From: Inex Code Date: Mon, 19 Sep 2022 03:21:08 +0300 Subject: [PATCH] Service migrations --- .../cubit/server_jobs/server_jobs_state.dart | 7 +++-- lib/logic/cubit/services/services_cubit.dart | 22 ++++++++++++++- lib/logic/cubit/services/services_state.dart | 19 ++++++++++++- .../components/jobs_content/jobs_content.dart | 13 ++++----- lib/ui/pages/more/more.dart | 5 ++-- ...migration.dart => services_migration.dart} | 27 ++++++++++++++----- lib/ui/pages/services/service_page.dart | 20 +++++++++++++- 7 files changed, 94 insertions(+), 19 deletions(-) rename lib/ui/pages/server_storage/binds_migration/{data_to_binds_migration.dart => services_migration.dart} (87%) diff --git a/lib/logic/cubit/server_jobs/server_jobs_state.dart b/lib/logic/cubit/server_jobs/server_jobs_state.dart index 25d8deab..cf9cb8e0 100644 --- a/lib/logic/cubit/server_jobs/server_jobs_state.dart +++ b/lib/logic/cubit/server_jobs/server_jobs_state.dart @@ -11,8 +11,11 @@ class ServerJobsState extends ServerInstallationDependendState { late final List _serverJobList; final String? migrationJobUid; - List get serverJobList => - _serverJobList.where((final ServerJob job) => !job.isHidden).toList(); + List get serverJobList { + final List list = _serverJobList; + list.sort((final a, final b) => b.createdAt.compareTo(a.createdAt)); + return list; + } @override List get props => [migrationJobUid, ..._serverJobList]; diff --git a/lib/logic/cubit/services/services_cubit.dart b/lib/logic/cubit/services/services_cubit.dart index c423fd9f..4bb575a6 100644 --- a/lib/logic/cubit/services/services_cubit.dart +++ b/lib/logic/cubit/services/services_cubit.dart @@ -19,6 +19,7 @@ class ServicesCubit extends ServerInstallationDependendCubit { emit( ServicesState( services: services, + lockedServices: const [], ), ); timer = Timer(const Duration(seconds: 10), () => reload(useTimer: true)); @@ -28,7 +29,7 @@ class ServicesCubit extends ServerInstallationDependendCubit { Future reload({final bool useTimer = false}) async { final List services = await api.getAllServices(); emit( - ServicesState( + state.copyWith( services: services, ), ); @@ -38,7 +39,26 @@ class ServicesCubit extends ServerInstallationDependendCubit { } Future restart(final String serviceId) async { + emit(state.copyWith(lockedServices: [...state.lockedServices, serviceId])); await api.restartService(serviceId); + await Future.delayed(const Duration(seconds: 2)); + reload(); + await Future.delayed(const Duration(seconds: 10)); + emit( + state.copyWith( + lockedServices: state.lockedServices + .where((final element) => element != serviceId) + .toList(), + ), + ); + reload(); + } + + Future moveService( + final String serviceId, + final String destination, + ) async { + await api.moveService(serviceId, destination); } @override diff --git a/lib/logic/cubit/services/services_state.dart b/lib/logic/cubit/services/services_state.dart index 07b82e45..d5aa95eb 100644 --- a/lib/logic/cubit/services/services_state.dart +++ b/lib/logic/cubit/services/services_state.dart @@ -3,11 +3,18 @@ part of 'services_cubit.dart'; class ServicesState extends ServerInstallationDependendState { const ServicesState({ required this.services, + required this.lockedServices, }); - const ServicesState.empty() : this(services: const []); + const ServicesState.empty() + : this(services: const [], lockedServices: const []); final List services; + final List lockedServices; + + bool isServiceLocked(final String serviceId) => + lockedServices.contains(serviceId); + bool get isPasswordManagerEnable => services .firstWhere( (final service) => service.id == 'bitwarden', @@ -53,6 +60,7 @@ class ServicesState extends ServerInstallationDependendState { @override List get props => [ services, + lockedServices, ]; bool isEnableByType(final ServiceTypes type) { @@ -71,4 +79,13 @@ class ServicesState extends ServerInstallationDependendState { throw Exception('wrong state'); } } + + ServicesState copyWith({ + final List? services, + final List? lockedServices, + }) => + ServicesState( + services: services ?? this.services, + lockedServices: lockedServices ?? this.lockedServices, + ); } diff --git a/lib/ui/components/jobs_content/jobs_content.dart b/lib/ui/components/jobs_content/jobs_content.dart index 58fffd74..d43a194c 100644 --- a/lib/ui/components/jobs_content/jobs_content.dart +++ b/lib/ui/components/jobs_content/jobs_content.dart @@ -126,13 +126,14 @@ class JobsContent extends StatelessWidget { const SizedBox(height: 8), const Divider(height: 0), const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - 'jobs.server_jobs'.tr(), - style: Theme.of(context).textTheme.titleMedium, + if (serverJobs.isNotEmpty) + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'jobs.server_jobs'.tr(), + style: Theme.of(context).textTheme.titleMedium, + ), ), - ), ...serverJobs.map( (final job) => Dismissible( key: ValueKey(job.uid), diff --git a/lib/ui/pages/more/more.dart b/lib/ui/pages/more/more.dart index eeedd4d8..2dcdd40d 100644 --- a/lib/ui/pages/more/more.dart +++ b/lib/ui/pages/more/more.dart @@ -10,7 +10,7 @@ import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; import 'package:selfprivacy/ui/pages/devices/devices.dart'; import 'package:selfprivacy/ui/pages/recovery_key/recovery_key.dart'; -import 'package:selfprivacy/ui/pages/server_storage/binds_migration/data_to_binds_migration.dart'; +import 'package:selfprivacy/ui/pages/server_storage/binds_migration/services_migration.dart'; import 'package:selfprivacy/ui/pages/setup/initializing.dart'; import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart'; import 'package:selfprivacy/ui/pages/root_route.dart'; @@ -50,7 +50,7 @@ class MorePage extends StatelessWidget { _MoreMenuItem( title: 'providers.storage.start_migration_button'.tr(), iconData: Icons.drive_file_move_outline, - goTo: DataToBindsMigrationPage( + goTo: ServicesMigrationPage( diskStatus: context .watch() .state @@ -68,6 +68,7 @@ class MorePage extends StatelessWidget { service.id == 'nextcloud', ) .toList(), + isMigration: true, ), subtitle: 'not_ready_card.in_menu'.tr(), accent: true, diff --git a/lib/ui/pages/server_storage/binds_migration/data_to_binds_migration.dart b/lib/ui/pages/server_storage/binds_migration/services_migration.dart similarity index 87% rename from lib/ui/pages/server_storage/binds_migration/data_to_binds_migration.dart rename to lib/ui/pages/server_storage/binds_migration/services_migration.dart index c984dd5b..9783a479 100644 --- a/lib/ui/pages/server_storage/binds_migration/data_to_binds_migration.dart +++ b/lib/ui/pages/server_storage/binds_migration/services_migration.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart'; +import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; import 'package:selfprivacy/logic/models/disk_size.dart'; import 'package:selfprivacy/logic/models/service.dart'; import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; @@ -15,22 +16,23 @@ import 'package:selfprivacy/ui/helpers/modals.dart'; import 'package:selfprivacy/ui/pages/root_route.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; -class DataToBindsMigrationPage extends StatefulWidget { - const DataToBindsMigrationPage({ +class ServicesMigrationPage extends StatefulWidget { + const ServicesMigrationPage({ required this.services, required this.diskStatus, + required this.isMigration, final super.key, }); final DiskStatus diskStatus; final List services; + final bool isMigration; @override - State createState() => - _DataToBindsMigrationPageState(); + State createState() => _ServicesMigrationPageState(); } -class _DataToBindsMigrationPageState extends State { +class _ServicesMigrationPageState extends State { /// Service id to target migration disk name final Map serviceToDisk = {}; @@ -164,7 +166,20 @@ class _DataToBindsMigrationPageState extends State { FilledButton( title: 'providers.storage.start_migration_button'.tr(), onPressed: () { - context.read().migrateToBinds(serviceToDisk); + if (widget.isMigration) { + context.read().migrateToBinds( + serviceToDisk, + ); + } else { + for (final service in widget.services) { + if (serviceToDisk[service.id] != null) { + context.read().moveService( + service.id, + serviceToDisk[service.id]!, + ); + } + } + } Navigator.of(context).pushAndRemoveUntil( materialRoute(const RootPage()), (final predicate) => false, diff --git a/lib/ui/pages/services/service_page.dart b/lib/ui/pages/services/service_page.dart index 82dde3dc..417f1bf2 100644 --- a/lib/ui/pages/services/service_page.dart +++ b/lib/ui/pages/services/service_page.dart @@ -9,6 +9,8 @@ import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/logic/models/service.dart'; import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart'; import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/pages/server_storage/binds_migration/services_migration.dart'; +import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:url_launcher/url_launcher.dart'; class ServicePage extends StatefulWidget { @@ -40,6 +42,9 @@ class _ServicePageState extends State { final bool serviceDisabled = service.status == ServiceStatus.inactive || service.status == ServiceStatus.off; + final bool serviceLocked = + context.watch().state.isServiceLocked(service.id); + return BrandHeroScreen( hasBackButton: true, children: [ @@ -90,6 +95,7 @@ class _ServicePageState extends State { 'services.service_page.restart'.tr(), style: Theme.of(context).textTheme.titleMedium, ), + enabled: !serviceDisabled && !serviceLocked, ), ListTile( iconColor: Theme.of(context).colorScheme.onBackground, @@ -108,11 +114,22 @@ class _ServicePageState extends State { : 'services.service_page.disable'.tr(), style: Theme.of(context).textTheme.titleMedium, ), + enabled: !serviceLocked, ), if (service.isMovable) ListTile( iconColor: Theme.of(context).colorScheme.onBackground, - onTap: () => {}, + // Open page ServicesMigrationPage + onTap: () => Navigator.of(context).push( + materialRoute( + ServicesMigrationPage( + services: [service], + diskStatus: + context.read().state.diskStatus, + isMigration: false, + ), + ), + ), leading: const Icon(Icons.drive_file_move_outlined), title: Text( 'services.service_page.move'.tr(), @@ -131,6 +148,7 @@ class _ServicePageState extends State { ), style: Theme.of(context).textTheme.bodyMedium, ), + enabled: !serviceDisabled && !serviceLocked, ), ], );