mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-03-11 17:24:09 +00:00
refactor: Replace RecoveryKeyCubit with RecoveryKeyBloc
This commit is contained in:
parent
1daf957245
commit
3a525f0d11
10 changed files with 294 additions and 205 deletions
lib
config
logic
bloc/recovery_key
cubit/recovery_key
get_it
ui/pages
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
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/connection_status/connection_status_bloc.dart';
|
import 'package:selfprivacy/logic/bloc/connection_status/connection_status_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/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/bloc/volumes/volumes_bloc.dart';
|
||||||
|
@ -9,7 +10,6 @@ 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/devices/devices_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/dns_records/dns_records_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/support_system/support_system_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart';
|
||||||
|
@ -31,7 +31,7 @@ class BlocAndProviderConfigState extends State<BlocAndProviderConfig> {
|
||||||
late final ServicesBloc servicesBloc;
|
late final ServicesBloc servicesBloc;
|
||||||
late final BackupsBloc backupsBloc;
|
late final BackupsBloc backupsBloc;
|
||||||
late final DnsRecordsCubit dnsRecordsCubit;
|
late final DnsRecordsCubit dnsRecordsCubit;
|
||||||
late final RecoveryKeyCubit recoveryKeyCubit;
|
late final RecoveryKeyBloc recoveryKeyBloc;
|
||||||
late final ApiDevicesCubit apiDevicesCubit;
|
late final ApiDevicesCubit apiDevicesCubit;
|
||||||
late final ServerJobsBloc serverJobsBloc;
|
late final ServerJobsBloc serverJobsBloc;
|
||||||
late final ConnectionStatusBloc connectionStatusBloc;
|
late final ConnectionStatusBloc connectionStatusBloc;
|
||||||
|
@ -47,7 +47,7 @@ class BlocAndProviderConfigState extends State<BlocAndProviderConfig> {
|
||||||
servicesBloc = ServicesBloc();
|
servicesBloc = ServicesBloc();
|
||||||
backupsBloc = BackupsBloc();
|
backupsBloc = BackupsBloc();
|
||||||
dnsRecordsCubit = DnsRecordsCubit();
|
dnsRecordsCubit = DnsRecordsCubit();
|
||||||
recoveryKeyCubit = RecoveryKeyCubit();
|
recoveryKeyBloc = RecoveryKeyBloc();
|
||||||
apiDevicesCubit = ApiDevicesCubit();
|
apiDevicesCubit = ApiDevicesCubit();
|
||||||
serverJobsBloc = ServerJobsBloc();
|
serverJobsBloc = ServerJobsBloc();
|
||||||
connectionStatusBloc = ConnectionStatusBloc();
|
connectionStatusBloc = ConnectionStatusBloc();
|
||||||
|
@ -90,7 +90,7 @@ class BlocAndProviderConfigState extends State<BlocAndProviderConfig> {
|
||||||
create: (final _) => dnsRecordsCubit,
|
create: (final _) => dnsRecordsCubit,
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (final _) => recoveryKeyCubit,
|
create: (final _) => recoveryKeyBloc,
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (final _) => apiDevicesCubit,
|
create: (final _) => apiDevicesCubit,
|
||||||
|
|
94
lib/logic/bloc/recovery_key/recovery_key_bloc.dart
Normal file
94
lib/logic/bloc/recovery_key/recovery_key_bloc.dart
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
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/recovery_token_status.dart';
|
||||||
|
|
||||||
|
part 'recovery_key_event.dart';
|
||||||
|
part 'recovery_key_state.dart';
|
||||||
|
|
||||||
|
class RecoveryKeyBloc extends Bloc<RecoveryKeyEvent, RecoveryKeyState> {
|
||||||
|
RecoveryKeyBloc() : super(RecoveryKeyInitial()) {
|
||||||
|
on<RecoveryKeyStatusChanged>(
|
||||||
|
_mapRecoveryKeyStatusChangedToState,
|
||||||
|
transformer: sequential(),
|
||||||
|
);
|
||||||
|
on<CreateNewRecoveryKey>(
|
||||||
|
_mapCreateNewRecoveryKeyToState,
|
||||||
|
transformer: sequential(),
|
||||||
|
);
|
||||||
|
on<ConsumedNewRecoveryKey>(
|
||||||
|
_mapRecoveryKeyStatusRefreshToState,
|
||||||
|
transformer: sequential(),
|
||||||
|
);
|
||||||
|
on<RecoveryKeyStatusRefresh>(
|
||||||
|
_mapRecoveryKeyStatusRefreshToState,
|
||||||
|
transformer: droppable(),
|
||||||
|
);
|
||||||
|
|
||||||
|
final apiConnectionRepository = getIt<ApiConnectionRepository>();
|
||||||
|
_apiDataSubscription = apiConnectionRepository.dataStream.listen(
|
||||||
|
(final ApiData apiData) {
|
||||||
|
add(
|
||||||
|
RecoveryKeyStatusChanged(apiData.recoveryKeyStatus.data),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamSubscription? _apiDataSubscription;
|
||||||
|
|
||||||
|
Future<void> _mapRecoveryKeyStatusChangedToState(
|
||||||
|
final RecoveryKeyStatusChanged event,
|
||||||
|
final Emitter<RecoveryKeyState> emit,
|
||||||
|
) async {
|
||||||
|
if (state is RecoveryKeyCreating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.recoveryKeyStatus == null) {
|
||||||
|
emit(RecoveryKeyError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit(RecoveryKeyLoaded(keyStatus: event.recoveryKeyStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _mapCreateNewRecoveryKeyToState(
|
||||||
|
final CreateNewRecoveryKey event,
|
||||||
|
final Emitter<RecoveryKeyState> emit,
|
||||||
|
) async {
|
||||||
|
emit(RecoveryKeyCreating());
|
||||||
|
final GenericResult<String> response =
|
||||||
|
await getIt<ApiConnectionRepository>().api.generateRecoveryToken(
|
||||||
|
event.expirationDate,
|
||||||
|
event.numberOfUses,
|
||||||
|
);
|
||||||
|
if (response.success) {
|
||||||
|
emit(RecoveryKeyCreating(recoveryKey: response.data));
|
||||||
|
} else {
|
||||||
|
emit(RecoveryKeyCreating(error: response.message ?? 'Unknown error'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _mapRecoveryKeyStatusRefreshToState(
|
||||||
|
final RecoveryKeyEvent event,
|
||||||
|
final Emitter<RecoveryKeyState> emit,
|
||||||
|
) async {
|
||||||
|
emit(RecoveryKeyRefreshing(keyStatus: state._status));
|
||||||
|
getIt<ApiConnectionRepository>().apiData.recoveryKeyStatus.invalidate();
|
||||||
|
await getIt<ApiConnectionRepository>().reload(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onChange(final Change<RecoveryKeyState> change) {
|
||||||
|
super.onChange(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
_apiDataSubscription?.cancel();
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
}
|
41
lib/logic/bloc/recovery_key/recovery_key_event.dart
Normal file
41
lib/logic/bloc/recovery_key/recovery_key_event.dart
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
part of 'recovery_key_bloc.dart';
|
||||||
|
|
||||||
|
sealed class RecoveryKeyEvent extends Equatable {
|
||||||
|
const RecoveryKeyEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class RecoveryKeyStatusChanged extends RecoveryKeyEvent {
|
||||||
|
const RecoveryKeyStatusChanged(this.recoveryKeyStatus);
|
||||||
|
|
||||||
|
final RecoveryKeyStatus? recoveryKeyStatus;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [recoveryKeyStatus];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CreateNewRecoveryKey extends RecoveryKeyEvent {
|
||||||
|
const CreateNewRecoveryKey({
|
||||||
|
this.expirationDate,
|
||||||
|
this.numberOfUses,
|
||||||
|
});
|
||||||
|
|
||||||
|
final DateTime? expirationDate;
|
||||||
|
final int? numberOfUses;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [expirationDate, numberOfUses];
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConsumedNewRecoveryKey extends RecoveryKeyEvent {
|
||||||
|
const ConsumedNewRecoveryKey();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class RecoveryKeyStatusRefresh extends RecoveryKeyEvent {
|
||||||
|
const RecoveryKeyStatusRefresh();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [];
|
||||||
|
}
|
67
lib/logic/bloc/recovery_key/recovery_key_state.dart
Normal file
67
lib/logic/bloc/recovery_key/recovery_key_state.dart
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
part of 'recovery_key_bloc.dart';
|
||||||
|
|
||||||
|
sealed class RecoveryKeyState extends Equatable {
|
||||||
|
RecoveryKeyState({
|
||||||
|
required final RecoveryKeyStatus? keyStatus,
|
||||||
|
}) : _hashCode = keyStatus.hashCode;
|
||||||
|
|
||||||
|
final int _hashCode;
|
||||||
|
|
||||||
|
RecoveryKeyStatus get _status =>
|
||||||
|
getIt<ApiConnectionRepository>().apiData.recoveryKeyStatus.data ??
|
||||||
|
const RecoveryKeyStatus(exists: false, valid: false);
|
||||||
|
|
||||||
|
bool get exists => _status.exists;
|
||||||
|
bool get isValid => _status.valid;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RecoveryKeyInitial extends RecoveryKeyState {
|
||||||
|
RecoveryKeyInitial()
|
||||||
|
: super(keyStatus: const RecoveryKeyStatus(exists: false, valid: false));
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [_hashCode];
|
||||||
|
}
|
||||||
|
|
||||||
|
class RecoveryKeyRefreshing extends RecoveryKeyState {
|
||||||
|
RecoveryKeyRefreshing({required super.keyStatus});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [_hashCode];
|
||||||
|
}
|
||||||
|
|
||||||
|
class RecoveryKeyLoaded extends RecoveryKeyState {
|
||||||
|
RecoveryKeyLoaded({required super.keyStatus});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [_hashCode];
|
||||||
|
}
|
||||||
|
|
||||||
|
class RecoveryKeyError extends RecoveryKeyState {
|
||||||
|
RecoveryKeyError()
|
||||||
|
: super(keyStatus: const RecoveryKeyStatus(exists: false, valid: false));
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> 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<Object?> get props => [_hashCode, recoveryKey, error];
|
||||||
|
}
|
|
@ -1,80 +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/server_connection_dependent/server_connection_dependent_cubit.dart';
|
|
||||||
import 'package:selfprivacy/logic/models/json/recovery_token_status.dart';
|
|
||||||
|
|
||||||
part 'recovery_key_state.dart';
|
|
||||||
|
|
||||||
class RecoveryKeyCubit
|
|
||||||
extends ServerConnectionDependentCubit<RecoveryKeyState> {
|
|
||||||
RecoveryKeyCubit() : super(const RecoveryKeyState.initial());
|
|
||||||
|
|
||||||
final ServerApi api = ServerApi();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void load() async {
|
|
||||||
// if (serverInstallationCubit.state is ServerInstallationFinished) {
|
|
||||||
final RecoveryKeyStatus? status = await _getRecoveryKeyStatus();
|
|
||||||
if (status == null) {
|
|
||||||
emit(state.copyWith(loadingStatus: LoadingStatus.error));
|
|
||||||
} else {
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
status: status,
|
|
||||||
loadingStatus: LoadingStatus.success,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// } else {
|
|
||||||
// emit(state.copyWith(loadingStatus: LoadingStatus.uninitialized));
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<RecoveryKeyStatus?> _getRecoveryKeyStatus() async {
|
|
||||||
final GenericResult<RecoveryKeyStatus?> response =
|
|
||||||
await api.getRecoveryTokenStatus();
|
|
||||||
if (response.success) {
|
|
||||||
return response.data;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> refresh() async {
|
|
||||||
emit(state.copyWith(loadingStatus: LoadingStatus.refreshing));
|
|
||||||
final RecoveryKeyStatus? status = await _getRecoveryKeyStatus();
|
|
||||||
if (status == null) {
|
|
||||||
emit(state.copyWith(loadingStatus: LoadingStatus.error));
|
|
||||||
} else {
|
|
||||||
emit(
|
|
||||||
state.copyWith(status: status, loadingStatus: LoadingStatus.success),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> generateRecoveryKey({
|
|
||||||
final DateTime? expirationDate,
|
|
||||||
final int? numberOfUses,
|
|
||||||
}) async {
|
|
||||||
final GenericResult<String> response =
|
|
||||||
await api.generateRecoveryToken(expirationDate, numberOfUses);
|
|
||||||
if (response.success) {
|
|
||||||
unawaited(refresh());
|
|
||||||
return response.data;
|
|
||||||
} else {
|
|
||||||
throw GenerationError(response.message ?? 'Unknown error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void clear() {
|
|
||||||
emit(state.copyWith(loadingStatus: LoadingStatus.uninitialized));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GenerationError extends Error {
|
|
||||||
GenerationError(this.message);
|
|
||||||
final String message;
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
part of 'recovery_key_cubit.dart';
|
|
||||||
|
|
||||||
class RecoveryKeyState extends ServerInstallationDependendState {
|
|
||||||
const RecoveryKeyState(this._status, this.loadingStatus);
|
|
||||||
|
|
||||||
const RecoveryKeyState.initial()
|
|
||||||
: this(
|
|
||||||
const RecoveryKeyStatus(exists: false, valid: false),
|
|
||||||
LoadingStatus.refreshing,
|
|
||||||
);
|
|
||||||
|
|
||||||
final RecoveryKeyStatus _status;
|
|
||||||
final LoadingStatus loadingStatus;
|
|
||||||
|
|
||||||
bool get exists => _status.exists;
|
|
||||||
bool get isValid => _status.valid;
|
|
||||||
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<Object> get props => [_status, loadingStatus];
|
|
||||||
|
|
||||||
RecoveryKeyState copyWith({
|
|
||||||
final RecoveryKeyStatus? status,
|
|
||||||
final LoadingStatus? loadingStatus,
|
|
||||||
}) =>
|
|
||||||
RecoveryKeyState(
|
|
||||||
status ?? _status,
|
|
||||||
loadingStatus ?? this.loadingStatus,
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -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/recovery_token_status.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/server_disk_volume.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';
|
||||||
|
@ -101,6 +102,8 @@ class ApiConnectionRepository {
|
||||||
_apiData.backups.data = await _apiData.backups.fetchData();
|
_apiData.backups.data = await _apiData.backups.fetchData();
|
||||||
_apiData.services.data = await _apiData.services.fetchData();
|
_apiData.services.data = await _apiData.services.fetchData();
|
||||||
_apiData.volumes.data = await _apiData.volumes.fetchData();
|
_apiData.volumes.data = await _apiData.volumes.fetchData();
|
||||||
|
_apiData.recoveryKeyStatus.data =
|
||||||
|
await _apiData.recoveryKeyStatus.fetchData();
|
||||||
_dataStream.add(_apiData);
|
_dataStream.add(_apiData);
|
||||||
|
|
||||||
connectionStatus = ConnectionStatus.connected;
|
connectionStatus = ConnectionStatus.connected;
|
||||||
|
@ -140,6 +143,8 @@ class ApiConnectionRepository {
|
||||||
.refetchData(version, () => _dataStream.add(_apiData));
|
.refetchData(version, () => _dataStream.add(_apiData));
|
||||||
await _apiData.volumes
|
await _apiData.volumes
|
||||||
.refetchData(version, () => _dataStream.add(_apiData));
|
.refetchData(version, () => _dataStream.add(_apiData));
|
||||||
|
await _apiData.recoveryKeyStatus
|
||||||
|
.refetchData(version, () => _dataStream.add(_apiData));
|
||||||
}
|
}
|
||||||
|
|
||||||
void emitData() {
|
void emitData() {
|
||||||
|
@ -159,10 +164,12 @@ class ApiData {
|
||||||
backupConfig = ApiDataElement<BackupConfiguration>(
|
backupConfig = ApiDataElement<BackupConfiguration>(
|
||||||
fetchData: () async => api.getBackupsConfiguration(),
|
fetchData: () async => api.getBackupsConfiguration(),
|
||||||
requiredApiVersion: '>=2.4.2',
|
requiredApiVersion: '>=2.4.2',
|
||||||
|
ttl: 120,
|
||||||
),
|
),
|
||||||
backups = ApiDataElement<List<Backup>>(
|
backups = ApiDataElement<List<Backup>>(
|
||||||
fetchData: () async => api.getBackups(),
|
fetchData: () async => api.getBackups(),
|
||||||
requiredApiVersion: '>=2.4.2',
|
requiredApiVersion: '>=2.4.2',
|
||||||
|
ttl: 120,
|
||||||
),
|
),
|
||||||
services = ApiDataElement<List<Service>>(
|
services = ApiDataElement<List<Service>>(
|
||||||
fetchData: () async => api.getAllServices(),
|
fetchData: () async => api.getAllServices(),
|
||||||
|
@ -170,6 +177,10 @@ class ApiData {
|
||||||
),
|
),
|
||||||
volumes = ApiDataElement<List<ServerDiskVolume>>(
|
volumes = ApiDataElement<List<ServerDiskVolume>>(
|
||||||
fetchData: () async => api.getServerDiskVolumes(),
|
fetchData: () async => api.getServerDiskVolumes(),
|
||||||
|
),
|
||||||
|
recoveryKeyStatus = ApiDataElement<RecoveryKeyStatus>(
|
||||||
|
fetchData: () async => (await api.getRecoveryTokenStatus()).data,
|
||||||
|
ttl: 300,
|
||||||
);
|
);
|
||||||
|
|
||||||
ApiDataElement<List<ServerJob>> serverJobs;
|
ApiDataElement<List<ServerJob>> serverJobs;
|
||||||
|
@ -178,6 +189,7 @@ class ApiData {
|
||||||
ApiDataElement<List<Backup>> backups;
|
ApiDataElement<List<Backup>> backups;
|
||||||
ApiDataElement<List<Service>> services;
|
ApiDataElement<List<Service>> services;
|
||||||
ApiDataElement<List<ServerDiskVolume>> volumes;
|
ApiDataElement<List<ServerDiskVolume>> volumes;
|
||||||
|
ApiDataElement<RecoveryKeyStatus> recoveryKeyStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ConnectionStatus {
|
enum ConnectionStatus {
|
||||||
|
|
|
@ -4,8 +4,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/tls_options.dart';
|
import 'package:selfprivacy/logic/api_maps/tls_options.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/devices/devices_cubit.dart';
|
|
||||||
import 'package:selfprivacy/logic/cubit/recovery_key/recovery_key_cubit.dart';
|
|
||||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
|
@ -89,18 +87,6 @@ class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
|
||||||
title: const Text('ApiDevicesCubit'),
|
|
||||||
subtitle: Text(
|
|
||||||
context.watch<ApiDevicesCubit>().state.status.toString(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: const Text('RecoveryKeyCubit'),
|
|
||||||
subtitle: Text(
|
|
||||||
context.watch<RecoveryKeyCubit>().state.loadingStatus.toString(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('ApiConnectionRepository status'),
|
title: const Text('ApiConnectionRepository status'),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
|
|
|
@ -2,9 +2,7 @@ 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/services.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/common_enum/common_enum.dart';
|
|
||||||
import 'package:selfprivacy/logic/cubit/recovery_key/recovery_key_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/ui/components/buttons/brand_button.dart';
|
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
|
||||||
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
|
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
|
||||||
|
@ -21,34 +19,29 @@ class RecoveryKeyPage extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
context.read<RecoveryKeyCubit>().load();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
final RecoveryKeyState keyStatus = context.watch<RecoveryKeyCubit>().state;
|
final RecoveryKeyState keyStatus = context.watch<RecoveryKeyBloc>().state;
|
||||||
|
|
||||||
final List<Widget> widgets;
|
final List<Widget> widgets;
|
||||||
String? subtitle =
|
String? subtitle =
|
||||||
keyStatus.exists ? null : 'recovery_key.key_main_description'.tr();
|
keyStatus.exists ? null : 'recovery_key.key_main_description'.tr();
|
||||||
|
|
||||||
switch (keyStatus.loadingStatus) {
|
switch (keyStatus) {
|
||||||
case LoadingStatus.refreshing:
|
case RecoveryKeyRefreshing():
|
||||||
subtitle = 'recovery_key.key_synchronizing'.tr();
|
subtitle = 'recovery_key.key_synchronizing'.tr();
|
||||||
widgets = [
|
widgets = [
|
||||||
const Center(child: CircularProgressIndicator()),
|
const Center(child: CircularProgressIndicator()),
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
case LoadingStatus.success:
|
case RecoveryKeyLoaded():
|
||||||
widgets = [
|
widgets = [
|
||||||
const RecoveryKeyContent(),
|
const RecoveryKeyContent(),
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
case LoadingStatus.uninitialized:
|
case RecoveryKeyInitial():
|
||||||
case LoadingStatus.error:
|
case RecoveryKeyError():
|
||||||
|
case RecoveryKeyCreating():
|
||||||
subtitle = 'recovery_key.key_connection_error'.tr();
|
subtitle = 'recovery_key.key_connection_error'.tr();
|
||||||
widgets = [
|
widgets = [
|
||||||
const Icon(Icons.sentiment_dissatisfied_outlined),
|
const Icon(Icons.sentiment_dissatisfied_outlined),
|
||||||
|
@ -58,7 +51,7 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
||||||
|
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
context.read<RecoveryKeyCubit>().load();
|
context.read<RecoveryKeyBloc>().add(const RecoveryKeyStatusRefresh());
|
||||||
},
|
},
|
||||||
child: BrandHeroScreen(
|
child: BrandHeroScreen(
|
||||||
heroTitle: 'recovery_key.key_main_header'.tr(),
|
heroTitle: 'recovery_key.key_main_header'.tr(),
|
||||||
|
@ -83,7 +76,7 @@ class _RecoveryKeyContentState extends State<RecoveryKeyContent> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
final RecoveryKeyState keyStatus = context.watch<RecoveryKeyCubit>().state;
|
final RecoveryKeyState keyStatus = context.watch<RecoveryKeyBloc>().state;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
|
@ -241,34 +234,24 @@ class _RecoveryKeyConfigurationState extends State<RecoveryKeyConfiguration> {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
});
|
});
|
||||||
try {
|
context.read<RecoveryKeyBloc>().add(
|
||||||
final String token =
|
CreateNewRecoveryKey(
|
||||||
await context.read<RecoveryKeyCubit>().generateRecoveryKey(
|
expirationDate: _isExpirationToggled ? _selectedDate : null,
|
||||||
numberOfUses: _isAmountToggled
|
numberOfUses:
|
||||||
? int.tryParse(_amountController.text)
|
_isAmountToggled ? int.tryParse(_amountController.text) : null,
|
||||||
: null,
|
),
|
||||||
expirationDate: _isExpirationToggled ? _selectedDate : null,
|
);
|
||||||
);
|
if (!mounted) {
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
await Navigator.of(context).push(
|
|
||||||
materialRoute(
|
|
||||||
RecoveryKeyReceiving(recoveryKey: token), // TO DO
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} on GenerationError catch (e) {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
getIt<NavigationService>().showSnackBar(
|
|
||||||
'recovery_key.generation_error'.tr(args: [e.message]),
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
await Navigator.of(context).push(
|
||||||
|
materialRoute(
|
||||||
|
const RecoveryKeyReceiving(),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateErrorStatuses() {
|
void _updateErrorStatuses() {
|
||||||
|
|
|
@ -1,23 +1,36 @@
|
||||||
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:selfprivacy/logic/bloc/recovery_key/recovery_key_bloc.dart';
|
||||||
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
|
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
|
||||||
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
|
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
|
||||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||||
|
|
||||||
class RecoveryKeyReceiving extends StatelessWidget {
|
class RecoveryKeyReceiving extends StatelessWidget {
|
||||||
const RecoveryKeyReceiving({required this.recoveryKey, super.key});
|
const RecoveryKeyReceiving({super.key});
|
||||||
|
|
||||||
final String recoveryKey;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) => BrandHeroScreen(
|
Widget build(final BuildContext context) {
|
||||||
heroTitle: 'recovery_key.key_main_header'.tr(),
|
final recoveryKeyState = context.watch<RecoveryKeyBloc>().state;
|
||||||
heroSubtitle: 'recovery_key.key_receiving_description'.tr(),
|
|
||||||
hasBackButton: true,
|
final String? recoveryKey = recoveryKeyState is RecoveryKeyCreating
|
||||||
hasFlashButton: false,
|
? recoveryKeyState.recoveryKey
|
||||||
children: [
|
: null;
|
||||||
const Divider(),
|
|
||||||
const SizedBox(height: 16),
|
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)
|
||||||
Text(
|
Text(
|
||||||
recoveryKey,
|
recoveryKey,
|
||||||
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||||
|
@ -26,19 +39,31 @@ class RecoveryKeyReceiving extends StatelessWidget {
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
if (error != null)
|
||||||
const Divider(),
|
Text(
|
||||||
const SizedBox(height: 16),
|
'recovery_key.generation_error'.tr(args: [error]),
|
||||||
InfoBox(
|
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||||
text: 'recovery_key.key_receiving_info'.tr(),
|
fontSize: 24,
|
||||||
|
fontFamily: 'RobotoMono',
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
BrandButton.filled(
|
const Divider(),
|
||||||
child: Text('recovery_key.key_receiving_done'.tr()),
|
const SizedBox(height: 16),
|
||||||
onPressed: () {
|
InfoBox(
|
||||||
Navigator.of(context).popUntil((final route) => route.isFirst);
|
text: 'recovery_key.key_receiving_info'.tr(),
|
||||||
},
|
),
|
||||||
),
|
const SizedBox(height: 16),
|
||||||
],
|
BrandButton.filled(
|
||||||
);
|
child: Text('recovery_key.key_receiving_done'.tr()),
|
||||||
|
onPressed: () {
|
||||||
|
context.read<RecoveryKeyBloc>().add(const ConsumedNewRecoveryKey());
|
||||||
|
Navigator.of(context).popUntil((final route) => route.isFirst);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue