Skip to content

Commit

Permalink
Merge pull request #520 from sparcs-kaist/feat/anony
Browse files Browse the repository at this point in the history
Feat/anony
  • Loading branch information
rjsdn0 authored Dec 2, 2024
2 parents 895d24d + 46b3381 commit 893fbe7
Show file tree
Hide file tree
Showing 20 changed files with 318 additions and 92 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `chat` ADD COLUMN `is_anon` BOOLEAN NOT NULL DEFAULT false;
Original file line number Diff line number Diff line change
@@ -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;
35 changes: 21 additions & 14 deletions packages/api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -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[]
Expand All @@ -26,35 +26,37 @@ 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")
}

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])
@@index([userId], map: "chat_user_id_fkey")
@@map("chat")
}

Expand All @@ -69,10 +71,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")
}

Expand All @@ -88,38 +91,42 @@ 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")
}

enum ChatType {
message
notice
anonymous
adminnotice
@@map("chat_type")
}
27 changes: 20 additions & 7 deletions packages/api/src/service/chat.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
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,
{ message, type }: schema.Send,
user: User,
): Promise<schema.Message> => {
const sendQuery: Prisma.ChatCreateInput = {
user: { connect: user },
type: "message",
type,
message,
createdAt: new Date(),
};
Expand All @@ -31,6 +30,13 @@ export const createMessage = async (
},
});

if (type === "anonymous") {
createdMessage.user.id = 0;
createdMessage.user.displayName = "익명";
}

console.log(createdMessage);

Check warning on line 38 in packages/api/src/service/chat.ts

View workflow job for this annotation

GitHub Actions / Lint and Format (18.x, 8.x)

Unexpected console statement

return {
...createdMessage,
createdAt: createdAt.toISOString(),
Expand Down Expand Up @@ -92,8 +98,15 @@ export const retrieve = async ({
},
});

return messages.map(({ createdAt, ...message }) => ({
...message,
createdAt: createdAt.toISOString(),
}));
return messages.map(({ createdAt, ...message }) => {
const displayMessage = message;
if (message.type === "anonymous") {
displayMessage.user.id = 0;
displayMessage.user.displayName = "익명";
}
return {
...displayMessage,
createdAt: createdAt.toISOString(),
};
});
};
3 changes: 2 additions & 1 deletion packages/interface/src/chat/client.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
/* 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
* description
*/
export const Send = z.object({
message: z.string().min(1).max(500),
type: MessageType,
});
export type Send = z.infer<typeof Send>;
export const SendCb = Message;
Expand Down
10 changes: 9 additions & 1 deletion packages/interface/src/chat/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof MessageType>;

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(),
});
Expand Down
1 change: 1 addition & 0 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,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",
Expand Down
32 changes: 32 additions & 0 deletions packages/web/src/components/atoms/Bubble.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { bg, center, h, padding, round, text, w } from "@biseo/web/styles";
import { css } from "@emotion/react";

export 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%);
white-space: nowrap;
`;

export const Bubble: React.FC<Props> = ({ label, position }: Props) => (
<div css={[BubbleStyle, position === "top" ? "bottom: 26px" : "top :26px"]}>
{label}
</div>
);
2 changes: 1 addition & 1 deletion packages/web/src/components/atoms/Scroll.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
31 changes: 31 additions & 0 deletions packages/web/src/components/molecules/BubbleItem.tsx
Original file line number Diff line number Diff line change
@@ -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<Props> = ({
label,
position,
children = null,
}) => {
const [hover, setHover] = useState<boolean>(false);

return (
<div
css={[center, w("hug"), h("hug"), "position: relative"]}
onPointerEnter={() => {
setHover(true);
}}
onPointerLeave={() => {
setHover(false);
}}
>
{hover && <Bubble label={label} position={position} />}
{children}
</div>
);
};
50 changes: 40 additions & 10 deletions packages/web/src/components/molecules/ChatHeader.tsx
Original file line number Diff line number Diff line change
@@ -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<Props> = ({ title }) => (
<Container>
<div css={containerStyle}>
<Text variant="title2">{title}</Text>
</Container>
<div css={iconStyle}>
<Megaphone size={20} color={colors.gray400} />
</div>
</div>
);
Loading

0 comments on commit 893fbe7

Please sign in to comment.