feat: render more stuff better
This commit is contained in:
parent
27dbbb9221
commit
e6989dc264
3 changed files with 178 additions and 16 deletions
|
@ -9,6 +9,7 @@ import 'package:voyagehandbook/api/classes.dart';
|
|||
import 'package:voyagehandbook/util/styles.dart';
|
||||
import 'package:voyagehandbook/util/widgets/warning.dart';
|
||||
import 'package:html_unescape/html_unescape_small.dart';
|
||||
import 'package:voyagehandbook/views/pageview.dart';
|
||||
import 'package:widget_zoom/widget_zoom.dart';
|
||||
|
||||
/*
|
||||
|
@ -33,8 +34,9 @@ class PageRenderer {
|
|||
final ColorScheme scheme;
|
||||
final double height;
|
||||
final double width;
|
||||
final BuildContext context;
|
||||
|
||||
PageRenderer(this.scheme, this.height, this.width);
|
||||
PageRenderer(this.scheme, this.height, this.width, this.context);
|
||||
|
||||
/// Used to create Widgets from raw HTML from WM API
|
||||
ListView renderFromPageHTML(RawPage page) {
|
||||
|
@ -184,6 +186,9 @@ class PageRenderer {
|
|||
switch (element.localName) {
|
||||
case "p":
|
||||
case "link":
|
||||
if (element
|
||||
.getElementsByClassName("mw-kartographer-maplink")
|
||||
.isNotEmpty) break;
|
||||
out.add(
|
||||
RichText(
|
||||
text: TextSpan(children: _renderText(element.innerHtml)),
|
||||
|
@ -207,6 +212,54 @@ class PageRenderer {
|
|||
case "section":
|
||||
out.addAll(_renderSection(element));
|
||||
break;
|
||||
case "dl":
|
||||
var dd = element.getElementsByTagName("dd").first;
|
||||
var link = RegExp(r'<a .+? href="\.\/(.+?)".+?>(.+?)<', dotAll: true)
|
||||
.firstMatch(dd.innerHtml);
|
||||
if (link == null) {
|
||||
break; // TODO: handle `dd` which are not "see also" links
|
||||
}
|
||||
out.add(
|
||||
Row(
|
||||
children: [
|
||||
const Text(
|
||||
"See also:",
|
||||
style: TextStyle(),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => ArticleView(
|
||||
pageKey: link.group(1)!, name: link.group(2)!),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
link.group(2)!,
|
||||
style: const TextStyle(
|
||||
fontStyle: FontStyle.italic,
|
||||
decoration: TextDecoration.underline),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
out.add(
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
);
|
||||
break;
|
||||
case "ul":
|
||||
out.add(_renderList(element));
|
||||
out.add(
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
);
|
||||
break;
|
||||
case "div":
|
||||
if (element.attributes["class"] != null &&
|
||||
element.attributes["class"] == "pp_cautionbox") {
|
||||
|
@ -277,9 +330,14 @@ class PageRenderer {
|
|||
height: 10,
|
||||
),
|
||||
);
|
||||
} else if (element.id == "thumbinner") {
|
||||
var imgs = element.getElementsByTagName("img");
|
||||
if (imgs.isEmpty) break;
|
||||
} else if (element.classes.contains("mw-kartographer-container")) {
|
||||
var imgs = element
|
||||
.getElementsByTagName("div")
|
||||
.first
|
||||
.getElementsByTagName("a")
|
||||
.first
|
||||
.getElementsByTagName("img");
|
||||
if (imgs.isEmpty) break; // load maps that have a static image
|
||||
var img = imgs[0];
|
||||
var cap = element.getElementsByClassName("thumbcaption")[0];
|
||||
out.add(const SizedBox(
|
||||
|
@ -296,13 +354,22 @@ class PageRenderer {
|
|||
),
|
||||
),
|
||||
);
|
||||
out.add(const SizedBox(
|
||||
out.add(
|
||||
const SizedBox(
|
||||
height: 3,
|
||||
));
|
||||
out.add(Text(cap.text));
|
||||
out.add(const SizedBox(
|
||||
),
|
||||
);
|
||||
out.add(
|
||||
Text(
|
||||
cap.text,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
out.add(
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
));
|
||||
),
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -426,4 +493,91 @@ class PageRenderer {
|
|||
content.add(TextSpan(text: noFormatting.last)); // add last
|
||||
return content;
|
||||
}
|
||||
|
||||
final _extraColorsLight = {
|
||||
"blue": const Color.fromARGB(255, 34, 34, 157),
|
||||
"red": const Color.fromARGB(255, 152, 33, 33)
|
||||
};
|
||||
|
||||
final _extraColorsDark = {
|
||||
"blue": const Color.fromARGB(255, 84, 95, 247),
|
||||
"red": const Color.fromARGB(255, 242, 69, 69)
|
||||
};
|
||||
|
||||
SingleChildScrollView _renderList(dom.Element element) {
|
||||
var out = <Widget>[];
|
||||
var i = 0;
|
||||
for (var item in element.getElementsByTagName("li")) {
|
||||
i++;
|
||||
String? title = (item.getElementsByClassName("listing-name").isNotEmpty)
|
||||
? item.getElementsByClassName("listing-name")[0].text
|
||||
: null;
|
||||
|
||||
Color bubbleColor;
|
||||
if (item.innerHtml.contains("background: #0000FF")) {
|
||||
bubbleColor =
|
||||
(MediaQuery.of(context).platformBrightness == Brightness.dark)
|
||||
? _extraColorsDark["blue"]!
|
||||
: _extraColorsLight["blue"]!;
|
||||
} else if (item.innerHtml.contains("background: #800000")) {
|
||||
bubbleColor =
|
||||
(MediaQuery.of(context).platformBrightness == Brightness.dark)
|
||||
? _extraColorsDark["red"]!
|
||||
: _extraColorsLight["red"]!;
|
||||
} else {
|
||||
bubbleColor = scheme.secondary;
|
||||
}
|
||||
|
||||
var rest = (title != null)
|
||||
? item.text.replaceAll(item.getElementsByTagName("span")[0].text, "")
|
||||
: item.text;
|
||||
out.add(const SizedBox(
|
||||
height: 5,
|
||||
));
|
||||
out.add(
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8), color: bubbleColor),
|
||||
width: 30,
|
||||
height: 30,
|
||||
child: Text(
|
||||
i.toString(),
|
||||
style: TextStyle(
|
||||
color: scheme.background,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 17),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
if (title != null)
|
||||
TextSpan(
|
||||
text: title,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
TextSpan(text: rest)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: out,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,9 +95,11 @@ class _HomeViewState extends State<HomeView> {
|
|||
width: MediaQuery.of(context).size.width * 0.9,
|
||||
height: 50,
|
||||
child: InkWell(
|
||||
onTap: () => Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (c) =>
|
||||
ArticleView(pageKey: r["key"], name: r["name"]))),
|
||||
onTap: () => Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (c) => ArticleView(pageKey: r["key"], name: r["name"]),
|
||||
),
|
||||
)..then((_) => load()),
|
||||
child: Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
|
|
|
@ -77,8 +77,11 @@ class _ArticleViewState extends State<ArticleView> {
|
|||
}
|
||||
|
||||
void loadPage() async {
|
||||
var renderer = PageRenderer(Theme.of(context).colorScheme,
|
||||
MediaQuery.of(context).size.height, MediaQuery.of(context).size.width);
|
||||
var renderer = PageRenderer(
|
||||
Theme.of(context).colorScheme,
|
||||
MediaQuery.of(context).size.height,
|
||||
MediaQuery.of(context).size.width,
|
||||
context);
|
||||
try {
|
||||
_content = [
|
||||
SizedBox(
|
||||
|
@ -107,5 +110,8 @@ class _ArticleViewState extends State<ArticleView> {
|
|||
|
||||
void addToRecents() {
|
||||
StorageAccess.addToRecents(widget.name, widget.pageKey);
|
||||
if (kDebugMode) {
|
||||
print("Added ${widget.name} to recent");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue