mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
Merge branch 'main' into upload-to-album
This commit is contained in:
@@ -77,7 +77,9 @@ class RenderFixedRow extends RenderBox
|
||||
double _height;
|
||||
|
||||
set height(double value) {
|
||||
if (_height == value) return;
|
||||
if (_height == value) {
|
||||
return;
|
||||
}
|
||||
_height = value;
|
||||
markNeedsLayout();
|
||||
}
|
||||
@@ -86,7 +88,9 @@ class RenderFixedRow extends RenderBox
|
||||
List<double> _widths;
|
||||
|
||||
set widths(List<double> value) {
|
||||
if (listEquals(_widths, value)) return;
|
||||
if (listEquals(_widths, value)) {
|
||||
return;
|
||||
}
|
||||
_widths = value;
|
||||
markNeedsLayout();
|
||||
}
|
||||
@@ -95,7 +99,9 @@ class RenderFixedRow extends RenderBox
|
||||
double _spacing;
|
||||
|
||||
set spacing(double value) {
|
||||
if (_spacing == value) return;
|
||||
if (_spacing == value) {
|
||||
return;
|
||||
}
|
||||
_spacing = value;
|
||||
markNeedsLayout();
|
||||
}
|
||||
@@ -104,7 +110,9 @@ class RenderFixedRow extends RenderBox
|
||||
TextDirection _textDirection;
|
||||
|
||||
set textDirection(TextDirection value) {
|
||||
if (_textDirection == value) return;
|
||||
if (_textDirection == value) {
|
||||
return;
|
||||
}
|
||||
_textDirection = value;
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
@@ -53,14 +53,18 @@ class FixedSegment extends Segment {
|
||||
@override
|
||||
int getMinChildIndexForScrollOffset(double scrollOffset) {
|
||||
final adjustedOffset = scrollOffset - gridOffset;
|
||||
if (!adjustedOffset.isFinite || adjustedOffset < 0) return firstIndex;
|
||||
if (!adjustedOffset.isFinite || adjustedOffset < 0) {
|
||||
return firstIndex;
|
||||
}
|
||||
return gridIndex + (adjustedOffset / mainAxisExtend).floor();
|
||||
}
|
||||
|
||||
@override
|
||||
int getMaxChildIndexForScrollOffset(double scrollOffset) {
|
||||
final adjustedOffset = scrollOffset - gridOffset;
|
||||
if (!adjustedOffset.isFinite || adjustedOffset < 0) return firstIndex;
|
||||
if (!adjustedOffset.isFinite || adjustedOffset < 0) {
|
||||
return firstIndex;
|
||||
}
|
||||
return gridIndex + (adjustedOffset / mainAxisExtend).ceil() - 1;
|
||||
}
|
||||
|
||||
@@ -162,8 +166,12 @@ class _FixedSegmentRow extends ConsumerWidget {
|
||||
// 0.5: width < mean - threshold
|
||||
// 1.5: width > mean + threshold
|
||||
final arConfiguration = aspectRatios.map((e) {
|
||||
if (e - meanAspectRatio > 0.3) return 1.5;
|
||||
if (e - meanAspectRatio < -0.3) return 0.5;
|
||||
if (e - meanAspectRatio > 0.3) {
|
||||
return 1.5;
|
||||
}
|
||||
if (e - meanAspectRatio < -0.3) {
|
||||
return 0.5;
|
||||
}
|
||||
return 1.0;
|
||||
});
|
||||
|
||||
|
||||
@@ -104,7 +104,9 @@ class ScrubberState extends ConsumerState<Scrubber> with TickerProviderStateMixi
|
||||
late ScrollController _scrollController;
|
||||
|
||||
double get _currentOffset {
|
||||
if (_scrollController.hasClients != true) return 0.0;
|
||||
if (_scrollController.hasClients != true) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return _scrollController.offset * _scrubberHeight / _scrollController.position.maxScrollExtent;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,9 @@ abstract class Segment {
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return other is Segment &&
|
||||
other.firstIndex == firstIndex &&
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/setting.model.dart';
|
||||
import 'package:immich_mobile/domain/models/timeline.model.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/constants.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/fixed/segment_builder.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||
|
||||
class TimelineArgs {
|
||||
@@ -93,7 +92,7 @@ final timelineSegmentProvider = StreamProvider.autoDispose<List<Segment>>((ref)
|
||||
final availableTileWidth = args.maxWidth - (spacing * (columnCount - 1));
|
||||
final tileExtent = math.max(0, availableTileWidth) / columnCount;
|
||||
|
||||
final groupBy = args.groupBy ?? GroupAssetsBy.values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)];
|
||||
final groupBy = args.groupBy ?? ref.watch(appConfigProvider.select((config) => config.timeline.groupAssetsBy));
|
||||
|
||||
final timelineService = ref.watch(timelineServiceProvider);
|
||||
yield* timelineService.watchBuckets().map((buckets) {
|
||||
@@ -102,7 +101,7 @@ final timelineSegmentProvider = StreamProvider.autoDispose<List<Segment>>((ref)
|
||||
tileHeight: tileExtent,
|
||||
columnCount: columnCount,
|
||||
spacing: spacing,
|
||||
groupBy: groupBy,
|
||||
groupBy: groupBy!,
|
||||
).generate();
|
||||
});
|
||||
}, dependencies: [timelineServiceProvider, timelineArgsProvider]);
|
||||
|
||||
@@ -10,7 +10,7 @@ import 'package:flutter/rendering.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/events.model.dart';
|
||||
import 'package:immich_mobile/domain/models/setting.model.dart';
|
||||
import 'package:immich_mobile/domain/models/metadata_key.dart';
|
||||
import 'package:immich_mobile/domain/models/timeline.model.dart';
|
||||
import 'package:immich_mobile/domain/utils/event_stream.dart';
|
||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||
@@ -22,8 +22,8 @@ import 'package:immich_mobile/presentation/widgets/timeline/scrubber.widget.dart
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart';
|
||||
@@ -74,7 +74,7 @@ class Timeline extends StatelessWidget {
|
||||
(ref) => TimelineArgs(
|
||||
maxWidth: constraints.maxWidth,
|
||||
maxHeight: constraints.maxHeight,
|
||||
columnCount: ref.watch(settingsProvider.select((s) => s.get(Setting.tilesPerRow))),
|
||||
columnCount: ref.watch(appConfigProvider.select((config) => config.timeline.tilesPerRow)),
|
||||
showStorageIndicator: showStorageIndicator,
|
||||
withStack: withStack,
|
||||
groupBy: groupBy,
|
||||
@@ -161,7 +161,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||
_scrollController = ScrollController(onAttach: _restoreAssetPosition);
|
||||
_eventSubscription = EventStream.shared.listen(_onEvent);
|
||||
|
||||
final currentTilesPerRow = ref.read(settingsProvider).get(Setting.tilesPerRow);
|
||||
final currentTilesPerRow = ref.read(appConfigProvider.select((config) => config.timeline.tilesPerRow));
|
||||
_perRow = currentTilesPerRow;
|
||||
_scaleFactor = 7.0 - _perRow;
|
||||
_baseScaleFactor = _scaleFactor;
|
||||
@@ -201,7 +201,9 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||
}
|
||||
|
||||
void _restoreAssetPosition(_) {
|
||||
if (_restoreAssetIndex == null) return;
|
||||
if (_restoreAssetIndex == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final asyncSegments = ref.read(timelineSegmentProvider);
|
||||
asyncSegments.whenData((segments) {
|
||||
@@ -329,7 +331,9 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||
}
|
||||
|
||||
void _handleDragAssetEnter(TimelineAssetIndex index) {
|
||||
if (_dragAnchorIndex == null || !_dragging) return;
|
||||
if (_dragAnchorIndex == null || !_dragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
final timelineService = ref.read(timelineServiceProvider);
|
||||
final dragAnchorIndex = _dragAnchorIndex!;
|
||||
@@ -399,7 +403,9 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||
segments: segments,
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(ctx, index) {
|
||||
if (index >= childCount) return null;
|
||||
if (index >= childCount) {
|
||||
return null;
|
||||
}
|
||||
final segment = segments.findByIndex(index);
|
||||
return segment?.builder(ctx, index) ?? const SizedBox.shrink();
|
||||
},
|
||||
@@ -453,7 +459,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||
_restoreAssetIndex = targetAssetIndex;
|
||||
});
|
||||
|
||||
ref.read(settingsProvider.notifier).set(Setting.tilesPerRow, _perRow);
|
||||
ref.read(metadataProvider).write(MetadataKey.timelineTilesPerRow, _perRow);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
@@ -81,11 +81,15 @@ class _TimelineDragRegionState extends State<TimelineDragRegion> {
|
||||
|
||||
TimelineAssetIndex? _getValueKeyAtPosition(Offset position) {
|
||||
final box = context.findAncestorRenderObjectOfType<RenderBox>();
|
||||
if (box == null) return null;
|
||||
if (box == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final hitTestResult = BoxHitTestResult();
|
||||
final local = box.globalToLocal(position);
|
||||
if (!box.hitTest(hitTestResult, position: local)) return null;
|
||||
if (!box.hitTest(hitTestResult, position: local)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (hitTestResult.path.firstWhereOrNull((hit) => hit.target is _TimelineAssetIndexProxy)?.target
|
||||
as _TimelineAssetIndexProxy?)
|
||||
@@ -103,7 +107,9 @@ class _TimelineDragRegionState extends State<TimelineDragRegion> {
|
||||
|
||||
final initialHit = _getValueKeyAtPosition(event.globalPosition);
|
||||
anchorAsset = initialHit;
|
||||
if (initialHit == null) return;
|
||||
if (initialHit == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (anchorAsset != null) {
|
||||
widget.onStart?.call(anchorAsset!);
|
||||
@@ -117,8 +123,12 @@ class _TimelineDragRegionState extends State<TimelineDragRegion> {
|
||||
}
|
||||
|
||||
void _onLongPressMove(LongPressMoveUpdateDetails event) {
|
||||
if (anchorAsset == null) return;
|
||||
if (topScrollOffset == null || bottomScrollOffset == null) return;
|
||||
if (anchorAsset == null) {
|
||||
return;
|
||||
}
|
||||
if (topScrollOffset == null || bottomScrollOffset == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final currentDy = event.localPosition.dy;
|
||||
|
||||
@@ -138,7 +148,9 @@ class _TimelineDragRegionState extends State<TimelineDragRegion> {
|
||||
}
|
||||
|
||||
final currentlyTouchingAsset = _getValueKeyAtPosition(event.globalPosition);
|
||||
if (currentlyTouchingAsset == null) return;
|
||||
if (currentlyTouchingAsset == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (assetUnderPointer != currentlyTouchingAsset) {
|
||||
if (!scrollNotified) {
|
||||
@@ -202,7 +214,9 @@ class TimelineAssetIndex {
|
||||
|
||||
@override
|
||||
bool operator ==(covariant TimelineAssetIndex other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return other.assetIndex == assetIndex && other.segmentIndex == segmentIndex;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user