ulogger-server/js/src/permalink.js
2020-06-10 22:07:44 +02:00

166 lines
4.6 KiB
JavaScript

/*
* μlogger
*
* Copyright(C) 2020 Bartek Fabiszewski (www.fabiszewski.net)
*
* This is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
import { lang as $, config } from './initializer.js';
import uTrack from './track.js';
import uUtils from './utils.js';
/**
* @typedef {Object} PermalinkState
* @property {string} title
* @property {number|null} userId
* @property {number|null} trackId
* @property {string|null} mapApi
* @property {MapParams|null} mapParams
*/
export default class uPermalink {
/**
* @param {uState} state
*/
constructor(state) {
this.state = state;
this.skipPush = false;
}
/**
* @return {uPermalink}
*/
init() {
this.state.onChanged('mapParams', () => this.pushState());
window.addEventListener('popstate', (event) => {
if (event.state === null) {
return;
}
const track = this.state.currentTrack;
const user = this.state.currentUser;
// remove elements that won't be updated
const state = {
title: event.state.title,
userId: (user && user.id === event.state.userId) ? null : event.state.userId,
trackId: (track && track.id === event.state.trackId) ? null : event.state.trackId,
mapApi: config.mapApi === event.state.mapApi ? null : event.state.mapApi,
mapParams: event.state.mapParams
}
this.onPop(state);
this.skipPush = true;
});
return this;
}
/**
* @return {Promise<?PermalinkState>}
*/
static parseHash() {
return uPermalink.parse(window.location.hash);
}
/**
* Parse URL hash string
* @param {string} hash
* @return {Promise<?PermalinkState>} Permalink state or null if not parsable
*/
static parse(hash) {
const parts = hash.replace('#', '').split('/');
parts.reverse();
const trackId = parseInt(parts.pop());
if (!isNaN(trackId)) {
let mapApi = 'openlayers';
if (parts.pop() === 'g') {
mapApi = 'gmaps';
}
let mapParams = null;
if (parts.length >= 4) {
mapParams = {};
mapParams.center = [ parseFloat(parts.pop()), parseFloat(parts.pop()) ];
mapParams.zoom = parseFloat(parts.pop());
mapParams.rotation = parseFloat(parts.pop());
}
return uTrack.getMeta(trackId)
.then((meta) => {
const userId = meta.userId;
const title = meta.name;
return { title, userId, trackId, mapApi, mapParams };
})
.catch((e) => {
console.log(`Ignoring unknown track ${trackId} ${e}`);
return null;
});
}
return Promise.resolve(null);
}
/**
* @param {?PermalinkState} state
*/
onPop(state) {
console.log('popState: #' + (state ? `${state.trackId}/${state.mapApi}/${state.mapParams}` : ''));
this.state.history = state;
if (state) {
document.title = `${$._('title')} ${state.title}`;
}
}
/**
* Push state into browser history
*/
pushState() {
if (this.skipPush) {
this.skipPush = false;
return;
}
if (this.state.currentUser === null || this.state.currentTrack === null) {
return;
}
const state = this.getState();
const prevState = window.history.state;
if (!prevState || !uUtils.isDeepEqual(prevState, state)) {
const hash = uPermalink.getHash(state);
console.log(`pushState: ${hash} => ${state}`);
window.history.pushState(state, state.title, hash);
document.title = `${$._('title')} ${state.title}`;
}
}
getState() {
return {
title: this.state.currentTrack.name,
userId: this.state.currentUser.id,
trackId: this.state.currentTrack.id,
mapApi: config.mapApi,
mapParams: this.state.mapParams
};
}
/**
* Get link hash
* @param {PermalinkState} state
* @return {string}
*/
static getHash(state) {
let hash = `#${state.trackId}/${state.mapApi.charAt(0)}`;
if (state.mapParams) {
hash += `/${state.mapParams.center[0]}/${state.mapParams.center[1]}`;
hash += `/${state.mapParams.zoom}/${state.mapParams.rotation}`;
}
return hash;
}
}