sort follow tag

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-04-27 20:53:55 +08:00
parent 89fdc28150
commit 80ecd35784
8 changed files with 214 additions and 21 deletions

View File

@@ -39,7 +39,9 @@ class _FavFolderSortPageState extends State<FavFolderSortPage> {
if (res.isSuccess) {
SmartDialog.showToast('排序完成');
_favController.loadingState.value = Success(sortList);
Get.back();
if (mounted) {
Get.back();
}
} else {
res.toast();
}

View File

@@ -51,22 +51,25 @@ class _FavSortPageState extends State<FavSortPage> {
title: Text('排序: ${_favDetailController.folderInfo.value.title}'),
actions: [
TextButton(
onPressed: () async {
onPressed: () {
if (sort.isEmpty) {
Get.back();
return;
}
final res = await FavHttp.sortFav(
FavHttp.sortFav(
mediaId: _favDetailController.mediaId,
sort: sort.join(','),
);
if (res.isSuccess) {
SmartDialog.showToast('排序完成');
_favDetailController.loadingState.value = Success(sortList);
Get.back();
} else {
res.toast();
}
).then((res) {
if (res.isSuccess) {
SmartDialog.showToast('排序完成');
_favDetailController.loadingState.value = Success(sortList);
if (mounted) {
Get.back();
}
} else {
res.toast();
}
});
},
child: const Text('完成'),
),
@@ -108,7 +111,7 @@ class _FavSortPageState extends State<FavSortPage> {
itemBuilder: (context, index) {
final item = sortList[index];
return SizedBox(
key: Key(item.id.toString()),
key: ValueKey(item.id),
height: 98,
child: FavVideoCardH(item: item),
);

View File

@@ -38,17 +38,40 @@ class FollowChildPage extends StatefulWidget {
class _FollowChildPageState extends State<FollowChildPage>
with AutomaticKeepAliveClientMixin {
late final FollowChildController _followController;
late String _tag;
late FollowChildController _followController;
String get _newTag =>
'${widget.tag ?? Utils.generateRandomString(8)}${widget.tagid}';
@override
void initState() {
super.initState();
_initController();
}
void _initController() {
_tag = _newTag;
_followController = Get.put(
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
Widget build(BuildContext context) {
super.build(context);

View File

@@ -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_view.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/request_utils.dart';
import 'package:PiliPlus/utils/utils.dart';
@@ -71,6 +72,16 @@ class _FollowPageState extends State<FollowPage> {
icon: const Icon(Icons.add),
tooltip: '新建分组',
),
IconButton(
onPressed: () {
if (_followController.followState.value is! Success) {
return;
}
Get.to(FollowTagSortPage(controller: _followController));
},
icon: const Icon(Icons.sort),
tooltip: '分组排序',
),
IconButton(
onPressed: () => Get.toNamed(
'/followSearch',
@@ -87,10 +98,10 @@ class _FollowPageState extends State<FollowPage> {
PopupMenuItem(
onTap: () => Get.toNamed('/blackListPage'),
child: const Row(
mainAxisSize: MainAxisSize.min,
spacing: 10,
mainAxisSize: .min,
children: [
Icon(Icons.block, size: 19),
SizedBox(width: 10),
Text('黑名单管理'),
],
),
@@ -109,10 +120,6 @@ class _FollowPageState extends State<FollowPage> {
tagid: item?.tagid,
);
bool _isCustomTag(int? tagid) {
return tagid != null && tagid != 0 && tagid != -10 && tagid != -2;
}
Widget _buildBody(LoadingState loadingState) {
return switch (loadingState) {
Loading() => m3eLoading,
@@ -128,7 +135,7 @@ class _FollowPageState extends State<FollowPage> {
return Obx(() {
final item = _followController.tabs[index];
int? count = item.count;
if (_isCustomTag(item.tagid)) {
if (Utils.isCustomFollowTag(item.tagid)) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onLongPress: () {

View 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,
);
}
}