language switcher, auto refresh auth session
All checks were successful
Build Container / Build Container (push) Successful in 34s

This commit is contained in:
kritzl 2026-05-14 18:11:26 +02:00
commit 1dab2f7f4e
Signed by: kritzl
SSH key fingerprint: SHA256:5BmINP9VjZWaUk5Z+2CTut1KFhwLtd0ZynMekKbtViM
4 changed files with 37 additions and 11 deletions

View file

@ -45,9 +45,19 @@ const loading: {
auth: false, auth: false,
} }
const timeouts: Record<string, number | null> = {
auth: null,
}
const doors: Array<DoorType> = [] const doors: Array<DoorType> = []
function triggerAuthTimeout() {
const diff = auth.until ? (auth.until.getTime() - new Date().getTime()) : 0
if (timeouts.auth) clearTimeout(timeouts.auth)
timeouts.auth = setTimeout(checkUser, diff)
}
function loadAuthFromLocalStorage() { function loadAuthFromLocalStorage() {
if (localStorage.getItem("auth")) { if (localStorage.getItem("auth")) {
const localAuth = JSON.parse(localStorage.getItem("auth")!) const localAuth = JSON.parse(localStorage.getItem("auth")!)
@ -60,7 +70,10 @@ function loadAuthFromLocalStorage() {
} }
async function checkUser() { async function checkUser() {
if (!auth.authenticated) {
loading.auth = true loading.auth = true
}
const getUserInfo = fetcher.path("/api/user-info/").method("get").create() const getUserInfo = fetcher.path("/api/user-info/").method("get").create()
try { try {
@ -72,6 +85,8 @@ async function checkUser() {
auth.until = userInfo.guaranteed_session_until ? new Date(userInfo.guaranteed_session_until) : null auth.until = userInfo.guaranteed_session_until ? new Date(userInfo.guaranteed_session_until) : null
auth.username = userInfo.username ?? "" auth.username = userInfo.username ?? ""
auth.recentLogout = false auth.recentLogout = false
triggerAuthTimeout()
} catch (e) { } catch (e) {
// check which operation threw the exception // check which operation threw the exception
if (e instanceof getUserInfo.Error) { if (e instanceof getUserInfo.Error) {
@ -142,6 +157,7 @@ async function fetchDoors() {
console.log("unauthorized") console.log("unauthorized")
} else if (error.status >= 500 && error.status < 600) { } else if (error.status >= 500 && error.status < 600) {
apiError.current = "serverError" apiError.current = "serverError"
clearInterval(doorsInterval)
} else { } else {
console.error("unknown error:", error) console.error("unknown error:", error)
} }
@ -262,7 +278,7 @@ function refresh() {
loadAuthFromLocalStorage() loadAuthFromLocalStorage()
setInterval(fetchDoors, 250) // TODO: replace with SSE const doorsInterval = setInterval(fetchDoors, 250) // TODO: replace with SSE
checkUser() checkUser()
document.addEventListener("loadeddata", () => { document.addEventListener("loadeddata", () => {

View file

@ -32,6 +32,7 @@ export const ui = {
"serverError.description": `Please try again later.`, "serverError.description": `Please try again later.`,
"networkError.title": "Network error", "networkError.title": "Network error",
"networkError.description": `Please check your network connection.`, "networkError.description": `Please check your network connection.`,
"loadingDoors": 'Loading doors',
}, },
de: { de: {
"dooris": "TüRIS <span class='text-neutral-content/50 text-xs'>(DOORIS)</span>", "dooris": "TüRIS <span class='text-neutral-content/50 text-xs'>(DOORIS)</span>",
@ -59,5 +60,6 @@ export const ui = {
"serverError.description": `Bitte versuche es später erneut.`, "serverError.description": `Bitte versuche es später erneut.`,
"networkError.title": "Netzwerkfehler", "networkError.title": "Netzwerkfehler",
"networkError.description": `Bitte überprüfe deine Internetverbindung.`, "networkError.description": `Bitte überprüfe deine Internetverbindung.`,
"loadingDoors": 'Lade Türen',
}, },
} as const } as const

View file

@ -1,6 +1,7 @@
--- ---
import {UserRound, LogIn, DoorOpen} from "@lucide/astro" import {UserRound, LogIn, DoorOpen} from "@lucide/astro"
import {getLangFromUrl, useTranslations} from "../i18n/utils" import {getLangFromUrl, useTranslations} from "../i18n/utils"
import {languages} from "../i18n/ui.ts"
const lang = getLangFromUrl(Astro.url) const lang = getLangFromUrl(Astro.url)
const t = useTranslations(lang) const t = useTranslations(lang)
@ -23,8 +24,14 @@ const t = useTranslations(lang)
<h1 class="text-xl font-semibold flex gap-1 items-center"> <h1 class="text-xl font-semibold flex gap-1 items-center">
<DoorOpen/> <DoorOpen/>
<span set:html={t("dooris")} /> <span set:html={t("dooris")}/>
</h1> </h1>
<div class="flex gap-4 items-center">
<div>
{Object.entries(languages).map(([l, label]) => (
<a href={`/${l}`} class=`btn btn-xs ${ l == lang ? 'btn-primary btn-soft' : 'btn-ghost'}`>{label}</a>
))}
</div>
<div class="badge badge-soft badge-xl hidden" id="badge-username"> <div class="badge badge-soft badge-xl hidden" id="badge-username">
<UserRound class="size-5"/> <UserRound class="size-5"/>
<span id="username"></span> <span id="username"></span>
@ -34,6 +41,7 @@ const t = useTranslations(lang)
{t("login")} {t("login")}
</a> </a>
</div> </div>
</div>
</header> </header>
<main class="p-2"> <main class="p-2">

View file

@ -76,7 +76,7 @@ const t = useTranslations(lang)
<div class="divider w-full my-0" id="alert-divider"></div> <div class="divider w-full my-0" id="alert-divider"></div>
<div id="loading-doors" class="card w-full bg-info/5 hidden"> <div id="loading-doors" class="card w-full bg-info/5 hidden">
<div class="card-body flex-row gap-4 items-center justify-center"> <div class="card-body flex-row gap-4 items-center justify-center">
<span class="text-xl">Loading Doors</span> <span class="text-xl">{t('loadingDoors')}</span>
<span class="loading loading-spinner loading-xl"></span> <span class="loading loading-spinner loading-xl"></span>
</div> </div>
</div> </div>