Adds photo thumbnail to videos (#2880)

* Motion photos use placeholder image for more seamless loading

* Fixes merge conflicts
This commit is contained in:
martyfuhry 2023-06-20 17:17:43 -04:00 committed by GitHub
parent 48e4ea5231
commit 1b15b5414c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 58 additions and 36 deletions

View File

@ -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( return Scaffold(
backgroundColor: Colors.black, backgroundColor: Colors.black,
body: WillPopScope( body: WillPopScope(
@ -460,26 +480,9 @@ class GalleryViewerPage extends HookConsumerWidget {
: null, : null,
builder: (context, index) { builder: (context, index) {
final asset = loadAsset(index); final asset = loadAsset(index);
final ImageProvider provider = imageProvider(asset);
if (asset.isImage && !isPlayingMotionVideo.value) { 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( return PhotoViewGalleryPageOptions(
onDragStart: (_, details, __) => onDragStart: (_, details, __) =>
localPosition = details.localPosition, localPosition = details.localPosition,
@ -512,18 +515,23 @@ class GalleryViewerPage extends HookConsumerWidget {
maxScale: 1.0, maxScale: 1.0,
minScale: 1.0, minScale: 1.0,
basePosition: Alignment.bottomCenter, basePosition: Alignment.bottomCenter,
child: SafeArea( child: VideoViewerPage(
child: VideoViewerPage( onPlaying: () => isPlayingVideo.value = true,
onPlaying: () => isPlayingVideo.value = true, onPaused: () => isPlayingVideo.value = false,
onPaused: () => isPlayingVideo.value = false, asset: asset,
asset: asset, isMotionVideo: isPlayingMotionVideo.value,
isMotionVideo: isPlayingMotionVideo.value, placeholder: Image(
onVideoEnded: () { image: provider,
if (isPlayingMotionVideo.value) { fit: BoxFit.fitWidth,
isPlayingMotionVideo.value = false; height: MediaQuery.of(context).size.height,
} width: MediaQuery.of(context).size.width,
}, alignment: Alignment.center,
), ),
onVideoEnded: () {
if (isPlayingMotionVideo.value) {
isPlayingMotionVideo.value = false;
}
},
), ),
); );
} }

View File

@ -15,6 +15,7 @@ import 'package:video_player/video_player.dart';
class VideoViewerPage extends HookConsumerWidget { class VideoViewerPage extends HookConsumerWidget {
final Asset asset; final Asset asset;
final bool isMotionVideo; final bool isMotionVideo;
final Widget? placeholder;
final VoidCallback onVideoEnded; final VoidCallback onVideoEnded;
final VoidCallback? onPlaying; final VoidCallback? onPlaying;
final VoidCallback? onPaused; final VoidCallback? onPaused;
@ -26,6 +27,7 @@ class VideoViewerPage extends HookConsumerWidget {
required this.onVideoEnded, required this.onVideoEnded,
this.onPlaying, this.onPlaying,
this.onPaused, this.onPaused,
this.placeholder,
}) : super(key: key); }) : super(key: key);
@override @override
@ -66,6 +68,7 @@ class VideoViewerPage extends HookConsumerWidget {
onVideoEnded: onVideoEnded, onVideoEnded: onVideoEnded,
onPaused: onPaused, onPaused: onPaused,
onPlaying: onPlaying, onPlaying: onPlaying,
placeholder: placeholder,
), ),
if (downloadAssetStatus == DownloadAssetStatus.loading) if (downloadAssetStatus == DownloadAssetStatus.loading)
const Center( const Center(
@ -95,6 +98,10 @@ class VideoPlayer extends StatefulWidget {
final Function()? onPlaying; final Function()? onPlaying;
final Function()? onPaused; final Function()? onPaused;
/// The placeholder to show while the video is loading
/// usually, a thumbnail of the video
final Widget? placeholder;
const VideoPlayer({ const VideoPlayer({
Key? key, Key? key,
this.url, this.url,
@ -104,6 +111,7 @@ class VideoPlayer extends StatefulWidget {
required this.isMotionVideo, required this.isMotionVideo,
this.onPlaying, this.onPlaying,
this.onPaused, this.onPaused,
this.placeholder,
}) : super(key: key); }) : super(key: key);
@override @override
@ -186,12 +194,18 @@ class _VideoPlayerState extends State<VideoPlayer> {
), ),
); );
} else { } else {
return const Center( return SizedBox(
child: SizedBox( height: MediaQuery.of(context).size.height,
width: 75, width: MediaQuery.of(context).size.width,
height: 75, child: Center(
child: CircularProgressIndicator.adaptive( child: Stack(
strokeWidth: 2, children: [
if (widget.placeholder != null)
widget.placeholder!,
const Center(
child: ImmichLoadingIndicator(),
),
],
), ),
), ),
); );