feat(backups): Add descriptions for backups

pull/228/head
Inex Code 2023-07-02 14:41:31 +03:00
parent b3b7c83461
commit 62b7a0ee7e
13 changed files with 121 additions and 55 deletions

View File

@ -1,10 +1,10 @@
import 'dart:async'; import 'dart:async';
import 'disk_volumes.graphql.dart';
import 'package:gql/ast.dart'; import 'package:gql/ast.dart';
import 'package:graphql/client.dart' as graphql; import 'package:graphql/client.dart' as graphql;
import 'package:selfprivacy/utils/scalars.dart'; import 'package:selfprivacy/utils/scalars.dart';
import 'schema.graphql.dart'; import 'schema.graphql.dart';
import 'server_api.graphql.dart'; import 'server_api.graphql.dart';
import 'services.graphql.dart';
class Fragment$genericBackupConfigReturn { class Fragment$genericBackupConfigReturn {
Fragment$genericBackupConfigReturn({ Fragment$genericBackupConfigReturn({

View File

@ -3,7 +3,6 @@ import 'package:gql/ast.dart';
import 'package:graphql/client.dart' as graphql; import 'package:graphql/client.dart' as graphql;
import 'schema.graphql.dart'; import 'schema.graphql.dart';
import 'server_api.graphql.dart'; import 'server_api.graphql.dart';
import 'services.graphql.dart';
class Fragment$basicMutationReturnFields { class Fragment$basicMutationReturnFields {
Fragment$basicMutationReturnFields({ Fragment$basicMutationReturnFields({

View File

@ -255,6 +255,7 @@ type Service {
isRequired: Boolean! isRequired: Boolean!
isEnabled: Boolean! isEnabled: Boolean!
canBeBackedUp: Boolean! canBeBackedUp: Boolean!
backupDescription: String!
status: ServiceStatusEnum! status: ServiceStatusEnum!
url: String url: String
dnsRecords: [DnsRecord!] dnsRecords: [DnsRecord!]

View File

@ -1,9 +1,9 @@
import 'dart:async'; import 'dart:async';
import 'disk_volumes.graphql.dart';
import 'package:gql/ast.dart'; import 'package:gql/ast.dart';
import 'package:graphql/client.dart' as graphql; import 'package:graphql/client.dart' as graphql;
import 'package:selfprivacy/utils/scalars.dart'; import 'package:selfprivacy/utils/scalars.dart';
import 'schema.graphql.dart'; import 'schema.graphql.dart';
import 'services.graphql.dart';
class Fragment$basicMutationReturnFields { class Fragment$basicMutationReturnFields {
Fragment$basicMutationReturnFields({ Fragment$basicMutationReturnFields({

View File

@ -1,8 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'disk_volumes.graphql.dart';
import 'package:gql/ast.dart'; import 'package:gql/ast.dart';
import 'package:graphql/client.dart' as graphql; import 'package:graphql/client.dart' as graphql;
import 'schema.graphql.dart'; import 'schema.graphql.dart';
import 'services.graphql.dart';
class Fragment$basicMutationReturnFields { class Fragment$basicMutationReturnFields {
Fragment$basicMutationReturnFields({ Fragment$basicMutationReturnFields({

View File

@ -17,6 +17,7 @@ query AllServices {
isMovable isMovable
isRequired isRequired
canBeBackedUp canBeBackedUp
backupDescription
status status
storageUsage { storageUsage {
title title

View File

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'disk_volumes.graphql.dart';
import 'package:gql/ast.dart'; import 'package:gql/ast.dart';
import 'package:graphql/client.dart' as graphql; import 'package:graphql/client.dart' as graphql;
import 'schema.graphql.dart'; import 'schema.graphql.dart';
@ -2502,6 +2503,13 @@ const documentNodeQueryAllServices = DocumentNode(definitions: [
directives: [], directives: [],
selectionSet: null, selectionSet: null,
), ),
FieldNode(
name: NameNode(value: 'backupDescription'),
alias: null,
arguments: [],
directives: [],
selectionSet: null,
),
FieldNode( FieldNode(
name: NameNode(value: 'status'), name: NameNode(value: 'status'),
alias: null, alias: null,
@ -2884,6 +2892,7 @@ class Query$AllServices$services$allServices {
required this.isMovable, required this.isMovable,
required this.isRequired, required this.isRequired,
required this.canBeBackedUp, required this.canBeBackedUp,
required this.backupDescription,
required this.status, required this.status,
required this.storageUsage, required this.storageUsage,
required this.svgIcon, required this.svgIcon,
@ -2901,6 +2910,7 @@ class Query$AllServices$services$allServices {
final l$isMovable = json['isMovable']; final l$isMovable = json['isMovable'];
final l$isRequired = json['isRequired']; final l$isRequired = json['isRequired'];
final l$canBeBackedUp = json['canBeBackedUp']; final l$canBeBackedUp = json['canBeBackedUp'];
final l$backupDescription = json['backupDescription'];
final l$status = json['status']; final l$status = json['status'];
final l$storageUsage = json['storageUsage']; final l$storageUsage = json['storageUsage'];
final l$svgIcon = json['svgIcon']; final l$svgIcon = json['svgIcon'];
@ -2918,6 +2928,7 @@ class Query$AllServices$services$allServices {
isMovable: (l$isMovable as bool), isMovable: (l$isMovable as bool),
isRequired: (l$isRequired as bool), isRequired: (l$isRequired as bool),
canBeBackedUp: (l$canBeBackedUp as bool), canBeBackedUp: (l$canBeBackedUp as bool),
backupDescription: (l$backupDescription as String),
status: fromJson$Enum$ServiceStatusEnum((l$status as String)), status: fromJson$Enum$ServiceStatusEnum((l$status as String)),
storageUsage: storageUsage:
Query$AllServices$services$allServices$storageUsage.fromJson( Query$AllServices$services$allServices$storageUsage.fromJson(
@ -2944,6 +2955,8 @@ class Query$AllServices$services$allServices {
final bool canBeBackedUp; final bool canBeBackedUp;
final String backupDescription;
final Enum$ServiceStatusEnum status; final Enum$ServiceStatusEnum status;
final Query$AllServices$services$allServices$storageUsage storageUsage; final Query$AllServices$services$allServices$storageUsage storageUsage;
@ -2972,6 +2985,8 @@ class Query$AllServices$services$allServices {
_resultData['isRequired'] = l$isRequired; _resultData['isRequired'] = l$isRequired;
final l$canBeBackedUp = canBeBackedUp; final l$canBeBackedUp = canBeBackedUp;
_resultData['canBeBackedUp'] = l$canBeBackedUp; _resultData['canBeBackedUp'] = l$canBeBackedUp;
final l$backupDescription = backupDescription;
_resultData['backupDescription'] = l$backupDescription;
final l$status = status; final l$status = status;
_resultData['status'] = toJson$Enum$ServiceStatusEnum(l$status); _resultData['status'] = toJson$Enum$ServiceStatusEnum(l$status);
final l$storageUsage = storageUsage; final l$storageUsage = storageUsage;
@ -2995,6 +3010,7 @@ class Query$AllServices$services$allServices {
final l$isMovable = isMovable; final l$isMovable = isMovable;
final l$isRequired = isRequired; final l$isRequired = isRequired;
final l$canBeBackedUp = canBeBackedUp; final l$canBeBackedUp = canBeBackedUp;
final l$backupDescription = backupDescription;
final l$status = status; final l$status = status;
final l$storageUsage = storageUsage; final l$storageUsage = storageUsage;
final l$svgIcon = svgIcon; final l$svgIcon = svgIcon;
@ -3009,6 +3025,7 @@ class Query$AllServices$services$allServices {
l$isMovable, l$isMovable,
l$isRequired, l$isRequired,
l$canBeBackedUp, l$canBeBackedUp,
l$backupDescription,
l$status, l$status,
l$storageUsage, l$storageUsage,
l$svgIcon, l$svgIcon,
@ -3077,6 +3094,11 @@ class Query$AllServices$services$allServices {
if (l$canBeBackedUp != lOther$canBeBackedUp) { if (l$canBeBackedUp != lOther$canBeBackedUp) {
return false; return false;
} }
final l$backupDescription = backupDescription;
final lOther$backupDescription = other.backupDescription;
if (l$backupDescription != lOther$backupDescription) {
return false;
}
final l$status = status; final l$status = status;
final lOther$status = other.status; final lOther$status = other.status;
if (l$status != lOther$status) { if (l$status != lOther$status) {
@ -3134,6 +3156,7 @@ abstract class CopyWith$Query$AllServices$services$allServices<TRes> {
bool? isMovable, bool? isMovable,
bool? isRequired, bool? isRequired,
bool? canBeBackedUp, bool? canBeBackedUp,
String? backupDescription,
Enum$ServiceStatusEnum? status, Enum$ServiceStatusEnum? status,
Query$AllServices$services$allServices$storageUsage? storageUsage, Query$AllServices$services$allServices$storageUsage? storageUsage,
String? svgIcon, String? svgIcon,
@ -3172,6 +3195,7 @@ class _CopyWithImpl$Query$AllServices$services$allServices<TRes>
Object? isMovable = _undefined, Object? isMovable = _undefined,
Object? isRequired = _undefined, Object? isRequired = _undefined,
Object? canBeBackedUp = _undefined, Object? canBeBackedUp = _undefined,
Object? backupDescription = _undefined,
Object? status = _undefined, Object? status = _undefined,
Object? storageUsage = _undefined, Object? storageUsage = _undefined,
Object? svgIcon = _undefined, Object? svgIcon = _undefined,
@ -3201,6 +3225,10 @@ class _CopyWithImpl$Query$AllServices$services$allServices<TRes>
canBeBackedUp: canBeBackedUp == _undefined || canBeBackedUp == null canBeBackedUp: canBeBackedUp == _undefined || canBeBackedUp == null
? _instance.canBeBackedUp ? _instance.canBeBackedUp
: (canBeBackedUp as bool), : (canBeBackedUp as bool),
backupDescription:
backupDescription == _undefined || backupDescription == null
? _instance.backupDescription
: (backupDescription as String),
status: status == _undefined || status == null status: status == _undefined || status == null
? _instance.status ? _instance.status
: (status as Enum$ServiceStatusEnum), : (status as Enum$ServiceStatusEnum),
@ -3251,6 +3279,7 @@ class _CopyWithStubImpl$Query$AllServices$services$allServices<TRes>
bool? isMovable, bool? isMovable,
bool? isRequired, bool? isRequired,
bool? canBeBackedUp, bool? canBeBackedUp,
String? backupDescription,
Enum$ServiceStatusEnum? status, Enum$ServiceStatusEnum? status,
Query$AllServices$services$allServices$storageUsage? storageUsage, Query$AllServices$services$allServices$storageUsage? storageUsage,
String? svgIcon, String? svgIcon,

View File

@ -1,8 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'disk_volumes.graphql.dart';
import 'package:gql/ast.dart'; import 'package:gql/ast.dart';
import 'package:graphql/client.dart' as graphql; import 'package:graphql/client.dart' as graphql;
import 'schema.graphql.dart'; import 'schema.graphql.dart';
import 'services.graphql.dart';
class Fragment$basicMutationReturnFields { class Fragment$basicMutationReturnFields {
Fragment$basicMutationReturnFields({ Fragment$basicMutationReturnFields({

View File

@ -30,6 +30,7 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
final BackupConfiguration? backupConfig = final BackupConfiguration? backupConfig =
await api.getBackupsConfiguration(); await api.getBackupsConfiguration();
final List<Backup> backups = await api.getBackups(); final List<Backup> backups = await api.getBackups();
backups.sort((final a, final b) => b.time.compareTo(a.time));
emit( emit(
state.copyWith( state.copyWith(
backblazeBucket: bucket, backblazeBucket: bucket,
@ -144,6 +145,7 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
Future<void> updateBackups({final bool useTimer = false}) async { Future<void> updateBackups({final bool useTimer = false}) async {
emit(state.copyWith(refreshing: true)); emit(state.copyWith(refreshing: true));
final backups = await api.getBackups(); final backups = await api.getBackups();
backups.sort((final a, final b) => b.time.compareTo(a.time));
final backupConfig = await api.getBackupsConfiguration(); final backupConfig = await api.getBackupsConfiguration();
emit( emit(

View File

@ -25,7 +25,7 @@ class BackupsState extends ServerInstallationDependendState {
backups, backups,
preventActions, preventActions,
refreshTimer, refreshTimer,
refreshing refreshing,
]; ];
BackupsState copyWith({ BackupsState copyWith({

View File

@ -18,6 +18,7 @@ class Service {
isRequired: service.isRequired, isRequired: service.isRequired,
isMovable: service.isMovable, isMovable: service.isMovable,
canBeBackedUp: service.canBeBackedUp, canBeBackedUp: service.canBeBackedUp,
backupDescription: service.backupDescription,
status: ServiceStatus.fromGraphQL(service.status), status: ServiceStatus.fromGraphQL(service.status),
storageUsage: ServiceStorageUsage( storageUsage: ServiceStorageUsage(
used: DiskSize(byte: int.parse(service.storageUsage.usedSpace)), used: DiskSize(byte: int.parse(service.storageUsage.usedSpace)),
@ -44,6 +45,7 @@ class Service {
required this.isRequired, required this.isRequired,
required this.isMovable, required this.isMovable,
required this.canBeBackedUp, required this.canBeBackedUp,
required this.backupDescription,
required this.status, required this.status,
required this.storageUsage, required this.storageUsage,
required this.svgIcon, required this.svgIcon,
@ -78,6 +80,7 @@ class Service {
isRequired: false, isRequired: false,
isMovable: false, isMovable: false,
canBeBackedUp: false, canBeBackedUp: false,
backupDescription: '',
status: ServiceStatus.off, status: ServiceStatus.off,
storageUsage: ServiceStorageUsage( storageUsage: ServiceStorageUsage(
used: const DiskSize(byte: 0), used: const DiskSize(byte: 0),
@ -95,6 +98,7 @@ class Service {
final bool isRequired; final bool isRequired;
final bool isMovable; final bool isMovable;
final bool canBeBackedUp; final bool canBeBackedUp;
final String backupDescription;
final ServiceStatus status; final ServiceStatus status;
final ServiceStorageUsage storageUsage; final ServiceStorageUsage storageUsage;
final String svgIcon; final String svgIcon;

View File

@ -147,7 +147,8 @@ class _HeroSliverAppBarState extends State<HeroSliverAppBar> {
context: context, context: context,
useRootNavigator: true, useRootNavigator: true,
isScrollControlled: true, isScrollControlled: true,
builder: (final BuildContext context) => DraggableScrollableSheet( builder: (final BuildContext context) =>
DraggableScrollableSheet(
expand: false, expand: false,
maxChildSize: 0.9, maxChildSize: 0.9,
minChildSize: 0.4, minChildSize: 0.4,

View File

@ -93,59 +93,88 @@ class _BackupDetailsPageState extends State<BackupDetailsPage>
// Each list item has a date // Each list item has a date
// When clicked, starts the restore action // When clicked, starts the restore action
if (isBackupInitialized) if (isBackupInitialized)
OutlinedCard( Column(
child: Column( crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children: [
children: [ ListTile(
title: Text(
'backups.latest_snapshots'.tr(),
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
color: Theme.of(context).colorScheme.secondary,
),
),
subtitle: Text(
'backups.latest_snapshots_subtitle'.tr(),
style: Theme.of(context).textTheme.labelMedium,
),
),
if (backups.isEmpty)
ListTile( ListTile(
leading: const Icon( leading: const Icon(
Icons.refresh, Icons.error_outline,
),
title: Text(
'backup.restore'.tr(),
style: Theme.of(context).textTheme.titleLarge,
), ),
title: Text('backup.no_backups'.tr()),
), ),
const Divider( if (backups.isNotEmpty)
height: 1.0, Column(
), children: backups.take(20).map(
if (backups.isEmpty) (final Backup backup) {
ListTile( final service = context
leading: const Icon( .read<ServicesCubit>()
Icons.error_outline, .state
), .getServiceById(backup.serviceId);
title: Text('backup.no_backups'.tr()), return ListTile(
), onTap: preventActions
if (backups.isNotEmpty) ? null
Column( : () {
children: backups showPopUpAlert(
.map( alertTitle: 'backup.restoring'.tr(),
(final Backup backup) => ListTile( description: 'backup.restore_alert'.tr(
onTap: preventActions args: [backup.time.toString()],
? null ),
: () { actionButtonTitle: 'modals.yes'.tr(),
showPopUpAlert( actionButtonOnPressed: () => {
alertTitle: 'backup.restoring'.tr(), context
description: 'backup.restore_alert'.tr( .read<BackupsCubit>()
args: [backup.time.toString()], .restoreBackup(backup.id)
),
actionButtonTitle: 'modals.yes'.tr(),
actionButtonOnPressed: () => {
context
.read<BackupsCubit>()
.restoreBackup(backup.id)
},
);
}, },
title: Text( );
'${MaterialLocalizations.of(context).formatShortDate(backup.time)} ${TimeOfDay.fromDateTime(backup.time).format(context)}', },
), title: Text(
), '${MaterialLocalizations.of(context).formatShortDate(backup.time)} ${TimeOfDay.fromDateTime(backup.time).format(context)}',
) ),
.toList(), subtitle: Text(
service?.displayName ?? backup.fallbackServiceName,
),
leading: service != null
? SvgPicture.string(
service.svgIcon,
height: 24,
width: 24,
colorFilter: ColorFilter.mode(
Theme.of(context).colorScheme.onBackground,
BlendMode.srcIn,
),
)
: const Icon(
Icons.question_mark_outlined,
),
);
},
).toList(),
),
if (backups.isNotEmpty && backups.length > 20)
ListTile(
title: Text(
'backups.show_more'.tr(),
style: Theme.of(context).textTheme.labelMedium,
), ),
], leading: const Icon(
), Icons.arrow_drop_down,
),
onTap: null,
)
],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
OutlinedCard( OutlinedCard(
@ -317,7 +346,7 @@ class _CreateBackupsModalState extends State<CreateBackupsModal> {
service.displayName, service.displayName,
), ),
subtitle: Text( subtitle: Text(
busy ? 'backup.service_busy'.tr() : service.description, busy ? 'backup.service_busy'.tr() : service.backupDescription,
), ),
secondary: SvgPicture.string( secondary: SvgPicture.string(
service.svgIcon, service.svgIcon,