diff --git a/assets/translations/en.json b/assets/translations/en.json index 172fce4e..1410a950 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -305,6 +305,10 @@ "extending_volume_description": "Resizing volume will allow you to store more data on your server without extending the server itself. Volume can only be extended: shrinking is not possible.", "extending_volume_price_info": "Price includes VAT and is estimated from pricing data provided by your server provider. Server will be rebooted after resizing.", "extending_volume_error": "Couldn't initialize volume extending.", + "extending_volume_started": "Volume extending started", + "extending_volume_provider_waiting": "Provider volume resized, waiting 10 seconds…", + "extending_volume_server_waiting": "Server volume resized, waiting 20 seconds…", + "extending_volume_rebooting": "Rebooting server…", "extending_volume_modal_description": "Upgrade to {} for {} plan per month.", "size": "Size", "price": "Price", @@ -634,4 +638,4 @@ "reset_onboarding_description": "Reset onboarding switch to show onboarding screen again", "cubit_statuses": "Cubit loading statuses" } -} \ No newline at end of file +} diff --git a/lib/config/bloc_config.dart b/lib/config/bloc_config.dart index 268048c5..8c635a80 100644 --- a/lib/config/bloc_config.dart +++ b/lib/config/bloc_config.dart @@ -9,7 +9,6 @@ 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/devices/devices_cubit.dart'; import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart'; -import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_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_installation/server_installation_cubit.dart'; @@ -34,7 +33,6 @@ class BlocAndProviderConfigState extends State { late final DnsRecordsCubit dnsRecordsCubit; late final RecoveryKeyCubit recoveryKeyCubit; late final ApiDevicesCubit apiDevicesCubit; - late final ProviderVolumeCubit apiVolumesCubit; late final ServerJobsBloc serverJobsBloc; late final ConnectionStatusBloc connectionStatusBloc; late final ServerDetailsCubit serverDetailsCubit; @@ -51,7 +49,6 @@ class BlocAndProviderConfigState extends State { dnsRecordsCubit = DnsRecordsCubit(); recoveryKeyCubit = RecoveryKeyCubit(); apiDevicesCubit = ApiDevicesCubit(); - apiVolumesCubit = ProviderVolumeCubit(); serverJobsBloc = ServerJobsBloc(); connectionStatusBloc = ConnectionStatusBloc(); serverDetailsCubit = ServerDetailsCubit(); @@ -98,9 +95,6 @@ class BlocAndProviderConfigState extends State { BlocProvider( create: (final _) => apiDevicesCubit, ), - BlocProvider( - create: (final _) => apiVolumesCubit, - ), BlocProvider( create: (final _) => serverJobsBloc, ), diff --git a/lib/logic/bloc/volumes/volumes_bloc.dart b/lib/logic/bloc/volumes/volumes_bloc.dart index ed365b3a..55fb15e7 100644 --- a/lib/logic/bloc/volumes/volumes_bloc.dart +++ b/lib/logic/bloc/volumes/volumes_bloc.dart @@ -5,6 +5,7 @@ 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_size.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'; @@ -28,6 +29,10 @@ class VolumesBloc extends Bloc { _updateState, transformer: droppable(), ); + on( + _resizeVolume, + transformer: droppable(), + ); final connectionRepository = getIt(); @@ -149,8 +154,21 @@ class VolumesBloc extends Bloc { ) async { final serverVolumes = event.volumes; final providerVolumes = state.providerVolumes; + if (state is VolumesLoading) { + emit( + VolumesLoaded( + diskStatus: DiskStatus.fromVolumes( + serverVolumes, + providerVolumes, + ), + providerVolumes: providerVolumes, + serverVolumesHashCode: Object.hashAll(serverVolumes), + ), + ); + return; + } emit( - VolumesLoaded( + state.copyWith( diskStatus: DiskStatus.fromVolumes( serverVolumes, providerVolumes, @@ -160,4 +178,69 @@ class VolumesBloc extends Bloc { ), ); } + + Future _resizeVolume( + final VolumeResize event, + final Emitter emit, + ) async { + if (state is! VolumesLoaded) { + return; + } + getIt().showSnackBar( + 'storage.extending_volume_started'.tr(), + ); + emit( + VolumesResizing( + serverVolumesHashCode: state._serverVolumesHashCode, + diskStatus: state.diskStatus, + providerVolumes: state.providerVolumes, + ), + ); + + final resizedResult = + await ProvidersController.currentServerProvider!.resizeVolume( + event.volume.providerVolume!, + event.newSize, + ); + + if (!resizedResult.success || !resizedResult.data) { + getIt().showSnackBar( + 'storage.extending_volume_error'.tr(), + ); + emit( + VolumesLoaded( + serverVolumesHashCode: state._serverVolumesHashCode, + diskStatus: state.diskStatus, + providerVolumes: state.providerVolumes, + ), + ); + return; + } + + getIt().showSnackBar( + 'storage.extending_volume_waiting'.tr(), + ); + + await Future.delayed(const Duration(seconds: 10)); + + await getIt().api.resizeVolume(event.volume.name); + getIt().showSnackBar( + 'storage.extending_volume_server_waiting'.tr(), + ); + + await Future.delayed(const Duration(seconds: 20)); + getIt().showSnackBar( + 'storage.extending_volume_rebooting'.tr(), + ); + + emit( + VolumesLoaded( + serverVolumesHashCode: state._serverVolumesHashCode, + diskStatus: state.diskStatus, + providerVolumes: state.providerVolumes, + ), + ); + + await getIt().api.reboot(); + } } diff --git a/lib/logic/bloc/volumes/volumes_event.dart b/lib/logic/bloc/volumes/volumes_event.dart index 5e65b07d..a4346ce5 100644 --- a/lib/logic/bloc/volumes/volumes_event.dart +++ b/lib/logic/bloc/volumes/volumes_event.dart @@ -28,3 +28,16 @@ class VolumesServerStateChanged extends VolumesEvent { @override List get props => [volumes]; } + +class VolumeResize extends VolumesEvent { + const VolumeResize( + this.volume, + this.newSize, + ); + + final DiskVolume volume; + final DiskSize newSize; + + @override + List get props => [volume, newSize]; +} diff --git a/lib/logic/bloc/volumes/volumes_state.dart b/lib/logic/bloc/volumes/volumes_state.dart index a9ba2d06..6fc4cb45 100644 --- a/lib/logic/bloc/volumes/volumes_state.dart +++ b/lib/logic/bloc/volumes/volumes_state.dart @@ -95,3 +95,28 @@ class VolumesLoaded extends VolumesState { serverVolumesHashCode: serverVolumesHashCode ?? _serverVolumesHashCode!, ); } + +class VolumesResizing extends VolumesState { + const VolumesResizing({ + required super.serverVolumesHashCode, + required super.diskStatus, + final List? providerVolumes, + }) : super( + providerVolumes: providerVolumes ?? const [], + ); + + @override + List get props => [providerVolumes, _serverVolumesHashCode]; + + @override + VolumesResizing copyWith({ + final DiskStatus? diskStatus, + final List? providerVolumes, + final int? serverVolumesHashCode, + }) => + VolumesResizing( + diskStatus: diskStatus ?? this.diskStatus, + providerVolumes: providerVolumes ?? this.providerVolumes, + serverVolumesHashCode: serverVolumesHashCode ?? _serverVolumesHashCode!, + ); +} diff --git a/lib/logic/cubit/provider_volumes/provider_volume_cubit.dart b/lib/logic/cubit/provider_volumes/provider_volume_cubit.dart deleted file mode 100644 index 3432fe7e..00000000 --- a/lib/logic/cubit/provider_volumes/provider_volume_cubit.dart +++ /dev/null @@ -1,147 +0,0 @@ -import 'dart:async'; - -import 'package:easy_localization/easy_localization.dart'; -import 'package:selfprivacy/config/get_it_config.dart'; -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/server_connection_dependent/server_connection_dependent_cubit.dart'; -import 'package:selfprivacy/logic/models/disk_size.dart'; -import 'package:selfprivacy/logic/models/disk_status.dart'; -import 'package:selfprivacy/logic/models/hive/server_details.dart'; -import 'package:selfprivacy/logic/models/price.dart'; -import 'package:selfprivacy/logic/providers/providers_controller.dart'; - -part 'provider_volume_state.dart'; - -class ProviderVolumeCubit - extends ServerConnectionDependentCubit { - ProviderVolumeCubit() : super(const ProviderVolumeState.initial()); - final ServerApi serverApi = ServerApi(); - - @override - Future load() async { - unawaited(_refetch()); - } - - Future getPricePerGb() async { - Price? price; - final pricingResult = - await ProvidersController.currentServerProvider!.getAdditionalPricing(); - if (pricingResult.data == null || !pricingResult.success) { - getIt().showSnackBar('server.pricing_error'.tr()); - return price; - } - price = pricingResult.data!.perVolumeGb; - return price; - } - - Future refresh() async { - emit(const ProviderVolumeState([], LoadingStatus.refreshing, false)); - unawaited(_refetch()); - } - - Future _refetch() async { - if (ProvidersController.currentServerProvider == null) { - return emit(const ProviderVolumeState([], LoadingStatus.error, false)); - } - - final volumesResult = - await ProvidersController.currentServerProvider!.getVolumes(); - - if (!volumesResult.success || volumesResult.data.isEmpty) { - return emit(const ProviderVolumeState([], LoadingStatus.error, false)); - } - - emit( - ProviderVolumeState( - volumesResult.data, - LoadingStatus.success, - false, - ), - ); - } - - // Future attachVolume(final DiskVolume volume) async { - // final ServerHostingDetails server = getIt().serverDetails!; - // await ProvidersController.currentServerProvider! - // .attachVolume(volume.providerVolume!, server.id); - // unawaited(refresh()); - // } - // - // Future detachVolume(final DiskVolume volume) async { - // await ProvidersController.currentServerProvider! - // .detachVolume(volume.providerVolume!); - // unawaited(refresh()); - // } - - Future resizeVolume( - final DiskVolume volume, - final DiskSize newSize, - final Function() callback, - ) async { - getIt().showSnackBar( - 'Starting resize', - ); - emit(state.copyWith(isResizing: true)); - final resizedResult = - await ProvidersController.currentServerProvider!.resizeVolume( - volume.providerVolume!, - newSize, - ); - - if (!resizedResult.success || !resizedResult.data) { - getIt().showSnackBar( - 'storage.extending_volume_error'.tr(), - ); - emit(state.copyWith(isResizing: false)); - return false; - } - - getIt().showSnackBar( - 'Provider volume resized, waiting 10 seconds', - ); - await Future.delayed(const Duration(seconds: 10)); - - await ServerApi().resizeVolume(volume.name); - getIt().showSnackBar( - 'Server volume resized, waiting 20 seconds', - ); - - await Future.delayed(const Duration(seconds: 20)); - getIt().showSnackBar( - 'Restarting server', - ); - - await refresh(); - emit(state.copyWith(isResizing: false)); - await callback(); - await serverApi.reboot(); - return true; - } - - // Future createVolume(final DiskSize size) async { - // final ServerProviderVolume? volume = (await ProvidersController - // .currentServerProvider! - // .createVolume(size.gibibyte.toInt())) - // .data; - // - // final diskVolume = DiskVolume(providerVolume: volume); - // await attachVolume(diskVolume); - // - // await Future.delayed(const Duration(seconds: 10)); - // - // await ServerApi().mountVolume(volume!.name); - // unawaited(refresh()); - // } - // - // Future deleteVolume(final DiskVolume volume) async { - // await ProvidersController.currentServerProvider! - // .deleteVolume(volume.providerVolume!); - // unawaited(refresh()); - // } - - @override - void clear() { - emit(const ProviderVolumeState.initial()); - } -} diff --git a/lib/logic/cubit/provider_volumes/provider_volume_state.dart b/lib/logic/cubit/provider_volumes/provider_volume_state.dart deleted file mode 100644 index 89603a5a..00000000 --- a/lib/logic/cubit/provider_volumes/provider_volume_state.dart +++ /dev/null @@ -1,27 +0,0 @@ -part of 'provider_volume_cubit.dart'; - -class ProviderVolumeState extends ServerInstallationDependendState { - const ProviderVolumeState(this._volumes, this.status, this.isResizing); - - const ProviderVolumeState.initial() - : this(const [], LoadingStatus.uninitialized, false); - final List _volumes; - final LoadingStatus status; - final bool isResizing; - - List get volumes => _volumes; - - ProviderVolumeState copyWith({ - final List? volumes, - final LoadingStatus? status, - final bool? isResizing, - }) => - ProviderVolumeState( - volumes ?? _volumes, - status ?? this.status, - isResizing ?? this.isResizing, - ); - - @override - List get props => [_volumes, status, isResizing]; -} diff --git a/lib/ui/pages/server_storage/extending_volume.dart b/lib/ui/pages/server_storage/extending_volume.dart index 60ac7379..e6961a95 100644 --- a/lib/ui/pages/server_storage/extending_volume.dart +++ b/lib/ui/pages/server_storage/extending_volume.dart @@ -3,7 +3,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.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/models/disk_size.dart'; import 'package:selfprivacy/logic/models/disk_status.dart'; import 'package:selfprivacy/logic/models/price.dart'; @@ -59,7 +58,7 @@ class _ExtendingVolumePageState extends State { @override Widget build(final BuildContext context) => FutureBuilder( - future: context.read().getPricePerGb(), + future: context.read().getPricePerGb(), builder: ( final BuildContext context, final AsyncSnapshot snapshot, @@ -92,7 +91,7 @@ class _ExtendingVolumePageState extends State { } final isAlreadyResizing = - context.watch().state.isResizing; + context.watch().state is VolumesResizing; return BrandHeroScreen( hasBackButton: true, @@ -163,12 +162,15 @@ class _ExtendingVolumePageState extends State { ), actionButtonTitle: 'basis.continue'.tr(), actionButtonOnPressed: () { - context.read().resizeVolume( - widget.diskVolumeToResize, - DiskSize.fromGibibyte( - _currentSliderGbValue.truncate().toDouble(), + context.read().add( + VolumeResize( + widget.diskVolumeToResize, + DiskSize.fromGibibyte( + _currentSliderGbValue + .truncate() + .toDouble(), + ), ), - context.read().invalidateCache, ); context.router.popUntilRoot(); },