Browse Source

增加im在线监测

dev-2.0
王子贤 1 month ago
parent
commit
76d30fb9c3
4 changed files with 128 additions and 47 deletions
  1. 12
      lib/im/im_manager.dart
  2. 14
      lib/pages/message/chat_page.dart
  3. 144
      lib/pages/message/conversation_tab.dart
  4. 5
      lib/pages/mine/mine_page.dart

12
lib/im/im_manager.dart

@ -61,6 +61,8 @@ class IMManager {
bool _isReconnecting = false; //
Completer<void>? _initCompleter; //
Timer? timer;
//
static const String _connectionHandlerKey = 'im_manager_connection_handler';
static const String _chatHandlerKey = 'im_manager_chat_handler';
@ -1331,6 +1333,13 @@ class IMManager {
// ConversationController
_refreshConversationList();
// await EMClient.getInstance.presenceManager.publishPresence("online");
// timer = Timer.periodic(const Duration(seconds: 30), (timer) async {
// // 5
// await EMClient.getInstance.presenceManager.publishPresence("online");
//
// });
debugPrint('✅ IM 登录成功: $userId');
return true;
} catch (e, s) {
@ -1929,7 +1938,8 @@ class IMManager {
final statusDescStr = presence.statusDescription ?? '';
final statusDesc = statusDescStr.toLowerCase();
// 线onlineavailable 线
final isOnline = statusDesc == 'online' || statusDesc == 'available';
Map<String, int> detail = presence.statusDetails ?? {};
final isOnline = detail.values.any((v) => v == 1);
if (Get.isLogEnable) {
Get.log(

14
lib/pages/message/chat_page.dart

@ -1,5 +1,6 @@
import 'package:dating_touchme_app/controller/discover/svga_player_manager.dart';
import 'package:dating_touchme_app/extension/ex_widget.dart';
import 'package:dating_touchme_app/im/im_manager.dart';
import 'package:dating_touchme_app/widget/live/svga_player_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
@ -69,9 +70,18 @@ class _ChatPageState extends State<ChatPage> {
// ChatInputBar GlobalKey
final GlobalKey<State<ChatInputBar>> _chatInputBarKey = GlobalKey<State<ChatInputBar>>();
bool isOnline = false;
@override
void initState() {
super.initState();
IMManager.instance.getUserPresenceStatus(widget.userId).then((e){
isOnline = e == true;
setState(() {
});
});
// controller
// userId controller
final tag = 'chat_${widget.userId}';
@ -262,7 +272,7 @@ class _ChatPageState extends State<ChatPage> {
style: TextStyle(fontSize: 18.sp),
),
// 使 Obx 线
Obx(() {
if(isOnline) Obx(() {
final isOnline = controller.isUserOnline.value;
if (isOnline) {
return Row(
@ -533,7 +543,7 @@ class _ChatPageState extends State<ChatPage> {
),
),
// 线
Container(
if(isOnline) Container(
padding: EdgeInsets.symmetric(
horizontal: 8.w,
vertical: 4.h,

144
lib/pages/message/conversation_tab.dart

@ -1,3 +1,4 @@
import 'package:dating_touchme_app/im/im_manager.dart';
import 'package:dating_touchme_app/pages/message/chat_page.dart';
import 'package:flutter/material.dart';
import 'package:dating_touchme_app/generated/assets.dart';
@ -71,7 +72,7 @@ class _ConversationTabState extends State<ConversationTab>
itemCount: filteredConversations.length,
itemBuilder: (context, index) {
final conversation = filteredConversations[index];
return _buildConversationItem(conversation);
return BuildConversationItem(conversation: conversation);
},
);
});
@ -81,14 +82,49 @@ class _ConversationTabState extends State<ConversationTab>
);
}
//
Widget _buildConversationItem(EMConversation conversation) {
//
final cachedUserInfo = controller.getCachedUserInfo(conversation.id);
@override
bool get wantKeepAlive => true;
}
class BuildConversationItem extends StatefulWidget {
final EMConversation conversation;
const BuildConversationItem({super.key, required this.conversation});
@override
State<BuildConversationItem> createState() => _BuildConversationItemState();
}
class _BuildConversationItemState extends State<BuildConversationItem> {
late EMConversation conversation;
bool isOnline = false;
late var cachedUserInfo;
final ConversationController controller = Get.find<ConversationController>();
@override
void initState() {
super.initState();
conversation = widget.conversation;
cachedUserInfo = controller.getCachedUserInfo(conversation.id);
IMManager.instance.getUserPresenceStatus(conversation.id).then((e){
isOnline = e == true;
setState(() {
});
});
}
@override
Widget build(BuildContext context) {
return FutureBuilder<ExtendedUserInfo?>(
future: cachedUserInfo != null
? Future.value(cachedUserInfo)
future: cachedUserInfo != null
? Future.value(cachedUserInfo)
: controller.loadContact(conversation.id),
initialData: cachedUserInfo,
builder: (context, userSnapshot) {
@ -126,41 +162,57 @@ class _ConversationTabState extends State<ConversationTab>
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(28),
color: Colors.grey[300],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(28),
child: (userInfo?.avatarUrl != null && userInfo!.avatarUrl!.isNotEmpty)
? CachedNetworkImage(
imageUrl: userInfo.avatarUrl!,
Stack(
children: [
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(28),
color: Colors.grey[300],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(28),
child: (userInfo?.avatarUrl != null && userInfo!.avatarUrl!.isNotEmpty)
? CachedNetworkImage(
imageUrl: userInfo.avatarUrl!,
width: 56,
height: 56,
fit: BoxFit.cover,
placeholder: (context, url) => Image.asset(
Assets.imagesUserAvatar,
width: 56,
height: 56,
fit: BoxFit.cover,
placeholder: (context, url) => Image.asset(
Assets.imagesUserAvatar,
width: 56,
height: 56,
fit: BoxFit.cover,
),
errorWidget: (context, url, error) => Image.asset(
Assets.imagesUserAvatar,
width: 56,
height: 56,
fit: BoxFit.cover,
),
)
: Image.asset(
),
errorWidget: (context, url, error) => Image.asset(
Assets.imagesUserAvatar,
width: 56,
height: 56,
fit: BoxFit.cover,
),
),
)
: Image.asset(
Assets.imagesUserAvatar,
width: 56,
height: 56,
fit: BoxFit.cover,
),
),
),
if(isOnline) Positioned(
bottom: 0,
right: 0,
child: Container(
width: 11.w,
height: 11.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(11.w)),
color: const Color.fromRGBO(43, 255, 191, 1)
),
),
)
],
),
const SizedBox(width: 12),
Expanded(
@ -169,7 +221,7 @@ class _ConversationTabState extends State<ConversationTab>
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
@ -246,16 +298,20 @@ class _ConversationTabState extends State<ConversationTab>
);
}
//
Widget _buildLastMessageContent(String content, EMMessage? message) {
//
final isFailed = message != null &&
message.direction == MessageDirection.SEND &&
message.status == MessageStatus.FAIL;
final isFailed = message != null &&
message.direction == MessageDirection.SEND &&
message.status == MessageStatus.FAIL;
//
final containsEmoji = EmojiTextHelper.containsEmoji(content);
if (containsEmoji && !isFailed) {
// 使 widget
return _buildSingleLineEmojiText(
@ -281,6 +337,8 @@ class _ConversationTabState extends State<ConversationTab>
}
}
//
Widget _buildSingleLineEmojiText(String text, TextStyle textStyle) {
final RegExp emojiRegex = RegExp(r'\[emoji:(\d+)\]');
@ -333,7 +391,5 @@ class _ConversationTabState extends State<ConversationTab>
overflow: TextOverflow.ellipsis,
);
}
@override
bool get wantKeepAlive => true;
}

5
lib/pages/mine/mine_page.dart

@ -2,6 +2,7 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:dating_touchme_app/controller/global.dart';
import 'package:dating_touchme_app/controller/mine/mine_controller.dart';
import 'package:dating_touchme_app/extension/ex_widget.dart';
import 'package:dating_touchme_app/im/im_manager.dart';
import 'package:dating_touchme_app/model/mine/user_count_data.dart';
import 'package:dating_touchme_app/network/user_api.dart';
import 'package:dating_touchme_app/pages/mine/auth_center_page.dart';
@ -58,6 +59,10 @@ class MinePageState extends State<MinePage> with AutomaticKeepAliveClientMixin{
_userApi = Get.find<UserApi>();
getUserCount();
// IMManager.instance.getUserPresenceStatus("1066419122415472640").then((e){
// print(e);
// });
}

Loading…
Cancel
Save