mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-05-15 05:33:59 +08:00
@@ -1006,4 +1006,6 @@ abstract final class Api {
|
|||||||
'${HttpString.liveBaseUrl}/xlive/app-ucenter/v1/guard/MainGuardCardAll';
|
'${HttpString.liveBaseUrl}/xlive/app-ucenter/v1/guard/MainGuardCardAll';
|
||||||
|
|
||||||
static const String bubble = '/x/tribee/v1/dyn/all';
|
static const String bubble = '/x/tribee/v1/dyn/all';
|
||||||
|
|
||||||
|
static const String sortFollowTag = '/x/relation/tags/update_sort';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import 'package:PiliPlus/http/error_msg.dart';
|
|||||||
import 'package:PiliPlus/http/init.dart';
|
import 'package:PiliPlus/http/init.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models_new/follow/data.dart';
|
import 'package:PiliPlus/models_new/follow/data.dart';
|
||||||
|
import 'package:PiliPlus/utils/accounts.dart';
|
||||||
|
import 'package:dio/dio.dart' show Options, Headers;
|
||||||
|
|
||||||
abstract final class FollowHttp {
|
abstract final class FollowHttp {
|
||||||
static Future<LoadingState<FollowData>> followings({
|
static Future<LoadingState<FollowData>> followings({
|
||||||
@@ -27,4 +29,26 @@ abstract final class FollowHttp {
|
|||||||
return Error(errorMsg[res.data['code']] ?? res.data['message']);
|
return Error(errorMsg[res.data['code']] ?? res.data['message']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<LoadingState<void>> sortFollowTag({
|
||||||
|
required String tagids,
|
||||||
|
}) async {
|
||||||
|
final res = await Request().post(
|
||||||
|
Api.sortFollowTag,
|
||||||
|
queryParameters: {
|
||||||
|
'x-bili-device-req-json':
|
||||||
|
'{"platform":"web","device":"pc","spmid":"333.1387"}',
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
'tagids': tagids,
|
||||||
|
'csrf': Accounts.main.csrf,
|
||||||
|
},
|
||||||
|
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return const Success(null);
|
||||||
|
} else {
|
||||||
|
return Error(res.data['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ class _FavFolderSortPageState extends State<FavFolderSortPage> {
|
|||||||
if (res.isSuccess) {
|
if (res.isSuccess) {
|
||||||
SmartDialog.showToast('排序完成');
|
SmartDialog.showToast('排序完成');
|
||||||
_favController.loadingState.value = Success(sortList);
|
_favController.loadingState.value = Success(sortList);
|
||||||
Get.back();
|
if (mounted) {
|
||||||
|
Get.back();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
res.toast();
|
res.toast();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,22 +51,25 @@ class _FavSortPageState extends State<FavSortPage> {
|
|||||||
title: Text('排序: ${_favDetailController.folderInfo.value.title}'),
|
title: Text('排序: ${_favDetailController.folderInfo.value.title}'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () {
|
||||||
if (sort.isEmpty) {
|
if (sort.isEmpty) {
|
||||||
Get.back();
|
Get.back();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final res = await FavHttp.sortFav(
|
FavHttp.sortFav(
|
||||||
mediaId: _favDetailController.mediaId,
|
mediaId: _favDetailController.mediaId,
|
||||||
sort: sort.join(','),
|
sort: sort.join(','),
|
||||||
);
|
).then((res) {
|
||||||
if (res.isSuccess) {
|
if (res.isSuccess) {
|
||||||
SmartDialog.showToast('排序完成');
|
SmartDialog.showToast('排序完成');
|
||||||
_favDetailController.loadingState.value = Success(sortList);
|
_favDetailController.loadingState.value = Success(sortList);
|
||||||
Get.back();
|
if (mounted) {
|
||||||
} else {
|
Get.back();
|
||||||
res.toast();
|
}
|
||||||
}
|
} else {
|
||||||
|
res.toast();
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
child: const Text('完成'),
|
child: const Text('完成'),
|
||||||
),
|
),
|
||||||
@@ -108,7 +111,7 @@ class _FavSortPageState extends State<FavSortPage> {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final item = sortList[index];
|
final item = sortList[index];
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
key: Key(item.id.toString()),
|
key: ValueKey(item.id),
|
||||||
height: 98,
|
height: 98,
|
||||||
child: FavVideoCardH(item: item),
|
child: FavVideoCardH(item: item),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -38,17 +38,40 @@ class FollowChildPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _FollowChildPageState extends State<FollowChildPage>
|
class _FollowChildPageState extends State<FollowChildPage>
|
||||||
with AutomaticKeepAliveClientMixin {
|
with AutomaticKeepAliveClientMixin {
|
||||||
late final FollowChildController _followController;
|
late String _tag;
|
||||||
|
late FollowChildController _followController;
|
||||||
|
|
||||||
|
String get _newTag =>
|
||||||
|
'${widget.tag ?? Utils.generateRandomString(8)}${widget.tagid}';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_initController();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initController() {
|
||||||
|
_tag = _newTag;
|
||||||
_followController = Get.put(
|
_followController = Get.put(
|
||||||
FollowChildController(widget.controller, widget.mid, widget.tagid),
|
FollowChildController(widget.controller, widget.mid, widget.tagid),
|
||||||
tag: '${widget.tag ?? Utils.generateRandomString(8)}${widget.tagid}',
|
tag: _tag,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(FollowChildPage oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (oldWidget.tagid != widget.tagid) {
|
||||||
|
final newTag = _newTag;
|
||||||
|
if (Get.isRegistered<FollowChildController>(tag: newTag)) {
|
||||||
|
_followController = Get.find<FollowChildController>(tag: newTag);
|
||||||
|
} else {
|
||||||
|
Get.delete<FollowChildController>(tag: _tag);
|
||||||
|
_initController();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:PiliPlus/models/member/tags.dart';
|
|||||||
import 'package:PiliPlus/pages/follow/child/child_controller.dart';
|
import 'package:PiliPlus/pages/follow/child/child_controller.dart';
|
||||||
import 'package:PiliPlus/pages/follow/child/child_view.dart';
|
import 'package:PiliPlus/pages/follow/child/child_view.dart';
|
||||||
import 'package:PiliPlus/pages/follow/controller.dart';
|
import 'package:PiliPlus/pages/follow/controller.dart';
|
||||||
|
import 'package:PiliPlus/pages/follow_tag_sort/view.dart';
|
||||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||||
import 'package:PiliPlus/utils/request_utils.dart';
|
import 'package:PiliPlus/utils/request_utils.dart';
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
@@ -71,6 +72,16 @@ class _FollowPageState extends State<FollowPage> {
|
|||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
tooltip: '新建分组',
|
tooltip: '新建分组',
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (_followController.followState.value is! Success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Get.to(FollowTagSortPage(controller: _followController));
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.sort),
|
||||||
|
tooltip: '分组排序',
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => Get.toNamed(
|
onPressed: () => Get.toNamed(
|
||||||
'/followSearch',
|
'/followSearch',
|
||||||
@@ -87,10 +98,10 @@ class _FollowPageState extends State<FollowPage> {
|
|||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
onTap: () => Get.toNamed('/blackListPage'),
|
onTap: () => Get.toNamed('/blackListPage'),
|
||||||
child: const Row(
|
child: const Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
spacing: 10,
|
||||||
|
mainAxisSize: .min,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.block, size: 19),
|
Icon(Icons.block, size: 19),
|
||||||
SizedBox(width: 10),
|
|
||||||
Text('黑名单管理'),
|
Text('黑名单管理'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -109,10 +120,6 @@ class _FollowPageState extends State<FollowPage> {
|
|||||||
tagid: item?.tagid,
|
tagid: item?.tagid,
|
||||||
);
|
);
|
||||||
|
|
||||||
bool _isCustomTag(int? tagid) {
|
|
||||||
return tagid != null && tagid != 0 && tagid != -10 && tagid != -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildBody(LoadingState loadingState) {
|
Widget _buildBody(LoadingState loadingState) {
|
||||||
return switch (loadingState) {
|
return switch (loadingState) {
|
||||||
Loading() => m3eLoading,
|
Loading() => m3eLoading,
|
||||||
@@ -128,7 +135,7 @@ class _FollowPageState extends State<FollowPage> {
|
|||||||
return Obx(() {
|
return Obx(() {
|
||||||
final item = _followController.tabs[index];
|
final item = _followController.tabs[index];
|
||||||
int? count = item.count;
|
int? count = item.count;
|
||||||
if (_isCustomTag(item.tagid)) {
|
if (Utils.isCustomFollowTag(item.tagid)) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
|
|||||||
128
lib/pages/follow_tag_sort/view.dart
Normal file
128
lib/pages/follow_tag_sort/view.dart
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import 'package:PiliPlus/http/follow.dart';
|
||||||
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/models/member/tags.dart';
|
||||||
|
import 'package:PiliPlus/pages/follow/controller.dart';
|
||||||
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class FollowTagSortPage extends StatefulWidget {
|
||||||
|
const FollowTagSortPage({super.key, required this.controller});
|
||||||
|
|
||||||
|
final FollowController controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FollowTagSortPage> createState() => _FollowTagSortPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FollowTagSortPageState extends State<FollowTagSortPage> {
|
||||||
|
late ColorScheme _scheme;
|
||||||
|
final GlobalKey _key = GlobalKey();
|
||||||
|
final List<MemberTagItemModel> _defTags = <MemberTagItemModel>[];
|
||||||
|
final List<MemberTagItemModel> _customTags = <MemberTagItemModel>[];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
for (final e in widget.controller.tabs) {
|
||||||
|
if (Utils.isCustomFollowTag(e.tagid)) {
|
||||||
|
_customTags.add(e);
|
||||||
|
} else {
|
||||||
|
_defTags.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
_scheme = ColorScheme.of(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
resizeToAvoidBottomInset: false,
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('关注分组排序'),
|
||||||
|
actions: _customTags.isNotEmpty
|
||||||
|
? [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final res = await FollowHttp.sortFollowTag(
|
||||||
|
tagids: _customTags.map((e) => e.tagid).join(','),
|
||||||
|
);
|
||||||
|
if (res.isSuccess) {
|
||||||
|
SmartDialog.showToast('排序完成');
|
||||||
|
final tabs = _defTags + _customTags;
|
||||||
|
widget.controller
|
||||||
|
..tabs.value = tabs
|
||||||
|
..onInitTab()
|
||||||
|
..followState.value = Success(tabs.hashCode);
|
||||||
|
if (mounted) {
|
||||||
|
Get.back();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.toast();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text('完成'),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
]
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
body: _buildBody,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onReorder(int oldIndex, int newIndex) {
|
||||||
|
if (newIndex > oldIndex) {
|
||||||
|
newIndex -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
final tabsItem = _customTags.removeAt(oldIndex);
|
||||||
|
_customTags.insert(newIndex, tabsItem);
|
||||||
|
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget get _buildBody {
|
||||||
|
return ReorderableListView.builder(
|
||||||
|
key: _key,
|
||||||
|
onReorder: onReorder,
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
padding:
|
||||||
|
MediaQuery.viewPaddingOf(context).copyWith(top: 0) +
|
||||||
|
const EdgeInsets.only(bottom: 100),
|
||||||
|
header: Column(
|
||||||
|
children: _defTags.map((e) => _buildItem(e, enabled: false)).toList(),
|
||||||
|
),
|
||||||
|
itemCount: _customTags.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return _buildItem(_customTags[index]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildItem(
|
||||||
|
MemberTagItemModel item, {
|
||||||
|
bool enabled = true,
|
||||||
|
}) {
|
||||||
|
return ListTile(
|
||||||
|
textColor: enabled ? null : _scheme.outline,
|
||||||
|
key: ValueKey(item.tagid),
|
||||||
|
leading: enabled
|
||||||
|
? const Icon(Icons.group_outlined)
|
||||||
|
: Icon(
|
||||||
|
size: 23,
|
||||||
|
Icons.lock_outline,
|
||||||
|
color: _scheme.outline,
|
||||||
|
),
|
||||||
|
minLeadingWidth: 0,
|
||||||
|
title: Text('${item.name} (${item.count})'),
|
||||||
|
subtitle: item.tip?.isNotEmpty == true ? Text(item.tip!) : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,10 @@ abstract final class Utils {
|
|||||||
|
|
||||||
static const jsonEncoder = JsonEncoder.withIndent(' ');
|
static const jsonEncoder = JsonEncoder.withIndent(' ');
|
||||||
|
|
||||||
|
static bool isCustomFollowTag(int? tagid) {
|
||||||
|
return tagid != null && tagid != 0 && tagid != -10 && tagid != -2;
|
||||||
|
}
|
||||||
|
|
||||||
static String levelName(
|
static String levelName(
|
||||||
Object level, {
|
Object level, {
|
||||||
bool isSeniorMember = false,
|
bool isSeniorMember = false,
|
||||||
|
|||||||
Reference in New Issue
Block a user