diff --git a/.gitignore b/.gitignore index 8005e0c2..55ed55ea 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ yarn-error.log* node_modules +# chrome extension zip files +baekjoon-hub.zip diff --git a/.yarn/cache/@esbuild-win32-arm64-npm-0.21.5-d0ef444aab-8.zip b/.yarn/cache/@esbuild-win32-arm64-npm-0.21.5-d0ef444aab-8.zip new file mode 100644 index 00000000..d624773b Binary files /dev/null and b/.yarn/cache/@esbuild-win32-arm64-npm-0.21.5-d0ef444aab-8.zip differ diff --git a/.yarn/cache/@esbuild-win32-ia32-npm-0.21.5-a4fb03dad4-8.zip b/.yarn/cache/@esbuild-win32-ia32-npm-0.21.5-a4fb03dad4-8.zip new file mode 100644 index 00000000..4a52574d Binary files /dev/null and b/.yarn/cache/@esbuild-win32-ia32-npm-0.21.5-a4fb03dad4-8.zip differ diff --git a/.yarn/cache/@esbuild-win32-x64-npm-0.21.5-eddc2b5ad6-8.zip b/.yarn/cache/@esbuild-win32-x64-npm-0.21.5-eddc2b5ad6-8.zip new file mode 100644 index 00000000..cbd0d73e Binary files /dev/null and b/.yarn/cache/@esbuild-win32-x64-npm-0.21.5-eddc2b5ad6-8.zip differ diff --git a/.yarn/cache/@rollup-rollup-win32-arm64-msvc-npm-4.18.1-bc8bdea5a8-8.zip b/.yarn/cache/@rollup-rollup-win32-arm64-msvc-npm-4.18.1-bc8bdea5a8-8.zip new file mode 100644 index 00000000..1da39c21 Binary files /dev/null and b/.yarn/cache/@rollup-rollup-win32-arm64-msvc-npm-4.18.1-bc8bdea5a8-8.zip differ diff --git a/.yarn/cache/@rollup-rollup-win32-ia32-msvc-npm-4.18.1-d0a091e910-8.zip b/.yarn/cache/@rollup-rollup-win32-ia32-msvc-npm-4.18.1-d0a091e910-8.zip new file mode 100644 index 00000000..0d4211ae Binary files /dev/null and b/.yarn/cache/@rollup-rollup-win32-ia32-msvc-npm-4.18.1-d0a091e910-8.zip differ diff --git a/.yarn/cache/@rollup-rollup-win32-x64-msvc-npm-4.18.1-4558fd3f13-93107c5c51.zip b/.yarn/cache/@rollup-rollup-win32-x64-msvc-npm-4.18.1-4558fd3f13-8.zip similarity index 100% rename from .yarn/cache/@rollup-rollup-win32-x64-msvc-npm-4.18.1-4558fd3f13-93107c5c51.zip rename to .yarn/cache/@rollup-rollup-win32-x64-msvc-npm-4.18.1-4558fd3f13-8.zip diff --git a/.yarnrc.yml b/.yarnrc.yml index a2d1d72c..616f7370 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -23,5 +23,6 @@ supportedArchitectures: os: - darwin - linux + - win32 yarnPath: .yarn/releases/yarn-3.8.1.cjs diff --git a/apps/baekjoon-hub/manifest.json b/apps/baekjoon-hub/manifest.json index 54a89228..d4804911 100644 --- a/apps/baekjoon-hub/manifest.json +++ b/apps/baekjoon-hub/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "PoolC Baekjoon Hub", - "version": "1.0.3", + "version": "1.0.5", "description": "Yonsei Univ Programming Club PoolC integration with Baekjoon", "action": { "default_icon": "assets/images/logo-poolc.png", diff --git a/apps/baekjoon-hub/scripts/background.js b/apps/baekjoon-hub/scripts/background.js index a5e7188e..9d652234 100644 --- a/apps/baekjoon-hub/scripts/background.js +++ b/apps/baekjoon-hub/scripts/background.js @@ -2,6 +2,10 @@ const POOLC_BASE_URL = 'https://poolc.org/api'; const appFetch = (...args) => fetch(...args).then((res) => { + if (res.status === 401) { + throw new Error('unauthorized'); + } + if (res.status >= 400) { throw new Error('fetch error. please check network tab'); } @@ -29,7 +33,9 @@ function handleMessage(request, _, sendResponse) { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, - }).then(() => sendResponse({ success: true })); + }) + .then(() => sendResponse({ success: true })) + .catch(() => sendResponse({ success: false })); }); } // login check diff --git a/apps/baekjoon-hub/scripts/baekjoon/baekjoon.js b/apps/baekjoon-hub/scripts/baekjoon/baekjoon.js index ab5617a4..ccdf6b61 100644 --- a/apps/baekjoon-hub/scripts/baekjoon/baekjoon.js +++ b/apps/baekjoon-hub/scripts/baekjoon/baekjoon.js @@ -17,7 +17,6 @@ function showToast(message) { document.body.append(box); - console.log('toast!'); setTimeout(() => { box.style.opacity = 0; }, 2000 - 300); @@ -27,6 +26,32 @@ function showToast(message) { }, 2000); } +function showErrorToast(message) { + const toast = `
${message}
`; + const box = document.createElement('div'); + box.innerHTML = toast; + box.style.position = 'fixed'; + box.style.top = '20px'; + box.style.right = '20px'; + box.style.backgroundColor = '#fa5252'; + box.style.boxShadow = '0 2px 7px 0 rgba(0, 0, 0, 0.2)'; + box.style.padding = '20px'; + box.style.zIndex = 9999; + box.style.transition = 'opacity .3s linear'; + box.style.color = '#fff'; + box.style.fontWeight = 700; + + document.body.append(box); + + setTimeout(() => { + box.style.opacity = 0; + }, 4000 - 300); + + setTimeout(() => { + box.remove(); + }, 4000); +} + function stopLoader() { clearInterval(loader); loader = null; @@ -85,14 +110,23 @@ function startLoader() { const psData = await findData(); - await notifyProblemSolved({ - language: psData.language, - level: psData.level, - problemId: psData.problemId, - problemTags: psData.tags.map((tag) => tag.key), // 국문 or 영문 어떤게 더 낫지?? - submissionId: psData.submissionId, - title: psData.titles[0].title, - }); + try { + await notifyProblemSolved({ + language: psData.language, + level: psData.level, + problemId: psData.problemId, + problemTags: psData.tags.map((tag) => tag.key), // 국문 or 영문 어떤게 더 낫지?? + submissionId: psData.submissionId, + title: psData.titles[0].title, + }); + } catch (error) { + showErrorToast(` + 인증이 만료되었습니다. 😭
+ 해당 링크를 통해 다시 로그인해주세요. 🙏 + `); + console.error(error); + return; + } await reIssueToken(); showToast(`${psData.problemId}번 문제 풀이가 풀씨에 업로드되었습니다. ❤`); diff --git a/apps/baekjoon-hub/scripts/baekjoon/parsing.js b/apps/baekjoon-hub/scripts/baekjoon/parsing.js index 3be43408..9d9f6c51 100644 --- a/apps/baekjoon-hub/scripts/baekjoon/parsing.js +++ b/apps/baekjoon-hub/scripts/baekjoon/parsing.js @@ -122,7 +122,7 @@ async function SolvedApiCall(problemId) { } async function notifyProblemSolved({ language, level, problemId, problemTags, submissionId, title }) { - const response = await new Promise((resolve) => { + const response = await new Promise((resolve, reject) => { chrome.runtime.sendMessage( { poolc: { @@ -138,7 +138,13 @@ async function notifyProblemSolved({ language, level, problemId, problemTags, su }, }, }, - resolve, + (res) => { + if (res.success) { + resolve(); + } else { + reject(); + } + }, ); }); return response; diff --git a/apps/web-client/src/components/activity/ActivityDetail/ActivityDetail.jsx b/apps/web-client/src/components/activity/ActivityDetail/ActivityDetail.jsx index 4dfa763e..09572b31 100644 --- a/apps/web-client/src/components/activity/ActivityDetail/ActivityDetail.jsx +++ b/apps/web-client/src/components/activity/ActivityDetail/ActivityDetail.jsx @@ -113,7 +113,7 @@ const ActivityDetail = ({ loading, activity, activityMembers, activityMemberIDs,

계획서

- + 첨부된 파일 목록 diff --git a/apps/web-client/src/components/activity/Session/Session.jsx b/apps/web-client/src/components/activity/Session/Session.jsx index 2bef7aee..9f346ac2 100644 --- a/apps/web-client/src/components/activity/Session/Session.jsx +++ b/apps/web-client/src/components/activity/Session/Session.jsx @@ -21,7 +21,7 @@ const Session = ({ session, memberInfo, activityID, attendance, host }) => { {date} ({hour}시간 진행) - + {isLogin && ( <> diff --git a/apps/web-client/src/components/board/BoardJobWriteSection.tsx b/apps/web-client/src/components/board/BoardJobWriteSection.tsx index 5aa2b7f4..6dae8f96 100644 --- a/apps/web-client/src/components/board/BoardJobWriteSection.tsx +++ b/apps/web-client/src/components/board/BoardJobWriteSection.tsx @@ -8,7 +8,6 @@ import { Link, useHistory } from 'react-router-dom'; import { stringify } from 'qs'; import { createStyles } from 'antd-style'; import { UploadOutlined } from '@ant-design/icons'; -import { useQueryClient } from '@tanstack/react-query'; import { ApiError, CustomApi, PostControllerService, PostCreateRequest, queryKey, useAppMutation, useAppQuery } from '~/lib/api-v2'; import { Block, WhiteBlock } from '~/styles/common/Block.styles'; import { MENU } from '~/constants/menus'; @@ -58,7 +57,6 @@ export default function BoardJobWriteSection({ postId }: { postId: number }) { const { styles } = useStyles(); const message = useMessage(); const history = useHistory(); - const queryClient = useQueryClient(); const editorRef = useRef(null); const form = useForm>({ @@ -166,11 +164,7 @@ export default function BoardJobWriteSection({ postId }: { postId: number }) { { onSuccess() { message.success('글이 수정되었습니다.'); - queryClient - .invalidateQueries({ - queryKey: queryKey.post.post(postId), - }) - .then(() => history.push(`/${MENU.BOARD}/${postId}`)); + history.push(`/${MENU.BOARD}/${postId}`); }, }, ); @@ -195,11 +189,7 @@ export default function BoardJobWriteSection({ postId }: { postId: number }) { { onSuccess() { message.success('글이 작성되었습니다.'); - queryClient - .invalidateQueries({ - queryKey: queryKey.post.all('JOB', 0), - }) - .then(() => history.push(`/${MENU.BOARD}?${stringify({ boardType: 'JOB' })}`)); + history.push(`/${MENU.BOARD}?${stringify({ boardType: 'JOB' })}`); }, }, ); diff --git a/apps/web-client/src/components/board/BoardList.tsx b/apps/web-client/src/components/board/BoardList.tsx index ff1a49a1..08a75c5e 100644 --- a/apps/web-client/src/components/board/BoardList.tsx +++ b/apps/web-client/src/components/board/BoardList.tsx @@ -7,7 +7,7 @@ import { stringify } from 'qs'; import { CommentOutlined, EditOutlined } from '@ant-design/icons'; import { MENU } from '~/constants/menus'; import { PostControllerService, PostResponse, queryKey, useAppQuery } from '~/lib/api-v2'; -import { BoardType, getBoardTitleByBoardType } from '~/lib/utils/boardUtil'; +import { BoardType, getBoardTitleForRequest } from '~/lib/utils/boardUtil'; import { dayjs } from '~/lib/utils/dayjs'; import getFileUrl from '~/lib/utils/getFileUrl'; import { useAppSelector } from '~/hooks/useAppSelector'; @@ -75,7 +75,7 @@ export default function BoardList({ boardType, page }: { boardType: BoardType; p queryKey: queryKey.post.all(boardType, page - 1), queryFn: () => PostControllerService.viewPostsByBoardUsingGet({ - boardTitle: getBoardTitleByBoardType(boardType), + boardTitle: getBoardTitleForRequest(boardType), page: page - 1, }), }); diff --git a/apps/web-client/src/components/board/BoardNormalWriteSection.tsx b/apps/web-client/src/components/board/BoardNormalWriteSection.tsx index fa07dc5e..f94dd465 100644 --- a/apps/web-client/src/components/board/BoardNormalWriteSection.tsx +++ b/apps/web-client/src/components/board/BoardNormalWriteSection.tsx @@ -13,7 +13,7 @@ import { useQueryClient } from '@tanstack/react-query'; import { ApiError, CustomApi, PostControllerService, queryKey, useAppMutation, useAppQuery } from '~/lib/api-v2'; import { Block, WhiteBlock } from '~/styles/common/Block.styles'; import { MENU } from '~/constants/menus'; -import { BoardType, getBoardTitleByBoardType } from '~/lib/utils/boardUtil'; +import { BoardType, getBoardTitle } from '~/lib/utils/boardUtil'; import { useMessage } from '~/hooks/useMessage'; import { useAppSelector } from '~/hooks/useAppSelector'; import getFileUrl from '~/lib/utils/getFileUrl'; @@ -56,7 +56,6 @@ export default function BoardNormalWriteSection({ boardType, postId }: { boardTy const history = useHistory(); const message = useMessage(); const loginId = useAppSelector((state) => state.auth.user.memberId); - const queryClient = useQueryClient(); const editorRef = useRef(null); @@ -120,11 +119,7 @@ export default function BoardNormalWriteSection({ boardType, postId }: { boardTy { onSuccess() { message.success('글이 수정되었습니다.'); - queryClient - .invalidateQueries({ - queryKey: queryKey.post.post(postId), - }) - .then(() => history.push(`/${MENU.BOARD}/${postId}`)); + history.push(`/${MENU.BOARD}/${postId}`); }, }, ); @@ -145,11 +140,7 @@ export default function BoardNormalWriteSection({ boardType, postId }: { boardTy { onSuccess() { message.success('글이 작성되었습니다.'); - queryClient - .invalidateQueries({ - queryKey: queryKey.post.all(boardType, 0), - }) - .then(() => history.push(`/${MENU.BOARD}?${stringify({ boardType })}`)); + history.push(`/${MENU.BOARD}?${stringify({ boardType })}`); }, }, ); @@ -220,14 +211,14 @@ export default function BoardNormalWriteSection({ boardType, postId }: { boardTy items={[ { title: 게시판 }, { - title: {getBoardTitleByBoardType(boardType)}, + title: {getBoardTitle(boardType)}, }, ]} />
{})}> - {getBoardTitleByBoardType(boardType)} + {getBoardTitle(boardType)} {renderDescription()}
diff --git a/apps/web-client/src/components/header/Header.jsx b/apps/web-client/src/components/header/Header.jsx index b48dcd8a..3fb66745 100644 --- a/apps/web-client/src/components/header/Header.jsx +++ b/apps/web-client/src/components/header/Header.jsx @@ -1,4 +1,4 @@ -import { Suspense, useState } from 'react'; +import { useState } from 'react'; import { Link } from 'react-router-dom'; import { Avatar, Button, Dropdown } from 'antd'; import { createStyles } from 'antd-style'; @@ -7,8 +7,7 @@ import poolcIcon from '~/assets/images/poolc-icon.png'; import { BarsIcon, HeaderBlock, HeaderIconBox, HeaderIcons, LogoImage } from './Header.styles'; import Menus from './Menus/Menus'; import { MENU } from '~/constants/menus'; -import Notification from './Notification/Notification.tsx'; -import Spinner from '../common/Spinner/Spinner'; +import Notification from './Notification/Notification'; const useStyles = createStyles(({ css }) => ({ avatarButton: css` @@ -23,6 +22,13 @@ const useStyles = createStyles(({ css }) => ({ gap: 5px; margin-right: 10px; `, + menuInner: css` + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: 8px; + `, })); const Header = ({ member, onLogout }) => { @@ -81,12 +87,9 @@ const Header = ({ member, onLogout }) => { {isLogin && ( -
+
{/** Noti */} - }> - - - + {/** Profile */} - +
+ {/** Noti */} + + + + +
)} {!isLogin && ( diff --git a/apps/web-client/src/components/header/Notification/Notification.tsx b/apps/web-client/src/components/header/Notification/Notification.tsx index 67cf9d05..61113bab 100644 --- a/apps/web-client/src/components/header/Notification/Notification.tsx +++ b/apps/web-client/src/components/header/Notification/Notification.tsx @@ -1,37 +1,43 @@ -import { MessageOutlined } from '@ant-design/icons'; +import { BellOutlined } from '@ant-design/icons'; import { Avatar, Badge, Button, Dropdown, Space } from 'antd'; -import { ReactNode } from 'react'; +import { ReactNode, useCallback } from 'react'; import { Link } from 'react-router-dom'; -// import { createStyles } from 'antd-style'; +import { createStyles } from 'antd-style'; import { NotificationControllerService, NotificationResponse, queryKey, useAppQuery } from '~/lib/api-v2'; +import { assert } from '~/lib/utils/assert'; +import { MENU } from '~/constants/menus'; // CSS -// const useStyles = createStyles(({ css }) => ({ -// dropdownItem: css` -// display: 'flex'; -// flex-direction: 'row'; -// gap: '4px'; -// `, -// dropdownButton: css` -// padding: '0'; -// margin: '0'; -// border: '0'; -// `, -// dropdownShape: css` -// display: 'flex'; -// justify-content: 'center'; -// align-items: 'center'; -// `, -// dropdownAvatar: css` -// padding: '2px'; -// display: 'flex'; -// justify-content: 'center'; -// align-items: 'center'; -// background-color: '#19a47d28 !important'; -// color: '#716e6e'; -// `, -// })); +const useStyles = createStyles(() => ({ + dropdownMenu: { + minHeight: '0px', + maxHeight: '250px', + overflowY: 'auto', + }, + + dropdownItem: { + display: 'flex', + flexDirection: 'row', + gap: '4px', + }, + + dropdownButton: { + padding: '0', + margin: '0', + border: '0', + }, + dropdownShape: { display: 'flex', justifyContent: 'center', alignItems: 'center' }, + + dropdownAvatar: { + padding: '2px', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + backgroundColor: '#19a47d28', + color: '#716e6e', + }, +})); // Helper function const convertDate = (inputDate: Date | string) => { @@ -39,100 +45,80 @@ const convertDate = (inputDate: Date | string) => { const date = new Date(inputDate); return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; }; -const Menu = (menu: ReactNode) =>
{menu}
; // Components export default function Notification() { - // const { styles } = useStyles(); - const { data }: { data?: NotificationResponse[] } = useAppQuery({ + const { styles } = useStyles(); + const { data } = useAppQuery({ queryKey: queryKey.notification.unread, - queryFn: NotificationControllerService.getUnreadNotificationsUsingGet, - // queryFn: NotificationControllerService.getAllNotificationsUsingGet, + queryFn: async () => { + const res = await NotificationControllerService.getUnreadNotificationsUsingGet(); + // NOTE: 204의 경우 아무것도 반환되지 않는다. 해당 케이스를 위한 처리 + return res ?? []; + }, + initialData: [], }); + assert(data, 'data is undefined'); + + const Menu = useCallback((menu: ReactNode) =>
{menu}
, [styles.dropdownMenu]); + const resultLinkAndDescription = (response: NotificationResponse) => { switch (response.notificationType) { case 'MESSAGE': return { - link: response?.causedById ? `/message/${response?.causedById}` : `/message`, - description: ( - <> -

{response.senderName}

-

님이 쪽지를 보냈습니다.

- - ), + link: `/${MENU.MESSAGE}`, + description:

새로운 쪽지가 왔습니다.

, }; case 'BADGE': - return { link: `/my-page/badge-list`, description:

새 뱃지를 받았습니다!

}; + return { link: `/${MENU.MY_PAGE}/${MENU.MY_PAGE_BADGE_LIST}`, description:

새 뱃지를 받았습니다!

}; case 'POST': return { - link: response?.causedById ? `/board/${response?.causedById}` : `/board/${response.postId}`, + link: `/board/${response?.causedById}`, description: ( <> -

{response.senderName}

-

님이 댓글을 달았습니다.

+ {/*

{response.senderName}

*/} +

게시물에 댓글이 달렸습니다.

), }; - case 'RECOMMENT': - return { - link: response?.causedById ? `/board/${response?.causedById}` : `/board/${response.parentCommentId}`, - description: ( - <> -

{response.senderName}

-

님이 대댓글을 달았습니다.

- - ), - }; // 기능 안 나오긴 함. + // case 'RECOMMENT': + // return { + // link: response?.causedById ? `/board/${response?.causedById}` : `/board/${response.parentCommentId}`, + // description: ( + // <> + //

{response.senderName}

+ //

님이 대댓글을 달았습니다.

+ // + // ), + // }; // 기능 안 나오긴 함. default: return { link: '/', description:

오류가 발생했습니다

}; } }; - const dropDownItems = data?.map((dataOne) => ({ - key: `${dataOne.createdAt}-${dataOne?.causedById ?? ''}-${dataOne.notificationType}`, - label: ( - -
-

{convertDate(dataOne.createdAt ?? '')}

-
- {resultLinkAndDescription(dataOne).description} -
-
- - ), - })); + + const dropDownItems = + data.length > 0 + ? data.map((dataOne) => ({ + key: `${dataOne.createdAt}-${dataOne?.causedById ?? ''}-${dataOne.notificationType}`, + label: ( + +
+

{convertDate(dataOne.createdAt ?? '')}

+
{resultLinkAndDescription(dataOne).description}
+
+ + ), + })) + : [{ key: 'empty', label:

새로운 알림이 없습니다.

}]; return (
- diff --git a/apps/web-client/src/components/home/HomeEntry.tsx b/apps/web-client/src/components/home/HomeEntry.tsx index 6130ef5b..729bc7d7 100644 --- a/apps/web-client/src/components/home/HomeEntry.tsx +++ b/apps/web-client/src/components/home/HomeEntry.tsx @@ -4,7 +4,7 @@ import RecentNotice from '~/components/home/RecentNotice'; import RecentProject from '~/components/home/RecentProject'; import { useAppSelector } from '~/hooks/useAppSelector'; import { PoolcControllerService, PostControllerService, ProjectControllerService, queryKey, useAppSuspeneseQueries } from '~/lib/api-v2'; -import { getBoardTitleByBoardType } from '~/lib/utils/boardUtil'; +import { getBoardTitleForRequest } from '~/lib/utils/boardUtil'; import ApplyBanner from '~/components/home/ApplyBanner'; const useStyles = createStyles(({ css }) => ({ @@ -34,7 +34,7 @@ export default function HomeEntry() { queryKey: queryKey.post.all('NOTICE', 0), queryFn: () => PostControllerService.viewPostsByBoardUsingGet({ - boardTitle: getBoardTitleByBoardType('NOTICE'), + boardTitle: getBoardTitleForRequest('NOTICE'), page: 0, }), }, diff --git a/apps/web-client/src/components/intro/IntroPoolc.tsx b/apps/web-client/src/components/intro/IntroPoolc.tsx index c29463fa..fbd4031f 100644 --- a/apps/web-client/src/components/intro/IntroPoolc.tsx +++ b/apps/web-client/src/components/intro/IntroPoolc.tsx @@ -87,7 +87,7 @@ const Intro = () => { PoolC 소개 - + 동아리방 위치 diff --git a/apps/web-client/src/components/members/MemberDetail/MemberDetailContent.tsx b/apps/web-client/src/components/members/MemberDetail/MemberDetailContent.tsx index ed5890f5..b2726c21 100644 --- a/apps/web-client/src/components/members/MemberDetail/MemberDetailContent.tsx +++ b/apps/web-client/src/components/members/MemberDetail/MemberDetailContent.tsx @@ -52,8 +52,8 @@ export default function MemberDetailContent({ loginId }: { loginId: string }) { const onMessageButtonClick = () => { if (confirm(`${member.name}님과의 대화를 시작할까요?`)) { mutate(undefined, { - onSuccess: (conversationId) => { - history.push(`/${MENU.MESSAGE}/${conversationId}/${MENU.MESSAGE_FORM}`); + onSuccess: ({id }) => { + history.push(`/${MENU.MESSAGE}/${id}/${MENU.MESSAGE_FORM}`); }, }); } diff --git a/apps/web-client/src/components/message/MessageAllListContent.tsx b/apps/web-client/src/components/message/MessageAllListContent.tsx index 4bf5bfaf..aa19edbd 100644 --- a/apps/web-client/src/components/message/MessageAllListContent.tsx +++ b/apps/web-client/src/components/message/MessageAllListContent.tsx @@ -1,7 +1,10 @@ import { Button, List, Space, Typography } from 'antd'; import { ArrowLeftOutlined } from '@ant-design/icons'; -import { useHistory } from 'react-router-dom'; +import { Link, useHistory } from 'react-router-dom'; import { createStyles } from 'antd-style'; +import { ConversationControllerService, queryKey, useAppSuspenseQuery } from '~/lib/api-v2'; +import { dayjs } from '~/lib/utils/dayjs'; +import { MENU } from '~/constants/menus'; const useStyles = createStyles(({ css }) => ({ wrapper: css` @@ -29,27 +32,22 @@ const useStyles = createStyles(({ css }) => ({ font-weight: 700; font-size: 20px; `, + listItemLink: css` + padding: 12px 0; + width: 100%; + `, })); 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', - }, - ]; + const { data } = useAppSuspenseQuery({ + queryKey: queryKey.conversation.all, + queryFn: ConversationControllerService.getAllConversationsUsingGet, + }); + + console.log(data); return ( @@ -58,21 +56,23 @@ export default function MessageAllListContent() { - 대화목록 + 대화 목록 ( + renderItem={(item) => ( - - - 받은 쪽지 - 2023.08.15 19:20:50 + + + + {item.otherLoginID} + {dayjs(item.lastMessage?.sentAt).format('YYYY-MM-DD HH:mm:ss')} + + {item.lastMessage?.content} - 내용내용내용 - + )} /> diff --git a/apps/web-client/src/components/message/MessageFormView.tsx b/apps/web-client/src/components/message/MessageFormView.tsx index 763f8a25..531c0587 100644 --- a/apps/web-client/src/components/message/MessageFormView.tsx +++ b/apps/web-client/src/components/message/MessageFormView.tsx @@ -4,6 +4,7 @@ import { createStyles } from 'antd-style'; import { FormEventHandler, useState } from 'react'; import { useHistory, useParams } from 'react-router-dom'; import { MENU } from '~/constants/menus'; +import { useMessage } from '~/hooks/useMessage'; import { MessageControllerService, useAppMutation } from '~/lib/api-v2'; import { Block, WhiteBlock } from '~/styles/common/Block.styles'; @@ -41,6 +42,7 @@ export default function MessageFormView() { const [content, setContent] = useState(''); const history = useHistory(); const { conversationId } = useParams<{ conversationId: string }>(); + const message = useMessage(); const { mutate: sendMessage } = useAppMutation({ mutationFn: () => @@ -56,6 +58,7 @@ export default function MessageFormView() { e.preventDefault(); sendMessage(undefined, { onSuccess: () => { + message.success('메시지를 전송했습니다.'); history.push(`/${MENU.MESSAGE}/${conversationId}`); }, }); diff --git a/apps/web-client/src/components/message/MessageListContent.tsx b/apps/web-client/src/components/message/MessageListContent.tsx index ea72058e..f2c24093 100644 --- a/apps/web-client/src/components/message/MessageListContent.tsx +++ b/apps/web-client/src/components/message/MessageListContent.tsx @@ -1,19 +1,12 @@ 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 { Link, useParams } from 'react-router-dom'; import { MENU } from '~/constants/menus'; +import { useAppSuspenseQuery, queryKey, ConversationControllerService } from '~/lib/api-v2'; +import { dayjs } from '~/lib/utils/dayjs'; const useStyles = createStyles(({ css }) => ({ - whiteBlock: css` - padding: 30px 20px; - `, - wrapper: css` - width: 100%; - max-width: 1200px; - box-sizing: border-box; - `, fullWidth: css` width: 100%; `, @@ -38,55 +31,44 @@ const useStyles = createStyles(({ css }) => ({ 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', - }, - ]; + const { conversationId } = useParams<{ conversationId: string }>(); + + const { data } = useAppSuspenseQuery({ + queryKey: queryKey.conversation.conversation(conversationId), + queryFn: () => ConversationControllerService.viewConversationUsingGet({ conversationId }), + }); return ( - - - - - - - 익명 - - - - - - ( - - - - 받은 쪽지 - 2023.08.15 19:20:50 - - 내용내용내용 - - - )} - /> + + + + + + + 대화 상세 - - + + + + + ( + + + + {item.sentByStarter ? '보낸 쪽지' : '받은 쪽지'} + {dayjs(item.sentAt).format('YYYY-MM-DD HH:mm:ss')} + + {item.content} + + + )} + /> + ); } diff --git a/apps/web-client/src/components/message/MessageListView.tsx b/apps/web-client/src/components/message/MessageListView.tsx index 3e7f0722..bbff2a20 100644 --- a/apps/web-client/src/components/message/MessageListView.tsx +++ b/apps/web-client/src/components/message/MessageListView.tsx @@ -10,7 +10,7 @@ const useStyles = createStyles(({ css }) => ({ `, })); -export default function MessageAllListView() { +export default function MessageListView() { const { styles } = useStyles(); return ( diff --git a/apps/web-client/src/components/my-page/MyPageGrassSection.tsx b/apps/web-client/src/components/my-page/MyPageGrassSection.tsx index 14abd630..d820ebf6 100644 --- a/apps/web-client/src/components/my-page/MyPageGrassSection.tsx +++ b/apps/web-client/src/components/my-page/MyPageGrassSection.tsx @@ -1,6 +1,6 @@ import { createStyles } from 'antd-style'; import { memo } from 'react'; -import ActivityCalendar, { Activity, Labels, Level } from 'react-activity-calendar'; +import ActivityCalendar, { Activity, Labels } from 'react-activity-calendar'; import { Tooltip } from 'antd'; import { BaekjoonResponse } from '~/lib/api-v2'; import { dayjs } from '~/lib/utils/dayjs'; @@ -9,6 +9,10 @@ const useStyles = createStyles(({ css }) => ({ wrapper: css` overflow-x: auto; padding: 10px 30px; + .react-activity-calendar__scroll-container { + overflow-x: visible; + overflow-y: visible; + } `, calendarWrap: css` width: 100%; @@ -57,7 +61,7 @@ function MyPageGrassSection({ baekjoonData }: { baekjoonData: BaekjoonResponse[] res.push({ date: formattedDate, count: filtered.length, - level: filtered.length as Level, + level: filtered.length, }); } } diff --git a/apps/web-client/src/components/projects/ProjectDetail/ProjectDetail.jsx b/apps/web-client/src/components/projects/ProjectDetail/ProjectDetail.jsx index 8c371d15..cb387c6c 100644 --- a/apps/web-client/src/components/projects/ProjectDetail/ProjectDetail.jsx +++ b/apps/web-client/src/components/projects/ProjectDetail/ProjectDetail.jsx @@ -45,7 +45,7 @@ const ProjectDetail = ({ project, member }) => { - + diff --git a/apps/web-client/src/lib/api-v2/queryKey.ts b/apps/web-client/src/lib/api-v2/queryKey.ts index 33aaae48..b4622272 100644 --- a/apps/web-client/src/lib/api-v2/queryKey.ts +++ b/apps/web-client/src/lib/api-v2/queryKey.ts @@ -31,7 +31,7 @@ export const queryKey = { }, conversation: { all: ['conversation.all'] as const, - conversation: (id: number) => ['conversation.conversation', id] as const, + conversation: (id: string) => ['conversation.conversation', id] as const, }, notification: { all: ['notification.all'] as const, diff --git a/apps/web-client/src/lib/utils/assert.ts b/apps/web-client/src/lib/utils/assert.ts new file mode 100644 index 00000000..278fdf3c --- /dev/null +++ b/apps/web-client/src/lib/utils/assert.ts @@ -0,0 +1,16 @@ +/** + * Asserts that a condition is true. If the condition is false, an error is thrown. + * + * @param condition - The condition to check. + * @param error - The error to throw if the condition is false. Defaults to a new Error object. + * @throws {Error} - Throws an error if the condition is false. + */ +export function assert(condition: unknown, error: Error | string = new Error()): asserts condition { + if (!condition) { + if (typeof error === 'string') { + throw new Error(error); + } else { + throw error; + } + } +} diff --git a/apps/web-client/src/lib/utils/boardUtil.ts b/apps/web-client/src/lib/utils/boardUtil.ts index 1bd3b73e..73a89517 100644 --- a/apps/web-client/src/lib/utils/boardUtil.ts +++ b/apps/web-client/src/lib/utils/boardUtil.ts @@ -2,7 +2,7 @@ import { match } from 'ts-pattern'; export type BoardType = 'NOTICE' | 'FREE' | 'JOB' | 'PROJECT' | 'CS'; -export function getBoardTitleByBoardType(boardType: BoardType) { +export function getBoardTitleForRequest(boardType: BoardType) { return match(boardType) .with('NOTICE', () => 'notice') .with('FREE', () => 'free') @@ -12,4 +12,14 @@ export function getBoardTitleByBoardType(boardType: BoardType) { .exhaustive(); } +export function getBoardTitle(boardType: BoardType) { + return match(boardType) + .with('NOTICE', () => '공지 게시판') + .with('FREE', () => '자유 게시판') + .with('JOB', () => '취업 게시판') + .with('PROJECT', () => '프로젝트 게시판') + .with('CS', () => 'CS 게시판') + .exhaustive(); +} + export type BoardWriteMode = 'NEW' | 'EDIT'; diff --git a/apps/web-client/src/pages/board/BoardDetailPage.tsx b/apps/web-client/src/pages/board/BoardDetailPage.tsx index 0498439a..d187d5ee 100644 --- a/apps/web-client/src/pages/board/BoardDetailPage.tsx +++ b/apps/web-client/src/pages/board/BoardDetailPage.tsx @@ -10,7 +10,7 @@ import { FolderOpenTwoTone } from '@ant-design/icons'; import { useQueryClient } from '@tanstack/react-query'; import { Block, WhiteBlock } from '~/styles/common/Block.styles'; import { MENU } from '~/constants/menus'; -import { getBoardTitleByBoardType } from '~/lib/utils/boardUtil'; +import { getBoardTitle } from '~/lib/utils/boardUtil'; import { CommentControllerService, PostControllerService, PostResponse, ScrapControllerService, queryKey, useAppMutation, useAppQuery } from '~/lib/api-v2'; import { dayjs } from '~/lib/utils/dayjs'; import { useMessage } from '~/hooks/useMessage'; @@ -243,11 +243,7 @@ export default function BoardDetailPage() { message.success('삭제되었습니다.'); // TODO: assert 이용해서 수정 const boardType = post!.boardType!; - queryClient - .invalidateQueries({ - queryKey: queryKey.post.all(boardType, 0), - }) - .then(() => history.push(`/${MENU.BOARD}?${stringify({ boardType })}`)); + history.push(`/${MENU.BOARD}?${stringify({ boardType })}`); }, }, ); @@ -281,7 +277,7 @@ export default function BoardDetailPage() { boardType: post.boardType, })}`} > - {getBoardTitleByBoardType(post.boardType ?? 'FREE')} + {getBoardTitle(post.boardType ?? 'FREE')} ), }, @@ -311,7 +307,7 @@ export default function BoardDetailPage() { {post.title} )}
- +
{post.fileList && post.fileList.length > 0 && (
diff --git a/apps/web-client/src/pages/board/BoardListPage.tsx b/apps/web-client/src/pages/board/BoardListPage.tsx index 612cc19d..8e575540 100644 --- a/apps/web-client/src/pages/board/BoardListPage.tsx +++ b/apps/web-client/src/pages/board/BoardListPage.tsx @@ -5,7 +5,7 @@ import { Block, WhiteBlock } from '~/styles/common/Block.styles'; import BoardList from '~/components/board/BoardList'; import { useSearchParams } from '~/hooks/useSearchParams'; import { MENU } from '~/constants/menus'; -import { BoardType, getBoardTitleByBoardType } from '~/lib/utils/boardUtil'; +import { BoardType, getBoardTitle } from '~/lib/utils/boardUtil'; import { useAppSelector } from '~/hooks/useAppSelector'; const useStyles = createStyles(({ css }) => ({ @@ -37,29 +37,29 @@ export default function BoardListPage() { }[] = [ { key: 'NOTICE', - label: getBoardTitleByBoardType('NOTICE'), + label: getBoardTitle('NOTICE'), children: , }, ...(isLogin ? [ { key: 'FREE' as BoardType, - label: getBoardTitleByBoardType('FREE'), + label: getBoardTitle('FREE'), children: , }, { key: 'JOB' as BoardType, - label: getBoardTitleByBoardType('JOB'), + label: getBoardTitle('JOB'), children: , }, { key: 'PROJECT' as BoardType, - label: getBoardTitleByBoardType('PROJECT'), + label: getBoardTitle('PROJECT'), children: , }, { key: 'CS' as BoardType, - label: getBoardTitleByBoardType('CS'), + label: getBoardTitle('CS'), children: , }, ] diff --git a/apps/web-client/src/pages/message/MessageListPage.tsx b/apps/web-client/src/pages/message/MessageListPage.tsx index c2738e74..9f8320fe 100644 --- a/apps/web-client/src/pages/message/MessageListPage.tsx +++ b/apps/web-client/src/pages/message/MessageListPage.tsx @@ -1,93 +1,5 @@ -import { Button, List, Space, Typography } from 'antd'; -import { createStyles } from 'antd-style'; -import { ArrowLeftOutlined } from '@ant-design/icons'; -import { Link, useHistory, useParams } 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; - `, -})); +import MessageListView from '~/components/message/MessageListView'; export default function MyPageMessageListPage() { - const { styles } = useStyles(); - const history = useHistory(); - const { conversationId } = useParams<{ conversationId: string }>(); - - const data = [ - { - title: 'Ant Design Title 1', - }, - { - title: 'Ant Design Title 2', - }, - { - title: 'Ant Design Title 3', - }, - { - title: 'Ant Design Title 4', - }, - ]; - - return ( - - - - - - - 익명 - - - - - - ( - - - - 받은 쪽지 - 2023.08.15 19:20:50 - - 내용내용내용 - - - )} - /> - - - - ); + return ; } diff --git a/apps/web-client/src/pages/my-page/MyPage.tsx b/apps/web-client/src/pages/my-page/MyPage.tsx index 33fc74e4..6bcb9ff4 100644 --- a/apps/web-client/src/pages/my-page/MyPage.tsx +++ b/apps/web-client/src/pages/my-page/MyPage.tsx @@ -9,7 +9,6 @@ import MyPageGrassSection from '~/components/my-page/MyPageGrassSection'; import { queryClient } from '~/lib/utils/queryClient'; import { getProfileImageUrl } from '~/lib/utils/getProfileImageUrl'; import getFileUrl from '~/lib/utils/getFileUrl'; -import { useMessage } from '~/hooks/useMessage'; const useStyles = createStyles(({ css }) => ({ whiteBlock: css` @@ -86,7 +85,6 @@ const useStyles = createStyles(({ css }) => ({ export default function MyPage() { const { styles, cx } = useStyles(); - const message = useMessage(); const listData: { title: string; @@ -112,7 +110,7 @@ export default function MyPage() { { title: '쪽지', icon: , - onClick: () => message.info('기능 준비중입니다!'), + link: `/${MENU.MESSAGE}`, }, ]; diff --git a/package.json b/package.json index d2d9845b..89746f92 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,5 @@ "eslint": "^8.57.0", "prettier": "^3.1.1", "typescript": "^5.3.3" - }, - "dependencies": { - "@rollup/rollup-win32-x64-msvc": "^4.18.1" } } diff --git a/yarn.lock b/yarn.lock index fc407287..c585d910 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2690,10 +2690,9 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-win32-x64-msvc@npm:4.18.1, @rollup/rollup-win32-x64-msvc@npm:^4.18.1": +"@rollup/rollup-win32-x64-msvc@npm:4.18.1": version: 4.18.1 resolution: "@rollup/rollup-win32-x64-msvc@npm:4.18.1" - checksum: 93107c5c513d119e20ed1ee155fa4cee16888df8ebc3b4687f6901050a3e911f24ebfcb334cb930f78d3e780e8d358d4336f16842b1f20fc13546d916a1d53c4 conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -7836,7 +7835,6 @@ __metadata: version: 0.0.0-use.local resolution: "poolc-dialga@workspace:." dependencies: - "@rollup/rollup-win32-x64-msvc": ^4.18.1 eslint: ^8.57.0 prettier: ^3.1.1 typescript: ^5.3.3