show article heading

Closes #1338

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-09-27 19:56:08 +08:00
parent 7fedfb8963
commit 2333736a72
2 changed files with 90 additions and 67 deletions

View File

@@ -6,7 +6,7 @@ import 'package:PiliPlus/http/constants.dart';
import 'package:PiliPlus/models/common/image_preview_type.dart';
import 'package:PiliPlus/models/common/image_type.dart';
import 'package:PiliPlus/models/dynamics/article_content_model.dart'
show ArticleContentModel, Rich, Style, Word;
show ArticleContentModel, Rich, Style, Word, Node;
import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/pages/dynamics/widgets/vote.dart';
import 'package:PiliPlus/utils/app_scheme.dart';
@@ -19,7 +19,7 @@ import 'package:cached_network_svg_image/cached_network_svg_image.dart';
import 'package:flutter/foundation.dart' show kDebugMode;
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart' hide ContextExtensionss;
import 'package:get/get.dart' hide ContextExtensionss, Node;
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:re_highlight/languages/all.dart';
import 'package:re_highlight/re_highlight.dart';
@@ -36,6 +36,76 @@ class OpusContent extends StatelessWidget {
required this.maxWidth,
});
static InlineSpan _node2Widget({
required Node item,
required ColorScheme colorScheme,
bool isQuote = false,
}) {
switch (item.type) {
case 'TEXT_NODE_TYPE_RICH' when (item.rich != null):
Rich rich = item.rich!;
switch (rich.type) {
case 'RICH_TEXT_NODE_TYPE_EMOJI':
Emoji emoji = rich.emoji!;
final size = 20.0 * emoji.size;
return WidgetSpan(
child: NetworkImgLayer(
width: size,
height: size,
src: emoji.url,
type: ImageType.emote,
),
);
default:
return TextSpan(
text:
'${rich.type == 'RICH_TEXT_NODE_TYPE_WEB' ? '\u{1F517}' : ''}${item.rich!.text}',
style: _getStyle(
rich.style,
rich.type == 'RICH_TEXT_NODE_TYPE_TEXT'
? null
: colorScheme.primary,
),
recognizer: TapGestureRecognizer()
..onTap = () {
switch (rich.type) {
case 'RICH_TEXT_NODE_TYPE_AT':
Get.toNamed('/member?mid=${rich.rid}');
// case 'RICH_TEXT_NODE_TYPE_TOPIC':
default:
if (rich.jumpUrl != null) {
PiliScheme.routePushFromUrl(
rich.jumpUrl!,
);
}
}
},
);
}
case 'TEXT_NODE_TYPE_FORMULA' when (item.formula != null):
final latex = item.formula!.latexContent!;
return WidgetSpan(
child: CachedNetworkSVGImage(
cacheKey: latex,
semanticsLabel: latex,
height: 65,
'${HttpString.apiBaseUrl}/x/web-frontend/mathjax/tex?formula=${Uri.encodeComponent(latex)}',
colorFilter: ColorFilter.mode(
colorScheme.onSurfaceVariant,
BlendMode.srcIn,
),
alignment: Alignment.centerLeft,
placeholderBuilder: (_) => Text(latex),
),
);
default:
return _getSpan(
item.word,
isQuote ? colorScheme.onSurfaceVariant : null,
);
}
}
static TextStyle _getStyle(Style? style, [Color? color, double? fontSize]) =>
TextStyle(
decoration: style?.strikethrough == true
@@ -79,71 +149,12 @@ class OpusContent extends StatelessWidget {
Widget widget = SelectableText.rich(
textAlign: element.align == 1 ? TextAlign.center : null,
TextSpan(
children: element.text?.nodes?.map((item) {
switch (item.type) {
case 'TEXT_NODE_TYPE_RICH' when (item.rich != null):
Rich rich = item.rich!;
switch (rich.type) {
case 'RICH_TEXT_NODE_TYPE_EMOJI':
Emoji emoji = rich.emoji!;
final size = 20.0 * emoji.size;
return WidgetSpan(
child: NetworkImgLayer(
width: size,
height: size,
src: emoji.url,
type: ImageType.emote,
),
);
default:
return TextSpan(
text:
'${rich.type == 'RICH_TEXT_NODE_TYPE_WEB' ? '\u{1F517}' : ''}${item.rich!.text}',
style: _getStyle(
rich.style,
rich.type == 'RICH_TEXT_NODE_TYPE_TEXT'
? null
: colorScheme.primary,
),
recognizer: TapGestureRecognizer()
..onTap = () {
switch (rich.type) {
case 'RICH_TEXT_NODE_TYPE_AT':
Get.toNamed('/member?mid=${rich.rid}');
// case 'RICH_TEXT_NODE_TYPE_TOPIC':
default:
if (rich.jumpUrl != null) {
PiliScheme.routePushFromUrl(
rich.jumpUrl!,
);
}
}
},
);
}
case 'TEXT_NODE_TYPE_FORMULA' when (item.formula != null):
final latex = item.formula!.latexContent!;
return WidgetSpan(
child: CachedNetworkSVGImage(
cacheKey: latex,
semanticsLabel: latex,
height: 65,
'${HttpString.apiBaseUrl}/x/web-frontend/mathjax/tex?formula=${Uri.encodeComponent(latex)}',
colorFilter: ColorFilter.mode(
colorScheme.onSurfaceVariant,
BlendMode.srcIn,
),
alignment: Alignment.centerLeft,
placeholderBuilder: (_) => Text(latex),
),
);
default:
return _getSpan(
item.word,
isQuote ? colorScheme.onSurfaceVariant : null,
);
}
}).toList(),
children: element.text?.nodes
?.map(
(item) =>
_node2Widget(item: item, colorScheme: colorScheme),
)
.toList(),
),
);
if (isQuote) {
@@ -586,6 +597,16 @@ class OpusContent extends StatelessWidget {
width: double.infinity,
child: SelectableText.rich(renderer.span!),
);
case 8 when (element.heading?.nodes?.isNotEmpty == true):
return SelectableText.rich(
TextSpan(
children: element.heading!.nodes!
.map(
(e) => _node2Widget(item: e, colorScheme: colorScheme),
)
.toList(),
),
);
default:
if (kDebugMode) debugPrint('unknown type ${element.paraType}');
if (element.text?.nodes?.isNotEmpty == true) {