diff options
author | crupest <crupest@outlook.com> | 2024-11-11 01:12:29 +0800 |
---|---|---|
committer | Yuqian Yang <crupest@crupest.life> | 2024-12-19 21:42:01 +0800 |
commit | f9aa02ec1a4c24e80a206857d4f68198bb027bb4 (patch) | |
tree | 5994f0a62733b13f9f330e3515260ae20dc4a0bd /docker/nginx/sites/www/src/main.ts | |
parent | 7b4d49e4bbdff6ddf1f8f7e937130e700024d5e9 (diff) | |
download | crupest-f9aa02ec1a4c24e80a206857d4f68198bb027bb4.tar.gz crupest-f9aa02ec1a4c24e80a206857d4f68198bb027bb4.tar.bz2 crupest-f9aa02ec1a4c24e80a206857d4f68198bb027bb4.zip |
HALF WORK: 2024.12.19
Re-organize file structure.
Diffstat (limited to 'docker/nginx/sites/www/src/main.ts')
-rw-r--r-- | docker/nginx/sites/www/src/main.ts | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/docker/nginx/sites/www/src/main.ts b/docker/nginx/sites/www/src/main.ts new file mode 100644 index 0000000..2f09deb --- /dev/null +++ b/docker/nginx/sites/www/src/main.ts @@ -0,0 +1,97 @@ +import "./style.css"; + +import { fetchTodos } from "./todos"; + +const happy = "happy" as const; +const angry = "angry" as const; +type Emotion = typeof happy | typeof angry; + +function emotionOpposite(emotion: Emotion): Emotion { + if (emotion === happy) { + return angry; + } else { + return happy; + } +} + +function emotionElement(emotion: Emotion): HTMLDivElement { + return document.querySelector<HTMLDivElement>(`.slogan.${emotion}`)!; +} + +function emotionElementHeight(emotion: Emotion): number { + return emotionElement(emotion).clientHeight; +} + +function updateBodyTopPadding(emotion: Emotion): void { + document.body.style.paddingTop = `${emotionElementHeight(emotion)}px`; +} + +const sloganEmotionKey = "sloganEmotion"; + +const savedEmotion = + (localStorage.getItem(sloganEmotionKey) as Emotion | null) ?? happy; +if (savedEmotion !== happy && savedEmotion !== angry) { + console.error(`Invalid saved emotion: ${savedEmotion}`); +} + +updateBodyTopPadding(savedEmotion); +// Then we add transition animation. +setTimeout(() => { + document.body.style.transition = "padding-top 1s"; +}); + +const sloganContainer = document.querySelector( + ".slogan-container", +) as HTMLDivElement; + +setTimeout(() => { + sloganContainer.dataset.sloganEmotion = savedEmotion; +}, 500); + +const sloganLoadedPromise = new Promise<void>((resolve) => { + setTimeout(() => { + resolve(); + }, 1500); +}); + +for (const emotion of [happy, angry]) { + emotionElement(emotion).addEventListener("click", () => { + const opposite = emotionOpposite(emotion); + localStorage.setItem(sloganEmotionKey, opposite); + sloganContainer.dataset.sloganEmotion = opposite; + updateBodyTopPadding(opposite); + }); +} + +async function loadTodos(syncWith: Promise<unknown>): Promise<void> { + const todoMessage = document.getElementById("todo-message")!; + const todoContainer = document.getElementById("todo-container")!; + + try { + const todosPromise = fetchTodos(); + await syncWith; // Let's wait this first. + const todos = await todosPromise; + todos.forEach((item, index) => { + const { status, title, closed } = item; + const li = document.createElement("li"); + li.dataset.status = closed ? "closed" : "open"; + li.style.animationDelay = `${index * 0.04}s`; + // The color from api server is kind of ugly at present. + // li.style.background = color; + const statusSpan = document.createElement("span"); + const titleSpan = document.createElement("span"); + statusSpan.textContent = status; + titleSpan.textContent = title; + li.appendChild(statusSpan); + li.append(" : "); + li.append(titleSpan); + todoContainer.appendChild(li); + }); + todoMessage.parentElement!.removeChild(todoMessage); + } catch (e) { + todoMessage.style.color = "red"; + todoMessage.textContent = (e as Error).message; + } +} + +loadTodos(sloganLoadedPromise); |