import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:provider/provider.dart'; import 'package:twonly/src/components/flame.dart'; import 'package:twonly/src/components/initialsavatar.dart'; import 'package:twonly/src/components/message_send_state_icon.dart'; import 'package:twonly/src/components/notification_badge.dart'; import 'package:twonly/src/components/user_context_menu.dart'; import 'package:twonly/src/model/contacts_model.dart'; import 'package:twonly/src/model/json/message.dart'; import 'package:twonly/src/model/messages_model.dart'; import 'package:twonly/src/providers/api/api.dart'; import 'package:twonly/src/providers/contacts_change_provider.dart'; import 'package:twonly/src/providers/download_change_provider.dart'; import 'package:twonly/src/providers/messages_change_provider.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/chats/chat_item_details_view.dart'; import 'package:twonly/src/views/chats/media_viewer_view.dart'; import 'package:twonly/src/views/settings/settings_main_view.dart'; import 'package:twonly/src/views/chats/search_username_view.dart'; import 'package:flutter/material.dart'; /// Displays a list of SampleItems. class ChatListView extends StatefulWidget { const ChatListView({super.key}); @override State createState() => _ChatListViewState(); } class _ChatListViewState extends State { @override Widget build(BuildContext context) { Map lastMessages = context.watch().lastMessage; List allUsers = context .watch() .allContacts .where((c) => c.accepted) .toList(); allUsers.sort((b, a) { DbMessage? msgA = lastMessages[a.userId.toInt()]; DbMessage? msgB = lastMessages[a.userId.toInt()]; if (msgA == null) return 1; if (msgB == null) return -1; return msgA.sendAt.compareTo(msgB.sendAt); }); int maxTotalMediaCounter = 0; if (allUsers.isNotEmpty) { maxTotalMediaCounter = allUsers .map((x) => x.totalMediaCounter) .reduce((a, b) => a > b ? a : b); } return Scaffold( appBar: AppBar( title: GestureDetector( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => ProfileView(), ), ); }, child: Text("twonly"), ), // title: actions: [ NotificationBadge( count: context .watch() .newContactRequests .toString(), child: IconButton( icon: FaIcon(FontAwesomeIcons.userPlus, size: 18), onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => SearchUsernameView(), ), ); }, ), ), IconButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => ProfileView(), ), ); }, icon: FaIcon(FontAwesomeIcons.gear, size: 19), ) ], ), body: (allUsers.isEmpty) ? Center( child: Padding( padding: const EdgeInsets.all(10), child: OutlinedButton.icon( icon: Icon(Icons.person_add), onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => SearchUsernameView(), ), ); }, label: Text(context.lang.chatListViewSearchUserNameBtn)), ), ) : ListView.builder( restorationId: 'chat_list_view', itemCount: allUsers.length, itemBuilder: (BuildContext context, int index) { final user = allUsers[index]; return UserListItem( user: user, maxTotalMediaCounter: maxTotalMediaCounter, lastMessage: lastMessages[user.userId.toInt()], ); }, ), ); } } class UserListItem extends StatefulWidget { final Contact user; final DbMessage? lastMessage; final int maxTotalMediaCounter; const UserListItem( {super.key, required this.user, required this.lastMessage, required this.maxTotalMediaCounter}); @override State createState() => _UserListItem(); } class _UserListItem extends State { int lastMessageInSeconds = 0; MessageSendState state = MessageSendState.send; bool isDownloading = false; List token = []; @override Widget build(BuildContext context) { if (widget.lastMessage != null) { lastMessageInSeconds = DateTime.now().difference(widget.lastMessage!.sendAt).inSeconds; state = widget.lastMessage!.getSendState(); final content = widget.lastMessage!.messageContent; if (widget.lastMessage!.messageReceived && content is MediaMessageContent) { token = content.downloadToken; isDownloading = context .watch() .currentlyDownloading .contains(token.toString()); } } int flameCounter = context .watch() .flamesCounter[widget.user.userId.toInt()] ?? 0; return UserContextMenu( user: widget.user, child: ListTile( title: Text(widget.user.displayName), subtitle: (widget.lastMessage == null) ? Text("Tap to send your first image.") : Row( children: [ MessageSendStateIcon(widget.lastMessage!), Text("•"), const SizedBox(width: 5), Text( formatDuration(lastMessageInSeconds), style: TextStyle(fontSize: 12), ), if (flameCounter > 0) FlameCounterWidget( widget.user, flameCounter, widget.maxTotalMediaCounter, prefix: true, ), ], ), leading: InitialsAvatar(displayName: widget.user.displayName), onTap: () { if (widget.lastMessage == null) { print("TODO: implement sending to one person!"); return; } if (isDownloading) return; if (!widget.lastMessage!.isDownloaded) { tryDownloadMedia(widget.lastMessage!.messageId, widget.lastMessage!.otherUserId, token, force: true); return; } if (state == MessageSendState.received && widget.lastMessage!.containsOtherMedia()) { Navigator.push( context, MaterialPageRoute(builder: (context) { return MediaViewerView(widget.user, widget.lastMessage!); }), ); return; } Navigator.push( context, MaterialPageRoute(builder: (context) { return ChatItemDetailsView(user: widget.user); }), ); }, ), ); } }