import '../../database/database.dart'; import '../../l10n/strings.dart'; import '../../mews/document.dart'; import 'mews_urls.dart'; import 'page_blocks.dart'; /// Every text the reader kept from a page, with the page it came from. The /// page mirrors the posts page: a searchable list ordered by the day the /// text was kept or by the page it came from, one row per quotation, each /// row carrying the citation's author when the page named one. Future buildQuotationsPage( Uri url, { required MewsDatabase db, required Strings strings, }) async { final query = url.queryParameters['q'] ?? ''; final sort = QuotationSort.parse(url.queryParameters['sort']); final quotations = await db.quotationsList(sort: sort, query: query); final total = await db.countQuotations(); final title = strings.t('quotations-title'); return internalPage( url: url, title: title, strings: strings, body: [ ...chrome(title: title, current: url, strings: strings), textForm( action: MewsUrls.quotations, label: strings.t('quotations-search-label'), submitLabel: strings.t('action-search'), field: 'q', value: query, ), ...linkLine([ if (query.isNotEmpty) action(MewsUrls.page(url, {'q': null}), strings.t('action-clear-search')), ]), ...choiceLine( label: strings.t('posts-sort-label'), options: [ for (final option in const [ (QuotationSort.date, 'posts-sort-date'), (QuotationSort.page, 'quotations-sort-page'), ]) ( href: MewsUrls.page(url, {'sort': option.$1.name}), label: strings.t(option.$2), current: option.$1 == sort, ), ], ), const RuleBlock(), ParagraphBlock([ MewsEmphasis(EmphasisStyle.emphasis, [ MewsText(countText(strings, total, none: 'count-quotations-none', one: 'count-quotations-one', many: 'count-quotations-many')), ]), ]), if (quotations.isEmpty) ParagraphBlock([ MewsText(strings.t( query.isEmpty ? 'quotations-empty' : 'quotations-no-matches')), ]) else itemList([ for (final quotation in quotations) itemRow( href: quotation.pageUrl, label: quotation.quote, date: dayText(quotation.createdAt), // The row names the page the text came from, not just the // text: the author and the article's title when they are // known, and the site by its own title when the reader's // subscriptions know it, otherwise by its host. note: _rowNote(quotation), actions: [ action( MewsUrls.action('share-quotation', {'id': '${quotation.id}'}), strings.t('quotations-share')), action( MewsUrls.action( 'remove-quotation', {'id': '${quotation.id}'}), strings.t('quotations-remove')), ], ), ]), ], ); } /// Who and what the row names behind a quotation: the author and the /// article's title when they are known, and the site the page came from by /// its own title, or by its host when the subscriptions do not know it. /// Nothing is named twice. String? _rowNote(Quotation quotation) { final host = Uri.tryParse(quotation.pageUrl)?.host; final source = (quotation.siteTitle?.trim().isNotEmpty ?? false) ? quotation.siteTitle!.trim() : (host != null && host.isNotEmpty ? host : quotation.pageUrl); final named = {}; final parts = [ for (final part in [ quotation.author?.trim(), quotation.pageTitle?.trim(), source, ]) if (part != null && part.isNotEmpty && named.add(part)) part, ]; return parts.isEmpty ? null : parts.join(actionSeparator); } /// The citation a quotation travels with, shaped as APA names a web page: /// the author first when the page's metadata carried one, otherwise the /// article's title; the year as a no-date marker, since a page declares no /// publication date; then the article's title, the site's own title and the /// address. The site is named by its title when the reader's subscriptions /// know it, and by its host when they do not; nothing is named twice. String citationFor(Quotation quotation, Strings strings) { final noDate = strings.t('quotations-no-date'); final uri = Uri.tryParse(quotation.pageUrl); final host = uri != null && uri.host.isNotEmpty ? uri.host : quotation.pageUrl; final author = quotation.author?.trim(); final title = quotation.pageTitle?.trim(); final site = (quotation.siteTitle?.trim().isNotEmpty ?? false) ? quotation.siteTitle!.trim() : host; final lead = (author != null && author.isNotEmpty) ? author : title ?? site; final named = [ if (lead != title && title != null && title.isNotEmpty) title, if (site != lead && site != title) site, ]; if (named.isEmpty) return '$lead ($noDate). ${quotation.pageUrl}.'; return '$lead ($noDate). ${named.join('. ')}. ${quotation.pageUrl}.'; }