WIP: major refactoring

This commit is contained in:
Jörn-Michael Miehe 2024-08-23 18:21:32 +00:00
parent 55081b24d8
commit 15b957791f
7 changed files with 79 additions and 99 deletions

View file

@ -2,33 +2,34 @@ module.exports = {
root: true, root: true,
env: { env: {
node: true node: true,
}, },
'extends': [ extends: [
'plugin:vue/vue3-essential', "plugin:vue/vue3-essential",
'eslint:recommended', "eslint:recommended",
'@vue/typescript/recommended' "@vue/typescript/recommended",
], ],
parserOptions: { parserOptions: {
ecmaVersion: 2020 ecmaVersion: 2020,
}, },
rules: { rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', "no-empty": "off",
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
}, },
overrides: [ overrides: [
{ {
files: [ files: [
'**/__tests__/*.{j,t}s?(x)', "**/__tests__/*.{j,t}s?(x)",
'**/tests/unit/**/*.spec.{j,t}s?(x)' "**/tests/unit/**/*.spec.{j,t}s?(x)",
], ],
env: { env: {
mocha: true mocha: true,
} },
} },
] ],
} };

View file

@ -29,7 +29,7 @@
<figure> <figure>
<div class="image is-unselectable"> <div class="image is-unselectable">
<img :src="store.calendar_background_image" /> <img :src="store.background_image" />
<ThouCanvas> <ThouCanvas>
<CalendarDoor <CalendarDoor
v-for="(door, index) in doors" v-for="(door, index) in doors"

View file

@ -11,7 +11,7 @@
</ul> </ul>
</div> </div>
<figure class="image is-unselectable"> <figure class="image is-unselectable">
<img :src="store.calendar_background_image" /> <img :src="store.background_image" />
<ThouCanvas> <ThouCanvas>
<PreviewDoor <PreviewDoor
v-for="(door, index) in doors" v-for="(door, index) in doors"

View file

@ -9,7 +9,7 @@
</ul> </ul>
</div> </div>
<figure class="image is-unselectable"> <figure class="image is-unselectable">
<img :src="store.calendar_background_image" /> <img :src="store.background_image" />
<DoorCanvas :doors="doors" /> <DoorCanvas :doors="doors" />
</figure> </figure>
</div> </div>

View file

@ -1,3 +1,5 @@
import { APIError } from "./api_error";
export function objForEach<T>( export function objForEach<T>(
obj: T, obj: T,
f: (k: keyof T, v: T[keyof T]) => void, f: (k: keyof T, v: T[keyof T]) => void,
@ -8,3 +10,20 @@ export function objForEach<T>(
} }
} }
} }
export type Loading<T> = T | "loading" | "error";
export function loading_success<T>(o: Loading<T>): o is T {
if (o === "loading") return false;
if (o === "error") return false;
return true;
}
export function handle_error(error: unknown) {
if (error instanceof APIError) {
error.alert();
} else {
console.error(error);
}
}

View file

