From 1b15b5414c5a8dad31163005a8bd618a753a6590 Mon Sep 17 00:00:00 2001 From: martyfuhry Date: Tue, 20 Jun 2023 17:17:43 -0400 Subject: [PATCH] Adds photo thumbnail to videos (#2880) * Motion photos use placeholder image for more seamless loading * Fixes merge conflicts --- .../asset_viewer/views/gallery_viewer.dart | 68 +++++++++++-------- .../asset_viewer/views/video_viewer_page.dart | 26 +++++-- 2 files changed, 58 insertions(+), 36 deletions(-) diff --git a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart index 7f9697d9a..7661bd508 100644 --- a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart +++ b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart @@ -367,6 +367,26 @@ class GalleryViewerPage extends HookConsumerWidget { ); } + ImageProvider imageProvider(Asset asset) { + if (asset.isLocal) { + return localImageProvider(asset); + } else { + if (isLoadOriginal.value) { + return originalImageProvider(asset); + } else if (isLoadPreview.value) { + return remoteThumbnailImageProvider( + asset, + api.ThumbnailFormat.JPEG, + ); + } else { + return remoteThumbnailImageProvider( + asset, + api.ThumbnailFormat.WEBP, + ); + } + } + } + return Scaffold( backgroundColor: Colors.black, body: WillPopScope( @@ -460,26 +480,9 @@ class GalleryViewerPage extends HookConsumerWidget { : null, builder: (context, index) { final asset = loadAsset(index); + final ImageProvider provider = imageProvider(asset); + if (asset.isImage && !isPlayingMotionVideo.value) { - // Show photo - final ImageProvider provider; - if (asset.isLocal) { - provider = localImageProvider(asset); - } else { - if (isLoadOriginal.value) { - provider = originalImageProvider(asset); - } else if (isLoadPreview.value) { - provider = remoteThumbnailImageProvider( - asset, - api.ThumbnailFormat.JPEG, - ); - } else { - provider = remoteThumbnailImageProvider( - asset, - api.ThumbnailFormat.WEBP, - ); - } - } return PhotoViewGalleryPageOptions( onDragStart: (_, details, __) => localPosition = details.localPosition, @@ -512,18 +515,23 @@ class GalleryViewerPage extends HookConsumerWidget { maxScale: 1.0, minScale: 1.0, basePosition: Alignment.bottomCenter, - child: SafeArea( - child: VideoViewerPage( - onPlaying: () => isPlayingVideo.value = true, - onPaused: () => isPlayingVideo.value = false, - asset: asset, - isMotionVideo: isPlayingMotionVideo.value, - onVideoEnded: () { - if (isPlayingMotionVideo.value) { - isPlayingMotionVideo.value = false; - } - }, + child: VideoViewerPage( + onPlaying: () => isPlayingVideo.value = true, + onPaused: () => isPlayingVideo.value = false, + asset: asset, + isMotionVideo: isPlayingMotionVideo.value, + placeholder: Image( + image: provider, + fit: BoxFit.fitWidth, + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + alignment: Alignment.center, ), + onVideoEnded: () { + if (isPlayingMotionVideo.value) { + isPlayingMotionVideo.value = false; + } + }, ), ); } diff --git a/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart b/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart index 37f6f9877..fb23fd3ef 100644 --- a/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart +++ b/mobile/lib/modules/asset_viewer/views/video_viewer_page.dart @@ -15,6 +15,7 @@ import 'package:video_player/video_player.dart'; class VideoViewerPage extends HookConsumerWidget { final Asset asset; final bool isMotionVideo; + final Widget? placeholder; final VoidCallback onVideoEnded; final VoidCallback? onPlaying; final VoidCallback? onPaused; @@ -26,6 +27,7 @@ class VideoViewerPage extends HookConsumerWidget { required this.onVideoEnded, this.onPlaying, this.onPaused, + this.placeholder, }) : super(key: key); @override @@ -66,6 +68,7 @@ class VideoViewerPage extends HookConsumerWidget { onVideoEnded: onVideoEnded, onPaused: onPaused, onPlaying: onPlaying, + placeholder: placeholder, ), if (downloadAssetStatus == DownloadAssetStatus.loading) const Center( @@ -95,6 +98,10 @@ class VideoPlayer extends StatefulWidget { final Function()? onPlaying; final Function()? onPaused; + /// The placeholder to show while the video is loading + /// usually, a thumbnail of the video + final Widget? placeholder; + const VideoPlayer({ Key? key, this.url, @@ -104,6 +111,7 @@ class VideoPlayer extends StatefulWidget { required this.isMotionVideo, this.onPlaying, this.onPaused, + this.placeholder, }) : super(key: key); @override @@ -186,12 +194,18 @@ class _VideoPlayerState extends State { ), ); } else { - return const Center( - child: SizedBox( - width: 75, - height: 75, - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, + return SizedBox( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + child: Center( + child: Stack( + children: [ + if (widget.placeholder != null) + widget.placeholder!, + const Center( + child: ImmichLoadingIndicator(), + ), + ], ), ), );