aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/App.vue35
-rw-r--r--src/components/LoadingModal.vue27
-rw-r--r--src/components/SearchModal.vue4
-rw-r--r--src/components/icon/LoaderIcon.vue13
4 files changed, 75 insertions, 4 deletions
diff --git a/src/App.vue b/src/App.vue
index b789d91..7b35c60 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -35,7 +35,8 @@
<!-- Modals -->
<transition name="fade">
<search-modal v-if="searchOpen" :eventTree="eventTree" :titleToEvent="titleToEvent"
- @close="searchOpen = false" @search="onSearch" @info-click="onInfoClick"/>
+ @close="searchOpen = false" @search="onSearch" @info-click="onInfoClick"
+ @net-wait="primeLoadInd(SERVER_WAIT_MSG)" @net-get="endLoadInd"/>
</transition>
<transition name="fade">
<info-modal v-if="infoModalData != null" :eventInfo="infoModalData" @close="infoModalData = null"/>
@@ -46,6 +47,9 @@
<transition name="fade">
<help-modal v-if="helpOpen" @close="helpOpen = false"/>
</transition>
+ <transition name="fade">
+ <loading-modal v-if="loadingMsg != null" :msg="loadingMsg"/>
+ </transition>
</div>
</template>
@@ -58,6 +62,7 @@ import InfoModal from './components/InfoModal.vue';
import SearchModal from './components/SearchModal.vue';
import SettingsModal from './components/SettingsModal.vue';
import HelpModal from './components/HelpModal.vue';
+import LoadingModal from './components/LoadingModal.vue';
import IconButton from './components/IconButton.vue';
// Icons
import HelpIcon from './components/icon/HelpIcon.vue';
@@ -259,7 +264,7 @@ async function onEventDisplay(
if (store.reqImgs){
urlParams.append('imgonly', 'true');
}
- let responseObj: EventResponseJson | null = await queryServer(urlParams);
+ let responseObj: EventResponseJson | null = await loadFromServer(urlParams, 2000);
if (responseObj == null){
console.log('WARNING: Server gave null response to event query');
return;
@@ -319,7 +324,7 @@ const infoModalData = ref(null as EventInfo | null);
async function onInfoClick(eventTitle: string){
// Query server for event info
let urlParams = new URLSearchParams({type: 'info', event: eventTitle});
- let responseObj: EventInfoJson | null = await queryServer(urlParams);
+ let responseObj: EventInfoJson | null = await loadFromServer(urlParams);
if (responseObj != null){
infoModalData.value = jsonToEventInfo(responseObj);
} else {
@@ -354,6 +359,30 @@ function onSettingChg(option: string){
// For help modal
const helpOpen = ref(false);
+// For loading modal
+const SERVER_WAIT_MSG = 'Loading data';
+const loadingMsg = ref(null as null | string);
+const pendingLoadingRevealHdlr = ref(0); // Used to delay showing the loading modal
+function primeLoadInd(msg: string, delay?: number){ // Sets up a loading message to display after a timeout
+ clearTimeout(pendingLoadingRevealHdlr.value);
+ pendingLoadingRevealHdlr.value = window.setTimeout(() => {
+ loadingMsg.value = msg;
+ }, delay == null ? 500 : delay);
+}
+function endLoadInd(){ // Cancels or closes a loading message
+ clearTimeout(pendingLoadingRevealHdlr.value);
+ pendingLoadingRevealHdlr.value = 0;
+ if (loadingMsg.value != null){
+ loadingMsg.value = null;
+ }
+}
+async function loadFromServer(urlParams: URLSearchParams, delay?: number){ // Like queryServer() but uses loading modal
+ primeLoadInd(SERVER_WAIT_MSG, delay);
+ let responseObj = await queryServer(urlParams);
+ endLoadInd();
+ return responseObj;
+}
+
// For timeline reset
const resetFlags: Ref<boolean[]> = ref([]);
function onReset(){
diff --git a/src/components/LoadingModal.vue b/src/components/LoadingModal.vue
new file mode 100644
index 0000000..5d207c7
--- /dev/null
+++ b/src/components/LoadingModal.vue
@@ -0,0 +1,27 @@
+<template>
+<div class="fixed left-0 top-0 w-full h-full bg-black/40">
+ <div class="absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2
+ flex items-center py-3 px-3 gap-2" :style="styles">
+ <loader-icon class="w-12 h-12 animate-[spin_6s_linear_infinite]"/>
+ <div class="whitespace-nowrap">{{msg}}</div>
+ </div>
+</div>
+</template>
+
+<script setup lang="ts">
+import {computed} from 'vue';
+import LoaderIcon from './icon/LoaderIcon.vue';
+import {useStore} from '../store';
+
+const store = useStore();
+
+defineProps({
+ msg: {type: String, required: true},
+});
+
+const styles = computed(() => ({
+ color: store.color.text,
+ backgroundColor: store.color.bgDark2,
+ borderRadius: store.borderRadius + 'px',
+}));
+</script>
diff --git a/src/components/SearchModal.vue b/src/components/SearchModal.vue
index ade86be..be8df51 100644
--- a/src/components/SearchModal.vue
+++ b/src/components/SearchModal.vue
@@ -47,7 +47,7 @@ const props = defineProps({
eventTree: {type: Object as PropType<RBTree<HistEvent>>, required: true},
titleToEvent: {type: Object as PropType<Map<string, HistEvent>>, required: true},
});
-const emit = defineEmits(['search', 'close', 'info-click']);
+const emit = defineEmits(['search', 'close', 'info-click', 'net-wait', 'net-get']);
// Search-suggestion data
const searchSuggs = ref([] as string[]);
@@ -179,7 +179,9 @@ async function resolveSearch(eventTitle: string){
if (store.reqImgs){
urlParams.append('imgonly', 'true');
}
+ emit('net-wait'); // Allows the parent component to show a loading-indicator
let responseObj: EventInfoJson | null = await queryServer(urlParams);
+ emit('net-get');
if (responseObj != null){
let eventInfo = jsonToEventInfo(responseObj);
if (store.reqImgs && eventInfo.event.imgId == null){
diff --git a/src/components/icon/LoaderIcon.vue b/src/components/icon/LoaderIcon.vue
new file mode 100644
index 0000000..c2a0369
--- /dev/null
+++ b/src/components/icon/LoaderIcon.vue
@@ -0,0 +1,13 @@
+<template>
+<svg viewBox="0 0 24 24" fill="none"
+ stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+ <line x1="12" y1="2" x2="12" y2="6"></line>
+ <line x1="12" y1="18" x2="12" y2="22"></line>
+ <line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line>
+ <line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line>
+ <line x1="2" y1="12" x2="6" y2="12"></line>
+ <line x1="18" y1="12" x2="22" y2="12"></line>
+ <line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line>
+ <line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line>
+</svg>
+</template>