Service migrations

Inex Code 2022-09-19 03:21:08 +03:00
parent d6d7a0dcb6
commit 10891881ae
7 changed files with 94 additions and 19 deletions

View File

@ -11,8 +11,11 @@ class ServerJobsState extends ServerInstallationDependendState {
late final List<ServerJob> _serverJobList;
final String? migrationJobUid;
List<ServerJob> get serverJobList =>
_serverJobList.where((final ServerJob job) => !job.isHidden).toList();
List<ServerJob> get serverJobList {
final List<ServerJob> list = _serverJobList;
list.sort((final a, final b) => b.createdAt.compareTo(a.createdAt));
return list;
}
@override
List<Object?> get props => [migrationJobUid, ..._serverJobList];

View File

@ -19,6 +19,7 @@ class ServicesCubit extends ServerInstallationDependendCubit<ServicesState> {
emit(
ServicesState(
services: services,
lockedServices: const [],
),
);
timer = Timer(const Duration(seconds: 10), () => reload(useTimer: true));
@ -28,7 +29,7 @@ class ServicesCubit extends ServerInstallationDependendCubit<ServicesState> {
Future<void> reload({final bool useTimer = false}) async {
final List<Service> services = await api.getAllServices();
emit(
ServicesState(
state.copyWith(
services: services,
),
);
@ -38,7 +39,26 @@ class ServicesCubit extends ServerInstallationDependendCubit<ServicesState> {
}
Future<void> 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<void> moveService(
final String serviceId,
final String destination,
) async {
await api.moveService(serviceId, destination);
}
@override

View File

@ -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<Service> services;
final List<String> 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<Object> 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<Service>? services,
final List<String>? lockedServices,
}) =>
ServicesState(
services: services ?? this.services,
lockedServices: lockedServices ?? this.lockedServices,
);
}

View File

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

View File

@ -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<ApiServerVolumeCubit>()
.state
@ -68,6 +68,7 @@ class MorePage extends StatelessWidget {
service.id == 'nextcloud',
)
.toList(),
isMigration: true,
),
subtitle: 'not_ready_card.in_menu'.tr(),
accent: true,

View File

@ -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<Service> services;
final bool isMigration;
@override
State<DataToBindsMigrationPage> createState() =>
_DataToBindsMigrationPageState();
State<ServicesMigrationPage> createState() => _ServicesMigrationPageState();
}
class _DataToBindsMigrationPageState extends State<DataToBindsMigrationPage> {
class _ServicesMigrationPageState extends State<ServicesMigrationPage> {
/// Service id to target migration disk name
final Map<String, String> serviceToDisk = {};
@ -164,7 +166,20 @@ class _DataToBindsMigrationPageState extends State<DataToBindsMigrationPage> {
FilledButton(
title: 'providers.storage.start_migration_button'.tr(),
onPressed: () {
context.read<ServerJobsCubit>().migrateToBinds(serviceToDisk);
if (widget.isMigration) {
context.read<ServerJobsCubit>().migrateToBinds(
serviceToDisk,
);
} else {
for (final service in widget.services) {
if (serviceToDisk[service.id] != null) {
context.read<ServicesCubit>().moveService(
service.id,
serviceToDisk[service.id]!,
);
}
}
}
Navigator.of(context).pushAndRemoveUntil(
materialRoute(const RootPage()),
(final predicate) => false,

View File

@ -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<ServicePage> {
final bool serviceDisabled = service.status == ServiceStatus.inactive ||
service.status == ServiceStatus.off;
final bool serviceLocked =
context.watch<ServicesCubit>().state.isServiceLocked(service.id);
return BrandHeroScreen(
hasBackButton: true,
children: [
@ -90,6 +95,7 @@ class _ServicePageState extends State<ServicePage> {
'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<ServicePage> {
: '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<ApiServerVolumeCubit>().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<ServicePage> {
),
style: Theme.of(context).textTheme.bodyMedium,
),
enabled: !serviceDisabled && !serviceLocked,
),
],
);