297 lines
7.9 KiB
TypeScript
297 lines
7.9 KiB
TypeScript
import {
|
|
PythagorasIncomingMessageType, type PythagorasClient, type PythagorasIncomingMessage
|
|
} from "./pythagoras_client";
|
|
import { ScrollingTextBox } from "./scrolling_textbox";
|
|
import { dict, IDLE_LOGOS, QUESTION_LINK, QUESTION_QR } from "./settings";
|
|
import { AsyncRunner, el, sleep, wait_for_dom_refresh } from "./tools";
|
|
|
|
interface PresentationScreen {
|
|
prepare(): Promise<void>;
|
|
start(): Promise<void>;
|
|
end(): Promise<void>;
|
|
serve(trigger: PythagorasIncomingMessage): Promise<void>;
|
|
dom: HTMLDivElement;
|
|
}
|
|
|
|
export class PresentationManager {
|
|
private screen: PresentationScreen;
|
|
private dom_root: HTMLDivElement;
|
|
|
|
public constructor() {
|
|
this.dom_root = el.div([]);
|
|
|
|
this.screen = new BlankScreen();
|
|
}
|
|
|
|
private async update_dom_screen(): Promise<void> {
|
|
await this.screen.prepare();
|
|
this.dom_root.innerHTML = "";
|
|
this.dom_root.appendChild(this.screen.dom);
|
|
await wait_for_dom_refresh();
|
|
}
|
|
|
|
public async serve(ws_client: PythagorasClient): Promise<void> {
|
|
while (true) {
|
|
const received = await ws_client.recv();
|
|
if (received.type == PythagorasIncomingMessageType.SetScreen) {
|
|
await this.screen.end();
|
|
this.set_screen(received.screen);
|
|
await this.update_dom_screen();
|
|
await this.screen.start();
|
|
continue;
|
|
}
|
|
await this.screen.serve(received);
|
|
}
|
|
}
|
|
|
|
private set_screen(screen: "idle" | "video" | "main"): void {
|
|
switch (screen) {
|
|
case "idle":
|
|
this.screen = new IdleScreen();
|
|
break;
|
|
case "video":
|
|
this.screen = new VideoScreen();
|
|
break;
|
|
case "main":
|
|
this.screen = new MainScreen();
|
|
break;
|
|
default:
|
|
throw new Error("unknown screen id");
|
|
}
|
|
}
|
|
|
|
public get dom(): HTMLDivElement {
|
|
return this.dom_root;
|
|
}
|
|
}
|
|
|
|
class MainScreen implements PresentationScreen {
|
|
private dom_root: HTMLDivElement;
|
|
private subs_english: AsyncRunner<ScrollingTextBox>;
|
|
private subs_czech: AsyncRunner<ScrollingTextBox>;
|
|
private dom_question: HTMLDivElement;
|
|
private dom_question_link: HTMLDivElement;
|
|
private question_insert: HTMLParagraphElement;
|
|
|
|
public constructor() {
|
|
const subs_en = new ScrollingTextBox();
|
|
const subs_cz = new ScrollingTextBox();
|
|
this.subs_english = new AsyncRunner(subs_en);
|
|
this.subs_czech = new AsyncRunner(subs_cz);
|
|
this.question_insert = el.p("Otázka");
|
|
this.dom_question_link = el.div([el.h1(QUESTION_LINK)], ["link"]);
|
|
this.dom_question = el.div([this.dom_question_link, this.question_insert], ["question"]);
|
|
this.dom_root = el.div([
|
|
this.dom_question,
|
|
el.div([
|
|
el.div([subs_en.dom], ["lang"]),
|
|
el.div([subs_cz.dom], ["lang"]),
|
|
], ["subtitles"]),
|
|
el.div([
|
|
el.p("Jedná se o automaticky generovaný přepis. Omluvte, prosíme, případné chyby.", ["disclaimer"]),
|
|
]),
|
|
], ["main"]);
|
|
}
|
|
|
|
public async prepare(): Promise<void> {
|
|
//this.dom_question_link.append(await el.img(QUESTION_QR, ["qr"]));
|
|
}
|
|
|
|
public async start(): Promise<void> {
|
|
await wait_for_dom_refresh();
|
|
this.dom_root.style.opacity = "1";
|
|
await sleep(500);
|
|
}
|
|
|
|
public async end(): Promise<void> {
|
|
this.dom_root.style.opacity = "0";
|
|
await sleep(500);
|
|
}
|
|
|
|
private async show_question(text: string): Promise<void> {
|
|
this.question_insert.innerText = text;
|
|
this.question_insert.style.opacity = "1";
|
|
await sleep(500);
|
|
}
|
|
|
|
private async hide_question(): Promise<void> {
|
|
this.question_insert.style.opacity = "0";
|
|
await sleep(500);
|
|
}
|
|
|
|
public async serve(trigger: PythagorasIncomingMessage): Promise<void> {
|
|
switch (trigger.type) {
|
|
case PythagorasIncomingMessageType.SubEnUpdateCur:
|
|
this.subs_english.run((target: ScrollingTextBox) => {
|
|
return target.update_current(trigger.text)
|
|
});
|
|
break;
|
|
case PythagorasIncomingMessageType.SubEnSubmit:
|
|
this.subs_english.run((target: ScrollingTextBox) => {
|
|
return target.finish_current(trigger.text);
|
|
});
|
|
break;
|
|
case PythagorasIncomingMessageType.SubCzSubmit:
|
|
this.subs_czech.run((target: ScrollingTextBox) => {
|
|
return target.add_line(trigger.text);
|
|
});
|
|
break;
|
|
case PythagorasIncomingMessageType.SelectedMessage:
|
|
if (trigger.message === null) { this.hide_question(); }
|
|
else { this.show_question(trigger.message); }
|
|
break;
|
|
}
|
|
}
|
|
|
|
public get dom(): HTMLDivElement {
|
|
return this.dom_root;
|
|
}
|
|
}
|
|
|
|
class VideoScreen implements PresentationScreen {
|
|
private dom_root: HTMLDivElement;
|
|
private dom_video: HTMLVideoElement;
|
|
|
|
private src: string | null;
|
|
private subtitles: string | null;
|
|
|
|
public constructor() {
|
|
this.src = null;
|
|
this.subtitles = null;
|
|
this.dom_video = document.createElement("video");
|
|
this.dom_root = el.div([this.dom_video], ["video"]);
|
|
}
|
|
|
|
public async prepare(): Promise<void> {
|
|
this.dom_root.innerHTML = "";
|
|
if (this.src !== null) {
|
|
this.dom_video = await el.video(
|
|
this.src, this.subtitles === null ? "" : this.subtitles
|
|
);
|
|
this.dom_video.volume = 0;
|
|
this.dom_root.appendChild(this.dom_video);
|
|
}
|
|
}
|
|
|
|
public async start(): Promise<void> {
|
|
this.dom_video.style.opacity = "1";
|
|
for (let i = 0; i <= 100; ++i) {
|
|
this.dom_video.volume = i / 100;
|
|
await sleep(10);
|
|
}
|
|
}
|
|
|
|
public async end(): Promise<void> {
|
|
this.dom_video.style.opacity = "0";
|
|
for (let i = 100; i >= 0; --i) {
|
|
this.dom_video.volume = i / 100;
|
|
await sleep(10);
|
|
}
|
|
}
|
|
|
|
public async serve(trigger: PythagorasIncomingMessage): Promise<void> {
|
|
switch (trigger.type) {
|
|
case PythagorasIncomingMessageType.PlayVideo:
|
|
await this.play_video(
|
|
trigger.filename, trigger.subtitles, trigger.seconds_from_start
|
|
);
|
|
break;
|
|
case PythagorasIncomingMessageType.SeekVideo:
|
|
this.seek_video(trigger.timestamp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private async play_video(
|
|
video_url: string, subtitles_url: string | null, seconds_from_start: number
|
|
): Promise<void> {
|
|
this.dom_video = await el.video(video_url, subtitles_url);
|
|
this.dom_video.currentTime = seconds_from_start;
|
|
this.dom_root.innerHTML = "";
|
|
this.dom_root.appendChild(this.dom_video);
|
|
await wait_for_dom_refresh();
|
|
await this.start();
|
|
}
|
|
|
|
private seek_video(seconds_from_start: number): void {
|
|
this.dom_video.currentTime = seconds_from_start;
|
|
}
|
|
|
|
public get dom(): HTMLDivElement {
|
|
return this.dom_root;
|
|
}
|
|
}
|
|
|
|
class IdleScreen implements PresentationScreen {
|
|
private dom_root: HTMLDivElement;
|
|
private dom_title: HTMLHeadingElement;
|
|
private dom_subtitle: HTMLHeadingElement;
|
|
private dom_logos: HTMLDivElement[];
|
|
|
|
public constructor() {
|
|
this.dom_title = el.h1(dict.IDLE_TITLE)
|
|
this.dom_subtitle = el.h2(dict.IDLE_STARTING);
|
|
this.dom_logos = [];
|
|
this.dom_root = el.div([this.dom_title, this.dom_subtitle], ["idle"]);
|
|
}
|
|
|
|
public async prepare(): Promise<void> {
|
|
for (const logo of IDLE_LOGOS) {
|
|
this.dom_logos.push(el.div([await el.img(logo)], ["logo"]));
|
|
}
|
|
const logos = el.div([...this.dom_logos], ["logos"]);
|
|
this.dom_root.appendChild(logos);
|
|
}
|
|
|
|
public async start(): Promise<void> {
|
|
await wait_for_dom_refresh();
|
|
this.dom_title.style.transform = "translateY(0)";
|
|
this.dom_title.style.opacity = "1";
|
|
await sleep(250);
|
|
this.dom_subtitle.style.opacity = "1";
|
|
await sleep(250);
|
|
for (const logo of this.dom_logos) {
|
|
logo.style.transform = "translateY(0)";
|
|
logo.style.opacity = "1";
|
|
await sleep(250);
|
|
}
|
|
await sleep(250);
|
|
}
|
|
|
|
public async end(): Promise<void> {
|
|
for (const logo of this.dom_logos) {
|
|
logo.style.transform = "translateY(100%)";
|
|
logo.style.opacity = "0";
|
|
await sleep(250);
|
|
}
|
|
this.dom_subtitle.style.opacity = "0";
|
|
await sleep(250);
|
|
this.dom_title.style.transform = "translateY(-100%)";
|
|
this.dom_title.style.opacity = "0";
|
|
await sleep(500);
|
|
}
|
|
|
|
public async serve(trigger: PythagorasIncomingMessage): Promise<void> {}
|
|
|
|
public get dom(): HTMLDivElement {
|
|
return this.dom_root;
|
|
}
|
|
}
|
|
|
|
class BlankScreen implements PresentationScreen {
|
|
private dom_root: HTMLDivElement;
|
|
|
|
public constructor() {
|
|
this.dom_root = el.div([]);
|
|
}
|
|
|
|
public async prepare() {}
|
|
public async start() {}
|
|
public async end() {}
|
|
public async serve(_: PythagorasIncomingMessage) {}
|
|
|
|
public get dom(): HTMLDivElement {
|
|
return this.dom_root;
|
|
}
|
|
}
|