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/styles.dart';
|
||||||
import 'package:voyagehandbook/util/widgets/warning.dart';
|
import 'package:voyagehandbook/util/widgets/warning.dart';
|
||||||
import 'package:html_unescape/html_unescape_small.dart';
|
import 'package:html_unescape/html_unescape_small.dart';
|
||||||
|
import 'package:voyagehandbook/views/pageview.dart';
|
||||||
import 'package:widget_zoom/widget_zoom.dart';
|
import 'package:widget_zoom/widget_zoom.dart';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -33,8 +34,9 @@ class PageRenderer {
|
||||||
final ColorScheme scheme;
|
final ColorScheme scheme;
|
||||||
final double height;
|
final double height;
|
||||||
final double width;
|
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
|
/// Used to create Widgets from raw HTML from WM API
|
||||||
ListView renderFromPageHTML(RawPage page) {
|
ListView renderFromPageHTML(RawPage page) {
|
||||||
|
@ -184,6 +186,9 @@ class PageRenderer {
|
||||||
switch (element.localName) {
|
switch (element.localName) {
|
||||||
case "p":
|
case "p":
|
||||||
case "link":
|
case "link":
|
||||||
|
if (element
|
||||||
|
.getElementsByClassName("mw-kartographer-maplink")
|
||||||
|
.isNotEmpty) break;
|
||||||
out.add(
|
out.add(
|
||||||
RichText(
|
RichText(
|
||||||
text: TextSpan(children: _renderText(element.innerHtml)),
|
text: TextSpan(children: _renderText(element.innerHtml)),
|
||||||
|
@ -207,6 +212,54 @@ class PageRenderer {
|
||||||
case "section":
|
case "section":
|
||||||
out.addAll(_renderSection(element));
|
out.addAll(_renderSection(element));
|
||||||
break;
|
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":
|
case "div":
|
||||||
if (element.attributes["class"] != null &&
|
if (element.attributes["class"] != null &&
|
||||||
element.attributes["class"] == "pp_cautionbox") {
|
element.attributes["class"] == "pp_cautionbox") {
|
||||||
|
@ -277,9 +330,14 @@ class PageRenderer {
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (element.id == "thumbinner") {
|
} else if (element.classes.contains("mw-kartographer-container")) {
|
||||||
var imgs = element.getElementsByTagName("img");
|
var imgs = element
|
||||||
if (imgs.isEmpty) break;
|
.getElementsByTagName("div")
|
||||||
|
.first
|
||||||
|
.getElementsByTagName("a")
|
||||||
|
.first
|
||||||
|
.getElementsByTagName("img");
|
||||||
|
if (imgs.isEmpty) break; // load maps that have a static image
|
||||||
var img = imgs[0];
|
var img = imgs[0];
|
||||||
var cap = element.getElementsByClassName("thumbcaption")[0];
|
var cap = element.getElementsByClassName("thumbcaption")[0];
|
||||||
out.add(const SizedBox(
|
out.add(const SizedBox(
|
||||||
|
@ -296,13 +354,22 @@ class PageRenderer {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
out.add(const SizedBox(
|
out.add(
|
||||||
|
const SizedBox(
|
||||||
height: 3,
|
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,
|
height: 10,
|
||||||
));
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -426,4 +493,91 @@ class PageRenderer {
|
||||||
content.add(TextSpan(text: noFormatting.last)); // add last
|
content.add(TextSpan(text: noFormatting.last)); // add last
|
||||||
return content;
|
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,
|
width: MediaQuery.of(context).size.width * 0.9,
|
||||||
height: 50,
|
height: 50,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () => Navigator.of(context).push(MaterialPageRoute(
|
onTap: () => Navigator.of(context).push(
|
||||||
builder: (c) =>
|
MaterialPageRoute(
|
||||||
ArticleView(pageKey: r["key"], name: r["name"]))),
|
builder: (c) => ArticleView(pageKey: r["key"], name: r["name"]),
|
||||||
|
),
|
||||||
|
)..then((_) => load()),
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|
|
@ -77,8 +77,11 @@ class _ArticleViewState extends State<ArticleView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadPage() async {
|
void loadPage() async {
|
||||||
var renderer = PageRenderer(Theme.of(context).colorScheme,
|
var renderer = PageRenderer(
|
||||||
MediaQuery.of(context).size.height, MediaQuery.of(context).size.width);
|
Theme.of(context).colorScheme,
|
||||||
|
MediaQuery.of(context).size.height,
|
||||||
|
MediaQuery.of(context).size.width,
|
||||||
|
context);
|
||||||
try {
|
try {
|
||||||
_content = [
|
_content = [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
@ -107,5 +110,8 @@ class _ArticleViewState extends State<ArticleView> {
|
||||||
|
|
||||||
void addToRecents() {
|
void addToRecents() {
|
||||||
StorageAccess.addToRecents(widget.name, widget.pageKey);
|
StorageAccess.addToRecents(widget.name, widget.pageKey);
|
||||||
|
if (kDebugMode) {
|
||||||
|
print("Added ${widget.name} to recent");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue