import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; import 'package:result_monad/result_monad.dart'; import '../globals.dart'; import '../models/exec_error.dart'; import '../models/user_notification.dart'; import '../routes.dart'; import '../services/connections_manager.dart'; import '../services/notifications_manager.dart'; import '../services/timeline_manager.dart'; import '../utils/active_profile_selector.dart'; import '../utils/dateutils.dart'; import '../utils/snackbar_builder.dart'; import 'html_text_viewer_control.dart'; import 'image_control.dart'; class NotificationControl extends StatelessWidget { static final _logger = Logger('$NotificationControl'); final UserNotification notification; const NotificationControl({ super.key, required this.notification, }); Future _goToStatus(BuildContext context) async { final manager = getIt>().activeEntry.value; final existingPostData = manager.getPostTreeEntryBy(notification.iid); if (existingPostData.isSuccess) { context .push('/post/view/${existingPostData.value.id}/${notification.iid}'); return; } final loadedPost = await manager.refreshStatusChain(notification.iid); if (loadedPost.isSuccess) { context.push('/post/view/${loadedPost.value.id}/${notification.iid}'); return; } buildSnackbar( context, 'Error getting data for notification: ${loadedPost.error}'); } @override Widget build(BuildContext context) { const iconSize = 50.0; final manager = context .watch>() .activeEntry .fold( onSuccess: (manager) => manager, onError: (error) { _logger.severe('Error getting notification manager: $error'); return null; }, ); final fromIcon = getIt>() .activeEntry .andThen((manager) => manager.getById(notification.fromId)) .fold( onSuccess: (connection) => ImageControl( imageUrl: connection.avatarUrl.toString(), iconOverride: const Icon(Icons.person), width: iconSize, height: iconSize, onTap: () async { context.pushNamed(ScreenPaths.userProfile, pathParameters: {'id': notification.fromId}); }, ), onError: (error) => GestureDetector( onTap: () async { context.pushNamed(ScreenPaths.userProfile, pathParameters: {'id': notification.fromId}); }, child: const SizedBox( width: iconSize, height: iconSize, child: Icon(Icons.person), ), ), ); Function()? onTap; switch (notification.type) { case NotificationType.follow: onTap = () { context.pushNamed(ScreenPaths.userProfile, pathParameters: {'id': notification.fromId}); }; break; case NotificationType.follow_request: onTap = () { context.push('/connect/${notification.fromId}'); }; break; case NotificationType.unknown: buildSnackbar(context, 'Unknown message type, nothing to do'); break; case NotificationType.favourite: case NotificationType.mention: case NotificationType.reshare: case NotificationType.reblog: case NotificationType.status: onTap = () { _goToStatus(context); }; break; case NotificationType.direct_message: onTap = () => context.pushNamed( ScreenPaths.thread, queryParameters: {'uri': notification.iid}, ); break; } return ListTile( tileColor: notification.dismissed ? null : Colors.black12, leading: fromIcon, title: GestureDetector( onTap: onTap == null ? null : () async => onTap!(), child: HtmlTextViewerControl( content: notification.content, onTapUrl: onTap == null ? null : (_) async => onTap!(), ), ), subtitle: GestureDetector( onTap: onTap == null ? null : () async => onTap!(), child: Text( ElapsedDateUtils.epochSecondsToString(notification.timestamp), ), ), trailing: notification.dismissed || notification.type == NotificationType.direct_message ? null : IconButton( onPressed: manager == null ? null : () async { await manager.markSeen(notification).withError((error) { buildSnackbar( context, 'Error marking notification: $error'); logError(error, _logger); }); }, icon: const Icon(Icons.close_rounded)), ); } }