From b19b19b0664979bdcde7efc227871b8ccc42a00c Mon Sep 17 00:00:00 2001 From: hye1ee Date: Mon, 11 Nov 2024 11:33:40 +0900 Subject: [PATCH 01/15] style: make new icons --- packages/web/package.json | 1 + packages/web/src/components/atoms/Bubble.tsx | 30 ++++++++++++ .../src/components/molecules/ChatInput.tsx | 46 ++++++++++++++++--- 3 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 packages/web/src/components/atoms/Bubble.tsx diff --git a/packages/web/package.json b/packages/web/package.json index efc521f5..b2cffc84 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -18,6 +18,7 @@ "axios": "^1.4.0", "framer-motion": "^10.16.1", "immer": "^10.0.2", + "lucide-react": "^0.456.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-intersection-observer": "^9.5.2", diff --git a/packages/web/src/components/atoms/Bubble.tsx b/packages/web/src/components/atoms/Bubble.tsx new file mode 100644 index 00000000..6821dc14 --- /dev/null +++ b/packages/web/src/components/atoms/Bubble.tsx @@ -0,0 +1,30 @@ +import { bg, center, h, padding, round, text, w } from "@biseo/web/styles"; +import { css } from "@emotion/react"; + +interface Props { + label: string; + position: "top" | "bottom"; +} + +const BubbleStyle = css` + ${w("hug")} + ${h(24)} + + ${center} + ${padding.horizontal(10)} + ${bg.black} + ${round.md} + + ${text.option2} + ${text.white} + + position: absolute; + left: 50%; + transform: translate(-50%, 0%); +`; + +export const Bubble: React.FC = ({ label, position }: Props) => ( +
+ {label} +
+); diff --git a/packages/web/src/components/molecules/ChatInput.tsx b/packages/web/src/components/molecules/ChatInput.tsx index 33811cc6..eacbe615 100644 --- a/packages/web/src/components/molecules/ChatInput.tsx +++ b/packages/web/src/components/molecules/ChatInput.tsx @@ -1,8 +1,10 @@ +/* eslint-disable import/no-extraneous-dependencies */ import React, { useCallback, useMemo } from "react"; import { css } from "@emotion/react"; -import { SendIcon } from "@biseo/web/assets"; -import { Divider, TextAreaAutosize } from "@biseo/web/components/atoms"; +/* TODO: */ +import { SendHorizontal, Ghost, Megaphone } from "lucide-react"; +import { TextAreaAutosize } from "@biseo/web/components/atoms"; import { useInput } from "@biseo/web/common/hooks"; import { w, @@ -17,6 +19,8 @@ import { gap, border, round, + center, + colors, } from "@biseo/web/styles"; const inputBoxStyle = css` @@ -38,7 +42,7 @@ const formStyle = css` ${bg.white} ${border.gray300} ${round.md} - ${align.center} + ${align.end} `; const textAreaScrollStyle = css` @@ -65,10 +69,23 @@ export const ChatInput: React.FC = ({ send }) => { setValue(""); }, [input.value, validated]); + const sendIconStyle = css` + ${center} + ${w(28)} + ${h(28)} + + ${validated ? bg.blue600 : bg.gray300} + ${round.md} + + flex: 0 0 auto; + cursor: pointer; + transition: all 0.2s; + `; + return (
-
+
{ @@ -85,10 +102,27 @@ export const ChatInput: React.FC = ({ send }) => { maxLength={maxMessageLength} onChange={input.onChange} /> - + {/* */}
{/* TODO: Add EmoticonIcon */} - +
+
+ + +
+
+ +
+
+ {/* TODO: Replace with button / add hover, actove effect */}
From 2934a1ea9e3f23f1f9c6933752b316f3cc24f79a Mon Sep 17 00:00:00 2001 From: hye1ee Date: Mon, 11 Nov 2024 20:53:59 +0900 Subject: [PATCH 02/15] feat: make bubble hover interaction --- packages/web/src/components/atoms/Bubble.tsx | 6 ++-- .../src/components/molecules/BubbleItem.tsx | 31 +++++++++++++++++ .../src/components/molecules/ChatInput.tsx | 33 ++++++++++++------- 3 files changed, 56 insertions(+), 14 deletions(-) create mode 100644 packages/web/src/components/molecules/BubbleItem.tsx diff --git a/packages/web/src/components/atoms/Bubble.tsx b/packages/web/src/components/atoms/Bubble.tsx index 6821dc14..49dc626b 100644 --- a/packages/web/src/components/atoms/Bubble.tsx +++ b/packages/web/src/components/atoms/Bubble.tsx @@ -1,7 +1,7 @@ import { bg, center, h, padding, round, text, w } from "@biseo/web/styles"; import { css } from "@emotion/react"; -interface Props { +export interface Props { label: string; position: "top" | "bottom"; } @@ -21,10 +21,12 @@ const BubbleStyle = css` position: absolute; left: 50%; transform: translate(-50%, 0%); + + white-space: nowrap; `; export const Bubble: React.FC = ({ label, position }: Props) => ( -
+
{label}
); diff --git a/packages/web/src/components/molecules/BubbleItem.tsx b/packages/web/src/components/molecules/BubbleItem.tsx new file mode 100644 index 00000000..647c84cd --- /dev/null +++ b/packages/web/src/components/molecules/BubbleItem.tsx @@ -0,0 +1,31 @@ +import React, { useState, type PropsWithChildren } from "react"; +import { center, h, w } from "@biseo/web/styles"; +import { + Bubble, + type Props as BubbleProps, +} from "@biseo/web/components/atoms/Bubble"; + +interface Props extends BubbleProps, PropsWithChildren {} + +export const BubbleItem: React.FC = ({ + label, + position, + children = null, +}) => { + const [hover, setHover] = useState(false); + + return ( +
{ + setHover(true); + }} + onPointerLeave={() => { + setHover(false); + }} + > + {hover && } + {children} +
+ ); +}; diff --git a/packages/web/src/components/molecules/ChatInput.tsx b/packages/web/src/components/molecules/ChatInput.tsx index eacbe615..c28f4c1e 100644 --- a/packages/web/src/components/molecules/ChatInput.tsx +++ b/packages/web/src/components/molecules/ChatInput.tsx @@ -1,8 +1,8 @@ -/* eslint-disable import/no-extraneous-dependencies */ import React, { useCallback, useMemo } from "react"; import { css } from "@emotion/react"; -/* TODO: */ +/* eslint-disable import/no-extraneous-dependencies */ +/* TODO: Solve package installation issue */ import { SendHorizontal, Ghost, Megaphone } from "lucide-react"; import { TextAreaAutosize } from "@biseo/web/components/atoms"; import { useInput } from "@biseo/web/common/hooks"; @@ -22,6 +22,8 @@ import { center, colors, } from "@biseo/web/styles"; +import { useAuth } from "@biseo/web/services/auth"; +import { BubbleItem } from "./BubbleItem"; const inputBoxStyle = css` ${w("fill")} @@ -107,16 +109,23 @@ export const ChatInput: React.FC = ({ send }) => { {/* TODO: Add EmoticonIcon */}
- - + + + + + {useAuth().userInfo?.isAdmin && ( + + + + )}
From 432dafce9c0734bdb87e5b57c825aa9514eb3503 Mon Sep 17 00:00:00 2001 From: messi Date: Mon, 11 Nov 2024 22:28:47 +0900 Subject: [PATCH 03/15] feat: anonymous chatting --- packages/api/src/service/chat.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/api/src/service/chat.ts b/packages/api/src/service/chat.ts index 7ea8e746..0a377a63 100644 --- a/packages/api/src/service/chat.ts +++ b/packages/api/src/service/chat.ts @@ -1,15 +1,21 @@ import type { Prisma, User } from "@prisma/client"; import type * as schema from "@biseo/interface/chat"; import type { Agenda } from "@biseo/interface/agenda"; - import { prisma } from "@biseo/api/db/prisma"; export const createMessage = async ( { message }: schema.Send, - user: User, + user: User | undefined, ): Promise => { + const anonUser: User = { + id: 0, + username: "익명", + displayName: "익명", + isAdmin: false, + }; + const displayAccount = user || anonUser; const sendQuery: Prisma.ChatCreateInput = { - user: { connect: user }, + user: { connect: displayAccount }, type: "message", message, createdAt: new Date(), From de565a888e128c250eb7bf7f124a92a0a708a3d6 Mon Sep 17 00:00:00 2001 From: rjsdn0 Date: Tue, 12 Nov 2024 13:24:26 +0900 Subject: [PATCH 04/15] feat: add lucide-react and react-oauth/google --- packages/web/package.json | 2 ++ pnpm-lock.yaml | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/packages/web/package.json b/packages/web/package.json index efc521f5..7d8cd552 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -25,6 +25,8 @@ "react-select": "^5.7.4", "react-textarea-autosize": "^8.5.2", "socket.io-client": "^4.5.4", + "lucide-react":"^0.456.0", + "@react-oauth/google":"^0.12.1", "zustand": "^4.3.2" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7a3436ae..4c7a3b09 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -144,6 +144,9 @@ importers: '@emotion/styled': specifier: ^11.11.0 version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.0.26)(react@18.2.0) + '@react-oauth/google': + specifier: ^0.12.1 + version: 0.12.1(react-dom@18.2.0)(react@18.2.0) axios: specifier: ^1.4.0 version: 1.4.0 @@ -153,6 +156,9 @@ importers: immer: specifier: ^10.0.2 version: 10.0.2 + lucide-react: + specifier: ^0.456.0 + version: 0.456.0(react@18.2.0) react: specifier: ^18.2.0 version: 18.2.0 @@ -1281,6 +1287,16 @@ packages: requiresBuild: true dev: false + /@react-oauth/google@0.12.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-qagsy22t+7UdkYAiT5ZhfM4StXi9PPNvw0zuwNmabrWyMKddczMtBIOARflbaIj+wHiQjnMAsZmzsUYuXeyoSg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@remix-run/router@1.2.1: resolution: {integrity: sha512-XiY0IsyHR+DXYS5vBxpoBe/8veTeoRpMHP+vDosLZxL5bnpetzI0igkxkLZS235ldLzyfkxF+2divEwWHP3vMQ==} engines: {node: '>=14'} @@ -4393,6 +4409,14 @@ packages: dependencies: yallist: 4.0.0 + /lucide-react@0.456.0(react@18.2.0): + resolution: {integrity: sha512-DIIGJqTT5X05sbAsQ+OhA8OtJYyD4NsEMCA/HQW/Y6ToPQ7gwbtujIoeAaup4HpHzV35SQOarKAWH8LYglB6eA==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + dependencies: + react: 18.2.0 + dev: false + /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true From 8b782ce53b6cce9d5d05e4f296789621d980bdfa Mon Sep 17 00:00:00 2001 From: rjsdn0 Date: Tue, 12 Nov 2024 13:31:10 +0900 Subject: [PATCH 05/15] fix: prettier --- packages/web/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/web/package.json b/packages/web/package.json index 7d8cd552..c9cb4e57 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -25,8 +25,8 @@ "react-select": "^5.7.4", "react-textarea-autosize": "^8.5.2", "socket.io-client": "^4.5.4", - "lucide-react":"^0.456.0", - "@react-oauth/google":"^0.12.1", + "lucide-react": "^0.456.0", + "@react-oauth/google": "^0.12.1", "zustand": "^4.3.2" }, "devDependencies": { @@ -38,4 +38,4 @@ "vite": "^5.0.0", "vite-plugin-svgr": "^4.2.0" } -} +} \ No newline at end of file From 92384edebdfd82168c10a8623a32d8afcf3275a3 Mon Sep 17 00:00:00 2001 From: rjsdn0 Date: Tue, 12 Nov 2024 13:37:25 +0900 Subject: [PATCH 06/15] fix: lint error --- packages/web/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/web/package.json b/packages/web/package.json index c9cb4e57..6fee61d0 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -15,9 +15,11 @@ "dependencies": { "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", + "@react-oauth/google": "^0.12.1", "axios": "^1.4.0", "framer-motion": "^10.16.1", "immer": "^10.0.2", + "lucide-react": "^0.456.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-intersection-observer": "^9.5.2", @@ -25,8 +27,6 @@ "react-select": "^5.7.4", "react-textarea-autosize": "^8.5.2", "socket.io-client": "^4.5.4", - "lucide-react": "^0.456.0", - "@react-oauth/google": "^0.12.1", "zustand": "^4.3.2" }, "devDependencies": { @@ -38,4 +38,4 @@ "vite": "^5.0.0", "vite-plugin-svgr": "^4.2.0" } -} \ No newline at end of file +} From 839b6a347afefa09bfdb194b771075fa4a780f73 Mon Sep 17 00:00:00 2001 From: hye1ee Date: Mon, 25 Nov 2024 22:20:03 +0900 Subject: [PATCH 07/15] style(chat): add admin notice icon --- .../src/components/molecules/ChatHeader.tsx | 50 +++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/packages/web/src/components/molecules/ChatHeader.tsx b/packages/web/src/components/molecules/ChatHeader.tsx index 77a4f3b8..eaadaee4 100644 --- a/packages/web/src/components/molecules/ChatHeader.tsx +++ b/packages/web/src/components/molecules/ChatHeader.tsx @@ -1,24 +1,54 @@ import React from "react"; -import styled from "@emotion/styled"; import { Text } from "@biseo/web/components/atoms"; +import { css } from "@emotion/react"; +import { + align, + bg, + colors, + h, + justify, + padding, + row, + w, +} from "@biseo/web/styles"; +import { Megaphone } from "lucide-react"; interface Props { title: string; } -const Container = styled.div` +const containerStyle = css` + ${row} + ${w("fill")} + ${bg.gray100} + ${padding.horizontal(20)} + ${align.center} + ${justify.between} position: relative; - display: flex; - flex-direction: column; - width: "100%"; min-height: 42px; - background-color: ${props => props.theme.colors.gray100}; - padding: 0 20px; - justify-content: center; +`; + +const iconStyle = css` + ${row} + ${align.center} + ${justify.center} + + ${w(24)} + ${h(24)} + + border-radius: 4px; + cursor: pointer; + + &:hover { + ${bg.gray200} + } `; export const ChatHeader: React.FC = ({ title }) => ( - +
{title} - +
+ +
+
); From 1622b59243c2545c36046849ab5ec21811fc7db1 Mon Sep 17 00:00:00 2001 From: hye1ee Date: Mon, 25 Nov 2024 22:23:25 +0900 Subject: [PATCH 08/15] feat(chat): add message type --- packages/interface/src/chat/client.ts | 3 ++- packages/interface/src/chat/common.ts | 10 +++++++++- packages/web/src/services/chat/hooks.ts | 4 +++- packages/web/src/services/chat/store.ts | 15 ++++++++++----- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/packages/interface/src/chat/client.ts b/packages/interface/src/chat/client.ts index 2e8170fa..6768f4d0 100644 --- a/packages/interface/src/chat/client.ts +++ b/packages/interface/src/chat/client.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-redeclare -- A Zod schema name and type should have the same names */ import { z } from "zod"; -import { Message } from "./common"; +import { Message, MessageType } from "./common"; /** * Send @@ -8,6 +8,7 @@ import { Message } from "./common"; */ export const Send = z.object({ message: z.string().min(1).max(500), + type: MessageType, }); export type Send = z.infer; export const SendCb = Message; diff --git a/packages/interface/src/chat/common.ts b/packages/interface/src/chat/common.ts index 8a922b47..490c57c2 100644 --- a/packages/interface/src/chat/common.ts +++ b/packages/interface/src/chat/common.ts @@ -6,10 +6,18 @@ import { ChatUser } from "@/user"; * Message * some description about message schema goes here */ +export const MessageType = z.enum([ + "message", + "notice", + "anonymous", + "adminnotice", +]); +export type MessageType = z.infer; + export const Message = z.object({ id: z.number(), user: ChatUser, - type: z.enum(["message", "notice"]), + type: MessageType, message: z.string().min(1).max(500), createdAt: z.string().datetime(), }); diff --git a/packages/web/src/services/chat/hooks.ts b/packages/web/src/services/chat/hooks.ts index c6de87be..7759bfd8 100644 --- a/packages/web/src/services/chat/hooks.ts +++ b/packages/web/src/services/chat/hooks.ts @@ -2,6 +2,7 @@ import { useCallback } from "react"; import { useAuth } from "@biseo/web/services/auth"; +import type { MessageType } from "@biseo/interface/chat/common"; import { useChatStore } from "./store"; import { byReverseCreatedTime } from "./common"; @@ -19,7 +20,8 @@ export const useChat = () => { ); const sendMessage = useCallback( - async (message: string) => userInfo && sendWithUser(message, userInfo), + async (message: string, type: MessageType) => + userInfo && sendWithUser(message, type, userInfo), [userInfo, sendWithUser], ); diff --git a/packages/web/src/services/chat/store.ts b/packages/web/src/services/chat/store.ts index 934c8ca6..7848e575 100644 --- a/packages/web/src/services/chat/store.ts +++ b/packages/web/src/services/chat/store.ts @@ -1,7 +1,7 @@ import { create } from "zustand"; import { immer } from "zustand/middleware/immer"; -import type { Message } from "@biseo/interface/chat"; +import type { Message, MessageType } from "@biseo/interface/chat"; import type { ChatUser } from "@biseo/interface/user"; import { socket } from "@biseo/web/socket"; @@ -37,7 +37,7 @@ interface ChatState { * Sends a message to the server and appends to the store * Use optimistic update using draft message */ - send: (message: string, user: ChatUser) => Promise; + send: (message: string, type: MessageType, user: ChatUser) => Promise; } const useChatStore = create( @@ -71,16 +71,21 @@ const useChatStore = create( state.loading = false; }); }, - send: async (message, user) => { + send: async (message, type, user) => { // Creates a local draft message - const draft = createDraftMessage(message, user, get().messages.keys()); + const draft = createDraftMessage( + message, + type, + user, + get().messages.keys(), + ); set(state => { state.messages.set(draft.id, draft); }); try { - const created = await socket.emitAsync("chat.send", { message }); + const created = await socket.emitAsync("chat.send", { message, type }); set(state => { // Removes the draft message and replaces with the created message state.messages.delete(draft.id); From a7a0c8bdcbdb6f5d2e0813f30da43532183793d2 Mon Sep 17 00:00:00 2001 From: hye1ee Date: Wed, 27 Nov 2024 16:22:38 +0900 Subject: [PATCH 09/15] feat(chat): add new type logic --- packages/api/src/service/chat.ts | 26 +++++++++++++------ .../src/components/molecules/ChatInput.tsx | 22 ++++++++++++---- packages/web/src/services/chat/common.ts | 7 ++--- 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/packages/api/src/service/chat.ts b/packages/api/src/service/chat.ts index 7ea8e746..81e091ea 100644 --- a/packages/api/src/service/chat.ts +++ b/packages/api/src/service/chat.ts @@ -5,16 +5,26 @@ import type { Agenda } from "@biseo/interface/agenda"; import { prisma } from "@biseo/api/db/prisma"; export const createMessage = async ( - { message }: schema.Send, - user: User, + { message, type }: schema.Send, + user: User | undefined, ): Promise => { - const sendQuery: Prisma.ChatCreateInput = { - user: { connect: user }, - type: "message", - message, - createdAt: new Date(), - }; + // if anonymous set user undefined + const sendQuery: Prisma.ChatCreateInput = + type === "anonymous" + ? { + user: undefined, + type: "message", + message, + createdAt: new Date(), + } + : { + user: { connect: user }, + type, + message, + createdAt: new Date(), + }; + // TODO-feat/anony const { createdAt, ...createdMessage } = await prisma.chat.create({ data: sendQuery, select: { diff --git a/packages/web/src/components/molecules/ChatInput.tsx b/packages/web/src/components/molecules/ChatInput.tsx index c28f4c1e..ee5d514e 100644 --- a/packages/web/src/components/molecules/ChatInput.tsx +++ b/packages/web/src/components/molecules/ChatInput.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo } from "react"; +import React, { useCallback, useMemo, useState } from "react"; import { css } from "@emotion/react"; /* eslint-disable import/no-extraneous-dependencies */ @@ -23,6 +23,7 @@ import { colors, } from "@biseo/web/styles"; import { useAuth } from "@biseo/web/services/auth"; +import type { MessageType } from "@biseo/interface/chat/common"; import { BubbleItem } from "./BubbleItem"; const inputBoxStyle = css` @@ -54,11 +55,18 @@ const textAreaScrollStyle = css` `; interface Props { - send: (message: string) => void; + send: (message: string, type: MessageType) => void; } export const ChatInput: React.FC = ({ send }) => { const { input, setValue } = useInput(); + const [type, setType] = useState("message"); + + const onTypeChange = (newType: MessageType) => + setType(prev => { + if (prev === newType) return "message"; + return newType; + }); const validated = useMemo(() => input.value.trim().length > 0, [input.value]); @@ -67,7 +75,7 @@ export const ChatInput: React.FC = ({ send }) => { const sendCurrent = useCallback(() => { if (!validated) return; - send(input.value.trim()); + send(input.value.trim(), type); setValue(""); }, [input.value, validated]); @@ -112,8 +120,9 @@ export const ChatInput: React.FC = ({ send }) => { onTypeChange("anonymous")} /> @@ -121,8 +130,11 @@ export const ChatInput: React.FC = ({ send }) => { onTypeChange("adminnotice")} /> )} diff --git a/packages/web/src/services/chat/common.ts b/packages/web/src/services/chat/common.ts index c6304b11..d3ad077c 100644 --- a/packages/web/src/services/chat/common.ts +++ b/packages/web/src/services/chat/common.ts @@ -1,15 +1,16 @@ -import type { ChatUser } from "@biseo/interface/user"; -import type { Message } from "@biseo/interface/chat"; +import type { Message, MessageType } from "@biseo/interface/chat"; +import type { ChatUser } from "@biseo/interface/user/common"; export const RETRIEVE_CHAT_OFFSET = 20; export const createDraftMessage = ( message: string, + type: MessageType, user: ChatUser, preExistingKeys: IterableIterator, ): Message => ({ id: Math.max(...preExistingKeys, 0) + 1, - type: "message", + type, user, message, createdAt: new Date().toISOString(), From 25b2ccac832305a625d140febe7305ab67104091 Mon Sep 17 00:00:00 2001 From: messi Date: Thu, 28 Nov 2024 02:00:00 +0900 Subject: [PATCH 10/15] feat: anonymous chatting BE --- .../migration.sql | 2 + packages/api/prisma/schema.prisma | 33 ++++++++++------- packages/api/src/service/chat.ts | 37 ++++++++++++++----- 3 files changed, 49 insertions(+), 23 deletions(-) create mode 100644 packages/api/prisma/migrations/20241127153936_create_categories/migration.sql diff --git a/packages/api/prisma/migrations/20241127153936_create_categories/migration.sql b/packages/api/prisma/migrations/20241127153936_create_categories/migration.sql new file mode 100644 index 00000000..e27dcf97 --- /dev/null +++ b/packages/api/prisma/migrations/20241127153936_create_categories/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE `chat` ADD COLUMN `is_anon` BOOLEAN NOT NULL DEFAULT false; diff --git a/packages/api/prisma/schema.prisma b/packages/api/prisma/schema.prisma index df9cec09..ec86d156 100644 --- a/packages/api/prisma/schema.prisma +++ b/packages/api/prisma/schema.prisma @@ -11,13 +11,13 @@ datasource db { model Agenda { id Int @id @default(autoincrement()) title String - resolution String content String startAt DateTime? @map("start_at") endAt DateTime? @map("end_at") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") deletedAt DateTime? @map("deleted_at") + resolution String choices Choice[] voters UserAgendaVotable[] @@ -26,12 +26,12 @@ model Agenda { model User { id Int @id @default(autoincrement()) - username String @unique - displayName String isAdmin Boolean @default(false) @map("is_admin") + displayName String + username String @unique chats Chat[] - choices UserChoice[] agendas UserAgendaVotable[] + choices UserChoice[] tags UserTag[] @@map("user") @@ -39,22 +39,25 @@ model User { model Choice { id Int @id @default(autoincrement()) - agenda Agenda @relation(fields: [agendaId], references: [id]) agendaId Int @map("agenda_id") name String + agenda Agenda @relation(fields: [agendaId], references: [id]) users UserChoice[] + @@index([agendaId], map: "choice_agenda_id_fkey") @@map("choice") } model Chat { id Int @id @default(autoincrement()) - user User @relation(fields: [userId], references: [id]) userId Int @map("user_id") type ChatType message String @db.VarChar(500) createdAt DateTime @default(now()) @map("created_at") + user User @relation(fields: [userId], references: [id]) + isAnon Boolean @default(false) @map("is_anon") + @@index([userId], map: "chat_user_id_fkey") @@map("chat") } @@ -69,10 +72,11 @@ model Tag { model TemplateChoice { id Int @id @default(autoincrement()) - template Template @relation(fields: [templateId], references: [id]) templateId Int @map("template_id") name String + template Template @relation(fields: [templateId], references: [id]) + @@index([templateId], map: "template_choice_template_id_fkey") @@map("template_choice") } @@ -88,32 +92,35 @@ model Template { } model UserChoice { - user User @relation(fields: [userId], references: [id]) userId Int @map("user_id") - choice Choice @relation(fields: [choiceId], references: [id]) choiceId Int @map("choice_id") + choice Choice @relation(fields: [choiceId], references: [id]) + user User @relation(fields: [userId], references: [id]) @@id([userId, choiceId]) + @@index([choiceId], map: "user_choice_choice_id_fkey") @@map("user_choice") } model UserAgendaVotable { - user User @relation(fields: [userId], references: [id]) userId Int @map("user_id") - agenda Agenda @relation(fields: [agendaId], references: [id]) agendaId Int @map("agenda_id") + agenda Agenda @relation(fields: [agendaId], references: [id]) + user User @relation(fields: [userId], references: [id]) @@id([userId, agendaId]) + @@index([agendaId], map: "user_agenda_votable_agenda_id_fkey") @@map("user_agenda_votable") } model UserTag { - user User @relation(fields: [userId], references: [id]) userId Int @map("user_id") - tag Tag @relation(fields: [tagId], references: [id]) tagId Int @map("tag_id") + tag Tag @relation(fields: [tagId], references: [id]) + user User @relation(fields: [userId], references: [id]) @@id([userId, tagId]) + @@index([tagId], map: "user_tag_tag_id_fkey") @@map("user_tag") } diff --git a/packages/api/src/service/chat.ts b/packages/api/src/service/chat.ts index e857a1ee..d38b83c9 100644 --- a/packages/api/src/service/chat.ts +++ b/packages/api/src/service/chat.ts @@ -5,25 +5,25 @@ import { prisma } from "@biseo/api/db/prisma"; export const createMessage = async ( { message, type }: schema.Send, - user: User | undefined, + user: User, ): Promise => { + const isAnon = type === "anonymous"; const anonUser: User = { id: 0, - username: "익명", - displayName: "익명", isAdmin: false, + displayName: "익명", + username: "익명", }; - const displayAccount = type === "anonymous" ? anonUser : user; const sendQuery: Prisma.ChatCreateInput = { - user: { connect: displayAccount }, + user: { connect: user }, type: "message", message, + isAnon, createdAt: new Date(), }; console.log("query", sendQuery); - // TODO-feat/anony const { createdAt, ...createdMessage } = await prisma.chat.create({ data: sendQuery, select: { @@ -37,9 +37,14 @@ export const createMessage = async ( type: true, message: true, createdAt: true, + isAnon: true, }, }); + if (isAnon) { + createdMessage.user = anonUser; + } + return { ...createdMessage, createdAt: createdAt.toISOString(), @@ -98,11 +103,23 @@ export const retrieve = async ({ type: true, message: true, createdAt: true, + isAnon: true, }, }); - return messages.map(({ createdAt, ...message }) => ({ - ...message, - createdAt: createdAt.toISOString(), - })); + const anonUser: User = { + id: 0, + isAdmin: false, + displayName: "익명", + username: "익명", + }; + + return messages.map(({ createdAt, isAnon, ...message }) => { + const displayMessage = message; + if (isAnon) displayMessage.user = anonUser; + return { + ...displayMessage, + createdAt: createdAt.toISOString(), + }; + }); }; From 4e363b9a7646f3f7aeb459cc3c074d83ab41364d Mon Sep 17 00:00:00 2001 From: hye1ee Date: Fri, 29 Nov 2024 01:33:52 +0900 Subject: [PATCH 11/15] feat: update draft message --- packages/api/src/service/chat.ts | 2 -- packages/web/src/components/molecules/ChatInput.tsx | 4 ++-- packages/web/src/components/molecules/Message/index.tsx | 6 +++--- packages/web/src/services/chat/common.ts | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/api/src/service/chat.ts b/packages/api/src/service/chat.ts index d38b83c9..94491ac1 100644 --- a/packages/api/src/service/chat.ts +++ b/packages/api/src/service/chat.ts @@ -22,8 +22,6 @@ export const createMessage = async ( createdAt: new Date(), }; - console.log("query", sendQuery); - const { createdAt, ...createdMessage } = await prisma.chat.create({ data: sendQuery, select: { diff --git a/packages/web/src/components/molecules/ChatInput.tsx b/packages/web/src/components/molecules/ChatInput.tsx index ee5d514e..f93bae96 100644 --- a/packages/web/src/components/molecules/ChatInput.tsx +++ b/packages/web/src/components/molecules/ChatInput.tsx @@ -77,7 +77,7 @@ export const ChatInput: React.FC = ({ send }) => { if (!validated) return; send(input.value.trim(), type); setValue(""); - }, [input.value, validated]); + }, [input.value, validated, type]); const sendIconStyle = css` ${center} @@ -134,7 +134,7 @@ export const ChatInput: React.FC = ({ send }) => { type === "adminnotice" ? colors.blue600 : colors.gray300 } style={{ cursor: "pointer" }} - onClick={() => onTypeChange("adminnotice")} + // onClick={() => onTypeChange("adminnotice")} /> )} diff --git a/packages/web/src/components/molecules/Message/index.tsx b/packages/web/src/components/molecules/Message/index.tsx index c3ffbffd..1222f619 100644 --- a/packages/web/src/components/molecules/Message/index.tsx +++ b/packages/web/src/components/molecules/Message/index.tsx @@ -10,10 +10,10 @@ interface Props { } const Component: React.FC = ({ message }) => - message.type === "message" ? ( - - ) : ( + message.type === "notice" ? ( + ) : ( + ); export const Message = Object.assign(Component, { List }); diff --git a/packages/web/src/services/chat/common.ts b/packages/web/src/services/chat/common.ts index d3ad077c..34ca0e10 100644 --- a/packages/web/src/services/chat/common.ts +++ b/packages/web/src/services/chat/common.ts @@ -11,7 +11,7 @@ export const createDraftMessage = ( ): Message => ({ id: Math.max(...preExistingKeys, 0) + 1, type, - user, + user: type === "anonymous" ? { id: 0, displayName: "익명" } : user, message, createdAt: new Date().toISOString(), }); From 5d5769e421e760a86dd76444dcfda14d515573dc Mon Sep 17 00:00:00 2001 From: hye1ee Date: Fri, 29 Nov 2024 01:53:44 +0900 Subject: [PATCH 12/15] style: style chat messages --- packages/web/src/components/atoms/Scroll.tsx | 2 +- .../molecules/Message/PlainMessage.tsx | 6 +++--- .../src/components/organisms/ChatSection.tsx | 19 +++++++++++++++---- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/web/src/components/atoms/Scroll.tsx b/packages/web/src/components/atoms/Scroll.tsx index 7bf5b6ad..d50d970a 100644 --- a/packages/web/src/components/atoms/Scroll.tsx +++ b/packages/web/src/components/atoms/Scroll.tsx @@ -24,7 +24,7 @@ export const Scroll = styled.div<{ hide?: boolean }>` ${props => !props.hide && css` - background-color: ${props.theme.colors.gray400}; + background-color: ${props.theme.colors.gray300}; `} border-radius: 100px; } diff --git a/packages/web/src/components/molecules/Message/PlainMessage.tsx b/packages/web/src/components/molecules/Message/PlainMessage.tsx index 92eb39ac..94b18c23 100644 --- a/packages/web/src/components/molecules/Message/PlainMessage.tsx +++ b/packages/web/src/components/molecules/Message/PlainMessage.tsx @@ -44,11 +44,11 @@ const parseURL: React.FC = (content: string) => { export const PlainMessage: React.FC = ({ message }) => ( - - + + {message.user.displayName} - + {formatTime(message.createdAt)} diff --git a/packages/web/src/components/organisms/ChatSection.tsx b/packages/web/src/components/organisms/ChatSection.tsx index 74980bd9..5fbd025f 100644 --- a/packages/web/src/components/organisms/ChatSection.tsx +++ b/packages/web/src/components/organisms/ChatSection.tsx @@ -8,8 +8,9 @@ import { ChatInput, Message, } from "@biseo/web/components/molecules"; -import { margin, round, padding, center, text } from "@biseo/web/styles"; +import { round, padding, center, text, bg } from "@biseo/web/styles"; import { useChat } from "@biseo/web/services"; +import { css } from "@emotion/react"; const Container = styled.div` display: flex; @@ -20,6 +21,16 @@ const Container = styled.div` overflow: hidden; `; +const dateDividerStyle = css` + ${bg.gray100} + ${round.lg} + ${padding.vertical(4)} + ${padding.horizontal(10)} + + ${text.option1} + white-space: nowrap; +`; + export const ChatSection: React.FC = () => { const { messages, loading, hasMore, sendMessage, loadMore } = useChat(); const { ref, inView } = useInView(); @@ -45,11 +56,11 @@ export const ChatSection: React.FC = () => {
- - + + {formatDate(message.createdAt)} - +
)} From e9bf14a68165c99c1f7670a76e3e13034e9b162a Mon Sep 17 00:00:00 2001 From: hye1ee Date: Fri, 29 Nov 2024 02:12:32 +0900 Subject: [PATCH 13/15] style: style profile hover modal --- .../web/src/components/molecules/Profile.tsx | 59 ++++++++++--------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/packages/web/src/components/molecules/Profile.tsx b/packages/web/src/components/molecules/Profile.tsx index b2b95e91..1edf7e71 100644 --- a/packages/web/src/components/molecules/Profile.tsx +++ b/packages/web/src/components/molecules/Profile.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Button, Divider } from "@biseo/web/components/atoms"; +import { Divider } from "@biseo/web/components/atoms"; import { text, align, @@ -12,50 +12,53 @@ import { gap, padding, justify, + row, + colors, } from "@biseo/web/styles"; +import { LogOut } from "lucide-react"; +import { css } from "@emotion/react"; type Props = { displayName: string; onLogout: () => void; }; +const buttonStyle = css` + ${w("fill")} + ${h(32)} + ${row} + ${align.center} + ${justify.start} + ${padding.horizontal(16)} + ${gap(8)} + + background-color: transparent; + border: none; + cursor: pointer; + + &:hover { + ${bg.gray100} + } +`; + export const Profile: React.FC = ({ displayName, onLogout }) => (
-
-
+
+
{displayName}
- - + +
); From b2d899bdbeb967d434c5be4b76d57cd5d74a1ee0 Mon Sep 17 00:00:00 2001 From: messi Date: Mon, 2 Dec 2024 16:49:39 +0900 Subject: [PATCH 14/15] fix: anonType management in DB and BE --- .../migration.sql | 9 +++++ packages/api/prisma/schema.prisma | 4 +-- packages/api/src/service/chat.ts | 33 +++++++------------ 3 files changed, 22 insertions(+), 24 deletions(-) create mode 100644 packages/api/prisma/migrations/20241202073949_alter_anon_flags/migration.sql diff --git a/packages/api/prisma/migrations/20241202073949_alter_anon_flags/migration.sql b/packages/api/prisma/migrations/20241202073949_alter_anon_flags/migration.sql new file mode 100644 index 00000000..c0721e15 --- /dev/null +++ b/packages/api/prisma/migrations/20241202073949_alter_anon_flags/migration.sql @@ -0,0 +1,9 @@ +/* + Warnings: + + - You are about to drop the column `is_anon` on the `chat` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE `chat` DROP COLUMN `is_anon`, + MODIFY `type` ENUM('message', 'notice', 'anonymous', 'adminnotice') NOT NULL; diff --git a/packages/api/prisma/schema.prisma b/packages/api/prisma/schema.prisma index ec86d156..ae11108a 100644 --- a/packages/api/prisma/schema.prisma +++ b/packages/api/prisma/schema.prisma @@ -55,7 +55,6 @@ model Chat { message String @db.VarChar(500) createdAt DateTime @default(now()) @map("created_at") user User @relation(fields: [userId], references: [id]) - isAnon Boolean @default(false) @map("is_anon") @@index([userId], map: "chat_user_id_fkey") @@map("chat") @@ -127,6 +126,7 @@ model UserTag { enum ChatType { message notice - + anonymous + adminnotice @@map("chat_type") } diff --git a/packages/api/src/service/chat.ts b/packages/api/src/service/chat.ts index 94491ac1..d29e2ec3 100644 --- a/packages/api/src/service/chat.ts +++ b/packages/api/src/service/chat.ts @@ -7,18 +7,10 @@ export const createMessage = async ( { message, type }: schema.Send, user: User, ): Promise => { - const isAnon = type === "anonymous"; - const anonUser: User = { - id: 0, - isAdmin: false, - displayName: "익명", - username: "익명", - }; const sendQuery: Prisma.ChatCreateInput = { user: { connect: user }, - type: "message", + type, message, - isAnon, createdAt: new Date(), }; @@ -35,14 +27,16 @@ export const createMessage = async ( type: true, message: true, createdAt: true, - isAnon: true, }, }); - if (isAnon) { - createdMessage.user = anonUser; + if (type === "anonymous") { + createdMessage.user.id = 0; + createdMessage.user.displayName = "익명"; } + console.log(createdMessage); + return { ...createdMessage, createdAt: createdAt.toISOString(), @@ -101,20 +95,15 @@ export const retrieve = async ({ type: true, message: true, createdAt: true, - isAnon: true, }, }); - const anonUser: User = { - id: 0, - isAdmin: false, - displayName: "익명", - username: "익명", - }; - - return messages.map(({ createdAt, isAnon, ...message }) => { + return messages.map(({ createdAt, ...message }) => { const displayMessage = message; - if (isAnon) displayMessage.user = anonUser; + if (message.type === "anonymous") { + displayMessage.user.id = 0; + displayMessage.user.displayName = "익명"; + } return { ...displayMessage, createdAt: createdAt.toISOString(), From 12287983486fbe3ed6dfee8ad4161a958b694f27 Mon Sep 17 00:00:00 2001 From: messi Date: Mon, 2 Dec 2024 18:28:26 +0900 Subject: [PATCH 15/15] fix: sending button: --- packages/web/src/components/molecules/ChatInput.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/web/src/components/molecules/ChatInput.tsx b/packages/web/src/components/molecules/ChatInput.tsx index f93bae96..3cbe3929 100644 --- a/packages/web/src/components/molecules/ChatInput.tsx +++ b/packages/web/src/components/molecules/ChatInput.tsx @@ -140,7 +140,11 @@ export const ChatInput: React.FC = ({ send }) => { )}
- +