Browse Source

Merge remote-tracking branch 'origin/wr_202303' into wr_202303

# Conflicts:
#	lib/im/chat_details_page.dart
wr_202303
zsw 4 months ago
parent
commit
0761defe24
  1. 2
      lib/im/SocketClient.dart
  2. 112
      lib/im/chat_details_page.dart
  3. 52
      lib/im/chat_friend_group.dart
  4. 70
      lib/im/database/hx_database.dart
  5. 10
      lib/im/im_search.dart
  6. 4
      lib/im/im_view/im_page.dart
  7. 20
      lib/im/im_view/time_formatter.dart
  8. 4
      lib/main.dart
  9. 1
      lib/main_page.dart
  10. 18
      lib/utils/flutter_utils.dart

2
lib/im/SocketClient.dart

@ -85,8 +85,8 @@ class SocketClient {
dispose() {
if (_socket != null) {
_socket = null;
_socket.close();
_socket = null;
}
}

112
lib/im/chat_details_page.dart

@ -1,13 +1,14 @@
import 'dart:convert';
import 'dart:io';
import 'dart:ui';
import 'package:dio/dio.dart';
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:huixiang/im/database/message.dart';
import 'package:huixiang/im/out/message.pb.dart';
import 'package:huixiang/main.dart';
import 'package:huixiang/retrofit/data/im_user.dart';
import 'package:huixiang/retrofit/retrofit_api.dart';
@ -21,6 +22,9 @@ import 'package:shared_preferences/shared_preferences.dart';
import '../../community/release_dynamic.dart';
import '../../generated/l10n.dart';
import '../../utils/font_weight.dart';
import '../retrofit/data/base_data.dart';
import '../retrofit/data/user_info.dart';
import '../utils/flutter_utils.dart';
import '../view_widget/custom_image.dart';
import 'im_view/on_chat_message.dart';
import 'im_view/on_chat_msg_instance.dart';
@ -54,6 +58,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
bool needHideMore = false;
var commentFocus = FocusNode();
String hintText = "输入消息内容...";
UserInfo userInfo;
final OnChatMessage _tempOnChatMessage =
OnChatMsgInstance.instance.onChatMessage;
final ScrollController scrollController = ScrollController();
@ -86,13 +91,30 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
messages.insert(0, message);
refreshState();
// if (scrollController.position != null) {
// double offset = scrollController.position.maxScrollExtent;
// debugPrint("offset: $offset");
// Future.delayed(const Duration(milliseconds: 500), () {
// scrollController.animateTo(offset + 108, duration: const Duration(milliseconds: 500), curve: Curves.easeIn);
// });
// }
if (scrollController.position != null) {
double offset = scrollController.position.maxScrollExtent;
double maxScrollExtent = scrollController.position.maxScrollExtent;
double viewportDimensions = scrollController.position.viewportDimension;
//
if (maxScrollExtent > viewportDimensions) {
double offset = maxScrollExtent;
debugPrint("offset: $offset");
Future.delayed(const Duration(milliseconds: 500), () {
scrollController.animateTo(offset + 108, duration: const Duration(milliseconds: 500), curve: Curves.easeIn);
scrollController.animateTo(
offset + 108,
duration: const Duration(milliseconds: 500),
curve: Curves.easeIn
);
});
}
}
});
refreshState();
@ -114,6 +136,28 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
return;
}
///
queryUser() async {
final SharedPreferences value = await SharedPreferences.getInstance();
if (value.containsKey('user') &&
value.getString('user') != null &&
value.getString('user') != "") {
userInfo = UserInfo.fromJson(jsonDecode(value.getString('user')));
}
if(apiService == null)
apiService = ApiService(Dio(), context: context, token: value.getString("token"));
BaseData<UserInfo> baseData =
await apiService.queryInfo().catchError((onError) {});
if (baseData != null && baseData.isSuccess) {
setState(() {
userInfo = baseData.data;
});
SharedPreferences.getInstance().then((value) => {
value.setString('user', jsonEncode(baseData.data)),
});
}
}
refreshState() {
if (mounted) setState(() {});
}
@ -127,6 +171,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
WidgetsBinding.instance.addObserver(this);
commentFocus.addListener(_focusNodeListener);
queryUser();
loadMessageList();
}
@ -309,24 +354,6 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
child: Column(
children: [
Expanded(
child: SmartRefresher(
enablePullDown: true,
enablePullUp: false,
header: MyHeader(),
physics: BouncingScrollPhysics(),
footer: CustomFooter(
loadStyle: LoadStyle.ShowWhenLoading,
builder: (BuildContext context, LoadStatus mode) {
return (messages.length == 0) ? Container() : MyFooter(mode);
},
),
controller: refreshController,
onRefresh: () {
refresh().then((){
refreshState();
});
},
onLoading: () {},
child: SingleChildScrollView(
physics: BouncingScrollPhysics(),
controller: scrollController,
@ -348,7 +375,6 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
],
),
),
),
flex: 1,
),
input()
@ -362,7 +388,6 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
Widget chatDetailsList() {
return Container(
margin: EdgeInsets.only(bottom: 48.h),
alignment: Alignment.bottomCenter,
child: ListView.builder(
itemCount: messages.length,
shrinkWrap: true,
@ -393,7 +418,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
child: Column(
children: [
Text(
"${DateTime.fromMillisecondsSinceEpoch(int.parse(message.time))}",
AppUtils.timeFormatter(DateTime.fromMillisecondsSinceEpoch(int.parse(message.time))),
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFFA29E9E),
@ -728,7 +753,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
width: 12.w,
),
MImage(
_toUser.avatar,
userInfo?.headimg??"",
isCircle: true,
height: 44.h,
width: 44.h,
@ -863,13 +888,38 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
chatController.clear();
if (mounted) setState(() {});
if (scrollController.position != null) {
double offset = scrollController.position.maxScrollExtent;
debugPrint("offset: $offset");
Future.delayed(const Duration(milliseconds: 500), () {
scrollController.animateTo(offset + 108, duration: const Duration(milliseconds: 500), curve: Curves.easeIn);
});
// if (scrollController.position != null) {
// double offset = scrollController.position.maxScrollExtent;
// debugPrint("offset: $offset");
// Future.delayed(const Duration(milliseconds: 500), () {
// scrollController.animateTo(offset + 108, duration: const Duration(milliseconds: 500), curve: Curves.easeIn);
// });
// }
if (scrollController.position.maxScrollExtent > scrollController.offset) {
//
scrollController.animateTo(
scrollController.position.maxScrollExtent,
duration: Duration(seconds: 1),
curve: Curves.easeOut,
);
}
// if (scrollController.position != null) {
// double maxScrollExtent = scrollController.position.maxScrollExtent;
// double viewportDimensions = scrollController.position.viewportDimension;
//
// //
// if (maxScrollExtent > viewportDimensions) {
// // double offset = maxScrollExtent;
// // debugPrint("offset: $offset");
// Future.delayed(const Duration(milliseconds: 500), () {
// scrollController.animateTo(
// maxScrollExtent+viewportDimensions,
// duration: const Duration(milliseconds: 500),
// curve: Curves.easeIn
// );
// });
// }
// }
});
},

52
lib/im/chat_friend_group.dart

@ -84,32 +84,32 @@ class _ChatFriendGroup extends State<ChatFriendGroup>
leading: true,
leadingColor: Colors.black,
background: Color(0xFFFFFFFF),
action: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.of(context).pushNamed('/router/add_friend');
},
child: Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(20.r),
boxShadow: [
BoxShadow(
color: Color(0xFF000000).withAlpha(25),
offset: Offset(0, 0),
blurRadius: 4,
spreadRadius: 0,
)
],
),
child: Image.asset(
"assets/image/add_friend.webp",
fit: BoxFit.fill,
height: 14.h,width: 14.h,
),
),
),
// action: GestureDetector(
// behavior: HitTestBehavior.opaque,
// onTap: () {
// Navigator.of(context).pushNamed('/router/add_friend');
// },
// child: Container(
// padding: EdgeInsets.all(12),
// decoration: BoxDecoration(
// color: Color(0xFFFFFFFF),
// borderRadius: BorderRadius.circular(20.r),
// boxShadow: [
// BoxShadow(
// color: Color(0xFF000000).withAlpha(25),
// offset: Offset(0, 0),
// blurRadius: 4,
// spreadRadius: 0,
// )
// ],
// ),
// child: Image.asset(
// "assets/image/add_friend.webp",
// fit: BoxFit.fill,
// height: 14.h,width: 14.h,
// ),
// ),
// ),
),
body: Container(
child: Column(

70
lib/im/database/hx_database.dart

@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart';
import 'package:huixiang/constant.dart';
import 'package:huixiang/im/database/message.dart';
import 'package:huixiang/im/database/migration.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sqflite/sqflite.dart';
import '../../retrofit/data/im_user.dart';
@ -37,10 +38,15 @@ class HxDatabase {
db.close();
}
Future<Message> lastMessage(String userId) async {
_dbIsOpen() async {
if (db == null || !db.isOpen) {
return Future.value();
var sp = await SharedPreferences.getInstance();
open(key: sp.getString("userId"));
}
}
Future<Message> lastMessage(String userId) async {
await _dbIsOpen();
String sql =
'SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC LIMIT 1';
List<Message> messages =
@ -55,10 +61,8 @@ class HxDatabase {
return (messages?.isNotEmpty ?? false) ? messages.first : null;
}
Future<List<Message>> queryList(userId) {
if (db == null || !db.isOpen) {
return Future.value(<Message>[]);
}
Future<List<Message>> queryList(userId) async{
await _dbIsOpen();
String sql =
'SELECT * FROM (SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC) mm GROUP BY mm.toId,mm.fromId';
return db.rawQuery(sql, [userId, userId]).then((value) {
@ -71,10 +75,8 @@ class HxDatabase {
});
}
Future<List<Message>> queryUList(userId, {int page = 1, int pageSize = 10}) {
if (db == null || !db.isOpen) {
return Future.value(<Message>[]);
}
Future<List<Message>> queryUList(userId, {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 ?, ?';
@ -86,9 +88,7 @@ class HxDatabase {
}
Future<Map<String, int>> messageUnreadCount(List<String> userIds, String selfUserId) async {
if (db == null || !db.isOpen) {
return Future.value({});
}
await _dbIsOpen();
String userStr = userIds.join(",");
debugPrint("userStr: $userStr");
List<Message> messages = await db.rawQuery(
@ -98,13 +98,11 @@ class HxDatabase {
}, onError: (error) {
debugPrint("Message-error: $error");
});
return messages.lGroupBy((p)=> p.fromId).mGroupCount;
return (messages??[]).lGroupBy((p) => p.fromId).mGroupCount;
}
Future<List<Map>> queryListAll() {
if (db == null || !db.isOpen) {
return Future.value();
}
Future<List<Map>> queryListAll() async{
await _dbIsOpen();
String sql = 'SELECT * FROM Message ORDER BY time DESC';
return db.rawQuery(sql);
}
@ -113,35 +111,29 @@ class HxDatabase {
return db.delete("Message");
}
update(Map<dynamic, dynamic> message) {
if (db == null || !db.isOpen) {
return Future.value(0);
}
update(Map<dynamic, dynamic> message) async{
await _dbIsOpen();
debugPrint("Message_insert: $message");
return db.update("Message", message,
where: 'id = ?', whereArgs: [message['id']]);
}
Future<int> insert(Map message) async {
if (db == null || !db.isOpen) {
return Future.value(0);
}
await _dbIsOpen();
debugPrint("Message_insert: $message");
return db.insert("Message", message);
}
/// update message read state
readMessage(String selfUserId, String userId) {
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]);
readMessage(String selfUserId, String userId) async{
await _dbIsOpen();
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 || !db.isOpen) {
return Future.value(0);
}
await _dbIsOpen();
debugPrint("imUser_insert: $imUserMap");
if ((await queryImUserById(imUserMap['mid'])) == null)
return db.insert("ImUser", imUserMap);
@ -151,10 +143,9 @@ class HxDatabase {
}
Future<List<ImUser>> queryImUser(List<String> userIds) async {
if (db == null || !db.isOpen) {
return Future.value(<ImUser>[]);
}
String query = 'SELECT * FROM ImUser WHERE mid IN (${userIds.map((mid) => "'$mid'").join(',')})';
await _dbIsOpen();
String query =
'SELECT * FROM ImUser WHERE mid IN (${userIds.map((mid) => "'$mid'").join(',')})';
return db.rawQuery(query).then((value) {
return value.map((e) => ImUser.fromJson(e)).toList();
}, onError: (error) {
@ -163,9 +154,7 @@ class HxDatabase {
}
Future<ImUser> queryImUserById(String userId) async {
if (db == null || !db.isOpen) {
return Future.value();
}
await _dbIsOpen();
List<ImUser> imUser = await db.query("ImUser",
distinct: true, where: "mid = ?", whereArgs: [userId]).then((value) {
return value.map((e) => ImUser.fromJson(e)).toList();
@ -206,5 +195,4 @@ class HxDatabase {
await migration.migrate(migrationDatabase);
}
}
}

10
lib/im/im_search.dart

@ -3,7 +3,6 @@ import 'dart:ui';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -59,9 +58,12 @@ class _ImSearch extends State<ImSearch> {
await apiService.memberSearch(keyword).catchError((onError) {});
if (baseData != null && baseData.isSuccess) {
searchUser.clear();
searchUser.addAll(baseData.data);
baseData.data.forEach((element) {
if(element.phone != "" && element.nickname != ""){
searchUser.add(element);
}
});
searchState = 1;
if(baseData.data.length == 0){
searchState = 2;
}
@ -214,7 +216,7 @@ class _ImSearch extends State<ImSearch> {
return Container(
padding: EdgeInsets.only(left:10.w,right:16.w,bottom:15.h),
child: Text(
textType == 1 ?(searchUser?.phone ?? searchUser?.nickname?? "") : (searchUser?.nickname ?? searchUser?.phone ?? ""),
textType == 1 ?(searchUser?.phone?? "") : (searchUser?.nickname ?? ""),
style: TextStyle(
fontSize: 16.sp,
color: Color(0xFF32A060),

4
lib/im/im_view/im_page.dart

@ -4,7 +4,6 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:huixiang/constant.dart';
import 'package:huixiang/generated/l10n.dart';
import 'package:huixiang/im/database/message.dart';
import 'package:huixiang/im/im_view/time_formatter.dart';
import 'package:huixiang/main.dart';
import 'package:huixiang/retrofit/data/base_data.dart';
import 'package:huixiang/retrofit/retrofit_api.dart';
@ -183,6 +182,7 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
}
refreshState() {
if(_refreshController.isRefresh)_refreshController.refreshCompleted();
if (mounted) setState(() {});
}
@ -489,7 +489,7 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
),
Text(
lastMessageMap[userId]?.time != null
? TimeFormatter.formatTime(
? AppUtils.timeFormatter(
DateTime.fromMillisecondsSinceEpoch(num.parse(
lastMessageMap[userId]?.time ?? "")))
: "",

20
lib/im/im_view/time_formatter.dart

@ -1,20 +0,0 @@
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); //
}
}
}

4
lib/main.dart

@ -224,6 +224,10 @@ initDatabase(String userId) async {
hxDatabase.open(key: userId);
}
closeDatabase() async {
hxDatabase.close();
}
final SocketClient socketClient = new SocketClient();
EventBus eventBus = EventBus(sync: true);

1
lib/main_page.dart

@ -70,6 +70,7 @@ class _MainPage extends State<MainPage> with WidgetsBindingObserver {
void dispose() {
super.dispose();
socketClient.dispose();
closeDatabase();
WidgetsBinding.instance.removeObserver(this);
}

18
lib/utils/flutter_utils.dart

@ -288,4 +288,22 @@ class AppUtils {
return true;
}
}
///im列表时间显示
static String timeFormatter(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); //
}
}
}

Loading…
Cancel
Save