diff --git a/lib/routes/transaction_page.dart b/lib/routes/transaction_page.dart index 1adb043d..4528cc53 100644 --- a/lib/routes/transaction_page.dart +++ b/lib/routes/transaction_page.dart @@ -110,6 +110,12 @@ class _TransactionPageState extends State { Geo? _geo; bool _geoHandpicked = false; + /// Device's current location, fetched independently of [_geo]. + /// + /// Used to surface nearby tag suggestions even when editing an existing + /// transaction whose saved location differs from where the user is now. + Geo? _deviceGeo; + bool locationFailed = false; dynamic error; @@ -242,9 +248,9 @@ class _TransactionPageState extends State { _mapController = enableGeo ? MapController() : null; - if (widget.isNewTransaction) { - tryFetchLocation(); + tryFetchLocation(); + if (widget.isNewTransaction) { SchedulerBinding.instance.addPostFrameCallback((timeStamp) { _orchestrateFlow(transactionEntryFlow); }); @@ -445,6 +451,7 @@ class _TransactionPageState extends State { selectedTags: _selectedTags, onTagsChanged: onTagsChanged, location: _geo, + deviceLocation: _deviceGeo, ), DescriptionSection( value: _descriptionMarkdown, @@ -654,7 +661,10 @@ class _TransactionPageState extends State { void tryFetchLocation() { if (Platform.isLinux) return; if (LocalPreferences().enableGeo.get() != true) return; - if (LocalPreferences().autoAttachTransactionGeo.get() != true) return; + + final bool autoAttach = + widget.isNewTransaction && + LocalPreferences().autoAttachTransactionGeo.get() == true; Geolocator.getLastKnownPosition() .then((lastKnown) { @@ -662,12 +672,14 @@ class _TransactionPageState extends State { return; } - if (_geo != null) { - // In case we already have a location, don't override with less accurate one - return; - } + final Geo geo = Geo.fromPosition(lastKnown); + _deviceGeo = geo; - _geo = Geo.fromPosition(lastKnown); + // Only seed the transaction's location from a less-accurate + // last-known fix when we'd otherwise have nothing. + if (autoAttach && _geo == null) { + _geo = geo; + } if (mounted) setState(() => {}); }) @@ -677,7 +689,11 @@ class _TransactionPageState extends State { Geolocator.getCurrentPosition() .then((current) { - _geo = Geo.fromPosition(current); + final Geo geo = Geo.fromPosition(current); + _deviceGeo = geo; + if (autoAttach) { + _geo = geo; + } }) .catchError((e, stackTrace) { locationFailed = true; diff --git a/lib/routes/transaction_page/sections/tags_section.dart b/lib/routes/transaction_page/sections/tags_section.dart index 5c4e6d59..dac10f3d 100644 --- a/lib/routes/transaction_page/sections/tags_section.dart +++ b/lib/routes/transaction_page/sections/tags_section.dart @@ -17,28 +17,44 @@ class TagsSection extends StatelessWidget { final VoidCallback selectTags; final ValueChanged> onTagsChanged; - /// Used for suggesting nearby tags based on the transaction's location. + /// Transaction's saved location, used for suggesting nearby tags. final Geo? location; + /// Device's current location, used in addition to [location] so that + /// suggestions reflect both where the transaction happened and where the + /// user is now (useful when editing an older transaction in a new place). + final Geo? deviceLocation; + const TagsSection({ super.key, this.selectedTags, required this.selectTags, required this.onTagsChanged, this.location, + this.deviceLocation, }); @override Widget build(BuildContext context) { - final List? suggestedGeoTags = switch (location - ?.toLatLngPosition()) { - LatLng latLng => TransactionTagsProvider.of( - context, - ).getCloseGeoTags(latLng, exclusionList: selectedTags), - _ => null, - }; + final TransactionTagsProvider provider = TransactionTagsProvider.of( + context, + ); + + final List suggestionOrigins = [ + ?location?.toLatLngPosition(), + ?deviceLocation?.toLatLngPosition(), + ]; + + final Set seen = {}; + final List suggestedGeoTags = suggestionOrigins + .expand( + (origin) => + provider.getCloseGeoTags(origin, exclusionList: selectedTags), + ) + .where((tag) => seen.add(tag.uuid)) + .toList(); - final bool hasSuggestedGeoTags = suggestedGeoTags?.isNotEmpty == true; + final bool hasSuggestedGeoTags = suggestedGeoTags.isNotEmpty; return Section( title: "transaction.tags".t(context), @@ -61,7 +77,7 @@ class TagsSection extends StatelessWidget { onPressed: selectTags, title: "transaction.tags.add".t(context), ), - ...?suggestedGeoTags?.map( + ...suggestedGeoTags.map( (tag) => TransactionTagChip( tag: tag, selected: false,