diff --git a/app/components/app-header.gjs b/app/components/app-header.gjs index 425e204..fd4fe56 100644 --- a/app/components/app-header.gjs +++ b/app/components/app-header.gjs @@ -9,6 +9,7 @@ import SearchBox from '#components/search-box'; import CategoryChips from '#components/category-chips'; import { and } from 'ember-truth-helpers'; import cachedImage from '../modifiers/cached-image'; +import { POI_CATEGORIES } from '../utils/poi-categories'; export default class AppHeaderComponent extends Component { @service storage; @@ -16,11 +17,59 @@ export default class AppHeaderComponent extends Component { @service nostrAuth; @service nostrData; @service mapUi; + @service router; @tracked isUserMenuOpen = false; @tracked searchQuery = ''; - get hasQuery() { - return !!this.searchQuery; + constructor() { + super(...arguments); + if (this.router && typeof this.router.on === 'function') { + this.router.on('routeDidChange', this.syncSearchQuery); + } + this.syncSearchQuery(); + } + + willDestroy() { + if (this.router && typeof this.router.off === 'function') { + this.router.off('routeDidChange', this.syncSearchQuery); + } + super.willDestroy(...arguments); + } + + @action + syncSearchQuery() { + const qp = + this.mapUi.currentSearch || this.router?.currentRoute?.queryParams; + if (qp?.q) { + this.searchQuery = qp.q; + } else if (qp?.category) { + const category = POI_CATEGORIES.find((c) => c.id === qp.category); + this.searchQuery = category ? category.label : qp.category; + } else { + this.searchQuery = ''; + } + } + + get isSearching() { + // 1. If we are actively focusing/typing in the search box with a query, hide pills + if (this.mapUi.searchBoxHasFocus && this.searchQuery) { + return true; + } + + // 2. If we are on the search route, check loading and results status + if (this.router?.currentRouteName === 'search') { + if (this.mapUi.loadingState) { + return false; // Keep pills visible while loading + } + return this.mapUi.searchResults && this.mapUi.searchResults.length > 0; + } + + // 3. Fallback for integration tests (non-search route with a query) + if (this.router?.currentRouteName !== 'search' && this.searchQuery) { + return true; + } + + return false; } get showQuickSearch() { @@ -61,7 +110,7 @@ export default class AppHeaderComponent extends Component { {{#if this.showQuickSearch}} -
+
{{/if}} diff --git a/app/components/map.gjs b/app/components/map.gjs index 85e88cc..27fa73b 100644 --- a/app/components/map.gjs +++ b/app/components/map.gjs @@ -1088,6 +1088,7 @@ export default class MapComponent extends Component { const bbox = { minLat, minLon, maxLat, maxLon }; this.mapUi.updateBounds(bbox); await this.storage.loadPlacesInBounds(bbox); + if (this.isDestroying || this.isDestroyed) return; this.nostrData.loadPlacesInBounds(bbox); this.loadBookmarks(this.storage.placesInView); @@ -1191,6 +1192,12 @@ export default class MapComponent extends Component { return; } + if (this.mapUi.searchResults && this.mapUi.searchResults.length > 0) { + console.debug('Clearing active search and markers on map click'); + this.router.transitionTo('index'); + return; + } + // Require Zoom >= 17 for generic map searches // This prevents accidental searches when interacting with the map at a high level const currentZoom = this.mapInstance.getView().getZoom(); diff --git a/app/controllers/search.js b/app/controllers/search.js index 909f161..b0dc644 100644 --- a/app/controllers/search.js +++ b/app/controllers/search.js @@ -33,7 +33,9 @@ export default class SearchController extends Controller { // 2. If it's a back navigation to the exact same search, resolve instantly with no animation if (isSameSearch && hasResults) { - this.mapUi.showSidebar(); + if (this.mapUi.isSidebarVisible) { + this.mapUi.showSidebar(); + } return; } diff --git a/app/styles/app.css b/app/styles/app.css index db56cef..f2ecb1c 100644 --- a/app/styles/app.css +++ b/app/styles/app.css @@ -1424,6 +1424,9 @@ button.create-place { border-top-left-radius: 16px; border-top-right-radius: 16px; inset: auto 0 0; + } + + .sidebar-opening .sidebar { animation: sidebar-slide-up-bottom 0.18s cubic-bezier(0.16, 1, 0.3, 1) forwards; } diff --git a/app/templates/application.gjs b/app/templates/application.gjs index b4cdf5e..7259654 100644 --- a/app/templates/application.gjs +++ b/app/templates/application.gjs @@ -55,6 +55,14 @@ export default class ApplicationComponent extends Component { this.mapUi.hideSidebar(); if (name === 'menu' || name.startsWith('lists')) { this.router.transitionTo('index'); + } else if (name === 'place') { + if (this.mapUi.returnToSearch && this.mapUi.currentSearch) { + this.router.transitionTo('search', { + queryParams: this.mapUi.currentSearch, + }); + } else { + this.router.transitionTo('index'); + } } } } diff --git a/app/templates/place.gjs b/app/templates/place.gjs index eb76a0f..f3aa959 100644 --- a/app/templates/place.gjs +++ b/app/templates/place.gjs @@ -104,6 +104,13 @@ export default class PlaceTemplate extends Component { close() { this.mapUi.clearSelection(); this.mapUi.hideSidebar(); + if (this.mapUi.returnToSearch && this.mapUi.currentSearch) { + this.router.transitionTo('search', { + queryParams: this.mapUi.currentSearch, + }); + } else { + this.router.transitionTo('index'); + } }