154 lines
3.8 KiB
Dart
154 lines
3.8 KiB
Dart
|
import 'dart:async';
|
||
|
import 'dart:convert';
|
||
|
|
||
|
import 'package:flutter/material.dart';
|
||
|
import 'package:hive_flutter/hive_flutter.dart';
|
||
|
import 'package:together_mobile/database/box_type.dart';
|
||
|
|
||
|
import 'package:web_socket_channel/web_socket_channel.dart';
|
||
|
import 'package:web_socket_channel/status.dart' as status;
|
||
|
|
||
|
class WebSocketManager extends ChangeNotifier {
|
||
|
late Uri wsUrl;
|
||
|
late WebSocketChannel channel;
|
||
|
String id = '';
|
||
|
SocketStatus? socketStatus;
|
||
|
Timer? heartBeatTimer;
|
||
|
Timer? serverTimer;
|
||
|
Timer? reconnectTimer;
|
||
|
int reconnectCount = 30;
|
||
|
int reconnectTimes = 0;
|
||
|
Duration timeout = const Duration(seconds: 4);
|
||
|
|
||
|
void connect(String userId) {
|
||
|
id = userId;
|
||
|
wsUrl = Uri.parse('ws://10.0.2.2:8000/ws/$id');
|
||
|
channel = WebSocketChannel.connect(wsUrl);
|
||
|
socketStatus = SocketStatus.connected;
|
||
|
print('websocket connected <$channel>');
|
||
|
heartBeatInspect();
|
||
|
if (reconnectTimer != null) {
|
||
|
reconnectTimer!.cancel();
|
||
|
reconnectTimer = null;
|
||
|
reconnectTimes = 0;
|
||
|
}
|
||
|
|
||
|
channel.stream.listen(onData, onError: onError, onDone: onDone);
|
||
|
}
|
||
|
|
||
|
void onData(jsonData) {
|
||
|
heartBeatInspect();
|
||
|
Map<String, dynamic> data = json.decode(jsonData);
|
||
|
switch (data['event']) {
|
||
|
case 'one-to-one-chat':
|
||
|
receiveFriendMsg(data);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void onDone() {
|
||
|
print('websocket disconnected <$channel>');
|
||
|
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);
|
||
|
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();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
enum SocketStatus {
|
||
|
connected,
|
||
|
closed,
|
||
|
error,
|
||
|
}
|
||
|
|
||
|
void receiveFriendMsg(Map<String, dynamic> msg) async {
|
||
|
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);
|
||
|
|
||
|
if (chatSetting == null) {
|
||
|
chatSettingBox.put(senderId, ChatSetting(senderId, false, true, false));
|
||
|
} else {
|
||
|
chatSetting.isOpen = true;
|
||
|
chatSettingBox.put(senderId, chatSetting);
|
||
|
}
|
||
|
|
||
|
messageTBox.add(
|
||
|
MessageT(
|
||
|
senderId,
|
||
|
msg['text'],
|
||
|
msg['type'],
|
||
|
DateTime.parse(msg['dateTime']),
|
||
|
msg['isShowTime'],
|
||
|
msg['attachments'],
|
||
|
),
|
||
|
);
|
||
|
}
|