Browse Source

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
Matthias Rupp 2 years ago
parent
commit
8e4c4c34e4

+ 20 - 11
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(

+ 3 - 0
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,
                     ),
                 ],

+ 14 - 14
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<AlbumResponseDto> 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(

+ 21 - 0
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));
+  }
+
+}

+ 21 - 4
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}';
+}

+ 1 - 1
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"

+ 1 - 0
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