265 lines
9.5 KiB
Dart
265 lines
9.5 KiB
Dart
import 'package:easy_refresh/easy_refresh.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:loopin/IM/im_service.dart';
|
||
import 'package:loopin/styles/index.dart';
|
||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_full_info.dart';
|
||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
|
||
|
||
class InviteActionSheet extends StatefulWidget {
|
||
final String groupID;
|
||
|
||
final Function(List<String> selected) onAction;
|
||
final String title;
|
||
final String actionLabel;
|
||
final bool showButton;
|
||
|
||
const InviteActionSheet({
|
||
super.key,
|
||
required this.groupID,
|
||
required this.onAction,
|
||
this.title = "",
|
||
this.actionLabel = "确认",
|
||
this.showButton = true,
|
||
});
|
||
|
||
@override
|
||
State<InviteActionSheet> createState() => _MemberActionSheetState();
|
||
}
|
||
|
||
class _MemberActionSheetState extends State<InviteActionSheet> {
|
||
String _query = "";
|
||
final Set<String> _selectedIDs = {}; // 选中的 userID 集合
|
||
late List<V2TimUserFullInfo> members = [];
|
||
String nextSeq = '';
|
||
bool hasMore = false;
|
||
bool loading = false;
|
||
//
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
getMemberData();
|
||
}
|
||
|
||
//互关好友
|
||
Future<void> getMemberData({bool reset = false}) async {
|
||
if (loading) return;
|
||
loading = true;
|
||
final res = await ImService.instance.getMutualFollowersList(
|
||
nextCursor: nextSeq,
|
||
);
|
||
if (res.success && res.data != null) {
|
||
final userInfoList = res.data!.userFullInfoList ?? [];
|
||
final isFinished = res.data!.nextCursor == null || res.data!.nextCursor!.isEmpty;
|
||
logger.w('获取成功:${res.data!.nextCursor},是否还有更多:$isFinished');
|
||
if (isFinished) {
|
||
hasMore = false;
|
||
nextSeq = '';
|
||
} else {
|
||
nextSeq = res.data!.nextCursor ?? '';
|
||
hasMore = nextSeq.isNotEmpty ? true : false;
|
||
}
|
||
|
||
final ids = userInfoList.map((item) => item.userID).whereType<String>().toList();
|
||
if (ids.isNotEmpty) {
|
||
getGroupMemberData(userIDs: ids, userList: userInfoList);
|
||
}
|
||
} else {
|
||
logger.e('获取数据失败:${res.desc}');
|
||
}
|
||
}
|
||
|
||
//群成员
|
||
Future<void> getGroupMemberData({required List<String> userIDs, required List<V2TimUserFullInfo> userList}) async {
|
||
final res = await ImService.instance.getGroupMembersInfo(
|
||
groupID: widget.groupID,
|
||
memberList: userIDs,
|
||
);
|
||
|
||
if (res.success && res.data != null) {
|
||
final List<V2TimGroupMemberFullInfo> groupMemberList = res.data ?? [];
|
||
// 已经在群里的 userID
|
||
final inGroupIds = groupMemberList.map((m) => m.userID).whereType<String>().where((id) => id.isNotEmpty).toSet();
|
||
// 过滤掉已经在群里的
|
||
final notInGroupUsers = userList.where((user) => user.userID != null && !inGroupIds.contains(user.userID)).toList();
|
||
setState(() {
|
||
members.addAll(notInGroupUsers);
|
||
});
|
||
}
|
||
setState(() {
|
||
loading = false;
|
||
});
|
||
}
|
||
|
||
String handleText(String? text, String defaultValue) {
|
||
if (text == null || text.trim().isEmpty) return defaultValue;
|
||
return text;
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
// 搜索过滤
|
||
final filteredMembers = members.where((m) {
|
||
final name = m.nickName ?? '';
|
||
return name.contains(_query);
|
||
}).toList();
|
||
|
||
return SafeArea(
|
||
child: GestureDetector(
|
||
behavior: HitTestBehavior.translucent, // 点击空白区域也能触发
|
||
onTap: () {
|
||
FocusScope.of(context).unfocus();
|
||
},
|
||
child: Scaffold(
|
||
backgroundColor: Colors.white,
|
||
appBar: AppBar(
|
||
centerTitle: true,
|
||
forceMaterialTransparency: true,
|
||
bottom: PreferredSize(
|
||
preferredSize: const Size.fromHeight(1.0),
|
||
child: Container(
|
||
color: Colors.grey[300],
|
||
height: 1.0,
|
||
),
|
||
),
|
||
title: Text(
|
||
widget.title,
|
||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||
),
|
||
leading: IconButton(
|
||
icon: const Icon(Icons.arrow_back),
|
||
onPressed: () {
|
||
Navigator.pop(context);
|
||
},
|
||
),
|
||
),
|
||
body: Column(
|
||
children: [
|
||
SizedBox(height: 10),
|
||
// 搜索框
|
||
Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
child: TextField(
|
||
decoration: InputDecoration(
|
||
prefixIcon: const Icon(Icons.search),
|
||
hintText: "搜索",
|
||
contentPadding: const EdgeInsets.symmetric(vertical: 8),
|
||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
||
),
|
||
onChanged: (value) {
|
||
setState(() {
|
||
_query = value.trim();
|
||
});
|
||
},
|
||
),
|
||
),
|
||
|
||
const SizedBox(height: 8),
|
||
|
||
// 成员列表
|
||
Expanded(
|
||
child: EasyRefresh(
|
||
footer: ClassicFooter(
|
||
dragText: '加载更多',
|
||
armedText: '释放加载',
|
||
readyText: '加载中...',
|
||
processingText: '加载中...',
|
||
processedText: hasMore ? '加载完成' : '没有更多了~',
|
||
failedText: '加载失败,请重试',
|
||
messageText: '最后更新于 %T',
|
||
),
|
||
onLoad: () async {
|
||
//
|
||
if (hasMore) {
|
||
await getMemberData();
|
||
}
|
||
},
|
||
child: ListView.builder(
|
||
itemCount: filteredMembers.length,
|
||
itemBuilder: (context, index) {
|
||
final m = filteredMembers[index];
|
||
final id = m.userID;
|
||
final uname = handleText(m.nickName, '');
|
||
final nickname = handleText(m.nickName, '未知昵称');
|
||
final showName = uname.isEmpty ? nickname : uname;
|
||
return InkWell(
|
||
onTap: () {
|
||
setState(() {
|
||
if (_selectedIDs.contains(id)) {
|
||
_selectedIDs.remove(id);
|
||
} else if (id != null && id.isNotEmpty) {
|
||
_selectedIDs.add(id);
|
||
}
|
||
});
|
||
},
|
||
child: Padding(
|
||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||
child: Row(
|
||
children: [
|
||
// 左侧圆形头像
|
||
CircleAvatar(
|
||
radius: 20,
|
||
backgroundImage: m.faceUrl != null ? NetworkImage(m.faceUrl!) : null,
|
||
child: m.faceUrl == null ? const Icon(Icons.person) : null,
|
||
),
|
||
const SizedBox(width: 12),
|
||
|
||
// 用户名
|
||
Expanded(
|
||
child: Text(
|
||
showName,
|
||
style: const TextStyle(fontSize: 14),
|
||
overflow: TextOverflow.ellipsis,
|
||
),
|
||
),
|
||
|
||
// 复选框
|
||
Container(
|
||
width: 24,
|
||
height: 24,
|
||
alignment: Alignment.center,
|
||
decoration: BoxDecoration(
|
||
shape: BoxShape.circle,
|
||
border: Border.all(color: _selectedIDs.contains(id) ? FStyle.primaryColor : Colors.grey),
|
||
color: _selectedIDs.contains(id) ? FStyle.primaryColor : Colors.transparent,
|
||
),
|
||
child: _selectedIDs.contains(id) ? const Icon(Icons.check, size: 16, color: Colors.white) : null,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
},
|
||
),
|
||
),
|
||
),
|
||
// 底部操作按钮
|
||
if (widget.showButton)
|
||
Padding(
|
||
padding: const EdgeInsets.all(16),
|
||
child: ElevatedButton(
|
||
style: ElevatedButton.styleFrom(
|
||
minimumSize: const Size(double.infinity, 48),
|
||
backgroundColor: FStyle.primaryColor,
|
||
),
|
||
onPressed: _selectedIDs.isEmpty
|
||
? null
|
||
: () {
|
||
Navigator.pop(context);
|
||
widget.onAction(_selectedIDs.toList());
|
||
},
|
||
child: Text(
|
||
"${widget.actionLabel}(${_selectedIDs.length})",
|
||
style: TextStyle(
|
||
color: _selectedIDs.isNotEmpty ? Colors.white : Colors.black,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|