mirror of
https://github.com/immich-app/immich.git
synced 2025-01-01 08:31:59 +00:00
use current asset provider and loadAsset
This commit is contained in:
parent
613ce513cd
commit
3b9a3d4037
6 changed files with 145 additions and 125 deletions
|
@ -67,22 +67,17 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
? ref.watch(assetStackStateProvider(currentAsset))
|
? ref.watch(assetStackStateProvider(currentAsset))
|
||||||
: <Asset>[];
|
: <Asset>[];
|
||||||
final stackElements = showStack ? [currentAsset, ...stack] : <Asset>[];
|
final stackElements = showStack ? [currentAsset, ...stack] : <Asset>[];
|
||||||
// Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
|
|
||||||
final isFromDto = currentAsset.id == noDbId;
|
|
||||||
|
|
||||||
Asset asset = stackIndex.value == -1
|
|
||||||
? currentAsset
|
|
||||||
: stackElements.elementAt(stackIndex.value);
|
|
||||||
|
|
||||||
// // Update is playing motion video
|
// // Update is playing motion video
|
||||||
if (asset.isMotionPhoto) {
|
ref.listen(
|
||||||
ref.listen(
|
videoPlaybackValueProvider.select(
|
||||||
videoPlaybackValueProvider.select(
|
(playback) => playback.state == VideoPlaybackState.playing,
|
||||||
(playback) => playback.state == VideoPlaybackState.playing,
|
), (_, isPlaying) {
|
||||||
), (_, isPlaying) {
|
final asset = ref.read(currentAssetProvider);
|
||||||
|
if (asset != null && asset.isMotionPhoto) {
|
||||||
isPlayingMotionVideo.value = isPlaying;
|
isPlayingMotionVideo.value = isPlaying;
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
Future<void> precacheNextImage(int index) async {
|
Future<void> precacheNextImage(int index) async {
|
||||||
if (!context.mounted) {
|
if (!context.mounted) {
|
||||||
|
@ -114,26 +109,29 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen provider to prevent autoDispose when navigating to other routes from within the gallery page
|
useEffect(
|
||||||
ref.listen(currentAssetProvider, (prev, cur) {});
|
() {
|
||||||
|
if (ref.read(showControlsProvider)) {
|
||||||
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||||
|
} else {
|
||||||
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() {
|
// Delay this a bit so we can finish loading the page
|
||||||
ref.read(currentAssetProvider.notifier).set(asset);
|
Timer(const Duration(milliseconds: 400), () {
|
||||||
if (ref.read(showControlsProvider)) {
|
precacheNextImage(currentIndex.value + 1);
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
});
|
||||||
} else {
|
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delay this a bit so we can finish loading the page
|
return null;
|
||||||
Timer(const Duration(milliseconds: 400), () {
|
},
|
||||||
precacheNextImage(currentIndex.value + 1);
|
[],
|
||||||
});
|
);
|
||||||
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
void showInfo() {
|
void showInfo() {
|
||||||
|
final asset = ref.read(currentAssetProvider);
|
||||||
|
if (asset == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(15.0)),
|
borderRadius: BorderRadius.all(Radius.circular(15.0)),
|
||||||
|
@ -205,7 +203,6 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
key: ValueKey(currentAsset),
|
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemCount: stackElements.length,
|
itemCount: stackElements.length,
|
||||||
|
@ -252,12 +249,6 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object getHeroTag(Asset asset) {
|
|
||||||
return isFromDto
|
|
||||||
? '${asset.remoteId}-$heroOffset'
|
|
||||||
: asset.id + heroOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
PhotoViewGalleryPageOptions buildImage(BuildContext context, Asset asset) {
|
PhotoViewGalleryPageOptions buildImage(BuildContext context, Asset asset) {
|
||||||
return PhotoViewGalleryPageOptions(
|
return PhotoViewGalleryPageOptions(
|
||||||
onDragStart: (_, details, __) {
|
onDragStart: (_, details, __) {
|
||||||
|
@ -277,10 +268,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
imageProvider: ImmichImage.imageProvider(asset: asset),
|
imageProvider: ImmichImage.imageProvider(asset: asset),
|
||||||
heroAttributes: PhotoViewHeroAttributes(
|
heroAttributes: _getHeroAttributes(asset),
|
||||||
tag: getHeroTag(asset),
|
|
||||||
transitionOnUserGestures: true,
|
|
||||||
),
|
|
||||||
filterQuality: FilterQuality.high,
|
filterQuality: FilterQuality.high,
|
||||||
tightMode: true,
|
tightMode: true,
|
||||||
minScale: PhotoViewComputedScale.contained,
|
minScale: PhotoViewComputedScale.contained,
|
||||||
|
@ -294,40 +282,33 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
PhotoViewGalleryPageOptions buildVideo(BuildContext context, Asset asset) {
|
PhotoViewGalleryPageOptions buildVideo(BuildContext context, Asset asset) {
|
||||||
// This key is to prevent the video player from being re-initialized during the hero animation
|
// This key is to prevent the video player from being re-initialized during the hero animation
|
||||||
final key = GlobalKey();
|
final key = GlobalKey();
|
||||||
final tag = getHeroTag(asset);
|
|
||||||
return PhotoViewGalleryPageOptions.customChild(
|
return PhotoViewGalleryPageOptions.customChild(
|
||||||
onDragStart: (_, details, __) =>
|
onDragStart: (_, details, __) =>
|
||||||
localPosition.value = details.localPosition,
|
localPosition.value = details.localPosition,
|
||||||
onDragUpdate: (_, details, __) => handleSwipeUpDown(details),
|
onDragUpdate: (_, details, __) => handleSwipeUpDown(details),
|
||||||
heroAttributes: PhotoViewHeroAttributes(
|
heroAttributes: _getHeroAttributes(asset),
|
||||||
tag: tag,
|
|
||||||
transitionOnUserGestures: true,
|
|
||||||
),
|
|
||||||
filterQuality: FilterQuality.high,
|
filterQuality: FilterQuality.high,
|
||||||
initialScale: 1.0,
|
initialScale: 1.0,
|
||||||
maxScale: 1.0,
|
maxScale: 1.0,
|
||||||
minScale: 1.0,
|
minScale: 1.0,
|
||||||
basePosition: Alignment.center,
|
basePosition: Alignment.center,
|
||||||
child: Hero(
|
child: SizedBox(
|
||||||
tag: tag,
|
width: context.width,
|
||||||
child: SizedBox(
|
height: context.height,
|
||||||
width: context.width,
|
child: NativeVideoViewerPage(
|
||||||
height: context.height,
|
key: key,
|
||||||
child: NativeVideoViewerPage(
|
asset: asset,
|
||||||
key: key,
|
placeholder: Image(
|
||||||
asset: asset,
|
key: ValueKey(asset),
|
||||||
placeholder: Image(
|
image: ImmichImage.imageProvider(
|
||||||
key: ValueKey(asset),
|
asset: asset,
|
||||||
image: ImmichImage.imageProvider(
|
|
||||||
asset: asset,
|
|
||||||
width: context.width,
|
|
||||||
height: context.height,
|
|
||||||
),
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
height: context.height,
|
|
||||||
width: context.width,
|
width: context.width,
|
||||||
alignment: Alignment.center,
|
height: context.height,
|
||||||
),
|
),
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
height: context.height,
|
||||||
|
width: context.width,
|
||||||
|
alignment: Alignment.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -335,7 +316,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
PhotoViewGalleryPageOptions buildAsset(BuildContext context, int index) {
|
PhotoViewGalleryPageOptions buildAsset(BuildContext context, int index) {
|
||||||
final newAsset = index == currentIndex.value ? asset : loadAsset(index);
|
final newAsset = loadAsset(index);
|
||||||
|
|
||||||
if (newAsset.isImage && !isPlayingMotionVideo.value) {
|
if (newAsset.isImage && !isPlayingMotionVideo.value) {
|
||||||
return buildImage(context, newAsset);
|
return buildImage(context, newAsset);
|
||||||
|
@ -343,6 +324,8 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
return buildVideo(context, newAsset);
|
return buildVideo(context, newAsset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info('GalleryViewerPage: Building gallery viewer page');
|
||||||
|
|
||||||
return PopScope(
|
return PopScope(
|
||||||
// Change immersive mode back to normal "edgeToEdge" mode
|
// Change immersive mode back to normal "edgeToEdge" mode
|
||||||
onPopInvokedWithResult: (didPop, _) =>
|
onPopInvokedWithResult: (didPop, _) =>
|
||||||
|
@ -352,34 +335,41 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: [
|
children: [
|
||||||
PhotoViewGallery.builder(
|
PhotoViewGallery.builder(
|
||||||
key: ValueKey(asset),
|
key: const ValueKey('gallery'),
|
||||||
scaleStateChangedCallback: (state) {
|
scaleStateChangedCallback: (state) {
|
||||||
|
final asset = ref.read(currentAssetProvider);
|
||||||
|
if (asset == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (asset.isImage && !isPlayingMotionVideo.value) {
|
if (asset.isImage && !isPlayingMotionVideo.value) {
|
||||||
isZoomed.value = state != PhotoViewScaleState.initial;
|
isZoomed.value = state != PhotoViewScaleState.initial;
|
||||||
ref.read(showControlsProvider.notifier).show =
|
ref.read(showControlsProvider.notifier).show =
|
||||||
!isZoomed.value;
|
!isZoomed.value;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// wantKeepAlive: true,
|
|
||||||
gaplessPlayback: true,
|
gaplessPlayback: true,
|
||||||
loadingBuilder: (context, event, index) => ClipRect(
|
loadingBuilder: (context, event, index) {
|
||||||
child: Stack(
|
final asset = loadAsset(index);
|
||||||
fit: StackFit.expand,
|
return ClipRect(
|
||||||
children: [
|
child: Stack(
|
||||||
BackdropFilter(
|
fit: StackFit.expand,
|
||||||
filter: ui.ImageFilter.blur(
|
children: [
|
||||||
sigmaX: 10,
|
BackdropFilter(
|
||||||
sigmaY: 10,
|
filter: ui.ImageFilter.blur(
|
||||||
|
sigmaX: 10,
|
||||||
|
sigmaY: 10,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
ImmichThumbnail(
|
||||||
ImmichThumbnail(
|
key: ValueKey(asset),
|
||||||
key: ValueKey(asset),
|
asset: asset,
|
||||||
asset: asset,
|
fit: BoxFit.contain,
|
||||||
fit: BoxFit.contain,
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
pageController: controller,
|
pageController: controller,
|
||||||
scrollPhysics: isZoomed.value
|
scrollPhysics: isZoomed.value
|
||||||
? const NeverScrollableScrollPhysics() // Don't allow paging while scrolled in
|
? const NeverScrollableScrollPhysics() // Don't allow paging while scrolled in
|
||||||
|
@ -394,8 +384,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
|
|
||||||
ref.read(hapticFeedbackProvider.notifier).selectionClick();
|
ref.read(hapticFeedbackProvider.notifier).selectionClick();
|
||||||
|
|
||||||
final newAsset =
|
final newAsset = loadAsset(value);
|
||||||
value == currentIndex.value ? asset : loadAsset(value);
|
|
||||||
|
|
||||||
currentIndex.value = value;
|
currentIndex.value = value;
|
||||||
stackIndex.value = -1;
|
stackIndex.value = -1;
|
||||||
|
@ -418,6 +407,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
child: GalleryAppBar(
|
child: GalleryAppBar(
|
||||||
|
key: const ValueKey('app-bar'),
|
||||||
showInfo: showInfo,
|
showInfo: showInfo,
|
||||||
isPlayingMotionVideo: isPlayingMotionVideo,
|
isPlayingMotionVideo: isPlayingMotionVideo,
|
||||||
),
|
),
|
||||||
|
@ -436,6 +426,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
BottomGalleryBar(
|
BottomGalleryBar(
|
||||||
|
key: const ValueKey('bottom-bar'),
|
||||||
renderList: renderList,
|
renderList: renderList,
|
||||||
totalAssets: totalAssets,
|
totalAssets: totalAssets,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
|
@ -452,4 +443,14 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
PhotoViewHeroAttributes _getHeroAttributes(Asset asset) {
|
||||||
|
return PhotoViewHeroAttributes(
|
||||||
|
tag: asset.isInDb
|
||||||
|
? asset.id + heroOffset
|
||||||
|
: '${asset.remoteId}-$heroOffset',
|
||||||
|
transitionOnUserGestures: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ import 'package:wakelock_plus/wakelock_plus.dart';
|
||||||
class NativeVideoViewerPage extends HookConsumerWidget {
|
class NativeVideoViewerPage extends HookConsumerWidget {
|
||||||
final Asset asset;
|
final Asset asset;
|
||||||
final bool showControls;
|
final bool showControls;
|
||||||
final Duration hideControlsTimer;
|
|
||||||
final Widget placeholder;
|
final Widget placeholder;
|
||||||
|
|
||||||
const NativeVideoViewerPage({
|
const NativeVideoViewerPage({
|
||||||
|
@ -32,7 +31,6 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
||||||
required this.asset,
|
required this.asset,
|
||||||
required this.placeholder,
|
required this.placeholder,
|
||||||
this.showControls = true,
|
this.showControls = true,
|
||||||
this.hideControlsTimer = const Duration(seconds: 5),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -370,6 +368,18 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
||||||
removeListeners(playerController);
|
removeListeners(playerController);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final curAsset = currentAsset.value;
|
||||||
|
if (curAsset == asset) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no need to delay video playback when swiping from an image to a video
|
||||||
|
if (curAsset != null && !curAsset.isVideo) {
|
||||||
|
currentAsset.value = value;
|
||||||
|
onPlaybackReady();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Delay the video playback to avoid a stutter in the swipe animation
|
// Delay the video playback to avoid a stutter in the swipe animation
|
||||||
Timer(const Duration(milliseconds: 300), () {
|
Timer(const Duration(milliseconds: 300), () {
|
||||||
if (!context.mounted) {
|
if (!context.mounted) {
|
||||||
|
@ -395,38 +405,30 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
||||||
log.severe('Error stopping video: $error');
|
log.severe('Error stopping video: $error');
|
||||||
});
|
});
|
||||||
|
|
||||||
controller.value = null;
|
|
||||||
WakelockPlus.disable();
|
WakelockPlus.disable();
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[videoSource],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
placeholder, // this is always under the video to avoid flickering
|
placeholder, // this is always under the video to avoid flickering
|
||||||
Center(
|
if (aspectRatio.value != null)
|
||||||
key: ValueKey('player-${asset.hashCode}'),
|
|
||||||
child: aspectRatio.value != null
|
|
||||||
? AspectRatio(
|
|
||||||
key: ValueKey(asset),
|
|
||||||
aspectRatio: aspectRatio.value!,
|
|
||||||
child: isCurrent
|
|
||||||
? NativeVideoPlayerView(
|
|
||||||
key: ValueKey(asset),
|
|
||||||
onViewReady: initController,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
if (showControls)
|
|
||||||
Center(
|
Center(
|
||||||
key: ValueKey('controls-${asset.hashCode}'),
|
key: ValueKey(asset),
|
||||||
child: CustomVideoPlayerControls(
|
child: AspectRatio(
|
||||||
hideTimerDuration: hideControlsTimer,
|
key: ValueKey(asset),
|
||||||
|
aspectRatio: aspectRatio.value!,
|
||||||
|
child: isCurrent
|
||||||
|
? NativeVideoPlayerView(
|
||||||
|
key: ValueKey(asset),
|
||||||
|
onViewReady: initController,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (showControls) const Center(child: CustomVideoPlayerControls()),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1087,7 +1087,6 @@ class NativeVideoViewerRoute extends PageRouteInfo<NativeVideoViewerRouteArgs> {
|
||||||
required Asset asset,
|
required Asset asset,
|
||||||
required Widget placeholder,
|
required Widget placeholder,
|
||||||
bool showControls = true,
|
bool showControls = true,
|
||||||
Duration hideControlsTimer = const Duration(seconds: 5),
|
|
||||||
List<PageRouteInfo>? children,
|
List<PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
NativeVideoViewerRoute.name,
|
NativeVideoViewerRoute.name,
|
||||||
|
@ -1096,7 +1095,6 @@ class NativeVideoViewerRoute extends PageRouteInfo<NativeVideoViewerRouteArgs> {
|
||||||
asset: asset,
|
asset: asset,
|
||||||
placeholder: placeholder,
|
placeholder: placeholder,
|
||||||
showControls: showControls,
|
showControls: showControls,
|
||||||
hideControlsTimer: hideControlsTimer,
|
|
||||||
),
|
),
|
||||||
initialChildren: children,
|
initialChildren: children,
|
||||||
);
|
);
|
||||||
|
@ -1112,7 +1110,6 @@ class NativeVideoViewerRoute extends PageRouteInfo<NativeVideoViewerRouteArgs> {
|
||||||
asset: args.asset,
|
asset: args.asset,
|
||||||
placeholder: args.placeholder,
|
placeholder: args.placeholder,
|
||||||
showControls: args.showControls,
|
showControls: args.showControls,
|
||||||
hideControlsTimer: args.hideControlsTimer,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1124,7 +1121,6 @@ class NativeVideoViewerRouteArgs {
|
||||||
required this.asset,
|
required this.asset,
|
||||||
required this.placeholder,
|
required this.placeholder,
|
||||||
this.showControls = true,
|
this.showControls = true,
|
||||||
this.hideControlsTimer = const Duration(seconds: 5),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final Key? key;
|
final Key? key;
|
||||||
|
@ -1135,11 +1131,9 @@ class NativeVideoViewerRouteArgs {
|
||||||
|
|
||||||
final bool showControls;
|
final bool showControls;
|
||||||
|
|
||||||
final Duration hideControlsTimer;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'NativeVideoViewerRouteArgs{key: $key, asset: $asset, placeholder: $placeholder, showControls: $showControls, hideControlsTimer: $hideControlsTimer}';
|
return 'NativeVideoViewerRouteArgs{key: $key, asset: $asset, placeholder: $placeholder, showControls: $showControls}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/collection_extensions.dart';
|
import 'package:immich_mobile/extensions/collection_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
|
import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/scroll_notifier.provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/scroll_notifier.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/asset_viewer/show_controls.provider.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/asset_drag_region.dart';
|
import 'package:immich_mobile/widgets/asset_grid/asset_drag_region.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/thumbnail_image.dart';
|
import 'package:immich_mobile/widgets/asset_grid/thumbnail_image.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/thumbnail_placeholder.dart';
|
import 'package:immich_mobile/widgets/asset_grid/thumbnail_placeholder.dart';
|
||||||
|
@ -89,6 +91,7 @@ class ImmichAssetGridViewState extends ConsumerState<ImmichAssetGridView> {
|
||||||
ScrollOffsetController();
|
ScrollOffsetController();
|
||||||
final ItemPositionsListener _itemPositionsListener =
|
final ItemPositionsListener _itemPositionsListener =
|
||||||
ItemPositionsListener.create();
|
ItemPositionsListener.create();
|
||||||
|
late final KeepAliveLink currentAssetLink;
|
||||||
|
|
||||||
/// The timestamp when the haptic feedback was last invoked
|
/// The timestamp when the haptic feedback was last invoked
|
||||||
int _hapticFeedbackTS = 0;
|
int _hapticFeedbackTS = 0;
|
||||||
|
@ -201,6 +204,12 @@ class ImmichAssetGridViewState extends ConsumerState<ImmichAssetGridView> {
|
||||||
allAssetsSelected: _allAssetsSelected,
|
allAssetsSelected: _allAssetsSelected,
|
||||||
showStack: widget.showStack,
|
showStack: widget.showStack,
|
||||||
heroOffset: widget.heroOffset,
|
heroOffset: widget.heroOffset,
|
||||||
|
onAssetTap: (asset) {
|
||||||
|
ref.read(currentAssetProvider.notifier).set(asset);
|
||||||
|
if (asset.isVideo) {
|
||||||
|
ref.read(showControlsProvider.notifier).show = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,6 +357,7 @@ class ImmichAssetGridViewState extends ConsumerState<ImmichAssetGridView> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
currentAssetLink = ref.read(currentAssetProvider.notifier).ref.keepAlive();
|
||||||
scrollToTopNotifierProvider.addListener(_scrollToTop);
|
scrollToTopNotifierProvider.addListener(_scrollToTop);
|
||||||
scrollToDateNotifierProvider.addListener(_scrollToDate);
|
scrollToDateNotifierProvider.addListener(_scrollToDate);
|
||||||
|
|
||||||
|
@ -369,6 +379,7 @@ class ImmichAssetGridViewState extends ConsumerState<ImmichAssetGridView> {
|
||||||
_itemPositionsListener.itemPositions.removeListener(_positionListener);
|
_itemPositionsListener.itemPositions.removeListener(_positionListener);
|
||||||
}
|
}
|
||||||
_itemPositionsListener.itemPositions.removeListener(_hapticsListener);
|
_itemPositionsListener.itemPositions.removeListener(_hapticsListener);
|
||||||
|
currentAssetLink.close();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -595,12 +606,13 @@ class _Section extends StatelessWidget {
|
||||||
final RenderList renderList;
|
final RenderList renderList;
|
||||||
final bool selectionActive;
|
final bool selectionActive;
|
||||||
final bool dynamicLayout;
|
final bool dynamicLayout;
|
||||||
final Function(List<Asset>) selectAssets;
|
final void Function(List<Asset>) selectAssets;
|
||||||
final Function(List<Asset>) deselectAssets;
|
final void Function(List<Asset>) deselectAssets;
|
||||||
final bool Function(List<Asset>) allAssetsSelected;
|
final bool Function(List<Asset>) allAssetsSelected;
|
||||||
final bool showStack;
|
final bool showStack;
|
||||||
final int heroOffset;
|
final int heroOffset;
|
||||||
final bool showStorageIndicator;
|
final bool showStorageIndicator;
|
||||||
|
final void Function(Asset) onAssetTap;
|
||||||
|
|
||||||
const _Section({
|
const _Section({
|
||||||
required this.section,
|
required this.section,
|
||||||
|
@ -618,6 +630,7 @@ class _Section extends StatelessWidget {
|
||||||
required this.showStack,
|
required this.showStack,
|
||||||
required this.heroOffset,
|
required this.heroOffset,
|
||||||
required this.showStorageIndicator,
|
required this.showStorageIndicator,
|
||||||
|
required this.onAssetTap,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -683,6 +696,7 @@ class _Section extends StatelessWidget {
|
||||||
selectionActive: selectionActive,
|
selectionActive: selectionActive,
|
||||||
onSelect: (asset) => selectAssets([asset]),
|
onSelect: (asset) => selectAssets([asset]),
|
||||||
onDeselect: (asset) => deselectAssets([asset]),
|
onDeselect: (asset) => deselectAssets([asset]),
|
||||||
|
onAssetTap: onAssetTap,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -724,9 +738,9 @@ class _Title extends StatelessWidget {
|
||||||
final String title;
|
final String title;
|
||||||
final List<Asset> assets;
|
final List<Asset> assets;
|
||||||
final bool selectionActive;
|
final bool selectionActive;
|
||||||
final Function(List<Asset>) selectAssets;
|
final void Function(List<Asset>) selectAssets;
|
||||||
final Function(List<Asset>) deselectAssets;
|
final void Function(List<Asset>) deselectAssets;
|
||||||
final Function(List<Asset>) allAssetsSelected;
|
final bool Function(List<Asset>) allAssetsSelected;
|
||||||
|
|
||||||
const _Title({
|
const _Title({
|
||||||
required this.title,
|
required this.title,
|
||||||
|
@ -765,8 +779,9 @@ class _AssetRow extends StatelessWidget {
|
||||||
final bool showStorageIndicator;
|
final bool showStorageIndicator;
|
||||||
final int heroOffset;
|
final int heroOffset;
|
||||||
final bool showStack;
|
final bool showStack;
|
||||||
final Function(Asset)? onSelect;
|
final void Function(Asset) onAssetTap;
|
||||||
final Function(Asset)? onDeselect;
|
final void Function(Asset)? onSelect;
|
||||||
|
final void Function(Asset)? onDeselect;
|
||||||
final bool isSelectionActive;
|
final bool isSelectionActive;
|
||||||
|
|
||||||
const _AssetRow({
|
const _AssetRow({
|
||||||
|
@ -786,6 +801,7 @@ class _AssetRow extends StatelessWidget {
|
||||||
required this.showStack,
|
required this.showStack,
|
||||||
required this.isSelectionActive,
|
required this.isSelectionActive,
|
||||||
required this.selectedAssets,
|
required this.selectedAssets,
|
||||||
|
required this.onAssetTap,
|
||||||
this.onSelect,
|
this.onSelect,
|
||||||
this.onDeselect,
|
this.onDeselect,
|
||||||
});
|
});
|
||||||
|
@ -838,6 +854,8 @@ class _AssetRow extends StatelessWidget {
|
||||||
onSelect?.call(asset);
|
onSelect?.call(asset);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
final asset = renderList.loadAsset(absoluteOffset + index);
|
||||||
|
onAssetTap(asset);
|
||||||
context.pushRoute(
|
context.pushRoute(
|
||||||
GalleryViewerRoute(
|
GalleryViewerRoute(
|
||||||
renderList: renderList,
|
renderList: renderList,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/show_controls.provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/show_controls.provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
|
||||||
|
@ -12,7 +13,7 @@ class CustomVideoPlayerControls extends HookConsumerWidget {
|
||||||
|
|
||||||
const CustomVideoPlayerControls({
|
const CustomVideoPlayerControls({
|
||||||
super.key,
|
super.key,
|
||||||
this.hideTimerDuration = const Duration(seconds: 3),
|
this.hideTimerDuration = const Duration(seconds: 5),
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -28,7 +29,12 @@ class CustomVideoPlayerControls extends HookConsumerWidget {
|
||||||
|
|
||||||
final state = ref.read(videoPlaybackValueProvider).state;
|
final state = ref.read(videoPlaybackValueProvider).state;
|
||||||
// Do not hide on paused
|
// Do not hide on paused
|
||||||
if (state != VideoPlaybackState.paused) {
|
if (state == VideoPlaybackState.paused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final asset = ref.read(currentAssetProvider);
|
||||||
|
if (asset != null && asset.isVideo) {
|
||||||
ref.read(showControlsProvider.notifier).show = false;
|
ref.read(showControlsProvider.notifier).show = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -71,7 +71,6 @@ class MemoryCard extends StatelessWidget {
|
||||||
child: NativeVideoViewerPage(
|
child: NativeVideoViewerPage(
|
||||||
key: ValueKey(asset.id),
|
key: ValueKey(asset.id),
|
||||||
asset: asset,
|
asset: asset,
|
||||||
hideControlsTimer: const Duration(seconds: 2),
|
|
||||||
showControls: false,
|
showControls: false,
|
||||||
placeholder: SizedBox.expand(
|
placeholder: SizedBox.expand(
|
||||||
child: ImmichImage(
|
child: ImmichImage(
|
||||||
|
|
Loading…
Reference in a new issue