fix websocket reconnect bug and accept unreceived msgs
parent
be64a626ce
commit
9a730d68e5
|
@ -15,7 +15,6 @@ class HiveDatabase {
|
||||||
if (_isInitialised) {
|
if (_isInitialised) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await Hive.close();
|
|
||||||
|
|
||||||
List<int> encryptionKeyUint8List = await _getEncryptKey();
|
List<int> encryptionKeyUint8List = await _getEncryptKey();
|
||||||
|
|
||||||
|
@ -74,8 +73,9 @@ class HiveDatabase {
|
||||||
return encryptionKeyUint8List;
|
return encryptionKeyUint8List;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void close() {
|
static Future<void> close() async {
|
||||||
_isInitialised = false;
|
_isInitialised = false;
|
||||||
|
await Hive.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,18 +33,18 @@ class WebSocketManager extends ChangeNotifier {
|
||||||
int reconnectTimes = 0;
|
int reconnectTimes = 0;
|
||||||
Duration timeout = const Duration(seconds: 4);
|
Duration timeout = const Duration(seconds: 4);
|
||||||
|
|
||||||
void connect(String userId) {
|
void connect(String userId, bool isReconnect) {
|
||||||
id = userId;
|
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);
|
channel = WebSocketChannel.connect(wsUrl);
|
||||||
socketStatus = SocketStatus.connected;
|
socketStatus = SocketStatus.connected;
|
||||||
print('websocket connected <$channel>');
|
print('websocket connected <$channel>');
|
||||||
heartBeatInspect();
|
|
||||||
if (reconnectTimer != null) {
|
if (reconnectTimer != null) {
|
||||||
// reconnectTimer!.cancel();
|
reconnectTimer!.cancel();
|
||||||
reconnectTimer = null;
|
reconnectTimer = null;
|
||||||
reconnectTimes = 0;
|
reconnectTimes = 0;
|
||||||
}
|
}
|
||||||
|
heartBeatInspect();
|
||||||
|
|
||||||
channel.stream.listen(onData, onError: onError, onDone: onDone);
|
channel.stream.listen(onData, onError: onError, onDone: onDone);
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ class WebSocketManager extends ChangeNotifier {
|
||||||
receiveFriendAdded(data);
|
receiveFriendAdded(data);
|
||||||
case 'friend-deleted':
|
case 'friend-deleted':
|
||||||
receiveFriendDeleted(data);
|
receiveFriendDeleted(data);
|
||||||
case 'friend-chat-image' || 'group-chat-image':
|
case 'chat-image':
|
||||||
receiveChatImages(data);
|
receiveChatImages(data);
|
||||||
case 'group-chat-creation':
|
case 'group-chat-creation':
|
||||||
receiveGroupChatCreation(data);
|
receiveGroupChatCreation(data);
|
||||||
|
@ -142,7 +142,7 @@ class WebSocketManager extends ChangeNotifier {
|
||||||
|
|
||||||
reconnectTimer = Timer.periodic(timeout, (timer) {
|
reconnectTimer = Timer.periodic(timeout, (timer) {
|
||||||
if (reconnectTimes < reconnectCount) {
|
if (reconnectTimes < reconnectCount) {
|
||||||
connect(id);
|
connect(id, true);
|
||||||
reconnectTimes++;
|
reconnectTimes++;
|
||||||
} else {
|
} else {
|
||||||
print('reconnection times exceed the max times......');
|
print('reconnection times exceed the max times......');
|
||||||
|
@ -185,6 +185,8 @@ void receiveFriendMsg(Map<String, dynamic> msg) async {
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} else if ((messageTBox.get(msg['msgId'] as String)) != null) {
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
chatSetting.isOpen = true;
|
chatSetting.isOpen = true;
|
||||||
chatSetting.latestDateTime = dateTime;
|
chatSetting.latestDateTime = dateTime;
|
||||||
|
@ -193,13 +195,15 @@ void receiveFriendMsg(Map<String, dynamic> msg) async {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> attachments = List.from(msg['attachments']);
|
List<String> attachments = List.from(msg['attachments']);
|
||||||
|
final DateTime now = DateTime.parse(msg['dateTime'] as String);
|
||||||
|
|
||||||
messageTBox.add(
|
messageTBox.put(
|
||||||
|
msg['msgId'] as String,
|
||||||
MessageT(
|
MessageT(
|
||||||
senderId,
|
senderId,
|
||||||
msg['text'],
|
msg['text'],
|
||||||
msg['type'],
|
msg['type'],
|
||||||
DateTime.parse(msg['dateTime'] as String),
|
now,
|
||||||
msg['isShowTime'],
|
msg['isShowTime'],
|
||||||
attachments,
|
attachments,
|
||||||
),
|
),
|
||||||
|
@ -269,10 +273,17 @@ void receiveChatImages(Map<String, dynamic> msg) async {
|
||||||
print(msg);
|
print(msg);
|
||||||
print('=======================================');
|
print('=======================================');
|
||||||
String chatImageDir = getIt.get<UserProfile>().baseImageDir;
|
String chatImageDir = getIt.get<UserProfile>().baseImageDir;
|
||||||
File file = await File('$chatImageDir/${msg['filename']}').create(
|
File file = File('$chatImageDir/${msg['filename']}');
|
||||||
recursive: true,
|
if (await file.exists()) {
|
||||||
);
|
return;
|
||||||
await file.writeAsBytes(msg['bytes']);
|
} else {
|
||||||
|
await file.create(recursive: true);
|
||||||
|
await file.writeAsBytes(List<int>.from(msg['bytes']));
|
||||||
|
}
|
||||||
|
// File file = await File('$chatImageDir/${msg['filename']}').create(
|
||||||
|
// recursive: true,
|
||||||
|
// // );
|
||||||
|
// await file.writeAsBytes(msg['bytes']);
|
||||||
}
|
}
|
||||||
|
|
||||||
void receiveGroupChatCreation(Map<String, dynamic> msg) {
|
void receiveGroupChatCreation(Map<String, dynamic> msg) {
|
||||||
|
@ -314,6 +325,8 @@ void receiveGroupChatMsg(Map<String, dynamic> msg) async {
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} else if ((messageTBox.get(msg['msgId'] as String)) != null) {
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
chatSetting.isOpen = true;
|
chatSetting.isOpen = true;
|
||||||
chatSetting.latestDateTime = dateTime;
|
chatSetting.latestDateTime = dateTime;
|
||||||
|
@ -322,12 +335,15 @@ void receiveGroupChatMsg(Map<String, dynamic> msg) async {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> attachments = List.from(msg['attachments']);
|
List<String> attachments = List.from(msg['attachments']);
|
||||||
messageTBox.add(
|
final DateTime now = DateTime.parse(msg['dateTime'] as String);
|
||||||
|
|
||||||
|
messageTBox.put(
|
||||||
|
msg['msgId'] as String,
|
||||||
MessageT(
|
MessageT(
|
||||||
senderId,
|
senderId,
|
||||||
msg['text'],
|
msg['text'],
|
||||||
msg['type'],
|
msg['type'],
|
||||||
DateTime.parse(msg['dateTime'] as String),
|
now,
|
||||||
msg['isShowTime'],
|
msg['isShowTime'],
|
||||||
attachments,
|
attachments,
|
||||||
),
|
),
|
||||||
|
|
|
@ -31,7 +31,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||||
if (!getIt.get<UserProfile>().isInitialised) {
|
if (!getIt.get<UserProfile>().isInitialised) {
|
||||||
await HiveDatabase.init();
|
await HiveDatabase.init();
|
||||||
|
|
||||||
getIt.get<WebSocketManager>().connect(getIt.get<UserAccount>().id);
|
getIt.get<WebSocketManager>().connect(getIt.get<UserAccount>().id, false);
|
||||||
|
|
||||||
List<Map<String, dynamic>> res = await Future.wait([
|
List<Map<String, dynamic>> res = await Future.wait([
|
||||||
getMyProfile(getIt.get<UserAccount>().id),
|
getMyProfile(getIt.get<UserAccount>().id),
|
||||||
|
|
|
@ -72,6 +72,8 @@ class _FriendMessageBubbleState extends State<FriendMessageBubble> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
bool isFriend = widget.senderId == widget.contactId;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: kDefaultPadding / 2,
|
vertical: kDefaultPadding / 2,
|
||||||
|
@ -91,12 +93,10 @@ class _FriendMessageBubbleState extends State<FriendMessageBubble> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
textDirection: widget.senderId == widget.contactId
|
textDirection: isFriend ? TextDirection.ltr : TextDirection.rtl,
|
||||||
? TextDirection.ltr
|
|
||||||
: TextDirection.rtl,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
widget.senderId == widget.contactId
|
isFriend
|
||||||
? getIt
|
? getIt
|
||||||
.get<ContactAccountProfile>()
|
.get<ContactAccountProfile>()
|
||||||
.friends[widget.contactId]!
|
.friends[widget.contactId]!
|
||||||
|
@ -120,7 +120,7 @@ class _FriendMessageBubbleState extends State<FriendMessageBubble> {
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: widget.senderId == widget.contactId
|
crossAxisAlignment: isFriend
|
||||||
? CrossAxisAlignment.start
|
? CrossAxisAlignment.start
|
||||||
: CrossAxisAlignment.end,
|
: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
|
@ -129,7 +129,7 @@ class _FriendMessageBubbleState extends State<FriendMessageBubble> {
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
|
padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: widget.senderId == widget.contactId
|
color: isFriend
|
||||||
? const Color.fromARGB(255, 241, 241, 241)
|
? const Color.fromARGB(255, 241, 241, 241)
|
||||||
: kPrimaryColor,
|
: kPrimaryColor,
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
|
|
@ -238,7 +238,11 @@ class _MessageInputBoxState extends State<MessageInputBox> {
|
||||||
attachments.add('$dirTime/${getRandomFilename()}');
|
attachments.add('$dirTime/${getRandomFilename()}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_messageTBox.add(
|
|
||||||
|
final String msgId = formatMsgIDFromTime(now);
|
||||||
|
|
||||||
|
_messageTBox.put(
|
||||||
|
msgId,
|
||||||
MessageT(
|
MessageT(
|
||||||
senderId,
|
senderId,
|
||||||
text,
|
text,
|
||||||
|
@ -260,6 +264,17 @@ class _MessageInputBoxState extends State<MessageInputBox> {
|
||||||
|
|
||||||
final msg = {
|
final msg = {
|
||||||
'type': 'text/multipart',
|
'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,
|
'senderId': senderId,
|
||||||
'text': text,
|
'text': text,
|
||||||
'attachments': attachments,
|
'attachments': attachments,
|
||||||
|
@ -270,6 +285,9 @@ class _MessageInputBoxState extends State<MessageInputBox> {
|
||||||
if (widget.chatType == 0) {
|
if (widget.chatType == 0) {
|
||||||
msg['event'] = 'friend-chat-msg';
|
msg['event'] = 'friend-chat-msg';
|
||||||
msg['receiverId'] = widget.contactId;
|
msg['receiverId'] = widget.contactId;
|
||||||
|
msg2['event'] = 'friend-chat-msg';
|
||||||
|
msg2['receiverId'] = getIt.get<UserAccount>().id;
|
||||||
|
// getIt.get<WebSocketManager>().channel.sink.add(json.encode(msg2));
|
||||||
getIt.get<WebSocketManager>().channel.sink.add(json.encode(msg));
|
getIt.get<WebSocketManager>().channel.sink.add(json.encode(msg));
|
||||||
if (attachments.isNotEmpty) {
|
if (attachments.isNotEmpty) {
|
||||||
String baseImageDir = getIt.get<UserProfile>().baseImageDir;
|
String baseImageDir = getIt.get<UserProfile>().baseImageDir;
|
||||||
|
@ -411,7 +429,7 @@ Future<List<String>> bytes2json(
|
||||||
encodedJson.add(
|
encodedJson.add(
|
||||||
json.encode(
|
json.encode(
|
||||||
{
|
{
|
||||||
'event': 'friend-chat-image',
|
'event': 'chat-image',
|
||||||
'receiverId': args.$2[0],
|
'receiverId': args.$2[0],
|
||||||
'filename': args.$3[i],
|
'filename': args.$3[i],
|
||||||
'bytes': bytes,
|
'bytes': bytes,
|
||||||
|
@ -422,7 +440,7 @@ Future<List<String>> bytes2json(
|
||||||
encodedJson.add(
|
encodedJson.add(
|
||||||
json.encode(
|
json.encode(
|
||||||
{
|
{
|
||||||
'event': 'friend-chat-image',
|
'event': 'chat-image',
|
||||||
'receiverIds': args.$2,
|
'receiverIds': args.$2,
|
||||||
'filename': args.$3[i],
|
'filename': args.$3[i],
|
||||||
'bytes': bytes,
|
'bytes': bytes,
|
||||||
|
|
|
@ -88,7 +88,7 @@ class _FriendMessageScreenState extends State<FriendMessageScreen> {
|
||||||
ChatSetting? chatSetting =
|
ChatSetting? chatSetting =
|
||||||
_chatSettingBox.get(widget.friendId);
|
_chatSettingBox.get(widget.friendId);
|
||||||
|
|
||||||
// whethe it is a brand new chat
|
// whether it is a brand new chat
|
||||||
if (chatSetting != null) {
|
if (chatSetting != null) {
|
||||||
chatSetting.unreadCount = 0;
|
chatSetting.unreadCount = 0;
|
||||||
_chatSettingBox.put(widget.friendId, chatSetting);
|
_chatSettingBox.put(widget.friendId, chatSetting);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.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/database/hive_database.dart';
|
||||||
|
|
||||||
import 'package:together_mobile/models/apply_list_model.dart';
|
import 'package:together_mobile/models/apply_list_model.dart';
|
||||||
|
@ -33,11 +34,12 @@ class SettingScreen extends StatelessWidget {
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await getIt.get<Token>().clear();
|
await getIt.get<Token>().clear();
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
context.go('/welcome');
|
context.go('/welcome');
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
Timer(
|
Timer(
|
||||||
const Duration(milliseconds: 200),
|
const Duration(milliseconds: 200),
|
||||||
() {
|
() async {
|
||||||
getIt.get<WebSocketManager>().disconnect();
|
getIt.get<WebSocketManager>().disconnect();
|
||||||
getIt.get<UserAccount>().clear();
|
getIt.get<UserAccount>().clear();
|
||||||
getIt.get<UserProfile>().clear();
|
getIt.get<UserProfile>().clear();
|
||||||
|
@ -45,7 +47,7 @@ class SettingScreen extends StatelessWidget {
|
||||||
getIt.get<Contact>().clear();
|
getIt.get<Contact>().clear();
|
||||||
getIt.get<ContactAccountProfile>().clear();
|
getIt.get<ContactAccountProfile>().clear();
|
||||||
// Hive.deleteFromDisk();
|
// Hive.deleteFromDisk();
|
||||||
HiveDatabase.close();
|
await HiveDatabase.close();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -82,8 +82,25 @@ String formatMessageDateTime(DateTime dateTime) {
|
||||||
String formatDirTime(DateTime dateTime) {
|
String formatDirTime(DateTime dateTime) {
|
||||||
int year = dateTime.year;
|
int year = dateTime.year;
|
||||||
String month =
|
String month =
|
||||||
dateTime.month < 0 ? '0${dateTime.month}' : '${dateTime.month}';
|
dateTime.month < 10 ? '0${dateTime.month}' : '${dateTime.month}';
|
||||||
String day = dateTime.day < 0 ? '0${dateTime.day}' : '${dateTime.day}';
|
String day = dateTime.day < 10 ? '0${dateTime.day}' : '${dateTime.day}';
|
||||||
|
|
||||||
return '$year$month$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';
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue