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_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'; 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 { VolumesBloc() : super(VolumesInitial()) { on( _loadState, transformer: droppable(), ); on( _resetState, transformer: droppable(), ); on( _updateState, transformer: droppable(), ); on( _resizeVolume, transformer: droppable(), ); final connectionRepository = getIt(); _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 getPricePerGb() async { if (ProvidersController.currentServerProvider == null) { return null; } 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 _loadState( final VolumesServerLoaded event, final Emitter 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().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 _resetState( final VolumesServerReset event, final Emitter emit, ) async { emit(VolumesInitial()); } @override void onChange(final Change change) { super.onChange(change); } @override Future close() async { await _apiStatusSubscription.cancel(); await _apiDataSubscription.cancel(); await super.close(); } Future invalidateCache() async { getIt().apiData.volumes.invalidate(); } Future _updateState( final VolumesServerStateChanged event, final Emitter emit, ) 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( state.copyWith( diskStatus: DiskStatus.fromVolumes( serverVolumes, providerVolumes, ), providerVolumes: providerVolumes, serverVolumesHashCode: Object.hashAll(serverVolumes), ), ); } 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(); } }