@ -60,7 +60,6 @@ export interface DoorSaved {
export interface ImageData { export interface ImageData {
height: number; height: number;
width: number; width: number;
aspect_ratio: number;
data_url: string; data_url: string;
} }

View file

@ -1,6 +1,7 @@
import { acceptHMRUpdate, defineStore } from "pinia"; import { acceptHMRUpdate, defineStore } from "pinia";
import { API } from "./api"; import { API } from "./api";
import { Credentials, DoorSaved, SiteConfigModel } from "./model"; import { Loading, loading_success } from "./helpers";
import { Credentials, DoorSaved, ImageData, SiteConfigModel } from "./model";
import { Door } from "./rects/door"; import { Door } from "./rects/door";
declare global { declare global {
@ -13,9 +14,8 @@ type State = {
on_initialized: (() => void)[]; on_initialized: (() => void)[];
is_touch_device: boolean; is_touch_device: boolean;
is_admin: boolean; is_admin: boolean;
site_config: SiteConfigModel; site_config: Loading<SiteConfigModel>;
calendar_background_image: string | undefined; background_image: Loading<ImageData>;
calendar_aspect_ratio: number;
user_doors: Door[]; user_doors: Door[];
next_door_target: number | null; next_door_target: number | null;
}; };
@ -31,14 +31,8 @@ export const advent22Store = defineStore({
navigator.maxTouchPoints > 0 || navigator.maxTouchPoints > 0 ||
navigator.msMaxTouchPoints > 0, navigator.msMaxTouchPoints > 0,
is_admin: false, is_admin: false,
site_config: { site_config: "loading",
title: document.title, background_image: "loading",
subtitle: "",
content: "",
footer: "",
},
calendar_background_image: undefined,
calendar_aspect_ratio: 1,
user_doors: [], user_doors: [],
next_door_target: null, next_door_target: null,
}), }),
@ -48,83 +42,58 @@ export const advent22Store = defineStore({
this.update().then(() => this.on_initialized.forEach((fn) => fn())); this.update().then(() => this.on_initialized.forEach((fn) => fn()));
}, },
update(): Promise<void> { async update(): Promise<void> {
return new Promise((resolve, reject) => { try {
API.request("user/favicon"); const favicon = await API.request<ImageData>("user/favicon");
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) const link: HTMLLinkElement =
document.getElementsByTagName("head")[0].appendChild(link); document.querySelector("link[rel*='icon']") ||
}) document.createElement("link");
.catch(() => {}); link.rel = "shortcut icon";
link.type = "image/x-icon";
link.href = favicon.data_url;
Promise.all([ if (link.parentElement === null)
document.getElementsByTagName("head")[0].appendChild(link);
} catch {}
const [is_admin, site_config, background_image, user_doors, next_door] =
await Promise.all([
this.update_is_admin(), this.update_is_admin(),
this.advent22.api_get<SiteConfigModel>("user/site_config"), API.request<SiteConfigModel>("user/site_config"),
this.advent22.api_get_blob("user/background_image"), API.request<ImageData>("user/background_image"),
this.advent22.api_get<DoorSaved[]>("user/doors"), API.request<DoorSaved[]>("user/doors"),
this.advent22.api_get<number | null>("user/next_door"), API.request<number | null>("user/next_door"),
]) ]);
.then( is_admin; // discard value
([
is_admin,
site_config,
background_image,
user_doors,
next_door,
]) => {
is_admin; // discard value
document.title = site_config.title; document.title = site_config.title;
if (site_config.subtitle !== "") if (site_config.subtitle !== "")
document.title += " " + site_config.subtitle; document.title += " " + site_config.subtitle;
this.site_config = site_config; this.site_config = site_config;
this.background_image = background_image;
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));
}
this.user_doors.length = 0; if (next_door !== null) this.next_door_target = Date.now() + next_door;
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 { when_initialized(callback: () => void): void {
if (this.is_initialized) { if (loading_success(this.site_config)) {
callback(); callback();
} else { } else {
this.on_initialized.push(callback); this.on_initialized.push(callback);
} }
}, },
update_is_admin(): Promise<boolean> { async update_is_admin(): Promise<boolean> {
return new Promise((resolve, reject) => { this.is_admin = await API.request<boolean>("admin/is_admin");
this.advent22 return this.is_admin;
.api_get<boolean>("admin/is_admin")
.then((is_admin) => {
this.is_admin = is_admin;
resolve(is_admin);
})
.catch(reject);
});
}, },
login(creds: Credentials): Promise<boolean> { login(creds: Credentials): Promise<boolean> {
@ -139,14 +108,6 @@ export const advent22Store = defineStore({
toggle_touch_device(): void { toggle_touch_device(): void {
this.is_touch_device = !this.is_touch_device; 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;
},
}, },
}); });