diff --git a/assets/svg/shequn.svg b/assets/svg/shequn.svg index e594bfec..49f53523 100644 --- a/assets/svg/shequn.svg +++ b/assets/svg/shequn.svg @@ -1,14 +1,13 @@ - + Created with Pixso. - - + + - - - + + diff --git a/lib/home/home_page.dart b/lib/home/home_page.dart index 6d0b0e76..f6ecb7ac 100644 --- a/lib/home/home_page.dart +++ b/lib/home/home_page.dart @@ -855,4 +855,5 @@ class HomePageState extends State with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; + } diff --git a/lib/im/SocketClient.dart b/lib/im/SocketClient.dart index 4b45ec73..0f169bf1 100644 --- a/lib/im/SocketClient.dart +++ b/lib/im/SocketClient.dart @@ -20,6 +20,8 @@ class SocketClient { final num port = 9090; Socket _socket; SharedPreferences _shared; + Timer timer; + bool get heartbeatActive => timer != null && timer.isActive; connect() async { _shared = await SharedPreferences.getInstance(); @@ -41,17 +43,17 @@ class SocketClient { print('收到来自:${dataResult.from},消息内容: ${utf8.decode(dataResult.data)} '); Map messageMap = createMessage(userId, utf8.decode(dataResult.data), msgType: dataResult.type.value, userId: dataResult.from); - Message message = Message.fromJson(messageMap); - callbacks[userId]?.call(message); /// user self conversation list callback - if (callbacks[dataResult.from] != null) { messageMap["state"] = 1; - message.state = 1; - callbacks[dataResult.from].call(message); /// user conversation callback } - - hxDatabase.insert(messageMap); - + hxDatabase.insert(messageMap).then((value) { + messageMap["id"] = value; + Message message = Message.fromJson(messageMap); + if (callbacks[dataResult.from] != null) { + callbacks[dataResult.from].call(message); /// user conversation callback + } + callbacks[userId]?.call(message); /// user self conversation list callback + }); }, onError: (Object error, StackTrace stackTrace) { debugPrint("socket-listen-error: $error, stackTrace: $stackTrace"); showDebugToast("socket-listen-error: $error, stackTrace: $stackTrace"); @@ -69,9 +71,6 @@ class SocketClient { _socket = null; reconnect(); }); - - // checkConnect(); - } showDebugToast(text) { @@ -80,27 +79,53 @@ class SocketClient { } } + Proto heartbeatData() { + DateTime dateTime = DateTime.now(); + int millisecondsTime = dateTime.millisecondsSinceEpoch; + Uint8List data = utf8.encode(jsonEncode({"heartbeat": millisecondsTime})); + MsgData msgData = MsgData(from: userId, type: MsgType.TEXT, data: data); + final proto2 = Proto(3, 1, msgData.writeToBuffer()); + debugPrint("heartbeat: ${dateTime.toString()}"); + return proto2; + } + heartbeat() { - Timer.periodic(const Duration(milliseconds: 30000), (timer) { - int millisecondsTime = DateTime.now().millisecondsSinceEpoch; - debugPrint("heartbeat: $millisecondsTime"); - Uint8List data = utf8.encode(jsonEncode({"heartbeat": millisecondsTime})); - MsgData msgData = MsgData(from: userId, type: MsgType.TEXT, data: data); - final proto2 = Proto(3, 1, msgData.writeToBuffer()); + cancelTimer(); + timer = Timer.periodic(const Duration(milliseconds: 30000), (timer) { if(!checkSocket()) { timer.cancel(); return; } - try { - _socket.add(proto2.toBytes()); - } catch (e) { - debugPrint("socket-send-error: ${e.toString()}"); - showDebugToast("socket-send-error: ${e.toString()}"); - reconnect(); - } + sendHeartBeatAndCheckSocket(); }); } + /// send heartBeat and check socket is connected + /// send error: reconnect, + /// else check Timer.periodic isActive , + /// if not active: Timer.periodic send heartBeat + sendHeartBeatAndCheckSocket() { + final proto2 = heartbeatData(); + try { + _socket.add(proto2.toBytes()); + if (!socketClient.heartbeatActive) { + heartbeat(); + debugPrint("socket-periodic-send-heart-beat"); + } + } catch (e) { + debugPrint("socket-send-error: ${e.toString()}"); + showDebugToast("socket-send-error: ${e.toString()}"); + reconnect(); + } + } + + cancelTimer() { + if (timer != null && timer.isActive) { + timer.cancel(); + timer = null; + } + } + reconnect() { dispose(); connect(); diff --git a/lib/im/chat_details_page.dart b/lib/im/chat_details_page.dart index 1daff184..4b982f75 100644 --- a/lib/im/chat_details_page.dart +++ b/lib/im/chat_details_page.dart @@ -89,8 +89,10 @@ class _ChatDetailsPage extends State socketClient.addCallback(_toUser.mid, (Message message) { messages.insert(0, message); - refreshState(); - jumpToBottom(); + messageShowTime().then((value) { + refreshState(); + jumpToBottom(); + }); }); refreshState(); jumpToBottom(); @@ -105,6 +107,7 @@ class _ChatDetailsPage extends State } else { messages.addAll(messagePage); } + await messageShowTime(); if (messagePage.isEmpty) { refreshController.loadNoData(); return; @@ -114,6 +117,14 @@ class _ChatDetailsPage extends State return Future.value(); } + Future messageShowTime() async { + List messagePages = await hxDatabase.queryTList(_toUser.mid); + for (var value in messages) { + Message message = messagePages.firstWhere((element) => value.id == element.id, orElse: () => null); + value.showTime = message != null; + } + } + ///查询个人信息 queryUser() async { final SharedPreferences value = await SharedPreferences.getInstance(); @@ -123,10 +134,8 @@ class _ChatDetailsPage extends State userInfo = UserInfo.fromJson(jsonDecode(value.getString('user'))); } if (apiService == null) - apiService = - ApiService(Dio(), context: context, token: value.getString("token")); - BaseData baseData = - await apiService.queryInfo().catchError((onError) {}); + apiService = ApiService(Dio(), context: context, token: value.getString("token")); + BaseData baseData = await apiService.queryInfo().catchError((onError) {}); if (baseData != null && baseData.isSuccess) { setState(() { userInfo = baseData.data; @@ -374,7 +383,8 @@ class _ChatDetailsPage extends State child: SingleChildScrollView( physics: BouncingScrollPhysics(), controller: scrollController, - child: chatDetailsList()), + child: chatDetailsList(), + ), ), flex: 1, ), @@ -382,7 +392,8 @@ class _ChatDetailsPage extends State ], ), ), - )); + ), + ); } ///聊天列表 @@ -419,24 +430,25 @@ class _ChatDetailsPage extends State ), child: Column( children: [ - Text( - // position == messages.length-1 - // ? - AppUtils.milliTimeFormatter(DateTime.fromMillisecondsSinceEpoch( - int.parse(messages[position].time))) - // : AppUtils.getTimeDisplay( - // DateTime.fromMillisecondsSinceEpoch( - // int.parse(messages[position].time)), - // DateTime.fromMillisecondsSinceEpoch( - // int.parse(messages[position+1].time))) - , - textAlign: TextAlign.center, - style: TextStyle( - color: Color(0xFFA29E9E), - fontSize: 10.sp, - fontWeight: MyFontWeight.regular, + if (messages[position].showTime) + Text( + // position == messages.length-1 + // ? + AppUtils.milliTimeFormatter(DateTime.fromMillisecondsSinceEpoch( + int.parse(messages[position].time))) + // : AppUtils.getTimeDisplay( + // DateTime.fromMillisecondsSinceEpoch( + // int.parse(messages[position].time)), + // DateTime.fromMillisecondsSinceEpoch( + // int.parse(messages[position+1].time))) + , + textAlign: TextAlign.center, + style: TextStyle( + color: Color(0xFFA29E9E), + fontSize: 10.sp, + fontWeight: MyFontWeight.regular, + ), ), - ), // if (messages.indexOf(message) == (messages.length - 1)) // Padding( // padding: EdgeInsets.only(top: 10.h, bottom: 24.h), diff --git a/lib/im/database/hx_database.dart b/lib/im/database/hx_database.dart index 0a8ce7d7..7e307f57 100644 --- a/lib/im/database/hx_database.dart +++ b/lib/im/database/hx_database.dart @@ -106,6 +106,16 @@ class HxDatabase { }); } + Future> queryTList(userId) async{ + await _dbIsOpen(); + String sql = 'SELECT *, time / 300000 * 300000 AS time_interval FROM Message WHERE toId = ? OR fromId = ? GROUP BY time_interval ORDER BY time DESC'; + return db.rawQuery(sql, [userId, userId]).then((value) { + return value.map((e) => Message.fromJson(e)).toList(); + }, onError: (error) { + debugPrint("Messageerror: $error"); + }); + } + Future> messageUnreadCount(List userIds, String selfUserId) async { await _dbIsOpen(); String userStr = userIds.join(","); diff --git a/lib/im/database/message.dart b/lib/im/database/message.dart index 188003b4..c5eadba2 100644 --- a/lib/im/database/message.dart +++ b/lib/im/database/message.dart @@ -21,6 +21,8 @@ class Message { int isDelete; + bool showTime = false; + Message(this.id, this.fromId, this.toId, this.replyId, this.content, this.attach, this.msgType, this.time, this.state, this.isDelete); factory Message.fromJson(Map json) => Message( diff --git a/lib/im/im_view/im_page.dart b/lib/im/im_view/im_page.dart index bf44da4d..724d5ce7 100644 --- a/lib/im/im_view/im_page.dart +++ b/lib/im/im_view/im_page.dart @@ -197,72 +197,6 @@ class _IMPage extends State implements OnChatMessage { if (mounted) setState(() {}); } - // queryMessage() async { - // BaseData> baseData = await apiService.msgList({ - // "pageNum": pageNum, - // "pageSize": 10, - // "searchKey": "", - // "state": "", - // "typed": "" - // }).catchError((onError) { - // _refreshController.loadFailed(); - // _refreshController.refreshFailed(); - // }); - // - // if (baseData != null && baseData.isSuccess) { - // if (pageNum == 1) { - // messages.clear(); - // } - // List message = []; - // message.addAll(baseData.data.list); - // message.forEach((element) { - // if (element.typed == 2 || element.typed == 3) { - // messages.add(element); - // } - // }); - // _refreshController.loadComplete(); - // _refreshController.refreshCompleted(); - // if (mounted) setState(() {}); - // if (pageNum * 10 > int.tryParse(baseData.data.total)) { - // _refreshController.loadNoData(); - // } else { - // pageNum += 1; - // } - // } else { - // _refreshController.loadFailed(); - // _refreshController.refreshFailed(); - // } - // } - - // queryMsgStats() async { - // if (apiService == null) { - // SharedPreferences value = await SharedPreferences.getInstance(); - // apiService = ApiService( - // Dio(), - // context: context, - // token: value.getString("token"), - // ); - // } - // BaseData> baseData = await apiService.stats().catchError((onError) { - // debugPrint("stats.error: $onError"); - // }); - // if (baseData != null && baseData.isSuccess) { - // setState(() { - // msgNumber.forEach((key, value) { - // msgNumber[key] = 0; - // }); - // baseData.data.forEach((element) { - // if (msgNumber.containsKey(element.name)) { - // msgNumber[element.name] = element.number; - // } - // }); - // }); - // _refreshController.loadComplete(); - // _refreshController.refreshCompleted(); - // } - // EasyLoading.dismiss(); - // } - ///批量查询用户信息 queryMemberInfo(List mids) async { if (mids.isEmpty) { @@ -271,8 +205,10 @@ class _IMPage extends State implements OnChatMessage { BaseData> baseData = await apiService.memberInfoByIds({ "mids": mids, }).catchError((error) { - SmartDialog.showToast(AppUtils.dioErrorTypeToString(error.type), - alignment: Alignment.center); + SmartDialog.showToast( + AppUtils.dioErrorTypeToString(error.type), + alignment: Alignment.center, + ); }); if (baseData != null && baseData.isSuccess) { if (baseData.data.isNotEmpty) { diff --git a/lib/main_page.dart b/lib/main_page.dart index 7fb53437..f318ce9f 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -82,6 +82,7 @@ class _MainPage extends State with WidgetsBindingObserver { break; case AppLifecycleState.resumed: //从后台切换前台,界面可见 pushRoute(); + socketClient.sendHeartBeatAndCheckSocket(); if (DateTime.now().millisecondsSinceEpoch - lastTime > 420000) //处于后台**分钟后刷新应用 // Navigator.of(context).popAndPushNamed('/router/start_page');