import 'dart:math'; import 'package:comunic/enums/load_error_level.dart'; import 'package:comunic/enums/report_target_type.dart'; import 'package:comunic/helpers/conversations_helper.dart'; import 'package:comunic/helpers/events_helper.dart'; import 'package:comunic/helpers/groups_helper.dart'; import 'package:comunic/helpers/users_helper.dart'; import 'package:comunic/lists/conversations_list.dart'; import 'package:comunic/lists/groups_list.dart'; import 'package:comunic/lists/users_list.dart'; import 'package:comunic/models/conversation.dart'; import 'package:comunic/models/report_target.dart'; import 'package:comunic/ui/dialogs/report_dialog.dart'; import 'package:comunic/ui/routes/main_route/main_route.dart'; import 'package:comunic/ui/screens/create_conversation_screen.dart'; import 'package:comunic/ui/tiles/conversation_tile.dart'; import 'package:comunic/ui/widgets/safe_state.dart'; import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/ui_utils.dart'; import 'package:flutter/material.dart'; /// Conversations screen /// /// @author Pierre HUBERT class ConversationsListScreen extends StatefulWidget { final bool useSmallFAB; final Function()? onOpen; const ConversationsListScreen({ Key? key, this.useSmallFAB = false, this.onOpen, }) : super(key: key); @override State createState() => _ConversationScreenState(); } class _ConversationScreenState extends SafeState { final ConversationsHelper _conversationsHelper = ConversationsHelper(); final UsersHelper _usersHelper = UsersHelper(); ConversationsList? _list; late UsersList _users; GroupsList? _groups; LoadErrorLevel _error = LoadErrorLevel.NONE; final GlobalKey _refreshIndicatorKey = GlobalKey(); @override void initState() { super.initState(); this.listen( (d) => _refreshIndicatorKey.currentState!.show()); } @override void didChangeDependencies() { super.didChangeDependencies(); _loadConversations(); } void setError(LoadErrorLevel err) => setState(() => _error = err); /// Get the list of conversations, once from the cache, once from the server Future _loadConversations() async { await _loadConversationsList(true); await _loadConversationsList(false); } /// Load the list of conversations Future _loadConversationsList(bool cached) async { setError(LoadErrorLevel.NONE); try { ConversationsList list = cached ? await _conversationsHelper.getCachedList() : await _conversationsHelper.downloadList(); // Get information about the members of the conversations _users = await _usersHelper.getList(list.allUsersID); _groups = await GroupsHelper().getListOrThrow(list.allGroupsID); setState(() => _list = list); } catch (e, s) { debugPrint("Failed to get conversations list! $e => $s", wrapWidth: 1024); setError(_list == null ? LoadErrorLevel.MAJOR : LoadErrorLevel.MINOR); } } /// Build an error card Widget _buildErrorCard() { return buildErrorCard( tr("Could not retrieve the list of conversations!"), actions: [ TextButton( onPressed: () => _refreshIndicatorKey.currentState!.show(), child: Text( tr("Retry")!.toUpperCase(), style: TextStyle( color: Colors.white, ), ), ) ], ); } /// Open a conversation void _openConversation(Conversation conv) { MainController.of(context)!.openConversation(conv); if (widget.onOpen != null) widget.onOpen!(); } /// Create a new conversation void _createConversation() { MainController.of(context)!.push( CreateConversationScreen(), canShowAsDialog: true, hideNavBar: true, ); if (widget.onOpen != null) widget.onOpen!(); } /// Handle conversations updated requests void _updateConversation(Conversation conversation) { MainController.of(context)!.openConversationSettingsRoute(conversation); } /// Handle conversation deletion request Future _requestLeaveConversation(Conversation conversation) async { if (conversation.isGroupConversation) { showAlert( context: context, title: tr("Group conversation"), message: tr( "This conversation is managed by a group. You can not leave it this way. If you do not want to receive notifications from this conversation anymore, you can stop following it in the conversation settings")); return; } final result = await showConfirmDialog( context: context, message: conversation.isLastAdmin ? tr( "Do you really want to leave this conversation ? As you are its last admin, it will be completely deleted!") : tr("Do you really want to leave this conversation ?")); if (!result) return; // Request the conversation to be deleted now try { await _conversationsHelper.deleteConversation(conversation.id); } catch (e, s) { print("Failed to leave conversation! $e => $s"); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(tr("Could not leave the conversation!")!))); } // Reload the list of conversations _loadConversationsList(false); } /// Handle conversation report request void _reportConversation(Conversation conversation) => showReportDialog( ctx: context, target: ReportTarget(ReportTargetType.Conversation, conversation.id!)); @override Widget build(BuildContext context) { if (_error == LoadErrorLevel.MAJOR) return _buildErrorCard(); if (_list == null) return buildCenteredProgressBar(); // Show the list of conversations return Stack( children: [ Column( children: [ Container( child: _error == LoadErrorLevel.MINOR ? _buildErrorCard() : null, ), Expanded( child: RefreshIndicator( onRefresh: () => _loadConversationsList(false), key: _refreshIndicatorKey, child: ListView.builder( physics: AlwaysScrollableScrollPhysics(), controller: ScrollController(), itemBuilder: (context, index) { if (_list!.isEmpty) return Padding( padding: const EdgeInsets.all(8.0), child: Center( child: Text( tr("You do not have any conversation yet!")!)), ); if (_list![index].isGroupConversation && !_list![index].following) return Container(); return ConversationTile( conversation: _list!.elementAt(index), usersList: _users, groupsList: _groups, onOpen: (c) { _openConversation(c); }, onRequestUpdate: _updateConversation, onRequestLeave: _requestLeaveConversation, onReport: _reportConversation, ); }, itemCount: max(_list!.length, 1), ), ), ), ], ), // Add conversation button Positioned( right: widget.useSmallFAB ? 5.0 : 20.0, bottom: widget.useSmallFAB ? 5.0 : 20.0, child: FloatingActionButton( mini: widget.useSmallFAB, onPressed: () => _createConversation(), child: Icon(Icons.add), ), ), ], ); } }