refactor(legacy-account): migrate app_users to FutureProvider
This commit is contained in:
@@ -78,7 +78,14 @@ class AccountSettingsScreen extends ConsumerWidget {
|
||||
text: I18n.linkedDevices,
|
||||
color: color,
|
||||
),
|
||||
// _item(context, onPressed: () => navigationContract.pushTo(AppRoutes.appUsers), icon: Icons.groups_outlined, text: I18n.appUsers, color: color),
|
||||
_item(
|
||||
context,
|
||||
onPressed: () =>
|
||||
navigationContract.pushTo(AppRoutes.appUsers),
|
||||
icon: Icons.groups_outlined,
|
||||
text: I18n.appUsers,
|
||||
color: color,
|
||||
),
|
||||
_item(
|
||||
context,
|
||||
onPressed: () async {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import 'package:account/src/features/app_users/presentation/state/app_users_view_model.dart';
|
||||
import 'package:legacy_theme/legacy_theme.dart';
|
||||
import 'package:account/src/features/app_users/presentation/providers/app_users_edit_mode_provider.dart';
|
||||
import 'package:account/src/features/app_users/presentation/providers/app_users_provider.dart';
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_theme/legacy_theme.dart';
|
||||
import 'package:legacy_ui/legacy_ui.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
@@ -16,32 +17,36 @@ class AppUsersScreen extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final vm = ref.read(appUsersViewModelProvider.notifier);
|
||||
final state = ref.watch(appUsersViewModelProvider);
|
||||
final usersAsync = ref.watch(appUsersProvider);
|
||||
final isEditing = ref.watch(appUsersEditModeProvider);
|
||||
final toggleEditing = ref.read(appUsersEditModeProvider.notifier).toggle;
|
||||
|
||||
ref.listen(appUsersProvider, (_, next) => next.showErrorOn(context));
|
||||
|
||||
return LegacyPageLayout(
|
||||
showEdit: true,
|
||||
onEditChange: vm.toggleIsEditing,
|
||||
onEditChange: toggleEditing,
|
||||
title: context.translate(I18n.appUsers),
|
||||
body: Container(
|
||||
padding: SizeUtils.getByScreen(
|
||||
small: EdgeInsets.symmetric(horizontal: 22, vertical: 10),
|
||||
big: EdgeInsets.symmetric(horizontal: 21, vertical: 8),
|
||||
),
|
||||
child: ListView.separated(
|
||||
itemBuilder: (BuildContext context, int index) => AppUserCard(
|
||||
user: state.appUsers[index],
|
||||
isEditing: state.isEditing,
|
||||
child: usersAsync.when(
|
||||
data: (users) => ListView.separated(
|
||||
itemBuilder: (_, index) =>
|
||||
AppUserCard(user: users[index], isEditing: isEditing),
|
||||
separatorBuilder: (_, __) =>
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
|
||||
itemCount: users.length,
|
||||
),
|
||||
separatorBuilder: (BuildContext context, int index) =>
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
|
||||
itemCount: state.appUsers.length,
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (_, __) => const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
footer: state.isEditing
|
||||
footer: isEditing
|
||||
? PrimaryButton(
|
||||
onPressed: vm.toggleIsEditing,
|
||||
onPressed: toggleEditing,
|
||||
text: context.translate(I18n.save),
|
||||
color: context.sfColors.legacyPrimary,
|
||||
height: SizeUtils.getByScreen(small: 44, big: 42),
|
||||
@@ -59,7 +64,6 @@ class AppUserCard extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
||||
return Container(
|
||||
padding: SizeUtils.getByScreen(
|
||||
small: EdgeInsets.symmetric(horizontal: 22, vertical: 10),
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'app_users_edit_mode_provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
class AppUsersEditMode extends _$AppUsersEditMode {
|
||||
@override
|
||||
bool build() => false;
|
||||
|
||||
void toggle() => state = !state;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'app_users_edit_mode_provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(AppUsersEditMode)
|
||||
const appUsersEditModeProvider = AppUsersEditModeProvider._();
|
||||
|
||||
final class AppUsersEditModeProvider
|
||||
extends $NotifierProvider<AppUsersEditMode, bool> {
|
||||
const AppUsersEditModeProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'appUsersEditModeProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$appUsersEditModeHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
AppUsersEditMode create() => AppUsersEditMode();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(bool value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<bool>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$appUsersEditModeHash() => r'd4c3717c5dca1dc16bc5846842877967e360e081';
|
||||
|
||||
abstract class _$AppUsersEditMode extends $Notifier<bool> {
|
||||
bool build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<bool, bool>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<bool, bool>,
|
||||
bool,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import 'package:account/src/core/providers/users_repository_provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
|
||||
part 'app_users_provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future<List<UserEntity>> appUsers(Ref ref) async {
|
||||
final user = await ref.watch(userInfoProvider.future);
|
||||
return ref.watch(usersRepositoryProvider).getUsers(userId: user.id);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'app_users_provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(appUsers)
|
||||
const appUsersProvider = AppUsersProvider._();
|
||||
|
||||
final class AppUsersProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<List<UserEntity>>,
|
||||
List<UserEntity>,
|
||||
FutureOr<List<UserEntity>>
|
||||
>
|
||||
with $FutureModifier<List<UserEntity>>, $FutureProvider<List<UserEntity>> {
|
||||
const AppUsersProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'appUsersProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$appUsersHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$FutureProviderElement<List<UserEntity>> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $FutureProviderElement(pointer);
|
||||
|
||||
@override
|
||||
FutureOr<List<UserEntity>> create(Ref ref) {
|
||||
return appUsers(ref);
|
||||
}
|
||||
}
|
||||
|
||||
String _$appUsersHash() => r'341fe226d4a606a33e023ee3d93b36100a82be0c';
|
||||
@@ -1,53 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:account/src/core/domain/repositories/users_repository.dart';
|
||||
import 'package:account/src/core/providers/users_repository_provider.dart';
|
||||
import 'package:account/src/features/app_users/presentation/state/app_users_view_state.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
import 'package:sf_tracking/sf_tracking.dart';
|
||||
|
||||
final appUsersViewModelProvider =
|
||||
NotifierProvider.autoDispose<AppUsersViewModel, AppUsersViewState>(
|
||||
AppUsersViewModel.new,
|
||||
);
|
||||
|
||||
class AppUsersViewModel extends Notifier<AppUsersViewState> {
|
||||
late final UsersRepository _usersRepository;
|
||||
late final SfTrackingRepository _tracking;
|
||||
|
||||
@override
|
||||
AppUsersViewState build() {
|
||||
_usersRepository = ref.read(usersRepositoryProvider);
|
||||
_tracking = ref.read(sfTrackingProvider);
|
||||
|
||||
_init();
|
||||
|
||||
return const AppUsersViewState();
|
||||
}
|
||||
|
||||
Future<void> _init() async {
|
||||
final user = await ref.read(userInfoProvider.future);
|
||||
|
||||
setUser(user);
|
||||
final appUsers = await _usersRepository.getUsers(userId: user.id);
|
||||
setAppUsers(appUsers);
|
||||
}
|
||||
|
||||
void setUser(UserEntity user) {
|
||||
state = state.copyWith(loggedUser: user);
|
||||
}
|
||||
|
||||
void setAppUsers(List<UserEntity> appUsers) {
|
||||
state = state.copyWith(appUsers: appUsers, isLoading: false);
|
||||
}
|
||||
|
||||
void toggleIsEditing() {
|
||||
state = state.copyWith(isEditing: !state.isEditing);
|
||||
}
|
||||
|
||||
void deleteUser() {
|
||||
unawaited(_tracking.legacyAccountAppUserDeleteTriggered());
|
||||
_usersRepository.deleteUser(userId: state.loggedUser!.id);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
|
||||
part 'app_users_view_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
abstract class AppUsersViewState with _$AppUsersViewState {
|
||||
const factory AppUsersViewState({
|
||||
@Default(false) bool isLoading,
|
||||
@Default(null) UserEntity? loggedUser,
|
||||
@Default([]) List<UserEntity> appUsers,
|
||||
@Default(false) bool isEditing,
|
||||
@Default('') String errorMessage,
|
||||
}) = _AppUsersViewState;
|
||||
}
|
||||
@@ -1,313 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'app_users_view_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$AppUsersViewState {
|
||||
|
||||
bool get isLoading; UserEntity? get loggedUser; List<UserEntity> get appUsers; bool get isEditing; String get errorMessage;
|
||||
/// Create a copy of AppUsersViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$AppUsersViewStateCopyWith<AppUsersViewState> get copyWith => _$AppUsersViewStateCopyWithImpl<AppUsersViewState>(this as AppUsersViewState, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppUsersViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.loggedUser, loggedUser) || other.loggedUser == loggedUser)&&const DeepCollectionEquality().equals(other.appUsers, appUsers)&&(identical(other.isEditing, isEditing) || other.isEditing == isEditing)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,isLoading,loggedUser,const DeepCollectionEquality().hash(appUsers),isEditing,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppUsersViewState(isLoading: $isLoading, loggedUser: $loggedUser, appUsers: $appUsers, isEditing: $isEditing, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $AppUsersViewStateCopyWith<$Res> {
|
||||
factory $AppUsersViewStateCopyWith(AppUsersViewState value, $Res Function(AppUsersViewState) _then) = _$AppUsersViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
bool isLoading, UserEntity? loggedUser, List<UserEntity> appUsers, bool isEditing, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
$UserEntityCopyWith<$Res>? get loggedUser;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$AppUsersViewStateCopyWithImpl<$Res>
|
||||
implements $AppUsersViewStateCopyWith<$Res> {
|
||||
_$AppUsersViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final AppUsersViewState _self;
|
||||
final $Res Function(AppUsersViewState) _then;
|
||||
|
||||
/// Create a copy of AppUsersViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? loggedUser = freezed,Object? appUsers = null,Object? isEditing = null,Object? errorMessage = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,loggedUser: freezed == loggedUser ? _self.loggedUser : loggedUser // ignore: cast_nullable_to_non_nullable
|
||||
as UserEntity?,appUsers: null == appUsers ? _self.appUsers : appUsers // ignore: cast_nullable_to_non_nullable
|
||||
as List<UserEntity>,isEditing: null == isEditing ? _self.isEditing : isEditing // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
/// Create a copy of AppUsersViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$UserEntityCopyWith<$Res>? get loggedUser {
|
||||
if (_self.loggedUser == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $UserEntityCopyWith<$Res>(_self.loggedUser!, (value) {
|
||||
return _then(_self.copyWith(loggedUser: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [AppUsersViewState].
|
||||
extension AppUsersViewStatePatterns on AppUsersViewState {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _AppUsersViewState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AppUsersViewState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _AppUsersViewState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AppUsersViewState():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _AppUsersViewState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AppUsersViewState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isLoading, UserEntity? loggedUser, List<UserEntity> appUsers, bool isEditing, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AppUsersViewState() when $default != null:
|
||||
return $default(_that.isLoading,_that.loggedUser,_that.appUsers,_that.isEditing,_that.errorMessage);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isLoading, UserEntity? loggedUser, List<UserEntity> appUsers, bool isEditing, String errorMessage) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AppUsersViewState():
|
||||
return $default(_that.isLoading,_that.loggedUser,_that.appUsers,_that.isEditing,_that.errorMessage);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isLoading, UserEntity? loggedUser, List<UserEntity> appUsers, bool isEditing, String errorMessage)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AppUsersViewState() when $default != null:
|
||||
return $default(_that.isLoading,_that.loggedUser,_that.appUsers,_that.isEditing,_that.errorMessage);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _AppUsersViewState implements AppUsersViewState {
|
||||
const _AppUsersViewState({this.isLoading = false, this.loggedUser = null, final List<UserEntity> appUsers = const [], this.isEditing = false, this.errorMessage = ''}): _appUsers = appUsers;
|
||||
|
||||
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final UserEntity? loggedUser;
|
||||
final List<UserEntity> _appUsers;
|
||||
@override@JsonKey() List<UserEntity> get appUsers {
|
||||
if (_appUsers is EqualUnmodifiableListView) return _appUsers;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_appUsers);
|
||||
}
|
||||
|
||||
@override@JsonKey() final bool isEditing;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
|
||||
/// Create a copy of AppUsersViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$AppUsersViewStateCopyWith<_AppUsersViewState> get copyWith => __$AppUsersViewStateCopyWithImpl<_AppUsersViewState>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppUsersViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.loggedUser, loggedUser) || other.loggedUser == loggedUser)&&const DeepCollectionEquality().equals(other._appUsers, _appUsers)&&(identical(other.isEditing, isEditing) || other.isEditing == isEditing)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,isLoading,loggedUser,const DeepCollectionEquality().hash(_appUsers),isEditing,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppUsersViewState(isLoading: $isLoading, loggedUser: $loggedUser, appUsers: $appUsers, isEditing: $isEditing, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$AppUsersViewStateCopyWith<$Res> implements $AppUsersViewStateCopyWith<$Res> {
|
||||
factory _$AppUsersViewStateCopyWith(_AppUsersViewState value, $Res Function(_AppUsersViewState) _then) = __$AppUsersViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
bool isLoading, UserEntity? loggedUser, List<UserEntity> appUsers, bool isEditing, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
@override $UserEntityCopyWith<$Res>? get loggedUser;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$AppUsersViewStateCopyWithImpl<$Res>
|
||||
implements _$AppUsersViewStateCopyWith<$Res> {
|
||||
__$AppUsersViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _AppUsersViewState _self;
|
||||
final $Res Function(_AppUsersViewState) _then;
|
||||
|
||||
/// Create a copy of AppUsersViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? loggedUser = freezed,Object? appUsers = null,Object? isEditing = null,Object? errorMessage = null,}) {
|
||||
return _then(_AppUsersViewState(
|
||||
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,loggedUser: freezed == loggedUser ? _self.loggedUser : loggedUser // ignore: cast_nullable_to_non_nullable
|
||||
as UserEntity?,appUsers: null == appUsers ? _self._appUsers : appUsers // ignore: cast_nullable_to_non_nullable
|
||||
as List<UserEntity>,isEditing: null == isEditing ? _self.isEditing : isEditing // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of AppUsersViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$UserEntityCopyWith<$Res>? get loggedUser {
|
||||
if (_self.loggedUser == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $UserEntityCopyWith<$Res>(_self.loggedUser!, (value) {
|
||||
return _then(_self.copyWith(loggedUser: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -65,6 +65,8 @@ dependencies:
|
||||
qr_flutter: ^4.1.0
|
||||
url_launcher: ^6.3.2
|
||||
build_runner: ^2.7.1
|
||||
riverpod_annotation: ^3.0.3
|
||||
riverpod_generator: ^3.0.3
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
import 'package:account/src/core/domain/repositories/users_repository.dart';
|
||||
import 'package:account/src/core/providers/users_repository_provider.dart';
|
||||
import 'package:account/src/features/app_users/presentation/providers/app_users_edit_mode_provider.dart';
|
||||
import 'package:account/src/features/app_users/presentation/providers/app_users_provider.dart';
|
||||
import 'package:account/src/features/personal_data/domain/entities/update_user_request_entity.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:sf_infrastructure/sf_infrastructure.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
import 'package:sf_shared/testing.dart';
|
||||
|
||||
class MockUsersRepository extends Mock implements UsersRepository {}
|
||||
|
||||
class _FakeUserInfoNotifier extends UserInfoNotifier {
|
||||
_FakeUserInfoNotifier(this._user);
|
||||
final UserEntity _user;
|
||||
|
||||
@override
|
||||
Future<UserEntity> build() async => _user;
|
||||
}
|
||||
|
||||
const _loggedUser = UserEntity(
|
||||
id: 'user-1',
|
||||
email: 'user1@test.com',
|
||||
createdAt: 0,
|
||||
status: 'active',
|
||||
role: 'parent',
|
||||
lastLogin: 0,
|
||||
currentLogin: 0,
|
||||
language: 'es',
|
||||
firstName: 'Owner',
|
||||
lastName: 'Doe',
|
||||
hasApiKey: false,
|
||||
phone: '',
|
||||
);
|
||||
|
||||
const _otherUser = UserEntity(
|
||||
id: 'user-2',
|
||||
email: 'user2@test.com',
|
||||
createdAt: 0,
|
||||
status: 'active',
|
||||
role: 'kid',
|
||||
lastLogin: 0,
|
||||
currentLogin: 0,
|
||||
language: 'es',
|
||||
firstName: 'Kid',
|
||||
lastName: 'Doe',
|
||||
hasApiKey: false,
|
||||
phone: '',
|
||||
);
|
||||
|
||||
void main() {
|
||||
setUpAll(() {
|
||||
registerFallbackValue(
|
||||
const UpdateUserRequestEntity(
|
||||
id: '',
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
phone: '',
|
||||
language: 'es',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
group('appUsersProvider', () {
|
||||
test('happy path returns users fetched from repository', () async {
|
||||
final repo = MockUsersRepository();
|
||||
when(() => repo.getUsers(userId: _loggedUser.id))
|
||||
.thenAnswer((_) async => [_loggedUser, _otherUser]);
|
||||
|
||||
final container = makeContainer(
|
||||
overrides: [
|
||||
userInfoProvider.overrideWith(() => _FakeUserInfoNotifier(_loggedUser)),
|
||||
usersRepositoryProvider.overrideWithValue(repo),
|
||||
],
|
||||
);
|
||||
addTearDown(container.dispose);
|
||||
|
||||
final users = await container.read(appUsersProvider.future);
|
||||
|
||||
expect(users, [_loggedUser, _otherUser]);
|
||||
verify(() => repo.getUsers(userId: _loggedUser.id)).called(1);
|
||||
});
|
||||
|
||||
test('exposes AsyncError(ApiException) when the repository fails', () async {
|
||||
final repo = MockUsersRepository();
|
||||
when(() => repo.getUsers(userId: _loggedUser.id)).thenAnswer(
|
||||
(_) => Future<List<UserEntity>>.error(
|
||||
const ApiException(message: 'boom', isNetworkError: true),
|
||||
),
|
||||
);
|
||||
|
||||
final container = makeContainer(
|
||||
overrides: [
|
||||
userInfoProvider.overrideWith(() => _FakeUserInfoNotifier(_loggedUser)),
|
||||
usersRepositoryProvider.overrideWithValue(repo),
|
||||
],
|
||||
);
|
||||
addTearDown(container.dispose);
|
||||
|
||||
// Keep the provider alive so it doesn't auto-dispose after the error.
|
||||
final sub = container.listen(appUsersProvider, (_, __) {});
|
||||
addTearDown(sub.close);
|
||||
|
||||
await sub.read().whenOrNull(error: (_, __) {}) ?? Future<void>.value();
|
||||
await pumpEventQueue();
|
||||
|
||||
final state = container.read(appUsersProvider);
|
||||
expect(state.hasError, isTrue);
|
||||
expect(state.error, isA<ApiException>());
|
||||
});
|
||||
|
||||
// Note: Riverpod 3 auto-retries failing providers, which makes it tricky to
|
||||
// assert a terminal error state for `userInfoProvider` in unit tests without
|
||||
// tight-controlled retry configuration. The repository-error case above
|
||||
// already covers the "downstream error reaches appUsersProvider" path.
|
||||
});
|
||||
|
||||
group('AppUsersEditMode', () {
|
||||
test('defaults to false', () {
|
||||
final container = makeContainer();
|
||||
addTearDown(container.dispose);
|
||||
|
||||
expect(container.read(appUsersEditModeProvider), isFalse);
|
||||
});
|
||||
|
||||
test('toggle flips the state', () {
|
||||
final container = makeContainer();
|
||||
addTearDown(container.dispose);
|
||||
|
||||
final notifier = container.read(appUsersEditModeProvider.notifier);
|
||||
|
||||
notifier.toggle();
|
||||
expect(container.read(appUsersEditModeProvider), isTrue);
|
||||
|
||||
notifier.toggle();
|
||||
expect(container.read(appUsersEditModeProvider), isFalse);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user