diff --git a/analysis_options.yaml b/analysis_options.yaml index 3ab00ee3..9d16cb20 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -52,7 +52,6 @@ linter: sized_box_shrink_expand: true sort_constructors_first: true unnecessary_await_in_return: true - unnecessary_lambdas: true unnecessary_null_checks: true unnecessary_parenthesis: true use_enums: true diff --git a/lib/logic/api_maps/backblaze.dart b/lib/logic/api_maps/backblaze.dart index f6626a26..8d827e78 100644 --- a/lib/logic/api_maps/backblaze.dart +++ b/lib/logic/api_maps/backblaze.dart @@ -73,20 +73,25 @@ class BackblazeApi extends ApiMap { Future isValid(final String encodedApiKey) async { final Dio client = await getClient(); - final Response response = await client.get( - 'b2_authorize_account', - options: Options(headers: {'Authorization': 'Basic $encodedApiKey'}), - ); - close(client); - if (response.statusCode == HttpStatus.ok) { - if (response.data['allowed']['capabilities'].contains('listBuckets')) { - return true; + try { + final Response response = await client.get( + 'b2_authorize_account', + options: Options(headers: {'Authorization': 'Basic $encodedApiKey'}), + ); + if (response.statusCode == HttpStatus.ok) { + if (response.data['allowed']['capabilities'].contains('listBuckets')) { + return true; + } + return false; + } else if (response.statusCode == HttpStatus.unauthorized) { + return false; + } else { + throw Exception('code: ${response.statusCode}'); } + } on DioError { return false; - } else if (response.statusCode == HttpStatus.unauthorized) { - return false; - } else { - throw Exception('code: ${response.statusCode}'); + } finally { + close(client); } } diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index bdd544f7..67a0739c 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -447,7 +447,8 @@ class ServerApi extends ApiMap { final Dio client = await getClient(); try { response = await client.get('/services/restic/backup/list'); - backups = response.data.map(Backup.fromJson).toList(); + backups = + response.data.map((final e) => Backup.fromJson(e)).toList(); } on DioError catch (e) { print(e.message); } catch (e) { @@ -846,7 +847,9 @@ class ServerApi extends ApiMap { return ApiResponse( statusCode: code, data: (response.data != null) - ? response.data.map(ApiToken.fromJson).toList() + ? response.data + .map((final e) => ApiToken.fromJson(e)) + .toList() : [], ); } diff --git a/lib/logic/cubit/devices/devices_cubit.dart b/lib/logic/cubit/devices/devices_cubit.dart index 01f17a7d..f0380635 100644 --- a/lib/logic/cubit/devices/devices_cubit.dart +++ b/lib/logic/cubit/devices/devices_cubit.dart @@ -16,7 +16,6 @@ class ApiDevicesCubit @override void load() async { if (serverInstallationCubit.state is ServerInstallationFinished) { - emit(const ApiDevicesState([], LoadingStatus.refreshing)); final List? devices = await _getApiTokens(); if (devices != null) { emit(ApiDevicesState(devices, LoadingStatus.success)); diff --git a/lib/logic/cubit/recovery_key/recovery_key_state.dart b/lib/logic/cubit/recovery_key/recovery_key_state.dart index 1b764298..b35ae9a3 100644 --- a/lib/logic/cubit/recovery_key/recovery_key_state.dart +++ b/lib/logic/cubit/recovery_key/recovery_key_state.dart @@ -17,6 +17,14 @@ class RecoveryKeyState extends ServerInstallationDependendState { DateTime? get generatedAt => _status.date; DateTime? get expiresAt => _status.expiration; int? get usesLeft => _status.usesLeft; + + bool get isInvalidBecauseExpired => + _status.expiration != null && + _status.expiration!.isBefore(DateTime.now()); + + bool get isInvalidBecauseUsed => + _status.usesLeft != null && _status.usesLeft == 0; + @override List get props => [_status, loadingStatus]; diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index ea6948ec..5dc414f5 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -38,6 +38,7 @@ class ServerAuthorizationException implements Exception { class ServerInstallationRepository { Box box = Hive.box(BNames.serverInstallationBox); + Box usersBox = Hive.box(BNames.usersBox); Future load() async { final String? hetznerToken = getIt().hetznerKey; @@ -123,6 +124,7 @@ class ServerInstallationRepository { void clearAppConfig() { box.clear(); + usersBox.clear(); } Future startServer( @@ -526,7 +528,7 @@ class ServerInstallationRepository { ), provider: ServerProvider.unknown, id: 0, - ip4: '', + ip4: serverIp, startTime: null, createTime: null, ); @@ -668,7 +670,6 @@ class ServerInstallationRepository { BNames.isServerResetedSecondTime, BNames.hasFinalChecked, BNames.isLoading, - BNames.isRecoveringServer, ]); getIt().init(); } diff --git a/lib/ui/pages/devices/devices.dart b/lib/ui/pages/devices/devices.dart index 5883064c..ad48096e 100644 --- a/lib/ui/pages/devices/devices.dart +++ b/lib/ui/pages/devices/devices.dart @@ -1,6 +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/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/cubit/devices/devices_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/models/json/api_token.dart'; @@ -30,6 +31,58 @@ class _DevicesScreenState extends State { heroSubtitle: 'devices.main_screen.description'.tr(), hasBackButton: true, hasFlashButton: false, + children: [ + if (devicesStatus.status == LoadingStatus.uninitialized) ...[ + const Center( + heightFactor: 8, + child: CircularProgressIndicator(), + ), + ], + if (devicesStatus.status != LoadingStatus.uninitialized) ...[ + _DevicesInfo( + devicesStatus: devicesStatus, + ), + const SizedBox(height: 16), + OutlinedButton( + onPressed: () => Navigator.of(context) + .push(materialRoute(const NewDeviceScreen())), + child: Text('devices.main_screen.authorize_new_device'.tr()), + ), + const SizedBox(height: 16), + const Divider(height: 1), + const SizedBox(height: 16), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon( + Icons.info_outline, + color: Theme.of(context).colorScheme.onBackground, + ), + const SizedBox(height: 16), + Text( + 'devices.main_screen.tip'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + ], + const SizedBox(height: 24), + ], + ), + ); + } +} + +class _DevicesInfo extends StatelessWidget { + const _DevicesInfo({ + required this.devicesStatus, + }); + + final ApiDevicesState devicesStatus; + + @override + Widget build(final BuildContext context) => Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'devices.main_screen.this_device'.tr(), @@ -49,34 +102,8 @@ class _DevicesScreenState extends State { ...devicesStatus.otherDevices .map((final device) => _DeviceTile(device: device)) .toList(), - const SizedBox(height: 16), - OutlinedButton( - onPressed: () => Navigator.of(context) - .push(materialRoute(const NewDeviceScreen())), - child: Text('devices.main_screen.authorize_new_device'.tr()), - ), - const SizedBox(height: 16), - const Divider(height: 1), - const SizedBox(height: 16), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Icon( - Icons.info_outline, - color: Theme.of(context).colorScheme.onBackground, - ), - const SizedBox(height: 16), - Text( - 'devices.main_screen.tip'.tr(), - style: Theme.of(context).textTheme.bodyMedium!, - ), - ], - ), - const SizedBox(height: 24), ], - ), - ); - } + ); } class _DeviceTile extends StatelessWidget { diff --git a/lib/ui/pages/recovery_key/recovery_key.dart b/lib/ui/pages/recovery_key/recovery_key.dart index d7167a6f..0b506d88 100644 --- a/lib/ui/pages/recovery_key/recovery_key.dart +++ b/lib/ui/pages/recovery_key/recovery_key.dart @@ -151,7 +151,7 @@ class RecoveryKeyInformation extends StatelessWidget { @override Widget build(final BuildContext context) { const EdgeInsets padding = - EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0); + EdgeInsets.symmetric(vertical: 8.0, horizontal: 8.0); return SizedBox( width: double.infinity, child: Column( @@ -164,6 +164,11 @@ class RecoveryKeyInformation extends StatelessWidget { 'recovery_key.key_valid_until'.tr( args: [DateFormat.yMMMMd().format(state.expiresAt!)], ), + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: state.isInvalidBecauseExpired + ? Theme.of(context).colorScheme.error + : Theme.of(context).colorScheme.onBackground, + ), ), ), if (state.usesLeft != null) @@ -173,6 +178,11 @@ class RecoveryKeyInformation extends StatelessWidget { 'recovery_key.key_valid_for'.tr( args: [state.usesLeft!.toString()], ), + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: state.isInvalidBecauseUsed + ? Theme.of(context).colorScheme.error + : Theme.of(context).colorScheme.onBackground, + ), ), ), if (state.generatedAt != null) @@ -182,7 +192,6 @@ class RecoveryKeyInformation extends StatelessWidget { 'recovery_key.key_creation_date'.tr( args: [DateFormat.yMMMMd().format(state.generatedAt!)], ), - textAlign: TextAlign.start, ), ), ],