diff --git a/lib/common/widgets/html_render.dart b/lib/common/widgets/html_render.dart index 86c3b666e..364bcdbd5 100644 --- a/lib/common/widgets/html_render.dart +++ b/lib/common/widgets/html_render.dart @@ -16,8 +16,7 @@ Widget htmlRender({ Function(List, int)? callback, }) { debugPrint('htmlRender'); - return SelectionArea( - child: Html( + return Html( data: htmlContent, onLinkTap: (String? url, Map buildContext, attributes) {}, extensions: [ @@ -124,5 +123,5 @@ Widget htmlRender({ margin: Margins.zero, ), }, - )); + ); } diff --git a/lib/http/api.dart b/lib/http/api.dart index 9465a0dae..16d16415a 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -773,4 +773,6 @@ class Api { static const String searchRecommend = '${HttpString.appBaseUrl}/x/v2/search/recommend'; + + static const String articleInfo = '/x/article/viewinfo'; } diff --git a/lib/http/dynamics.dart b/lib/http/dynamics.dart index 0c561f1b9..f55dd61ea 100644 --- a/lib/http/dynamics.dart +++ b/lib/http/dynamics.dart @@ -1,6 +1,7 @@ import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/wbi_sign.dart'; import 'package:dio/dio.dart'; import '../models/dynamics/result.dart'; @@ -137,4 +138,23 @@ class DynamicsHttp { return {'status': false, 'msg': res.data['message']}; } } + + static Future articleInfo({ + required dynamic cvId, + }) async { + var res = await Request().get( + Api.articleInfo, + queryParameters: await WbiSign.makSign({ + 'id': cvId, + 'mobi_app': 'pc', + 'from': 'web', + 'gaia_source': 'main_web', + }), + ); + if (res.data['code'] == 0) { + return {'status': true, 'data': res.data['data']}; + } else { + return {'status': false, 'msg': res.data['message']}; + } + } } diff --git a/lib/http/html.dart b/lib/http/html.dart index e50a8307d..719537fa1 100644 --- a/lib/http/html.dart +++ b/lib/http/html.dart @@ -72,14 +72,18 @@ class HtmlHttp { // List imgList = opusDetail.querySelectorAll('bili-album__preview__picture__img'); dynamic mid; + Map? favorite; try { final regex = RegExp(r'window\.__INITIAL_STATE__\s*=\s*(\{.*?\});'); final match = regex.firstMatch(response.data); if (match != null) { final json = jsonDecode(match.group(1)!); mid = json['detail']['basic']['uid']; + favorite = json['detail']['modules'].last['module_stat']['favorite']; } - } catch (_) {} + } catch (e) { + debugPrint('req html: $e'); + } return { 'status': true, @@ -90,6 +94,7 @@ class HtmlHttp { 'content': (test ?? '') + opusContent, 'commentType': int.parse(comment[1]), 'commentId': int.parse(comment[2]), + 'favorite': favorite, }; } catch (err) { debugPrint('err: $err'); diff --git a/lib/http/user.dart b/lib/http/user.dart index 75b5d1dd9..2ecaa010f 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -496,7 +496,7 @@ class UserHttp { } static Future addFavArticle({ - required int id, + required dynamic id, }) async { var res = await Request().post( Api.addFavArticle, @@ -516,7 +516,7 @@ class UserHttp { } static Future delFavArticle({ - required int id, + required dynamic id, }) async { var res = await Request().post( Api.delFavArticle, diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index a7908afc7..65e3a46a9 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -792,9 +792,9 @@ class _DynamicDetailPageState extends State _dynamicDetailController.onLoadMore(); return Container( alignment: Alignment.center, - padding: EdgeInsets.only( + margin: EdgeInsets.only( bottom: MediaQuery.of(context).padding.bottom), - height: MediaQuery.of(context).padding.bottom + 100, + height: 125, child: Text( _dynamicDetailController.isEnd.not ? '加载中...' diff --git a/lib/pages/html/controller.dart b/lib/pages/html/controller.dart index 4ed20c366..24cbd9c0d 100644 --- a/lib/pages/html/controller.dart +++ b/lib/pages/html/controller.dart @@ -1,6 +1,7 @@ import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart'; import 'package:PiliPlus/http/dynamics.dart'; import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/http/user.dart'; import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/pages/common/reply_controller.dart'; @@ -8,6 +9,7 @@ import 'package:PiliPlus/pages/mine/controller.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/url_utils.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:PiliPlus/http/html.dart'; import 'package:PiliPlus/http/reply.dart'; @@ -24,7 +26,8 @@ class HtmlRenderController extends ReplyController { Rx item = DynamicItemModel().obs; - RxBool loaded = false.obs; + final RxMap favStat = {'status': false}.obs; + final RxBool loaded = false.obs; late final horizontalPreview = GStorage.horizontalPreview; late final showDynActionBar = GStorage.showDynActionBar; @@ -40,13 +43,22 @@ class HtmlRenderController extends ReplyController { type = dynamicType == 'picture' ? 11 : 12; if (showDynActionBar) { - if (RegExp(r'^cv', caseSensitive: false).hasMatch(id)) { + if (dynamicType == 'read') { UrlUtils.parseRedirectUrl('https://www.bilibili.com/read/$id/') .then((url) { if (url != null) { _queryDyn(url.split('/').last); } }); + DynamicsHttp.articleInfo(cvId: id.substring(2)).then((res) { + if (res['status']) { + favStat.value = { + 'status': true, + 'isFav': res['data']?['favorite'] ?? false, + 'favNum': res['data']?['stats']?['favorite'] ?? 0, + }; + } + }); } else { _queryDyn(id); } @@ -72,6 +84,13 @@ class HtmlRenderController extends ReplyController { res = await HtmlHttp.reqHtml(id, dynamicType); if (res != null) { type = res['commentType']; + if (res['favorite'] != null) { + favStat.value = { + 'status': true, + 'isFav': res['favorite']['status'] ?? false, + 'favNum': res['favorite']['count'] ?? 0, + }; + } } } else { res = await HtmlHttp.reqReadHtml(id, dynamicType); @@ -80,7 +99,7 @@ class HtmlRenderController extends ReplyController { response = res; mid = res['mid']; oid.value = res['commentId']; - if (Accounts.main.isLogin && !MineController.anonymity.value) { + if (isLogin && !MineController.anonymity.value) { VideoHttp.historyReport(aid: oid.value, type: 5); } queryData(); @@ -107,4 +126,24 @@ class HtmlRenderController extends ReplyController { antiGoodsReply: antiGoodsReply, ); } + + Future onFav() async { + bool isFav = favStat['isFav'] == true; + final res = dynamicType == 'read' + ? isFav + ? await UserHttp.delFavArticle(id: id.substring(2)) + : await UserHttp.addFavArticle(id: id.substring(2)) + : await UserHttp.communityAction(opusId: id, action: isFav ? 4 : 3); + if (res['status']) { + favStat['isFav'] = !isFav; + if (isFav) { + favStat['favNum'] -= 1; + } else { + favStat['favNum'] += 1; + } + SmartDialog.showToast('${isFav ? '取消' : ''}收藏成功'); + } else { + SmartDialog.showToast(res['msg']); + } + } } diff --git a/lib/pages/html/view.dart b/lib/pages/html/view.dart index 2301cf863..89bcbad3e 100644 --- a/lib/pages/html/view.dart +++ b/lib/pages/html/view.dart @@ -636,6 +636,46 @@ class _HtmlRenderPageState extends State label: const Text('分享'), ), ), + if (_htmlRenderCtr + .favStat['status']) + Expanded( + child: TextButton.icon( + onPressed: () { + _htmlRenderCtr.onFav(); + }, + icon: Icon( + _htmlRenderCtr.favStat[ + 'isFav'] == + true + ? FontAwesomeIcons + .solidStar + : FontAwesomeIcons.star, + size: 16, + color: + _htmlRenderCtr.favStat[ + 'isFav'] == + true + ? Theme.of(context) + .colorScheme + .primary + : Theme.of(context) + .colorScheme + .outline, + semanticLabel: "收藏", + ), + style: TextButton.styleFrom( + padding: const EdgeInsets + .fromLTRB(15, 0, 15, 0), + foregroundColor: + Theme.of(context) + .colorScheme + .outline, + ), + label: Text(_htmlRenderCtr + .favStat['favNum'] + .toString()), + ), + ), Expanded( child: Builder( builder: (context) => @@ -781,9 +821,9 @@ class _HtmlRenderPageState extends State _htmlRenderCtr.onLoadMore(); return Container( alignment: Alignment.center, - padding: EdgeInsets.only( + margin: EdgeInsets.only( bottom: MediaQuery.of(context).padding.bottom), - height: MediaQuery.of(context).padding.bottom + 100, + height: 125, child: Text( _htmlRenderCtr.isEnd.not ? '加载中...' diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 8ad8ea539..9d03ae63e 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -407,9 +407,9 @@ class _VideoReplyReplyPanelState _videoReplyReplyController.onLoadMore(); return Container( alignment: Alignment.center, - padding: EdgeInsets.only( + margin: EdgeInsets.only( bottom: MediaQuery.of(context).padding.bottom), - height: MediaQuery.of(context).padding.bottom + 100, + height: 125, child: Text( _videoReplyReplyController.isEnd.not ? '加载中...'