2025-09-03 11:25:31 +08:00
|
|
|
|
/// 粉丝列表
|
|
|
|
|
library;
|
|
|
|
|
|
|
|
|
|
import 'package:easy_refresh/easy_refresh.dart';
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:get/get.dart';
|
|
|
|
|
import 'package:loopin/IM/controller/chat_controller.dart';
|
|
|
|
|
import 'package:loopin/IM/im_service.dart';
|
|
|
|
|
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
|
|
|
|
import 'package:loopin/components/network_or_asset_image.dart';
|
|
|
|
|
import 'package:loopin/styles/index.dart';
|
|
|
|
|
import 'package:loopin/utils/index.dart';
|
|
|
|
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
|
|
|
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_follow_type_check_result.dart';
|
|
|
|
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
|
|
|
|
|
|
|
|
|
|
class UserWithFollow {
|
|
|
|
|
final V2TimUserFullInfo userInfo;
|
|
|
|
|
int followType;
|
|
|
|
|
|
|
|
|
|
UserWithFollow({
|
|
|
|
|
required this.userInfo,
|
|
|
|
|
this.followType = 0,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class Fans extends StatefulWidget {
|
|
|
|
|
const Fans({super.key});
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
State<Fans> createState() => FansState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class FansState extends State<Fans> with SingleTickerProviderStateMixin {
|
|
|
|
|
bool isLoading = false; // 是否在加载中
|
|
|
|
|
bool hasMore = true; // 是否还有更多数据
|
|
|
|
|
String page = '';
|
|
|
|
|
List<UserWithFollow> dataList = <UserWithFollow>[];
|
|
|
|
|
|
|
|
|
|
///-------------------
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
super.initState();
|
|
|
|
|
getData();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 分页获取陌粉丝数据
|
|
|
|
|
Future<void> getData() async {
|
|
|
|
|
/// 0:不是好友也没有关注
|
|
|
|
|
/// 1:你关注了对方(单向)
|
|
|
|
|
/// 2:对方关注了你(单向)
|
|
|
|
|
/// 3:互相关注(双向好友)
|
|
|
|
|
final res = await ImService.instance.getMyFollowersList(
|
|
|
|
|
nextCursor: page,
|
|
|
|
|
);
|
|
|
|
|
if (res.success && res.data != null) {
|
|
|
|
|
logger.i('获取成功:${res.data!.nextCursor}');
|
|
|
|
|
final userInfoList = res.data!.userFullInfoList ?? [];
|
|
|
|
|
// 构建数据
|
|
|
|
|
List<UserWithFollow> wrappedList = userInfoList.map((u) {
|
|
|
|
|
return UserWithFollow(userInfo: u);
|
|
|
|
|
}).toList();
|
|
|
|
|
// 获取id
|
|
|
|
|
final userIDList = userInfoList.map((item) => item.userID).whereType<String>().toList();
|
|
|
|
|
if (userIDList.isNotEmpty) {
|
|
|
|
|
final shiRes = await ImService.instance.checkFollowType(userIDList: userIDList);
|
|
|
|
|
if (shiRes.success && shiRes.data != null) {
|
|
|
|
|
final shipResData = shiRes.data!;
|
|
|
|
|
for (final uwf in wrappedList) {
|
|
|
|
|
final userID = uwf.userInfo.userID;
|
|
|
|
|
if (userID != null) {
|
|
|
|
|
// 查找对应关系
|
|
|
|
|
final match = shipResData.firstWhere(
|
|
|
|
|
(e) => e.userID == userID,
|
|
|
|
|
orElse: () => V2TimFollowTypeCheckResult(userID: ''),
|
|
|
|
|
);
|
|
|
|
|
if (match.userID?.isNotEmpty ?? false) {
|
|
|
|
|
uwf.followType = match.followType ?? 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
final isFinished = res.data!.nextCursor == null || res.data!.nextCursor!.isEmpty;
|
|
|
|
|
if (isFinished) {
|
|
|
|
|
setState(() {
|
|
|
|
|
hasMore = false;
|
|
|
|
|
});
|
|
|
|
|
// 加载没数据了
|
|
|
|
|
page = '';
|
|
|
|
|
} else {
|
|
|
|
|
page = res.data!.nextCursor ?? '';
|
|
|
|
|
}
|
|
|
|
|
logger.i('获取数据成功:$userInfoList');
|
|
|
|
|
setState(() {
|
|
|
|
|
dataList.addAll(wrappedList);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
logger.e('获取数据失败:${res.desc}');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 下拉刷新
|
|
|
|
|
Future<void> handleRefresh() async {
|
|
|
|
|
dataList.clear();
|
|
|
|
|
page = '';
|
|
|
|
|
getData();
|
|
|
|
|
setState(() {});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
return Scaffold(
|
|
|
|
|
backgroundColor: Colors.grey[50],
|
|
|
|
|
appBar: AppBar(
|
|
|
|
|
centerTitle: true,
|
|
|
|
|
forceMaterialTransparency: true,
|
|
|
|
|
bottom: PreferredSize(
|
|
|
|
|
preferredSize: const Size.fromHeight(1.0),
|
|
|
|
|
child: Container(
|
|
|
|
|
color: Colors.grey[300],
|
|
|
|
|
height: 1.0,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
title: const Text(
|
|
|
|
|
'粉丝',
|
|
|
|
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
|
|
|
|
),
|
|
|
|
|
actions: [],
|
|
|
|
|
),
|
|
|
|
|
body: ScrollConfiguration(
|
|
|
|
|
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
Expanded(
|
|
|
|
|
child: EasyRefresh.builder(
|
|
|
|
|
callLoadOverOffset: 20, //触底距离
|
|
|
|
|
callRefreshOverOffset: 20, // 下拉距离
|
|
|
|
|
header: ClassicHeader(
|
|
|
|
|
dragText: '下拉刷新',
|
|
|
|
|
armedText: '释放刷新',
|
|
|
|
|
readyText: '加载中...',
|
|
|
|
|
processingText: '加载中...',
|
|
|
|
|
processedText: '加载完成',
|
|
|
|
|
failedText: '加载失败,请重试',
|
|
|
|
|
messageText: '最后更新于 %T',
|
|
|
|
|
),
|
|
|
|
|
footer: ClassicFooter(
|
|
|
|
|
dragText: '加载更多',
|
|
|
|
|
armedText: '释放加载',
|
|
|
|
|
readyText: '加载中...',
|
|
|
|
|
processingText: '加载中...',
|
|
|
|
|
processedText: hasMore ? '加载完成' : '没有更多了~',
|
|
|
|
|
failedText: '加载失败,请重试',
|
|
|
|
|
messageText: '最后更新于 %T',
|
|
|
|
|
),
|
|
|
|
|
onRefresh: () async {
|
|
|
|
|
await handleRefresh();
|
|
|
|
|
},
|
|
|
|
|
onLoad: () async {
|
|
|
|
|
if (hasMore) {
|
|
|
|
|
await getData();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
childBuilder: (context, physics) {
|
|
|
|
|
return ListView.builder(
|
|
|
|
|
physics: physics,
|
|
|
|
|
itemCount: dataList.length,
|
|
|
|
|
itemBuilder: (context, index) {
|
|
|
|
|
final item = dataList[index];
|
|
|
|
|
return Ink(
|
|
|
|
|
key: ValueKey(item.userInfo.userID),
|
|
|
|
|
child: Container(
|
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
|
|
|
|
child: Row(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
|
|
children: <Widget>[
|
|
|
|
|
// 左侧部分(头像 + 昵称 + 描述)
|
|
|
|
|
Expanded(
|
|
|
|
|
child: InkWell(
|
|
|
|
|
onTap: () {
|
|
|
|
|
Get.toNamed(
|
|
|
|
|
'/vloger',
|
2025-09-04 22:19:56 +08:00
|
|
|
|
arguments: {'memberId': item.userInfo.userID},
|
2025-09-03 11:25:31 +08:00
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
child: Row(
|
|
|
|
|
children: [
|
|
|
|
|
ClipOval(
|
|
|
|
|
child: NetworkOrAssetImage(
|
|
|
|
|
imageUrl: item.userInfo.faceUrl,
|
|
|
|
|
width: 50,
|
|
|
|
|
height: 50,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(width: 10),
|
|
|
|
|
Expanded(
|
|
|
|
|
child: Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: <Widget>[
|
|
|
|
|
Text(
|
|
|
|
|
item.userInfo.nickName?.isNotEmpty == true ? item.userInfo.nickName! : '未知昵称',
|
|
|
|
|
style: const TextStyle(
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
fontWeight: FontWeight.normal,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
if (item.userInfo.selfSignature?.isNotEmpty ?? false) ...[
|
|
|
|
|
const SizedBox(height: 2.0),
|
|
|
|
|
Text(
|
|
|
|
|
item.userInfo.selfSignature!,
|
|
|
|
|
style: const TextStyle(
|
|
|
|
|
color: Colors.grey,
|
|
|
|
|
fontSize: 13.0,
|
|
|
|
|
),
|
|
|
|
|
maxLines: 2,
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
SizedBox(width: 10),
|
|
|
|
|
|
|
|
|
|
// 右侧按钮
|
|
|
|
|
TextButton(
|
|
|
|
|
style: TextButton.styleFrom(
|
|
|
|
|
backgroundColor: item.followType != 3 ? FStyle.primaryColor : Colors.grey,
|
|
|
|
|
minimumSize: const Size(70, 32),
|
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
|
|
|
|
shape: RoundedRectangleBorder(
|
|
|
|
|
borderRadius: BorderRadius.circular(4),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
onPressed: () async {
|
|
|
|
|
final ctl = Get.find<ChatController>();
|
|
|
|
|
final checkRes = await ImService.instance.checkFollowType(userIDList: [item.userInfo.userID!]);
|
|
|
|
|
int realFollowType = 0;
|
|
|
|
|
if (checkRes.success && checkRes.data != null) {
|
|
|
|
|
realFollowType = checkRes.data!.first.followType ?? 0;
|
|
|
|
|
if ([1, 3].contains(realFollowType)) {
|
|
|
|
|
// 取关
|
|
|
|
|
final unRes = await ImService.instance.unfollowUser(userIDList: [item.userInfo.userID!]);
|
|
|
|
|
if (unRes.success) {
|
|
|
|
|
setState(() {
|
|
|
|
|
item.followType = 2;
|
|
|
|
|
});
|
|
|
|
|
ctl.mergeNoFriend(conversationID: 'c2c_${item.userInfo.userID!}');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 关注
|
|
|
|
|
final res = await ImService.instance.followUser(userIDList: [item.userInfo.userID!]);
|
|
|
|
|
if (res.success) {
|
|
|
|
|
setState(() {
|
|
|
|
|
item.followType = realFollowType == 0
|
|
|
|
|
? 1
|
|
|
|
|
: realFollowType == 2
|
|
|
|
|
? 3
|
|
|
|
|
: 0;
|
|
|
|
|
});
|
|
|
|
|
final chatRes = await ImService.instance.followUser(userIDList: [item.userInfo.userID!]);
|
|
|
|
|
if (chatRes.success) {
|
|
|
|
|
final res = await ImService.instance.getConversation(conversationID: 'c2c_${item.userInfo.userID}');
|
|
|
|
|
if (res.success) {
|
|
|
|
|
V2TimConversation conversation = res.data;
|
|
|
|
|
if (conversation.conversationGroupList?.isNotEmpty ?? false) {
|
|
|
|
|
await ImService.instance.deleteConversationsFromGroup(
|
|
|
|
|
groupName: conversation.conversationGroupList!.first!,
|
|
|
|
|
conversationIDList: [conversation.conversationID],
|
|
|
|
|
);
|
|
|
|
|
ctl.updateNoFriendMenu();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
child: Text(
|
|
|
|
|
Utils.getTipText(item.followType),
|
|
|
|
|
style: const TextStyle(color: Colors.white, fontSize: 14),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|