Implement Server Storage card for provider menu

Co-authored-by: Inex Code <inex.code@selfprivacy.org>
This commit is contained in:
NaiJi 2022-07-29 08:38:21 +03:00
parent c56a0f5976
commit c747dcd4ae
15 changed files with 928 additions and 1 deletions

View File

@ -13,6 +13,7 @@ analyzer:
exclude:
- lib/generated_plugin_registrant.dart
- lib/**.g.dart
- lib/**.graphql.dart
linter:
# The lint rules applied to this project can be customized in the

View File

@ -163,6 +163,16 @@
"refresh": "Refresh status",
"refetchBackups": "Refetch backup list",
"refetchingList": "In a few minutes list will be updated"
},
"storage": {
"card_title": "Server Storage",
"status_ok": "Disk usage is OK",
"status_error": "Low disk space",
"disk_usage": "{} GB used",
"disk_total": "{} GB total · {}",
"gb": "{} GB",
"mb": "{} MB",
"extend_volume_button": "Extend volume"
}
},
"not_ready_card": {

View File

@ -0,0 +1,11 @@
query GetServerDiskVolumesQuery {
storage {
volumes {
freeSpace
name
root
totalSpace
usedSpace
}
}
}

View File

@ -0,0 +1,542 @@
import 'package:gql/ast.dart';
import 'package:graphql/client.dart' as graphql;
import 'package:json_annotation/json_annotation.dart';
part 'get_server_disk_volumes.graphql.g.dart';
@JsonSerializable(explicitToJson: true)
class Query$GetServerDiskVolumesQuery {
Query$GetServerDiskVolumesQuery(
{required this.storage, required this.$__typename});
@override
factory Query$GetServerDiskVolumesQuery.fromJson(Map<String, dynamic> json) =>
_$Query$GetServerDiskVolumesQueryFromJson(json);
final Query$GetServerDiskVolumesQuery$storage storage;
@JsonKey(name: '__typename')
final String $__typename;
Map<String, dynamic> toJson() =>
_$Query$GetServerDiskVolumesQueryToJson(this);
int get hashCode {
final l$storage = storage;
final l$$__typename = $__typename;
return Object.hashAll([l$storage, l$$__typename]);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (!(other is Query$GetServerDiskVolumesQuery) ||
runtimeType != other.runtimeType) return false;
final l$storage = storage;
final lOther$storage = other.storage;
if (l$storage != lOther$storage) return false;
final l$$__typename = $__typename;
final lOther$$__typename = other.$__typename;
if (l$$__typename != lOther$$__typename) return false;
return true;
}
}
extension UtilityExtension$Query$GetServerDiskVolumesQuery
on Query$GetServerDiskVolumesQuery {
CopyWith$Query$GetServerDiskVolumesQuery<Query$GetServerDiskVolumesQuery>
get copyWith => CopyWith$Query$GetServerDiskVolumesQuery(this, (i) => i);
}
abstract class CopyWith$Query$GetServerDiskVolumesQuery<TRes> {
factory CopyWith$Query$GetServerDiskVolumesQuery(
Query$GetServerDiskVolumesQuery instance,
TRes Function(Query$GetServerDiskVolumesQuery) then) =
_CopyWithImpl$Query$GetServerDiskVolumesQuery;
factory CopyWith$Query$GetServerDiskVolumesQuery.stub(TRes res) =
_CopyWithStubImpl$Query$GetServerDiskVolumesQuery;
TRes call(
{Query$GetServerDiskVolumesQuery$storage? storage, String? $__typename});
CopyWith$Query$GetServerDiskVolumesQuery$storage<TRes> get storage;
}
class _CopyWithImpl$Query$GetServerDiskVolumesQuery<TRes>
implements CopyWith$Query$GetServerDiskVolumesQuery<TRes> {
_CopyWithImpl$Query$GetServerDiskVolumesQuery(this._instance, this._then);
final Query$GetServerDiskVolumesQuery _instance;
final TRes Function(Query$GetServerDiskVolumesQuery) _then;
static const _undefined = {};
TRes call({Object? storage = _undefined, Object? $__typename = _undefined}) =>
_then(Query$GetServerDiskVolumesQuery(
storage: storage == _undefined || storage == null
? _instance.storage
: (storage as Query$GetServerDiskVolumesQuery$storage),
$__typename: $__typename == _undefined || $__typename == null
? _instance.$__typename
: ($__typename as String)));
CopyWith$Query$GetServerDiskVolumesQuery$storage<TRes> get storage {
final local$storage = _instance.storage;
return CopyWith$Query$GetServerDiskVolumesQuery$storage(
local$storage, (e) => call(storage: e));
}
}
class _CopyWithStubImpl$Query$GetServerDiskVolumesQuery<TRes>
implements CopyWith$Query$GetServerDiskVolumesQuery<TRes> {
_CopyWithStubImpl$Query$GetServerDiskVolumesQuery(this._res);
TRes _res;
call(
{Query$GetServerDiskVolumesQuery$storage? storage,
String? $__typename}) =>
_res;
CopyWith$Query$GetServerDiskVolumesQuery$storage<TRes> get storage =>
CopyWith$Query$GetServerDiskVolumesQuery$storage.stub(_res);
}
const documentNodeQueryGetServerDiskVolumesQuery = DocumentNode(definitions: [
OperationDefinitionNode(
type: OperationType.query,
name: NameNode(value: 'GetServerDiskVolumesQuery'),
variableDefinitions: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'storage'),
alias: null,
arguments: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'volumes'),
alias: null,
arguments: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'freeSpace'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'name'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'root'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'totalSpace'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'usedSpace'),
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)
])),
]);
Query$GetServerDiskVolumesQuery _parserFn$Query$GetServerDiskVolumesQuery(
Map<String, dynamic> data) =>
Query$GetServerDiskVolumesQuery.fromJson(data);
class Options$Query$GetServerDiskVolumesQuery
extends graphql.QueryOptions<Query$GetServerDiskVolumesQuery> {
Options$Query$GetServerDiskVolumesQuery(
{String? operationName,
graphql.FetchPolicy? fetchPolicy,
graphql.ErrorPolicy? errorPolicy,
graphql.CacheRereadPolicy? cacheRereadPolicy,
Object? optimisticResult,
Duration? pollInterval,
graphql.Context? context})
: super(
operationName: operationName,
fetchPolicy: fetchPolicy,
errorPolicy: errorPolicy,
cacheRereadPolicy: cacheRereadPolicy,
optimisticResult: optimisticResult,
pollInterval: pollInterval,
context: context,
document: documentNodeQueryGetServerDiskVolumesQuery,
parserFn: _parserFn$Query$GetServerDiskVolumesQuery);
}
class WatchOptions$Query$GetServerDiskVolumesQuery
extends graphql.WatchQueryOptions<Query$GetServerDiskVolumesQuery> {
WatchOptions$Query$GetServerDiskVolumesQuery(
{String? operationName,
graphql.FetchPolicy? fetchPolicy,
graphql.ErrorPolicy? errorPolicy,
graphql.CacheRereadPolicy? cacheRereadPolicy,
Object? optimisticResult,
graphql.Context? context,
Duration? pollInterval,
bool? eagerlyFetchResults,
bool carryForwardDataOnException = true,
bool fetchResults = false})
: super(
operationName: operationName,
fetchPolicy: fetchPolicy,
errorPolicy: errorPolicy,
cacheRereadPolicy: cacheRereadPolicy,
optimisticResult: optimisticResult,
context: context,
document: documentNodeQueryGetServerDiskVolumesQuery,
pollInterval: pollInterval,
eagerlyFetchResults: eagerlyFetchResults,
carryForwardDataOnException: carryForwardDataOnException,
fetchResults: fetchResults,
parserFn: _parserFn$Query$GetServerDiskVolumesQuery);
}
class FetchMoreOptions$Query$GetServerDiskVolumesQuery
extends graphql.FetchMoreOptions {
FetchMoreOptions$Query$GetServerDiskVolumesQuery(
{required graphql.UpdateQuery updateQuery})
: super(
updateQuery: updateQuery,
document: documentNodeQueryGetServerDiskVolumesQuery);
}
extension ClientExtension$Query$GetServerDiskVolumesQuery
on graphql.GraphQLClient {
Future<graphql.QueryResult<Query$GetServerDiskVolumesQuery>>
query$GetServerDiskVolumesQuery(
[Options$Query$GetServerDiskVolumesQuery? options]) async =>
await this
.query(options ?? Options$Query$GetServerDiskVolumesQuery());
graphql.ObservableQuery<Query$GetServerDiskVolumesQuery>
watchQuery$GetServerDiskVolumesQuery(
[WatchOptions$Query$GetServerDiskVolumesQuery? options]) =>
this.watchQuery(
options ?? WatchOptions$Query$GetServerDiskVolumesQuery());
void writeQuery$GetServerDiskVolumesQuery(
{required Query$GetServerDiskVolumesQuery data,
bool broadcast = true}) =>
this.writeQuery(
graphql.Request(
operation: graphql.Operation(
document: documentNodeQueryGetServerDiskVolumesQuery)),
data: data.toJson(),
broadcast: broadcast);
Query$GetServerDiskVolumesQuery? readQuery$GetServerDiskVolumesQuery(
{bool optimistic = true}) {
final result = this.readQuery(
graphql.Request(
operation: graphql.Operation(
document: documentNodeQueryGetServerDiskVolumesQuery)),
optimistic: optimistic);
return result == null
? null
: Query$GetServerDiskVolumesQuery.fromJson(result);
}
}
@JsonSerializable(explicitToJson: true)
class Query$GetServerDiskVolumesQuery$storage {
Query$GetServerDiskVolumesQuery$storage(
{required this.volumes, required this.$__typename});
@override
factory Query$GetServerDiskVolumesQuery$storage.fromJson(
Map<String, dynamic> json) =>
_$Query$GetServerDiskVolumesQuery$storageFromJson(json);
final List<Query$GetServerDiskVolumesQuery$storage$volumes> volumes;
@JsonKey(name: '__typename')
final String $__typename;
Map<String, dynamic> toJson() =>
_$Query$GetServerDiskVolumesQuery$storageToJson(this);
int get hashCode {
final l$volumes = volumes;
final l$$__typename = $__typename;
return Object.hashAll(
[Object.hashAll(l$volumes.map((v) => v)), l$$__typename]);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (!(other is Query$GetServerDiskVolumesQuery$storage) ||
runtimeType != other.runtimeType) return false;
final l$volumes = volumes;
final lOther$volumes = other.volumes;
if (l$volumes.length != lOther$volumes.length) return false;
for (int i = 0; i < l$volumes.length; i++) {
final l$volumes$entry = l$volumes[i];
final lOther$volumes$entry = lOther$volumes[i];
if (l$volumes$entry != lOther$volumes$entry) return false;
}
final l$$__typename = $__typename;
final lOther$$__typename = other.$__typename;
if (l$$__typename != lOther$$__typename) return false;
return true;
}
}
extension UtilityExtension$Query$GetServerDiskVolumesQuery$storage
on Query$GetServerDiskVolumesQuery$storage {
CopyWith$Query$GetServerDiskVolumesQuery$storage<
Query$GetServerDiskVolumesQuery$storage>
get copyWith =>
CopyWith$Query$GetServerDiskVolumesQuery$storage(this, (i) => i);
}
abstract class CopyWith$Query$GetServerDiskVolumesQuery$storage<TRes> {
factory CopyWith$Query$GetServerDiskVolumesQuery$storage(
Query$GetServerDiskVolumesQuery$storage instance,
TRes Function(Query$GetServerDiskVolumesQuery$storage) then) =
_CopyWithImpl$Query$GetServerDiskVolumesQuery$storage;
factory CopyWith$Query$GetServerDiskVolumesQuery$storage.stub(TRes res) =
_CopyWithStubImpl$Query$GetServerDiskVolumesQuery$storage;
TRes call(
{List<Query$GetServerDiskVolumesQuery$storage$volumes>? volumes,
String? $__typename});
TRes volumes(
Iterable<Query$GetServerDiskVolumesQuery$storage$volumes> Function(
Iterable<
CopyWith$Query$GetServerDiskVolumesQuery$storage$volumes<
Query$GetServerDiskVolumesQuery$storage$volumes>>)
_fn);
}
class _CopyWithImpl$Query$GetServerDiskVolumesQuery$storage<TRes>
implements CopyWith$Query$GetServerDiskVolumesQuery$storage<TRes> {
_CopyWithImpl$Query$GetServerDiskVolumesQuery$storage(
this._instance, this._then);
final Query$GetServerDiskVolumesQuery$storage _instance;
final TRes Function(Query$GetServerDiskVolumesQuery$storage) _then;
static const _undefined = {};
TRes call({Object? volumes = _undefined, Object? $__typename = _undefined}) =>
_then(Query$GetServerDiskVolumesQuery$storage(
volumes: volumes == _undefined || volumes == null
? _instance.volumes
: (volumes
as List<Query$GetServerDiskVolumesQuery$storage$volumes>),
$__typename: $__typename == _undefined || $__typename == null
? _instance.$__typename
: ($__typename as String)));
TRes volumes(
Iterable<Query$GetServerDiskVolumesQuery$storage$volumes> Function(
Iterable<
CopyWith$Query$GetServerDiskVolumesQuery$storage$volumes<
Query$GetServerDiskVolumesQuery$storage$volumes>>)
_fn) =>
call(
volumes: _fn(_instance.volumes.map((e) =>
CopyWith$Query$GetServerDiskVolumesQuery$storage$volumes(
e, (i) => i))).toList());
}
class _CopyWithStubImpl$Query$GetServerDiskVolumesQuery$storage<TRes>
implements CopyWith$Query$GetServerDiskVolumesQuery$storage<TRes> {
_CopyWithStubImpl$Query$GetServerDiskVolumesQuery$storage(this._res);
TRes _res;
call(
{List<Query$GetServerDiskVolumesQuery$storage$volumes>? volumes,
String? $__typename}) =>
_res;
volumes(_fn) => _res;
}
@JsonSerializable(explicitToJson: true)
class Query$GetServerDiskVolumesQuery$storage$volumes {
Query$GetServerDiskVolumesQuery$storage$volumes(
{required this.freeSpace,
required this.name,
required this.root,
required this.totalSpace,
required this.usedSpace,
required this.$__typename});
@override
factory Query$GetServerDiskVolumesQuery$storage$volumes.fromJson(
Map<String, dynamic> json) =>
_$Query$GetServerDiskVolumesQuery$storage$volumesFromJson(json);
final String freeSpace;
final String name;
final bool root;
final String totalSpace;
final String usedSpace;
@JsonKey(name: '__typename')
final String $__typename;
Map<String, dynamic> toJson() =>
_$Query$GetServerDiskVolumesQuery$storage$volumesToJson(this);
int get hashCode {
final l$freeSpace = freeSpace;
final l$name = name;
final l$root = root;
final l$totalSpace = totalSpace;
final l$usedSpace = usedSpace;
final l$$__typename = $__typename;
return Object.hashAll([
l$freeSpace,
l$name,
l$root,
l$totalSpace,
l$usedSpace,
l$$__typename
]);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (!(other is Query$GetServerDiskVolumesQuery$storage$volumes) ||
runtimeType != other.runtimeType) return false;
final l$freeSpace = freeSpace;
final lOther$freeSpace = other.freeSpace;
if (l$freeSpace != lOther$freeSpace) return false;
final l$name = name;
final lOther$name = other.name;
if (l$name != lOther$name) return false;
final l$root = root;
final lOther$root = other.root;
if (l$root != lOther$root) return false;
final l$totalSpace = totalSpace;
final lOther$totalSpace = other.totalSpace;
if (l$totalSpace != lOther$totalSpace) return false;
final l$usedSpace = usedSpace;
final lOther$usedSpace = other.usedSpace;
if (l$usedSpace != lOther$usedSpace) return false;
final l$$__typename = $__typename;
final lOther$$__typename = other.$__typename;
if (l$$__typename != lOther$$__typename) return false;
return true;
}
}
extension UtilityExtension$Query$GetServerDiskVolumesQuery$storage$volumes
on Query$GetServerDiskVolumesQuery$storage$volumes {
CopyWith$Query$GetServerDiskVolumesQuery$storage$volumes<
Query$GetServerDiskVolumesQuery$storage$volumes>
get copyWith => CopyWith$Query$GetServerDiskVolumesQuery$storage$volumes(
this, (i) => i);
}
abstract class CopyWith$Query$GetServerDiskVolumesQuery$storage$volumes<TRes> {
factory CopyWith$Query$GetServerDiskVolumesQuery$storage$volumes(
Query$GetServerDiskVolumesQuery$storage$volumes instance,
TRes Function(Query$GetServerDiskVolumesQuery$storage$volumes) then) =
_CopyWithImpl$Query$GetServerDiskVolumesQuery$storage$volumes;
factory CopyWith$Query$GetServerDiskVolumesQuery$storage$volumes.stub(
TRes res) =
_CopyWithStubImpl$Query$GetServerDiskVolumesQuery$storage$volumes;
TRes call(
{String? freeSpace,
String? name,
bool? root,
String? totalSpace,
String? usedSpace,
String? $__typename});
}
class _CopyWithImpl$Query$GetServerDiskVolumesQuery$storage$volumes<TRes>
implements CopyWith$Query$GetServerDiskVolumesQuery$storage$volumes<TRes> {
_CopyWithImpl$Query$GetServerDiskVolumesQuery$storage$volumes(
this._instance, this._then);
final Query$GetServerDiskVolumesQuery$storage$volumes _instance;
final TRes Function(Query$GetServerDiskVolumesQuery$storage$volumes) _then;
static const _undefined = {};
TRes call(
{Object? freeSpace = _undefined,
Object? name = _undefined,
Object? root = _undefined,
Object? totalSpace = _undefined,
Object? usedSpace = _undefined,
Object? $__typename = _undefined}) =>
_then(Query$GetServerDiskVolumesQuery$storage$volumes(
freeSpace: freeSpace == _undefined || freeSpace == null
? _instance.freeSpace
: (freeSpace as String),
name: name == _undefined || name == null
? _instance.name
: (name as String),
root: root == _undefined || root == null
? _instance.root
: (root as bool),
totalSpace: totalSpace == _undefined || totalSpace == null
? _instance.totalSpace
: (totalSpace as String),
usedSpace: usedSpace == _undefined || usedSpace == null
? _instance.usedSpace
: (usedSpace as String),
$__typename: $__typename == _undefined || $__typename == null
? _instance.$__typename
: ($__typename as String)));
}
class _CopyWithStubImpl$Query$GetServerDiskVolumesQuery$storage$volumes<TRes>
implements CopyWith$Query$GetServerDiskVolumesQuery$storage$volumes<TRes> {
_CopyWithStubImpl$Query$GetServerDiskVolumesQuery$storage$volumes(this._res);
TRes _res;
call(
{String? freeSpace,
String? name,
bool? root,
String? totalSpace,
String? usedSpace,
String? $__typename}) =>
_res;
}

View File

@ -0,0 +1,64 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'get_server_disk_volumes.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Query$GetServerDiskVolumesQuery _$Query$GetServerDiskVolumesQueryFromJson(
Map<String, dynamic> json) =>
Query$GetServerDiskVolumesQuery(
storage: Query$GetServerDiskVolumesQuery$storage.fromJson(
json['storage'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetServerDiskVolumesQueryToJson(
Query$GetServerDiskVolumesQuery instance) =>
<String, dynamic>{
'storage': instance.storage.toJson(),
'__typename': instance.$__typename,
};
Query$GetServerDiskVolumesQuery$storage
_$Query$GetServerDiskVolumesQuery$storageFromJson(
Map<String, dynamic> json) =>
Query$GetServerDiskVolumesQuery$storage(
volumes: (json['volumes'] as List<dynamic>)
.map((e) =>
Query$GetServerDiskVolumesQuery$storage$volumes.fromJson(
e as Map<String, dynamic>))
.toList(),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetServerDiskVolumesQuery$storageToJson(
Query$GetServerDiskVolumesQuery$storage instance) =>
<String, dynamic>{
'volumes': instance.volumes.map((e) => e.toJson()).toList(),
'__typename': instance.$__typename,
};
Query$GetServerDiskVolumesQuery$storage$volumes
_$Query$GetServerDiskVolumesQuery$storage$volumesFromJson(
Map<String, dynamic> json) =>
Query$GetServerDiskVolumesQuery$storage$volumes(
freeSpace: json['freeSpace'] as String,
name: json['name'] as String,
root: json['root'] as bool,
totalSpace: json['totalSpace'] as String,
usedSpace: json['usedSpace'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetServerDiskVolumesQuery$storage$volumesToJson(
Query$GetServerDiskVolumesQuery$storage$volumes instance) =>
<String, dynamic>{
'freeSpace': instance.freeSpace,
'name': instance.name,
'root': instance.root,
'totalSpace': instance.totalSpace,
'usedSpace': instance.usedSpace,
'__typename': instance.$__typename,
};

View File

@ -7,6 +7,18 @@ type Alert {
timestamp: DateTime
}
type Storage {
volumes: [StorageVolume!]!
}
type StorageVolume {
freeSpace: String!
name: String!
root: Boolean!
totalSpace: String!
usedSpace: String!
}
type Api {
version: String!
devices: [ApiDevice!]!
@ -82,6 +94,7 @@ interface MutationReturnInterface {
type Query {
system: System!
storage: Storage!
api: Api!
}

View File

@ -3,8 +3,10 @@ import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/api_map.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/get_api_tokens.graphql.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/get_api_version.graphql.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/get_server_disk_volumes.graphql.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/json/api_token.dart';
import 'package:selfprivacy/logic/models/json/server_disk_volume.dart';
class ServerApi extends ApiMap {
ServerApi({
@ -54,4 +56,21 @@ class ServerApi extends ApiMap {
return tokens;
}
Future<List<ServerDiskVolume>> getServerDiskVolumes() async {
QueryResult response;
List<ServerDiskVolume> volumes = [];
try {
final GraphQLClient client = await getClient();
response = await client.query$GetServerDiskVolumesQuery();
volumes = response.data!['storage']['volumes']
.map<ServerDiskVolume>((final e) => ServerDiskVolume.fromJson(e))
.toList();
} catch (e) {
print(e);
}
return volumes;
}
}

View File

@ -4,12 +4,14 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:equatable/equatable.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/provider_api_settings.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/models/json/server_disk_volume.dart';
import 'package:selfprivacy/logic/models/server_basic_info.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_repository.dart';
@ -553,6 +555,10 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
);
}
Future<List<ServerDiskVolume>> getServerDiskVolumes() async =>
ServerApi(authToken: 'HARDCODE OUR BEARER HERE FOR NOW')
.getServerDiskVolumes();
Future<void> setAndValidateCloudflareToken(final String token) async {
final ServerInstallationRecovery dataState =
state as ServerInstallationRecovery;

View File

@ -0,0 +1,22 @@
import 'package:json_annotation/json_annotation.dart';
part 'server_disk_volume.g.dart';
@JsonSerializable()
class ServerDiskVolume {
factory ServerDiskVolume.fromJson(final Map<String, dynamic> json) =>
_$ServerDiskVolumeFromJson(json);
ServerDiskVolume({
required this.freeSpace,
required this.name,
required this.root,
required this.totalSpace,
required this.usedSpace,
});
final String freeSpace;
final String name;
final bool root;
final String totalSpace;
final String usedSpace;
}

View File

@ -0,0 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'server_disk_volume.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
ServerDiskVolume _$ServerDiskVolumeFromJson(Map<String, dynamic> json) =>
ServerDiskVolume(
freeSpace: json['freeSpace'] as String,
name: json['name'] as String,
root: json['root'] as bool,
totalSpace: json['totalSpace'] as String,
usedSpace: json['usedSpace'] as String,
);
Map<String, dynamic> _$ServerDiskVolumeToJson(ServerDiskVolume instance) =>
<String, dynamic>{
'freeSpace': instance.freeSpace,
'name': instance.name,
'root': instance.root,
'totalSpace': instance.totalSpace,
'usedSpace': instance.usedSpace,
};

View File

@ -1 +1 @@
enum StateType { uninitialized, stable, warning }
enum StateType { uninitialized, stable, warning, error }

View File

@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
class BrandLinearIndicator extends StatelessWidget {
const BrandLinearIndicator({
required this.value,
required this.color,
required this.backgroundColor,
required this.height,
final super.key,
});
final double value;
final Color color;
final Color backgroundColor;
final double height;
@override
Widget build(final BuildContext context) => Container(
height: height,
width: double.infinity,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(height),
),
alignment: Alignment.centerLeft,
child: FractionallySizedBox(
widthFactor: value,
child: Container(
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(height),
),
),
),
);
}

View File

@ -28,6 +28,12 @@ class IconStatusMask extends StatelessWidget {
case StateType.warning:
colors = BrandColors.warningGradientColors;
break;
case StateType.error:
colors = [
Theme.of(context).colorScheme.error,
Theme.of(context).colorScheme.error,
];
break;
}
return ShaderMask(
shaderCallback: (final bounds) => LinearGradient(

View File

@ -5,6 +5,7 @@ import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart';
import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/models/json/server_disk_volume.dart';
import 'package:selfprivacy/logic/models/provider.dart';
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart';
@ -15,6 +16,7 @@ import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart';
import 'package:selfprivacy/ui/helpers/modals.dart';
import 'package:selfprivacy/ui/pages/backup_details/backup_details.dart';
import 'package:selfprivacy/ui/pages/dns_details/dns_details.dart';
import 'package:selfprivacy/ui/pages/providers/storage_card.dart';
import 'package:selfprivacy/ui/pages/server_details/server_details_screen.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
@ -67,6 +69,23 @@ class _ProvidersPageState extends State<ProvidersPage> {
),
)
.toList();
cards.add(
Padding(
padding: const EdgeInsets.only(bottom: 30),
child: FutureBuilder(
future:
context.read<ServerInstallationCubit>().getServerDiskVolumes(),
builder: (
final BuildContext context,
final AsyncSnapshot<Object?> snapshot,
) =>
StorageCard(
volumes:
snapshot.hasData ? snapshot.data as List<ServerDiskVolume> : [],
),
),
),
);
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(52),

View File

@ -0,0 +1,152 @@
import 'dart:ffi';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart';
import 'package:selfprivacy/logic/models/json/server_disk_volume.dart';
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart';
import 'package:selfprivacy/ui/components/brand_linear_indicator/brand_linear_indicator.dart';
import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart';
class DiskVolume {
int gbUsed = 0;
int gbTotal = 0;
String name = '';
bool root = false;
/// from 0.0 to 1.0
double percentage = 0.0;
}
class DiskStatus {
bool isDiskOkay = false;
List<DiskVolume> diskVolumes = [];
}
class StorageCard extends StatelessWidget {
const StorageCard({required this.volumes, final super.key});
final List<ServerDiskVolume> volumes;
@override
Widget build(
final BuildContext context,
) {
final DiskStatus diskStatus = toDiskStatus(volumes);
final List<Widget> sections = [];
for (final DiskVolume volume in diskStatus.diskVolumes) {
sections.add(
const SizedBox(height: 16),
);
sections.add(
Text(
'providers.storage.disk_usage'.tr(args: [volume.gbUsed.toString()]),
style: Theme.of(context).textTheme.titleMedium,
),
);
sections.add(
const SizedBox(height: 4),
);
sections.add(
BrandLinearIndicator(
value: volume.percentage,
color: volume.root
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.secondary,
backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
height: 14.0,
),
);
sections.add(
const SizedBox(height: 4),
);
sections.add(
Text(
'providers.storage.disk_total'.tr(args: [
volume.gbTotal.toString(),
volume.name,
]),
style: Theme.of(context).textTheme.bodySmall,
),
);
}
return GestureDetector(
onTap: null,
child: BrandCards.big(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const IconStatusMask(
status: StateType.stable,
child: Icon(
Icons.storage_outlined,
size: 30,
color: Colors.white,
),
),
IconStatusMask(
status: StateType.stable,
child: Icon(
diskStatus.isDiskOkay
? Icons.check_circle_outline
: Icons.error_outline,
size: 24,
color: Colors.white,
),
),
],
),
const SizedBox(height: 16),
Text(
'providers.storage.card_title'.tr(),
style: Theme.of(context).textTheme.titleLarge,
),
Text(
diskStatus.isDiskOkay
? 'providers.storage.status_ok'.tr()
: 'providers.storage.status_error'.tr(),
style: Theme.of(context).textTheme.bodyLarge,
),
...sections,
const SizedBox(height: 8),
],
),
),
);
}
DiskStatus toDiskStatus(final List<ServerDiskVolume> status) {
final DiskStatus diskStatus = DiskStatus();
diskStatus.isDiskOkay = true;
diskStatus.diskVolumes = status.map((
final ServerDiskVolume volume,
) {
final DiskVolume diskVolume = DiskVolume();
diskVolume.gbUsed = volume.usedSpace == 'None'
? 0
: int.parse(volume.usedSpace) ~/ 1000000000;
diskVolume.gbTotal = volume.totalSpace == 'None'
? 0
: int.parse(volume.totalSpace) ~/ 1000000000;
diskVolume.name = volume.name;
diskVolume.root = volume.root;
diskVolume.percentage =
volume.usedSpace != 'None' && volume.totalSpace != 'None'
? 1.0 / int.parse(volume.totalSpace) * int.parse(volume.usedSpace)
: 0.0;
if (diskVolume.percentage >= 0.8 ||
diskVolume.gbTotal - diskVolume.gbUsed <= 2) {
diskStatus.isDiskOkay = false;
}
return diskVolume;
}).toList();
return diskStatus;
}
}