refactor: Introduce VolumesBloc, remove ServerVolumeCubit

pull/440/head
Inex Code 2024-02-01 18:30:06 +04:00
parent 3222a9b500
commit 3b9d616045
17 changed files with 382 additions and 187 deletions

View File

@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart'; import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart';
import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart'; import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart'; import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
import 'package:selfprivacy/logic/bloc/volumes/volumes_bloc.dart';
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
import 'package:selfprivacy/logic/cubit/connection_status/connection_status_bloc.dart'; import 'package:selfprivacy/logic/cubit/connection_status/connection_status_bloc.dart';
@ -12,7 +13,6 @@ import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.d
import 'package:selfprivacy/logic/cubit/recovery_key/recovery_key_cubit.dart'; import 'package:selfprivacy/logic/cubit/recovery_key/recovery_key_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart'; import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart';
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
@ -34,11 +34,11 @@ class BlocAndProviderConfigState extends State<BlocAndProviderConfig> {
late final DnsRecordsCubit dnsRecordsCubit; late final DnsRecordsCubit dnsRecordsCubit;
late final RecoveryKeyCubit recoveryKeyCubit; late final RecoveryKeyCubit recoveryKeyCubit;
late final ApiDevicesCubit apiDevicesCubit; late final ApiDevicesCubit apiDevicesCubit;
late final ApiProviderVolumeCubit apiVolumesCubit; late final ProviderVolumeCubit apiVolumesCubit;
late final ApiServerVolumeCubit apiServerVolumesCubit;
late final ServerJobsBloc serverJobsBloc; late final ServerJobsBloc serverJobsBloc;
late final ConnectionStatusBloc connectionStatusBloc; late final ConnectionStatusBloc connectionStatusBloc;
late final ServerDetailsCubit serverDetailsCubit; late final ServerDetailsCubit serverDetailsCubit;
late final VolumesBloc volumesBloc;
@override @override
void initState() { void initState() {
@ -51,11 +51,11 @@ class BlocAndProviderConfigState extends State<BlocAndProviderConfig> {
dnsRecordsCubit = DnsRecordsCubit(); dnsRecordsCubit = DnsRecordsCubit();
recoveryKeyCubit = RecoveryKeyCubit(); recoveryKeyCubit = RecoveryKeyCubit();
apiDevicesCubit = ApiDevicesCubit(); apiDevicesCubit = ApiDevicesCubit();
apiVolumesCubit = ApiProviderVolumeCubit(); apiVolumesCubit = ProviderVolumeCubit();
apiServerVolumesCubit = ApiServerVolumeCubit(apiVolumesCubit);
serverJobsBloc = ServerJobsBloc(); serverJobsBloc = ServerJobsBloc();
connectionStatusBloc = ConnectionStatusBloc(); connectionStatusBloc = ConnectionStatusBloc();
serverDetailsCubit = ServerDetailsCubit(); serverDetailsCubit = ServerDetailsCubit();
volumesBloc = VolumesBloc();
} }
@override @override
@ -101,9 +101,6 @@ class BlocAndProviderConfigState extends State<BlocAndProviderConfig> {
BlocProvider( BlocProvider(
create: (final _) => apiVolumesCubit, create: (final _) => apiVolumesCubit,
), ),
BlocProvider(
create: (final _) => apiServerVolumesCubit,
),
BlocProvider( BlocProvider(
create: (final _) => serverJobsBloc, create: (final _) => serverJobsBloc,
), ),
@ -111,6 +108,7 @@ class BlocAndProviderConfigState extends State<BlocAndProviderConfig> {
BlocProvider( BlocProvider(
create: (final _) => serverDetailsCubit, create: (final _) => serverDetailsCubit,
), ),
BlocProvider(create: (final _) => volumesBloc),
BlocProvider( BlocProvider(
create: (final _) => JobsCubit( create: (final _) => JobsCubit(
usersCubit: usersCubit, usersCubit: usersCubit,

View File

@ -11,6 +11,7 @@ mixin VolumeApi on GraphQLApiMap {
if (response.hasException) { if (response.hasException) {
print(response.exception.toString()); print(response.exception.toString());
} }
// TODO: Rewrite to use fromGraphQL
volumes = response.data!['storage']['volumes'] volumes = response.data!['storage']['volumes']
.map<ServerDiskVolume>((final e) => ServerDiskVolume.fromJson(e)) .map<ServerDiskVolume>((final e) => ServerDiskVolume.fromJson(e))
.toList(); .toList();

View File

@ -0,0 +1,163 @@
import 'dart:async';
import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/models/disk_status.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/json/server_disk_volume.dart';
import 'package:selfprivacy/logic/models/price.dart';
import 'package:selfprivacy/logic/providers/providers_controller.dart';
part 'volumes_event.dart';
part 'volumes_state.dart';
class VolumesBloc extends Bloc<VolumesEvent, VolumesState> {
VolumesBloc() : super(VolumesInitial()) {
on<VolumesServerLoaded>(
_loadState,
transformer: droppable(),
);
on<VolumesServerReset>(
_resetState,
transformer: droppable(),
);
on<VolumesServerStateChanged>(
_updateState,
transformer: droppable(),
);
final connectionRepository = getIt<ApiConnectionRepository>();
_apiStatusSubscription = connectionRepository.connectionStatusStream
.listen((final ConnectionStatus connectionStatus) {
switch (connectionStatus) {
case ConnectionStatus.nonexistent:
add(const VolumesServerReset());
isLoaded = false;
break;
case ConnectionStatus.connected:
if (!isLoaded) {
add(const VolumesServerLoaded());
isLoaded = true;
}
break;
default:
break;
}
});
_apiDataSubscription = connectionRepository.dataStream.listen(
(final ApiData apiData) {
if (apiData.volumes.data == null) {
add(const VolumesServerReset());
} else {
add(
VolumesServerStateChanged(
apiData.volumes.data!,
),
);
}
},
);
}
late StreamSubscription _apiStatusSubscription;
late StreamSubscription _apiDataSubscription;
bool isLoaded = false;
Future<Price?> getPricePerGb() async {
if (ProvidersController.currentServerProvider == null) {
return null;
}
Price? price;
final pricingResult =
await ProvidersController.currentServerProvider!.getAdditionalPricing();
if (pricingResult.data == null || !pricingResult.success) {
getIt<NavigationService>().showSnackBar('server.pricing_error'.tr());
return price;
}
price = pricingResult.data!.perVolumeGb;
return price;
}
Future<void> _loadState(
final VolumesServerLoaded event,
final Emitter<VolumesState> emit,
) async {
if (ProvidersController.currentServerProvider == null) {
return;
}
emit(VolumesLoading());
final volumesResult =
await ProvidersController.currentServerProvider!.getVolumes();
if (!volumesResult.success || volumesResult.data.isEmpty) {
emit(VolumesInitial());
return;
}
final serverVolumes = getIt<ApiConnectionRepository>().apiData.volumes.data;
if (serverVolumes == null) {
emit(VolumesLoading(providerVolumes: volumesResult.data));
return;
} else {
emit(
VolumesLoaded(
diskStatus: DiskStatus.fromVolumes(
serverVolumes,
volumesResult.data,
),
providerVolumes: volumesResult.data,
serverVolumesHashCode: Object.hashAll(serverVolumes),
),
);
}
}
Future<void> _resetState(
final VolumesServerReset event,
final Emitter<VolumesState> emit,
) async {
emit(VolumesInitial());
}
@override
void onChange(final Change<VolumesState> change) {
super.onChange(change);
}
@override
Future<void> close() async {
await _apiStatusSubscription.cancel();
await _apiDataSubscription.cancel();
await super.close();
}
Future<void> invalidateCache() async {
getIt<ApiConnectionRepository>().apiData.volumes.invalidate();
}
Future<void> _updateState(
final VolumesServerStateChanged event,
final Emitter<VolumesState> emit,
) async {
final serverVolumes = event.volumes;
final providerVolumes = state.providerVolumes;
emit(
VolumesLoaded(
diskStatus: DiskStatus.fromVolumes(
serverVolumes,
providerVolumes,
),
providerVolumes: providerVolumes,
serverVolumesHashCode: Object.hashAll(serverVolumes),
),
);
}
}

View File

@ -0,0 +1,30 @@
part of 'volumes_bloc.dart';
abstract class VolumesEvent extends Equatable {
const VolumesEvent();
}
class VolumesServerLoaded extends VolumesEvent {
const VolumesServerLoaded();
@override
List<Object> get props => [];
}
class VolumesServerReset extends VolumesEvent {
const VolumesServerReset();
@override
List<Object> get props => [];
}
class VolumesServerStateChanged extends VolumesEvent {
const VolumesServerStateChanged(
this.volumes,
);
final List<ServerDiskVolume> volumes;
@override
List<Object> get props => [volumes];
}

View File

@ -0,0 +1,97 @@
part of 'volumes_bloc.dart';
abstract class VolumesState extends Equatable {
const VolumesState({
required this.diskStatus,
required final serverVolumesHashCode,
this.providerVolumes = const [],
}) : _serverVolumesHashCode = serverVolumesHashCode;
final DiskStatus diskStatus;
final List<ServerProviderVolume> providerVolumes;
List<DiskVolume> get volumes => diskStatus.diskVolumes;
final int? _serverVolumesHashCode;
DiskVolume getVolume(final String volumeName) => volumes.firstWhere(
(final volume) => volume.name == volumeName,
orElse: () => DiskVolume(),
);
bool get isProviderVolumesLoaded => providerVolumes.isNotEmpty;
VolumesState copyWith({
required final int? serverVolumesHashCode,
final DiskStatus? diskStatus,
final List<ServerProviderVolume>? providerVolumes,
});
}
class VolumesInitial extends VolumesState {
VolumesInitial()
: super(
diskStatus: DiskStatus(),
serverVolumesHashCode: null,
);
@override
List<Object?> get props => [providerVolumes, _serverVolumesHashCode];
@override
VolumesInitial copyWith({
required final int? serverVolumesHashCode,
final DiskStatus? diskStatus,
final List<ServerProviderVolume>? providerVolumes,
}) =>
VolumesInitial();
}
class VolumesLoading extends VolumesState {
VolumesLoading({
super.serverVolumesHashCode,
final DiskStatus? diskStatus,
final List<ServerProviderVolume>? providerVolumes,
}) : super(
diskStatus: diskStatus ?? DiskStatus(),
providerVolumes: providerVolumes ?? const [],
);
@override
List<Object?> get props => [providerVolumes, _serverVolumesHashCode];
@override
VolumesLoading copyWith({
required final int? serverVolumesHashCode,
final DiskStatus? diskStatus,
final List<ServerProviderVolume>? providerVolumes,
}) =>
VolumesLoading(
diskStatus: diskStatus ?? this.diskStatus,
providerVolumes: providerVolumes ?? this.providerVolumes,
serverVolumesHashCode: serverVolumesHashCode ?? _serverVolumesHashCode!,
);
}
class VolumesLoaded extends VolumesState {
const VolumesLoaded({
required super.serverVolumesHashCode,
required super.diskStatus,
final List<ServerProviderVolume>? providerVolumes,
}) : super(
providerVolumes: providerVolumes ?? const [],
);
@override
List<Object?> get props => [providerVolumes, _serverVolumesHashCode];
@override
VolumesLoaded copyWith({
final DiskStatus? diskStatus,
final List<ServerProviderVolume>? providerVolumes,
final int? serverVolumesHashCode,
}) =>
VolumesLoaded(
diskStatus: diskStatus ?? this.diskStatus,
providerVolumes: providerVolumes ?? this.providerVolumes,
serverVolumesHashCode: serverVolumesHashCode ?? _serverVolumesHashCode!,
);
}

View File

@ -13,9 +13,9 @@ import 'package:selfprivacy/logic/providers/providers_controller.dart';
part 'provider_volume_state.dart'; part 'provider_volume_state.dart';
class ApiProviderVolumeCubit class ProviderVolumeCubit
extends ServerConnectionDependentCubit<ApiProviderVolumeState> { extends ServerConnectionDependentCubit<ProviderVolumeState> {
ApiProviderVolumeCubit() : super(const ApiProviderVolumeState.initial()); ProviderVolumeCubit() : super(const ProviderVolumeState.initial());
final ServerApi serverApi = ServerApi(); final ServerApi serverApi = ServerApi();
@override @override
@ -36,24 +36,24 @@ class ApiProviderVolumeCubit
} }
Future<void> refresh() async { Future<void> refresh() async {
emit(const ApiProviderVolumeState([], LoadingStatus.refreshing, false)); emit(const ProviderVolumeState([], LoadingStatus.refreshing, false));
unawaited(_refetch()); unawaited(_refetch());
} }
Future<void> _refetch() async { Future<void> _refetch() async {
if (ProvidersController.currentServerProvider == null) { if (ProvidersController.currentServerProvider == null) {
return emit(const ApiProviderVolumeState([], LoadingStatus.error, false)); return emit(const ProviderVolumeState([], LoadingStatus.error, false));
} }
final volumesResult = final volumesResult =
await ProvidersController.currentServerProvider!.getVolumes(); await ProvidersController.currentServerProvider!.getVolumes();
if (!volumesResult.success || volumesResult.data.isEmpty) { if (!volumesResult.success || volumesResult.data.isEmpty) {
return emit(const ApiProviderVolumeState([], LoadingStatus.error, false)); return emit(const ProviderVolumeState([], LoadingStatus.error, false));
} }
emit( emit(
ApiProviderVolumeState( ProviderVolumeState(
volumesResult.data, volumesResult.data,
LoadingStatus.success, LoadingStatus.success,
false, false,
@ -61,18 +61,18 @@ class ApiProviderVolumeCubit
); );
} }
Future<void> attachVolume(final DiskVolume volume) async { // Future<void> attachVolume(final DiskVolume volume) async {
final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!; // final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!;
await ProvidersController.currentServerProvider! // await ProvidersController.currentServerProvider!
.attachVolume(volume.providerVolume!, server.id); // .attachVolume(volume.providerVolume!, server.id);
unawaited(refresh()); // unawaited(refresh());
} // }
//
Future<void> detachVolume(final DiskVolume volume) async { // Future<void> detachVolume(final DiskVolume volume) async {
await ProvidersController.currentServerProvider! // await ProvidersController.currentServerProvider!
.detachVolume(volume.providerVolume!); // .detachVolume(volume.providerVolume!);
unawaited(refresh()); // unawaited(refresh());
} // }
Future<bool> resizeVolume( Future<bool> resizeVolume(
final DiskVolume volume, final DiskVolume volume,
@ -119,29 +119,29 @@ class ApiProviderVolumeCubit
return true; return true;
} }
Future<void> createVolume(final DiskSize size) async { // Future<void> createVolume(final DiskSize size) async {
final ServerProviderVolume? volume = (await ProvidersController // final ServerProviderVolume? volume = (await ProvidersController
.currentServerProvider! // .currentServerProvider!
.createVolume(size.gibibyte.toInt())) // .createVolume(size.gibibyte.toInt()))
.data; // .data;
//
final diskVolume = DiskVolume(providerVolume: volume); // final diskVolume = DiskVolume(providerVolume: volume);
await attachVolume(diskVolume); // await attachVolume(diskVolume);
//
await Future.delayed(const Duration(seconds: 10)); // await Future.delayed(const Duration(seconds: 10));
//
await ServerApi().mountVolume(volume!.name); // await ServerApi().mountVolume(volume!.name);
unawaited(refresh()); // unawaited(refresh());
} // }
//
Future<void> deleteVolume(final DiskVolume volume) async { // Future<void> deleteVolume(final DiskVolume volume) async {
await ProvidersController.currentServerProvider! // await ProvidersController.currentServerProvider!
.deleteVolume(volume.providerVolume!); // .deleteVolume(volume.providerVolume!);
unawaited(refresh()); // unawaited(refresh());
} // }
@override @override
void clear() { void clear() {
emit(const ApiProviderVolumeState.initial()); emit(const ProviderVolumeState.initial());
} }
} }

View File

@ -1,9 +1,9 @@
part of 'provider_volume_cubit.dart'; part of 'provider_volume_cubit.dart';
class ApiProviderVolumeState extends ServerInstallationDependendState { class ProviderVolumeState extends ServerInstallationDependendState {
const ApiProviderVolumeState(this._volumes, this.status, this.isResizing); const ProviderVolumeState(this._volumes, this.status, this.isResizing);
const ApiProviderVolumeState.initial() const ProviderVolumeState.initial()
: this(const [], LoadingStatus.uninitialized, false); : this(const [], LoadingStatus.uninitialized, false);
final List<ServerProviderVolume> _volumes; final List<ServerProviderVolume> _volumes;
final LoadingStatus status; final LoadingStatus status;
@ -11,12 +11,12 @@ class ApiProviderVolumeState extends ServerInstallationDependendState {
List<ServerProviderVolume> get volumes => _volumes; List<ServerProviderVolume> get volumes => _volumes;
ApiProviderVolumeState copyWith({ ProviderVolumeState copyWith({
final List<ServerProviderVolume>? volumes, final List<ServerProviderVolume>? volumes,
final LoadingStatus? status, final LoadingStatus? status,
final bool? isResizing, final bool? isResizing,
}) => }) =>
ApiProviderVolumeState( ProviderVolumeState(
volumes ?? _volumes, volumes ?? _volumes,
status ?? this.status, status ?? this.status,
isResizing ?? this.isResizing, isResizing ?? this.isResizing,

View File

@ -1,73 +0,0 @@
import 'dart:async';
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_connection_dependent/server_connection_dependent_cubit.dart';
import 'package:selfprivacy/logic/models/disk_status.dart';
import 'package:selfprivacy/logic/models/json/server_disk_volume.dart';
part 'server_volume_state.dart';
class ApiServerVolumeCubit
extends ServerConnectionDependentCubit<ApiServerVolumeState> {
ApiServerVolumeCubit(
this.providerVolumeCubit,
) : super(ApiServerVolumeState.initial()) {
// TODO: Remove this connection between cubits
_providerVolumeSubscription =
providerVolumeCubit.stream.listen(checkProviderVolumes);
}
final ServerApi serverApi = ServerApi();
@override
Future<void> load() async {
unawaited(reload());
}
late StreamSubscription<ApiProviderVolumeState> _providerVolumeSubscription;
final ApiProviderVolumeCubit providerVolumeCubit;
void checkProviderVolumes(final ApiProviderVolumeState state) {
emit(
ApiServerVolumeState(
this.state._volumes,
this.state.status,
DiskStatus.fromVolumes(this.state._volumes, state.volumes),
),
);
return;
}
Future<void> reload() async {
final volumes = await serverApi.getServerDiskVolumes();
var status = LoadingStatus.error;
if (volumes.isNotEmpty) {
status = LoadingStatus.success;
}
emit(
ApiServerVolumeState(
volumes,
status,
DiskStatus.fromVolumes(
volumes,
providerVolumeCubit.state.volumes,
),
),
);
}
@override
void clear() {
emit(ApiServerVolumeState.initial());
}
@override
Future<void> close() {
_providerVolumeSubscription.cancel();
return super.close();
}
}

View File

@ -1,38 +0,0 @@
part of 'server_volume_cubit.dart';
class ApiServerVolumeState extends ServerInstallationDependendState {
const ApiServerVolumeState(
this._volumes,
this.status,
this._diskStatus,
);
ApiServerVolumeState.initial()
: this(const [], LoadingStatus.uninitialized, DiskStatus());
final List<ServerDiskVolume> _volumes;
final DiskStatus _diskStatus;
final LoadingStatus status;
List<DiskVolume> get volumes => _diskStatus.diskVolumes;
DiskStatus get diskStatus => _diskStatus;
DiskVolume getVolume(final String volumeName) => volumes.firstWhere(
(final volume) => volume.name == volumeName,
orElse: () => DiskVolume(),
);
ApiServerVolumeState copyWith({
final List<ServerDiskVolume>? volumes,
final LoadingStatus? status,
final DiskStatus? diskStatus,
}) =>
ApiServerVolumeState(
volumes ?? _volumes,
status ?? this.status,
diskStatus ?? _diskStatus,
);
@override
List<Object?> get props => [_volumes, status];
}

View File

@ -8,6 +8,7 @@ import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.da
import 'package:selfprivacy/logic/models/backup.dart'; import 'package:selfprivacy/logic/models/backup.dart';
import 'package:selfprivacy/logic/models/hive/server_details.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/server_domain.dart';
import 'package:selfprivacy/logic/models/json/server_disk_volume.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart'; import 'package:selfprivacy/logic/models/json/server_job.dart';
import 'package:selfprivacy/logic/models/service.dart'; import 'package:selfprivacy/logic/models/service.dart';
@ -95,10 +96,11 @@ class ApiConnectionRepository {
_dataStream.add(_apiData); _dataStream.add(_apiData);
} }
_apiData.serverJobs.data = await api.getServerJobs(); _apiData.serverJobs.data = await _apiData.serverJobs.fetchData();
_apiData.backupConfig.data = await api.getBackupsConfiguration(); _apiData.backupConfig.data = await _apiData.backupConfig.fetchData();
_apiData.backups.data = await api.getBackups(); _apiData.backups.data = await _apiData.backups.fetchData();
_apiData.services.data = await api.getAllServices(); _apiData.services.data = await _apiData.services.fetchData();
_apiData.volumes.data = await _apiData.volumes.fetchData();
_dataStream.add(_apiData); _dataStream.add(_apiData);
connectionStatus = ConnectionStatus.connected; connectionStatus = ConnectionStatus.connected;
@ -136,6 +138,8 @@ class ApiConnectionRepository {
.refetchData(version, () => _dataStream.add(_apiData)); .refetchData(version, () => _dataStream.add(_apiData));
await _apiData.services await _apiData.services
.refetchData(version, () => _dataStream.add(_apiData)); .refetchData(version, () => _dataStream.add(_apiData));
await _apiData.volumes
.refetchData(version, () => _dataStream.add(_apiData));
} }
void emitData() { void emitData() {
@ -163,6 +167,9 @@ class ApiData {
services = ApiDataElement<List<Service>>( services = ApiDataElement<List<Service>>(
fetchData: () async => api.getAllServices(), fetchData: () async => api.getAllServices(),
requiredApiVersion: '>=2.4.3', requiredApiVersion: '>=2.4.3',
),
volumes = ApiDataElement<List<ServerDiskVolume>>(
fetchData: () async => api.getServerDiskVolumes(),
); );
ApiDataElement<List<ServerJob>> serverJobs; ApiDataElement<List<ServerJob>> serverJobs;
@ -170,6 +177,7 @@ class ApiData {
ApiDataElement<BackupConfiguration> backupConfig; ApiDataElement<BackupConfiguration> backupConfig;
ApiDataElement<List<Backup>> backups; ApiDataElement<List<Backup>> backups;
ApiDataElement<List<Service>> services; ApiDataElement<List<Service>> services;
ApiDataElement<List<ServerDiskVolume>> volumes;
} }
enum ConnectionStatus { enum ConnectionStatus {

View File

@ -9,7 +9,6 @@ class DiskVolume {
this.sizeUsed = const DiskSize(byte: 0), this.sizeUsed = const DiskSize(byte: 0),
this.root = false, this.root = false,
this.isResizable = false, this.isResizable = false,
this.serverDiskVolume,
this.providerVolume, this.providerVolume,
}); });
@ -27,7 +26,6 @@ class DiskVolume {
), ),
root: volume.root, root: volume.root,
isResizable: providerVolume != null, isResizable: providerVolume != null,
serverDiskVolume: volume,
providerVolume: providerVolume, providerVolume: providerVolume,
); );
@ -51,7 +49,6 @@ class DiskVolume {
String name; String name;
bool root; bool root;
bool isResizable; bool isResizable;
ServerDiskVolume? serverDiskVolume;
ServerProviderVolume? providerVolume; ServerProviderVolume? providerVolume;
/// from 0.0 to 1.0 /// from 0.0 to 1.0
@ -75,7 +72,6 @@ class DiskVolume {
name: name ?? this.name, name: name ?? this.name,
root: root ?? this.root, root: root ?? this.root,
isResizable: isResizable ?? this.isResizable, isResizable: isResizable ?? this.isResizable,
serverDiskVolume: serverDiskVolume ?? this.serverDiskVolume,
providerVolume: providerVolume ?? this.providerVolume, providerVolume: providerVolume ?? this.providerVolume,
); );
} }

View File

@ -27,6 +27,7 @@ class ServerHostingDetails {
@HiveField(2) @HiveField(2)
final DateTime? startTime; final DateTime? startTime;
// TODO: Check if it is still needed
@HiveField(4) @HiveField(4)
final ServerProviderVolume volume; final ServerProviderVolume volume;

View File

@ -1,12 +1,13 @@
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
part 'server_disk_volume.g.dart'; part 'server_disk_volume.g.dart';
@JsonSerializable() @JsonSerializable()
class ServerDiskVolume { class ServerDiskVolume extends Equatable {
factory ServerDiskVolume.fromJson(final Map<String, dynamic> json) => factory ServerDiskVolume.fromJson(final Map<String, dynamic> json) =>
_$ServerDiskVolumeFromJson(json); _$ServerDiskVolumeFromJson(json);
ServerDiskVolume({ const ServerDiskVolume({
required this.freeSpace, required this.freeSpace,
required this.model, required this.model,
required this.name, required this.name,
@ -25,4 +26,16 @@ class ServerDiskVolume {
final String totalSpace; final String totalSpace;
final String type; final String type;
final String usedSpace; final String usedSpace;
@override
List<Object?> get props => [
freeSpace,
model,
name,
root,
serial,
totalSpace,
type,
usedSpace,
];
} }

View File

@ -3,9 +3,9 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart'; import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart';
import 'package:selfprivacy/logic/bloc/volumes/volumes_bloc.dart';
import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart'; import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
import 'package:selfprivacy/logic/models/state_types.dart'; import 'package:selfprivacy/logic/models/state_types.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
@ -34,7 +34,7 @@ class _ProvidersPageState extends State<ProvidersPage> {
final DnsRecordsStatus dnsStatus = final DnsRecordsStatus dnsStatus =
context.watch<DnsRecordsCubit>().state.dnsState; context.watch<DnsRecordsCubit>().state.dnsState;
final diskStatus = context.watch<ApiServerVolumeCubit>().state.diskStatus; final diskStatus = context.watch<VolumesBloc>().state.diskStatus;
final ServerInstallationState appConfig = final ServerInstallationState appConfig =
context.watch<ServerInstallationCubit>().state; context.watch<ServerInstallationCubit>().state;

View File

@ -2,12 +2,12 @@ import 'package:auto_route/auto_route.dart';
import 'package:cubit_form/cubit_form.dart'; import 'package:cubit_form/cubit_form.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/bloc/volumes/volumes_bloc.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
import 'package:selfprivacy/logic/cubit/metrics/metrics_cubit.dart'; import 'package:selfprivacy/logic/cubit/metrics/metrics_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart'; import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/logic/models/job.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
@ -81,7 +81,7 @@ class _ServerDetailsScreenState extends State<ServerDetailsScreen>
heroSubtitle: 'server.description'.tr(), heroSubtitle: 'server.description'.tr(),
children: [ children: [
StorageCard( StorageCard(
diskStatus: context.watch<ApiServerVolumeCubit>().state.diskStatus, diskStatus: context.watch<VolumesBloc>().state.diskStatus,
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
const _ServerSettings(), const _ServerSettings(),

View File

@ -2,8 +2,8 @@ import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/logic/bloc/volumes/volumes_bloc.dart';
import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.dart'; import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
import 'package:selfprivacy/logic/models/disk_size.dart'; import 'package:selfprivacy/logic/models/disk_size.dart';
import 'package:selfprivacy/logic/models/disk_status.dart'; import 'package:selfprivacy/logic/models/disk_status.dart';
import 'package:selfprivacy/logic/models/price.dart'; import 'package:selfprivacy/logic/models/price.dart';
@ -59,7 +59,7 @@ class _ExtendingVolumePageState extends State<ExtendingVolumePage> {
@override @override
Widget build(final BuildContext context) => FutureBuilder( Widget build(final BuildContext context) => FutureBuilder(
future: context.read<ApiProviderVolumeCubit>().getPricePerGb(), future: context.read<ProviderVolumeCubit>().getPricePerGb(),
builder: ( builder: (
final BuildContext context, final BuildContext context,
final AsyncSnapshot<void> snapshot, final AsyncSnapshot<void> snapshot,
@ -92,7 +92,7 @@ class _ExtendingVolumePageState extends State<ExtendingVolumePage> {
} }
final isAlreadyResizing = final isAlreadyResizing =
context.watch<ApiProviderVolumeCubit>().state.isResizing; context.watch<ProviderVolumeCubit>().state.isResizing;
return BrandHeroScreen( return BrandHeroScreen(
hasBackButton: true, hasBackButton: true,
@ -163,12 +163,12 @@ class _ExtendingVolumePageState extends State<ExtendingVolumePage> {
), ),
actionButtonTitle: 'basis.continue'.tr(), actionButtonTitle: 'basis.continue'.tr(),
actionButtonOnPressed: () { actionButtonOnPressed: () {
context.read<ApiProviderVolumeCubit>().resizeVolume( context.read<ProviderVolumeCubit>().resizeVolume(
widget.diskVolumeToResize, widget.diskVolumeToResize,
DiskSize.fromGibibyte( DiskSize.fromGibibyte(
_currentSliderGbValue.truncate().toDouble(), _currentSliderGbValue.truncate().toDouble(),
), ),
context.read<ApiServerVolumeCubit>().reload, context.read<VolumesBloc>().invalidateCache,
); );
context.router.popUntilRoot(); context.router.popUntilRoot();
}, },

View File

@ -3,8 +3,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart'; import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
import 'package:selfprivacy/logic/bloc/volumes/volumes_bloc.dart';
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/logic/models/job.dart';
import 'package:selfprivacy/logic/models/service.dart'; import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/ui/components/cards/filled_card.dart'; import 'package:selfprivacy/ui/components/cards/filled_card.dart';
@ -113,8 +113,7 @@ class _ServicePageState extends State<ServicePage> {
onTap: () => context.pushRoute( onTap: () => context.pushRoute(
ServicesMigrationRoute( ServicesMigrationRoute(
services: [service], services: [service],
diskStatus: diskStatus: context.read<VolumesBloc>().state.diskStatus,
context.read<ApiServerVolumeCubit>().state.diskStatus,
), ),
), ),
leading: const Icon(Icons.drive_file_move_outlined), leading: const Icon(Icons.drive_file_move_outlined),
@ -127,7 +126,7 @@ class _ServicePageState extends State<ServicePage> {
namedArgs: { namedArgs: {
'usage': service.storageUsage.used.toString(), 'usage': service.storageUsage.used.toString(),
'volume': context 'volume': context
.read<ApiServerVolumeCubit>() .read<VolumesBloc>()
.state .state
.getVolume(service.storageUsage.volume ?? '') .getVolume(service.storageUsage.volume ?? '')
.displayName, .displayName,