Browse Source

聊天列表数据更改;

添加好友弹窗更改;
wr_202303
wurong 4 months ago
parent
commit
d221f5e87c
  1. 143
      lib/im/add_friend.dart
  2. 77
      lib/im/chat_details_page.dart
  3. 33
      lib/im/database/contact.dart
  4. 97
      lib/im/database/hx_database.dart
  5. 0
      lib/im/im_search.dart
  6. 98
      lib/im/im_view/im_page.dart
  7. 20
      lib/im/im_view/time_formatter.dart
  8. 61
      lib/retrofit/data/im_user_list.dart
  9. 10
      lib/retrofit/retrofit_api.dart
  10. 48
      lib/retrofit/retrofit_api.g.dart

143
lib/im/add_friend.dart

@ -96,7 +96,7 @@ class _AddFriend extends State<AddFriend> {
);
}
///
///
Widget addFriendItem() {
return Container(
margin: EdgeInsets.only(left:16.w,right:16.w,bottom: 24.h),
@ -122,32 +122,133 @@ class _AddFriend extends State<AddFriend> {
fontWeight: FontWeight.w500),
),
),
Container(
padding: EdgeInsets.symmetric(horizontal:18.w,vertical:6.h),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(26.5.r),
color: Color(0xFF32A060),
boxShadow: [
BoxShadow(
color: Color(0xFF32A060).withOpacity(0.2),
spreadRadius: 0,
blurRadius: 4,
offset: Offset(0, 4), // changes position of shadow
),
],
),
child: Text(
"添加",
style: TextStyle(
fontSize: 12.sp,
color: Colors.white,
fontWeight:MyFontWeight.regular),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap:(){
showFriendVerificationDialog();
},
child: Container(
padding: EdgeInsets.symmetric(horizontal:18.w,vertical:6.h),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(26.5.r),
color: Color(0xFF32A060),
boxShadow: [
BoxShadow(
color: Color(0xFF32A060).withOpacity(0.2),
spreadRadius: 0,
blurRadius: 4,
offset: Offset(0, 4), // changes position of shadow
),
],
),
child: Text(
"添加",
style: TextStyle(
fontSize: 12.sp,
color: Colors.white,
fontWeight:MyFontWeight.regular),
),
),
)
]),
);
}
///
showFriendVerificationDialog() {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
contentPadding: EdgeInsets.zero, //
content: Container(
// width: MediaQuery.of(context).size.width - 84,
height: 130.h,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(padding:EdgeInsets.symmetric(vertical:16.h),
child: Text(
"好友验证",
style: TextStyle(
color: Color(0xFF060606),
fontSize: 16.sp,
fontWeight: MyFontWeight.bold,
),
),),
Expanded(child:Text(
"我是秀才8856",
style: TextStyle(
color: Color(0xFF060606),
fontSize: 12.sp,
fontWeight: MyFontWeight.regular,
),
)),
// Spacer(),
Container(
margin:EdgeInsets.only(top:18.h),
height:1.h,
width: double.infinity,
color:Color(0xFFEDEDED),
),
Row(
children: [
Expanded(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.of(context).pop();
},
child: Container(
// height: 38.h,
child: Text(
"取消",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16.sp,
color: Color(0xFF060606),
)
)
)
)
),
Container(
height:45,
width: 1.w,
color: Color(0xFFEDEDED),
),
Expanded(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.of(context).pop();
},
child: Container(
// height: 38.h,
child: Text(
"确认",
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFF060606),
)
)
)
)
)
],
)
],
),
),
);
},
);
}
///
Widget friendGroupSearch() {
return Container(

77
lib/im/chat_details_page.dart

@ -17,6 +17,8 @@ import 'package:shared_preferences/shared_preferences.dart';
import '../../community/release_dynamic.dart';
import '../../generated/l10n.dart';
import '../../utils/font_weight.dart';
import '../retrofit/data/im_user_list.dart';
import '../view_widget/custom_image.dart';
import 'im_view/on_chat_message.dart';
import 'im_view/on_chat_msg_instance.dart';
@ -54,6 +56,8 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
final ScrollController scrollController = ScrollController();
String tex = "";
int copyIndex = 0;
String selfUserId = "";
ImUserList _toUser;
@override
void onMessage(txt) {
@ -64,11 +68,9 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
loadMessageList() async {
selfUserId = (await SharedPreferences.getInstance()).getString("userId");
messages = await hxDatabase.queryUList(_toUser.mid);
toUserId = widget.arguments["toId"];
messages = await hxDatabase.queryUList(toUserId);
socketClient.addCallback(toUserId, (Message message) {
socketClient.addCallback(_toUser.mid, (Message message) {
messages.add(message);
refreshState();
@ -80,12 +82,11 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
if (mounted) setState(() {});
}
String selfUserId = "";
String toUserId = "";
@override
void initState() {
super.initState();
_toUser = widget.arguments["toUser"];
OnChatMsgInstance.instance.onChatMessage = this;
WidgetsBinding.instance.addObserver(this);
commentFocus.addListener(_focusNodeListener);
@ -154,7 +155,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
WidgetsBinding.instance.removeObserver(this);
commentFocus.removeListener(_focusNodeListener);
socketClient.removeCallback(toUserId);
socketClient.removeCallback(_toUser.mid);
}
@ -250,7 +251,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
// resizeToAvoidBottomInset: false,
backgroundColor: Color(0xFFF6F6F6),
appBar: MyAppBar(
title: "哈哈哈哈",
title: _toUser.nickname,
titleColor: Color(0xFF0D0D0D),
titleSize: 17.sp,
leading: true,
@ -367,6 +368,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
height: 16.h,
),
if (copyIndex == 1)
///
Stack(
alignment: Alignment.bottomCenter,
children: [
@ -456,19 +458,14 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
padding: EdgeInsets.only(left: 17.w, right: 39.w),
child: Row(
children: [
// MImage(
// "",
// isCircle: true,
// width: 44,
// height: 44,
// fit: BoxFit.cover,
// errorSrc: "assets/image/default_user.webp",
// fadeSrc: "assets/image/default_user.webp",
// ),
Image.asset(
"assets/image/fuka_zj.webp",
MImage(
_toUser.avatar,
isCircle: true,
height: 44.h,
width: 44.h,
fit: BoxFit.cover,
errorSrc: "assets/image/default_1.webp",
fadeSrc: "assets/image/default_1.webp",
),
SizedBox(
width: 12.w,
@ -658,19 +655,14 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
SizedBox(
width: 12.w,
),
// MImage(
// "",
// isCircle: true,
// width: 44,
// height: 44,
// fit: BoxFit.cover,
// errorSrc: "assets/image/default_user.webp",
// fadeSrc: "assets/image/default_user.webp",
// ),
Image.asset(
"assets/image/fuka_zj.webp",
height: 44,
width: 44,
MImage(
"",
isCircle: true,
height: 44.h,
width: 44.h,
fit: BoxFit.cover,
errorSrc: "assets/image/default_1.webp",
fadeSrc: "assets/image/default_1.webp",
),
],
),
@ -682,19 +674,14 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
padding: EdgeInsets.only(left: 17.w, right: 39.w, top: 20.h),
child: Row(
children: [
// MImage(
// "",
// isCircle: true,
// width: 44,
// height: 44,
// fit: BoxFit.cover,
// errorSrc: "assets/image/default_user.webp",
// fadeSrc: "assets/image/default_user.webp",
// ),
Image.asset(
"assets/image/fuka_zj.webp",
MImage(
_toUser.avatar,
isCircle: true,
height: 44.h,
width: 44.h,
fit: BoxFit.cover,
errorSrc: "assets/image/default_1.webp",
fadeSrc: "assets/image/default_1.webp",
),
SizedBox(
width: 12.w,
@ -786,7 +773,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
if (commentText.trim() == "") {
return;
}
socketClient.sendMessage(toUserId, commentText).then((value) {
socketClient.sendMessage(_toUser.mid, commentText).then((value) {
Message message = value;
messages.insert(0, message);
chatController.clear();

33
lib/im/database/contact.dart

@ -1,33 +0,0 @@
class Contact {
int id;
String userId;
String nickName;
String imageUrl;
int state;
int isDelete;
Contact(this.id, this.userId, this.nickName, this.imageUrl, this.state,
this.isDelete);
factory Contact.fromJson(Map<String, dynamic> json) => Contact(
json["id"],
json["userId"],
json["nickName"],
json["imageUrl"],
json["state"],
json["isDelete"]);
Map<String, dynamic> toJson() => <String, dynamic>{
"id": id,
"fromId": userId,
"toId": nickName,
"replyId": imageUrl,
"state": state,
"isDelete": isDelete == null ? 0 : isDelete
};
}

97
lib/im/database/hx_database.dart

@ -1,17 +1,14 @@
import 'package:flutter/cupertino.dart';
import 'package:huixiang/im/database/contact.dart';
import 'package:huixiang/im/database/message.dart';
import 'package:huixiang/im/database/migration.dart';
import 'package:sqflite/sqflite.dart';
import '../../retrofit/data/im_user_list.dart';
class HxDatabase {
Database db;
void open({String key}) async {
// _migrations.add(Migration(1, 2, (Database database) async {
// database.execute('ALTER TABLE `Message` ADD COLUMN `replyId` VARCHAR(20) DEFAULT NULL AFTER `toId`');
// }));
@ -20,23 +17,19 @@ class HxDatabase {
if (key?.isNotEmpty ?? false) {
databaseName = 'hx_$key.db';
}
await openDatabase(
databaseName,
version: 2,
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`))');
db.execute('CREATE TABLE IF NOT EXISTS `Contact` (`id` INTEGER, `userId` VARCHAR(20), `nickName` VARCHAR(20), `imageUrl` VARCHAR(200), `state` INTEGER, `isDelete` INTEGER, PRIMARY KEY (`id`))');
},
onConfigure: (database) async {
await database.execute('PRAGMA foreign_keys = ON');
},
onUpgrade: (database, startVersion, endVersion) async {
await runMigrations(database, startVersion, endVersion, _migrations);
},
onOpen: (Database db) {
this.db = db;
}
);
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`))');
db.execute(
'CREATE TABLE IF NOT EXISTS `ImUser` (`id` INTEGER, `mid` VARCHAR(20), `nickname` VARCHAR(20), `avatar` VARCHAR(200), `phone` VARCHAR(200), `isDelete` INTEGER, PRIMARY KEY (`id`))');
}, onConfigure: (database) async {
await database.execute('PRAGMA foreign_keys = ON');
}, onUpgrade: (database, startVersion, endVersion) async {
await runMigrations(database, startVersion, endVersion, _migrations);
}, onOpen: (Database db) {
this.db = db;
});
}
void close() {
@ -47,8 +40,10 @@ class HxDatabase {
if (db == null) {
return Future.value();
}
String sql = 'SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC LIMIT 1';
List<Message> messages = await db.rawQuery(sql, [userId, userId]).then((value) {
String sql =
'SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC LIMIT 1';
List<Message> messages =
await db.rawQuery(sql, [userId, userId]).then((value) {
return value.map((e) {
debugPrint("Message: ${e}");
return Message.fromJson(e);
@ -63,7 +58,8 @@ class HxDatabase {
if (db == null) {
return Future.value(<Message>[]);
}
String sql = 'SELECT * FROM Message WHERE toId = ? OR fromId = ? GROUP BY toId,fromId ORDER BY time DESC';
String sql =
'SELECT * FROM Message WHERE toId = ? OR fromId = ? GROUP BY toId,fromId ORDER BY time DESC';
return db.rawQuery(sql, [userId, userId]).then((value) {
return value.map((e) {
debugPrint("Message: ${e}");
@ -78,7 +74,8 @@ class HxDatabase {
if (db == null) {
return Future.value(<Message>[]);
}
String sql = 'SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC';
String sql =
'SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC';
return db.rawQuery(sql, [userId, userId]).then((value) {
return value.map((e) => Message.fromJson(e)).toList();
}, onError: (error) {
@ -103,7 +100,8 @@ class HxDatabase {
return Future.value(0);
}
debugPrint("Message_insert: $message");
return db.update("Message", message, where: 'id = ?', whereArgs: [message['id']]);
return db.update("Message", message,
where: 'id = ?', whereArgs: [message['id']]);
}
Future<int> insert(Map message) async {
@ -114,37 +112,41 @@ class HxDatabase {
return db.insert("Message", message);
}
Future<int> insertContact(Map contactMap) async {
Future<int> insertOrUpdateImUser(Map imUserMap) async {
if (db == null) {
return Future.value(0);
}
debugPrint("contact_insert: $contactMap");
return db.insert("Contact", contactMap);
debugPrint("imUser_insert: $imUserMap");
if ((await queryImUserById(imUserMap['mid'])) == null)
return db.insert("ImUser", imUserMap);
else
return db.update("ImUser", imUserMap,
where: 'mid = ?', whereArgs: [imUserMap['mid']]);
}
Future<List<Contact>> queryContact(List<String> userIds) async {
Future<List<ImUserList>> queryImUser(List<String> userIds) async {
if (db == null) {
return Future.value(<Contact>[]);
return Future.value(<ImUserList>[]);
}
String sql = 'SELECT * FROM Contact WHERE userId IN (?) AND state = 0 AND isDelete = 0';
return db.rawQuery(sql, userIds).then((value) {
return value.map((e) => Contact.fromJson(e)).toList();
String query = 'SELECT * FROM ImUser WHERE mid IN (${userIds.map((mid) => "'$mid'").join(',')})';
return db.rawQuery(query).then((value) {
return value.map((e) => ImUserList.fromJson(e)).toList();
}, onError: (error) {
debugPrint("Contact_error: $error");
debugPrint("ImUser_error: $error");
});
}
Future<Contact> queryContactById(String userId) async {
Future<ImUserList> queryImUserById(String userId) async {
if (db == null) {
return Future.value();
}
List<Contact> contact = await db.query("Contact", distinct: true, where: "userId = ?", whereArgs: [userId])
.then((value) {
return value.map((e) => Contact.fromJson(e)).toList();
List<ImUserList> imUser = await db.query("ImUser",
distinct: true, where: "mid = ?", whereArgs: [userId]).then((value) {
return value.map((e) => ImUserList.fromJson(e)).toList();
}, onError: (error) {
debugPrint("Contact_error: $error");
debugPrint("ImUser_error: $error");
});
return (contact?.isNotEmpty ?? false) ? contact.first : null;
return (imUser?.isNotEmpty ?? false) ? imUser.first : null;
}
final List<Migration> _migrations = [];
@ -155,22 +157,22 @@ class HxDatabase {
}
Future<void> runMigrations(
final Database migrationDatabase,
final int startVersion,
final int endVersion,
final List<Migration> migrations,
) async {
final Database migrationDatabase,
final int startVersion,
final int endVersion,
final List<Migration> migrations,
) async {
final relevantMigrations = migrations
.where((migration) => migration.startVersion >= startVersion)
.toList()
..sort(
(first, second) => first.startVersion.compareTo(second.startVersion));
(first, second) => first.startVersion.compareTo(second.startVersion));
if (relevantMigrations.isEmpty ||
relevantMigrations.last.endVersion != endVersion) {
throw StateError(
'There is no migration supplied to update the database to the current version.'
' Aborting the migration.',
' Aborting the migration.',
);
}
@ -178,5 +180,4 @@ class HxDatabase {
await migration.migrate(migrationDatabase);
}
}
}

0
lib/im/im_search.dart

98
lib/im/im_view/im_page.dart

@ -4,9 +4,10 @@ import 'dart:collection';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:huixiang/generated/l10n.dart';
import 'package:huixiang/im/database/contact.dart';
import 'package:huixiang/im/database/message.dart';
import 'package:huixiang/im/im_view/time_formatter.dart';
import 'package:huixiang/im/out/message.pb.dart';
import 'package:huixiang/main.dart';
import 'package:huixiang/retrofit/data/base_data.dart';
@ -17,9 +18,13 @@ import 'package:huixiang/utils/font_weight.dart';
import 'package:huixiang/view_widget/classic_header.dart';
import 'package:huixiang/view_widget/my_footer.dart';
import 'package:huixiang/view_widget/round_button.dart';
import 'package:intl/intl.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../retrofit/data/im_user_list.dart';
import '../../utils/flutter_utils.dart';
import '../../view_widget/custom_image.dart';
import 'on_chat_message.dart';
import 'on_chat_msg_instance.dart';
@ -36,6 +41,7 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
ApiService apiService;
int pageNum = 1;
List<Message> messages = [];
Map<String, ImUserList> imUserList = {};
Map<String, int> msgNumber = {
"1": 0,
"2": 0,
@ -67,7 +73,8 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
loadMessageList();
SharedPreferences.getInstance().then((value) {
apiService = ApiService(Dio(), token: value.getString("token"), context: context);
apiService =
ApiService(Dio(), token: value.getString("token"), context: context);
queryMsgStats();
});
}
@ -79,9 +86,8 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
}
List<String> userIds = [];
Map<String, Contact> contactMap = {};
Map<String, Message> lastMessageMap = {};
Stream streamSubscription ;
Stream streamSubscription;
loadMessageList() async {
SharedPreferences shared = await SharedPreferences.getInstance();
@ -97,9 +103,9 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
lastMessageMap[message.fromId] = message;
debugPrint("messages_records : ${message.toJson()}");
if (contactMap[message.fromId] == null) {
/// TODO: message.fromId request Api and setState
}
// if (contactMap[message.fromId] == null) {
// /// TODO: message.fromId request Api and setState
// }
if (mounted) {
setState(() {});
}
@ -112,23 +118,20 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
});
userIds = messages
.map((e) => e.toId != userId ? e.toId : e.fromId)
.toSet().where((element) => element != userId)
.toList();
.toSet()
.where((element) => element != userId)
.toList(); //
List<ImUserList> contacts = (await hxDatabase.queryImUser(userIds)) ?? [];
lastMessageMap = groupBy(messages, (p0) => p0.toId != userId ? p0.toId : p0.fromId);
if (contacts.isNotEmpty) imUserList = groupBy(contacts, (p0) => p0.mid);
List<Contact> contacts = await hxDatabase.queryContact(userIds);
if (contacts?.isEmpty ?? false) {
/// TODO: userIds request Api
} else {
List<String> queryUserIds = userIds.where((u) => contacts.where((c) => c.userId == u).isEmpty).toList();
/// TODO: queryUserIds request Api
}
contactMap = groupBy(contacts, (p0) => p0.userId);
lastMessageMap =
groupBy(messages, (p0) => p0.toId != userId ? p0.toId : p0.fromId);
if (mounted) {
setState(() {});
}
queryMemberInfo(userIds);
}
void updateLastMessage(String userId) async {
@ -211,6 +214,25 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
EasyLoading.dismiss();
}
///
queryMemberInfo(List<String> mids) async {
BaseData<List<ImUserList>> baseData = await apiService.memberInfoByIds({
"mids": mids,
}).catchError((error) {
SmartDialog.showToast(AppUtils.dioErrorTypeToString(error.type),
alignment: Alignment.center);
});
if (baseData != null && baseData.isSuccess) {
baseData.data.forEach((element) async {
await hxDatabase.insertOrUpdateImUser(element.toJson());
});
imUserList = groupBy(baseData.data, (p0) => p0.mid);
setState(() {});
} else {
SmartDialog.showToast(baseData.msg, alignment: Alignment.center);
}
}
RefreshController _refreshController = RefreshController();
@override
@ -280,7 +302,8 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.of(context)
.pushNamed('/router/chat_friend_group').then((value) {
.pushNamed('/router/chat_friend_group')
.then((value) {
_refresh();
});
},
@ -375,12 +398,13 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.of(context).pushNamed(
'/router/chat_details_page',
'/router/chat_details_page',
arguments: {
"toId": userIds[position],
"toUser": imUserList[userIds[position]],
},
).then((value) {
updateLastMessage(userIds[position]);
_refresh();
});
},
child: chatItem(userIds[position]),
@ -395,19 +419,16 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
padding: EdgeInsets.only(left: 16.w, right: 17.w, bottom: 18.h),
child: Row(
children: [
// MImage(
// "",
// isCircle: true,
// width: 40,
// height: 40,
// fit: BoxFit.cover,
// errorSrc: "assets/image/default_user.webp",
// fadeSrc: "assets/image/default_user.webp",
// ),
Image.asset(
"assets/image/fuka_zj.webp",
MImage(
!imUserList.containsKey(userId)
? null
: imUserList[userId]?.avatar ?? "",
isCircle: true,
height: 54.h,
width: 54.h,
fit: BoxFit.cover,
errorSrc: "assets/image/fuka_zj.webp",
fadeSrc: "assets/image/fuka_zj.webp",
),
SizedBox(
width: 12.w,
@ -420,7 +441,9 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
children: [
Expanded(
child: Text(
"喽哈 $userId",
!imUserList.containsKey(userId)
? ""
: imUserList[userId]?.nickname ?? "",
// overflow: TextOverflow.fade,
maxLines: 1,
style: TextStyle(
@ -431,7 +454,11 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
),
),
Text(
"2021.03.08 13:22",
lastMessageMap[userId]?.time != null
? TimeFormatter.formatTime(
DateTime.fromMillisecondsSinceEpoch(num.parse(
lastMessageMap[userId]?.time ?? "")))
: "",
style: TextStyle(
fontSize: 12.sp,
color: Color(0xFFA29E9E),
@ -447,7 +474,7 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
children: [
Expanded(
child: Text(
"新开的火锅店好吃得很",
lastMessageMap[userId]?.content ?? "",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
@ -482,5 +509,4 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
),
);
}
}

20
lib/im/im_view/time_formatter.dart

@ -0,0 +1,20 @@
import 'package:intl/intl.dart';
class TimeFormatter {
static String formatTime(DateTime time) {
final now = DateTime.now();
final diff = now.difference(time).inHours;
if (diff < 24) {
return '刚刚'; // 24
} else if (diff < 48) {
return '昨天'; //
} else if (diff < 72) {
return '前天'; //
} else if (time.year == now.year) {
return DateFormat('MM月dd日').format(time); //
} else {
return DateFormat('yyyy年MM月dd日').format(time); //
}
}
}

61
lib/retrofit/data/im_user_list.dart

@ -0,0 +1,61 @@
/// mid : "1379254113602109440"
/// nickname : "哈哈"
/// avatar : "https://pos.upload.lotus-wallet.com/admin/2022/11/d501d2cd-ffc0-49f2-967c-2e463462f500.jpeg"
/// phone : "13052919193"
/// isFollow : null
/// createTime : null
class ImUserList {
ImUserList({
String mid,
String nickname,
num isDelete,
String avatar,
String phone, }){
_mid = mid;
_nickname = nickname;
_isDelete = isDelete;
_avatar = avatar;
_phone = phone;
}
ImUserList.fromJson(dynamic json) {
_mid = json['mid'];
_nickname = json['nickname'];
_isDelete = json['isDelete'];
_avatar = json['avatar'];
_phone = json['phone'];
}
String _mid;
String _nickname;
num _isDelete;
String _avatar;
String _phone;
ImUserList copyWith({ String mid,
String nickname,
num isDelete,
String avatar,
String phone,
}) => ImUserList( mid: mid ?? _mid,
nickname: nickname ?? _nickname,
isDelete: isDelete ?? _isDelete,
avatar: avatar ?? _avatar,
phone: phone ?? _phone,
);
String get mid => _mid;
String get nickname => _nickname;
num get isDelete => _isDelete;
String get avatar => _avatar;
String get phone => _phone;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['mid'] = _mid;
map['nickname'] = _nickname;
map['isDelete'] = _isDelete;
map['avatar'] = _avatar;
map['phone'] = _phone;
return map;
}
}

10
lib/retrofit/retrofit_api.dart

@ -39,6 +39,7 @@ import 'data/goods_category.dart';
import 'data/headlines_list.dart';
import 'data/headlines_list_details.dart';
import 'data/home_rank.dart';
import 'data/im_user_list.dart';
import 'data/invitation_list.dart';
import 'data/invoice_list.dart';
import 'data/invoices_detail_info.dart';
@ -653,4 +654,13 @@ abstract class ApiService {
///
@GET("invoice/detail{id}")
Future<BaseData<InvoicesDetailInfo>> invoiceDetail(@Path("id") String id);
///
@POST("/member/memberInfoByIds")
Future<BaseData<List<ImUserList>>> memberInfoByIds(@Body() Map<String, dynamic> param);
///Im关键字搜索
@GET("/member/memberSearch?keyword={keyword}")
Future<BaseData<List<ImUserList>>> memberSearch(
@Path("keyword") String keyword);
}

48
lib/retrofit/retrofit_api.g.dart

@ -2528,4 +2528,52 @@ class _ApiService implements ApiService {
);
return value;
}
@override
Future<BaseData<List<ImUserList>>> memberInfoByIds(param) async {
ArgumentError.checkNotNull(param, 'param');
const _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _data = <String, dynamic>{};
_data.addAll(param ?? <String, dynamic>{});
final _result = await _dio.request<Map<String, dynamic>>(
'/member/memberInfoByIds',
queryParameters: queryParameters,
options: RequestOptions(
method: 'POST',
headers: <String, dynamic>{},
extra: _extra,
baseUrl: baseUrl),
data: _data);
final value = BaseData<List<ImUserList>>.fromJson(
_result.data,
(json) => (json as List<dynamic>)
.map<ImUserList>((i) => ImUserList.fromJson(i as Map<String, dynamic>))
.toList());
return value;
}
@override
Future<BaseData<List<ImUserList>>> memberSearch(keyword) async {
ArgumentError.checkNotNull(keyword, 'keyword');
const _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _data = <String, dynamic>{};
final _result = await _dio.request<Map<String, dynamic>>(
'/member/memberSearch?keyword=$keyword',
queryParameters: queryParameters,
options: RequestOptions(
method: 'GET',
headers: <String, dynamic>{},
extra: _extra,
baseUrl: baseUrl),
data: _data);
final value = BaseData<List<ImUserList>>.fromJson(
_result.data,
(json) => (json as List<dynamic>)
.map<ImUserList>(
(i) => ImUserList.fromJson(i as Map<String, dynamic>))
.toList());
return value;
}
}

Loading…
Cancel
Save