Browse Source

add message conversationId; conversion list refresh sort; message list align top

wr_202303
zsw 4 months ago
parent
commit
51ec8411de
  1. 27
      lib/im/chat_details_page.dart
  2. 48
      lib/im/database/hx_database.dart
  3. 18
      lib/im/database/message.dart
  4. 176
      lib/im/im_view/im_page.dart

27
lib/im/chat_details_page.dart

@ -4,6 +4,7 @@ import 'dart:ui';
import 'package:dio/dio.dart';
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
@ -66,6 +67,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
int copyIndex = 0;
String selfUserId = "";
ImUser _toUser;
String conversation ;
@override
void onMessage(txt) {
@ -77,14 +79,16 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
int page = 0;
loadMessageList() async {
ImUser imUser = await hxDatabase.queryImUserById(_toUser.mid);
selfUserId = (await SharedPreferences.getInstance()).getString("userId");
conversation = conversationId(selfUserId, _toUser.mid);
ImUser imUser = await hxDatabase.queryImUserById(conversation);
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);
await hxDatabase.readMessage(conversation);
await refresh();
socketClient.addCallback(_toUser.mid, (Message message) {
@ -100,7 +104,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
Future refresh() async {
List<Message> messagePage =
await hxDatabase.queryUList(_toUser.mid, page: page + 1, pageSize: 10);
await hxDatabase.queryUList(conversation, page: page + 1, pageSize: 10);
page += 1;
if (page == 1) {
messages = messagePage;
@ -118,7 +122,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
}
Future messageShowTime() async {
List<Message> messagePages = await hxDatabase.queryTList(_toUser.mid);
List<Message> messagePages = await hxDatabase.queryTList(conversation);
for (var value in messages) {
Message message = messagePages.firstWhere((element) => value.id == element.id, orElse: () => null);
value.showTime = message != null;
@ -318,7 +322,6 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
// setState(() {});
}
}
return GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
@ -372,9 +375,6 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
footer: CustomFooter(
loadStyle: LoadStyle.ShowWhenLoading,
builder: (BuildContext context, LoadStatus mode) {
// return (messages.length == 0)
// ? Container()
// : MyFooter(mode);
return SizedBox();
},
),
@ -384,12 +384,15 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
refreshState();
});
},
child: Container(
alignment: Alignment.topCenter,
child: SingleChildScrollView(
physics: BouncingScrollPhysics(),
controller: scrollController,
child: chatDetailsList(),
),
),
),
flex: 1,
),
input()
@ -403,11 +406,12 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
///
Widget chatDetailsList() {
return Container(
margin: EdgeInsets.only(bottom: 48.h),
alignment: Alignment.topCenter,
child: ListView.builder(
itemCount: messages.length,
shrinkWrap: true,
reverse: true,
padding: EdgeInsets.zero,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, position) {
return GestureDetector(
@ -430,7 +434,8 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
bool isText = messages[position].msgType == 1;
return Container(
padding: EdgeInsets.only(
top: 32.h,
top: 16.h,
bottom: 16.h,
),
child: Column(
children: [

48
lib/im/database/hx_database.dart

@ -22,7 +22,7 @@ class HxDatabase {
await openDatabase(databaseName, version: 2,
onCreate: (Database db, int version) async {
db.execute(
'CREATE TABLE IF NOT EXISTS `Message` (`id` INTEGER, `fromId` VARCHAR(20), `toId` VARCHAR(20), `replyId` VARCHAR(20), `content` TEXT, `attach` TEXT, `msgType` INTEGER, `time` VARCHAR(20), `state` INTEGER, `isDelete` INTEGER, PRIMARY KEY (`id`))');
'CREATE TABLE IF NOT EXISTS `Message` (`id` INTEGER, `conversationId` VARCHAR(40), `fromId` VARCHAR(20), `toId` VARCHAR(20), `replyId` VARCHAR(20), `content` TEXT, `attach` TEXT, `msgType` INTEGER, `time` VARCHAR(20), `state` INTEGER, `isDelete` INTEGER, PRIMARY KEY (`id`))');
db.execute(
'CREATE TABLE IF NOT EXISTS `ImUser` (`id` INTEGER, `mid` VARCHAR(20), `nickname` VARCHAR(20), `avatar` VARCHAR(200), `phone` VARCHAR(200), `isDelete` INTEGER, `isTop` INTEGER, PRIMARY KEY (`id`))');
}, onConfigure: (database) async {
@ -49,12 +49,12 @@ class HxDatabase {
}
}
Future<Message> lastMessage(String userId) async {
Future<Message> lastMessage(String conversationId) async {
await _dbIsOpen();
String sql =
'SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC LIMIT 1';
'SELECT * FROM Message WHERE conversationId = ? ORDER BY time DESC LIMIT 1';
List<Message> messages =
await db.rawQuery(sql, [userId, userId]).then((value) {
await db.rawQuery(sql, [conversationId]).then((value) {
return value.map((e) {
debugPrint("Message: $e");
return Message.fromJson(e);
@ -72,56 +72,54 @@ class HxDatabase {
WHERE ROWID IN (
SELECT ROWID
FROM (
SELECT ROWID, `fromId`, `toId`, MAX(`time`) AS max_time
SELECT ROWID, conversationId, MAX(`time`) AS max_time
FROM `Message`
GROUP BY `fromId` || '_' || `toId`, `toId` || '_' || `fromId`
GROUP BY conversationId
) AS grouped_messages
WHERE max_time = (
SELECT MAX(`time`)
FROM `Message`
WHERE (`fromId` = grouped_messages.`fromId` AND `toId` = grouped_messages.`toId`)
OR (`fromId` = grouped_messages.`toId` AND `toId` = grouped_messages.`fromId`)
WHERE conversationId = grouped_messages.conversationId
)
)
ORDER BY `time` DESC;''';
return db.rawQuery(sql).then((value) {
return value.map((e) {
debugPrint("Message: ${e}");
debugPrint("Message: $e");
return Message.fromJson(e);
}).toList();
}, onError: (error) {
debugPrint("Messageerror: $error");
debugPrint("MessageError: $error");
});
}
Future<List<Message>> queryUList(userId, {int page = 1, int pageSize = 10}) async{
Future<List<Message>> queryUList(conversationId, {int page = 1, int pageSize = 10}) async{
await _dbIsOpen();
int start = (page - 1) * pageSize;
String sql =
'SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC LIMIT ?, ?';
return db.rawQuery(sql, [userId, userId, start, pageSize]).then((value) {
String sql = 'SELECT * FROM Message WHERE conversationId = ? ORDER BY time DESC LIMIT ?, ?';
return db.rawQuery(sql, [conversationId, start, pageSize]).then((value) {
return value.map((e) => Message.fromJson(e)).toList();
}, onError: (error) {
debugPrint("Messageerror: $error");
});
}
Future<List<Message>> queryTList(userId) async{
Future<List<Message>> queryTList(conversationId) async{
await _dbIsOpen();
String sql = 'SELECT *, time / 300000 * 300000 AS time_interval FROM Message WHERE toId = ? OR fromId = ? GROUP BY time_interval ORDER BY time DESC';
return db.rawQuery(sql, [userId, userId]).then((value) {
String sql = 'SELECT *, time / 300000 * 300000 AS time_interval FROM Message WHERE conversationId = ? GROUP BY time_interval ORDER BY time DESC';
return db.rawQuery(sql, [conversationId]).then((value) {
return value.map((e) => Message.fromJson(e)).toList();
}, onError: (error) {
debugPrint("Messageerror: $error");
});
}
Future<Map<String, int>> messageUnreadCount(List<String> userIds, String selfUserId) async {
Future<Map<String, int>> messageUnreadCount(List<String> conversationIds) async {
await _dbIsOpen();
String userStr = userIds.join(",");
String userStr = conversationIds.join(",");
debugPrint("userStr: $userStr");
List<Message> messages = await db.rawQuery(
"SELECT * FROM Message WHERE fromId IN ($userStr) AND toId = $selfUserId AND state = 0 AND isDelete = 0",
"SELECT * FROM Message WHERE `conversationId` IN ($userStr) AND state = 0 AND isDelete = 0",
).then((value) {
return value.map((e) => Message.fromJson(e)).toList();
}, onError: (error) {
@ -136,8 +134,8 @@ class HxDatabase {
return db.rawQuery(sql);
}
Future<int> deleteByUser(String userId) async {
return db.delete("Message",where: "fromId = ? OR toId = ?", whereArgs: [userId, userId]);
Future<int> deleteByUser(String conversationId) async {
return db.delete("Message",where: "conversationId = ?", whereArgs: [conversationId]);
}
Future<int> deleteAll() async {
@ -158,11 +156,11 @@ class HxDatabase {
}
/// update message read state
readMessage(String selfUserId, String userId) async{
readMessage(String conversationId) async{
await _dbIsOpen();
db.update("Message", {"state": 1},
where: "fromId = ? AND toId = ? AND state = 0 AND isDelete = 0",
whereArgs: [userId, selfUserId]);
where: "conversationId = ? AND state = 0 AND isDelete = 0",
whereArgs: [conversationId]);
}
Future<int> insertOrUpdateImUser(Map imUserMap) async {

18
lib/im/database/message.dart

@ -1,7 +1,11 @@
import 'dart:math';
class Message {
int id;
String conversationId;
String fromId;
String toId;
@ -23,10 +27,11 @@ class Message {
bool showTime = false;
Message(this.id, this.fromId, this.toId, this.replyId, this.content, this.attach, this.msgType, this.time, this.state, this.isDelete);
Message(this.id, this.conversationId, this.fromId, this.toId, this.replyId, this.content, this.attach, this.msgType, this.time, this.state, this.isDelete);
factory Message.fromJson(Map<String, dynamic> json) => Message(
json["id"],
json["conversationId"],
json["fromId"],
json["toId"],
json["replyId"],
@ -39,6 +44,7 @@ class Message {
Map<String, dynamic> toJson() => <String, dynamic>{
"id": id,
"conversationId": conversationId,
"fromId": fromId,
"toId": toId,
"replyId": replyId,
@ -53,6 +59,7 @@ class Message {
createMessage(var toId, String content, {String attach, int msgType, userId, replyId}) {
return <String, dynamic>{
"conversationId": conversationId(userId, toId),
"fromId": userId,
"toId": toId,
"replyId": replyId,
@ -64,3 +71,12 @@ createMessage(var toId, String content, {String attach, int msgType, userId, rep
"isDelete": 0
};
}
conversationId(tid, fid) {
num itid = num.parse(tid);
num ifid = num.parse(fid);
if (itid > ifid) {
return "$ifid-$itid";
}
return "$itid-$ifid";
}

176
lib/im/im_view/im_page.dart

@ -43,11 +43,12 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
"6": 0,
};
int state = 0;
List<String> userIds = [];
List<String> conversationIds = [];
Map<String, Message> lastMessageMap = {};
Map<String, int> unreadCountMap = {};
Map<String, ImUser> contactMap = {};
int insertIndex = 0;
String selfUserId;
@override
void onMessage(txt) {
@ -77,14 +78,14 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
initSocketClient() async {
SharedPreferences shared = await SharedPreferences.getInstance();
String userId = shared.getString("userId");
socketClient.addCallback(userId, (Message message) {
if (userIds.contains(message.fromId)) {
userIds.remove(message.fromId);
selfUserId = shared.getString("userId");
socketClient.addCallback(selfUserId, (Message message) {
if (conversationIds.contains(message.conversationId)) {
conversationIds.remove(message.conversationId);
}
userIds.insert(insertIndex, message.fromId);
conversationIds.insert(insertIndex, message.conversationId);
lastMessageMap[message.fromId] = message;
lastMessageMap[message.conversationId] = message;
listenerRefresh(message);
});
@ -99,12 +100,9 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
}
listenerRefresh(Message message) async {
SharedPreferences shared = await SharedPreferences.getInstance();
String userId = shared.getString("userId");
await sortConversation(lastMessageMap);
await queryUnreadCount(userIds, userId);
await queryUnreadCount(conversationId);
debugPrint("messages_records : ${message.toJson()}");
if (contactMap[message.fromId] == null) {
@ -115,38 +113,35 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
}
loadMessageList() async {
SharedPreferences shared = await SharedPreferences.getInstance();
String userId = shared.getString("userId");
messages = await hxDatabase.queryList();
lastMessageMap = messages.lGroupBy((p0) => p0.toId != userId ? p0.toId : p0.fromId).mGroupItem(key: (p1) => num.parse(p1.time));
lastMessageMap = messages.lGroupBy((p0) => p0.conversationId).mGroupItem(key: (p1) => num.parse(p1.time));
await sortConversation(lastMessageMap);
await queryImUserInfo(messages.map((e) => e.toId != selfUserId ? e.toId : e.fromId).toList());
await queryUnreadCount(userIds, userId);
await sortConversation(lastMessageMap);
await queryImUserInfo(userIds);
await queryUnreadCount(conversationIds);
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)
conversationIds = sortMessages
.map((e) => e.conversationId)
.toSet()
.where((element) => element != userId)
.toList();
conversationIds.forEach((element) {
debugPrint("conversationIds: ${element}");
});
}
/// update conversation unreadcount
queryUnreadCount(userIds, userId) async {
unreadCountMap = await hxDatabase.messageUnreadCount(userIds, userId);
debugPrint("unreadCount: $unreadCountMap");
queryUnreadCount(conversationId) async {
unreadCountMap = await hxDatabase.messageUnreadCount(conversationId);
}
/// update imuser info by mids
@ -162,33 +157,31 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
return;
}
}
contactMap = contacts.lGroupBy((p0) => p0.mid).mGroupItem();
List<String> topUserIds = [],notTopUserIds = [];
contactMap = contacts.lGroupBy((p0) => conversationId(p0.mid, selfUserId)).mGroupItem();
List<String> topConversationIds = [],notTopUserIds = [];
contactMap.forEach((key, value) {
if(value.isTop == 1)
topUserIds.add(key);
topConversationIds.add(key);
else
notTopUserIds.add(key);
});
insertIndex = topUserIds.length;
this.userIds = topUserIds..addAll(notTopUserIds);
insertIndex = topConversationIds.length;
this.conversationIds = topConversationIds..addAll(notTopUserIds);
}
/// 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()}");
void updateLastMessage(String conversationId) async {
Message message = await hxDatabase.lastMessage(conversationId);
debugPrint("lastmessage: $conversationId ${message.content} ${message.toJson()}");
if (message != null) {
lastMessageMap[userId] = message;
lastMessageMap[conversationId] = message;
await sortConversation(lastMessageMap);
refreshState();
}
}
void updateUnreadCount() async {
SharedPreferences shared = await SharedPreferences.getInstance();
String userId = shared.getString("userId");
unreadCountMap = await hxDatabase.messageUnreadCount(userIds, userId);
unreadCountMap = await hxDatabase.messageUnreadCount(conversationIds);
refreshState();
}
@ -216,8 +209,8 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
await hxDatabase.insertOrUpdateImUser(element.toJson());
});
baseData.data.forEach((element) {
if (contactMap[element.mid] == null) {
contactMap[element.mid] = element;
if (contactMap[conversationId(element.mid, selfUserId)] == null) {
contactMap[conversationId(element.mid, selfUserId)] = element;
}
});
refreshState();
@ -233,7 +226,21 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFFFFFFF),
body: SmartRefresher(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFFD9FFDE),
Color(0xFFD9FFDE),
Color(0xFFFFFFFF),
Color(0xFFFFFFFF),
],
stops: [0, 0.2, 0.4, 1],
),
),
child: SmartRefresher(
enablePullDown: true,
enablePullUp: false,
header: MyHeader(),
@ -249,25 +256,8 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
onLoading: () {
_refresh();
},
child: Container(
// color: Colors.white,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFFD9FFDE),
Color(0xFFD9FFDE),
Color(0xFFFFFFFF),
Color(0xFFFFFFFF),
],
stops: [0, 0.2, 0.4, 1],
),
),
child: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Container(
padding: EdgeInsets.only(bottom: 30.h),
child: Column(
children: [
Container(
@ -275,7 +265,8 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
top: MediaQuery.of(context).padding.top + 12.h,
bottom: 15.h,
right: 16.w,
left: 16.w),
left: 16.w,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
@ -293,8 +284,8 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.of(context)
.pushNamed('/router/chat_friend_group').then((value) {
Navigator.of(context).pushNamed('/router/chat_friend_group')
.then((value) {
_refresh();
});
},
@ -317,13 +308,11 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
),
imPageSearch(),
chatList(),
// buildMessage(),fgg
],
),
),
),
),
),
);
}
@ -372,12 +361,13 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
Widget chatList() {
return Container(
child: SlidableAutoCloseBehavior(
child: ListView.builder(
child: ListView(
padding: EdgeInsets.only(top: 16),
itemCount: userIds.length,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, position) {
children: conversationIds.map((e) {
debugPrint("---conversationIds: ${e}");
int position = conversationIds.indexOf(e);
return ClipRRect(
// borderRadius: BorderRadius.all(Radius.circular(4)),
child: Slidable(
@ -388,7 +378,7 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
children: [
CustomSlidableAction(
onPressed: (bc) {
showDelDialog(userIds[position]);
showDelDialog(conversationIds[position]);
},
backgroundColor: Color(0xFFFB312B),
foregroundColor: Colors.white,
@ -411,39 +401,38 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
if (userIds[position] == sharedPreferences.getString("userId")) {
SmartDialog.showToast("不能跟自己聊天", alignment: Alignment.center);
return;
}
Navigator.of(context).pushNamed(
'/router/chat_details_page',
arguments: {
"toUser": contactMap[userIds[position]],
"toUser": contactMap[conversationIds[position]],
},
).then((value) {
unreadCountMap[userIds[position]] = 0;
updateLastMessage(userIds[position]);
unreadCountMap[conversationIds[position]] = 0;
updateLastMessage(conversationIds[position]);
_refresh();
});
},
child: chatItem(userIds[position])),
child: chatItem(conversationIds[position]),
),
),
);
},
}).toList(),
),
),
);
}
Widget chatItem(userId) {
Widget chatItem(conversationId) {
return Container(
padding: EdgeInsets.only(left: 16.w, right: 17.w, bottom: 18.h),
padding: EdgeInsets.only(
left: 16.w, right: 17.w, bottom: 18.h,
),
child: Row(
children: [
MImage(
!contactMap.containsKey(userId)
!contactMap.containsKey(conversationId)
? null
: contactMap[userId]?.avatar ?? "",
: contactMap[conversationId]?.avatar ?? "",
isCircle: true,
height: 54.h,
width: 54.h,
@ -462,9 +451,9 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
children: [
Expanded(
child: Text(
!contactMap.containsKey(userId)
!contactMap.containsKey(conversationId)
? ""
: contactMap[userId]?.nickname ?? "",
: contactMap[conversationId]?.nickname ?? "",
// overflow: TextOverflow.fade,
maxLines: 1,
style: TextStyle(
@ -475,10 +464,10 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
),
),
Text(
lastMessageMap[userId]?.time != null
lastMessageMap[conversationId]?.time != null
? AppUtils.timeFormatter(
DateTime.fromMillisecondsSinceEpoch(num.parse(
lastMessageMap[userId]?.time ?? "")))
lastMessageMap[conversationId]?.time ?? "")))
: "",
style: TextStyle(
fontSize: 12.sp,
@ -495,7 +484,7 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
children: [
Expanded(
child: Text(
lastMessageMap[userId]?.content ?? "",
lastMessageMap[conversationId]?.content ?? "",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
@ -505,7 +494,7 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
),
),
),
if (unreadCountMap[userId] != null && unreadCountMap[userId] > 0)
if (unreadCountMap[conversationId] != null && unreadCountMap[conversationId] > 0)
Container(
width: 16,
height: 16,
@ -514,7 +503,7 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
color: Color(0xFFFF441A),
),
child: RoundButton(
text: "${unreadCountMap[userId]}",
text: "${unreadCountMap[conversationId]}",
textColor: Colors.white,
fontWeight: MyFontWeight.regular,
backgroup: Color(0xFFFF441A),
@ -534,7 +523,7 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
///
showDelDialog(userId) {
showDelDialog(conversationId) {
showDialog(
context: context,
builder: (context) {
@ -560,7 +549,8 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
fontWeight: MyFontWeight.bold,
),
),
)),
),
),
// Spacer(),
Container(
height:1.h,
@ -596,7 +586,7 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () async{
await hxDatabase.deleteByUser(contactMap[userId]?.mid ?? "");
await hxDatabase.deleteByUser(conversationId);
_refresh();
Navigator.of(context).pop();
},
@ -605,10 +595,10 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFFFF370A),
)
)
)
)
),
),
),
),
],
)
],

Loading…
Cancel
Save