mirror of
https://github.com/immich-app/immich.git
synced 2025-03-01 15:11:21 +01:00

* Update current asset to play video. * Updated location of currentAssetProvider update per feedback. * Added a playbackDelayFactor to the video viewer to resolve an issue in memories. Also adjusted the scale of the memory preview image to match the ratio of the video. This still appears to jump because the video preview doesn't seem to be the first frame for some reason :\ * add video indicator --------- Co-authored-by: Tom graham <tomg@questps.com.au> Co-authored-by: Alex <alex.tran1502@gmail.com>
156 lines
4.7 KiB
Dart
156 lines
4.7 KiB
Dart
import 'dart:ui';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|
import 'package:immich_mobile/pages/common/native_video_viewer.page.dart';
|
|
import 'package:immich_mobile/utils/hooks/blurhash_hook.dart';
|
|
import 'package:immich_mobile/widgets/common/immich_image.dart';
|
|
|
|
class MemoryCard extends StatelessWidget {
|
|
final Asset asset;
|
|
final String title;
|
|
final bool showTitle;
|
|
final Function()? onVideoEnded;
|
|
|
|
const MemoryCard({
|
|
required this.asset,
|
|
required this.title,
|
|
required this.showTitle,
|
|
this.onVideoEnded,
|
|
super.key,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Card(
|
|
color: Colors.black,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(25.0),
|
|
side: const BorderSide(
|
|
color: Colors.black,
|
|
width: 1.0,
|
|
),
|
|
),
|
|
clipBehavior: Clip.hardEdge,
|
|
child: Stack(
|
|
children: [
|
|
SizedBox.expand(
|
|
child: _BlurredBackdrop(asset: asset),
|
|
),
|
|
LayoutBuilder(
|
|
builder: (context, constraints) {
|
|
// Determine the fit using the aspect ratio
|
|
BoxFit fit = BoxFit.contain;
|
|
if (asset.width != null && asset.height != null) {
|
|
final aspectRatio = asset.width! / asset.height!;
|
|
final phoneAspectRatio =
|
|
constraints.maxWidth / constraints.maxHeight;
|
|
// Look for a 25% difference in either direction
|
|
if (phoneAspectRatio * .75 < aspectRatio &&
|
|
phoneAspectRatio * 1.25 > aspectRatio) {
|
|
// Cover to look nice if we have nearly the same aspect ratio
|
|
fit = BoxFit.cover;
|
|
}
|
|
}
|
|
|
|
if (asset.isImage) {
|
|
return Hero(
|
|
tag: 'memory-${asset.id}',
|
|
child: ImmichImage(
|
|
asset,
|
|
fit: fit,
|
|
height: double.infinity,
|
|
width: double.infinity,
|
|
),
|
|
);
|
|
} else {
|
|
return Hero(
|
|
tag: 'memory-${asset.id}',
|
|
child: SizedBox(
|
|
width: context.width,
|
|
height: context.height,
|
|
child: NativeVideoViewerPage(
|
|
key: ValueKey(asset.id),
|
|
asset: asset,
|
|
showControls: false,
|
|
playbackDelayFactor: 2,
|
|
image: ImmichImage(
|
|
asset,
|
|
width: context.width,
|
|
height: context.height,
|
|
fit: BoxFit.contain,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
},
|
|
),
|
|
if (showTitle)
|
|
Positioned(
|
|
left: 18.0,
|
|
bottom: 18.0,
|
|
child: Text(
|
|
title,
|
|
style: context.textTheme.headlineMedium?.copyWith(
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _BlurredBackdrop extends HookWidget {
|
|
final Asset asset;
|
|
|
|
const _BlurredBackdrop({required this.asset});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final blurhash = useBlurHashRef(asset).value;
|
|
if (blurhash != null) {
|
|
// Use a nice cheap blur hash image decoration
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
image: DecorationImage(
|
|
image: MemoryImage(
|
|
blurhash,
|
|
),
|
|
fit: BoxFit.cover,
|
|
),
|
|
),
|
|
child: Container(
|
|
color: Colors.black.withOpacity(0.2),
|
|
),
|
|
);
|
|
} else {
|
|
// Fall back to using a more expensive image filtered
|
|
// Since the ImmichImage is already precached, we can
|
|
// safely use that as the image provider
|
|
return ImageFiltered(
|
|
imageFilter: ImageFilter.blur(sigmaX: 30, sigmaY: 30),
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
image: DecorationImage(
|
|
image: ImmichImage.imageProvider(
|
|
asset: asset,
|
|
height: context.height,
|
|
width: context.width,
|
|
),
|
|
fit: BoxFit.cover,
|
|
),
|
|
),
|
|
child: Container(
|
|
color: Colors.black.withOpacity(0.2),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|