import 'package:flutter/material.dart'; import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import 'package:result_monad/result_monad.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/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 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); } late final Widget body; if (result.isFailure) { body = Text('Error getting request info: ${result.error}'); } else { final contact = result.value; final contactStatus = cm .getById(widget.userId) .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('Invalid state, nothing to do here: ${contact.status}'); 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, ) { // Options are: // Accept and follow back // Accept and don't follow back // Reject // Back with no action // Calling method should check if completed (true) or not (false) to decide if updating their view of that item 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 => 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, params: {'id': contact.id}, ), child: const Text('Posts')), ElevatedButton( onPressed: () async => await openProfileExternal(context, contact), child: const Text('Open In Browser'), ), ], ), const VerticalPadding(), HtmlWidget( 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.updateNotifications()); } }