mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: copy/move fav
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
25
lib/common/widgets/radio_widget.dart
Normal file
25
lib/common/widgets/radio_widget.dart
Normal 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),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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';
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user