mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-22 08:46:18 +01:00
Improve initial location when adding map to album
This commit is contained in:
parent
281efe0d0d
commit
b85afaac07
14 changed files with 311 additions and 40 deletions
|
@ -22,6 +22,7 @@ import 'package:nc_photos/controller/collection_items_controller.dart';
|
|||
import 'package:nc_photos/controller/collections_controller.dart';
|
||||
import 'package:nc_photos/controller/files_controller.dart';
|
||||
import 'package:nc_photos/controller/pref_controller.dart';
|
||||
import 'package:nc_photos/db/entity_converter.dart';
|
||||
import 'package:nc_photos/di_container.dart';
|
||||
import 'package:nc_photos/download_handler.dart';
|
||||
import 'package:nc_photos/entity/collection.dart';
|
||||
|
@ -44,6 +45,7 @@ import 'package:nc_photos/session_storage.dart';
|
|||
import 'package:nc_photos/snack_bar_manager.dart';
|
||||
import 'package:nc_photos/stream_util.dart';
|
||||
import 'package:nc_photos/widget/album_share_outlier_browser.dart';
|
||||
import 'package:nc_photos/widget/app_bar_circular_progress_indicator.dart';
|
||||
import 'package:nc_photos/widget/app_intermediate_circular_progress_indicator.dart';
|
||||
import 'package:nc_photos/widget/collection_picker.dart';
|
||||
import 'package:nc_photos/widget/draggable_item_list.dart';
|
||||
|
@ -63,8 +65,11 @@ import 'package:nc_photos/widget/simple_input_dialog.dart';
|
|||
import 'package:nc_photos/widget/sliver_visualized_scale.dart';
|
||||
import 'package:nc_photos/widget/viewer.dart';
|
||||
import 'package:np_codegen/np_codegen.dart';
|
||||
import 'package:np_common/object_util.dart';
|
||||
import 'package:np_common/or_null.dart';
|
||||
import 'package:np_common/unique.dart';
|
||||
import 'package:np_datetime/np_datetime.dart';
|
||||
import 'package:np_db/np_db.dart';
|
||||
import 'package:np_gps_map/np_gps_map.dart';
|
||||
import 'package:np_ui/np_ui.dart';
|
||||
import 'package:to_string/to_string.dart';
|
||||
|
@ -117,6 +122,7 @@ class CollectionBrowser extends StatelessWidget {
|
|||
prefController: context.read(),
|
||||
collectionsController: accountController.collectionsController,
|
||||
filesController: accountController.filesController,
|
||||
db: context.read(),
|
||||
collection: collection,
|
||||
),
|
||||
child: const _WrappedCollectionBrowser(),
|
||||
|
@ -220,6 +226,29 @@ class _WrappedCollectionBrowserState extends State<_WrappedCollectionBrowser>
|
|||
}
|
||||
},
|
||||
),
|
||||
_BlocListenerT(
|
||||
selector: (state) => state.placePickerRequest,
|
||||
listener: (context, placePickerRequest) async {
|
||||
if (placePickerRequest.value != null) {
|
||||
final result =
|
||||
await Navigator.of(context).pushNamed<CameraPosition>(
|
||||
PlacePicker.routeName,
|
||||
arguments: PlacePickerArguments(
|
||||
initialPosition:
|
||||
placePickerRequest.value!.initialPosition,
|
||||
initialZoom:
|
||||
placePickerRequest.value!.initialPosition == null
|
||||
? null
|
||||
: 15.5,
|
||||
),
|
||||
);
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
context.read<_Bloc>().add(_AddMapToCollection(result));
|
||||
}
|
||||
},
|
||||
),
|
||||
_BlocListenerT<ExceptionEvent?>(
|
||||
selector: (state) => state.error,
|
||||
listener: (context, error) {
|
||||
|
@ -418,6 +447,7 @@ typedef _BlocBuilder = BlocBuilder<_Bloc, _State>;
|
|||
typedef _BlocListener = BlocListener<_Bloc, _State>;
|
||||
typedef _BlocListenerT<T> = BlocListenerT<_Bloc, _State, T>;
|
||||
typedef _BlocSelector<T> = BlocSelector<_Bloc, _State, T>;
|
||||
typedef _Emitter = Emitter<_State>;
|
||||
|
||||
extension on BuildContext {
|
||||
_Bloc get bloc => read<_Bloc>();
|
||||
|
|
|
@ -31,6 +31,8 @@ abstract class $_StateCopyWithWorker {
|
|||
List<CollectionItem>? editItems,
|
||||
List<_Item>? editTransformedItems,
|
||||
CollectionItemSort? editSort,
|
||||
bool? isAddMapBusy,
|
||||
Unique<_PlacePickerRequest?>? placePickerRequest,
|
||||
bool? isDragging,
|
||||
int? zoom,
|
||||
double? scale,
|
||||
|
@ -61,6 +63,8 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
|||
dynamic editItems = copyWithNull,
|
||||
dynamic editTransformedItems = copyWithNull,
|
||||
dynamic editSort = copyWithNull,
|
||||
dynamic isAddMapBusy,
|
||||
dynamic placePickerRequest,
|
||||
dynamic isDragging,
|
||||
dynamic zoom,
|
||||
dynamic scale = copyWithNull,
|
||||
|
@ -99,6 +103,10 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
|||
editSort: editSort == copyWithNull
|
||||
? that.editSort
|
||||
: editSort as CollectionItemSort?,
|
||||
isAddMapBusy: isAddMapBusy as bool? ?? that.isAddMapBusy,
|
||||
placePickerRequest:
|
||||
placePickerRequest as Unique<_PlacePickerRequest?>? ??
|
||||
that.placePickerRequest,
|
||||
isDragging: isDragging as bool? ?? that.isDragging,
|
||||
zoom: zoom as int? ?? that.zoom,
|
||||
scale: scale == copyWithNull ? that.scale : scale as double?,
|
||||
|
@ -151,7 +159,7 @@ extension _$_BlocNpLog on _Bloc {
|
|||
extension _$_StateToString on _State {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_State {collection: $collection, coverUrl: $coverUrl, items: [length: ${items.length}], rawItems: [length: ${rawItems.length}], itemsWhitelist: ${itemsWhitelist == null ? null : "{length: ${itemsWhitelist!.length}}"}, isLoading: $isLoading, transformedItems: [length: ${transformedItems.length}], selectedItems: {length: ${selectedItems.length}}, isSelectionRemovable: $isSelectionRemovable, isSelectionManageableFile: $isSelectionManageableFile, isSelectionDeletable: $isSelectionDeletable, isEditMode: $isEditMode, isEditBusy: $isEditBusy, editName: $editName, editItems: ${editItems == null ? null : "[length: ${editItems!.length}]"}, editTransformedItems: ${editTransformedItems == null ? null : "[length: ${editTransformedItems!.length}]"}, editSort: ${editSort == null ? null : "${editSort!.name}"}, isDragging: $isDragging, zoom: $zoom, scale: ${scale == null ? null : "${scale!.toStringAsFixed(3)}"}, importResult: $importResult, error: $error, message: $message}";
|
||||
return "_State {collection: $collection, coverUrl: $coverUrl, items: [length: ${items.length}], rawItems: [length: ${rawItems.length}], itemsWhitelist: ${itemsWhitelist == null ? null : "{length: ${itemsWhitelist!.length}}"}, isLoading: $isLoading, transformedItems: [length: ${transformedItems.length}], selectedItems: {length: ${selectedItems.length}}, isSelectionRemovable: $isSelectionRemovable, isSelectionManageableFile: $isSelectionManageableFile, isSelectionDeletable: $isSelectionDeletable, isEditMode: $isEditMode, isEditBusy: $isEditBusy, editName: $editName, editItems: ${editItems == null ? null : "[length: ${editItems!.length}]"}, editTransformedItems: ${editTransformedItems == null ? null : "[length: ${editTransformedItems!.length}]"}, editSort: ${editSort == null ? null : "${editSort!.name}"}, isAddMapBusy: $isAddMapBusy, placePickerRequest: $placePickerRequest, isDragging: $isDragging, zoom: $zoom, scale: ${scale == null ? null : "${scale!.toStringAsFixed(3)}"}, importResult: $importResult, error: $error, message: $message}";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,6 +227,13 @@ extension _$_AddLabelToCollectionToString on _AddLabelToCollection {
|
|||
}
|
||||
}
|
||||
|
||||
extension _$_RequestAddMapToString on _RequestAddMap {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_RequestAddMap {}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$_AddMapToCollectionToString on _AddMapToCollection {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
|
|
|
@ -365,10 +365,20 @@ class _EditAppBar extends StatelessWidget {
|
|||
onPressed: () => _onAddTextPressed(context),
|
||||
),
|
||||
if (capabilitiesAdapter.isPermitted(CollectionCapability.mapItem))
|
||||
IconButton(
|
||||
icon: const Icon(Icons.map_outlined),
|
||||
tooltip: L10n.global().albumAddMapTooltip,
|
||||
onPressed: () => _onAddMapPressed(context),
|
||||
_BlocSelector(
|
||||
selector: (state) => state.isAddMapBusy,
|
||||
builder: (context, isAddMapBusy) => isAddMapBusy
|
||||
? const IconButton(
|
||||
icon: AppBarProgressIndicator(),
|
||||
onPressed: null,
|
||||
)
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.map_outlined),
|
||||
tooltip: L10n.global().albumAddMapTooltip,
|
||||
onPressed: () {
|
||||
context.addEvent(const _RequestAddMap());
|
||||
},
|
||||
),
|
||||
),
|
||||
if (capabilitiesAdapter.isPermitted(CollectionCapability.sort))
|
||||
IconButton(
|
||||
|
@ -393,15 +403,6 @@ class _EditAppBar extends StatelessWidget {
|
|||
context.read<_Bloc>().add(_AddLabelToCollection(result));
|
||||
}
|
||||
|
||||
Future<void> _onAddMapPressed(BuildContext context) async {
|
||||
final result = await Navigator.of(context)
|
||||
.pushNamed<CameraPosition>(PlacePicker.routeName);
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
context.read<_Bloc>().add(_AddMapToCollection(result));
|
||||
}
|
||||
|
||||
Future<void> _onSortPressed(BuildContext context) async {
|
||||
final current = context
|
||||
.read<_Bloc>()
|
||||
|
|
|
@ -9,6 +9,7 @@ class _Bloc extends Bloc<_Event, _State>
|
|||
required this.prefController,
|
||||
required this.collectionsController,
|
||||
required this.filesController,
|
||||
required this.db,
|
||||
required Collection collection,
|
||||
}) : _c = container,
|
||||
_isAdHocCollection = !collectionsController.stream.value.data
|
||||
|
@ -30,6 +31,7 @@ class _Bloc extends Bloc<_Event, _State>
|
|||
on<_BeginEdit>(_onBeginEdit);
|
||||
on<_EditName>(_onEditName);
|
||||
on<_AddLabelToCollection>(_onAddLabelToCollection);
|
||||
on<_RequestAddMap>(_onRequestAddMap);
|
||||
on<_AddMapToCollection>(_onAddMapToCollection);
|
||||
on<_EditSort>(_onEditSort);
|
||||
on<_EditManualSort>(_onEditManualSort);
|
||||
|
@ -212,6 +214,34 @@ class _Bloc extends Bloc<_Event, _State>
|
|||
));
|
||||
}
|
||||
|
||||
Future<void> _onRequestAddMap(_RequestAddMap ev, _Emitter emit) async {
|
||||
_log.info(ev);
|
||||
emit(state.copyWith(isAddMapBusy: true));
|
||||
try {
|
||||
final location = await db.getFirstLocationOfFileIds(
|
||||
account: account.toDb(),
|
||||
fileIds: state.transformedItems
|
||||
.whereType<_FileItem>()
|
||||
.map((e) => e.file.fdId)
|
||||
.toList(),
|
||||
);
|
||||
final mapCoord =
|
||||
location?.let((e) => MapCoord(e.latitude!, e.longitude!));
|
||||
emit(state.copyWith(
|
||||
placePickerRequest:
|
||||
Unique(_PlacePickerRequest(initialPosition: mapCoord)),
|
||||
));
|
||||
} catch (e, stackTrace) {
|
||||
_log.severe("[_onRequestAddMap] Failed while getFirstLocationOfFileIds",
|
||||
e, stackTrace);
|
||||
emit(state.copyWith(
|
||||
placePickerRequest: Unique(const _PlacePickerRequest()),
|
||||
));
|
||||
} finally {
|
||||
emit(state.copyWith(isAddMapBusy: false));
|
||||
}
|
||||
}
|
||||
|
||||
void _onAddMapToCollection(_AddMapToCollection ev, Emitter<_State> emit) {
|
||||
_log.info(ev);
|
||||
assert(isCollectionCapabilityPermitted(CollectionCapability.mapItem));
|
||||
|
@ -597,6 +627,7 @@ class _Bloc extends Bloc<_Event, _State>
|
|||
final PrefController prefController;
|
||||
final CollectionsController collectionsController;
|
||||
final FilesController filesController;
|
||||
final NpDb db;
|
||||
late final CollectionItemsController itemsController;
|
||||
|
||||
/// Specify if the supplied [collection] is an "inline" one, which means it's
|
||||
|
|
|
@ -21,6 +21,8 @@ class _State {
|
|||
this.editItems,
|
||||
this.editTransformedItems,
|
||||
this.editSort,
|
||||
required this.isAddMapBusy,
|
||||
required this.placePickerRequest,
|
||||
required this.isDragging,
|
||||
required this.zoom,
|
||||
this.scale,
|
||||
|
@ -47,6 +49,8 @@ class _State {
|
|||
isSelectionDeletable: true,
|
||||
isEditMode: false,
|
||||
isEditBusy: false,
|
||||
isAddMapBusy: false,
|
||||
placePickerRequest: Unique(null),
|
||||
isDragging: false,
|
||||
zoom: zoom,
|
||||
);
|
||||
|
@ -76,6 +80,8 @@ class _State {
|
|||
final List<CollectionItem>? editItems;
|
||||
final List<_Item>? editTransformedItems;
|
||||
final CollectionItemSort? editSort;
|
||||
final bool isAddMapBusy;
|
||||
final Unique<_PlacePickerRequest?> placePickerRequest;
|
||||
|
||||
final bool isDragging;
|
||||
|
||||
|
@ -174,6 +180,14 @@ class _AddLabelToCollection implements _Event {
|
|||
final String label;
|
||||
}
|
||||
|
||||
@toString
|
||||
class _RequestAddMap implements _Event {
|
||||
const _RequestAddMap();
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
}
|
||||
|
||||
@toString
|
||||
class _AddMapToCollection implements _Event {
|
||||
const _AddMapToCollection(this.location);
|
||||
|
|
|
@ -221,6 +221,14 @@ class _DateItem extends _Item {
|
|||
final Date date;
|
||||
}
|
||||
|
||||
class _PlacePickerRequest {
|
||||
const _PlacePickerRequest({
|
||||
this.initialPosition,
|
||||
});
|
||||
|
||||
final MapCoord? initialPosition;
|
||||
}
|
||||
|
||||
@toString
|
||||
class _ArchiveFailedError implements Exception {
|
||||
const _ArchiveFailedError(this.count);
|
||||
|
|
|
@ -214,7 +214,6 @@ class _WrappedAppState extends State<_WrappedApp>
|
|||
ArchiveBrowser.routeName: ArchiveBrowser.buildRoute,
|
||||
TrustedCertManager.routeName: TrustedCertManager.buildRoute,
|
||||
MapBrowser.routeName: MapBrowser.buildRoute,
|
||||
PlacePicker.routeName: PlacePicker.buildRoute,
|
||||
};
|
||||
|
||||
Route<dynamic>? _onGenerateRoute(RouteSettings settings) {
|
||||
|
@ -242,6 +241,7 @@ class _WrappedAppState extends State<_WrappedApp>
|
|||
route ??= _handleImageEnhancerRoute(settings);
|
||||
route ??= _handleCollectionBrowserRoute(settings);
|
||||
route ??= _handleAccountSettingsRoute(settings);
|
||||
route ??= _handlePlacePickerRoute(settings);
|
||||
return route;
|
||||
}
|
||||
|
||||
|
@ -551,6 +551,18 @@ class _WrappedAppState extends State<_WrappedApp>
|
|||
return null;
|
||||
}
|
||||
|
||||
Route<dynamic>? _handlePlacePickerRoute(RouteSettings settings) {
|
||||
try {
|
||||
if (settings.name == PlacePicker.routeName) {
|
||||
final args = settings.arguments as PlacePickerArguments?;
|
||||
return PlacePicker.buildRoute(args, settings);
|
||||
}
|
||||
} catch (e) {
|
||||
_log.severe("[_handlePlacePickerRoute] Failed while handling route", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
late final _bloc = context.read<_Bloc>();
|
||||
final _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
|
||||
final _navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
|
|
@ -2,8 +2,13 @@ part of 'place_picker.dart';
|
|||
|
||||
@npLog
|
||||
class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
||||
_Bloc() : super(_State.init()) {
|
||||
_Bloc({
|
||||
required this.prefController,
|
||||
required this.initialPosition,
|
||||
required this.initialZoom,
|
||||
}) : super(_State.init()) {
|
||||
on<_SetPosition>(_onSetPosition);
|
||||
on<_Done>(_onDone);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -18,4 +23,17 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
|||
// _log.info(ev);
|
||||
emit(state.copyWith(position: ev.value));
|
||||
}
|
||||
|
||||
void _onDone(_Done ev, _Emitter emit) {
|
||||
_log.info(ev);
|
||||
if (prefController.mapBrowserPrevPositionValue == null &&
|
||||
state.position != null) {
|
||||
prefController.setMapBrowserPrevPosition(state.position!.center);
|
||||
}
|
||||
emit(state.copyWith(isDone: true));
|
||||
}
|
||||
|
||||
final PrefController prefController;
|
||||
final MapCoord? initialPosition;
|
||||
final double? initialZoom;
|
||||
}
|
||||
|
|
|
@ -14,24 +14,54 @@ part 'bloc.dart';
|
|||
part 'place_picker.g.dart';
|
||||
part 'state_event.dart';
|
||||
|
||||
class PlacePickerArguments {
|
||||
const PlacePickerArguments({
|
||||
this.initialPosition,
|
||||
this.initialZoom,
|
||||
});
|
||||
|
||||
final MapCoord? initialPosition;
|
||||
final double? initialZoom;
|
||||
}
|
||||
|
||||
class PlacePicker extends StatelessWidget {
|
||||
static const routeName = "/place-picker";
|
||||
|
||||
static Route buildRoute(RouteSettings settings) =>
|
||||
static Route buildRoute(PlacePickerArguments? args, RouteSettings settings) =>
|
||||
MaterialPageRoute<CameraPosition>(
|
||||
builder: (_) => const PlacePicker(),
|
||||
builder: (_) => PlacePicker.fromArgs(args),
|
||||
settings: settings,
|
||||
);
|
||||
|
||||
const PlacePicker({super.key});
|
||||
const PlacePicker({
|
||||
super.key,
|
||||
required this.initialPosition,
|
||||
required this.initialZoom,
|
||||
});
|
||||
|
||||
PlacePicker.fromArgs(
|
||||
PlacePickerArguments? args, {
|
||||
Key? key,
|
||||
}) : this(
|
||||
key: key,
|
||||
initialPosition: args?.initialPosition,
|
||||
initialZoom: args?.initialZoom,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => _Bloc(),
|
||||
create: (context) => _Bloc(
|
||||
prefController: context.read(),
|
||||
initialPosition: initialPosition,
|
||||
initialZoom: initialZoom,
|
||||
),
|
||||
child: const _WrappedPlacePicker(),
|
||||
);
|
||||
}
|
||||
|
||||
final MapCoord? initialPosition;
|
||||
final double? initialZoom;
|
||||
}
|
||||
|
||||
@npLog
|
||||
|
@ -40,19 +70,31 @@ class _WrappedPlacePicker extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.global().placePickerTitle),
|
||||
leading: IconButton(
|
||||
onPressed: () {
|
||||
final position = context.state.position;
|
||||
_log.info("[build] Position picked: $position");
|
||||
Navigator.of(context).pop(position);
|
||||
return MultiBlocListener(
|
||||
listeners: [
|
||||
_BlocListenerT(
|
||||
selector: (state) => state.isDone,
|
||||
listener: (context, isDone) {
|
||||
if (isDone) {
|
||||
final position = context.state.position;
|
||||
_log.info("[build] Position picked: $position");
|
||||
Navigator.of(context).pop(position);
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.check_outlined),
|
||||
),
|
||||
],
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.global().placePickerTitle),
|
||||
leading: IconButton(
|
||||
onPressed: () {
|
||||
context.addEvent(const _Done());
|
||||
},
|
||||
icon: const Icon(Icons.check_outlined),
|
||||
),
|
||||
),
|
||||
body: const _BodyView(),
|
||||
),
|
||||
body: const _BodyView(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -62,15 +104,16 @@ class _BodyView extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final prevPosition =
|
||||
context.read<PrefController>().mapBrowserPrevPositionValue;
|
||||
final position = context.bloc.initialPosition ??
|
||||
context.bloc.prefController.mapBrowserPrevPositionValue;
|
||||
return ValueStreamBuilderEx<GpsMapProvider>(
|
||||
stream: context.read<PrefController>().gpsMapProvider,
|
||||
builder: StreamWidgetBuilder.value(
|
||||
(context, gpsMapProvider) => PlacePickerView(
|
||||
providerHint: gpsMapProvider,
|
||||
initialPosition: prevPosition ?? const MapCoord(0, 0),
|
||||
initialZoom: prevPosition == null ? 2.5 : 10,
|
||||
initialPosition: position ?? const MapCoord(0, 0),
|
||||
initialZoom:
|
||||
context.bloc.initialZoom ?? (position == null ? 2.5 : 10),
|
||||
onCameraMove: (position) {
|
||||
context.addEvent(_SetPosition(position));
|
||||
},
|
||||
|
@ -82,7 +125,7 @@ class _BodyView extends StatelessWidget {
|
|||
|
||||
// typedef _BlocBuilder = BlocBuilder<_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 _Emitter = Emitter<_State>;
|
||||
|
||||
|
|
|
@ -13,18 +13,19 @@ part of 'place_picker.dart';
|
|||
// **************************************************************************
|
||||
|
||||
abstract class $_StateCopyWithWorker {
|
||||
_State call({CameraPosition? position});
|
||||
_State call({CameraPosition? position, bool? isDone});
|
||||
}
|
||||
|
||||
class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
||||
_$_StateCopyWithWorkerImpl(this.that);
|
||||
|
||||
@override
|
||||
_State call({dynamic position = copyWithNull}) {
|
||||
_State call({dynamic position = copyWithNull, dynamic isDone}) {
|
||||
return _State(
|
||||
position: position == copyWithNull
|
||||
? that.position
|
||||
: position as CameraPosition?);
|
||||
: position as CameraPosition?,
|
||||
isDone: isDone as bool? ?? that.isDone);
|
||||
}
|
||||
|
||||
final _State that;
|
||||
|
@ -61,7 +62,7 @@ extension _$_BlocNpLog on _Bloc {
|
|||
extension _$_StateToString on _State {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_State {position: $position}";
|
||||
return "_State {position: $position, isDone: $isDone}";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,3 +72,10 @@ extension _$_SetPositionToString on _SetPosition {
|
|||
return "_SetPosition {value: $value}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$_DoneToString on _Done {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_Done {}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,14 +5,18 @@ part of 'place_picker.dart';
|
|||
class _State {
|
||||
const _State({
|
||||
this.position,
|
||||
required this.isDone,
|
||||
});
|
||||
|
||||
factory _State.init() => const _State();
|
||||
factory _State.init() => const _State(
|
||||
isDone: false,
|
||||
);
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final CameraPosition? position;
|
||||
final bool isDone;
|
||||
}
|
||||
|
||||
abstract class _Event {}
|
||||
|
@ -26,3 +30,11 @@ class _SetPosition implements _Event {
|
|||
|
||||
final CameraPosition value;
|
||||
}
|
||||
|
||||
@toString
|
||||
class _Done implements _Event {
|
||||
const _Done();
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
}
|
||||
|
|
|
@ -454,6 +454,13 @@ abstract class NpDb {
|
|||
List<String>? excludeRelativeRoots,
|
||||
});
|
||||
|
||||
/// Return the location data of the first file (sorted by date time in
|
||||
/// descending order) in a group of files
|
||||
Future<DbLocation?> getFirstLocationOfFileIds({
|
||||
required DbAccount account,
|
||||
required List<int> fileIds,
|
||||
});
|
||||
|
||||
/// Return the latitude, longitude and the file id of all files
|
||||
Future<List<DbImageLatLng>> getImageLatLngWithFileIds({
|
||||
required DbAccount account,
|
||||
|
|
|
@ -200,6 +200,64 @@ extension SqliteDbImageLocationExtension on SqliteDb {
|
|||
}).get();
|
||||
}
|
||||
|
||||
Future<ImageLocation?> queryFirstImageLocationByFileIds({
|
||||
required ByAccount account,
|
||||
required List<int> fileIds,
|
||||
}) async {
|
||||
final candidates = await fileIds.withPartition((sublist) async {
|
||||
final query = selectOnly(files).join([
|
||||
innerJoin(accountFiles, accountFiles.file.equalsExp(files.rowId),
|
||||
useColumns: false),
|
||||
if (account.dbAccount != null) ...[
|
||||
innerJoin(accounts, accounts.rowId.equalsExp(accountFiles.account),
|
||||
useColumns: false),
|
||||
innerJoin(servers, servers.rowId.equalsExp(accounts.server),
|
||||
useColumns: false),
|
||||
],
|
||||
innerJoin(imageLocations,
|
||||
imageLocations.accountFile.equalsExp(accountFiles.rowId),
|
||||
useColumns: false),
|
||||
]);
|
||||
query.addColumns([
|
||||
accountFiles.rowId,
|
||||
accountFiles.bestDateTime,
|
||||
]);
|
||||
|
||||
if (account.sqlAccount != null) {
|
||||
query.where(accountFiles.account.equals(account.sqlAccount!.rowId));
|
||||
} else if (account.dbAccount != null) {
|
||||
query
|
||||
..where(servers.address.equals(account.dbAccount!.serverAddress))
|
||||
..where(accounts.userId
|
||||
.equals(account.dbAccount!.userId.toCaseInsensitiveString()));
|
||||
}
|
||||
|
||||
query
|
||||
..where(files.fileId.isIn(sublist))
|
||||
..where(imageLocations.latitude.isNotNull() &
|
||||
imageLocations.longitude.isNotNull())
|
||||
..orderBy([OrderingTerm.desc(accountFiles.bestDateTime)])
|
||||
..limit(1);
|
||||
return [
|
||||
await query
|
||||
.map((r) => (
|
||||
rowId: r.read(accountFiles.rowId)!,
|
||||
dateTime: r.read(accountFiles.bestDateTime)!,
|
||||
))
|
||||
.getSingleOrNull()
|
||||
];
|
||||
}, _maxByFileIdsSize);
|
||||
final winner =
|
||||
candidates.nonNulls.sortedBy((e) => e.dateTime).reversed.firstOrNull;
|
||||
if (winner == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final reusltQuery = select(imageLocations)
|
||||
..where((t) => t.accountFile.equals(winner.rowId));
|
||||
return reusltQuery.getSingleOrNull();
|
||||
}
|
||||
|
||||
Future<List<ImageLocationGroup>> _groupImageLocationsBy({
|
||||
required ByAccount account,
|
||||
required GeneratedColumn<String> by,
|
||||
|
|
|
@ -607,6 +607,20 @@ class NpDbSqlite implements NpDb {
|
|||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DbLocation?> getFirstLocationOfFileIds({
|
||||
required DbAccount account,
|
||||
required List<int> fileIds,
|
||||
}) async {
|
||||
final sqlObj = await _db.use((db) async {
|
||||
return await db.queryFirstImageLocationByFileIds(
|
||||
account: ByAccount.db(account),
|
||||
fileIds: fileIds,
|
||||
);
|
||||
});
|
||||
return sqlObj?.let(ImageLocationConverter.fromSql);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<DbImageLatLng>> getImageLatLngWithFileIds({
|
||||
required DbAccount account,
|
||||
|
|
Loading…
Reference in a new issue