diff --git a/lib/config/bloc_config.dart b/lib/config/bloc_config.dart index 881159a6..4f5baeed 100644 --- a/lib/config/bloc_config.dart +++ b/lib/config/bloc_config.dart @@ -6,6 +6,7 @@ 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/users/users_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'; @@ -13,7 +14,6 @@ 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'; import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart'; -import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; class BlocAndProviderConfig extends StatefulWidget { const BlocAndProviderConfig({super.key, this.child}); @@ -27,7 +27,7 @@ class BlocAndProviderConfig extends StatefulWidget { class BlocAndProviderConfigState extends State { late final ServerInstallationCubit serverInstallationCubit; late final SupportSystemCubit supportSystemCubit; - late final UsersCubit usersCubit; + late final UsersBloc usersBloc; late final ServicesBloc servicesBloc; late final BackupsBloc backupsBloc; late final DnsRecordsCubit dnsRecordsCubit; @@ -43,7 +43,7 @@ class BlocAndProviderConfigState extends State { super.initState(); serverInstallationCubit = ServerInstallationCubit()..load(); supportSystemCubit = SupportSystemCubit(); - usersCubit = UsersCubit(); + usersBloc = UsersBloc(); servicesBloc = ServicesBloc(); backupsBloc = BackupsBloc(); dnsRecordsCubit = DnsRecordsCubit(); @@ -77,7 +77,7 @@ class BlocAndProviderConfigState extends State { lazy: false, ), BlocProvider( - create: (final _) => usersCubit, + create: (final _) => usersBloc, lazy: false, ), BlocProvider( @@ -105,7 +105,6 @@ class BlocAndProviderConfigState extends State { BlocProvider(create: (final _) => volumesBloc), BlocProvider( create: (final _) => JobsCubit( - usersCubit: usersCubit, servicesBloc: servicesBloc, ), ), diff --git a/lib/logic/bloc/connection_status/connection_status_bloc.dart b/lib/logic/bloc/connection_status/connection_status_bloc.dart index f44288ed..868f05d0 100644 --- a/lib/logic/bloc/connection_status/connection_status_bloc.dart +++ b/lib/logic/bloc/connection_status/connection_status_bloc.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/config/get_it_config.dart'; @@ -13,16 +15,25 @@ class ConnectionStatusBloc connectionStatus: ConnectionStatus.nonexistent, ), ) { + on((final event, final emit) { + emit(ConnectionStatusState(connectionStatus: event.connectionStatus)); + }); final apiConnectionRepository = getIt(); - apiConnectionRepository.connectionStatusStream.listen( + _apiConnectionStatusSubscription = + apiConnectionRepository.connectionStatusStream.listen( (final ConnectionStatus connectionStatus) { add( ConnectionStatusChanged(connectionStatus), ); }, ); - on((final event, final emit) { - emit(ConnectionStatusState(connectionStatus: event.connectionStatus)); - }); + } + + StreamSubscription? _apiConnectionStatusSubscription; + + @override + Future close() { + _apiConnectionStatusSubscription?.cancel(); + return super.close(); } } diff --git a/lib/logic/bloc/devices/devices_state.dart b/lib/logic/bloc/devices/devices_state.dart index 85e932df..b31bdf19 100644 --- a/lib/logic/bloc/devices/devices_state.dart +++ b/lib/logic/bloc/devices/devices_state.dart @@ -3,7 +3,7 @@ part of 'devices_bloc.dart'; sealed class DevicesState extends Equatable { DevicesState({ required final List devices, - }) : _hashCode = devices.hashCode; + }) : _hashCode = Object.hashAll(devices); final int _hashCode; diff --git a/lib/logic/bloc/users/users_bloc.dart b/lib/logic/bloc/users/users_bloc.dart new file mode 100644 index 00000000..32ca7a26 --- /dev/null +++ b/lib/logic/bloc/users/users_bloc.dart @@ -0,0 +1,105 @@ +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/models/hive/user.dart'; + +part 'users_event.dart'; +part 'users_state.dart'; + +class UsersBloc extends Bloc { + UsersBloc() : super(UsersInitial()) { + on( + _updateList, + transformer: sequential(), + ); + on( + _reload, + transformer: droppable(), + ); + on( + _mapConnectionStatusChangedToState, + transformer: sequential(), + ); + + final apiConnectionRepository = getIt(); + _apiConnectionStatusSubscription = + apiConnectionRepository.connectionStatusStream.listen( + (final ConnectionStatus connectionStatus) { + add( + UsersConnectionStatusChanged(connectionStatus), + ); + }, + ); + _apiDataSubscription = apiConnectionRepository.dataStream.listen( + (final ApiData apiData) { + add( + UsersListChanged(apiData.users.data ?? []), + ); + }, + ); + } + + Future _updateList( + final UsersListChanged event, + final Emitter emit, + ) async { + if (event.users.isEmpty) { + emit(UsersInitial()); + return; + } + final newState = UsersLoaded( + users: event.users, + ); + emit(newState); + } + + Future refresh() async { + getIt().apiData.users.invalidate(); + await getIt().reload(null); + } + + Future _reload( + final UsersListRefresh event, + final Emitter emit, + ) async { + emit(UsersRefreshing(users: state.users)); + await refresh(); + } + + Future _mapConnectionStatusChangedToState( + final UsersConnectionStatusChanged event, + final Emitter emit, + ) async { + switch (event.connectionStatus) { + case ConnectionStatus.nonexistent: + emit(UsersInitial()); + break; + case ConnectionStatus.connected: + if (state is! UsersLoaded) { + emit(UsersRefreshing(users: state.users)); + } + case ConnectionStatus.reconnecting: + case ConnectionStatus.offline: + case ConnectionStatus.unauthorized: + break; + } + } + + StreamSubscription? _apiDataSubscription; + StreamSubscription? _apiConnectionStatusSubscription; + + @override + void onChange(final Change change) { + super.onChange(change); + } + + @override + Future close() { + _apiDataSubscription?.cancel(); + _apiConnectionStatusSubscription?.cancel(); + return super.close(); + } +} diff --git a/lib/logic/bloc/users/users_event.dart b/lib/logic/bloc/users/users_event.dart new file mode 100644 index 00000000..7fdd1431 --- /dev/null +++ b/lib/logic/bloc/users/users_event.dart @@ -0,0 +1,30 @@ +part of 'users_bloc.dart'; + +sealed class UsersEvent extends Equatable { + const UsersEvent(); +} + +class UsersListChanged extends UsersEvent { + const UsersListChanged(this.users); + + final List users; + + @override + List get props => [users]; +} + +class UsersListRefresh extends UsersEvent { + const UsersListRefresh(); + + @override + List get props => []; +} + +class UsersConnectionStatusChanged extends UsersEvent { + const UsersConnectionStatusChanged(this.connectionStatus); + + final ConnectionStatus connectionStatus; + + @override + List get props => [connectionStatus]; +} diff --git a/lib/logic/cubit/users/users_state.dart b/lib/logic/bloc/users/users_state.dart similarity index 63% rename from lib/logic/cubit/users/users_state.dart rename to lib/logic/bloc/users/users_state.dart index 4e2ed42e..69c4917b 100644 --- a/lib/logic/cubit/users/users_state.dart +++ b/lib/logic/bloc/users/users_state.dart @@ -1,10 +1,14 @@ -part of 'users_cubit.dart'; +part of 'users_bloc.dart'; -class UsersState extends ServerInstallationDependendState { - const UsersState(this.users, this.isLoading); +sealed class UsersState extends Equatable { + UsersState({ + required final List users, + }) : _hashCode = Object.hashAll(users); - final List users; - final bool isLoading; + final int _hashCode; + + List get users => + getIt().apiData.users.data ?? const []; User get rootUser => users.firstWhere((final user) => user.type == UserType.root); @@ -15,9 +19,6 @@ class UsersState extends ServerInstallationDependendState { List get normalUsers => users.where((final user) => user.type == UserType.normal).toList(); - @override - List get props => [users, isLoading]; - /// Makes a copy of existing users list, but places 'primary' /// to the beginning and sorts the rest alphabetically /// @@ -44,17 +45,29 @@ class UsersState extends ServerInstallationDependendState { return primaryUser == null ? normalUsers : [primaryUser] + normalUsers; } - UsersState copyWith({ - final List? users, - final bool? isLoading, - }) => - UsersState( - users ?? this.users, - isLoading ?? this.isLoading, - ); - bool isLoginRegistered(final String login) => users.any((final User user) => user.login == login); bool get isEmpty => users.isEmpty; } + +class UsersInitial extends UsersState { + UsersInitial() : super(users: const []); + + @override + List get props => [_hashCode]; +} + +class UsersRefreshing extends UsersState { + UsersRefreshing({required super.users}); + + @override + List get props => [_hashCode]; +} + +class UsersLoaded extends UsersState { + UsersLoaded({required super.users}); + + @override + List get props => [_hashCode]; +} diff --git a/lib/logic/cubit/client_jobs/client_jobs_cubit.dart b/lib/logic/cubit/client_jobs/client_jobs_cubit.dart index f9188985..16b87c77 100644 --- a/lib/logic/cubit/client_jobs/client_jobs_cubit.dart +++ b/lib/logic/cubit/client_jobs/client_jobs_cubit.dart @@ -6,7 +6,6 @@ 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/bloc/services/services_bloc.dart'; -import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; import 'package:selfprivacy/logic/models/job.dart'; export 'package:provider/provider.dart'; @@ -15,12 +14,10 @@ part 'client_jobs_state.dart'; class JobsCubit extends Cubit { JobsCubit({ - required this.usersCubit, required this.servicesBloc, }) : super(JobsStateEmpty()); final ServerApi api = ServerApi(); - final UsersCubit usersCubit; final ServicesBloc servicesBloc; void addJob(final ClientJob job) { diff --git a/lib/logic/cubit/forms/factories/field_cubit_factory.dart b/lib/logic/cubit/forms/factories/field_cubit_factory.dart index 42359d1e..0aabc0e0 100644 --- a/lib/logic/cubit/forms/factories/field_cubit_factory.dart +++ b/lib/logic/cubit/forms/factories/field_cubit_factory.dart @@ -1,8 +1,8 @@ import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/bloc/users/users_bloc.dart'; import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; -import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; class FieldCubitFactory { FieldCubitFactory(this.context); @@ -27,7 +27,7 @@ class FieldCubitFactory { ), ValidationModel( (final String login) => - context.read().state.isLoginRegistered(login), + context.read().state.isLoginRegistered(login), 'validations.already_exist'.tr(), ), RequiredStringValidation('validations.required'.tr()), diff --git a/lib/logic/cubit/users/users_cubit.dart b/lib/logic/cubit/users/users_cubit.dart deleted file mode 100644 index 30185e60..00000000 --- a/lib/logic/cubit/users/users_cubit.dart +++ /dev/null @@ -1,183 +0,0 @@ -import 'dart:async'; - -import 'package:easy_localization/easy_localization.dart'; -import 'package:hive/hive.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/cubit/server_connection_dependent/server_connection_dependent_cubit.dart'; -import 'package:selfprivacy/logic/models/hive/user.dart'; - -export 'package:provider/provider.dart'; - -part 'users_state.dart'; - -class UsersCubit extends ServerConnectionDependentCubit { - UsersCubit() - : super( - const UsersState( - [], - false, - ), - ); - Box box = Hive.box(BNames.usersBox); - Box serverInstallationBox = Hive.box(BNames.serverInstallationBox); - - final ServerApi api = ServerApi(); - - @override - Future load() async { - final List loadedUsers = box.values.toList(); - if (loadedUsers.isNotEmpty) { - emit( - UsersState( - loadedUsers, - false, - ), - ); - } - - unawaited(refresh()); - } - - Future refresh() async { - if (getIt().connectionStatus == - ConnectionStatus.nonexistent) { - return; - } - emit(state.copyWith(isLoading: true)); - final List usersFromServer = await api.getAllUsers(); - if (usersFromServer.isNotEmpty) { - emit( - UsersState( - usersFromServer, - false, - ), - ); - // Update the users it the box - await box.clear(); - await box.addAll(usersFromServer); - } else { - getIt() - .showSnackBar('users.could_not_fetch_users'.tr()); - emit(state.copyWith(isLoading: false)); - } - } - - Future createUser(final User user) async { - // If user exists on server, do nothing - if (state.users - .any((final User u) => u.login == user.login && u.isFoundOnServer)) { - return; - } - final String? password = user.password; - if (password == null) { - getIt() - .showSnackBar('users.could_not_create_user'.tr()); - return; - } - // If API returned error, do nothing - final GenericResult result = - await api.createUser(user.login, password); - if (result.data == null) { - getIt() - .showSnackBar(result.message ?? 'users.could_not_create_user'.tr()); - return; - } - - final List loadedUsers = List.from(state.users); - loadedUsers.add(result.data!); - await box.clear(); - await box.addAll(loadedUsers); - emit(state.copyWith(users: loadedUsers)); - } - - Future deleteUser(final User user) async { - // If user is primary or root, don't delete - if (user.type != UserType.normal) { - getIt() - .showSnackBar('users.could_not_delete_user'.tr()); - return; - } - final List loadedUsers = List.from(state.users); - final GenericResult result = await api.deleteUser(user.login); - if (result.success && result.data) { - loadedUsers.removeWhere((final User u) => u.login == user.login); - await box.clear(); - await box.addAll(loadedUsers); - emit(state.copyWith(users: loadedUsers)); - } - - if (!result.success) { - getIt().showSnackBar('jobs.generic_error'.tr()); - } - - if (!result.data) { - getIt() - .showSnackBar(result.message ?? 'jobs.generic_error'.tr()); - } - } - - Future changeUserPassword( - final User user, - final String newPassword, - ) async { - if (user.type == UserType.root) { - getIt() - .showSnackBar('users.could_not_change_password'.tr()); - return; - } - final GenericResult result = - await api.updateUser(user.login, newPassword); - if (result.data == null) { - getIt().showSnackBar( - result.message ?? 'users.could_not_change_password'.tr(), - ); - } - } - - Future addSshKey(final User user, final String publicKey) async { - final GenericResult result = - await api.addSshKey(user.login, publicKey); - if (result.data != null) { - final User updatedUser = result.data!; - final int index = - state.users.indexWhere((final User u) => u.login == user.login); - await box.putAt(index, updatedUser); - emit( - state.copyWith( - users: box.values.toList(), - ), - ); - } else { - getIt() - .showSnackBar(result.message ?? 'users.could_not_add_ssh_key'.tr()); - } - } - - Future deleteSshKey(final User user, final String publicKey) async { - final GenericResult result = - await api.removeSshKey(user.login, publicKey); - if (result.data != null) { - final User updatedUser = result.data!; - final int index = - state.users.indexWhere((final User u) => u.login == user.login); - await box.putAt(index, updatedUser); - emit( - state.copyWith( - users: box.values.toList(), - ), - ); - } - } - - @override - void clear() async { - emit( - const UsersState( - [], - false, - ), - ); - } -} diff --git a/lib/logic/get_it/api_connection_repository.dart b/lib/logic/get_it/api_connection_repository.dart index c504e06a..13229442 100644 --- a/lib/logic/get_it/api_connection_repository.dart +++ b/lib/logic/get_it/api_connection_repository.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:easy_localization/easy_localization.dart'; import 'package:hive/hive.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:selfprivacy/config/get_it_config.dart'; @@ -8,6 +9,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/hive/user.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'; @@ -68,6 +70,116 @@ class ApiConnectionRepository { ); } + Future createUser(final User user) async { + final List? loadedUsers = _apiData.users.data; + if (loadedUsers == null) { + return; + } + // If user exists on server, do nothing + if (loadedUsers + .any((final User u) => u.login == user.login && u.isFoundOnServer)) { + return; + } + final String? password = user.password; + if (password == null) { + getIt() + .showSnackBar('users.could_not_create_user'.tr()); + return; + } + // If API returned error, do nothing + final GenericResult result = + await api.createUser(user.login, password); + if (result.data == null) { + getIt() + .showSnackBar(result.message ?? 'users.could_not_create_user'.tr()); + return; + } + + _apiData.users.data?.add(result.data!); + _apiData.users.invalidate(); + } + + Future deleteUser(final User user) async { + final List? loadedUsers = _apiData.users.data; + if (loadedUsers == null) { + return; + } + // If user is primary or root, don't delete + if (user.type != UserType.normal) { + getIt() + .showSnackBar('users.could_not_delete_user'.tr()); + return; + } + final GenericResult result = await api.deleteUser(user.login); + if (result.success && result.data) { + _apiData.users.data?.removeWhere((final User u) => u.login == user.login); + _apiData.users.invalidate(); + } + + if (!result.success || !result.data) { + getIt() + .showSnackBar(result.message ?? 'jobs.generic_error'.tr()); + } + } + + Future changeUserPassword( + final User user, + final String newPassword, + ) async { + if (user.type == UserType.root) { + getIt() + .showSnackBar('users.could_not_change_password'.tr()); + return; + } + final GenericResult result = await api.updateUser( + user.login, + newPassword, + ); + if (result.data == null) { + getIt().showSnackBar( + result.message ?? 'users.could_not_change_password'.tr(), + ); + } + } + + Future addSshKey(final User user, final String publicKey) async { + final List? loadedUsers = _apiData.users.data; + if (loadedUsers == null) { + return; + } + final GenericResult result = + await api.addSshKey(user.login, publicKey); + if (result.data != null) { + final User updatedUser = result.data!; + final int index = + loadedUsers.indexWhere((final User u) => u.login == user.login); + loadedUsers[index] = updatedUser; + _apiData.users.invalidate(); + } else { + getIt() + .showSnackBar(result.message ?? 'users.could_not_add_ssh_key'.tr()); + } + } + + Future deleteSshKey(final User user, final String publicKey) async { + final List? loadedUsers = _apiData.users.data; + if (loadedUsers == null) { + return; + } + final GenericResult result = + await api.removeSshKey(user.login, publicKey); + if (result.data != null) { + final User updatedUser = result.data!; + final int index = + loadedUsers.indexWhere((final User u) => u.login == user.login); + loadedUsers[index] = updatedUser; + _apiData.users.invalidate(); + } else { + getIt() + .showSnackBar(result.message ?? 'jobs.generic_error'.tr()); + } + } + void dispose() { _dataStream.close(); _connectionStatusStream.close(); @@ -106,6 +218,7 @@ class ApiConnectionRepository { _apiData.recoveryKeyStatus.data = await _apiData.recoveryKeyStatus.fetchData(); _apiData.devices.data = await _apiData.devices.fetchData(); + _apiData.users.data = await _apiData.users.fetchData(); _dataStream.add(_apiData); connectionStatus = ConnectionStatus.connected; @@ -149,6 +262,7 @@ class ApiConnectionRepository { .refetchData(version, () => _dataStream.add(_apiData)); await _apiData.devices .refetchData(version, () => _dataStream.add(_apiData)); + await _apiData.users.refetchData(version, () => _dataStream.add(_apiData)); } void emitData() { @@ -188,6 +302,9 @@ class ApiData { ), devices = ApiDataElement>( fetchData: () async => (await api.getApiTokens()).data, + ), + users = ApiDataElement>( + fetchData: () async => api.getAllUsers(), ); ApiDataElement> serverJobs; @@ -198,6 +315,7 @@ class ApiData { ApiDataElement> volumes; ApiDataElement recoveryKeyStatus; ApiDataElement> devices; + ApiDataElement> users; } enum ConnectionStatus { diff --git a/lib/logic/models/job.dart b/lib/logic/models/job.dart index e1dd9354..6e3d2a1d 100644 --- a/lib/logic/models/job.dart +++ b/lib/logic/models/job.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/service.dart'; @@ -48,7 +49,7 @@ class CreateUserJob extends ClientJob { @override void execute(final JobsCubit cubit) async { - await cubit.usersCubit.createUser(user); + await getIt().createUser(user); } @override @@ -64,7 +65,8 @@ class ResetUserPasswordJob extends ClientJob { @override void execute(final JobsCubit cubit) async { - await cubit.usersCubit.changeUserPassword(user, user.password!); + await getIt() + .changeUserPassword(user, user.password!); } @override @@ -85,7 +87,7 @@ class DeleteUserJob extends ClientJob { @override void execute(final JobsCubit cubit) async { - await cubit.usersCubit.deleteUser(user); + await getIt().deleteUser(user); } @override @@ -129,7 +131,7 @@ class CreateSSHKeyJob extends ClientJob { @override void execute(final JobsCubit cubit) async { - await cubit.usersCubit.addSshKey(user, publicKey); + await getIt().addSshKey(user, publicKey); } @override @@ -155,7 +157,7 @@ class DeleteSSHKeyJob extends ClientJob { @override void execute(final JobsCubit cubit) async { - await cubit.usersCubit.deleteSshKey(user, publicKey); + await getIt().deleteSshKey(user, publicKey); } @override diff --git a/lib/ui/pages/users/new_user.dart b/lib/ui/pages/users/new_user.dart index 2315bdb1..25ba5f98 100644 --- a/lib/ui/pages/users/new_user.dart +++ b/lib/ui/pages/users/new_user.dart @@ -16,7 +16,7 @@ class NewUserPage extends StatelessWidget { final jobCubit = context.read(); final jobState = jobCubit.state; final users = []; - users.addAll(context.read().state.users); + users.addAll(context.read().state.users); if (jobState is JobsStateWithJobs) { final jobs = jobState.clientJobList; for (final job in jobs) { diff --git a/lib/ui/pages/users/user_details.dart b/lib/ui/pages/users/user_details.dart index 0dd2c6d8..105a8362 100644 --- a/lib/ui/pages/users/user_details.dart +++ b/lib/ui/pages/users/user_details.dart @@ -15,7 +15,7 @@ class UserDetailsPage extends StatelessWidget { final String domainName = UiHelpers.getDomainName(config); - final User user = context.watch().state.users.firstWhere( + final User user = context.watch().state.users.firstWhere( (final User user) => user.login == login, orElse: () => const User( type: UserType.normal, diff --git a/lib/ui/pages/users/users.dart b/lib/ui/pages/users/users.dart index c2fb5214..f7edce9e 100644 --- a/lib/ui/pages/users/users.dart +++ b/lib/ui/pages/users/users.dart @@ -4,12 +4,12 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/bloc/users/users_bloc.dart'; import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; import 'package:selfprivacy/logic/cubit/forms/user/ssh_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/user/user_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; @@ -49,18 +49,18 @@ class UsersPage extends StatelessWidget { iconData: BrandIcons.users, ); } else { - child = BlocBuilder( + child = BlocBuilder( builder: (final BuildContext context, final UsersState state) { final users = state.orderedUsers; if (users.isEmpty) { - if (state.isLoading) { + if (state is UsersRefreshing) { return const Center( child: CircularProgressIndicator(), ); } return RefreshIndicator( onRefresh: () async { - await context.read().refresh(); + await context.read().refresh(); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 15), @@ -76,7 +76,7 @@ class UsersPage extends StatelessWidget { const SizedBox(height: 18), BrandOutlinedButton( onPressed: () { - context.read().refresh(); + context.read().refresh(); }, title: 'users.refresh_users'.tr(), ), @@ -88,7 +88,7 @@ class UsersPage extends StatelessWidget { } return RefreshIndicator( onRefresh: () async { - await context.read().refresh(); + await context.read().refresh(); }, child: Column( children: [