import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:loopin/IM/controller/chat_detail_controller.dart'; import 'package:loopin/IM/im_service.dart'; import 'package:loopin/api/common_api.dart'; import 'package:loopin/components/my_confirm.dart'; import 'package:loopin/components/my_toast.dart'; import 'package:loopin/components/network_or_asset_image.dart'; import 'package:loopin/pages/groupChat/components/invite_action_sheet.dart'; import 'package:loopin/pages/groupChat/components/member_action_sheet.dart'; import 'package:loopin/pages/groupChat/components/set_group_info.dart'; import 'package:loopin/pages/groupChat/controller/group_detail_controller.dart'; import 'package:loopin/pages/groupChat/reportGp.dart'; import 'package:loopin/service/http.dart'; import 'package:loopin/utils/index.dart'; import 'package:loopin/utils/permissions.dart'; import 'package:shirne_dialog/shirne_dialog.dart'; import 'package:tencent_cloud_chat_sdk/enum/group_type.dart'; import 'package:tencent_cloud_chat_sdk/enum/receive_message_opt.dart'; import 'package:tencent_cloud_chat_sdk/enum/receive_message_opt_enum.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_info.dart'; import 'package:wechat_assets_picker/wechat_assets_picker.dart'; class Groupdetail extends StatefulWidget { const Groupdetail({super.key}); // final String groupID; // const Groupdetail({super.key, required this.groupID}); @override State createState() => GroupdetailState(); } class GroupdetailState extends State { late final GroupDetailController controller; @override void initState() { super.initState(); // 跳转前先put controller = Get.find(); // controller = Get.put(GroupDetailController(groupID: widget.groupID)); } ///设置群头像 void pickFaceUrl(BuildContext context) async { final hasPer = await Permissions.requestPhotoPermission(); if (!hasPer) { Permissions.showPermissionDialog('相册'); return; } final pickedAssets = await AssetPicker.pickAssets( context, pickerConfig: AssetPickerConfig( textDelegate: const AssetPickerTextDelegate(), pathNameBuilder: (AssetPathEntity album) { return Utils.translateAlbumName(album); }, maxAssets: 1, requestType: RequestType.image, filterOptions: FilterOptionGroup( imageOption: const FilterOption(), ), ), ); if (pickedAssets != null && pickedAssets.isNotEmpty) { final asset = pickedAssets.first; final file = await asset.file; // 获取实际文件 if (file != null) { final fileSizeInBytes = await file.length(); final sizeInMB = fileSizeInBytes / (1024 * 1024); if (sizeInMB > 20) { MyDialog.toast('图片大小不能超过20MB', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200))); } else { logger.w("图片合法,大小:$sizeInMB MB"); //走upload(file)上传图片拿到url地址 final istance = MyDialog.loading('上传中', duration: Duration(minutes: 1)); final res = await Http.upload(CommonApi.uploadFile, filePath: file.path); final imgUrl = res['data']['url']; logger.e(imgUrl); // 设置群头像 await controller.setGroupInfo( changedInfo: V2TimGroupInfo( groupID: controller.info.value!.groupID, groupType: GroupType.Work, faceUrl: imgUrl, ), ); istance.close(); } } } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.grey[50], appBar: AppBar( centerTitle: true, forceMaterialTransparency: true, title: const Text( "群资料", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), leading: IconButton( icon: const Icon(Icons.arrow_back), onPressed: () { Get.back(result: controller.info.value?.groupName ?? ''); }, ), bottom: PreferredSize( preferredSize: const Size.fromHeight(1), child: Container(height: 1, color: Colors.grey[300]), ), ), body: ListView( children: [ // 介绍 ListTile( // 群头像 leading: Obx( () => GestureDetector( onTap: () { // 编辑头像 if (controller.isOwner.value) { // pickFaceUrl(context); } }, child: ClipOval( child: NetworkOrAssetImage( imageUrl: controller.info.value?.faceUrl ?? '', placeholderAsset: 'assets/images/group.png', height: 60, width: 60, ), ), ), ), // 群名称 title: Obx( () => GestureDetector( onTap: () { // 去setinfo页 logger.w('点了名称'); if (controller.isOwner.value) { Get.to( () => SetGroupInfoPage( appBarTitle: "修改群名称", fieldLabel: "群名称", maxLines: 1, maxLength: 20, initialValue: controller.info.value?.groupName ?? "", onSubmit: (value) async { // 修改群名称 await controller.setGroupInfo( changedInfo: V2TimGroupInfo( groupID: controller.info.value!.groupID, groupType: GroupType.Work, groupName: value, ), ); }, ), ); } }, child: Row( mainAxisSize: MainAxisSize.max, children: [ Flexible( child: Text( Utils.handleText(controller.info.value?.groupName, '', "未命名群聊"), // '很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), SizedBox( width: 10, ), if (controller.isOwner.value) Icon( Icons.edit, size: 14, ) ], ), ), ), // 群简介 subtitle: Padding( padding: const EdgeInsets.only(top: 6.0), child: Obx( () => GestureDetector( onTap: () { // 去setinfo页 logger.w('点了简介绍'); if (controller.isOwner.value) { Get.to( () => SetGroupInfoPage( appBarTitle: "修改群简介", fieldLabel: "群简介", maxLines: 5, maxLength: 100, initialValue: controller.info.value?.introduction ?? "", onSubmit: (value) async { // 修改群名称 await controller.setGroupInfo( changedInfo: V2TimGroupInfo( groupID: controller.info.value!.groupID, groupType: GroupType.Work, introduction: value, ), ); }, ), ); } else { // 普通成员点击,查看完成的描述内容 showModalBottomSheet( context: context, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), isScrollControlled: true, // 超出可滚动 builder: (ctx) => SafeArea( child: Container( width: double.infinity, // 👈 占满宽度 padding: const EdgeInsets.all(16), child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, // 根据内容自适应高度 crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( "群简介", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 12), Text( Utils.handleText(controller.info.value?.introduction, '', "暂无群介绍"), style: const TextStyle(fontSize: 14), ), const SizedBox(height: 16), ], ), ), ), ), ); } }, child: Row(mainAxisSize: MainAxisSize.max, children: [ Flexible( child: Text( Utils.handleText(controller.info.value?.introduction, '', "暂无群介绍"), // '非常长的内容非常长的内容非常长的内容非常长的内容非常长的内容非常长的内容', maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(color: Colors.grey), ), ), SizedBox( width: 10, ), if (controller.isOwner.value) Icon( Icons.edit, size: 14, ) ]), ), ), ), // trailing: const Icon(Icons.chevron_right), ), const Divider(height: 1), // 群成员 ListTile( title: Obx( () => Text("群成员(${controller.info.value?.memberCount ?? 0}/${controller.info.value?.memberMaxCount ?? 0})"), ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ Obx( () => Text("查看${controller.info.value?.memberCount ?? 0}个群成员", style: const TextStyle(color: Colors.grey, fontSize: 14)), ), const Icon(Icons.chevron_right), ], ), onTap: () { // 群成员列表 showModalBottomSheet( context: context, barrierColor: Colors.white, isScrollControlled: true, useSafeArea: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), builder: (context) { return MemberActionSheet( showButton: false, groupID: controller.groupID, title: '群成员', showSelf: true, onAction: (userIDs) { // }, ); }, ); }, ), // 群成员头像区域 Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Obx(() { // 成员头像 final memberWidgets = controller.memberList.take(8).map((m) { return GestureDetector( onTap: () { final currentUserID = controller.selfInfo.value?.userID ?? ''; if (m.userID != currentUserID) { Get.toNamed('/vloger', arguments: {'memberId': m.userID}); } }, child: Column( mainAxisSize: MainAxisSize.min, children: [ ClipOval( child: NetworkOrAssetImage( imageUrl: m.faceUrl, height: 50, width: 50, ), ), const SizedBox(height: 4), SizedBox( width: 50, child: Text( m.nickName ?? '未知昵称', maxLines: 1, overflow: TextOverflow.ellipsis, softWrap: false, style: const TextStyle(fontSize: 12), textAlign: TextAlign.center, ), ), ], ), ); }).toList(); // 邀请按钮 final inviteWidget = GestureDetector( onTap: () { logger.w('当前人数${controller.info.value?.memberCount ?? 0}---上限人数${controller.info.value?.memberMaxCount ?? 0}'); if ((controller.info.value?.memberCount ?? 0) < (controller.info.value?.memberMaxCount ?? 0)) { showModalBottomSheet( context: context, barrierColor: Colors.white, isScrollControlled: true, useSafeArea: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), builder: (context) { return InviteActionSheet( groupID: controller.groupID, title: '邀请新成员', onAction: (userIDs) { logger.w("准备添加群成员: $userIDs"); controller.inviteUserToGroup(userList: userIDs); }, ); }, ); } else { MyToast().tip(title: '群人数已达上限', position: 'top'); } }, child: Column( mainAxisSize: MainAxisSize.min, children: const [ CircleAvatar(radius: 25, child: Icon(Icons.add)), SizedBox(height: 4), Text("邀请", style: TextStyle(fontSize: 12)), ], ), ); // 移除按钮(只有群主显示) final removeWidget = controller.isOwner.value ? GestureDetector( onTap: () { showModalBottomSheet( context: context, barrierColor: Colors.white, isScrollControlled: true, useSafeArea: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), builder: (context) { return MemberActionSheet( groupID: controller.groupID, title: '移出成员', onAction: (userIDs) { logger.w("准备移除群成员: $userIDs"); controller.kickGroupMember(userIDs); }, ); }, ); }, child: Column( mainAxisSize: MainAxisSize.min, children: const [ CircleAvatar(radius: 25, child: Icon(Icons.remove)), SizedBox(height: 4), Text("移除", style: TextStyle(fontSize: 12)), ], ), ) : const SizedBox.shrink(); final allWidgets = [ ...memberWidgets, inviteWidget, ]; if (controller.isOwner.value) allWidgets.add(removeWidget); return GridView.count( shrinkWrap: true, // 适应内容高度 physics: const NeverScrollableScrollPhysics(), crossAxisCount: 4, // 每行显示 4 个 mainAxisSpacing: 0, // 行间距 crossAxisSpacing: 16, // 列间距 childAspectRatio: 1, // 调整宽高比 children: allWidgets, ); }), ), const Divider(height: 1), // 群公告 ListTile( title: const Text("群公告"), subtitle: Obx( () => Text( Utils.handleText(controller.info.value?.notification, '', "暂无公告"), maxLines: 3, overflow: TextOverflow.ellipsis, ), ), trailing: const Icon(Icons.chevron_right), onTap: () { logger.w('点击了群公告'); if (controller.isOwner.value) { Get.to( () => SetGroupInfoPage( appBarTitle: "修改群公告", fieldLabel: "群公告", maxLines: 8, maxLength: 200, initialValue: controller.info.value?.notification ?? "", onSubmit: (value) async { // 修改群公告 await controller.setGroupInfo( changedInfo: V2TimGroupInfo( groupID: controller.info.value!.groupID, groupType: GroupType.Work, notification: value, ), ); }, ), ); } }, ), const Divider(height: 1), // 我的本群昵称 ListTile( title: const Text("我的本群昵称"), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ Obx( () { return Text( Utils.handleText(controller.selfInfo.value?.nameCard, controller.selfInfo.value?.nickName, '未设置昵称'), style: TextStyle(fontSize: 14), ); }, ), const Icon(Icons.chevron_right), ], ), onTap: () async { Get.to( () => SetGroupInfoPage( appBarTitle: "修改群昵称", fieldLabel: "群昵称", maxLines: 1, maxLength: 12, initialValue: controller.selfInfo.value?.nameCard ?? "", onSubmit: (value) { // 修改自己的群昵称 controller.setSelfInfo(nameCard: value); }, ), ); }, ), const Divider(height: 1), // 消息免打扰 Obx( () => SwitchListTile( title: Text("消息免打扰"), value: controller.info.value?.recvOpt == ReceiveMsgOptType.kTIMRecvMsgOpt_Not_Notify_Except_At, // 暂时只提供0和3 activeColor: Colors.green, inactiveThumbColor: Colors.grey, inactiveTrackColor: Colors.white, onChanged: (v) async { if (controller.info.value != null) { logger.w(v); logger.e(controller.info.value?.recvOpt); // v=true -> 开启免打扰 = 3 final res = await ImService.instance.setGroupReceiveMessageOpt( groupID: controller.groupID, opt: v ? ReceiveMsgOptEnum.V2TIM_RECEIVE_NOT_NOTIFY_MESSAGE_EXCEPT_AT : ReceiveMsgOptEnum.V2TIM_RECEIVE_MESSAGE, // 关闭免打扰 = 0 ); if (res.success) { controller.info.value!.recvOpt = v ? ReceiveMsgOptType.kTIMRecvMsgOpt_Not_Notify_Except_At : ReceiveMsgOptType.kTIMRecvMsgOpt_Receive; // 关闭免打扰 = 0 开启=3 controller.info.refresh(); } else { MyToast().tip(title: '网络繁忙,请稍后再试', position: 'top'); } } // class ReceiveMsgOptType { // 在线正常接收消息,离线时会进行 APNs 推送 // static const int kTIMRecvMsgOpt_Receive = 0; // 不会接收到消息,离线不会有推送通知 // static const int kTIMRecvMsgOpt_Not_Receive = 1; // 在线正常接收消息,离线不会有推送通知 // static const int kTIMRecvMsgOpt_Not_Notify = 2; // 在线接收消息,离线只接收 at 消息的推送 // static const int kTIMRecvMsgOpt_Not_Notify_Except_At = 3; // 在线和离线都只接收@消息 // static const int kTIMRecvMsgOpt_Not_Receive_Except_At = 4; // } }, ), ), const Divider(height: 1), // 举报 ListTile( title: const Text("举报"), trailing: const Icon(Icons.chevron_right), onTap: () { // Get.to(() => ReportGp(groupID: controller.info.value!.groupID)); }, ), // 退出群聊 const SizedBox(height: 20), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.red, minimumSize: const Size(double.infinity, 48), ), onPressed: () async { // 二次确认 final confirmed = await ConfirmDialog.show( title: "提示", content: "确认要退出群聊吗?", ); if (confirmed == true) { await controller.quitGroup(); Get.find().toolFlag.value = false; // 禁用工具栏 Get.back(); // 返回上一个页面 } }, child: const Text("退出群聊", style: TextStyle(color: Colors.white)), ), ), const SizedBox(height: 30), ], ), ); } }