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
? '加载中...'