feat: copy/move fav

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-01-27 14:43:17 +08:00
parent 896510f852
commit 49b631d560
8 changed files with 224 additions and 29 deletions

View File

@@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
Widget radioWidget<T>({
required T value,
T? groupValue,
required ValueChanged onChanged,
required String title,
double? paddingStart,
}) {
return InkWell(
onTap: () => onChanged(value),
child: Row(
children: [
if (paddingStart != null) SizedBox(width: paddingStart),
Radio(
value: value,
groupValue: groupValue,
onChanged: onChanged,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
Text(title),
],
),
);
}

View File

@@ -121,7 +121,11 @@ class Api {
// up_mid num 目标用户mid 必要 // up_mid num 目标用户mid 必要
// type num 目标内容属性 非必要 默认为全部 0全部 2视频稿件 // type num 目标内容属性 非必要 默认为全部 0全部 2视频稿件
// rid num 目标 视频稿件avid // rid num 目标 视频稿件avid
static const String videoInFolder = '/x/v3/fav/folder/created/list-all'; static const String favFolder = '/x/v3/fav/folder/created/list-all';
static const String copyFav = '/x/v3/fav/resource/copy';
static const String moveFav = '/x/v3/fav/resource/move';
// 视频详情页 相关视频 // 视频详情页 相关视频
static const String relatedList = '/x/web-interface/archive/related'; static const String relatedList = '/x/web-interface/archive/related';

View File

@@ -676,6 +676,45 @@ class VideoHttp {
// } // }
// } // }
static Future copyOrMoveFav({
required bool isCopy,
required dynamic srcMediaId,
required dynamic tarMediaId,
dynamic mid,
required List resources,
}) async {
var res = await Request().post(
isCopy ? Api.copyFav : Api.moveFav,
data: {
if (srcMediaId != null) 'src_media_id': srcMediaId,
'tar_media_id': tarMediaId,
if (mid != null) 'mid': mid,
'resources': resources.join(','),
'platform': 'web',
'csrf': await Request.getCsrf(),
},
options: Options(contentType: Headers.formUrlEncodedContentType),
);
if (res.data['code'] == 0) {
return {'status': true};
} else {
return {'status': false, 'msg': res.data['message']};
}
}
static Future allFavFolders(mid) async {
var res = await Request().get(
Api.favFolder,
queryParameters: {'up_mid': mid},
);
if (res.data['code'] == 0) {
FavFolderData data = FavFolderData.fromJson(res.data['data']);
return {'status': true, 'data': data};
} else {
return {'status': false, 'msg': res.data['message']};
}
}
// 查看视频被收藏在哪个文件夹 // 查看视频被收藏在哪个文件夹
static Future videoInFolder({ static Future videoInFolder({
dynamic mid, dynamic mid,
@@ -683,7 +722,7 @@ class VideoHttp {
dynamic type, dynamic type,
}) async { }) async {
var res = await Request().get( var res = await Request().get(
Api.videoInFolder, Api.favFolder,
queryParameters: { queryParameters: {
'up_mid': mid, 'up_mid': mid,
'rid': rid, 'rid': rid,

View File

@@ -1,6 +1,6 @@
import 'package:PiliPlus/common/widgets/radio_widget.dart';
import 'package:PiliPlus/http/index.dart'; import 'package:PiliPlus/http/index.dart';
import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/pages/member/view.dart' show radioWidget;
import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';

View File

@@ -17,6 +17,8 @@ class FavDetailController extends MultiSelectController {
RxBool isOwner = false.obs; RxBool isOwner = false.obs;
RxBool titleCtr = false.obs; RxBool titleCtr = false.obs;
dynamic mid;
@override @override
void onInit() { void onInit() {
// item = Get.arguments; // item = Get.arguments;
@@ -26,6 +28,8 @@ class FavDetailController extends MultiSelectController {
} }
super.onInit(); super.onInit();
mid = GStorage.userInfo.get('userInfoCache')?.mid;
queryData(); queryData();
} }
@@ -33,8 +37,7 @@ class FavDetailController extends MultiSelectController {
bool customHandleResponse(Success response) { bool customHandleResponse(Success response) {
if (currentPage == 1) { if (currentPage == 1) {
item.value = response.response.info; item.value = response.response.info;
isOwner.value = response.response.info.mid == isOwner.value = response.response.info.mid == mid;
GStorage.userInfo.get('userInfoCache')?.mid;
} }
if (response.response.medias.isEmpty) { if (response.response.medias.isEmpty) {
isEnd = true; isEnd = true;

View File

@@ -111,11 +111,57 @@ class _FavDetailPageState extends State<FavDetailPage> {
actions: _favDetailController.enableMultiSelect.value actions: _favDetailController.enableMultiSelect.value
? [ ? [
TextButton( TextButton(
style: TextButton.styleFrom(
visualDensity:
VisualDensity(horizontal: -2, vertical: -2),
),
onPressed: () => onPressed: () =>
_favDetailController.handleSelect(true), _favDetailController.handleSelect(true),
child: const Text('全选'), child: const Text('全选'),
), ),
TextButton( TextButton(
style: TextButton.styleFrom(
visualDensity:
VisualDensity(horizontal: -2, vertical: -2),
),
onPressed: () => Utils.onCopyOrMove(
context: context,
isCopy: true,
ctr: _favDetailController,
),
child: Text(
'复制到',
style: TextStyle(
color: Theme.of(context)
.colorScheme
.onSurfaceVariant,
),
),
),
TextButton(
style: TextButton.styleFrom(
visualDensity:
VisualDensity(horizontal: -2, vertical: -2),
),
onPressed: () => Utils.onCopyOrMove(
context: context,
isCopy: false,
ctr: _favDetailController,
),
child: Text(
'移动到',
style: TextStyle(
color: Theme.of(context)
.colorScheme
.onSurfaceVariant,
),
),
),
TextButton(
style: TextButton.styleFrom(
visualDensity:
VisualDensity(horizontal: -2, vertical: -2),
),
onPressed: () => onPressed: () =>
_favDetailController.onDelChecked(context), _favDetailController.onDelChecked(context),
child: Text( child: Text(

View File

@@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:PiliPlus/common/widgets/radio_widget.dart';
import 'package:PiliPlus/http/member.dart'; import 'package:PiliPlus/http/member.dart';
import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/extension.dart';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
@@ -649,27 +650,3 @@ Widget _checkBoxWidget(
), ),
); );
} }
Widget radioWidget<T>({
required T value,
T? groupValue,
required ValueChanged onChanged,
required String title,
double? paddingStart,
}) {
return InkWell(
onTap: () => onChanged(value),
child: Row(
children: [
if (paddingStart != null) SizedBox(width: paddingStart),
Radio(
value: value,
groupValue: groupValue,
onChanged: onChanged,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
Text(title),
],
),
);
}

View File

@@ -4,10 +4,12 @@ import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:PiliPlus/build_config.dart'; import 'package:PiliPlus/build_config.dart';
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'; import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart';
import 'package:PiliPlus/common/widgets/radio_widget.dart';
import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart'; import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart';
import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/api.dart';
import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/http/constants.dart';
import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/member.dart'; import 'package:PiliPlus/http/member.dart';
import 'package:PiliPlus/http/search.dart'; import 'package:PiliPlus/http/search.dart';
import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/http/video.dart';
@@ -15,6 +17,7 @@ import 'package:PiliPlus/models/bangumi/info.dart';
import 'package:PiliPlus/models/common/search_type.dart'; import 'package:PiliPlus/models/common/search_type.dart';
import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/models/live/item.dart'; import 'package:PiliPlus/models/live/item.dart';
import 'package:PiliPlus/models/user/fav_folder.dart';
import 'package:PiliPlus/pages/video/detail/introduction/widgets/fav_panel.dart'; import 'package:PiliPlus/pages/video/detail/introduction/widgets/fav_panel.dart';
import 'package:PiliPlus/pages/video/detail/introduction/widgets/group_panel.dart'; import 'package:PiliPlus/pages/video/detail/introduction/widgets/group_panel.dart';
import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/extension.dart';
@@ -39,6 +42,104 @@ class Utils {
static const channel = MethodChannel("PiliPlus"); static const channel = MethodChannel("PiliPlus");
static void onCopyOrMove({
required BuildContext context,
required bool isCopy,
required dynamic ctr,
dynamic mediaId,
}) {
VideoHttp.allFavFolders(ctr.mid).then((res) {
if (context.mounted &&
res['status'] &&
(res['data'].list as List?)?.isNotEmpty == true) {
List<FavFolderItemData> list = res['data'].list;
dynamic checkedId;
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('${isCopy ? '复制' : '移动'}'),
contentPadding: const EdgeInsets.only(top: 5),
content: SingleChildScrollView(
child: Builder(
builder: (context) => Column(
children: List.generate(list.length, (index) {
return radioWidget(
paddingStart: 14,
title: list[index].title ?? '',
groupValue: checkedId,
value: list[index].id,
onChanged: (value) {
checkedId = value;
if (context.mounted) {
(context as Element).markNeedsBuild();
}
},
);
}),
),
),
),
actions: [
TextButton(
onPressed: Get.back,
child: Text(
'取消',
style:
TextStyle(color: Theme.of(context).colorScheme.outline),
),
),
TextButton(
onPressed: () {
if (checkedId != null) {
List resources =
((ctr.loadingState.value as Success).response as List)
.where((e) => e.checked == true)
.toList();
SmartDialog.showLoading();
VideoHttp.copyOrMoveFav(
isCopy: isCopy,
srcMediaId: mediaId,
tarMediaId: checkedId,
resources: resources
.map((item) => '${item.id}:${item.type}')
.toList(),
mid: isCopy ? ctr.mid : null,
).then((res) {
if (res['status']) {
ctr.handleSelect(false);
if (isCopy.not) {
List dataList =
(ctr.loadingState.value as Success).response;
List remainList = dataList
.toSet()
.difference(resources.toSet())
.toList();
ctr.loadingState.value =
LoadingState.success(remainList);
}
SmartDialog.dismiss();
SmartDialog.showToast('${isCopy ? '复制' : '移动'}成功');
Get.back();
} else {
SmartDialog.dismiss();
SmartDialog.showToast('${res['msg']}');
}
});
}
},
child: Text('确认'),
),
],
);
},
);
} else {
SmartDialog.showToast('${res['msg']}');
}
});
}
static void showFavBottomSheet({ static void showFavBottomSheet({
required BuildContext context, required BuildContext context,
required dynamic ctr, required dynamic ctr,