import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.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/group_data.dart'; import '../routes.dart'; import '../services/auth_service.dart'; import '../services/blocks_manager.dart'; import '../services/connections_manager.dart'; import '../utils/active_profile_selector.dart'; import '../utils/snackbar_builder.dart'; import '../utils/url_opening_utils.dart'; class UserProfileScreen extends StatefulWidget { final String userId; const UserProfileScreen({super.key, required this.userId}); @override State createState() => _UserProfileScreenState(); } class _UserProfileScreenState extends State { var isUpdating = false; @override void initState() { super.initState(); getIt>().activeEntry.withResult( (m) => m.getById(widget.userId, forceUpdate: true), ); } @override Widget build(BuildContext context) { final connectionManager = context .watch>() .activeEntry .value; final blocksManager = context.watch>().activeEntry.value; final body = connectionManager.getById(widget.userId).fold(onSuccess: (profile) { final notMyProfile = getIt().currentProfile.userId != profile.id; return RefreshIndicator( onRefresh: () async { await connectionManager.fullRefresh(profile, withNotifications: false); await blocksManager.updateBlock(profile); }, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ LoginAwareCachedNetworkImage( imageUrl: profile.avatarUrl.toString()), const Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [], ), Text( notMyProfile ? '${profile.name} (${profile.handle})' : '${profile.name} (Your Account)', softWrap: true, textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleLarge, ), const VerticalPadding(), Text('( ${profile.status.label()} )'), const VerticalPadding(), Wrap( spacing: 10.0, runSpacing: 10.0, children: [ if (notMyProfile) ...[ buildConnectionStatusToggle( context, profile, connectionManager), buildBlockToggle(context, profile, blocksManager), ], ElevatedButton( onPressed: () => context.pushNamed( ScreenPaths.userPosts, pathParameters: {'id': profile.id}, ), child: const Text('Posts')), ElevatedButton( onPressed: () async => await openProfileExternal(context, profile), child: const Text('Open In Browser'), ), ], ), const VerticalPadding(), HtmlTextViewerControl( content: profile.note, onTapUrl: (url) async { return await openUrlStringInSystembrowser( context, url, 'link'); }, ), const VerticalPadding(), Text( '#Followers: ${profile.followerCount} followers, #Following, ${profile.followingCount}, #Statuses: ${profile.statusesCount}'), const VerticalPadding(), Text('Last Status: ${profile.lastStatus ?? "Unknown"}'), const VerticalPadding(), if (profile.status == ConnectionStatus.mutual || profile.status == ConnectionStatus.youFollowThem) buildGroups(context, profile, connectionManager), ], ), ), ); }, onError: (error) { return Text('Error getting profile: $error'); }); return Scaffold( appBar: AppBar( title: const Text('Profile'), ), body: Padding( padding: const EdgeInsets.all(8.0), child: Center( child: body, ), ), ); } Widget buildGroups( BuildContext context, Connection profile, ConnectionsManager manager, ) { final myGroups = manager.getMyGroups(); final usersGroups = manager.getGroupsForUser(profile.id).fold( onSuccess: (groups) => groups.toSet(), onError: (error) { buildSnackbar(context, 'Error getting group data: $error'); return {}; }); myGroups.sort((g1, g2) => g1.name.compareTo(g2.name)); final groupsWidgets = myGroups.map((g) { return CheckboxListTile( title: Text(g.name), value: usersGroups.contains(g), onChanged: isUpdating ? null : (bool? value) async { final isAdding = value == true; final confirm = await showYesNoDialog( context, isAdding ? 'Add user to ${g.name}' : 'Remove user from ${g.name}'); if (confirm != true) { return; } setState(() { isUpdating = true; }); if (isAdding) { await manager.addUserToGroup(g, profile); } else { await manager.removeUserFromGroup(g, profile); } setState(() { isUpdating = false; }); }, ); }).toList(); return Column( children: [ Text( 'Groups: ', style: Theme.of(context).textTheme.titleMedium, ), const VerticalPadding(), ...groupsWidgets, ], ); } Widget buildBlockToggle( BuildContext context, Connection profile, BlocksManager manager, ) { late Widget blockToggleButton; switch (profile.status) { case ConnectionStatus.blocked: blockToggleButton = ElevatedButton( onPressed: isUpdating ? null : () async { final confirm = await showYesNoDialog(context, 'Unblock ${profile.name}'); if (confirm != true) { return; } setState(() { isUpdating = true; }); await manager.unblockConnection(profile); setState(() { isUpdating = false; }); }, child: const Text('Unblock'), ); break; case ConnectionStatus.mutual: case ConnectionStatus.theyFollowYou: case ConnectionStatus.youFollowThem: case ConnectionStatus.none: case ConnectionStatus.unknown: blockToggleButton = ElevatedButton( onPressed: isUpdating ? null : () async { final confirm = await showYesNoDialog(context, 'Block ${profile.name}'); if (confirm != true) { return; } setState(() { isUpdating = true; }); await manager.blockConnection(profile); setState(() { isUpdating = false; }); }, child: const Text('Block'), ); break; case ConnectionStatus.you: blockToggleButton = const SizedBox(); break; } return blockToggleButton; } Widget buildConnectionStatusToggle( BuildContext context, Connection profile, ConnectionsManager manager, ) { late Widget followToggleButton; switch (profile.status) { case ConnectionStatus.mutual: case ConnectionStatus.youFollowThem: followToggleButton = ElevatedButton( onPressed: isUpdating ? null : () async { final confirm = await showYesNoDialog( context, 'Unfollow ${profile.name}'); if (confirm != true) { return; } setState(() { isUpdating = true; }); await manager.unfollow(profile); setState(() { isUpdating = false; }); }, child: const Text('Unfollow'), ); break; case ConnectionStatus.theyFollowYou: case ConnectionStatus.none: followToggleButton = ElevatedButton( onPressed: isUpdating ? null : () async { final confirm = await showYesNoDialog(context, 'Follow ${profile.name}'); if (confirm != true) { return; } setState(() { isUpdating = true; }); await manager.follow(profile); setState(() { isUpdating = false; }); }, child: const Text('Follow'), ); break; case ConnectionStatus.blocked: case ConnectionStatus.you: case ConnectionStatus.unknown: followToggleButton = const SizedBox(); break; } return followToggleButton; } }