diff --git a/SPEC.md b/SPEC.md index 6c22adc..b69d668 100644 --- a/SPEC.md +++ b/SPEC.md @@ -87,7 +87,7 @@ The v1 client presents one reader screen: - Subscribed sites are checked once in the background at launch, and whenever the reader asks. Sites are checked one after another rather than at once, and a site that cannot be reached keeps the posts already held and is still recorded as checked, so one site being down never stops the rest. - A fresh install comes with the project's directory of sites already bookmarked, so a first session has somewhere to go. It is a bookmark like any other: removing it is the reader's own, and no later start gives it back. - The reader can tag bookmarks and sites. A tag exists only while something carries it, so adding a tag and making one are the same act; `mews:tags` lists every tag in use and what carries it, and following a tag narrows the list it belongs to. There is no renaming and no empty tag. -- The reader can keep marked text as a quotation. Marking text on a page offers, in the selection toolbar, to save it; the kept text is stored with the page it came from, with the author when the page's metadata named one, and with the site's title when the reader's subscriptions know the site the page belongs to. `mews:quotations` lists what is kept, one row per quotation naming the author, the article's title and the site it came from — by its own title when the reader's subscriptions know it, by its host otherwise — with the day it was kept, ordered by that day or by the page it came from, narrowed by a search over the text, the author, the page's title and its address. A row shares its quotation through the system share sheet, together with a citation shaped as APA names a web page: the author first when the page carried one, otherwise the article's title; the year as a no-date marker, since a page declares no date; then the article's title, the site's own title (its host when the subscriptions do not know it), and the address. A row can remove its quotation, and nothing else is offered, since the citation is the record of where the words came from. +- The reader can keep marked text as a quotation. Marking text on a page offers, in the selection toolbar, to save it, and a short note confirms the save, since closing the toolbar looks the same either way; the kept text is stored with the page it came from, with the author when the page's metadata named one, and with the site's title when the reader's subscriptions know the site the page belongs to. `mews:quotations` lists what is kept, one row per quotation naming the author, the article's title and the site it came from — by its own title when the reader's subscriptions know it, by its host otherwise — with the day it was kept, ordered by that day or by the page it came from, narrowed by a search over the text, the author, the page's title and its address. A row shares its quotation through the system share sheet, together with a citation shaped as APA names a web page: the author first when the page carried one, otherwise the article's title; the year as a no-date marker, since a page declares no date; then the article's title, the site's own title (its host when the subscriptions do not know it), and the address. A row can remove its quotation, and nothing else is offered, since the citation is the record of where the words came from. - Each library page carries a search box that narrows it by title or address as the reader types, a quarter second after they stop. The query rides in the address as any other view state does, but it replaces the step of the walk they are standing on rather than adding one, so a search of eight letters is one step back and not eight; the box keeps what they typed as the page rebuilds under them, and follows the page when the page sets it, as clearing the search does. Submitting the box still works, for a reader who would rather say when to search. Each row carries its own actions: remove a bookmark, leave a site or check it now, forget a page. History lists one row per address, headed by the most recent visit, rather than repeating a page once per visit. Clearing history takes a second step on a page the reader can simply leave, since it cannot be undone. - A change is a link too: following `mews:do/` applies it and rerenders the page in place without adding a history entry. Only a link followed from one of the client's own pages is honored, so a fetched page cannot clear history or change a setting on the reader's behalf (spec 8.1). A form on one of these pages is routed the same way rather than sent to the network. - Our own pages are built from the same blocks as a page but are interface, not an article, so the renderer lays them out differently: wider before they stop growing, denser, rows divided by a line instead of bulleted, section headings quieter, a note under a row set smaller and dimmer, links carried by weight rather than an underline, and a one-field form on a single line. diff --git a/app/lib/presentation/reader/reader_screen.dart b/app/lib/presentation/reader/reader_screen.dart index 81d7e99..f34a812 100644 --- a/app/lib/presentation/reader/reader_screen.dart +++ b/app/lib/presentation/reader/reader_screen.dart @@ -338,12 +338,24 @@ class _ReaderScreenState extends State { deps.browser.narrowWhileTyping(form.action, values), // The reader's marked text is kept with the page it came from, so // its citation can name the author when the page did. - onSaveQuotation: (text) => deps.db.addQuotation( - quote: text, - pageUrl: page.url, - pageTitle: page.title, - author: page.author, - ), + onSaveQuotation: (text) async { + // Taken before the wait, so the note does not reach across it. + final messenger = ScaffoldMessenger.of(context); + final saved = AppScope.of(context).strings.t('quotations-saved'); + await deps.db.addQuotation( + quote: text, + pageUrl: page.url, + pageTitle: page.title, + author: page.author, + ); + if (!mounted) return; + // Closing the toolbar looks the same either way, so the save + // says it happened. + messenger.showSnackBar(SnackBar( + duration: const Duration(seconds: 2), + content: Text(saved), + )); + }, fetchImageBytes: deps.browser.fetchImageBytes, scrollController: _scroll, ); diff --git a/app/locales/de/main.ftl b/app/locales/de/main.ftl index eb187bc..dd3d20a 100644 --- a/app/locales/de/main.ftl +++ b/app/locales/de/main.ftl @@ -70,6 +70,8 @@ posts-mark-unread = Als ungelesen markieren # The selection toolbar button that keeps the marked text. quotations-save = Als Zitat speichern +# Says the marked text was kept. +quotations-saved = Als Zitat gespeichert quotations-title = Zitate quotations-empty = Text, den du von einer Seite behältst, erscheint hier. quotations-no-matches = Kein Zitat passt zu deiner Suche. diff --git a/app/locales/en-US/main.ftl b/app/locales/en-US/main.ftl index 7c7a46a..14cadf4 100644 --- a/app/locales/en-US/main.ftl +++ b/app/locales/en-US/main.ftl @@ -70,6 +70,8 @@ posts-mark-unread = Mark as unread # The selection toolbar button that keeps the marked text. quotations-save = Save as quotation +# Says the marked text was kept. +quotations-saved = Saved as a quotation quotations-title = Quotations quotations-empty = Text you keep from a page appears here. quotations-no-matches = No quotations match your search. diff --git a/app/locales/es/main.ftl b/app/locales/es/main.ftl index e6cc278..5f2c4a5 100644 --- a/app/locales/es/main.ftl +++ b/app/locales/es/main.ftl @@ -70,6 +70,8 @@ posts-mark-unread = Marcar como no leída # The selection toolbar button that keeps the marked text. quotations-save = Guardar como cita +# Says the marked text was kept. +quotations-saved = Guardada como cita quotations-title = Citas quotations-empty = El texto que guardes de una página aparece aquí. quotations-no-matches = Ninguna cita coincide con tu búsqueda. diff --git a/app/locales/fr/main.ftl b/app/locales/fr/main.ftl index a80c4c9..51bdfd8 100644 --- a/app/locales/fr/main.ftl +++ b/app/locales/fr/main.ftl @@ -70,6 +70,8 @@ posts-mark-unread = Marquer comme non lu # The selection toolbar button that keeps the marked text. quotations-save = Enregistrer comme citation +# Says the marked text was kept. +quotations-saved = Enregistrée comme citation quotations-title = Citations quotations-empty = Le texte que vous gardez d’une page apparaît ici. quotations-no-matches = Aucune citation ne correspond à votre recherche. diff --git a/app/locales/he/main.ftl b/app/locales/he/main.ftl index 9342109..7e3ee91 100644 --- a/app/locales/he/main.ftl +++ b/app/locales/he/main.ftl @@ -76,6 +76,8 @@ posts-mark-unread = סימון כלא נקראה # The selection toolbar button that keeps the marked text. quotations-save = שמירה כציטוט +# Says the marked text was kept. +quotations-saved = נשמר כציטוט quotations-title = ציטוטים quotations-empty = טקסט שתשמרו מעמוד יופיע כאן. quotations-no-matches = אין ציטוטים שתואמים לחיפוש שלכם. diff --git a/app/locales/ja/main.ftl b/app/locales/ja/main.ftl index c018d9e..b911a4b 100644 --- a/app/locales/ja/main.ftl +++ b/app/locales/ja/main.ftl @@ -70,6 +70,8 @@ posts-mark-unread = 未読にする # The selection toolbar button that keeps the marked text. quotations-save = 引用として保存 +# Says the marked text was kept. +quotations-saved = 引用として保存しました quotations-title = 引用 quotations-empty = ページから保存したテキストがここに表示されます。 quotations-no-matches = 検索に一致する引用はありません。 diff --git a/app/locales/pt/main.ftl b/app/locales/pt/main.ftl index e2366eb..2dfcc29 100644 --- a/app/locales/pt/main.ftl +++ b/app/locales/pt/main.ftl @@ -70,6 +70,8 @@ posts-mark-unread = Marcar como não lida # The selection toolbar button that keeps the marked text. quotations-save = Salvar como citação +# Says the marked text was kept. +quotations-saved = Salva como citação quotations-title = Citações quotations-empty = O texto que você guardar de uma página aparece aqui. quotations-no-matches = Nenhuma citação corresponde à sua busca. diff --git a/app/locales/ru/main.ftl b/app/locales/ru/main.ftl index 22fda07..fe6e73e 100644 --- a/app/locales/ru/main.ftl +++ b/app/locales/ru/main.ftl @@ -76,6 +76,8 @@ posts-mark-unread = Отметить непрочитанной # The selection toolbar button that keeps the marked text. quotations-save = Сохранить как цитату +# Says the marked text was kept. +quotations-saved = Сохранено как цитата quotations-title = Цитаты quotations-empty = Текст, который вы сохраните со страницы, появится здесь. quotations-no-matches = Ни одна цитата не совпадает с вашим поиском. diff --git a/app/test/app_widget_test.dart b/app/test/app_widget_test.dart index ebe4191..a998929 100644 --- a/app/test/app_widget_test.dart +++ b/app/test/app_widget_test.dart @@ -1231,6 +1231,10 @@ void main() { await tester.pumpAndSettle(); await tester.tap(find.text('Save as quotation')); + await tester.pump(); + + // The save says it happened; closing the toolbar would not. + expect(find.text('Saved as a quotation'), findsOneWidget); await tester.pumpAndSettle(); // The selection kept its page: address, title and the author the page's