Support message receipts

This commit is contained in:
SL 2023-09-04 14:22:12 +08:00
parent 5f073e3b62
commit a3343eb5e2
12 changed files with 167 additions and 19 deletions

View File

@ -11,4 +11,7 @@
### 1.0.5
* Optimize processing of incorrect data
### 1.0.6
* Update sending messages without notification to refresh conversation messsage
* Update sending messages without notification to refresh conversation messsage
### 1.0.7
* Support message receipts

View File

@ -9,7 +9,7 @@
#### 安装
```
dependencies:
wukongimfluttersdk: ^1.0.6
wukongimfluttersdk: ^1.0.7
```
#### 引入
```dart

View File

@ -1,11 +1,13 @@
import 'package:example/const.dart';
import 'package:flutter/material.dart';
import 'package:wukongimfluttersdk/entity/channel.dart';
import 'package:wukongimfluttersdk/entity/msg.dart';
import 'package:wukongimfluttersdk/model/wk_card_content.dart';
import 'package:wukongimfluttersdk/model/wk_image_content.dart';
import 'package:wukongimfluttersdk/model/wk_text_content.dart';
import 'package:wukongimfluttersdk/model/wk_video_content.dart';
import 'package:wukongimfluttersdk/model/wk_voice_content.dart';
import 'package:wukongimfluttersdk/proto/proto.dart';
import 'package:wukongimfluttersdk/type/const.dart';
import 'package:wukongimfluttersdk/wkim.dart';
@ -72,6 +74,10 @@ class ChatListDataState extends State<ChatList> {
WKIM.shared.messageManager.addOnNewMsgListener('chat', (msgs) {
setState(() {
for (var i = 0; i < msgs.length; i++) {
if (msgs[i].setting.receipt == 1) {
//
testReceipt(msgs[i]);
}
msgList.add(UIMsg(msgs[i]));
}
});
@ -85,6 +91,7 @@ class ChatListDataState extends State<ChatList> {
msgList[i].wkMsg.messageID = wkMsg.messageID;
msgList[i].wkMsg.messageSeq = wkMsg.messageSeq;
msgList[i].wkMsg.status = wkMsg.status;
msgList[i].wkMsg.wkMsgExtra = wkMsg.wkMsgExtra;
break;
}
}
@ -92,6 +99,24 @@ class ChatListDataState extends State<ChatList> {
});
}
// db
testReceipt(WKMsg wkMsg) async {
if (wkMsg.viewed == 0) {
var maxVersion = await WKIM.shared.messageManager
.getMaxExtraVersionWithChannel(channelID, channelType);
var extra = WKMsgExtra();
extra.messageID = wkMsg.messageID;
extra.channelID = channelID;
extra.channelType = channelType;
extra.readed = 1;
extra.readedCount = 1;
extra.extraVersion = maxVersion + 1;
List<WKMsgExtra> list = [];
list.add(extra);
WKIM.shared.messageManager.saveRemoteExtraMsg(list);
}
}
getMsgList() {
WKIM.shared.messageManager.getOrSyncHistoryMessages(
channelID, channelType, 0, true, 0, 100, 0, (list) {
@ -274,10 +299,11 @@ class ChatListDataState extends State<ChatList> {
onPressed: () {
if (content != '') {
_textEditingController.text = '';
Setting setting = Setting();
setting.receipt = 1; //
WKTextContent text = WKTextContent(content);
WKIM.shared.messageManager
.sendMessage(text, WKChannel(channelID, channelType));
WKIM.shared.messageManager.sendMessageWithSetting(
text, WKChannel(channelID, channelType), setting);
// WKImageContent imageContent = WKImageContent(100, 200);
// imageContent.localPath = 'addskds';
// WKIM.shared.messageManager.sendMessage(

View File

@ -42,21 +42,21 @@ class IMUtils {
WKImageContent imageContent = wkMsg.messageContent! as WKImageContent;
imageContent.url = 'xxxxxx';
wkMsg.messageContent = imageContent;
back(wkMsg);
back(true, wkMsg);
}
if (wkMsg.contentType == WkMessageContentType.voice) {
// todo
WKVoiceContent voiceContent = wkMsg.messageContent! as WKVoiceContent;
voiceContent.url = 'xxxxxx';
wkMsg.messageContent = voiceContent;
back(wkMsg);
back(true, wkMsg);
} else if (wkMsg.contentType == WkMessageContentType.video) {
WKVideoContent videoContent = wkMsg.messageContent! as WKVideoContent;
// todo
videoContent.cover = 'xxxxxx';
videoContent.url = 'ssssss';
wkMsg.messageContent = videoContent;
back(wkMsg);
back(true, wkMsg);
}
});
}

View File

@ -11,7 +11,11 @@ class UIMsg {
if (wkMsg.messageContent == null) {
return '';
}
return wkMsg.messageContent!.displayText();
var readCount = 0;
if (wkMsg.wkMsgExtra != null) {
readCount = wkMsg.wkMsgExtra!.readedCount;
}
return "${wkMsg.messageContent!.displayText()} [是否需要回执:${wkMsg.setting.receipt}][已读数量:$readCount]";
}
String getShowTime() {

View File

@ -499,7 +499,7 @@ packages:
path: ".."
relative: true
source: path
version: "1.0.6"
version: "1.0.7"
x25519:
dependency: transitive
description:

View File

@ -87,6 +87,30 @@ class MessaggeDB {
return wkMsg;
}
Future<List<WKMsg>> queryWithMessageIds(List<String> messageIds) async {
StringBuffer sb = StringBuffer();
for (int i = 0, size = messageIds.length; i < size; i++) {
if (i != 0) {
sb.write(",");
}
sb.write("'");
sb.write(messageIds[i]);
sb.write("'");
}
String sql =
"select $messageCols,$extraCols from ${WKDBConst.tableMessage} LEFT JOIN ${WKDBConst.tableMessageExtra} ON ${WKDBConst.tableMessage}.message_id=${WKDBConst.tableMessageExtra}.message_id WHERE ${WKDBConst.tableMessage}.message_id in (${sb.toString()})";
List<WKMsg> list = [];
List<Map<String, Object?>> results =
await WKDBHelper.shared.getDB().rawQuery(sql);
if (results.isNotEmpty) {
for (Map<String, Object?> data in results) {
list.add(WKDBConst.serializeWKMsg(data));
}
}
return list;
}
Future<List<WKMsgReaction>> queryReactions(String messageID) async {
String sql =
"select * from ${WKDBConst.tableMessageReaction} where message_id='$messageID' and is_deleted=0 ORDER BY created_at desc";
@ -536,7 +560,7 @@ class MessaggeDB {
return msgs;
}
insertOrUpdateMsgExtras(List<WKMsgExtra> list) async {
Future<bool> insertOrUpdateMsgExtras(List<WKMsgExtra> list) async {
List<String> msgIds = [];
for (int i = 0, size = list.length; i < size; i++) {
if (list[i].messageID != '') {
@ -574,6 +598,21 @@ class MessaggeDB {
}
});
}
return true;
}
Future<int> queryMaxExtraVersionWithChannel(
String channelID, int channelType) async {
int extraVersion = 0;
String sql =
"select max(extra_version) extra_version from ${WKDBConst.tableMessageExtra} where channel_id ='$channelID' and channel_type=$channelType";
List<Map<String, Object?>> list =
await WKDBHelper.shared.getDB().rawQuery(sql);
if (list.isNotEmpty) {
dynamic data = list[0];
extraVersion = WKDBConst.readInt(data, 'extra_version');
}
return extraVersion;
}
Future<List<WKMsgExtra>> queryMsgExtrasWithMsgIds(List<String> msgIds) async {
@ -720,7 +759,6 @@ class MessaggeDB {
map['is_mutual_deleted'] = extra.isMutualDeleted;
map['content_edit'] = extra.contentEdit;
map['edited_at'] = extra.editedAt;
map['edited_at'] = extra.editedAt;
map['need_upload'] = extra.needUpload;
map['message_id'] = extra.messageID;
return map;

View File

@ -343,10 +343,15 @@ class WKConnectionManager {
sendMessage(WKMsg wkMsg) {
SendPacket packet = SendPacket();
packet.setting = wkMsg.setting;
packet.header.noPersist = wkMsg.header.noPersist;
packet.header.showUnread = wkMsg.header.redDot;
packet.header.syncOnce = wkMsg.header.syncOnce;
packet.channelID = wkMsg.channelID;
packet.channelType = wkMsg.channelType;
packet.clientSeq = wkMsg.clientSeq;
packet.clientMsgNO = wkMsg.clientMsgNO;
packet.topic = wkMsg.topicID;
packet.payload = wkMsg.content;
_addSendingMsg(packet);
_sendPacket(packet);
@ -382,6 +387,7 @@ class WKConnectionManager {
msg.header.redDot = recvMsg.header.showUnread;
msg.header.noPersist = recvMsg.header.noPersist;
msg.header.syncOnce = recvMsg.header.syncOnce;
msg.setting = recvMsg.setting;
msg.channelType = recvMsg.channelType;
msg.channelID = recvMsg.channelID;
msg.content = recvMsg.payload;
@ -389,8 +395,6 @@ class WKConnectionManager {
msg.messageSeq = recvMsg.messageSeq;
msg.timestamp = recvMsg.messageTime;
msg.fromUID = recvMsg.fromUID;
msg.setting = recvMsg.setting;
msg.clientMsgNO = recvMsg.clientMsgNO;
msg.status = WKSendMsgResult.sendSuccess;
msg.topicID = recvMsg.topic;
msg.orderSeq = await WKIM.shared.messageManager

View File

@ -7,6 +7,7 @@ import 'package:wukongimfluttersdk/db/const.dart';
import 'package:wukongimfluttersdk/db/message.dart';
import 'package:wukongimfluttersdk/entity/msg.dart';
import 'package:wukongimfluttersdk/model/wk_media_message_content.dart';
import 'package:wukongimfluttersdk/proto/proto.dart';
import 'package:wukongimfluttersdk/type/const.dart';
import '../entity/channel.dart';
@ -23,7 +24,8 @@ class WKMessageManager {
final Map<int, WKMessageContent Function(dynamic data)> _msgContentList =
HashMap<int, WKMessageContent Function(dynamic data)>();
Function(WKMsg wkMsg, Function(WKMsg wkMsg))? _uploadAttachmentBack;
Function(WKMsg wkMsg, Function(bool isSuccess, WKMsg wkMsg))?
_uploadAttachmentBack;
Function(WKMsg liMMsg)? _msgInsertedBack;
HashMap<String, Function(List<WKMsg>)>? _newMsgBack;
HashMap<String, Function(WKMsg)>? _refreshMsgBack;
@ -105,6 +107,52 @@ class WKMessageManager {
return messageSeq * wkOrderSeqFactor;
}
Future<int> updateViewedAt(int viewedAt, String clientMsgNO) async {
dynamic json = <String, Object>{};
json['viewed'] = 1;
json['viewed_at'] = viewedAt;
return MessaggeDB.shared
.updateMsgWithFieldAndClientMsgNo(json, clientMsgNO);
}
Future<int> getMaxExtraVersionWithChannel(
String channelID, int channelType) async {
return MessaggeDB.shared
.queryMaxExtraVersionWithChannel(channelID, channelType);
}
saveRemoteExtraMsg(List<WKMsgExtra> list) async {
MessaggeDB.shared.insertOrUpdateMsgExtras(list);
List<String> msgIds = [];
for (var extra in list) {
msgIds.add(extra.messageID);
}
var msgList = await MessaggeDB.shared.queryWithMessageIds(msgIds);
for (var msg in msgList) {
for (var extra in list) {
msg.wkMsgExtra ??= WKMsgExtra();
if (msg.messageID == extra.messageID) {
msg.wkMsgExtra!.readed = extra.readed;
msg.wkMsgExtra!.readedCount = extra.readedCount;
msg.wkMsgExtra!.unreadCount = extra.unreadCount;
msg.wkMsgExtra!.revoke = extra.revoke;
msg.wkMsgExtra!.revoker = extra.revoker;
msg.wkMsgExtra!.isMutualDeleted = extra.isMutualDeleted;
msg.wkMsgExtra!.editedAt = extra.editedAt;
msg.wkMsgExtra!.contentEdit = extra.contentEdit;
msg.wkMsgExtra!.extraVersion = extra.extraVersion;
if (extra.contentEdit != '') {
dynamic contentJson = jsonDecode(extra.contentEdit);
msg.wkMsgExtra!.messageContent = WKIM.shared.messageManager
.getMessageModel(WkMessageContentType.text, contentJson);
}
break;
}
}
setRefreshMsg(msg);
}
}
void setSyncChannelMsgListener(
String channelID,
int channelType,
@ -317,13 +365,30 @@ class WKMessageManager {
_msgInsertedBack = insertListener;
}
addOnUploadAttachmentListener(Function(WKMsg, Function(WKMsg)) back) {
addOnUploadAttachmentListener(Function(WKMsg, Function(bool, WKMsg)) back) {
_uploadAttachmentBack = back;
}
sendMessage(WKMessageContent messageContent, WKChannel channel) async {
var header = MessageHeader();
header.redDot = true;
sendMessageWithSettingAndHeader(messageContent, channel, Setting(), header);
}
sendMessageWithSetting(
WKMessageContent messageContent, WKChannel channel, Setting setting) {
var header = MessageHeader();
header.redDot = true;
sendMessageWithSettingAndHeader(messageContent, channel, setting, header);
}
sendMessageWithSettingAndHeader(WKMessageContent messageContent,
WKChannel channel, Setting setting, MessageHeader header) async {
WKMsg wkMsg = WKMsg();
wkMsg.setting = setting;
wkMsg.header = header;
wkMsg.messageContent = messageContent;
wkMsg.topicID = messageContent.topicId;
wkMsg.channelID = channel.channelID;
wkMsg.channelType = channel.channelType;
wkMsg.fromUID = WKIM.shared.options.uid!;
@ -347,7 +412,12 @@ class WKMessageManager {
if (wkMsg.messageContent is WKMediaMessageContent) {
//
if (_uploadAttachmentBack != null) {
_uploadAttachmentBack!(wkMsg, (uploadedMsg) {
_uploadAttachmentBack!(wkMsg, (isSuccess, uploadedMsg) {
if (!isSuccess) {
wkMsg.status = WKSendMsgResult.sendFail;
updateMsgStatusFail(wkMsg.clientSeq);
return;
}
//
Map<String, dynamic> json = uploadedMsg.messageContent!.encodeJson();
json['type'] = uploadedMsg.contentType;

View File

@ -1,6 +1,7 @@
class WKMessageContent {
var contentType = 0;
String content = "";
String topicId = "";
Map<String, dynamic> encodeJson() {
return {};
}

View File

@ -22,7 +22,8 @@ class WKVideoContent extends WKMediaMessageContent {
'size': size,
'width': width,
'height': height,
'second': second
'second': second,
'url': url
};
}
@ -35,6 +36,7 @@ class WKVideoContent extends WKMediaMessageContent {
width = readInt(json, 'width');
height = readInt(json, 'height');
second = readInt(json, 'second');
url = readString(json, 'url');
return this;
}

View File

@ -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.0.6
version: 1.0.7
homepage: https://github.com/WuKongIM/WuKongIMFlutterSDK
environment: