From 17f0277be9c0a5efe782f4d7d1dfdef72ccd0996 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Sat, 12 Oct 2024 13:58:14 +0800 Subject: [PATCH] opt: article content --- lib/common/widgets/article_content.dart | 71 ++++-- lib/http/html.dart | 7 +- .../dynamics/article_content_model.dart | 240 ++++++++++++++---- 3 files changed, 242 insertions(+), 76 deletions(-) diff --git a/lib/common/widgets/article_content.dart b/lib/common/widgets/article_content.dart index 71ec1885..283a4869 100644 --- a/lib/common/widgets/article_content.dart +++ b/lib/common/widgets/article_content.dart @@ -1,9 +1,9 @@ import 'package:PiliPalaX/common/widgets/network_img_layer.dart'; import 'package:PiliPalaX/models/dynamics/article_content_model.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_html/flutter_html.dart'; -import 'package:get/get.dart'; class ArticleContent extends StatelessWidget { const ArticleContent({ @@ -15,22 +15,51 @@ class ArticleContent extends StatelessWidget { @override Widget build(BuildContext context) { + List? imgList = list + .where((item) => item.pic != null) + .toList() + .map((item) => item.pic?.pics?.first.url ?? '') + .toList(); return SliverList.separated( itemCount: list.length, itemBuilder: (_, index) { ArticleContentModel item = list[index]; - if (item.insert is String) { - return SelectableText( - (item.insert as String).replaceAll('\n', '\n\n'), - style: TextStyle( - letterSpacing: 0.3, - fontSize: FontSize.large.value, - height: LineHeight.percent(125).size, - fontWeight: - item.attributes?.bold == true ? FontWeight.bold : null, + if (item.text != null) { + List spanList = []; + item.text?.nodes?.forEach((item) { + spanList.add(TextSpan( + text: item.word?.words, + style: TextStyle( + letterSpacing: 0.3, + fontSize: FontSize.large.value, + 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( builder: (_, constraints) => GestureDetector( onTap: () { @@ -39,8 +68,8 @@ class ArticleContent extends StatelessWidget { context: context, builder: (context) { return ImagePreview( - initialPage: 0, - imgList: [item.insert.nativeImage?.url], + initialPage: imgList.indexOf(item.pic!.pics!.first.url!), + imgList: imgList, ); }, ); @@ -48,22 +77,12 @@ class ArticleContent extends StatelessWidget { child: NetworkImgLayer( width: constraints.maxWidth, height: constraints.maxWidth * - item.insert.nativeImage?.height / - item.insert.nativeImage?.width, - src: item.insert.nativeImage?.url, + item.pic!.pics!.first.height! / + item.pic!.pics!.first.width!, + 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 { return const SizedBox.shrink(); // return Text('unsupported content'); diff --git a/lib/http/html.dart b/lib/http/html.dart index 6bc38c32..5767ce0e 100644 --- a/lib/http/html.dart +++ b/lib/http/html.dart @@ -1,6 +1,5 @@ import 'dart:convert'; -import 'package:PiliPalaX/common/widgets/article_content.dart'; import 'package:PiliPalaX/models/dynamics/article_content_model.dart'; import 'package:html/dom.dart'; import 'package:html/parser.dart'; @@ -116,9 +115,11 @@ class HtmlHttp { final jsonString = match.group(1); if (jsonString != null) { try { - opusContent = jsonDecode(jsonString)['readInfo']['content']; + dynamic json = jsonDecode(jsonString); + opusContent = json['readInfo']['content']; try { - opusContent = (jsonDecode(opusContent)['ops'] as List) + opusContent = (json['readInfo']?['opus']?['content'] + ?['paragraphs'] as List) .map((item) => ArticleContentModel.fromJson(item)) .toList(); isJsonContent = true; diff --git a/lib/models/dynamics/article_content_model.dart b/lib/models/dynamics/article_content_model.dart index 231ad452..d51a6255 100644 --- a/lib/models/dynamics/article_content_model.dart +++ b/lib/models/dynamics/article_content_model.dart @@ -1,72 +1,218 @@ class ArticleContentModel { ArticleContentModel({ - this.attributes, - this.insert, + this.paraType, + this.text, + this.format, + this.line, }); - Attributes? attributes; - dynamic insert; + int? paraType; + Text? text; + Format? format; + Line? line; + Pic? pic; ArticleContentModel.fromJson(Map 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']; + paraType = json['para_type']; + text = json['text'] == null ? null : Text.fromJson(json['text']); + format = json['format'] == null ? null : Format.fromJson(json['format']); + line = json['line'] == null ? null : Line.fromJson(json['line']); + pic = json['pic'] == null ? null : Pic.fromJson(json['pic']); } } -class Insert { - Insert({ - this.nativeImage, - }); - NativeImage? nativeImage; - - Insert.fromJson(Map json) { - nativeImage = json['native-image'] == null - ? null - : NativeImage.fromJson(json['native-image']); - } -} - -class NativeImage { - NativeImage({ - this.alt, +class Pic { + Pic({ this.url, this.width, this.height, this.size, - this.status, + this.pics, + this.style, }); + String? url; + int? width; + int? height; + double? size; + List? pics; + int? style; - dynamic alt; - dynamic url; - dynamic width; - dynamic height; - dynamic size; - dynamic status; - - NativeImage.fromJson(Map json) { - alt = json['alt']; + Pic.fromJson(Map json) { url = json['url']; width = json['width']; height = json['height']; size = json['size']; - status = json['status']; + pics = (json['pics'] as List?) + ?.map((item) => Pic.fromJson(item)) + .toList(); + style = json['style']; } } -class Attributes { - Attributes({ - this.clazz, +class Line { + Line({ + this.pic, }); - String? clazz; - bool? bold; + Pic? pic; - Attributes.fromJson(Map json) { - clazz = json['class']; - bold = json['bold']; + Line.fromJson(Map json) { + pic = json['pic'] == null ? null : Pic.fromJson(json['pic']); } } + +class Format { + Format({ + this.align, + }); + int? align; + + Format.fromJson(Map json) { + align = json['align']; + } +} + +class Text { + Text({ + this.nodes, + }); + List? nodes; + + Text.fromJson(Map json) { + nodes = (json['nodes'] as List?) + ?.map((item) => Nodes.fromJson(item)) + .toList(); + } +} + +class Nodes { + Nodes({ + this.nodeType, + this.word, + }); + int? nodeType; + Word? word; + + Nodes.fromJson(Map 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 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 json) { + bold = json['bold']; + italic = json['italic']; + strikethrough = json['strikethrough']; + } +} + +// class ArticleContentModel { +// ArticleContentModel({ +// this.attributes, +// this.insert, +// }); +// Attributes? attributes; +// dynamic insert; + +// ArticleContentModel.fromJson(Map 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 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 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 json) { +// clazz = json['class']; +// bold = json['bold']; +// color = json['color']; +// italic = json['italic']; +// strike = json['strike']; +// } +// }