Add timeout modal.
Build and Deploy Web App / build-and-deploy (push) Successful in 20s
Details
Build and Deploy Web App / build-and-deploy (push) Successful in 20s
Details
This commit is contained in:
parent
7455a55766
commit
83db4baa5b
|
|
@ -0,0 +1,53 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useAuthStore } from '@/stores/auth-store'
|
||||||
|
import { useTemplateRef, ref, computed } from 'vue'
|
||||||
|
import ModalWrapper from './common/ModalWrapper.vue'
|
||||||
|
import AppButton from './common/AppButton.vue'
|
||||||
|
|
||||||
|
const timeoutModal = useTemplateRef("timeoutModal")
|
||||||
|
const secondsUntilLogout = ref(30)
|
||||||
|
const timeoutTimerId = ref<number | undefined>()
|
||||||
|
const timePhrase = computed(() => {
|
||||||
|
if (secondsUntilLogout.value !== 1) {
|
||||||
|
return secondsUntilLogout.value + " seconds"
|
||||||
|
}
|
||||||
|
return secondsUntilLogout.value + " second"
|
||||||
|
})
|
||||||
|
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
if (timeoutModal.value?.isOpen()) return
|
||||||
|
secondsUntilLogout.value = 30
|
||||||
|
timeoutModal.value?.show()
|
||||||
|
timeoutTimerId.value = window.setInterval(() => {
|
||||||
|
secondsUntilLogout.value = secondsUntilLogout.value - 1
|
||||||
|
if (secondsUntilLogout.value <= 0) {
|
||||||
|
authStore.onUserLoggedOut()
|
||||||
|
window.clearInterval(timeoutTimerId.value)
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
function dismissTimeout() {
|
||||||
|
window.clearInterval(timeoutTimerId.value)
|
||||||
|
timeoutModal.value?.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ start })
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<ModalWrapper ref="timeoutModal">
|
||||||
|
<template v-slot:default>
|
||||||
|
<p>
|
||||||
|
You've been inactive for a while, so to ensure the safety of your
|
||||||
|
personal information, you will be logged out in
|
||||||
|
<strong>{{ timePhrase }}</strong>
|
||||||
|
unless you click below to remain logged in.
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
<template v-slot:buttons>
|
||||||
|
<AppButton @click="dismissTimeout()">Stay Logged In</AppButton>
|
||||||
|
</template>
|
||||||
|
</ModalWrapper>
|
||||||
|
</template>
|
||||||
|
|
@ -23,24 +23,20 @@ function close(returnValue?: string) {
|
||||||
dialog.value?.close(returnValue)
|
dialog.value?.close(returnValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({ show, close })
|
function isOpen(): boolean {
|
||||||
|
return dialog.value?.open ?? false
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ show, close, isOpen })
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
<dialog
|
<dialog ref="dialog" class="app-modal-dialog" :id="id">
|
||||||
ref="dialog"
|
|
||||||
class="app-modal-dialog"
|
|
||||||
:id="id"
|
|
||||||
>
|
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
|
||||||
<div class="app-modal-dialog-actions">
|
<div class="app-modal-dialog-actions">
|
||||||
<slot name="buttons">
|
<slot name="buttons">
|
||||||
<AppButton
|
<AppButton button-style="secondary" @click="close()">Close</AppButton>
|
||||||
button-style="secondary"
|
|
||||||
@click="close()"
|
|
||||||
>Close</AppButton
|
|
||||||
>
|
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,23 @@ pages.
|
||||||
import { AuthApiClient } from '@/api/auth'
|
import { AuthApiClient } from '@/api/auth'
|
||||||
import { getSelectedProfile } from '@/api/profile'
|
import { getSelectedProfile } from '@/api/profile'
|
||||||
import { secondsUntilExpired } from '@/api/token-util'
|
import { secondsUntilExpired } from '@/api/token-util'
|
||||||
|
import IdleTimeoutModal from '@/components/IdleTimeoutModal.vue'
|
||||||
import { useAuthStore } from '@/stores/auth-store'
|
import { useAuthStore } from '@/stores/auth-store'
|
||||||
import { onMounted, ref, type Ref } from 'vue'
|
import { useIdleObserver } from '@idle-observer/vue3'
|
||||||
|
import { onMounted, ref, useTemplateRef, type Ref } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
|
const IDLE_TIMEOUT_SECONDS = 300
|
||||||
|
const idleTimeoutModal = useTemplateRef("idleTimeoutModal")
|
||||||
|
useIdleObserver({
|
||||||
|
timeout: IDLE_TIMEOUT_SECONDS * 1000,
|
||||||
|
onIdle: () => idleTimeoutModal.value?.start()
|
||||||
|
})
|
||||||
|
|
||||||
const authCheckTimer: Ref<number | undefined> = ref(undefined)
|
const authCheckTimer: Ref<number | undefined> = ref(undefined)
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|
@ -61,31 +70,24 @@ async function checkAuth() {
|
||||||
<div>
|
<div>
|
||||||
<header class="app-header-bar">
|
<header class="app-header-bar">
|
||||||
<div>
|
<div>
|
||||||
<h1
|
<h1 class="app-header-text" @click="onHeaderClicked()">
|
||||||
class="app-header-text"
|
|
||||||
@click="onHeaderClicked()"
|
|
||||||
>
|
|
||||||
Finnow
|
Finnow
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span class="app-user-widget" @click="router.push('/me')">
|
||||||
class="app-user-widget"
|
|
||||||
@click="router.push('/me')"
|
|
||||||
>
|
|
||||||
<font-awesome-icon icon="fa-user"></font-awesome-icon>
|
<font-awesome-icon icon="fa-user"></font-awesome-icon>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span
|
<span class="app-logout-button" @click="authStore.onUserLoggedOut()">
|
||||||
class="app-logout-button"
|
|
||||||
@click="authStore.onUserLoggedOut()"
|
|
||||||
>
|
|
||||||
<font-awesome-icon icon="fa-solid fa-arrow-right-from-bracket"></font-awesome-icon>
|
<font-awesome-icon icon="fa-solid fa-arrow-right-from-bracket"></font-awesome-icon>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<RouterView></RouterView>
|
<RouterView></RouterView>
|
||||||
|
|
||||||
|
<IdleTimeoutModal ref="idleTimeoutModal" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue