kopia lustrzana https://gitlab.com/mysocialportal/relatica
				
				
				
			Refactory Interactions Details to Riverpod providers
							rodzic
							
								
									c4e739a962
								
							
						
					
					
						commit
						733751038b
					
				|  | @ -0,0 +1,43 @@ | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||||||
|  | 
 | ||||||
|  | typedef ValueWidgetBuilder<T> = Widget Function( | ||||||
|  |     BuildContext context, WidgetRef ref, T value); | ||||||
|  | 
 | ||||||
|  | class AsyncValueWidget<T> extends ConsumerWidget { | ||||||
|  |   final AsyncValue<T> asyncValue; | ||||||
|  |   final ValueWidgetBuilder<T> valueBuilder; | ||||||
|  |   final bool standaloneHolders; | ||||||
|  | 
 | ||||||
|  |   const AsyncValueWidget( | ||||||
|  |     this.asyncValue, { | ||||||
|  |     required this.valueBuilder, | ||||||
|  |     this.standaloneHolders = false, | ||||||
|  |     super.key, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|  |     return switch (asyncValue) { | ||||||
|  |       AsyncError(:final error) => !standaloneHolders | ||||||
|  |           ? Center( | ||||||
|  |               child: Column( | ||||||
|  |                 mainAxisAlignment: MainAxisAlignment.center, | ||||||
|  |                 children: [Text('Error getting data: $error')], | ||||||
|  |               ), | ||||||
|  |             ) | ||||||
|  |           : Text('Error getting data: $error'), | ||||||
|  |       AsyncData<T>(:final value) => valueBuilder(context, ref, value), | ||||||
|  |       _ => !standaloneHolders | ||||||
|  |           ? const Center( | ||||||
|  |               child: Column( | ||||||
|  |                 mainAxisAlignment: MainAxisAlignment.center, | ||||||
|  |                 children: [ | ||||||
|  |                   CircularProgressIndicator(), | ||||||
|  |                 ], | ||||||
|  |               ), | ||||||
|  |             ) | ||||||
|  |           : const CircularProgressIndicator(), | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,25 @@ | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | 
 | ||||||
|  | class ErrorMessageWidget extends StatelessWidget { | ||||||
|  |   final String message; | ||||||
|  |   final bool standalone; | ||||||
|  | 
 | ||||||
|  |   const ErrorMessageWidget( | ||||||
|  |       {super.key, required this.message, this.standalone = true}); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     final errorWidget = Text(message); | ||||||
|  | 
 | ||||||
|  |     if (standalone) { | ||||||
|  |       return errorWidget; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return Center( | ||||||
|  |       child: Column( | ||||||
|  |         mainAxisAlignment: MainAxisAlignment.center, | ||||||
|  |         children: [errorWidget], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -22,7 +22,6 @@ import 'services/feature_version_checker.dart'; | ||||||
| import 'services/fediverse_server_validator.dart'; | import 'services/fediverse_server_validator.dart'; | ||||||
| import 'services/follow_requests_manager.dart'; | import 'services/follow_requests_manager.dart'; | ||||||
| import 'services/gallery_service.dart'; | import 'services/gallery_service.dart'; | ||||||
| import 'services/interactions_manager.dart'; |  | ||||||
| import 'services/network_status_service.dart'; | import 'services/network_status_service.dart'; | ||||||
| import 'services/notifications_manager.dart'; | import 'services/notifications_manager.dart'; | ||||||
| import 'services/persistent_info_service.dart'; | import 'services/persistent_info_service.dart'; | ||||||
|  | @ -110,9 +109,6 @@ Future<void> dependencyInjectionInitialization() async { | ||||||
|   getIt.registerSingleton<ActiveProfileSelector<FollowRequestsManager>>( |   getIt.registerSingleton<ActiveProfileSelector<FollowRequestsManager>>( | ||||||
|       ActiveProfileSelector((p) => FollowRequestsManager(p)) |       ActiveProfileSelector((p) => FollowRequestsManager(p)) | ||||||
|         ..subscribeToProfileSwaps()); |         ..subscribeToProfileSwaps()); | ||||||
|   getIt.registerSingleton<ActiveProfileSelector<InteractionsManager>>( |  | ||||||
|       ActiveProfileSelector((p) => InteractionsManager(p)) |  | ||||||
|         ..subscribeToProfileSwaps()); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Future<void> updateProfileDependencyInjectors(Profile profile) async { | Future<void> updateProfileDependencyInjectors(Profile profile) async { | ||||||
|  |  | ||||||
|  | @ -20,7 +20,6 @@ import 'services/connections_manager.dart'; | ||||||
| import 'services/entry_manager_service.dart'; | import 'services/entry_manager_service.dart'; | ||||||
| import 'services/follow_requests_manager.dart'; | import 'services/follow_requests_manager.dart'; | ||||||
| import 'services/gallery_service.dart'; | import 'services/gallery_service.dart'; | ||||||
| import 'services/interactions_manager.dart'; |  | ||||||
| import 'services/notifications_manager.dart'; | import 'services/notifications_manager.dart'; | ||||||
| import 'services/timeline_manager.dart'; | import 'services/timeline_manager.dart'; | ||||||
| import 'update_timer_initialization.dart'; | import 'update_timer_initialization.dart'; | ||||||
|  | @ -137,9 +136,6 @@ class _AppState extends fr.ConsumerState<App> { | ||||||
|             create: (_) => |             create: (_) => | ||||||
|                 getIt<ActiveProfileSelector<FollowRequestsManager>>(), |                 getIt<ActiveProfileSelector<FollowRequestsManager>>(), | ||||||
|           ), |           ), | ||||||
|           ChangeNotifierProvider<ActiveProfileSelector<InteractionsManager>>( |  | ||||||
|             create: (_) => getIt<ActiveProfileSelector<InteractionsManager>>(), |  | ||||||
|           ), |  | ||||||
|         ], |         ], | ||||||
|         child: MaterialApp.router( |         child: MaterialApp.router( | ||||||
|           // TODO Add back Device Preview once supported in Flutter 3.22+ |           // TODO Add back Device Preview once supported in Flutter 3.22+ | ||||||
|  |  | ||||||
|  | @ -0,0 +1,39 @@ | ||||||
|  | import 'package:logging/logging.dart'; | ||||||
|  | import 'package:relatica/riverpod_controllers/rp_provider_extension.dart'; | ||||||
|  | import 'package:result_monad/result_monad.dart'; | ||||||
|  | import 'package:riverpod_annotation/riverpod_annotation.dart'; | ||||||
|  | 
 | ||||||
|  | import '../friendica_client/friendica_client.dart'; | ||||||
|  | import '../models/auth/profile.dart'; | ||||||
|  | import '../models/connection.dart'; | ||||||
|  | import '../models/exec_error.dart'; | ||||||
|  | 
 | ||||||
|  | part 'interactions_details_services.g.dart'; | ||||||
|  | 
 | ||||||
|  | const _cacheDuration = Duration(minutes: 5); | ||||||
|  | final _likesLogger = Logger('LikesForStatusProvider'); | ||||||
|  | final _resharesLogger = Logger('ResharesForStatusProvider'); | ||||||
|  | 
 | ||||||
|  | @riverpod | ||||||
|  | Future<Result<List<Connection>, ExecError>> likesForStatus( | ||||||
|  |     LikesForStatusRef ref, Profile profile, String statusId) async { | ||||||
|  |   _likesLogger.info('Creating provider for $statusId for Profile $profile'); | ||||||
|  |   ref.cacheFor(_cacheDuration); | ||||||
|  |   final likesResult = await InteractionsClient(profile).getLikes(statusId); | ||||||
|  |   _likesLogger.info('Values received for $statusId for Profile $profile'); | ||||||
|  | 
 | ||||||
|  |   return likesResult; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @riverpod | ||||||
|  | Future<Result<List<Connection>, ExecError>> resharesForStatus( | ||||||
|  |     ResharesForStatusRef ref, Profile profile, String statusId) async { | ||||||
|  |   ref.cacheFor(_cacheDuration); | ||||||
|  |   _resharesLogger.info('Creating provider for $statusId for Profile $profile'); | ||||||
|  | 
 | ||||||
|  |   final resharesResult = | ||||||
|  |       await InteractionsClient(profile).getReshares(statusId); | ||||||
|  |   _resharesLogger.info('Values received for $statusId for Profile $profile'); | ||||||
|  | 
 | ||||||
|  |   return resharesResult; | ||||||
|  | } | ||||||
|  | @ -2,7 +2,7 @@ import 'package:flutter/material.dart'; | ||||||
| import 'package:flutter_riverpod/flutter_riverpod.dart'; | import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||||||
| import 'package:go_router/go_router.dart'; | import 'package:go_router/go_router.dart'; | ||||||
| import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||||
| import 'package:relatica/models/connection.dart'; | import 'package:relatica/controls/async_value_widget.dart'; | ||||||
| 
 | 
 | ||||||
| import '../controls/image_control.dart'; | import '../controls/image_control.dart'; | ||||||
| import '../riverpod_controllers/blocks_services.dart'; | import '../riverpod_controllers/blocks_services.dart'; | ||||||
|  | @ -16,15 +16,14 @@ class BlocksScreen extends ConsumerWidget { | ||||||
|   Widget build(BuildContext context, WidgetRef ref) { |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|     final profile = context.watch<AccountsService>().currentProfile; |     final profile = context.watch<AccountsService>().currentProfile; | ||||||
|     final blocksValue = ref.watch(blocksManagerProvider(profile)); |     final blocksValue = ref.watch(blocksManagerProvider(profile)); | ||||||
| 
 |     return Scaffold( | ||||||
|     final body = switch (blocksValue) { |       appBar: AppBar( | ||||||
|       AsyncError(:final error) => Center( |         title: const Text('Blocks'), | ||||||
|           child: Column( |  | ||||||
|             mainAxisAlignment: MainAxisAlignment.center, |  | ||||||
|             children: [Text('Error getting blocks: $error')], |  | ||||||
|       ), |       ), | ||||||
|         ), |       body: SafeArea( | ||||||
|       AsyncData<List<Connection>>(:final value) => ListView.builder( |         child: AsyncValueWidget( | ||||||
|  |           blocksValue, | ||||||
|  |           valueBuilder: (context, ref, value) => ListView.builder( | ||||||
|             itemBuilder: (context, index) { |             itemBuilder: (context, index) { | ||||||
|               final contact = value[index]; |               final contact = value[index]; | ||||||
|               return ListTile( |               return ListTile( | ||||||
|  | @ -55,22 +54,8 @@ class BlocksScreen extends ConsumerWidget { | ||||||
|             }, |             }, | ||||||
|             itemCount: value.length, |             itemCount: value.length, | ||||||
|           ), |           ), | ||||||
|       _ => const Center( |  | ||||||
|           child: Column( |  | ||||||
|             mainAxisAlignment: MainAxisAlignment.center, |  | ||||||
|             children: [ |  | ||||||
|               CircularProgressIndicator(), |  | ||||||
|             ], |  | ||||||
|         ), |         ), | ||||||
|       ), |       ), | ||||||
|     }; |  | ||||||
|     return Scaffold( |  | ||||||
|       appBar: AppBar( |  | ||||||
|         title: const Text('Blocks'), |  | ||||||
|       ), |  | ||||||
|       body: SafeArea( |  | ||||||
|         child: body, |  | ||||||
|       ), |  | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,23 +1,25 @@ | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||||||
| import 'package:go_router/go_router.dart'; | import 'package:go_router/go_router.dart'; | ||||||
| import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||||
| import 'package:result_monad/result_monad.dart'; | import 'package:relatica/controls/async_value_widget.dart'; | ||||||
|  | import 'package:relatica/riverpod_controllers/interactions_details_services.dart'; | ||||||
| 
 | 
 | ||||||
|  | import '../controls/error_message_widget.dart'; | ||||||
| import '../controls/image_control.dart'; | import '../controls/image_control.dart'; | ||||||
| import '../controls/responsive_max_width.dart'; | import '../controls/responsive_max_width.dart'; | ||||||
| import '../controls/standard_appbar.dart'; | import '../controls/standard_appbar.dart'; | ||||||
| import '../controls/status_and_refresh_button.dart'; | import '../controls/status_and_refresh_button.dart'; | ||||||
| import '../globals.dart'; | import '../globals.dart'; | ||||||
| import '../models/connection.dart'; | import '../models/auth/profile.dart'; | ||||||
| import '../models/exec_error.dart'; |  | ||||||
| import '../models/interaction_type_enum.dart'; | import '../models/interaction_type_enum.dart'; | ||||||
| import '../routes.dart'; | import '../routes.dart'; | ||||||
|  | import '../services/auth_service.dart'; | ||||||
| import '../services/connections_manager.dart'; | import '../services/connections_manager.dart'; | ||||||
| import '../services/interactions_manager.dart'; |  | ||||||
| import '../services/network_status_service.dart'; | import '../services/network_status_service.dart'; | ||||||
| import '../utils/active_profile_selector.dart'; | import '../utils/active_profile_selector.dart'; | ||||||
| 
 | 
 | ||||||
| class InteractionsViewerScreen extends StatelessWidget { | class InteractionsViewerScreen extends ConsumerWidget { | ||||||
|   final String statusId; |   final String statusId; | ||||||
|   final InteractionType type; |   final InteractionType type; | ||||||
| 
 | 
 | ||||||
|  | @ -27,44 +29,43 @@ class InteractionsViewerScreen extends StatelessWidget { | ||||||
|     required this.type, |     required this.type, | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   List<Connection> getInteractors(InteractionsManager manager) { |   void refreshInteractors(WidgetRef ref, Profile profile) async { | ||||||
|     switch (type) { |     switch (type) { | ||||||
|       case InteractionType.like: |       case InteractionType.like: | ||||||
|         return manager.getLikes(statusId); |         return ref.invalidate(likesForStatusProvider(profile, statusId)); | ||||||
|       case InteractionType.reshare: |       case InteractionType.reshare: | ||||||
|         return manager.getReshares(statusId); |         return ref.invalidate(resharesForStatusProvider(profile, statusId)); | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   FutureResult<List<Connection>, ExecError> refreshInteractors( |  | ||||||
|       InteractionsManager manager) async { |  | ||||||
|     switch (type) { |  | ||||||
|       case InteractionType.like: |  | ||||||
|         return await manager.updateLikesForStatus(statusId); |  | ||||||
|       case InteractionType.reshare: |  | ||||||
|         return await manager.updateResharesForStatus(statusId); |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|  |     final profile = context.watch<AccountsService>().currentProfile; | ||||||
|     final nss = getIt<NetworkStatusService>(); |     final nss = getIt<NetworkStatusService>(); | ||||||
|     final manager = context |     final connectionsAsyncValue = switch (type) { | ||||||
|         .watch<ActiveProfileSelector<InteractionsManager>>() |       InteractionType.like => | ||||||
|         .activeEntry |         ref.watch(likesForStatusProvider(profile, statusId)), | ||||||
|         .value; |       InteractionType.reshare => | ||||||
|     final connections = getInteractors(manager); |         ref.watch(resharesForStatusProvider(profile, statusId)), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     return Scaffold( |     return Scaffold( | ||||||
|       appBar: StandardAppBar.build(context, buildTitle(), actions: [ |       appBar: StandardAppBar.build(context, buildTitle(), actions: [ | ||||||
|         StatusAndRefreshButton( |         StatusAndRefreshButton( | ||||||
|           valueListenable: nss.interactionsLoadingStatus, |           valueListenable: nss.interactionsLoadingStatus, | ||||||
|           refreshFunction: () async => await refreshInteractors(manager), |           refreshFunction: () async => refreshInteractors(ref, profile), | ||||||
|           busyColor: Theme.of(context).colorScheme.surface, |           busyColor: Theme.of(context).colorScheme.surface, | ||||||
|         ) |         ) | ||||||
|       ]), |       ]), | ||||||
|       body: Center( |       body: Center( | ||||||
|         child: ResponsiveMaxWidth( |         child: ResponsiveMaxWidth( | ||||||
|           child: ListView.separated( |             child: AsyncValueWidget(connectionsAsyncValue, | ||||||
|  |                 valueBuilder: (context, ref, connectionsResult) { | ||||||
|  |           if (connectionsResult.isFailure) { | ||||||
|  |             return ErrorMessageWidget(message: connectionsResult.error.message); | ||||||
|  |           } | ||||||
|  |           final connections = connectionsResult.value; | ||||||
|  |           return ListView.separated( | ||||||
|             itemCount: connections.length, |             itemCount: connections.length, | ||||||
|             itemBuilder: (context, index) { |             itemBuilder: (context, index) { | ||||||
|               final connection = connections[index]; |               final connection = connections[index]; | ||||||
|  | @ -94,8 +95,8 @@ class InteractionsViewerScreen extends StatelessWidget { | ||||||
|               ); |               ); | ||||||
|             }, |             }, | ||||||
|             separatorBuilder: (_, __) => const Divider(), |             separatorBuilder: (_, __) => const Divider(), | ||||||
|           ), |           ); | ||||||
|         ), |         })), | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -1,66 +0,0 @@ | ||||||
| import 'package:flutter/foundation.dart'; |  | ||||||
| import 'package:result_monad/result_monad.dart'; |  | ||||||
| 
 |  | ||||||
| import '../friendica_client/friendica_client.dart'; |  | ||||||
| import '../globals.dart'; |  | ||||||
| import '../models/auth/profile.dart'; |  | ||||||
| import '../models/connection.dart'; |  | ||||||
| import '../models/exec_error.dart'; |  | ||||||
| import 'auth_service.dart'; |  | ||||||
| 
 |  | ||||||
| class InteractionsManager extends ChangeNotifier { |  | ||||||
|   final _likesByStatusId = <String, List<Connection>>{}; |  | ||||||
|   final _resharesByStatusId = <String, List<Connection>>{}; |  | ||||||
| 
 |  | ||||||
|   final Profile profile; |  | ||||||
| 
 |  | ||||||
|   InteractionsManager(this.profile); |  | ||||||
| 
 |  | ||||||
|   void clear() { |  | ||||||
|     _likesByStatusId.clear(); |  | ||||||
|     _resharesByStatusId.clear(); |  | ||||||
|     notifyListeners(); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   List<Connection> getLikes(String statusId) { |  | ||||||
|     if (!_likesByStatusId.containsKey(statusId)) { |  | ||||||
|       updateLikesForStatus(statusId); |  | ||||||
|       return []; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return _likesByStatusId[statusId]!; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   List<Connection> getReshares(String statusId) { |  | ||||||
|     if (!_resharesByStatusId.containsKey(statusId)) { |  | ||||||
|       updateResharesForStatus(statusId); |  | ||||||
|       return []; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return _resharesByStatusId[statusId]!; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   FutureResult<List<Connection>, ExecError> updateLikesForStatus( |  | ||||||
|       String statusId) async { |  | ||||||
|     final likesResult = |  | ||||||
|         await InteractionsClient(getIt<AccountsService>().currentProfile) |  | ||||||
|             .getLikes(statusId); |  | ||||||
|     if (likesResult.isSuccess) { |  | ||||||
|       _likesByStatusId[statusId] = likesResult.value; |  | ||||||
|       notifyListeners(); |  | ||||||
|     } |  | ||||||
|     return likesResult; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   FutureResult<List<Connection>, ExecError> updateResharesForStatus( |  | ||||||
|       String statusId) async { |  | ||||||
|     final resharesResult = |  | ||||||
|         await InteractionsClient(getIt<AccountsService>().currentProfile) |  | ||||||
|             .getReshares(statusId); |  | ||||||
|     if (resharesResult.isSuccess) { |  | ||||||
|       _resharesByStatusId[statusId] = resharesResult.value; |  | ||||||
|       notifyListeners(); |  | ||||||
|     } |  | ||||||
|     return resharesResult; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
		Ładowanie…
	
		Reference in New Issue
	
	 Hank Grabowski
						Hank Grabowski