mirror of
https://code.lenaisten.de/Lenaisten/advent22.git
synced 2024-12-23 21:22:59 +00:00
first part of refactoring
- use `ImageData` and `Loading` types for image transfer - consolidate library directory - remove `advent22` plugin in favor of static `API` class
This commit is contained in:
parent
e3e19a0572
commit
109dec73d2
14 changed files with 113 additions and 72 deletions
|
@ -7,7 +7,18 @@
|
|||
</section>
|
||||
|
||||
<section class="section px-3">
|
||||
<div class="container">
|
||||
<progress
|
||||
v-if="store.background_image === 'loading'"
|
||||
class="progress is-primary"
|
||||
max="100"
|
||||
/>
|
||||
<div
|
||||
v-else-if="store.background_image === 'error'"
|
||||
class="notification is-danger"
|
||||
>
|
||||
Hintergrundbild konnte nicht geladen werden
|
||||
</div>
|
||||
<div v-else class="container">
|
||||
<AdminView v-if="store.is_admin" />
|
||||
<UserView v-else />
|
||||
</div>
|
||||
|
|
|
@ -15,6 +15,7 @@ import { Credentials } from "@/lib/model";
|
|||
import { advent22Store } from "@/lib/store";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
import { APIError } from "@/lib/api_error";
|
||||
import BulmaButton from "./bulma/Button.vue";
|
||||
import LoginModal from "./LoginModal.vue";
|
||||
|
||||
|
@ -44,7 +45,7 @@ export default class extends Vue {
|
|||
|
||||
this.store
|
||||
.login(creds)
|
||||
.catch(this.store.alert_user_error)
|
||||
.catch((error) => APIError.alert(error))
|
||||
.finally(() => (this.is_busy = false));
|
||||
}
|
||||
|
||||
|
|
|
@ -29,14 +29,14 @@
|
|||
|
||||
<figure>
|
||||
<div class="image is-unselectable">
|
||||
<img :src="store.background_image" />
|
||||
<img :src="_ensure_loaded(store.background_image).data_url" />
|
||||
<ThouCanvas>
|
||||
<CalendarDoor
|
||||
v-for="(door, index) in doors"
|
||||
:key="`door-${index}`"
|
||||
:door="door"
|
||||
:visible="store.is_touch_device"
|
||||
:title="$advent22.name_door(door.day)"
|
||||
:title="_name_door(door.day)"
|
||||
@click="door_click(door.day)"
|
||||
style="cursor: pointer"
|
||||
/>
|
||||
|
@ -46,6 +46,10 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { API } from "@/lib/api";
|
||||
import { APIError } from "@/lib/api_error";
|
||||
import { ensure_loaded, Loading, name_door } from "@/lib/helpers";
|
||||
import { ImageData } from "@/lib/model";
|
||||
import { Door } from "@/lib/rects/door";
|
||||
import { advent22Store } from "@/lib/store";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
@ -96,26 +100,32 @@ export default class extends Vue {
|
|||
});
|
||||
}
|
||||
|
||||
public door_click(day: number) {
|
||||
public async door_click(day: number) {
|
||||
if (this.toast_timeout !== undefined) clearTimeout(this.toast_timeout);
|
||||
this.toast?.hide();
|
||||
|
||||
if (this.multi_modal === undefined) return;
|
||||
this.multi_modal.show_progress();
|
||||
|
||||
this.$advent22
|
||||
.api_get_blob(`user/image_${day}`)
|
||||
.then((image_src) => {
|
||||
this.multi_modal!.show_image(image_src, this.$advent22.name_door(day));
|
||||
})
|
||||
.catch((error) => {
|
||||
this.store.alert_user_error(error);
|
||||
this.multi_modal!.hide();
|
||||
});
|
||||
try {
|
||||
const day_image = await API.request<ImageData>(`user/image_${day}`);
|
||||
this.multi_modal!.show_image(day_image.data_url, name_door(day));
|
||||
} catch (error) {
|
||||
APIError.alert(error);
|
||||
this.multi_modal!.hide();
|
||||
}
|
||||
}
|
||||
|
||||
public beforeUnmount(): void {
|
||||
this.toast?.hide();
|
||||
}
|
||||
|
||||
public _ensure_loaded<T>(o: Loading<T>): T {
|
||||
return ensure_loaded(o);
|
||||
}
|
||||
|
||||
public _name_door(day: number): string {
|
||||
return name_door(day);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,25 +1,22 @@
|
|||
<template>
|
||||
<template v-if="store.is_initialized === true">
|
||||
<Calendar :doors="store.user_doors" />
|
||||
<hr />
|
||||
<div class="content" v-html="store.site_config.content" />
|
||||
<div class="content has-text-primary">
|
||||
<template v-if="store.next_door_target === null">
|
||||
Alle {{ store.user_doors.length }} Türchen offen!
|
||||
<Calendar :doors="store.user_doors" />
|
||||
<hr />
|
||||
<div class="content" v-html="store.site_config.content" />
|
||||
<div class="content has-text-primary">
|
||||
<template v-if="store.next_door_target === null">
|
||||
Alle {{ store.user_doors.length }} Türchen offen!
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="store.user_doors.length === 0">
|
||||
Zeit bis zum ersten Türchen:
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="store.user_doors.length === 0">
|
||||
Zeit bis zum ersten Türchen:
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ store.user_doors.length }} Türchen offen. Zeit bis zum nächsten
|
||||
Türchen:
|
||||
</template>
|
||||
<CountDown :until="store.next_door_target" />
|
||||
{{ store.user_doors.length }} Türchen offen. Zeit bis zum nächsten
|
||||
Türchen:
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<progress v-else class="progress is-primary" max="100" />
|
||||
<CountDown :until="store.next_door_target" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
|
|
@ -46,8 +46,9 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { objForEach } from "@/lib/helpers";
|
||||
import { NumStrDict } from "@/lib/model";
|
||||
import { API } from "@/lib/api";
|
||||
import { name_door, objForEach } from "@/lib/helpers";
|
||||
import { ImageData, NumStrDict } from "@/lib/model";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
import MultiModal from "../MultiModal.vue";
|
||||
|
@ -77,8 +78,8 @@ export default class extends Vue {
|
|||
|
||||
public on_open(ready: () => void, fail: () => void): void {
|
||||
Promise.all([
|
||||
this.$advent22.api_get<NumStrDict>("admin/day_parts"),
|
||||
this.$advent22.api_get<NumStrDict>("admin/day_image_names"),
|
||||
API.request<NumStrDict>("admin/day_parts"),
|
||||
API.request<NumStrDict>("admin/day_image_names"),
|
||||
])
|
||||
.then(([day_parts, day_image_names]) => {
|
||||
const _ensure_day_in_data = (day: number) => {
|
||||
|
@ -102,16 +103,16 @@ export default class extends Vue {
|
|||
.catch(fail);
|
||||
}
|
||||
|
||||
public door_click(day: number) {
|
||||
public async door_click(day: number) {
|
||||
if (this.multi_modal === undefined) return;
|
||||
this.multi_modal.show_progress();
|
||||
|
||||
this.$advent22
|
||||
.api_get_blob(`user/image_${day}`)
|
||||
.then((image_src) =>
|
||||
this.multi_modal!.show_image(image_src, this.$advent22.name_door(day)),
|
||||
)
|
||||
.catch(() => this.multi_modal!.hide());
|
||||
try {
|
||||
const day_image = await API.request<ImageData>(`user/image_${day}`);
|
||||
this.multi_modal!.show_image(day_image.data_url, name_door(day));
|
||||
} catch (error) {
|
||||
this.multi_modal!.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -189,6 +189,7 @@ import { advent22Store } from "@/lib/store";
|
|||
import { DateTime } from "luxon";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
import { API } from "@/lib/api";
|
||||
import BulmaDrawer from "../bulma/Drawer.vue";
|
||||
import BulmaSecret from "../bulma/Secret.vue";
|
||||
import CountDown from "../CountDown.vue";
|
||||
|
@ -256,8 +257,8 @@ export default class extends Vue {
|
|||
public on_open(ready: () => void, fail: () => void): void {
|
||||
Promise.all([
|
||||
this.store.update(),
|
||||
this.$advent22.api_get<AdminConfigModel>("admin/config_model"),
|
||||
this.$advent22.api_get<DoorSaved[]>("admin/doors"),
|
||||
API.request<AdminConfigModel>("admin/config_model"),
|
||||
API.request<DoorSaved[]>("admin/doors"),
|
||||
])
|
||||
.then(([store_update, admin_config_model, doors]) => {
|
||||
store_update; // discard value
|
||||
|
@ -271,15 +272,13 @@ export default class extends Vue {
|
|||
}
|
||||
|
||||
public load_dav_credentials(): void {
|
||||
this.$advent22
|
||||
.api_get<Credentials>("admin/dav_credentials")
|
||||
API.request<Credentials>("admin/dav_credentials")
|
||||
.then((creds) => (this.dav_credentials = creds))
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
public load_ui_credentials(): void {
|
||||
this.$advent22
|
||||
.api_get<Credentials>("admin/ui_credentials")
|
||||
API.request<Credentials>("admin/ui_credentials")
|
||||
.then((creds) => (this.ui_credentials = creds))
|
||||
.catch(() => {});
|
||||
}
|
||||
|
|
|
@ -74,6 +74,8 @@ import { Door } from "@/lib/rects/door";
|
|||
import { advent22Store } from "@/lib/store";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
import { API } from "@/lib/api";
|
||||
import { APIError } from "@/lib/api_error";
|
||||
import { toast } from "bulma-toast";
|
||||
import Calendar from "../Calendar.vue";
|
||||
import BulmaBreadcrumbs, { Step } from "../bulma/Breadcrumbs.vue";
|
||||
|
@ -107,8 +109,7 @@ export default class extends Vue {
|
|||
|
||||
private load_doors(): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this.$advent22
|
||||
.api_get<DoorSaved[]>("admin/doors")
|
||||
API.request<DoorSaved[]>("admin/doors")
|
||||
.then((data) => {
|
||||
this.doors.length = 0;
|
||||
|
||||
|
@ -119,7 +120,7 @@ export default class extends Vue {
|
|||
resolve();
|
||||
})
|
||||
.catch((error) => {
|
||||
this.store.alert_user_error(error);
|
||||
APIError.alert(error);
|
||||
reject();
|
||||
});
|
||||
});
|
||||
|
@ -133,11 +134,10 @@ export default class extends Vue {
|
|||
data.push(door.save());
|
||||
}
|
||||
|
||||
this.$advent22
|
||||
.api_put("admin/doors", data)
|
||||
API.request<void>({ endpoint: "admin/doors", method: "PUT", data: data })
|
||||
.then(resolve)
|
||||
.catch((error) => {
|
||||
this.store.alert_user_error(error);
|
||||
APIError.alert(error);
|
||||
reject();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<template>
|
||||
<foreignObject
|
||||
:x="Math.round(store.calendar_aspect_ratio * rectangle.left)"
|
||||
:x="Math.round(aspect_ratio * rectangle.left)"
|
||||
:y="rectangle.top"
|
||||
:width="Math.round(store.calendar_aspect_ratio * rectangle.width)"
|
||||
:width="Math.round(aspect_ratio * rectangle.width)"
|
||||
:height="rectangle.height"
|
||||
:style="`transform: scaleX(${1 / store.calendar_aspect_ratio})`"
|
||||
:style="`transform: scaleX(${1 / aspect_ratio})`"
|
||||
>
|
||||
<div
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
|
@ -18,6 +18,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { loading_success } from "@/lib/helpers";
|
||||
import { Rectangle } from "@/lib/rects/rectangle";
|
||||
import { advent22Store } from "@/lib/store";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
@ -59,6 +60,14 @@ export default class extends Vue {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
public get aspect_ratio(): number {
|
||||
if (!loading_success(this.store.background_image)) return 1;
|
||||
|
||||
return (
|
||||
this.store.background_image.height / this.store.background_image.width
|
||||
);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -57,15 +57,6 @@ function mouse_event_validator(event: object, point: object): boolean {
|
|||
export default class extends Vue {
|
||||
public readonly store = advent22Store();
|
||||
|
||||
public mounted(): void {
|
||||
new ResizeObserver(([first, ...rest]) => {
|
||||
if (rest.length > 0)
|
||||
console.warn(`Unexpected ${rest.length} extra entries!`);
|
||||
|
||||
this.store.set_calendar_aspect_ratio(first.contentRect);
|
||||
}).observe(this.$el);
|
||||
}
|
||||
|
||||
public transform_mouse_event(event: MouseEvent) {
|
||||
const point = get_event_thous(event);
|
||||
this.$emit(event.type, event, point);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
</ul>
|
||||
</div>
|
||||
<figure class="image is-unselectable">
|
||||
<img :src="store.background_image" />
|
||||
<img :src="_ensure_loaded(store.background_image).data_url" />
|
||||
<ThouCanvas>
|
||||
<PreviewDoor
|
||||
v-for="(door, index) in doors"
|
||||
|
@ -24,6 +24,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ensure_loaded, Loading } from "@/lib/helpers";
|
||||
import { Door } from "@/lib/rects/door";
|
||||
import { advent22Store } from "@/lib/store";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
@ -43,5 +44,9 @@ import PreviewDoor from "./PreviewDoor.vue";
|
|||
export default class extends Vue {
|
||||
public doors!: Door[];
|
||||
public readonly store = advent22Store();
|
||||
|
||||
public _ensure_loaded<T>(o: Loading<T>): T {
|
||||
return ensure_loaded(o);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -9,13 +9,14 @@
|
|||
</ul>
|
||||
</div>
|
||||
<figure class="image is-unselectable">
|
||||
<img :src="store.background_image" />
|
||||
<img :src="_ensure_loaded(store.background_image).data_url" />
|
||||
<DoorCanvas :doors="doors" />
|
||||
</figure>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ensure_loaded, Loading } from "@/lib/helpers";
|
||||
import { Door } from "@/lib/rects/door";
|
||||
import { advent22Store } from "@/lib/store";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
@ -33,5 +34,9 @@ import DoorCanvas from "./DoorCanvas.vue";
|
|||
export default class extends Vue {
|
||||
public doors!: Door[];
|
||||
public readonly store = advent22Store();
|
||||
|
||||
public _ensure_loaded<T>(o: Loading<T>): T {
|
||||
return ensure_loaded(o);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -70,4 +70,8 @@ export class APIError extends Error {
|
|||
type: "is-danger",
|
||||
});
|
||||
}
|
||||
|
||||
public static alert(error: unknown) {
|
||||
new APIError(error, "").alert();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,12 @@ export function loading_success<T>(o: Loading<T>): o is T {
|
|||
return true;
|
||||
}
|
||||
|
||||
export function ensure_loaded<T>(o: Loading<T>): T {
|
||||
if (!loading_success(o)) throw "";
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
export function handle_error(error: unknown) {
|
||||
if (error instanceof APIError) {
|
||||
error.alert();
|
||||
|
@ -27,3 +33,7 @@ export function handle_error(error: unknown) {
|
|||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
export function name_door(day: number): string {
|
||||
return `Türchen ${day}`;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { advent22Store } from "@/lib/store";
|
||||
import { Advent22Plugin } from "@/plugins/advent22";
|
||||
import { FontAwesomePlugin } from "@/plugins/fontawesome";
|
||||
import * as bulmaToast from "bulma-toast";
|
||||
import { createPinia } from "pinia";
|
||||
|
@ -10,10 +9,9 @@ import "@/main.scss";
|
|||
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(Advent22Plugin);
|
||||
app.use(FontAwesomePlugin);
|
||||
|
||||
app.use(createPinia());
|
||||
|
||||
advent22Store().init();
|
||||
|
||||
app.mount("#app");
|
||||
|
|
Loading…
Reference in a new issue