move script to own file
This commit is contained in:
parent
bcfcf264f6
commit
6ff45b81bf
2 changed files with 228 additions and 217 deletions
214
app/src/assets/main.ts
Normal file
214
app/src/assets/main.ts
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
import {Fetcher} from "openapi-typescript-fetch"
|
||||
import {type paths} from "../api/schema"
|
||||
|
||||
const fetcher = Fetcher.for<paths>()
|
||||
|
||||
const list: HTMLDivElement = document.querySelector("#list")!
|
||||
const template: HTMLTemplateElement = document.querySelector("#template-door")!
|
||||
|
||||
type DoorType = {
|
||||
id: string;
|
||||
label: string;
|
||||
state: "unlocked" | "locked" | "unknown" | "unlocking" | "locking";
|
||||
batteryLow: boolean;
|
||||
unreachable: boolean;
|
||||
jammed: boolean;
|
||||
}
|
||||
|
||||
type AuthType = {
|
||||
username: string;
|
||||
authorized: boolean;
|
||||
authenticated: boolean;
|
||||
until: Date | null;
|
||||
recentLogout: boolean;
|
||||
}
|
||||
|
||||
const auth: AuthType = {
|
||||
username: "user",
|
||||
authorized: true,
|
||||
authenticated: false,
|
||||
until: null,
|
||||
recentLogout: false,
|
||||
}
|
||||
|
||||
const apiError: {
|
||||
current: "serverError" | null;
|
||||
} = {
|
||||
current: null,
|
||||
}
|
||||
|
||||
const doors: Array<DoorType> = []
|
||||
|
||||
function loadAuthFromLocalStorage() {
|
||||
const localAuth = JSON.parse(localStorage.getItem("auth") ?? "")
|
||||
auth.recentLogout = localAuth.recentLogout // set recentLogout true, if user was logged in before
|
||||
auth.authenticated = localAuth.authenticated
|
||||
auth.authorized = localAuth.authorized
|
||||
auth.until = localAuth.until ? new Date(localAuth.until) : null
|
||||
auth.username = localAuth.username
|
||||
}
|
||||
|
||||
async function checkUser() {
|
||||
const getUserInfo = fetcher.path("/api/user-info/").method("get").create()
|
||||
|
||||
try {
|
||||
const {status, data: userInfo} = await getUserInfo({})
|
||||
|
||||
apiError.current = null
|
||||
auth.authenticated = userInfo.is_logged_in
|
||||
auth.authorized = true
|
||||
auth.until = userInfo.guaranteed_session_until ? new Date(userInfo.guaranteed_session_until) : null
|
||||
auth.username = userInfo.user_info?.username ?? ""
|
||||
auth.recentLogout = false
|
||||
} catch (e) {
|
||||
// check which operation threw the exception
|
||||
if (e instanceof getUserInfo.Error) {
|
||||
const error = e.getActualType()
|
||||
|
||||
if (error.status === 401) {
|
||||
if (!auth.recentLogout)
|
||||
auth.recentLogout = auth.authenticated // set recentLogout true, if user was logged in before
|
||||
auth.authenticated = false
|
||||
auth.authorized = false
|
||||
auth.until = null
|
||||
auth.username = ""
|
||||
} else if (error.status >= 500 && error.status < 600) {
|
||||
apiError.current = "serverError"
|
||||
} else {
|
||||
console.error("unknown error:", error)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
localStorage.setItem("auth", JSON.stringify(auth))
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchDoors() {
|
||||
const getDoors = fetcher.path("/api/locks/").method("get").create()
|
||||
|
||||
try {
|
||||
const {status, data: doorInfo} = await getDoors({})
|
||||
|
||||
apiError.current = null
|
||||
while (doors.length) {
|
||||
doors.pop()
|
||||
}
|
||||
doorInfo.forEach(door => {
|
||||
let state: DoorType["state"] = "unknown"
|
||||
|
||||
switch (door.status.activity_state) {
|
||||
case "locking":
|
||||
case "unlocking":
|
||||
case "unknown":
|
||||
state = door.status.activity_state
|
||||
break
|
||||
case "stable":
|
||||
state = door.status.lock_state
|
||||
}
|
||||
|
||||
doors.push({
|
||||
id: door.name, // TODO: replace by actual ID
|
||||
label: door.name,
|
||||
state: state,
|
||||
batteryLow: door.status.is_low_battery,
|
||||
unreachable: door.status.is_unreachable,
|
||||
jammed: door.status.is_error_jammed,
|
||||
})
|
||||
})
|
||||
} catch (e) {
|
||||
// check which operation threw the exception
|
||||
if (e instanceof getDoors.Error) {
|
||||
const error = e.getActualType()
|
||||
|
||||
if (error.status === 401) {
|
||||
console.log("unauthorized")
|
||||
} else if (error.status >= 500 && error.status < 600) {
|
||||
apiError.current = "serverError"
|
||||
} else {
|
||||
console.error("unknown error:", error)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
function setDoorInfo(doorElement: HTMLDivElement, door: DoorType) {
|
||||
const labelElement: HTMLDivElement = doorElement.querySelector("[data-label]")!
|
||||
const buttonElements: Array<HTMLButtonElement> = Array.from(doorElement.querySelectorAll("button"))
|
||||
|
||||
doorElement.dataset.id = door.id
|
||||
doorElement.dataset.state = door.state
|
||||
|
||||
if (auth.authenticated && auth.authorized) {
|
||||
doorElement.dataset.active = ""
|
||||
} else {
|
||||
delete doorElement.dataset.active
|
||||
}
|
||||
|
||||
if (door.batteryLow) {
|
||||
doorElement.dataset.battery = ""
|
||||
} else {
|
||||
delete doorElement.dataset.battery
|
||||
}
|
||||
|
||||
if (door.unreachable) {
|
||||
doorElement.dataset.unreachable = ""
|
||||
} else {
|
||||
delete doorElement.dataset.unreachable
|
||||
}
|
||||
|
||||
if (door.jammed) {
|
||||
doorElement.dataset.jammed = ""
|
||||
} else {
|
||||
delete doorElement.dataset.jammed
|
||||
}
|
||||
|
||||
labelElement.innerHTML = door.label
|
||||
|
||||
buttonElements.forEach(button => {
|
||||
button.disabled = ["unlocking", "locking"].includes(door.state) || apiError.current !== null
|
||||
})
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
for (const door of doors) {
|
||||
let doorElement: HTMLDivElement | null = list.querySelector(`[data-id='${door.id}']`)
|
||||
if (doorElement) {
|
||||
setDoorInfo(doorElement, door)
|
||||
continue
|
||||
}
|
||||
|
||||
const targetElement = document.importNode(template.content, true)
|
||||
doorElement = targetElement.querySelector("[data-state]")!
|
||||
setDoorInfo(doorElement, door)
|
||||
list.appendChild(targetElement)
|
||||
}
|
||||
|
||||
const alertUnauthenticated: HTMLDivElement = document.querySelector("#alert-unauthenticated")!
|
||||
const alertUnauthorized: HTMLDivElement = document.querySelector("#alert-unauthorized")!
|
||||
const alertLoggedout: HTMLDivElement = document.querySelector("#alert-loggedout")!
|
||||
const alertServerError: HTMLDivElement = document.querySelector("#alert-serverError")!
|
||||
const alertDivider: HTMLDivElement = document.querySelector("#alert-divider")!
|
||||
const badgeUsername: HTMLDivElement = document.querySelector("#badge-username")!
|
||||
const buttonLogin: HTMLDivElement = document.querySelector("#button-login")!
|
||||
const usernameElement: HTMLSpanElement = badgeUsername.querySelector("#username")!
|
||||
|
||||
alertUnauthenticated.classList.toggle("hidden", auth.authenticated)
|
||||
alertUnauthorized.classList.toggle("hidden", !auth.authenticated || auth.authorized)
|
||||
alertLoggedout.classList.toggle("hidden", !auth.recentLogout)
|
||||
alertServerError.classList.toggle("hidden", apiError.current !== "serverError")
|
||||
alertDivider.classList.toggle("hidden", auth.authenticated && auth.authorized && !auth.recentLogout && apiError.current === null)
|
||||
badgeUsername.classList.toggle("hidden", !auth.authenticated)
|
||||
usernameElement.innerHTML = auth.username
|
||||
buttonLogin.classList.toggle("hidden", auth.authenticated)
|
||||
}
|
||||
|
||||
loadAuthFromLocalStorage()
|
||||
fetchDoors()
|
||||
checkUser()
|
||||
|
||||
document.addEventListener("loadeddata", () => {
|
||||
refresh()
|
||||
})
|
||||
|
|
@ -20,222 +20,7 @@ const t = useTranslations(lang)
|
|||
|
||||
<Layout>
|
||||
<Fragment slot="head">
|
||||
<script>
|
||||
import {Fetcher} from "openapi-typescript-fetch"
|
||||
import {type paths} from "../../api/schema"
|
||||
|
||||
const fetcher = Fetcher.for<paths>()
|
||||
|
||||
const list: HTMLDivElement = document.querySelector("#list")!
|
||||
const template: HTMLTemplateElement = document.querySelector("#template-door")!
|
||||
|
||||
type DoorType = {
|
||||
id: string;
|
||||
label: string;
|
||||
state: "unlocked" | "locked" | "unknown" | "unlocking" | "locking";
|
||||
batteryLow: boolean;
|
||||
unreachable: boolean;
|
||||
jammed: boolean;
|
||||
}
|
||||
|
||||
type AuthType = {
|
||||
username: string;
|
||||
authorized: boolean;
|
||||
authenticated: boolean;
|
||||
until: Date | null;
|
||||
recentLogout: boolean;
|
||||
}
|
||||
|
||||
const auth: AuthType = {
|
||||
username: "user",
|
||||
authorized: true,
|
||||
authenticated: false,
|
||||
until: null,
|
||||
recentLogout: false,
|
||||
}
|
||||
|
||||
const apiError: {
|
||||
current: "serverError" | null;
|
||||
} = {
|
||||
current: null,
|
||||
}
|
||||
|
||||
const doors: Array<DoorType> = []
|
||||
|
||||
function loadAuthFromLocalStorage() {
|
||||
const localAuth = JSON.parse(localStorage.getItem("auth") ?? "")
|
||||
auth.recentLogout = localAuth.recentLogout // set recentLogout true, if user was logged in before
|
||||
auth.authenticated = localAuth.authenticated
|
||||
auth.authorized = localAuth.authorized
|
||||
auth.until = localAuth.until ? new Date(localAuth.until) : null
|
||||
auth.username = localAuth.username
|
||||
}
|
||||
|
||||
async function checkUser() {
|
||||
const getUserInfo = fetcher.path("/api/user-info/").method("get").create()
|
||||
|
||||
try {
|
||||
const {status, data: userInfo} = await getUserInfo({})
|
||||
|
||||
apiError.current = null
|
||||
auth.authenticated = userInfo.is_logged_in
|
||||
auth.authorized = true
|
||||
auth.until = userInfo.guaranteed_session_until ? new Date(userInfo.guaranteed_session_until) : null
|
||||
auth.username = userInfo.user_info?.username ?? ""
|
||||
auth.recentLogout = false
|
||||
} catch (e) {
|
||||
// check which operation threw the exception
|
||||
if (e instanceof getUserInfo.Error) {
|
||||
const error = e.getActualType()
|
||||
|
||||
if (error.status === 401) {
|
||||
if (!auth.recentLogout)
|
||||
auth.recentLogout = auth.authenticated // set recentLogout true, if user was logged in before
|
||||
auth.authenticated = false
|
||||
auth.authorized = false
|
||||
auth.until = null
|
||||
auth.username = ""
|
||||
} else if (error.status >= 500 && error.status < 600) {
|
||||
apiError.current = "serverError"
|
||||
} else {
|
||||
console.error("unknown error:", error)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
localStorage.setItem("auth", JSON.stringify(auth))
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchDoors() {
|
||||
const getDoors = fetcher.path("/api/locks/").method("get").create()
|
||||
|
||||
try {
|
||||
const {status, data: doorInfo} = await getDoors({})
|
||||
|
||||
apiError.current = null
|
||||
while (doors.length) {
|
||||
doors.pop()
|
||||
}
|
||||
doorInfo.forEach(door => {
|
||||
let state: DoorType["state"] = "unknown"
|
||||
|
||||
switch (door.status.activity_state) {
|
||||
case "locking":
|
||||
case "unlocking":
|
||||
case "unknown":
|
||||
state = door.status.activity_state
|
||||
break
|
||||
case "stable":
|
||||
state = door.status.lock_state
|
||||
}
|
||||
|
||||
doors.push({
|
||||
id: door.name, // TODO: replace by actual ID
|
||||
label: door.name,
|
||||
state: state,
|
||||
batteryLow: door.status.is_low_battery,
|
||||
unreachable: door.status.is_unreachable,
|
||||
jammed: door.status.is_error_jammed,
|
||||
})
|
||||
})
|
||||
} catch (e) {
|
||||
// check which operation threw the exception
|
||||
if (e instanceof getDoors.Error) {
|
||||
const error = e.getActualType()
|
||||
|
||||
if (error.status === 401) {
|
||||
console.log("unauthorized")
|
||||
} else if (error.status >= 500 && error.status < 600) {
|
||||
apiError.current = "serverError"
|
||||
} else {
|
||||
console.error("unknown error:", error)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
function setDoorInfo(doorElement: HTMLDivElement, door: DoorType) {
|
||||
const labelElement: HTMLDivElement = doorElement.querySelector("[data-label]")!
|
||||
const buttonElements: Array<HTMLButtonElement> = Array.from(doorElement.querySelectorAll("button"))
|
||||
|
||||
doorElement.dataset.id = door.id
|
||||
doorElement.dataset.state = door.state
|
||||
|
||||
if (auth.authenticated && auth.authorized) {
|
||||
doorElement.dataset.active = ""
|
||||
} else {
|
||||
delete doorElement.dataset.active
|
||||
}
|
||||
|
||||
if (door.batteryLow) {
|
||||
doorElement.dataset.battery = ""
|
||||
} else {
|
||||
delete doorElement.dataset.battery
|
||||
}
|
||||
|
||||
if (door.unreachable) {
|
||||
doorElement.dataset.unreachable = ""
|
||||
} else {
|
||||
delete doorElement.dataset.unreachable
|
||||
}
|
||||
|
||||
if (door.jammed) {
|
||||
doorElement.dataset.jammed = ""
|
||||
} else {
|
||||
delete doorElement.dataset.jammed
|
||||
}
|
||||
|
||||
labelElement.innerHTML = door.label
|
||||
|
||||
buttonElements.forEach(button => {
|
||||
button.disabled = ["unlocking", "locking"].includes(door.state) || apiError.current !== null
|
||||
})
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
for (const door of doors) {
|
||||
let doorElement: HTMLDivElement | null = list.querySelector(`[data-id='${door.id}']`)
|
||||
if (doorElement) {
|
||||
setDoorInfo(doorElement, door)
|
||||
continue
|
||||
}
|
||||
|
||||
const targetElement = document.importNode(template.content, true)
|
||||
doorElement = targetElement.querySelector("[data-state]")!
|
||||
setDoorInfo(doorElement, door)
|
||||
list.appendChild(targetElement)
|
||||
}
|
||||
|
||||
const alertUnauthenticated: HTMLDivElement = document.querySelector("#alert-unauthenticated")!
|
||||
const alertUnauthorized: HTMLDivElement = document.querySelector("#alert-unauthorized")!
|
||||
const alertLoggedout: HTMLDivElement = document.querySelector("#alert-loggedout")!
|
||||
const alertServerError: HTMLDivElement = document.querySelector("#alert-serverError")!
|
||||
const alertDivider: HTMLDivElement = document.querySelector("#alert-divider")!
|
||||
const badgeUsername: HTMLDivElement = document.querySelector("#badge-username")!
|
||||
const buttonLogin: HTMLDivElement = document.querySelector("#button-login")!
|
||||
const usernameElement: HTMLSpanElement = badgeUsername.querySelector("#username")!
|
||||
|
||||
alertUnauthenticated.classList.toggle("hidden", auth.authenticated)
|
||||
alertUnauthorized.classList.toggle("hidden", !auth.authenticated || auth.authorized)
|
||||
alertLoggedout.classList.toggle("hidden", !auth.recentLogout)
|
||||
alertServerError.classList.toggle("hidden", apiError.current !== "serverError")
|
||||
alertDivider.classList.toggle("hidden", auth.authenticated && auth.authorized && !auth.recentLogout && apiError.current === null)
|
||||
badgeUsername.classList.toggle("hidden", !auth.authenticated)
|
||||
usernameElement.innerHTML = auth.username
|
||||
buttonLogin.classList.toggle("hidden", auth.authenticated)
|
||||
}
|
||||
|
||||
loadAuthFromLocalStorage()
|
||||
fetchDoors()
|
||||
checkUser()
|
||||
|
||||
document.addEventListener("loadeddata", () => {
|
||||
refresh()
|
||||
})
|
||||
</script>
|
||||
<script src="../../assets/main.ts"/>
|
||||
</Fragment>
|
||||
<div class="grid place-items-center grid-cols-1 gap-4 max-w-xl mx-auto" id="list">
|
||||
<Alert
|
||||
|
|
@ -281,8 +66,20 @@ const t = useTranslations(lang)
|
|||
title={t("serverError.title")}
|
||||
description={t("serverError.description")}
|
||||
/>
|
||||
<Alert
|
||||
classList="hidden"
|
||||
id="alert-networkError"
|
||||
color="error"
|
||||
title={t("networkError.title")}
|
||||
description={t("networkError.description")}
|
||||
/>
|
||||
<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="loading loading-spinner loading-xl"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DoorTemplate state="unknown"/>
|
||||
</Layout>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue