refactor: implement suggestions

This commit is contained in:
Yaros
2026-05-08 15:59:41 +02:00
parent 7956756d38
commit 12c4ee83d6
6 changed files with 96 additions and 59 deletions
@@ -27,19 +27,20 @@ class DriftMapRepository extends DriftDatabaseRepository {
condition = condition & _db.remoteAssetEntity.isFavorite.equals(true); condition = condition & _db.remoteAssetEntity.isFavorite.equals(true);
} }
final from = options.timeRange.from; final timeRange = options.timeRange;
final to = options.timeRange.to; final hasCustomRange = timeRange.from.isSome || timeRange.to.isSome;
if (from != null || to != null) { if (hasCustomRange) {
if (from != null) { timeRange.from.ifSome((from) {
condition = condition & _db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(from); condition = condition & _db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(from);
} });
if (to != null) {
timeRange.to.ifSome((to) {
condition = condition & _db.remoteAssetEntity.createdAt.isSmallerOrEqualValue(to); condition = condition & _db.remoteAssetEntity.createdAt.isSmallerOrEqualValue(to);
} });
} else if (options.relativeDays > 0) { } else if (options.relativeDays > 0) {
final fromDate = DateTime.now().subtract(Duration(days: options.relativeDays)); final cutoffDate = DateTime.now().toUtc().subtract(Duration(days: options.relativeDays));
condition = condition & _db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(fromDate); condition = condition & _db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(cutoffDate);
} }
return condition; return condition;
@@ -552,20 +552,21 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
query.where(_db.remoteAssetEntity.isFavorite.equals(true)); query.where(_db.remoteAssetEntity.isFavorite.equals(true));
} }
final from = options.timeRange.from; final timeRange = options.timeRange;
final to = options.timeRange.to;
if (from != null || to != null) { final hasCustomRange = timeRange.from.isSome || timeRange.to.isSome;
// Use custom from/to filters
if (from != null) { if (hasCustomRange) {
timeRange.from.ifSome((from) {
query.where(_db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(from)); query.where(_db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(from));
} });
if (to != null) {
timeRange.to.ifSome((to) {
query.where(_db.remoteAssetEntity.createdAt.isSmallerOrEqualValue(to)); query.where(_db.remoteAssetEntity.createdAt.isSmallerOrEqualValue(to));
} });
} else if (options.relativeDays > 0) { } else if (options.relativeDays > 0) {
// Use relative days
final cutoffDate = DateTime.now().toUtc().subtract(Duration(days: options.relativeDays)); final cutoffDate = DateTime.now().toUtc().subtract(Duration(days: options.relativeDays));
query.where(_db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(cutoffDate)); query.where(_db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(cutoffDate));
} }
@@ -606,20 +607,21 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
query.where(_db.remoteAssetEntity.isFavorite.equals(true)); query.where(_db.remoteAssetEntity.isFavorite.equals(true));
} }
final from = options.timeRange.from; final timeRange = options.timeRange;
final to = options.timeRange.to;
if (from != null || to != null) { final hasCustomRange = timeRange.from.isSome || timeRange.to.isSome;
// Use custom from/to filters
if (from != null) { if (hasCustomRange) {
timeRange.from.ifSome((from) {
query.where(_db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(from)); query.where(_db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(from));
} });
if (to != null) {
timeRange.to.ifSome((to) {
query.where(_db.remoteAssetEntity.createdAt.isSmallerOrEqualValue(to)); query.where(_db.remoteAssetEntity.createdAt.isSmallerOrEqualValue(to));
} });
} else if (options.relativeDays > 0) { } else if (options.relativeDays > 0) {
// Use relative days
final cutoffDate = DateTime.now().toUtc().subtract(Duration(days: options.relativeDays)); final cutoffDate = DateTime.now().toUtc().subtract(Duration(days: options.relativeDays));
query.where(_db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(cutoffDate)); query.where(_db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(cutoffDate));
} }
@@ -7,15 +7,16 @@ import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/infrastructure/map.provider.dart'; import 'package:immich_mobile/providers/infrastructure/map.provider.dart';
import 'package:immich_mobile/providers/map/map_state.provider.dart'; import 'package:immich_mobile/providers/map/map_state.provider.dart';
import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/utils/option.dart';
import 'package:maplibre_gl/maplibre_gl.dart'; import 'package:maplibre_gl/maplibre_gl.dart';
class TimeRange { class TimeRange {
final DateTime? from; final Option<DateTime> from;
final DateTime? to; final Option<DateTime> to;
const TimeRange({this.from, this.to}); const TimeRange({this.from = const None(), this.to = const None()});
TimeRange copyWith({DateTime? from, DateTime? to}) { TimeRange copyWith({Option<DateTime>? from, Option<DateTime>? to}) {
return TimeRange(from: from ?? this.from, to: to ?? this.to); return TimeRange(from: from ?? this.from, to: to ?? this.to);
} }
@@ -75,6 +76,7 @@ class MapState {
onlyFavorites: onlyFavorites, onlyFavorites: onlyFavorites,
includeArchived: includeArchived, includeArchived: includeArchived,
withPartners: withPartners, withPartners: withPartners,
relativeDays: relativeDays,
timeRange: timeRange, timeRange: timeRange,
); );
} }
@@ -123,31 +125,40 @@ class MapStateNotifier extends Notifier<MapState> {
} }
void setTimeRange(TimeRange range) { void setTimeRange(TimeRange range) {
ref final from = range.from.unwrapOrNull;
.read(appSettingsServiceProvider) final to = range.to.unwrapOrNull;
.setSetting(AppSettingsEnum.mapCustomFrom, range.from == null ? "" : range.from!.toIso8601String());
ref ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapCustomFrom, from?.toIso8601String() ?? "");
.read(appSettingsServiceProvider) ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapCustomTo, to?.toIso8601String() ?? "");
.setSetting(AppSettingsEnum.mapCustomTo, range.to == null ? "" : range.to!.toIso8601String());
state = state.copyWith(timeRange: range); state = state.copyWith(timeRange: range);
EventStream.shared.emit(const MapMarkerReloadEvent()); EventStream.shared.emit(const MapMarkerReloadEvent());
} }
Option<DateTime> parseDateOption(String s) {
try {
if (s.trim().isEmpty) return const Option.none();
return Option.some(DateTime.parse(s));
} catch (_) {
return const Option.none();
}
}
@override @override
MapState build() { MapState build() {
final appSettingsService = ref.read(appSettingsServiceProvider); final appSettingsService = ref.read(appSettingsServiceProvider);
final customFrom = appSettingsService.getSetting(AppSettingsEnum.mapCustomFrom);
final customTo = appSettingsService.getSetting(AppSettingsEnum.mapCustomTo); final customFrom = appSettingsService.getSetting(AppSettingsEnum.mapCustomFrom).toOption().flatMap(parseDateOption);
final customTo = appSettingsService.getSetting(AppSettingsEnum.mapCustomTo).toOption().flatMap(parseDateOption);
return MapState( return MapState(
themeMode: ThemeMode.values[appSettingsService.getSetting(AppSettingsEnum.mapThemeMode)], themeMode: ThemeMode.values[appSettingsService.getSetting(AppSettingsEnum.mapThemeMode)],
onlyFavorites: appSettingsService.getSetting(AppSettingsEnum.mapShowFavoriteOnly), onlyFavorites: appSettingsService.getSetting(AppSettingsEnum.mapShowFavoriteOnly),
includeArchived: appSettingsService.getSetting(AppSettingsEnum.mapIncludeArchived), includeArchived: appSettingsService.getSetting(AppSettingsEnum.mapIncludeArchived),
withPartners: appSettingsService.getSetting(AppSettingsEnum.mapwithPartners), withPartners: appSettingsService.getSetting(AppSettingsEnum.mapwithPartners),
bounds: LatLngBounds(northeast: const LatLng(0, 0), southwest: const LatLng(0, 0)), bounds: LatLngBounds(northeast: const LatLng(0, 0), southwest: const LatLng(0, 0)),
timeRange: TimeRange( relativeDays: appSettingsService.getSetting(AppSettingsEnum.mapRelativeDate),
from: customFrom.isNotEmpty ? DateTime.parse(customFrom) : null, timeRange: TimeRange(from: customFrom, to: customTo),
to: customTo.isNotEmpty ? DateTime.parse(customTo) : null,
),
); );
} }
} }
@@ -1,6 +1,7 @@
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/extensions/translate_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/map/map.state.dart'; import 'package:immich_mobile/presentation/widgets/map/map.state.dart';
import 'package:immich_mobile/widgets/map/map_settings/map_custom_time_range.dart'; import 'package:immich_mobile/widgets/map/map_settings/map_custom_time_range.dart';
import 'package:immich_mobile/widgets/map/map_settings/map_settings_list_tile.dart'; import 'package:immich_mobile/widgets/map/map_settings/map_settings_list_tile.dart';
@@ -22,7 +23,7 @@ class _DriftMapSettingsSheetState extends ConsumerState<DriftMapSettingsSheet> {
super.initState(); super.initState();
final mapState = ref.read(mapStateProvider); final mapState = ref.read(mapStateProvider);
final timeRange = mapState.timeRange; final timeRange = mapState.timeRange;
useCustomRange = timeRange.from != null || timeRange.to != null; useCustomRange = timeRange.from.isSome || timeRange.to.isSome;
} }
@override @override
@@ -78,7 +79,7 @@ class _DriftMapSettingsSheetState extends ConsumerState<DriftMapSettingsSheet> {
ref.read(mapStateProvider.notifier).setRelativeTime(0); ref.read(mapStateProvider.notifier).setRelativeTime(0);
ref.read(mapStateProvider.notifier).setTimeRange(const TimeRange()); ref.read(mapStateProvider.notifier).setTimeRange(const TimeRange());
}), }),
child: Text("remove_custom_date_range".t(context: context)), child: Text(context.t.remove_custom_date_range),
), ),
), ),
] else ...[ ] else ...[
@@ -94,7 +95,7 @@ class _DriftMapSettingsSheetState extends ConsumerState<DriftMapSettingsSheet> {
ref.read(mapStateProvider.notifier).setRelativeTime(0); ref.read(mapStateProvider.notifier).setRelativeTime(0);
ref.read(mapStateProvider.notifier).setTimeRange(const TimeRange()); ref.read(mapStateProvider.notifier).setTimeRange(const TimeRange());
}), }),
child: Text("use_custom_date_range".t(context: context)), child: Text(context.t.use_custom_date_range),
), ),
), ),
], ],
+15
View File
@@ -24,6 +24,21 @@ sealed class Option<T> {
None() => onNone(), None() => onNone(),
}; };
Option<U> flatMap<U>(Option<U> Function(T value) f) => switch (this) {
Some(:final value) => f(value),
None() => const Option.none(),
};
void ifSome(void Function(T value) action) {
switch (this) {
case Some(:final value):
action(value);
break;
case None():
break;
}
}
@override @override
String toString() => switch (this) { String toString() => switch (this) {
Some(:final value) => 'Some($value)', Some(:final value) => 'Some($value)',
@@ -1,6 +1,8 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/map/map.state.dart'; import 'package:immich_mobile/presentation/widgets/map/map.state.dart';
import 'package:immich_mobile/utils/option.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
class MapTimeRange extends StatelessWidget { class MapTimeRange extends StatelessWidget {
@@ -15,44 +17,49 @@ class MapTimeRange extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
ListTile( ListTile(
title: Text("date_after".t(context: context)), title: Text(context.t.date_after),
subtitle: Text( subtitle: Text(
timeRange.from != null timeRange.from.fold((from) => DateFormat.yMMMd().add_jm().format(from), () => context.t.not_set),
? DateFormat.yMMMd().add_jm().format(timeRange.from!)
: "not_set".t(context: context),
), ),
trailing: timeRange.from != null trailing: timeRange.from.isSome
? IconButton(icon: const Icon(Icons.close), onPressed: () => onChanged(timeRange.clearFrom())) ? IconButton(icon: const Icon(Icons.close), onPressed: () => onChanged(timeRange.clearFrom()))
: null, : null,
onTap: () async { onTap: () async {
final initial = timeRange.from.unwrapOrNull ?? DateTime.now();
final picked = await showDatePicker( final picked = await showDatePicker(
context: context, context: context,
initialDate: timeRange.from ?? DateTime.now(), initialDate: initial,
firstDate: DateTime(1970), firstDate: DateTime(1970),
lastDate: DateTime.now(), lastDate: DateTime.now(),
); );
if (picked != null) { if (picked != null) {
onChanged(timeRange.copyWith(from: picked)); onChanged(timeRange.copyWith(from: Option.some(picked)));
} }
}, },
), ),
ListTile( ListTile(
title: Text("date_before".t(context: context)), title: Text(context.t.date_before),
subtitle: Text( subtitle: Text(
timeRange.to != null ? DateFormat.yMMMd().add_jm().format(timeRange.to!) : "not_set".t(context: context), timeRange.to.fold<String>((to) => DateFormat.yMMMd().add_jm().format(to), () => context.t.not_set),
), ),
trailing: timeRange.to != null trailing: timeRange.to.isSome
? IconButton(icon: const Icon(Icons.close), onPressed: () => onChanged(timeRange.clearTo())) ? IconButton(icon: const Icon(Icons.close), onPressed: () => onChanged(timeRange.clearTo()))
: null, : null,
onTap: () async { onTap: () async {
final initial = timeRange.to.unwrapOrNull ?? DateTime.now();
final picked = await showDatePicker( final picked = await showDatePicker(
context: context, context: context,
initialDate: timeRange.to ?? DateTime.now(), initialDate: initial,
firstDate: DateTime(1970), firstDate: DateTime(1970),
lastDate: DateTime.now(), lastDate: DateTime.now(),
); );
if (picked != null) { if (picked != null) {
onChanged(timeRange.copyWith(to: picked)); onChanged(timeRange.copyWith(to: Option.some(picked)));
} }
}, },
), ),