Add Supa account and search changes

This commit is contained in:
Fabian Freund
2026-05-22 18:10:22 +02:00
parent 3a19865b2e
commit 51289f1266
374 changed files with 54061 additions and 5013 deletions
@@ -0,0 +1,94 @@
import 'package:fading_scroll/fading_scroll.dart';
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/features/web_search/domain/controllers/search_controller.dart';
import 'package:weblibre/features/web_search/presentation/open_in_new_tab.dart';
import 'package:weblibre/features/web_search/presentation/widgets/search_result_metadata_chips.dart';
import 'package:weblibre/presentation/widgets/failure_widget.dart';
import 'package:weblibre/presentation/widgets/uri_breadcrumb.dart';
import 'package:weblibre/presentation/widgets/url_icon.dart';
class PagePreviewScreen extends ConsumerWidget {
final Uri uri;
final WebSearchOpenTarget Function() resolveOpenTarget;
const PagePreviewScreen({
super.key,
required this.uri,
required this.resolveOpenTarget,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
// The preview screen only depends on the document for this one URL and
// the matching result row. Selecting both via a record means unrelated
// controller updates (favicons, image streams, other URLs' fetches)
// don't rebuild the (potentially-large) Markdown body below.
final (document, result) = ref.watch(
metaSearchControllerProvider.select(
(s) => (
s.documentsByUrl[uri],
s.results.where((item) => item.url == uri).firstOrNull,
),
),
);
final title = result?.title ?? document?.metadata.title ?? uri.authority;
if (document == null) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: const FailureWidget(
title: 'Preview unavailable',
exception:
'Fetch the page from the result list before opening a preview.',
),
);
}
return Scaffold(
appBar: AppBar(
title: Text(title),
actions: [
IconButton(
tooltip: 'Open in browser',
onPressed: () async {
await ref
.read(webSearchTabOpenerProvider)
.open(context, ref, uri, target: resolveOpenTarget());
},
icon: const Icon(Icons.open_in_new),
),
],
),
body: FadingScroll(
fadingSize: 25,
builder: (context, controller) {
return SingleChildScrollView(
controller: controller,
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
UriBreadcrumb(
uri: uri,
icon: UrlIcon([uri], iconSize: 16, cacheOnly: true),
),
const SizedBox(height: 16),
SearchResultMetadataChips(
metadata: result?.metadata ?? const [],
pageMetadata: document.metadata,
),
if (result?.metadata case final metadata?
when metadata.isNotEmpty)
SearchResultMetadataExpandable(metadata: metadata),
const SizedBox(height: 16),
MarkdownBody(selectable: true, data: document.content),
],
),
);
},
),
);
}
}