refactor(ui): Refactor the SnapshotModal

This commit is contained in:
Inex Code 2024-11-14 18:04:35 +03:00
parent bff84dc2ef
commit 33e9d23043
8 changed files with 182 additions and 146 deletions

View file

@ -30,6 +30,15 @@ sealed class ServerJobsState extends Equatable {
)
.toList();
List<String> get busyServices => backupJobList
.where(
(final ServerJob job) =>
job.status == JobStatusEnum.running ||
job.status == JobStatusEnum.created,
)
.map((final ServerJob job) => job.typeId.split('.')[1])
.toList();
bool get hasRemovableJobs => serverJobList.any(
(final job) =>
job.status == JobStatusEnum.finished ||

View file

@ -0,0 +1,25 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
class SnapshotCreationReasonTile extends StatelessWidget {
const SnapshotCreationReasonTile({
required this.reason,
super.key,
});
final String reason;
@override
Widget build(final BuildContext context) => ListTile(
leading: Icon(
Icons.info_outline,
color: Theme.of(context).colorScheme.onSurface,
),
title: Text(
'backup.snapshot_reason_title'.tr(),
),
subtitle: Text(
reason,
),
);
}

View file

@ -0,0 +1,25 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
class SnapshotCreationTimeTile extends StatelessWidget {
const SnapshotCreationTimeTile({
required this.time,
super.key,
});
final DateTime time;
@override
Widget build(final BuildContext context) => ListTile(
leading: Icon(
Icons.access_time_outlined,
color: Theme.of(context).colorScheme.onSurface,
),
title: Text(
'backup.snapshot_creation_time_title'.tr(),
),
subtitle: Text(
'${MaterialLocalizations.of(context).formatShortDate(time.toLocal())} ${TimeOfDay.fromDateTime(time.toLocal()).format(context)}',
),
);
}

View file

@ -3,8 +3,8 @@ import 'package:flutter/material.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/utils/platform_adapter.dart';
class SnapshotIdListTile extends StatelessWidget {
const SnapshotIdListTile({
class SnapshotIdTile extends StatelessWidget {
const SnapshotIdTile({
required this.snapshotId,
super.key,
});

View file

@ -0,0 +1,38 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:selfprivacy/logic/models/service.dart';
class SnapshotServiceTile extends StatelessWidget {
const SnapshotServiceTile({
required this.service,
required this.fallbackServiceName,
super.key,
});
final Service? service;
final String fallbackServiceName;
@override
Widget build(final BuildContext context) => ListTile(
leading: service != null
? SvgPicture.string(
service!.svgIcon,
height: 24,
width: 24,
colorFilter: ColorFilter.mode(
Theme.of(context).colorScheme.onSurface,
BlendMode.srcIn,
),
)
: const Icon(
Icons.question_mark_outlined,
),
title: Text(
'backup.snapshot_service_title'.tr(),
),
subtitle: Text(
service?.displayName ?? fallbackServiceName,
),
);
}

View file

@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/ui/atoms/cards/outlined_card.dart';
class RadioSelectionCard extends StatelessWidget {
const RadioSelectionCard({
required this.isSelected,
required this.title,
required this.subtitle,
required this.onTap,
super.key,
});
final bool isSelected;
final String title;
final String subtitle;
final void Function() onTap;
@override
Widget build(final BuildContext context) => OutlinedCard(
borderColor: isSelected ? Theme.of(context).colorScheme.primary : null,
borderWidth: isSelected ? 3 : 1,
child: InkResponse(
highlightShape: BoxShape.rectangle,
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
if (isSelected)
Icon(
Icons.radio_button_on_outlined,
color: Theme.of(context).colorScheme.primary,
)
else
Icon(
Icons.radio_button_off_outlined,
color: Theme.of(context).colorScheme.outline,
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: Theme.of(context).textTheme.titleMedium,
),
Text(
subtitle,
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
),
],
),
),
),
);
}

View file

@ -4,7 +4,6 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart';
import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/bloc/volumes/volumes_bloc.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
import 'package:selfprivacy/logic/models/service.dart';
class CreateBackupsModal extends StatefulWidget {
@ -29,17 +28,8 @@ class _CreateBackupsModalState extends State<CreateBackupsModal> {
@override
void initState() {
super.initState();
final List<String> busyServices = context
.read<ServerJobsBloc>()
.state
.backupJobList
.where(
(final ServerJob job) =>
job.status == JobStatusEnum.running ||
job.status == JobStatusEnum.created,
)
.map((final ServerJob job) => job.typeId.split('.')[1])
.toList();
final List<String> busyServices =
context.read<ServerJobsBloc>().state.busyServices;
selectedServices.addAll(
widget.services
.where((final Service service) => !busyServices.contains(service.id)),
@ -48,17 +38,8 @@ class _CreateBackupsModalState extends State<CreateBackupsModal> {
@override
Widget build(final BuildContext context) {
final List<String> busyServices = context
.watch<ServerJobsBloc>()
.state
.backupJobList
.where(
(final ServerJob job) =>
job.status == JobStatusEnum.running ||
job.status == JobStatusEnum.created,
)
.map((final ServerJob job) => job.typeId.split('.')[1])
.toList();
final List<String> busyServices =
context.watch<ServerJobsBloc>().state.busyServices;
return ListView(
controller: widget.scrollController,

View file

@ -1,17 +1,18 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.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/backup.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/ui/atoms/buttons/brand_button.dart';
import 'package:selfprivacy/ui/atoms/cards/outlined_card.dart';
import 'package:selfprivacy/ui/atoms/list_tiles/backup_snapshot_tiles/snapshot_creation_reason_tile.dart';
import 'package:selfprivacy/ui/atoms/list_tiles/backup_snapshot_tiles/snapshot_creation_time_tile.dart';
import 'package:selfprivacy/ui/atoms/list_tiles/backup_snapshot_tiles/snapshot_id_tile.dart';
import 'package:selfprivacy/ui/atoms/list_tiles/backup_snapshot_tiles/snapshot_service_tile.dart';
import 'package:selfprivacy/ui/molecules/cards/radio_selection_card.dart';
import 'package:selfprivacy/ui/molecules/info_box/info_box.dart';
import 'package:selfprivacy/ui/pages/backups/snapshot_id_list_tile.dart';
class SnapshotModal extends StatefulWidget {
const SnapshotModal({
@ -33,17 +34,8 @@ class _SnapshotModalState extends State<SnapshotModal> {
@override
Widget build(final BuildContext context) {
final List<String> busyServices = context
.watch<ServerJobsBloc>()
.state
.backupJobList
.where(
(final ServerJob job) =>
job.status == JobStatusEnum.running ||
job.status == JobStatusEnum.created,
)
.map((final ServerJob job) => job.typeId.split('.')[1])
.toList();
final List<String> busyServices =
context.watch<ServerJobsBloc>().state.busyServices;
final bool isServiceBusy = busyServices.contains(widget.snapshot.serviceId);
@ -65,62 +57,25 @@ class _SnapshotModalState extends State<SnapshotModal> {
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
ListTile(
leading: service != null
? SvgPicture.string(
service.svgIcon,
height: 24,
width: 24,
colorFilter: ColorFilter.mode(
Theme.of(context).colorScheme.onSurface,
BlendMode.srcIn,
),
)
: const Icon(
Icons.question_mark_outlined,
),
title: Text(
'backup.snapshot_service_title'.tr(),
),
subtitle: Text(
service?.displayName ?? widget.snapshot.fallbackServiceName,
),
SnapshotServiceTile(
service: service,
fallbackServiceName: widget.snapshot.fallbackServiceName,
),
ListTile(
leading: Icon(
Icons.access_time_outlined,
color: Theme.of(context).colorScheme.onSurface,
),
title: Text(
'backup.snapshot_creation_time_title'.tr(),
),
subtitle: Text(
'${MaterialLocalizations.of(context).formatShortDate(widget.snapshot.time.toLocal())} ${TimeOfDay.fromDateTime(widget.snapshot.time.toLocal()).format(context)}',
),
),
SnapshotIdListTile(snapshotId: widget.snapshot.id),
ListTile(
leading: Icon(
Icons.info_outline,
color: Theme.of(context).colorScheme.onSurface,
),
title: Text(
'backup.snapshot_reason_title'.tr(),
),
subtitle: Text(
widget.snapshot.reason.displayName.tr(),
),
SnapshotCreationTimeTile(time: widget.snapshot.time),
SnapshotIdTile(snapshotId: widget.snapshot.id),
SnapshotCreationReasonTile(
reason: widget.snapshot.reason.displayName.tr(),
),
if (service != null)
Column(
children: [
const SizedBox(height: 8),
const SizedBox(height: 16),
Text(
'backup.snapshot_modal_select_strategy'.tr(),
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
_BackupStrategySelectionCard(
const SizedBox(height: 16),
RadioSelectionCard(
isSelected: selectedStrategy ==
BackupRestoreStrategy.downloadVerifyOverwrite,
onTap: () {
@ -136,7 +91,7 @@ class _SnapshotModalState extends State<SnapshotModal> {
.tr(),
),
const SizedBox(height: 8),
_BackupStrategySelectionCard(
RadioSelectionCard(
isSelected: selectedStrategy == BackupRestoreStrategy.inplace,
onTap: () {
setState(() {
@ -147,7 +102,7 @@ class _SnapshotModalState extends State<SnapshotModal> {
subtitle:
'backup.snapshot_modal_inplace_option_description'.tr(),
),
const SizedBox(height: 8),
const SizedBox(height: 16),
// Restore backup button
BrandButton.filled(
onPressed: isServiceBusy
@ -180,60 +135,3 @@ class _SnapshotModalState extends State<SnapshotModal> {
);
}
}
class _BackupStrategySelectionCard extends StatelessWidget {
const _BackupStrategySelectionCard({
required this.isSelected,
required this.title,
required this.subtitle,
required this.onTap,
});
final bool isSelected;
final String title;
final String subtitle;
final void Function() onTap;
@override
Widget build(final BuildContext context) => OutlinedCard(
borderColor: isSelected ? Theme.of(context).colorScheme.primary : null,
borderWidth: isSelected ? 3 : 1,
child: InkResponse(
highlightShape: BoxShape.rectangle,
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
if (isSelected)
Icon(
Icons.radio_button_on_outlined,
color: Theme.of(context).colorScheme.primary,
)
else
Icon(
Icons.radio_button_off_outlined,
color: Theme.of(context).colorScheme.outline,
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: Theme.of(context).textTheme.bodyLarge,
),
Text(
subtitle,
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
),
],
),
),
),
);
}