From 7ee7ab5c014b319986da785dd2c5b265ac8c8ec4 Mon Sep 17 00:00:00 2001 From: holenet Date: Wed, 28 Aug 2024 18:05:43 +0900 Subject: [PATCH] add: vote record by you --- LDLWebClient/src/TopicEntry.tsx | 53 ++++++++++++++++++++++---- LDLWebClient/src/TopicForm.tsx | 66 +++++++++++++++++++++++++++++++++ LDLWebClient/src/hooks.ts | 19 ++++++++++ LDLWebClient/src/index.tsx | 55 ++------------------------- 4 files changed, 135 insertions(+), 58 deletions(-) create mode 100644 LDLWebClient/src/TopicForm.tsx create mode 100644 LDLWebClient/src/hooks.ts diff --git a/LDLWebClient/src/TopicEntry.tsx b/LDLWebClient/src/TopicEntry.tsx index a02c493..87a940a 100644 --- a/LDLWebClient/src/TopicEntry.tsx +++ b/LDLWebClient/src/TopicEntry.tsx @@ -1,10 +1,12 @@ -import { useSignal } from "@preact/signals"; +import { useComputed, useSignal } from "@preact/signals"; import { useEffect, useRef } from "preact/hooks"; import classNames from "classnames"; import { Topic } from "./model"; +import { useLocalStorageState } from "./hooks"; const API_BASE_URL = import.meta.env.VITE_API_BASE_URL; -const COLORS = ["bg-red-400", "bg-blue-400", "bg-green-400", "bg-orange-400", "bg-purple-400", "bg-yellow-400"]; +const BG_COLORS = ["bg-red-300", "bg-blue-300", "bg-green-300", "bg-orange-300", "bg-purple-300", "bg-yellow-200"]; +const COLORS = ["bg-red-500", "bg-blue-500", "bg-green-500", "bg-orange-500", "bg-purple-500", "bg-yellow-500"]; type Props = { topic: Topic; @@ -13,6 +15,15 @@ type Props = { export function TopicEntry({ topic: _topic, colorOffset }: Props) { const connectedWebSocket = useRef(); const topic = useSignal(); + const votePercentages = useComputed(() => { + const sum = topic.value.Votes.reduce((a, x) => a + x, 0); + if (sum === 0) return topic.value.Votes.map((_) => 0); + return topic.value.Votes.map((v) => v / sum); + }); + const [voteRecord, setVoteRecord] = useLocalStorageState( + `vote-record-${_topic.Id}`, + _topic.Votes.map((_) => 0) + ); useEffect(() => { topic.value = _topic; @@ -61,26 +72,54 @@ export function TopicEntry({ topic: _topic, colorOffset }: Props) { ...topic.value, Votes: topic.value.Votes.map((v, i) => Math.max(v, votes[i])), }; + setVoteRecord(voteRecord.map((v, i) => (i === index ? v + 1 : v))); }); }; return (
{topic.value && ( -
+
{topic.value.Content}
{topic.value.Choices.map((choice, i) => ( <> - {i !== 0 && vs} ))} diff --git a/LDLWebClient/src/TopicForm.tsx b/LDLWebClient/src/TopicForm.tsx new file mode 100644 index 0000000..d7efd09 --- /dev/null +++ b/LDLWebClient/src/TopicForm.tsx @@ -0,0 +1,66 @@ +import { useState } from "preact/hooks"; +import { Topic } from "./model"; + +const API_BASE_URL = import.meta.env.VITE_API_BASE_URL; + +type Props = { + onTopicAdded?: (topic: Topic) => any; +}; +export function TopicForm({ onTopicAdded }: Props) { + const [contentForm, setContentForm] = useState(""); + const [choiceAForm, setChoiceAForm] = useState(""); + const [choiceBForm, setChoiceBForm] = useState(""); + + const addTopic = async (e: Event) => { + e.preventDefault(); + const url = `${API_BASE_URL}/topic`; + const res = await fetch(url, { + method: "POST", + body: JSON.stringify({ + Content: contentForm, + Choices: [choiceAForm, choiceBForm], + }), + }); + setContentForm(""); + setChoiceAForm(""); + setChoiceBForm(""); + + const topic = await res.json(); + onTopicAdded?.(topic); + }; + + return ( +
+
+
토픽:
+ setContentForm(e.currentTarget.value)} + /> +
+
+
선택지A:
+ setChoiceAForm(e.currentTarget.value)} + /> +
+
+
선택지B:
+ setChoiceBForm(e.currentTarget.value)} + /> +
+ +
+ ); +} diff --git a/LDLWebClient/src/hooks.ts b/LDLWebClient/src/hooks.ts new file mode 100644 index 0000000..3051c1b --- /dev/null +++ b/LDLWebClient/src/hooks.ts @@ -0,0 +1,19 @@ +import { useEffect, useMemo, useState } from "preact/hooks"; + +export function useLocalStorageState(key: string, defaultValue: T): [T, (v: T) => any] { + const [_value, _setValue] = useState(); + + useEffect(() => { + const v = localStorage.getItem(key); + _setValue(v !== null ? JSON.parse(v) : defaultValue); + }, [key]); + + const setValue = useMemo(() => { + return (v: T) => { + _setValue(v); + localStorage.setItem(key, JSON.stringify(v)); + }; + }, [key]); + + return [_value, setValue]; +} diff --git a/LDLWebClient/src/index.tsx b/LDLWebClient/src/index.tsx index 628203a..529dd2d 100644 --- a/LDLWebClient/src/index.tsx +++ b/LDLWebClient/src/index.tsx @@ -1,9 +1,10 @@ import { useSignal } from "@preact/signals"; import { render } from "preact"; -import { useEffect, useState } from "preact/hooks"; +import { useEffect, useMemo, useState } from "preact/hooks"; import "./style.css"; import { Topic } from "./model"; import { TopicEntry } from "./TopicEntry"; +import { TopicForm } from "./TopicForm"; const API_BASE_URL = import.meta.env.VITE_API_BASE_URL; @@ -32,26 +33,9 @@ function App() { } }; - const [contentForm, setContentForm] = useState(""); - const [choiceAForm, setChoiceAForm] = useState(""); - const [choiceBForm, setChoiceBForm] = useState(""); - - const addTopic = async (e: Event) => { - e.preventDefault(); - const url = `${API_BASE_URL}/topic`; - const res = await fetch(url, { - method: "POST", - body: JSON.stringify({ - Content: contentForm, - Choices: [choiceAForm, choiceBForm], - }), - }); - const topic = await res.json(); + const onTopicAdded = (topic: Topic) => { topics.value = [...topics.value, topic]; currentIndex.value = topics.value.length - 1; - setContentForm(""); - setChoiceAForm(""); - setChoiceBForm(""); }; return ( @@ -89,38 +73,7 @@ function App() {
)}
-
-
-
토픽:
- setContentForm(e.currentTarget.value)} - /> -
-
-
선택지A:
- setChoiceAForm(e.currentTarget.value)} - /> -
-
-
선택지B:
- setChoiceBForm(e.currentTarget.value)} - /> -
- -
+
);