fix: mount StringsScope, keep list inline text, show FOB until scroll down
This commit is contained in:
parent
9a11194f77
commit
65e693b9c6
6 changed files with 281 additions and 27 deletions
|
|
@ -156,9 +156,21 @@ void _appendBlocks(XmlElement element, String pageUrl, List<MewsBlock> out) {
|
|||
out.addAll(embedded);
|
||||
case 'blockquote':
|
||||
final children = <MewsBlock>[];
|
||||
for (final child in element.children.whereType<XmlElement>()) {
|
||||
_appendBlocks(child, pageUrl, children);
|
||||
final embedded = <MewsBlock>[];
|
||||
// Bare inline content (quotes are often written as plain text) becomes
|
||||
// a paragraph, like in list items.
|
||||
final inline = <MewsInline>[];
|
||||
for (final node in element.children) {
|
||||
final isInlineNode = node is XmlText ||
|
||||
(node is XmlElement && _inlineElements.contains(node.name.qualified));
|
||||
if (isInlineNode) {
|
||||
_appendInline(node, pageUrl, inline, embedded);
|
||||
} else if (node is XmlElement) {
|
||||
_appendBlocks(node, pageUrl, children);
|
||||
}
|
||||
}
|
||||
if (inline.isNotEmpty) children.insert(0, ParagraphBlock(inline));
|
||||
children.addAll(embedded);
|
||||
out.add(QuoteBlock(children));
|
||||
case 'pre':
|
||||
out.add(PreBlock(element.innerText));
|
||||
|
|
@ -193,13 +205,33 @@ void _appendBlocks(XmlElement element, String pageUrl, List<MewsBlock> out) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Inline-only elements; text and images directly inside these become part of
|
||||
/// the enclosing paragraph's inline run.
|
||||
const _inlineElements = {
|
||||
'a', 'br', 'em', 'strong', 'b', 'i', 'code', 'kbd', 'samp', 'var', 'q',
|
||||
'cite', 'abbr', 'dfn', 'img',
|
||||
};
|
||||
|
||||
ListBlock _listBlock(XmlElement element, String pageUrl) {
|
||||
final items = <MewsListItem>[];
|
||||
for (final li in element.findElements('li')) {
|
||||
final blocks = <MewsBlock>[];
|
||||
for (final child in li.children.whereType<XmlElement>()) {
|
||||
_appendBlocks(child, pageUrl, blocks);
|
||||
final embedded = <MewsBlock>[];
|
||||
// List items commonly hold bare inline content (the dated-link entry
|
||||
// pattern in spec 6.1), so direct text and inline elements form a
|
||||
// paragraph while block-level children append as blocks.
|
||||
final inline = <MewsInline>[];
|
||||
for (final node in li.children) {
|
||||
final isInlineNode = node is XmlText ||
|
||||
(node is XmlElement && _inlineElements.contains(node.name.qualified));
|
||||
if (isInlineNode) {
|
||||
_appendInline(node, pageUrl, inline, embedded);
|
||||
} else if (node is XmlElement) {
|
||||
_appendBlocks(node, pageUrl, blocks);
|
||||
}
|
||||
}
|
||||
if (inline.isNotEmpty) blocks.insert(0, ParagraphBlock(inline));
|
||||
blocks.addAll(embedded);
|
||||
items.add(MewsListItem(blocks));
|
||||
}
|
||||
return ListBlock(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import '../application/controllers/reader_settings_controller.dart';
|
|||
import '../database/database.dart';
|
||||
import '../l10n/strings.dart';
|
||||
import 'reader/reader_screen.dart';
|
||||
|
||||
/// Wires the app's dependencies together and provides them to the tree.
|
||||
/// Everything is local-first: the database is created up front and the fetch
|
||||
/// actor is the only path to the network.
|
||||
|
|
@ -63,7 +62,10 @@ class MewsApp extends StatelessWidget {
|
|||
return AppScope(
|
||||
deps: deps,
|
||||
strings: strings,
|
||||
child: const _ThemedApp(),
|
||||
child: StringsScope(
|
||||
strings: strings,
|
||||
child: const _ThemedApp(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,10 @@ class ReaderScreen extends StatefulWidget {
|
|||
|
||||
class _ReaderScreenState extends State<ReaderScreen> {
|
||||
final _scroll = ScrollController();
|
||||
bool _barVisible = true;
|
||||
bool _fobVisible = false;
|
||||
|
||||
/// Address bar and FOB share one visibility: on screen from the start,
|
||||
/// hidden while the reader scrolls down, back on any upward scroll.
|
||||
bool _chromeVisible = true;
|
||||
bool _showBrokenText = false;
|
||||
final _reportedNewPosts = <String>{};
|
||||
final _checkedSubscriptions = <String>{};
|
||||
|
|
@ -48,20 +50,13 @@ class _ReaderScreenState extends State<ReaderScreen> {
|
|||
if (!_scroll.hasClients) return;
|
||||
final position = _scroll.position;
|
||||
|
||||
// The bar hides when the reader scrolls down and comes back on any
|
||||
// upward scroll; it is always visible at the top of the page.
|
||||
final wantBar = position.pixels <= 0 ||
|
||||
// The bar and FOB hide when the reader scrolls down and come back on any
|
||||
// upward scroll; both are always visible at the top of the page.
|
||||
final wantVisible = position.pixels <= 0 ||
|
||||
position.userScrollDirection == ScrollDirection.forward;
|
||||
// The FOB appears once the page is scrolled, and stays reachable on pages
|
||||
// that fit without scrolling.
|
||||
final contentScrolls = position.maxScrollExtent > 0;
|
||||
final wantFob = contentScrolls ? position.pixels > 16 : true;
|
||||
|
||||
if (wantBar != _barVisible || wantFob != _fobVisible) {
|
||||
setState(() {
|
||||
_barVisible = wantBar;
|
||||
_fobVisible = wantFob;
|
||||
});
|
||||
if (wantVisible != _chromeVisible) {
|
||||
setState(() => _chromeVisible = wantVisible);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,14 +70,14 @@ class _ReaderScreenState extends State<ReaderScreen> {
|
|||
body: Column(
|
||||
children: [
|
||||
AnimatedSlide(
|
||||
offset: _barVisible ? Offset.zero : const Offset(0, -1),
|
||||
offset: _chromeVisible ? Offset.zero : const Offset(0, -1),
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeOut,
|
||||
child: AnimatedSize(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeOut,
|
||||
alignment: Alignment.topCenter,
|
||||
child: _barVisible
|
||||
child: _chromeVisible
|
||||
? const AddressBar()
|
||||
: const SizedBox(width: double.infinity),
|
||||
),
|
||||
|
|
@ -110,7 +105,7 @@ class _ReaderScreenState extends State<ReaderScreen> {
|
|||
right: 0,
|
||||
bottom: 0,
|
||||
child: Fob(
|
||||
visible: _fobVisible,
|
||||
visible: _chromeVisible,
|
||||
label: strings.t('reader-fob-label'),
|
||||
onTap: () => showReaderSettingsSheet(context),
|
||||
),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue