feat: bookmark the site directory on a first start

This commit is contained in:
randogoth 2026-10-11 22:58:02 +03:00
parent 95602ac623
commit dcb70a93d1
14 changed files with 506 additions and 1 deletions

View file

@ -137,6 +137,97 @@ void main() {
expect(find.textContaining('Try again'), findsOneWidget);
});
testWidgets('a first start gives the directory bookmark once', (tester) async {
final db = MewsDatabase.test(NativeDatabase.memory());
addTearDown(db.close);
// The first start of the app wires in the directory of sites, named in
// the reader's language.
var deps = AppDependencies(
database: db, fetch: FakeFetch(const {}), strings: strings);
await deps.start();
expect(await db.allBookmarks(), hasLength(1));
final bookmark = (await db.allBookmarks()).single;
expect(bookmark.url, 'https://mews.page/directory');
expect(bookmark.title, 'Mews directory');
expect(deps.bookmarks.isBookmarked(bookmark.url), isTrue);
// The reader removes it, as any bookmark can be removed.
await deps.bookmarks.toggle(bookmark.url, bookmark.title);
expect(deps.bookmarks.isBookmarked(bookmark.url), isFalse);
// A later start does not give it again: what the reader removed stays
// removed.
deps = AppDependencies(
database: db, fetch: FakeFetch(const {}), strings: strings);
await deps.start();
expect(await db.allBookmarks(), isEmpty);
});
testWidgets('a post a refresh finds arrives unread on the posts page',
(tester) async {
// The site's index, as gemtext with dated links (spec 6.1).
var index = '''
# A capsule
=> a.gmi 2026-10-08 Post a
=> b.gmi 2026-09-21 Post b
''';
final sources = {'gemini://capsule.example': index};
final stubDeps = AppDependencies(
database: MewsDatabase.test(NativeDatabase.memory()),
fetch: FakeFetch(sources),
strings: strings,
);
await tester.pumpWidget(MewsApp(deps: stubDeps, strings: strings));
await tester.pumpAndSettle();
// Subscribing takes on what the site already published as read.
await stubDeps.browser.open('gemini://capsule.example');
await tester.pumpAndSettle();
await stubDeps.subscriptions.toggle(stubDeps.browser.currentPage.value!);
// The site publishes a new post, and a refresh finds it.
sources['gemini://capsule.example'] = '''
# A capsule
=> a.gmi 2026-10-08 Post a
=> b.gmi 2026-09-21 Post b
=> c.gmi 2026-10-11 New post
''';
await stubDeps.subscriptions.refreshAll();
// On the posts page the new post carries weight and the back catalogue
// does not, which is how the page says unread.
await stubDeps.browser.open('mews:posts');
await tester.pumpAndSettle();
// Whether a row's glyphs carry bold weight somewhere in their spans.
// TextSpan.visitChildren walks whole subtrees, so this walks the children
// itself, one level per step.
bool bold(String label) {
final text = tester.widget<Text>(find.textContaining(label).first);
InlineSpan? span = text.textSpan;
bool weight(InlineSpan span) {
if (span is TextSpan && span.style?.fontWeight == FontWeight.bold) {
return true;
}
final children = span is TextSpan ? span.children : null;
if (children == null) return false;
for (final child in children) {
if (weight(child)) return true;
}
return false;
}
return span != null && weight(span);
}
expect(bold('New post'), isTrue, reason: 'a found post is unread');
expect(bold('Post a'), isFalse, reason: 'the back catalogue was read');
expect(find.textContaining('1 unread post'), findsOneWidget);
});
testWidgets('a web page that will not load can open in a web browser',
(tester) async {
final stubDeps = AppDependencies(