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 '../controls/linear_status_indicator.dart'; import '../controls/responsive_max_width.dart'; import '../controls/status_and_refresh_button.dart'; import '../globals.dart'; import '../models/connection.dart'; import '../models/exec_error.dart'; import '../models/group_data.dart'; import '../routes.dart'; import '../services/connections_manager.dart'; import '../services/network_status_service.dart'; import '../utils/active_profile_selector.dart'; import '../utils/snackbar_builder.dart'; class GroupAddUsersScreen extends StatefulWidget { final String groupId; const GroupAddUsersScreen({super.key, required this.groupId}); @override State createState() => _GroupAddUsersScreenState(); } class _GroupAddUsersScreenState extends State { static final _logger = Logger('$GroupAddUsersScreen'); var filterText = ''; late GroupData groupData; @override void initState() { super.initState(); final manager = getIt>().activeEntry.value; groupData = manager.getMyGroups().where((g) => g.id == widget.groupId).first; } Future addUserToGroup( ConnectionsManager manager, Connection connection, ) async { final messageBase = '${connection.name} from ${groupData.name}'; final confirm = await showYesNoDialog(context, 'Add $messageBase?'); if (context.mounted && confirm == true) { final message = await manager .addUserToGroup(groupData, connection) .withResult((p0) => setState(() {})) .fold( onSuccess: (_) => 'Added $messageBase', onError: (error) => 'Error adding $messageBase: $error', ); buildSnackbar(context, message); } } @override Widget build(BuildContext context) { _logger.finer('Build'); final nss = getIt(); final manager = context .watch>() .activeEntry .value; final groupMembers = manager .getGroupMembers(groupData) .withError((e) => logError(e, _logger)) .getValueOrElse(() => []) .toSet(); final allContacts = manager.getMyContacts(); final filterTextLC = filterText.toLowerCase(); final contacts = allContacts .where((c) => !groupMembers.contains(c)) .where((c) => filterText.isEmpty || c.name.toLowerCase().contains(filterTextLC) || c.handle.toLowerCase().contains(filterTextLC)) .toList(); contacts.sort((c1, c2) => c1.name.compareTo(c2.name)); _logger.finer( () => '# in group: ${groupMembers.length} # Contacts: ${allContacts.length}, #filtered: ${contacts.length}', ); late Widget body; if (contacts.isEmpty) { body = const SingleChildScrollView( physics: AlwaysScrollableScrollPhysics(), child: Center( child: Text('No contacts'), )); } else { body = ListView.separated( physics: const AlwaysScrollableScrollPhysics(), itemBuilder: (context, index) { final contact = contacts[index]; return ListTile( onTap: () { context.pushNamed(ScreenPaths.userProfile, pathParameters: {'id': contact.id}); }, title: Text( '${contact.name} (${contact.handle})', softWrap: true, ), subtitle: Text( 'Last Status: ${contact.lastStatus?.toIso8601String() ?? "Unknown"}', softWrap: true, ), trailing: IconButton( onPressed: () async => await addUserToGroup(manager, contact), icon: const Icon(Icons.add)), ); }, separatorBuilder: (context, index) => const Divider(), itemCount: contacts.length); } return Scaffold( appBar: AppBar( title: const Text('Add Users'), ), body: SafeArea( child: RefreshIndicator( onRefresh: () async { if (nss.connectionUpdateStatus.value) { return; } manager.refreshGroupMemberships(groupData); return; }, child: ResponsiveMaxWidth( child: Column( children: [ Text( 'Group: ${groupData.name}', style: Theme.of(context).textTheme.bodyLarge, softWrap: true, ), Row( children: [ Expanded( child: Padding( padding: const EdgeInsets.all(8.0), child: TextField( onChanged: (value) { setState(() { filterText = value.toLowerCase(); }); }, decoration: InputDecoration( labelText: 'Filter By Name', alignLabelWithHint: true, border: OutlineInputBorder( borderSide: BorderSide( color: Theme.of(context).highlightColor, ), borderRadius: BorderRadius.circular(5.0), ), ), ), ), ), Padding( padding: const EdgeInsets.all(8.0), child: StatusAndRefreshButton( valueListenable: nss.connectionUpdateStatus, refreshFunction: () async => manager.refreshGroupMemberships(groupData), ), ) ], ), StandardLinearProgressIndicator(nss.connectionUpdateStatus), Expanded(child: body), ], ), ), ), ), ); } }