import { Credentials, DoorSaved, SiteConfigModel } from "@/lib/api"; import { Door } from "@/lib/door"; import { Advent22 } from "@/plugins/advent22"; import { RemovableRef, useLocalStorage } from "@vueuse/core"; import { AxiosBasicCredentials, AxiosError } from "axios"; import { toast } from "bulma-toast"; import { acceptHMRUpdate, defineStore } from "pinia"; declare global { interface Navigator { readonly msMaxTouchPoints: number; } } type State = { advent22: Advent22; api_creds: RemovableRef; is_initialized: boolean; on_initialized: (() => void)[]; is_touch_device: boolean; is_admin: boolean; site_config: SiteConfigModel; calendar_background_image: string | undefined; calendar_aspect_ratio: number; user_doors: Door[]; next_door_target: number | null; }; export const advent22Store = defineStore({ id: "advent22", state: (): State => ({ advent22: new Advent22(), api_creds: useLocalStorage("advent22/auth", ["", ""]), is_initialized: false, on_initialized: [], is_touch_device: window.matchMedia("(any-hover: none)").matches || "ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0, is_admin: false, site_config: { title: document.title, subtitle: "", content: "", footer: "", }, calendar_background_image: undefined, calendar_aspect_ratio: 1, user_doors: [], next_door_target: null, }), getters: { axios_creds: (state): AxiosBasicCredentials => { const [username, password] = state.api_creds; return { username: username, password: password }; }, }, actions: { init(): void { this.update() .then(() => { this.is_initialized = true; for (const callback of this.on_initialized) callback(); }) .catch(this.alert_user_error); }, format_user_error([reason, endpoint]: [unknown, string]): string { let msg = "Unbekannter Fehler, bitte wiederholen! Besteht das Problem länger, bitte Admin benachrichtigen!"; let code = "U"; const result = () => `${msg} (Fehlercode: ${code}/${endpoint})`; if (!(reason instanceof AxiosError)) return result(); switch (reason.code) { case "ECONNABORTED": // API unerreichbar msg = "API antwortet nicht, bitte später wiederholen! Besteht das Problem länger, bitte Admin benachrichtigen!"; code = "D"; break; case "ERR_NETWORK": // Netzwerk nicht verbunden msg = "Sieht aus, als sei deine Netzwerkverbindung gestört."; code = "N"; break; default: if (reason.response === undefined) return result(); switch (reason.response.status) { case 401: // UNAUTHORIZED msg = "Netter Versuch :)"; code = "A"; break; case 422: // UNPROCESSABLE ENTITY msg = "Funktion ist kaputt, bitte Admin benachrichtigen!"; code = "I"; break; default: // HTTP code = `H${reason.response.status}`; break; } break; } return result(); }, alert_user_error(param: [unknown, string]): void { toast({ message: this.format_user_error(param), type: "is-danger", }); }, update(): Promise { return new Promise((resolve, reject) => { this.advent22 .api_get_blob("user/favicon") .then((favicon_src) => { const link: HTMLLinkElement = document.querySelector("link[rel*='icon']") || document.createElement("link"); link.rel = "shortcut icon"; link.type = "image/x-icon"; link.href = favicon_src; if (link.parentElement === null) document.getElementsByTagName("head")[0].appendChild(link); }) .catch(() => {}); Promise.all([ this.update_is_admin(), this.advent22.api_get("user/site_config"), this.advent22.api_get_blob("user/background_image"), this.advent22.api_get("user/doors"), this.advent22.api_get("user/next_door"), ]) .then( ([ is_admin, site_config, background_image, user_doors, next_door, ]) => { is_admin; // discard value document.title = site_config.title; if (site_config.subtitle !== "") document.title += " – " + site_config.subtitle; this.site_config = site_config; this.calendar_background_image = background_image; this.user_doors.length = 0; for (const door_saved of user_doors) { this.user_doors.push(Door.load(door_saved)); } if (next_door !== null) this.next_door_target = Date.now() + next_door; resolve(); }, ) .catch(reject); }); }, when_initialized(callback: () => void): void { if (this.is_initialized) { callback(); } else { this.on_initialized.push(callback); } }, update_is_admin(): Promise { return new Promise((resolve, reject) => { this.advent22 .api_get("admin/is_admin") .then((is_admin) => { this.is_admin = is_admin; resolve(is_admin); }) .catch(reject); }); }, login(creds: Credentials): Promise { this.api_creds = creds; return this.update_is_admin(); }, logout(): Promise { return this.login(["", ""]); }, toggle_touch_device(): void { this.is_touch_device = !this.is_touch_device; }, set_calendar_aspect_ratio(rect: DOMRectReadOnly): void { const result = rect.width / rect.height; // filter suspicious results if (result !== 0 && isFinite(result) && !isNaN(result)) this.calendar_aspect_ratio = result; }, }, }); if (import.meta.webpackHot) { import.meta.webpackHot.accept( acceptHMRUpdate(advent22Store, import.meta.webpackHot), ); }