From b795d5d2b26f811cfa0cbdc64c39b156039564f3 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Thu, 3 Oct 2024 12:02:03 +0800 Subject: [PATCH] feat: unfollow bangumi, change followState --- README.md | 1 + lib/http/api.dart | 2 + lib/http/video.dart | 21 ++++ .../bangumi/introduction/controller.dart | 47 ++++++- lib/pages/bangumi/introduction/view.dart | 116 +++++++++++++++--- 5 files changed, 165 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 066e5b6d1..a017cc79a 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ ## feat +- [x] 取消/追番,更新追番状态 - [x] 取消/订阅合集 - [x] SponsorBlock - [x] 显示视频完整合集 diff --git a/lib/http/api.dart b/lib/http/api.dart index e14fa4b03..13895d3ae 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -320,6 +320,8 @@ class Api { // 取消追番 static const String bangumiDel = '/pgc/web/follow/del'; + static const String bangumiUpdate = '/pgc/web/follow/status/update'; + // 番剧列表 // https://api.bilibili.com/pgc/season/index/result? // st=1& diff --git a/lib/http/video.dart b/lib/http/video.dart index e857ef3e7..9bdafb2fe 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -766,6 +766,27 @@ class VideoHttp { } } + static Future bangumiUpdate({ + dynamic seasonId, + dynamic status, + }) async { + var res = await Request().post( + Api.bangumiUpdate, + data: { + 'season_id': seasonId, + 'status': status, + 'csrf': await Request.getCsrf(), + }, + options: Options( + contentType: Headers.formUrlEncodedContentType, + ), + ); + return { + 'status': res.data['code'] == 0, + 'msg': res.data['result'] == null ? 'failed' : res.data['result']['toast'] + }; + } + // 查看视频同时在看人数 static Future onlineTotal({int? aid, String? bvid, int? cid}) async { var res = await Request().get(Api.onlineTotal, data: { diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 9afacfb8e..feadd3e6d 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; + +import 'package:PiliPalaX/http/init.dart'; import 'package:PiliPalaX/http/loading_state.dart'; import 'package:PiliPalaX/http/user.dart'; import 'package:PiliPalaX/pages/common/common_controller.dart'; @@ -19,6 +22,8 @@ import 'package:PiliPalaX/utils/id_utils.dart'; import 'package:PiliPalaX/utils/storage.dart'; import 'package:share_plus/share_plus.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'package:html/parser.dart' as html_parser; +import 'package:html/dom.dart' as dom; class BangumiIntroController extends CommonController { // 视频bvid @@ -52,8 +57,6 @@ class BangumiIntroController extends CommonController { Rx favFolderData = FavFolderData().obs; List addMediaIdsNew = []; List delMediaIdsNew = []; - // 关注状态 默认未关注 - RxMap followStatus = {}.obs; int _tempThemeValue = -1; dynamic userInfo; @@ -96,6 +99,10 @@ class BangumiIntroController extends CommonController { } queryData(); + + if (userLogin) { + queryIsFollowed(); + } } Future queryVideoTags() async { @@ -354,6 +361,10 @@ class BangumiIntroController extends CommonController { Future bangumiAdd() async { var result = await VideoHttp.bangumiAdd( seasonId: (loadingState.value as Success).response.seasonId); + if (result['status']) { + isFollowed.value = true; + followStatus.value = 2; + } SmartDialog.showToast(result['msg']); } @@ -361,6 +372,20 @@ class BangumiIntroController extends CommonController { Future bangumiDel() async { var result = await VideoHttp.bangumiDel( seasonId: (loadingState.value as Success).response.seasonId); + if (result['status']) { + isFollowed.value = false; + } + SmartDialog.showToast(result['msg']); + } + + Future bangumiUpdate(status) async { + var result = await VideoHttp.bangumiUpdate( + seasonId: (loadingState.value as Success).response.seasonId, + status: status, + ); + if (result['status']) { + followStatus.value = status; + } SmartDialog.showToast(result['msg']); } @@ -466,4 +491,22 @@ class BangumiIntroController extends CommonController { SmartDialog.showToast(result['msg']); } } + + RxBool isFollowed = false.obs; + RxInt followStatus = (-1).obs; + + Future queryIsFollowed() async { + dynamic result = await Request().get( + 'https://www.bilibili.com/bangumi/play/ss$seasonId', + ); + dom.Document document = html_parser.parse(result.data); + dom.Element? scriptElement = document.querySelector('script#__NEXT_DATA__'); + if (scriptElement != null) { + dynamic scriptContent = jsonDecode(scriptElement.text); + isFollowed.value = + scriptContent['props']['pageProps']['followState']['isFollowed']; + followStatus.value = + scriptContent['props']['pageProps']['followState']['followStatus']; + } + } } diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index fe446e9c2..81fbb708b 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -261,28 +261,49 @@ class _BangumiInfoState extends State ), ), const SizedBox(width: 20), - SizedBox( - width: 30, - height: 30, - child: IconButton( - tooltip: '收藏', - style: ButtonStyle( - padding: MaterialStateProperty.all( - EdgeInsets.zero), + Obx( + () => FilledButton.tonal( + style: FilledButton.styleFrom( + padding: const EdgeInsets.symmetric( + horizontal: 20, vertical: 10), + visualDensity: const VisualDensity( + horizontal: -2, + vertical: -2, + ), + foregroundColor: + bangumiIntroController + .isFollowed.value + ? t.colorScheme.onSurface + : null, backgroundColor: - MaterialStateProperty.resolveWith( - (Set states) { - return t - .colorScheme.primaryContainer - .withOpacity(0.7); - }), + bangumiIntroController + .isFollowed.value + ? t.colorScheme + .onInverseSurface + : null, ), - onPressed: () => - bangumiIntroController.bangumiAdd(), - icon: Icon( - Icons.favorite_border_rounded, - color: t.colorScheme.primary, - size: 22, + onPressed: bangumiIntroController + .followStatus.value == + -1 + ? null + : () { + if (bangumiIntroController + .isFollowed.value) { + showDialog( + context: context, + builder: (_) => + _followDialog(), + ); + } else { + bangumiIntroController + .bangumiAdd(); + } + }, + child: Text( + bangumiIntroController + .isFollowed.value + ? '已追番' + : '追番', ), ), ), @@ -552,6 +573,61 @@ class _BangumiInfoState extends State text: '转发'), ]); } + + Widget _followDialog() { + return Dialog( + clipBehavior: Clip.hardEdge, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + _followDialogItem(3, '看过'), + _followDialogItem(2, '在看'), + _followDialogItem(1, '想看'), + ListTile( + dense: true, + title: const Padding( + padding: EdgeInsets.only(left: 10), + child: Text( + '取消追番', + style: TextStyle(fontSize: 14), + ), + ), + onTap: () { + Get.back(); + bangumiIntroController.bangumiDel(); + }, + ) + ], + ), + ), + ); + } + + Widget _followDialogItem( + int followStatus, + String text, + ) { + return ListTile( + dense: true, + enabled: bangumiIntroController.followStatus.value != followStatus, + title: Padding( + padding: const EdgeInsets.only(left: 10), + child: Text( + '标记为 $text', + style: const TextStyle(fontSize: 14), + ), + ), + trailing: bangumiIntroController.followStatus.value == followStatus + ? const Icon(size: 22, Icons.check) + : null, + onTap: () { + Get.back(); + bangumiIntroController.bangumiUpdate(followStatus); + }, + ); + } } class AreasAndPubTime extends StatelessWidget {