diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ca0380..b8a0c66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -127,4 +127,6 @@ ### 1.6.3 * fix: 优化在未收到服务端心跳消息时主动断开重连 ### 1.6.4 - * fix: 新增监听头像改变事件 \ No newline at end of file + * fix: 新增监听头像改变事件### +### 1.6.5 + * fix: 修改频道和频道成员及提醒项扩展字段保存错误问题 \ No newline at end of file diff --git a/example/lib/http.dart b/example/lib/http.dart index deeec3c..be3d4c5 100644 --- a/example/lib/http.dart +++ b/example/lib/http.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:io'; import 'package:dio/dio.dart'; @@ -13,27 +14,28 @@ 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 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 - + (X509Certificate cert, String host, int port) => + true; // Trust all certificates + _dio = Dio(BaseOptions( baseUrl: apiURL, // 允许所有状态码,避免自动抛出异常 validateStatus: (status) => true, )); - (_dio!.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = + (_dio!.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) => httpClient; } return _dio!; } - + static String getAvatarUrl(String uid) { return "$apiURL/users/$uid/avatar"; } @@ -50,7 +52,7 @@ class HttpUtils { 'device_flag': 0, 'device_level': 1 }); - + if (response.statusCode == HttpStatus.ok) { UserInfo.name = response.data['name']; } @@ -73,8 +75,8 @@ class HttpUtils { return ''; } - static Future syncConversation(String lastSsgSeqs, int msgCount, int version, - Function(WKSyncConversation) back) async { + static Future syncConversation(String lastSsgSeqs, int msgCount, + int version, Function(WKSyncConversation) back) async { try { // 检查是否已登录 if (UserInfo.uid.isEmpty) { @@ -90,7 +92,7 @@ class HttpUtils { "msg_count": msgCount, "device_uuid": UserInfo.uid, }); - + WKSyncConversation conversation = WKSyncConversation(); conversation.conversations = []; @@ -149,11 +151,11 @@ class HttpUtils { "channel_id": channelID, "channel_type": channelType, "start_message_seq": startMsgSeq, - "end_message_seq": endMsgSeq, + "end_message_seq": endMsgSeq, "limit": limit, "pull_mode": pullMode }); - + if (response.statusCode == HttpStatus.ok) { var data = response.data; WKSyncChannelMsg msg = WKSyncChannelMsg(); @@ -232,7 +234,7 @@ class HttpUtils { } final response = await dio.get('/groups/$groupId'); - + if (response.statusCode == HttpStatus.ok) { var json = response.data; var channel = WKChannel(groupId, WKChannelType.group); @@ -266,12 +268,15 @@ class HttpUtils { } 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']; + // 测试扩展数据 + // channel.remoteExtraMap = jsonEncode({"name": "xx", "sex": 1}); + // channel.localExtra = jsonEncode({"name1": "xx", "sex1": 1, "t": "2"}); WKIM.shared.channelManager.addOrUpdateChannel(channel); } else { print('获取用户信息失败: HTTP ${response.statusCode}'); @@ -285,8 +290,8 @@ class HttpUtils { } } - static Future revokeMsg(String clientMsgNo, String channelId, int channelType, - int msgSeq, String msgId) async { + static Future revokeMsg(String clientMsgNo, String channelId, + int channelType, int msgSeq, String msgId) async { try { // 检查必要参数 if (clientMsgNo.isEmpty || channelId.isEmpty || msgId.isEmpty) { @@ -308,7 +313,7 @@ class HttpUtils { 'message_seq': msgSeq, 'message_id': msgId, }); - + if (response.statusCode == HttpStatus.ok) { print('消息撤回成功'); return true; @@ -325,8 +330,8 @@ class HttpUtils { } } - static Future deleteMsg(String clientMsgNo, String channelId, int channelType, - int msgSeq, String msgId) async { + static Future deleteMsg(String clientMsgNo, String channelId, + int channelType, int msgSeq, String msgId) async { try { // 检查必要参数 if (clientMsgNo.isEmpty || channelId.isEmpty || msgId.isEmpty) { @@ -347,7 +352,7 @@ class HttpUtils { 'message_seq': msgSeq, 'message_id': msgId, }); - + if (response.statusCode == HttpStatus.ok) { WKIM.shared.messageManager.deleteWithClientMsgNo(clientMsgNo); print('消息删除成功'); @@ -365,7 +370,8 @@ class HttpUtils { } } - static Future syncMsgExtra(String channelId, int channelType, int version) async { + static Future syncMsgExtra( + String channelId, int channelType, int version) async { try { final response = await dio.post('/message/extra/sync', data: { 'login_uid': UserInfo.uid, @@ -375,7 +381,7 @@ class HttpUtils { 'limit': 100, 'extra_version': version, }); - + if (response.statusCode == HttpStatus.ok) { var arrJson = response.data; if (arrJson != null && arrJson.length > 0) { @@ -409,7 +415,7 @@ class HttpUtils { 'channel_type': channelType, 'unread': 0, }); - + if (response.statusCode == HttpStatus.ok) { print('Unread count cleared successfully'); } @@ -423,14 +429,14 @@ class HttpUtils { try { int maxSeq = await WKIM.shared.messageManager .getMaxMessageSeq(channelId, channelType); - + 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); } diff --git a/example/lib/ui_chat.dart b/example/lib/ui_chat.dart index 4dc7e9b..3914839 100644 --- a/example/lib/ui_chat.dart +++ b/example/lib/ui_chat.dart @@ -51,6 +51,8 @@ class ChatListDataState extends State { .then((channel) { WKIM.shared.channelManager.fetchChannelInfo(channelID, channelType); title = '${channel?.channelName}'; + print("扩展:${channel?.localExtra}"); + print("扩展1:${channel?.remoteExtraMap}"); }); } List msgList = []; @@ -506,7 +508,7 @@ Widget _buildItem(UIMsg uiMsg, BuildContext context) { Navigator.push( context, MaterialPageRoute( - builder: (context) => ChatPage(), + builder: (context) => const ChatPage(), settings: RouteSettings( arguments: ChatChannel( uiMsg.wkMsg.channelID, diff --git a/example/lib/ui_conversation.dart b/example/lib/ui_conversation.dart index c203ba9..2e1af4c 100644 --- a/example/lib/ui_conversation.dart +++ b/example/lib/ui_conversation.dart @@ -1,6 +1,9 @@ +import 'dart:convert'; + import 'package:example/const.dart'; import 'package:example/http.dart'; import 'package:flutter/material.dart'; +import 'package:wukongimfluttersdk/db/const.dart'; import 'package:wukongimfluttersdk/entity/conversation.dart'; import 'package:wukongimfluttersdk/entity/reminder.dart'; import 'package:wukongimfluttersdk/type/const.dart'; @@ -81,10 +84,10 @@ class ListViewShowDataState extends State { for (var i = 0; i < msgList.length; i++) { msgList[i].msg.unreadCount = 0; } - + // 清除红点后也重新排序,保持列表排序的一致性 _sortMessagesByTimestamp(); - + setState(() {}); }); WKIM.shared.conversationManager @@ -111,9 +114,9 @@ class ListViewShowDataState extends State { if (list.isNotEmpty) { msgList.addAll(list); } - + _sortMessagesByTimestamp(); - + if (mounted) { setState(() {}); } @@ -127,10 +130,10 @@ class ListViewShowDataState extends State { msgList[i].msg.setWkChannel(channel); msgList[i].channelAvatar = "${HttpUtils.apiURL}/${channel.avatar}"; msgList[i].channelName = channel.channelName; - + // 刷新频道信息后重新排序(虽然时间戳没变,但保持一致性) _sortMessagesByTimestamp(); - + setState(() {}); break; } @@ -140,7 +143,8 @@ class ListViewShowDataState extends State { /// 对会话列表按时间戳排序,最新的会话排在前面 void _sortMessagesByTimestamp() { - msgList.sort((a, b) => b.msg.lastMsgTimestamp.compareTo(a.msg.lastMsgTimestamp)); + msgList.sort( + (a, b) => b.msg.lastMsgTimestamp.compareTo(a.msg.lastMsgTimestamp)); } void _getDataList() { @@ -150,7 +154,7 @@ class ListViewShowDataState extends State { for (var i = 0; i < result.length; i++) { msgList.add(UIConversation(result[i])); } - + _sortMessagesByTimestamp(); setState(() {}); }); @@ -183,8 +187,16 @@ class ListViewShowDataState extends State { if (uiConversation.reminders![i].type == WKMentionType.wkReminderTypeMentionMe && uiConversation.reminders![i].done == 0) { - content = uiConversation.reminders![i].data; - content = '[有人@你]'; + var d = uiConversation.reminders![i].data; + if (d is Map) { + content = d['type']; + } else if (d is String && WKDBConst.isJsonString(d)) { + var obj = jsonDecode(d); + content = obj['type']; + } else { + content = d; + } + // content = uiConversation.reminders![i].data; break; } } @@ -441,13 +453,16 @@ class ListViewShowDataState extends State { WKReminder reminder = WKReminder(); reminder.needUpload = 0; reminder.type = WKMentionType.wkReminderTypeMentionMe; - reminder.data = '[有人@你]'; + // reminder.data = '[有人@你]'; reminder.done = 0; reminder.reminderID = 11; reminder.version = 1; reminder.publisher = "uid_1"; reminder.channelID = uiMsg.msg.channelID; reminder.channelType = uiMsg.msg.channelType; + // 这两种都可以 + reminder.data = "[有人@你]"; + // reminder.data = jsonEncode({"type": "[有人@你]"}); list.add(reminder); WKIM.shared.reminderManager.saveOrUpdateReminders(list); })); diff --git a/lib/db/channel.dart b/lib/db/channel.dart index fd5e1ca..ae9fee2 100644 --- a/lib/db/channel.dart +++ b/lib/db/channel.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:collection'; -import 'dart:convert'; import 'package:sqflite/sqflite.dart'; import 'package:wukongimfluttersdk/db/const.dart'; @@ -216,12 +215,8 @@ class ChannelDB { data['device_flag'] = channel.deviceFlag; data['parent_channel_id'] = channel.parentChannelID; data['parent_channel_type'] = channel.parentChannelType; - if (channel.remoteExtraMap != null) { - data['remote_extra'] = jsonEncode(channel.remoteExtraMap); - } - if (channel.localExtra != null) { - data['extra'] = jsonEncode(channel.localExtra); - } + data['remote_extra'] = channel.remoteExtraMap?.toString() ?? ""; + data['extra'] = channel.localExtra?.toString() ?? ""; return data; } } diff --git a/lib/db/channel_member.dart b/lib/db/channel_member.dart index cc80516..c577427 100644 --- a/lib/db/channel_member.dart +++ b/lib/db/channel_member.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:sqflite/sqflite.dart'; import '../entity/channel_member.dart'; @@ -152,9 +150,7 @@ class ChannelMemberDB { map['forbidden_expiration_time'] = member.forbiddenExpirationTime; map['created_at'] = member.createdAt; map['updated_at'] = member.updatedAt; - if (member.extraMap != null) { - map['extra'] = jsonEncode(member.extraMap); - } + map['extra'] = member.extraMap?.toString() ?? ""; return map; } } diff --git a/lib/db/reminder.dart b/lib/db/reminder.dart index a597c62..795f6ef 100644 --- a/lib/db/reminder.dart +++ b/lib/db/reminder.dart @@ -1,5 +1,4 @@ import 'dart:collection'; -import 'dart:convert'; import 'package:sqflite/sqflite.dart'; import 'package:wukongimfluttersdk/db/const.dart'; @@ -180,12 +179,13 @@ class ReminderDB { map['done'] = reminder.done; map['need_upload'] = reminder.needUpload; map['publisher'] = reminder.publisher; - if (reminder.data != null) { - map['data'] = jsonEncode(reminder.data); + // 可以有错误数据 + var len = reminder.data?.toString().length ?? 0; + if (len < 1000000) { + map['data'] = reminder.data?.toString() ?? ""; } else { map['data'] = ''; } - return map; } } diff --git a/pubspec.yaml b/pubspec.yaml index d793a94..467b1dd 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.6.4 +version: 1.6.5 homepage: https://github.com/WuKongIM/WuKongIMFlutterSDK environment: diff --git a/test/json_encode_test.dart b/test/json_encode_test.dart new file mode 100644 index 0000000..394c572 --- /dev/null +++ b/test/json_encode_test.dart @@ -0,0 +1,43 @@ +import 'dart:convert'; + +void main() { + // 测试 dynamic 类型的空数据 + dynamic emptyList = []; + print('Empty list: ${jsonEncode(emptyList)}'); + + dynamic emptyMap = {}; + print('Empty map: ${jsonEncode(emptyMap)}'); + + // 测试其他类型的空数据 + dynamic emptyInt = 0; + print('Empty int (0): ${jsonEncode(emptyInt)}'); + + dynamic emptyDouble = 0.0; + print('Empty double (0.0): ${jsonEncode(emptyDouble)}'); + + dynamic emptyBool = false; + print('Empty bool (false): ${jsonEncode(emptyBool)}'); + + // 测试 null + print('Null value: ${jsonEncode(null)}'); + + // 测试空字符串 + String emptyString = ''; + print('Empty string: ${jsonEncode(emptyString)}'); + + // 测试普通字符串 + String normalString = 'Hello World'; + print('Normal string: ${jsonEncode(normalString)}'); + + // 测试包含特殊字符的字符串 + String specialString = 'Hello "World" with \'quotes\' and \\backslash\\'; + print('Special string: ${jsonEncode(specialString)}'); + + // 测试包含换行符的字符串 + String multilineString = 'Line 1\nLine 2\nLine 3'; + print('Multiline string: ${jsonEncode(multilineString)}'); + + // 测试包含Unicode字符的字符串 + String unicodeString = '你好,世界!'; + print('Unicode string: ${jsonEncode(unicodeString)}'); +} \ No newline at end of file