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/follow_requests_manager.dart';
|
||||
import 'services/gallery_service.dart';
|
||||
import 'services/interactions_manager.dart';
|
||||
import 'services/network_status_service.dart';
|
||||
import 'services/notifications_manager.dart';
|
||||
import 'services/persistent_info_service.dart';
|
||||
|
|
@ -110,9 +109,6 @@ Future<void> dependencyInjectionInitialization() async {
|
|||
getIt.registerSingleton<ActiveProfileSelector<FollowRequestsManager>>(
|
||||
ActiveProfileSelector((p) => FollowRequestsManager(p))
|
||||
..subscribeToProfileSwaps());
|
||||
getIt.registerSingleton<ActiveProfileSelector<InteractionsManager>>(
|
||||
ActiveProfileSelector((p) => InteractionsManager(p))
|
||||
..subscribeToProfileSwaps());
|
||||
}
|
||||
|
||||
Future<void> updateProfileDependencyInjectors(Profile profile) async {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import 'services/connections_manager.dart';
|
|||
import 'services/entry_manager_service.dart';
|
||||
import 'services/follow_requests_manager.dart';
|
||||
import 'services/gallery_service.dart';
|
||||
import 'services/interactions_manager.dart';
|
||||
import 'services/notifications_manager.dart';
|
||||
import 'services/timeline_manager.dart';
|
||||
import 'update_timer_initialization.dart';
|
||||
|
|
@ -137,9 +136,6 @@ class _AppState extends fr.ConsumerState<App> {
|
|||
create: (_) =>
|
||||
getIt<ActiveProfileSelector<FollowRequestsManager>>(),
|
||||
),
|
||||
ChangeNotifierProvider<ActiveProfileSelector<InteractionsManager>>(
|
||||
create: (_) => getIt<ActiveProfileSelector<InteractionsManager>>(),
|
||||
),
|
||||
],
|
||||
child: MaterialApp.router(
|
||||
// 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:go_router/go_router.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 '../riverpod_controllers/blocks_services.dart';
|
||||
|
|
@ -16,60 +16,45 @@ class BlocksScreen extends ConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final profile = context.watch<AccountsService>().currentProfile;
|
||||
final blocksValue = ref.watch(blocksManagerProvider(profile));
|
||||
|
||||
final body = switch (blocksValue) {
|
||||
AsyncError(:final error) => Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [Text('Error getting blocks: $error')],
|
||||
),
|
||||
),
|
||||
AsyncData<List<Connection>>(:final value) => ListView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
final contact = value[index];
|
||||
return ListTile(
|
||||
onTap: () async {
|
||||
context.pushNamed(ScreenPaths.userProfile,
|
||||
pathParameters: {'id': contact.id});
|
||||
},
|
||||
leading: ImageControl(
|
||||
imageUrl: contact.avatarUrl.toString(),
|
||||
iconOverride: const Icon(Icons.person),
|
||||
width: 32.0,
|
||||
),
|
||||
title: Text(
|
||||
'${contact.name} (${contact.handle})',
|
||||
softWrap: true,
|
||||
),
|
||||
subtitle: Text(
|
||||
'Last Status: ${contact.lastStatus?.toIso8601String() ?? "Unknown"}',
|
||||
softWrap: true,
|
||||
),
|
||||
trailing: ElevatedButton(
|
||||
onPressed: () async => await ref
|
||||
.read(blocksManagerProvider(profile).notifier)
|
||||
.unblockConnection(contact),
|
||||
child: const Text('Unblock'),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: value.length,
|
||||
),
|
||||
_ => const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
CircularProgressIndicator(),
|
||||
],
|
||||
),
|
||||
),
|
||||
};
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Blocks'),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: body,
|
||||
child: AsyncValueWidget(
|
||||
blocksValue,
|
||||
valueBuilder: (context, ref, value) => ListView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
final contact = value[index];
|
||||
return ListTile(
|
||||
onTap: () async {
|
||||
context.pushNamed(ScreenPaths.userProfile,
|
||||
pathParameters: {'id': contact.id});
|
||||
},
|
||||
leading: ImageControl(
|
||||
imageUrl: contact.avatarUrl.toString(),
|
||||
iconOverride: const Icon(Icons.person),
|
||||
width: 32.0,
|
||||
),
|
||||
title: Text(
|
||||
'${contact.name} (${contact.handle})',
|
||||
softWrap: true,
|
||||
),
|
||||
subtitle: Text(
|
||||
'Last Status: ${contact.lastStatus?.toIso8601String() ?? "Unknown"}',
|
||||
softWrap: true,
|
||||
),
|
||||
trailing: ElevatedButton(
|
||||
onPressed: () async => await ref
|
||||
.read(blocksManagerProvider(profile).notifier)
|
||||
.unblockConnection(contact),
|
||||
child: const Text('Unblock'),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: value.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,25 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.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/responsive_max_width.dart';
|
||||
import '../controls/standard_appbar.dart';
|
||||
import '../controls/status_and_refresh_button.dart';
|
||||
import '../globals.dart';
|
||||
import '../models/connection.dart';
|
||||
import '../models/exec_error.dart';
|
||||
import '../models/auth/profile.dart';
|
||||
import '../models/interaction_type_enum.dart';
|
||||
import '../routes.dart';
|
||||
import '../services/auth_service.dart';
|
||||
import '../services/connections_manager.dart';
|
||||
import '../services/interactions_manager.dart';
|
||||
import '../services/network_status_service.dart';
|
||||
import '../utils/active_profile_selector.dart';
|
||||
|
||||
class InteractionsViewerScreen extends StatelessWidget {
|
||||
class InteractionsViewerScreen extends ConsumerWidget {
|
||||
final String statusId;
|
||||
final InteractionType type;
|
||||
|
||||
|
|
@ -27,44 +29,43 @@ class InteractionsViewerScreen extends StatelessWidget {
|
|||
required this.type,
|
||||
});
|
||||
|
||||
List<Connection> getInteractors(InteractionsManager manager) {
|
||||
void refreshInteractors(WidgetRef ref, Profile profile) async {
|
||||
switch (type) {
|
||||
case InteractionType.like:
|
||||
return manager.getLikes(statusId);
|
||||
return ref.invalidate(likesForStatusProvider(profile, statusId));
|
||||
case InteractionType.reshare:
|
||||
return manager.getReshares(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);
|
||||
return ref.invalidate(resharesForStatusProvider(profile, statusId));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final profile = context.watch<AccountsService>().currentProfile;
|
||||
final nss = getIt<NetworkStatusService>();
|
||||
final manager = context
|
||||
.watch<ActiveProfileSelector<InteractionsManager>>()
|
||||
.activeEntry
|
||||
.value;
|
||||
final connections = getInteractors(manager);
|
||||
final connectionsAsyncValue = switch (type) {
|
||||
InteractionType.like =>
|
||||
ref.watch(likesForStatusProvider(profile, statusId)),
|
||||
InteractionType.reshare =>
|
||||
ref.watch(resharesForStatusProvider(profile, statusId)),
|
||||
};
|
||||
|
||||
return Scaffold(
|
||||
appBar: StandardAppBar.build(context, buildTitle(), actions: [
|
||||
StatusAndRefreshButton(
|
||||
valueListenable: nss.interactionsLoadingStatus,
|
||||
refreshFunction: () async => await refreshInteractors(manager),
|
||||
refreshFunction: () async => refreshInteractors(ref, profile),
|
||||
busyColor: Theme.of(context).colorScheme.surface,
|
||||
)
|
||||
]),
|
||||
body: Center(
|
||||
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,
|
||||
itemBuilder: (context, index) {
|
||||
final connection = connections[index];
|
||||
|
|
@ -94,8 +95,8 @@ class InteractionsViewerScreen extends StatelessWidget {
|
|||
);
|
||||
},
|
||||
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