diff --git a/lib/common/widgets/html_render.dart b/lib/common/widgets/html_render.dart
index 86c3b666e..bac8f6a5e 100644
--- a/lib/common/widgets/html_render.dart
+++ b/lib/common/widgets/html_render.dart
@@ -6,19 +6,20 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'network_img_layer.dart';
+import 'package:html/dom.dart' as dom;
Widget htmlRender({
required BuildContext context,
- String? htmlContent,
+ required dom.Element element,
int? imgCount,
List? imgList,
- required double constrainedWidth,
+ required double maxWidth,
Function(List, int)? callback,
}) {
debugPrint('htmlRender');
return SelectionArea(
- child: Html(
- data: htmlContent,
+ child: Html.fromElement(
+ documentElement: element,
onLinkTap: (String? url, Map buildContext, attributes) {},
extensions: [
TagExtension(
@@ -43,7 +44,7 @@ Widget htmlRender({
?.group(1);
if (clazz?.contains('cut-off') == true || height != null) {
return CachedNetworkImage(
- width: constrainedWidth,
+ width: maxWidth,
height: height != null ? double.parse(height) : null,
imageUrl: Utils.thumbnailImgUrl(imgUrl),
fit: BoxFit.contain,
@@ -62,7 +63,7 @@ Widget htmlRender({
}
},
child: NetworkImgLayer(
- width: isEmote ? 22 : constrainedWidth,
+ width: isEmote ? 22 : maxWidth,
height: isEmote ? 22 : 200,
src: imgUrl,
ignoreHeight: !isEmote,
@@ -70,7 +71,7 @@ Widget htmlRender({
),
);
} catch (err) {
- return const SizedBox();
+ return const SizedBox.shrink();
}
},
),
diff --git a/lib/pages/common/reply_controller.dart b/lib/pages/common/reply_controller.dart
index 7a916d2bf..87a025328 100644
--- a/lib/pages/common/reply_controller.dart
+++ b/lib/pages/common/reply_controller.dart
@@ -500,10 +500,4 @@ https://api.bilibili.com/x/v2/reply/reply?oid=$oid&pn=1&ps=20&root=${rpid ?? rep
SmartDialog.showToast(res['msg']);
}
}
-
- @override
- Future onReload() {
- scrollController.jumpToTop();
- return super.onReload();
- }
}
diff --git a/lib/pages/html/view.dart b/lib/pages/html/view.dart
index 7f5f3a751..da164af3a 100644
--- a/lib/pages/html/view.dart
+++ b/lib/pages/html/view.dart
@@ -25,6 +25,7 @@ import 'package:PiliPlus/common/widgets/network_img_layer.dart';
import 'package:PiliPlus/models/common/reply_type.dart';
import 'package:PiliPlus/pages/video/detail/reply_reply/index.dart';
import 'package:PiliPlus/utils/feed_back.dart';
+import 'package:html/parser.dart' as parser;
import '../../utils/grid.dart';
import 'controller.dart';
@@ -229,7 +230,303 @@ class _HtmlRenderPageState extends State
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
- appBar: AppBar(
+ appBar: _buildAppBar,
+ body: Stack(
+ children: [
+ SafeArea(
+ top: false,
+ bottom: false,
+ child: Builder(
+ builder: (context) {
+ final isPortrait = context.orientation == Orientation.portrait;
+ double padding =
+ max(context.width / 2 - Grid.smallCardWidth, 0);
+ if (isPortrait) {
+ return LayoutBuilder(builder: (context, constraints) {
+ final maxWidth = constraints.maxWidth - 2 * padding - 24;
+ return Padding(
+ padding: EdgeInsets.symmetric(horizontal: padding),
+ child: CustomScrollView(
+ controller: _htmlRenderCtr.scrollController,
+ physics: const AlwaysScrollableScrollPhysics(),
+ slivers: [
+ _buildHeader,
+ _buildContent(maxWidth),
+ SliverToBoxAdapter(
+ child: Divider(
+ thickness: 8,
+ color: Theme.of(context)
+ .dividerColor
+ .withOpacity(0.05),
+ ),
+ ),
+ _buildReplyHeader,
+ Obx(() => _buildReplyList(
+ _htmlRenderCtr.loadingState.value)),
+ ],
+ ),
+ );
+ });
+ } else {
+ return Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Expanded(
+ flex: _ratio[0].toInt(),
+ child: LayoutBuilder(
+ builder: (context, constraints) {
+ final maxWidth =
+ constraints.maxWidth - padding / 4 - 24;
+ return CustomScrollView(
+ controller: _htmlRenderCtr.scrollController,
+ physics: const AlwaysScrollableScrollPhysics(),
+ slivers: [
+ SliverPadding(
+ padding: EdgeInsets.only(left: padding / 4),
+ sliver: _buildHeader,
+ ),
+ SliverPadding(
+ padding: EdgeInsets.only(
+ left: padding / 4,
+ bottom:
+ MediaQuery.paddingOf(context).bottom +
+ 80,
+ ),
+ sliver: _buildContent(maxWidth),
+ ),
+ ],
+ );
+ },
+ ),
+ ),
+ VerticalDivider(
+ thickness: 8,
+ color: Theme.of(context).dividerColor.withOpacity(0.05),
+ ),
+ Expanded(
+ flex: _ratio[1].toInt(),
+ child: Scaffold(
+ key: _key,
+ backgroundColor: Colors.transparent,
+ body: refreshIndicator(
+ onRefresh: () async {
+ await _htmlRenderCtr.onRefresh();
+ },
+ child: Padding(
+ padding: EdgeInsets.only(right: padding / 4),
+ child: CustomScrollView(
+ controller: _htmlRenderCtr.scrollController,
+ physics: const AlwaysScrollableScrollPhysics(),
+ slivers: [
+ _buildReplyHeader,
+ Obx(() => _buildReplyList(
+ _htmlRenderCtr.loadingState.value)),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+ },
+ ),
+ ),
+ _buildBottom,
+ ],
+ ),
+ );
+ }
+
+ Widget _buildContent(double maxWidth) => SliverPadding(
+ padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
+ sliver: Obx(
+ () {
+ if (_htmlRenderCtr.loaded.value) {
+ if (_htmlRenderCtr.response['isJsonContent'] == true) {
+ return articleContent(
+ context: context,
+ list: _htmlRenderCtr.response['content'],
+ callback: _getImageCallback,
+ maxWidth: maxWidth,
+ );
+ }
+
+ // html
+ var res = parser.parse(_htmlRenderCtr.response['content']);
+ return SliverList.builder(
+ itemCount: res.body!.children.length,
+ itemBuilder: (context, index) {
+ return htmlRender(
+ context: context,
+ element: res.body!.children[index],
+ maxWidth: maxWidth,
+ callback: _getImageCallback,
+ );
+ },
+ );
+ }
+
+ return const SliverToBoxAdapter();
+ },
+ ),
+ );
+
+ Widget _buildReplyList(LoadingState?> loadingState) {
+ return switch (loadingState) {
+ Loading() => SliverList.builder(
+ itemCount: 5,
+ itemBuilder: (context, index) {
+ return const VideoReplySkeleton();
+ },
+ ),
+ Success() => loadingState.response?.isNotEmpty == true
+ ? SliverList.builder(
+ itemCount: loadingState.response!.length + 1,
+ itemBuilder: (context, index) {
+ if (index == loadingState.response!.length) {
+ _htmlRenderCtr.onLoadMore();
+ return Container(
+ alignment: Alignment.center,
+ margin: EdgeInsets.only(
+ bottom: MediaQuery.of(context).padding.bottom),
+ height: 125,
+ child: Text(
+ _htmlRenderCtr.isEnd.not
+ ? '加载中...'
+ : loadingState.response!.isEmpty
+ ? '还没有评论'
+ : '没有更多了',
+ style: TextStyle(
+ fontSize: 12,
+ color: Theme.of(context).colorScheme.outline,
+ ),
+ ),
+ );
+ } else {
+ return ReplyItemGrpc(
+ replyItem: loadingState.response![index],
+ replyLevel: '1',
+ replyReply: (replyItem, id) =>
+ replyReply(context, replyItem, id),
+ onReply: () {
+ _htmlRenderCtr.onReply(
+ context,
+ replyItem: loadingState.response![index],
+ index: index,
+ );
+ },
+ onDelete: (subIndex) =>
+ _htmlRenderCtr.onRemove(index, subIndex),
+ upMid: _htmlRenderCtr.upMid,
+ callback: _getImageCallback,
+ onCheckReply: (item) =>
+ _htmlRenderCtr.onCheckReply(context, item),
+ onToggleTop: (isUpTop, rpid) => _htmlRenderCtr.onToggleTop(
+ index,
+ _htmlRenderCtr.oid,
+ _htmlRenderCtr.type,
+ isUpTop,
+ rpid,
+ ),
+ );
+ }
+ },
+ )
+ : HttpError(
+ onReload: _htmlRenderCtr.onReload,
+ ),
+ Error() => HttpError(
+ errMsg: loadingState.errMsg,
+ onReload: _htmlRenderCtr.onReload,
+ ),
+ LoadingState() => throw UnimplementedError(),
+ };
+ }
+
+ Widget get _buildReplyHeader {
+ return SliverToBoxAdapter(
+ child: Container(
+ height: 45,
+ padding: const EdgeInsets.only(left: 12, right: 6),
+ child: Row(
+ children: [
+ const Text('回复'),
+ const Spacer(),
+ SizedBox(
+ height: 35,
+ child: TextButton.icon(
+ onPressed: () => _htmlRenderCtr.queryBySort(),
+ icon: const Icon(Icons.sort, size: 16),
+ label: Obx(
+ () => Text(
+ _htmlRenderCtr.sortType.value.label,
+ style: const TextStyle(fontSize: 13),
+ ),
+ ),
+ ),
+ )
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget get _buildHeader => SliverToBoxAdapter(
+ child: Obx(
+ () => _htmlRenderCtr.loaded.value
+ ? Padding(
+ padding: const EdgeInsets.fromLTRB(12, 12, 12, 8),
+ child: GestureDetector(
+ onTap: () {
+ if (_htmlRenderCtr.mid != null) {
+ Get.toNamed('/member?mid=${_htmlRenderCtr.mid}');
+ }
+ },
+ child: Row(
+ children: [
+ NetworkImgLayer(
+ width: 40,
+ height: 40,
+ type: 'avatar',
+ src: _htmlRenderCtr.response['avatar']!,
+ ),
+ const SizedBox(width: 10),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ _htmlRenderCtr.response['uname'],
+ style: TextStyle(
+ fontSize: Theme.of(context)
+ .textTheme
+ .titleSmall!
+ .fontSize,
+ ),
+ ),
+ Text(
+ _htmlRenderCtr.response['updateTime'],
+ style: TextStyle(
+ color: Theme.of(context).colorScheme.outline,
+ fontSize: Theme.of(context)
+ .textTheme
+ .labelSmall!
+ .fontSize,
+ ),
+ ),
+ ],
+ ),
+ const Spacer(),
+ ],
+ ),
+ ),
+ )
+ : const SizedBox.shrink(),
+ ),
+ );
+
+ PreferredSizeWidget get _buildAppBar => AppBar(
title: Text(title),
actions: [
const SizedBox(width: 4),
@@ -340,640 +637,336 @@ class _HtmlRenderPageState extends State
),
const SizedBox(width: 6)
],
- ),
- body: SafeArea(
- top: false,
- bottom: false,
- child: Stack(
- children: [
- OrientationBuilder(
- builder: (context, orientation) {
- double padding =
- max(context.width / 2 - Grid.smallCardWidth, 0);
- return Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Expanded(
- flex: _ratio[0].toInt(),
- child: CustomScrollView(
- controller: _htmlRenderCtr.scrollController,
- physics: const AlwaysScrollableScrollPhysics(),
- slivers: [
- SliverPadding(
- padding: orientation == Orientation.portrait
- ? EdgeInsets.symmetric(horizontal: padding)
- : EdgeInsets.only(left: padding / 4),
- sliver: SliverToBoxAdapter(
- child: Obx(
- () => _htmlRenderCtr.loaded.value
- ? _buildHeader
- : const SizedBox(),
- ),
- ),
- ),
- SliverPadding(
- padding: orientation == Orientation.portrait
- ? EdgeInsets.symmetric(horizontal: padding)
- : EdgeInsets.only(
- left: padding / 4,
- bottom:
- MediaQuery.paddingOf(context).bottom +
- 80,
- ),
- sliver: _buildContent,
- ),
- if (orientation == Orientation.portrait) ...[
- SliverPadding(
- padding:
- EdgeInsets.symmetric(horizontal: padding),
- sliver: SliverToBoxAdapter(
- child: Divider(
- thickness: 8,
- color: Theme.of(context)
- .dividerColor
- .withOpacity(0.05),
- ),
- ),
- ),
- SliverPadding(
- padding:
- EdgeInsets.symmetric(horizontal: padding),
- sliver: SliverToBoxAdapter(child: replyHeader()),
- ),
- SliverPadding(
- padding:
- EdgeInsets.symmetric(horizontal: padding),
- sliver: Obx(
- () => replyList(
- _htmlRenderCtr.loadingState.value),
- ),
- ),
- ],
- ],
- ),
- ),
- if (orientation == Orientation.landscape) ...[
- VerticalDivider(
- thickness: 8,
- color: Theme.of(context).dividerColor.withOpacity(0.05),
- ),
- Expanded(
- flex: _ratio[1].toInt(),
- child: Scaffold(
- key: _key,
- backgroundColor: Colors.transparent,
- body: refreshIndicator(
- onRefresh: () async {
- await _htmlRenderCtr.onRefresh();
- },
- child: CustomScrollView(
- controller: _htmlRenderCtr.scrollController,
- physics: const AlwaysScrollableScrollPhysics(),
- slivers: [
- SliverPadding(
- padding: EdgeInsets.only(right: padding / 4),
- sliver: SliverToBoxAdapter(
- child: replyHeader(),
- ),
- ),
- SliverPadding(
- padding: EdgeInsets.only(right: padding / 4),
- sliver: Obx(
- () => replyList(
- _htmlRenderCtr.loadingState.value),
- ),
- ),
- ],
- ),
- ),
+ );
+
+ Widget get _buildBottom => Positioned(
+ left: 0,
+ bottom: 0,
+ right: 0,
+ child: SlideTransition(
+ position: Tween(
+ begin: const Offset(0, 1),
+ end: const Offset(0, 0),
+ ).animate(CurvedAnimation(
+ parent: fabAnimationCtr,
+ curve: Curves.easeInOut,
+ )),
+ child: Builder(
+ builder: (context) {
+ Widget button() => FloatingActionButton(
+ heroTag: null,
+ onPressed: () {
+ feedBack();
+ _htmlRenderCtr.onReply(
+ context,
+ oid: _htmlRenderCtr.oid.value,
+ replyType: ReplyType.values[type],
+ );
+ },
+ tooltip: '评论动态',
+ child: const Icon(Icons.reply),
+ );
+ return _htmlRenderCtr.showDynActionBar.not
+ ? Align(
+ alignment: Alignment.bottomRight,
+ child: Padding(
+ padding: EdgeInsets.only(
+ right: 14,
+ bottom: MediaQuery.of(context).padding.bottom + 14,
),
+ child: button(),
),
- ],
- ],
- );
- },
- ),
- Positioned(
- left: 0,
- right: 0,
- bottom: 0,
- child: SlideTransition(
- position: Tween(
- begin: const Offset(0, 1),
- end: const Offset(0, 0),
- ).animate(CurvedAnimation(
- parent: fabAnimationCtr,
- curve: Curves.easeInOut,
- )),
- child: Builder(
- builder: (context) {
- Widget button() => FloatingActionButton(
- heroTag: null,
- onPressed: () {
- feedBack();
- _htmlRenderCtr.onReply(
- context,
- oid: _htmlRenderCtr.oid.value,
- replyType: ReplyType.values[type],
- );
- },
- tooltip: '评论动态',
- child: const Icon(Icons.reply),
- );
- return _htmlRenderCtr.showDynActionBar.not
- ? Align(
- alignment: Alignment.bottomRight,
- child: Padding(
- padding: EdgeInsets.only(
- right: 14,
- bottom:
- MediaQuery.of(context).padding.bottom + 14,
- ),
- child: button(),
+ )
+ : Obx(
+ () => Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ Padding(
+ padding: EdgeInsets.only(
+ right: 14,
+ bottom: 14 +
+ (_htmlRenderCtr.item.value.idStr != null
+ ? 0
+ : MediaQuery.of(context).padding.bottom),
),
- )
- : Obx(
- () => Column(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.end,
- children: [
- Padding(
- padding: EdgeInsets.only(
- right: 14,
- bottom: 14 +
- (_htmlRenderCtr.item.value.idStr != null
- ? 0
- : MediaQuery.of(context)
- .padding
- .bottom),
+ child: button(),
+ ),
+ _htmlRenderCtr.item.value.idStr != null
+ ? Container(
+ decoration: BoxDecoration(
+ color:
+ Theme.of(context).colorScheme.surface,
+ border: Border(
+ top: BorderSide(
+ color: Theme.of(context)
+ .colorScheme
+ .outline
+ .withOpacity(0.08),
+ ),
+ ),
),
- child: button(),
- ),
- _htmlRenderCtr.item.value.idStr != null
- ? Container(
- decoration: BoxDecoration(
- color: Theme.of(context)
- .colorScheme
- .surface,
- border: Border(
- top: BorderSide(
+ padding: EdgeInsets.only(
+ bottom:
+ MediaQuery.paddingOf(context).bottom),
+ child: Row(
+ mainAxisAlignment:
+ MainAxisAlignment.spaceAround,
+ children: [
+ Expanded(
+ child: Builder(
+ builder: (btnContext) =>
+ TextButton.icon(
+ onPressed: () {
+ showModalBottomSheet(
+ context: context,
+ isScrollControlled: true,
+ useSafeArea: true,
+ builder: (context) =>
+ RepostPanel(
+ item:
+ _htmlRenderCtr.item.value,
+ callback: () {
+ int count = int.tryParse(
+ _htmlRenderCtr
+ .item
+ .value
+ .modules
+ ?.moduleStat
+ ?.forward
+ ?.count ??
+ '0') ??
+ 0;
+ _htmlRenderCtr
+ .item
+ .value
+ .modules
+ ?.moduleStat
+ ?.forward!
+ .count =
+ (count + 1).toString();
+ if (btnContext.mounted) {
+ (btnContext as Element?)
+ ?.markNeedsBuild();
+ }
+ },
+ ),
+ );
+ },
+ icon: Icon(
+ FontAwesomeIcons.shareFromSquare,
+ size: 16,
color: Theme.of(context)
.colorScheme
- .outline
- .withOpacity(0.08),
+ .outline,
+ semanticLabel: "转发",
+ ),
+ style: TextButton.styleFrom(
+ padding:
+ const EdgeInsets.fromLTRB(
+ 15, 0, 15, 0),
+ foregroundColor: Theme.of(context)
+ .colorScheme
+ .outline,
+ ),
+ label: Text(
+ _htmlRenderCtr
+ .item
+ .value
+ .modules
+ ?.moduleStat
+ ?.forward!
+ .count !=
+ null
+ ? Utils.numFormat(
+ _htmlRenderCtr
+ .item
+ .value
+ .modules
+ ?.moduleStat
+ ?.forward!
+ .count)
+ : '转发',
),
),
),
- padding: EdgeInsets.only(
- bottom:
- MediaQuery.paddingOf(context)
- .bottom),
- child: Row(
- mainAxisAlignment:
- MainAxisAlignment.spaceAround,
- children: [
- Expanded(
- child: Builder(
- builder: (btnContext) =>
- TextButton.icon(
- onPressed: () {
- showModalBottomSheet(
- context: context,
- isScrollControlled: true,
- useSafeArea: true,
- builder: (context) =>
- RepostPanel(
- item: _htmlRenderCtr
- .item.value,
- callback: () {
- int count = int.tryParse(
- _htmlRenderCtr
- .item
- .value
- .modules
- ?.moduleStat
- ?.forward
- ?.count ??
- '0') ??
- 0;
- _htmlRenderCtr
- .item
- .value
- .modules
- ?.moduleStat
- ?.forward!
- .count =
- (count + 1)
- .toString();
- if (btnContext
- .mounted) {
- (btnContext
- as Element?)
- ?.markNeedsBuild();
- }
- },
- ),
- );
- },
- icon: Icon(
- FontAwesomeIcons
- .shareFromSquare,
- size: 16,
- color: 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
- .item
- .value
- .modules
- ?.moduleStat
- ?.forward!
- .count !=
- null
- ? Utils.numFormat(
- _htmlRenderCtr
- .item
- .value
- .modules
- ?.moduleStat
- ?.forward!
- .count)
- : '转发',
- ),
- ),
- ),
- ),
- Expanded(
- child: TextButton.icon(
- onPressed: () {
- Utils.shareText(
- '${HttpString.dynamicShareBaseUrl}/${_htmlRenderCtr.item.value.idStr}');
- },
- icon: Icon(
- FontAwesomeIcons.shareNodes,
- size: 16,
- color: Theme.of(context)
+ ),
+ Expanded(
+ child: TextButton.icon(
+ onPressed: () {
+ Utils.shareText(
+ '${HttpString.dynamicShareBaseUrl}/${_htmlRenderCtr.item.value.idStr}');
+ },
+ icon: Icon(
+ FontAwesomeIcons.shareNodes,
+ size: 16,
+ color: Theme.of(context)
+ .colorScheme
+ .outline,
+ semanticLabel: "分享",
+ ),
+ style: TextButton.styleFrom(
+ padding: const EdgeInsets.fromLTRB(
+ 15, 0, 15, 0),
+ foregroundColor: Theme.of(context)
+ .colorScheme
+ .outline,
+ ),
+ 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)
+ 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) => TextButton.icon(
+ onPressed: () =>
+ RequestUtils.onLikeDynamic(
+ _htmlRenderCtr.item.value,
+ () {
+ if (context.mounted) {
+ (context as Element?)
+ ?.markNeedsBuild();
+ }
+ },
+ ),
+ icon: Icon(
+ _htmlRenderCtr
+ .item
+ .value
+ .modules
+ ?.moduleStat
+ ?.like
+ ?.status ==
+ true
+ ? FontAwesomeIcons
+ .solidThumbsUp
+ : FontAwesomeIcons.thumbsUp,
+ size: 16,
+ color: _htmlRenderCtr
+ .item
+ .value
+ .modules
+ ?.moduleStat
+ ?.like
+ ?.status ==
+ true
+ ? Theme.of(context)
+ .colorScheme
+ .primary
+ : Theme.of(context)
+ .colorScheme
+ .outline,
+ semanticLabel: _htmlRenderCtr
+ .item
+ .value
+ .modules
+ ?.moduleStat
+ ?.like
+ ?.status ==
+ true
+ ? "已赞"
+ : "点赞",
+ ),
+ style: TextButton.styleFrom(
+ padding:
+ const EdgeInsets.fromLTRB(
+ 15, 0, 15, 0),
+ foregroundColor: Theme.of(context)
+ .colorScheme
+ .outline,
+ ),
+ label: AnimatedSwitcher(
+ duration: const Duration(
+ milliseconds: 400),
+ transitionBuilder: (Widget child,
+ Animation animation) {
+ return ScaleTransition(
+ scale: animation,
+ child: child);
+ },
+ child: Text(
+ _htmlRenderCtr
+ .item
+ .value
+ .modules
+ ?.moduleStat
+ ?.like
+ ?.count !=
+ null
+ ? Utils.numFormat(
+ _htmlRenderCtr
+ .item
+ .value
+ .modules!
+ .moduleStat!
+ .like!
+ .count)
+ : '点赞',
+ style: TextStyle(
+ color: _htmlRenderCtr
+ .item
+ .value
+ .modules
+ ?.moduleStat
+ ?.like
+ ?.status ==
+ true
+ ? Theme.of(context)
+ .colorScheme
+ .primary
+ : Theme.of(context)
.colorScheme
.outline,
),
- 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) =>
- TextButton.icon(
- onPressed: () => RequestUtils
- .onLikeDynamic(
- _htmlRenderCtr.item.value,
- () {
- if (context.mounted) {
- (context as Element?)
- ?.markNeedsBuild();
- }
- },
- ),
- icon: Icon(
- _htmlRenderCtr
- .item
- .value
- .modules
- ?.moduleStat
- ?.like
- ?.status ==
- true
- ? FontAwesomeIcons
- .solidThumbsUp
- : FontAwesomeIcons
- .thumbsUp,
- size: 16,
- color: _htmlRenderCtr
- .item
- .value
- .modules
- ?.moduleStat
- ?.like
- ?.status ==
- true
- ? Theme.of(context)
- .colorScheme
- .primary
- : Theme.of(context)
- .colorScheme
- .outline,
- semanticLabel:
- _htmlRenderCtr
- .item
- .value
- .modules
- ?.moduleStat
- ?.like
- ?.status ==
- true
- ? "已赞"
- : "点赞",
- ),
- style: TextButton.styleFrom(
- padding: const EdgeInsets
- .fromLTRB(15, 0, 15, 0),
- foregroundColor:
- Theme.of(context)
- .colorScheme
- .outline,
- ),
- label: AnimatedSwitcher(
- duration: const Duration(
- milliseconds: 400),
- transitionBuilder:
- (Widget child,
- Animation
- animation) {
- return ScaleTransition(
- scale: animation,
- child: child);
- },
- child: Text(
- _htmlRenderCtr
- .item
- .value
- .modules
- ?.moduleStat
- ?.like
- ?.count !=
- null
- ? Utils.numFormat(
- _htmlRenderCtr
- .item
- .value
- .modules!
- .moduleStat!
- .like!
- .count)
- : '点赞',
- style: TextStyle(
- color: _htmlRenderCtr
- .item
- .value
- .modules
- ?.moduleStat
- ?.like
- ?.status ==
- true
- ? Theme.of(context)
- .colorScheme
- .primary
- : Theme.of(context)
- .colorScheme
- .outline,
- ),
- ),
- ),
- ),
- ),
- ),
- ],
+ ),
),
- )
- : const SizedBox.shrink(),
- ],
- ),
- );
- },
- ),
- ),
- ),
- ],
- ),
- ),
- );
- }
-
- Widget replyList(LoadingState?> loadingState) {
- return switch (loadingState) {
- Loading() => SliverList.builder(
- itemCount: 5,
- itemBuilder: (context, index) {
- return const VideoReplySkeleton();
- },
- ),
- Success() => loadingState.response?.isNotEmpty == true
- ? SliverList.builder(
- itemCount: loadingState.response!.length + 1,
- itemBuilder: (context, index) {
- if (index == loadingState.response!.length) {
- _htmlRenderCtr.onLoadMore();
- return Container(
- alignment: Alignment.center,
- margin: EdgeInsets.only(
- bottom: MediaQuery.of(context).padding.bottom),
- height: 125,
- child: Text(
- _htmlRenderCtr.isEnd.not
- ? '加载中...'
- : loadingState.response!.isEmpty
- ? '还没有评论'
- : '没有更多了',
- style: TextStyle(
- fontSize: 12,
- color: Theme.of(context).colorScheme.outline,
+ ),
+ ],
+ ),
+ )
+ : const SizedBox.shrink(),
+ ],
),
- ),
- );
- } else {
- return ReplyItemGrpc(
- replyItem: loadingState.response![index],
- replyLevel: '1',
- replyReply: (replyItem, id) =>
- replyReply(context, replyItem, id),
- onReply: () {
- _htmlRenderCtr.onReply(
- context,
- replyItem: loadingState.response![index],
- index: index,
- );
- },
- onDelete: (subIndex) =>
- _htmlRenderCtr.onRemove(index, subIndex),
- upMid: _htmlRenderCtr.upMid,
- callback: _getImageCallback,
- onCheckReply: (item) =>
- _htmlRenderCtr.onCheckReply(context, item),
- onToggleTop: (isUpTop, rpid) => _htmlRenderCtr.onToggleTop(
- index,
- _htmlRenderCtr.oid,
- _htmlRenderCtr.type,
- isUpTop,
- rpid,
- ),
- );
- }
- },
- )
- : HttpError(
- onReload: _htmlRenderCtr.onReload,
- ),
- Error() => HttpError(
- errMsg: loadingState.errMsg,
- onReload: _htmlRenderCtr.onReload,
- ),
- LoadingState() => throw UnimplementedError(),
- };
- }
-
- Container replyHeader() {
- return Container(
- height: 45,
- padding: const EdgeInsets.only(left: 12, right: 6),
- child: Row(
- children: [
- const Text('回复'),
- const Spacer(),
- SizedBox(
- height: 35,
- child: TextButton.icon(
- onPressed: () => _htmlRenderCtr.queryBySort(),
- icon: const Icon(Icons.sort, size: 16),
- label: Obx(
- () => Text(
- _htmlRenderCtr.sortType.value.label,
- style: const TextStyle(fontSize: 13),
- ),
- ),
- ),
- )
- ],
- ),
- );
- }
-
- Widget get _buildHeader => Padding(
- padding: const EdgeInsets.fromLTRB(12, 12, 12, 8),
- child: GestureDetector(
- onTap: () {
- if (_htmlRenderCtr.mid != null) {
- Get.toNamed('/member?mid=${_htmlRenderCtr.mid}');
- }
- },
- child: Row(
- children: [
- NetworkImgLayer(
- width: 40,
- height: 40,
- type: 'avatar',
- src: _htmlRenderCtr.response['avatar']!,
- ),
- const SizedBox(width: 10),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- _htmlRenderCtr.response['uname'],
- style: TextStyle(
- fontSize:
- Theme.of(context).textTheme.titleSmall!.fontSize,
- ),
- ),
- Text(
- _htmlRenderCtr.response['updateTime'],
- style: TextStyle(
- color: Theme.of(context).colorScheme.outline,
- fontSize:
- Theme.of(context).textTheme.labelSmall!.fontSize,
- ),
- ),
- ],
- ),
- const Spacer(),
- ],
+ );
+ },
),
),
);
-
- Widget get _buildContent => SliverPadding(
- padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
- sliver: Obx(
- () => _htmlRenderCtr.loaded.value
- ? _htmlRenderCtr.response['isJsonContent'] == true
- ? SliverLayoutBuilder(
- builder: (context, constraints) => articleContent(
- context: context,
- list: _htmlRenderCtr.response['content'],
- callback: _getImageCallback,
- maxWidth: constraints.crossAxisExtent,
- ),
- )
- : SliverToBoxAdapter(
- child: LayoutBuilder(
- builder: (context, constraints) => htmlRender(
- context: context,
- htmlContent: _htmlRenderCtr.response['content'],
- constrainedWidth: constraints.maxWidth,
- callback: _getImageCallback,
- ),
- ),
- )
- : SliverToBoxAdapter(child: const SizedBox()),
- ),
- );
}