diff --git a/app/lib/presentation/controllers/website_title.dart b/app/lib/presentation/controllers/website_title.dart index ed705b13..a297e753 100644 --- a/app/lib/presentation/controllers/website_title.dart +++ b/app/lib/presentation/controllers/website_title.dart @@ -24,7 +24,9 @@ import 'package:weblibre/core/logger.dart'; import 'package:weblibre/data/models/web_page_info.dart'; import 'package:weblibre/domain/services/generic_website.dart'; import 'package:weblibre/extensions/ref_cache.dart'; +import 'package:weblibre/features/geckoview/domain/entities/states/tab.dart'; import 'package:weblibre/features/geckoview/domain/providers/selected_tab.dart'; +import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/container.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart'; import 'package:weblibre/features/tor/domain/services/tor_proxy.dart'; @@ -36,26 +38,27 @@ const _supportedFetchSchemes = {'http', 'https'}; @Riverpod() class CompletePageInfo extends _$CompletePageInfo { @override - AsyncValue build(Uri url, WebPageInfo? cached) { + AsyncValue build(TabState cached) { ref.cacheFor(const Duration(minutes: 2)); - if (cached?.isPageInfoComplete == true || - !_supportedFetchSchemes.contains(url.scheme)) { - return AsyncData(cached!); + if (cached.isPageInfoComplete || + !_supportedFetchSchemes.contains(cached.url.scheme)) { + return AsyncData(cached); } ref.listen( fireImmediately: true, - pageInfoProvider(url, isImageRequest: false), + pageInfoProvider(cached.url, isImageRequest: false), (previous, next) { - if (cached != null && next.hasValue) { + if (next.hasValue) { + final current = stateOrNull?.valueOrNull ?? cached; + state = AsyncData( - WebPageInfo( - url: url, + current.copyWith( //Cached is preferred as this comes from gecko and is more likely to be correct compared to manual request - favicon: cached.favicon ?? next.value!.favicon, - feeds: cached.feeds ?? next.value!.feeds, - title: cached.title ?? next.value!.title, + favicon: current.favicon ?? next.value!.favicon, + feeds: current.feeds ?? next.value!.feeds, + title: current.title.whenNotEmpty ?? next.value!.title, ), ); } else { @@ -71,7 +74,29 @@ class CompletePageInfo extends _$CompletePageInfo { }, ); - return (cached != null) ? AsyncData(cached) : const AsyncLoading(); + ref.listen( + fireImmediately: true, + tabStateProvider(cached.id).select((value) => value?.title.whenNotEmpty), + (previous, next) { + if (next != null) { + final current = stateOrNull?.valueOrNull ?? cached; + state = AsyncData(current.copyWith.title(next)); + } + }, + ); + + ref.listen( + fireImmediately: true, + tabStateProvider(cached.id).select((value) => value?.favicon), + (previous, next) { + if (next != null) { + final current = stateOrNull?.valueOrNull ?? cached; + state = AsyncData(current.copyWith.favicon(next)); + } + }, + ); + + return AsyncData(cached); } } diff --git a/app/lib/presentation/controllers/website_title.g.dart b/app/lib/presentation/controllers/website_title.g.dart index f8f71c8d..e7724edc 100644 --- a/app/lib/presentation/controllers/website_title.g.dart +++ b/app/lib/presentation/controllers/website_title.g.dart @@ -157,14 +157,13 @@ class _PageInfoProviderElement bool get isImageRequest => (origin as PageInfoProvider).isImageRequest; } -String _$completePageInfoHash() => r'51474d7b110cfac44dd64e9f71fde091c5a35d4e'; +String _$completePageInfoHash() => r'face1bf561e0447a1260515816bac00c2b47fc71'; abstract class _$CompletePageInfo extends BuildlessAutoDisposeNotifier> { - late final Uri url; - late final WebPageInfo? cached; + late final TabState cached; - AsyncValue build(Uri url, WebPageInfo? cached); + AsyncValue build(TabState cached); } /// See also [CompletePageInfo]. @@ -177,15 +176,15 @@ class CompletePageInfoFamily extends Family> { const CompletePageInfoFamily(); /// See also [CompletePageInfo]. - CompletePageInfoProvider call(Uri url, WebPageInfo? cached) { - return CompletePageInfoProvider(url, cached); + CompletePageInfoProvider call(TabState cached) { + return CompletePageInfoProvider(cached); } @override CompletePageInfoProvider getProviderOverride( covariant CompletePageInfoProvider provider, ) { - return call(provider.url, provider.cached); + return call(provider.cached); } static const Iterable? _dependencies = null; @@ -211,11 +210,9 @@ class CompletePageInfoProvider AsyncValue > { /// See also [CompletePageInfo]. - CompletePageInfoProvider(Uri url, WebPageInfo? cached) + CompletePageInfoProvider(TabState cached) : this._internal( - () => CompletePageInfo() - ..url = url - ..cached = cached, + () => CompletePageInfo()..cached = cached, from: completePageInfoProvider, name: r'completePageInfoProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') @@ -224,7 +221,6 @@ class CompletePageInfoProvider dependencies: CompletePageInfoFamily._dependencies, allTransitiveDependencies: CompletePageInfoFamily._allTransitiveDependencies, - url: url, cached: cached, ); @@ -235,18 +231,16 @@ class CompletePageInfoProvider required super.allTransitiveDependencies, required super.debugGetCreateSourceHash, required super.from, - required this.url, required this.cached, }) : super.internal(); - final Uri url; - final WebPageInfo? cached; + final TabState cached; @override AsyncValue runNotifierBuild( covariant CompletePageInfo notifier, ) { - return notifier.build(url, cached); + return notifier.build(cached); } @override @@ -254,15 +248,12 @@ class CompletePageInfoProvider return ProviderOverride( origin: this, override: CompletePageInfoProvider._internal( - () => create() - ..url = url - ..cached = cached, + () => create()..cached = cached, from: from, name: null, dependencies: null, allTransitiveDependencies: null, debugGetCreateSourceHash: null, - url: url, cached: cached, ), ); @@ -276,15 +267,12 @@ class CompletePageInfoProvider @override bool operator ==(Object other) { - return other is CompletePageInfoProvider && - other.url == url && - other.cached == cached; + return other is CompletePageInfoProvider && other.cached == cached; } @override int get hashCode { var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, url.hashCode); hash = _SystemHash.combine(hash, cached.hashCode); return _SystemHash.finish(hash); @@ -295,11 +283,8 @@ class CompletePageInfoProvider // ignore: unused_element mixin CompletePageInfoRef on AutoDisposeNotifierProviderRef> { - /// The parameter `url` of this provider. - Uri get url; - /// The parameter `cached` of this provider. - WebPageInfo? get cached; + TabState get cached; } class _CompletePageInfoProviderElement @@ -312,9 +297,7 @@ class _CompletePageInfoProviderElement _CompletePageInfoProviderElement(super.provider); @override - Uri get url => (origin as CompletePageInfoProvider).url; - @override - WebPageInfo? get cached => (origin as CompletePageInfoProvider).cached; + TabState get cached => (origin as CompletePageInfoProvider).cached; } // ignore_for_file: type=lint diff --git a/app/lib/presentation/widgets/website_feed_tile.dart b/app/lib/presentation/widgets/website_feed_tile.dart index 1712c85c..2269d11b 100644 --- a/app/lib/presentation/widgets/website_feed_tile.dart +++ b/app/lib/presentation/widgets/website_feed_tile.dart @@ -24,24 +24,21 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:nullability/nullability.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'package:weblibre/core/routing/routes.dart'; -import 'package:weblibre/data/models/web_page_info.dart'; +import 'package:weblibre/features/geckoview/domain/entities/states/tab.dart'; import 'package:weblibre/presentation/controllers/website_title.dart'; import 'package:weblibre/presentation/widgets/rounded_text.dart'; class WebsiteFeedTile extends HookConsumerWidget { - final Uri url; - final WebPageInfo? precachedInfo; + final TabState initialTabState; - const WebsiteFeedTile(this.url, {this.precachedInfo, super.key}); + const WebsiteFeedTile(this.initialTabState, {super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final pageInfoAsync = ref.watch( - completePageInfoProvider(url, precachedInfo), - ); + final pageInfoAsync = ref.watch(completePageInfoProvider(initialTabState)); return Skeletonizer( - enabled: pageInfoAsync.isLoading && precachedInfo?.feeds == null, + enabled: pageInfoAsync.isLoading && initialTabState.feeds == null, child: pageInfoAsync.when( skipLoadingOnReload: true, data: (info) { diff --git a/app/lib/presentation/widgets/website_title_tile.dart b/app/lib/presentation/widgets/website_title_tile.dart index 06e5acb1..1d151e49 100644 --- a/app/lib/presentation/widgets/website_title_tile.dart +++ b/app/lib/presentation/widgets/website_title_tile.dart @@ -19,25 +19,23 @@ */ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:nullability/nullability.dart'; import 'package:skeletonizer/skeletonizer.dart'; -import 'package:weblibre/data/models/web_page_info.dart'; +import 'package:weblibre/features/geckoview/domain/entities/states/tab.dart'; import 'package:weblibre/presentation/controllers/website_title.dart'; import 'package:weblibre/presentation/widgets/failure_widget.dart'; class WebsiteTitleTile extends HookConsumerWidget { - final Uri url; - final WebPageInfo? precachedInfo; + final TabState initialTabState; - const WebsiteTitleTile(this.url, {this.precachedInfo, super.key}); + const WebsiteTitleTile(this.initialTabState, {super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final pageInfoAsync = ref.watch( - completePageInfoProvider(url, precachedInfo), - ); + final pageInfoAsync = ref.watch(completePageInfoProvider(initialTabState)); return Skeletonizer( - enabled: pageInfoAsync.isLoading && precachedInfo == null, + enabled: pageInfoAsync.isLoading, child: pageInfoAsync.when( skipLoadingOnReload: true, data: (info) { @@ -49,36 +47,31 @@ class WebsiteTitleTile extends HookConsumerWidget { ), contentPadding: EdgeInsets.zero, title: Text( - info.title ?? 'Unknown Title', + info.title.whenNotEmpty ?? 'Untitled', maxLines: 6, overflow: TextOverflow.ellipsis, ), - subtitle: Text(url.authority), + subtitle: Text(initialTabState.url.authority), ); }, error: (error, stackTrace) { return FailureWidget( title: error.toString(), - onRetry: () => - ref.refresh(pageInfoProvider(url, isImageRequest: false)), + onRetry: () => ref.refresh( + pageInfoProvider(initialTabState.url, isImageRequest: false), + ), ); }, - loading: () => (precachedInfo != null) - ? ListTile( - leading: RawImage( - image: precachedInfo!.favicon?.image.value, - height: 24, - width: 24, - ), - contentPadding: EdgeInsets.zero, - title: Text(precachedInfo!.title ?? 'Unknown Title'), - subtitle: Text(url.authority), - ) - : const ListTile( - contentPadding: EdgeInsets.zero, - title: Bone.text(), - subtitle: Bone.text(), - ), + loading: () => ListTile( + leading: RawImage( + image: initialTabState.favicon?.image.value, + height: 24, + width: 24, + ), + contentPadding: EdgeInsets.zero, + title: Text(initialTabState.title.whenNotEmpty ?? 'Untitled'), + subtitle: Text(initialTabState.url.authority), + ), ), ); }