import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import 'package:result_monad/result_monad.dart'; import '../controls/html_text_viewer_control.dart'; import '../controls/login_aware_cached_network_image.dart'; import '../controls/padding.dart'; import '../globals.dart'; import '../models/connection.dart'; import '../models/exec_error.dart'; import '../routes.dart'; import '../services/connections_manager.dart'; import '../services/feature_version_checker.dart'; import '../services/follow_requests_manager.dart'; import '../services/network_status_service.dart'; import '../services/notifications_manager.dart'; import '../utils/active_profile_selector.dart'; import '../utils/url_opening_utils.dart'; class FollowRequestAdjudicationScreen extends StatefulWidget { final String userId; const FollowRequestAdjudicationScreen({super.key, required this.userId}); @override State createState() => _FollowRequestAdjudicationScreenState(); } class _FollowRequestAdjudicationScreenState extends State { var processing = false; @override Widget build(BuildContext context) { final nss = getIt(); final fm = getIt>().activeEntry.value; final cm = context .watch>() .activeEntry .value; late final Result result; if (getIt() .canUseFeature(RelaticaFeatures.usingActualFollowRequests)) { result = fm .getByUserId(widget.userId) .mapValue((request) => request.connection); } else { result = cm.getById(widget.userId, forceUpdate: true); } late final Widget body; if (result.isFailure) { if (result.error.type == ErrorType.notFound && nss.connectionUpdateStatus.value) { fm.update(); body = const Text('Loading...'); } else { body = Text('Error getting request info: ${result.error}'); } } else { final contact = result.value; final contactStatus = cm .getById(widget.userId, forceUpdate: true) .getValueOrElse(() => Connection(status: ConnectionStatus.none)) .status; switch (contactStatus) { case ConnectionStatus.theyFollowYou: case ConnectionStatus.youFollowThem: case ConnectionStatus.none: body = _buildMainPanel(context, contact, cm, fm); break; case ConnectionStatus.mutual: body = const Text('Already allowed them to connect'); break; case ConnectionStatus.you: case ConnectionStatus.unknown: body = Text(nss.connectionUpdateStatus.value ? 'Loading...' : 'Invalid state, nothing to do here: ${contact.status}'); break; case ConnectionStatus.blocked: // we should never get here because a blocked user shouldn't be allowed to create a connection request. body = const Text( 'Use is blocked. Unblock to accept connection request.', ); break; } } return Scaffold( appBar: AppBar( title: const Text( 'Accept Request?', )), body: Padding( padding: const EdgeInsets.all(8.0), child: Center(child: body), ), ); } Widget _buildMainPanel( BuildContext context, Connection contact, ConnectionsManager connectionsManager, FollowRequestsManager followRequestsManager, ) { return SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ LoginAwareCachedNetworkImage(imageUrl: contact.avatarUrl.toString()), Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '${contact.name}(${contact.handle})', style: Theme.of(context).textTheme.titleLarge, ), const HorizontalPadding(), ], ), const VerticalPadding(), Wrap( runSpacing: 5.0, spacing: 5.0, alignment: WrapAlignment.center, children: [ ElevatedButton( onPressed: processing ? null : () async => await accept(connectionsManager, followRequestsManager, contact, true), child: const Text('Accept and follow back'), ), ElevatedButton( onPressed: processing ? null : () async => accept(connectionsManager, followRequestsManager, contact, false), child: const Text("Accept but don't follow back"), ), ElevatedButton( onPressed: processing ? null : () async => reject( connectionsManager, followRequestsManager, contact), child: const Text('Reject'), ), ElevatedButton( onPressed: processing ? null : () async => ignore( connectionsManager, followRequestsManager, contact), child: const Text('Ignore (Rejects but user cannot ask again)'), ), ], ), const VerticalPadding(), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: () => context.pushNamed( ScreenPaths.userPosts, pathParameters: {'id': contact.id}, ), child: const Text('Posts')), ElevatedButton( onPressed: () async => await openProfileExternal(context, contact), child: const Text('Open In Browser'), ), ], ), const VerticalPadding(), HtmlTextViewerControl( content: contact.note, onTapUrl: (url) async { return await openUrlStringInSystembrowser(context, url, 'link'); }, ), const VerticalPadding(), Text( '#Followers: ${contact.followerCount} followers, #Following, ${contact.followingCount}, #Statuses: ${contact.statusesCount}'), const VerticalPadding(), Text('Last Status: ${contact.lastStatus ?? "Unknown"}'), ], ), ); } Future accept( ConnectionsManager manager, FollowRequestsManager followRequestsManager, Connection contact, bool followBack, ) async { setState(() { processing = true; }); await manager.acceptFollowRequest(contact); if (followBack) { await manager.follow(contact); } _performUpdates(followRequestsManager); setState(() { processing = false; }); if (mounted && context.canPop()) { context.pop(); } } Future reject(ConnectionsManager manager, FollowRequestsManager followRequestsManager, Connection contact) async { setState(() { processing = true; }); await manager.rejectFollowRequest(contact); _performUpdates(followRequestsManager); setState(() { processing = false; }); if (mounted && context.canPop()) { context.pop(); } } Future ignore(ConnectionsManager manager, FollowRequestsManager followRequestsManager, Connection contact) async { setState(() { processing = true; }); await manager.ignoreFollowRequest(contact); _performUpdates(followRequestsManager); setState(() { processing = false; }); if (mounted && context.canPop()) { context.pop(); } } void _performUpdates(FollowRequestsManager followRequestsManager) { followRequestsManager.update(); getIt>() .activeEntry .andThenSuccess((m) => m.refreshNotifications()); } }