mirror of
https://github.com/immich-app/immich.git
synced 2025-01-01 08:31:59 +00:00
fix controls when swiping between image and video
This commit is contained in:
parent
190dbb0042
commit
0010eda67f
6 changed files with 51 additions and 45 deletions
|
@ -168,6 +168,9 @@ class Asset {
|
||||||
@ignore
|
@ignore
|
||||||
bool get isImage => type == AssetType.image;
|
bool get isImage => type == AssetType.image;
|
||||||
|
|
||||||
|
@ignore
|
||||||
|
bool get isVideo => type == AssetType.video;
|
||||||
|
|
||||||
@ignore
|
@ignore
|
||||||
bool get isMotionPhoto => livePhotoVideoId != null;
|
bool get isMotionPhoto => livePhotoVideoId != null;
|
||||||
|
|
||||||
|
|
|
@ -79,10 +79,8 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
ref.listen(
|
ref.listen(
|
||||||
videoPlaybackValueProvider.select(
|
videoPlaybackValueProvider.select(
|
||||||
(playback) => playback.state == VideoPlaybackState.playing,
|
(playback) => playback.state == VideoPlaybackState.playing,
|
||||||
), (wasPlaying, isPlaying) {
|
), (_, isPlaying) {
|
||||||
if (wasPlaying != null && wasPlaying && !isPlaying) {
|
isPlayingMotionVideo.value = isPlaying;
|
||||||
isPlayingMotionVideo.value = false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,11 +269,13 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
onTapDown: (_, __, ___) {
|
onTapDown: (_, __, ___) {
|
||||||
ref.read(showControlsProvider.notifier).toggle();
|
ref.read(showControlsProvider.notifier).toggle();
|
||||||
},
|
},
|
||||||
onLongPressStart: (_, __, ___) {
|
onLongPressStart: asset.isMotionPhoto
|
||||||
if (asset.livePhotoVideoId != null) {
|
? (_, __, ___) {
|
||||||
isPlayingMotionVideo.value = true;
|
if (asset.isMotionPhoto) {
|
||||||
}
|
isPlayingMotionVideo.value = true;
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
: null,
|
||||||
imageProvider: ImmichImage.imageProvider(asset: asset),
|
imageProvider: ImmichImage.imageProvider(asset: asset),
|
||||||
heroAttributes: PhotoViewHeroAttributes(
|
heroAttributes: PhotoViewHeroAttributes(
|
||||||
tag: getHeroTag(asset),
|
tag: getHeroTag(asset),
|
||||||
|
@ -336,10 +336,6 @@ 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 = index == currentIndex.value ? asset : loadAsset(index);
|
||||||
|
|
||||||
if (newAsset.isImage) {
|
|
||||||
ref.read(showControlsProvider.notifier).show = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newAsset.isImage && !isPlayingMotionVideo.value) {
|
if (newAsset.isImage && !isPlayingMotionVideo.value) {
|
||||||
return buildImage(context, newAsset);
|
return buildImage(context, newAsset);
|
||||||
}
|
}
|
||||||
|
@ -357,8 +353,11 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
PhotoViewGallery.builder(
|
PhotoViewGallery.builder(
|
||||||
key: ValueKey(asset),
|
key: ValueKey(asset),
|
||||||
scaleStateChangedCallback: (state) {
|
scaleStateChangedCallback: (state) {
|
||||||
isZoomed.value = state != PhotoViewScaleState.initial;
|
if (asset.isImage && !isPlayingMotionVideo.value) {
|
||||||
ref.read(showControlsProvider.notifier).show = !isZoomed.value;
|
isZoomed.value = state != PhotoViewScaleState.initial;
|
||||||
|
ref.read(showControlsProvider.notifier).show =
|
||||||
|
!isZoomed.value;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// wantKeepAlive: true,
|
// wantKeepAlive: true,
|
||||||
gaplessPlayback: true,
|
gaplessPlayback: true,
|
||||||
|
@ -396,15 +395,15 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
|
|
||||||
final newAsset =
|
final newAsset =
|
||||||
value == currentIndex.value ? asset : loadAsset(value);
|
value == currentIndex.value ? asset : loadAsset(value);
|
||||||
if (!newAsset.isImage || newAsset.isMotionPhoto) {
|
|
||||||
ref.read(videoPlaybackValueProvider.notifier).reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
currentIndex.value = value;
|
currentIndex.value = value;
|
||||||
stackIndex.value = -1;
|
stackIndex.value = -1;
|
||||||
isPlayingMotionVideo.value = false;
|
isPlayingMotionVideo.value = false;
|
||||||
|
|
||||||
ref.read(currentAssetProvider.notifier).set(newAsset);
|
ref.read(currentAssetProvider.notifier).set(newAsset);
|
||||||
|
if (newAsset.isVideo || newAsset.isMotionPhoto) {
|
||||||
|
ref.read(videoPlaybackValueProvider.notifier).reset();
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for page change animation to finish, then precache the next image
|
// Wait for page change animation to finish, then precache the next image
|
||||||
Timer(const Duration(milliseconds: 400), () {
|
Timer(const Duration(milliseconds: 400), () {
|
||||||
|
@ -418,11 +417,8 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
child: GalleryAppBar(
|
child: GalleryAppBar(
|
||||||
asset: asset,
|
|
||||||
showInfo: showInfo,
|
showInfo: showInfo,
|
||||||
isPlayingVideo: isPlayingMotionVideo.value,
|
isPlayingMotionVideo: isPlayingMotionVideo,
|
||||||
onToggleMotionVideo: () =>
|
|
||||||
isPlayingMotionVideo.value = !isPlayingMotionVideo.value,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
|
@ -444,10 +440,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
controller: controller,
|
controller: controller,
|
||||||
showStack: showStack,
|
showStack: showStack,
|
||||||
stackIndex: stackIndex.value,
|
stackIndex: stackIndex.value,
|
||||||
asset: asset,
|
|
||||||
assetIndex: currentIndex,
|
assetIndex: currentIndex,
|
||||||
showVideoPlayerControls:
|
|
||||||
!asset.isImage && !asset.isMotionPhoto,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -184,8 +184,8 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
||||||
try {
|
try {
|
||||||
if (mute && playbackInfo.volume != 0.0) {
|
if (mute && playbackInfo.volume != 0.0) {
|
||||||
await playerController.setVolume(0.0);
|
await playerController.setVolume(0.0);
|
||||||
} else if (!mute && playbackInfo.volume != 0.7) {
|
} else if (!mute && playbackInfo.volume != 0.9) {
|
||||||
await playerController.setVolume(0.7);
|
await playerController.setVolume(0.9);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.severe('Error setting volume: $error');
|
log.severe('Error setting volume: $error');
|
||||||
|
|
|
@ -10,6 +10,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/asset_stack.provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/asset_stack.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/download.provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/download.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/services/stack.service.dart';
|
import 'package:immich_mobile/services/stack.service.dart';
|
||||||
|
@ -26,12 +27,10 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||||
import 'package:immich_mobile/pages/editing/edit.page.dart';
|
import 'package:immich_mobile/pages/editing/edit.page.dart';
|
||||||
|
|
||||||
class BottomGalleryBar extends ConsumerWidget {
|
class BottomGalleryBar extends ConsumerWidget {
|
||||||
final Asset asset;
|
|
||||||
final ValueNotifier<int> assetIndex;
|
final ValueNotifier<int> assetIndex;
|
||||||
final bool showStack;
|
final bool showStack;
|
||||||
final int stackIndex;
|
final int stackIndex;
|
||||||
final ValueNotifier<int> totalAssets;
|
final ValueNotifier<int> totalAssets;
|
||||||
final bool showVideoPlayerControls;
|
|
||||||
final PageController controller;
|
final PageController controller;
|
||||||
final RenderList renderList;
|
final RenderList renderList;
|
||||||
|
|
||||||
|
@ -39,17 +38,20 @@ class BottomGalleryBar extends ConsumerWidget {
|
||||||
super.key,
|
super.key,
|
||||||
required this.showStack,
|
required this.showStack,
|
||||||
required this.stackIndex,
|
required this.stackIndex,
|
||||||
required this.asset,
|
|
||||||
required this.assetIndex,
|
required this.assetIndex,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
required this.totalAssets,
|
required this.totalAssets,
|
||||||
required this.showVideoPlayerControls,
|
|
||||||
required this.renderList,
|
required this.renderList,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final asset = ref.watch(currentAssetProvider);
|
||||||
|
if (asset == null) {
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
final isOwner = asset.ownerId == ref.watch(currentUserProvider)?.isarId;
|
final isOwner = asset.ownerId == ref.watch(currentUserProvider)?.isarId;
|
||||||
|
final showControls = ref.watch(showControlsProvider);
|
||||||
|
|
||||||
final stackItems = showStack && asset.stackCount > 0
|
final stackItems = showStack && asset.stackCount > 0
|
||||||
? ref.watch(assetStackStateProvider(asset))
|
? ref.watch(assetStackStateProvider(asset))
|
||||||
|
@ -324,7 +326,7 @@ class BottomGalleryBar extends ConsumerWidget {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
return IgnorePointer(
|
return IgnorePointer(
|
||||||
ignoring: !ref.watch(showControlsProvider),
|
ignoring: !showControls,
|
||||||
child: AnimatedOpacity(
|
child: AnimatedOpacity(
|
||||||
duration: const Duration(milliseconds: 100),
|
duration: const Duration(milliseconds: 100),
|
||||||
opacity: ref.watch(showControlsProvider) ? 1.0 : 0.0,
|
opacity: ref.watch(showControlsProvider) ? 1.0 : 0.0,
|
||||||
|
@ -341,7 +343,7 @@ class BottomGalleryBar extends ConsumerWidget {
|
||||||
padding: EdgeInsets.only(top: 40.0),
|
padding: EdgeInsets.only(top: 40.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
if (showVideoPlayerControls) const VideoControls(),
|
if (asset.isVideo) const VideoControls(),
|
||||||
BottomNavigationBar(
|
BottomNavigationBar(
|
||||||
elevation: 0.0,
|
elevation: 0.0,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
|
|
|
@ -17,10 +17,15 @@ class CustomVideoPlayerControls extends HookConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final showControls = ref.watch(showControlsProvider);
|
||||||
// A timer to hide the controls
|
// A timer to hide the controls
|
||||||
final hideTimer = useTimer(
|
final hideTimer = useTimer(
|
||||||
hideTimerDuration,
|
hideTimerDuration,
|
||||||
() {
|
() {
|
||||||
|
if (!context.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
@ -66,7 +71,7 @@ class CustomVideoPlayerControls extends HookConsumerWidget {
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
onTap: showControlsAndStartHideTimer,
|
onTap: showControlsAndStartHideTimer,
|
||||||
child: AbsorbPointer(
|
child: AbsorbPointer(
|
||||||
absorbing: !ref.watch(showControlsProvider),
|
absorbing: !showControls,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
if (showBuffering)
|
if (showBuffering)
|
||||||
|
@ -84,7 +89,7 @@ class CustomVideoPlayerControls extends HookConsumerWidget {
|
||||||
iconColor: Colors.white,
|
iconColor: Colors.white,
|
||||||
isFinished: state == VideoPlaybackState.completed,
|
isFinished: state == VideoPlaybackState.completed,
|
||||||
isPlaying: state == VideoPlaybackState.playing,
|
isPlaying: state == VideoPlaybackState.playing,
|
||||||
show: ref.watch(showControlsProvider),
|
show: showControls,
|
||||||
onPressed: togglePlay,
|
onPressed: togglePlay,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
|
||||||
import 'package:immich_mobile/widgets/album/add_to_album_bottom_sheet.dart';
|
import 'package:immich_mobile/widgets/album/add_to_album_bottom_sheet.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/download.provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/download.provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/show_controls.provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/show_controls.provider.dart';
|
||||||
|
@ -19,23 +20,24 @@ import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||||
|
|
||||||
class GalleryAppBar extends ConsumerWidget {
|
class GalleryAppBar extends ConsumerWidget {
|
||||||
final Asset asset;
|
|
||||||
final void Function() showInfo;
|
final void Function() showInfo;
|
||||||
final void Function() onToggleMotionVideo;
|
final ValueNotifier<bool> isPlayingMotionVideo;
|
||||||
final bool isPlayingVideo;
|
|
||||||
|
|
||||||
const GalleryAppBar({
|
const GalleryAppBar({
|
||||||
super.key,
|
super.key,
|
||||||
required this.asset,
|
|
||||||
required this.showInfo,
|
required this.showInfo,
|
||||||
required this.onToggleMotionVideo,
|
required this.isPlayingMotionVideo,
|
||||||
required this.isPlayingVideo,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final asset = ref.watch(currentAssetProvider);
|
||||||
|
if (asset == null) {
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
final album = ref.watch(currentAlbumProvider);
|
final album = ref.watch(currentAlbumProvider);
|
||||||
final isOwner = asset.ownerId == ref.watch(currentUserProvider)?.isarId;
|
final isOwner = asset.ownerId == ref.watch(currentUserProvider)?.isarId;
|
||||||
|
final showControls = ref.watch(showControlsProvider);
|
||||||
|
|
||||||
final isPartner = ref
|
final isPartner = ref
|
||||||
.watch(partnerSharedWithProvider)
|
.watch(partnerSharedWithProvider)
|
||||||
|
@ -98,23 +100,24 @@ class GalleryAppBar extends ConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
return IgnorePointer(
|
return IgnorePointer(
|
||||||
ignoring: !ref.watch(showControlsProvider),
|
ignoring: !showControls,
|
||||||
child: AnimatedOpacity(
|
child: AnimatedOpacity(
|
||||||
duration: const Duration(milliseconds: 100),
|
duration: const Duration(milliseconds: 100),
|
||||||
opacity: ref.watch(showControlsProvider) ? 1.0 : 0.0,
|
opacity: showControls ? 1.0 : 0.0,
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.black.withOpacity(0.4),
|
color: Colors.black.withOpacity(0.4),
|
||||||
child: TopControlAppBar(
|
child: TopControlAppBar(
|
||||||
isOwner: isOwner,
|
isOwner: isOwner,
|
||||||
isPartner: isPartner,
|
isPartner: isPartner,
|
||||||
isPlayingMotionVideo: isPlayingVideo,
|
isPlayingMotionVideo: isPlayingMotionVideo.value,
|
||||||
asset: asset,
|
asset: asset,
|
||||||
onMoreInfoPressed: showInfo,
|
onMoreInfoPressed: showInfo,
|
||||||
onFavorite: toggleFavorite,
|
onFavorite: toggleFavorite,
|
||||||
onRestorePressed: () => handleRestore(asset),
|
onRestorePressed: () => handleRestore(asset),
|
||||||
onUploadPressed: asset.isLocal ? () => handleUpload(asset) : null,
|
onUploadPressed: asset.isLocal ? () => handleUpload(asset) : null,
|
||||||
onDownloadPressed: asset.isLocal ? null : handleDownloadAsset,
|
onDownloadPressed: asset.isLocal ? null : handleDownloadAsset,
|
||||||
onToggleMotionVideo: onToggleMotionVideo,
|
onToggleMotionVideo: () =>
|
||||||
|
isPlayingMotionVideo.value = !isPlayingMotionVideo.value,
|
||||||
onAddToAlbumPressed: () => addToAlbum(asset),
|
onAddToAlbumPressed: () => addToAlbum(asset),
|
||||||
onActivitiesPressed: handleActivities,
|
onActivitiesPressed: handleActivities,
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in a new issue