mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-22 08:46:18 +01:00
Filter photos by date in map browser
This commit is contained in:
parent
877bed1640
commit
17e7aa55f3
10 changed files with 637 additions and 30 deletions
|
@ -1487,6 +1487,14 @@
|
||||||
"trustedCertManagerFailedToRemoveCertError": "Failed to remove certificate",
|
"trustedCertManagerFailedToRemoveCertError": "Failed to remove certificate",
|
||||||
"missingVideoThumbnailHelpDialogTitle": "Having trouble with video thumbnails?",
|
"missingVideoThumbnailHelpDialogTitle": "Having trouble with video thumbnails?",
|
||||||
"dontShowAgain": "Don't show again",
|
"dontShowAgain": "Don't show again",
|
||||||
|
"mapBrowserDateRangeLabel": "Date range",
|
||||||
|
"@mapBrowserDateRangeLabel": {
|
||||||
|
"description": "Filter photos by date range"
|
||||||
|
},
|
||||||
|
"mapBrowserDateRangeThisMonth": "This month",
|
||||||
|
"mapBrowserDateRangePrevMonth": "Previous month",
|
||||||
|
"mapBrowserDateRangeThisYear": "This year",
|
||||||
|
"mapBrowserDateRangeCustom": "Custom",
|
||||||
|
|
||||||
"errorUnauthenticated": "Unauthenticated access. Please sign-in again if the problem continues",
|
"errorUnauthenticated": "Unauthenticated access. Please sign-in again if the problem continues",
|
||||||
"@errorUnauthenticated": {
|
"@errorUnauthenticated": {
|
||||||
|
|
|
@ -249,6 +249,11 @@
|
||||||
"trustedCertManagerFailedToRemoveCertError",
|
"trustedCertManagerFailedToRemoveCertError",
|
||||||
"missingVideoThumbnailHelpDialogTitle",
|
"missingVideoThumbnailHelpDialogTitle",
|
||||||
"dontShowAgain",
|
"dontShowAgain",
|
||||||
|
"mapBrowserDateRangeLabel",
|
||||||
|
"mapBrowserDateRangeThisMonth",
|
||||||
|
"mapBrowserDateRangePrevMonth",
|
||||||
|
"mapBrowserDateRangeThisYear",
|
||||||
|
"mapBrowserDateRangeCustom",
|
||||||
"errorUnauthenticated",
|
"errorUnauthenticated",
|
||||||
"errorDisconnected",
|
"errorDisconnected",
|
||||||
"errorLocked",
|
"errorLocked",
|
||||||
|
@ -284,7 +289,12 @@
|
||||||
"trustedCertManagerNoHttpsServerError",
|
"trustedCertManagerNoHttpsServerError",
|
||||||
"trustedCertManagerFailedToRemoveCertError",
|
"trustedCertManagerFailedToRemoveCertError",
|
||||||
"missingVideoThumbnailHelpDialogTitle",
|
"missingVideoThumbnailHelpDialogTitle",
|
||||||
"dontShowAgain"
|
"dontShowAgain",
|
||||||
|
"mapBrowserDateRangeLabel",
|
||||||
|
"mapBrowserDateRangeThisMonth",
|
||||||
|
"mapBrowserDateRangePrevMonth",
|
||||||
|
"mapBrowserDateRangeThisYear",
|
||||||
|
"mapBrowserDateRangeCustom"
|
||||||
],
|
],
|
||||||
|
|
||||||
"de": [
|
"de": [
|
||||||
|
@ -318,7 +328,12 @@
|
||||||
"trustedCertManagerNoHttpsServerError",
|
"trustedCertManagerNoHttpsServerError",
|
||||||
"trustedCertManagerFailedToRemoveCertError",
|
"trustedCertManagerFailedToRemoveCertError",
|
||||||
"missingVideoThumbnailHelpDialogTitle",
|
"missingVideoThumbnailHelpDialogTitle",
|
||||||
"dontShowAgain"
|
"dontShowAgain",
|
||||||
|
"mapBrowserDateRangeLabel",
|
||||||
|
"mapBrowserDateRangeThisMonth",
|
||||||
|
"mapBrowserDateRangePrevMonth",
|
||||||
|
"mapBrowserDateRangeThisYear",
|
||||||
|
"mapBrowserDateRangeCustom"
|
||||||
],
|
],
|
||||||
|
|
||||||
"el": [
|
"el": [
|
||||||
|
@ -455,7 +470,12 @@
|
||||||
"trustedCertManagerNoHttpsServerError",
|
"trustedCertManagerNoHttpsServerError",
|
||||||
"trustedCertManagerFailedToRemoveCertError",
|
"trustedCertManagerFailedToRemoveCertError",
|
||||||
"missingVideoThumbnailHelpDialogTitle",
|
"missingVideoThumbnailHelpDialogTitle",
|
||||||
"dontShowAgain"
|
"dontShowAgain",
|
||||||
|
"mapBrowserDateRangeLabel",
|
||||||
|
"mapBrowserDateRangeThisMonth",
|
||||||
|
"mapBrowserDateRangePrevMonth",
|
||||||
|
"mapBrowserDateRangeThisYear",
|
||||||
|
"mapBrowserDateRangeCustom"
|
||||||
],
|
],
|
||||||
|
|
||||||
"es": [
|
"es": [
|
||||||
|
@ -483,7 +503,12 @@
|
||||||
"trustedCertManagerNoHttpsServerError",
|
"trustedCertManagerNoHttpsServerError",
|
||||||
"trustedCertManagerFailedToRemoveCertError",
|
"trustedCertManagerFailedToRemoveCertError",
|
||||||
"missingVideoThumbnailHelpDialogTitle",
|
"missingVideoThumbnailHelpDialogTitle",
|
||||||
"dontShowAgain"
|
"dontShowAgain",
|
||||||
|
"mapBrowserDateRangeLabel",
|
||||||
|
"mapBrowserDateRangeThisMonth",
|
||||||
|
"mapBrowserDateRangePrevMonth",
|
||||||
|
"mapBrowserDateRangeThisYear",
|
||||||
|
"mapBrowserDateRangeCustom"
|
||||||
],
|
],
|
||||||
|
|
||||||
"fi": [
|
"fi": [
|
||||||
|
@ -511,7 +536,12 @@
|
||||||
"trustedCertManagerNoHttpsServerError",
|
"trustedCertManagerNoHttpsServerError",
|
||||||
"trustedCertManagerFailedToRemoveCertError",
|
"trustedCertManagerFailedToRemoveCertError",
|
||||||
"missingVideoThumbnailHelpDialogTitle",
|
"missingVideoThumbnailHelpDialogTitle",
|
||||||
"dontShowAgain"
|
"dontShowAgain",
|
||||||
|
"mapBrowserDateRangeLabel",
|
||||||
|
"mapBrowserDateRangeThisMonth",
|
||||||
|
"mapBrowserDateRangePrevMonth",
|
||||||
|
"mapBrowserDateRangeThisYear",
|
||||||
|
"mapBrowserDateRangeCustom"
|
||||||
],
|
],
|
||||||
|
|
||||||
"fr": [
|
"fr": [
|
||||||
|
@ -539,7 +569,12 @@
|
||||||
"trustedCertManagerNoHttpsServerError",
|
"trustedCertManagerNoHttpsServerError",
|
||||||
"trustedCertManagerFailedToRemoveCertError",
|
"trustedCertManagerFailedToRemoveCertError",
|
||||||
"missingVideoThumbnailHelpDialogTitle",
|
"missingVideoThumbnailHelpDialogTitle",
|
||||||
"dontShowAgain"
|
"dontShowAgain",
|
||||||
|
"mapBrowserDateRangeLabel",
|
||||||
|
"mapBrowserDateRangeThisMonth",
|
||||||
|
"mapBrowserDateRangePrevMonth",
|
||||||
|
"mapBrowserDateRangeThisYear",
|
||||||
|
"mapBrowserDateRangeCustom"
|
||||||
],
|
],
|
||||||
|
|
||||||
"it": [
|
"it": [
|
||||||
|
@ -572,7 +607,12 @@
|
||||||
"trustedCertManagerNoHttpsServerError",
|
"trustedCertManagerNoHttpsServerError",
|
||||||
"trustedCertManagerFailedToRemoveCertError",
|
"trustedCertManagerFailedToRemoveCertError",
|
||||||
"missingVideoThumbnailHelpDialogTitle",
|
"missingVideoThumbnailHelpDialogTitle",
|
||||||
"dontShowAgain"
|
"dontShowAgain",
|
||||||
|
"mapBrowserDateRangeLabel",
|
||||||
|
"mapBrowserDateRangeThisMonth",
|
||||||
|
"mapBrowserDateRangePrevMonth",
|
||||||
|
"mapBrowserDateRangeThisYear",
|
||||||
|
"mapBrowserDateRangeCustom"
|
||||||
],
|
],
|
||||||
|
|
||||||
"nl": [
|
"nl": [
|
||||||
|
@ -942,6 +982,11 @@
|
||||||
"trustedCertManagerFailedToRemoveCertError",
|
"trustedCertManagerFailedToRemoveCertError",
|
||||||
"missingVideoThumbnailHelpDialogTitle",
|
"missingVideoThumbnailHelpDialogTitle",
|
||||||
"dontShowAgain",
|
"dontShowAgain",
|
||||||
|
"mapBrowserDateRangeLabel",
|
||||||
|
"mapBrowserDateRangeThisMonth",
|
||||||
|
"mapBrowserDateRangePrevMonth",
|
||||||
|
"mapBrowserDateRangeThisYear",
|
||||||
|
"mapBrowserDateRangeCustom",
|
||||||
"errorUnauthenticated",
|
"errorUnauthenticated",
|
||||||
"errorDisconnected",
|
"errorDisconnected",
|
||||||
"errorLocked",
|
"errorLocked",
|
||||||
|
@ -981,7 +1026,12 @@
|
||||||
"trustedCertManagerNoHttpsServerError",
|
"trustedCertManagerNoHttpsServerError",
|
||||||
"trustedCertManagerFailedToRemoveCertError",
|
"trustedCertManagerFailedToRemoveCertError",
|
||||||
"missingVideoThumbnailHelpDialogTitle",
|
"missingVideoThumbnailHelpDialogTitle",
|
||||||
"dontShowAgain"
|
"dontShowAgain",
|
||||||
|
"mapBrowserDateRangeLabel",
|
||||||
|
"mapBrowserDateRangeThisMonth",
|
||||||
|
"mapBrowserDateRangePrevMonth",
|
||||||
|
"mapBrowserDateRangeThisYear",
|
||||||
|
"mapBrowserDateRangeCustom"
|
||||||
],
|
],
|
||||||
|
|
||||||
"pt": [
|
"pt": [
|
||||||
|
@ -1029,7 +1079,12 @@
|
||||||
"trustedCertManagerNoHttpsServerError",
|
"trustedCertManagerNoHttpsServerError",
|
||||||
"trustedCertManagerFailedToRemoveCertError",
|
"trustedCertManagerFailedToRemoveCertError",
|
||||||
"missingVideoThumbnailHelpDialogTitle",
|
"missingVideoThumbnailHelpDialogTitle",
|
||||||
"dontShowAgain"
|
"dontShowAgain",
|
||||||
|
"mapBrowserDateRangeLabel",
|
||||||
|
"mapBrowserDateRangeThisMonth",
|
||||||
|
"mapBrowserDateRangePrevMonth",
|
||||||
|
"mapBrowserDateRangeThisYear",
|
||||||
|
"mapBrowserDateRangeCustom"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ru": [
|
"ru": [
|
||||||
|
@ -1057,7 +1112,20 @@
|
||||||
"trustedCertManagerNoHttpsServerError",
|
"trustedCertManagerNoHttpsServerError",
|
||||||
"trustedCertManagerFailedToRemoveCertError",
|
"trustedCertManagerFailedToRemoveCertError",
|
||||||
"missingVideoThumbnailHelpDialogTitle",
|
"missingVideoThumbnailHelpDialogTitle",
|
||||||
"dontShowAgain"
|
"dontShowAgain",
|
||||||
|
"mapBrowserDateRangeLabel",
|
||||||
|
"mapBrowserDateRangeThisMonth",
|
||||||
|
"mapBrowserDateRangePrevMonth",
|
||||||
|
"mapBrowserDateRangeThisYear",
|
||||||
|
"mapBrowserDateRangeCustom"
|
||||||
|
],
|
||||||
|
|
||||||
|
"tr": [
|
||||||
|
"mapBrowserDateRangeLabel",
|
||||||
|
"mapBrowserDateRangeThisMonth",
|
||||||
|
"mapBrowserDateRangePrevMonth",
|
||||||
|
"mapBrowserDateRangeThisYear",
|
||||||
|
"mapBrowserDateRangeCustom"
|
||||||
],
|
],
|
||||||
|
|
||||||
"zh": [
|
"zh": [
|
||||||
|
@ -1116,7 +1184,12 @@
|
||||||
"trustedCertManagerNoHttpsServerError",
|
"trustedCertManagerNoHttpsServerError",
|
||||||
"trustedCertManagerFailedToRemoveCertError",
|
"trustedCertManagerFailedToRemoveCertError",
|
||||||
"missingVideoThumbnailHelpDialogTitle",
|
"missingVideoThumbnailHelpDialogTitle",
|
||||||
"dontShowAgain"
|
"dontShowAgain",
|
||||||
|
"mapBrowserDateRangeLabel",
|
||||||
|
"mapBrowserDateRangeThisMonth",
|
||||||
|
"mapBrowserDateRangePrevMonth",
|
||||||
|
"mapBrowserDateRangeThisYear",
|
||||||
|
"mapBrowserDateRangeCustom"
|
||||||
],
|
],
|
||||||
|
|
||||||
"zh_Hant": [
|
"zh_Hant": [
|
||||||
|
@ -1269,6 +1342,11 @@
|
||||||
"trustedCertManagerNoHttpsServerError",
|
"trustedCertManagerNoHttpsServerError",
|
||||||
"trustedCertManagerFailedToRemoveCertError",
|
"trustedCertManagerFailedToRemoveCertError",
|
||||||
"missingVideoThumbnailHelpDialogTitle",
|
"missingVideoThumbnailHelpDialogTitle",
|
||||||
"dontShowAgain"
|
"dontShowAgain",
|
||||||
|
"mapBrowserDateRangeLabel",
|
||||||
|
"mapBrowserDateRangeThisMonth",
|
||||||
|
"mapBrowserDateRangePrevMonth",
|
||||||
|
"mapBrowserDateRangeThisYear",
|
||||||
|
"mapBrowserDateRangeCustom"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:clock/clock.dart';
|
||||||
import 'package:copy_with/copy_with.dart';
|
import 'package:copy_with/copy_with.dart';
|
||||||
import 'package:flex_seed_scheme/flex_seed_scheme.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:google_maps_cluster_manager/google_maps_cluster_manager.dart';
|
import 'package:google_maps_cluster_manager/google_maps_cluster_manager.dart';
|
||||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||||
|
import 'package:intl/intl.dart' as intl;
|
||||||
import 'package:kiwi/kiwi.dart';
|
import 'package:kiwi/kiwi.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:nc_photos/account.dart';
|
import 'package:nc_photos/account.dart';
|
||||||
|
import 'package:nc_photos/app_localizations.dart';
|
||||||
import 'package:nc_photos/bloc_util.dart';
|
import 'package:nc_photos/bloc_util.dart';
|
||||||
import 'package:nc_photos/controller/account_controller.dart';
|
import 'package:nc_photos/controller/account_controller.dart';
|
||||||
import 'package:nc_photos/di_container.dart';
|
import 'package:nc_photos/di_container.dart';
|
||||||
|
@ -18,9 +20,14 @@ import 'package:nc_photos/entity/collection.dart';
|
||||||
import 'package:nc_photos/entity/collection/content_provider/ad_hoc.dart';
|
import 'package:nc_photos/entity/collection/content_provider/ad_hoc.dart';
|
||||||
import 'package:nc_photos/entity/image_location/repo.dart';
|
import 'package:nc_photos/entity/image_location/repo.dart';
|
||||||
import 'package:nc_photos/exception_event.dart';
|
import 'package:nc_photos/exception_event.dart';
|
||||||
|
import 'package:nc_photos/k.dart' as k;
|
||||||
import 'package:nc_photos/snack_bar_manager.dart';
|
import 'package:nc_photos/snack_bar_manager.dart';
|
||||||
|
import 'package:nc_photos/stream_extension.dart';
|
||||||
|
import 'package:nc_photos/theme.dart';
|
||||||
import 'package:nc_photos/widget/collection_browser.dart';
|
import 'package:nc_photos/widget/collection_browser.dart';
|
||||||
|
import 'package:nc_photos/widget/measure.dart';
|
||||||
import 'package:np_codegen/np_codegen.dart';
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
|
import 'package:np_datetime/np_datetime.dart';
|
||||||
import 'package:to_string/to_string.dart';
|
import 'package:to_string/to_string.dart';
|
||||||
|
|
||||||
part 'map_browser.g.dart';
|
part 'map_browser.g.dart';
|
||||||
|
@ -44,7 +51,7 @@ class MapBrowser extends StatelessWidget {
|
||||||
create: (_) => _Bloc(
|
create: (_) => _Bloc(
|
||||||
KiwiContainer().resolve(),
|
KiwiContainer().resolve(),
|
||||||
account: context.read<AccountController>().account,
|
account: context.read<AccountController>().account,
|
||||||
)..add(const _Init()),
|
)..add(const _LoadData()),
|
||||||
child: const _WrappedMapBrowser(),
|
child: const _WrappedMapBrowser(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -69,7 +76,42 @@ class _WrappedMapBrowser extends StatelessWidget {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: const _MapView(),
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
const _MapView(),
|
||||||
|
Positioned.directional(
|
||||||
|
textDirection: Directionality.of(context),
|
||||||
|
top: MediaQuery.of(context).padding.top + 8,
|
||||||
|
end: 8,
|
||||||
|
child: const _DateRangeToggle(),
|
||||||
|
),
|
||||||
|
_BlocSelector<bool>(
|
||||||
|
selector: (state) => state.isShowDataRangeControlPanel,
|
||||||
|
builder: (context, isShowAnyPanel) => Positioned.fill(
|
||||||
|
child: isShowAnyPanel
|
||||||
|
? GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
context.addEvent(const _CloseControlPanel());
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
left: 8,
|
||||||
|
right: 8,
|
||||||
|
top: MediaQuery.of(context).padding.top + 8,
|
||||||
|
child: _BlocSelector<bool>(
|
||||||
|
selector: (state) => state.isShowDataRangeControlPanel,
|
||||||
|
builder: (context, isShowDataRangeControlPanel) =>
|
||||||
|
_PanelContainer(
|
||||||
|
isShow: isShowDataRangeControlPanel,
|
||||||
|
child: const _DateRangeControlPanel(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -79,7 +121,7 @@ class _WrappedMapBrowser extends StatelessWidget {
|
||||||
typedef _BlocBuilder = BlocBuilder<_Bloc, _State>;
|
typedef _BlocBuilder = BlocBuilder<_Bloc, _State>;
|
||||||
// typedef _BlocListener = BlocListener<_Bloc, _State>;
|
// typedef _BlocListener = BlocListener<_Bloc, _State>;
|
||||||
typedef _BlocListenerT<T> = BlocListenerT<_Bloc, _State, T>;
|
typedef _BlocListenerT<T> = BlocListenerT<_Bloc, _State, T>;
|
||||||
// typedef _BlocSelector<T> = BlocSelector<_Bloc, _State, T>;
|
typedef _BlocSelector<T> = BlocSelector<_Bloc, _State, T>;
|
||||||
|
|
||||||
extension on BuildContext {
|
extension on BuildContext {
|
||||||
_Bloc get bloc => read<_Bloc>();
|
_Bloc get bloc => read<_Bloc>();
|
||||||
|
|
|
@ -17,6 +17,9 @@ abstract class $_StateCopyWithWorker {
|
||||||
{List<_DataPoint>? data,
|
{List<_DataPoint>? data,
|
||||||
LatLng? initialPoint,
|
LatLng? initialPoint,
|
||||||
Set<Marker>? markers,
|
Set<Marker>? markers,
|
||||||
|
bool? isShowDataRangeControlPanel,
|
||||||
|
_DateRangeType? dateRangeType,
|
||||||
|
DateRange? localDateRange,
|
||||||
ExceptionEvent? error});
|
ExceptionEvent? error});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +31,9 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
||||||
{dynamic data,
|
{dynamic data,
|
||||||
dynamic initialPoint = copyWithNull,
|
dynamic initialPoint = copyWithNull,
|
||||||
dynamic markers,
|
dynamic markers,
|
||||||
|
dynamic isShowDataRangeControlPanel,
|
||||||
|
dynamic dateRangeType,
|
||||||
|
dynamic localDateRange,
|
||||||
dynamic error = copyWithNull}) {
|
dynamic error = copyWithNull}) {
|
||||||
return _State(
|
return _State(
|
||||||
data: data as List<_DataPoint>? ?? that.data,
|
data: data as List<_DataPoint>? ?? that.data,
|
||||||
|
@ -35,6 +41,10 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
||||||
? that.initialPoint
|
? that.initialPoint
|
||||||
: initialPoint as LatLng?,
|
: initialPoint as LatLng?,
|
||||||
markers: markers as Set<Marker>? ?? that.markers,
|
markers: markers as Set<Marker>? ?? that.markers,
|
||||||
|
isShowDataRangeControlPanel: isShowDataRangeControlPanel as bool? ??
|
||||||
|
that.isShowDataRangeControlPanel,
|
||||||
|
dateRangeType: dateRangeType as _DateRangeType? ?? that.dateRangeType,
|
||||||
|
localDateRange: localDateRange as DateRange? ?? that.localDateRange,
|
||||||
error: error == copyWithNull ? that.error : error as ExceptionEvent?);
|
error: error == copyWithNull ? that.error : error as ExceptionEvent?);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,14 +74,14 @@ extension _$_BlocNpLog on _Bloc {
|
||||||
extension _$_StateToString on _State {
|
extension _$_StateToString on _State {
|
||||||
String _$toString() {
|
String _$toString() {
|
||||||
// ignore: unnecessary_string_interpolations
|
// ignore: unnecessary_string_interpolations
|
||||||
return "_State {data: [length: ${data.length}], initialPoint: $initialPoint, markers: {length: ${markers.length}}, error: $error}";
|
return "_State {data: [length: ${data.length}], initialPoint: $initialPoint, markers: {length: ${markers.length}}, isShowDataRangeControlPanel: $isShowDataRangeControlPanel, dateRangeType: ${dateRangeType.name}, localDateRange: $localDateRange, error: $error}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension _$_InitToString on _Init {
|
extension _$_LoadDataToString on _LoadData {
|
||||||
String _$toString() {
|
String _$toString() {
|
||||||
// ignore: unnecessary_string_interpolations
|
// ignore: unnecessary_string_interpolations
|
||||||
return "_Init {}";
|
return "_LoadData {}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,6 +92,34 @@ extension _$_SetMarkersToString on _SetMarkers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension _$_OpenDataRangeControlPanelToString on _OpenDataRangeControlPanel {
|
||||||
|
String _$toString() {
|
||||||
|
// ignore: unnecessary_string_interpolations
|
||||||
|
return "_OpenDataRangeControlPanel {}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension _$_CloseControlPanelToString on _CloseControlPanel {
|
||||||
|
String _$toString() {
|
||||||
|
// ignore: unnecessary_string_interpolations
|
||||||
|
return "_CloseControlPanel {}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension _$_SetDateRangeTypeToString on _SetDateRangeType {
|
||||||
|
String _$toString() {
|
||||||
|
// ignore: unnecessary_string_interpolations
|
||||||
|
return "_SetDateRangeType {value: ${value.name}}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension _$_SetLocalDateRangeToString on _SetLocalDateRange {
|
||||||
|
String _$toString() {
|
||||||
|
// ignore: unnecessary_string_interpolations
|
||||||
|
return "_SetLocalDateRange {value: $value}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension _$_SetErrorToString on _SetError {
|
extension _$_SetErrorToString on _SetError {
|
||||||
String _$toString() {
|
String _$toString() {
|
||||||
// ignore: unnecessary_string_interpolations
|
// ignore: unnecessary_string_interpolations
|
||||||
|
|
|
@ -6,10 +6,31 @@ class _Bloc extends Bloc<_Event, _State>
|
||||||
_Bloc(
|
_Bloc(
|
||||||
this._c, {
|
this._c, {
|
||||||
required this.account,
|
required this.account,
|
||||||
}) : super(_State.init()) {
|
}) : super(_State.init(
|
||||||
on<_Init>(_onInit);
|
dateRangeType: _DateRangeType.thisMonth,
|
||||||
|
localDateRange:
|
||||||
|
_calcDateRange(clock.now().toDate(), _DateRangeType.thisMonth),
|
||||||
|
)) {
|
||||||
|
on<_LoadData>(_onLoadData);
|
||||||
on<_SetMarkers>(_onSetMarkers);
|
on<_SetMarkers>(_onSetMarkers);
|
||||||
|
on<_OpenDataRangeControlPanel>(_onOpenDataRangeControlPanel);
|
||||||
|
on<_CloseControlPanel>(_onCloseControlPanel);
|
||||||
|
on<_SetDateRangeType>(_onSetDateRangeType);
|
||||||
|
on<_SetLocalDateRange>(_onSetDateRange);
|
||||||
on<_SetError>(_onSetError);
|
on<_SetError>(_onSetError);
|
||||||
|
|
||||||
|
_subscriptions
|
||||||
|
.add(stream.distinctBy((state) => state.localDateRange).listen((state) {
|
||||||
|
add(const _LoadData());
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
for (final s in _subscriptions) {
|
||||||
|
s.cancel();
|
||||||
|
}
|
||||||
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -28,10 +49,16 @@ class _Bloc extends Bloc<_Event, _State>
|
||||||
super.onError(error, stackTrace);
|
super.onError(error, stackTrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onInit(_Init ev, Emitter<_State> emit) async {
|
Future<void> _onLoadData(_LoadData ev, Emitter<_State> emit) async {
|
||||||
_log.info(ev);
|
_log.info(ev);
|
||||||
final raw = await _c.imageLocationRepo.getLocations(account);
|
// convert local DateRange to TimeRange in UTC
|
||||||
_log.info("[_onInit] Loaded ${raw.length} markers");
|
final localTimeRange = state.localDateRange.toLocalTimeRange();
|
||||||
|
final utcTimeRange = localTimeRange.copyWith(
|
||||||
|
from: localTimeRange.from?.toUtc(),
|
||||||
|
to: localTimeRange.to?.toUtc(),
|
||||||
|
);
|
||||||
|
final raw = await _c.imageLocationRepo.getLocations(account, utcTimeRange);
|
||||||
|
_log.info("[_onLoadData] Loaded ${raw.length} markers");
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
data: raw.map(_DataPoint.fromImageLatLng).toList(),
|
data: raw.map(_DataPoint.fromImageLatLng).toList(),
|
||||||
initialPoint: state.initialPoint ??
|
initialPoint: state.initialPoint ??
|
||||||
|
@ -46,13 +73,86 @@ class _Bloc extends Bloc<_Event, _State>
|
||||||
emit(state.copyWith(markers: ev.markers));
|
emit(state.copyWith(markers: ev.markers));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onOpenDataRangeControlPanel(
|
||||||
|
_OpenDataRangeControlPanel ev, Emitter<_State> emit) {
|
||||||
|
_log.info(ev);
|
||||||
|
emit(state.copyWith(
|
||||||
|
isShowDataRangeControlPanel: true,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onCloseControlPanel(_CloseControlPanel ev, Emitter<_State> emit) {
|
||||||
|
_log.info(ev);
|
||||||
|
emit(state.copyWith(
|
||||||
|
isShowDataRangeControlPanel: false,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onSetDateRangeType(_SetDateRangeType ev, Emitter<_State> emit) {
|
||||||
|
_log.info(ev);
|
||||||
|
emit(state.copyWith(
|
||||||
|
dateRangeType: ev.value,
|
||||||
|
localDateRange: ev.value == _DateRangeType.custom
|
||||||
|
? null
|
||||||
|
: _calcDateRange(clock.now().toDate(), ev.value),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onSetDateRange(_SetLocalDateRange ev, Emitter<_State> emit) {
|
||||||
|
_log.info(ev);
|
||||||
|
emit(state.copyWith(
|
||||||
|
dateRangeType: _DateRangeType.custom,
|
||||||
|
localDateRange: ev.value,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
void _onSetError(_SetError ev, Emitter<_State> emit) {
|
void _onSetError(_SetError ev, Emitter<_State> emit) {
|
||||||
_log.info(ev);
|
_log.info(ev);
|
||||||
emit(state.copyWith(error: ExceptionEvent(ev.error, ev.stackTrace)));
|
emit(state.copyWith(error: ExceptionEvent(ev.error, ev.stackTrace)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static DateRange _calcDateRange(Date today, _DateRangeType type) {
|
||||||
|
assert(type != _DateRangeType.custom);
|
||||||
|
switch (type) {
|
||||||
|
case _DateRangeType.thisMonth:
|
||||||
|
return DateRange(
|
||||||
|
from: today.copyWith(day: 1),
|
||||||
|
to: today,
|
||||||
|
toBound: TimeRangeBound.inclusive,
|
||||||
|
);
|
||||||
|
case _DateRangeType.prevMonth:
|
||||||
|
if (today.month == 1) {
|
||||||
|
return DateRange(
|
||||||
|
from: Date(today.year - 1, 12, 1),
|
||||||
|
to: Date(today.year - 1, 12, 31),
|
||||||
|
toBound: TimeRangeBound.inclusive,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return DateRange(
|
||||||
|
from: Date(today.year, today.month - 1, 1),
|
||||||
|
to: Date(today.year, today.month, 1).add(day: -1),
|
||||||
|
toBound: TimeRangeBound.inclusive,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case _DateRangeType.thisYear:
|
||||||
|
return DateRange(
|
||||||
|
from: today.copyWith(month: 1, day: 1),
|
||||||
|
to: today,
|
||||||
|
toBound: TimeRangeBound.inclusive,
|
||||||
|
);
|
||||||
|
case _DateRangeType.custom:
|
||||||
|
return DateRange(
|
||||||
|
from: today,
|
||||||
|
to: today,
|
||||||
|
toBound: TimeRangeBound.inclusive,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final DiContainer _c;
|
final DiContainer _c;
|
||||||
final Account account;
|
final Account account;
|
||||||
|
|
||||||
|
final _subscriptions = <StreamSubscription>[];
|
||||||
|
|
||||||
var _isHandlingError = false;
|
var _isHandlingError = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,22 @@ class _State {
|
||||||
required this.data,
|
required this.data,
|
||||||
this.initialPoint,
|
this.initialPoint,
|
||||||
required this.markers,
|
required this.markers,
|
||||||
|
required this.isShowDataRangeControlPanel,
|
||||||
|
required this.dateRangeType,
|
||||||
|
required this.localDateRange,
|
||||||
this.error,
|
this.error,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory _State.init() {
|
factory _State.init({
|
||||||
return const _State(
|
required _DateRangeType dateRangeType,
|
||||||
data: [],
|
required DateRange localDateRange,
|
||||||
markers: {},
|
}) {
|
||||||
|
return _State(
|
||||||
|
data: const [],
|
||||||
|
markers: const {},
|
||||||
|
isShowDataRangeControlPanel: false,
|
||||||
|
dateRangeType: dateRangeType,
|
||||||
|
localDateRange: localDateRange,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +33,10 @@ class _State {
|
||||||
final LatLng? initialPoint;
|
final LatLng? initialPoint;
|
||||||
final Set<Marker> markers;
|
final Set<Marker> markers;
|
||||||
|
|
||||||
|
final bool isShowDataRangeControlPanel;
|
||||||
|
final _DateRangeType dateRangeType;
|
||||||
|
final DateRange localDateRange;
|
||||||
|
|
||||||
final ExceptionEvent? error;
|
final ExceptionEvent? error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,8 +45,8 @@ abstract class _Event {
|
||||||
}
|
}
|
||||||
|
|
||||||
@toString
|
@toString
|
||||||
class _Init implements _Event {
|
class _LoadData implements _Event {
|
||||||
const _Init();
|
const _LoadData();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => _$toString();
|
String toString() => _$toString();
|
||||||
|
@ -49,6 +62,42 @@ class _SetMarkers implements _Event {
|
||||||
final Set<Marker> markers;
|
final Set<Marker> markers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@toString
|
||||||
|
class _OpenDataRangeControlPanel implements _Event {
|
||||||
|
const _OpenDataRangeControlPanel();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => _$toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@toString
|
||||||
|
class _CloseControlPanel implements _Event {
|
||||||
|
const _CloseControlPanel();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => _$toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@toString
|
||||||
|
class _SetDateRangeType implements _Event {
|
||||||
|
const _SetDateRangeType(this.value);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => _$toString();
|
||||||
|
|
||||||
|
final _DateRangeType value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@toString
|
||||||
|
class _SetLocalDateRange implements _Event {
|
||||||
|
const _SetLocalDateRange(this.value);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => _$toString();
|
||||||
|
|
||||||
|
final DateRange value;
|
||||||
|
}
|
||||||
|
|
||||||
@toString
|
@toString
|
||||||
class _SetError implements _Event {
|
class _SetError implements _Event {
|
||||||
const _SetError(this.error, [this.stackTrace]);
|
const _SetError(this.error, [this.stackTrace]);
|
||||||
|
|
|
@ -19,3 +19,24 @@ class _DataPoint implements ClusterItem {
|
||||||
final LatLng location;
|
final LatLng location;
|
||||||
final int fileId;
|
final int fileId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum _DateRangeType {
|
||||||
|
thisMonth,
|
||||||
|
prevMonth,
|
||||||
|
thisYear,
|
||||||
|
custom,
|
||||||
|
;
|
||||||
|
|
||||||
|
String toDisplayString() {
|
||||||
|
switch (this) {
|
||||||
|
case thisMonth:
|
||||||
|
return L10n.global().mapBrowserDateRangeThisMonth;
|
||||||
|
case prevMonth:
|
||||||
|
return L10n.global().mapBrowserDateRangePrevMonth;
|
||||||
|
case thisYear:
|
||||||
|
return L10n.global().mapBrowserDateRangeThisYear;
|
||||||
|
case custom:
|
||||||
|
return L10n.global().mapBrowserDateRangeCustom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -200,3 +200,242 @@ class _MapViewState extends State<_MapView> {
|
||||||
late final _colorHsl =
|
late final _colorHsl =
|
||||||
HSLColor.fromColor(Theme.of(context).colorScheme.primaryContainer);
|
HSLColor.fromColor(Theme.of(context).colorScheme.primaryContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _PanelContainer extends StatefulWidget {
|
||||||
|
const _PanelContainer({
|
||||||
|
required this.isShow,
|
||||||
|
required this.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _PanelContainerState();
|
||||||
|
|
||||||
|
final bool isShow;
|
||||||
|
final Widget child;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PanelContainerState extends State<_PanelContainer>
|
||||||
|
with TickerProviderStateMixin {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_animationController = AnimationController(
|
||||||
|
duration: k.animationDurationNormal,
|
||||||
|
vsync: this,
|
||||||
|
value: 0,
|
||||||
|
);
|
||||||
|
_animation = CurvedAnimation(
|
||||||
|
parent: _animationController,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_animationController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant _PanelContainer oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (oldWidget.isShow != widget.isShow) {
|
||||||
|
if (widget.isShow) {
|
||||||
|
_animationController.animateTo(1);
|
||||||
|
} else {
|
||||||
|
_animationController.animateBack(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MatrixTransition(
|
||||||
|
animation: _animation,
|
||||||
|
onTransform: (animationValue) => Matrix4.identity()
|
||||||
|
..translate(0.0, -(_size.height / 2) * (1 - animationValue), 0.0)
|
||||||
|
..scale(1.0, animationValue, 1.0),
|
||||||
|
child: MeasureSize(
|
||||||
|
onChange: (size) => setState(() {
|
||||||
|
_size = size;
|
||||||
|
}),
|
||||||
|
child: widget.child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late AnimationController _animationController;
|
||||||
|
late Animation<double> _animation;
|
||||||
|
var _size = Size.zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DateRangeToggle extends StatelessWidget {
|
||||||
|
const _DateRangeToggle();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FloatingActionButton.small(
|
||||||
|
onPressed: () {
|
||||||
|
context.addEvent(const _OpenDataRangeControlPanel());
|
||||||
|
},
|
||||||
|
child: const Icon(Icons.date_range_outlined),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DateRangeControlPanel extends StatelessWidget {
|
||||||
|
const _DateRangeControlPanel();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: theme.elevate(theme.colorScheme.surface, 2),
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(
|
||||||
|
blurRadius: 4,
|
||||||
|
offset: Offset(0, 2),
|
||||||
|
color: Colors.black26,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
L10n.global().mapBrowserDateRangeLabel,
|
||||||
|
style: Theme.of(context).listTileTheme.titleTextStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _BlocSelector<_DateRangeType>(
|
||||||
|
selector: (state) => state.dateRangeType,
|
||||||
|
builder: (context, dateRangeType) =>
|
||||||
|
DropdownButtonFormField<_DateRangeType>(
|
||||||
|
items: _DateRangeType.values
|
||||||
|
.map((e) => DropdownMenuItem(
|
||||||
|
value: e,
|
||||||
|
child: Text(e.toDisplayString()),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
value: dateRangeType,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
context.addEvent(_SetDateRangeType(value));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _BlocSelector<DateRange>(
|
||||||
|
selector: (state) => state.localDateRange,
|
||||||
|
builder: (context, localDateRange) => _DateField(
|
||||||
|
localDateRange.from!,
|
||||||
|
onChanged: (value) {
|
||||||
|
context.addEvent(_SetLocalDateRange(
|
||||||
|
localDateRange.copyWith(from: value.toDate())));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Text(" - "),
|
||||||
|
Expanded(
|
||||||
|
child: _BlocSelector<DateRange>(
|
||||||
|
selector: (state) => state.localDateRange,
|
||||||
|
builder: (context, localDateRange) => _DateField(
|
||||||
|
localDateRange.to!,
|
||||||
|
onChanged: (value) {
|
||||||
|
context.addEvent(_SetLocalDateRange(
|
||||||
|
localDateRange.copyWith(to: value.toDate())));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DateField extends StatefulWidget {
|
||||||
|
const _DateField(
|
||||||
|
this.date, {
|
||||||
|
this.onChanged,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _DateFieldState();
|
||||||
|
|
||||||
|
final Date date;
|
||||||
|
final ValueChanged<DateTime>? onChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DateFieldState extends State<_DateField> {
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
_controller.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant _DateField oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (widget.date != oldWidget.date) {
|
||||||
|
_controller.text = _stringify(widget.date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Material(
|
||||||
|
type: MaterialType.transparency,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
final result = await showDatePicker(
|
||||||
|
context: context,
|
||||||
|
firstDate: DateTime(1970),
|
||||||
|
lastDate: clock.now(),
|
||||||
|
currentDate: widget.date.toLocalDateTime(),
|
||||||
|
);
|
||||||
|
if (result == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
widget.onChanged?.call(result);
|
||||||
|
},
|
||||||
|
child: IgnorePointer(
|
||||||
|
child: ExcludeFocus(
|
||||||
|
child: TextFormField(
|
||||||
|
controller: _controller,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _stringify(Date date) {
|
||||||
|
return intl.DateFormat(intl.DateFormat.YEAR_ABBR_MONTH_DAY,
|
||||||
|
Localizations.localeOf(context).languageCode)
|
||||||
|
.format(date.toLocalDateTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _controller = TextEditingController(text: _stringify(widget.date));
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,22 @@ class DateRange {
|
||||||
this.toBound = TimeRangeBound.exclusive,
|
this.toBound = TimeRangeBound.exclusive,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Return a copy of the current instance with some changed fields. Setting
|
||||||
|
/// null is not supported
|
||||||
|
DateRange copyWith({
|
||||||
|
Date? from,
|
||||||
|
TimeRangeBound? fromBound,
|
||||||
|
Date? to,
|
||||||
|
TimeRangeBound? toBound,
|
||||||
|
}) {
|
||||||
|
return DateRange(
|
||||||
|
from: from ?? this.from,
|
||||||
|
fromBound: fromBound ?? this.fromBound,
|
||||||
|
to: to ?? this.to,
|
||||||
|
toBound: toBound ?? this.toBound,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return "${fromBound == TimeRangeBound.inclusive ? "[" : "("}"
|
return "${fromBound == TimeRangeBound.inclusive ? "[" : "("}"
|
||||||
|
|
|
@ -11,6 +11,22 @@ class TimeRange {
|
||||||
this.toBound = TimeRangeBound.exclusive,
|
this.toBound = TimeRangeBound.exclusive,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Return a copy of the current instance with some changed fields. Setting
|
||||||
|
/// null is not supported
|
||||||
|
TimeRange copyWith({
|
||||||
|
DateTime? from,
|
||||||
|
TimeRangeBound? fromBound,
|
||||||
|
DateTime? to,
|
||||||
|
TimeRangeBound? toBound,
|
||||||
|
}) {
|
||||||
|
return TimeRange(
|
||||||
|
from: from ?? this.from,
|
||||||
|
fromBound: fromBound ?? this.fromBound,
|
||||||
|
to: to ?? this.to,
|
||||||
|
toBound: toBound ?? this.toBound,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return "${fromBound == TimeRangeBound.inclusive ? "[" : "("}"
|
return "${fromBound == TimeRangeBound.inclusive ? "[" : "("}"
|
||||||
|
|
Loading…
Reference in a new issue