diff --git a/assets/translations/en.json b/assets/translations/en.json index f5194cdd..263c50b1 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -565,6 +565,8 @@ "upgrade_success": "Server upgrade started", "upgrade_failed": "Failed to upgrade server", "upgrade_server": "Upgrade server", + "collect_nix_garbage": "Collect system garbage", + "collect_nix_garbage_failed": "Failed to collect system garbage", "reboot_server": "Reboot server", "create_ssh_key": "Create SSH key for {}", "delete_ssh_key": "Delete SSH key for {}", diff --git a/lib/logic/api_maps/graphql_maps/schema/schema.graphql b/lib/logic/api_maps/graphql_maps/schema/schema.graphql index 66627055..bd86f454 100644 --- a/lib/logic/api_maps/graphql_maps/schema/schema.graphql +++ b/lib/logic/api_maps/graphql_maps/schema/schema.graphql @@ -443,6 +443,7 @@ type SystemMutations { runSystemUpgrade: GenericJobMutationReturn! rebootSystem: GenericMutationReturn! pullRepositoryChanges: GenericMutationReturn! + nixCollectGarbage: GenericJobMutationReturn! } type SystemProviderInfo { diff --git a/lib/logic/api_maps/graphql_maps/schema/server_api.graphql b/lib/logic/api_maps/graphql_maps/schema/server_api.graphql index d8e21485..ae1a4edb 100644 --- a/lib/logic/api_maps/graphql_maps/schema/server_api.graphql +++ b/lib/logic/api_maps/graphql_maps/schema/server_api.graphql @@ -79,6 +79,17 @@ mutation RunSystemUpgrade { } } +mutation NixCollectGarbage { + system { + nixCollectGarbage { + ...basicMutationReturnFields + job { + ...basicApiJobsFields + } + } + } +} + mutation RunSystemUpgradeFallback { system { runSystemUpgrade { diff --git a/lib/logic/api_maps/graphql_maps/schema/server_api.graphql.dart b/lib/logic/api_maps/graphql_maps/schema/server_api.graphql.dart index 7fcc3d4d..36f60bac 100644 --- a/lib/logic/api_maps/graphql_maps/schema/server_api.graphql.dart +++ b/lib/logic/api_maps/graphql_maps/schema/server_api.graphql.dart @@ -7043,6 +7043,663 @@ class _CopyWithStubImpl$Mutation$RunSystemUpgrade$system$runSystemUpgrade CopyWith$Fragment$basicApiJobsFields.stub(_res); } +class Mutation$NixCollectGarbage { + Mutation$NixCollectGarbage({ + required this.system, + this.$__typename = 'Mutation', + }); + + factory Mutation$NixCollectGarbage.fromJson(Map json) { + final l$system = json['system']; + final l$$__typename = json['__typename']; + return Mutation$NixCollectGarbage( + system: Mutation$NixCollectGarbage$system.fromJson( + (l$system as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$NixCollectGarbage$system system; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$system = system; + _resultData['system'] = l$system.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$system = system; + final l$$__typename = $__typename; + return Object.hashAll([ + l$system, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$NixCollectGarbage) || + runtimeType != other.runtimeType) { + return false; + } + final l$system = system; + final lOther$system = other.system; + if (l$system != lOther$system) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$NixCollectGarbage + on Mutation$NixCollectGarbage { + CopyWith$Mutation$NixCollectGarbage + get copyWith => CopyWith$Mutation$NixCollectGarbage( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$NixCollectGarbage { + factory CopyWith$Mutation$NixCollectGarbage( + Mutation$NixCollectGarbage instance, + TRes Function(Mutation$NixCollectGarbage) then, + ) = _CopyWithImpl$Mutation$NixCollectGarbage; + + factory CopyWith$Mutation$NixCollectGarbage.stub(TRes res) = + _CopyWithStubImpl$Mutation$NixCollectGarbage; + + TRes call({ + Mutation$NixCollectGarbage$system? system, + String? $__typename, + }); + CopyWith$Mutation$NixCollectGarbage$system get system; +} + +class _CopyWithImpl$Mutation$NixCollectGarbage + implements CopyWith$Mutation$NixCollectGarbage { + _CopyWithImpl$Mutation$NixCollectGarbage( + this._instance, + this._then, + ); + + final Mutation$NixCollectGarbage _instance; + + final TRes Function(Mutation$NixCollectGarbage) _then; + + static const _undefined = {}; + + TRes call({ + Object? system = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$NixCollectGarbage( + system: system == _undefined || system == null + ? _instance.system + : (system as Mutation$NixCollectGarbage$system), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + + CopyWith$Mutation$NixCollectGarbage$system get system { + final local$system = _instance.system; + return CopyWith$Mutation$NixCollectGarbage$system( + local$system, (e) => call(system: e)); + } +} + +class _CopyWithStubImpl$Mutation$NixCollectGarbage + implements CopyWith$Mutation$NixCollectGarbage { + _CopyWithStubImpl$Mutation$NixCollectGarbage(this._res); + + TRes _res; + + call({ + Mutation$NixCollectGarbage$system? system, + String? $__typename, + }) => + _res; + + CopyWith$Mutation$NixCollectGarbage$system get system => + CopyWith$Mutation$NixCollectGarbage$system.stub(_res); +} + +const documentNodeMutationNixCollectGarbage = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'NixCollectGarbage'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'system'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'nixCollectGarbage'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: 'job'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicApiJobsFields'), + 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, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, + fragmentDefinitionbasicApiJobsFields, +]); +Mutation$NixCollectGarbage _parserFn$Mutation$NixCollectGarbage( + Map data) => + Mutation$NixCollectGarbage.fromJson(data); +typedef OnMutationCompleted$Mutation$NixCollectGarbage = FutureOr + Function( + Map?, + Mutation$NixCollectGarbage?, +); + +class Options$Mutation$NixCollectGarbage + extends graphql.MutationOptions { + Options$Mutation$NixCollectGarbage({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$NixCollectGarbage? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$NixCollectGarbage? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + 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$NixCollectGarbage(data), + ), + update: update, + onError: onError, + document: documentNodeMutationNixCollectGarbage, + parserFn: _parserFn$Mutation$NixCollectGarbage, + ); + + final OnMutationCompleted$Mutation$NixCollectGarbage? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$NixCollectGarbage + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$NixCollectGarbage({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$NixCollectGarbage? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationNixCollectGarbage, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$NixCollectGarbage, + ); +} + +extension ClientExtension$Mutation$NixCollectGarbage on graphql.GraphQLClient { + Future> + mutate$NixCollectGarbage( + [Options$Mutation$NixCollectGarbage? options]) async => + await this.mutate(options ?? Options$Mutation$NixCollectGarbage()); + graphql.ObservableQuery< + Mutation$NixCollectGarbage> watchMutation$NixCollectGarbage( + [WatchOptions$Mutation$NixCollectGarbage? options]) => + this.watchMutation(options ?? WatchOptions$Mutation$NixCollectGarbage()); +} + +class Mutation$NixCollectGarbage$system { + Mutation$NixCollectGarbage$system({ + required this.nixCollectGarbage, + this.$__typename = 'SystemMutations', + }); + + factory Mutation$NixCollectGarbage$system.fromJson( + Map json) { + final l$nixCollectGarbage = json['nixCollectGarbage']; + final l$$__typename = json['__typename']; + return Mutation$NixCollectGarbage$system( + nixCollectGarbage: + Mutation$NixCollectGarbage$system$nixCollectGarbage.fromJson( + (l$nixCollectGarbage as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$NixCollectGarbage$system$nixCollectGarbage nixCollectGarbage; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$nixCollectGarbage = nixCollectGarbage; + _resultData['nixCollectGarbage'] = l$nixCollectGarbage.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$nixCollectGarbage = nixCollectGarbage; + final l$$__typename = $__typename; + return Object.hashAll([ + l$nixCollectGarbage, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$NixCollectGarbage$system) || + runtimeType != other.runtimeType) { + return false; + } + final l$nixCollectGarbage = nixCollectGarbage; + final lOther$nixCollectGarbage = other.nixCollectGarbage; + if (l$nixCollectGarbage != lOther$nixCollectGarbage) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$NixCollectGarbage$system + on Mutation$NixCollectGarbage$system { + CopyWith$Mutation$NixCollectGarbage$system + get copyWith => CopyWith$Mutation$NixCollectGarbage$system( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$NixCollectGarbage$system { + factory CopyWith$Mutation$NixCollectGarbage$system( + Mutation$NixCollectGarbage$system instance, + TRes Function(Mutation$NixCollectGarbage$system) then, + ) = _CopyWithImpl$Mutation$NixCollectGarbage$system; + + factory CopyWith$Mutation$NixCollectGarbage$system.stub(TRes res) = + _CopyWithStubImpl$Mutation$NixCollectGarbage$system; + + TRes call({ + Mutation$NixCollectGarbage$system$nixCollectGarbage? nixCollectGarbage, + String? $__typename, + }); + CopyWith$Mutation$NixCollectGarbage$system$nixCollectGarbage + get nixCollectGarbage; +} + +class _CopyWithImpl$Mutation$NixCollectGarbage$system + implements CopyWith$Mutation$NixCollectGarbage$system { + _CopyWithImpl$Mutation$NixCollectGarbage$system( + this._instance, + this._then, + ); + + final Mutation$NixCollectGarbage$system _instance; + + final TRes Function(Mutation$NixCollectGarbage$system) _then; + + static const _undefined = {}; + + TRes call({ + Object? nixCollectGarbage = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$NixCollectGarbage$system( + nixCollectGarbage: + nixCollectGarbage == _undefined || nixCollectGarbage == null + ? _instance.nixCollectGarbage + : (nixCollectGarbage + as Mutation$NixCollectGarbage$system$nixCollectGarbage), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + + CopyWith$Mutation$NixCollectGarbage$system$nixCollectGarbage + get nixCollectGarbage { + final local$nixCollectGarbage = _instance.nixCollectGarbage; + return CopyWith$Mutation$NixCollectGarbage$system$nixCollectGarbage( + local$nixCollectGarbage, (e) => call(nixCollectGarbage: e)); + } +} + +class _CopyWithStubImpl$Mutation$NixCollectGarbage$system + implements CopyWith$Mutation$NixCollectGarbage$system { + _CopyWithStubImpl$Mutation$NixCollectGarbage$system(this._res); + + TRes _res; + + call({ + Mutation$NixCollectGarbage$system$nixCollectGarbage? nixCollectGarbage, + String? $__typename, + }) => + _res; + + CopyWith$Mutation$NixCollectGarbage$system$nixCollectGarbage + get nixCollectGarbage => + CopyWith$Mutation$NixCollectGarbage$system$nixCollectGarbage.stub( + _res); +} + +class Mutation$NixCollectGarbage$system$nixCollectGarbage + implements Fragment$basicMutationReturnFields$$GenericJobMutationReturn { + Mutation$NixCollectGarbage$system$nixCollectGarbage({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericJobMutationReturn', + this.job, + }); + + factory Mutation$NixCollectGarbage$system$nixCollectGarbage.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + final l$job = json['job']; + return Mutation$NixCollectGarbage$system$nixCollectGarbage( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + job: l$job == null + ? null + : Fragment$basicApiJobsFields.fromJson( + (l$job as Map)), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + final Fragment$basicApiJobsFields? job; + + Map toJson() { + final _resultData = {}; + 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; + final l$job = job; + _resultData['job'] = l$job?.toJson(); + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + final l$job = job; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + l$job, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$NixCollectGarbage$system$nixCollectGarbage) || + 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; + } + final l$job = job; + final lOther$job = other.job; + if (l$job != lOther$job) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$NixCollectGarbage$system$nixCollectGarbage + on Mutation$NixCollectGarbage$system$nixCollectGarbage { + CopyWith$Mutation$NixCollectGarbage$system$nixCollectGarbage< + Mutation$NixCollectGarbage$system$nixCollectGarbage> + get copyWith => + CopyWith$Mutation$NixCollectGarbage$system$nixCollectGarbage( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$NixCollectGarbage$system$nixCollectGarbage< + TRes> { + factory CopyWith$Mutation$NixCollectGarbage$system$nixCollectGarbage( + Mutation$NixCollectGarbage$system$nixCollectGarbage instance, + TRes Function(Mutation$NixCollectGarbage$system$nixCollectGarbage) then, + ) = _CopyWithImpl$Mutation$NixCollectGarbage$system$nixCollectGarbage; + + factory CopyWith$Mutation$NixCollectGarbage$system$nixCollectGarbage.stub( + TRes res) = + _CopyWithStubImpl$Mutation$NixCollectGarbage$system$nixCollectGarbage; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$basicApiJobsFields? job, + }); + CopyWith$Fragment$basicApiJobsFields get job; +} + +class _CopyWithImpl$Mutation$NixCollectGarbage$system$nixCollectGarbage + implements + CopyWith$Mutation$NixCollectGarbage$system$nixCollectGarbage { + _CopyWithImpl$Mutation$NixCollectGarbage$system$nixCollectGarbage( + this._instance, + this._then, + ); + + final Mutation$NixCollectGarbage$system$nixCollectGarbage _instance; + + final TRes Function(Mutation$NixCollectGarbage$system$nixCollectGarbage) + _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + Object? job = _undefined, + }) => + _then(Mutation$NixCollectGarbage$system$nixCollectGarbage( + 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), + job: job == _undefined + ? _instance.job + : (job as Fragment$basicApiJobsFields?), + )); + + CopyWith$Fragment$basicApiJobsFields get job { + final local$job = _instance.job; + return local$job == null + ? CopyWith$Fragment$basicApiJobsFields.stub(_then(_instance)) + : CopyWith$Fragment$basicApiJobsFields(local$job, (e) => call(job: e)); + } +} + +class _CopyWithStubImpl$Mutation$NixCollectGarbage$system$nixCollectGarbage< + TRes> + implements + CopyWith$Mutation$NixCollectGarbage$system$nixCollectGarbage { + _CopyWithStubImpl$Mutation$NixCollectGarbage$system$nixCollectGarbage( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$basicApiJobsFields? job, + }) => + _res; + + CopyWith$Fragment$basicApiJobsFields get job => + CopyWith$Fragment$basicApiJobsFields.stub(_res); +} + class Mutation$RunSystemUpgradeFallback { Mutation$RunSystemUpgradeFallback({ required this.system, diff --git a/lib/logic/api_maps/graphql_maps/server_api/server_actions_api.dart b/lib/logic/api_maps/graphql_maps/server_api/server_actions_api.dart index f568a064..f020b0e9 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/server_actions_api.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/server_actions_api.dart @@ -144,4 +144,38 @@ mixin ServerActionsApi on GraphQLApiMap { ); } } + + Future> collectNixGarbage() async { + try { + final GraphQLClient client = await getClient(); + final result = await client.mutate$NixCollectGarbage(); + if (result.hasException) { + return GenericResult( + success: false, + data: null, + ); + } else if (result.parsedData!.system.nixCollectGarbage.success && + result.parsedData!.system.nixCollectGarbage.job != null) { + return GenericResult( + success: true, + data: ServerJob.fromGraphQL( + result.parsedData!.system.nixCollectGarbage.job!, + ), + message: result.parsedData!.system.nixCollectGarbage.message, + ); + } else { + return GenericResult( + success: false, + message: result.parsedData!.system.nixCollectGarbage.message, + data: null, + ); + } + } catch (e) { + return GenericResult( + success: false, + message: e.toString(), + data: null, + ); + } + } } diff --git a/lib/logic/cubit/client_jobs/client_jobs_cubit.dart b/lib/logic/cubit/client_jobs/client_jobs_cubit.dart index 81d1b0f2..86d99fb2 100644 --- a/lib/logic/cubit/client_jobs/client_jobs_cubit.dart +++ b/lib/logic/cubit/client_jobs/client_jobs_cubit.dart @@ -178,6 +178,45 @@ class JobsCubit extends Cubit { } } + Future collectNixGarbage() async { + if (state is JobsStateEmpty) { + emit( + JobsStateLoading( + [CollectNixGarbageJob(status: JobStatusEnum.running)], + null, + const [], + ), + ); + final result = + await getIt().api.collectNixGarbage(); + if (result.success && result.data != null) { + emit( + JobsStateLoading( + [CollectNixGarbageJob(status: JobStatusEnum.finished)], + result.data!.uid, + const [], + ), + ); + } else if (result.success) { + emit( + JobsStateFinished( + [CollectNixGarbageJob(status: JobStatusEnum.finished)], + null, + const [], + ), + ); + } else { + emit( + JobsStateFinished( + [CollectNixGarbageJob(status: JobStatusEnum.error)], + null, + const [], + ), + ); + } + } + } + Future acknowledgeFinished() async { if (state is! JobsStateFinished) { return; diff --git a/lib/logic/models/job.dart b/lib/logic/models/job.dart index 896da72a..02ac9d84 100644 --- a/lib/logic/models/job.dart +++ b/lib/logic/models/job.dart @@ -62,6 +62,36 @@ class UpgradeServerJob extends ClientJob { ); } +class CollectNixGarbageJob extends ClientJob { + CollectNixGarbageJob({ + super.status, + super.message, + super.id, + }) : super(title: 'jobs.collect_nix_garbage'.tr()); + + @override + bool canAddTo(final List jobs) => + !jobs.any((final job) => job is CollectNixGarbageJob); + + @override + Future<(bool, String)> execute() async { + final result = + await getIt().api.collectNixGarbage(); + return (result.success, result.message ?? ''); + } + + @override + CollectNixGarbageJob copyWithNewStatus({ + required final JobStatusEnum status, + final String? message, + }) => + CollectNixGarbageJob( + status: status, + message: message, + id: id, + ); +} + class RebootServerJob extends ClientJob { RebootServerJob({ super.status, diff --git a/lib/ui/components/jobs_content/jobs_content.dart b/lib/ui/components/jobs_content/jobs_content.dart index c4e8d43a..d982ba72 100644 --- a/lib/ui/components/jobs_content/jobs_content.dart +++ b/lib/ui/components/jobs_content/jobs_content.dart @@ -2,6 +2,7 @@ import 'package:collection/collection.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:gap/gap.dart'; import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart'; import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; @@ -63,7 +64,7 @@ class JobsContent extends StatelessWidget { context.read().state; if (state is JobsStateEmpty) { widgets = [ - const SizedBox(height: 80), + const Gap(80), Center( child: Text( 'jobs.empty'.tr(), @@ -75,12 +76,12 @@ class JobsContent extends StatelessWidget { if (installationState is ServerInstallationFinished) { widgets = [ ...widgets, - const SizedBox(height: 80), + const Gap(80), BrandButton.rised( onPressed: () => context.read().upgradeServer(), text: 'jobs.upgrade_server'.tr(), ), - const SizedBox(height: 10), + const Gap(10), BrandButton.text( title: 'jobs.reboot_server'.tr(), onPressed: () { @@ -189,7 +190,7 @@ class JobsContent extends StatelessWidget { style: Theme.of(context).textTheme.labelSmall, ), - const SizedBox(height: 8), + const Gap(8), LinearProgressIndicator( value: rebuildJob?.progress == null ? 0.0 @@ -206,7 +207,7 @@ class JobsContent extends StatelessWidget { minHeight: 7.0, borderRadius: BorderRadius.circular(7.0), ), - const SizedBox(height: 8), + const Gap(8), if (rebuildJob?.error != null || rebuildJob?.result != null || rebuildJob?.statusText != null) @@ -282,7 +283,7 @@ class JobsContent extends StatelessWidget { (final job) => job.uid == state.rebuildJobUid, ); if (rebuildJob == null) { - return const SizedBox(); + return const Gap(0); } return Row( children: [ @@ -322,7 +323,7 @@ class JobsContent extends StatelessWidget { rebuildJob.description, style: Theme.of(context).textTheme.labelSmall, ), - const SizedBox(height: 8), + const Gap(8), LinearProgressIndicator( value: rebuildJob.progress == null ? 0.0 @@ -339,7 +340,7 @@ class JobsContent extends StatelessWidget { minHeight: 7.0, borderRadius: BorderRadius.circular(7.0), ), - const SizedBox(height: 8), + const Gap(8), if (rebuildJob.error != null || rebuildJob.result != null || rebuildJob.statusText != null) @@ -360,7 +361,7 @@ class JobsContent extends StatelessWidget { ); }, ), - const SizedBox(height: 16), + const Gap(16), BrandButton.rised( onPressed: () => context.read().acknowledgeFinished(), text: 'basis.done'.tr(), @@ -403,7 +404,7 @@ class JobsContent extends StatelessWidget { ), ), ), - const SizedBox(width: 8), + const Gap(8), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: @@ -423,7 +424,7 @@ class JobsContent extends StatelessWidget { ], ), ), - const SizedBox(height: 16), + const Gap(16), BrandButton.rised( onPressed: hasBlockingJobs ? null @@ -436,18 +437,18 @@ class JobsContent extends StatelessWidget { controller: controller, padding: paddingH15V0, children: [ - const SizedBox(height: 16), + const Gap(16), Center( child: Text( 'jobs.title'.tr(), style: Theme.of(context).textTheme.headlineSmall, ), ), - const SizedBox(height: 20), + const Gap(20), ...widgets, - const SizedBox(height: 8), + const Gap(8), const Divider(height: 0), - const SizedBox(height: 8), + const Gap(8), if (serverJobs.isNotEmpty) Padding( padding: const EdgeInsets.all(8.0), @@ -489,7 +490,7 @@ class JobsContent extends StatelessWidget { }, ), ), - const SizedBox(height: 24), + const Gap(24), ], ); }, diff --git a/lib/ui/pages/server_storage/server_storage.dart b/lib/ui/pages/server_storage/server_storage.dart index 5f7f01bc..2f7932a8 100644 --- a/lib/ui/pages/server_storage/server_storage.dart +++ b/lib/ui/pages/server_storage/server_storage.dart @@ -2,7 +2,9 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:gap/gap.dart'; import 'package:selfprivacy/logic/bloc/services/services_bloc.dart'; +import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/models/disk_status.dart'; import 'package:selfprivacy/logic/models/service.dart'; @@ -44,6 +46,7 @@ class _ServerStoragePageState extends State { hasBackButton: true, heroTitle: 'storage.card_title'.tr(), bodyPadding: const EdgeInsets.symmetric(vertical: 16.0), + hasFlashButton: true, children: [ ...widget.diskStatus.diskVolumes.map( (final volume) => Column( @@ -59,13 +62,20 @@ class _ServerStoragePageState extends State { ) .toList(), ), - const SizedBox(height: 16), + const Gap(16), const Divider(), - const SizedBox(height: 16), + const Gap(16), ], ), ), - const SizedBox(height: 8), + const Gap(8), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: BrandOutlinedButton( + title: 'jobs.collect_nix_garbage'.tr(), + onPressed: context.read().collectNixGarbage, + ), + ), ], ); } @@ -93,7 +103,7 @@ class ServerStorageSection extends StatelessWidget { volume: volume, ), ), - const SizedBox(height: 16), + const Gap(16), ...services.map( (final service) => ServerConsumptionListTile( service: service, @@ -106,7 +116,7 @@ class ServerStorageSection extends StatelessWidget { ), ), if (volume.isResizable) ...[ - const SizedBox(height: 16), + const Gap(16), Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: BrandOutlinedButton(