update api, add loading states

This commit is contained in:
kritzl 2026-05-14 15:58:12 +02:00
commit 63e99885a4
Signed by: kritzl
SSH key fingerprint: SHA256:5BmINP9VjZWaUk5Z+2CTut1KFhwLtd0ZynMekKbtViM
4 changed files with 52 additions and 21 deletions

View file

@ -1,3 +1,3 @@
# Update schema # Update schema
`pnpm dlx openapi-typescript http://localhost:8000/openapi.json -o ./schema.d.ts` `pnpm dlx openapi-typescript http://localhost:8000/openapi.json -o ./schema.ts`

View file

@ -132,18 +132,16 @@ export interface components {
*/ */
activity_state: "unknown" | "locking" | "unlocking" | "stable"; activity_state: "unknown" | "locking" | "unlocking" | "stable";
}; };
/** UserInfo */
UserInfo: {
/** Username */
username: string;
};
/** UserStatus */ /** UserStatus */
UserStatus: { UserStatus: {
/** Is Logged In */ /** Is Logged In */
is_logged_in: boolean; is_logged_in: boolean;
/** Is Authorized */
is_authorized: boolean;
/** Guaranteed Session Until */ /** Guaranteed Session Until */
guaranteed_session_until: string | null; guaranteed_session_until: string | null;
user_info: components["schemas"]["UserInfo"] | null; /** Username */
username: string | null;
}; };
/** ValidationError */ /** ValidationError */
ValidationError: { ValidationError: {

View file

@ -32,33 +32,45 @@ const auth: AuthType = {
} }
const apiError: { const apiError: {
current: "serverError" | null; current: "serverError" | "networkError" | null;
} = { } = {
current: null, current: null,
} }
const loading: {
doors: boolean;
auth: boolean;
} = {
doors: false,
auth: false,
}
const doors: Array<DoorType> = [] const doors: Array<DoorType> = []
function loadAuthFromLocalStorage() { function loadAuthFromLocalStorage() {
const localAuth = JSON.parse(localStorage.getItem("auth") ?? "") if (localStorage.getItem("auth")) {
const localAuth = JSON.parse(localStorage.getItem("auth")!)
auth.recentLogout = localAuth.recentLogout // set recentLogout true, if user was logged in before auth.recentLogout = localAuth.recentLogout // set recentLogout true, if user was logged in before
auth.authenticated = localAuth.authenticated auth.authenticated = localAuth.authenticated
auth.authorized = localAuth.authorized auth.authorized = localAuth.authorized
auth.until = localAuth.until ? new Date(localAuth.until) : null auth.until = localAuth.until ? new Date(localAuth.until) : null
auth.username = localAuth.username auth.username = localAuth.username
} }
}
async function checkUser() { async function checkUser() {
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 {
const {status, data: userInfo} = await getUserInfo({}) const {data: userInfo} = await getUserInfo({})
apiError.current = null apiError.current = null
auth.authenticated = userInfo.is_logged_in auth.authenticated = userInfo.is_logged_in
auth.authorized = true auth.authorized = userInfo.is_authorized
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.user_info?.username ?? "" auth.username = userInfo.username ?? ""
auth.recentLogout = false auth.recentLogout = false
} catch (e) { } catch (e) {
// check which operation threw the exception // check which operation threw the exception
@ -80,15 +92,18 @@ async function checkUser() {
} }
} finally { } finally {
localStorage.setItem("auth", JSON.stringify(auth)) localStorage.setItem("auth", JSON.stringify(auth))
loading.auth = false
refresh() refresh()
} }
} }
async function fetchDoors() { async function fetchDoors() {
loading.doors = true
refresh()
const getDoors = fetcher.path("/api/locks/").method("get").create() const getDoors = fetcher.path("/api/locks/").method("get").create()
try { try {
const {status, data: doorInfo} = await getDoors({}) const {data: doorInfo} = await getDoors({})
apiError.current = null apiError.current = null
while (doors.length) { while (doors.length) {
@ -129,7 +144,15 @@ async function fetchDoors() {
console.error("unknown error:", error) console.error("unknown error:", error)
} }
} }
if (e instanceof Error) {
switch (e.name) {
case "TypeError":
apiError.current = "networkError"
}
}
} finally { } finally {
loading.doors = false
refresh() refresh()
} }
} }
@ -190,21 +213,27 @@ function refresh() {
const alertUnauthorized: HTMLDivElement = document.querySelector("#alert-unauthorized")! const alertUnauthorized: HTMLDivElement = document.querySelector("#alert-unauthorized")!
const alertLoggedout: HTMLDivElement = document.querySelector("#alert-loggedout")! const alertLoggedout: HTMLDivElement = document.querySelector("#alert-loggedout")!
const alertServerError: HTMLDivElement = document.querySelector("#alert-serverError")! const alertServerError: HTMLDivElement = document.querySelector("#alert-serverError")!
const alertNetworkError: HTMLDivElement = document.querySelector("#alert-networkError")!
const alertDivider: HTMLDivElement = document.querySelector("#alert-divider")! const alertDivider: HTMLDivElement = document.querySelector("#alert-divider")!
const badgeUsername: HTMLDivElement = document.querySelector("#badge-username")! const badgeUsername: HTMLDivElement = document.querySelector("#badge-username")!
const buttonLogin: HTMLDivElement = document.querySelector("#button-login")! const buttonLogin: HTMLDivElement = document.querySelector("#button-login")!
const usernameElement: HTMLSpanElement = badgeUsername.querySelector("#username")! const usernameElement: HTMLSpanElement = badgeUsername.querySelector("#username")!
const loadingDoors: HTMLDivElement = document.querySelector("#loading-doors")!
alertUnauthenticated.classList.toggle("hidden", auth.authenticated) alertUnauthenticated.classList.toggle("hidden", auth.authenticated || auth.recentLogout)
alertUnauthorized.classList.toggle("hidden", !auth.authenticated || auth.authorized) alertUnauthorized.classList.toggle("hidden", !auth.authenticated || auth.authorized)
alertLoggedout.classList.toggle("hidden", !auth.recentLogout) alertLoggedout.classList.toggle("hidden", !auth.recentLogout)
alertServerError.classList.toggle("hidden", apiError.current !== "serverError") alertServerError.classList.toggle("hidden", apiError.current !== "serverError")
alertDivider.classList.toggle("hidden", auth.authenticated && auth.authorized && !auth.recentLogout && apiError.current === null) alertNetworkError.classList.toggle("hidden", apiError.current !== "networkError")
alertDivider.classList.toggle("hidden", auth.authenticated && auth.authorized && !auth.recentLogout && apiError.current === null || doors.length === 0)
badgeUsername.classList.toggle("hidden", !auth.authenticated) badgeUsername.classList.toggle("hidden", !auth.authenticated)
usernameElement.innerHTML = auth.username usernameElement.innerHTML = auth.username
buttonLogin.classList.toggle("hidden", auth.authenticated) buttonLogin.classList.toggle("hidden", auth.authenticated)
loadingDoors.classList.toggle("hidden", !loading.doors)
} }
loadAuthFromLocalStorage() loadAuthFromLocalStorage()
fetchDoors() fetchDoors()
checkUser() checkUser()

View file

@ -28,8 +28,10 @@ export const ui = {
"loggedOut.title": "Signed out", "loggedOut.title": "Signed out",
"loggedOut.description": `Your session has expired and you have been logged out.<br> "loggedOut.description": `Your session has expired and you have been logged out.<br>
Please log in again.`, Please log in again.`,
"serverError.title": "Server Error", "serverError.title": "Server error",
"serverError.description": `Please try again later.`, "serverError.description": `Please try again later.`,
"networkError.title": "Network error",
"networkError.description": `Please check your network connection.`,
}, },
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>",
@ -55,5 +57,7 @@ export const ui = {
Melde dich erneut an.`, Melde dich erneut an.`,
"serverError.title": "Serverfehler", "serverError.title": "Serverfehler",
"serverError.description": `Bitte versuche es später erneut.`, "serverError.description": `Bitte versuche es später erneut.`,
"networkError.title": "Netzwerkfehler",
"networkError.description": `Bitte überprüfe deine Internetverbindung.`,
}, },
} as const } as const