diff --git a/CHANGELOG.md b/CHANGELOG.md index 09328f7..a81520a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,4 +29,6 @@ ### 1.1.4 * update set reddot method ### 1.1.5 - * update connnection device id with uid \ No newline at end of file + * update connnection device id with uid + ### 1.1.6 + * New features such as message likes and replies added \ No newline at end of file diff --git a/README.md b/README.md index 7b4fbc3..07879ed 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ #### 安装 ``` dependencies: - wukongimfluttersdk: ^1.1.5 + wukongimfluttersdk: ^1.1.6 ``` #### 引入 ```dart diff --git a/example/pubspec.lock b/example/pubspec.lock index 2c14fc1..e0d1809 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -499,7 +499,7 @@ packages: path: ".." relative: true source: path - version: "1.1.5" + version: "1.1.6" x25519: dependency: transitive description: diff --git a/lib/db/message.dart b/lib/db/message.dart index d9cc116..cac5fd9 100644 --- a/lib/db/message.dart +++ b/lib/db/message.dart @@ -182,7 +182,7 @@ class MessageDB { } } List messageIds = []; - // List replyMsgIds = []; + List replyMsgIds = []; List fromUIDs = []; List> results = await WKDBHelper.shared.getDB().rawQuery(sql); @@ -197,9 +197,11 @@ class MessageDB { messageIds.add(wkMsg.messageID); } - // if (wkMsg.messageContent != null && wkMsg.messageContent.reply != null && !TextUtils.isEmpty(wkMsg.messageContent.reply.message_id)) { - // replyMsgIds.add(wkMsg.messageContent.reply.message_id); - // } + if (wkMsg.messageContent != null && + wkMsg.messageContent!.reply != null && + wkMsg.messageContent!.reply!.messageId != '') { + replyMsgIds.add(wkMsg.messageContent!.reply!.messageId); + } if (wkMsg.fromUID != '') { bool isAdd = true; for (int i = 0; i < fromUIDs.length; i++) { @@ -263,6 +265,36 @@ class MessageDB { } } // 查询编辑内容 + if (replyMsgIds.isNotEmpty) { + List msgExtraList = + await queryMsgExtrasWithMsgIds(replyMsgIds); + if (msgExtraList.isNotEmpty) { + for (WKMsgExtra extra in msgExtraList) { + for (int i = 0, size = msgList.length; i < size; i++) { + if (msgList[i].messageContent != null && + msgList[i].messageContent!.reply != null && + extra.messageID == + msgList[i].messageContent!.reply!.messageId) { + msgList[i].messageContent!.reply!.revoke = extra.revoke; + } + if (extra.contentEdit != '' && + msgList[i].messageContent != null && + msgList[i].messageContent!.reply != null && + msgList[i].messageContent!.reply!.messageId != '' && + extra.messageID == + msgList[i].messageContent!.reply!.messageId) { + msgList[i].messageContent!.reply!.editAt = extra.editedAt; + msgList[i].messageContent!.reply!.contentEdit = extra.contentEdit; + var json = jsonEncode(extra.contentEdit); + var type = WKDBConst.readInt(json, 'type'); + msgList[i].messageContent!.reply!.contentEditMsgModel = + WKIM.shared.messageManager.getMessageModel(type, json); + break; + } + } + } + } + } return msgList; } @@ -605,6 +637,33 @@ class MessageDB { return extraVersion; } + Future> queryMsgExtraWithNeedUpload(int needUpload) async { + String sql = + "select * from ${WKDBConst.tableMessageExtra} where needUpload=$needUpload"; + List list = []; + List> results = + await WKDBHelper.shared.getDB().rawQuery(sql); + if (results.isNotEmpty) { + for (Map data in results) { + list.add(WKDBConst.serializeMsgExtra(data)); + } + } + + return list; + } + + Future queryMsgExtraWithMsgID(String messageID) async { + WKMsgExtra? msgExtra; + String sql = + "select * from ${WKDBConst.tableMessageExtra} where message_id='$messageID'"; + List> list = + await WKDBHelper.shared.getDB().rawQuery(sql); + if (list.isNotEmpty) { + msgExtra = WKDBConst.serializeMsgExtra(list[0]); + } + return msgExtra; + } + Future> queryMsgExtrasWithMsgIds(List msgIds) async { StringBuffer sb = StringBuffer(); sb.write( diff --git a/lib/entity/msg.dart b/lib/entity/msg.dart index b8a0882..ccb68ef 100644 --- a/lib/entity/msg.dart +++ b/lib/entity/msg.dart @@ -220,6 +220,65 @@ class WKSyncExtraMsg { int editedAt = 0; } +class WKReply { + // 被回复的消息根ID,多级回复时的第一次回复的消息ID + String rootMid = ''; + // 被回复的消息ID + String messageId = ''; + // 被回复的MessageSeq + int messageSeq = 0; + // 被回复者uid + String fromUID = ''; + // 被回复者名称 + String fromName = ''; + // 被回复的消息体 + WKMessageContent? payload; + // 被回复消息编辑后的内容 + String contentEdit = ''; + // 被回复消息编辑后的消息实体 + WKMessageContent? contentEditMsgModel; + // 编辑时间 + int editAt = 0; + int revoke = 0; + + dynamic encode() { + var json = {}; + json['root_mid'] = rootMid; + json['message_id'] = messageId; + json['message_seq'] = messageSeq; + json['from_uid'] = fromUID; + json['from_name'] = fromName; + if (payload != null) { + var contentJson = payload!.encodeJson(); + contentJson['type'] = payload!.contentType; + json['payload'] = contentJson; + } + return json; + } + + WKReply decode(dynamic data) { + rootMid = WKDBConst.readString(data, 'root_mid'); + messageId = WKDBConst.readString(data, 'message_id'); + messageSeq = WKDBConst.readInt(data, 'message_seq'); + fromUID = WKDBConst.readString(data, 'from_uid'); + fromName = WKDBConst.readString(data, 'from_name'); + String contentJson = WKDBConst.readString(data, 'payload'); + if (contentJson != '') { + var json = jsonDecode(contentJson); + var type = json['type']; + payload = WKIM.shared.messageManager.getMessageModel(type, json); + } + return this; + } +} + +class WKMsgEntity { + int offset = 0; + int length = 0; + String type = ''; + String value = ''; +} + class WKSyncChannelMsg { int startMessageSeq = 0; int endMessageSeq = 0; diff --git a/lib/manager/message_manager.dart b/lib/manager/message_manager.dart index 7a79043..a64d175 100644 --- a/lib/manager/message_manager.dart +++ b/lib/manager/message_manager.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:collection'; import 'dart:convert'; @@ -29,6 +30,7 @@ class WKMessageManager { Function(WKMsg wkMsg, Function(bool isSuccess, WKMsg wkMsg))? _uploadAttachmentBack; Function(WKMsg liMMsg)? _msgInsertedBack; + Function(WKMsgExtra)? _iUploadMsgExtraListener; HashMap)>? _newMsgBack; HashMap? _refreshMsgBack; HashMap? _deleteMsgBack; @@ -58,6 +60,24 @@ class WKMessageManager { } } content ??= WKUnknownContent(); + // 回复 + var replyJson = WKDBConst.readString(json, 'reply'); + if (replyJson != '') { + var reply = WKReply().decode(jsonDecode(replyJson)); + content.reply = reply; + } + var entities = WKDBConst.readString(json, 'entities'); + var jsonArray = jsonDecode(entities); + List list = []; + for (var entityJson in jsonArray) { + WKMsgEntity entity = WKMsgEntity(); + entity.type = WKDBConst.readString(entityJson, 'type'); + entity.offset = WKDBConst.readInt(entityJson, 'offset'); + entity.length = WKDBConst.readInt(entityJson, 'length'); + entity.value = WKDBConst.readString(entityJson, 'value'); + list.add(entity); + } + content.entities = list; return content; } @@ -390,6 +410,44 @@ class WKMessageManager { } } + _setUploadMsgExtra(WKMsgExtra extra) { + if (_iUploadMsgExtraListener != null) { + _iUploadMsgExtraListener!(extra); + } + Future.delayed(const Duration(seconds: 5), () { + _startCheckTimer(); + }); + } + + Timer? checkMsgNeedUploadTimer; + _startCheckTimer() { + _stopCheckMsgNeedUploadTimer(); + checkMsgNeedUploadTimer = + Timer.periodic(const Duration(seconds: 5), (timer) async { + var list = await MessageDB.shared.queryMsgExtraWithNeedUpload(1); + if (list.isNotEmpty) { + for (var extra in list) { + if (_iUploadMsgExtraListener != null) { + _iUploadMsgExtraListener!(extra); + } + } + } else { + _stopCheckMsgNeedUploadTimer(); + } + }); + } + + _stopCheckMsgNeedUploadTimer() { + if (checkMsgNeedUploadTimer != null) { + checkMsgNeedUploadTimer!.cancel(); + checkMsgNeedUploadTimer = null; + } + } + + addOnUploadMsgExtra(Function(WKMsgExtra) back) { + _iUploadMsgExtraListener = back; + } + addOnRefreshMsgListener(String key, Function(WKMsg) back) { _refreshMsgBack ??= HashMap(); if (key != '') { @@ -465,9 +523,8 @@ class WKMessageManager { int tempOrderSeq = await MessageDB.shared .queryMaxOrderSeq(wkMsg.channelID, wkMsg.channelType); wkMsg.orderSeq = tempOrderSeq + 1; - dynamic json = wkMsg.messageContent!.encodeJson(); - json['type'] = wkMsg.contentType; - wkMsg.content = jsonEncode(json); + + wkMsg.content = _getSendPayload(wkMsg); int row = await saveMsg(wkMsg); wkMsg.clientSeq = row; WKIM.shared.messageManager.setOnMsgInserted(wkMsg); @@ -512,6 +569,29 @@ class WKMessageManager { } } + String _getSendPayload(WKMsg wkMsg) { + dynamic json = wkMsg.messageContent!.encodeJson(); + json['type'] = wkMsg.contentType; + if (wkMsg.messageContent!.reply != null) { + json['reply'] = wkMsg.messageContent!.reply!.encode(); + } + + if (wkMsg.messageContent!.entities != null && + wkMsg.messageContent!.entities!.isNotEmpty) { + var jsonArray = []; + for (WKMsgEntity entity in wkMsg.messageContent!.entities!) { + var jo = {}; + jo['offset'] = entity.offset; + jo['length'] = entity.length; + jo['type'] = entity.type; + jo['value'] = entity.value; + jsonArray.add(jo); + } + json['entities'] = jsonArray; + } + return jsonEncode(json); + } + updateSendResult( String messageID, int clientSeq, int messageSeq, int reasonCode) async { WKMsg? wkMsg = await MessageDB.shared.queryWithClientSeq(clientSeq); @@ -578,6 +658,29 @@ class WKMessageManager { } } + updateMsgEdit(String messageID, String channelID, int channelType, + String content) async { + var msgExtra = await MessageDB.shared.queryMsgExtraWithMsgID(messageID); + msgExtra ??= WKMsgExtra(); + msgExtra.messageID = messageID; + msgExtra.channelID = channelID; + msgExtra.channelType = channelType; + msgExtra.editedAt = + (DateTime.now().millisecondsSinceEpoch / 1000).truncate(); + msgExtra.contentEdit = content; + msgExtra.needUpload = 1; + List list = []; + list.add(msgExtra); + List messageIds = []; + messageIds.add(messageID); + var result = await MessageDB.shared.insertOrUpdateMsgExtras(list); + if (result) { + var wkMsgs = await MessageDB.shared.queryWithMessageIds(messageIds); + getMsgReactionsAndRefreshMsg(messageIds, wkMsgs); + _setUploadMsgExtra(msgExtra); + } + } + deleteWithClientMsgNo(String clientMsgNo) async { var map = {}; map['is_deleted'] = 1; diff --git a/lib/model/wk_message_content.dart b/lib/model/wk_message_content.dart index b1c1263..d4564a6 100644 --- a/lib/model/wk_message_content.dart +++ b/lib/model/wk_message_content.dart @@ -1,7 +1,11 @@ +import 'package:wukongimfluttersdk/entity/msg.dart'; + class WKMessageContent { var contentType = 0; String content = ""; String topicId = ""; + WKReply? reply; + List? entities; Map encodeJson() { return {}; } diff --git a/pubspec.yaml b/pubspec.yaml index 2b5d716..5fff826 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ description: wukong IM flutter sdk # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.1.5 +version: 1.1.6 homepage: https://github.com/WuKongIM/WuKongIMFlutterSDK environment: