mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: remove fan
Closes #733 Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -4,15 +4,21 @@ part 'relation.g.dart';
|
|||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class Relation {
|
class Relation {
|
||||||
int? status;
|
int? status;
|
||||||
@JsonKey(name: 'is_follow')
|
@JsonKey(name: 'is_follow')
|
||||||
int? isFollow;
|
int? isFollow;
|
||||||
|
@JsonKey(name: 'is_followed')
|
||||||
|
int? isFollowed;
|
||||||
|
|
||||||
Relation({this.status, this.isFollow});
|
Relation({
|
||||||
|
this.status,
|
||||||
|
this.isFollow,
|
||||||
|
this.isFollowed,
|
||||||
|
});
|
||||||
|
|
||||||
factory Relation.fromJson(Map<String, dynamic> json) {
|
factory Relation.fromJson(Map<String, dynamic> json) {
|
||||||
return _$RelationFromJson(json);
|
return _$RelationFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$RelationToJson(this);
|
Map<String, dynamic> toJson() => _$RelationToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ part of 'relation.dart';
|
|||||||
Relation _$RelationFromJson(Map<String, dynamic> json) => Relation(
|
Relation _$RelationFromJson(Map<String, dynamic> json) => Relation(
|
||||||
status: (json['status'] as num?)?.toInt(),
|
status: (json['status'] as num?)?.toInt(),
|
||||||
isFollow: (json['is_follow'] as num?)?.toInt(),
|
isFollow: (json['is_follow'] as num?)?.toInt(),
|
||||||
|
isFollowed: (json['is_followed'] as num?)?.toInt(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$RelationToJson(Relation instance) => <String, dynamic>{
|
Map<String, dynamic> _$RelationToJson(Relation instance) => <String, dynamic>{
|
||||||
'status': instance.status,
|
'status': instance.status,
|
||||||
'is_follow': instance.isFollow,
|
'is_follow': instance.isFollow,
|
||||||
|
'is_followed': instance.isFollowed,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import 'package:PiliPlus/http/fan.dart';
|
import 'package:PiliPlus/http/fan.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/http/video.dart';
|
||||||
import 'package:PiliPlus/models/fans/result.dart';
|
import 'package:PiliPlus/models/fans/result.dart';
|
||||||
import 'package:PiliPlus/pages/common/common_list_controller.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:get/get.dart';
|
||||||
import 'package:PiliPlus/utils/storage.dart';
|
import 'package:PiliPlus/utils/storage.dart';
|
||||||
|
|
||||||
@@ -12,7 +14,7 @@ class FansController
|
|||||||
late int? mid;
|
late int? mid;
|
||||||
late String? name;
|
late String? name;
|
||||||
dynamic userInfo;
|
dynamic userInfo;
|
||||||
RxBool isOwner = false.obs;
|
late bool isOwner = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@@ -21,7 +23,7 @@ class FansController
|
|||||||
mid = Get.parameters['mid'] != null
|
mid = Get.parameters['mid'] != null
|
||||||
? int.parse(Get.parameters['mid']!)
|
? int.parse(Get.parameters['mid']!)
|
||||||
: userInfo?.mid;
|
: userInfo?.mid;
|
||||||
isOwner.value = mid == userInfo?.mid;
|
isOwner = mid == userInfo?.mid;
|
||||||
name = Get.parameters['name'] ?? userInfo?.uname;
|
name = Get.parameters['name'] ?? userInfo?.uname;
|
||||||
|
|
||||||
queryData();
|
queryData();
|
||||||
@@ -39,4 +41,20 @@ class FansController
|
|||||||
ps: ps,
|
ps: ps,
|
||||||
orderType: 'attention',
|
orderType: 'attention',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Future onRemoveFan(int index, int mid) async {
|
||||||
|
final res = await VideoHttp.relationMod(
|
||||||
|
mid: mid,
|
||||||
|
act: 7,
|
||||||
|
reSrc: 11,
|
||||||
|
);
|
||||||
|
if (res['status']) {
|
||||||
|
List<FansItemModel> list = (loadingState.value as Success).response;
|
||||||
|
list.removeAt(index);
|
||||||
|
loadingState.refresh();
|
||||||
|
SmartDialog.showToast('移除成功');
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import 'package:PiliPlus/common/skeleton/msg_feed_top.dart';
|
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/common/widgets/refresh_indicator.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models/fans/result.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 '../../utils/grid.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
import 'widgets/fan_item.dart';
|
|
||||||
|
|
||||||
class FansPage extends StatefulWidget {
|
class FansPage extends StatefulWidget {
|
||||||
const FansPage({super.key});
|
const FansPage({super.key});
|
||||||
@@ -34,7 +35,7 @@ class _FansPageState extends State<FansPage> {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
_fansController.isOwner.value ? '我的粉丝' : '${_fansController.name}的粉丝',
|
_fansController.isOwner ? '我的粉丝' : '${_fansController.name}的粉丝',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
@@ -81,7 +82,47 @@ class _FansPageState extends State<FansPage> {
|
|||||||
if (index == loadingState.response!.length - 1) {
|
if (index == loadingState.response!.length - 1) {
|
||||||
_fansController.onLoadMore();
|
_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,
|
childCount: loadingState.response!.length,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -25,31 +25,22 @@ class MemberControllerNew extends CommonDataController<Data, dynamic>
|
|||||||
with GetTickerProviderStateMixin {
|
with GetTickerProviderStateMixin {
|
||||||
MemberControllerNew({required this.mid});
|
MemberControllerNew({required this.mid});
|
||||||
int mid;
|
int mid;
|
||||||
RxBool showUname = false.obs;
|
|
||||||
String? username;
|
|
||||||
int? ownerMid;
|
int? ownerMid;
|
||||||
RxInt relation = 0.obs;
|
String? username;
|
||||||
TabController? tabController;
|
RxBool showUname = false.obs;
|
||||||
late List<Tab> tabs;
|
|
||||||
List<Tab2>? 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamic live;
|
dynamic live;
|
||||||
|
|
||||||
int? silence;
|
int? silence;
|
||||||
String? endTime;
|
String? endTime;
|
||||||
|
|
||||||
|
int? isFollowed; // 被关注
|
||||||
|
RxInt relation = 0.obs;
|
||||||
|
bool get isFollow => relation.value != 0 && relation.value != 128;
|
||||||
|
|
||||||
|
List<Tab2>? tab2;
|
||||||
|
late List<Tab> tabs;
|
||||||
|
TabController? tabController;
|
||||||
|
RxInt contributeInitialIndex = 0.obs;
|
||||||
late final implTabs = const [
|
late final implTabs = const [
|
||||||
'home',
|
'home',
|
||||||
'dynamic',
|
'dynamic',
|
||||||
@@ -58,10 +49,22 @@ class MemberControllerNew extends CommonDataController<Data, dynamic>
|
|||||||
'bangumi',
|
'bangumi',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
bool? hasSeasonOrSeries;
|
||||||
|
|
||||||
|
final fromViewAid = Get.parameters['from_view_aid'];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
ownerMid = Accounts.main.mid;
|
||||||
|
queryData();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool customHandleResponse(bool isRefresh, Success<Data> response) {
|
bool customHandleResponse(bool isRefresh, Success<Data> response) {
|
||||||
Data data = response.response;
|
Data data = response.response;
|
||||||
username = data.card?.name ?? '';
|
username = data.card?.name ?? '';
|
||||||
|
isFollowed = data.card?.relation?.isFollowed;
|
||||||
if (data.relation == -1) {
|
if (data.relation == -1) {
|
||||||
relation.value = 128;
|
relation.value = 128;
|
||||||
} else {
|
} else {
|
||||||
@@ -69,7 +72,7 @@ class MemberControllerNew extends CommonDataController<Data, dynamic>
|
|||||||
? data.relSpecial == 1
|
? data.relSpecial == 1
|
||||||
? -10
|
? -10
|
||||||
: data.card?.relation?.status ?? 2
|
: data.card?.relation?.status ?? 2
|
||||||
: 0;
|
: data.card?.relation?.status ?? 0;
|
||||||
}
|
}
|
||||||
tab2 = data.tab2;
|
tab2 = data.tab2;
|
||||||
live = data.live;
|
live = data.live;
|
||||||
@@ -226,4 +229,21 @@ class MemberControllerNew extends CommonDataController<Data, dynamic>
|
|||||||
tabController?.dispose();
|
tabController?.dispose();
|
||||||
super.onClose();
|
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']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,8 @@ class _MemberPageNewState extends State<MemberPageNew> {
|
|||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
icon: const Icon(Icons.more_vert),
|
icon: const Icon(Icons.more_vert),
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
||||||
if (_userController.ownerMid != _mid) ...[
|
if (_userController.ownerMid != 0 &&
|
||||||
|
_userController.ownerMid != _mid) ...[
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
onTap: () => _userController.blockUser(context),
|
onTap: () => _userController.blockUser(context),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -97,7 +98,19 @@ class _MemberPageNewState extends State<MemberPageNew> {
|
|||||||
: '移除黑名单'),
|
: '移除黑名单'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
|
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(
|
PopupMenuItem(
|
||||||
onTap: () => _userController.shareUser(),
|
onTap: () => _userController.shareUser(),
|
||||||
|
|||||||
@@ -375,7 +375,7 @@ class UserInfoCard extends StatelessWidget {
|
|||||||
child: FilledButton.tonal(
|
child: FilledButton.tonal(
|
||||||
onPressed: onFollow,
|
onPressed: onFollow,
|
||||||
style: FilledButton.styleFrom(
|
style: FilledButton.styleFrom(
|
||||||
backgroundColor: relation != 0
|
backgroundColor: relation != 0 && relation != 3
|
||||||
? Theme.of(context).colorScheme.onInverseSurface
|
? Theme.of(context).colorScheme.onInverseSurface
|
||||||
: null,
|
: null,
|
||||||
visualDensity: const VisualDensity(
|
visualDensity: const VisualDensity(
|
||||||
@@ -385,13 +385,13 @@ class UserInfoCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Text.rich(
|
child: Text.rich(
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: relation != 0
|
color: relation != 0 && relation != 3
|
||||||
? Theme.of(context).colorScheme.outline
|
? Theme.of(context).colorScheme.outline
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
children: [
|
children: [
|
||||||
if (relation != 0 && relation != 128)
|
if (relation != 0 && relation != 128 && relation != 3)
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
alignment: PlaceholderAlignment.top,
|
alignment: PlaceholderAlignment.top,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
@@ -407,6 +407,7 @@ class UserInfoCard extends StatelessWidget {
|
|||||||
0 => '关注',
|
0 => '关注',
|
||||||
1 => '悄悄关注',
|
1 => '悄悄关注',
|
||||||
2 => '已关注',
|
2 => '已关注',
|
||||||
|
3 => '回关',
|
||||||
4 || 6 => '已互关',
|
4 || 6 => '已互关',
|
||||||
128 => '移除黑名单',
|
128 => '移除黑名单',
|
||||||
-10 => '特别关注', // 该状态码并不是官方状态码
|
-10 => '特别关注', // 该状态码并不是官方状态码
|
||||||
|
|||||||
Reference in New Issue
Block a user