opt: article content

This commit is contained in:
bggRGjQaUbCoE
2024-10-12 13:58:14 +08:00
parent 1ecbaf16c7
commit 17f0277be9
3 changed files with 242 additions and 76 deletions

View File

@@ -1,9 +1,9 @@
import 'package:PiliPalaX/common/widgets/network_img_layer.dart'; import 'package:PiliPalaX/common/widgets/network_img_layer.dart';
import 'package:PiliPalaX/models/dynamics/article_content_model.dart'; import 'package:PiliPalaX/models/dynamics/article_content_model.dart';
import 'package:PiliPalaX/pages/preview/view.dart'; import 'package:PiliPalaX/pages/preview/view.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_html/flutter_html.dart';
import 'package:get/get.dart';
class ArticleContent extends StatelessWidget { class ArticleContent extends StatelessWidget {
const ArticleContent({ const ArticleContent({
@@ -15,22 +15,51 @@ class ArticleContent extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
List<String>? imgList = list
.where((item) => item.pic != null)
.toList()
.map((item) => item.pic?.pics?.first.url ?? '')
.toList();
return SliverList.separated( return SliverList.separated(
itemCount: list.length, itemCount: list.length,
itemBuilder: (_, index) { itemBuilder: (_, index) {
ArticleContentModel item = list[index]; ArticleContentModel item = list[index];
if (item.insert is String) { if (item.text != null) {
return SelectableText( List<InlineSpan> spanList = [];
(item.insert as String).replaceAll('\n', '\n\n'), item.text?.nodes?.forEach((item) {
style: TextStyle( spanList.add(TextSpan(
letterSpacing: 0.3, text: item.word?.words,
fontSize: FontSize.large.value, style: TextStyle(
height: LineHeight.percent(125).size, letterSpacing: 0.3,
fontWeight: fontSize: FontSize.large.value,
item.attributes?.bold == true ? FontWeight.bold : null, height: LineHeight.percent(125).size,
fontStyle:
item.word?.style?.italic == true ? FontStyle.italic : null,
color: item.word?.color != null
? Color(int.parse(
item.word!.color!.replaceFirst('#', 'FF'),
radix: 16,
))
: null,
decoration: item.word?.style?.strikethrough == true
? TextDecoration.lineThrough
: null,
fontWeight:
item.word?.style?.bold == true ? FontWeight.bold : null,
),
));
});
return SelectableText.rich(TextSpan(children: spanList));
} else if (item.line != null) {
return Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(vertical: 10),
child: CachedNetworkImage(
imageUrl: item.line?.pic?.url ?? '',
height: item.line?.pic?.height?.toDouble(),
), ),
); );
} else if (item.attributes?.clazz == 'normal-img') { } else if (item.pic != null) {
return LayoutBuilder( return LayoutBuilder(
builder: (_, constraints) => GestureDetector( builder: (_, constraints) => GestureDetector(
onTap: () { onTap: () {
@@ -39,8 +68,8 @@ class ArticleContent extends StatelessWidget {
context: context, context: context,
builder: (context) { builder: (context) {
return ImagePreview( return ImagePreview(
initialPage: 0, initialPage: imgList.indexOf(item.pic!.pics!.first.url!),
imgList: [item.insert.nativeImage?.url], imgList: imgList,
); );
}, },
); );
@@ -48,22 +77,12 @@ class ArticleContent extends StatelessWidget {
child: NetworkImgLayer( child: NetworkImgLayer(
width: constraints.maxWidth, width: constraints.maxWidth,
height: constraints.maxWidth * height: constraints.maxWidth *
item.insert.nativeImage?.height / item.pic!.pics!.first.height! /
item.insert.nativeImage?.width, item.pic!.pics!.first.width!,
src: item.insert.nativeImage?.url, src: item.pic!.pics!.first.url,
), ),
), ),
); );
// return image(
// constrainedWidth,
// [
// ImageModel(
// width: item.insert.nativeImage?.width,
// height: item.insert.nativeImage?.height,
// url: item.insert.nativeImage?.url,
// ),
// ],
// );
} else { } else {
return const SizedBox.shrink(); return const SizedBox.shrink();
// return Text('unsupported content'); // return Text('unsupported content');

View File

@@ -1,6 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'package:PiliPalaX/common/widgets/article_content.dart';
import 'package:PiliPalaX/models/dynamics/article_content_model.dart'; import 'package:PiliPalaX/models/dynamics/article_content_model.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart';
import 'package:html/parser.dart'; import 'package:html/parser.dart';
@@ -116,9 +115,11 @@ class HtmlHttp {
final jsonString = match.group(1); final jsonString = match.group(1);
if (jsonString != null) { if (jsonString != null) {
try { try {
opusContent = jsonDecode(jsonString)['readInfo']['content']; dynamic json = jsonDecode(jsonString);
opusContent = json['readInfo']['content'];
try { try {
opusContent = (jsonDecode(opusContent)['ops'] as List) opusContent = (json['readInfo']?['opus']?['content']
?['paragraphs'] as List)
.map((item) => ArticleContentModel.fromJson(item)) .map((item) => ArticleContentModel.fromJson(item))
.toList(); .toList();
isJsonContent = true; isJsonContent = true;

View File

@@ -1,72 +1,218 @@
class ArticleContentModel { class ArticleContentModel {
ArticleContentModel({ ArticleContentModel({
this.attributes, this.paraType,
this.insert, this.text,
this.format,
this.line,
}); });
Attributes? attributes; int? paraType;
dynamic insert; Text? text;
Format? format;
Line? line;
Pic? pic;
ArticleContentModel.fromJson(Map<String, dynamic> json) { ArticleContentModel.fromJson(Map<String, dynamic> json) {
attributes = json['attributes'] == null paraType = json['para_type'];
? null text = json['text'] == null ? null : Text.fromJson(json['text']);
: Attributes.fromJson(json['attributes']); format = json['format'] == null ? null : Format.fromJson(json['format']);
insert = json['insert'] == null line = json['line'] == null ? null : Line.fromJson(json['line']);
? null pic = json['pic'] == null ? null : Pic.fromJson(json['pic']);
: json['attributes']?['class'] == 'normal-img'
? Insert.fromJson(json['insert'])
: json['insert'];
} }
} }
class Insert { class Pic {
Insert({ Pic({
this.nativeImage,
});
NativeImage? nativeImage;
Insert.fromJson(Map<String, dynamic> json) {
nativeImage = json['native-image'] == null
? null
: NativeImage.fromJson(json['native-image']);
}
}
class NativeImage {
NativeImage({
this.alt,
this.url, this.url,
this.width, this.width,
this.height, this.height,
this.size, this.size,
this.status, this.pics,
this.style,
}); });
String? url;
int? width;
int? height;
double? size;
List<Pic>? pics;
int? style;
dynamic alt; Pic.fromJson(Map<String, dynamic> json) {
dynamic url;
dynamic width;
dynamic height;
dynamic size;
dynamic status;
NativeImage.fromJson(Map<String, dynamic> json) {
alt = json['alt'];
url = json['url']; url = json['url'];
width = json['width']; width = json['width'];
height = json['height']; height = json['height'];
size = json['size']; size = json['size'];
status = json['status']; pics = (json['pics'] as List<dynamic>?)
?.map((item) => Pic.fromJson(item))
.toList();
style = json['style'];
} }
} }
class Attributes { class Line {
Attributes({ Line({
this.clazz, this.pic,
}); });
String? clazz; Pic? pic;
bool? bold;
Attributes.fromJson(Map<String, dynamic> json) { Line.fromJson(Map<String, dynamic> json) {
clazz = json['class']; pic = json['pic'] == null ? null : Pic.fromJson(json['pic']);
bold = json['bold'];
} }
} }
class Format {
Format({
this.align,
});
int? align;
Format.fromJson(Map<String, dynamic> json) {
align = json['align'];
}
}
class Text {
Text({
this.nodes,
});
List<Nodes>? nodes;
Text.fromJson(Map<String, dynamic> json) {
nodes = (json['nodes'] as List<dynamic>?)
?.map((item) => Nodes.fromJson(item))
.toList();
}
}
class Nodes {
Nodes({
this.nodeType,
this.word,
});
int? nodeType;
Word? word;
Nodes.fromJson(Map<String, dynamic> json) {
nodeType = json['node_type'];
word = json['word'] == null ? null : Word.fromJson(json['word']);
}
}
class Word {
Word({
this.words,
this.fontSize,
this.style,
this.color,
});
String? words;
int? fontSize;
Style? style;
String? color;
Word.fromJson(Map<String, dynamic> json) {
words = json['words'];
fontSize = json['font_size'];
style = json['style'] == null ? null : Style.fromJson(json['style']);
color = json['color'];
}
}
class Style {
Style({
this.bold,
this.italic,
this.strikethrough,
});
bool? bold;
bool? italic;
bool? strikethrough;
Style.fromJson(Map<String, dynamic> json) {
bold = json['bold'];
italic = json['italic'];
strikethrough = json['strikethrough'];
}
}
// class ArticleContentModel {
// ArticleContentModel({
// this.attributes,
// this.insert,
// });
// Attributes? attributes;
// dynamic insert;
// ArticleContentModel.fromJson(Map<String, dynamic> json) {
// attributes = json['attributes'] == null
// ? null
// : Attributes.fromJson(json['attributes']);
// insert = json['insert'] == null
// ? null
// : json['attributes']?['class'] == 'normal-img'
// ? Insert.fromJson(json['insert'])
// : json['insert'];
// }
// }
// class Insert {
// Insert({
// this.nativeImage,
// });
// NativeImage? nativeImage;
// Insert.fromJson(Map<String, dynamic> json) {
// nativeImage = json['native-image'] == null
// ? null
// : NativeImage.fromJson(json['native-image']);
// }
// }
// class NativeImage {
// NativeImage({
// this.alt,
// this.url,
// this.width,
// this.height,
// this.size,
// this.status,
// });
// dynamic alt;
// dynamic url;
// dynamic width;
// dynamic height;
// dynamic size;
// dynamic status;
// NativeImage.fromJson(Map<String, dynamic> json) {
// alt = json['alt'];
// url = json['url'];
// width = json['width'];
// height = json['height'];
// size = json['size'];
// status = json['status'];
// }
// }
// class Attributes {
// Attributes({
// this.clazz,
// this.bold,
// this.color,
// this.italic,
// this.strike,
// });
// String? clazz;
// bool? bold;
// String? color;
// bool? italic;
// bool? strike;
// Attributes.fromJson(Map<String, dynamic> json) {
// clazz = json['class'];
// bold = json['bold'];
// color = json['color'];
// italic = json['italic'];
// strike = json['strike'];
// }
// }