Implement link copying and url handling
This commit is contained in:
parent
a0326b00d1
commit
9e28e695ee
44
package-lock.json
generated
44
package-lock.json
generated
@ -1317,6 +1317,12 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/clipboard": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/clipboard/-/clipboard-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-gJJX9Jjdt3bIAePQRRjYWG20dIhAgEqonguyHxXuqALxsoDsDLimihqrSg8fXgVTJ4KZCzkfglKtwsh/8dLfbA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/connect": {
|
"@types/connect": {
|
||||||
"version": "3.4.33",
|
"version": "3.4.33",
|
||||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
|
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
|
||||||
@ -3929,6 +3935,17 @@
|
|||||||
"integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
|
"integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"clipboard": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz",
|
||||||
|
"integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"good-listener": "^1.2.2",
|
||||||
|
"select": "^1.1.2",
|
||||||
|
"tiny-emitter": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"clipboardy": {
|
"clipboardy": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
|
||||||
@ -4921,6 +4938,12 @@
|
|||||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
|
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"delegate": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"depd": {
|
"depd": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||||
@ -6616,6 +6639,15 @@
|
|||||||
"slash": "^2.0.0"
|
"slash": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"good-listener": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
|
||||||
|
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"delegate": "^3.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"graceful-fs": {
|
"graceful-fs": {
|
||||||
"version": "4.2.4",
|
"version": "4.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
|
||||||
@ -10528,6 +10560,12 @@
|
|||||||
"ajv-keywords": "^3.5.2"
|
"ajv-keywords": "^3.5.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"select": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"select-hose": {
|
"select-hose": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
|
||||||
@ -11846,6 +11884,12 @@
|
|||||||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
|
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"tiny-emitter": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"tmp": {
|
"tmp": {
|
||||||
"version": "0.0.33",
|
"version": "0.0.33",
|
||||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
"vue": "^3.0.0"
|
"vue": "^3.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/clipboard": "^2.0.1",
|
||||||
"@types/leaflet": "^1.5.19",
|
"@types/leaflet": "^1.5.19",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.1.0",
|
"@typescript-eslint/eslint-plugin": "^4.1.0",
|
||||||
"@typescript-eslint/parser": "^4.1.0",
|
"@typescript-eslint/parser": "^4.1.0",
|
||||||
@ -22,6 +23,7 @@
|
|||||||
"@vue/compiler-sfc": "^3.0.0",
|
"@vue/compiler-sfc": "^3.0.0",
|
||||||
"@vue/eslint-config-typescript": "^5.0.2",
|
"@vue/eslint-config-typescript": "^5.0.2",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
|
"clipboard": "^2.0.6",
|
||||||
"eslint": "^7.5.0",
|
"eslint": "^7.5.0",
|
||||||
"eslint-plugin-vue": "^7.0.0-0",
|
"eslint-plugin-vue": "^7.0.0-0",
|
||||||
"leaflet": "^1.7.1",
|
"leaflet": "^1.7.1",
|
||||||
|
22
src/App.vue
22
src/App.vue
@ -9,6 +9,8 @@ import Map from './components/Map.vue';
|
|||||||
import Sidebar from './components/Sidebar.vue';
|
import Sidebar from './components/Sidebar.vue';
|
||||||
import {useStore} from "./store";
|
import {useStore} from "./store";
|
||||||
import {ActionTypes} from "@/store/action-types";
|
import {ActionTypes} from "@/store/action-types";
|
||||||
|
import Util from '@/util';
|
||||||
|
import {MutationTypes} from "@/store/mutation-types";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'App',
|
name: 'App',
|
||||||
@ -18,9 +20,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const store = useStore(),
|
const initialUrl = window.location.hash.replace('#', ''),
|
||||||
|
store = useStore(),
|
||||||
updateInterval = computed(() => store.state.configuration.updateInterval),
|
updateInterval = computed(() => store.state.configuration.updateInterval),
|
||||||
title = computed(() => store.state.configuration.title),
|
title = computed(() => store.state.configuration.title),
|
||||||
|
currentUrl = computed(() => store.getters.url),
|
||||||
updatesEnabled = ref(false),
|
updatesEnabled = ref(false),
|
||||||
updateTimeout = ref(0),
|
updateTimeout = ref(0),
|
||||||
|
|
||||||
@ -52,12 +56,28 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateTimeout.value = 0;
|
updateTimeout.value = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
parseUrl = () => {
|
||||||
|
if(!initialUrl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = Util.parseMapHash(initialUrl);
|
||||||
|
store.commit(MutationTypes.SET_PARSED_URL, result);
|
||||||
|
} catch(e) {
|
||||||
|
console.warn('Ignoring invalid url ' + e);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(title, (title) => document.title = title);
|
watch(title, (title) => document.title = title);
|
||||||
|
watch(currentUrl, (url) => window.history.replaceState({}, '', url));
|
||||||
|
|
||||||
onMounted(() => loadConfiguration());
|
onMounted(() => loadConfiguration());
|
||||||
onBeforeUnmount(() => stopUpdates());
|
onBeforeUnmount(() => stopUpdates());
|
||||||
|
|
||||||
|
parseUrl();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, computed} from "@vue/runtime-core";
|
import {computed, defineComponent} from "@vue/runtime-core";
|
||||||
import {LatLng, CRS} from 'leaflet';
|
import {CRS, LatLng} from 'leaflet';
|
||||||
import {useStore} from '@/store';
|
import {useStore} from '@/store';
|
||||||
import MapLayer from "@/components/map/layer/MapLayer.vue";
|
import MapLayer from "@/components/map/layer/MapLayer.vue";
|
||||||
import PlayersLayer from "@/components/map/layer/PlayersLayer.vue";
|
import PlayersLayer from "@/components/map/layer/PlayersLayer.vue";
|
||||||
@ -23,7 +23,7 @@ import ClockControl from "@/components/map/control/ClockControl.vue";
|
|||||||
import LinkControl from "@/components/map/control/LinkControl.vue";
|
import LinkControl from "@/components/map/control/LinkControl.vue";
|
||||||
import LogoControl from "@/components/map/control/LogoControl.vue";
|
import LogoControl from "@/components/map/control/LogoControl.vue";
|
||||||
import {MutationTypes} from "@/store/mutation-types";
|
import {MutationTypes} from "@/store/mutation-types";
|
||||||
import {DynmapPlayer} from "@/dynmap";
|
import {Coordinate, DynmapPlayer} from "@/dynmap";
|
||||||
import {ActionTypes} from "@/store/action-types";
|
import {ActionTypes} from "@/store/action-types";
|
||||||
import DynmapMap from "@/leaflet/DynmapMap";
|
import DynmapMap from "@/leaflet/DynmapMap";
|
||||||
|
|
||||||
@ -85,9 +85,36 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
deep: true
|
deep: true
|
||||||
},
|
},
|
||||||
currentWorld(newValue) {
|
currentWorld(newValue, oldValue) {
|
||||||
|
const store = useStore();
|
||||||
|
|
||||||
if(newValue) {
|
if(newValue) {
|
||||||
useStore().dispatch(ActionTypes.GET_MARKER_SETS, undefined);
|
let location: Coordinate;
|
||||||
|
let zoom: number;
|
||||||
|
|
||||||
|
store.dispatch(ActionTypes.GET_MARKER_SETS, undefined);
|
||||||
|
|
||||||
|
if(oldValue || !store.state.parsedUrl.location) {
|
||||||
|
location = newValue.center;
|
||||||
|
} else {
|
||||||
|
location = store.state.parsedUrl.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!oldValue) {
|
||||||
|
zoom = store.state.parsedUrl.zoom || store.state.configuration.defaultZoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Delay the pan a frame, to allow the projection to be updated by the new world
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
this.leaflet!.panTo(this.currentProjection.locationToLatLng(location), {
|
||||||
|
animate: false,
|
||||||
|
noMoveStart: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.leaflet!.setZoom(zoom, {
|
||||||
|
animate: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
configuration: {
|
configuration: {
|
||||||
@ -100,7 +127,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
deep: true,
|
deep: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -119,16 +146,12 @@ export default defineComponent({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
this.leaflet.on('moveend', () => {
|
this.leaflet.on('moveend', () => {
|
||||||
const location = this.currentProjection.latLngToLocation(this.leaflet!.getCenter(), 64),
|
useStore().commit(MutationTypes.SET_CURRENT_LOCATION, this.currentProjection.latLngToLocation(this.leaflet!.getCenter(), 64));
|
||||||
locationString = `${Math.round(location.x)},${Math.round(location.y)},${Math.round(location.z)}`,
|
});
|
||||||
url = `#${this.currentWorld!.name};${this.currentMap!.name};${locationString}`;
|
|
||||||
|
|
||||||
window.history.replaceState({
|
this.leaflet.on('zoomend', () => {
|
||||||
location,
|
useStore().commit(MutationTypes.SET_CURRENT_ZOOM, this.leaflet!.getZoom());
|
||||||
world: this.currentWorld!.name,
|
});
|
||||||
map: this.currentMap!.name,
|
|
||||||
}, '', url);
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, onMounted, onUnmounted, computed, watch} from "@vue/runtime-core";
|
import {defineComponent, onUnmounted, computed, watch} from "@vue/runtime-core";
|
||||||
import {DynmapWorldMap} from "@/dynmap";
|
import {DynmapWorldMap} from "@/dynmap";
|
||||||
import {Map} from 'leaflet';
|
import {Map} from 'leaflet';
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
@ -36,12 +36,9 @@ export default defineComponent({
|
|||||||
active = computed(() => props.map === store.state.currentMap),
|
active = computed(() => props.map === store.state.currentMap),
|
||||||
|
|
||||||
enableLayer = () => {
|
enableLayer = () => {
|
||||||
|
console.log('Set current projection');
|
||||||
useStore().commit(MutationTypes.SET_CURRENT_PROJECTION, layer.getProjection());
|
useStore().commit(MutationTypes.SET_CURRENT_PROJECTION, layer.getProjection());
|
||||||
props.leaflet.addLayer(layer);
|
props.leaflet.addLayer(layer);
|
||||||
props.leaflet.panTo(layer.getProjection().locationToLatLng(props.map.world.center), {
|
|
||||||
noMoveStart: true,
|
|
||||||
animate: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
stopUpdateWatch = watch(pendingUpdates, (newValue, oldValue) => {
|
stopUpdateWatch = watch(pendingUpdates, (newValue, oldValue) => {
|
||||||
if(newValue && !oldValue && !updateFrame) {
|
if(newValue && !oldValue && !updateFrame) {
|
||||||
@ -75,13 +72,11 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(active, (newValue) => newValue ? enableLayer() : disableLayer());
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if(active.value) {
|
if(active.value) {
|
||||||
enableLayer();
|
enableLayer();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
watch(active, (newValue) => newValue ? enableLayer() : disableLayer());
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
disableLayer();
|
disableLayer();
|
||||||
|
7
src/dynmap.d.ts
vendored
7
src/dynmap.d.ts
vendored
@ -251,3 +251,10 @@ interface DynmapTileUpdate {
|
|||||||
name: string
|
name: string
|
||||||
timestamp: number
|
timestamp: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DynmapParsedUrl {
|
||||||
|
world?: string;
|
||||||
|
map?: string;
|
||||||
|
location?: Coordinate;
|
||||||
|
zoom?: number;
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import {Control, ControlOptions, DomUtil, Map} from 'leaflet';
|
import {Control, ControlOptions, DomUtil, Map} from 'leaflet';
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import linkIcon from '@/assets/icons/link.svg';
|
import linkIcon from '@/assets/icons/link.svg';
|
||||||
|
import ClipboardJS from 'clipboard';
|
||||||
|
|
||||||
export class LinkControl extends Control {
|
export class LinkControl extends Control {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -22,9 +23,8 @@ export class LinkControl extends Control {
|
|||||||
<use xlink:href="${linkIcon.url}" />
|
<use xlink:href="${linkIcon.url}" />
|
||||||
</svg>`;
|
</svg>`;
|
||||||
|
|
||||||
linkButton.addEventListener('click', () => {
|
new ClipboardJS(linkButton, {
|
||||||
const projection = useStore().state.currentProjection;
|
text: () => window.location.href.split("#")[0] + useStore().getters.url,
|
||||||
console.log(projection.latLngToLocation(this._map!.getCenter(), 64));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return linkButton;
|
return linkButton;
|
||||||
|
@ -9,8 +9,8 @@ import {
|
|||||||
DynmapConfigurationResponse, DynmapLineUpdate,
|
DynmapConfigurationResponse, DynmapLineUpdate,
|
||||||
DynmapMarkerSet,
|
DynmapMarkerSet,
|
||||||
DynmapMarkerUpdate,
|
DynmapMarkerUpdate,
|
||||||
DynmapPlayer, DynmapServerConfig, DynmapTileUpdate,
|
DynmapPlayer, DynmapTileUpdate,
|
||||||
DynmapUpdateResponse
|
DynmapUpdateResponse, DynmapWorld
|
||||||
} from "@/dynmap";
|
} from "@/dynmap";
|
||||||
|
|
||||||
type AugmentedActionContext = {
|
type AugmentedActionContext = {
|
||||||
@ -57,17 +57,52 @@ export interface Actions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const actions: ActionTree<State, State> & Actions = {
|
export const actions: ActionTree<State, State> & Actions = {
|
||||||
[ActionTypes.LOAD_CONFIGURATION]({commit}): Promise<DynmapConfigurationResponse> {
|
[ActionTypes.LOAD_CONFIGURATION]({commit, state}): Promise<DynmapConfigurationResponse> {
|
||||||
return API.getConfiguration().then(config => {
|
return API.getConfiguration().then(config => {
|
||||||
commit(MutationTypes.SET_CONFIGURATION, config.config);
|
commit(MutationTypes.SET_CONFIGURATION, config.config);
|
||||||
commit(MutationTypes.SET_MESSAGES, config.messages);
|
commit(MutationTypes.SET_MESSAGES, config.messages);
|
||||||
commit(MutationTypes.SET_WORLDS, config.worlds);
|
commit(MutationTypes.SET_WORLDS, config.worlds);
|
||||||
commit(MutationTypes.SET_COMPONENTS, config.components);
|
commit(MutationTypes.SET_COMPONENTS, config.components);
|
||||||
|
|
||||||
if(config.config.defaultWorld && config.config.defaultMap) {
|
let worldName, mapName;
|
||||||
|
|
||||||
|
// Use config default world if it exists
|
||||||
|
if(config.config.defaultWorld && state.worlds.has(config.config.defaultWorld)) {
|
||||||
|
worldName = config.config.defaultWorld;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer world from parsed url if present and it exists
|
||||||
|
if(state.parsedUrl.world && state.worlds.has(state.parsedUrl.world)) {
|
||||||
|
worldName = state.parsedUrl.world;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use first world, if any, if neither of the above exist
|
||||||
|
if(!worldName) {
|
||||||
|
worldName = state.worlds.size ? state.worlds.entries().next().value.name : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(worldName) {
|
||||||
|
const world = state.worlds.get(worldName) as DynmapWorld;
|
||||||
|
|
||||||
|
// Use config default map if it exists
|
||||||
|
if(config.config.defaultMap && world.maps.has(config.config.defaultMap)) {
|
||||||
|
mapName = config.config.defaultMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer map from parsed url if present and it exists
|
||||||
|
if(state.parsedUrl.map && world.maps.has(state.parsedUrl.map)) {
|
||||||
|
mapName = state.parsedUrl.map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use first map, if any, if neither of the above exist
|
||||||
|
if(!mapName) {
|
||||||
|
mapName = world.maps.size ? world.maps.entries().next().value.name : undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(worldName && mapName) {
|
||||||
commit(MutationTypes.SET_CURRENT_MAP, {
|
commit(MutationTypes.SET_CURRENT_MAP, {
|
||||||
worldName: config.config.defaultWorld,
|
worldName, mapName
|
||||||
mapName: config.config.defaultMap
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ export type Getters = {
|
|||||||
clockControlEnabled(state: State): boolean;
|
clockControlEnabled(state: State): boolean;
|
||||||
night(state: State): boolean;
|
night(state: State): boolean;
|
||||||
mapBackground(state: State, getters: GetterTree<State, State> & Getters): string;
|
mapBackground(state: State, getters: GetterTree<State, State> & Getters): string;
|
||||||
|
url(state: State, getters: GetterTree<State, State> & Getters): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getters: GetterTree<State, State> & Getters = {
|
export const getters: GetterTree<State, State> & Getters = {
|
||||||
@ -41,5 +42,19 @@ export const getters: GetterTree<State, State> & Getters = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return state.currentMap.background || 'transparent';
|
return state.currentMap.background || 'transparent';
|
||||||
|
},
|
||||||
|
|
||||||
|
url(state: State): string {
|
||||||
|
const x = Math.round(state.currentLocation.x),
|
||||||
|
y = Math.round(state.currentLocation.y),
|
||||||
|
z = Math.round(state.currentLocation.z),
|
||||||
|
locationString = `${x},${y},${z}`,
|
||||||
|
zoom = state.currentZoom;
|
||||||
|
|
||||||
|
if(!state.currentWorld || !state.currentMap) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `#${state.currentWorld.name};${state.currentMap.name};${locationString};${zoom}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -20,6 +20,9 @@ export enum MutationTypes {
|
|||||||
SYNC_PLAYERS = 'syncPlayers',
|
SYNC_PLAYERS = 'syncPlayers',
|
||||||
SET_CURRENT_MAP = 'setCurrentMap',
|
SET_CURRENT_MAP = 'setCurrentMap',
|
||||||
SET_CURRENT_PROJECTION = 'setCurrentProjection',
|
SET_CURRENT_PROJECTION = 'setCurrentProjection',
|
||||||
|
SET_CURRENT_LOCATION = 'setCurrentLocation',
|
||||||
|
SET_CURRENT_ZOOM = 'setCurrentZoom',
|
||||||
|
SET_PARSED_URL = 'setParsedUrl',
|
||||||
FOLLOW_PLAYER = 'followPlayer',
|
FOLLOW_PLAYER = 'followPlayer',
|
||||||
CLEAR_FOLLOW = 'clearFollow',
|
CLEAR_FOLLOW = 'clearFollow',
|
||||||
}
|
}
|
@ -8,7 +8,7 @@ import {
|
|||||||
DynmapCircleUpdate,
|
DynmapCircleUpdate,
|
||||||
DynmapComponentConfig,
|
DynmapComponentConfig,
|
||||||
DynmapLine,
|
DynmapLine,
|
||||||
DynmapLineUpdate,
|
DynmapLineUpdate, Coordinate,
|
||||||
DynmapMarker,
|
DynmapMarker,
|
||||||
DynmapMarkerSet,
|
DynmapMarkerSet,
|
||||||
DynmapMarkerSetUpdates,
|
DynmapMarkerSetUpdates,
|
||||||
@ -17,7 +17,7 @@ import {
|
|||||||
DynmapPlayer,
|
DynmapPlayer,
|
||||||
DynmapServerConfig, DynmapTileUpdate,
|
DynmapServerConfig, DynmapTileUpdate,
|
||||||
DynmapWorld,
|
DynmapWorld,
|
||||||
DynmapWorldState
|
DynmapWorldState, DynmapParsedUrl
|
||||||
} from "@/dynmap";
|
} from "@/dynmap";
|
||||||
import {DynmapProjection} from "@/leaflet/projection/DynmapProjection";
|
import {DynmapProjection} from "@/leaflet/projection/DynmapProjection";
|
||||||
|
|
||||||
@ -49,6 +49,9 @@ export type Mutations<S = State> = {
|
|||||||
[MutationTypes.SYNC_PLAYERS](state: S, keep: Set<string>): void
|
[MutationTypes.SYNC_PLAYERS](state: S, keep: Set<string>): void
|
||||||
[MutationTypes.SET_CURRENT_MAP](state: S, payload: CurrentMapPayload): void
|
[MutationTypes.SET_CURRENT_MAP](state: S, payload: CurrentMapPayload): void
|
||||||
[MutationTypes.SET_CURRENT_PROJECTION](state: S, payload: DynmapProjection): void
|
[MutationTypes.SET_CURRENT_PROJECTION](state: S, payload: DynmapProjection): void
|
||||||
|
[MutationTypes.SET_CURRENT_LOCATION](state: S, payload: Coordinate): void
|
||||||
|
[MutationTypes.SET_CURRENT_ZOOM](state: S, payload: number): void
|
||||||
|
[MutationTypes.SET_PARSED_URL](state: S, payload: DynmapParsedUrl): void
|
||||||
[MutationTypes.FOLLOW_PLAYER](state: S, payload: DynmapPlayer): void
|
[MutationTypes.FOLLOW_PLAYER](state: S, payload: DynmapPlayer): void
|
||||||
[MutationTypes.CLEAR_FOLLOW](state: S, a?: void): void
|
[MutationTypes.CLEAR_FOLLOW](state: S, a?: void): void
|
||||||
}
|
}
|
||||||
@ -288,6 +291,18 @@ export const mutations: MutationTree<State> & Mutations = {
|
|||||||
state.currentProjection = projection;
|
state.currentProjection = projection;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[MutationTypes.SET_CURRENT_LOCATION](state: State, payload: Coordinate) {
|
||||||
|
state.currentLocation = payload;
|
||||||
|
},
|
||||||
|
|
||||||
|
[MutationTypes.SET_CURRENT_ZOOM](state: State, payload: number) {
|
||||||
|
state.currentZoom = payload;
|
||||||
|
},
|
||||||
|
|
||||||
|
[MutationTypes.SET_PARSED_URL](state: State, payload: DynmapParsedUrl) {
|
||||||
|
state.parsedUrl = payload;
|
||||||
|
},
|
||||||
|
|
||||||
[MutationTypes.FOLLOW_PLAYER](state: State, player: DynmapPlayer) {
|
[MutationTypes.FOLLOW_PLAYER](state: State, player: DynmapPlayer) {
|
||||||
state.following = player;
|
state.following = player;
|
||||||
},
|
},
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
DynmapMessageConfig,
|
DynmapMessageConfig,
|
||||||
DynmapPlayer,
|
DynmapPlayer,
|
||||||
DynmapServerConfig, DynmapTileUpdate,
|
DynmapServerConfig, DynmapTileUpdate,
|
||||||
DynmapWorld, DynmapWorldState
|
DynmapWorld, DynmapWorldState, Coordinate, DynmapParsedUrl
|
||||||
} from "@/dynmap";
|
} from "@/dynmap";
|
||||||
import {DynmapProjection} from "@/leaflet/projection/DynmapProjection";
|
import {DynmapProjection} from "@/leaflet/projection/DynmapProjection";
|
||||||
|
|
||||||
@ -26,10 +26,14 @@ export type State = {
|
|||||||
currentWorldState: DynmapWorldState;
|
currentWorldState: DynmapWorldState;
|
||||||
currentWorld?: DynmapWorld;
|
currentWorld?: DynmapWorld;
|
||||||
currentMap?: DynmapWorldMap;
|
currentMap?: DynmapWorldMap;
|
||||||
|
currentLocation: Coordinate;
|
||||||
|
currentZoom: number;
|
||||||
currentProjection: DynmapProjection;
|
currentProjection: DynmapProjection;
|
||||||
|
|
||||||
updateRequestId: number;
|
updateRequestId: number;
|
||||||
updateTimestamp: Date;
|
updateTimestamp: Date;
|
||||||
|
|
||||||
|
parsedUrl: DynmapParsedUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const state: State = {
|
export const state: State = {
|
||||||
@ -100,6 +104,13 @@ export const state: State = {
|
|||||||
|
|
||||||
currentWorld: undefined,
|
currentWorld: undefined,
|
||||||
currentMap: undefined,
|
currentMap: undefined,
|
||||||
|
currentLocation: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0,
|
||||||
|
},
|
||||||
|
currentZoom: 0,
|
||||||
|
|
||||||
currentProjection: new DynmapProjection(), //Projection for converting location <-> latlg. Object itself isn't reactive for performance reasons
|
currentProjection: new DynmapProjection(), //Projection for converting location <-> latlg. Object itself isn't reactive for performance reasons
|
||||||
currentWorldState: {
|
currentWorldState: {
|
||||||
raining: false,
|
raining: false,
|
||||||
@ -109,4 +120,11 @@ export const state: State = {
|
|||||||
|
|
||||||
updateRequestId: 0,
|
updateRequestId: 0,
|
||||||
updateTimestamp: new Date(),
|
updateTimestamp: new Date(),
|
||||||
|
|
||||||
|
parsedUrl: {
|
||||||
|
world: undefined,
|
||||||
|
map: undefined,
|
||||||
|
location: undefined,
|
||||||
|
zoom: undefined,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
36
src/util.ts
36
src/util.ts
@ -58,5 +58,41 @@ export default {
|
|||||||
return (x: number, y: number, z: number) => {
|
return (x: number, y: number, z: number) => {
|
||||||
return projection.locationToLatLng({x, y, z});
|
return projection.locationToLatLng({x, y, z});
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
parseMapHash(hash: string) {
|
||||||
|
const parts = hash.replace('#', '').split(';');
|
||||||
|
|
||||||
|
if(parts.length < 3) {
|
||||||
|
throw new TypeError('Not enough parts');
|
||||||
|
}
|
||||||
|
|
||||||
|
const world = parts[0],
|
||||||
|
map = parts[1],
|
||||||
|
location = parts[2].split(',').map(item => parseFloat(item)),
|
||||||
|
zoom = parts[3] ? parseInt(parts[3]) : undefined;
|
||||||
|
|
||||||
|
if(location.length !== 3) {
|
||||||
|
throw new TypeError('Location should contain exactly 3 numbers');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(location.filter(item => isNaN(item) || !isFinite(item)).length) {
|
||||||
|
throw new TypeError('Invalid value in location');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof zoom !== 'undefined' && (isNaN(zoom) || zoom < 0 || !isFinite(zoom))) {
|
||||||
|
throw new TypeError('Invalid value for zoom');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
world,
|
||||||
|
map,
|
||||||
|
location: {
|
||||||
|
x: location[0],
|
||||||
|
y: location[1],
|
||||||
|
z: location[2],
|
||||||
|
},
|
||||||
|
zoom,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user