Ensure map marker clicks preserve search context

Fixes the back button just closing the sidebar and clearing the whole
search after having seleted a result via map marker
This commit is contained in:
2026-03-23 16:32:49 +04:00
parent 46605dbd32
commit 818ec35071
7 changed files with 45 additions and 25 deletions

View File

@@ -1099,6 +1099,20 @@ export default class MapComponent extends Component {
} }
} }
// Helper to transition with proper state
const transitionToPlace = (place) => {
// If we are currently in search mode OR have active search results,
// we want the "Back" button on the place details to return to the search results.
if (
this.router.currentRouteName === 'search' ||
(this.mapUi.currentSearch && this.mapUi.searchResults.length > 0)
) {
this.mapUi.returnToSearch = true;
}
this.mapUi.preventNextZoom = true;
this.router.transitionTo('place', place);
};
// Special handling when sidebar is OPEN // Special handling when sidebar is OPEN
if (this.args.isSidebarOpen) { if (this.args.isSidebarOpen) {
// If it's a bookmark or search result, we allow "switching" to it even if sidebar is open // If it's a bookmark or search result, we allow "switching" to it even if sidebar is open
@@ -1108,8 +1122,7 @@ export default class MapComponent extends Component {
'Clicked feature while sidebar open (switching):', 'Clicked feature while sidebar open (switching):',
targetPlace targetPlace
); );
this.mapUi.preventNextZoom = true; transitionToPlace(targetPlace);
this.router.transitionTo('place', targetPlace);
return; return;
} }
@@ -1123,15 +1136,13 @@ export default class MapComponent extends Component {
// Normal behavior (sidebar is closed) // Normal behavior (sidebar is closed)
if (clickedBookmark) { if (clickedBookmark) {
console.debug('Clicked bookmark:', clickedBookmark); console.debug('Clicked bookmark:', clickedBookmark);
this.mapUi.preventNextZoom = true; transitionToPlace(clickedBookmark);
this.router.transitionTo('place', clickedBookmark);
return; return;
} }
if (clickedSearchResult) { if (clickedSearchResult) {
console.debug('Clicked search result:', clickedSearchResult); console.debug('Clicked search result:', clickedSearchResult);
this.mapUi.preventNextZoom = true; transitionToPlace(clickedSearchResult);
this.router.transitionTo('place', clickedSearchResult);
return; return;
} }

View File

@@ -170,6 +170,10 @@ export default class SearchRoute extends Route {
// Ensure pulse is stopped if we reach here // Ensure pulse is stopped if we reach here
this.mapUi.stopSearch(); this.mapUi.stopSearch();
this.mapUi.setSearchResults(model); this.mapUi.setSearchResults(model);
// Store current search params to allow "Up" navigation from place details
const { q, category, lat, lon } = this.paramsFor('search');
this.mapUi.currentSearch = { q, category, lat, lon };
} }
@action @action

View File

@@ -13,6 +13,7 @@ export default class MapUiService extends Service {
@tracked selectionOptions = {}; @tracked selectionOptions = {};
@tracked preventNextZoom = false; @tracked preventNextZoom = false;
@tracked searchResults = []; @tracked searchResults = [];
@tracked currentSearch = null;
selectPlace(place, options = {}) { selectPlace(place, options = {}) {
this.selectedPlace = place; this.selectedPlace = place;
@@ -31,6 +32,7 @@ export default class MapUiService extends Service {
clearSearchResults() { clearSearchResults() {
this.searchResults = []; this.searchResults = [];
this.currentSearch = null;
} }
startSearch() { startSearch() {

View File

@@ -77,9 +77,11 @@ export default class PlaceTemplate extends Component {
navigateBack(place) { navigateBack(place) {
// The sidebar calls this with null when "Back" is clicked. // The sidebar calls this with null when "Back" is clicked.
if (place === null) { if (place === null) {
// If we came from search results, go back in history // If we have an active search context, return to it (UP navigation)
if (this.mapUi.returnToSearch) { if (this.mapUi.returnToSearch && this.mapUi.currentSearch) {
window.history.back(); this.router.transitionTo('search', {
queryParams: this.mapUi.currentSearch
});
} else { } else {
// Otherwise just close the sidebar (return to map index) // Otherwise just close the sidebar (return to map index)
this.router.transitionTo('index'); this.router.transitionTo('index');

View File

@@ -11,6 +11,8 @@ export default class SearchTemplate extends Component {
selectPlace(place) { selectPlace(place) {
if (place) { if (place) {
this.mapUi.returnToSearch = true; this.mapUi.returnToSearch = true;
// We don't need to manually set currentSearch here because
// it was already set in the route's setupController
this.router.transitionTo('place', place); this.router.transitionTo('place', place);
} }
} }

View File

@@ -64,25 +64,24 @@ module('Acceptance | navigation', function (hooks) {
this.owner.register('service:storage', MockStorageService); this.owner.register('service:storage', MockStorageService);
}); });
test('navigating from search results to place and back uses history', async function (assert) { test('navigating from search results to place and back returns to search', async function (assert) {
const mapUi = this.owner.lookup('service:map-ui'); const mapUi = this.owner.lookup('service:map-ui');
const backStub = sinon.stub(window.history, 'back');
try { await visit('/search?lat=1&lon=1');
await visit('/search?lat=1&lon=1'); assert.strictEqual(currentURL(), '/search?lat=1&lon=1');
assert.strictEqual(currentURL(), '/search?lat=1&lon=1');
await click('.place-item'); await click('.place-item');
assert.ok(currentURL().includes('/place/'), 'Navigated to place'); assert.ok(currentURL().includes('/place/'), 'Navigated to place');
assert.true(mapUi.returnToSearch, 'Flag returnToSearch is set'); assert.true(mapUi.returnToSearch, 'Flag returnToSearch is set');
// Click the back button in the sidebar // Click the back button in the sidebar
await click('.back-btn'); await click('.back-btn');
assert.true(backStub.calledOnce, 'window.history.back() was called'); assert.strictEqual(
} finally { currentURL(),
backStub.restore(); '/search?lat=1&lon=1',
} 'Returned to search results'
);
}); });
test('closing the sidebar resets the returnToSearch flag', async function (assert) { test('closing the sidebar resets the returnToSearch flag', async function (assert) {

View File

@@ -27,9 +27,9 @@ module('Unit | Utility | osm-icons', function () {
assert.strictEqual(result, 'shopping-basket'); assert.strictEqual(result, 'shopping-basket');
}); });
test('it returns shopping-basket for unknown shop types (catch-all)', function (assert) { test('it returns shopping-bag for unknown shop types (catch-all)', function (assert) {
let result = getIconNameForTags({ shop: 'unknown_shop_type' }); let result = getIconNameForTags({ shop: 'unknown_shop_type' });
assert.strictEqual(result, 'shopping-basket'); assert.strictEqual(result, 'shopping-bag');
}); });
test('it returns null for unknown tags', function (assert) { test('it returns null for unknown tags', function (assert) {