feat: lay our own pages out as interface rather than as articles

This commit is contained in:
randogoth 2026-10-11 14:03:44 +03:00
parent e7a8e91334
commit dfba1cfb16
8 changed files with 162 additions and 55 deletions

View file

@ -29,15 +29,12 @@ Future<MewsPage> buildBookmarksPage(
field: 'q',
value: query,
),
ParagraphBlock([
if (query.isNotEmpty) ...[
MewsLink(MewsUrls.page(url, {'q': null}),
[MewsText(strings.t('action-clear-search'))]),
const MewsText(actionSeparator),
],
...linkLine([
if (query.isNotEmpty)
action(MewsUrls.page(url, {'q': null}),
strings.t('action-clear-search')),
if (tag != null && tag.isNotEmpty)
MewsLink(MewsUrls.page(url, {'tag': null}),
[MewsText(strings.t('bookmarks-show-all'))]),
action(MewsUrls.page(url, {'tag': null}), strings.t('bookmarks-show-all')),
]),
if (tag != null && tag.isNotEmpty)
ParagraphBlock([

View file

@ -37,15 +37,13 @@ Future<MewsPage> buildHistoryPage(
MewsLink(MewsUrls.history, [MewsText(strings.t('history-clear-no'))]),
]),
] else
ParagraphBlock([
if (query.isNotEmpty) ...[
MewsLink(MewsUrls.page(url, {'q': null}),
[MewsText(strings.t('action-clear-search'))]),
const MewsText(actionSeparator),
],
...linkLine([
if (query.isNotEmpty)
action(MewsUrls.page(url, {'q': null}),
strings.t('action-clear-search')),
if (pages.isNotEmpty)
MewsLink(MewsUrls.page(url, {'confirm': 'clear'}),
[MewsText(strings.t('history-clear'))]),
action(MewsUrls.page(url, {'confirm': 'clear'}),
strings.t('history-clear')),
]),
if (pages.isEmpty)
ParagraphBlock([

View file

@ -9,7 +9,7 @@ import 'mews_urls.dart';
/// Separates independent labels on one line. A separator is layout, so it is a
/// constant here rather than a message.
const actionSeparator = ' ';
const actionSeparator = ' · ';
/// A link with its label: a choice in a group, or an action on a row. For an
/// action [current] is always false.
@ -59,10 +59,9 @@ List<MewsBlock> chrome({
];
}
/// One row in a list: the item, then an optional note on its own line. A date
/// goes inside the link, the way a Mews page writes its own dated links (spec
/// 6.1), so the whole row is one target. One paragraph per row keeps rows
/// close together, since each paragraph carries its own bottom padding.
/// One row in a list: the item as a link, then its note and date on a second,
/// quieter line, then its chips and actions. One paragraph per row keeps the
/// lines together, since each paragraph carries its own bottom padding.
MewsListItem itemRow({
required String href,
required String label,
@ -72,23 +71,28 @@ MewsListItem itemRow({
List<Choice> chips = const [],
List<Choice> actions = const [],
}) {
final text = date == null ? label : '$date $label';
final meta = [?note, ?date];
return MewsListItem([
ParagraphBlock([
MewsLink(href, [
if (strong)
MewsEmphasis(EmphasisStyle.strong, [MewsText(text)])
MewsEmphasis(EmphasisStyle.strong, [MewsText(label)])
else
MewsText(text),
MewsText(label),
]),
if (note != null) ...[
if (meta.isNotEmpty) ...[
const MewsLineBreak(),
MewsEmphasis(EmphasisStyle.emphasis, [MewsText(note)]),
MewsEmphasis(
EmphasisStyle.emphasis, [MewsText(meta.join(actionSeparator))]),
],
if (chips.isNotEmpty) ...[
const MewsLineBreak(),
..._separated(
[for (final chip in chips) MewsLink(chip.href, [MewsText(chip.label)])]),
// A leading hash tells a tag apart from the actions beside it, which
// links alone give no way to distinguish.
..._separated([
for (final chip in chips)
MewsLink(chip.href, [MewsText('#${chip.label}')]),
]),
],
if (actions.isNotEmpty) ...[
const MewsLineBreak(),
@ -175,6 +179,16 @@ FormBlock textForm({
);
}
/// A line of links with the separator between them, never trailing. Returns
/// nothing when there is no link to show, so a page never holds an empty line.
List<MewsBlock> linkLine(List<Choice> links) {
if (links.isEmpty) return const [];
return [
ParagraphBlock(_separated(
[for (final link in links) MewsLink(link.href, [MewsText(link.label)])])),
];
}
/// A plain list of links, used for menus and library lists alike.
ListBlock itemList(List<MewsListItem> items) =>
ListBlock(ordered: false, start: 1, items: items);

View file

@ -30,15 +30,12 @@ Future<MewsPage> buildSubscriptionsPage(
field: 'q',
value: query,
),
ParagraphBlock([
if (query.isNotEmpty) ...[
MewsLink(MewsUrls.page(url, {'q': null}),
[MewsText(strings.t('action-clear-search'))]),
const MewsText(actionSeparator),
],
...linkLine([
if (query.isNotEmpty)
action(MewsUrls.page(url, {'q': null}),
strings.t('action-clear-search')),
if (tag != null && tag.isNotEmpty)
MewsLink(MewsUrls.page(url, {'tag': null}),
[MewsText(strings.t('subscriptions-show-all'))]),
action(MewsUrls.page(url, {'tag': null}), strings.t('subscriptions-show-all')),
]),
if (tag != null && tag.isNotEmpty)
ParagraphBlock([