mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-09 17:39:42 +00:00
refactor(ui): Refactor the SnapshotModal
This commit is contained in:
parent
bff84dc2ef
commit
33e9d23043
|
@ -30,6 +30,15 @@ sealed class ServerJobsState extends Equatable {
|
||||||
)
|
)
|
||||||
.toList();
|
.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(
|
bool get hasRemovableJobs => serverJobList.any(
|
||||||
(final job) =>
|
(final job) =>
|
||||||
job.status == JobStatusEnum.finished ||
|
job.status == JobStatusEnum.finished ||
|
||||||
|
|
|
@ -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,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
|
@ -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)}',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
|
@ -3,8 +3,8 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/utils/platform_adapter.dart';
|
import 'package:selfprivacy/utils/platform_adapter.dart';
|
||||||
|
|
||||||
class SnapshotIdListTile extends StatelessWidget {
|
class SnapshotIdTile extends StatelessWidget {
|
||||||
const SnapshotIdListTile({
|
const SnapshotIdTile({
|
||||||
required this.snapshotId,
|
required this.snapshotId,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
|
@ -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,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
60
lib/ui/molecules/cards/radio_selection_card.dart
Normal file
60
lib/ui/molecules/cards/radio_selection_card.dart
Normal 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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
|
@ -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/backups/backups_bloc.dart';
|
||||||
import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_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/bloc/volumes/volumes_bloc.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/server_job.dart';
|
|
||||||
import 'package:selfprivacy/logic/models/service.dart';
|
import 'package:selfprivacy/logic/models/service.dart';
|
||||||
|
|
||||||
class CreateBackupsModal extends StatefulWidget {
|
class CreateBackupsModal extends StatefulWidget {
|
||||||
|
@ -29,17 +28,8 @@ class _CreateBackupsModalState extends State<CreateBackupsModal> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final List<String> busyServices = context
|
final List<String> busyServices =
|
||||||
.read<ServerJobsBloc>()
|
context.read<ServerJobsBloc>().state.busyServices;
|
||||||
.state
|
|
||||||
.backupJobList
|
|
||||||
.where(
|
|
||||||
(final ServerJob job) =>
|
|
||||||
job.status == JobStatusEnum.running ||
|
|
||||||
job.status == JobStatusEnum.created,
|
|
||||||
)
|
|
||||||
.map((final ServerJob job) => job.typeId.split('.')[1])
|
|
||||||
.toList();
|
|
||||||
selectedServices.addAll(
|
selectedServices.addAll(
|
||||||
widget.services
|
widget.services
|
||||||
.where((final Service service) => !busyServices.contains(service.id)),
|
.where((final Service service) => !busyServices.contains(service.id)),
|
||||||
|
@ -48,17 +38,8 @@ class _CreateBackupsModalState extends State<CreateBackupsModal> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
final List<String> busyServices = context
|
final List<String> busyServices =
|
||||||
.watch<ServerJobsBloc>()
|
context.watch<ServerJobsBloc>().state.busyServices;
|
||||||
.state
|
|
||||||
.backupJobList
|
|
||||||
.where(
|
|
||||||
(final ServerJob job) =>
|
|
||||||
job.status == JobStatusEnum.running ||
|
|
||||||
job.status == JobStatusEnum.created,
|
|
||||||
)
|
|
||||||
.map((final ServerJob job) => job.typeId.split('.')[1])
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
return ListView(
|
return ListView(
|
||||||
controller: widget.scrollController,
|
controller: widget.scrollController,
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.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/server_jobs/server_jobs_bloc.dart';
|
||||||
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
|
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
|
||||||
import 'package:selfprivacy/logic/models/backup.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/logic/models/service.dart';
|
||||||
import 'package:selfprivacy/ui/atoms/buttons/brand_button.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/molecules/info_box/info_box.dart';
|
||||||
import 'package:selfprivacy/ui/pages/backups/snapshot_id_list_tile.dart';
|
|
||||||
|
|
||||||
class SnapshotModal extends StatefulWidget {
|
class SnapshotModal extends StatefulWidget {
|
||||||
const SnapshotModal({
|
const SnapshotModal({
|
||||||
|
@ -33,17 +34,8 @@ class _SnapshotModalState extends State<SnapshotModal> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
final List<String> busyServices = context
|
final List<String> busyServices =
|
||||||
.watch<ServerJobsBloc>()
|
context.watch<ServerJobsBloc>().state.busyServices;
|
||||||
.state
|
|
||||||
.backupJobList
|
|
||||||
.where(
|
|
||||||
(final ServerJob job) =>
|
|
||||||
job.status == JobStatusEnum.running ||
|
|
||||||
job.status == JobStatusEnum.created,
|
|
||||||
)
|
|
||||||
.map((final ServerJob job) => job.typeId.split('.')[1])
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
final bool isServiceBusy = busyServices.contains(widget.snapshot.serviceId);
|
final bool isServiceBusy = busyServices.contains(widget.snapshot.serviceId);
|
||||||
|
|
||||||
|
@ -65,62 +57,25 @@ class _SnapshotModalState extends State<SnapshotModal> {
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
ListTile(
|
SnapshotServiceTile(
|
||||||
leading: service != null
|
service: service,
|
||||||
? SvgPicture.string(
|
fallbackServiceName: widget.snapshot.fallbackServiceName,
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
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)
|
if (service != null)
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(
|
||||||
'backup.snapshot_modal_select_strategy'.tr(),
|
'backup.snapshot_modal_select_strategy'.tr(),
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 16),
|
||||||
_BackupStrategySelectionCard(
|
RadioSelectionCard(
|
||||||
isSelected: selectedStrategy ==
|
isSelected: selectedStrategy ==
|
||||||
BackupRestoreStrategy.downloadVerifyOverwrite,
|
BackupRestoreStrategy.downloadVerifyOverwrite,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
@ -136,7 +91,7 @@ class _SnapshotModalState extends State<SnapshotModal> {
|
||||||
.tr(),
|
.tr(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
_BackupStrategySelectionCard(
|
RadioSelectionCard(
|
||||||
isSelected: selectedStrategy == BackupRestoreStrategy.inplace,
|
isSelected: selectedStrategy == BackupRestoreStrategy.inplace,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
@ -147,7 +102,7 @@ class _SnapshotModalState extends State<SnapshotModal> {
|
||||||
subtitle:
|
subtitle:
|
||||||
'backup.snapshot_modal_inplace_option_description'.tr(),
|
'backup.snapshot_modal_inplace_option_description'.tr(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 16),
|
||||||
// Restore backup button
|
// Restore backup button
|
||||||
BrandButton.filled(
|
BrandButton.filled(
|
||||||
onPressed: isServiceBusy
|
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,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue