From 8e4c4c34e41dbaa7bc97ee4f2cd1b61b113a303a Mon Sep 17 00:00:00 2001 From: Matthias Rupp Date: Sun, 21 Aug 2022 18:41:36 +0200 Subject: [PATCH] Use CachedNetworkImage and separate cache for thumbnails on library page (#509) * Use CachedNetworkImage and separate cache for thumbnails on library page * Use caching for shared albums as well * Introduce cache service --- .../album/ui/album_thumbnail_card.dart | 31 ++++++++++++------- .../lib/modules/album/views/library_page.dart | 3 ++ .../lib/modules/album/views/sharing_page.dart | 28 ++++++++--------- mobile/lib/shared/services/cache.service.dart | 21 +++++++++++++ mobile/lib/utils/image_url_builder.dart | 25 ++++++++++++--- mobile/pubspec.lock | 2 +- mobile/pubspec.yaml | 1 + 7 files changed, 81 insertions(+), 30 deletions(-) create mode 100644 mobile/lib/shared/services/cache.service.dart diff --git a/mobile/lib/modules/album/ui/album_thumbnail_card.dart b/mobile/lib/modules/album/ui/album_thumbnail_card.dart index 51aa598f6c..3954710a75 100644 --- a/mobile/lib/modules/album/ui/album_thumbnail_card.dart +++ b/mobile/lib/modules/album/ui/album_thumbnail_card.dart @@ -1,16 +1,25 @@ +import 'dart:math'; + import 'package:auto_route/auto_route.dart'; +import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:immich_mobile/constants/hive_box.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/shared/services/cache.service.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; import 'package:openapi/api.dart'; -import 'package:transparent_image/transparent_image.dart'; class AlbumThumbnailCard extends StatelessWidget { - const AlbumThumbnailCard({Key? key, required this.album}) : super(key: key); + const AlbumThumbnailCard({ + Key? key, + required this.album, + required this.cacheService, + }) : super(key: key); final AlbumResponseDto album; + final CacheService cacheService; @override Widget build(BuildContext context) { @@ -29,19 +38,19 @@ class AlbumThumbnailCard extends StatelessWidget { children: [ ClipRRect( borderRadius: BorderRadius.circular(8), - child: FadeInImage( + child: CachedNetworkImage( + cacheManager: cacheService.getCache(CacheType.albumThumbnail), + memCacheHeight: max(400, cardSize.toInt() * 3), width: cardSize, height: cardSize, fit: BoxFit.cover, - placeholder: MemoryImage(kTransparentImage), - image: NetworkImage( - '${box.get(serverEndpointKey)}/asset/thumbnail/${album.albumThumbnailAssetId}?format=JPEG', - headers: { - "Authorization": "Bearer ${box.get(accessTokenKey)}" - }, - ), fadeInDuration: const Duration(milliseconds: 200), - fadeOutDuration: const Duration(milliseconds: 200), + imageUrl: + getAlbumThumbnailUrl(album, type: ThumbnailFormat.JPEG), + httpHeaders: { + "Authorization": "Bearer ${box.get(accessTokenKey)}" + }, + cacheKey: "${album.albumThumbnailAssetId}", ), ), Padding( diff --git a/mobile/lib/modules/album/views/library_page.dart b/mobile/lib/modules/album/views/library_page.dart index 7bb91bc815..ce429cd3bd 100644 --- a/mobile/lib/modules/album/views/library_page.dart +++ b/mobile/lib/modules/album/views/library_page.dart @@ -6,6 +6,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/album/providers/album.provider.dart'; import 'package:immich_mobile/modules/album/ui/album_thumbnail_card.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/shared/services/cache.service.dart'; class LibraryPage extends HookConsumerWidget { const LibraryPage({Key? key}) : super(key: key); @@ -13,6 +14,7 @@ class LibraryPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final albums = ref.watch(albumProvider); + final cacheService = ref.watch(cacheServiceProvider); useEffect( () { @@ -102,6 +104,7 @@ class LibraryPage extends HookConsumerWidget { _buildCreateAlbumButton(), for (var album in albums) AlbumThumbnailCard( + cacheService: cacheService, album: album, ), ], diff --git a/mobile/lib/modules/album/views/sharing_page.dart b/mobile/lib/modules/album/views/sharing_page.dart index 5fb8ee00ce..114888a0b5 100644 --- a/mobile/lib/modules/album/views/sharing_page.dart +++ b/mobile/lib/modules/album/views/sharing_page.dart @@ -1,4 +1,5 @@ import 'package:auto_route/auto_route.dart'; +import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; @@ -8,8 +9,9 @@ import 'package:immich_mobile/constants/hive_box.dart'; import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart'; import 'package:immich_mobile/modules/album/ui/sharing_sliver_appbar.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/shared/services/cache.service.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; import 'package:openapi/api.dart'; -import 'package:transparent_image/transparent_image.dart'; class SharingPage extends HookConsumerWidget { const SharingPage({Key? key}) : super(key: key); @@ -19,6 +21,7 @@ class SharingPage extends HookConsumerWidget { var box = Hive.box(userInfoBox); var thumbnailRequestUrl = '${box.get(serverEndpointKey)}/asset/thumbnail'; final List sharedAlbums = ref.watch(sharedAlbumProvider); + final CacheService cacheService = ref.watch(cacheServiceProvider); useEffect( () { @@ -32,29 +35,26 @@ class SharingPage extends HookConsumerWidget { return SliverList( delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { - String thumbnailUrl = sharedAlbums[index].albumThumbnailAssetId != - null - ? "$thumbnailRequestUrl/${sharedAlbums[index].albumThumbnailAssetId}" - : "https://images.unsplash.com/photo-1612178537253-bccd437b730e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8Ymxhbmt8ZW58MHx8MHx8&auto=format&fit=crop&w=700&q=60"; + final album = sharedAlbums[index]; return ListTile( contentPadding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12), leading: ClipRRect( borderRadius: BorderRadius.circular(8), - child: FadeInImage( + child: CachedNetworkImage( width: 60, height: 60, + memCacheHeight: 200, fit: BoxFit.cover, - placeholder: MemoryImage(kTransparentImage), - image: NetworkImage( - thumbnailUrl, - headers: { - "Authorization": "Bearer ${box.get(accessTokenKey)}" - }, - ), + cacheManager: + cacheService.getCache(CacheType.sharedAlbumThumbnail), + imageUrl: getAlbumThumbnailUrl(album), + cacheKey: album.albumThumbnailAssetId, + httpHeaders: { + "Authorization": "Bearer ${box.get(accessTokenKey)}" + }, fadeInDuration: const Duration(milliseconds: 200), - fadeOutDuration: const Duration(milliseconds: 200), ), ), title: Text( diff --git a/mobile/lib/shared/services/cache.service.dart b/mobile/lib/shared/services/cache.service.dart new file mode 100644 index 0000000000..25e08b63cb --- /dev/null +++ b/mobile/lib/shared/services/cache.service.dart @@ -0,0 +1,21 @@ +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +enum CacheType { + albumThumbnail, + sharedAlbumThumbnail; +} + +final cacheServiceProvider = Provider((_) => CacheService()); + +class CacheService { + + BaseCacheManager getCache(CacheType type) { + return _getDefaultCache(type.name); + } + + BaseCacheManager _getDefaultCache(String cacheName) { + return CacheManager(Config(cacheName)); + } + +} \ No newline at end of file diff --git a/mobile/lib/utils/image_url_builder.dart b/mobile/lib/utils/image_url_builder.dart index 0a22b54dd8..5d6ac302bc 100644 --- a/mobile/lib/utils/image_url_builder.dart +++ b/mobile/lib/utils/image_url_builder.dart @@ -3,14 +3,31 @@ import 'package:openapi/api.dart'; import '../constants/hive_box.dart'; -String getThumbnailUrl(final AssetResponseDto asset, - {ThumbnailFormat type = ThumbnailFormat.WEBP}) { - final box = Hive.box(userInfoBox); +String getThumbnailUrl( + final AssetResponseDto asset, { + ThumbnailFormat type = ThumbnailFormat.WEBP, +}) { + return _getThumbnailUrl(asset.id, type: type); +} - return '${box.get(serverEndpointKey)}/asset/thumbnail/${asset.id}?format=${type.value}'; +String getAlbumThumbnailUrl( + final AlbumResponseDto album, { + ThumbnailFormat type = ThumbnailFormat.WEBP, +}) { + if (album.albumThumbnailAssetId == null) { + return ''; + } + return _getThumbnailUrl(album.albumThumbnailAssetId!, type: type); } String getImageUrl(final AssetResponseDto asset) { final box = Hive.box(userInfoBox); return '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=false'; } + +String _getThumbnailUrl(final String id, + {ThumbnailFormat type = ThumbnailFormat.WEBP}) { + final box = Hive.box(userInfoBox); + + return '${box.get(serverEndpointKey)}/asset/thumbnail/${id}?format=${type.value}'; +} diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 5e5131f3d2..379cefa34d 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -322,7 +322,7 @@ packages: source: hosted version: "0.6.8" flutter_cache_manager: - dependency: transitive + dependency: "direct main" description: name: flutter_cache_manager url: "https://pub.dartlang.org" diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index e5fb22d83e..2c5c9ce051 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -43,6 +43,7 @@ dependencies: easy_localization: ^3.0.1 share_plus: ^4.0.10 flutter_displaymode: ^0.4.0 + flutter_cache_manager: 3.3.0 path: ^1.8.1 path_provider: ^2.0.11