selfprivacy.org.app/lib/ui/pages/server_storage/binds_migration/services_migration.dart
Inex Code a9a7b04ad5 fix: Return the binds migration interface
Turns out, there are still servers that didn't perform the binds migration. The can't perform it anymore because email changed the id. I'm getting back the option to perform the binds migration, with some fallback defaults.
2024-02-23 19:50:28 +03:00

218 lines
7.4 KiB
Dart

import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
import 'package:selfprivacy/logic/models/disk_size.dart';
import 'package:selfprivacy/logic/models/disk_status.dart';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
import 'package:selfprivacy/ui/components/jobs_content/jobs_content.dart';
import 'package:selfprivacy/ui/components/storage_list_items/server_storage_list_item.dart';
import 'package:selfprivacy/ui/components/storage_list_items/service_migration_list_item.dart';
@RoutePage()
class ServicesMigrationPage extends StatefulWidget {
const ServicesMigrationPage({
required this.services,
required this.diskStatus,
required this.isMigration,
super.key,
});
final DiskStatus diskStatus;
final List<Service> services;
final bool isMigration;
@override
State<ServicesMigrationPage> createState() => _ServicesMigrationPageState();
}
class _ServicesMigrationPageState extends State<ServicesMigrationPage> {
/// Service id to target migration disk name
final Map<String, String> serviceToDisk = {};
static const headerHeight = 52.0;
static const headerVerticalPadding = 8.0;
static const listItemHeight = 62.0;
@override
void initState() {
super.initState();
for (final Service service in widget.services) {
if (service.storageUsage.volume != null) {
serviceToDisk[service.id] = service.storageUsage.volume!;
}
}
}
void onChange(final String volumeName, final String serviceId) {
setState(() {
serviceToDisk[serviceId] = volumeName;
});
}
bool get isVolumePicked {
bool isChangeFound = false;
for (final Service service in widget.services) {
for (final String serviceId in serviceToDisk.keys) {
if (serviceId == service.id &&
serviceToDisk[serviceId] != service.storageUsage.volume!) {
isChangeFound = true;
}
}
}
return isChangeFound;
}
/// Check the services and if a service is moved (in a serviceToDisk entry)
/// subtract the used storage from the old volume and add it to the new volume.
/// The old volume is the volume the service is currently on, shown in services list.
DiskVolume recalculatedDiskUsages(
final DiskVolume volume,
final List<Service> services,
) {
DiskSize used = volume.sizeUsed;
for (final Service service in services) {
if (service.storageUsage.volume != null) {
if (service.storageUsage.volume == volume.name) {
if (serviceToDisk[service.id] != null &&
serviceToDisk[service.id] != volume.name) {
used -= service.storageUsage.used;
}
} else {
if (serviceToDisk[service.id] != null &&
serviceToDisk[service.id] == volume.name) {
used += service.storageUsage.used;
}
}
}
}
return volume.copyWith(sizeUsed: used);
}
@override
Widget build(final BuildContext context) {
final Size appBarHeight = Size.fromHeight(
headerHeight +
headerVerticalPadding * 2 +
listItemHeight * widget.diskStatus.diskVolumes.length +
headerVerticalPadding * widget.diskStatus.diskVolumes.length,
);
return SafeArea(
child: Scaffold(
appBar: PreferredSize(
preferredSize: appBarHeight,
child: Column(
children: [
BrandHeader(
title: 'storage.data_migration_title'.tr(),
hasBackButton: true,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: headerVerticalPadding,
),
child: Column(
children: [
...widget.diskStatus.diskVolumes.map(
(final volume) => Column(
children: [
ServerStorageListItem(
volume: recalculatedDiskUsages(
volume,
widget.services,
),
dense: true,
),
const SizedBox(height: headerVerticalPadding),
],
),
),
],
),
),
const Divider(height: 0),
],
),
),
body: ListView(
padding: const EdgeInsets.all(16.0),
children: <Widget>[
if (widget.services.isEmpty)
const Center(child: CircularProgressIndicator()),
...widget.services.map(
(final service) => Column(
children: [
const SizedBox(height: 8),
ServiceMigrationListItem(
service: service,
diskStatus: widget.diskStatus,
selectedVolume: serviceToDisk[service.id]!,
onChange: onChange,
),
const SizedBox(height: 4),
const Divider(),
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: InfoBox(
text: 'storage.data_migration_notice'.tr(),
isWarning: true,
),
),
const SizedBox(height: 16),
if (widget.isMigration || (!widget.isMigration && isVolumePicked))
BrandButton.filled(
child: Text('storage.start_migration_button'.tr()),
onPressed: () {
if (widget.isMigration) {
context.read<ServerJobsBloc>().migrateToBinds(
serviceToDisk,
);
} else {
for (final service in widget.services) {
if (serviceToDisk[service.id] != null) {
context.read<ServicesBloc>().add(
ServiceMove(
service,
serviceToDisk[service.id]!,
),
);
}
}
}
context.router.popUntilRoot();
showModalBottomSheet(
context: context,
useRootNavigator: true,
isScrollControlled: true,
builder: (final BuildContext context) =>
DraggableScrollableSheet(
expand: false,
maxChildSize: 0.9,
minChildSize: 0.4,
initialChildSize: 0.6,
builder: (final context, final scrollController) =>
JobsContent(controller: scrollController),
),
);
},
),
const SizedBox(height: 32),
],
),
),
);
}
}