Browse Source

发消息修改

wr_202303
zsw 4 months ago
parent
commit
c9e9335f28
  1. 2
      lib/constant.dart
  2. 16
      lib/im/SocketClient.dart
  3. 128
      lib/im/chat_details_page.dart
  4. 25
      lib/im/database/hx_database.dart
  5. 138
      lib/im/im_view/im_page.dart

2
lib/constant.dart

@ -41,6 +41,8 @@ T max<T>(Iterable<T> list, int Function(T) key) {
return tt;
}
extension ListExtension<S, T> on Iterable<T> {
Map<S, List<T>> lGroupBy(S Function(T) key) {

16
lib/im/SocketClient.dart

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'dart:core';
import 'dart:core';
@ -37,16 +38,29 @@ class SocketClient {
}, onError: (Object error, StackTrace stackTrace) {
debugPrint("socket-listen-error: $error, stackTrace: $stackTrace");
}, onDone: () {
debugPrint("socket-listen-down: down");
});
authRequest(shared.getString("token"));
}).catchError((error) {
debugPrint("socket-connect-error: $error");
reconnectTime = 1500;
_socket = null;
reconnect();
});
}
heartbeat() {
Timer.periodic(const Duration(milliseconds: 3000), (timer) {
Uint8List data = utf8.encode(jsonEncode({"heartbeat": DateTime.now().millisecondsSinceEpoch}));
MsgData msgData = MsgData(to: "0", from: userId, type: MsgType.TEXT, data: data);
final proto2 = Proto(5, 1, msgData.writeToBuffer());
_socket.add(proto2.toBytes());
});
}
int reconnectTime = 1500;
reconnect() {
@ -68,6 +82,7 @@ class SocketClient {
dispose() {
if (_socket != null) {
_socket = null;
_socket.close();
}
}
@ -109,6 +124,7 @@ class SocketClient {
checkSocket() {
if (_socket == null) {
reconnectTime = 1500;
reconnect();
return false;
}

128
lib/im/chat_details_page.dart

@ -67,11 +67,15 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
List<Message> messages = [];
loadMessageList() async {
ImUser imUser = await hxDatabase.queryImUserById(_toUser.mid);
if (imUser == null) {
await hxDatabase.insertOrUpdateImUser(_toUser.toJson());
}
selfUserId = (await SharedPreferences.getInstance()).getString("userId");
// unread msg 2 read state
await hxDatabase.readMessage(selfUserId, _toUser.mid);
messages = await hxDatabase.queryUList(_toUser.mid);
messages = await hxDatabase.queryUList(_toUser.mid, pageSize: 100);
socketClient.addCallback(_toUser.mid, (Message message) {
messages.add(message);
@ -98,7 +102,6 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
commentFocus.addListener(_focusNodeListener);
loadMessageList();
}
void jumpToBottom() {
@ -344,19 +347,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
child: Column(
children: [
Text(
"3月08日 上午 12:10",
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),
child: Text(
"在对方未回复或关注你之前,你只能发送一条信息",
"${DateTime.fromMillisecondsSinceEpoch(int.parse(message.time))}",
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFFA29E9E),
@ -364,11 +355,23 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
fontWeight: MyFontWeight.regular,
),
),
),
if (messages.indexOf(message) == (messages.length - 1))
SizedBox(
height: 16.h,
),
// if (messages.indexOf(message) == (messages.length - 1))
// Padding(
// padding: EdgeInsets.only(top: 10.h, bottom: 24.h),
// child: Text(
// "在对方未回复或关注你之前,你只能发送一条信息",
// textAlign: TextAlign.center,
// style: TextStyle(
// color: Color(0xFFA29E9E),
// fontSize: 10.sp,
// fontWeight: MyFontWeight.regular,
// ),
// ),
// ),
// if (messages.indexOf(message) == (messages.length - 1))
// SizedBox(
// height: 16.h,
// ),
if (copyIndex == 1)
Stack(
alignment: Alignment.bottomCenter,
@ -382,7 +385,8 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
borderRadius: BorderRadius.circular(6),
),
padding: EdgeInsets.symmetric(
horizontal: 32.w, vertical: 7.5.h,
horizontal: 32.w,
vertical: 7.5.h,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -454,7 +458,11 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
),
/// not self
if (!isSelf && !isText)
if (!isSelf && isText)
SizedBox(
height: 10.h,
),
if (!isSelf && isText)
Padding(
padding: EdgeInsets.only(left: 17.w, right: 39.w),
child: Row(
@ -472,6 +480,8 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
width: 12.w,
),
Expanded(
child: Container(
alignment: Alignment.centerLeft,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
@ -482,11 +492,13 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
offset: Offset(0, 4),
blurRadius: 4,
spreadRadius: 0,
)
),
],
),
padding: EdgeInsets.symmetric(
vertical: 8.h, horizontal: 12.w),
vertical: 8.h,
horizontal: 12.w,
),
child: GestureDetector(
onLongPress: () {
setState(() {
@ -503,14 +515,13 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
fontWeight: MyFontWeight.medium,
),
),
))),
],
),
),),
),
],
),
if (!isSelf && isText)
SizedBox(
height: 40.h,
),
if (copyIndex == 1)
Stack(
alignment: Alignment.bottomCenter,
@ -595,6 +606,10 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
),
/// self
if (isSelf && isText)
SizedBox(
height: 10.h,
),
if (isSelf && isText)
Padding(
padding: EdgeInsets.only(left: 36.w, right: 16.w),
@ -630,9 +645,19 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: Color(0xFF32A060),
boxShadow: [
BoxShadow(
color: Color(0xFFA8A3A3).withAlpha(12),
offset: Offset(0, 4),
blurRadius: 4,
spreadRadius: 0,
),
],
),
padding: EdgeInsets.symmetric(
vertical: 8.h,
horizontal: 12.w,
),
padding:
EdgeInsets.symmetric(vertical: 8.h, horizontal: 12.w),
child: GestureDetector(
onLongPress: () {
setState(() {
@ -732,19 +757,19 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
),
/// no reply long time
if (messages.indexOf(message) == 0)
Padding(
padding: EdgeInsets.only(left: 17.w, right: 17.w, top: 24.h),
child: Text(
"由于对方没有回复你,你只能发送一条消息,需要对方关注或回复后才能恢复正常聊天",
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFFA29E9E),
fontSize: 10.sp,
fontWeight: MyFontWeight.regular,
),
),
),
// if (messages.indexOf(message) == 0)
// Padding(
// padding: EdgeInsets.only(left: 17.w, right: 17.w, top: 24.h),
// child: Text(
// "由于对方没有回复你,你只能发送一条消息,需要对方关注或回复后才能恢复正常聊天",
// textAlign: TextAlign.center,
// style: TextStyle(
// color: Color(0xFFA29E9E),
// fontSize: 10.sp,
// fontWeight: MyFontWeight.regular,
// ),
// ),
// ),
],
),
);
@ -784,14 +809,21 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
if (commentText.trim() == "") {
return;
}
socketClient.sendMessage(_toUser.mid, commentText).then((value) {
socketClient
.sendMessage(_toUser.mid, commentText)
.then((value) {
Message message = value;
messages.insert(0, message);
chatController.clear();
if (mounted)
setState(() {});
if (mounted) setState(() {});
if (scrollController.position != null) {
double offset = scrollController.position.maxScrollExtent;
debugPrint("offset: $offset");
scrollController.animateTo(offset + 50, duration: const Duration(milliseconds: 500), curve: Curves.easeIn);
}
});
// widget.queryMemberComment(commentText);
},
maxLines: 8,
minLines: 1,

25
lib/im/database/hx_database.dart

@ -38,7 +38,7 @@ class HxDatabase {
}
Future<Message> lastMessage(String userId) async {
if (db == null) {
if (db == null || !db.isOpen) {
return Future.value();
}
String sql =
@ -56,7 +56,7 @@ class HxDatabase {
}
Future<List<Message>> queryList(userId) {
if (db == null) {
if (db == null || !db.isOpen) {
return Future.value(<Message>[]);
}
String sql =
@ -72,7 +72,7 @@ class HxDatabase {
}
Future<List<Message>> queryUList(userId, {int page = 1, int pageSize = 10}) {
if (db == null) {
if (db == null || !db.isOpen) {
return Future.value(<Message>[]);
}
int start = (page - 1) * pageSize;
@ -86,7 +86,7 @@ class HxDatabase {
}
Future<Map<String, int>> messageUnreadCount(List<String> userIds, String selfUserId) async {
if (db == null) {
if (db == null || !db.isOpen) {
return Future.value({});
}
List<Message> messages = await db.query(
@ -102,7 +102,7 @@ class HxDatabase {
}
Future<List<Map>> queryListAll() {
if (db == null) {
if (db == null || !db.isOpen) {
return Future.value();
}
String sql = 'SELECT * FROM Message ORDER BY time DESC';
@ -114,7 +114,7 @@ class HxDatabase {
}
update(Map<dynamic, dynamic> message) {
if (db == null) {
if (db == null || !db.isOpen) {
return Future.value(0);
}
debugPrint("Message_insert: $message");
@ -123,21 +123,23 @@ class HxDatabase {
}
Future<int> insert(Map message) async {
if (db == null) {
if (db == null || !db.isOpen) {
return Future.value(0);
}
debugPrint("Message_insert: $message");
return db.insert("Message", message);
}
/// update message read state
readMessage(String selfUserId, String userId) {
if (db == null) {
if (db == null || !db.isOpen) {
return Future.value(<Message>[]);
}
db.update("Message", {"state": 1}, where: "fromId = ? AND toId = ? AND state = 0 AND isDelete = 0", whereArgs: [userId, selfUserId]);
}
Future<int> insertOrUpdateImUser(Map imUserMap) async {
if (db == null) {
if (db == null || !db.isOpen) {
return Future.value(0);
}
debugPrint("imUser_insert: $imUserMap");
@ -149,7 +151,7 @@ class HxDatabase {
}
Future<List<ImUser>> queryImUser(List<String> userIds) async {
if (db == null) {
if (db == null || !db.isOpen) {
return Future.value(<ImUser>[]);
}
String query = 'SELECT * FROM ImUser WHERE mid IN (${userIds.map((mid) => "'$mid'").join(',')})';
@ -161,7 +163,7 @@ class HxDatabase {
}
Future<ImUser> queryImUserById(String userId) async {
if (db == null) {
if (db == null || !db.isOpen) {
return Future.value();
}
List<ImUser> imUser = await db.query("ImUser",
@ -204,4 +206,5 @@ class HxDatabase {
await migration.migrate(migrationDatabase);
}
}
}

138
lib/im/im_view/im_page.dart

@ -74,25 +74,17 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
super.initState();
OnChatMsgInstance.instance.onChatMessage = this;
loadMessageList();
initSocketClient();
SharedPreferences.getInstance().then((value) {
apiService =
ApiService(Dio(), token: value.getString("token"), context: context);
queryMsgStats();
apiService = ApiService(Dio(), token: value.getString("token"), context: context);
// queryMsgStats();
});
}
_refresh() {
pageNum = 1;
loadMessageList();
queryMsgStats();
}
loadMessageList() async {
initSocketClient() async {
SharedPreferences shared = await SharedPreferences.getInstance();
String userId = shared.getString("userId");
debugPrint("messages: loadMessageList");
socketClient.addCallback(userId, (Message message) {
if (userIds.contains(message.fromId)) {
userIds.remove(message.fromId);
@ -101,28 +93,62 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
lastMessageMap[message.fromId] = message;
sortConversation(lastMessageMap);
debugPrint("messages_records : ${message.toJson()}");
if (contactMap[message.fromId] == null) {
queryMemberInfo([message.fromId]);
return;
}
if (mounted) {
setState(() {});
}
refreshState();
});
debugPrint("messages: queryList");
loadMessageList();
}
_refresh() {
pageNum = 1;
loadMessageList();
// queryMsgStats();
}
loadMessageList() async {
SharedPreferences shared = await SharedPreferences.getInstance();
String userId = shared.getString("userId");
messages = await hxDatabase.queryList(userId);
userIds = messages
lastMessageMap = messages.lGroupBy((p0) => p0.toId != userId ? p0.toId : p0.fromId).mGroupItem(key: (p1) => num.parse(p1.time));
await sortConversation(lastMessageMap);
await queryUnreadCount(userIds, userId);
await queryImUserInfo(userIds);
refreshState();
}
/// update conversation by time sort
sortConversation(lastMessageMap) async {
SharedPreferences shared = await SharedPreferences.getInstance();
String userId = shared.getString("userId");
List<Message> sortMessages = lastMessageMap.values.toList();
sortMessages.sort((a, b) => (num.parse(b.time)).compareTo(num.parse(a.time)));
userIds = sortMessages
.map((e) => e.toId != userId ? e.toId : e.fromId)
.toSet()
.where((element) => element != userId)
.toList();
List<ImUser> contacts = (await hxDatabase.queryImUser(userIds)) ?? [];
}
/// update conversation unreadcount
queryUnreadCount(userIds, userId) async {
unreadCountMap = await hxDatabase.messageUnreadCount(userIds, userId);
lastMessageMap = messages.lGroupBy((p0) => p0.toId != userId ? p0.toId : p0.fromId).mGroupItem(key: (p1) => num.parse(p1.time));
}
/// update imuser info by mids
queryImUserInfo(userIds) async {
List<ImUser> contacts = (await hxDatabase.queryImUser(userIds)) ?? [];
if (contacts?.isEmpty ?? true) {
await queryMemberInfo(userIds);
return;
@ -134,17 +160,15 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
}
}
contactMap = contacts.lGroupBy((p0) => p0.mid).mGroupItem();
if (mounted) {
setState(() {});
}
}
/// update one conversation last message ,and update conversation sort
void updateLastMessage(String userId) async {
Message message = await hxDatabase.lastMessage(userId);
debugPrint("lastmessage: $userId ${message.content} ${message.toJson()}");
if (message != null) {
lastMessageMap[userId] = message;
await sortConversation(lastMessageMap);
refreshState();
}
}
@ -197,34 +221,34 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
// }
// }
queryMsgStats() async {
if (apiService == null) {
SharedPreferences value = await SharedPreferences.getInstance();
apiService = ApiService(
Dio(),
context: context,
token: value.getString("token"),
);
}
BaseData<List<MsgStats>> 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();
}
// queryMsgStats() async {
// if (apiService == null) {
// SharedPreferences value = await SharedPreferences.getInstance();
// apiService = ApiService(
// Dio(),
// context: context,
// token: value.getString("token"),
// );
// }
// BaseData<List<MsgStats>> 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<String> mids) async {
@ -242,8 +266,12 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
baseData.data.forEach((element) async {
await hxDatabase.insertOrUpdateImUser(element.toJson());
});
contactMap = baseData.data.lGroupBy((p0) => p0.mid).mGroupItem();
setState(() {});
baseData.data.forEach((element) {
if (contactMap[element.mid] == null) {
contactMap[element.mid] = element;
}
});
refreshState();
}
} else {
SmartDialog.showToast(baseData.msg, alignment: Alignment.center);
@ -270,9 +298,7 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
controller: _refreshController,
onRefresh: _refresh,
onLoading: () {
setState(() {
_refresh();
});
},
child: Container(
// color: Colors.white,

Loading…
Cancel
Save