import 'dart:async'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hive_flutter/hive_flutter.dart'; import '/database/box_type.dart'; import '/models/contact_model.dart'; import '/models/init_get_it.dart'; import '/models/route_state_model.dart'; import '/models/user_model.dart'; import '/request/group_chat.dart'; import '/utils/format_datetime.dart'; import 'components/group_chat_message_bubble.dart'; import 'components/message_input_box.dart'; class GroupChatMessageScreen extends StatefulWidget { const GroupChatMessageScreen({ super.key, required this.groupChatId, }); final String groupChatId; @override State createState() => _GroupChatMessageScreenState(); } class _GroupChatMessageScreenState extends State { final ScrollController _controller = ScrollController(); final Box _chatSettingBox = Hive.box('chat_setting'); final Map _isGettingNameAvatar = {}; Future _getMemberProfile( bool isFriend, bool isOther, String senderId, ) async { if (!isOther) { return true; } if (_isGettingNameAvatar.containsKey(senderId)) { await Future.doWhile(() async { await Future.delayed(const Duration(milliseconds: 300)); return _isGettingNameAvatar.containsKey(senderId); }); return Future(() => true); } if (!getIt .get() .groupChatMemberProfiles .containsKey(widget.groupChatId) || !getIt .get() .groupChatMemberProfiles[widget.groupChatId]! .containsKey(senderId)) { _isGettingNameAvatar[senderId] = true; final res = await getGroupChatMemberNameAvatar( widget.groupChatId, senderId, isFriend, ); if (res['code'] == 10800) { getIt.get().addGroupChatMemberProfile( widget.groupChatId, senderId, res['data'], ); } } _isGettingNameAvatar.remove(senderId); return Future(() => true); } @override void dispose() { super.dispose(); _controller.dispose(); } @override Widget build(BuildContext context) { Timer( const Duration(milliseconds: 300), () => getIt.get().changeRoute( 'Message', newQuery: {'groupChatId': widget.groupChatId}, ), ); return Scaffold( appBar: AppBar( leading: IconButton( onPressed: () { context.go('/chat'); }, icon: const Icon(Icons.arrow_back), ), // friend remark or nickname title: getIt .get() .groupChats[widget.groupChatId]! .groupChatRemark .isEmpty ? Text( getIt .get() .groupChats[widget.groupChatId]! .name, ) : Text( getIt .get() .groupChats[widget.groupChatId]! .groupChatRemark, ), centerTitle: true, actions: [ IconButton( onPressed: () { context.pushNamed( 'GroupChatProfile', queryParameters: { 'groupChatId': widget.groupChatId, }, ); }, icon: const Icon(Icons.menu), splashRadius: 18, ), ], ), body: Column( children: [ Expanded( child: Align( alignment: Alignment.topCenter, child: ValueListenableBuilder( valueListenable: Hive.box('message_${widget.groupChatId}') .listenable(), builder: (context, value, _) { // Set unreadCount to 0 when at message screen ChatSetting? chatSetting = _chatSettingBox.get(widget.groupChatId); // whethe it is a brand new chat if (chatSetting != null) { chatSetting.unreadCount = 0; _chatSettingBox.put(widget.groupChatId, chatSetting); } Future.delayed( const Duration( milliseconds: 100, ), () => _controller.animateTo( 0, duration: const Duration(milliseconds: 500), curve: Curves.linear, ), ); return ListView.builder( physics: const BouncingScrollPhysics( parent: AlwaysScrollableScrollPhysics(), ), controller: _controller, shrinkWrap: true, reverse: true, itemCount: Hive.box('message_${widget.groupChatId}') .length, itemBuilder: (context, index) { Box messageTBox = Hive.box('message_${widget.groupChatId}'); int length = messageTBox.length; int i = length - index - 1; MessageT messageT = messageTBox.getAt(i)!; List allAttachments = []; // Do not reverse the list, cause what i want is // rigth slide to next image, left to last otherwise for (var element in messageTBox.values) { if (element.attachments.isNotEmpty) { allAttachments.addAll(element.attachments); } } bool isOther = messageT.senderId != getIt.get().id; // Because myself is also in `friends` map bool isFriend = getIt .get() .friends .containsKey(messageT.senderId) && isOther; return FutureBuilder( future: _getMemberProfile( isFriend, isOther, messageT.senderId, ), builder: (context, snapshot) { if (snapshot.hasData || !isOther) { return GroupChatMessageBubble( key: ValueKey(i), index: i, length: length, contactId: widget.groupChatId, senderId: messageT.senderId, isFriend: isFriend, isOther: isOther, hasNameAvatar: true, dateTime: formatMessageDateTime(messageT.dateTime), isShowTime: messageT.isShowTime, type: messageT.type, text: messageT.text, attachments: messageT.attachments, allAttachments: allAttachments, ); } else { return GroupChatMessageBubble( key: ValueKey(i), index: i, length: length, contactId: widget.groupChatId, senderId: messageT.senderId, isFriend: isFriend, isOther: isOther, hasNameAvatar: false, dateTime: formatMessageDateTime(messageT.dateTime), isShowTime: messageT.isShowTime, type: messageT.type, text: messageT.text, attachments: messageT.attachments, allAttachments: allAttachments, ); } }, ); }, ); }, ), ), ), MessageInputBox( chatType: 1, contactId: widget.groupChatId, scrollController: _controller, ), ], ), bottomNavigationBar: null, ); } }