together_mobile/lib/models/websocket_model.dart

422 lines
12 KiB
Dart
Raw Normal View History

2023-08-10 19:08:46 +08:00
import 'dart:async';
import 'dart:convert';
import 'dart:io';
2023-08-10 19:08:46 +08:00
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
2023-09-09 16:48:47 +08:00
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:web_socket_channel/status.dart' as status;
2023-09-10 11:30:20 +08:00
import 'package:together_mobile/models/route_state_model.dart';
2023-08-10 19:08:46 +08:00
import 'package:together_mobile/database/box_type.dart';
2023-08-11 23:02:31 +08:00
import 'package:together_mobile/models/apply_list_model.dart';
import 'package:together_mobile/models/contact_model.dart';
import 'package:together_mobile/models/init_get_it.dart';
import 'package:together_mobile/models/user_model.dart';
2023-09-09 16:48:47 +08:00
import 'package:together_mobile/notification_api.dart';
2023-08-10 19:08:46 +08:00
2023-08-27 19:14:37 +08:00
enum SocketStatus {
connected,
closed,
error,
}
2023-08-10 19:08:46 +08:00
class WebSocketManager extends ChangeNotifier {
late Uri wsUrl;
late WebSocketChannel channel;
String id = '';
2023-08-27 19:14:37 +08:00
SocketStatus socketStatus = SocketStatus.closed;
2023-08-10 19:08:46 +08:00
Timer? heartBeatTimer;
Timer? serverTimer;
Timer? reconnectTimer;
int reconnectCount = 30;
int reconnectTimes = 0;
Duration timeout = const Duration(seconds: 4);
void connect(String userId, bool isReconnect) {
2023-08-10 19:08:46 +08:00
id = userId;
wsUrl = Uri.parse('ws://10.0.2.2:8000/ws/$id?is_reconnect=$isReconnect');
2023-08-10 19:08:46 +08:00
channel = WebSocketChannel.connect(wsUrl);
socketStatus = SocketStatus.connected;
print('websocket connected <$channel>');
if (reconnectTimer != null) {
reconnectTimer!.cancel();
2023-08-10 19:08:46 +08:00
reconnectTimer = null;
reconnectTimes = 0;
}
heartBeatInspect();
2023-08-10 19:08:46 +08:00
channel.stream.listen(onData, onError: onError, onDone: onDone);
}
2023-08-27 19:14:37 +08:00
void disconnect() {
channel.sink.close();
wsUrl = Uri();
id = '';
socketStatus = SocketStatus.closed;
heartBeatTimer?.cancel();
serverTimer?.cancel();
reconnectTimer?.cancel();
heartBeatTimer = null;
serverTimer = null;
reconnectTimer = null;
reconnectTimes = 0;
}
2023-08-10 19:08:46 +08:00
void onData(jsonData) {
heartBeatInspect();
Map<String, dynamic> data = json.decode(jsonData);
switch (data['event']) {
case 'friend-chat-msg':
2023-08-10 19:08:46 +08:00
receiveFriendMsg(data);
2023-08-11 23:02:31 +08:00
case 'apply-friend':
receiveApplyFriend(data);
case 'friend-added':
receiveFriendAdded(data);
case 'friend-deleted':
receiveFriendDeleted(data);
case 'chat-image':
receiveChatImages(data);
case 'group-chat-creation':
receiveGroupChatCreation(data);
case 'group-chat-msg':
receiveGroupChatMsg(data);
2023-08-10 19:08:46 +08:00
}
}
void onDone() {
print('websocket disconnected <$channel>');
2023-08-27 19:14:37 +08:00
if (socketStatus == SocketStatus.closed) {
return;
}
2023-08-10 19:08:46 +08:00
reconnect();
}
void onError(Object error) {}
void heartBeatInspect() {
if (heartBeatTimer != null) {
heartBeatTimer!.cancel();
heartBeatTimer = null;
}
if (serverTimer != null) {
serverTimer!.cancel();
serverTimer = null;
}
print('start heartbeat inspect......');
heartBeatTimer = Timer(timeout, () {
channel.sink.add(json.encode({'event': 'ping'}));
serverTimer = Timer(timeout, () {
channel.sink.close(status.internalServerError);
socketStatus = SocketStatus.closed;
});
});
}
void reconnect() {
if (socketStatus == SocketStatus.error) {
if (heartBeatTimer != null) {
heartBeatTimer!.cancel();
heartBeatTimer = null;
}
if (serverTimer != null) {
serverTimer!.cancel();
serverTimer = null;
}
return;
}
print('websocket reconnecting......');
if (heartBeatTimer != null) {
heartBeatTimer!.cancel();
heartBeatTimer = null;
}
if (serverTimer != null) {
serverTimer!.cancel();
serverTimer = null;
}
reconnectTimer = Timer.periodic(timeout, (timer) {
if (reconnectTimes < reconnectCount) {
connect(id, true);
2023-08-10 19:08:46 +08:00
reconnectTimes++;
} else {
print('reconnection times exceed the max times......');
// If it is still disconnection after reconnect 30 times, set the socket
// status to error, means the network is bad, and stop reconnecting.
socketStatus = SocketStatus.error;
channel.sink.close();
timer.cancel();
}
});
}
}
void receiveFriendMsg(Map<String, dynamic> msg) async {
print('=================收到了好友信息事件==================');
print(msg);
print('=======================================');
2023-08-10 19:08:46 +08:00
String senderId = msg['senderId'] as String;
late Box<MessageT> messageTBox;
try {
messageTBox = Hive.box('message_$senderId');
} catch (e) {
messageTBox = await Hive.openBox('message_$senderId');
}
Box<ChatSetting> chatSettingBox = Hive.box<ChatSetting>('chat_setting');
ChatSetting? chatSetting = chatSettingBox.get(senderId);
2023-08-11 23:02:31 +08:00
DateTime dateTime = DateTime.parse(msg['dateTime'] as String);
2023-08-10 19:08:46 +08:00
if (chatSetting == null) {
2023-08-11 23:02:31 +08:00
chatSettingBox.put(
senderId,
ChatSetting(
senderId,
0,
2023-08-11 23:02:31 +08:00
false,
true,
false,
dateTime,
1,
2023-08-11 23:02:31 +08:00
),
);
} else if ((messageTBox.get(msg['msgId'] as String)) != null) {
return;
2023-08-10 19:08:46 +08:00
} else {
chatSetting.isOpen = true;
2023-08-11 23:02:31 +08:00
chatSetting.latestDateTime = dateTime;
chatSetting.unreadCount++;
2023-08-10 19:08:46 +08:00
chatSettingBox.put(senderId, chatSetting);
}
2023-08-11 23:02:31 +08:00
List<String> attachments = List.from(msg['attachments']);
final DateTime now = DateTime.parse(msg['dateTime'] as String);
2023-08-11 23:02:31 +08:00
messageTBox.put(
msg['msgId'] as String,
2023-08-10 19:08:46 +08:00
MessageT(
senderId,
msg['text'],
msg['type'],
now,
2023-08-10 19:08:46 +08:00
msg['isShowTime'],
2023-08-11 23:02:31 +08:00
attachments,
2023-08-10 19:08:46 +08:00
),
);
2023-09-09 16:48:47 +08:00
String name = getIt.get<Contact>().friends[senderId]!.friendRemark.isEmpty
? getIt.get<ContactAccountProfile>().friends[senderId]!.nickname
: getIt.get<Contact>().friends[senderId]!.friendRemark;
String routeName = getIt.get<RouteState>().currentPathName;
String avatar = getIt.get<ContactAccountProfile>().friends[senderId]!.avatar;
if (!getIt.get<RouteState>().isVisible) {
2023-09-10 11:30:20 +08:00
NotificationAPI.showMessageNotifications(
2023-09-09 16:48:47 +08:00
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
);
} else if (routeName == 'Message') {
if (!getIt.get<RouteState>().query.containsValue(senderId)) {
2023-09-10 11:30:20 +08:00
NotificationAPI.showMessageNotifications(
2023-09-09 16:48:47 +08:00
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
);
}
} else if (routeName != 'Chat') {
2023-09-10 11:30:20 +08:00
NotificationAPI.showMessageNotifications(
2023-09-09 16:48:47 +08:00
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
);
}
2023-08-10 19:08:46 +08:00
}
2023-08-11 23:02:31 +08:00
void receiveApplyFriend(Map<String, dynamic> msg) {
print('=================收到了申请好友事件==================');
print(msg);
print('=======================================');
2023-08-11 23:02:31 +08:00
getIt.get<ApplyList>().addJson(msg);
}
void receiveFriendAdded(Map<String, dynamic> msg) {
print('=================收到了申请好友通过事件==================');
print(msg);
print('=======================================');
getIt.get<Contact>().addFriend(msg['friendId'], msg['setting']);
getIt
.get<ContactAccountProfile>()
.addFriendAccountProfile(msg['friendId'], msg['accountProfile']);
2023-08-11 23:02:31 +08:00
}
void receiveFriendDeleted(Map<String, dynamic> msg) {
print('=================收到了解除好友事件==================');
print(msg);
print('=======================================');
getIt.get<Contact>().removeFriend(msg['friendId']);
getIt.get<ContactAccountProfile>().removeFriend(msg['friendId']);
}
void receiveChatImages(Map<String, dynamic> msg) async {
print('=================收到了聊天图片事件==================');
print(msg);
print('=======================================');
String chatImageDir = getIt.get<UserProfile>().baseImageDir;
File file = File('$chatImageDir/${msg['filename']}');
if (await file.exists()) {
return;
} 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) {
print('=================收到了群聊邀请事件==================');
print(msg);
print('=======================================');
String groupChatId = msg['groupChat']['id'];
getIt.get<Contact>().addGroupChat(groupChatId);
getIt.get<ContactAccountProfile>().addGroupChatProfile(groupChatId, msg);
}
void receiveGroupChatMsg(Map<String, dynamic> msg) async {
print('=================收到了群聊信息事件==================');
print(msg);
print('=======================================');
String senderId = msg['senderId'] as String;
String groupChatId = msg['groupChatId'] as String;
late Box<MessageT> messageTBox;
try {
messageTBox = Hive.box('message_$groupChatId');
} catch (e) {
messageTBox = await Hive.openBox('message_$groupChatId');
}
Box<ChatSetting> chatSettingBox = Hive.box<ChatSetting>('chat_setting');
ChatSetting? chatSetting = chatSettingBox.get(groupChatId);
DateTime dateTime = DateTime.parse(msg['dateTime'] as String);
if (chatSetting == null) {
chatSettingBox.put(
2023-08-27 19:14:37 +08:00
groupChatId,
ChatSetting(
groupChatId,
2023-08-27 19:14:37 +08:00
1,
false,
true,
false,
dateTime,
1,
),
);
} else if ((messageTBox.get(msg['msgId'] as String)) != null) {
return;
} else {
chatSetting.isOpen = true;
chatSetting.latestDateTime = dateTime;
chatSetting.unreadCount++;
chatSettingBox.put(groupChatId, chatSetting);
}
List<String> attachments = List.from(msg['attachments']);
final DateTime now = DateTime.parse(msg['dateTime'] as String);
messageTBox.put(
msg['msgId'] as String,
MessageT(
senderId,
msg['text'],
msg['type'],
now,
msg['isShowTime'],
attachments,
),
);
2023-09-09 16:48:47 +08:00
String avatar =
getIt.get<ContactAccountProfile>().groupChats[groupChatId]!.avatar;
late String name;
if (getIt.get<Contact>().friends.containsKey(senderId)) {
name = getIt.get<Contact>().friends[senderId]!.friendRemark.isEmpty
? getIt.get<ContactAccountProfile>().friends[senderId]!.nickname
: getIt.get<Contact>().friends[senderId]!.friendRemark;
} else if (getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles
.containsKey(groupChatId)) {
if (getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[groupChatId]!
.containsKey(senderId)) {
name = getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[groupChatId]![senderId]!
.remark
.isEmpty
? getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[groupChatId]![senderId]!
.nickname
.isEmpty
? senderId.substring(0, 6)
: getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[groupChatId]![senderId]!
.nickname
: getIt
.get<ContactAccountProfile>()
.grouChatMemberProfiles[groupChatId]![senderId]!
.remark;
}
} else {
name = senderId.substring(0, 6);
}
String routeName = getIt.get<RouteState>().currentPathName;
if (!getIt.get<RouteState>().isVisible) {
2023-09-10 11:30:20 +08:00
NotificationAPI.showMessageNotifications(
2023-09-09 16:48:47 +08:00
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
groupChatId: groupChatId,
);
} else if (routeName == 'Message') {
if (!getIt.get<RouteState>().query.containsValue(senderId)) {
2023-09-10 11:30:20 +08:00
NotificationAPI.showMessageNotifications(
2023-09-09 16:48:47 +08:00
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
groupChatId: groupChatId,
);
}
} else if (routeName != 'Chat') {
2023-09-10 11:30:20 +08:00
NotificationAPI.showMessageNotifications(
2023-09-09 16:48:47 +08:00
senderId: senderId,
name: name,
avatar: avatar,
text: msg['text'] as String,
groupChatId: groupChatId,
);
}
}