diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/App.vue | 35 | ||||
| -rw-r--r-- | src/components/LoadingModal.vue | 27 | ||||
| -rw-r--r-- | src/components/SearchModal.vue | 4 | ||||
| -rw-r--r-- | src/components/icon/LoaderIcon.vue | 13 |
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> |
