diff --git a/lib/im/SocketClient.dart b/lib/im/SocketClient.dart index 4ec2a8e0..023313cf 100644 --- a/lib/im/SocketClient.dart +++ b/lib/im/SocketClient.dart @@ -84,8 +84,8 @@ class SocketClient { dispose() { if (_socket != null) { - _socket = null; _socket.close(); + _socket = null; } } diff --git a/lib/im/chat_details_page.dart b/lib/im/chat_details_page.dart index 98eb5086..b0dee47f 100644 --- a/lib/im/chat_details_page.dart +++ b/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'; @@ -18,6 +19,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'; @@ -51,6 +55,7 @@ class _ChatDetailsPage extends State bool needHideMore = false; var commentFocus = FocusNode(); String hintText = "输入消息内容..."; + UserInfo userInfo; final OnChatMessage _tempOnChatMessage = OnChatMsgInstance.instance.onChatMessage; final ScrollController scrollController = ScrollController(); @@ -81,12 +86,29 @@ class _ChatDetailsPage extends State 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; - debugPrint("offset: $offset"); - Future.delayed(const Duration(milliseconds: 500), () { - scrollController.animateTo(offset + 108, duration: const Duration(milliseconds: 500), curve: Curves.easeIn); - }); + 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 + ); + }); + } } }); @@ -97,6 +119,28 @@ class _ChatDetailsPage extends State }); } + ///查询个人信息 + 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 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(() {}); } @@ -110,6 +154,7 @@ class _ChatDetailsPage extends State WidgetsBinding.instance.addObserver(this); commentFocus.addListener(_focusNodeListener); + queryUser(); loadMessageList(); } @@ -356,7 +401,7 @@ class _ChatDetailsPage extends State 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), @@ -691,7 +736,7 @@ class _ChatDetailsPage extends State width: 12.w, ), MImage( - _toUser.avatar, + userInfo?.headimg??"", isCircle: true, height: 44.h, width: 44.h, @@ -826,13 +871,38 @@ class _ChatDetailsPage extends State 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 + // ); + // }); + // } + // } }); }, diff --git a/lib/im/chat_friend_group.dart b/lib/im/chat_friend_group.dart index ccb49dc6..5e5657d5 100644 --- a/lib/im/chat_friend_group.dart +++ b/lib/im/chat_friend_group.dart @@ -84,32 +84,32 @@ class _ChatFriendGroup extends State 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( diff --git a/lib/im/database/hx_database.dart b/lib/im/database/hx_database.dart index 2af0d7a8..7590f47f 100644 --- a/lib/im/database/hx_database.dart +++ b/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 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 lastMessage(String userId) async { + await _dbIsOpen(); String sql = 'SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC LIMIT 1'; List messages = @@ -55,12 +61,11 @@ class HxDatabase { return (messages?.isNotEmpty ?? false) ? messages.first : null; } - Future> queryList(userId) { - if (db == null || !db.isOpen) { - return Future.value([]); - } + Future> 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'; + debugPrint("66666666666$sql"); return db.rawQuery(sql, [userId, userId]).then((value) { return value.map((e) { debugPrint("Message: ${e}"); @@ -71,10 +76,8 @@ class HxDatabase { }); } - Future> queryUList(userId, {int page = 1, int pageSize = 10}) { - if (db == null || !db.isOpen) { - return Future.value([]); - } + Future> 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 ?, ?'; @@ -85,26 +88,25 @@ class HxDatabase { }); } - Future> messageUnreadCount(List userIds, String selfUserId) async { - if (db == null || !db.isOpen) { - return Future.value({}); - } - List messages = await db.query( - "Message", - where: 'fromId IN (?) AND toId = ? AND state = 0 AND isDelete = 0', - whereArgs: []..addAll(userIds)..add(selfUserId) - ).then((value) { + Future> messageUnreadCount( + List userIds, String selfUserId) async { + await _dbIsOpen(); + List messages = await db + .query("Message", + where: 'fromId IN (?) AND toId = ? AND state = 0 AND isDelete = 0', + whereArgs: [] + ..addAll(userIds) + ..add(selfUserId)) + .then((value) { return value.map((e) => Message.fromJson(e)).toList(); }, onError: (error) { debugPrint("Message-error: $error"); }); - return messages.lGroupBy((p)=> p.fromId).mGroupCount; + return (messages??[]).lGroupBy((p) => p.fromId).mGroupCount; } - Future> queryListAll() { - if (db == null || !db.isOpen) { - return Future.value(); - } + Future> queryListAll() async{ + await _dbIsOpen(); String sql = 'SELECT * FROM Message ORDER BY time DESC'; return db.rawQuery(sql); } @@ -113,35 +115,29 @@ class HxDatabase { return db.delete("Message"); } - update(Map message) { - if (db == null || !db.isOpen) { - return Future.value(0); - } + update(Map message) async{ + await _dbIsOpen(); debugPrint("Message_insert: $message"); return db.update("Message", message, where: 'id = ?', whereArgs: [message['id']]); } Future 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([]); - } - 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 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 +147,9 @@ class HxDatabase { } Future> queryImUser(List userIds) async { - if (db == null || !db.isOpen) { - return Future.value([]); - } - 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 +158,7 @@ class HxDatabase { } Future queryImUserById(String userId) async { - if (db == null || !db.isOpen) { - return Future.value(); - } + await _dbIsOpen(); List imUser = await db.query("ImUser", distinct: true, where: "mid = ?", whereArgs: [userId]).then((value) { return value.map((e) => ImUser.fromJson(e)).toList(); @@ -206,5 +199,4 @@ class HxDatabase { await migration.migrate(migrationDatabase); } } - } diff --git a/lib/im/im_search.dart b/lib/im/im_search.dart index 5768f20d..833df735 100644 --- a/lib/im/im_search.dart +++ b/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 { 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 { 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), diff --git a/lib/im/im_view/im_page.dart b/lib/im/im_view/im_page.dart index 663179b7..4428f370 100644 --- a/lib/im/im_view/im_page.dart +++ b/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'; @@ -182,6 +181,7 @@ class _IMPage extends State implements OnChatMessage { } refreshState() { + if(_refreshController.isRefresh)_refreshController.refreshCompleted(); if (mounted) setState(() {}); } @@ -488,7 +488,7 @@ class _IMPage extends State implements OnChatMessage { ), Text( lastMessageMap[userId]?.time != null - ? TimeFormatter.formatTime( + ? AppUtils.timeFormatter( DateTime.fromMillisecondsSinceEpoch(num.parse( lastMessageMap[userId]?.time ?? ""))) : "", diff --git a/lib/im/im_view/time_formatter.dart b/lib/im/im_view/time_formatter.dart deleted file mode 100644 index c406c9a5..00000000 --- a/lib/im/im_view/time_formatter.dart +++ /dev/null @@ -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); // 其他年份的日期 - } - } -} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index f3b09399..fe142f67 100644 --- a/lib/main.dart +++ b/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); diff --git a/lib/main_page.dart b/lib/main_page.dart index f2349dcf..7fb53437 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -70,6 +70,7 @@ class _MainPage extends State with WidgetsBindingObserver { void dispose() { super.dispose(); socketClient.dispose(); + closeDatabase(); WidgetsBinding.instance.removeObserver(this); } diff --git a/lib/utils/flutter_utils.dart b/lib/utils/flutter_utils.dart index a691abaa..e39eac84 100644 --- a/lib/utils/flutter_utils.dart +++ b/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); // 其他年份的日期 + } + } }