diff --git a/frontend/src/lib/components/facil-map-context-provider/map-context.ts b/frontend/src/lib/components/facil-map-context-provider/map-context.ts index 03fc9b6e..5ed3b95e 100644 --- a/frontend/src/lib/components/facil-map-context-provider/map-context.ts +++ b/frontend/src/lib/components/facil-map-context-provider/map-context.ts @@ -17,7 +17,7 @@ export interface MapComponents { bboxHandler: BboxHandler; container: HTMLElement; graphicScale: any; - hashHandler: HashHandler; + hashHandler: HashHandler & { _fmActivate: () => Promise }; linesLayer: LinesLayer; locateControl?: L.Control.Locate; map: Map; diff --git a/frontend/src/lib/components/facil-map-context-provider/search-form-tab-context.ts b/frontend/src/lib/components/facil-map-context-provider/search-form-tab-context.ts index 9ce635f6..5d2501e4 100644 --- a/frontend/src/lib/components/facil-map-context-provider/search-form-tab-context.ts +++ b/frontend/src/lib/components/facil-map-context-provider/search-form-tab-context.ts @@ -1,5 +1,5 @@ export interface WritableSearchFormTabContext { - setQuery(query: string, zoom?: boolean, smooth?: boolean, autofocus?: boolean): void; + setQuery(query: string, zoom?: boolean, smooth?: boolean, autofocus?: boolean): Promise; } export type SearchFormTabContext = Readonly; \ No newline at end of file diff --git a/frontend/src/lib/components/leaflet-map/leaflet-map-components.ts b/frontend/src/lib/components/leaflet-map/leaflet-map-components.ts index c0ada1af..633afbd0 100644 --- a/frontend/src/lib/components/leaflet-map/leaflet-map-components.ts +++ b/frontend/src/lib/components/leaflet-map/leaflet-map-components.ts @@ -1,4 +1,4 @@ -import { type Ref, ref, watch, markRaw, reactive, watchEffect, shallowRef, shallowReadonly, type Raw } from "vue"; +import { type Ref, ref, watch, markRaw, reactive, watchEffect, shallowRef, shallowReadonly, type Raw, nextTick } from "vue"; import { type Control, latLng, latLngBounds, type Map, map as leafletMap, DomUtil, control } from "leaflet"; import "leaflet/dist/leaflet.css"; import { BboxHandler, getSymbolHtml, getVisibleLayers, HashHandler, LinesLayer, MarkersLayer, SearchResultsLayer, OverpassLayer, OverpassLoadStatus, displayView, getInitialView, coreSymbolList } from "facilmap-leaflet"; @@ -15,7 +15,7 @@ import type { MapComponents, MapContextData, MapContextEvents, WritableMapContex import type { ClientContext } from "../facil-map-context-provider/client-context"; import type { FacilMapContext } from "../facil-map-context-provider/facil-map-context"; import { requireClientContext } from "../facil-map-context-provider/facil-map-context-provider.vue"; -import { type Optional, sleep } from "facilmap-utils"; +import { type Optional } from "facilmap-utils"; import { getI18n, i18nResourceChangeCounter } from "../../utils/i18n"; import { AttributionControl } from "./attribution"; import { fixOnCleanup } from "../../utils/vue"; @@ -310,31 +310,36 @@ function useSelectionHandler(map: Ref, context: FacilMapContext, mapContext ); } -function useHashHandler(map: Ref, client: Ref, context: FacilMapContext, mapContext: MapContextWithoutComponents, overpassLayer: Ref): Ref> { +function useHashHandler(map: Ref, client: Ref, context: FacilMapContext, mapContext: MapContextWithoutComponents, overpassLayer: Ref): Ref Promise }>> { return useMapComponent( map, - () => markRaw(new HashHandler(map.value, client.value, { overpassLayer: overpassLayer.value, simulate: !context.settings.updateHash })) - .on("fmQueryChange", async (e: any) => { - let smooth = true; - let autofocus = false; - if (!mapContext.components) { - // This is called while the hash handler is being enabled, so it is the initial view - smooth = false; - autofocus = context.settings.autofocus; - await sleep(0); // Wait for components to be initialized (needed by openSpecialQuery()) - } + () => { + let queryChangePromise: Promise | undefined; + const hashHandler = markRaw(new HashHandler(map.value, client.value, { overpassLayer: overpassLayer.value, simulate: !context.settings.updateHash })) + .on("fmQueryChange", async (e: any) => { + let smooth = true; + let autofocus = false; - const searchFormTab = context.components.searchFormTab; - if (!e.query) - searchFormTab?.setQuery("", false, false); - else if (!await openSpecialQuery(e.query, context, e.zoom, smooth)) - searchFormTab?.setQuery(e.query, e.zoom, smooth, autofocus); - }) - .on("fmHash", (e: any) => { - mapContext.hash = e.hash; - }), + const searchFormTab = context.components.searchFormTab; + queryChangePromise = (async () => { + if (!e.query) + await searchFormTab?.setQuery("", false, false); + else if (!await openSpecialQuery(e.query, context, e.zoom, smooth)) + await searchFormTab?.setQuery(e.query, e.zoom, smooth, autofocus); + })(); + await queryChangePromise; + }) + .on("fmHash", (e: any) => { + mapContext.hash = e.hash; + }); + return Object.assign(hashHandler, { + _fmActivate: async () => { + hashHandler.enable(); + await queryChangePromise; + } + }); + }, (hashHandler, onCleanup) => { - hashHandler.enable(); onCleanup(() => { hashHandler.disable(); }); @@ -420,20 +425,31 @@ export async function useMapContext(context: FacilMapContext, mapRef: Ref { + await nextTick(); // useMapContext() return promise is resolved, setting mapContext.value in + await nextTick(); // rerenders with its slot, search box tabs are now available and can receive the query from the hash handler - watchEffect(() => { - mapContext.activeQuery = getHashQuery(mapContext.components.map, client.value, mapContext.selection) || mapContext.fallbackQuery; - mapContext.components.hashHandler.setQuery(mapContext.activeQuery); - }); + await mapContext.components.hashHandler._fmActivate(); + + if (!map._loaded) { + try { + // Initial view was not set by hash handler + displayView(map, await getInitialView(client.value), { overpassLayer }); + } catch (error) { + console.error(error); + displayView(map, undefined, { overpassLayer }); + } + } + + watch(() => mapContext.components.hashHandler, async (hashHandler) => { + await hashHandler._fmActivate(); + }); + + watchEffect(() => { + mapContext.activeQuery = getHashQuery(mapContext.components.map, client.value, mapContext.selection) || mapContext.fallbackQuery; + mapContext.components.hashHandler.setQuery(mapContext.activeQuery); + }); + })().catch(console.error); return mapContext; } diff --git a/frontend/src/lib/components/search-form/search-form-tab.vue b/frontend/src/lib/components/search-form/search-form-tab.vue index f214c22f..ce229f93 100644 --- a/frontend/src/lib/components/search-form/search-form-tab.vue +++ b/frontend/src/lib/components/search-form/search-form-tab.vue @@ -27,10 +27,10 @@ } const searchFormTabContext: WritableSearchFormTabContext = { - setQuery(query, zoom = false, smooth = true, autofocus = false): void { + async setQuery(query, zoom = false, smooth = true, autofocus = false): Promise { searchForm.value!.setSearchString(query); - searchForm.value!.search(zoom, undefined, smooth); searchBoxContext.value.activateTab(`fm${context.id}-search-form-tab`, { expand: !!query, autofocus }); + await searchForm.value!.search(zoom, undefined, smooth); } }; diff --git a/leaflet/package.json b/leaflet/package.json index d97972dd..5a8b895b 100644 --- a/leaflet/package.json +++ b/leaflet/package.json @@ -47,7 +47,7 @@ "leaflet-auto-graticule": "^2.0.0", "leaflet-draggable-lines": "^2.0.0", "leaflet-freie-tonne": "^2.0.1", - "leaflet-highlightable-layers": "^3.0.0", + "leaflet-highlightable-layers": "^3.0.1", "leaflet.markercluster": "^1.5.3", "lodash-es": "^4.17.21" }, diff --git a/leaflet/src/lines/lines-layer.ts b/leaflet/src/lines/lines-layer.ts index 96a67abb..953d9a19 100644 --- a/leaflet/src/lines/lines-layer.ts +++ b/leaflet/src/lines/lines-layer.ts @@ -142,13 +142,13 @@ export default class LinesLayer extends FeatureGroup { highlightLine(id: ID): void { this.highlightedLinesIds.add(id); - if (this.client.lines[id]) + if (this._map && this.client.lines[id]) this.handleLine(this.client.lines[id]); } unhighlightLine(id: ID): void { this.highlightedLinesIds.delete(id); - if (this.client.lines[id]) + if (this._map && this.client.lines[id]) this.handleLine(this.client.lines[id]); } diff --git a/leaflet/src/markers/markers-layer.ts b/leaflet/src/markers/markers-layer.ts index 767b613d..fbc1bc6d 100644 --- a/leaflet/src/markers/markers-layer.ts +++ b/leaflet/src/markers/markers-layer.ts @@ -104,13 +104,13 @@ export default class MarkersLayer extends MarkerCluster { highlightMarker(id: ID): void { this.highlightedMarkerIds.add(id); - if (this.client.markers[id]) + if (this._map && this.client.markers[id]) this.handleMarker(this.client.markers[id]); } unhighlightMarker(id: ID): void { this.highlightedMarkerIds.delete(id); - if (this.client.markers[id]) + if (this._map && this.client.markers[id]) this.handleMarker(this.client.markers[id]); } diff --git a/yarn.lock b/yarn.lock index 0b595cb0..9248a0fa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4013,7 +4013,7 @@ __metadata: leaflet-auto-graticule: ^2.0.0 leaflet-draggable-lines: ^2.0.0 leaflet-freie-tonne: ^2.0.1 - leaflet-highlightable-layers: ^3.0.0 + leaflet-highlightable-layers: ^3.0.1 leaflet.markercluster: ^1.5.3 lodash-es: ^4.17.21 node-fetch: ^3.3.2 @@ -5490,12 +5490,12 @@ __metadata: languageName: node linkType: hard -"leaflet-highlightable-layers@npm:^3.0.0": - version: 3.0.0 - resolution: "leaflet-highlightable-layers@npm:3.0.0" +"leaflet-highlightable-layers@npm:^3.0.1": + version: 3.0.1 + resolution: "leaflet-highlightable-layers@npm:3.0.1" peerDependencies: leaflet: x - checksum: b0ffe1210f5f5cde618866980589f41b4083e6361624bece3bd8f1cce9e0498ad8554bea5791d73ab95fe36aeb23094f4aa372773c0c96036dcedc94ff865703 + checksum: 7f9478533eb86cf411f247b38a446c25d3530d077349896ba55527c9bb9300b4162ad6786a74b62ca103b8941c839336216412fd5fb03f120e247da3cb0946bf languageName: node linkType: hard