aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTerry Truong <terry06890@gmail.com>2022-07-05 15:57:46 +1000
committerTerry Truong <terry06890@gmail.com>2022-07-05 15:57:46 +1000
commit195e1111f286631fc871b49487755eaeafaf03a8 (patch)
tree1c319e8b7e291232c5e097a2f45e762ae54b22c1 /src
parent4bfd6889b2d6184f5500f47231b8288b4c04df04 (diff)
Add loading-from-server indicator
Diffstat (limited to 'src')
-rw-r--r--src/App.vue43
-rw-r--r--src/components/LoadingModal.vue34
-rw-r--r--src/components/SearchModal.vue18
-rw-r--r--src/components/icon/LoaderIcon.vue18
-rw-r--r--src/lib.ts2
5 files changed, 101 insertions, 14 deletions
diff --git a/src/App.vue b/src/App.vue
index 03a254b..df2b9b7 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -48,9 +48,9 @@
</div>
<!-- Modals -->
<transition name="fade">
- <search-modal v-if="searchOpen" :tolMap="tolMap" :lytOpts="lytOpts" :uiOpts="uiOpts" class="z-10"
+ <search-modal v-if="searchOpen" :tolMap="tolMap" :lytMap="layoutMap" :lytOpts="lytOpts" :uiOpts="uiOpts"
@close="onSearchClose" @search="onSearch" @info-click="onInfoClick" @setting-chg="onSettingChg"
- ref="searchModal"/>
+ @net-wait="primeLoadInd" @net-get="endLoadInd" class="z-10" ref="searchModal"/>
</transition>
<transition name="fade">
<tile-info-modal v-if="infoModalNodeName != null && infoModalData != null"
@@ -63,6 +63,9 @@
</transition>
<settings-modal v-if="settingsOpen" :lytOpts="lytOpts" :uiOpts="uiOpts" class="z-10"
@close="settingsOpen = false" @reset="onResetSettings" @setting-chg="onSettingChg"/>
+ <transition name="fade">
+ <loading-modal v-if="loadingOpen" :uiOpts="uiOpts" class="z-10"/>
+ </transition>
<!-- Overlay used to capture clicks during auto mode, etc -->
<div :style="{visibility: modeRunning != null ? 'visible' : 'hidden'}"
class="absolute left-0 top-0 w-full h-full" @click="modeRunning = null"></div>
@@ -79,6 +82,7 @@ import SettingsModal from './components/SettingsModal.vue';
import HelpModal from './components/HelpModal.vue';
import AncestryBar from './components/AncestryBar.vue';
import TutorialPane from './components/TutorialPane.vue';
+import LoadingModal from './components/LoadingModal.vue';
import IconButton from './components/IconButton.vue';
// Icons
import SearchIcon from './components/icon/SearchIcon.vue';
@@ -91,7 +95,7 @@ import HelpIcon from './components/icon/HelpIcon.vue';
import {TolNode, TolMap} from './tol';
import {LayoutNode, LayoutOptions, LayoutTreeChg,
initLayoutTree, initLayoutMap, tryLayout} from './layout';
-import {getServerResponse, InfoResponse, Action,
+import {queryServer, InfoResponse, Action,
UiOptions, getDefaultLytOpts, getDefaultUiOpts, OptionType} from './lib';
import {arraySum, randWeightedChoice, timeout} from './util';
@@ -136,6 +140,7 @@ export default defineComponent({
searchOpen: false,
settingsOpen: false,
helpOpen: false,
+ loadingOpen: false,
// For search and auto-mode
modeRunning: null as null | 'search' | 'autoMode',
lastFocused: null as LayoutNode | null, // Used to un-focus
@@ -160,6 +165,7 @@ export default defineComponent({
tutPaneInTransition: false,
// Other
justInitialised: false, // Used to skip transition for the tile initially loaded from server
+ pendingLoadingRevealHdlr: 0, // Used to delay showing the loading modal
changedSweepToParent: false, // Set during search animation for efficiency
excessTolNodeThreshold: 1000, // Threshold where excess tolMap entries get removed
};
@@ -319,7 +325,7 @@ export default defineComponent({
if (!this.tolMap.has(tolNode.children[0])){
let urlParams = 'type=node&name=' + encodeURIComponent(layoutNode.name);
urlParams += '&tree=' + this.uiOpts.tree;
- let responseObj: {[x: string]: TolNode} = await getServerResponse(urlParams);
+ let responseObj: {[x: string]: TolNode} = await this.loadFromServer(urlParams);
if (responseObj == null){
return false;
}
@@ -414,7 +420,7 @@ export default defineComponent({
if (!this.tolMap.has(tolNode.children[0])){
let urlParams = 'type=node&name=' + encodeURIComponent(layoutNode.name);
urlParams += '&tree=' + this.uiOpts.tree;
- let responseObj: {[x: string]: TolNode} = await getServerResponse(urlParams);
+ let responseObj: {[x: string]: TolNode} = await this.loadFromServer(urlParams);
if (responseObj == null){
return false;
}
@@ -483,7 +489,7 @@ export default defineComponent({
// Query server for tol-node info
let urlParams = 'type=info&name=' + encodeURIComponent(nodeName);
urlParams += '&tree=' + this.uiOpts.tree;
- let responseObj: InfoResponse = await getServerResponse(urlParams);
+ let responseObj: InfoResponse = await this.loadFromServer(urlParams);
if (responseObj == null){
return;
}
@@ -791,6 +797,27 @@ export default defineComponent({
this.tutTriggerFlag = !this.tutTriggerFlag;
}
},
+ // For the loading-indicator
+ async loadFromServer(urlParams: string){ // Like queryServer(), but enables the loading indicator
+ this.primeLoadInd();
+ let responseObj = await queryServer(urlParams);
+ this.endLoadInd();
+ return responseObj;
+ },
+ primeLoadInd(){
+ if (this.pendingLoadingRevealHdlr == 0){
+ this.pendingLoadingRevealHdlr = setTimeout(() => {
+ this.loadingOpen = true;
+ }, 300);
+ }
+ },
+ endLoadInd(){
+ clearTimeout(this.pendingLoadingRevealHdlr);
+ this.pendingLoadingRevealHdlr = 0;
+ if (this.loadingOpen){
+ this.loadingOpen = false;
+ }
+ },
// For other events
async onResize(){
// Handle event if not recently done
@@ -865,7 +892,7 @@ export default defineComponent({
// Query server
let urlParams = 'type=node';
urlParams += '&tree=' + this.uiOpts.tree;
- let responseObj: {[x: string]: TolNode} = await getServerResponse(urlParams);
+ let responseObj: {[x: string]: TolNode} = await this.loadFromServer(urlParams);
if (responseObj == null){
return;
}
@@ -1027,7 +1054,7 @@ export default defineComponent({
components: {
Tile, TutorialPane, AncestryBar,
IconButton, SearchIcon, PlayIcon, PauseIcon, SettingsIcon, HelpIcon,
- TileInfoModal, SearchModal, SettingsModal, HelpModal,
+ TileInfoModal, SearchModal, SettingsModal, HelpModal, LoadingModal,
},
});
</script>
diff --git a/src/components/LoadingModal.vue b/src/components/LoadingModal.vue
new file mode 100644
index 0000000..6ef7a97
--- /dev/null
+++ b/src/components/LoadingModal.vue
@@ -0,0 +1,34 @@
+<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="block w-12 h-12 animate-[spin_6s_linear_infinite]"/>
+ <div class="whitespace-nowrap">Querying server ...</div>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+import {defineComponent, PropType} from 'vue';
+import LoaderIcon from './icon/LoaderIcon.vue';
+import {UiOptions} from '../lib';
+
+export default defineComponent({
+ props: {
+ uiOpts: {type: Object as PropType<UiOptions>, required: true},
+ },
+ computed: {
+ styles(): Record<string,string> {
+ return {
+ color: this.uiOpts.textColor,
+ backgroundColor: this.uiOpts.bgColorDark2,
+ borderRadius: this.uiOpts.borderRadius + 'px',
+ boxShadow: this.uiOpts.shadowNormal,
+ };
+ },
+ },
+ components: {
+ LoaderIcon,
+ },
+});
+</script>
diff --git a/src/components/SearchModal.vue b/src/components/SearchModal.vue
index 55e50a1..e9abef5 100644
--- a/src/components/SearchModal.vue
+++ b/src/components/SearchModal.vue
@@ -37,11 +37,12 @@ import SearchIcon from './icon/SearchIcon.vue';
import LogInIcon from './icon/LogInIcon.vue';
import InfoIcon from './icon/InfoIcon.vue';
import {TolNode, TolMap} from '../tol';
-import {LayoutNode, LayoutOptions} from '../layout';
-import {getServerResponse, SearchSugg, SearchSuggResponse, UiOptions} from '../lib';
+import {LayoutNode, LayoutMap, LayoutOptions} from '../layout';
+import {queryServer, SearchSugg, SearchSuggResponse, UiOptions} from '../lib';
export default defineComponent({
props: {
+ lytMap: {type: Object as PropType<LayoutMap>, required: true}, // Used to check if a searched-for node exists
tolMap: {type: Object as PropType<TolMap>, required: true}, // Upon a search response, gets new nodes added
lytOpts: {type: Object as PropType<LayoutOptions>, required: true},
uiOpts: {type: Object as PropType<UiOptions>, required: true},
@@ -131,7 +132,7 @@ export default defineComponent({
let doReq = async () => {
let suggInput = this.pendingSuggInput;
let responseObj: SearchSuggResponse =
- await getServerResponse(this.pendingSuggReqParams);
+ await queryServer(this.pendingSuggReqParams);
if (responseObj == null){
return;
}
@@ -191,10 +192,17 @@ export default defineComponent({
if (tolNodeName == ''){
return;
}
+ // Check if the node has already been retrieved
+ if (this.lytMap.has(tolNodeName)){
+ this.$emit('search', tolNodeName);
+ return;
+ }
// Ask server for nodes in parent-chain, updates tolMap, then emits search event
let urlParams = 'type=node&toroot=true&name=' + encodeURIComponent(tolNodeName);
urlParams += '&tree=' + this.uiOpts.tree;
- let responseObj: {[x: string]: TolNode} = await getServerResponse(urlParams);
+ this.$emit('net-wait'); // Allows the parent component to show a loading-indicator
+ let responseObj: {[x: string]: TolNode} = await queryServer(urlParams);
+ this.$emit('net-get');
if (responseObj == null){
return;
}
@@ -232,6 +240,6 @@ export default defineComponent({
(this.$refs.searchInput as HTMLInputElement).focus();
},
components: {SearchIcon, InfoIcon, LogInIcon, },
- emits: ['search', 'close', 'info-click', 'setting-chg', ],
+ emits: ['search', 'close', 'info-click', 'setting-chg', 'net-wait', 'net-get', ],
});
</script>
diff --git a/src/components/icon/LoaderIcon.vue b/src/components/icon/LoaderIcon.vue
new file mode 100644
index 0000000..cd5093b
--- /dev/null
+++ b/src/components/icon/LoaderIcon.vue
@@ -0,0 +1,18 @@
+<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>
+
+<script lang="ts">
+import {defineComponent, PropType} from 'vue';
+export default defineComponent({});
+</script>
diff --git a/src/lib.ts b/src/lib.ts
index ee2103e..c034a3b 100644
--- a/src/lib.ts
+++ b/src/lib.ts
@@ -8,7 +8,7 @@ import {getBreakpoint, Breakpoint, getScrollBarWidth, onTouchDevice} from './uti
// For server requests
const SERVER_URL = 'http://localhost:8000/cgi-bin/data.py'
-export async function getServerResponse(params: string){
+export async function queryServer(params: string){
// Construct URL
let url = new URL(SERVER_URL);
url.search = params;