feat: audio page (#1518)

* feat: audio page

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>

* opt ui

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>

* impl intro, share, fav

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>

* tweaks

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>

* load prev/next

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>

---------

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
dom
2025-10-11 22:16:16 +08:00
committed by GitHub
parent a65edab7d1
commit 16f577f3fd
62 changed files with 49877 additions and 774 deletions

View File

@@ -11,6 +11,7 @@ import 'package:PiliPlus/models_new/video/video_detail/stat_detail.dart';
import 'package:PiliPlus/models_new/video/video_tag/data.dart';
import 'package:PiliPlus/pages/video/introduction/ugc/widgets/triple_mixin.dart';
import 'package:PiliPlus/services/account_service.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/global_data.dart';
import 'package:PiliPlus/utils/id_utils.dart';
import 'package:PiliPlus/utils/page_utils.dart';
@@ -23,7 +24,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
abstract class CommonIntroController extends GetxController
with GetSingleTickerProviderStateMixin, TripleMixin {
with GetSingleTickerProviderStateMixin, TripleMixin, FavMixin {
late final String heroTag;
late String bvid;
@@ -41,15 +42,15 @@ abstract class CommonIntroController extends GetxController
}
}
Set? favIds;
final Rx<FavFolderData> favFolderData = FavFolderData().obs;
AccountService accountService = Get.find<AccountService>();
(Object, int) getFavRidType();
StatDetail? getStat();
@override
void updateFavCount(int count) {
getStat()?.favorite += count;
}
final Rx<VideoDetailData> videoDetail = VideoDetailData().obs;
void queryVideoIntro();
@@ -115,11 +116,57 @@ abstract class CommonIntroController extends GetxController
super.onClose();
}
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']);
}
}
Future<void> queryVideoTags() async {
final result = await UserHttp.videoTags(bvid: bvid, cid: cid.value);
videoTags.value = result.dataOrNull;
}
Future<void> viewLater() async {
var res = await (hasLater.value
? UserHttp.toViewDel(aids: IdUtils.bv2av(bvid).toString())
: await UserHttp.toViewLater(bvid: bvid));
if (res['status']) hasLater.value = !hasLater.value;
SmartDialog.showToast(res['msg']);
}
}
mixin FavMixin on TripleMixin {
Set? favIds;
int? quickFavId;
late final enableQuickFav = Pref.enableQuickFav;
final Rx<FavFolderData> favFolderData = FavFolderData().obs;
(Object, int) get getFavRidType;
Future<LoadingState<FavFolderData>> queryVideoInFolder() async {
favIds = null;
final (rid, type) = getFavRidType();
final (rid, type) = getFavRidType;
final result = await FavHttp.videoInFolder(
mid: accountService.mid,
mid: Accounts.main.mid,
rid: rid,
type: type,
);
@@ -133,8 +180,47 @@ abstract class CommonIntroController extends GetxController
return result;
}
int get favFolderId {
if (this.quickFavId != null) {
return this.quickFavId!;
}
final quickFavId = Pref.quickFavId;
final list = favFolderData.value.list!;
if (quickFavId != null) {
final folderInfo = list.firstWhereOrNull((e) => e.id == quickFavId);
if (folderInfo != null) {
return this.quickFavId = quickFavId;
} else {
GStorage.setting.delete(SettingBoxKey.quickFavId);
}
}
return this.quickFavId = list.first.id;
}
// 收藏
void showFavBottomSheet(BuildContext context, {bool isLongPress = false}) {
if (!Accounts.main.isLogin) {
SmartDialog.showToast('账号未登录');
return;
}
// 快速收藏 &
// 点按 收藏至默认文件夹
// 长按选择文件夹
if (enableQuickFav) {
if (!isLongPress) {
actionFavVideo(isQuick: true);
} else {
PageUtils.showFavBottomSheet(context: context, ctr: this);
}
} else if (!isLongPress) {
PageUtils.showFavBottomSheet(context: context, ctr: this);
}
}
void updateFavCount(int count);
Future<void> actionFavVideo({bool isQuick = false}) async {
final (rid, type) = getFavRidType();
final (rid, type) = getFavRidType;
// 收藏至默认文件夹
if (isQuick) {
SmartDialog.showLoading(msg: '请求中');
@@ -149,7 +235,7 @@ abstract class CommonIntroController extends GetxController
);
SmartDialog.dismiss();
if (result['status']) {
getStat()!.favorite += hasFav ? -1 : 1;
updateFavCount(hasFav ? -1 : 1);
this.hasFav.value = !hasFav;
SmartDialog.showToast('✅ 快速收藏/取消收藏成功');
} else {
@@ -192,7 +278,7 @@ abstract class CommonIntroController extends GetxController
final newVal =
addMediaIdsNew.isNotEmpty || favIds?.length != delMediaIdsNew.length;
if (hasFav.value != newVal) {
getStat()!.favorite += newVal ? 1 : -1;
updateFavCount(newVal ? 1 : -1);
hasFav.value = newVal;
}
SmartDialog.showToast('操作成功');
@@ -200,81 +286,4 @@ abstract class CommonIntroController extends GetxController
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;
int get favFolderId {
if (this.quickFavId != null) {
return this.quickFavId!;
}
final quickFavId = Pref.quickFavId;
final list = favFolderData.value.list!;
if (quickFavId != null) {
final folderInfo = list.firstWhereOrNull((e) => e.id == quickFavId);
if (folderInfo != null) {
return this.quickFavId = quickFavId;
} else {
GStorage.setting.delete(SettingBoxKey.quickFavId);
}
}
return this.quickFavId = list.first.id;
}
// 收藏
void showFavBottomSheet(BuildContext context, {bool isLongPress = false}) {
if (!accountService.isLogin.value) {
SmartDialog.showToast('账号未登录');
return;
}
// 快速收藏 &
// 点按 收藏至默认文件夹
// 长按选择文件夹
if (enableQuickFav) {
if (!isLongPress) {
actionFavVideo(isQuick: true);
} else {
PageUtils.showFavBottomSheet(context: context, ctr: this);
}
} else if (!isLongPress) {
PageUtils.showFavBottomSheet(context: context, ctr: this);
}
}
Future<void> queryVideoTags() async {
final result = await UserHttp.videoTags(bvid: bvid, cid: cid.value);
videoTags.value = result.dataOrNull;
}
Future<void> viewLater() async {
var res = await (hasLater.value
? UserHttp.toViewDel(aids: IdUtils.bv2av(bvid).toString())
: await UserHttp.toViewLater(bvid: bvid));
if (res['status']) hasLater.value = !hasLater.value;
SmartDialog.showToast(res['msg']);
}
}