diff --git a/src/App.vue b/src/App.vue index 7c2b637..8dc826d 100644 --- a/src/App.vue +++ b/src/App.vue @@ -160,6 +160,14 @@ export default defineComponent({ e.preventDefault(); store.commit(MutationTypes.TOGGLE_UI_ELEMENT_VISIBILITY, element); + }, + + onUrlChange = () => { + const parsedUrl = parseUrl(); + + if(parsedUrl) { + store.commit(MutationTypes.SET_PARSED_URL, parsedUrl); + } }; watch(title, (title) => document.title = title); @@ -200,10 +208,12 @@ export default defineComponent({ onMounted(() => { window.addEventListener('resize', onResize); window.addEventListener('keydown', onKeydown); + window.addEventListener('hashchange', onUrlChange); }); onUnmounted(() => { window.addEventListener('resize', onResize); window.addEventListener('keydown', onKeydown); + window.addEventListener('hashchange', onUrlChange); }); return { diff --git a/src/components/Map.vue b/src/components/Map.vue index b912431..fbf6a71 100644 --- a/src/components/Map.vue +++ b/src/components/Map.vue @@ -86,6 +86,7 @@ export default defineComponent({ followTarget = computed(() => store.state.followTarget), panTarget = computed(() => store.state.panTarget), + parsedUrl = computed(() => store.state.parsedUrl), //Location and zoom to pan to upon next projection change scheduledPan = ref(null), @@ -108,6 +109,7 @@ export default defineComponent({ logoControls, followTarget, panTarget, + parsedUrl, mapBackground, currentWorld, @@ -167,30 +169,69 @@ export default defineComponent({ store.dispatch(ActionTypes.GET_MARKER_SETS, undefined); - //Abort if follow target is present, to avoid panning twice + // Abort if follow target is present, to avoid panning twice if(store.state.followTarget && store.state.followTarget.location.world === newValue.name) { return; - //Abort if pan target is present, to avoid panning to the wrong place. + // Abort if pan target is present, to avoid panning to the wrong place. // Also clear it to allow repeated panning. } else if(store.state.panTarget && store.state.panTarget.location.world === newValue.name) { store.commit(MutationTypes.CLEAR_PAN_TARGET, undefined); return; - //Otherwise pan to url location, if present and if we have just loaded - } else if(!oldValue && store.state.parsedUrl.location) { + // Otherwise pan to url location, if present + } else if(store.state.parsedUrl?.location) { location = store.state.parsedUrl.location; - //Otherwise pan to world center + store.commit(MutationTypes.CLEAR_PARSED_URL, undefined); + // Otherwise pan to world center } else { location = newValue.center; } if(!oldValue) { - this.scheduledZoom = typeof store.state.parsedUrl.zoom !== 'undefined' ? - store.state.parsedUrl.zoom : store.state.configuration.defaultZoom; + this.scheduledZoom = store.state.parsedUrl?.zoom || store.state.configuration.defaultZoom; } //Set pan location for when the projection changes this.scheduledPan = location; } + }, + parsedUrl: { + handler(newValue) { + if(!newValue || !this.currentWorld || !this.leaflet) { + return; + } + + //URL points to different map + if(newValue.world !== this.currentWorld.name || newValue.map !== this.currentMap!.name) { + //Set scheduled pan for after map change + this.scheduledPan = newValue.location; + this.scheduledZoom = newValue.zoom; + + try { + useStore().commit(MutationTypes.SET_CURRENT_MAP, { + worldName: newValue.world, + mapName: newValue.map + }); + } catch(e) { + //Clear scheduled pan if change fails + console.warn(`Failed to handle URL change`, e); + this.scheduledPan = null; + this.scheduledZoom = null; + } + } else { //Same map, just pan + this.scheduledPan = null; + this.scheduledZoom = null; + + this.leaflet.setZoom(newValue.zoom, { + animate: false, + }); + + this.leaflet.panTo(this.currentProjection.locationToLatLng(newValue.location), { + animate: false, + noMoveStart: true, + }); + } + }, + deep: true, } }, diff --git a/src/store/actions.ts b/src/store/actions.ts index 602e658..58ad40c 100644 --- a/src/store/actions.ts +++ b/src/store/actions.ts @@ -109,7 +109,7 @@ export const actions: ActionTree & Actions = { } // Prefer world from parsed url if present and it exists - if(state.parsedUrl.world && state.worlds.has(state.parsedUrl.world)) { + if(state.parsedUrl?.world && state.worlds.has(state.parsedUrl.world)) { worldName = state.parsedUrl.world; } @@ -127,7 +127,7 @@ export const actions: ActionTree & Actions = { } // Prefer map from parsed url if present and it exists - if(state.parsedUrl.map && world.maps.has(state.parsedUrl.map)) { + if(state.parsedUrl?.map && world.maps.has(state.parsedUrl.map)) { mapName = state.parsedUrl.map; } diff --git a/src/store/mutations.ts b/src/store/mutations.ts index b5ad394..0a79289 100644 --- a/src/store/mutations.ts +++ b/src/store/mutations.ts @@ -507,13 +507,7 @@ export const mutations: MutationTree & Mutations = { //Clear any existing parsed url [MutationTypes.CLEAR_PARSED_URL](state: State) { - state.parsedUrl = { - world: undefined, - map: undefined, - location: undefined, - zoom: undefined, - legacy: false, - }; + state.parsedUrl = undefined; }, //Set the follow target, which the map will automatically pan to keep in view diff --git a/src/store/state.ts b/src/store/state.ts index 0ec1d27..03aa17d 100644 --- a/src/store/state.ts +++ b/src/store/state.ts @@ -78,7 +78,7 @@ export type State = { } }; - parsedUrl: LiveAtlasParsedUrl; + parsedUrl?: LiveAtlasParsedUrl; } export const state: State = { @@ -228,13 +228,5 @@ export const state: State = { sidebar: { collapsedSections: new Set(), }, - }, - - parsedUrl: { - world: undefined, - map: undefined, - location: undefined, - zoom: undefined, - legacy: false, } };