mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2024-11-17 22:29:15 +00:00
Merge pull request 'feat(backups): Implement restore backup strategy and forget snapshot' (#274) from restore-strategy into master
Reviewed-on: https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/pulls/274 Reviewed-by: Inex Code <inex.code@selfprivacy.org>
This commit is contained in:
commit
645d58d513
|
@ -203,8 +203,23 @@
|
|||
"backups_encryption_key_copy": "Copy the encryption key",
|
||||
"backups_encryption_key_show": "Show the encryption key",
|
||||
"backups_encryption_key_description": "This key is used to encrypt your backups. If you lose it, you will not be able to restore your backups. Keep it in a safe place, as it will be useful if you ever need to restore from backups manually.",
|
||||
"backups_encryption_key_not_found": "Encryption key not found yet, please try again later.",
|
||||
"pending_jobs": "Currently running backup jobs",
|
||||
"snapshots_title": "Snapshot list"
|
||||
"snapshots_title": "Snapshot list",
|
||||
"forget_snapshot": "Forget snapshot",
|
||||
"forget_snapshot_alert": "You are about to delete this snapshot. Are you sure? This action usually cannot be undone.",
|
||||
"forget_snapshot_error": "Couldn't forget snapshot",
|
||||
"snapshot_modal_heading": "Snapshot details",
|
||||
"snapshot_service_title": "Service",
|
||||
"snapshot_creation_time_title": "Creation time",
|
||||
"snapshot_id_title": "Snapshot ID",
|
||||
"snapshot_modal_select_strategy": "Select the restore strategy",
|
||||
"snapshot_modal_download_verify_option_title": "Download, verify and then replace",
|
||||
"snapshot_modal_download_verify_option_description": "Less risk, but more free space needed. Downloads entire snapshot to the temporary storage, verifies it and then replaces the current data.",
|
||||
"snapshot_modal_inplace_option_title": "Replace in place",
|
||||
"snapshot_modal_inplace_option_description": "Less free space needed, but more risk. Replaces current data with the snapshot data during the download.",
|
||||
"snapshot_modal_service_not_found": "This is a snapshot of a service you don't have on your server anymore. Usually this shouldn't happen, and we cannot do the automatic restore. You can still download the snapshot and restore it manually. Contact SelfPrivacy support if you need help.",
|
||||
"restore_started": "Restore started, check the jobs list for the current status"
|
||||
},
|
||||
"storage": {
|
||||
"card_title": "Server Storage",
|
||||
|
|
|
@ -199,7 +199,8 @@
|
|||
"autobackup_set_period": "Установить период",
|
||||
"autobackup_period_set": "Период установлен",
|
||||
"backups_encryption_key": "Ключ шифрования",
|
||||
"snapshots_title": "Список снимков"
|
||||
"snapshots_title": "Список снимков",
|
||||
"forget_snapshot_error": "Не удалось забыть снимок"
|
||||
},
|
||||
"storage": {
|
||||
"card_title": "Хранилище",
|
||||
|
|
|
@ -81,9 +81,9 @@ mutation InitializeRepository($repository: InitializeRepositoryInput!) {
|
|||
}
|
||||
}
|
||||
|
||||
mutation RestoreBackup($snapshotId: String!) {
|
||||
mutation RestoreBackup($snapshotId: String!, $strategy: RestoreStrategy! = DOWNLOAD_VERIFY_OVERWRITE) {
|
||||
backup {
|
||||
restoreBackup(snapshotId: $snapshotId) {
|
||||
restoreBackup(snapshotId: $snapshotId, strategy: $strategy) {
|
||||
...basicMutationReturnFields
|
||||
job {
|
||||
...basicApiJobsFields
|
||||
|
@ -91,3 +91,11 @@ mutation RestoreBackup($snapshotId: String!) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutation ForgetSnapshot($snapshotId: String!) {
|
||||
backup {
|
||||
forgetSnapshot(snapshotId: $snapshotId) {
|
||||
...basicMutationReturnFields
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4982,9 +4982,13 @@ class _CopyWithStubImpl$Mutation$InitializeRepository$backup<TRes>
|
|||
}
|
||||
|
||||
class Variables$Mutation$RestoreBackup {
|
||||
factory Variables$Mutation$RestoreBackup({required String snapshotId}) =>
|
||||
factory Variables$Mutation$RestoreBackup({
|
||||
required String snapshotId,
|
||||
required Enum$RestoreStrategy strategy,
|
||||
}) =>
|
||||
Variables$Mutation$RestoreBackup._({
|
||||
r'snapshotId': snapshotId,
|
||||
r'strategy': strategy,
|
||||
});
|
||||
|
||||
Variables$Mutation$RestoreBackup._(this._$data);
|
||||
|
@ -4993,16 +4997,23 @@ class Variables$Mutation$RestoreBackup {
|
|||
final result$data = <String, dynamic>{};
|
||||
final l$snapshotId = data['snapshotId'];
|
||||
result$data['snapshotId'] = (l$snapshotId as String);
|
||||
final l$strategy = data['strategy'];
|
||||
result$data['strategy'] =
|
||||
fromJson$Enum$RestoreStrategy((l$strategy as String));
|
||||
return Variables$Mutation$RestoreBackup._(result$data);
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$data;
|
||||
|
||||
String get snapshotId => (_$data['snapshotId'] as String);
|
||||
Enum$RestoreStrategy get strategy =>
|
||||
(_$data['strategy'] as Enum$RestoreStrategy);
|
||||
Map<String, dynamic> toJson() {
|
||||
final result$data = <String, dynamic>{};
|
||||
final l$snapshotId = snapshotId;
|
||||
result$data['snapshotId'] = l$snapshotId;
|
||||
final l$strategy = strategy;
|
||||
result$data['strategy'] = toJson$Enum$RestoreStrategy(l$strategy);
|
||||
return result$data;
|
||||
}
|
||||
|
||||
|
@ -5025,13 +5036,22 @@ class Variables$Mutation$RestoreBackup {
|
|||
if (l$snapshotId != lOther$snapshotId) {
|
||||
return false;
|
||||
}
|
||||
final l$strategy = strategy;
|
||||
final lOther$strategy = other.strategy;
|
||||
if (l$strategy != lOther$strategy) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
final l$snapshotId = snapshotId;
|
||||
return Object.hashAll([l$snapshotId]);
|
||||
final l$strategy = strategy;
|
||||
return Object.hashAll([
|
||||
l$snapshotId,
|
||||
l$strategy,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5044,7 +5064,10 @@ abstract class CopyWith$Variables$Mutation$RestoreBackup<TRes> {
|
|||
factory CopyWith$Variables$Mutation$RestoreBackup.stub(TRes res) =
|
||||
_CopyWithStubImpl$Variables$Mutation$RestoreBackup;
|
||||
|
||||
TRes call({String? snapshotId});
|
||||
TRes call({
|
||||
String? snapshotId,
|
||||
Enum$RestoreStrategy? strategy,
|
||||
});
|
||||
}
|
||||
|
||||
class _CopyWithImpl$Variables$Mutation$RestoreBackup<TRes>
|
||||
|
@ -5060,11 +5083,16 @@ class _CopyWithImpl$Variables$Mutation$RestoreBackup<TRes>
|
|||
|
||||
static const _undefined = <dynamic, dynamic>{};
|
||||
|
||||
TRes call({Object? snapshotId = _undefined}) =>
|
||||
TRes call({
|
||||
Object? snapshotId = _undefined,
|
||||
Object? strategy = _undefined,
|
||||
}) =>
|
||||
_then(Variables$Mutation$RestoreBackup._({
|
||||
..._instance._$data,
|
||||
if (snapshotId != _undefined && snapshotId != null)
|
||||
'snapshotId': (snapshotId as String),
|
||||
if (strategy != _undefined && strategy != null)
|
||||
'strategy': (strategy as Enum$RestoreStrategy),
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -5074,7 +5102,11 @@ class _CopyWithStubImpl$Variables$Mutation$RestoreBackup<TRes>
|
|||
|
||||
TRes _res;
|
||||
|
||||
call({String? snapshotId}) => _res;
|
||||
call({
|
||||
String? snapshotId,
|
||||
Enum$RestoreStrategy? strategy,
|
||||
}) =>
|
||||
_res;
|
||||
}
|
||||
|
||||
class Mutation$RestoreBackup {
|
||||
|
@ -5223,7 +5255,18 @@ const documentNodeMutationRestoreBackup = DocumentNode(definitions: [
|
|||
),
|
||||
defaultValue: DefaultValueNode(value: null),
|
||||
directives: [],
|
||||
)
|
||||
),
|
||||
VariableDefinitionNode(
|
||||
variable: VariableNode(name: NameNode(value: 'strategy')),
|
||||
type: NamedTypeNode(
|
||||
name: NameNode(value: 'RestoreStrategy'),
|
||||
isNonNull: true,
|
||||
),
|
||||
defaultValue: DefaultValueNode(
|
||||
value: EnumValueNode(
|
||||
name: NameNode(value: 'DOWNLOAD_VERIFY_OVERWRITE'))),
|
||||
directives: [],
|
||||
),
|
||||
],
|
||||
directives: [],
|
||||
selectionSet: SelectionSetNode(selections: [
|
||||
|
@ -5240,7 +5283,11 @@ const documentNodeMutationRestoreBackup = DocumentNode(definitions: [
|
|||
ArgumentNode(
|
||||
name: NameNode(value: 'snapshotId'),
|
||||
value: VariableNode(name: NameNode(value: 'snapshotId')),
|
||||
)
|
||||
),
|
||||
ArgumentNode(
|
||||
name: NameNode(value: 'strategy'),
|
||||
value: VariableNode(name: NameNode(value: 'strategy')),
|
||||
),
|
||||
],
|
||||
directives: [],
|
||||
selectionSet: SelectionSetNode(selections: [
|
||||
|
@ -5727,3 +5774,702 @@ class _CopyWithStubImpl$Mutation$RestoreBackup$backup$restoreBackup<TRes>
|
|||
CopyWith$Fragment$basicApiJobsFields<TRes> get job =>
|
||||
CopyWith$Fragment$basicApiJobsFields.stub(_res);
|
||||
}
|
||||
|
||||
class Variables$Mutation$ForgetSnapshot {
|
||||
factory Variables$Mutation$ForgetSnapshot({required String snapshotId}) =>
|
||||
Variables$Mutation$ForgetSnapshot._({
|
||||
r'snapshotId': snapshotId,
|
||||
});
|
||||
|
||||
Variables$Mutation$ForgetSnapshot._(this._$data);
|
||||
|
||||
factory Variables$Mutation$ForgetSnapshot.fromJson(
|
||||
Map<String, dynamic> data) {
|
||||
final result$data = <String, dynamic>{};
|
||||
final l$snapshotId = data['snapshotId'];
|
||||
result$data['snapshotId'] = (l$snapshotId as String);
|
||||
return Variables$Mutation$ForgetSnapshot._(result$data);
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$data;
|
||||
|
||||
String get snapshotId => (_$data['snapshotId'] as String);
|
||||
Map<String, dynamic> toJson() {
|
||||
final result$data = <String, dynamic>{};
|
||||
final l$snapshotId = snapshotId;
|
||||
result$data['snapshotId'] = l$snapshotId;
|
||||
return result$data;
|
||||
}
|
||||
|
||||
CopyWith$Variables$Mutation$ForgetSnapshot<Variables$Mutation$ForgetSnapshot>
|
||||
get copyWith => CopyWith$Variables$Mutation$ForgetSnapshot(
|
||||
this,
|
||||
(i) => i,
|
||||
);
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (!(other is Variables$Mutation$ForgetSnapshot) ||
|
||||
runtimeType != other.runtimeType) {
|
||||
return false;
|
||||
}
|
||||
final l$snapshotId = snapshotId;
|
||||
final lOther$snapshotId = other.snapshotId;
|
||||
if (l$snapshotId != lOther$snapshotId) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
final l$snapshotId = snapshotId;
|
||||
return Object.hashAll([l$snapshotId]);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class CopyWith$Variables$Mutation$ForgetSnapshot<TRes> {
|
||||
factory CopyWith$Variables$Mutation$ForgetSnapshot(
|
||||
Variables$Mutation$ForgetSnapshot instance,
|
||||
TRes Function(Variables$Mutation$ForgetSnapshot) then,
|
||||
) = _CopyWithImpl$Variables$Mutation$ForgetSnapshot;
|
||||
|
||||
factory CopyWith$Variables$Mutation$ForgetSnapshot.stub(TRes res) =
|
||||
_CopyWithStubImpl$Variables$Mutation$ForgetSnapshot;
|
||||
|
||||
TRes call({String? snapshotId});
|
||||
}
|
||||
|
||||
class _CopyWithImpl$Variables$Mutation$ForgetSnapshot<TRes>
|
||||
implements CopyWith$Variables$Mutation$ForgetSnapshot<TRes> {
|
||||
_CopyWithImpl$Variables$Mutation$ForgetSnapshot(
|
||||
this._instance,
|
||||
this._then,
|
||||
);
|
||||
|
||||
final Variables$Mutation$ForgetSnapshot _instance;
|
||||
|
||||
final TRes Function(Variables$Mutation$ForgetSnapshot) _then;
|
||||
|
||||
static const _undefined = <dynamic, dynamic>{};
|
||||
|
||||
TRes call({Object? snapshotId = _undefined}) =>
|
||||
_then(Variables$Mutation$ForgetSnapshot._({
|
||||
..._instance._$data,
|
||||
if (snapshotId != _undefined && snapshotId != null)
|
||||
'snapshotId': (snapshotId as String),
|
||||
}));
|
||||
}
|
||||
|
||||
class _CopyWithStubImpl$Variables$Mutation$ForgetSnapshot<TRes>
|
||||
implements CopyWith$Variables$Mutation$ForgetSnapshot<TRes> {
|
||||
_CopyWithStubImpl$Variables$Mutation$ForgetSnapshot(this._res);
|
||||
|
||||
TRes _res;
|
||||
|
||||
call({String? snapshotId}) => _res;
|
||||
}
|
||||
|
||||
class Mutation$ForgetSnapshot {
|
||||
Mutation$ForgetSnapshot({
|
||||
required this.backup,
|
||||
this.$__typename = 'Mutation',
|
||||
});
|
||||
|
||||
factory Mutation$ForgetSnapshot.fromJson(Map<String, dynamic> json) {
|
||||
final l$backup = json['backup'];
|
||||
final l$$__typename = json['__typename'];
|
||||
return Mutation$ForgetSnapshot(
|
||||
backup: Mutation$ForgetSnapshot$backup.fromJson(
|
||||
(l$backup as Map<String, dynamic>)),
|
||||
$__typename: (l$$__typename as String),
|
||||
);
|
||||
}
|
||||
|
||||
final Mutation$ForgetSnapshot$backup backup;
|
||||
|
||||
final String $__typename;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final _resultData = <String, dynamic>{};
|
||||
final l$backup = backup;
|
||||
_resultData['backup'] = l$backup.toJson();
|
||||
final l$$__typename = $__typename;
|
||||
_resultData['__typename'] = l$$__typename;
|
||||
return _resultData;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
final l$backup = backup;
|
||||
final l$$__typename = $__typename;
|
||||
return Object.hashAll([
|
||||
l$backup,
|
||||
l$$__typename,
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (!(other is Mutation$ForgetSnapshot) ||
|
||||
runtimeType != other.runtimeType) {
|
||||
return false;
|
||||
}
|
||||
final l$backup = backup;
|
||||
final lOther$backup = other.backup;
|
||||
if (l$backup != lOther$backup) {
|
||||
return false;
|
||||
}
|
||||
final l$$__typename = $__typename;
|
||||
final lOther$$__typename = other.$__typename;
|
||||
if (l$$__typename != lOther$$__typename) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension UtilityExtension$Mutation$ForgetSnapshot on Mutation$ForgetSnapshot {
|
||||
CopyWith$Mutation$ForgetSnapshot<Mutation$ForgetSnapshot> get copyWith =>
|
||||
CopyWith$Mutation$ForgetSnapshot(
|
||||
this,
|
||||
(i) => i,
|
||||
);
|
||||
}
|
||||
|
||||
abstract class CopyWith$Mutation$ForgetSnapshot<TRes> {
|
||||
factory CopyWith$Mutation$ForgetSnapshot(
|
||||
Mutation$ForgetSnapshot instance,
|
||||
TRes Function(Mutation$ForgetSnapshot) then,
|
||||
) = _CopyWithImpl$Mutation$ForgetSnapshot;
|
||||
|
||||
factory CopyWith$Mutation$ForgetSnapshot.stub(TRes res) =
|
||||
_CopyWithStubImpl$Mutation$ForgetSnapshot;
|
||||
|
||||
TRes call({
|
||||
Mutation$ForgetSnapshot$backup? backup,
|
||||
String? $__typename,
|
||||
});
|
||||
CopyWith$Mutation$ForgetSnapshot$backup<TRes> get backup;
|
||||
}
|
||||
|
||||
class _CopyWithImpl$Mutation$ForgetSnapshot<TRes>
|
||||
implements CopyWith$Mutation$ForgetSnapshot<TRes> {
|
||||
_CopyWithImpl$Mutation$ForgetSnapshot(
|
||||
this._instance,
|
||||
this._then,
|
||||
);
|
||||
|
||||
final Mutation$ForgetSnapshot _instance;
|
||||
|
||||
final TRes Function(Mutation$ForgetSnapshot) _then;
|
||||
|
||||
static const _undefined = <dynamic, dynamic>{};
|
||||
|
||||
TRes call({
|
||||
Object? backup = _undefined,
|
||||
Object? $__typename = _undefined,
|
||||
}) =>
|
||||
_then(Mutation$ForgetSnapshot(
|
||||
backup: backup == _undefined || backup == null
|
||||
? _instance.backup
|
||||
: (backup as Mutation$ForgetSnapshot$backup),
|
||||
$__typename: $__typename == _undefined || $__typename == null
|
||||
? _instance.$__typename
|
||||
: ($__typename as String),
|
||||
));
|
||||
CopyWith$Mutation$ForgetSnapshot$backup<TRes> get backup {
|
||||
final local$backup = _instance.backup;
|
||||
return CopyWith$Mutation$ForgetSnapshot$backup(
|
||||
local$backup, (e) => call(backup: e));
|
||||
}
|
||||
}
|
||||
|
||||
class _CopyWithStubImpl$Mutation$ForgetSnapshot<TRes>
|
||||
implements CopyWith$Mutation$ForgetSnapshot<TRes> {
|
||||
_CopyWithStubImpl$Mutation$ForgetSnapshot(this._res);
|
||||
|
||||
TRes _res;
|
||||
|
||||
call({
|
||||
Mutation$ForgetSnapshot$backup? backup,
|
||||
String? $__typename,
|
||||
}) =>
|
||||
_res;
|
||||
CopyWith$Mutation$ForgetSnapshot$backup<TRes> get backup =>
|
||||
CopyWith$Mutation$ForgetSnapshot$backup.stub(_res);
|
||||
}
|
||||
|
||||
const documentNodeMutationForgetSnapshot = DocumentNode(definitions: [
|
||||
OperationDefinitionNode(
|
||||
type: OperationType.mutation,
|
||||
name: NameNode(value: 'ForgetSnapshot'),
|
||||
variableDefinitions: [
|
||||
VariableDefinitionNode(
|
||||
variable: VariableNode(name: NameNode(value: 'snapshotId')),
|
||||
type: NamedTypeNode(
|
||||
name: NameNode(value: 'String'),
|
||||
isNonNull: true,
|
||||
),
|
||||
defaultValue: DefaultValueNode(value: null),
|
||||
directives: [],
|
||||
)
|
||||
],
|
||||
directives: [],
|
||||
selectionSet: SelectionSetNode(selections: [
|
||||
FieldNode(
|
||||
name: NameNode(value: 'backup'),
|
||||
alias: null,
|
||||
arguments: [],
|
||||
directives: [],
|
||||
selectionSet: SelectionSetNode(selections: [
|
||||
FieldNode(
|
||||
name: NameNode(value: 'forgetSnapshot'),
|
||||
alias: null,
|
||||
arguments: [
|
||||
ArgumentNode(
|
||||
name: NameNode(value: 'snapshotId'),
|
||||
value: VariableNode(name: NameNode(value: 'snapshotId')),
|
||||
)
|
||||
],
|
||||
directives: [],
|
||||
selectionSet: SelectionSetNode(selections: [
|
||||
FragmentSpreadNode(
|
||||
name: NameNode(value: 'basicMutationReturnFields'),
|
||||
directives: [],
|
||||
),
|
||||
FieldNode(
|
||||
name: NameNode(value: '__typename'),
|
||||
alias: null,
|
||||
arguments: [],
|
||||
directives: [],
|
||||
selectionSet: null,
|
||||
),
|
||||
]),
|
||||
),
|
||||
FieldNode(
|
||||
name: NameNode(value: '__typename'),
|
||||
alias: null,
|
||||
arguments: [],
|
||||
directives: [],
|
||||
selectionSet: null,
|
||||
),
|
||||
]),
|
||||
),
|
||||
FieldNode(
|
||||
name: NameNode(value: '__typename'),
|
||||
alias: null,
|
||||
arguments: [],
|
||||
directives: [],
|
||||
selectionSet: null,
|
||||
),
|
||||
]),
|
||||
),
|
||||
fragmentDefinitionbasicMutationReturnFields,
|
||||
]);
|
||||
Mutation$ForgetSnapshot _parserFn$Mutation$ForgetSnapshot(
|
||||
Map<String, dynamic> data) =>
|
||||
Mutation$ForgetSnapshot.fromJson(data);
|
||||
typedef OnMutationCompleted$Mutation$ForgetSnapshot = FutureOr<void> Function(
|
||||
Map<String, dynamic>?,
|
||||
Mutation$ForgetSnapshot?,
|
||||
);
|
||||
|
||||
class Options$Mutation$ForgetSnapshot
|
||||
extends graphql.MutationOptions<Mutation$ForgetSnapshot> {
|
||||
Options$Mutation$ForgetSnapshot({
|
||||
String? operationName,
|
||||
required Variables$Mutation$ForgetSnapshot variables,
|
||||
graphql.FetchPolicy? fetchPolicy,
|
||||
graphql.ErrorPolicy? errorPolicy,
|
||||
graphql.CacheRereadPolicy? cacheRereadPolicy,
|
||||
Object? optimisticResult,
|
||||
Mutation$ForgetSnapshot? typedOptimisticResult,
|
||||
graphql.Context? context,
|
||||
OnMutationCompleted$Mutation$ForgetSnapshot? onCompleted,
|
||||
graphql.OnMutationUpdate<Mutation$ForgetSnapshot>? update,
|
||||
graphql.OnError? onError,
|
||||
}) : onCompletedWithParsed = onCompleted,
|
||||
super(
|
||||
variables: variables.toJson(),
|
||||
operationName: operationName,
|
||||
fetchPolicy: fetchPolicy,
|
||||
errorPolicy: errorPolicy,
|
||||
cacheRereadPolicy: cacheRereadPolicy,
|
||||
optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(),
|
||||
context: context,
|
||||
onCompleted: onCompleted == null
|
||||
? null
|
||||
: (data) => onCompleted(
|
||||
data,
|
||||
data == null
|
||||
? null
|
||||
: _parserFn$Mutation$ForgetSnapshot(data),
|
||||
),
|
||||
update: update,
|
||||
onError: onError,
|
||||
document: documentNodeMutationForgetSnapshot,
|
||||
parserFn: _parserFn$Mutation$ForgetSnapshot,
|
||||
);
|
||||
|
||||
final OnMutationCompleted$Mutation$ForgetSnapshot? onCompletedWithParsed;
|
||||
|
||||
@override
|
||||
List<Object?> get properties => [
|
||||
...super.onCompleted == null
|
||||
? super.properties
|
||||
: super.properties.where((property) => property != onCompleted),
|
||||
onCompletedWithParsed,
|
||||
];
|
||||
}
|
||||
|
||||
class WatchOptions$Mutation$ForgetSnapshot
|
||||
extends graphql.WatchQueryOptions<Mutation$ForgetSnapshot> {
|
||||
WatchOptions$Mutation$ForgetSnapshot({
|
||||
String? operationName,
|
||||
required Variables$Mutation$ForgetSnapshot variables,
|
||||
graphql.FetchPolicy? fetchPolicy,
|
||||
graphql.ErrorPolicy? errorPolicy,
|
||||
graphql.CacheRereadPolicy? cacheRereadPolicy,
|
||||
Object? optimisticResult,
|
||||
Mutation$ForgetSnapshot? typedOptimisticResult,
|
||||
graphql.Context? context,
|
||||
Duration? pollInterval,
|
||||
bool? eagerlyFetchResults,
|
||||
bool carryForwardDataOnException = true,
|
||||
bool fetchResults = false,
|
||||
}) : super(
|
||||
variables: variables.toJson(),
|
||||
operationName: operationName,
|
||||
fetchPolicy: fetchPolicy,
|
||||
errorPolicy: errorPolicy,
|
||||
cacheRereadPolicy: cacheRereadPolicy,
|
||||
optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(),
|
||||
context: context,
|
||||
document: documentNodeMutationForgetSnapshot,
|
||||
pollInterval: pollInterval,
|
||||
eagerlyFetchResults: eagerlyFetchResults,
|
||||
carryForwardDataOnException: carryForwardDataOnException,
|
||||
fetchResults: fetchResults,
|
||||
parserFn: _parserFn$Mutation$ForgetSnapshot,
|
||||
);
|
||||
}
|
||||
|
||||
extension ClientExtension$Mutation$ForgetSnapshot on graphql.GraphQLClient {
|
||||
Future<graphql.QueryResult<Mutation$ForgetSnapshot>> mutate$ForgetSnapshot(
|
||||
Options$Mutation$ForgetSnapshot options) async =>
|
||||
await this.mutate(options);
|
||||
graphql.ObservableQuery<Mutation$ForgetSnapshot> watchMutation$ForgetSnapshot(
|
||||
WatchOptions$Mutation$ForgetSnapshot options) =>
|
||||
this.watchMutation(options);
|
||||
}
|
||||
|
||||
class Mutation$ForgetSnapshot$backup {
|
||||
Mutation$ForgetSnapshot$backup({
|
||||
required this.forgetSnapshot,
|
||||
this.$__typename = 'BackupMutations',
|
||||
});
|
||||
|
||||
factory Mutation$ForgetSnapshot$backup.fromJson(Map<String, dynamic> json) {
|
||||
final l$forgetSnapshot = json['forgetSnapshot'];
|
||||
final l$$__typename = json['__typename'];
|
||||
return Mutation$ForgetSnapshot$backup(
|
||||
forgetSnapshot: Mutation$ForgetSnapshot$backup$forgetSnapshot.fromJson(
|
||||
(l$forgetSnapshot as Map<String, dynamic>)),
|
||||
$__typename: (l$$__typename as String),
|
||||
);
|
||||
}
|
||||
|
||||
final Mutation$ForgetSnapshot$backup$forgetSnapshot forgetSnapshot;
|
||||
|
||||
final String $__typename;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final _resultData = <String, dynamic>{};
|
||||
final l$forgetSnapshot = forgetSnapshot;
|
||||
_resultData['forgetSnapshot'] = l$forgetSnapshot.toJson();
|
||||
final l$$__typename = $__typename;
|
||||
_resultData['__typename'] = l$$__typename;
|
||||
return _resultData;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
final l$forgetSnapshot = forgetSnapshot;
|
||||
final l$$__typename = $__typename;
|
||||
return Object.hashAll([
|
||||
l$forgetSnapshot,
|
||||
l$$__typename,
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (!(other is Mutation$ForgetSnapshot$backup) ||
|
||||
runtimeType != other.runtimeType) {
|
||||
return false;
|
||||
}
|
||||
final l$forgetSnapshot = forgetSnapshot;
|
||||
final lOther$forgetSnapshot = other.forgetSnapshot;
|
||||
if (l$forgetSnapshot != lOther$forgetSnapshot) {
|
||||
return false;
|
||||
}
|
||||
final l$$__typename = $__typename;
|
||||
final lOther$$__typename = other.$__typename;
|
||||
if (l$$__typename != lOther$$__typename) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension UtilityExtension$Mutation$ForgetSnapshot$backup
|
||||
on Mutation$ForgetSnapshot$backup {
|
||||
CopyWith$Mutation$ForgetSnapshot$backup<Mutation$ForgetSnapshot$backup>
|
||||
get copyWith => CopyWith$Mutation$ForgetSnapshot$backup(
|
||||
this,
|
||||
(i) => i,
|
||||
);
|
||||
}
|
||||
|
||||
abstract class CopyWith$Mutation$ForgetSnapshot$backup<TRes> {
|
||||
factory CopyWith$Mutation$ForgetSnapshot$backup(
|
||||
Mutation$ForgetSnapshot$backup instance,
|
||||
TRes Function(Mutation$ForgetSnapshot$backup) then,
|
||||
) = _CopyWithImpl$Mutation$ForgetSnapshot$backup;
|
||||
|
||||
factory CopyWith$Mutation$ForgetSnapshot$backup.stub(TRes res) =
|
||||
_CopyWithStubImpl$Mutation$ForgetSnapshot$backup;
|
||||
|
||||
TRes call({
|
||||
Mutation$ForgetSnapshot$backup$forgetSnapshot? forgetSnapshot,
|
||||
String? $__typename,
|
||||
});
|
||||
CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot<TRes>
|
||||
get forgetSnapshot;
|
||||
}
|
||||
|
||||
class _CopyWithImpl$Mutation$ForgetSnapshot$backup<TRes>
|
||||
implements CopyWith$Mutation$ForgetSnapshot$backup<TRes> {
|
||||
_CopyWithImpl$Mutation$ForgetSnapshot$backup(
|
||||
this._instance,
|
||||
this._then,
|
||||
);
|
||||
|
||||
final Mutation$ForgetSnapshot$backup _instance;
|
||||
|
||||
final TRes Function(Mutation$ForgetSnapshot$backup) _then;
|
||||
|
||||
static const _undefined = <dynamic, dynamic>{};
|
||||
|
||||
TRes call({
|
||||
Object? forgetSnapshot = _undefined,
|
||||
Object? $__typename = _undefined,
|
||||
}) =>
|
||||
_then(Mutation$ForgetSnapshot$backup(
|
||||
forgetSnapshot: forgetSnapshot == _undefined || forgetSnapshot == null
|
||||
? _instance.forgetSnapshot
|
||||
: (forgetSnapshot as Mutation$ForgetSnapshot$backup$forgetSnapshot),
|
||||
$__typename: $__typename == _undefined || $__typename == null
|
||||
? _instance.$__typename
|
||||
: ($__typename as String),
|
||||
));
|
||||
CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot<TRes>
|
||||
get forgetSnapshot {
|
||||
final local$forgetSnapshot = _instance.forgetSnapshot;
|
||||
return CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot(
|
||||
local$forgetSnapshot, (e) => call(forgetSnapshot: e));
|
||||
}
|
||||
}
|
||||
|
||||
class _CopyWithStubImpl$Mutation$ForgetSnapshot$backup<TRes>
|
||||
implements CopyWith$Mutation$ForgetSnapshot$backup<TRes> {
|
||||
_CopyWithStubImpl$Mutation$ForgetSnapshot$backup(this._res);
|
||||
|
||||
TRes _res;
|
||||
|
||||
call({
|
||||
Mutation$ForgetSnapshot$backup$forgetSnapshot? forgetSnapshot,
|
||||
String? $__typename,
|
||||
}) =>
|
||||
_res;
|
||||
CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot<TRes>
|
||||
get forgetSnapshot =>
|
||||
CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot.stub(_res);
|
||||
}
|
||||
|
||||
class Mutation$ForgetSnapshot$backup$forgetSnapshot
|
||||
implements Fragment$basicMutationReturnFields$$GenericMutationReturn {
|
||||
Mutation$ForgetSnapshot$backup$forgetSnapshot({
|
||||
required this.code,
|
||||
required this.message,
|
||||
required this.success,
|
||||
this.$__typename = 'GenericMutationReturn',
|
||||
});
|
||||
|
||||
factory Mutation$ForgetSnapshot$backup$forgetSnapshot.fromJson(
|
||||
Map<String, dynamic> json) {
|
||||
final l$code = json['code'];
|
||||
final l$message = json['message'];
|
||||
final l$success = json['success'];
|
||||
final l$$__typename = json['__typename'];
|
||||
return Mutation$ForgetSnapshot$backup$forgetSnapshot(
|
||||
code: (l$code as int),
|
||||
message: (l$message as String),
|
||||
success: (l$success as bool),
|
||||
$__typename: (l$$__typename as String),
|
||||
);
|
||||
}
|
||||
|
||||
final int code;
|
||||
|
||||
final String message;
|
||||
|
||||
final bool success;
|
||||
|
||||
final String $__typename;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final _resultData = <String, dynamic>{};
|
||||
final l$code = code;
|
||||
_resultData['code'] = l$code;
|
||||
final l$message = message;
|
||||
_resultData['message'] = l$message;
|
||||
final l$success = success;
|
||||
_resultData['success'] = l$success;
|
||||
final l$$__typename = $__typename;
|
||||
_resultData['__typename'] = l$$__typename;
|
||||
return _resultData;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
final l$code = code;
|
||||
final l$message = message;
|
||||
final l$success = success;
|
||||
final l$$__typename = $__typename;
|
||||
return Object.hashAll([
|
||||
l$code,
|
||||
l$message,
|
||||
l$success,
|
||||
l$$__typename,
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (!(other is Mutation$ForgetSnapshot$backup$forgetSnapshot) ||
|
||||
runtimeType != other.runtimeType) {
|
||||
return false;
|
||||
}
|
||||
final l$code = code;
|
||||
final lOther$code = other.code;
|
||||
if (l$code != lOther$code) {
|
||||
return false;
|
||||
}
|
||||
final l$message = message;
|
||||
final lOther$message = other.message;
|
||||
if (l$message != lOther$message) {
|
||||
return false;
|
||||
}
|
||||
final l$success = success;
|
||||
final lOther$success = other.success;
|
||||
if (l$success != lOther$success) {
|
||||
return false;
|
||||
}
|
||||
final l$$__typename = $__typename;
|
||||
final lOther$$__typename = other.$__typename;
|
||||
if (l$$__typename != lOther$$__typename) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension UtilityExtension$Mutation$ForgetSnapshot$backup$forgetSnapshot
|
||||
on Mutation$ForgetSnapshot$backup$forgetSnapshot {
|
||||
CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot<
|
||||
Mutation$ForgetSnapshot$backup$forgetSnapshot>
|
||||
get copyWith => CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot(
|
||||
this,
|
||||
(i) => i,
|
||||
);
|
||||
}
|
||||
|
||||
abstract class CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot<TRes> {
|
||||
factory CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot(
|
||||
Mutation$ForgetSnapshot$backup$forgetSnapshot instance,
|
||||
TRes Function(Mutation$ForgetSnapshot$backup$forgetSnapshot) then,
|
||||
) = _CopyWithImpl$Mutation$ForgetSnapshot$backup$forgetSnapshot;
|
||||
|
||||
factory CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot.stub(
|
||||
TRes res) =
|
||||
_CopyWithStubImpl$Mutation$ForgetSnapshot$backup$forgetSnapshot;
|
||||
|
||||
TRes call({
|
||||
int? code,
|
||||
String? message,
|
||||
bool? success,
|
||||
String? $__typename,
|
||||
});
|
||||
}
|
||||
|
||||
class _CopyWithImpl$Mutation$ForgetSnapshot$backup$forgetSnapshot<TRes>
|
||||
implements CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot<TRes> {
|
||||
_CopyWithImpl$Mutation$ForgetSnapshot$backup$forgetSnapshot(
|
||||
this._instance,
|
||||
this._then,
|
||||
);
|
||||
|
||||
final Mutation$ForgetSnapshot$backup$forgetSnapshot _instance;
|
||||
|
||||
final TRes Function(Mutation$ForgetSnapshot$backup$forgetSnapshot) _then;
|
||||
|
||||
static const _undefined = <dynamic, dynamic>{};
|
||||
|
||||
TRes call({
|
||||
Object? code = _undefined,
|
||||
Object? message = _undefined,
|
||||
Object? success = _undefined,
|
||||
Object? $__typename = _undefined,
|
||||
}) =>
|
||||
_then(Mutation$ForgetSnapshot$backup$forgetSnapshot(
|
||||
code:
|
||||
code == _undefined || code == null ? _instance.code : (code as int),
|
||||
message: message == _undefined || message == null
|
||||
? _instance.message
|
||||
: (message as String),
|
||||
success: success == _undefined || success == null
|
||||
? _instance.success
|
||||
: (success as bool),
|
||||
$__typename: $__typename == _undefined || $__typename == null
|
||||
? _instance.$__typename
|
||||
: ($__typename as String),
|
||||
));
|
||||
}
|
||||
|
||||
class _CopyWithStubImpl$Mutation$ForgetSnapshot$backup$forgetSnapshot<TRes>
|
||||
implements CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot<TRes> {
|
||||
_CopyWithStubImpl$Mutation$ForgetSnapshot$backup$forgetSnapshot(this._res);
|
||||
|
||||
TRes _res;
|
||||
|
||||
call({
|
||||
int? code,
|
||||
String? message,
|
||||
bool? success,
|
||||
String? $__typename,
|
||||
}) =>
|
||||
_res;
|
||||
}
|
||||
|
|
|
@ -94,7 +94,8 @@ type BackupMutations {
|
|||
removeRepository: GenericBackupConfigReturn!
|
||||
setAutobackupPeriod(period: Int = null): GenericBackupConfigReturn!
|
||||
startBackup(serviceId: String!): GenericJobMutationReturn!
|
||||
restoreBackup(snapshotId: String!): GenericJobMutationReturn!
|
||||
restoreBackup(snapshotId: String!, strategy: RestoreStrategy! = DOWNLOAD_VERIFY_OVERWRITE): GenericJobMutationReturn!
|
||||
forgetSnapshot(snapshotId: String!): GenericMutationReturn!
|
||||
forceSnapshotsReload: GenericMutationReturn!
|
||||
}
|
||||
|
||||
|
@ -241,6 +242,11 @@ input RecoveryKeyLimitsInput {
|
|||
uses: Int = null
|
||||
}
|
||||
|
||||
enum RestoreStrategy {
|
||||
INPLACE
|
||||
DOWNLOAD_VERIFY_OVERWRITE
|
||||
}
|
||||
|
||||
enum ServerProvider {
|
||||
HETZNER
|
||||
DIGITALOCEAN
|
||||
|
|
|
@ -1338,6 +1338,30 @@ Enum$DnsProvider fromJson$Enum$DnsProvider(String value) {
|
|||
}
|
||||
}
|
||||
|
||||
enum Enum$RestoreStrategy { INPLACE, DOWNLOAD_VERIFY_OVERWRITE, $unknown }
|
||||
|
||||
String toJson$Enum$RestoreStrategy(Enum$RestoreStrategy e) {
|
||||
switch (e) {
|
||||
case Enum$RestoreStrategy.INPLACE:
|
||||
return r'INPLACE';
|
||||
case Enum$RestoreStrategy.DOWNLOAD_VERIFY_OVERWRITE:
|
||||
return r'DOWNLOAD_VERIFY_OVERWRITE';
|
||||
case Enum$RestoreStrategy.$unknown:
|
||||
return r'$unknown';
|
||||
}
|
||||
}
|
||||
|
||||
Enum$RestoreStrategy fromJson$Enum$RestoreStrategy(String value) {
|
||||
switch (value) {
|
||||
case r'INPLACE':
|
||||
return Enum$RestoreStrategy.INPLACE;
|
||||
case r'DOWNLOAD_VERIFY_OVERWRITE':
|
||||
return Enum$RestoreStrategy.DOWNLOAD_VERIFY_OVERWRITE;
|
||||
default:
|
||||
return Enum$RestoreStrategy.$unknown;
|
||||
}
|
||||
}
|
||||
|
||||
enum Enum$ServerProvider { HETZNER, DIGITALOCEAN, $unknown }
|
||||
|
||||
String toJson$Enum$ServerProvider(Enum$ServerProvider e) {
|
||||
|
|
|
@ -209,14 +209,17 @@ mixin BackupsApi on GraphQLApiMap {
|
|||
|
||||
Future<GenericResult<ServerJob?>> restoreBackup(
|
||||
final String snapshotId,
|
||||
final BackupRestoreStrategy strategy,
|
||||
) async {
|
||||
QueryResult<Mutation$RestoreBackup> response;
|
||||
GenericResult<ServerJob?>? result;
|
||||
|
||||
try {
|
||||
final GraphQLClient client = await getClient();
|
||||
final variables =
|
||||
Variables$Mutation$RestoreBackup(snapshotId: snapshotId);
|
||||
final variables = Variables$Mutation$RestoreBackup(
|
||||
snapshotId: snapshotId,
|
||||
strategy: strategy.toGraphQL,
|
||||
);
|
||||
final options = Options$Mutation$RestoreBackup(variables: variables);
|
||||
response = await client.mutate$RestoreBackup(options);
|
||||
if (response.hasException) {
|
||||
|
@ -245,4 +248,42 @@ mixin BackupsApi on GraphQLApiMap {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<GenericResult<bool?>> forgetSnapshot(
|
||||
final String snapshotId,
|
||||
) async {
|
||||
QueryResult<Mutation$ForgetSnapshot> response;
|
||||
GenericResult<bool?>? result;
|
||||
|
||||
try {
|
||||
final GraphQLClient client = await getClient();
|
||||
final variables = Variables$Mutation$ForgetSnapshot(
|
||||
snapshotId: snapshotId,
|
||||
);
|
||||
final options = Options$Mutation$ForgetSnapshot(variables: variables);
|
||||
response = await client.mutate$ForgetSnapshot(options);
|
||||
if (response.hasException) {
|
||||
final message = response.exception.toString();
|
||||
print(message);
|
||||
result = GenericResult(
|
||||
success: false,
|
||||
data: null,
|
||||
message: message,
|
||||
);
|
||||
}
|
||||
result = GenericResult(
|
||||
success: true,
|
||||
data: response.parsedData!.backup.forgetSnapshot.success,
|
||||
);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
result = GenericResult(
|
||||
success: false,
|
||||
data: null,
|
||||
message: e.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
|||
refreshing: false,
|
||||
),
|
||||
);
|
||||
print(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,9 +112,7 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
|||
final BackblazeBucket? bucket = getIt<ApiConfigModel>().backblazeBucket;
|
||||
if (bucket == null) {
|
||||
emit(state.copyWith(isInitialized: false));
|
||||
print('bucket is null');
|
||||
} else {
|
||||
print('bucket is not null');
|
||||
final GenericResult result = await api.initializeRepository(
|
||||
InitializeRepositoryInput(
|
||||
provider: BackupsProviderType.backblaze,
|
||||
|
@ -125,7 +122,6 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
|||
password: bucket.applicationKey,
|
||||
),
|
||||
);
|
||||
print('result is $result');
|
||||
if (result.success == false) {
|
||||
getIt<NavigationService>()
|
||||
.showSnackBar(result.message ?? 'Unknown error');
|
||||
|
@ -185,9 +181,12 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
|||
emit(state.copyWith(preventActions: false));
|
||||
}
|
||||
|
||||
Future<void> restoreBackup(final String backupId) async {
|
||||
Future<void> restoreBackup(
|
||||
final String backupId,
|
||||
final BackupRestoreStrategy strategy,
|
||||
) async {
|
||||
emit(state.copyWith(preventActions: true));
|
||||
await api.restoreBackup(backupId);
|
||||
await api.restoreBackup(backupId, strategy);
|
||||
emit(state.copyWith(preventActions: false));
|
||||
}
|
||||
|
||||
|
@ -211,6 +210,30 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
|||
await updateBackups();
|
||||
}
|
||||
|
||||
Future<void> forgetSnapshot(final String snapshotId) async {
|
||||
final result = await api.forgetSnapshot(snapshotId);
|
||||
if (!result.success) {
|
||||
getIt<NavigationService>().showSnackBar('jobs.generic_error'.tr());
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.data == false) {
|
||||
getIt<NavigationService>()
|
||||
.showSnackBar('backup.forget_snapshot_error'.tr());
|
||||
}
|
||||
|
||||
// Optimistic update
|
||||
final backups = state.backups;
|
||||
final index =
|
||||
backups.indexWhere((final snapshot) => snapshot.id == snapshotId);
|
||||
if (index != -1) {
|
||||
backups.removeAt(index);
|
||||
emit(state.copyWith(backups: backups));
|
||||
}
|
||||
|
||||
await updateBackups();
|
||||
}
|
||||
|
||||
@override
|
||||
void clear() async {
|
||||
emit(const BackupsState());
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/backups.graphql.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
|
||||
|
||||
class Backup {
|
||||
|
@ -58,3 +59,26 @@ class BackupConfiguration {
|
|||
final String? locationName;
|
||||
final BackupsProviderType provider;
|
||||
}
|
||||
|
||||
enum BackupRestoreStrategy {
|
||||
inplace,
|
||||
downloadVerifyOverwrite,
|
||||
unknown;
|
||||
|
||||
factory BackupRestoreStrategy.fromGraphQL(
|
||||
final Enum$RestoreStrategy strategy,
|
||||
) =>
|
||||
switch (strategy) {
|
||||
Enum$RestoreStrategy.INPLACE => inplace,
|
||||
Enum$RestoreStrategy.DOWNLOAD_VERIFY_OVERWRITE =>
|
||||
downloadVerifyOverwrite,
|
||||
Enum$RestoreStrategy.$unknown => unknown,
|
||||
};
|
||||
|
||||
Enum$RestoreStrategy get toGraphQL => switch (this) {
|
||||
inplace => Enum$RestoreStrategy.INPLACE,
|
||||
downloadVerifyOverwrite =>
|
||||
Enum$RestoreStrategy.DOWNLOAD_VERIFY_OVERWRITE,
|
||||
unknown => Enum$RestoreStrategy.$unknown,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,17 +3,22 @@ import 'package:flutter/material.dart';
|
|||
class OutlinedCard extends StatelessWidget {
|
||||
const OutlinedCard({
|
||||
required this.child,
|
||||
this.borderColor,
|
||||
this.borderWidth,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
final Color? borderColor;
|
||||
final double? borderWidth;
|
||||
@override
|
||||
Widget build(final BuildContext context) => Card(
|
||||
elevation: 0.0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
side: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
color: borderColor ?? Theme.of(context).colorScheme.outline,
|
||||
width: borderWidth ?? 1.0,
|
||||
),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
|
|
|
@ -18,6 +18,7 @@ import 'package:selfprivacy/ui/helpers/modals.dart';
|
|||
import 'package:selfprivacy/ui/pages/backups/change_period_modal.dart';
|
||||
import 'package:selfprivacy/ui/pages/backups/copy_encryption_key_modal.dart';
|
||||
import 'package:selfprivacy/ui/pages/backups/create_backups_modal.dart';
|
||||
import 'package:selfprivacy/ui/pages/backups/snapshot_modal.dart';
|
||||
import 'package:selfprivacy/ui/router/router.dart';
|
||||
import 'package:selfprivacy/utils/extensions/duration.dart';
|
||||
|
||||
|
@ -62,6 +63,14 @@ class BackupDetailsPage extends StatelessWidget {
|
|||
heroTitle: 'backup.card_title'.tr(),
|
||||
heroSubtitle: 'backup.description'.tr(),
|
||||
children: [
|
||||
if (preventActions)
|
||||
const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
if (!preventActions)
|
||||
BrandButton.rised(
|
||||
onPressed: preventActions
|
||||
? null
|
||||
|
@ -176,7 +185,9 @@ class BackupDetailsPage extends StatelessWidget {
|
|||
'backup.backups_encryption_key_subtitle'.tr(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const SizedBox(height: 8),
|
||||
const Divider(),
|
||||
const SizedBox(height: 8),
|
||||
if (backupJobs.isNotEmpty)
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
@ -233,16 +244,40 @@ class BackupDetailsPage extends StatelessWidget {
|
|||
onTap: preventActions
|
||||
? null
|
||||
: () {
|
||||
showPopUpAlert(
|
||||
alertTitle: 'backup.restoring'.tr(),
|
||||
description: 'backup.restore_alert'.tr(
|
||||
args: [backup.time.toString()],
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (final BuildContext context) =>
|
||||
DraggableScrollableSheet(
|
||||
expand: false,
|
||||
maxChildSize: 0.9,
|
||||
minChildSize: 0.5,
|
||||
initialChildSize: 0.7,
|
||||
builder: (
|
||||
final context,
|
||||
final scrollController,
|
||||
) =>
|
||||
SnapshotModal(
|
||||
snapshot: backup,
|
||||
scrollController: scrollController,
|
||||
),
|
||||
actionButtonTitle: 'modals.yes'.tr(),
|
||||
),
|
||||
);
|
||||
},
|
||||
onLongPress: preventActions
|
||||
? null
|
||||
: () {
|
||||
showPopUpAlert(
|
||||
alertTitle: 'backup.forget_snapshot'.tr(),
|
||||
description:
|
||||
'backup.forget_snapshot_alert'.tr(),
|
||||
actionButtonTitle:
|
||||
'backup.forget_snapshot'.tr(),
|
||||
actionButtonOnPressed: () => {
|
||||
context
|
||||
.read<BackupsCubit>()
|
||||
.restoreBackup(backup.id)
|
||||
context.read<BackupsCubit>().forgetSnapshot(
|
||||
backup.id,
|
||||
)
|
||||
},
|
||||
);
|
||||
},
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'package:selfprivacy/logic/models/backup.dart';
|
|||
import 'package:selfprivacy/logic/models/service.dart';
|
||||
import 'package:selfprivacy/ui/helpers/modals.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/pages/backups/snapshot_modal.dart';
|
||||
|
||||
@RoutePage()
|
||||
class BackupsListPage extends StatelessWidget {
|
||||
|
@ -47,14 +48,35 @@ class BackupsListPage extends StatelessWidget {
|
|||
onTap: preventActions
|
||||
? null
|
||||
: () {
|
||||
showPopUpAlert(
|
||||
alertTitle: 'backup.restoring'.tr(),
|
||||
description: 'backup.restore_alert'.tr(
|
||||
args: [backup.time.toString()],
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (final BuildContext context) =>
|
||||
DraggableScrollableSheet(
|
||||
expand: false,
|
||||
maxChildSize: 0.9,
|
||||
minChildSize: 0.5,
|
||||
initialChildSize: 0.7,
|
||||
builder: (final context, final scrollController) =>
|
||||
SnapshotModal(
|
||||
snapshot: backup,
|
||||
scrollController: scrollController,
|
||||
),
|
||||
actionButtonTitle: 'modals.yes'.tr(),
|
||||
),
|
||||
);
|
||||
},
|
||||
onLongPress: preventActions
|
||||
? null
|
||||
: () {
|
||||
showPopUpAlert(
|
||||
alertTitle: 'backup.forget_snapshot'.tr(),
|
||||
description: 'backup.forget_snapshot_alert'.tr(),
|
||||
actionButtonTitle: 'backup.forget_snapshot'.tr(),
|
||||
actionButtonOnPressed: () => {
|
||||
context.read<BackupsCubit>().restoreBackup(backup.id)
|
||||
context.read<BackupsCubit>().forgetSnapshot(
|
||||
backup.id,
|
||||
)
|
||||
},
|
||||
);
|
||||
},
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
|
||||
|
||||
class CopyEncryptionKeyModal extends StatefulWidget {
|
||||
const CopyEncryptionKeyModal({
|
||||
|
@ -32,8 +33,27 @@ class _CopyEncryptionKeyModalState extends State<CopyEncryptionKeyModal> {
|
|||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final String encryptionKey =
|
||||
context.watch<BackupsCubit>().state.backblazeBucket!.encryptionKey;
|
||||
final String? encryptionKey =
|
||||
context.watch<BackupsCubit>().state.backblazeBucket?.encryptionKey;
|
||||
if (encryptionKey == null) {
|
||||
return ListView(
|
||||
controller: widget.scrollController,
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'backup.backups_encryption_key'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
InfoBox(
|
||||
text: 'backup.backups_encryption_key_not_found'.tr(),
|
||||
isWarning: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
return ListView(
|
||||
controller: widget.scrollController,
|
||||
padding: const EdgeInsets.all(16),
|
||||
|
|
232
lib/ui/pages/backups/snapshot_modal.dart
Normal file
232
lib/ui/pages/backups/snapshot_modal.dart
Normal file
|
@ -0,0 +1,232 @@
|
|||
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/cubit/backups/backups_cubit.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/backup.dart';
|
||||
import 'package:selfprivacy/logic/models/json/server_job.dart';
|
||||
import 'package:selfprivacy/logic/models/service.dart';
|
||||
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/cards/outlined_card.dart';
|
||||
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
|
||||
|
||||
class SnapshotModal extends StatefulWidget {
|
||||
const SnapshotModal({
|
||||
required this.snapshot,
|
||||
required this.scrollController,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Backup snapshot;
|
||||
final ScrollController scrollController;
|
||||
|
||||
@override
|
||||
State<SnapshotModal> createState() => _SnapshotModalState();
|
||||
}
|
||||
|
||||
class _SnapshotModalState extends State<SnapshotModal> {
|
||||
BackupRestoreStrategy selectedStrategy =
|
||||
BackupRestoreStrategy.downloadVerifyOverwrite;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final List<String> busyServices = context
|
||||
.watch<ServerJobsCubit>()
|
||||
.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 Service? service = context
|
||||
.read<ServicesCubit>()
|
||||
.state
|
||||
.getServiceById(widget.snapshot.serviceId);
|
||||
|
||||
return ListView(
|
||||
controller: widget.scrollController,
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'backup.snapshot_modal_heading'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
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,
|
||||
),
|
||||
),
|
||||
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)} ${TimeOfDay.fromDateTime(widget.snapshot.time).format(context)}',
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
Icons.numbers_outlined,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
title: Text(
|
||||
'backup.snapshot_id_title'.tr(),
|
||||
),
|
||||
subtitle: Text(
|
||||
widget.snapshot.id,
|
||||
),
|
||||
),
|
||||
if (service != null)
|
||||
Column(
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'backup.snapshot_modal_select_strategy'.tr(),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_BackupStrategySelectionCard(
|
||||
isSelected: selectedStrategy ==
|
||||
BackupRestoreStrategy.downloadVerifyOverwrite,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
selectedStrategy =
|
||||
BackupRestoreStrategy.downloadVerifyOverwrite;
|
||||
});
|
||||
},
|
||||
title:
|
||||
'backup.snapshot_modal_download_verify_option_title'.tr(),
|
||||
subtitle:
|
||||
'backup.snapshot_modal_download_verify_option_description'
|
||||
.tr(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_BackupStrategySelectionCard(
|
||||
isSelected: selectedStrategy == BackupRestoreStrategy.inplace,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
selectedStrategy = BackupRestoreStrategy.inplace;
|
||||
});
|
||||
},
|
||||
title: 'backup.snapshot_modal_inplace_option_title'.tr(),
|
||||
subtitle:
|
||||
'backup.snapshot_modal_inplace_option_description'.tr(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// Restore backup button
|
||||
BrandButton.filled(
|
||||
onPressed: isServiceBusy
|
||||
? null
|
||||
: () {
|
||||
context.read<BackupsCubit>().restoreBackup(
|
||||
widget.snapshot.id,
|
||||
selectedStrategy,
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
getIt<NavigationService>()
|
||||
.showSnackBar('backup.restore_started'.tr());
|
||||
},
|
||||
text: 'backup.restore'.tr(),
|
||||
),
|
||||
],
|
||||
)
|
||||
else
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: InfoBox(
|
||||
isWarning: true,
|
||||
text: 'backup.snapshot_modal_service_not_found'.tr(),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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