mirror of
https://github.com/immich-app/immich.git
synced 2026-04-28 12:13:09 -07:00
Compare commits
7 Commits
debug/back
...
bugfix/liv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14193b82df | ||
|
|
5271863291 | ||
|
|
9263e2f2e1 | ||
|
|
6b3e07dc52 | ||
|
|
a3ee615c5b | ||
|
|
39cfad7136 | ||
|
|
350056dd1a |
@@ -48,14 +48,14 @@ FROM python:3.13-slim-trixie@sha256:d168b8d9eb761f4d3fe305ebd04aeb7e7f2de0297cec
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \
|
||||
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/v2.28.4/intel-igc-core-2_2.28.4+20760_amd64.deb && \
|
||||
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/v2.28.4/intel-igc-opencl-2_2.28.4+20760_amd64.deb && \
|
||||
wget -nv https://github.com/intel/compute-runtime/releases/download/26.05.37020.3/intel-opencl-icd_26.05.37020.3-0_amd64.deb && \
|
||||
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/v2.32.7/intel-igc-core-2_2.32.7+21184_amd64.deb && \
|
||||
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/v2.32.7/intel-igc-opencl-2_2.32.7+21184_amd64.deb && \
|
||||
wget -nv https://github.com/intel/compute-runtime/releases/download/26.14.37833.4/intel-opencl-icd_26.14.37833.4-0_amd64.deb && \
|
||||
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.24/intel-igc-core_1.0.17537.24_amd64.deb && \
|
||||
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.24/intel-igc-opencl_1.0.17537.24_amd64.deb && \
|
||||
wget -nv https://github.com/intel/compute-runtime/releases/download/24.35.30872.36/intel-opencl-icd-legacy1_24.35.30872.36_amd64.deb && \
|
||||
# TODO: Figure out how to get renovate to manage this differently versioned libigdgmm file
|
||||
wget -nv https://github.com/intel/compute-runtime/releases/download/26.05.37020.3/libigdgmm12_22.9.0_amd64.deb && \
|
||||
wget -nv https://github.com/intel/compute-runtime/releases/download/26.14.37833.4/libigdgmm12_22.9.0_amd64.deb && \
|
||||
dpkg -i *.deb && \
|
||||
rm *.deb && \
|
||||
apt-get remove wget -yqq && \
|
||||
|
||||
@@ -9,12 +9,12 @@ dependencies = [
|
||||
"aiocache>=0.12.1,<1.0",
|
||||
"fastapi>=0.95.2,<1.0",
|
||||
"gunicorn>=21.1.0",
|
||||
"huggingface-hub>=0.20.1,<1.0",
|
||||
"huggingface-hub>=1.0,<2.0",
|
||||
"insightface>=0.7.3,<1.0",
|
||||
"numpy<2.4.0",
|
||||
"opencv-python-headless>=4.7.0.72,<5.0",
|
||||
"orjson>=3.9.5",
|
||||
"pillow>=12.2,<12.3",
|
||||
"pillow>=12.2,<13",
|
||||
"pydantic>=2.0.0,<3",
|
||||
"pydantic-settings>=2.5.2,<3",
|
||||
"python-multipart>=0.0.6,<1.0",
|
||||
|
||||
@@ -48,6 +48,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||
|
||||
bool _showingDetails = false;
|
||||
bool _isZoomed = false;
|
||||
// Frozen during dismiss drag + settle to prevent widget tree swap mid-animation.
|
||||
bool _frozenMotionPlaying = false;
|
||||
bool _dismissSettling = false;
|
||||
|
||||
final _scrollController = SnapScrollController();
|
||||
double _snapOffset = 0.0;
|
||||
@@ -135,6 +138,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||
> 0 => _DragIntent.dismiss,
|
||||
_ => _DragIntent.none,
|
||||
};
|
||||
if (_dragIntent == _DragIntent.dismiss) {
|
||||
_frozenMotionPlaying = ref.read(isPlayingMotionVideoProvider);
|
||||
}
|
||||
}
|
||||
|
||||
switch (_dragIntent) {
|
||||
@@ -172,12 +178,18 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||
context.maybePop();
|
||||
return;
|
||||
}
|
||||
_viewController?.animateMultiple(
|
||||
position: _initialPhotoViewState.position,
|
||||
scale: _viewController?.initialScale ?? _initialPhotoViewState.scale,
|
||||
rotation: _initialPhotoViewState.rotation,
|
||||
);
|
||||
_viewer.setOpacity(1.0);
|
||||
_dismissSettling = true;
|
||||
_viewController
|
||||
?.animateMultiple(
|
||||
position: _initialPhotoViewState.position,
|
||||
scale: _viewController?.initialScale ?? _initialPhotoViewState.scale,
|
||||
rotation: _initialPhotoViewState.rotation,
|
||||
)
|
||||
.whenComplete(() {
|
||||
if (!mounted) return;
|
||||
setState(() => _dismissSettling = false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +367,10 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||
final currentHeroTag = ref.watch(assetViewerProvider.select((s) => s.currentAsset?.heroTag));
|
||||
_showingDetails = ref.watch(assetViewerProvider.select((s) => s.showingDetails));
|
||||
final stackIndex = ref.watch(assetViewerProvider.select((s) => s.stackIndex));
|
||||
final isPlayingMotionVideo = ref.watch(isPlayingMotionVideoProvider);
|
||||
final liveMotionPlaying = ref.watch(isPlayingMotionVideoProvider);
|
||||
final isPlayingMotionVideo = (_dragIntent == _DragIntent.dismiss || _dismissSettling)
|
||||
? _frozenMotionPlaying
|
||||
: liveMotionPlaying;
|
||||
|
||||
final asset = ref.read(timelineServiceProvider).getAssetSafe(widget.index);
|
||||
if (asset == null) {
|
||||
|
||||
@@ -2,17 +2,21 @@ import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/map/map.state.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
|
||||
class MapBottomSheet extends StatelessWidget {
|
||||
const MapBottomSheet({super.key});
|
||||
final Key? sheetKey;
|
||||
|
||||
const MapBottomSheet({super.key, this.sheetKey});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BaseBottomSheet(
|
||||
key: sheetKey,
|
||||
initialChildSize: 0.25,
|
||||
maxChildSize: 0.75,
|
||||
shouldCloseOnMinExtent: false,
|
||||
@@ -49,7 +53,7 @@ class _ScopedMapTimeline extends StatelessWidget {
|
||||
return timelineService;
|
||||
}),
|
||||
],
|
||||
child: const Timeline(appBar: null, bottomSheet: null, withScrubber: false),
|
||||
child: const Timeline(appBar: null, bottomSheet: GeneralBottomSheet(minChildSize: 0.23), withScrubber: false),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'package:immich_mobile/domain/utils/event_stream.dart';
|
||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/map/map.state.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/map/map_utils.dart';
|
||||
@@ -53,6 +54,7 @@ class _DriftMapState extends ConsumerState<DriftMap> {
|
||||
final _reloadMutex = AsyncMutex();
|
||||
final _debouncer = Debouncer(interval: const Duration(milliseconds: 500), maxWaitTime: const Duration(seconds: 2));
|
||||
final ValueNotifier<double> bottomSheetOffset = ValueNotifier(0.25);
|
||||
final GlobalKey _bottomSheetKey = GlobalKey();
|
||||
StreamSubscription? _eventSubscription;
|
||||
|
||||
@override
|
||||
@@ -184,7 +186,7 @@ class _DriftMapState extends ConsumerState<DriftMap> {
|
||||
return Stack(
|
||||
children: [
|
||||
_Map(initialLocation: widget.initialLocation, onMapCreated: onMapCreated, onMapReady: onMapReady),
|
||||
_DynamicBottomSheet(bottomSheetOffset: bottomSheetOffset),
|
||||
_DynamicBottomSheet(bottomSheetOffset: bottomSheetOffset, sheetKey: _bottomSheetKey),
|
||||
_DynamicMyLocationButton(onZoomToLocation: onZoomToLocation, bottomSheetOffset: bottomSheetOffset),
|
||||
],
|
||||
);
|
||||
@@ -224,8 +226,9 @@ class _Map extends StatelessWidget {
|
||||
|
||||
class _DynamicBottomSheet extends StatefulWidget {
|
||||
final ValueNotifier<double> bottomSheetOffset;
|
||||
final GlobalKey sheetKey;
|
||||
|
||||
const _DynamicBottomSheet({required this.bottomSheetOffset});
|
||||
const _DynamicBottomSheet({required this.bottomSheetOffset, required this.sheetKey});
|
||||
|
||||
@override
|
||||
State<_DynamicBottomSheet> createState() => _DynamicBottomSheetState();
|
||||
@@ -236,10 +239,13 @@ class _DynamicBottomSheetState extends State<_DynamicBottomSheet> {
|
||||
Widget build(BuildContext context) {
|
||||
return NotificationListener<DraggableScrollableNotification>(
|
||||
onNotification: (notification) {
|
||||
widget.bottomSheetOffset.value = notification.extent;
|
||||
return true;
|
||||
final sheet = notification.context.findAncestorWidgetOfExactType<BaseBottomSheet>();
|
||||
if (sheet?.key == widget.sheetKey) {
|
||||
widget.bottomSheetOffset.value = notification.extent;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: const MapBottomSheet(),
|
||||
child: MapBottomSheet(sheetKey: widget.sheetKey),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,6 +469,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||
ref.read(timelineStateProvider.notifier).setScrolling(true);
|
||||
},
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
timeline,
|
||||
if (isBottomWidgetVisible)
|
||||
|
||||
@@ -38,12 +38,13 @@ abstract class PhotoViewControllerBase<T extends PhotoViewControllerValue> {
|
||||
/// Closes streams and removes eventual listeners.
|
||||
void dispose();
|
||||
|
||||
void positionAnimationBuilder(void Function(Offset)? value);
|
||||
void scaleAnimationBuilder(void Function(double)? value);
|
||||
void rotationAnimationBuilder(void Function(double)? value);
|
||||
void positionAnimationBuilder(Future<void> Function(Offset)? value);
|
||||
void scaleAnimationBuilder(Future<void> Function(double)? value);
|
||||
void rotationAnimationBuilder(Future<void> Function(double)? value);
|
||||
|
||||
/// Animates multiple fields of the state
|
||||
void animateMultiple({Offset? position, double? scale, double? rotation});
|
||||
/// Animates multiple fields of the state. The returned future completes
|
||||
/// when all underlying animations have settled.
|
||||
Future<void> animateMultiple({Offset? position, double? scale, double? rotation});
|
||||
|
||||
/// Add a listener that will ignore updates made internally
|
||||
///
|
||||
@@ -148,9 +149,9 @@ class PhotoViewController implements PhotoViewControllerBase<PhotoViewController
|
||||
@override
|
||||
ScaleBoundaries? scaleBoundaries;
|
||||
|
||||
late void Function(Offset)? _animatePosition;
|
||||
late void Function(double)? _animateScale;
|
||||
late void Function(double)? _animateRotation;
|
||||
late Future<void> Function(Offset)? _animatePosition;
|
||||
late Future<void> Function(double)? _animateScale;
|
||||
late Future<void> Function(double)? _animateRotation;
|
||||
|
||||
@override
|
||||
Stream<PhotoViewControllerValue> get outputStateStream => _outputCtrl.stream;
|
||||
@@ -159,17 +160,17 @@ class PhotoViewController implements PhotoViewControllerBase<PhotoViewController
|
||||
late PhotoViewControllerValue prevValue;
|
||||
|
||||
@override
|
||||
void positionAnimationBuilder(void Function(Offset)? value) {
|
||||
void positionAnimationBuilder(Future<void> Function(Offset)? value) {
|
||||
_animatePosition = value;
|
||||
}
|
||||
|
||||
@override
|
||||
void scaleAnimationBuilder(void Function(double)? value) {
|
||||
void scaleAnimationBuilder(Future<void> Function(double)? value) {
|
||||
_animateScale = value;
|
||||
}
|
||||
|
||||
@override
|
||||
void rotationAnimationBuilder(void Function(double)? value) {
|
||||
void rotationAnimationBuilder(Future<void> Function(double)? value) {
|
||||
_animateRotation = value;
|
||||
}
|
||||
|
||||
@@ -193,18 +194,18 @@ class PhotoViewController implements PhotoViewControllerBase<PhotoViewController
|
||||
}
|
||||
|
||||
@override
|
||||
void animateMultiple({Offset? position, double? scale, double? rotation}) {
|
||||
Future<void> animateMultiple({Offset? position, double? scale, double? rotation}) {
|
||||
final futures = <Future<void>>[];
|
||||
if (position != null && _animatePosition != null) {
|
||||
_animatePosition!(position);
|
||||
futures.add(_animatePosition!(position));
|
||||
}
|
||||
|
||||
if (scale != null && _animateScale != null) {
|
||||
_animateScale!(scale);
|
||||
futures.add(_animateScale!(scale));
|
||||
}
|
||||
|
||||
if (rotation != null && _animateRotation != null) {
|
||||
_animateRotation!(rotation);
|
||||
futures.add(_animateRotation!(rotation));
|
||||
}
|
||||
return Future.wait(futures);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -237,34 +237,31 @@ class PhotoViewCoreState extends State<PhotoViewCore>
|
||||
nextScaleState();
|
||||
}
|
||||
|
||||
void animateScale(double from, double to) {
|
||||
Future<void> animateScale(double from, double to) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
return Future.value();
|
||||
}
|
||||
_scaleAnimation = Tween<double>(begin: from, end: to).animate(_scaleAnimationController);
|
||||
_scaleAnimationController
|
||||
..value = 0.0
|
||||
..fling(velocity: 0.4);
|
||||
_scaleAnimationController.value = 0.0;
|
||||
return _scaleAnimationController.fling(velocity: 0.4);
|
||||
}
|
||||
|
||||
void animatePosition(Offset from, Offset to) {
|
||||
Future<void> animatePosition(Offset from, Offset to) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
return Future.value();
|
||||
}
|
||||
_positionAnimation = Tween<Offset>(begin: from, end: to).animate(_positionAnimationController);
|
||||
_positionAnimationController
|
||||
..value = 0.0
|
||||
..fling(velocity: 0.4);
|
||||
_positionAnimationController.value = 0.0;
|
||||
return _positionAnimationController.fling(velocity: 0.4);
|
||||
}
|
||||
|
||||
void animateRotation(double from, double to) {
|
||||
Future<void> animateRotation(double from, double to) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
return Future.value();
|
||||
}
|
||||
_rotationAnimation = Tween<double>(begin: from, end: to).animate(_rotationAnimationController);
|
||||
_rotationAnimationController
|
||||
..value = 0.0
|
||||
..fling(velocity: 0.4);
|
||||
_rotationAnimationController.value = 0.0;
|
||||
return _rotationAnimationController.fling(velocity: 0.4);
|
||||
}
|
||||
|
||||
void onAnimationStatus(AnimationStatus status) {
|
||||
@@ -280,18 +277,19 @@ class PhotoViewCoreState extends State<PhotoViewCore>
|
||||
}
|
||||
}
|
||||
|
||||
void _animateControllerPosition(Offset position) {
|
||||
animatePosition(controller.position, position);
|
||||
Future<void> _animateControllerPosition(Offset position) {
|
||||
return animatePosition(controller.position, position);
|
||||
}
|
||||
|
||||
void _animateControllerScale(double scale) {
|
||||
Future<void> _animateControllerScale(double scale) {
|
||||
if (controller.scale != null) {
|
||||
animateScale(controller.scale!, scale);
|
||||
return animateScale(controller.scale!, scale);
|
||||
}
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
void _animateControllerRotation(double rotation) {
|
||||
animateRotation(controller.rotation, rotation);
|
||||
Future<void> _animateControllerRotation(double rotation) {
|
||||
return animateRotation(controller.rotation, rotation);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@@ -570,8 +570,8 @@ importers:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.9
|
||||
uuid:
|
||||
specifier: ^11.1.0
|
||||
version: 11.1.0
|
||||
specifier: ^14.0.0
|
||||
version: 14.0.0
|
||||
validator:
|
||||
specifier: ^13.12.0
|
||||
version: 13.15.35
|
||||
@@ -12110,6 +12110,10 @@ packages:
|
||||
resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
|
||||
hasBin: true
|
||||
|
||||
uuid@14.0.0:
|
||||
resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==}
|
||||
hasBin: true
|
||||
|
||||
uuid@8.3.2:
|
||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||
hasBin: true
|
||||
@@ -25779,6 +25783,8 @@ snapshots:
|
||||
|
||||
uuid@11.1.0: {}
|
||||
|
||||
uuid@14.0.0: {}
|
||||
|
||||
uuid@8.3.2: {}
|
||||
|
||||
validator@13.15.35: {}
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
"thumbhash": "^0.1.1",
|
||||
"transformation-matrix": "^3.1.0",
|
||||
"ua-parser-js": "^2.0.0",
|
||||
"uuid": "^11.1.0",
|
||||
"uuid": "^14.0.0",
|
||||
"validator": "^13.12.0",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user