mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
opt: opus quote (#771)
This commit is contained in:
committed by
GitHub
parent
e65ec1b0b9
commit
451a84e696
@@ -357,8 +357,7 @@ class _ArticlePageState extends State<ArticlePage>
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
debugPrint('json page');
|
debugPrint('json page');
|
||||||
content = opusContent(
|
content = OpusContent(
|
||||||
context: context,
|
|
||||||
opus: _articleCtr.opus!,
|
opus: _articleCtr.opus!,
|
||||||
callback: _getImageCallback,
|
callback: _getImageCallback,
|
||||||
maxWidth: maxWidth,
|
maxWidth: maxWidth,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactivevie
|
|||||||
show SourceModel;
|
show SourceModel;
|
||||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||||
import 'package:PiliPlus/models/dynamics/article_content_model.dart'
|
import 'package:PiliPlus/models/dynamics/article_content_model.dart'
|
||||||
show ArticleContentModel;
|
show ArticleContentModel, Style, Word;
|
||||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||||
import 'package:PiliPlus/utils/extension.dart';
|
import 'package:PiliPlus/utils/extension.dart';
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
@@ -15,259 +15,257 @@ import 'package:re_highlight/languages/all.dart';
|
|||||||
import 'package:re_highlight/re_highlight.dart';
|
import 'package:re_highlight/re_highlight.dart';
|
||||||
import 'package:re_highlight/styles/all.dart';
|
import 'package:re_highlight/styles/all.dart';
|
||||||
|
|
||||||
Widget opusContent({
|
class OpusContent extends StatelessWidget {
|
||||||
required BuildContext context,
|
final List<ArticleContentModel> opus;
|
||||||
required List<ArticleContentModel> opus,
|
final void Function(List<String>, int)? callback;
|
||||||
Function(List<String>, int)? callback,
|
final double maxWidth;
|
||||||
required double maxWidth,
|
|
||||||
}) {
|
|
||||||
debugPrint('opusContent');
|
|
||||||
|
|
||||||
if (opus.isEmpty) {
|
const OpusContent({
|
||||||
return const SliverToBoxAdapter();
|
super.key,
|
||||||
}
|
required this.opus,
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
this.callback,
|
||||||
return SliverList.separated(
|
required this.maxWidth,
|
||||||
itemCount: opus.length,
|
});
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final element = opus[index];
|
static TextStyle _getStyle(Style? style, [Color? color, double? fontSize]) =>
|
||||||
try {
|
TextStyle(
|
||||||
switch (element.paraType) {
|
decoration:
|
||||||
case 1 || 4:
|
style?.strikethrough == true ? TextDecoration.lineThrough : null,
|
||||||
return SelectableText.rich(
|
fontStyle: style?.italic == true ? FontStyle.italic : null,
|
||||||
textAlign: element.align == 1 ? TextAlign.center : null,
|
fontWeight: style?.bold == true ? FontWeight.bold : null,
|
||||||
TextSpan(
|
color: color,
|
||||||
children: element.text?.nodes?.map<TextSpan>((item) {
|
fontSize: fontSize,
|
||||||
if (item.rich != null) {
|
);
|
||||||
return TextSpan(
|
|
||||||
text: '\u{1F517}${item.rich?.text}',
|
static TextSpan _getSpan(Word? word) => TextSpan(
|
||||||
style: TextStyle(
|
text: word?.words,
|
||||||
decoration: item.rich?.style?.strikethrough == true
|
style: _getStyle(
|
||||||
? TextDecoration.lineThrough
|
word?.style,
|
||||||
: null,
|
word?.color != null ? Color(word!.color!) : null,
|
||||||
fontStyle: item.rich?.style?.italic == true
|
word?.fontSize,
|
||||||
? FontStyle.italic
|
));
|
||||||
: null,
|
|
||||||
fontWeight: item.rich?.style?.bold == true
|
@override
|
||||||
? FontWeight.bold
|
Widget build(BuildContext context) {
|
||||||
: null,
|
debugPrint('opusContent');
|
||||||
color: colorScheme.primary,
|
|
||||||
),
|
if (opus.isEmpty) {
|
||||||
recognizer: TapGestureRecognizer()
|
return const SliverToBoxAdapter();
|
||||||
..onTap = () {
|
}
|
||||||
if (item.rich?.jumpUrl != null) {
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
PiliScheme.routePushFromUrl(item.rich!.jumpUrl!);
|
return SliverList.separated(
|
||||||
}
|
itemCount: opus.length,
|
||||||
},
|
itemBuilder: (context, index) {
|
||||||
);
|
final element = opus[index];
|
||||||
}
|
try {
|
||||||
return TextSpan(
|
switch (element.paraType) {
|
||||||
text: item.word?.words,
|
case 1:
|
||||||
style: TextStyle(
|
|
||||||
decoration: item.word?.style?.strikethrough == true
|
|
||||||
? TextDecoration.lineThrough
|
|
||||||
: null,
|
|
||||||
fontStyle: item.word?.style?.italic == true
|
|
||||||
? FontStyle.italic
|
|
||||||
: null,
|
|
||||||
fontWeight:
|
|
||||||
item.word?.style?.bold == true ? FontWeight.bold : null,
|
|
||||||
color: item.word?.color != null
|
|
||||||
? Color(item.word!.color!)
|
|
||||||
: null,
|
|
||||||
fontSize: item.word?.fontSize,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList()),
|
|
||||||
);
|
|
||||||
case 2 when (element.pic != null):
|
|
||||||
element.pic!.pics!.first.onCalHeight(maxWidth);
|
|
||||||
return Hero(
|
|
||||||
tag: element.pic!.pics!.first.url!,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
if (callback != null) {
|
|
||||||
callback([element.pic!.pics!.first.url!], 0);
|
|
||||||
} else {
|
|
||||||
context.imageView(
|
|
||||||
initialPage: 0,
|
|
||||||
imgList: [
|
|
||||||
SourceModel(url: element.pic!.pics!.first.url!)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: NetworkImgLayer(
|
|
||||||
width: maxWidth,
|
|
||||||
height: element.pic!.pics!.first.calHeight,
|
|
||||||
src: element.pic!.pics!.first.url!,
|
|
||||||
quality: 60,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
case 3 when (element.line != null):
|
|
||||||
return CachedNetworkImage(
|
|
||||||
width: maxWidth,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
height: element.line?.pic?.height?.toDouble(),
|
|
||||||
imageUrl: Utils.thumbnailImgUrl(element.line!.pic!.url!),
|
|
||||||
);
|
|
||||||
case 5 when (element.list != null):
|
|
||||||
return SelectableText.rich(
|
|
||||||
TextSpan(
|
|
||||||
children: element.list!.items?.asMap().entries.map((entry) {
|
|
||||||
return TextSpan(
|
|
||||||
children: [
|
|
||||||
WidgetSpan(
|
|
||||||
child: Icon(MdiIcons.circleMedium),
|
|
||||||
alignment: PlaceholderAlignment.middle,
|
|
||||||
),
|
|
||||||
...entry.value.nodes!.map((item) {
|
|
||||||
return TextSpan(
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: item.word?.words,
|
|
||||||
style: TextStyle(
|
|
||||||
decoration:
|
|
||||||
item.word?.style?.strikethrough == true
|
|
||||||
? TextDecoration.lineThrough
|
|
||||||
: null,
|
|
||||||
fontStyle: item.word?.style?.italic == true
|
|
||||||
? FontStyle.italic
|
|
||||||
: null,
|
|
||||||
fontWeight: item.word?.style?.bold == true
|
|
||||||
? FontWeight.bold
|
|
||||||
: null,
|
|
||||||
color: item.word?.color != null
|
|
||||||
? Color(item.word!.color!)
|
|
||||||
: null,
|
|
||||||
fontSize: item.word?.fontSize,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
if (entry.key < element.list!.items!.length - 1)
|
|
||||||
const TextSpan(text: '\n'),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
case 6 when (element.linkCard?.card?.ugc != null):
|
|
||||||
return Material(
|
|
||||||
shape: const RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(8)),
|
|
||||||
),
|
|
||||||
color: colorScheme.onInverseSurface,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
try {
|
|
||||||
PiliScheme.videoPush(
|
|
||||||
int.parse(element.linkCard!.card!.oid!),
|
|
||||||
null,
|
|
||||||
);
|
|
||||||
} catch (_) {}
|
|
||||||
},
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
NetworkImgLayer(
|
|
||||||
radius: 6,
|
|
||||||
width: 65 * StyleString.aspectRatio,
|
|
||||||
height: 65,
|
|
||||||
src: element.linkCard!.card!.ugc!.cover,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(element.linkCard!.card!.ugc!.title!),
|
|
||||||
Text(
|
|
||||||
element.linkCard!.card!.ugc!.descSecond!,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 13,
|
|
||||||
color: colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
case 7 when (element.code != null):
|
|
||||||
final Highlight highlight = Highlight()
|
|
||||||
..registerLanguages(builtinAllLanguages);
|
|
||||||
final HighlightResult result = highlight.highlightAuto(
|
|
||||||
element.code!.content!,
|
|
||||||
element.code!.lang == 'language-clike'
|
|
||||||
? const ['c', 'java']
|
|
||||||
: [
|
|
||||||
element.code!.lang!
|
|
||||||
.replaceAll('language-', '')
|
|
||||||
.replaceAll('like', ''),
|
|
||||||
]);
|
|
||||||
final TextSpanRenderer renderer = TextSpanRenderer(
|
|
||||||
const TextStyle(), builtinAllThemes['github']!);
|
|
||||||
result.render(renderer);
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
|
||||||
color: colorScheme.onInverseSurface,
|
|
||||||
),
|
|
||||||
width: double.infinity,
|
|
||||||
child: SelectableText.rich(renderer.span!),
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
debugPrint('unknown type ${element.paraType}');
|
|
||||||
if (element.text?.nodes?.isNotEmpty == true) {
|
|
||||||
return SelectableText.rich(
|
return SelectableText.rich(
|
||||||
textAlign: element.align == 1 ? TextAlign.center : null,
|
textAlign: element.align == 1 ? TextAlign.center : null,
|
||||||
TextSpan(
|
TextSpan(
|
||||||
children: element.text!.nodes!.map<TextSpan>((item) {
|
children: element.text?.nodes?.map<TextSpan>((item) {
|
||||||
return TextSpan(
|
if (item.rich != null) {
|
||||||
text: item.word?.words,
|
return TextSpan(
|
||||||
style: TextStyle(
|
text: '\u{1F517}${item.rich?.text}',
|
||||||
decoration: item.word?.style?.strikethrough == true
|
style: _getStyle(item.rich?.style, colorScheme.primary),
|
||||||
? TextDecoration.lineThrough
|
recognizer: item.rich?.jumpUrl == null
|
||||||
: null,
|
? null
|
||||||
fontStyle: item.word?.style?.italic == true
|
: (TapGestureRecognizer()
|
||||||
? FontStyle.italic
|
..onTap = () {
|
||||||
: null,
|
PiliScheme.routePushFromUrl(item.rich!.jumpUrl!);
|
||||||
fontWeight: item.word?.style?.bold == true
|
}),
|
||||||
? FontWeight.bold
|
);
|
||||||
: null,
|
}
|
||||||
color: item.word?.color != null
|
return _getSpan(item.word);
|
||||||
? Color(item.word!.color!)
|
|
||||||
: null,
|
|
||||||
fontSize: item.word?.fontSize,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList()),
|
}).toList()),
|
||||||
);
|
);
|
||||||
}
|
case 4:
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.only(left: 8),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
left: BorderSide(color: Color(0xFFE0E0E0), width: 4),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: SelectableText.rich(
|
||||||
|
textAlign: element.align == 1 ? TextAlign.center : null,
|
||||||
|
TextSpan(
|
||||||
|
children: element.text?.nodes?.map<TextSpan>((item) {
|
||||||
|
if (item.rich != null) {
|
||||||
|
return TextSpan(
|
||||||
|
text: '\u{1F517}${item.rich?.text}',
|
||||||
|
style: _getStyle(item.rich?.style, colorScheme.primary),
|
||||||
|
recognizer: item.rich?.jumpUrl == null
|
||||||
|
? null
|
||||||
|
: (TapGestureRecognizer()
|
||||||
|
..onTap = () {
|
||||||
|
PiliScheme.routePushFromUrl(
|
||||||
|
item.rich!.jumpUrl!);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return TextSpan(
|
||||||
|
text: item.word?.words,
|
||||||
|
style: _getStyle(
|
||||||
|
item.word?.style,
|
||||||
|
item.word?.color != null
|
||||||
|
? Color(item.word!.color!).withOpacity(0.7)
|
||||||
|
: colorScheme.onSurface.withOpacity(0.7),
|
||||||
|
item.word?.fontSize,
|
||||||
|
));
|
||||||
|
}).toList()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case 2 when (element.pic != null):
|
||||||
|
element.pic!.pics!.first.onCalHeight(maxWidth);
|
||||||
|
return Hero(
|
||||||
|
tag: element.pic!.pics!.first.url!,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
if (callback != null) {
|
||||||
|
callback!([element.pic!.pics!.first.url!], 0);
|
||||||
|
} else {
|
||||||
|
context.imageView(
|
||||||
|
initialPage: 0,
|
||||||
|
imgList: [
|
||||||
|
SourceModel(url: element.pic!.pics!.first.url!)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: NetworkImgLayer(
|
||||||
|
width: maxWidth,
|
||||||
|
height: element.pic!.pics!.first.calHeight,
|
||||||
|
src: element.pic!.pics!.first.url!,
|
||||||
|
quality: 60,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case 3 when (element.line != null):
|
||||||
|
return CachedNetworkImage(
|
||||||
|
width: maxWidth,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
height: element.line!.pic!.height?.toDouble(),
|
||||||
|
imageUrl: Utils.thumbnailImgUrl(element.line!.pic!.url!),
|
||||||
|
);
|
||||||
|
case 5 when (element.list != null):
|
||||||
|
return SelectableText.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: element.list!.items?.indexed.map((entry) {
|
||||||
|
return TextSpan(
|
||||||
|
children: [
|
||||||
|
WidgetSpan(
|
||||||
|
child: Icon(MdiIcons.circleMedium),
|
||||||
|
alignment: PlaceholderAlignment.middle,
|
||||||
|
),
|
||||||
|
...entry.$2.nodes!.map((item) {
|
||||||
|
return _getSpan(item.word);
|
||||||
|
}),
|
||||||
|
if (entry.$1 < element.list!.items!.length - 1)
|
||||||
|
const TextSpan(text: '\n'),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case 6 when (element.linkCard?.card?.ugc != null):
|
||||||
|
return Material(
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
|
),
|
||||||
|
color: colorScheme.onInverseSurface,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
try {
|
||||||
|
PiliScheme.videoPush(
|
||||||
|
int.parse(element.linkCard!.card!.oid!),
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
} catch (_) {}
|
||||||
|
},
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
NetworkImgLayer(
|
||||||
|
radius: 6,
|
||||||
|
width: 65 * StyleString.aspectRatio,
|
||||||
|
height: 65,
|
||||||
|
src: element.linkCard!.card!.ugc!.cover,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(element.linkCard!.card!.ugc!.title!),
|
||||||
|
Text(
|
||||||
|
element.linkCard!.card!.ugc!.descSecond!,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case 7 when (element.code != null):
|
||||||
|
final Highlight highlight = Highlight()
|
||||||
|
..registerLanguages(builtinAllLanguages);
|
||||||
|
final HighlightResult result = highlight.highlightAuto(
|
||||||
|
element.code!.content!,
|
||||||
|
element.code!.lang == 'language-clike'
|
||||||
|
? const ['c', 'java']
|
||||||
|
: [
|
||||||
|
element.code!.lang!
|
||||||
|
.replaceAll('language-', '')
|
||||||
|
.replaceAll('like', ''),
|
||||||
|
]);
|
||||||
|
final TextSpanRenderer renderer = TextSpanRenderer(
|
||||||
|
const TextStyle(), builtinAllThemes['github']!);
|
||||||
|
result.render(renderer);
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
color: colorScheme.onInverseSurface,
|
||||||
|
),
|
||||||
|
width: double.infinity,
|
||||||
|
child: SelectableText.rich(renderer.span!),
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
debugPrint('unknown type ${element.paraType}');
|
||||||
|
if (element.text?.nodes?.isNotEmpty == true) {
|
||||||
|
return SelectableText.rich(
|
||||||
|
textAlign: element.align == 1 ? TextAlign.center : null,
|
||||||
|
TextSpan(
|
||||||
|
children: element.text!.nodes!
|
||||||
|
.map<TextSpan>((item) => _getSpan(item.word))
|
||||||
|
.toList()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return SelectableText('不支持的类型 (${element.paraType})',
|
return SelectableText('不支持的类型 (${element.paraType})',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
));
|
));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return SelectableText('错误的类型 $e',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.red,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
},
|
||||||
return SelectableText('错误的类型 $e',
|
separatorBuilder: (context, index) => const SizedBox(height: 10),
|
||||||
style: const TextStyle(
|
);
|
||||||
fontWeight: FontWeight.bold,
|
}
|
||||||
color: Colors.red,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
separatorBuilder: (context, index) => const SizedBox(height: 10),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user