diff --git a/lib/models/space/relation.dart b/lib/models/space/relation.dart index ff2237e9..6eaeab08 100644 --- a/lib/models/space/relation.dart +++ b/lib/models/space/relation.dart @@ -4,15 +4,21 @@ part 'relation.g.dart'; @JsonSerializable() class Relation { - int? status; - @JsonKey(name: 'is_follow') - int? isFollow; + int? status; + @JsonKey(name: 'is_follow') + int? isFollow; + @JsonKey(name: 'is_followed') + int? isFollowed; - Relation({this.status, this.isFollow}); + Relation({ + this.status, + this.isFollow, + this.isFollowed, + }); - factory Relation.fromJson(Map json) { - return _$RelationFromJson(json); - } + factory Relation.fromJson(Map json) { + return _$RelationFromJson(json); + } - Map toJson() => _$RelationToJson(this); + Map toJson() => _$RelationToJson(this); } diff --git a/lib/models/space/relation.g.dart b/lib/models/space/relation.g.dart index 1e9047c6..73f7a1c2 100644 --- a/lib/models/space/relation.g.dart +++ b/lib/models/space/relation.g.dart @@ -9,9 +9,11 @@ part of 'relation.dart'; Relation _$RelationFromJson(Map json) => Relation( status: (json['status'] as num?)?.toInt(), isFollow: (json['is_follow'] as num?)?.toInt(), + isFollowed: (json['is_followed'] as num?)?.toInt(), ); Map _$RelationToJson(Relation instance) => { 'status': instance.status, 'is_follow': instance.isFollow, + 'is_followed': instance.isFollowed, }; diff --git a/lib/pages/fan/controller.dart b/lib/pages/fan/controller.dart index d04c45df..76121cfe 100644 --- a/lib/pages/fan/controller.dart +++ b/lib/pages/fan/controller.dart @@ -1,7 +1,9 @@ import 'package:PiliPlus/http/fan.dart'; import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/models/fans/result.dart'; import 'package:PiliPlus/pages/common/common_list_controller.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:PiliPlus/utils/storage.dart'; @@ -12,7 +14,7 @@ class FansController late int? mid; late String? name; dynamic userInfo; - RxBool isOwner = false.obs; + late bool isOwner = false; @override void onInit() { @@ -21,7 +23,7 @@ class FansController mid = Get.parameters['mid'] != null ? int.parse(Get.parameters['mid']!) : userInfo?.mid; - isOwner.value = mid == userInfo?.mid; + isOwner = mid == userInfo?.mid; name = Get.parameters['name'] ?? userInfo?.uname; queryData(); @@ -39,4 +41,20 @@ class FansController ps: ps, orderType: 'attention', ); + + Future onRemoveFan(int index, int mid) async { + final res = await VideoHttp.relationMod( + mid: mid, + act: 7, + reSrc: 11, + ); + if (res['status']) { + List list = (loadingState.value as Success).response; + list.removeAt(index); + loadingState.refresh(); + SmartDialog.showToast('移除成功'); + } else { + SmartDialog.showToast(res['msg']); + } + } } diff --git a/lib/pages/fan/view.dart b/lib/pages/fan/view.dart index 56fc12e6..7e2263cd 100644 --- a/lib/pages/fan/view.dart +++ b/lib/pages/fan/view.dart @@ -1,4 +1,6 @@ import 'package:PiliPlus/common/skeleton/msg_feed_top.dart'; +import 'package:PiliPlus/common/widgets/dialog.dart'; +import 'package:PiliPlus/common/widgets/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/fans/result.dart'; @@ -9,7 +11,6 @@ import 'package:PiliPlus/common/widgets/http_error.dart'; import '../../utils/grid.dart'; import 'controller.dart'; -import 'widgets/fan_item.dart'; class FansPage extends StatefulWidget { const FansPage({super.key}); @@ -34,7 +35,7 @@ class _FansPageState extends State { return Scaffold( appBar: AppBar( title: Text( - _fansController.isOwner.value ? '我的粉丝' : '${_fansController.name}的粉丝', + _fansController.isOwner ? '我的粉丝' : '${_fansController.name}的粉丝', ), ), body: SafeArea( @@ -81,7 +82,47 @@ class _FansPageState extends State { if (index == loadingState.response!.length - 1) { _fansController.onLoadMore(); } - return fanItem(item: loadingState.response![index]); + final item = loadingState.response![index]; + String heroTag = Utils.makeHeroTag(item.mid); + return ListTile( + onTap: () { + Get.toNamed( + '/member?mid=${item.mid}', + arguments: {'face': item.face, 'heroTag': heroTag}, + ); + }, + onLongPress: _fansController.isOwner + ? () { + showConfirmDialog( + context: context, + title: '确定移除 ${item.uname} ?', + onConfirm: () { + _fansController.onRemoveFan(index, item.mid!); + }, + ); + } + : null, + leading: Hero( + tag: heroTag, + child: NetworkImgLayer( + width: 45, + height: 45, + type: 'avatar', + src: item.face, + ), + ), + title: Text( + item.uname!, + style: const TextStyle(fontSize: 14), + ), + subtitle: Text( + item.sign ?? '', + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + dense: true, + trailing: const SizedBox(width: 6), + ); }, childCount: loadingState.response!.length, ), diff --git a/lib/pages/fan/widgets/fan_item.dart b/lib/pages/fan/widgets/fan_item.dart deleted file mode 100644 index 7504233f..00000000 --- a/lib/pages/fan/widgets/fan_item.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:PiliPlus/common/widgets/network_img_layer.dart'; -import 'package:PiliPlus/utils/utils.dart'; - -Widget fanItem({item}) { - String heroTag = Utils.makeHeroTag(item!.mid); - return ListTile( - onTap: () => Get.toNamed('/member?mid=${item.mid}', - arguments: {'face': item.face, 'heroTag': heroTag}), - leading: Hero( - tag: heroTag, - child: NetworkImgLayer( - width: 45, - height: 45, - type: 'avatar', - src: item.face, - ), - ), - title: Text( - item.uname, - style: const TextStyle(fontSize: 14), - ), - subtitle: Text( - item.sign, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - dense: true, - trailing: const SizedBox(width: 6), - ); -} diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index b80f3ee2..7f37c60c 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -25,31 +25,22 @@ class MemberControllerNew extends CommonDataController with GetTickerProviderStateMixin { MemberControllerNew({required this.mid}); int mid; - RxBool showUname = false.obs; - String? username; int? ownerMid; - RxInt relation = 0.obs; - TabController? tabController; - late List tabs; - List? tab2; - RxInt contributeInitialIndex = 0.obs; - bool? hasSeasonOrSeries; - final fromViewAid = Get.parameters['from_view_aid']; - - bool get isFollow => relation.value != 0 && relation.value != 128; - - @override - void onInit() { - super.onInit(); - ownerMid = Accounts.main.mid; - queryData(); - } + String? username; + RxBool showUname = false.obs; dynamic live; - int? silence; String? endTime; + int? isFollowed; // 被关注 + RxInt relation = 0.obs; + bool get isFollow => relation.value != 0 && relation.value != 128; + + List? tab2; + late List tabs; + TabController? tabController; + RxInt contributeInitialIndex = 0.obs; late final implTabs = const [ 'home', 'dynamic', @@ -58,10 +49,22 @@ class MemberControllerNew extends CommonDataController 'bangumi', ]; + bool? hasSeasonOrSeries; + + final fromViewAid = Get.parameters['from_view_aid']; + + @override + void onInit() { + super.onInit(); + ownerMid = Accounts.main.mid; + queryData(); + } + @override bool customHandleResponse(bool isRefresh, Success response) { Data data = response.response; username = data.card?.name ?? ''; + isFollowed = data.card?.relation?.isFollowed; if (data.relation == -1) { relation.value = 128; } else { @@ -69,7 +72,7 @@ class MemberControllerNew extends CommonDataController ? data.relSpecial == 1 ? -10 : data.card?.relation?.status ?? 2 - : 0; + : data.card?.relation?.status ?? 0; } tab2 = data.tab2; live = data.live; @@ -226,4 +229,21 @@ class MemberControllerNew extends CommonDataController tabController?.dispose(); super.onClose(); } + + Future onRemoveFan() async { + final res = await VideoHttp.relationMod( + mid: mid, + act: 7, + reSrc: 11, + ); + if (res['status']) { + isFollowed = null; + if (relation.value == 4) { + relation.value = 2; + } + SmartDialog.showToast('移除成功'); + } else { + SmartDialog.showToast(res['msg']); + } + } } diff --git a/lib/pages/member/member_page.dart b/lib/pages/member/member_page.dart index 89b39625..382073fa 100644 --- a/lib/pages/member/member_page.dart +++ b/lib/pages/member/member_page.dart @@ -84,7 +84,8 @@ class _MemberPageNewState extends State { PopupMenuButton( icon: const Icon(Icons.more_vert), itemBuilder: (BuildContext context) => [ - if (_userController.ownerMid != _mid) ...[ + if (_userController.ownerMid != 0 && + _userController.ownerMid != _mid) ...[ PopupMenuItem( onTap: () => _userController.blockUser(context), child: Row( @@ -97,7 +98,19 @@ class _MemberPageNewState extends State { : '移除黑名单'), ], ), - ) + ), + if (_userController.isFollowed == 1) + PopupMenuItem( + onTap: _userController.onRemoveFan, + child: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Icon(Icons.remove_circle_outline_outlined, size: 19), + SizedBox(width: 10), + Text('移除粉丝'), + ], + ), + ), ], PopupMenuItem( onTap: () => _userController.shareUser(), diff --git a/lib/pages/member/widget/user_info_card.dart b/lib/pages/member/widget/user_info_card.dart index dc2218eb..382f3d69 100644 --- a/lib/pages/member/widget/user_info_card.dart +++ b/lib/pages/member/widget/user_info_card.dart @@ -375,7 +375,7 @@ class UserInfoCard extends StatelessWidget { child: FilledButton.tonal( onPressed: onFollow, style: FilledButton.styleFrom( - backgroundColor: relation != 0 + backgroundColor: relation != 0 && relation != 3 ? Theme.of(context).colorScheme.onInverseSurface : null, visualDensity: const VisualDensity( @@ -385,13 +385,13 @@ class UserInfoCard extends StatelessWidget { ), child: Text.rich( style: TextStyle( - color: relation != 0 + color: relation != 0 && relation != 3 ? Theme.of(context).colorScheme.outline : null, ), TextSpan( children: [ - if (relation != 0 && relation != 128) + if (relation != 0 && relation != 128 && relation != 3) WidgetSpan( alignment: PlaceholderAlignment.top, child: Icon( @@ -407,6 +407,7 @@ class UserInfoCard extends StatelessWidget { 0 => '关注', 1 => '悄悄关注', 2 => '已关注', + 3 => '回关', 4 || 6 => '已互关', 128 => '移除黑名单', -10 => '特别关注', // 该状态码并不是官方状态码