diff --git a/mobile/lib/pages/common/native_video_viewer.page.dart b/mobile/lib/pages/common/native_video_viewer.page.dart index ad9d53b1bb0ef..8ab0da1b44993 100644 --- a/mobile/lib/pages/common/native_video_viewer.page.dart +++ b/mobile/lib/pages/common/native_video_viewer.page.dart @@ -26,6 +26,7 @@ import 'package:wakelock_plus/wakelock_plus.dart'; class NativeVideoViewerPage extends HookConsumerWidget { final Asset asset; final bool showControls; + final int playbackDelayFactor; final Widget image; const NativeVideoViewerPage({ @@ -33,6 +34,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { required this.asset, required this.image, this.showControls = true, + this.playbackDelayFactor = 1, }); @override @@ -317,12 +319,16 @@ class NativeVideoViewerPage extends HookConsumerWidget { } // Delay the video playback to avoid a stutter in the swipe animation + // Note, in some circumstances a longer delay is needed (eg: memories), + // the playbackDelayFactor can be used for this + // This delay seems like a hacky way to resolve underlying bugs in video + // playback, but other resolutions failed thus far Timer( Platform.isIOS - ? const Duration(milliseconds: 300) + ? Duration(milliseconds: 300 * playbackDelayFactor) : imageToVideo - ? const Duration(milliseconds: 200) - : const Duration(milliseconds: 400), () { + ? Duration(milliseconds: 200 * playbackDelayFactor) + : Duration(milliseconds: 400 * playbackDelayFactor), () { if (!context.mounted) { return; } diff --git a/mobile/lib/pages/photos/memory.page.dart b/mobile/lib/pages/photos/memory.page.dart index 74a94ed6ee084..8f33d1e46363f 100644 --- a/mobile/lib/pages/photos/memory.page.dart +++ b/mobile/lib/pages/photos/memory.page.dart @@ -5,6 +5,8 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/models/memories/memory.model.dart'; +import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/widgets/common/immich_image.dart'; import 'package:immich_mobile/widgets/memories/memory_bottom_info.dart'; @@ -13,6 +15,8 @@ import 'package:immich_mobile/widgets/memories/memory_epilogue.dart'; import 'package:immich_mobile/widgets/memories/memory_progress_indicator.dart'; @RoutePage() + +/// Expects [currentAssetProvider] to be set before navigating to this page class MemoryPage extends HookConsumerWidget { final List memories; final int memoryIndex; @@ -135,6 +139,13 @@ class MemoryPage extends HookConsumerWidget { ref.read(hapticFeedbackProvider.notifier).selectionClick(); currentAssetPage.value = otherIndex; updateProgressText(); + + final asset = currentMemory.value.assets[otherIndex]; + ref.read(currentAssetProvider.notifier).set(asset); + if (asset.isVideo || asset.isMotionPhoto) { + ref.read(videoPlaybackValueProvider.notifier).reset(); + } + // Wait for page change animation to finish await Future.delayed(const Duration(milliseconds: 400)); // And then precache the next asset diff --git a/mobile/lib/widgets/memories/memory_card.dart b/mobile/lib/widgets/memories/memory_card.dart index 4954d0bfccc8d..b63a310b32944 100644 --- a/mobile/lib/widgets/memories/memory_card.dart +++ b/mobile/lib/widgets/memories/memory_card.dart @@ -75,11 +75,12 @@ class MemoryCard extends StatelessWidget { key: ValueKey(asset.id), asset: asset, showControls: false, + playbackDelayFactor: 2, image: ImmichImage( asset, width: context.width, height: context.height, - fit: fit, + fit: BoxFit.contain, ), ), ), diff --git a/mobile/lib/widgets/memories/memory_lane.dart b/mobile/lib/widgets/memories/memory_lane.dart index 41e9cc628e71a..d5b46dab5160a 100644 --- a/mobile/lib/widgets/memories/memory_lane.dart +++ b/mobile/lib/widgets/memories/memory_lane.dart @@ -4,6 +4,8 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/memories/memory.model.dart'; import 'package:immich_mobile/widgets/asset_grid/thumbnail_placeholder.dart'; +import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; import 'package:immich_mobile/providers/memory.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; @@ -33,6 +35,13 @@ class MemoryLane extends HookConsumerWidget { ), onTap: (memoryIndex) { ref.read(hapticFeedbackProvider.notifier).heavyImpact(); + if (memories[memoryIndex].assets.isNotEmpty) { + final asset = memories[memoryIndex].assets[0]; + ref.read(currentAssetProvider.notifier).set(asset); + if (asset.isVideo || asset.isMotionPhoto) { + ref.read(videoPlaybackValueProvider.notifier).reset(); + } + } context.pushRoute( MemoryRoute( memories: memories,