From 9a730d68e550c6b7ad89dcd4db89bfb853825ff4 Mon Sep 17 00:00:00 2001 From: htylight Date: Sat, 16 Sep 2023 11:33:57 +0800 Subject: [PATCH] fix websocket reconnect bug and accept unreceived msgs --- lib/database/hive_database.dart | 6 +-- lib/models/websocket_model.dart | 44 +++++++++++++------ lib/screens/chat/chat_screen.dart | 2 +- .../components/friend_message_bubble.dart | 12 ++--- .../message/components/message_input_box.dart | 24 ++++++++-- .../message/friend_message_screen.dart | 2 +- .../more/setting_screen/setting_screen.dart | 6 ++- lib/utils/format_datetime.dart | 21 ++++++++- 8 files changed, 85 insertions(+), 32 deletions(-) diff --git a/lib/database/hive_database.dart b/lib/database/hive_database.dart index 6ef5c33..75d9cd3 100644 --- a/lib/database/hive_database.dart +++ b/lib/database/hive_database.dart @@ -15,8 +15,7 @@ class HiveDatabase { if (_isInitialised) { return; } - await Hive.close(); - + List encryptionKeyUint8List = await _getEncryptKey(); await Hive.initFlutter(await getBoxDir()); @@ -74,8 +73,9 @@ class HiveDatabase { return encryptionKeyUint8List; } - static void close() { + static Future close() async { _isInitialised = false; + await Hive.close(); } } diff --git a/lib/models/websocket_model.dart b/lib/models/websocket_model.dart index 9d44b9b..a58325e 100644 --- a/lib/models/websocket_model.dart +++ b/lib/models/websocket_model.dart @@ -33,18 +33,18 @@ class WebSocketManager extends ChangeNotifier { int reconnectTimes = 0; Duration timeout = const Duration(seconds: 4); - void connect(String userId) { + void connect(String userId, bool isReconnect) { id = userId; - wsUrl = Uri.parse('ws://10.0.2.2:8000/ws/$id'); + wsUrl = Uri.parse('ws://10.0.2.2:8000/ws/$id?is_reconnect=$isReconnect'); channel = WebSocketChannel.connect(wsUrl); socketStatus = SocketStatus.connected; print('websocket connected <$channel>'); - heartBeatInspect(); if (reconnectTimer != null) { - // reconnectTimer!.cancel(); + reconnectTimer!.cancel(); reconnectTimer = null; reconnectTimes = 0; } + heartBeatInspect(); channel.stream.listen(onData, onError: onError, onDone: onDone); } @@ -75,7 +75,7 @@ class WebSocketManager extends ChangeNotifier { receiveFriendAdded(data); case 'friend-deleted': receiveFriendDeleted(data); - case 'friend-chat-image' || 'group-chat-image': + case 'chat-image': receiveChatImages(data); case 'group-chat-creation': receiveGroupChatCreation(data); @@ -142,7 +142,7 @@ class WebSocketManager extends ChangeNotifier { reconnectTimer = Timer.periodic(timeout, (timer) { if (reconnectTimes < reconnectCount) { - connect(id); + connect(id, true); reconnectTimes++; } else { print('reconnection times exceed the max times......'); @@ -185,6 +185,8 @@ void receiveFriendMsg(Map msg) async { 1, ), ); + } else if ((messageTBox.get(msg['msgId'] as String)) != null) { + return; } else { chatSetting.isOpen = true; chatSetting.latestDateTime = dateTime; @@ -193,13 +195,15 @@ void receiveFriendMsg(Map msg) async { } List attachments = List.from(msg['attachments']); + final DateTime now = DateTime.parse(msg['dateTime'] as String); - messageTBox.add( + messageTBox.put( + msg['msgId'] as String, MessageT( senderId, msg['text'], msg['type'], - DateTime.parse(msg['dateTime'] as String), + now, msg['isShowTime'], attachments, ), @@ -269,10 +273,17 @@ void receiveChatImages(Map msg) async { print(msg); print('======================================='); String chatImageDir = getIt.get().baseImageDir; - File file = await File('$chatImageDir/${msg['filename']}').create( - recursive: true, - ); - await file.writeAsBytes(msg['bytes']); + File file = File('$chatImageDir/${msg['filename']}'); + if (await file.exists()) { + return; + } else { + await file.create(recursive: true); + await file.writeAsBytes(List.from(msg['bytes'])); + } + // File file = await File('$chatImageDir/${msg['filename']}').create( + // recursive: true, + // // ); + // await file.writeAsBytes(msg['bytes']); } void receiveGroupChatCreation(Map msg) { @@ -314,6 +325,8 @@ void receiveGroupChatMsg(Map msg) async { 1, ), ); + } else if ((messageTBox.get(msg['msgId'] as String)) != null) { + return; } else { chatSetting.isOpen = true; chatSetting.latestDateTime = dateTime; @@ -322,12 +335,15 @@ void receiveGroupChatMsg(Map msg) async { } List attachments = List.from(msg['attachments']); - messageTBox.add( + final DateTime now = DateTime.parse(msg['dateTime'] as String); + + messageTBox.put( + msg['msgId'] as String, MessageT( senderId, msg['text'], msg['type'], - DateTime.parse(msg['dateTime'] as String), + now, msg['isShowTime'], attachments, ), diff --git a/lib/screens/chat/chat_screen.dart b/lib/screens/chat/chat_screen.dart index 83b285a..21e211b 100755 --- a/lib/screens/chat/chat_screen.dart +++ b/lib/screens/chat/chat_screen.dart @@ -31,7 +31,7 @@ class _ChatScreenState extends State { if (!getIt.get().isInitialised) { await HiveDatabase.init(); - getIt.get().connect(getIt.get().id); + getIt.get().connect(getIt.get().id, false); List> res = await Future.wait([ getMyProfile(getIt.get().id), diff --git a/lib/screens/message/components/friend_message_bubble.dart b/lib/screens/message/components/friend_message_bubble.dart index 35060aa..c824645 100755 --- a/lib/screens/message/components/friend_message_bubble.dart +++ b/lib/screens/message/components/friend_message_bubble.dart @@ -72,6 +72,8 @@ class _FriendMessageBubbleState extends State { @override Widget build(BuildContext context) { + bool isFriend = widget.senderId == widget.contactId; + return Container( padding: const EdgeInsets.symmetric( vertical: kDefaultPadding / 2, @@ -91,12 +93,10 @@ class _FriendMessageBubbleState extends State { ), ), Row( - textDirection: widget.senderId == widget.contactId - ? TextDirection.ltr - : TextDirection.rtl, + textDirection: isFriend ? TextDirection.ltr : TextDirection.rtl, crossAxisAlignment: CrossAxisAlignment.start, children: [ - widget.senderId == widget.contactId + isFriend ? getIt .get() .friends[widget.contactId]! @@ -120,7 +120,7 @@ class _FriendMessageBubbleState extends State { ), Expanded( child: Column( - crossAxisAlignment: widget.senderId == widget.contactId + crossAxisAlignment: isFriend ? CrossAxisAlignment.start : CrossAxisAlignment.end, children: [ @@ -129,7 +129,7 @@ class _FriendMessageBubbleState extends State { Container( padding: const EdgeInsets.fromLTRB(10, 10, 10, 0), decoration: BoxDecoration( - color: widget.senderId == widget.contactId + color: isFriend ? const Color.fromARGB(255, 241, 241, 241) : kPrimaryColor, borderRadius: BorderRadius.circular(10.0), diff --git a/lib/screens/message/components/message_input_box.dart b/lib/screens/message/components/message_input_box.dart index 520c4b8..db679ea 100755 --- a/lib/screens/message/components/message_input_box.dart +++ b/lib/screens/message/components/message_input_box.dart @@ -238,7 +238,11 @@ class _MessageInputBoxState extends State { attachments.add('$dirTime/${getRandomFilename()}'); } } - _messageTBox.add( + + final String msgId = formatMsgIDFromTime(now); + + _messageTBox.put( + msgId, MessageT( senderId, text, @@ -260,6 +264,17 @@ class _MessageInputBoxState extends State { final msg = { 'type': 'text/multipart', + 'msgId': msgId, + 'senderId': senderId, + 'text': text, + 'attachments': attachments, + 'dateTime': now.toString(), + 'isShowTime': isShowTime, + }; + + final msg2 = { + 'type': 'text/multipart', + 'msgId': msgId, 'senderId': senderId, 'text': text, 'attachments': attachments, @@ -270,6 +285,9 @@ class _MessageInputBoxState extends State { if (widget.chatType == 0) { msg['event'] = 'friend-chat-msg'; msg['receiverId'] = widget.contactId; + msg2['event'] = 'friend-chat-msg'; + msg2['receiverId'] = getIt.get().id; + // getIt.get().channel.sink.add(json.encode(msg2)); getIt.get().channel.sink.add(json.encode(msg)); if (attachments.isNotEmpty) { String baseImageDir = getIt.get().baseImageDir; @@ -411,7 +429,7 @@ Future> bytes2json( encodedJson.add( json.encode( { - 'event': 'friend-chat-image', + 'event': 'chat-image', 'receiverId': args.$2[0], 'filename': args.$3[i], 'bytes': bytes, @@ -422,7 +440,7 @@ Future> bytes2json( encodedJson.add( json.encode( { - 'event': 'friend-chat-image', + 'event': 'chat-image', 'receiverIds': args.$2, 'filename': args.$3[i], 'bytes': bytes, diff --git a/lib/screens/message/friend_message_screen.dart b/lib/screens/message/friend_message_screen.dart index 666e6b2..eababd6 100755 --- a/lib/screens/message/friend_message_screen.dart +++ b/lib/screens/message/friend_message_screen.dart @@ -88,7 +88,7 @@ class _FriendMessageScreenState extends State { ChatSetting? chatSetting = _chatSettingBox.get(widget.friendId); - // whethe it is a brand new chat + // whether it is a brand new chat if (chatSetting != null) { chatSetting.unreadCount = 0; _chatSettingBox.put(widget.friendId, chatSetting); diff --git a/lib/screens/more/setting_screen/setting_screen.dart b/lib/screens/more/setting_screen/setting_screen.dart index cae8020..7c98935 100644 --- a/lib/screens/more/setting_screen/setting_screen.dart +++ b/lib/screens/more/setting_screen/setting_screen.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:hive_flutter/hive_flutter.dart'; import 'package:together_mobile/database/hive_database.dart'; import 'package:together_mobile/models/apply_list_model.dart'; @@ -33,11 +34,12 @@ class SettingScreen extends StatelessWidget { child: TextButton( onPressed: () async { await getIt.get().clear(); + // ignore: use_build_context_synchronously context.go('/welcome'); // ignore: use_build_context_synchronously Timer( const Duration(milliseconds: 200), - () { + () async { getIt.get().disconnect(); getIt.get().clear(); getIt.get().clear(); @@ -45,7 +47,7 @@ class SettingScreen extends StatelessWidget { getIt.get().clear(); getIt.get().clear(); // Hive.deleteFromDisk(); - HiveDatabase.close(); + await HiveDatabase.close(); }, ); }, diff --git a/lib/utils/format_datetime.dart b/lib/utils/format_datetime.dart index d4a3276..46844cc 100644 --- a/lib/utils/format_datetime.dart +++ b/lib/utils/format_datetime.dart @@ -82,8 +82,25 @@ String formatMessageDateTime(DateTime dateTime) { String formatDirTime(DateTime dateTime) { int year = dateTime.year; String month = - dateTime.month < 0 ? '0${dateTime.month}' : '${dateTime.month}'; - String day = dateTime.day < 0 ? '0${dateTime.day}' : '${dateTime.day}'; + dateTime.month < 10 ? '0${dateTime.month}' : '${dateTime.month}'; + String day = dateTime.day < 10 ? '0${dateTime.day}' : '${dateTime.day}'; return '$year$month$day'; } + +String formatMsgIDFromTime(DateTime dateTime) { + int year = dateTime.year; + String month = + dateTime.month < 10 ? '0${dateTime.month}' : '${dateTime.month}'; + String day = dateTime.day < 10 ? '0${dateTime.day}' : '${dateTime.day}'; + String hour = dateTime.hour < 10 ? '0${dateTime.hour}' : '${dateTime.hour}'; + String minute = + dateTime.minute < 10 ? '0${dateTime.minute}' : '${dateTime.minute}'; + String millisecond = dateTime.millisecond >= 100 + ? '${dateTime.millisecond}' + : dateTime.millisecond < 10 + ? '0${dateTime.minute}' + : '00${dateTime.minute}'; + + return '$year$month$day$hour$minute$millisecond'; +}