Map browser now default to the position of the last known latest photo

This commit is contained in:
Ming Ming 2024-07-20 03:34:46 +08:00
parent 0c8b611e46
commit 8c8dcb3e8e
11 changed files with 95 additions and 16 deletions

View file

@ -1,11 +1,14 @@
// ignore_for_file: deprecated_member_use_from_same_package
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart';
import 'package:nc_photos/di_container.dart';
import 'package:nc_photos/entity/collection/util.dart';
import 'package:nc_photos/entity/pref.dart';
import 'package:nc_photos/json_util.dart';
import 'package:nc_photos/language_util.dart';
import 'package:nc_photos/object_extension.dart';
import 'package:nc_photos/protected_page_handler.dart';
@ -158,6 +161,13 @@ class PrefController {
value: value,
);
Future<bool> setMapBrowserPrevPosition(MapCoord value) => _set<MapCoord?>(
controller: _mapBrowserPrevPositionController,
setter: (pref, value) => pref.setMapBrowserPrevPosition(
jsonEncode([value!.latitude, value.longitude])),
value: value,
);
Future<bool> _set<T>({
required BehaviorSubject<T> controller,
required Future<bool> Function(Pref pref, T value) setter,
@ -258,6 +268,11 @@ class PrefController {
@npSubjectAccessor
late final _isDontShowVideoPreviewHintController =
BehaviorSubject.seeded(_c.pref.isDontShowVideoPreviewHintOr(false));
@npSubjectAccessor
late final _mapBrowserPrevPositionController = BehaviorSubject.seeded(_c.pref
.getMapBrowserPrevPosition()
?.let(tryJsonDecode)
?.let(_tryMapCoordFromJson));
}
@npSubjectAccessor

View file

@ -157,6 +157,15 @@ extension $PrefControllerNpSubjectAccessor on PrefController {
isDontShowVideoPreviewHint.distinct().skip(1);
bool get isDontShowVideoPreviewHintValue =>
_isDontShowVideoPreviewHintController.value;
// _mapBrowserPrevPositionController
ValueStream<MapCoord?> get mapBrowserPrevPosition =>
_mapBrowserPrevPositionController.stream;
Stream<MapCoord?> get mapBrowserPrevPositionNew =>
mapBrowserPrevPosition.skip(1);
Stream<MapCoord?> get mapBrowserPrevPositionChange =>
mapBrowserPrevPosition.distinct().skip(1);
MapCoord? get mapBrowserPrevPositionValue =>
_mapBrowserPrevPositionController.value;
}
extension $SecurePrefControllerNpSubjectAccessor on SecurePrefController {

View file

@ -88,4 +88,20 @@ extension on Pref {
isDontShowVideoPreviewHint() ?? def;
Future<bool> setDontShowVideoPreviewHint(bool value) =>
provider.setBool(PrefKey.dontShowVideoPreviewHint, value);
String? getMapBrowserPrevPosition() =>
provider.getString(PrefKey.mapBrowserPrevPosition);
Future<bool> setMapBrowserPrevPosition(String value) =>
provider.setString(PrefKey.mapBrowserPrevPosition, value);
}
MapCoord? _tryMapCoordFromJson(dynamic json) {
try {
final j = (json as List).cast<double>();
return MapCoord(j[0], j[1]);
} catch (e, stackTrace) {
_$__NpLog.log
.severe("[_tryMapCoordFromJson] Failed to parse json", e, stackTrace);
return null;
}
}

View file

@ -113,6 +113,7 @@ enum PrefKey implements PrefKeyInterface {
protectedPageAuthPin,
protectedPageAuthPassword,
dontShowVideoPreviewHint,
mapBrowserPrevPosition,
;
@override
@ -199,6 +200,8 @@ enum PrefKey implements PrefKeyInterface {
return "protectedPageAuthPassword";
case PrefKey.dontShowVideoPreviewHint:
return "dontShowVideoPreviewHint";
case PrefKey.mapBrowserPrevPosition:
return "mapBrowserPrevPosition";
}
}
}

View file

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:nc_photos/object_extension.dart';
/// Convert a boolean to an indexable type in json for DB
@ -8,3 +10,14 @@ Object? boolToJson(bool? value) => value?.run((v) => v ? 1 : 0);
/// Convert a boolean from an indexable type in json for DB
bool? boolFromJson(Object? value) => value?.run((v) => v != 0);
Object? tryJsonDecode(String source) {
try {
return jsonDecode(source);
} catch (_) {
return null;
}
}
Object? jsonDecodeOr(String source, dynamic def) =>
tryJsonDecode(source) ?? def;

View file

@ -15,6 +15,7 @@ import 'package:nc_photos/account.dart';
import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/bloc_util.dart';
import 'package:nc_photos/controller/account_controller.dart';
import 'package:nc_photos/controller/pref_controller.dart';
import 'package:nc_photos/di_container.dart';
import 'package:nc_photos/entity/collection.dart';
import 'package:nc_photos/entity/collection/content_provider/ad_hoc.dart';
@ -27,7 +28,9 @@ import 'package:nc_photos/theme.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_common/object_util.dart';
import 'package:np_datetime/np_datetime.dart';
import 'package:np_gps_map/np_gps_map.dart';
import 'package:to_string/to_string.dart';
part 'map_browser.g.dart';
@ -51,6 +54,7 @@ class MapBrowser extends StatelessWidget {
create: (_) => _Bloc(
KiwiContainer().resolve(),
account: context.read<AccountController>().account,
prefController: context.read(),
)..add(const _LoadData()),
child: const _WrappedMapBrowser(),
);

View file

@ -15,7 +15,7 @@ part of 'map_browser.dart';
abstract class $_StateCopyWithWorker {
_State call(
{List<_DataPoint>? data,
LatLng? initialPoint,
MapCoord? initialPoint,
Set<Marker>? markers,
bool? isShowDataRangeControlPanel,
_DateRangeType? dateRangeType,
@ -39,7 +39,7 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
data: data as List<_DataPoint>? ?? that.data,
initialPoint: initialPoint == copyWithNull
? that.initialPoint
: initialPoint as LatLng?,
: initialPoint as MapCoord?,
markers: markers as Set<Marker>? ?? that.markers,
isShowDataRangeControlPanel: isShowDataRangeControlPanel as bool? ??
that.isShowDataRangeControlPanel,

View file

@ -6,6 +6,7 @@ class _Bloc extends Bloc<_Event, _State>
_Bloc(
this._c, {
required this.account,
required this.prefController,
}) : super(_State.init(
dateRangeType: _DateRangeType.thisMonth,
localDateRange:
@ -59,13 +60,21 @@ class _Bloc extends Bloc<_Event, _State>
);
final raw = await _c.imageLocationRepo.getLocations(account, utcTimeRange);
_log.info("[_onLoadData] Loaded ${raw.length} markers");
emit(state.copyWith(
data: raw.map(_DataPoint.fromImageLatLng).toList(),
initialPoint: state.initialPoint ??
(raw.firstOrNull == null
? null
: LatLng(raw.first.latitude, raw.first.longitude)),
));
if (state.initialPoint == null) {
final initialPoint =
raw.firstOrNull?.let((obj) => MapCoord(obj.latitude, obj.longitude));
if (initialPoint != null) {
unawaited(prefController.setMapBrowserPrevPosition(initialPoint));
}
emit(state.copyWith(
data: raw.map(_DataPoint.fromImageLatLng).toList(),
initialPoint: initialPoint,
));
} else {
emit(state.copyWith(
data: raw.map(_DataPoint.fromImageLatLng).toList(),
));
}
}
void _onSetMarkers(_SetMarkers ev, Emitter<_State> emit) {
@ -151,6 +160,7 @@ class _Bloc extends Bloc<_Event, _State>
final DiContainer _c;
final Account account;
final PrefController prefController;
final _subscriptions = <StreamSubscription>[];

View file

@ -30,7 +30,7 @@ class _State {
String toString() => _$toString();
final List<_DataPoint> data;
final LatLng? initialPoint;
final MapCoord? initialPoint;
final Set<Marker> markers;
final bool isShowDataRangeControlPanel;

View file

@ -40,3 +40,7 @@ enum _DateRangeType {
}
}
}
extension on MapCoord {
LatLng toLatLng() => LatLng(latitude, longitude);
}

View file

@ -18,12 +18,12 @@ class _MapViewState extends State<_MapView> {
_clusterManager.setItems(data);
},
),
_BlocListenerT<LatLng?>(
_BlocListenerT<MapCoord?>(
selector: (state) => state.initialPoint,
listener: (context, initialPoint) {
if (initialPoint != null) {
_mapController
?.animateCamera(CameraUpdate.newLatLngZoom(initialPoint, 10));
_mapController?.animateCamera(
CameraUpdate.newLatLngZoom(initialPoint.toLatLng(), 10));
}
},
),
@ -32,14 +32,19 @@ class _MapViewState extends State<_MapView> {
buildWhen: (previous, current) => previous.markers != current.markers,
builder: (context, state) => GoogleMap(
mapType: MapType.normal,
initialCameraPosition: const CameraPosition(target: LatLng(0, 0)),
initialCameraPosition: context
.read<PrefController>()
.mapBrowserPrevPositionValue
?.let(
(p) => CameraPosition(target: p.toLatLng(), zoom: 10)) ??
const CameraPosition(target: LatLng(0, 0)),
markers: state.markers,
onMapCreated: (controller) {
_clusterManager.setMapId(controller.mapId);
_mapController = controller;
if (state.initialPoint != null) {
controller.animateCamera(
CameraUpdate.newLatLngZoom(state.initialPoint!, 10));
controller.animateCamera(CameraUpdate.newLatLngZoom(
state.initialPoint!.toLatLng(), 10));
}
},
onCameraMove: _clusterManager.onCameraMove,