Improved favicons load
This commit is contained in:
@@ -2,8 +2,10 @@ import 'package:linkdy/models/favicon_item.dart';
|
||||
|
||||
class FaviconLoaderModel {
|
||||
List<FaviconItem> favicons;
|
||||
bool loadingFavicons;
|
||||
|
||||
FaviconLoaderModel({
|
||||
required this.favicons,
|
||||
this.loadingFavicons = true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import 'package:linkdy/screens/bookmarks/provider/favicon_loader.provider.dart';
|
||||
import 'package:linkdy/screens/bookmarks/model/bookmarks.model.dart';
|
||||
|
||||
import 'package:linkdy/providers/api_client.provider.dart';
|
||||
@@ -15,6 +16,7 @@ FutureOr<void> bookmarksRequest(BookmarksRequestRef ref, int limit) async {
|
||||
);
|
||||
|
||||
if (result.successful == true) {
|
||||
ref.read(faviconStoreProvider.notifier).loadFavicons(result.content!.results!);
|
||||
ref.read(bookmarksProvider).bookmarks = result.content!.results!;
|
||||
ref.read(bookmarksProvider).maxNumber = result.content!.count!;
|
||||
ref.read(bookmarksProvider).currentPage = 0;
|
||||
@@ -38,6 +40,7 @@ FutureOr<void> bookmarksRequestLoadMore(BookmarksRequestLoadMoreRef ref) async {
|
||||
);
|
||||
|
||||
if (result.successful == true) {
|
||||
ref.read(faviconStoreProvider.notifier).loadFavicons(result.content!.results!);
|
||||
provider.bookmarks = [...provider.bookmarks, ...result.content!.results!];
|
||||
provider.maxNumber = result.content!.count!;
|
||||
provider.currentPage = provider.currentPage + 1;
|
||||
|
||||
@@ -6,7 +6,7 @@ part of 'bookmarks.provider.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$bookmarksRequestHash() => r'04ee313bc986046f5c401f7539b0ba2386001303';
|
||||
String _$bookmarksRequestHash() => r'2da88ec52b4f959e6355f5f5fa8660d6cebd157d';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
@@ -156,7 +156,7 @@ class _BookmarksRequestProviderElement
|
||||
}
|
||||
|
||||
String _$bookmarksRequestLoadMoreHash() =>
|
||||
r'523edffa3f18851e1879948e7f8996904aa0aa32';
|
||||
r'36b0703684218e1c6bf01643788d5f9d3da32327';
|
||||
|
||||
/// See also [bookmarksRequestLoadMore].
|
||||
@ProviderFor(bookmarksRequestLoadMore)
|
||||
|
||||
@@ -1,40 +1,39 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:favicon/favicon.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import 'package:linkdy/screens/bookmarks/model/favicon_loader.model.dart';
|
||||
|
||||
import 'package:linkdy/models/favicon_item.dart';
|
||||
import 'package:linkdy/models/data/bookmarks.dart';
|
||||
|
||||
part 'favicon_loader.provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
FutureOr<FaviconItem?> fetchFavicon(FetchFaviconRef ref, String bookmarkUrl) async {
|
||||
final exists = ref.watch(faviconStoreProvider).favicons.where((favicon) => favicon.url == bookmarkUrl).toList();
|
||||
if (exists.isNotEmpty) {
|
||||
return exists[0];
|
||||
}
|
||||
// @riverpod
|
||||
// FutureOr<FaviconItem?> fetchFavicon(FetchFaviconRef ref, String bookmarkUrl) async {
|
||||
// final exists = ref.watch(faviconStoreProvider).favicons.where((favicon) => favicon.url == bookmarkUrl).toList();
|
||||
// if (exists.isNotEmpty) {
|
||||
// return exists[0];
|
||||
// }
|
||||
|
||||
final faviconResult = await FaviconFinder.getBest(bookmarkUrl);
|
||||
if (faviconResult == null) {
|
||||
return null;
|
||||
}
|
||||
// final faviconResult = await FaviconFinder.getBest(bookmarkUrl);
|
||||
// if (faviconResult == null) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
final result = await http.get(Uri.parse(faviconResult.url));
|
||||
if (result.statusCode == 200) {
|
||||
final faviconItem = FaviconItem(
|
||||
url: bookmarkUrl,
|
||||
favicon: faviconResult.url.contains("svg") ? result.body : base64.encode(result.bodyBytes),
|
||||
isSvg: faviconResult.url.contains("svg"),
|
||||
);
|
||||
ref.read(faviconStoreProvider).favicons.add(faviconItem);
|
||||
return faviconItem;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// final result = await http.get(Uri.parse(faviconResult.url));
|
||||
// if (result.statusCode == 200) {
|
||||
// final faviconItem = FaviconItem(
|
||||
// url: bookmarkUrl,
|
||||
// favicon: faviconResult.url.contains("svg") ? result.body : base64.encode(result.bodyBytes),
|
||||
// isSvg: faviconResult.url.contains("svg"),
|
||||
// );
|
||||
// ref.read(faviconStoreProvider).favicons.add(faviconItem);
|
||||
// return faviconItem;
|
||||
// } else {
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class FaviconStore extends _$FaviconStore {
|
||||
@@ -44,4 +43,33 @@ class FaviconStore extends _$FaviconStore {
|
||||
favicons: [],
|
||||
);
|
||||
}
|
||||
|
||||
void clearFavicons() {
|
||||
state.favicons = [];
|
||||
}
|
||||
|
||||
void loadFavicons(List<Bookmark> bookmarks) async {
|
||||
state.loadingFavicons = true;
|
||||
ref.notifyListeners();
|
||||
|
||||
final mappedSaved = state.favicons.map((e) => e.url);
|
||||
final notExist = bookmarks.map((b) => b.url!).where((b) => !mappedSaved.contains(b)).toList();
|
||||
|
||||
final favicons = await compute(
|
||||
(message) => Future.wait(message.map((e) => FaviconFinder.getBest(e))),
|
||||
notExist,
|
||||
);
|
||||
|
||||
final faviconUrls = favicons.asMap().entries.map(
|
||||
(e) => FaviconItem(
|
||||
url: notExist[e.key],
|
||||
favicon: e.value!.url,
|
||||
isSvg: e.value!.url.contains('svg'),
|
||||
),
|
||||
);
|
||||
|
||||
state.favicons = [...state.favicons, ...faviconUrls];
|
||||
state.loadingFavicons = false;
|
||||
ref.notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,157 +6,7 @@ part of 'favicon_loader.provider.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$fetchFaviconHash() => r'90d9f0060aa68162174a1d76a6faeee5ae820494';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
_SystemHash._();
|
||||
|
||||
static int combine(int hash, int value) {
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + value);
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||
return hash ^ (hash >> 6);
|
||||
}
|
||||
|
||||
static int finish(int hash) {
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||
// ignore: parameter_assignments
|
||||
hash = hash ^ (hash >> 11);
|
||||
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [fetchFavicon].
|
||||
@ProviderFor(fetchFavicon)
|
||||
const fetchFaviconProvider = FetchFaviconFamily();
|
||||
|
||||
/// See also [fetchFavicon].
|
||||
class FetchFaviconFamily extends Family<AsyncValue<FaviconItem?>> {
|
||||
/// See also [fetchFavicon].
|
||||
const FetchFaviconFamily();
|
||||
|
||||
/// See also [fetchFavicon].
|
||||
FetchFaviconProvider call(
|
||||
String bookmarkUrl,
|
||||
) {
|
||||
return FetchFaviconProvider(
|
||||
bookmarkUrl,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FetchFaviconProvider getProviderOverride(
|
||||
covariant FetchFaviconProvider provider,
|
||||
) {
|
||||
return call(
|
||||
provider.bookmarkUrl,
|
||||
);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'fetchFaviconProvider';
|
||||
}
|
||||
|
||||
/// See also [fetchFavicon].
|
||||
class FetchFaviconProvider extends AutoDisposeFutureProvider<FaviconItem?> {
|
||||
/// See also [fetchFavicon].
|
||||
FetchFaviconProvider(
|
||||
String bookmarkUrl,
|
||||
) : this._internal(
|
||||
(ref) => fetchFavicon(
|
||||
ref as FetchFaviconRef,
|
||||
bookmarkUrl,
|
||||
),
|
||||
from: fetchFaviconProvider,
|
||||
name: r'fetchFaviconProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$fetchFaviconHash,
|
||||
dependencies: FetchFaviconFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
FetchFaviconFamily._allTransitiveDependencies,
|
||||
bookmarkUrl: bookmarkUrl,
|
||||
);
|
||||
|
||||
FetchFaviconProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.bookmarkUrl,
|
||||
}) : super.internal();
|
||||
|
||||
final String bookmarkUrl;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
FutureOr<FaviconItem?> Function(FetchFaviconRef provider) create,
|
||||
) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: FetchFaviconProvider._internal(
|
||||
(ref) => create(ref as FetchFaviconRef),
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
bookmarkUrl: bookmarkUrl,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeFutureProviderElement<FaviconItem?> createElement() {
|
||||
return _FetchFaviconProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is FetchFaviconProvider && other.bookmarkUrl == bookmarkUrl;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, bookmarkUrl.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
mixin FetchFaviconRef on AutoDisposeFutureProviderRef<FaviconItem?> {
|
||||
/// The parameter `bookmarkUrl` of this provider.
|
||||
String get bookmarkUrl;
|
||||
}
|
||||
|
||||
class _FetchFaviconProviderElement
|
||||
extends AutoDisposeFutureProviderElement<FaviconItem?>
|
||||
with FetchFaviconRef {
|
||||
_FetchFaviconProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
String get bookmarkUrl => (origin as FetchFaviconProvider).bookmarkUrl;
|
||||
}
|
||||
|
||||
String _$faviconStoreHash() => r'a082b856f1564d04dfd9209b7c2efaff6f146e68';
|
||||
String _$faviconStoreHash() => r'87cd0d0c0df889ca3238b6c05389b8dd351d189c';
|
||||
|
||||
/// See also [FaviconStore].
|
||||
@ProviderFor(FaviconStore)
|
||||
|
||||
@@ -46,44 +46,76 @@ class BookmarkItem extends ConsumerWidget {
|
||||
Row(
|
||||
children: [
|
||||
if (ref.watch(appStatusProvider).showFavicon == true)
|
||||
FutureBuilder(
|
||||
future: ref.read(fetchFaviconProvider(bookmark.url!).future),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData == false) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: Skeletonizer(
|
||||
enabled: true,
|
||||
ignoreContainers: true,
|
||||
child: Container(
|
||||
width: 16,
|
||||
height: 16,
|
||||
color: Colors.black,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: ref.watch(faviconStoreProvider).loadingFavicons == true
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: Skeletonizer(
|
||||
enabled: true,
|
||||
ignoreContainers: true,
|
||||
child: Container(
|
||||
width: 16,
|
||||
height: 16,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Builder(
|
||||
builder: (context) {
|
||||
final faviconItem = ref
|
||||
.watch(faviconStoreProvider)
|
||||
.favicons
|
||||
.where((f) => f.url == bookmark.url!)
|
||||
.toList();
|
||||
if (faviconItem.isEmpty) return const SizedBox();
|
||||
if (faviconItem[0].isSvg == true) {
|
||||
return SvgPicture.network(
|
||||
faviconItem[0].favicon,
|
||||
width: 16,
|
||||
height: 16,
|
||||
placeholderBuilder: (context) => ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: Skeletonizer(
|
||||
enabled: true,
|
||||
ignoreContainers: true,
|
||||
child: Container(
|
||||
width: 16,
|
||||
height: 16,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Image.network(
|
||||
faviconItem[0].favicon,
|
||||
width: 16,
|
||||
height: 16,
|
||||
loadingBuilder: (context, child, loadingProgress) {
|
||||
if (loadingProgress != null) {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: Skeletonizer(
|
||||
enabled: true,
|
||||
ignoreContainers: true,
|
||||
child: Container(
|
||||
width: 16,
|
||||
height: 16,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return child;
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
return Row(
|
||||
children: [
|
||||
if (snapshot.data!.isSvg == true)
|
||||
SvgPicture.string(
|
||||
snapshot.data!.favicon,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
if (snapshot.data!.isSvg == false)
|
||||
Image.memory(
|
||||
base64Decode(snapshot.data!.favicon),
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
|
||||
Reference in New Issue
Block a user