mirror of
				https://github.com/immich-app/immich.git
				synced 2025-10-31 10:37:11 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			191 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			5.2 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;
 | |
|   final int heroOffset;
 | |
| 
 | |
|   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,
 | |
|     this.heroOffset = 0,
 | |
|   }) : super(key: key);
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context, WidgetRef ref) {
 | |
|     final isDarkTheme = Theme.of(context).brightness == Brightness.dark;
 | |
|     final assetContainerColor =
 | |
|         isDarkTheme ? Colors.blueGrey : Theme.of(context).primaryColorLight;
 | |
| 
 | |
|     Widget buildSelectionIcon(Asset asset) {
 | |
|       if (isSelected) {
 | |
|         return Container(
 | |
|           decoration: BoxDecoration(
 | |
|             shape: BoxShape.circle,
 | |
|             color: assetContainerColor,
 | |
|           ),
 | |
|           child: Icon(
 | |
|             Icons.check_circle_rounded,
 | |
|             color: Theme.of(context).primaryColor,
 | |
|           ),
 | |
|         );
 | |
|       } else {
 | |
|         return const Icon(
 | |
|           Icons.circle_outlined,
 | |
|           color: Colors.white,
 | |
|         );
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     Widget buildImage(Asset asset) {
 | |
|       var image = SizedBox(
 | |
|         width: 300,
 | |
|         height: 300,
 | |
|         child: Hero(
 | |
|           tag: asset.id + heroOffset,
 | |
|           child: ImmichImage(
 | |
|             asset,
 | |
|             useGrayBoxPlaceholder: useGrayBoxPlaceholder,
 | |
|             fit: BoxFit.cover,
 | |
|           ),
 | |
|         ),
 | |
|       );
 | |
|       if (!multiselectEnabled || !isSelected) {
 | |
|         return image;
 | |
|       }
 | |
|       return Container(
 | |
|         decoration: BoxDecoration(
 | |
|           border: Border.all(
 | |
|             width: 0,
 | |
|             color: assetContainerColor,
 | |
|           ),
 | |
|           color: assetContainerColor,
 | |
|         ),
 | |
|         child: ClipRRect(
 | |
|           borderRadius: const BorderRadius.only(
 | |
|             topRight: Radius.circular(15.0),
 | |
|             bottomRight: Radius.circular(15.0),
 | |
|             bottomLeft: Radius.circular(15.0),
 | |
|             topLeft: Radius.zero,
 | |
|           ),
 | |
|           child: image,
 | |
|         ),
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     return GestureDetector(
 | |
|       onTap: () {
 | |
|         if (multiselectEnabled) {
 | |
|           if (isSelected) {
 | |
|             onDeselect?.call();
 | |
|           } else {
 | |
|             onSelect?.call();
 | |
|           }
 | |
|         } else {
 | |
|           AutoRouter.of(context).push(
 | |
|             GalleryViewerRoute(
 | |
|               initialIndex: index,
 | |
|               loadAsset: loadAsset,
 | |
|               totalAssets: totalAssets,
 | |
|               heroOffset: heroOffset,
 | |
|             ),
 | |
|           );
 | |
|         }
 | |
|       },
 | |
|       onLongPress: () {
 | |
|         onSelect?.call();
 | |
|         HapticFeedback.heavyImpact();
 | |
|       },
 | |
|       child: Stack(
 | |
|         children: [
 | |
|           Container(
 | |
|             decoration: BoxDecoration(
 | |
|               border: multiselectEnabled && isSelected
 | |
|                   ? Border.all(
 | |
|                       color: onDeselect == null
 | |
|                           ? Colors.grey
 | |
|                           : assetContainerColor,
 | |
|                       width: 8,
 | |
|                     )
 | |
|                   : const Border(),
 | |
|             ),
 | |
|             child: buildImage(asset),
 | |
|           ),
 | |
|           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,
 | |
|                   ),
 | |
|                 ],
 | |
|               ),
 | |
|             ),
 | |
|         ],
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| }
 |