Skip to content

Commit

Permalink
Merge pull request #101 from PoolC/feat/99
Browse files Browse the repository at this point in the history
message UI 및 보내기 기능
  • Loading branch information
jinoov committed Jul 8, 2024
2 parents c3a9b67 + 4a6b941 commit 060396e
Show file tree
Hide file tree
Showing 14 changed files with 369 additions and 160 deletions.
2 changes: 1 addition & 1 deletion apps/web-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"sync:type": "yarn dlx typesync",
"check:type": "tsc",
"check:type:watch": "yarn check:type --watch",
"codegen": "openapi -i https://dev.poolc.org/api/v2/api-docs -o src/lib/api-v2/__generated__ -c fetch --useUnionTypes --useOptions",
"codegen": "openapi -i https://dev.poolc.org/api/v2/api-docs -o src/lib/api-v2/__generated__ --useUnionTypes --useOptions -c axios",
"postinstall": "yarn codegen",
"lint": "eslint . && prettier . --check --ignore-path .gitignore",
"format": "eslint . --fix && prettier . --write --ignore-path .gitignore"
Expand Down
6 changes: 3 additions & 3 deletions apps/web-client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ function App() {
<Route component={BoardListPage} path={`/${MENU.BOARD}`} exact />
<Route component={BoardWritePage} path={`/${MENU.BOARD}/write`} />
<Route component={BoardDetailPage} path={`/${MENU.BOARD}/:id`} />
<Route component={MessageAllListPage} path={`/${MENU.MESSAGE_ALL_LIST}`} />
<Route component={MessageListPage} path={`/${MENU.MESSAGE_LIST}`} />
<Route component={MessageFormPage} path={`/${MENU.MESSAGE_FORM}`} />
<Route component={MessageAllListPage} path={`/${MENU.MESSAGE}`} exact />
<Route component={MessageListPage} path={`/${MENU.MESSAGE}/:conversationId`} exact />
<Route component={MessageFormPage} path={`/${MENU.MESSAGE}/:conversationId/${MENU.MESSAGE_FORM}`} />
<Route component={MyPage} path={`/${MENU.MY_PAGE}`} exact />
<Route component={MyPageBadgeListPage} path={`/${MENU.MY_PAGE}/${MENU.MY_PAGE_BADGE_LIST}`} />
<Route component={MyPageMyPostsPage} path={`/${MENU.MY_PAGE}/${MENU.MY_PAGE_MY_POSTS}`} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const TextContainer = styled.div`
export const NameContainer = styled.div`
display: flex;
align-items: center;
gap: 8px;
@media (max-width: 576px) {
justify-content: center;
}
Expand All @@ -50,7 +51,6 @@ export const Name = styled.p`

export const Status = styled.span`
font-weight: 300;
margin-left: 5px;
`;

export const DepartmentContainer = styled.div`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Icon from '@ant-design/icons';
import { Avatar } from 'antd';
import Icon, { MessageOutlined } from '@ant-design/icons';
import { Avatar, Button, Tooltip } from 'antd';
import { useHistory } from 'react-router';
import ActivityCard from '~/components/activity/ActivityCard/ActivityCard';
import ProjectCard from '~/components/projects/ProjectCard/ProjectCard';
import getFileUrl from '~/lib/utils/getFileUrl';
Expand All @@ -19,16 +20,45 @@ import {
ActivityContainer,
Activities,
} from './MemberDetailContent.styles';
import { MemberControllerService, MemberResponse, queryKey, useAppSuspenseQuery } from '~/lib/api-v2';
import { ConversationControllerService, MemberControllerService, MemberResponse, queryKey, useAppMutation, useAppSuspeneseQueries } from '~/lib/api-v2';
import { MENU } from '~/constants/menus';

export default function MemberDetailContent({ loginId }: { loginId: string }) {
const { data: _member } = useAppSuspenseQuery({
queryKey: queryKey.member.id(loginId),
queryFn: () => MemberControllerService.getMemberWithProjectAndActivityUsingGet({ loginId }),
const history = useHistory();
const [{ data: _member }, { data: me }] = useAppSuspeneseQueries({
queries: [
{
queryKey: queryKey.member.id(loginId),
queryFn: () => MemberControllerService.getMemberWithProjectAndActivityUsingGet({ loginId }),
},
{
queryKey: queryKey.member.me,
queryFn: MemberControllerService.getMeUsingGet,
},
],
});

const member = _member as unknown as Required<MemberResponse>;

const { mutate } = useAppMutation({
mutationFn: () =>
ConversationControllerService.createConversationUsingPost({
request: {
otherLoginID: member.loginID,
},
}),
});

const onMessageButtonClick = () => {
if (confirm(`${member.name}님과의 대화를 시작할까요?`)) {
mutate(undefined, {
onSuccess: (conversationId) => {
history.push(`/${MENU.MESSAGE}/${conversationId}/${MENU.MESSAGE_FORM}`);
},
});
}
};

return (
<>
<ContentContainer>
Expand All @@ -40,6 +70,11 @@ export default function MemberDetailContent({ loginId }: { loginId: string }) {
<Name>{member.name}</Name>
{member.isAdmin && <Status>PoolC임원</Status>}
{member.badge && <Avatar src={getFileUrl(member.badge.imageUrl)} size={60} />}
{member.loginID !== me.loginID && (
<Tooltip title={`${member.name}님과 대화를 해보아요`}>
<Button shape="circle" icon={<MessageOutlined />} type="primary" onClick={onMessageButtonClick} />
</Tooltip>
)}
</NameContainer>
<DepartmentContainer>
{member.department && (
Expand Down
81 changes: 81 additions & 0 deletions apps/web-client/src/components/message/MessageAllListContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Button, List, Space, Typography } from 'antd';
import { ArrowLeftOutlined } from '@ant-design/icons';
import { useHistory } from 'react-router-dom';
import { createStyles } from 'antd-style';

const useStyles = createStyles(({ css }) => ({
wrapper: css`
width: 100%;
max-width: 1200px;
box-sizing: border-box;
`,
fullWidth: css`
width: 100%;
`,
metaInfo: css`
justify-content: space-between;
width: 100%;
`,
messageType: css`
font-weight: 700;
`,
topBox: css`
justify-content: space-between;
align-items: center;
width: 100%;
`,
topBoxName: css`
margin-bottom: 0;
font-weight: 700;
font-size: 20px;
`,
}));

export default function MessageAllListContent() {
const { styles } = useStyles();
const history = useHistory();

// TODO: API 호출로 대체
const data = [
{
title: 'Ant Design Title 1',
},
{
title: 'Ant Design Title 2',
},
{
title: 'Ant Design Title 3',
},
{
title: 'Ant Design Title 4',
},
];

return (
<Space direction="vertical" className={styles.fullWidth} size="large">
<Space className={styles.topBox}>
<Space>
<Button shape="circle" type="text" onClick={() => history.goBack()}>
<ArrowLeftOutlined />
</Button>
<Typography.Text className={styles.topBoxName}>대화목록</Typography.Text>
</Space>
</Space>
<List
itemLayout="horizontal"
dataSource={data}
renderItem={() => (
<List.Item>
<Space direction="vertical" className={styles.fullWidth}>
<Space className={styles.metaInfo}>
<Typography.Text className={styles.messageType}>받은 쪽지</Typography.Text>
<Typography.Text>2023.08.15 19:20:50</Typography.Text>
</Space>
<Typography.Text>내용내용내용</Typography.Text>
</Space>
</List.Item>
)}
/>
</Space>
);
}
25 changes: 25 additions & 0 deletions apps/web-client/src/components/message/MessageAllListView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { createStyles } from 'antd-style';
import { Suspense } from 'react';
import { Block, WhiteBlock } from '~/styles/common/Block.styles';
import MessageAllListContent from './MessageAllListContent';
import Spinner from '../common/Spinner/Spinner';

const useStyles = createStyles(({ css }) => ({
whiteBlock: css`
padding: 30px 20px;
`,
}));

export default function MessageAllListView() {
const { styles } = useStyles();

return (
<Block>
<WhiteBlock className={styles.whiteBlock}>
<Suspense fallback={<Spinner />}>
<MessageAllListContent />
</Suspense>
</WhiteBlock>
</Block>
);
}
86 changes: 86 additions & 0 deletions apps/web-client/src/components/message/MessageFormView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { ArrowLeftOutlined } from '@ant-design/icons';
import { Button, Input, Space, Typography } from 'antd';
import { createStyles } from 'antd-style';
import { FormEventHandler, useState } from 'react';
import { useHistory, useParams } from 'react-router-dom';
import { MENU } from '~/constants/menus';
import { MessageControllerService, useAppMutation } from '~/lib/api-v2';
import { Block, WhiteBlock } from '~/styles/common/Block.styles';

const useStyles = createStyles(({ css }) => ({
whiteBlock: css`
padding: 30px 20px;
`,
wrapper: css`
width: 100%;
max-width: 1200px;
box-sizing: border-box;
`,
fullWidth: css`
width: 100%;
`,
topBox: css`
justify-content: space-between;
align-items: center;
width: 100%;
`,
topBoxName: css`
margin-bottom: 0;
font-weight: 700;
font-size: 20px;
`,
textarea: css`
min-height: 300px !important;
resize: none;
`,
}));

export default function MessageFormView() {
const { styles } = useStyles();
// TODO: react-hook-form 기반으로 변경
const [content, setContent] = useState('');
const history = useHistory();
const { conversationId } = useParams<{ conversationId: string }>();

const { mutate: sendMessage } = useAppMutation({
mutationFn: () =>
MessageControllerService.sendMessageUsingPost({
request: {
content,
conversationId,
},
}),
});

const onFormSubmit: FormEventHandler = (e) => {
e.preventDefault();
sendMessage(undefined, {
onSuccess: () => {
history.push(`/${MENU.MESSAGE}/${conversationId}`);
},
});
};

return (
<Block>
<WhiteBlock className={styles.whiteBlock}>
<form onSubmit={onFormSubmit} className={styles.fullWidth}>
<Space direction="vertical" className={styles.fullWidth} size="large">
<Space className={styles.topBox}>
<Space>
<Button shape="circle" type="text" onClick={() => history.goBack()}>
<ArrowLeftOutlined />
</Button>
<Typography.Text className={styles.topBoxName}>쪽지보내기</Typography.Text>
</Space>
</Space>
<Input.TextArea className={styles.textarea} value={content} onChange={(e) => setContent(e.target.value)} />
<Button type="primary" block htmlType="submit">
보내기
</Button>
</Space>
</form>
</WhiteBlock>
</Block>
);
}
92 changes: 92 additions & 0 deletions apps/web-client/src/components/message/MessageListContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Button, List, Space, Typography } from 'antd';
import { createStyles } from 'antd-style';
import { ArrowLeftOutlined } from '@ant-design/icons';
import { Link, useHistory } from 'react-router-dom';
import { Block, WhiteBlock } from '~/styles/common/Block.styles';
import { MENU } from '~/constants/menus';

const useStyles = createStyles(({ css }) => ({
whiteBlock: css`
padding: 30px 20px;
`,
wrapper: css`
width: 100%;
max-width: 1200px;
box-sizing: border-box;
`,
fullWidth: css`
width: 100%;
`,
metaInfo: css`
justify-content: space-between;
width: 100%;
`,
messageType: css`
font-weight: 700;
`,
topBox: css`
justify-content: space-between;
align-items: center;
width: 100%;
`,
topBoxName: css`
margin-bottom: 0;
font-weight: 700;
font-size: 20px;
`,
}));

export default function MessageListContent() {
const { styles } = useStyles();
const history = useHistory();

const data = [
{
title: 'Ant Design Title 1',
},
{
title: 'Ant Design Title 2',
},
{
title: 'Ant Design Title 3',
},
{
title: 'Ant Design Title 4',
},
];

return (
<Block>
<WhiteBlock className={styles.whiteBlock}>
<Space direction="vertical" className={styles.fullWidth} size="large">
<Space className={styles.topBox}>
<Space>
<Button shape="circle" type="text" onClick={() => history.goBack()}>
<ArrowLeftOutlined />
</Button>
<Typography.Text className={styles.topBoxName}>익명</Typography.Text>
</Space>
<Link to={`/${MENU.MY_PAGE}/${MENU.MESSAGE_FORM}`}>
<Button>쪽지 보내기</Button>
</Link>
</Space>
<List
itemLayout="horizontal"
dataSource={data}
renderItem={() => (
<List.Item>
<Space direction="vertical" className={styles.fullWidth}>
<Space className={styles.metaInfo}>
<Typography.Text className={styles.messageType}>받은 쪽지</Typography.Text>
<Typography.Text>2023.08.15 19:20:50</Typography.Text>
</Space>
<Typography.Text>내용내용내용</Typography.Text>
</Space>
</List.Item>
)}
/>
</Space>
</WhiteBlock>
</Block>
);
}
Loading

0 comments on commit 060396e

Please sign in to comment.