From 710b9b53ddd2dbbbe8cbcd90a85f94c7b2d4edbf Mon Sep 17 00:00:00 2001 From: Inex Code Date: Fri, 9 Feb 2024 14:07:03 +0300 Subject: [PATCH] refactor: Replace ApiDevicesCubit with DevicesBloc --- lib/config/bloc_config.dart | 8 +- lib/logic/bloc/devices/devices_bloc.dart | 114 ++++++++++++++++++ lib/logic/bloc/devices/devices_event.dart | 23 ++++ lib/logic/bloc/devices/devices_state.dart | 53 ++++++++ .../bloc/recovery_key/recovery_key_bloc.dart | 38 +++--- .../bloc/recovery_key/recovery_key_event.dart | 20 --- .../bloc/recovery_key/recovery_key_state.dart | 11 -- lib/logic/cubit/devices/devices_cubit.dart | 75 ------------ lib/logic/cubit/devices/devices_state.dart | 34 ------ lib/logic/cubit/users/users_cubit.dart | 2 +- .../get_it/api_connection_repository.dart | 8 ++ lib/logic/models/json/api_token.dart | 8 +- lib/ui/pages/devices/devices.dart | 32 ++--- lib/ui/pages/devices/new_device.dart | 4 +- lib/ui/pages/recovery_key/recovery_key.dart | 44 ++++--- .../recovery_key/recovery_key_receiving.dart | 75 ++++-------- 16 files changed, 297 insertions(+), 252 deletions(-) create mode 100644 lib/logic/bloc/devices/devices_bloc.dart create mode 100644 lib/logic/bloc/devices/devices_event.dart create mode 100644 lib/logic/bloc/devices/devices_state.dart delete mode 100644 lib/logic/cubit/devices/devices_cubit.dart delete mode 100644 lib/logic/cubit/devices/devices_state.dart diff --git a/lib/config/bloc_config.dart b/lib/config/bloc_config.dart index 55fca0fe..881159a6 100644 --- a/lib/config/bloc_config.dart +++ b/lib/config/bloc_config.dart @@ -2,13 +2,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart'; import 'package:selfprivacy/logic/bloc/connection_status/connection_status_bloc.dart'; +import 'package:selfprivacy/logic/bloc/devices/devices_bloc.dart'; import 'package:selfprivacy/logic/bloc/recovery_key/recovery_key_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/volumes/volumes_bloc.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/devices/devices_cubit.dart'; import 'package:selfprivacy/logic/cubit/dns_records/dns_records_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'; @@ -32,7 +32,7 @@ class BlocAndProviderConfigState extends State { late final BackupsBloc backupsBloc; late final DnsRecordsCubit dnsRecordsCubit; late final RecoveryKeyBloc recoveryKeyBloc; - late final ApiDevicesCubit apiDevicesCubit; + late final DevicesBloc devicesBloc; late final ServerJobsBloc serverJobsBloc; late final ConnectionStatusBloc connectionStatusBloc; late final ServerDetailsCubit serverDetailsCubit; @@ -48,7 +48,7 @@ class BlocAndProviderConfigState extends State { backupsBloc = BackupsBloc(); dnsRecordsCubit = DnsRecordsCubit(); recoveryKeyBloc = RecoveryKeyBloc(); - apiDevicesCubit = ApiDevicesCubit(); + devicesBloc = DevicesBloc(); serverJobsBloc = ServerJobsBloc(); connectionStatusBloc = ConnectionStatusBloc(); serverDetailsCubit = ServerDetailsCubit(); @@ -93,7 +93,7 @@ class BlocAndProviderConfigState extends State { create: (final _) => recoveryKeyBloc, ), BlocProvider( - create: (final _) => apiDevicesCubit, + create: (final _) => devicesBloc, ), BlocProvider( create: (final _) => serverJobsBloc, diff --git a/lib/logic/bloc/devices/devices_bloc.dart b/lib/logic/bloc/devices/devices_bloc.dart new file mode 100644 index 00000000..e1d248c7 --- /dev/null +++ b/lib/logic/bloc/devices/devices_bloc.dart @@ -0,0 +1,114 @@ +import 'dart:async'; + +import 'package:bloc_concurrency/bloc_concurrency.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/api_maps/generic_result.dart'; +import 'package:selfprivacy/logic/models/json/api_token.dart'; + +part 'devices_event.dart'; +part 'devices_state.dart'; + +class DevicesBloc extends Bloc { + DevicesBloc() : super(DevicesInitial()) { + on( + _mapDevicesListChangedToState, + transformer: sequential(), + ); + on( + _mapDeleteDeviceToState, + transformer: sequential(), + ); + + final apiConnectionRepository = getIt(); + _apiDataSubscription = apiConnectionRepository.dataStream.listen( + (final ApiData apiData) { + print('============'); + print(apiData.devices.data); + add( + DevicesListChanged(apiData.devices.data), + ); + }, + ); + } + + StreamSubscription? _apiDataSubscription; + + Future _mapDevicesListChangedToState( + final DevicesListChanged event, + final Emitter emit, + ) async { + if (state is DevicesDeleting) { + return; + } + print(event.devices); + if (event.devices == null) { + emit(DevicesError()); + return; + } + emit(DevicesLoaded(devices: event.devices!)); + } + + Future refresh() async { + getIt().apiData.devices.invalidate(); + await getIt().reload(null); + } + + Future _mapDeleteDeviceToState( + final DeleteDevice event, + final Emitter emit, + ) async { + // Optimistically remove the device from the list + emit( + DevicesDeleting( + devices: state.devices + .where((final d) => d.name != event.device.name) + .toList(), + ), + ); + + final GenericResult response = await getIt() + .api + .deleteApiToken(event.device.name); + if (response.success) { + getIt().apiData.devices.invalidate(); + emit( + DevicesLoaded( + devices: state.devices + .where((final d) => d.name != event.device.name) + .toList(), + ), + ); + } else { + getIt() + .showSnackBar(response.message ?? 'Error deleting device'); + emit(DevicesLoaded(devices: state.devices)); + } + } + + Future getNewDeviceKey() async { + final GenericResult response = + await getIt().api.createDeviceToken(); + if (response.success) { + return response.data; + } else { + getIt().showSnackBar( + response.message ?? 'Error getting new device key', + ); + return null; + } + } + + @override + void onChange(final Change change) { + super.onChange(change); + print(change); + } + + @override + Future close() { + _apiDataSubscription?.cancel(); + return super.close(); + } +} diff --git a/lib/logic/bloc/devices/devices_event.dart b/lib/logic/bloc/devices/devices_event.dart new file mode 100644 index 00000000..655cca39 --- /dev/null +++ b/lib/logic/bloc/devices/devices_event.dart @@ -0,0 +1,23 @@ +part of 'devices_bloc.dart'; + +sealed class DevicesEvent extends Equatable { + const DevicesEvent(); +} + +class DevicesListChanged extends DevicesEvent { + const DevicesListChanged(this.devices); + + final List? devices; + + @override + List get props => []; +} + +class DeleteDevice extends DevicesEvent { + const DeleteDevice(this.device); + + final ApiToken device; + + @override + List get props => [device]; +} diff --git a/lib/logic/bloc/devices/devices_state.dart b/lib/logic/bloc/devices/devices_state.dart new file mode 100644 index 00000000..85e932df --- /dev/null +++ b/lib/logic/bloc/devices/devices_state.dart @@ -0,0 +1,53 @@ +part of 'devices_bloc.dart'; + +sealed class DevicesState extends Equatable { + DevicesState({ + required final List devices, + }) : _hashCode = devices.hashCode; + + final int _hashCode; + + List get _devices => + getIt().apiData.devices.data ?? const []; + + List get devices => _devices; + ApiToken get thisDevice => _devices.firstWhere( + (final device) => device.isCaller, + orElse: () => ApiToken( + name: 'Error fetching device', + isCaller: true, + date: DateTime.now(), + ), + ); + + List get otherDevices => + _devices.where((final device) => !device.isCaller).toList(); +} + +class DevicesInitial extends DevicesState { + DevicesInitial() : super(devices: const []); + + @override + List get props => [_hashCode]; +} + +class DevicesLoaded extends DevicesState { + DevicesLoaded({required super.devices}); + + @override + List get props => [_hashCode]; +} + +class DevicesError extends DevicesState { + DevicesError() : super(devices: const []); + + @override + List get props => [_hashCode]; +} + +class DevicesDeleting extends DevicesState { + DevicesDeleting({required super.devices}); + + @override + List get props => [_hashCode]; +} diff --git a/lib/logic/bloc/recovery_key/recovery_key_bloc.dart b/lib/logic/bloc/recovery_key/recovery_key_bloc.dart index 905d6594..eedcdaff 100644 --- a/lib/logic/bloc/recovery_key/recovery_key_bloc.dart +++ b/lib/logic/bloc/recovery_key/recovery_key_bloc.dart @@ -16,14 +16,6 @@ class RecoveryKeyBloc extends Bloc { _mapRecoveryKeyStatusChangedToState, transformer: sequential(), ); - on( - _mapCreateNewRecoveryKeyToState, - transformer: sequential(), - ); - on( - _mapRecoveryKeyStatusRefreshToState, - transformer: sequential(), - ); on( _mapRecoveryKeyStatusRefreshToState, transformer: droppable(), @@ -45,9 +37,6 @@ class RecoveryKeyBloc extends Bloc { final RecoveryKeyStatusChanged event, final Emitter emit, ) async { - if (state is RecoveryKeyCreating) { - return; - } if (event.recoveryKeyStatus == null) { emit(RecoveryKeyError()); return; @@ -55,20 +44,20 @@ class RecoveryKeyBloc extends Bloc { emit(RecoveryKeyLoaded(keyStatus: event.recoveryKeyStatus)); } - Future _mapCreateNewRecoveryKeyToState( - final CreateNewRecoveryKey event, - final Emitter emit, - ) async { - emit(RecoveryKeyCreating()); + Future generateRecoveryKey({ + final DateTime? expirationDate, + final int? numberOfUses, + }) async { final GenericResult response = - await getIt().api.generateRecoveryToken( - event.expirationDate, - event.numberOfUses, - ); + await getIt() + .api + .generateRecoveryToken(expirationDate, numberOfUses); if (response.success) { - emit(RecoveryKeyCreating(recoveryKey: response.data)); + getIt().apiData.recoveryKeyStatus.invalidate(); + unawaited(getIt().reload(null)); + return response.data; } else { - emit(RecoveryKeyCreating(error: response.message ?? 'Unknown error')); + throw GenerationError(response.message ?? 'Unknown error'); } } @@ -92,3 +81,8 @@ class RecoveryKeyBloc extends Bloc { return super.close(); } } + +class GenerationError extends Error { + GenerationError(this.message); + final String message; +} diff --git a/lib/logic/bloc/recovery_key/recovery_key_event.dart b/lib/logic/bloc/recovery_key/recovery_key_event.dart index 80104ada..bd54ff06 100644 --- a/lib/logic/bloc/recovery_key/recovery_key_event.dart +++ b/lib/logic/bloc/recovery_key/recovery_key_event.dart @@ -13,26 +13,6 @@ class RecoveryKeyStatusChanged extends RecoveryKeyEvent { List get props => [recoveryKeyStatus]; } -class CreateNewRecoveryKey extends RecoveryKeyEvent { - const CreateNewRecoveryKey({ - this.expirationDate, - this.numberOfUses, - }); - - final DateTime? expirationDate; - final int? numberOfUses; - - @override - List get props => [expirationDate, numberOfUses]; -} - -class ConsumedNewRecoveryKey extends RecoveryKeyEvent { - const ConsumedNewRecoveryKey(); - - @override - List get props => []; -} - class RecoveryKeyStatusRefresh extends RecoveryKeyEvent { const RecoveryKeyStatusRefresh(); diff --git a/lib/logic/bloc/recovery_key/recovery_key_state.dart b/lib/logic/bloc/recovery_key/recovery_key_state.dart index 1e22ac9f..5a5730b2 100644 --- a/lib/logic/bloc/recovery_key/recovery_key_state.dart +++ b/lib/logic/bloc/recovery_key/recovery_key_state.dart @@ -54,14 +54,3 @@ class RecoveryKeyError extends RecoveryKeyState { @override List get props => [_hashCode]; } - -class RecoveryKeyCreating extends RecoveryKeyState { - RecoveryKeyCreating({this.recoveryKey, this.error}) - : super(keyStatus: const RecoveryKeyStatus(exists: false, valid: false)); - - final String? recoveryKey; - final String? error; - - @override - List get props => [_hashCode, recoveryKey, error]; -} diff --git a/lib/logic/cubit/devices/devices_cubit.dart b/lib/logic/cubit/devices/devices_cubit.dart deleted file mode 100644 index 699764dd..00000000 --- a/lib/logic/cubit/devices/devices_cubit.dart +++ /dev/null @@ -1,75 +0,0 @@ -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/json/api_token.dart'; - -part 'devices_state.dart'; - -class ApiDevicesCubit extends ServerConnectionDependentCubit { - ApiDevicesCubit() : super(const ApiDevicesState.initial()); - - final ServerApi api = ServerApi(); - - @override - void load() async { - // if (serverInstallationCubit.state is ServerInstallationFinished) { - _refetch(); - // } - } - - Future refresh() async { - emit(ApiDevicesState([state.thisDevice], LoadingStatus.refreshing)); - _refetch(); - } - - void _refetch() async { - final List? devices = await _getApiTokens(); - if (devices != null) { - emit(ApiDevicesState(devices, LoadingStatus.success)); - } else { - emit(const ApiDevicesState([], LoadingStatus.error)); - } - } - - Future?> _getApiTokens() async { - final GenericResult> response = await api.getApiTokens(); - if (response.success) { - return response.data; - } else { - return null; - } - } - - Future deleteDevice(final ApiToken device) async { - final GenericResult response = await api.deleteApiToken(device.name); - if (response.success) { - emit( - ApiDevicesState( - state.devices.where((final d) => d.name != device.name).toList(), - LoadingStatus.success, - ), - ); - } else { - getIt() - .showSnackBar(response.message ?? 'Error deleting device'); - } - } - - Future getNewDeviceKey() async { - final GenericResult response = await api.createDeviceToken(); - if (response.success) { - return response.data; - } else { - getIt().showSnackBar( - response.message ?? 'Error getting new device key', - ); - return null; - } - } - - @override - void clear() { - emit(const ApiDevicesState.initial()); - } -} diff --git a/lib/logic/cubit/devices/devices_state.dart b/lib/logic/cubit/devices/devices_state.dart deleted file mode 100644 index 86fd53c2..00000000 --- a/lib/logic/cubit/devices/devices_state.dart +++ /dev/null @@ -1,34 +0,0 @@ -part of 'devices_cubit.dart'; - -class ApiDevicesState extends ServerInstallationDependendState { - const ApiDevicesState(this._devices, this.status); - - const ApiDevicesState.initial() : this(const [], LoadingStatus.uninitialized); - final List _devices; - final LoadingStatus status; - - List get devices => _devices; - ApiToken get thisDevice => _devices.firstWhere( - (final device) => device.isCaller, - orElse: () => ApiToken( - name: 'Error fetching device', - isCaller: true, - date: DateTime.now(), - ), - ); - - List get otherDevices => - _devices.where((final device) => !device.isCaller).toList(); - - ApiDevicesState copyWith({ - final List? devices, - final LoadingStatus? status, - }) => - ApiDevicesState( - devices ?? _devices, - status ?? this.status, - ); - - @override - List get props => [_devices]; -} diff --git a/lib/logic/cubit/users/users_cubit.dart b/lib/logic/cubit/users/users_cubit.dart index 1c242adb..30185e60 100644 --- a/lib/logic/cubit/users/users_cubit.dart +++ b/lib/logic/cubit/users/users_cubit.dart @@ -41,7 +41,7 @@ class UsersCubit extends ServerConnectionDependentCubit { } Future refresh() async { - if (getIt().connectionStatus != + if (getIt().connectionStatus == ConnectionStatus.nonexistent) { return; } diff --git a/lib/logic/get_it/api_connection_repository.dart b/lib/logic/get_it/api_connection_repository.dart index 6ed4ba79..c504e06a 100644 --- a/lib/logic/get_it/api_connection_repository.dart +++ b/lib/logic/get_it/api_connection_repository.dart @@ -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/hive/server_details.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/recovery_token_status.dart'; import 'package:selfprivacy/logic/models/json/server_disk_volume.dart'; import 'package:selfprivacy/logic/models/json/server_job.dart'; @@ -104,6 +105,7 @@ class ApiConnectionRepository { _apiData.volumes.data = await _apiData.volumes.fetchData(); _apiData.recoveryKeyStatus.data = await _apiData.recoveryKeyStatus.fetchData(); + _apiData.devices.data = await _apiData.devices.fetchData(); _dataStream.add(_apiData); connectionStatus = ConnectionStatus.connected; @@ -145,6 +147,8 @@ class ApiConnectionRepository { .refetchData(version, () => _dataStream.add(_apiData)); await _apiData.recoveryKeyStatus .refetchData(version, () => _dataStream.add(_apiData)); + await _apiData.devices + .refetchData(version, () => _dataStream.add(_apiData)); } void emitData() { @@ -181,6 +185,9 @@ class ApiData { recoveryKeyStatus = ApiDataElement( fetchData: () async => (await api.getRecoveryTokenStatus()).data, ttl: 300, + ), + devices = ApiDataElement>( + fetchData: () async => (await api.getApiTokens()).data, ); ApiDataElement> serverJobs; @@ -190,6 +197,7 @@ class ApiData { ApiDataElement> services; ApiDataElement> volumes; ApiDataElement recoveryKeyStatus; + ApiDataElement> devices; } enum ConnectionStatus { diff --git a/lib/logic/models/json/api_token.dart b/lib/logic/models/json/api_token.dart index f53f7f02..d5f4050e 100644 --- a/lib/logic/models/json/api_token.dart +++ b/lib/logic/models/json/api_token.dart @@ -1,13 +1,14 @@ +import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_api.graphql.dart'; part 'api_token.g.dart'; @JsonSerializable() -class ApiToken { +class ApiToken extends Equatable { factory ApiToken.fromJson(final Map json) => _$ApiTokenFromJson(json); - ApiToken({ + const ApiToken({ required this.name, required this.date, required this.isCaller, @@ -25,4 +26,7 @@ class ApiToken { final DateTime date; @JsonKey(name: 'is_caller') final bool isCaller; + + @override + List get props => [name, date, isCaller]; } diff --git a/lib/ui/pages/devices/devices.dart b/lib/ui/pages/devices/devices.dart index d39eb31d..4d92cd56 100644 --- a/lib/ui/pages/devices/devices.dart +++ b/lib/ui/pages/devices/devices.dart @@ -2,8 +2,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:selfprivacy/logic/common_enum/common_enum.dart'; -import 'package:selfprivacy/logic/cubit/devices/devices_cubit.dart'; +import 'package:selfprivacy/logic/bloc/devices/devices_bloc.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/models/json/api_token.dart'; import 'package:selfprivacy/ui/components/info_box/info_box.dart'; @@ -22,12 +21,11 @@ class DevicesScreen extends StatefulWidget { class _DevicesScreenState extends State { @override Widget build(final BuildContext context) { - final ApiDevicesState devicesStatus = - context.watch().state; + final DevicesState devicesStatus = context.watch().state; return RefreshIndicator( onRefresh: () async { - await context.read().refresh(); + await context.read().refresh(); }, child: BrandHeroScreen( heroTitle: 'devices.main_screen.header'.tr(), @@ -35,13 +33,13 @@ class _DevicesScreenState extends State { hasBackButton: true, hasFlashButton: false, children: [ - if (devicesStatus.status == LoadingStatus.uninitialized) ...[ + if (devicesStatus is DevicesInitial) ...[ const Center( heightFactor: 8, child: CircularProgressIndicator(), ), ], - if (devicesStatus.status != LoadingStatus.uninitialized) ...[ + if (devicesStatus is! DevicesInitial) ...[ _DevicesInfo( devicesStatus: devicesStatus, ), @@ -70,7 +68,7 @@ class _DevicesInfo extends StatelessWidget { required this.devicesStatus, }); - final ApiDevicesState devicesStatus; + final DevicesState devicesStatus; @override Widget build(final BuildContext context) => Column( @@ -82,7 +80,9 @@ class _DevicesInfo extends StatelessWidget { color: Theme.of(context).colorScheme.secondary, ), ), - _DeviceTile(device: devicesStatus.thisDevice), + _DeviceTile( + device: devicesStatus.thisDevice, + ), const Divider(height: 1), const SizedBox(height: 16), Text( @@ -91,14 +91,18 @@ class _DevicesInfo extends StatelessWidget { color: Theme.of(context).colorScheme.secondary, ), ), - if (devicesStatus.status == LoadingStatus.refreshing) ...[ + if (devicesStatus is DevicesDeleting) ...[ const Center( heightFactor: 4, child: CircularProgressIndicator(), ), ], - ...devicesStatus.otherDevices - .map((final device) => _DeviceTile(device: device)), + if (devicesStatus is! DevicesDeleting) + ...devicesStatus.otherDevices.map( + (final device) => _DeviceTile( + device: device, + ), + ), ], ); } @@ -110,7 +114,7 @@ class _DeviceTile extends StatelessWidget { @override Widget build(final BuildContext context) => ListTile( - contentPadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 4), + contentPadding: EdgeInsets.zero, title: Text(device.name), subtitle: Text( 'devices.main_screen.access_granted_on' @@ -161,7 +165,7 @@ class _DeviceTile extends StatelessWidget { TextButton( child: Text('devices.revoke_device_alert.yes'.tr()), onPressed: () { - context.read().deleteDevice(device); + context.read().add(DeleteDevice(device)); Navigator.of(context).pop(); }, ), diff --git a/lib/ui/pages/devices/new_device.dart b/lib/ui/pages/devices/new_device.dart index 9a64fa72..6f343b22 100644 --- a/lib/ui/pages/devices/new_device.dart +++ b/lib/ui/pages/devices/new_device.dart @@ -1,7 +1,7 @@ import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:selfprivacy/logic/cubit/devices/devices_cubit.dart'; +import 'package:selfprivacy/logic/bloc/devices/devices_bloc.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; @@ -17,7 +17,7 @@ class NewDeviceScreen extends StatelessWidget { hasFlashButton: false, children: [ FutureBuilder( - future: context.read().getNewDeviceKey(), + future: context.read().getNewDeviceKey(), builder: ( final BuildContext context, final AsyncSnapshot snapshot, diff --git a/lib/ui/pages/recovery_key/recovery_key.dart b/lib/ui/pages/recovery_key/recovery_key.dart index a8a47593..b211bad7 100644 --- a/lib/ui/pages/recovery_key/recovery_key.dart +++ b/lib/ui/pages/recovery_key/recovery_key.dart @@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/bloc/recovery_key/recovery_key_bloc.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; @@ -41,7 +42,6 @@ class _RecoveryKeyPageState extends State { break; case RecoveryKeyInitial(): case RecoveryKeyError(): - case RecoveryKeyCreating(): subtitle = 'recovery_key.key_connection_error'.tr(); widgets = [ const Icon(Icons.sentiment_dissatisfied_outlined), @@ -234,24 +234,34 @@ class _RecoveryKeyConfigurationState extends State { setState(() { _isLoading = true; }); - context.read().add( - CreateNewRecoveryKey( - expirationDate: _isExpirationToggled ? _selectedDate : null, - numberOfUses: - _isAmountToggled ? int.tryParse(_amountController.text) : null, - ), - ); - if (!mounted) { + try { + final String token = + await context.read().generateRecoveryKey( + numberOfUses: _isAmountToggled + ? int.tryParse(_amountController.text) + : null, + expirationDate: _isExpirationToggled ? _selectedDate : null, + ); + if (!mounted) { + return; + } + setState(() { + _isLoading = false; + }); + await Navigator.of(context).push( + materialRoute( + RecoveryKeyReceiving(recoveryKey: token), + ), + ); + } on GenerationError catch (e) { + setState(() { + _isLoading = false; + }); + getIt().showSnackBar( + 'recovery_key.generation_error'.tr(args: [e.message]), + ); return; } - setState(() { - _isLoading = false; - }); - await Navigator.of(context).push( - materialRoute( - const RecoveryKeyReceiving(), - ), - ); } void _updateErrorStatuses() { diff --git a/lib/ui/pages/recovery_key/recovery_key_receiving.dart b/lib/ui/pages/recovery_key/recovery_key_receiving.dart index 8167ac1e..e7983bb3 100644 --- a/lib/ui/pages/recovery_key/recovery_key_receiving.dart +++ b/lib/ui/pages/recovery_key/recovery_key_receiving.dart @@ -1,36 +1,23 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:selfprivacy/logic/bloc/recovery_key/recovery_key_bloc.dart'; import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; import 'package:selfprivacy/ui/components/info_box/info_box.dart'; import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; class RecoveryKeyReceiving extends StatelessWidget { - const RecoveryKeyReceiving({super.key}); + const RecoveryKeyReceiving({required this.recoveryKey, super.key}); + + final String recoveryKey; @override - Widget build(final BuildContext context) { - final recoveryKeyState = context.watch().state; - - final String? recoveryKey = recoveryKeyState is RecoveryKeyCreating - ? recoveryKeyState.recoveryKey - : null; - - final String? error = - recoveryKeyState is RecoveryKeyCreating ? recoveryKeyState.error : null; - - return BrandHeroScreen( - heroTitle: 'recovery_key.key_main_header'.tr(), - heroSubtitle: 'recovery_key.key_receiving_description'.tr(), - hasBackButton: true, - hasFlashButton: false, - children: [ - const Divider(), - const SizedBox(height: 16), - if (recoveryKey == null && error == null) - const Center(child: CircularProgressIndicator()), - if (recoveryKey != null) + Widget build(final BuildContext context) => BrandHeroScreen( + heroTitle: 'recovery_key.key_main_header'.tr(), + heroSubtitle: 'recovery_key.key_receiving_description'.tr(), + hasBackButton: false, + hasFlashButton: false, + children: [ + const Divider(), + const SizedBox(height: 16), Text( recoveryKey, style: Theme.of(context).textTheme.bodyLarge!.copyWith( @@ -39,31 +26,19 @@ class RecoveryKeyReceiving extends StatelessWidget { ), textAlign: TextAlign.center, ), - if (error != null) - Text( - 'recovery_key.generation_error'.tr(args: [error]), - style: Theme.of(context).textTheme.bodyLarge!.copyWith( - fontSize: 24, - fontFamily: 'RobotoMono', - color: Theme.of(context).colorScheme.error, - ), - textAlign: TextAlign.center, + const SizedBox(height: 16), + const Divider(), + const SizedBox(height: 16), + InfoBox( + text: 'recovery_key.key_receiving_info'.tr(), ), - const SizedBox(height: 16), - const Divider(), - const SizedBox(height: 16), - InfoBox( - text: 'recovery_key.key_receiving_info'.tr(), - ), - const SizedBox(height: 16), - BrandButton.filled( - child: Text('recovery_key.key_receiving_done'.tr()), - onPressed: () { - context.read().add(const ConsumedNewRecoveryKey()); - Navigator.of(context).popUntil((final route) => route.isFirst); - }, - ), - ], - ); - } + const SizedBox(height: 16), + BrandButton.filled( + child: Text('recovery_key.key_receiving_done'.tr()), + onPressed: () { + Navigator.of(context).popUntil((final route) => route.isFirst); + }, + ), + ], + ); }