opt: unify fav & coin of video & pgc (#916)

This commit is contained in:
My-Responsitories
2025-07-31 20:16:42 +08:00
committed by GitHub
parent e945daba3a
commit 05c9269531
15 changed files with 194 additions and 316 deletions

View File

@@ -1,11 +1,17 @@
import 'package:PiliPlus/http/fav.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/user.dart';
import 'package:PiliPlus/http/video.dart';
import 'package:PiliPlus/models_new/fav/fav_folder/data.dart';
import 'package:PiliPlus/models_new/video/video_detail/stat_detail.dart';
import 'package:PiliPlus/models_new/video/video_tag/data.dart';
import 'package:PiliPlus/services/account_service.dart';
import 'package:PiliPlus/utils/global_data.dart';
import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_key.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
@@ -29,9 +35,119 @@ abstract class CommonIntroController extends GetxController {
AccountService accountService = Get.find<AccountService>();
Future queryVideoInFolder();
(Object, int) getFavRidType();
Future<void> actionFavVideo({bool isQuick = false});
StatDetail? getStat();
Future<LoadingState<FavFolderData>> queryVideoInFolder() async {
favIds = null;
final (rid, type) = getFavRidType();
final result = await FavHttp.videoInFolder(
mid: accountService.mid,
rid: rid,
type: type,
);
if (result.isSuccess) {
favFolderData.value = result.data;
favIds = result.data.list
?.where((item) => item.favState == 1)
.map((item) => item.id)
.toSet();
}
return result;
}
Future<void> actionFavVideo({bool isQuick = false}) async {
final (rid, type) = getFavRidType();
// 收藏至默认文件夹
if (isQuick) {
SmartDialog.showLoading(msg: '请求中');
queryVideoInFolder().then((res) async {
if (res.isSuccess) {
final hasFav = this.hasFav.value;
var result = hasFav
? await FavHttp.unfavAll(rid: rid, type: type)
: await FavHttp.favVideo(
resources: '$rid:$type',
addIds: favFolderId.toString(),
);
SmartDialog.dismiss();
if (result['status']) {
getStat()!.favorite += hasFav ? -1 : 1;
this.hasFav.value = !hasFav;
SmartDialog.showToast('✅ 快速收藏/取消收藏成功');
} else {
SmartDialog.showToast(result['msg']);
}
} else {
SmartDialog.dismiss();
}
});
return;
}
List<int?> addMediaIdsNew = [];
List<int?> delMediaIdsNew = [];
try {
for (var i in favFolderData.value.list!) {
bool isFaved = favIds?.contains(i.id) == true;
if (i.favState == 1) {
if (!isFaved) {
addMediaIdsNew.add(i.id);
}
} else {
if (isFaved) {
delMediaIdsNew.add(i.id);
}
}
}
} catch (e) {
if (kDebugMode) debugPrint(e.toString());
}
SmartDialog.showLoading(msg: '请求中');
var result = await FavHttp.favVideo(
resources: '$rid:$type',
addIds: addMediaIdsNew.join(','),
delIds: delMediaIdsNew.join(','),
);
SmartDialog.dismiss();
if (result['status']) {
Get.back();
final newVal =
addMediaIdsNew.isNotEmpty || favIds?.length != delMediaIdsNew.length;
if (hasFav.value != newVal) {
getStat()!.favorite += newVal ? 1 : -1;
hasFav.value = newVal;
}
SmartDialog.showToast('操作成功');
} else {
SmartDialog.showToast(result['msg']);
}
}
Future<void> coinVideo(int coin, [bool selectLike = false]) async {
final stat = getStat();
if (stat == null) {
return;
}
var res = await VideoHttp.coinVideo(
bvid: bvid,
multiply: coin,
selectLike: selectLike ? 1 : 0,
);
if (res['status']) {
SmartDialog.showToast('投币成功');
coinNum.value += coin;
GlobalData().afterCoin(coin);
stat.coin += coin;
if (selectLike && !hasLike.value) {
stat.like++;
hasLike.value = true;
}
} else {
SmartDialog.showToast(res['msg']);
}
}
late final enableQuickFav = Pref.enableQuickFav;
int? quickFavId;

View File

@@ -33,11 +33,7 @@ class _FavPanelState extends State<FavPanel> {
Future<void> _query() async {
var res = await widget.ctr.queryVideoInFolder();
if (mounted) {
if (res['status']) {
loadingState = const Success(null);
} else {
loadingState = Error(res['msg']);
}
loadingState = res;
setState(() {});
}
}

View File

@@ -5,12 +5,12 @@ import 'package:PiliPlus/grpc/bilibili/app/viewunite/pgcanymodel.pb.dart'
show ViewPgcAny;
import 'package:PiliPlus/grpc/view.dart';
import 'package:PiliPlus/http/constants.dart';
import 'package:PiliPlus/http/fav.dart';
import 'package:PiliPlus/http/video.dart';
import 'package:PiliPlus/models/pgc_lcf.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/episode.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/result.dart';
import 'package:PiliPlus/models_new/triple/pgc_triple.dart';
import 'package:PiliPlus/models_new/video/video_detail/stat_detail.dart';
import 'package:PiliPlus/pages/common/common_intro_controller.dart';
import 'package:PiliPlus/pages/dynamics_repost/view.dart';
import 'package:PiliPlus/pages/video/controller.dart';
@@ -64,7 +64,7 @@ class PgcIntroController extends CommonIntroController {
final hasFav = data.favorite == 1;
late final stat = pgcItem.stat!;
if (hasLike) {
stat.likes = max(1, stat.likes);
stat.like = max(1, stat.like);
}
if (hasFav) {
stat.favorite = max(1, stat.favorite);
@@ -87,33 +87,13 @@ class PgcIntroController extends CommonIntroController {
var result = await VideoHttp.likeVideo(bvid: bvid, type: newVal);
if (result['status']) {
SmartDialog.showToast(newVal ? result['data']['toast'] : '取消赞');
pgcItem.stat!.likes += newVal ? 1 : -1;
pgcItem.stat!.like += newVal ? 1 : -1;
hasLike.value = newVal;
} else {
SmartDialog.showToast(result['msg']);
}
}
Future<void> coinVideo(int coin, [bool selectLike = false]) async {
var res = await VideoHttp.coinVideo(
bvid: bvid,
multiply: coin,
selectLike: selectLike ? 1 : 0,
);
if (res['status']) {
SmartDialog.showToast('投币成功');
coinNum.value += coin;
GlobalData().afterCoin(coin);
final stat = pgcItem.stat!..coins += coin;
if (selectLike && !hasLike.value) {
stat.likes++;
hasLike.value = true;
}
} else {
SmartDialog.showToast(res['msg']);
}
}
// 投币
void actionCoinVideo() {
if (!accountService.isLogin.value) {
@@ -137,70 +117,11 @@ class PgcIntroController extends CommonIntroController {
);
}
// (取消)收藏 pgc
@override
Future<void> actionFavVideo({bool isQuick = false}) async {
// 收藏至默认文件夹
if (isQuick) {
SmartDialog.showLoading(msg: '请求中');
queryVideoInFolder().then((res) async {
if (res['status']) {
final hasFav = this.hasFav.value;
var result = hasFav
? await FavHttp.unfavAll(rid: epId, type: 24)
: await FavHttp.favVideo(
resources: '$epId:24',
addIds: favFolderId.toString(),
);
SmartDialog.dismiss();
if (result['status']) {
pgcItem.stat!.favorite += hasFav ? -1 : 1;
this.hasFav.value = !hasFav;
SmartDialog.showToast('✅ 快速收藏/取消收藏成功');
} else {
SmartDialog.showToast(result['msg']);
}
} else {
SmartDialog.dismiss();
}
});
return;
}
(Object, int) getFavRidType() => (epId!, 24);
List<int?> addMediaIdsNew = [];
List<int?> delMediaIdsNew = [];
try {
for (var i in favFolderData.value.list!.toList()) {
bool isFaved = favIds?.contains(i.id) == true;
if (i.favState == 1) {
if (!isFaved) {
addMediaIdsNew.add(i.id);
}
} else {
if (isFaved) {
delMediaIdsNew.add(i.id);
}
}
}
} catch (_) {}
var result = await FavHttp.favVideo(
resources: '$epId:24',
addIds: addMediaIdsNew.join(','),
delIds: delMediaIdsNew.join(','),
);
if (result['status']) {
SmartDialog.showToast('操作成功');
Get.back();
final newVal =
addMediaIdsNew.isNotEmpty || favIds?.length != delMediaIdsNew.length;
if (hasFav.value != newVal) {
pgcItem.stat!.favorite += newVal ? 1 : -1;
hasFav.value = newVal;
}
} else {
SmartDialog.showToast(result['msg']);
}
}
@override
StatDetail? getStat() => pgcItem.stat;
// 分享视频
void actionShareVideo(BuildContext context) {
@@ -407,24 +328,6 @@ class PgcIntroController extends CommonIntroController {
SmartDialog.showToast(result['msg']);
}
@override
Future queryVideoInFolder() async {
favIds = null;
var result = await FavHttp.videoInFolder(
mid: accountService.mid,
rid: epId, // pgc
type: 24, // pgc
);
if (result['status']) {
favFolderData.value = result['data'];
favIds = favFolderData.value.list
?.where((item) => item.favState == 1)
.map((item) => item.id)
.toSet();
}
return result;
}
bool prevPlay() {
List episodes = pgcItem.episodes!;
VideoDetailController videoDetailCtr = Get.find<VideoDetailController>(
@@ -503,11 +406,11 @@ class PgcIntroController extends CommonIntroController {
PgcTriple data = result['data'];
late final stat = pgcItem.stat!;
if ((data.like == 1) != hasLike.value) {
stat.likes++;
stat.like++;
hasLike.value = true;
}
if ((data.coin == 1) != hasCoin) {
stat.coins += 2;
stat.coin += 2;
coinNum.value = 2;
GlobalData().afterCoin(2);
}

View File

@@ -209,11 +209,11 @@ class _PgcIntroPageState extends State<PgcIntroPage>
children: [
StatWidget(
type: StatType.play,
value: item.stat!.views,
value: item.stat!.view,
),
StatWidget(
type: StatType.danmaku,
value: item.stat!.danmakus,
value: item.stat!.danmaku,
),
if (isLandscape) ...[
areasAndPubTime(theme, item),
@@ -280,7 +280,7 @@ class _PgcIntroPageState extends State<PgcIntroPage>
onLongPress: pgcIntroController.actionOneThree,
selectStatus: pgcIntroController.hasLike.value,
semanticsLabel: '点赞',
text: NumUtil.numFormat(item.stat!.likes),
text: NumUtil.numFormat(item.stat!.like),
needAnim: true,
hasTriple:
pgcIntroController.hasLike.value &&
@@ -306,7 +306,7 @@ class _PgcIntroPageState extends State<PgcIntroPage>
onTap: () => handleState(pgcIntroController.actionCoinVideo),
selectStatus: pgcIntroController.hasCoin,
semanticsLabel: '投币',
text: NumUtil.numFormat(item.stat!.coins),
text: NumUtil.numFormat(item.stat!.coin),
needAnim: true,
),
),

View File

@@ -114,11 +114,11 @@ class _IntroDetailState extends CommonCollapseSlidePageState<PgcIntroPanel> {
children: [
StatWidget(
type: StatType.play,
value: widget.item.stat!.views,
value: widget.item.stat!.view,
),
StatWidget(
type: StatType.danmaku,
value: widget.item.stat!.danmakus,
value: widget.item.stat!.danmaku,
),
],
),

View File

@@ -4,14 +4,12 @@ import 'dart:math';
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
import 'package:PiliPlus/http/api.dart';
import 'package:PiliPlus/http/constants.dart';
import 'package:PiliPlus/http/fav.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/member.dart';
import 'package:PiliPlus/http/search.dart';
import 'package:PiliPlus/http/user.dart';
import 'package:PiliPlus/http/video.dart';
import 'package:PiliPlus/models_new/fav/fav_folder/data.dart';
import 'package:PiliPlus/models_new/member_card_info/data.dart';
import 'package:PiliPlus/models_new/triple/ugc_triple.dart';
import 'package:PiliPlus/models_new/video/video_ai_conclusion/data.dart';
@@ -21,6 +19,7 @@ import 'package:PiliPlus/models_new/video/video_detail/episode.dart';
import 'package:PiliPlus/models_new/video/video_detail/page.dart';
import 'package:PiliPlus/models_new/video/video_detail/section.dart';
import 'package:PiliPlus/models_new/video/video_detail/staff.dart';
import 'package:PiliPlus/models_new/video/video_detail/stat_detail.dart';
import 'package:PiliPlus/models_new/video/video_detail/ugc_season.dart';
import 'package:PiliPlus/models_new/video/video_relation/data.dart';
import 'package:PiliPlus/pages/common/common_intro_controller.dart';
@@ -40,7 +39,6 @@ import 'package:PiliPlus/utils/request_utils.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:expandable/expandable.dart';
import 'package:flutter/foundation.dart' show kDebugMode;
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
@@ -308,29 +306,6 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
SmartDialog.showToast(res['msg']);
}
Future<void> coinVideo(int coin, [bool selectLike = false]) async {
if (videoDetail.value.stat == null) {
return;
}
var res = await VideoHttp.coinVideo(
bvid: bvid,
multiply: coin,
selectLike: selectLike ? 1 : 0,
);
if (res['status']) {
SmartDialog.showToast('投币成功');
coinNum.value += coin;
GlobalData().afterCoin(coin);
final stat = videoDetail.value.stat!..coin += coin;
if (selectLike && !hasLike.value) {
stat.like++;
hasLike.value = true;
}
} else {
SmartDialog.showToast(res['msg']);
}
}
// 投币
Future<void> actionCoinVideo() async {
if (!accountService.isLogin.value) {
@@ -356,74 +331,11 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
);
}
// (取消)收藏
@override
Future<void> actionFavVideo({bool isQuick = false}) async {
// 收藏至默认文件夹
if (isQuick) {
SmartDialog.showLoading(msg: '请求中');
queryVideoInFolder().then((res) async {
if (res['status']) {
final hasFav = this.hasFav.value;
var result = hasFav
? await FavHttp.unfavAll(rid: IdUtils.bv2av(bvid), type: 2)
: await FavHttp.favVideo(
resources: '${IdUtils.bv2av(bvid)}:2',
addIds: favFolderId.toString(),
);
SmartDialog.dismiss();
if (result['status']) {
videoDetail.value.stat!.favorite += hasFav ? -1 : 1;
this.hasFav.value = !hasFav;
SmartDialog.showToast('✅ 快速收藏/取消收藏成功');
} else {
SmartDialog.showToast(result['msg']);
}
} else {
SmartDialog.dismiss();
}
});
return;
}
(Object, int) getFavRidType() => (IdUtils.bv2av(bvid), 2);
List<int?> addMediaIdsNew = [];
List<int?> delMediaIdsNew = [];
try {
for (var i in favFolderData.value.list!.toList()) {
bool isFaved = favIds?.contains(i.id) == true;
if (i.favState == 1) {
if (!isFaved) {
addMediaIdsNew.add(i.id);
}
} else {
if (isFaved) {
delMediaIdsNew.add(i.id);
}
}
}
} catch (e) {
if (kDebugMode) debugPrint(e.toString());
}
SmartDialog.showLoading(msg: '请求中');
var result = await FavHttp.favVideo(
resources: '${IdUtils.bv2av(bvid)}:2',
addIds: addMediaIdsNew.join(','),
delIds: delMediaIdsNew.join(','),
);
SmartDialog.dismiss();
if (result['status']) {
Get.back();
final newVal =
addMediaIdsNew.isNotEmpty || favIds?.length != delMediaIdsNew.length;
if (hasFav.value != newVal) {
videoDetail.value.stat!.favorite += newVal ? 1 : -1;
hasFav.value = newVal;
}
SmartDialog.showToast('操作成功');
} else {
SmartDialog.showToast(result['msg']);
}
}
@override
StatDetail? getStat() => videoDetail.value.stat;
// 分享视频
void actionShareVideo(BuildContext context) {
@@ -530,24 +442,6 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
);
}
@override
Future queryVideoInFolder() async {
favIds = null;
var result = await FavHttp.videoInFolder(
mid: accountService.mid,
rid: IdUtils.bv2av(bvid),
);
if (result['status']) {
FavFolderData data = result['data'];
favFolderData.value = data;
favIds = data.list
?.where((item) => item.favState == 1)
.map((item) => item.id)
.toSet();
}
return result;
}
// 查询关注状态
Future<void> queryFollowStatus() async {
final videoDetail = this.videoDetail.value;