From 12c4ee83d61f6c1080d6db7401bfb56b2cc85cc2 Mon Sep 17 00:00:00 2001 From: Yaros Date: Fri, 8 May 2026 15:59:41 +0200 Subject: [PATCH] refactor: implement suggestions --- .../repositories/map.repository.dart | 19 ++++---- .../repositories/timeline.repository.dart | 38 ++++++++-------- .../presentation/widgets/map/map.state.dart | 43 ++++++++++++------- .../widgets/map/map_settings_sheet.dart | 7 +-- mobile/lib/utils/option.dart | 15 +++++++ .../map_settings/map_custom_time_range.dart | 33 ++++++++------ 6 files changed, 96 insertions(+), 59 deletions(-) diff --git a/mobile/lib/infrastructure/repositories/map.repository.dart b/mobile/lib/infrastructure/repositories/map.repository.dart index a7652d0678..9de0ca722d 100644 --- a/mobile/lib/infrastructure/repositories/map.repository.dart +++ b/mobile/lib/infrastructure/repositories/map.repository.dart @@ -27,19 +27,20 @@ class DriftMapRepository extends DriftDatabaseRepository { condition = condition & _db.remoteAssetEntity.isFavorite.equals(true); } - final from = options.timeRange.from; - final to = options.timeRange.to; + final timeRange = options.timeRange; + final hasCustomRange = timeRange.from.isSome || timeRange.to.isSome; - if (from != null || to != null) { - if (from != null) { + if (hasCustomRange) { + timeRange.from.ifSome((from) { condition = condition & _db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(from); - } - if (to != null) { + }); + + timeRange.to.ifSome((to) { condition = condition & _db.remoteAssetEntity.createdAt.isSmallerOrEqualValue(to); - } + }); } else if (options.relativeDays > 0) { - final fromDate = DateTime.now().subtract(Duration(days: options.relativeDays)); - condition = condition & _db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(fromDate); + final cutoffDate = DateTime.now().toUtc().subtract(Duration(days: options.relativeDays)); + condition = condition & _db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(cutoffDate); } return condition; diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index 291f111913..ac6ccf37ef 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -552,20 +552,21 @@ class DriftTimelineRepository extends DriftDatabaseRepository { query.where(_db.remoteAssetEntity.isFavorite.equals(true)); } - final from = options.timeRange.from; - final to = options.timeRange.to; + final timeRange = options.timeRange; - if (from != null || to != null) { - // Use custom from/to filters - if (from != null) { + final hasCustomRange = timeRange.from.isSome || timeRange.to.isSome; + + if (hasCustomRange) { + timeRange.from.ifSome((from) { query.where(_db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(from)); - } - if (to != null) { + }); + + timeRange.to.ifSome((to) { query.where(_db.remoteAssetEntity.createdAt.isSmallerOrEqualValue(to)); - } + }); } else if (options.relativeDays > 0) { - // Use relative days final cutoffDate = DateTime.now().toUtc().subtract(Duration(days: options.relativeDays)); + query.where(_db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(cutoffDate)); } @@ -606,20 +607,21 @@ class DriftTimelineRepository extends DriftDatabaseRepository { query.where(_db.remoteAssetEntity.isFavorite.equals(true)); } - final from = options.timeRange.from; - final to = options.timeRange.to; + final timeRange = options.timeRange; - if (from != null || to != null) { - // Use custom from/to filters - if (from != null) { + final hasCustomRange = timeRange.from.isSome || timeRange.to.isSome; + + if (hasCustomRange) { + timeRange.from.ifSome((from) { query.where(_db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(from)); - } - if (to != null) { + }); + + timeRange.to.ifSome((to) { query.where(_db.remoteAssetEntity.createdAt.isSmallerOrEqualValue(to)); - } + }); } else if (options.relativeDays > 0) { - // Use relative days final cutoffDate = DateTime.now().toUtc().subtract(Duration(days: options.relativeDays)); + query.where(_db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(cutoffDate)); } diff --git a/mobile/lib/presentation/widgets/map/map.state.dart b/mobile/lib/presentation/widgets/map/map.state.dart index a872757961..a7b4ce19e9 100644 --- a/mobile/lib/presentation/widgets/map/map.state.dart +++ b/mobile/lib/presentation/widgets/map/map.state.dart @@ -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/map/map_state.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/utils/option.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; class TimeRange { - final DateTime? from; - final DateTime? to; + final Option from; + final Option 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? from, Option? to}) { return TimeRange(from: from ?? this.from, to: to ?? this.to); } @@ -75,6 +76,7 @@ class MapState { onlyFavorites: onlyFavorites, includeArchived: includeArchived, withPartners: withPartners, + relativeDays: relativeDays, timeRange: timeRange, ); } @@ -123,31 +125,40 @@ class MapStateNotifier extends Notifier { } void setTimeRange(TimeRange range) { - ref - .read(appSettingsServiceProvider) - .setSetting(AppSettingsEnum.mapCustomFrom, range.from == null ? "" : range.from!.toIso8601String()); - ref - .read(appSettingsServiceProvider) - .setSetting(AppSettingsEnum.mapCustomTo, range.to == null ? "" : range.to!.toIso8601String()); + final from = range.from.unwrapOrNull; + final to = range.to.unwrapOrNull; + + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapCustomFrom, from?.toIso8601String() ?? ""); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapCustomTo, to?.toIso8601String() ?? ""); + state = state.copyWith(timeRange: range); EventStream.shared.emit(const MapMarkerReloadEvent()); } + Option parseDateOption(String s) { + try { + if (s.trim().isEmpty) return const Option.none(); + return Option.some(DateTime.parse(s)); + } catch (_) { + return const Option.none(); + } + } + @override MapState build() { 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( themeMode: ThemeMode.values[appSettingsService.getSetting(AppSettingsEnum.mapThemeMode)], onlyFavorites: appSettingsService.getSetting(AppSettingsEnum.mapShowFavoriteOnly), includeArchived: appSettingsService.getSetting(AppSettingsEnum.mapIncludeArchived), withPartners: appSettingsService.getSetting(AppSettingsEnum.mapwithPartners), bounds: LatLngBounds(northeast: const LatLng(0, 0), southwest: const LatLng(0, 0)), - timeRange: TimeRange( - from: customFrom.isNotEmpty ? DateTime.parse(customFrom) : null, - to: customTo.isNotEmpty ? DateTime.parse(customTo) : null, - ), + relativeDays: appSettingsService.getSetting(AppSettingsEnum.mapRelativeDate), + timeRange: TimeRange(from: customFrom, to: customTo), ); } } diff --git a/mobile/lib/presentation/widgets/map/map_settings_sheet.dart b/mobile/lib/presentation/widgets/map/map_settings_sheet.dart index 00e5341f08..9a88c6c55c 100644 --- a/mobile/lib/presentation/widgets/map/map_settings_sheet.dart +++ b/mobile/lib/presentation/widgets/map/map_settings_sheet.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.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/widgets/map/map_settings/map_custom_time_range.dart'; import 'package:immich_mobile/widgets/map/map_settings/map_settings_list_tile.dart'; @@ -22,7 +23,7 @@ class _DriftMapSettingsSheetState extends ConsumerState { super.initState(); final mapState = ref.read(mapStateProvider); final timeRange = mapState.timeRange; - useCustomRange = timeRange.from != null || timeRange.to != null; + useCustomRange = timeRange.from.isSome || timeRange.to.isSome; } @override @@ -78,7 +79,7 @@ class _DriftMapSettingsSheetState extends ConsumerState { ref.read(mapStateProvider.notifier).setRelativeTime(0); 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 ...[ @@ -94,7 +95,7 @@ class _DriftMapSettingsSheetState extends ConsumerState { ref.read(mapStateProvider.notifier).setRelativeTime(0); ref.read(mapStateProvider.notifier).setTimeRange(const TimeRange()); }), - child: Text("use_custom_date_range".t(context: context)), + child: Text(context.t.use_custom_date_range), ), ), ], diff --git a/mobile/lib/utils/option.dart b/mobile/lib/utils/option.dart index 3470e8489e..326b60cb39 100644 --- a/mobile/lib/utils/option.dart +++ b/mobile/lib/utils/option.dart @@ -24,6 +24,21 @@ sealed class Option { None() => onNone(), }; + Option flatMap(Option 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 String toString() => switch (this) { Some(:final value) => 'Some($value)', diff --git a/mobile/lib/widgets/map/map_settings/map_custom_time_range.dart b/mobile/lib/widgets/map/map_settings/map_custom_time_range.dart index 60e7f57951..2dc0cdaea4 100644 --- a/mobile/lib/widgets/map/map_settings/map_custom_time_range.dart +++ b/mobile/lib/widgets/map/map_settings/map_custom_time_range.dart @@ -1,6 +1,8 @@ +import 'package:easy_localization/easy_localization.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/utils/option.dart'; import 'package:intl/intl.dart'; class MapTimeRange extends StatelessWidget { @@ -15,44 +17,49 @@ class MapTimeRange extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ ListTile( - title: Text("date_after".t(context: context)), + title: Text(context.t.date_after), subtitle: Text( - timeRange.from != null - ? DateFormat.yMMMd().add_jm().format(timeRange.from!) - : "not_set".t(context: context), + timeRange.from.fold((from) => DateFormat.yMMMd().add_jm().format(from), () => context.t.not_set), ), - trailing: timeRange.from != null + trailing: timeRange.from.isSome ? IconButton(icon: const Icon(Icons.close), onPressed: () => onChanged(timeRange.clearFrom())) : null, onTap: () async { + final initial = timeRange.from.unwrapOrNull ?? DateTime.now(); + final picked = await showDatePicker( context: context, - initialDate: timeRange.from ?? DateTime.now(), + initialDate: initial, firstDate: DateTime(1970), lastDate: DateTime.now(), ); + if (picked != null) { - onChanged(timeRange.copyWith(from: picked)); + onChanged(timeRange.copyWith(from: Option.some(picked))); } }, ), + ListTile( - title: Text("date_before".t(context: context)), + title: Text(context.t.date_before), subtitle: Text( - timeRange.to != null ? DateFormat.yMMMd().add_jm().format(timeRange.to!) : "not_set".t(context: context), + timeRange.to.fold((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())) : null, onTap: () async { + final initial = timeRange.to.unwrapOrNull ?? DateTime.now(); + final picked = await showDatePicker( context: context, - initialDate: timeRange.to ?? DateTime.now(), + initialDate: initial, firstDate: DateTime(1970), lastDate: DateTime.now(), ); + if (picked != null) { - onChanged(timeRange.copyWith(to: picked)); + onChanged(timeRange.copyWith(to: Option.some(picked))); } }, ),