refactor: Rewrite backups cubit to bloc, using ApiRepo streams

pull/440/head
Inex Code 2024-01-29 17:54:09 +04:00
parent b1be3f24d6
commit a5e7725733
31 changed files with 859 additions and 452 deletions

View File

@ -1,15 +1,15 @@
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/cubit/connection_status/connection_status_bloc.dart';
import 'package:selfprivacy/logic/cubit/devices/devices_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_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart';
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart';
@ -29,7 +29,7 @@ class BlocAndProviderConfig extends StatelessWidget {
final supportSystemCubit = SupportSystemCubit();
final usersCubit = UsersCubit(serverInstallationCubit);
final servicesCubit = ServicesCubit(serverInstallationCubit);
final backupsCubit = BackupsCubit(serverInstallationCubit);
final backupsBloc = BackupsBloc();
final dnsRecordsCubit = DnsRecordsCubit(serverInstallationCubit);
final recoveryKeyCubit = RecoveryKeyCubit(serverInstallationCubit);
final apiDevicesCubit = ApiDevicesCubit(serverInstallationCubit);
@ -65,8 +65,7 @@ class BlocAndProviderConfig extends StatelessWidget {
lazy: false,
),
BlocProvider(
create: (final _) => backupsCubit,
lazy: false,
create: (final _) => backupsBloc,
),
BlocProvider(
create: (final _) => dnsRecordsCubit,

View File

@ -7,6 +7,7 @@ import 'package:selfprivacy/logic/get_it/navigation.dart';
export 'package:selfprivacy/logic/get_it/api_config.dart';
export 'package:selfprivacy/logic/get_it/console.dart';
export 'package:selfprivacy/logic/get_it/navigation.dart';
export 'package:selfprivacy/logic/get_it/api_connection_repository.dart';
final GetIt getIt = GetIt.instance;

View File

@ -0,0 +1,396 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart';
import 'package:selfprivacy/logic/models/backup.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
import 'package:selfprivacy/logic/models/initialize_repository_input.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
import 'package:selfprivacy/logic/models/service.dart';
part 'backups_event.dart';
part 'backups_state.dart';
class BackupsBloc extends Bloc<BackupsEvent, BackupsState> {
BackupsBloc() : super(BackupsInitial()) {
final connectionRepository = getIt<ApiConnectionRepository>();
_apiStatusSubscription = connectionRepository.connectionStatusStream
.listen((final ConnectionStatus connectionStatus) {
switch (connectionStatus) {
case ConnectionStatus.nonexistent:
add(const BackupsServerReset());
isLoaded = false;
break;
case ConnectionStatus.connected:
if (!isLoaded) {
add(const BackupsServerLoaded());
isLoaded = true;
}
break;
default:
break;
}
});
_apiDataSubscription = connectionRepository.dataStream.listen(
(final ApiData apiData) {
if (apiData.backups.data == null || apiData.backupConfig.data == null) {
add(const BackupsServerReset());
isLoaded = false;
} else {
add(
BackupsStateChanged(
apiData.backups.data!,
apiData.backupConfig.data,
),
);
isLoaded = true;
}
},
);
if (connectionRepository.connectionStatus == ConnectionStatus.connected) {
add(const BackupsServerLoaded());
isLoaded = true;
}
on<BackupsServerLoaded>(
_loadState,
);
on<BackupsServerReset>(
_resetState,
);
on<BackupsStateChanged>(
_updateState,
);
on<InitializeBackupsRepository>(
_initializeRepository,
);
on<ForceSnapshotListUpdate>(
_forceSnapshotListUpdate,
);
on<CreateBackups>(
_createBackups,
);
on<RestoreBackup>(
_restoreBackup,
);
on<SetAutobackupPeriod>(
_setAutobackupPeriod,
);
on<SetAutobackupQuotas>(
_setAutobackupQuotas,
);
on<ForgetSnapshot>(
_forgetSnapshot,
);
}
final BackblazeApi backblaze = BackblazeApi();
Future<void> _loadState(
final BackupsServerLoaded event,
final Emitter<BackupsState> emit,
) async {
BackblazeBucket? bucket = getIt<ApiConfigModel>().backblazeBucket;
final backups = getIt<ApiConnectionRepository>().apiData.backups;
final backupConfig = getIt<ApiConnectionRepository>().apiData.backupConfig;
if (backupConfig.data == null || backups.data == null) {
emit(BackupsLoading());
return;
}
if (bucket != null &&
backupConfig.data!.encryptionKey != bucket.encryptionKey) {
bucket = bucket.copyWith(
encryptionKey: backupConfig.data!.encryptionKey,
);
await getIt<ApiConfigModel>().setBackblazeBucket(bucket);
}
if (backupConfig.data!.isInitialized) {
emit(
BackupsInitialized(
backblazeBucket: bucket,
backupConfig: backupConfig.data,
backups: backups.data ?? [],
),
);
} else {
emit(BackupsUnititialized());
}
}
Future<void> _resetState(
final BackupsServerReset event,
final Emitter<BackupsState> emit,
) async {
emit(BackupsInitial());
}
Future<void> _initializeRepository(
final InitializeBackupsRepository event,
final Emitter<BackupsState> emit,
) async {
if (state is! BackupsUnititialized) {
return;
}
emit(BackupsInitializing());
final String? encryptionKey = getIt<ApiConnectionRepository>()
.apiData
.backupConfig
.data
?.encryptionKey;
if (encryptionKey == null) {
emit(BackupsUnititialized());
getIt<NavigationService>()
.showSnackBar("Couldn't get encryption key from your server.");
return;
}
final BackblazeBucket bucket;
if (state.backblazeBucket == null) {
final String domain = getIt<ApiConnectionRepository>()
.serverDomain!
.domainName
.replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-');
final int serverId = getIt<ApiConnectionRepository>().serverDetails!.id;
String bucketName =
'${DateTime.now().millisecondsSinceEpoch}-$serverId-$domain';
if (bucketName.length > 49) {
bucketName = bucketName.substring(0, 49);
}
final String bucketId = await backblaze.createBucket(bucketName);
final BackblazeApplicationKey key = await backblaze.createKey(bucketId);
bucket = BackblazeBucket(
bucketId: bucketId,
bucketName: bucketName,
applicationKey: key.applicationKey,
applicationKeyId: key.applicationKeyId,
encryptionKey: encryptionKey,
);
await getIt<ApiConfigModel>().setBackblazeBucket(bucket);
emit(state.copyWith(backblazeBucket: bucket));
} else {
bucket = state.backblazeBucket!;
}
final GenericResult result =
await getIt<ApiConnectionRepository>().api.initializeRepository(
InitializeRepositoryInput(
provider: BackupsProviderType.backblaze,
locationId: bucket.bucketId,
locationName: bucket.bucketName,
login: bucket.applicationKeyId,
password: bucket.applicationKey,
),
);
if (result.success == false) {
getIt<NavigationService>().showSnackBar(
result.message ?? "Couldn't initialize repository on your server.");
emit(BackupsUnititialized());
return;
}
getIt<ApiConnectionRepository>().apiData.backupConfig.invalidate();
getIt<ApiConnectionRepository>().apiData.backups.invalidate();
await getIt<ApiConnectionRepository>().reload(null);
getIt<NavigationService>().showSnackBar(
'Backups repository is now initializing. It may take a while.',
);
}
Future<void> _updateState(
final BackupsStateChanged event,
final Emitter<BackupsState> emit,
) async {
if (event.backupConfiguration == null ||
event.backupConfiguration!.isInitialized == false) {
emit(BackupsUnititialized());
return;
}
final BackblazeBucket? bucket = getIt<ApiConfigModel>().backblazeBucket;
emit(
BackupsInitialized(
backblazeBucket: bucket,
backupConfig: event.backupConfiguration,
backups: event.backups,
),
);
}
Future<void> _forceSnapshotListUpdate(
final ForceSnapshotListUpdate event,
final Emitter<BackupsState> emit,
) async {
final currentState = state;
if (currentState is BackupsInitialized) {
emit(BackupsBusy.fromState(currentState));
getIt<NavigationService>().showSnackBar('backup.refetching_list'.tr());
await getIt<ApiConnectionRepository>().api.forceBackupListReload();
getIt<ApiConnectionRepository>().apiData.backups.invalidate();
emit(currentState);
}
}
Future<void> _createBackups(
final CreateBackups event,
final Emitter<BackupsState> emit,
) async {
final currentState = state;
if (currentState is BackupsInitialized) {
emit(BackupsBusy.fromState(currentState));
for (final service in event.services) {
final GenericResult<ServerJob?> result =
await getIt<ApiConnectionRepository>().api.startBackup(
service.id,
);
if (result.success == false) {
getIt<NavigationService>()
.showSnackBar(result.message ?? 'Unknown error');
}
if (result.data != null) {
getIt<ApiConnectionRepository>()
.apiData
.serverJobs
.data
?.add(result.data!);
}
}
emit(currentState);
getIt<ApiConnectionRepository>().emitData();
}
}
Future<void> _restoreBackup(
final RestoreBackup event,
final Emitter<BackupsState> emit,
) async {
final currentState = state;
if (currentState is BackupsInitialized) {
emit(BackupsBusy.fromState(currentState));
final GenericResult result =
await getIt<ApiConnectionRepository>().api.restoreBackup(
event.backupId,
event.restoreStrategy,
);
if (result.success == false) {
getIt<NavigationService>()
.showSnackBar(result.message ?? 'Unknown error');
}
emit(currentState);
}
}
Future<void> _setAutobackupPeriod(
final SetAutobackupPeriod event,
final Emitter<BackupsState> emit,
) async {
final currentState = state;
if (currentState is BackupsInitialized) {
emit(BackupsBusy.fromState(currentState));
final GenericResult result =
await getIt<ApiConnectionRepository>().api.setAutobackupPeriod(
period: event.period?.inMinutes,
);
if (result.success == false) {
getIt<NavigationService>()
.showSnackBar(result.message ?? 'Unknown error');
}
if (result.success == true) {
getIt<ApiConnectionRepository>().apiData.backupConfig.data =
getIt<ApiConnectionRepository>()
.apiData
.backupConfig
.data
?.copyWith(
autobackupPeriod: event.period,
);
}
emit(currentState);
getIt<ApiConnectionRepository>().emitData();
}
}
Future<void> _setAutobackupQuotas(
final SetAutobackupQuotas event,
final Emitter<BackupsState> emit,
) async {
final currentState = state;
if (currentState is BackupsInitialized) {
emit(BackupsBusy.fromState(currentState));
final GenericResult result =
await getIt<ApiConnectionRepository>().api.setAutobackupQuotas(
event.quotas,
);
if (result.success == false) {
getIt<NavigationService>()
.showSnackBar(result.message ?? 'Unknown error');
}
if (result.success == true) {
getIt<ApiConnectionRepository>().apiData.backupConfig.data =
getIt<ApiConnectionRepository>()
.apiData
.backupConfig
.data
?.copyWith(
autobackupQuotas: event.quotas,
);
}
emit(currentState);
getIt<ApiConnectionRepository>().emitData();
}
}
Future<void> _forgetSnapshot(
final ForgetSnapshot event,
final Emitter<BackupsState> emit,
) async {
final currentState = state;
if (currentState is BackupsInitialized) {
emit(BackupsBusy.fromState(currentState));
final GenericResult result =
await getIt<ApiConnectionRepository>().api.forgetSnapshot(
event.backupId,
);
if (result.success == false) {
getIt<NavigationService>()
.showSnackBar(result.message ?? 'jobs.generic_error'.tr());
} else if (result.data == false) {
getIt<NavigationService>()
.showSnackBar('backup.forget_snapshot_error'.tr());
} else {
final backups = getIt<ApiConnectionRepository>().apiData.backups.data;
if (backups != null) {
getIt<ApiConnectionRepository>().apiData.backups.data = backups
.where((final Backup backup) => backup.id != event.backupId)
.toList();
}
}
emit(currentState);
getIt<ApiConnectionRepository>().emitData();
}
}
@override
Future<void> close() {
_apiStatusSubscription.cancel();
_apiDataSubscription.cancel();
return super.close();
}
@override
void onChange(final Change<BackupsState> change) {
super.onChange(change);
}
late StreamSubscription _apiStatusSubscription;
late StreamSubscription _apiDataSubscription;
bool isLoaded = false;
}

View File

@ -0,0 +1,89 @@
part of 'backups_bloc.dart';
sealed class BackupsEvent extends Equatable {
const BackupsEvent();
}
class BackupsServerLoaded extends BackupsEvent {
const BackupsServerLoaded();
@override
List<Object?> get props => [];
}
class BackupsServerReset extends BackupsEvent {
const BackupsServerReset();
@override
List<Object?> get props => [];
}
class InitializeBackupsRepository extends BackupsEvent {
const InitializeBackupsRepository();
@override
List<Object?> get props => [];
}
class BackupsStateChanged extends BackupsEvent {
const BackupsStateChanged(this.backups, this.backupConfiguration);
final List<Backup> backups;
final BackupConfiguration? backupConfiguration;
@override
List<Object?> get props => [backups, backupConfiguration];
}
class ForceSnapshotListUpdate extends BackupsEvent {
const ForceSnapshotListUpdate();
@override
List<Object?> get props => [];
}
class CreateBackups extends BackupsEvent {
const CreateBackups(this.services);
final List<Service> services;
@override
List<Object?> get props => [services];
}
class RestoreBackup extends BackupsEvent {
const RestoreBackup(this.backupId, this.restoreStrategy);
final String backupId;
final BackupRestoreStrategy restoreStrategy;
@override
List<Object?> get props => [backupId, restoreStrategy];
}
class SetAutobackupPeriod extends BackupsEvent {
const SetAutobackupPeriod(this.period);
final Duration? period;
@override
List<Object?> get props => [period];
}
class SetAutobackupQuotas extends BackupsEvent {
const SetAutobackupQuotas(this.quotas);
final AutobackupQuotas quotas;
@override
List<Object?> get props => [quotas];
}
class ForgetSnapshot extends BackupsEvent {
const ForgetSnapshot(this.backupId);
final String backupId;
@override
List<Object?> get props => [backupId];
}

View File

@ -0,0 +1,168 @@
part of 'backups_bloc.dart';
sealed class BackupsState extends Equatable {
BackupsState({
this.backblazeBucket,
});
final apiConnectionRepository = getIt<ApiConnectionRepository>();
final BackblazeBucket? backblazeBucket;
@Deprecated('Infer the initializations status from state')
bool get isInitialized => false;
@Deprecated('Infer the loading status from state')
bool get refreshing => false;
@Deprecated('Infer the prevent actions status from state')
bool get preventActions => true;
List<Backup> get backups => [];
List<Backup> serviceBackups(final String serviceId) => [];
Duration? get autobackupPeriod => null;
AutobackupQuotas? get autobackupQuotas => null;
BackupsState copyWith({required final BackblazeBucket backblazeBucket});
}
class BackupsInitial extends BackupsState {
BackupsInitial({
super.backblazeBucket,
});
@override
List<Object> get props => [];
@override
BackupsInitial copyWith({
final BackblazeBucket? backblazeBucket,
}) =>
BackupsInitial(backblazeBucket: backblazeBucket ?? this.backblazeBucket);
}
class BackupsLoading extends BackupsState {
BackupsLoading({
super.backblazeBucket,
});
@override
List<Object> get props => [];
@override
@Deprecated('Infer the loading status from state')
bool get refreshing => true;
@override
BackupsLoading copyWith({
final BackblazeBucket? backblazeBucket,
}) =>
BackupsLoading(backblazeBucket: backblazeBucket ?? this.backblazeBucket);
}
class BackupsUnititialized extends BackupsState {
BackupsUnititialized({
super.backblazeBucket,
});
@override
List<Object> get props => [];
@override
BackupsUnititialized copyWith({
final BackblazeBucket? backblazeBucket,
}) =>
BackupsUnititialized(
backblazeBucket: backblazeBucket ?? this.backblazeBucket);
}
class BackupsInitializing extends BackupsState {
BackupsInitializing({
super.backblazeBucket,
});
@override
List<Object> get props => [];
@override
BackupsInitializing copyWith({
final BackblazeBucket? backblazeBucket,
}) =>
BackupsInitializing(
backblazeBucket: backblazeBucket ?? this.backblazeBucket);
}
class BackupsInitialized extends BackupsState {
BackupsInitialized({
final List<Backup> backups = const [],
final BackupConfiguration? backupConfig,
super.backblazeBucket,
}) : _backupsHashCode = Object.hashAll(backups),
_backupConfigHashCode = Object.hashAll([backupConfig]);
final int _backupsHashCode;
final int _backupConfigHashCode;
List<Backup> get _backupList =>
apiConnectionRepository.apiData.backups.data ?? [];
BackupConfiguration? get _backupConfig =>
apiConnectionRepository.apiData.backupConfig.data;
@override
AutobackupQuotas? get autobackupQuotas => _backupConfig?.autobackupQuotas;
@override
Duration? get autobackupPeriod =>
_backupConfig?.autobackupPeriod?.inMinutes == 0
? null
: _backupConfig?.autobackupPeriod;
@override
@Deprecated('Infer the initializations status from state')
bool get isInitialized => true;
@override
@Deprecated('Infer the prevent actions status from state')
bool get preventActions => false;
@override
List<Backup> get backups {
try {
final List<Backup> list = _backupList;
list.sort((final a, final b) => b.time.compareTo(a.time));
return list;
} on UnsupportedError {
return _backupList;
}
}
@override
List<Backup> serviceBackups(final String serviceId) => backups
.where((final backup) => backup.serviceId == serviceId)
.toList(growable: false);
@override
List<Object> get props => [_backupsHashCode, _backupConfigHashCode];
@override
BackupsState copyWith({required final BackblazeBucket backblazeBucket}) =>
BackupsInitialized(
backups: backups,
backupConfig: _backupConfig,
backblazeBucket: backblazeBucket,
);
}
class BackupsBusy extends BackupsInitialized {
BackupsBusy.fromState(final BackupsInitialized state)
: super(
backups: state.backups,
backupConfig: state._backupConfig,
backblazeBucket: state.backblazeBucket,
);
@override
@Deprecated('Infer the prevent actions status from state')
bool get preventActions => true;
@override
List<Object> get props => [];
}

View File

@ -3,7 +3,6 @@ import 'dart:async';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/get_it/api_connection_repository.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
export 'package:provider/provider.dart';

View File

@ -1,278 +0,0 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
import 'package:selfprivacy/logic/cubit/server_connection_dependent/server_connection_dependent_cubit.dart';
import 'package:selfprivacy/logic/get_it/api_connection_repository.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
import 'package:selfprivacy/logic/models/backup.dart';
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
import 'package:selfprivacy/logic/models/initialize_repository_input.dart';
import 'package:selfprivacy/logic/models/service.dart';
part 'backups_state.dart';
class BackupsCubit extends ServerConnectionDependentCubit<BackupsState> {
BackupsCubit(final ServerInstallationCubit serverInstallationCubit)
: super(
const BackupsState(preventActions: true),
);
final ServerApi api = ServerApi();
final BackblazeApi backblaze = BackblazeApi();
@override
Future<void> load() async {
final BackblazeBucket? bucket = getIt<ApiConfigModel>().backblazeBucket;
final BackupConfiguration? backupConfig =
await api.getBackupsConfiguration();
final List<Backup> backups = await api.getBackups();
backups.sort((final a, final b) => b.time.compareTo(a.time));
emit(
state.copyWith(
backblazeBucket: bucket,
isInitialized: backupConfig?.isInitialized,
autobackupPeriod: backupConfig?.autobackupPeriod ?? Duration.zero,
autobackupQuotas: backupConfig?.autobackupQuotas,
backups: backups,
preventActions: false,
refreshing: false,
),
);
}
Future<void> initializeBackups() async {
emit(state.copyWith(preventActions: true));
final String? encryptionKey =
(await api.getBackupsConfiguration())?.encryptionKey;
if (encryptionKey == null) {
getIt<NavigationService>()
.showSnackBar("Couldn't get encryption key from your server.");
emit(state.copyWith(preventActions: false));
return;
}
final BackblazeBucket bucket;
if (state.backblazeBucket == null) {
final String domain = getIt<ApiConnectionRepository>()
.serverDomain!
.domainName
.replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-');
final int serverId = getIt<ApiConnectionRepository>().serverDetails!.id;
String bucketName =
'${DateTime.now().millisecondsSinceEpoch}-$serverId-$domain';
if (bucketName.length > 49) {
bucketName = bucketName.substring(0, 49);
}
final String bucketId = await backblaze.createBucket(bucketName);
final BackblazeApplicationKey key = await backblaze.createKey(bucketId);
bucket = BackblazeBucket(
bucketId: bucketId,
bucketName: bucketName,
applicationKey: key.applicationKey,
applicationKeyId: key.applicationKeyId,
encryptionKey: encryptionKey,
);
await getIt<ApiConfigModel>().storeBackblazeBucket(bucket);
emit(state.copyWith(backblazeBucket: bucket));
} else {
bucket = state.backblazeBucket!;
}
final GenericResult result = await api.initializeRepository(
InitializeRepositoryInput(
provider: BackupsProviderType.backblaze,
locationId: bucket.bucketId,
locationName: bucket.bucketName,
login: bucket.applicationKeyId,
password: bucket.applicationKey,
),
);
if (result.success == false) {
getIt<NavigationService>()
.showSnackBar(result.message ?? 'Unknown error');
emit(state.copyWith(preventActions: false));
return;
}
await updateBackups();
getIt<NavigationService>().showSnackBar(
'Backups repository is now initializing. It may take a while.',
);
emit(state.copyWith(preventActions: false));
}
Future<void> reuploadKey() async {
emit(state.copyWith(preventActions: true));
BackblazeBucket? bucket = getIt<ApiConfigModel>().backblazeBucket;
if (bucket == null) {
emit(state.copyWith(isInitialized: false));
} else {
String login = bucket.applicationKeyId;
String password = bucket.applicationKey;
if (login.isEmpty || password.isEmpty) {
final BackblazeApplicationKey key =
await backblaze.createKey(bucket.bucketId);
login = key.applicationKeyId;
password = key.applicationKey;
bucket = BackblazeBucket(
bucketId: bucket.bucketId,
bucketName: bucket.bucketName,
encryptionKey: bucket.encryptionKey,
applicationKey: password,
applicationKeyId: login,
);
await getIt<ApiConfigModel>().storeBackblazeBucket(bucket);
emit(state.copyWith(backblazeBucket: bucket));
}
final GenericResult result = await api.initializeRepository(
InitializeRepositoryInput(
provider: BackupsProviderType.backblaze,
locationId: bucket.bucketId,
locationName: bucket.bucketName,
login: login,
password: password,
),
);
if (result.success == false) {
getIt<NavigationService>()
.showSnackBar(result.message ?? 'Unknown error');
emit(state.copyWith(preventActions: false));
return;
} else {
emit(state.copyWith(preventActions: false));
getIt<NavigationService>().showSnackBar('backup.reuploaded_key'.tr());
await updateBackups();
}
}
}
@Deprecated("we don't have states")
Duration refreshTimeFromState() => const Duration(seconds: 60);
Future<void> updateBackups({final bool useTimer = false}) async {
emit(state.copyWith(refreshing: true));
final backups = await api.getBackups();
backups.sort((final a, final b) => b.time.compareTo(a.time));
final backupConfig = await api.getBackupsConfiguration();
emit(
state.copyWith(
backups: backups,
refreshTimer: refreshTimeFromState(),
refreshing: false,
isInitialized: backupConfig?.isInitialized ?? false,
autobackupPeriod: backupConfig?.autobackupPeriod,
autobackupQuotas: backupConfig?.autobackupQuotas,
),
);
if (useTimer) {
Timer(state.refreshTimer, () => updateBackups(useTimer: true));
}
}
Future<void> forceUpdateBackups() async {
emit(state.copyWith(preventActions: true));
getIt<NavigationService>().showSnackBar('backup.refetching_list'.tr());
await api.forceBackupListReload();
emit(state.copyWith(preventActions: false));
}
Future<void> createMultipleBackups(final List<Service> services) async {
emit(state.copyWith(preventActions: true));
for (final service in services) {
await api.startBackup(service.id);
}
await updateBackups();
emit(state.copyWith(preventActions: false));
}
Future<void> createBackup(final String serviceId) async {
emit(state.copyWith(preventActions: true));
await api.startBackup(serviceId);
await updateBackups();
emit(state.copyWith(preventActions: false));
}
Future<void> restoreBackup(
final String backupId,
final BackupRestoreStrategy strategy,
) async {
emit(state.copyWith(preventActions: true));
await api.restoreBackup(backupId, strategy);
emit(state.copyWith(preventActions: false));
}
Future<void> setAutobackupPeriod(final Duration? period) async {
emit(state.copyWith(preventActions: true));
final result = await api.setAutobackupPeriod(period: period?.inMinutes);
if (result.success == false) {
getIt<NavigationService>()
.showSnackBar(result.message ?? 'Unknown error');
emit(state.copyWith(preventActions: false));
} else {
getIt<NavigationService>()
.showSnackBar('backup.autobackup_period_set'.tr());
emit(
state.copyWith(
preventActions: false,
autobackupPeriod: period ?? Duration.zero,
),
);
}
await updateBackups();
}
Future<void> setAutobackupQuotas(final AutobackupQuotas quotas) async {
emit(state.copyWith(preventActions: true));
final result = await api.setAutobackupQuotas(quotas);
if (result.success == false) {
getIt<NavigationService>()
.showSnackBar(result.message ?? 'Unknown error');
emit(state.copyWith(preventActions: false));
} else {
getIt<NavigationService>().showSnackBar('backup.quotas_set'.tr());
emit(
state.copyWith(
preventActions: false,
autobackupQuotas: quotas,
),
);
}
await updateBackups();
}
Future<void> forgetSnapshot(final String snapshotId) async {
final result = await api.forgetSnapshot(snapshotId);
if (!result.success) {
getIt<NavigationService>().showSnackBar('jobs.generic_error'.tr());
return;
}
if (result.data == false) {
getIt<NavigationService>()
.showSnackBar('backup.forget_snapshot_error'.tr());
}
// Optimistic update
final backups = state.backups;
final index =
backups.indexWhere((final snapshot) => snapshot.id == snapshotId);
if (index != -1) {
backups.removeAt(index);
emit(state.copyWith(backups: backups));
}
await updateBackups();
}
@override
void clear() async {
emit(const BackupsState());
}
}

View File

@ -1,61 +0,0 @@
part of 'backups_cubit.dart';
class BackupsState extends ServerInstallationDependendState {
const BackupsState({
this.isInitialized = false,
this.backups = const [],
this.preventActions = true,
this.refreshTimer = const Duration(seconds: 60),
this.refreshing = true,
this.autobackupPeriod,
this.backblazeBucket,
this.autobackupQuotas,
});
final bool isInitialized;
final List<Backup> backups;
final bool preventActions;
final Duration refreshTimer;
final bool refreshing;
final Duration? autobackupPeriod;
final BackblazeBucket? backblazeBucket;
final AutobackupQuotas? autobackupQuotas;
List<Backup> serviceBackups(final String serviceId) => backups
.where((final backup) => backup.serviceId == serviceId)
.toList(growable: false);
@override
List<Object> get props => [
isInitialized,
backups,
preventActions,
refreshTimer,
refreshing,
];
BackupsState copyWith({
final bool? isInitialized,
final List<Backup>? backups,
final bool? preventActions,
final Duration? refreshTimer,
final bool? refreshing,
final Duration? autobackupPeriod,
final BackblazeBucket? backblazeBucket,
final AutobackupQuotas? autobackupQuotas,
}) =>
BackupsState(
isInitialized: isInitialized ?? this.isInitialized,
backups: backups ?? this.backups,
preventActions: preventActions ?? this.preventActions,
refreshTimer: refreshTimer ?? this.refreshTimer,
refreshing: refreshing ?? this.refreshing,
// The autobackupPeriod might be null, so if the duration is set to 0, we
// set it to null.
autobackupPeriod: autobackupPeriod?.inSeconds == 0
? null
: autobackupPeriod ?? this.autobackupPeriod,
backblazeBucket: backblazeBucket ?? this.backblazeBucket,
autobackupQuotas: autobackupQuotas ?? this.autobackupQuotas,
);
}

View File

@ -1,7 +1,6 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/get_it/api_connection_repository.dart';
part 'connection_status_event.dart';
part 'connection_status_state.dart';

View File

@ -2,7 +2,6 @@ import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart';
import 'package:selfprivacy/logic/cubit/server_connection_dependent/server_connection_dependent_cubit.dart';
import 'package:selfprivacy/logic/get_it/api_connection_repository.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/json/dns_records.dart';

View File

@ -2,7 +2,6 @@ import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/get_it/api_connection_repository.dart';
export 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';

View File

@ -7,7 +7,6 @@ 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/api_maps/rest_maps/backblaze.dart';
import 'package:selfprivacy/logic/api_maps/tls_options.dart';
import 'package:selfprivacy/logic/get_it/api_connection_repository.dart';
import 'package:selfprivacy/logic/models/disk_size.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
@ -234,7 +233,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
try {
bucket = await BackblazeApi()
.fetchBucket(backblazeCredential, configuration);
await getIt<ApiConfigModel>().storeBackblazeBucket(bucket!);
await getIt<ApiConfigModel>().setBackblazeBucket(bucket!);
} catch (e) {
print(e);
}

View File

@ -470,7 +470,7 @@ class ServerInstallationRepository {
Future<void> saveServerDetails(
final ServerHostingDetails serverDetails,
) async {
await getIt<ApiConfigModel>().storeServerDetails(serverDetails);
await getIt<ApiConfigModel>().setServerDetails(serverDetails);
}
Future<void> deleteServerDetails() async {
@ -483,18 +483,18 @@ class ServerInstallationRepository {
}
Future<void> saveDnsProviderType(final DnsProviderType type) async {
await getIt<ApiConfigModel>().storeDnsProviderType(type);
await getIt<ApiConfigModel>().setDnsProviderType(type);
}
Future<void> saveServerProviderKey(final String key) async {
await getIt<ApiConfigModel>().storeServerProviderKey(key);
await getIt<ApiConfigModel>().setServerProviderKey(key);
}
Future<void> saveServerType(final ServerType serverType) async {
await getIt<ApiConfigModel>().storeServerTypeIdentifier(
await getIt<ApiConfigModel>().setServerTypeIdentifier(
serverType.identifier,
);
await getIt<ApiConfigModel>().storeServerLocation(
await getIt<ApiConfigModel>().setServerLocation(
serverType.location.identifier,
);
}
@ -507,7 +507,7 @@ class ServerInstallationRepository {
Future<void> saveBackblazeKey(
final BackupsCredential backblazeCredential,
) async {
await getIt<ApiConfigModel>().storeBackblazeCredential(backblazeCredential);
await getIt<ApiConfigModel>().setBackblazeCredential(backblazeCredential);
}
Future<void> deleteBackblazeKey() async {
@ -516,7 +516,7 @@ class ServerInstallationRepository {
}
Future<void> setDnsApiToken(final String key) async {
await getIt<ApiConfigModel>().storeDnsProviderKey(key);
await getIt<ApiConfigModel>().setDnsProviderKey(key);
}
Future<void> deleteDnsProviderKey() async {
@ -525,7 +525,7 @@ class ServerInstallationRepository {
}
Future<void> saveDomain(final ServerDomain serverDomain) async {
await getIt<ApiConfigModel>().storeServerDomain(serverDomain);
await getIt<ApiConfigModel>().setServerDomain(serverDomain);
}
Future<void> deleteDomain() async {

View File

@ -6,7 +6,6 @@ import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
import 'package:selfprivacy/logic/cubit/server_connection_dependent/server_connection_dependent_cubit.dart';
import 'package:selfprivacy/logic/get_it/api_connection_repository.dart';
import 'package:selfprivacy/logic/models/hive/user.dart';
export 'package:provider/provider.dart';

View File

@ -42,47 +42,47 @@ class ApiConfigModel {
_serverProvider = value;
}
Future<void> storeDnsProviderType(final DnsProviderType value) async {
Future<void> setDnsProviderType(final DnsProviderType value) async {
await _box.put(BNames.dnsProvider, value);
_dnsProvider = value;
}
Future<void> storeServerProviderKey(final String value) async {
Future<void> setServerProviderKey(final String value) async {
await _box.put(BNames.hetznerKey, value);
_serverProviderKey = value;
}
Future<void> storeDnsProviderKey(final String value) async {
Future<void> setDnsProviderKey(final String value) async {
await _box.put(BNames.cloudFlareKey, value);
_dnsProviderKey = value;
}
Future<void> storeServerTypeIdentifier(final String typeIdentifier) async {
Future<void> setServerTypeIdentifier(final String typeIdentifier) async {
await _box.put(BNames.serverTypeIdentifier, typeIdentifier);
_serverType = typeIdentifier;
}
Future<void> storeServerLocation(final String serverLocation) async {
Future<void> setServerLocation(final String serverLocation) async {
await _box.put(BNames.serverLocation, serverLocation);
_serverLocation = serverLocation;
}
Future<void> storeBackblazeCredential(final BackupsCredential value) async {
Future<void> setBackblazeCredential(final BackupsCredential value) async {
await _box.put(BNames.backblazeCredential, value);
_backblazeCredential = value;
}
Future<void> storeServerDomain(final ServerDomain value) async {
Future<void> setServerDomain(final ServerDomain value) async {
await _box.put(BNames.serverDomain, value);
_serverDomain = value;
}
Future<void> storeServerDetails(final ServerHostingDetails value) async {
Future<void> setServerDetails(final ServerHostingDetails value) async {
await _box.put(BNames.serverDetails, value);
_serverDetails = value;
}
Future<void> storeBackblazeBucket(final BackblazeBucket value) async {
Future<void> setBackblazeBucket(final BackblazeBucket value) async {
await _box.put(BNames.backblazeBucket, value);
_backblazeBucket = value;
}

View File

@ -5,6 +5,7 @@ import 'package:pub_semver/pub_semver.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
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/server_job.dart';
@ -15,7 +16,7 @@ class ApiConnectionRepository {
Box box = Hive.box(BNames.serverInstallationBox);
final ServerApi api = ServerApi();
final ApiData _apiData = ApiData();
final ApiData _apiData = ApiData(ServerApi());
ApiData get apiData => _apiData;
@ -66,15 +67,18 @@ class ApiConnectionRepository {
_connectionStatusStream.add(connectionStatus);
return;
} else {
connectionStatus = ConnectionStatus.connected;
_connectionStatusStream.add(connectionStatus);
_apiData.apiVersion.data = apiVersion;
_dataStream.add(_apiData);
}
_apiData.serverJobs.data = await api.getServerJobs();
_apiData.backupConfig.data = await api.getBackupsConfiguration();
_apiData.backups.data = await api.getBackups();
_dataStream.add(_apiData);
connectionStatus = ConnectionStatus.connected;
_connectionStatusStream.add(connectionStatus);
// Use timer to periodically check for new jobs
_timer = Timer.periodic(
const Duration(seconds: 10),
@ -82,7 +86,7 @@ class ApiConnectionRepository {
);
}
void reload(final Timer timer) async {
Future<void> reload(final Timer? timer) async {
final serverDetails = getIt<ApiConfigModel>().serverDetails;
if (serverDetails == null) {
return;
@ -98,26 +102,42 @@ class ApiConnectionRepository {
_connectionStatusStream.add(connectionStatus);
_apiData.apiVersion.data = apiVersion;
}
final Version version = Version.parse(apiVersion);
await _apiData.serverJobs
.refetchData(version, () => _dataStream.add(_apiData));
await _apiData.backups
.refetchData(version, () => _dataStream.add(_apiData));
await _apiData.backupConfig
.refetchData(version, () => _dataStream.add(_apiData));
}
if (VersionConstraint.parse(_apiData.apiVersion.requiredApiVersion)
.allows(Version.parse(apiVersion))) {
final jobs = await api.getServerJobs();
if (Object.hashAll(_apiData.serverJobs.data ?? []) !=
Object.hashAll(jobs)) {
_apiData.serverJobs.data = [...jobs];
_dataStream.add(_apiData);
}
}
void emitData() {
_dataStream.add(_apiData);
}
}
class ApiData {
ApiData()
: serverJobs = ApiDataElement<List<ServerJob>>(null),
apiVersion = ApiDataElement<String>(null);
ApiData(final ServerApi api)
: apiVersion = ApiDataElement<String>(
fetchData: () async => api.getApiVersion(),
),
serverJobs = ApiDataElement<List<ServerJob>>(
fetchData: () async => api.getServerJobs(),
ttl: 10,
),
backupConfig = ApiDataElement<BackupConfiguration>(
fetchData: () async => api.getBackupsConfiguration(),
requiredApiVersion: '>=2.4.2',
),
backups = ApiDataElement<List<Backup>>(
fetchData: () async => api.getBackups(),
requiredApiVersion: '>=2.4.2',
);
ApiDataElement<List<ServerJob>> serverJobs;
ApiDataElement<String> apiVersion;
ApiDataElement<BackupConfiguration> backupConfig;
ApiDataElement<List<Backup>> backups;
}
enum ConnectionStatus {
@ -129,8 +149,9 @@ enum ConnectionStatus {
}
class ApiDataElement<T> {
ApiDataElement(
final T? data, {
ApiDataElement({
required this.fetchData,
final T? data,
this.requiredApiVersion = '>=2.3.0',
this.ttl = 60,
}) : _data = data,
@ -139,9 +160,40 @@ class ApiDataElement<T> {
T? _data;
final String requiredApiVersion;
final Future<T?> Function() fetchData;
Future<void> refetchData(
final Version version, final Function callback) async {
if (VersionConstraint.parse(requiredApiVersion).allows(version)) {
print('Fetching data for $runtimeType');
if (isExpired) {
print('Data is expired');
final newData = await fetchData();
print(newData);
if (T is List) {
if (Object.hashAll(newData as Iterable<Object?>) !=
Object.hashAll(_data as Iterable<Object?>)) {
_data = [...newData] as T?;
}
} else {
if (newData.hashCode != _data.hashCode) {
_data = newData;
}
}
callback();
}
}
}
/// TTL of the data in seconds
final int ttl;
Type get type => T;
void invalidate() {
_lastUpdated = DateTime.fromMillisecondsSinceEpoch(0);
}
/// Timestamp of when the data was last updated
DateTime _lastUpdated;

View File

@ -1,3 +1,4 @@
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/backups.graphql.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart';
@ -40,7 +41,7 @@ extension BackupReasonExtension on Enum$BackupReason {
};
}
class BackupConfiguration {
class BackupConfiguration extends Equatable {
BackupConfiguration.fromGraphQL(
final Query$BackupConfiguration$backup$configuration configuration,
) : this(
@ -58,7 +59,7 @@ class BackupConfiguration {
),
);
BackupConfiguration({
const BackupConfiguration({
required this.autobackupPeriod,
required this.encryptionKey,
required this.isInitialized,
@ -75,9 +76,39 @@ class BackupConfiguration {
final String? locationName;
final BackupsProviderType provider;
final AutobackupQuotas autobackupQuotas;
@override
List<Object?> get props => [
autobackupPeriod,
encryptionKey,
isInitialized,
locationId,
locationName,
provider,
autobackupQuotas,
];
BackupConfiguration copyWith({
final Duration? autobackupPeriod,
final String? encryptionKey,
final bool? isInitialized,
final String? locationId,
final String? locationName,
final BackupsProviderType? provider,
final AutobackupQuotas? autobackupQuotas,
}) =>
BackupConfiguration(
autobackupPeriod: autobackupPeriod ?? this.autobackupPeriod,
encryptionKey: encryptionKey ?? this.encryptionKey,
isInitialized: isInitialized ?? this.isInitialized,
locationId: locationId ?? this.locationId,
locationName: locationName ?? this.locationName,
provider: provider ?? this.provider,
autobackupQuotas: autobackupQuotas ?? this.autobackupQuotas,
);
}
class AutobackupQuotas {
class AutobackupQuotas extends Equatable {
AutobackupQuotas.fromGraphQL(
final Query$BackupConfiguration$backup$configuration$autobackupQuotas
autobackupQuotas,
@ -89,7 +120,7 @@ class AutobackupQuotas {
yearly: autobackupQuotas.yearly,
);
AutobackupQuotas({
const AutobackupQuotas({
required this.last,
required this.daily,
required this.weekly,
@ -117,6 +148,15 @@ class AutobackupQuotas {
monthly: monthly ?? this.monthly,
yearly: yearly ?? this.yearly,
);
@override
List<Object?> get props => [
last,
daily,
weekly,
monthly,
yearly,
];
}
enum BackupRestoreStrategy {

View File

@ -29,4 +29,19 @@ class BackblazeBucket {
@override
String toString() => bucketName;
BackblazeBucket copyWith({
final String? bucketId,
final String? applicationKeyId,
final String? applicationKey,
final String? bucketName,
final String? encryptionKey,
}) =>
BackblazeBucket(
bucketId: bucketId ?? this.bucketId,
applicationKeyId: applicationKeyId ?? this.applicationKeyId,
applicationKey: applicationKey ?? this.applicationKey,
bucketName: bucketName ?? this.bucketName,
encryptionKey: encryptionKey ?? this.encryptionKey,
);
}

View File

@ -4,7 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_loader/brand_loader.dart';

View File

@ -2,9 +2,9 @@ import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/models/backup.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
@ -31,14 +31,13 @@ class BackupDetailsPage extends StatelessWidget {
Widget build(final BuildContext context) {
final bool isReady = context.watch<ServerInstallationCubit>().state
is ServerInstallationFinished;
final BackupsState backupsState = context.watch<BackupsCubit>().state;
final BackupsState backupsState = context.watch<BackupsBloc>().state;
final bool isBackupInitialized = backupsState.isInitialized;
final StateType providerState = isReady && isBackupInitialized
? StateType.stable
: StateType.uninitialized;
final bool preventActions = backupsState.preventActions;
final List<Backup> backups = backupsState.backups;
final bool refreshing = backupsState.refreshing;
final List<Service> services =
context.watch<ServicesCubit>().state.servicesThatCanBeBackedUp;
final Duration? autobackupPeriod = backupsState.autobackupPeriod;
@ -75,8 +74,10 @@ class BackupDetailsPage extends StatelessWidget {
BrandButton.rised(
onPressed: preventActions
? null
: () async {
await context.read<BackupsCubit>().initializeBackups();
: () {
context
.read<BackupsBloc>()
.add(const InitializeBackupsRepository());
},
text: 'backup.initialize'.tr(),
),
@ -335,8 +336,10 @@ class BackupDetailsPage extends StatelessWidget {
actionButtonTitle:
'backup.forget_snapshot'.tr(),
actionButtonOnPressed: () => {
context.read<BackupsCubit>().forgetSnapshot(
backup.id,
context.read<BackupsBloc>().add(
ForgetSnapshot(
backup.id,
),
),
},
);
@ -391,18 +394,6 @@ class BackupDetailsPage extends StatelessWidget {
const SizedBox(height: 8),
const Divider(),
const SizedBox(height: 8),
ListTile(
title: Text(
'backup.refresh'.tr(),
),
onTap: refreshing
? null
: () => {context.read<BackupsCubit>().updateBackups()},
enabled: !refreshing,
leading: const Icon(
Icons.refresh_outlined,
),
),
if (providerState != StateType.uninitialized)
Column(
children: [
@ -425,7 +416,11 @@ class BackupDetailsPage extends StatelessWidget {
),
onTap: preventActions
? null
: () => {context.read<BackupsCubit>().forceUpdateBackups()},
: () => {
context
.read<BackupsBloc>()
.add(const ForceSnapshotListUpdate())
},
),
const SizedBox(height: 8),
const Divider(),
@ -447,9 +442,9 @@ class BackupDetailsPage extends StatelessWidget {
Icons.warning_amber_outlined,
color: overrideColor,
),
onTap: preventActions
? null
: () => {context.read<BackupsCubit>().reuploadKey()},
// onTap: preventActions
// ? null
// : () => {context.read<BackupsCubit>().reuploadKey()},
),
],
),

View File

@ -3,7 +3,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/models/backup.dart';
import 'package:selfprivacy/logic/models/service.dart';
@ -25,10 +25,10 @@ class BackupsListPage extends StatelessWidget {
// If the service is null, get all backups from state. If not null, call the
// serviceBackups(serviceId) on the backups state.
final List<Backup> backups = service == null
? context.watch<BackupsCubit>().state.backups
: context.watch<BackupsCubit>().state.serviceBackups(service!.id);
? context.watch<BackupsBloc>().state.backups
: context.watch<BackupsBloc>().state.serviceBackups(service!.id);
final bool preventActions =
context.watch<BackupsCubit>().state.preventActions;
context.watch<BackupsBloc>().state.preventActions;
return BrandHeroScreen(
heroTitle: 'backup.snapshots_title'.tr(),
hasFlashButton: true,
@ -76,9 +76,9 @@ class BackupsListPage extends StatelessWidget {
description: 'backup.forget_snapshot_alert'.tr(),
actionButtonTitle: 'backup.forget_snapshot'.tr(),
actionButtonOnPressed: () => {
context.read<BackupsCubit>().forgetSnapshot(
backup.id,
),
context
.read<BackupsBloc>()
.add(ForgetSnapshot(backup.id)),
},
);
},

View File

@ -1,8 +1,8 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/utils/extensions/duration.dart';
class ChangeAutobackupsPeriodModal extends StatefulWidget {
@ -34,13 +34,13 @@ class _ChangeAutobackupsPeriodModalState
@override
void initState() {
super.initState();
selectedPeriod = context.read<BackupsCubit>().state.autobackupPeriod;
selectedPeriod = context.read<BackupsBloc>().state.autobackupPeriod;
}
@override
Widget build(final BuildContext context) {
final Duration? initialAutobackupPeriod =
context.watch<BackupsCubit>().state.autobackupPeriod;
context.watch<BackupsBloc>().state.autobackupPeriod;
return ListView(
controller: widget.scrollController,
padding: const EdgeInsets.all(16),
@ -91,8 +91,8 @@ class _ChangeAutobackupsPeriodModalState
? null
: () {
context
.read<BackupsCubit>()
.setAutobackupPeriod(selectedPeriod);
.read<BackupsBloc>()
.add(SetAutobackupPeriod(selectedPeriod));
Navigator.of(context).pop();
},
child: Text(

View File

@ -1,8 +1,8 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/models/backup.dart';
class ChangeRotationQuotasModal extends StatefulWidget {
@ -27,7 +27,7 @@ enum QuotaUnits {
}
class _ChangeRotationQuotasModalState extends State<ChangeRotationQuotasModal> {
AutobackupQuotas selectedQuotas = AutobackupQuotas(
AutobackupQuotas selectedQuotas = const AutobackupQuotas(
last: 3,
daily: 7,
weekly: 4,
@ -40,7 +40,7 @@ class _ChangeRotationQuotasModalState extends State<ChangeRotationQuotasModal> {
void initState() {
super.initState();
selectedQuotas =
context.read<BackupsCubit>().state.autobackupQuotas ?? selectedQuotas;
context.read<BackupsBloc>().state.autobackupQuotas ?? selectedQuotas;
}
String generateSubtitle(final int value, final QuotaUnits unit) {
@ -83,7 +83,7 @@ class _ChangeRotationQuotasModalState extends State<ChangeRotationQuotasModal> {
@override
Widget build(final BuildContext context) {
final AutobackupQuotas? initialAutobackupQuotas =
context.watch<BackupsCubit>().state.autobackupQuotas;
context.watch<BackupsBloc>().state.autobackupQuotas;
return ListView(
controller: widget.scrollController,
padding: const EdgeInsets.all(16),
@ -190,8 +190,8 @@ class _ChangeRotationQuotasModalState extends State<ChangeRotationQuotasModal> {
? null
: () {
context
.read<BackupsCubit>()
.setAutobackupQuotas(selectedQuotas);
.read<BackupsBloc>()
.add(SetAutobackupQuotas(selectedQuotas));
Navigator.of(context).pop();
},
child: Text(

View File

@ -2,9 +2,9 @@ import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
import 'package:selfprivacy/utils/platform_adapter.dart';
@ -34,7 +34,7 @@ class _CopyEncryptionKeyModalState extends State<CopyEncryptionKeyModal> {
@override
Widget build(final BuildContext context) {
final String? encryptionKey =
context.watch<BackupsCubit>().state.backblazeBucket?.encryptionKey;
context.watch<BackupsBloc>().state.backblazeBucket?.encryptionKey;
if (encryptionKey == null) {
return ListView(
controller: widget.scrollController,

View File

@ -1,8 +1,8 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart';
import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
import 'package:selfprivacy/logic/models/service.dart';
@ -147,8 +147,8 @@ class _CreateBackupsModalState extends State<CreateBackupsModal> {
? null
: () {
context
.read<BackupsCubit>()
.createMultipleBackups(selectedServices);
.read<BackupsBloc>()
.add(CreateBackups(selectedServices));
Navigator.of(context).pop();
},
child: Text(

View File

@ -2,8 +2,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart';
import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/models/backup.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
@ -153,10 +153,8 @@ class _SnapshotModalState extends State<SnapshotModal> {
onPressed: isServiceBusy
? null
: () {
context.read<BackupsCubit>().restoreBackup(
widget.snapshot.id,
selectedStrategy,
);
context.read<BackupsBloc>().add(RestoreBackup(
widget.snapshot.id, selectedStrategy));
Navigator.of(context).pop();
getIt<NavigationService>()
.showSnackBar('backup.restore_started'.tr());

View File

@ -5,7 +5,6 @@ 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/devices/devices_cubit.dart';
import 'package:selfprivacy/logic/cubit/recovery_key/recovery_key_cubit.dart';
import 'package:selfprivacy/logic/get_it/api_connection_repository.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:easy_localization/easy_localization.dart';

View File

@ -2,7 +2,7 @@ import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart';
import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
@ -30,7 +30,7 @@ class _ProvidersPageState extends State<ProvidersPage> {
final bool isReady = context.watch<ServerInstallationCubit>().state
is ServerInstallationFinished;
final bool isBackupInitialized =
context.watch<BackupsCubit>().state.isInitialized;
context.watch<BackupsBloc>().state.isInitialized;
final DnsRecordsStatus dnsStatus =
context.watch<DnsRecordsCubit>().state.dnsState;

View File

@ -1,7 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/models/disk_size.dart';
import 'package:selfprivacy/logic/models/service.dart';