mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-23 09:16:54 +00:00
refactor: Replace ApiDevicesCubit with DevicesBloc
This commit is contained in:
parent
3a525f0d11
commit
710b9b53dd
|
@ -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<BlocAndProviderConfig> {
|
|||
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<BlocAndProviderConfig> {
|
|||
backupsBloc = BackupsBloc();
|
||||
dnsRecordsCubit = DnsRecordsCubit();
|
||||
recoveryKeyBloc = RecoveryKeyBloc();
|
||||
apiDevicesCubit = ApiDevicesCubit();
|
||||
devicesBloc = DevicesBloc();
|
||||
serverJobsBloc = ServerJobsBloc();
|
||||
connectionStatusBloc = ConnectionStatusBloc();
|
||||
serverDetailsCubit = ServerDetailsCubit();
|
||||
|
@ -93,7 +93,7 @@ class BlocAndProviderConfigState extends State<BlocAndProviderConfig> {
|
|||
create: (final _) => recoveryKeyBloc,
|
||||
),
|
||||
BlocProvider(
|
||||
create: (final _) => apiDevicesCubit,
|
||||
create: (final _) => devicesBloc,
|
||||
),
|
||||
BlocProvider(
|
||||
create: (final _) => serverJobsBloc,
|
||||
|
|
114
lib/logic/bloc/devices/devices_bloc.dart
Normal file
114
lib/logic/bloc/devices/devices_bloc.dart
Normal file
|
@ -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<DevicesEvent, DevicesState> {
|
||||
DevicesBloc() : super(DevicesInitial()) {
|
||||
on<DevicesListChanged>(
|
||||
_mapDevicesListChangedToState,
|
||||
transformer: sequential(),
|
||||
);
|
||||
on<DeleteDevice>(
|
||||
_mapDeleteDeviceToState,
|
||||
transformer: sequential(),
|
||||
);
|
||||
|
||||
final apiConnectionRepository = getIt<ApiConnectionRepository>();
|
||||
_apiDataSubscription = apiConnectionRepository.dataStream.listen(
|
||||
(final ApiData apiData) {
|
||||
print('============');
|
||||
print(apiData.devices.data);
|
||||
add(
|
||||
DevicesListChanged(apiData.devices.data),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
StreamSubscription? _apiDataSubscription;
|
||||
|
||||
Future<void> _mapDevicesListChangedToState(
|
||||
final DevicesListChanged event,
|
||||
final Emitter<DevicesState> emit,
|
||||
) async {
|
||||
if (state is DevicesDeleting) {
|
||||
return;
|
||||
}
|
||||
print(event.devices);
|
||||
if (event.devices == null) {
|
||||
emit(DevicesError());
|
||||
return;
|
||||
}
|
||||
emit(DevicesLoaded(devices: event.devices!));
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
getIt<ApiConnectionRepository>().apiData.devices.invalidate();
|
||||
await getIt<ApiConnectionRepository>().reload(null);
|
||||
}
|
||||
|
||||
Future<void> _mapDeleteDeviceToState(
|
||||
final DeleteDevice event,
|
||||
final Emitter<DevicesState> 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<void> response = await getIt<ApiConnectionRepository>()
|
||||
.api
|
||||
.deleteApiToken(event.device.name);
|
||||
if (response.success) {
|
||||
getIt<ApiConnectionRepository>().apiData.devices.invalidate();
|
||||
emit(
|
||||
DevicesLoaded(
|
||||
devices: state.devices
|
||||
.where((final d) => d.name != event.device.name)
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
getIt<NavigationService>()
|
||||
.showSnackBar(response.message ?? 'Error deleting device');
|
||||
emit(DevicesLoaded(devices: state.devices));
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> getNewDeviceKey() async {
|
||||
final GenericResult<String> response =
|
||||
await getIt<ApiConnectionRepository>().api.createDeviceToken();
|
||||
if (response.success) {
|
||||
return response.data;
|
||||
} else {
|
||||
getIt<NavigationService>().showSnackBar(
|
||||
response.message ?? 'Error getting new device key',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onChange(final Change<DevicesState> change) {
|
||||
super.onChange(change);
|
||||
print(change);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_apiDataSubscription?.cancel();
|
||||
return super.close();
|
||||
}
|
||||
}
|
23
lib/logic/bloc/devices/devices_event.dart
Normal file
23
lib/logic/bloc/devices/devices_event.dart
Normal file
|
@ -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<ApiToken>? devices;
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class DeleteDevice extends DevicesEvent {
|
||||
const DeleteDevice(this.device);
|
||||
|
||||
final ApiToken device;
|
||||
|
||||
@override
|
||||
List<Object> get props => [device];
|
||||
}
|
53
lib/logic/bloc/devices/devices_state.dart
Normal file
53
lib/logic/bloc/devices/devices_state.dart
Normal file
|
@ -0,0 +1,53 @@
|
|||
part of 'devices_bloc.dart';
|
||||
|
||||
sealed class DevicesState extends Equatable {
|
||||
DevicesState({
|
||||
required final List<ApiToken> devices,
|
||||
}) : _hashCode = devices.hashCode;
|
||||
|
||||
final int _hashCode;
|
||||
|
||||
List<ApiToken> get _devices =>
|
||||
getIt<ApiConnectionRepository>().apiData.devices.data ?? const [];
|
||||
|
||||
List<ApiToken> get devices => _devices;
|
||||
ApiToken get thisDevice => _devices.firstWhere(
|
||||
(final device) => device.isCaller,
|
||||
orElse: () => ApiToken(
|
||||
name: 'Error fetching device',
|
||||
isCaller: true,
|
||||
date: DateTime.now(),
|
||||
),
|
||||
);
|
||||
|
||||
List<ApiToken> get otherDevices =>
|
||||
_devices.where((final device) => !device.isCaller).toList();
|
||||
}
|
||||
|
||||
class DevicesInitial extends DevicesState {
|
||||
DevicesInitial() : super(devices: const []);
|
||||
|
||||
@override
|
||||
List<Object> get props => [_hashCode];
|
||||
}
|
||||
|
||||
class DevicesLoaded extends DevicesState {
|
||||
DevicesLoaded({required super.devices});
|
||||
|
||||
@override
|
||||
List<Object> get props => [_hashCode];
|
||||
}
|
||||
|
||||
class DevicesError extends DevicesState {
|
||||
DevicesError() : super(devices: const []);
|
||||
|
||||
@override
|
||||
List<Object> get props => [_hashCode];
|
||||
}
|
||||
|
||||
class DevicesDeleting extends DevicesState {
|
||||
DevicesDeleting({required super.devices});
|
||||
|
||||
@override
|
||||
List<Object> get props => [_hashCode];
|
||||
}
|
|
@ -16,14 +16,6 @@ class RecoveryKeyBloc extends Bloc<RecoveryKeyEvent, RecoveryKeyState> {
|
|||
_mapRecoveryKeyStatusChangedToState,
|
||||
transformer: sequential(),
|
||||
);
|
||||
on<CreateNewRecoveryKey>(
|
||||
_mapCreateNewRecoveryKeyToState,
|
||||
transformer: sequential(),
|
||||
);
|
||||
on<ConsumedNewRecoveryKey>(
|
||||
_mapRecoveryKeyStatusRefreshToState,
|
||||
transformer: sequential(),
|
||||
);
|
||||
on<RecoveryKeyStatusRefresh>(
|
||||
_mapRecoveryKeyStatusRefreshToState,
|
||||
transformer: droppable(),
|
||||
|
@ -45,9 +37,6 @@ class RecoveryKeyBloc extends Bloc<RecoveryKeyEvent, RecoveryKeyState> {
|
|||
final RecoveryKeyStatusChanged event,
|
||||
final Emitter<RecoveryKeyState> emit,
|
||||
) async {
|
||||
if (state is RecoveryKeyCreating) {
|
||||
return;
|
||||
}
|
||||
if (event.recoveryKeyStatus == null) {
|
||||
emit(RecoveryKeyError());
|
||||
return;
|
||||
|
@ -55,20 +44,20 @@ class RecoveryKeyBloc extends Bloc<RecoveryKeyEvent, RecoveryKeyState> {
|
|||
emit(RecoveryKeyLoaded(keyStatus: event.recoveryKeyStatus));
|
||||
}
|
||||
|
||||
Future<void> _mapCreateNewRecoveryKeyToState(
|
||||
final CreateNewRecoveryKey event,
|
||||
final Emitter<RecoveryKeyState> emit,
|
||||
) async {
|
||||
emit(RecoveryKeyCreating());
|
||||
Future<String> generateRecoveryKey({
|
||||
final DateTime? expirationDate,
|
||||
final int? numberOfUses,
|
||||
}) async {
|
||||
final GenericResult<String> response =
|
||||
await getIt<ApiConnectionRepository>().api.generateRecoveryToken(
|
||||
event.expirationDate,
|
||||
event.numberOfUses,
|
||||
);
|
||||
await getIt<ApiConnectionRepository>()
|
||||
.api
|
||||
.generateRecoveryToken(expirationDate, numberOfUses);
|
||||
if (response.success) {
|
||||
emit(RecoveryKeyCreating(recoveryKey: response.data));
|
||||
getIt<ApiConnectionRepository>().apiData.recoveryKeyStatus.invalidate();
|
||||
unawaited(getIt<ApiConnectionRepository>().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<RecoveryKeyEvent, RecoveryKeyState> {
|
|||
return super.close();
|
||||
}
|
||||
}
|
||||
|
||||
class GenerationError extends Error {
|
||||
GenerationError(this.message);
|
||||
final String message;
|
||||
}
|
||||
|
|
|
@ -13,26 +13,6 @@ class RecoveryKeyStatusChanged extends RecoveryKeyEvent {
|
|||
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();
|
||||
|
||||
|
|
|
@ -54,14 +54,3 @@ class RecoveryKeyError extends RecoveryKeyState {
|
|||
@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,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<ApiDevicesState> {
|
||||
ApiDevicesCubit() : super(const ApiDevicesState.initial());
|
||||
|
||||
final ServerApi api = ServerApi();
|
||||
|
||||
@override
|
||||
void load() async {
|
||||
// if (serverInstallationCubit.state is ServerInstallationFinished) {
|
||||
_refetch();
|
||||
// }
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
emit(ApiDevicesState([state.thisDevice], LoadingStatus.refreshing));
|
||||
_refetch();
|
||||
}
|
||||
|
||||
void _refetch() async {
|
||||
final List<ApiToken>? devices = await _getApiTokens();
|
||||
if (devices != null) {
|
||||
emit(ApiDevicesState(devices, LoadingStatus.success));
|
||||
} else {
|
||||
emit(const ApiDevicesState([], LoadingStatus.error));
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<ApiToken>?> _getApiTokens() async {
|
||||
final GenericResult<List<ApiToken>> response = await api.getApiTokens();
|
||||
if (response.success) {
|
||||
return response.data;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteDevice(final ApiToken device) async {
|
||||
final GenericResult<void> 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<NavigationService>()
|
||||
.showSnackBar(response.message ?? 'Error deleting device');
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> getNewDeviceKey() async {
|
||||
final GenericResult<String> response = await api.createDeviceToken();
|
||||
if (response.success) {
|
||||
return response.data;
|
||||
} else {
|
||||
getIt<NavigationService>().showSnackBar(
|
||||
response.message ?? 'Error getting new device key',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void clear() {
|
||||
emit(const ApiDevicesState.initial());
|
||||
}
|
||||
}
|
|
@ -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<ApiToken> _devices;
|
||||
final LoadingStatus status;
|
||||
|
||||
List<ApiToken> get devices => _devices;
|
||||
ApiToken get thisDevice => _devices.firstWhere(
|
||||
(final device) => device.isCaller,
|
||||
orElse: () => ApiToken(
|
||||
name: 'Error fetching device',
|
||||
isCaller: true,
|
||||
date: DateTime.now(),
|
||||
),
|
||||
);
|
||||
|
||||
List<ApiToken> get otherDevices =>
|
||||
_devices.where((final device) => !device.isCaller).toList();
|
||||
|
||||
ApiDevicesState copyWith({
|
||||
final List<ApiToken>? devices,
|
||||
final LoadingStatus? status,
|
||||
}) =>
|
||||
ApiDevicesState(
|
||||
devices ?? _devices,
|
||||
status ?? this.status,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [_devices];
|
||||
}
|
|
@ -41,7 +41,7 @@ class UsersCubit extends ServerConnectionDependentCubit<UsersState> {
|
|||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
if (getIt<ApiConnectionRepository>().connectionStatus !=
|
||||
if (getIt<ApiConnectionRepository>().connectionStatus ==
|
||||
ConnectionStatus.nonexistent) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -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<RecoveryKeyStatus>(
|
||||
fetchData: () async => (await api.getRecoveryTokenStatus()).data,
|
||||
ttl: 300,
|
||||
),
|
||||
devices = ApiDataElement<List<ApiToken>>(
|
||||
fetchData: () async => (await api.getApiTokens()).data,
|
||||
);
|
||||
|
||||
ApiDataElement<List<ServerJob>> serverJobs;
|
||||
|
@ -190,6 +197,7 @@ class ApiData {
|
|||
ApiDataElement<List<Service>> services;
|
||||
ApiDataElement<List<ServerDiskVolume>> volumes;
|
||||
ApiDataElement<RecoveryKeyStatus> recoveryKeyStatus;
|
||||
ApiDataElement<List<ApiToken>> devices;
|
||||
}
|
||||
|
||||
enum ConnectionStatus {
|
||||
|
|
|
@ -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<String, dynamic> 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<Object?> get props => [name, date, isCaller];
|
||||
}
|
||||
|
|
|
@ -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<DevicesScreen> {
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final ApiDevicesState devicesStatus =
|
||||
context.watch<ApiDevicesCubit>().state;
|
||||
final DevicesState devicesStatus = context.watch<DevicesBloc>().state;
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await context.read<ApiDevicesCubit>().refresh();
|
||||
await context.read<DevicesBloc>().refresh();
|
||||
},
|
||||
child: BrandHeroScreen(
|
||||
heroTitle: 'devices.main_screen.header'.tr(),
|
||||
|
@ -35,13 +33,13 @@ class _DevicesScreenState extends State<DevicesScreen> {
|
|||
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<ApiDevicesCubit>().deleteDevice(device);
|
||||
context.read<DevicesBloc>().add(DeleteDevice(device));
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
|
|
|
@ -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<ApiDevicesCubit>().getNewDeviceKey(),
|
||||
future: context.read<DevicesBloc>().getNewDeviceKey(),
|
||||
builder: (
|
||||
final BuildContext context,
|
||||
final AsyncSnapshot<Object?> snapshot,
|
||||
|
|
|
@ -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<RecoveryKeyPage> {
|
|||
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<RecoveryKeyConfiguration> {
|
|||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
context.read<RecoveryKeyBloc>().add(
|
||||
CreateNewRecoveryKey(
|
||||
expirationDate: _isExpirationToggled ? _selectedDate : null,
|
||||
numberOfUses:
|
||||
_isAmountToggled ? int.tryParse(_amountController.text) : null,
|
||||
),
|
||||
);
|
||||
if (!mounted) {
|
||||
try {
|
||||
final String token =
|
||||
await context.read<RecoveryKeyBloc>().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<NavigationService>().showSnackBar(
|
||||
'recovery_key.generation_error'.tr(args: [e.message]),
|
||||
);
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
await Navigator.of(context).push(
|
||||
materialRoute(
|
||||
const RecoveryKeyReceiving(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _updateErrorStatuses() {
|
||||
|
|
|
@ -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<RecoveryKeyBloc>().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<RecoveryKeyBloc>().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);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue