fix:新增修改频道头像方法,优化example

This commit is contained in:
SL 2025-04-26 12:59:17 +08:00
parent c0bb596c6a
commit 66fb59ac67
10 changed files with 512 additions and 445 deletions

View File

@ -1,3 +1,4 @@
{
"DockerRun.DisableDockerrc": true
"DockerRun.DisableDockerrc": true,
"java.compile.nullAnalysis.mode": "automatic"
}

View File

@ -125,4 +125,6 @@
### 1.6.2
* fix: 修改数据库解码错误数据导致oom
### 1.6.3
* fix: 优化在未收到服务端心跳消息时主动断开重连
* fix: 优化在未收到服务端心跳消息时主动断开重连
### 1.6.4
* fix: 新增监听头像改变事件

View File

@ -13,125 +13,126 @@ class HttpUtils {
// static String apiURL = "https://api.githubim.com";
static String apiURL = "http://62.234.8.38:7090/v1";
// static String apiURL = "http://175.27.245.108:15001";
static getAvatarUrl(String uid) {
static Dio? _dio;
/// Get Dio instance with trust all certificates configuration
static Dio get dio {
if (_dio == null) {
final httpClient = HttpClient();
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) => true; // Trust all certificates
_dio = Dio(BaseOptions(
baseUrl: apiURL,
//
validateStatus: (status) => true,
));
(_dio!.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(client) => httpClient;
}
return _dio!;
}
static String getAvatarUrl(String uid) {
return "$apiURL/users/$uid/avatar";
}
static getGroupAvatarUrl(String gid) {
static String getGroupAvatarUrl(String gid) {
return "$apiURL/groups/$gid/avatar";
}
static Future<int> login(String uid, String token) async {
final httpClient = HttpClient();
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) {
//
return true;
};
final dio = Dio();
dio.httpClientAdapter = DefaultHttpClientAdapter()
..onHttpClientCreate = (client) {
return httpClient;
};
final response = await dio.post("$apiURL/user/login", data: {
'uid': uid,
'token': token,
'device_flag': 0,
'device_level': 1
});
try {
final response = await dio.post("/user/login", data: {
'uid': uid,
'token': token,
'device_flag': 0,
'device_level': 1
});
if (response.statusCode == HttpStatus.ok) {
UserInfo.name = response.data['name'];
}
return response.statusCode ?? HttpStatus.badRequest;
} catch (e) {
print('获取用户信息失败');
print('Login error: $e');
return HttpStatus.internalServerError;
}
return response.statusCode!;
}
static Future<String> getIP(String uid) async {
final httpClient = HttpClient();
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) {
//
return true;
};
final dio = Dio();
dio.httpClientAdapter = DefaultHttpClientAdapter()
..onHttpClientCreate = (client) {
return httpClient;
};
String ip = '';
try {
final response = await dio.get('$apiURL/users/$uid/route');
final response = await dio.get('/users/$uid/route');
if (response.statusCode == HttpStatus.ok) {
ip = response.data['tcp_addr'];
return response.data['tcp_addr'] ?? '';
}
} catch (e) {
ip = '';
print('Get IP error: $e');
}
return ip;
return '';
}
static syncConversation(String lastSsgSeqs, int msgCount, int version,
static Future<void> syncConversation(String lastSsgSeqs, int msgCount, int version,
Function(WKSyncConversation) back) async {
final httpClient = HttpClient();
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) {
//
return true;
};
final dio = Dio();
dio.httpClientAdapter = DefaultHttpClientAdapter()
..onHttpClientCreate = (client) {
return httpClient;
};
final response = await dio.post('$apiURL/conversation/sync', data: {
"login_uid": UserInfo.uid, // uid
"version": version, // (version0)
"last_msg_seqs":
lastSsgSeqs, // channelID:channelType:last_msg_seq|channelID:channelType:last_msg_seq
"msg_count": 10, // app点进去第一屏的数据
"device_uuid": UserInfo.uid,
});
// print(response.data);
WKSyncConversation conversation = WKSyncConversation();
conversation.conversations = [];
if (response.statusCode == HttpStatus.ok) {
try {
var list = response.data['conversations'];
// var list = jsonDecode(response.data);
for (int i = 0; i < list.length; i++) {
var json = list[i];
WKSyncConvMsg convMsg = WKSyncConvMsg();
convMsg.channelID = json['channel_id'];
convMsg.channelType = json['channel_type'];
convMsg.unread = json['unread'] ?? 0;
convMsg.timestamp = json['timestamp'];
convMsg.lastMsgSeq = json['last_msg_seq'];
convMsg.lastClientMsgNO = json['last_client_msg_no'];
convMsg.version = json['version'];
var msgListJson = json['recents'] as List<dynamic>;
List<WKSyncMsg> msgList = [];
if (msgListJson.isNotEmpty) {
for (int j = 0; j < msgListJson.length; j++) {
var msgJson = msgListJson[j];
msgList.add(getWKSyncMsg(msgJson));
}
}
convMsg.recents = msgList;
conversation.conversations!.add(convMsg);
}
} catch (e) {
print('同步最近会话错误');
try {
//
if (UserInfo.uid.isEmpty) {
print('请先登录');
back(WKSyncConversation()..conversations = []);
return;
}
final response = await dio.post('/conversation/sync', data: {
"login_uid": UserInfo.uid,
"version": version,
"last_msg_seqs": lastSsgSeqs,
"msg_count": msgCount,
"device_uuid": UserInfo.uid,
});
WKSyncConversation conversation = WKSyncConversation();
conversation.conversations = [];
if (response.statusCode == HttpStatus.ok) {
try {
var list = response.data['conversations'];
for (int i = 0; i < list.length; i++) {
var json = list[i];
WKSyncConvMsg convMsg = WKSyncConvMsg();
convMsg.channelID = json['channel_id'];
convMsg.channelType = json['channel_type'];
convMsg.unread = json['unread'] ?? 0;
convMsg.timestamp = json['timestamp'];
convMsg.lastMsgSeq = json['last_msg_seq'];
convMsg.lastClientMsgNO = json['last_client_msg_no'];
convMsg.version = json['version'];
var msgListJson = json['recents'] as List<dynamic>;
List<WKSyncMsg> msgList = [];
if (msgListJson.isNotEmpty) {
for (int j = 0; j < msgListJson.length; j++) {
var msgJson = msgListJson[j];
msgList.add(getWKSyncMsg(msgJson));
}
}
convMsg.recents = msgList;
conversation.conversations!.add(convMsg);
}
} catch (e) {
print('解析会话数据错误: $e');
}
} else {
print('同步会话失败: HTTP ${response.statusCode}');
if (response.data != null && response.data is Map) {
print('错误信息: ${response.data['message'] ?? response.data}');
}
}
back(conversation);
} catch (e) {
print('同步会话错误: $e');
back(WKSyncConversation()..conversations = []);
}
back(conversation);
}
static syncChannelMsg(
@ -142,42 +143,37 @@ class HttpUtils {
int limit,
int pullMode,
Function(WKSyncChannelMsg) back) async {
final httpClient = HttpClient();
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) {
//
return true;
};
final dio = Dio();
dio.httpClientAdapter = DefaultHttpClientAdapter()
..onHttpClientCreate = (client) {
return httpClient;
};
final response = await dio.post('$apiURL/message/channel/sync', data: {
"login_uid": UserInfo.uid, // uid
"channel_id": channelID, // ID
"channel_type": channelType, //
"start_message_seq": startMsgSeq, // start_message_seq的消息
"end_message_seq": endMsgSeq, // end_message_seq的消息
"limit": limit, //
"pull_mode": pullMode // 0: 1:
});
if (response.statusCode == HttpStatus.ok) {
var data = response.data;
WKSyncChannelMsg msg = WKSyncChannelMsg();
msg.startMessageSeq = data['start_message_seq'];
msg.endMessageSeq = data['end_message_seq'];
msg.more = data['more'];
var messages = data['messages'];
try {
final response = await dio.post('/message/channel/sync', data: {
"login_uid": UserInfo.uid,
"channel_id": channelID,
"channel_type": channelType,
"start_message_seq": startMsgSeq,
"end_message_seq": endMsgSeq,
"limit": limit,
"pull_mode": pullMode
});
if (response.statusCode == HttpStatus.ok) {
var data = response.data;
WKSyncChannelMsg msg = WKSyncChannelMsg();
msg.startMessageSeq = data['start_message_seq'];
msg.endMessageSeq = data['end_message_seq'];
msg.more = data['more'];
var messages = data['messages'];
List<WKSyncMsg> msgList = [];
for (int i = 0; i < messages.length; i++) {
dynamic json = messages[i];
msgList.add(getWKSyncMsg(json));
List<WKSyncMsg> msgList = [];
for (int i = 0; i < messages.length; i++) {
dynamic json = messages[i];
msgList.add(getWKSyncMsg(json));
}
print('同步channel消息数量${msgList.length}');
msg.messages = msgList;
back(msg);
}
print('同步channel消息数量${msgList.length}');
msg.messages = msgList;
back(msg);
} catch (e) {
print('Sync channel message error: $e');
back(WKSyncChannelMsg());
}
}
@ -221,71 +217,90 @@ class HttpUtils {
return extra;
}
static getGroupInfo(String groupId) async {
final httpClient = HttpClient();
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) {
//
return true;
};
final dio = Dio();
dio.httpClientAdapter = DefaultHttpClientAdapter()
..onHttpClientCreate = (client) {
return httpClient;
};
final response = await dio.get('$apiURL/groups/$groupId');
if (response.statusCode == HttpStatus.ok) {
var json = response.data;
var channel = WKChannel(groupId, WKChannelType.group);
channel.channelName = json['name'];
channel.avatar = json['avatar'];
WKIM.shared.channelManager.addOrUpdateChannel(channel);
} else {
print('获取群信息失败');
static Future<void> getGroupInfo(String groupId) async {
try {
// ID是否有效
if (groupId.isEmpty) {
print('群ID不能为空');
return;
}
//
if (UserInfo.uid.isEmpty) {
print('请先登录');
return;
}
final response = await dio.get('/groups/$groupId');
if (response.statusCode == HttpStatus.ok) {
var json = response.data;
var channel = WKChannel(groupId, WKChannelType.group);
channel.channelName = json['name'];
channel.avatar = json['avatar'];
WKIM.shared.channelManager.addOrUpdateChannel(channel);
} else {
print('获取群信息失败: HTTP ${response.statusCode}');
//
if (response.data != null && response.data is Map) {
print('错误信息: ${response.data['message'] ?? response.data}');
}
}
} catch (e) {
print('获取群信息错误: $e');
}
}
static getUserInfo(String uid) async {
final httpClient = HttpClient();
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) {
//
return true;
};
final dio = Dio();
dio.httpClientAdapter = DefaultHttpClientAdapter()
..onHttpClientCreate = (client) {
return httpClient;
};
static Future<void> getUserInfo(String uid) async {
try {
final response = await dio.get('$apiURL/users/$uid');
// UID是否有效
if (uid.isEmpty) {
print('用户ID不能为空');
return;
}
//
if (UserInfo.uid.isEmpty) {
print('请先登录');
return;
}
final response = await dio.get('/users/$uid');
if (response.statusCode == HttpStatus.ok) {
var json = response.data;
var channel = WKChannel(uid, WKChannelType.personal);
channel.channelName = json['name'];
channel.avatar = json['avatar'];
WKIM.shared.channelManager.addOrUpdateChannel(channel);
} else {
print('获取用户信息失败: HTTP ${response.statusCode}');
//
if (response.data != null && response.data is Map) {
print('错误信息: ${response.data['message'] ?? response.data}');
}
}
} catch (e) {
print('获取用户信息失败$e');
print('获取用户信息错误: $e');
}
}
static revokeMsg(String clientMsgNo, String channelId, int channelType,
static Future<bool> revokeMsg(String clientMsgNo, String channelId, int channelType,
int msgSeq, String msgId) async {
final httpClient = HttpClient();
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) {
//
return true;
};
final dio = Dio();
dio.httpClientAdapter = DefaultHttpClientAdapter()
..onHttpClientCreate = (client) {
return httpClient;
};
try {
final response = await dio.post('$apiURL/message/revoke', data: {
//
if (clientMsgNo.isEmpty || channelId.isEmpty || msgId.isEmpty) {
print('撤回消息需提供完整的消息信息');
return false;
}
//
if (UserInfo.uid.isEmpty) {
print('请先登录');
return false;
}
final response = await dio.post('/message/revoke', data: {
'login_uid': UserInfo.uid,
'channel_id': channelId,
'channel_type': channelType,
@ -293,57 +308,66 @@ class HttpUtils {
'message_seq': msgSeq,
'message_id': msgId,
});
if (response.statusCode == HttpStatus.ok) {
print('撤回消息成功');
print('消息撤回成功');
return true;
} else {
print('撤回消息失败: HTTP ${response.statusCode}');
if (response.data != null && response.data is Map) {
print('错误信息: ${response.data['message'] ?? response.data}');
}
return false;
}
} catch (e) {
print('获取用户信息失败$e');
print('撤回消息错误: $e');
return false;
}
}
static deleteMsg(String clientMsgNo, String channelId, int channelType,
static Future<bool> deleteMsg(String clientMsgNo, String channelId, int channelType,
int msgSeq, String msgId) async {
final httpClient = HttpClient();
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) {
//
return true;
};
final dio = Dio();
dio.httpClientAdapter = DefaultHttpClientAdapter()
..onHttpClientCreate = (client) {
return httpClient;
};
try {
final response = await dio.post('$apiURL/message/delete', data: {
//
if (clientMsgNo.isEmpty || channelId.isEmpty || msgId.isEmpty) {
print('删除消息需提供完整的消息信息');
return false;
}
//
if (UserInfo.uid.isEmpty) {
print('请先登录');
return false;
}
final response = await dio.post('/message/delete', data: {
'login_uid': UserInfo.uid,
'channel_id': channelId,
'channel_type': channelType,
'message_seq': msgSeq,
'message_id': msgId,
});
if (response.statusCode == HttpStatus.ok) {
WKIM.shared.messageManager.deleteWithClientMsgNo(clientMsgNo);
print('消息删除成功');
return true;
} else {
print('删除消息失败: HTTP ${response.statusCode}');
if (response.data != null && response.data is Map) {
print('错误信息: ${response.data['message'] ?? response.data}');
}
return false;
}
} catch (e) {
print('删除消息失败$e');
print('删除消息错误: $e');
return false;
}
}
static syncMsgExtra(String channelId, int channelType, int version) async {
final httpClient = HttpClient();
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) {
//
return true;
};
final dio = Dio();
dio.httpClientAdapter = DefaultHttpClientAdapter()
..onHttpClientCreate = (client) {
return httpClient;
};
static Future<void> syncMsgExtra(String channelId, int channelType, int version) async {
try {
final response = await dio.post('$apiURL/message/extra/sync', data: {
final response = await dio.post('/message/extra/sync', data: {
'login_uid': UserInfo.uid,
'channel_id': channelId,
'channel_type': channelType,
@ -351,6 +375,7 @@ class HttpUtils {
'limit': 100,
'extra_version': version,
});
if (response.statusCode == HttpStatus.ok) {
var arrJson = response.data;
if (arrJson != null && arrJson.length > 0) {
@ -371,122 +396,73 @@ class HttpUtils {
}
}
} catch (e) {
print('同步消息扩展失败$e');
print('Sync message extra error: $e');
}
}
//
static clearUnread(String channelId, int channelType) async {
final httpClient = HttpClient();
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) {
//
return true;
};
final dio = Dio();
dio.httpClientAdapter = DefaultHttpClientAdapter()
..onHttpClientCreate = (client) {
return httpClient;
};
static Future<void> clearUnread(String channelId, int channelType) async {
try {
final response = await dio.put('$apiURL/conversation/clearUnread', data: {
final response = await dio.put('/conversation/clearUnread', data: {
'login_uid': UserInfo.uid,
'channel_id': channelId,
'channel_type': channelType,
'unread': 0,
});
if (response.statusCode == HttpStatus.ok) {
print('清空红点成功');
print('Unread count cleared successfully');
}
} catch (e) {
print('清空红点失败$e');
print('Clear unread count error: $e');
}
}
//
static clearChannelMsg(String channelId, int channelType) async {
final httpClient = HttpClient();
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) {
//
return true;
};
final dio = Dio();
dio.httpClientAdapter = DefaultHttpClientAdapter()
..onHttpClientCreate = (client) {
return httpClient;
};
static Future<void> clearChannelMsg(String channelId, int channelType) async {
try {
int maxSeq = await WKIM.shared.messageManager
.getMaxMessageSeq(channelId, channelType);
final response = await dio.post('$apiURL/message/offset', data: {
final response = await dio.post('/message/offset', data: {
'login_uid': UserInfo.uid,
'channel_id': channelId,
'channel_type': channelType,
'message_seq': maxSeq
});
if (response.statusCode == HttpStatus.ok) {
WKIM.shared.messageManager.clearWithChannel(channelId, channelType);
}
} catch (e) {
print('清除频道消息失败$e');
print('Clear channel message error: $e');
}
}
//
static Future<bool> createGroup(String groupNo) async {
final httpClient = HttpClient();
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) {
//
return true;
};
final dio = Dio();
dio.httpClientAdapter = DefaultHttpClientAdapter()
..onHttpClientCreate = (client) {
return httpClient;
};
try {
final response = await dio.post('$apiURL/group/create', data: {
final response = await dio.post('/group/create', data: {
'login_uid': UserInfo.uid,
'group_no': groupNo,
});
if (response.statusCode == HttpStatus.ok) {
return true;
} else {
return false;
}
return response.statusCode == HttpStatus.ok;
} catch (e) {
print('创建群失败$e');
print('Create group error: $e');
return false;
}
}
//
static Future<bool> updateGroupName(String groupNo, String groupName) async {
final httpClient = HttpClient();
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) {
//
return true;
};
final dio = Dio();
dio.httpClientAdapter = DefaultHttpClientAdapter()
..onHttpClientCreate = (client) {
return httpClient;
};
try {
final response = await dio.put('$apiURL/groups/$groupNo', data: {
final response = await dio.put('/groups/$groupNo', data: {
'login_uid': UserInfo.uid,
'name': groupName,
});
if (response.statusCode == HttpStatus.ok) {
return true;
} else {
return false;
}
return response.statusCode == HttpStatus.ok;
} catch (e) {
print('修改群名称失败$e');
print('Update group name error: $e');
return false;
}
}

View File

@ -81,6 +81,10 @@ class ListViewShowDataState extends State<ListViewShowData> {
for (var i = 0; i < msgList.length; i++) {
msgList[i].msg.unreadCount = 0;
}
//
_sortMessagesByTimestamp();
setState(() {});
});
WKIM.shared.conversationManager
@ -107,6 +111,9 @@ class ListViewShowDataState extends State<ListViewShowData> {
if (list.isNotEmpty) {
msgList.addAll(list);
}
_sortMessagesByTimestamp();
if (mounted) {
setState(() {});
}
@ -120,6 +127,10 @@ class ListViewShowDataState extends State<ListViewShowData> {
msgList[i].msg.setWkChannel(channel);
msgList[i].channelAvatar = "${HttpUtils.apiURL}/${channel.avatar}";
msgList[i].channelName = channel.channelName;
//
_sortMessagesByTimestamp();
setState(() {});
break;
}
@ -127,6 +138,11 @@ class ListViewShowDataState extends State<ListViewShowData> {
});
}
///
void _sortMessagesByTimestamp() {
msgList.sort((a, b) => b.msg.lastMsgTimestamp.compareTo(a.msg.lastMsgTimestamp));
}
void _getDataList() {
Future<List<WKUIConversationMsg>> list =
WKIM.shared.conversationManager.getAll();
@ -134,7 +150,8 @@ class ListViewShowDataState extends State<ListViewShowData> {
for (var i = 0; i < result.length; i++) {
msgList.add(UIConversation(result[i]));
}
_sortMessagesByTimestamp();
setState(() {});
});
}
@ -315,7 +332,7 @@ class ListViewShowDataState extends State<ListViewShowData> {
style: const TextStyle(color: Colors.black, fontSize: 16),
)
],
)
),
],
),
),

View File

@ -4,13 +4,17 @@ import '../db/channel.dart';
import '../entity/channel.dart';
class WKChannelManager {
WKChannelManager._privateConstructor();
WKChannelManager._privateConstructor() {
_refreshChannelMap = HashMap<String, Function(WKChannel)>();
_refreshChannelAvatarMap = HashMap<String, Function(WKChannel)>();
}
static final WKChannelManager _instance =
WKChannelManager._privateConstructor();
static WKChannelManager get shared => _instance;
final List<WKChannel> _list = [];
HashMap<String, Function(WKChannel)>? _refeshChannelMap;
late final HashMap<String, Function(WKChannel)> _refreshChannelMap;
late final HashMap<String, Function(WKChannel)> _refreshChannelAvatarMap;
Function(String channelID, int channelType, Function(WKChannel) back)?
_getChannelInfoBack;
@ -70,6 +74,21 @@ class WKChannelManager {
.searchWithChannelTypeAndFollow(searchKey, channelType, follow);
}
//
updateAvatarCacheKey(
String channelID, int channelType, String avatarCacheKey) async {
WKChannel? channel = await getChannel(channelID, channelType);
if (channel == null) {
return;
}
channel.avatarCacheKey = avatarCacheKey;
_updateChannel(channel);
ChannelDB.shared.saveOrUpdate(channel);
_refreshChannelAvatarMap.forEach((key, back) {
back(channel);
});
}
addOrUpdateChannel(WKChannel channel) {
_updateChannel(channel);
_setRefresh(channel);
@ -115,25 +134,28 @@ class WKChannelManager {
}
_setRefresh(WKChannel liMChannel) {
if (_refeshChannelMap != null) {
_refeshChannelMap!.forEach((key, back) {
back(liMChannel);
});
}
_refreshChannelMap.forEach((key, back) {
back(liMChannel);
});
}
addOnRefreshListener(String key, Function(WKChannel) back) {
_refeshChannelMap ??= HashMap();
_refeshChannelMap![key] = back;
_refreshChannelMap[key] = back;
}
removeOnRefreshListener(String key) {
if (_refeshChannelMap != null) {
_refeshChannelMap!.remove(key);
}
_refreshChannelMap.remove(key);
}
addOnGetChannelListener(Function(String, int, Function(WKChannel)) back) {
_getChannelInfoBack = back;
}
addOnRefreshAvatarListener(String key, Function(WKChannel) back) {
_refreshChannelAvatarMap[key] = back;
}
removeOnRefreshAvatarListener(String key) {
_refreshChannelAvatarMap.remove(key);
}
}

View File

@ -5,14 +5,18 @@ import 'package:wukongimfluttersdk/db/channel_member.dart';
import '../entity/channel_member.dart';
class WKChannelMemberManager {
WKChannelMemberManager._privateConstructor();
WKChannelMemberManager._privateConstructor() {
_newMembersBack = HashMap<String, Function(List<WKChannelMember>)>();
_refreshMembersBack = HashMap<String, Function(WKChannelMember, bool)>();
_deleteMembersBack = HashMap<String, Function(List<WKChannelMember>)>();
}
static final WKChannelMemberManager _instance =
WKChannelMemberManager._privateConstructor();
static WKChannelMemberManager get shared => _instance;
HashMap<String, Function(List<WKChannelMember>)>? _newMembersBack;
HashMap<String, Function(WKChannelMember, bool)>? _refreshMembersBack;
HashMap<String, Function(List<WKChannelMember>)>? _deleteMembersBack;
late final HashMap<String, Function(List<WKChannelMember>)> _newMembersBack;
late final HashMap<String, Function(WKChannelMember, bool)> _refreshMembersBack;
late final HashMap<String, Function(List<WKChannelMember>)> _deleteMembersBack;
Future<int> getMaxVersion(String channelID, int channelType) async {
return ChannelMemberDB.shared.getMaxVersion(channelID, channelType);
@ -29,8 +33,9 @@ class WKChannelMemberManager {
.queryWithUID(channelID, channelType, memberUID);
}
saveOrUpdateList(List<WKChannelMember> list) async {
Future<void> saveOrUpdateList(List<WKChannelMember> list) async {
if (list.isEmpty) return;
String channelID = list[0].channelID;
int channelType = list[0].channelType;
@ -40,7 +45,9 @@ class WKChannelMemberManager {
List<WKChannelMember> existList = [];
List<String> uidList = [];
for (WKChannelMember channelMember in list) {
// 200UID查询一次数据库
if (uidList.length == 200) {
List<WKChannelMember> tempList = await ChannelMemberDB.shared
.queryWithUIDs(
@ -54,16 +61,16 @@ class WKChannelMemberManager {
uidList.add(channelMember.memberUID);
}
// UID
if (uidList.isNotEmpty) {
List<WKChannelMember> tempList = await ChannelMemberDB.shared
.queryWithUIDs(channelID, channelType, uidList);
if (tempList.isNotEmpty) {
existList.addAll(tempList);
}
uidList.clear();
}
//
for (WKChannelMember channelMember in list) {
bool isNewMember = true;
for (int i = 0, size = existList.length; i < size; i++) {
@ -86,77 +93,62 @@ class WKChannelMemberManager {
}
}
//
ChannelMemberDB.shared.insertList(list);
//
await ChannelMemberDB.shared.insertList(list);
//
if (addList.isNotEmpty) {
setOnNewChannelMember(addList);
_notifyNewChannelMembers(addList);
}
if (deleteList.isNotEmpty) {
setDeleteChannelMember(deleteList);
_notifyDeleteChannelMembers(deleteList);
}
if (updateList.isNotEmpty) {
for (int i = 0, size = updateList.length; i < size; i++) {
setRefreshChannelMember(updateList[i], i == updateList.length - 1);
_notifyRefreshChannelMember(updateList[i], i == updateList.length - 1);
}
}
}
setRefreshChannelMember(WKChannelMember member, bool isEnd) {
if (_refreshMembersBack != null) {
_refreshMembersBack!.forEach((key, back) {
back(member, isEnd);
});
}
void _notifyRefreshChannelMember(WKChannelMember member, bool isEnd) {
_refreshMembersBack.forEach((key, back) {
back(member, isEnd);
});
}
addOnRefreshMemberListener(String key, Function(WKChannelMember, bool) back) {
_refreshMembersBack ??= HashMap();
_refreshMembersBack![key] = back;
void addOnRefreshMemberListener(String key, Function(WKChannelMember, bool) back) {
_refreshMembersBack[key] = back;
}
removeRefreshMemberListener(String key) {
if (_refreshMembersBack != null) {
_refreshMembersBack!.remove(key);
}
void removeRefreshMemberListener(String key) {
_refreshMembersBack.remove(key);
}
setDeleteChannelMember(List<WKChannelMember> list) {
if (_deleteMembersBack != null) {
_deleteMembersBack!.forEach((key, back) {
back(list);
});
}
void _notifyDeleteChannelMembers(List<WKChannelMember> list) {
_deleteMembersBack.forEach((key, back) {
back(list);
});
}
addOnDeleteMemberListener(String key, Function(List<WKChannelMember>) back) {
_deleteMembersBack ??= HashMap();
_deleteMembersBack![key] = back;
void addOnDeleteMemberListener(String key, Function(List<WKChannelMember>) back) {
_deleteMembersBack[key] = back;
}
removeDeleteMemberListener(String key) {
if (_deleteMembersBack != null) {
_deleteMembersBack!.remove(key);
}
void removeDeleteMemberListener(String key) {
_deleteMembersBack.remove(key);
}
setOnNewChannelMember(List<WKChannelMember> list) {
if (_newMembersBack != null) {
_newMembersBack!.forEach((key, back) {
back(list);
});
}
void _notifyNewChannelMembers(List<WKChannelMember> list) {
_newMembersBack.forEach((key, back) {
back(list);
});
}
addOnNewMemberListener(String key, Function(List<WKChannelMember>) back) {
_newMembersBack ??= HashMap();
_newMembersBack![key] = back;
void addOnNewMemberListener(String key, Function(List<WKChannelMember>) back) {
_newMembersBack[key] = back;
}
removeNewMemberListener(String key) {
if (_newMembersBack != null) {
_newMembersBack!.remove(key);
}
void removeNewMemberListener(String key) {
_newMembersBack.remove(key);
}
}

View File

@ -4,43 +4,55 @@ import 'package:wukongimfluttersdk/db/const.dart';
import '../entity/cmd.dart';
///
class WKCMDManager {
WKCMDManager._privateConstructor();
WKCMDManager._privateConstructor() {
_cmdListeners = HashMap<String, Function(WKCMD)>();
}
static final WKCMDManager _instance = WKCMDManager._privateConstructor();
static WKCMDManager get shared => _instance;
HashMap<String, Function(WKCMD)>? _cmdback;
handleCMD(dynamic json) {
///
late final HashMap<String, Function(WKCMD)> _cmdListeners;
///
void handleCMD(dynamic json) {
//
String cmd = WKDBConst.readString(json, 'cmd');
dynamic param = json['param'];
//
if (param != null && param is Map) {
if (!param.containsKey('channel_id')) {
param['channel_id'] = json['channel_id'];
param['channel_type'] = json['channel_type'];
}
}
//
WKCMD wkcmd = WKCMD();
wkcmd.cmd = cmd;
wkcmd.param = param;
pushCMD(wkcmd);
_notifyListeners(wkcmd);
}
pushCMD(WKCMD wkcmd) {
if (_cmdback != null) {
_cmdback!.forEach((key, back) {
back(wkcmd);
});
}
///
void _notifyListeners(WKCMD wkcmd) {
_cmdListeners.forEach((key, listener) {
listener(wkcmd);
});
}
addOnCmdListener(String key, Function(WKCMD) back) {
_cmdback ??= HashMap();
_cmdback![key] = back;
///
/// [key]
/// [listener]
void addOnCmdListener(String key, Function(WKCMD) listener) {
_cmdListeners[key] = listener;
}
removeCmdListener(String key) {
if (_cmdback != null) {
_cmdback!.remove(key);
}
///
/// [key]
void removeCmdListener(String key) {
_cmdListeners.remove(key);
}
}

View File

@ -315,6 +315,7 @@ class WKConnectionManager {
}
} else if (packet.header.packetType == PacketType.sendack) {
var sendack = packet as SendAckPacket;
Logs.debug('发送结果:${sendack.reasonCode}');
WKIM.shared.messageManager.updateSendResult(sendack.messageID,
sendack.clientSeq, sendack.messageSeq, sendack.reasonCode);
if (_sendingMsgMap.containsKey(sendack.clientSeq)) {

View File

@ -9,37 +9,54 @@ import '../db/conversation.dart';
import '../entity/conversation.dart';
import '../type/const.dart';
///
class WKConversationManager {
WKConversationManager._privateConstructor();
WKConversationManager._privateConstructor() {
_refreshMsgMap = HashMap<String, Function(WKUIConversationMsg, bool)>();
_refreshMsgListMap = HashMap<String, Function(List<WKUIConversationMsg>)>();
_deleteMsgMap = HashMap<String, Function(String, int)>();
_clearAllRedDotMap = HashMap<String, Function()>();
}
static final WKConversationManager _instance =
WKConversationManager._privateConstructor();
static WKConversationManager get shared => _instance;
HashMap<String, Function(WKUIConversationMsg, bool)>? _refeshMsgMap;
HashMap<String, Function(List<WKUIConversationMsg>)>? _refreshMsgListMap;
HashMap<String, Function(String, int)>? _deleteMsgMap;
HashMap<String, Function()>? _clearAllRedDotMap;
///
late final HashMap<String, Function(WKUIConversationMsg, bool)> _refreshMsgMap;
///
late final HashMap<String, Function(List<WKUIConversationMsg>)> _refreshMsgListMap;
///
late final HashMap<String, Function(String, int)> _deleteMsgMap;
///
late final HashMap<String, Function()> _clearAllRedDotMap;
///
Function(String lastSsgSeqs, int msgCount, int version,
Function(WKSyncConversation))? _syncConersationBack;
Function(WKSyncConversation))? _syncConversationBack;
///
Future<List<WKUIConversationMsg>> getAll() async {
return await ConversationDB.shared.queryAll();
}
///
Future<bool> deleteMsg(String channelID, int channelType) async {
bool result = await ConversationDB.shared.delete(channelID, channelType);
if (result) {
_setDeleteMsg(channelID, channelType);
_notifyDeleteMsg(channelID, channelType);
}
return result;
}
///
Future<WKUIConversationMsg?> saveWithLiMMsg(WKMsg msg, int redDot) async {
WKConversationMsg wkConversationMsg = WKConversationMsg();
if (msg.channelType == WKChannelType.communityTopic &&
msg.channelID != '') {
msg.channelID.isNotEmpty) {
if (msg.channelID.contains("@")) {
var str = msg.channelID.split("@");
wkConversationMsg.parentChannelID = str[0];
@ -57,14 +74,17 @@ class WKConversationManager {
return uiMsg;
}
///
Future<int> getAllUnreadCount() async {
return ConversationDB.shared.queryAllUnreadCount();
}
///
Future<int> getExtraMaxVersion() async {
return ConversationDB.shared.queryExtraMaxVersion();
}
///
Future<WKUIConversationMsg?> getWithChannel(
String channelID, int channelType) async {
var msg = await ConversationDB.shared
@ -75,165 +95,172 @@ class WKConversationManager {
return null;
}
clearAll() {
ConversationDB.shared.clearAll();
///
Future<void> clearAll() async {
await ConversationDB.shared.clearAll();
}
clearAllRedDot() async {
///
Future<void> clearAllRedDot() async {
int row = await ConversationDB.shared.clearAllRedDot();
if (row > 0) {
_setClearAllRedDot();
_notifyClearAllRedDot();
}
}
updateRedDot(String channelID, int channelType, int redDot) async {
///
Future<void> updateRedDot(String channelID, int channelType, int redDot) async {
var map = <String, Object>{};
map['unread_count'] = redDot;
var result = await ConversationDB.shared
.updateWithField(map, channelID, channelType);
if (result > 0) {
_refreshMsg(channelID, channelType);
await _refreshChannelMsg(channelID, channelType);
}
}
_refreshMsg(String channelID, int channelType) async {
///
Future<void> _refreshChannelMsg(String channelID, int channelType) async {
var msg = await ConversationDB.shared
.queryMsgByMsgChannelId(channelID, channelType);
if (msg != null) {
var uiMsg = ConversationDB.shared.getUIMsg(msg);
List<WKUIConversationMsg> uiMsgs = [];
uiMsgs.add(uiMsg);
List<WKUIConversationMsg> uiMsgs = [uiMsg];
setRefreshUIMsgs(uiMsgs);
}
}
addOnClearAllRedDotListener(String key, Function() back) {
_clearAllRedDotMap ??= HashMap();
_clearAllRedDotMap![key] = back;
///
void addOnClearAllRedDotListener(String key, Function() listener) {
_clearAllRedDotMap[key] = listener;
}
removeClearAllRedDotListener(String key) {
if (_clearAllRedDotMap != null) {
_clearAllRedDotMap!.remove(key);
}
///
void removeClearAllRedDotListener(String key) {
_clearAllRedDotMap.remove(key);
}
_setClearAllRedDot() {
if (_clearAllRedDotMap != null) {
_clearAllRedDotMap!.forEach((key, back) {
back();
});
}
///
void _notifyClearAllRedDot() {
_clearAllRedDotMap.forEach((_, listener) {
listener();
});
}
addOnDeleteMsgListener(String key, Function(String, int) back) {
_deleteMsgMap ??= HashMap();
_deleteMsgMap![key] = back;
///
void addOnDeleteMsgListener(String key, Function(String, int) listener) {
_deleteMsgMap[key] = listener;
}
removeDeleteMsgListener(String key) {
if (_deleteMsgMap != null) {
_deleteMsgMap!.remove(key);
}
///
void removeDeleteMsgListener(String key) {
_deleteMsgMap.remove(key);
}
_setDeleteMsg(String channelID, int channelType) {
if (_deleteMsgMap != null) {
_deleteMsgMap!.forEach((key, back) {
back(channelID, channelType);
});
}
///
void _notifyDeleteMsg(String channelID, int channelType) {
_deleteMsgMap.forEach((_, listener) {
listener(channelID, channelType);
});
}
_setRefreshMsg(WKUIConversationMsg msg, bool isEnd) {
if (_refeshMsgMap != null) {
_refeshMsgMap!.forEach((key, back) {
back(msg, isEnd);
});
}
///
void _notifyRefreshMsg(WKUIConversationMsg msg, bool isEnd) {
_refreshMsgMap.forEach((_, listener) {
listener(msg, isEnd);
});
}
@Deprecated("Please replace with `addOnRefreshMsgListListener` method")
addOnRefreshMsgListener(
String key, Function(WKUIConversationMsg, bool) back) {
_refeshMsgMap ??= HashMap();
_refeshMsgMap![key] = back;
///
@Deprecated("请使用 addOnRefreshMsgListListener 方法替代")
void addOnRefreshMsgListener(
String key, Function(WKUIConversationMsg, bool) listener) {
_refreshMsgMap[key] = listener;
}
removeOnRefreshMsg(String key) {
if (_refeshMsgMap != null) {
_refeshMsgMap!.remove(key);
}
///
void removeOnRefreshMsg(String key) {
_refreshMsgMap.remove(key);
}
setRefreshUIMsgs(List<WKUIConversationMsg> msgs) {
_setRefreshMsgList(msgs);
/// UI列表
void setRefreshUIMsgs(List<WKUIConversationMsg> msgs) {
_notifyRefreshMsgList(msgs);
for (int i = 0, size = msgs.length; i < size; i++) {
_setRefreshMsg(msgs[i], i == msgs.length - 1);
_notifyRefreshMsg(msgs[i], i == msgs.length - 1);
}
}
_setRefreshMsgList(List<WKUIConversationMsg> msgs) {
if (_refreshMsgListMap != null) {
_refreshMsgListMap!.forEach((key, back) {
back(msgs);
});
}
///
void _notifyRefreshMsgList(List<WKUIConversationMsg> msgs) {
_refreshMsgListMap.forEach((_, listener) {
listener(msgs);
});
}
addOnRefreshMsgListListener(
String key, Function(List<WKUIConversationMsg>) back) {
_refreshMsgListMap ??= HashMap();
_refreshMsgListMap![key] = back;
///
void addOnRefreshMsgListListener(
String key, Function(List<WKUIConversationMsg>) listener) {
_refreshMsgListMap[key] = listener;
}
removeOnRefreshMsgListListener(String key) {
if (_refreshMsgListMap != null) {
_refreshMsgListMap!.remove(key);
}
///
void removeOnRefreshMsgListListener(String key) {
_refreshMsgListMap.remove(key);
}
addOnSyncConversationListener(
///
void addOnSyncConversationListener(
Function(String lastSsgSeqs, int msgCount, int version,
Function(WKSyncConversation))
back) {
_syncConersationBack = back;
listener) {
_syncConversationBack = listener;
}
setSyncConversation(Function() back) async {
///
Future<void> setSyncConversation(Function() callback) async {
WKIM.shared.connectionManager.setConnectionStatus(WKConnectStatus.syncMsg);
if (_syncConersationBack != null) {
if (_syncConversationBack != null) {
int version = await ConversationDB.shared.getMaxVersion();
String lastMsgSeqStr = await ConversationDB.shared.getLastMsgSeqs();
_syncConersationBack!(lastMsgSeqStr, 200, version, (msgs) {
_saveSyncCoversation(msgs);
back();
_syncConversationBack!(lastMsgSeqStr, 200, version, (msgs) {
_saveSyncConversation(msgs);
callback();
});
}
}
_saveSyncCoversation(WKSyncConversation? syncChat) {
///
void _saveSyncConversation(WKSyncConversation? syncChat) {
if (syncChat == null ||
syncChat.conversations == null ||
syncChat.conversations!.isEmpty) {
return;
}
//
List<WKConversationMsg> conversationMsgList = [];
List<WKMsg> msgList = [];
List<WKMsgReaction> msgReactionList = [];
List<WKMsgExtra> msgExtraList = [];
List<WKUIConversationMsg> uiMsgList = [];
//
if (syncChat.conversations != null && syncChat.conversations!.isNotEmpty) {
for (int i = 0, size = syncChat.conversations!.length; i < size; i++) {
WKConversationMsg conversationMsg = WKConversationMsg();
int channelType = syncChat.conversations![i].channelType;
String channelID = syncChat.conversations![i].channelID;
//
if (channelType == WKChannelType.communityTopic) {
var str = channelID.split("@");
conversationMsg.parentChannelID = str[0];
conversationMsg.parentChannelType = WKChannelType.community;
}
//
conversationMsg.channelID = syncChat.conversations![i].channelID;
conversationMsg.channelType = syncChat.conversations![i].channelType;
conversationMsg.lastMsgSeq = syncChat.conversations![i].lastMsgSeq;
@ -242,57 +269,74 @@ class WKConversationManager {
conversationMsg.lastMsgTimestamp = syncChat.conversations![i].timestamp;
conversationMsg.unreadCount = syncChat.conversations![i].unread;
conversationMsg.version = syncChat.conversations![i].version;
WKUIConversationMsg uiMsg =
ConversationDB.shared.getUIMsg(conversationMsg);
//
//
if (syncChat.conversations![i].recents != null &&
syncChat.conversations![i].recents!.isNotEmpty) {
for (WKSyncMsg wkSyncRecent in syncChat.conversations![i].recents!) {
WKMsg msg = wkSyncRecent.getWKMsg();
//
if (msg.reactionList != null && msg.reactionList!.isNotEmpty) {
msgReactionList.addAll(msg.reactionList!);
}
//fromUID
// fromUID
if (conversationMsg.lastClientMsgNO == msg.clientMsgNO) {
conversationMsg.isDeleted = msg.isDeleted;
uiMsg.isDeleted = conversationMsg.isDeleted;
uiMsg.setWkMsg(msg);
}
//
if (wkSyncRecent.messageExtra != null) {
WKMsgExtra extra = WKIM.shared.messageManager
.wkSyncExtraMsg2WKMsgExtra(msg.channelID, msg.channelType,
wkSyncRecent.messageExtra!);
msgExtraList.add(extra);
}
msgList.add(msg);
}
}
conversationMsgList.add(conversationMsg);
uiMsgList.add(uiMsg);
}
}
//
if (msgExtraList.isNotEmpty) {
MessageDB.shared.insertMsgExtras(msgExtraList);
// MessageDB.shared.insertOrUpdateMsgExtras(msgExtraList);
}
if (msgList.isNotEmpty) {
MessageDB.shared.insertMsgList(msgList);
}
if (conversationMsgList.isNotEmpty) {
// ConversationDB.shared.insertMsgList(conversationMsgList);
ConversationDB.shared.insetMsgs(conversationMsgList);
}
if (msgReactionList.isNotEmpty) {
ReactionDB.shared.insertOrUpdateReactionList(msgReactionList);
}
// 20
if (msgList.isNotEmpty && msgList.length < 20) {
msgList.sort((a, b) => a.messageSeq.compareTo(b.messageSeq));
WKIM.shared.messageManager.pushNewMsg(msgList);
}
// UI
if (uiMsgList.isNotEmpty) {
setRefreshUIMsgs(uiMsgList);
}
//
if (syncChat.cmds != null && syncChat.cmds!.isNotEmpty) {
for (int i = 0, size = syncChat.cmds!.length; i < size; i++) {
dynamic json = <String, dynamic>{};

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.6.3
version: 1.6.4
homepage: https://github.com/WuKongIM/WuKongIMFlutterSDK
environment: