refactor: Rewrite services cubit to bloc, using ApiRepo streams

pull/440/head
Inex Code 2024-01-29 19:57:22 +04:00
parent a5e7725733
commit 831a0e95eb
18 changed files with 382 additions and 173 deletions

View File

@ -1,6 +1,7 @@
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/services/services_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';
@ -11,7 +12,6 @@ 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/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';
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.dart';
@ -28,7 +28,7 @@ class BlocAndProviderConfig extends StatelessWidget {
final serverInstallationCubit = ServerInstallationCubit()..load();
final supportSystemCubit = SupportSystemCubit();
final usersCubit = UsersCubit(serverInstallationCubit);
final servicesCubit = ServicesCubit(serverInstallationCubit);
final servicesBloc = ServicesBloc();
final backupsBloc = BackupsBloc();
final dnsRecordsCubit = DnsRecordsCubit(serverInstallationCubit);
final recoveryKeyCubit = RecoveryKeyCubit(serverInstallationCubit);
@ -61,8 +61,7 @@ class BlocAndProviderConfig extends StatelessWidget {
lazy: false,
),
BlocProvider(
create: (final _) => servicesCubit..load(),
lazy: false,
create: (final _) => servicesBloc,
),
BlocProvider(
create: (final _) => backupsBloc,
@ -92,7 +91,7 @@ class BlocAndProviderConfig extends StatelessWidget {
BlocProvider(
create: (final _) => JobsCubit(
usersCubit: usersCubit,
servicesCubit: servicesCubit,
servicesBloc: servicesBloc,
),
),
],

View File

@ -193,7 +193,8 @@ class BackupsBloc extends Bloc<BackupsEvent, BackupsState> {
);
if (result.success == false) {
getIt<NavigationService>().showSnackBar(
result.message ?? "Couldn't initialize repository on your server.");
result.message ?? "Couldn't initialize repository on your server.",
);
emit(BackupsUnititialized());
return;
}

View File

@ -71,7 +71,8 @@ class BackupsUnititialized extends BackupsState {
final BackblazeBucket? backblazeBucket,
}) =>
BackupsUnititialized(
backblazeBucket: backblazeBucket ?? this.backblazeBucket);
backblazeBucket: backblazeBucket ?? this.backblazeBucket,
);
}
class BackupsInitializing extends BackupsState {
@ -86,7 +87,8 @@ class BackupsInitializing extends BackupsState {
final BackblazeBucket? backblazeBucket,
}) =>
BackupsInitializing(
backblazeBucket: backblazeBucket ?? this.backblazeBucket);
backblazeBucket: backblazeBucket ?? this.backblazeBucket,
);
}
class BackupsInitialized extends BackupsState {

View File

@ -0,0 +1,144 @@
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/models/service.dart';
part 'services_event.dart';
part 'services_state.dart';
class ServicesBloc extends Bloc<ServicesEvent, ServicesState> {
ServicesBloc() : super(ServicesInitial()) {
final connectionRepository = getIt<ApiConnectionRepository>();
_apiDataSubscription = connectionRepository.dataStream.listen(
(final ApiData apiData) {
add(
ServicesListUpdate([...apiData.services.data ?? []]),
);
},
);
if (connectionRepository.connectionStatus == ConnectionStatus.connected) {
add(
ServicesListUpdate(
[...connectionRepository.apiData.services.data ?? []],
),
);
}
on<ServicesListUpdate>(
_updateList,
);
on<ServicesReload>(
_reload,
);
on<ServiceRestart>(
_restart,
);
on<ServiceMove>(
_move,
);
}
Future<void> _updateList(
final ServicesListUpdate event,
final Emitter<ServicesState> emit,
) async {
if (event.services.isEmpty) {
emit(ServicesInitial());
return;
}
final newState = ServicesLoaded(
services: event.services,
lockedServices: state._lockedServices,
);
emit(newState);
}
Future<void> _reload(
final ServicesReload event,
final Emitter<ServicesState> emit,
) async {
final currentState = state;
if (currentState is ServicesLoaded) {
emit(ServicesReloading.fromState(currentState));
getIt<ApiConnectionRepository>().apiData.services.invalidate();
await getIt<ApiConnectionRepository>().reload(null);
}
}
Future<void> awaitReload() async {
final currentState = state;
if (currentState is ServicesLoaded) {
getIt<ApiConnectionRepository>().apiData.services.invalidate();
await getIt<ApiConnectionRepository>().reload(null);
}
}
Future<void> _restart(
final ServiceRestart event,
final Emitter<ServicesState> emit,
) async {
emit(
state.copyWith(
lockedServices: [
...state._lockedServices,
ServiceLock(
serviceId: event.service.id,
lockDuration: const Duration(seconds: 15),
),
],
),
);
final result = await getIt<ApiConnectionRepository>()
.api
.restartService(event.service.id);
if (!result.success) {
getIt<NavigationService>().showSnackBar('jobs.generic_error'.tr());
return;
}
if (!result.data) {
getIt<NavigationService>()
.showSnackBar(result.message ?? 'jobs.generic_error'.tr());
return;
}
}
Future<void> _move(
final ServiceMove event,
final Emitter<ServicesState> emit,
) async {
final migrationJob = await getIt<ApiConnectionRepository>()
.api
.moveService(event.service.id, event.destination);
if (!migrationJob.success) {
getIt<NavigationService>()
.showSnackBar(migrationJob.message ?? 'jobs.generic_error'.tr());
}
if (migrationJob.data != null) {
getIt<ApiConnectionRepository>()
.apiData
.serverJobs
.data
?.add(migrationJob.data!);
getIt<ApiConnectionRepository>().emitData();
}
}
late StreamSubscription _apiDataSubscription;
@override
void onChange(final Change<ServicesState> change) {
super.onChange(change);
}
@override
Future<void> close() {
_apiDataSubscription.cancel();
return super.close();
}
}

View File

@ -0,0 +1,40 @@
part of 'services_bloc.dart';
abstract class ServicesEvent extends Equatable {
const ServicesEvent();
}
class ServicesListUpdate extends ServicesEvent {
const ServicesListUpdate(this.services);
final List<Service> services;
@override
List<Object?> get props => [services];
}
class ServicesReload extends ServicesEvent {
const ServicesReload();
@override
List<Object?> get props => [];
}
class ServiceRestart extends ServicesEvent {
const ServiceRestart(this.service);
final Service service;
@override
List<Object?> get props => [service];
}
class ServiceMove extends ServicesEvent {
const ServiceMove(this.service, this.destination);
final Service service;
final String destination;
@override
List<Object?> get props => [service, destination];
}

View File

@ -0,0 +1,115 @@
part of 'services_bloc.dart';
abstract class ServicesState extends Equatable {
ServicesState({final List<ServiceLock> lockedServices = const []})
: _lockedServices =
lockedServices.where((final lock) => lock.isLocked).toList();
final List<ServiceLock> _lockedServices;
List<Service> get services;
List<String> get lockedServices => _lockedServices
.where((final lock) => lock.isLocked)
.map((final lock) => lock.serviceId)
.toList();
List<Service> get servicesThatCanBeBackedUp => services
.where(
(final service) => service.canBeBackedUp,
)
.toList();
bool isServiceLocked(final String serviceId) =>
lockedServices.contains(serviceId);
Service? getServiceById(final String id) {
final service = services.firstWhere(
(final service) => service.id == id,
orElse: () => Service.empty,
);
if (service.id == 'empty') {
return null;
}
return service;
}
ServicesState copyWith({
final List<Service>? services,
final List<ServiceLock>? lockedServices,
});
}
class ServiceLock extends Equatable {
ServiceLock({
required this.serviceId,
required this.lockDuration,
}) : lockTime = DateTime.now();
final String serviceId;
final Duration lockDuration;
final DateTime lockTime;
bool get isLocked => DateTime.now().isBefore(lockTime.add(lockDuration));
@override
List<Object?> get props => [serviceId, lockDuration, lockTime];
}
class ServicesInitial extends ServicesState {
@override
List<Object> get props => [];
@override
List<Service> get services => [];
@override
ServicesState copyWith({
final List<Service>? services,
final List<ServiceLock>? lockedServices,
}) =>
ServicesInitial();
}
class ServicesLoaded extends ServicesState {
ServicesLoaded({
required final List<Service> services,
required super.lockedServices,
}) : _servicesHachCode = Object.hashAll([...services]);
final int _servicesHachCode;
final apiConnectionRepository = getIt<ApiConnectionRepository>();
List<Service> get _services =>
apiConnectionRepository.apiData.services.data ?? [];
@override
List<Service> get services => _services;
@override
List<Object?> get props => [_servicesHachCode, _lockedServices];
@override
ServicesLoaded copyWith({
final List<Service>? services,
final List<ServiceLock>? lockedServices,
}) =>
ServicesLoaded(
services: services ?? this.services,
lockedServices: lockedServices ?? _lockedServices,
);
}
class ServicesReloading extends ServicesLoaded {
ServicesReloading({
required super.services,
required super.lockedServices,
});
ServicesReloading.fromState(final ServicesLoaded state)
: super(
services: state.services,
lockedServices: state._lockedServices,
);
@override
List<Object?> get props => [services, lockedServices];
}

View File

@ -5,7 +5,7 @@ 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/graphql_maps/server_api/server_api.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
import 'package:selfprivacy/logic/models/job.dart';
@ -16,12 +16,12 @@ part 'client_jobs_state.dart';
class JobsCubit extends Cubit<JobsState> {
JobsCubit({
required this.usersCubit,
required this.servicesCubit,
required this.servicesBloc,
}) : super(JobsStateEmpty());
final ServerApi api = ServerApi();
final UsersCubit usersCubit;
final ServicesCubit servicesCubit;
final ServicesBloc servicesBloc;
void addJob(final ClientJob job) {
final jobs = currentJobList;
@ -90,7 +90,7 @@ class JobsCubit extends Cubit<JobsState> {
await api.pullConfigurationUpdate();
await api.apply();
await servicesCubit.load();
servicesBloc.add(const ServicesReload());
emit(JobsStateEmpty());
}

View File

@ -1,81 +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/graphql_maps/server_api/server_api.dart';
import 'package:selfprivacy/logic/cubit/server_connection_dependent/server_connection_dependent_cubit.dart';
import 'package:selfprivacy/logic/models/service.dart';
part 'services_state.dart';
class ServicesCubit extends ServerConnectionDependentCubit<ServicesState> {
ServicesCubit(final ServerInstallationCubit serverInstallationCubit)
: super(const ServicesState.empty());
final ServerApi api = ServerApi();
Timer? timer;
@override
Future<void> load() async {
final List<Service> services = await api.getAllServices();
emit(
ServicesState(
services: services,
lockedServices: const [],
),
);
timer = Timer(const Duration(seconds: 10), () => reload(useTimer: true));
}
Future<void> reload({final bool useTimer = false}) async {
final List<Service> services = await api.getAllServices();
emit(
state.copyWith(
services: services,
),
);
if (useTimer) {
timer = Timer(const Duration(seconds: 60), () => reload(useTimer: true));
}
}
Future<void> restart(final String serviceId) async {
emit(state.copyWith(lockedServices: [...state.lockedServices, serviceId]));
final result = await api.restartService(serviceId);
if (!result.success) {
getIt<NavigationService>().showSnackBar('jobs.generic_error'.tr());
return;
}
if (!result.data) {
getIt<NavigationService>()
.showSnackBar(result.message ?? 'jobs.generic_error'.tr());
return;
}
await Future.delayed(const Duration(seconds: 2));
unawaited(reload());
await Future.delayed(const Duration(seconds: 10));
emit(
state.copyWith(
lockedServices: state.lockedServices
.where((final element) => element != serviceId)
.toList(),
),
);
unawaited(reload());
}
Future<void> moveService(
final String serviceId,
final String destination,
) async {
await api.moveService(serviceId, destination);
}
@override
void clear() async {
emit(const ServicesState.empty());
if (timer != null && timer!.isActive) {
timer!.cancel();
timer = null;
}
}
}

View File

@ -1,49 +0,0 @@
part of 'services_cubit.dart';
class ServicesState extends ServerInstallationDependendState {
const ServicesState({
required this.services,
required this.lockedServices,
});
const ServicesState.empty()
: this(services: const [], lockedServices: const []);
final List<Service> services;
final List<String> lockedServices;
List<Service> get servicesThatCanBeBackedUp => services
.where(
(final service) => service.canBeBackedUp,
)
.toList();
bool isServiceLocked(final String serviceId) =>
lockedServices.contains(serviceId);
Service? getServiceById(final String id) {
final service = services.firstWhere(
(final service) => service.id == id,
orElse: () => Service.empty,
);
if (service.id == 'empty') {
return null;
}
return service;
}
@override
List<Object> get props => [
services,
lockedServices,
];
ServicesState copyWith({
final List<Service>? services,
final List<String>? lockedServices,
}) =>
ServicesState(
services: services ?? this.services,
lockedServices: lockedServices ?? this.lockedServices,
);
}

View File

@ -9,6 +9,7 @@ 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';
import 'package:selfprivacy/logic/models/service.dart';
/// Repository for all API calls
/// Stores the current state of all data from API and exposes it to Blocs.
@ -74,6 +75,7 @@ class ApiConnectionRepository {
_apiData.serverJobs.data = await api.getServerJobs();
_apiData.backupConfig.data = await api.getBackupsConfiguration();
_apiData.backups.data = await api.getBackups();
_apiData.services.data = await api.getAllServices();
_dataStream.add(_apiData);
connectionStatus = ConnectionStatus.connected;
@ -109,6 +111,8 @@ class ApiConnectionRepository {
.refetchData(version, () => _dataStream.add(_apiData));
await _apiData.backupConfig
.refetchData(version, () => _dataStream.add(_apiData));
await _apiData.services
.refetchData(version, () => _dataStream.add(_apiData));
}
void emitData() {
@ -132,12 +136,17 @@ class ApiData {
backups = ApiDataElement<List<Backup>>(
fetchData: () async => api.getBackups(),
requiredApiVersion: '>=2.4.2',
),
services = ApiDataElement<List<Service>>(
fetchData: () async => api.getAllServices(),
requiredApiVersion: '>=2.4.3',
);
ApiDataElement<List<ServerJob>> serverJobs;
ApiDataElement<String> apiVersion;
ApiDataElement<BackupConfiguration> backupConfig;
ApiDataElement<List<Backup>> backups;
ApiDataElement<List<Service>> services;
}
enum ConnectionStatus {
@ -163,7 +172,9 @@ class ApiDataElement<T> {
final Future<T?> Function() fetchData;
Future<void> refetchData(
final Version version, final Function callback) async {
final Version version,
final Function callback,
) async {
if (VersionConstraint.parse(requiredApiVersion).allows(version)) {
print('Fetching data for $runtimeType');
if (isExpired) {

View File

@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:equatable/equatable.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/services.graphql.dart';
import 'package:selfprivacy/logic/models/disk_size.dart';
@ -8,7 +9,7 @@ import 'package:selfprivacy/logic/models/json/dns_records.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart';
class Service {
class Service extends Equatable {
Service.fromGraphQL(final Query$AllServices$services$allServices service)
: this(
id: service.id,
@ -37,7 +38,7 @@ class Service {
[],
url: service.url,
);
Service({
const Service({
required this.id,
required this.displayName,
required this.description,
@ -72,7 +73,7 @@ class Service {
return '';
}
static Service empty = Service(
static Service empty = const Service(
id: 'empty',
displayName: '',
description: '',
@ -83,7 +84,7 @@ class Service {
backupDescription: '',
status: ServiceStatus.off,
storageUsage: ServiceStorageUsage(
used: const DiskSize(byte: 0),
used: DiskSize(byte: 0),
volume: '',
),
svgIcon: '',
@ -104,16 +105,36 @@ class Service {
final String svgIcon;
final String? url;
final List<DnsRecord> dnsRecords;
@override
List<Object?> get props => [
id,
displayName,
description,
isEnabled,
isRequired,
isMovable,
canBeBackedUp,
backupDescription,
status,
storageUsage,
svgIcon,
dnsRecords,
url,
];
}
class ServiceStorageUsage {
ServiceStorageUsage({
class ServiceStorageUsage extends Equatable {
const ServiceStorageUsage({
required this.used,
required this.volume,
});
final DiskSize used;
final String? volume;
@override
List<Object?> get props => [used, volume];
}
enum ServiceStatus {

View File

@ -5,7 +5,7 @@ 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/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
import 'package:selfprivacy/logic/models/backup.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
import 'package:selfprivacy/logic/models/service.dart';
@ -39,7 +39,7 @@ class BackupDetailsPage extends StatelessWidget {
final bool preventActions = backupsState.preventActions;
final List<Backup> backups = backupsState.backups;
final List<Service> services =
context.watch<ServicesCubit>().state.servicesThatCanBeBackedUp;
context.watch<ServicesBloc>().state.servicesThatCanBeBackedUp;
final Duration? autobackupPeriod = backupsState.autobackupPeriod;
final List<ServerJob> backupJobs = context
.watch<ServerJobsBloc>()
@ -298,7 +298,7 @@ class BackupDetailsPage extends StatelessWidget {
children: backups.take(15).map(
(final Backup backup) {
final service = context
.read<ServicesCubit>()
.read<ServicesBloc>()
.state
.getServiceById(backup.serviceId);
return ListTile(
@ -419,7 +419,7 @@ class BackupDetailsPage extends StatelessWidget {
: () => {
context
.read<BackupsBloc>()
.add(const ForceSnapshotListUpdate())
.add(const ForceSnapshotListUpdate()),
},
),
const SizedBox(height: 8),

View File

@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
import 'package:selfprivacy/logic/models/backup.dart';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/ui/helpers/modals.dart';
@ -43,7 +43,7 @@ class BackupsListPage extends StatelessWidget {
...backups.map(
(final Backup backup) {
final service = context
.read<ServicesCubit>()
.read<ServicesBloc>()
.state
.getServiceById(backup.serviceId);
return ListTile(

View File

@ -4,7 +4,7 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:selfprivacy/config/get_it_config.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/bloc/services/services_bloc.dart';
import 'package:selfprivacy/logic/models/backup.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
import 'package:selfprivacy/logic/models/service.dart';
@ -48,7 +48,7 @@ class _SnapshotModalState extends State<SnapshotModal> {
final bool isServiceBusy = busyServices.contains(widget.snapshot.serviceId);
final Service? service = context
.read<ServicesCubit>()
.read<ServicesBloc>()
.state
.getServiceById(widget.snapshot.serviceId);
@ -153,8 +153,12 @@ class _SnapshotModalState extends State<SnapshotModal> {
onPressed: isServiceBusy
? null
: () {
context.read<BackupsBloc>().add(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

@ -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/logic/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
import 'package:selfprivacy/logic/models/disk_size.dart';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
@ -175,9 +175,11 @@ class _ServicesMigrationPageState extends State<ServicesMigrationPage> {
onPressed: () {
for (final service in widget.services) {
if (serviceToDisk[service.id] != null) {
context.read<ServicesCubit>().moveService(
service.id,
serviceToDisk[service.id]!,
context.read<ServicesBloc>().add(
ServiceMove(
service,
serviceToDisk[service.id]!,
),
);
}
}

View File

@ -3,7 +3,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/ui/components/buttons/outlined_button.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
@ -30,8 +30,7 @@ class _ServerStoragePageState extends State<ServerStoragePage> {
final bool isReady = context.watch<ServerInstallationCubit>().state
is ServerInstallationFinished;
final List<Service> services =
context.watch<ServicesCubit>().state.services;
final List<Service> services = context.watch<ServicesBloc>().state.services;
if (!isReady) {
return BrandHeroScreen(

View File

@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.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/bloc/services/services_bloc.dart';
import 'package:selfprivacy/logic/models/job.dart';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
@ -26,7 +26,7 @@ class _ServicePageState extends State<ServicePage> {
@override
Widget build(final BuildContext context) {
final Service? service =
context.watch<ServicesCubit>().state.getServiceById(widget.serviceId);
context.watch<ServicesBloc>().state.getServiceById(widget.serviceId);
if (service == null) {
return const BrandHeroScreen(
@ -43,7 +43,7 @@ class _ServicePageState extends State<ServicePage> {
service.status == ServiceStatus.off;
final bool serviceLocked =
context.watch<ServicesCubit>().state.isServiceLocked(service.id);
context.watch<ServicesBloc>().state.isServiceLocked(service.id);
return BrandHeroScreen(
hasBackButton: true,
@ -81,7 +81,7 @@ class _ServicePageState extends State<ServicePage> {
ListTile(
iconColor: Theme.of(context).colorScheme.onBackground,
onTap: () => {
context.read<ServicesCubit>().restart(service.id),
context.read<ServicesBloc>().add(ServiceRestart(service)),
},
leading: const Icon(Icons.restart_alt_outlined),
title: Text(

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/logic/models/state_types.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
@ -30,7 +30,7 @@ class _ServicesPageState extends State<ServicesPage> {
final isReady = context.watch<ServerInstallationCubit>().state
is ServerInstallationFinished;
final services = [...context.watch<ServicesCubit>().state.services];
final services = [...context.watch<ServicesBloc>().state.services];
services
.sort((final a, final b) => a.status.index.compareTo(b.status.index));
@ -52,7 +52,8 @@ class _ServicesPageState extends State<ServicesPage> {
)
: RefreshIndicator(
onRefresh: () async {
await context.read<ServicesCubit>().reload();
// Create a ServicesRelaod event and wait for the state to change.
await context.read<ServicesBloc>().awaitReload();
},
child: ListView(
padding: paddingH15V0,