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

View file

@ -32,6 +32,7 @@ export const ui = {
"serverError.description": `Please try again later.`,
"networkError.title": "Network error",
"networkError.description": `Please check your network connection.`,
"loadingDoors": 'Loading doors',
},
de: {
"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.`,
"networkError.title": "Netzwerkfehler",
"networkError.description": `Bitte überprüfe deine Internetverbindung.`,
"loadingDoors": 'Lade Türen',
},
} as const

View file

@ -1,6 +1,7 @@
---
import {UserRound, LogIn, DoorOpen} from "@lucide/astro"
import {getLangFromUrl, useTranslations} from "../i18n/utils"
import {languages} from "../i18n/ui.ts"
const lang = getLangFromUrl(Astro.url)
const t = useTranslations(lang)
@ -23,16 +24,23 @@ const t = useTranslations(lang)
<h1 class="text-xl font-semibold flex gap-1 items-center">
<DoorOpen/>
<span set:html={t("dooris")} />
<span set:html={t("dooris")}/>
</h1>
<div class="badge badge-soft badge-xl hidden" id="badge-username">
<UserRound class="size-5"/>
<span id="username"></span>
<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">
<UserRound class="size-5"/>
<span id="username"></span>
</div>
<a href={`/auth/login?next=/${lang}`} class="btn btn-soft btn-info" id="button-login">
<LogIn/>
{t("login")}
</a>
</div>
<a href={`/auth/login?next=/${lang}`} class="btn btn-soft btn-info" id="button-login">
<LogIn/>
{t("login")}
</a>
</div>
</header>

View file

@ -76,7 +76,7 @@ const t = useTranslations(lang)
<div class="divider w-full my-0" id="alert-divider"></div>
<div id="loading-doors" class="card w-full bg-info/5 hidden">
<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>
</div>
</div>