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

@@ -11,6 +11,7 @@ class ArticleContentModel {
LinkCard? linkCard; LinkCard? linkCard;
Code? code; Code? code;
L1st? list; L1st? list;
Text? heading;
ArticleContentModel.fromJson(Map<String, dynamic> json) { ArticleContentModel.fromJson(Map<String, dynamic> json) {
align = json['align']; align = json['align'];
@@ -24,6 +25,7 @@ class ArticleContentModel {
: LinkCard.fromJson(json['link_card']); : LinkCard.fromJson(json['link_card']);
code = json['code'] == null ? null : Code.fromJson(json['code']); code = json['code'] == null ? null : Code.fromJson(json['code']);
list = json['list'] == null ? null : L1st.fromJson(json['list']); list = json['list'] == null ? null : L1st.fromJson(json['list']);
heading = json['heading'] == null ? null : Text.fromJson(json['heading']);
} }
} }

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_preview_type.dart';
import 'package:PiliPlus/models/common/image_type.dart'; import 'package:PiliPlus/models/common/image_type.dart';
import 'package:PiliPlus/models/dynamics/article_content_model.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/models/dynamics/result.dart';
import 'package:PiliPlus/pages/dynamics/widgets/vote.dart'; import 'package:PiliPlus/pages/dynamics/widgets/vote.dart';
import 'package:PiliPlus/utils/app_scheme.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/foundation.dart' show kDebugMode;
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.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:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:re_highlight/languages/all.dart'; import 'package:re_highlight/languages/all.dart';
import 'package:re_highlight/re_highlight.dart'; import 'package:re_highlight/re_highlight.dart';
@@ -36,50 +36,11 @@ class OpusContent extends StatelessWidget {
required this.maxWidth, required this.maxWidth,
}); });
static TextStyle _getStyle(Style? style, [Color? color, double? fontSize]) => static InlineSpan _node2Widget({
TextStyle( required Node item,
decoration: style?.strikethrough == true required ColorScheme colorScheme,
? TextDecoration.lineThrough bool isQuote = false,
: null, }) {
decorationColor: color,
fontStyle: style?.italic == true ? FontStyle.italic : null,
fontWeight: style?.bold == true ? FontWeight.bold : null,
color: color,
fontSize: fontSize,
);
static TextSpan _getSpan(Word? word, [Color? defaultColor]) => TextSpan(
text: word?.words,
style: _getStyle(
word?.style,
word?.color != null ? Color(word!.color!) : defaultColor,
word?.fontSize,
),
);
@override
Widget build(BuildContext context) {
// if (kDebugMode) debugPrint('opusContent');
if (opus.isEmpty) {
return const SliverToBoxAdapter();
}
late final highlight = Highlight()..registerLanguages(builtinAllLanguages);
late final isDarkMode = context.isDarkMode;
final colorScheme = Theme.of(context).colorScheme;
return SliverList.separated(
itemCount: opus.length,
itemBuilder: (context, index) {
final element = opus[index];
try {
switch (element.paraType) {
case 1 || 4:
final isQuote = element.paraType == 4;
Widget widget = SelectableText.rich(
textAlign: element.align == 1 ? TextAlign.center : null,
TextSpan(
children: element.text?.nodes?.map((item) {
switch (item.type) { switch (item.type) {
case 'TEXT_NODE_TYPE_RICH' when (item.rich != null): case 'TEXT_NODE_TYPE_RICH' when (item.rich != null):
Rich rich = item.rich!; Rich rich = item.rich!;
@@ -143,7 +104,57 @@ class OpusContent extends StatelessWidget {
isQuote ? colorScheme.onSurfaceVariant : null, isQuote ? colorScheme.onSurfaceVariant : null,
); );
} }
}).toList(), }
static TextStyle _getStyle(Style? style, [Color? color, double? fontSize]) =>
TextStyle(
decoration: style?.strikethrough == true
? TextDecoration.lineThrough
: null,
decorationColor: color,
fontStyle: style?.italic == true ? FontStyle.italic : null,
fontWeight: style?.bold == true ? FontWeight.bold : null,
color: color,
fontSize: fontSize,
);
static TextSpan _getSpan(Word? word, [Color? defaultColor]) => TextSpan(
text: word?.words,
style: _getStyle(
word?.style,
word?.color != null ? Color(word!.color!) : defaultColor,
word?.fontSize,
),
);
@override
Widget build(BuildContext context) {
// if (kDebugMode) debugPrint('opusContent');
if (opus.isEmpty) {
return const SliverToBoxAdapter();
}
late final highlight = Highlight()..registerLanguages(builtinAllLanguages);
late final isDarkMode = context.isDarkMode;
final colorScheme = Theme.of(context).colorScheme;
return SliverList.separated(
itemCount: opus.length,
itemBuilder: (context, index) {
final element = opus[index];
try {
switch (element.paraType) {
case 1 || 4:
final isQuote = element.paraType == 4;
Widget widget = SelectableText.rich(
textAlign: element.align == 1 ? TextAlign.center : null,
TextSpan(
children: element.text?.nodes
?.map(
(item) =>
_node2Widget(item: item, colorScheme: colorScheme),
)
.toList(),
), ),
); );
if (isQuote) { if (isQuote) {
@@ -586,6 +597,16 @@ class OpusContent extends StatelessWidget {
width: double.infinity, width: double.infinity,
child: SelectableText.rich(renderer.span!), 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: default:
if (kDebugMode) debugPrint('unknown type ${element.paraType}'); if (kDebugMode) debugPrint('unknown type ${element.paraType}');
if (element.text?.nodes?.isNotEmpty == true) { if (element.text?.nodes?.isNotEmpty == true) {