mirror of
				https://github.com/immich-app/immich.git
				synced 2025-10-31 02:39:03 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			175 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'package:auto_route/auto_route.dart';
 | |
| import 'package:flutter/material.dart';
 | |
| import 'package:flutter/services.dart';
 | |
| import 'package:hooks_riverpod/hooks_riverpod.dart';
 | |
| import 'package:immich_mobile/routing/router.dart';
 | |
| import 'package:immich_mobile/shared/models/asset.dart';
 | |
| import 'package:immich_mobile/shared/ui/immich_image.dart';
 | |
| import 'package:immich_mobile/utils/storage_indicator.dart';
 | |
| 
 | |
| class ThumbnailImage extends HookConsumerWidget {
 | |
|   final Asset asset;
 | |
|   final int index;
 | |
|   final Asset Function(int index) loadAsset;
 | |
|   final int totalAssets;
 | |
|   final bool showStorageIndicator;
 | |
|   final bool useGrayBoxPlaceholder;
 | |
|   final bool isSelected;
 | |
|   final bool multiselectEnabled;
 | |
|   final Function? onSelect;
 | |
|   final Function? onDeselect;
 | |
| 
 | |
|   const ThumbnailImage({
 | |
|     Key? key,
 | |
|     required this.asset,
 | |
|     required this.index,
 | |
|     required this.loadAsset,
 | |
|     required this.totalAssets,
 | |
|     this.showStorageIndicator = true,
 | |
|     this.useGrayBoxPlaceholder = false,
 | |
|     this.isSelected = false,
 | |
|     this.multiselectEnabled = false,
 | |
|     this.onDeselect,
 | |
|     this.onSelect,
 | |
|   }) : super(key: key);
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context, WidgetRef ref) {
 | |
|     Widget buildSelectionIcon(Asset asset) {
 | |
|       if (isSelected) {
 | |
|         return Icon(
 | |
|           Icons.check_circle,
 | |
|           color: Theme.of(context).primaryColor,
 | |
|         );
 | |
|       } else {
 | |
|         return const Icon(
 | |
|           Icons.circle_outlined,
 | |
|           color: Colors.white,
 | |
|         );
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return GestureDetector(
 | |
|       onTap: () {
 | |
|         if (multiselectEnabled) {
 | |
|           if (isSelected) {
 | |
|             onDeselect?.call();
 | |
|           } else {
 | |
|             onSelect?.call();
 | |
|           }
 | |
|         } else {
 | |
|           AutoRouter.of(context).push(
 | |
|             GalleryViewerRoute(
 | |
|               initialIndex: index,
 | |
|               loadAsset: loadAsset,
 | |
|               totalAssets: totalAssets,
 | |
|             ),
 | |
|           );
 | |
|         }
 | |
|       },
 | |
|       onLongPress: () {
 | |
|         onSelect?.call();
 | |
|         HapticFeedback.heavyImpact();
 | |
|       },
 | |
|       child: Hero(
 | |
|         createRectTween: (begin, end) {
 | |
|           double? top;
 | |
|           // Uses the [BoxFit.contain] algorithm
 | |
|           if (asset.width != null && asset.height != null) {
 | |
|             final assetAR = asset.width! / asset.height!;
 | |
|             final w = MediaQuery.of(context).size.width;
 | |
|             final deviceAR = MediaQuery.of(context).size.aspectRatio;
 | |
|             if (deviceAR < assetAR) {
 | |
|               top = asset.height! * w / asset.width!;
 | |
|             } else {
 | |
|               top = 0;
 | |
|             }
 | |
|             // get the height offset
 | |
|           }
 | |
| 
 | |
|           return MaterialRectCenterArcTween(
 | |
|             begin: Rect.fromLTRB(
 | |
|               0,
 | |
|               top ?? 0.0,
 | |
|               MediaQuery.of(context).size.width,
 | |
|               MediaQuery.of(context).size.height,
 | |
|             ),
 | |
|             end: end,
 | |
|           );
 | |
|         },
 | |
|         tag: asset.id,
 | |
|         child: Stack(
 | |
|           children: [
 | |
|             Container(
 | |
|               decoration: BoxDecoration(
 | |
|                 border: multiselectEnabled && isSelected
 | |
|                     ? Border.all(
 | |
|                         color: onDeselect == null
 | |
|                             ? Colors.grey
 | |
|                             : Theme.of(context).primaryColorLight,
 | |
|                         width: 10,
 | |
|                       )
 | |
|                     : const Border(),
 | |
|               ),
 | |
|               child: ImmichImage(
 | |
|                 asset,
 | |
|                 width: 300,
 | |
|                 height: 300,
 | |
|                 useGrayBoxPlaceholder: useGrayBoxPlaceholder,
 | |
|               ),
 | |
|             ),
 | |
|             if (multiselectEnabled)
 | |
|               Padding(
 | |
|                 padding: const EdgeInsets.all(3.0),
 | |
|                 child: Align(
 | |
|                   alignment: Alignment.topLeft,
 | |
|                   child: buildSelectionIcon(asset),
 | |
|                 ),
 | |
|               ),
 | |
|             if (showStorageIndicator)
 | |
|               Positioned(
 | |
|                 right: 10,
 | |
|                 bottom: 5,
 | |
|                 child: Icon(
 | |
|                   storageIcon(asset),
 | |
|                   color: Colors.white,
 | |
|                   size: 18,
 | |
|                 ),
 | |
|               ),
 | |
|             if (asset.isFavorite)
 | |
|               const Positioned(
 | |
|                 left: 10,
 | |
|                 bottom: 5,
 | |
|                 child: Icon(
 | |
|                   Icons.favorite,
 | |
|                   color: Colors.white,
 | |
|                   size: 18,
 | |
|                 ),
 | |
|               ),
 | |
|             if (!asset.isImage)
 | |
|               Positioned(
 | |
|                 top: 5,
 | |
|                 right: 5,
 | |
|                 child: Row(
 | |
|                   children: [
 | |
|                     Text(
 | |
|                       asset.duration.toString().substring(0, 7),
 | |
|                       style: const TextStyle(
 | |
|                         color: Colors.white,
 | |
|                         fontSize: 10,
 | |
|                       ),
 | |
|                     ),
 | |
|                     const Icon(
 | |
|                       Icons.play_circle_outline_rounded,
 | |
|                       color: Colors.white,
 | |
|                     ),
 | |
|                   ],
 | |
|                 ),
 | |
|               ),
 | |
|           ],
 | |
|         ),
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| }
 |