aboutsummaryrefslogtreecommitdiff
path: root/docker/nginx/sites/www/src/main.ts
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2024-11-11 01:12:29 +0800
committerYuqian Yang <crupest@crupest.life>2024-12-19 21:42:01 +0800
commitf9aa02ec1a4c24e80a206857d4f68198bb027bb4 (patch)
tree5994f0a62733b13f9f330e3515260ae20dc4a0bd /docker/nginx/sites/www/src/main.ts
parent7b4d49e4bbdff6ddf1f8f7e937130e700024d5e9 (diff)
downloadcrupest-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.ts97
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);