mirror of
https://github.com/immich-app/immich
synced 2026-08-15 13:03:57 +00:00
74 lines
2.9 KiB
Dart
74 lines
2.9 KiB
Dart
import 'dart:math' as math;
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
import 'package:hooks_riverpod/hooks_riverpod.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/settings.provider.dart';
|
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
|
|
|
part 'timeline.state.freezed.dart';
|
|
|
|
@freezed
|
|
abstract class TimelineArgs with _$TimelineArgs {
|
|
const factory TimelineArgs({
|
|
required double maxWidth,
|
|
required double maxHeight,
|
|
@Default(kTimelineSpacing) double spacing,
|
|
@Default(kTimelineColumnCount) int columnCount,
|
|
@Default(false) bool showStorageIndicator,
|
|
@Default(false) bool withStack,
|
|
GroupAssetsBy? groupBy,
|
|
}) = _TimelineArgs;
|
|
}
|
|
|
|
@freezed
|
|
abstract class TimelineState with _$TimelineState {
|
|
const TimelineState._();
|
|
|
|
const factory TimelineState({@Default(false) bool isScrubbing, @Default(false) bool isScrolling}) = _TimelineState;
|
|
|
|
bool get isInteracting => isScrubbing || isScrolling;
|
|
}
|
|
|
|
class TimelineStateNotifier extends Notifier<TimelineState> {
|
|
void setScrubbing(bool isScrubbing) {
|
|
state = state.copyWith(isScrubbing: isScrubbing);
|
|
}
|
|
|
|
void setScrolling(bool isScrolling) {
|
|
state = state.copyWith(isScrolling: isScrolling);
|
|
}
|
|
|
|
@override
|
|
TimelineState build() => const TimelineState(isScrubbing: false, isScrolling: false);
|
|
}
|
|
|
|
// This provider watches the buckets from the timeline service & args and serves the segments.
|
|
// It should be used only after the timeline service and timeline args provider is overridden
|
|
final timelineSegmentProvider = StreamProvider.autoDispose<List<Segment>>((ref) async* {
|
|
// maxHeight is left out on purpose, a height-only change must not restart the bucket stream
|
|
final (maxWidth, columnCount, spacing, groupByArg) = ref.watch(
|
|
timelineArgsProvider.select((args) => (args.maxWidth, args.columnCount, args.spacing, args.groupBy)),
|
|
);
|
|
final availableTileWidth = maxWidth - (spacing * (columnCount - 1));
|
|
final tileExtent = math.max(0, availableTileWidth) / columnCount;
|
|
|
|
final groupBy = groupByArg ?? ref.watch(appConfigProvider.select((config) => config.timeline.groupAssetsBy));
|
|
|
|
final timelineService = ref.watch(timelineServiceProvider);
|
|
yield* timelineService.watchBuckets().map((buckets) {
|
|
return FixedSegmentBuilder(
|
|
buckets: buckets,
|
|
tileHeight: tileExtent,
|
|
columnCount: columnCount,
|
|
spacing: spacing,
|
|
groupBy: groupBy!,
|
|
).generate();
|
|
});
|
|
}, dependencies: [timelineServiceProvider, timelineArgsProvider]);
|
|
|
|
final timelineStateProvider = NotifierProvider<TimelineStateNotifier, TimelineState>(TimelineStateNotifier.new);
|