2020-12-16 16:54:41 +00:00
|
|
|
<!--
|
2021-07-25 14:12:40 +00:00
|
|
|
- Copyright 2021 James Lyne
|
2020-12-16 16:54:41 +00:00
|
|
|
-
|
2021-07-25 14:12:40 +00:00
|
|
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
- you may not use this file except in compliance with the License.
|
|
|
|
- You may obtain a copy of the License at
|
2020-12-16 16:54:41 +00:00
|
|
|
-
|
2021-07-25 14:12:40 +00:00
|
|
|
- http://www.apache.org/licenses/LICENSE-2.0
|
2020-12-16 16:54:41 +00:00
|
|
|
-
|
2021-07-25 14:12:40 +00:00
|
|
|
- Unless required by applicable law or agreed to in writing, software
|
|
|
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
- See the License for the specific language governing permissions and
|
|
|
|
- limitations under the License.
|
2020-12-16 16:54:41 +00:00
|
|
|
-->
|
|
|
|
|
2020-11-20 18:43:30 +00:00
|
|
|
<template>
|
2020-12-01 23:20:38 +00:00
|
|
|
<Map></Map>
|
2021-01-06 16:02:36 +00:00
|
|
|
<ChatBox v-if="chatBoxEnabled" v-show="chatBoxEnabled && chatVisible"></ChatBox>
|
2021-09-01 01:17:56 +00:00
|
|
|
<LoginModal v-if="loginEnabled" v-show="loginModalVisible" :required="loginRequired"></LoginModal>
|
2021-05-28 14:55:17 +00:00
|
|
|
<Sidebar></Sidebar>
|
2021-05-26 23:01:09 +00:00
|
|
|
<notifications position="bottom center" :speed="250" :max="3" :ignoreDuplicates="true" classes="notification" />
|
2020-11-20 18:43:30 +00:00
|
|
|
</template>
|
|
|
|
|
2020-11-20 21:47:56 +00:00
|
|
|
<script lang="ts">
|
2020-12-31 13:05:54 +00:00
|
|
|
import {computed, defineComponent, onBeforeUnmount, onMounted, onUnmounted, ref, watch} from 'vue';
|
2020-12-01 23:20:38 +00:00
|
|
|
import Map from './components/Map.vue';
|
2020-11-23 12:13:28 +00:00
|
|
|
import Sidebar from './components/Sidebar.vue';
|
2021-01-06 16:02:36 +00:00
|
|
|
import ChatBox from './components/ChatBox.vue';
|
2021-05-15 19:25:03 +00:00
|
|
|
import {useStore} from "@/store";
|
2020-11-23 16:16:26 +00:00
|
|
|
import {ActionTypes} from "@/store/action-types";
|
2021-05-27 00:41:58 +00:00
|
|
|
import {parseUrl} from '@/util';
|
|
|
|
import {hideSplash, showSplash, showSplashError} from '@/util/splash';
|
2020-12-13 02:50:17 +00:00
|
|
|
import {MutationTypes} from "@/store/mutation-types";
|
2021-05-28 23:08:43 +00:00
|
|
|
import {LiveAtlasServerDefinition, LiveAtlasUIElement} from "@/index";
|
2021-08-30 21:27:41 +00:00
|
|
|
import LoginModal from "@/components/login/LoginModal.vue";
|
|
|
|
import {notify} from "@kyvg/vue3-notification";
|
2020-11-23 12:13:28 +00:00
|
|
|
|
|
|
|
export default defineComponent({
|
2020-12-12 19:04:51 +00:00
|
|
|
name: 'App',
|
2020-11-23 12:13:28 +00:00
|
|
|
components: {
|
2020-12-01 23:20:38 +00:00
|
|
|
Map,
|
2020-11-23 12:13:28 +00:00
|
|
|
Sidebar,
|
2021-08-30 21:27:41 +00:00
|
|
|
ChatBox,
|
|
|
|
LoginModal
|
2020-11-23 12:13:28 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
setup() {
|
2021-08-31 16:45:05 +00:00
|
|
|
let loadingTimeout = 0;
|
|
|
|
|
2020-12-21 18:40:28 +00:00
|
|
|
const store = useStore(),
|
2021-08-12 13:27:39 +00:00
|
|
|
title = computed(() => store.getters.pageTitle),
|
2020-12-13 02:50:17 +00:00
|
|
|
currentUrl = computed(() => store.getters.url),
|
2021-05-17 02:39:25 +00:00
|
|
|
currentServer = computed(() => store.state.currentServer),
|
2021-05-17 23:19:51 +00:00
|
|
|
configurationHash = computed(() => store.state.configurationHash),
|
2021-01-06 16:02:36 +00:00
|
|
|
chatBoxEnabled = computed(() => store.state.components.chatBox),
|
2020-12-31 13:05:54 +00:00
|
|
|
chatVisible = computed(() => store.state.ui.visibleElements.has('chat')),
|
2021-09-01 00:02:32 +00:00
|
|
|
|
2021-09-01 01:17:56 +00:00
|
|
|
loggedIn = computed(() => store.state.loggedIn), //Whether the user is currently logged in
|
2021-09-07 22:10:57 +00:00
|
|
|
loginRequired = computed(() => store.state.loginRequired), //Whether logging is required to view the current server
|
|
|
|
loginEnabled = computed(() => store.state.components.login || loginRequired.value), //Whether logging in is enabled for the current server
|
2021-09-01 01:17:56 +00:00
|
|
|
|
|
|
|
//Hide the login modal if we are logged out on a login-required server, but the server list is open
|
|
|
|
//Allows switching servers without the modal overlapping
|
|
|
|
loginModalVisible = computed(() => store.state.ui.visibleModal === 'login'
|
|
|
|
&& (!loginRequired.value || !store.state.ui.visibleElements.has('maps'))),
|
2021-08-30 21:08:51 +00:00
|
|
|
|
|
|
|
loading = ref(false),
|
|
|
|
loadingAttempts = ref(0),
|
2020-12-12 19:04:51 +00:00
|
|
|
|
2021-05-18 17:27:44 +00:00
|
|
|
loadConfiguration = async () => {
|
|
|
|
try {
|
2021-08-31 16:45:05 +00:00
|
|
|
clearTimeout(loadingTimeout);
|
2021-09-01 17:14:19 +00:00
|
|
|
showSplash(!loadingAttempts.value);
|
2021-08-30 21:08:51 +00:00
|
|
|
loading.value = true;
|
|
|
|
|
|
|
|
await store.dispatch(ActionTypes.STOP_UPDATES, undefined);
|
|
|
|
store.commit(MutationTypes.RESET, undefined);
|
2021-05-18 17:27:44 +00:00
|
|
|
await store.dispatch(ActionTypes.LOAD_CONFIGURATION, undefined);
|
2021-07-24 00:15:52 +00:00
|
|
|
await store.dispatch(ActionTypes.START_UPDATES, undefined);
|
|
|
|
|
2021-05-26 23:01:09 +00:00
|
|
|
requestAnimationFrame(() => {
|
2021-05-26 23:50:34 +00:00
|
|
|
hideSplash();
|
2021-05-26 23:01:09 +00:00
|
|
|
|
|
|
|
const map = document.getElementById('#app');
|
|
|
|
|
|
|
|
if(map) {
|
|
|
|
(map as HTMLElement).focus();
|
|
|
|
}
|
|
|
|
});
|
2021-08-30 21:08:51 +00:00
|
|
|
} catch(e: any) {
|
2021-05-18 23:37:12 +00:00
|
|
|
//Request was aborted, probably because another server was selected before the request finished. Don't retry
|
|
|
|
if(e instanceof DOMException && e.name === 'AbortError') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-09-07 22:10:57 +00:00
|
|
|
//Logging in is required
|
|
|
|
if(loginRequired.value) {
|
2021-08-30 21:27:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-09-01 01:17:56 +00:00
|
|
|
//Any other errors
|
2021-05-20 14:30:55 +00:00
|
|
|
const error = `Failed to load server configuration for '${store.state.currentServer!.id}'`;
|
2021-05-18 17:27:44 +00:00
|
|
|
console.error(`${error}:`, e);
|
2021-08-30 21:08:51 +00:00
|
|
|
showSplashError(`${error}\n${e}`, false, ++loadingAttempts.value);
|
2021-08-31 16:45:05 +00:00
|
|
|
|
|
|
|
clearTimeout(loadingTimeout);
|
|
|
|
loadingTimeout = setTimeout(() => loadConfiguration(), 1000);
|
2021-08-30 21:08:51 +00:00
|
|
|
} finally {
|
|
|
|
loading.value = false;
|
2021-05-18 17:27:44 +00:00
|
|
|
}
|
2020-12-12 19:04:51 +00:00
|
|
|
},
|
|
|
|
|
2021-01-26 20:37:29 +00:00
|
|
|
handleUrl = () => {
|
|
|
|
const parsedUrl = parseUrl();
|
2020-12-13 02:50:17 +00:00
|
|
|
|
2020-12-21 18:40:28 +00:00
|
|
|
if(parsedUrl) {
|
2020-12-21 18:46:01 +00:00
|
|
|
//Remove legacy url if one was parsed
|
|
|
|
if(parsedUrl.legacy) {
|
|
|
|
const url = new URL(window.location.href);
|
2021-09-07 22:08:44 +00:00
|
|
|
url.searchParams.delete('worldname'); //Dynmap
|
|
|
|
url.searchParams.delete('world'); //Pl3xmap
|
2020-12-21 18:46:01 +00:00
|
|
|
url.searchParams.delete('mapname');
|
|
|
|
url.searchParams.delete('x');
|
|
|
|
url.searchParams.delete('y');
|
|
|
|
url.searchParams.delete('z');
|
|
|
|
url.searchParams.delete('zoom');
|
|
|
|
history.replaceState({}, '', url.toString());
|
|
|
|
}
|
|
|
|
|
2020-12-21 18:40:28 +00:00
|
|
|
store.commit(MutationTypes.SET_PARSED_URL, parsedUrl);
|
2020-12-13 02:50:17 +00:00
|
|
|
}
|
2020-12-31 13:05:54 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
onResize = () => {
|
|
|
|
store.commit(MutationTypes.SET_SMALL_SCREEN, window.innerWidth < 480 || window.innerHeight < 500);
|
2021-05-28 23:08:43 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
onKeydown = (e: KeyboardEvent) => {
|
|
|
|
if(!e.ctrlKey || !e.shiftKey) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let element: LiveAtlasUIElement;
|
|
|
|
|
2021-09-01 01:17:56 +00:00
|
|
|
//Disable shortcuts if modal is open - except login required to allow server list toggling
|
|
|
|
if(store.state.ui.visibleModal && !loginRequired.value) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-28 23:08:43 +00:00
|
|
|
switch(e.key) {
|
|
|
|
case 'M':
|
|
|
|
element = 'maps';
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
element = 'chat';
|
|
|
|
break;
|
|
|
|
case 'P':
|
|
|
|
element = 'players';
|
|
|
|
break;
|
|
|
|
case 'L':
|
|
|
|
element = 'layers';
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
store.commit(MutationTypes.TOGGLE_UI_ELEMENT_VISIBILITY, element);
|
2021-07-20 02:25:42 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
onUrlChange = () => {
|
|
|
|
const parsedUrl = parseUrl();
|
|
|
|
|
|
|
|
if(parsedUrl) {
|
|
|
|
store.commit(MutationTypes.SET_PARSED_URL, parsedUrl);
|
|
|
|
}
|
2020-12-12 19:04:51 +00:00
|
|
|
};
|
2020-11-23 12:13:28 +00:00
|
|
|
|
2020-12-12 19:04:51 +00:00
|
|
|
watch(title, (title) => document.title = title);
|
2020-12-13 02:50:17 +00:00
|
|
|
watch(currentUrl, (url) => window.history.replaceState({}, '', url));
|
2021-05-20 14:30:55 +00:00
|
|
|
watch(currentServer, (newServer?: LiveAtlasServerDefinition) => {
|
2021-05-19 23:06:57 +00:00
|
|
|
if(!newServer) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-09-01 17:14:19 +00:00
|
|
|
loadingAttempts.value = 0;
|
2021-05-19 23:06:57 +00:00
|
|
|
window.history.replaceState({}, '', newServer.id);
|
2021-05-17 02:39:25 +00:00
|
|
|
loadConfiguration();
|
2021-05-19 23:29:17 +00:00
|
|
|
}, {deep: true});
|
2021-07-24 00:15:52 +00:00
|
|
|
watch(configurationHash, async (newHash, oldHash) => {
|
2021-05-17 23:19:51 +00:00
|
|
|
if(newHash && oldHash) {
|
2021-07-24 00:15:52 +00:00
|
|
|
await loadConfiguration();
|
2021-05-17 23:19:51 +00:00
|
|
|
}
|
|
|
|
});
|
2021-08-30 21:27:41 +00:00
|
|
|
watch(loggedIn, async () => {
|
|
|
|
if(!loading.value) {
|
|
|
|
console.log('Login state changed. Reloading configuration');
|
|
|
|
await loadConfiguration();
|
|
|
|
}
|
2021-09-01 01:17:56 +00:00
|
|
|
});
|
|
|
|
watch(loginRequired, (newValue) => {
|
|
|
|
if(newValue) {
|
|
|
|
store.commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {
|
|
|
|
element: 'maps',
|
|
|
|
state: false,
|
|
|
|
});
|
|
|
|
store.commit(MutationTypes.SHOW_UI_MODAL, 'login');
|
|
|
|
notify('Login required');
|
|
|
|
showSplashError('Login required', true, 1);
|
2021-09-07 22:10:57 +00:00
|
|
|
} else {
|
|
|
|
store.commit(MutationTypes.HIDE_UI_MODAL, 'login');
|
2021-09-01 01:17:56 +00:00
|
|
|
}
|
2021-08-30 21:27:41 +00:00
|
|
|
})
|
2020-11-23 12:13:28 +00:00
|
|
|
|
2020-12-12 19:04:51 +00:00
|
|
|
onMounted(() => loadConfiguration());
|
2021-08-31 16:45:05 +00:00
|
|
|
onBeforeUnmount(() => {
|
|
|
|
store.dispatch(ActionTypes.STOP_UPDATES, undefined);
|
|
|
|
clearTimeout(loadingTimeout);
|
|
|
|
});
|
2020-12-13 02:50:17 +00:00
|
|
|
|
2021-01-26 20:37:29 +00:00
|
|
|
handleUrl();
|
2020-12-31 13:05:54 +00:00
|
|
|
onResize();
|
|
|
|
|
2021-05-28 23:08:43 +00:00
|
|
|
onMounted(() => {
|
|
|
|
window.addEventListener('resize', onResize);
|
|
|
|
window.addEventListener('keydown', onKeydown);
|
2021-07-20 02:25:42 +00:00
|
|
|
window.addEventListener('hashchange', onUrlChange);
|
2021-05-28 23:08:43 +00:00
|
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
|
|
window.addEventListener('resize', onResize);
|
|
|
|
window.addEventListener('keydown', onKeydown);
|
2021-07-20 02:25:42 +00:00
|
|
|
window.addEventListener('hashchange', onUrlChange);
|
2021-05-28 23:08:43 +00:00
|
|
|
});
|
2020-12-17 14:50:12 +00:00
|
|
|
|
|
|
|
return {
|
2021-01-06 16:02:36 +00:00
|
|
|
chatBoxEnabled,
|
2020-12-31 13:05:54 +00:00
|
|
|
chatVisible,
|
2021-08-30 21:27:41 +00:00
|
|
|
loginEnabled,
|
2021-09-01 00:02:32 +00:00
|
|
|
loginRequired,
|
2021-09-01 01:17:56 +00:00
|
|
|
loginModalVisible
|
2020-12-17 14:50:12 +00:00
|
|
|
}
|
2020-12-12 19:04:51 +00:00
|
|
|
},
|
2020-11-23 12:13:28 +00:00
|
|
|
});
|
2020-11-20 18:43:30 +00:00
|
|
|
</script>
|