-
Notifications
You must be signed in to change notification settings - Fork 2
feat : 알림 2차 기능 구현 #81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f14d139
665f7b4
9dc3f83
cdc0183
8b1447f
aa1f280
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import client from './client'; | ||
|
|
||
| const getTimeLines = async () => { | ||
| try { | ||
| const res = await client.get('/api/timelines'); | ||
| if (!res) throw new Error('타임라인을 받아오는 도중 오류가 발생했습니다.'); | ||
| console.log(res); | ||
| return res; | ||
| } catch (error) { | ||
| console.error(error); | ||
| } | ||
| }; | ||
|
|
||
| const patchReadNotification = async (timelineId: number) => { | ||
| try { | ||
| const res = await client.patch(`/api/notifications/${timelineId}/read`); | ||
| if (!res) throw new Error('편지 개별 읽음 처리를 하는 도중 오류가 발생했습니다.'); | ||
| return res; | ||
| } catch (error) { | ||
| console.error(error); | ||
| } | ||
| }; | ||
|
|
||
| const patchReadNotificationAll = async () => { | ||
| try { | ||
| const res = await client.patch(`/api/notifications/read`); | ||
| if (!res) throw new Error('편지 개별 읽음 처리를 하는 도중 오류가 발생했습니다.'); | ||
| return res; | ||
| } catch (error) { | ||
| console.error(error); | ||
| } | ||
| }; | ||
|
|
||
| export { getTimeLines, patchReadNotification, patchReadNotificationAll }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| import { EventSourcePolyfill } from 'event-source-polyfill'; | ||
| import { useEffect, useRef } from 'react'; | ||
|
|
||
| import useAuthStore from '@/stores/authStore'; | ||
|
|
||
| export const useServerSentEvents = () => { | ||
| const accessToken = useAuthStore.getState().accessToken; | ||
| const sourceRef = useRef<EventSourcePolyfill | null>(null); | ||
|
|
||
| useEffect(() => { | ||
| if (!accessToken) { | ||
| console.log('로그인 정보 확인불가'); | ||
| return; | ||
| } | ||
|
|
||
| const connectSSE = () => { | ||
| try { | ||
| console.log('구독 시작'); | ||
| sourceRef.current = new EventSourcePolyfill( | ||
| `${import.meta.env.VITE_API_URL}/api/notifications/sub`, | ||
| { | ||
| headers: { | ||
| Authorization: `Bearer ${accessToken}`, | ||
| }, | ||
| }, | ||
| ); | ||
|
|
||
| sourceRef.current.onmessage = (event) => { | ||
| console.log(event); | ||
| console.log('알림 전송'); | ||
| }; | ||
|
|
||
| sourceRef.current.addEventListener('notification', (event) => { | ||
| console.log(event); | ||
| console.log('알림 전송 dd'); | ||
| }); | ||
|
|
||
| sourceRef.current.onerror = (error) => { | ||
| console.log(error); | ||
| console.log('에러 발생함'); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| sourceRef.current?.close(); | ||
| // 재연결 로직 추가 가능 | ||
| setTimeout(connectSSE, 5000); // 5초 후 재연결 시도 | ||
| }; | ||
| } catch (error) { | ||
| console.error(error); | ||
| } | ||
| }; | ||
|
|
||
| connectSSE(); | ||
|
|
||
| return () => { | ||
| console.log('컴포넌트 언마운트로 인한 구독해제'); | ||
| closeSSE(); | ||
| }; | ||
| }, [accessToken]); | ||
|
|
||
| // 바깥으로 보낼 closeSSE 함수 | ||
| const closeSSE = () => { | ||
| sourceRef.current?.close(); | ||
| sourceRef.current = null; | ||
| }; | ||
|
|
||
| return { closeSSE }; | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,50 +1,93 @@ | ||
| import { useState } from 'react'; | ||
| import { useEffect, useState } from 'react'; | ||
| import { useNavigate } from 'react-router'; | ||
|
|
||
| import { getTimeLines, patchReadNotification, patchReadNotificationAll } from '@/apis/notification'; | ||
| import PageTitle from '@/components/PageTitle'; | ||
|
|
||
| import NotificationItem from './components/NotificationItem'; | ||
| import WarningModal from './components/WarningModal'; | ||
|
|
||
| const DUMMY_NOTI = [ | ||
| { id: 1, type: 'letter', message: '12E31님이 편지를 보냈습니다.', isRead: false }, | ||
| { id: 2, type: 'warning', message: '따숨님, 욕설로 인해 경고를 받으셨어요.', isRead: false }, | ||
| { id: 3, type: 'letter', message: '12E31님이 편지를 보냈습니다.', isRead: false }, | ||
| { id: 4, type: 'letter', message: '12E31님이 편지를 보냈습니다.', isRead: true }, | ||
| { id: 5, type: 'letter', message: '12E31님이 편지를 보냈습니다.', isRead: false }, | ||
| { id: 6, type: 'board', message: '12E31님과의 대화가 게시판에 공유되었어요.', isRead: false }, | ||
| { | ||
| id: 7, | ||
| type: 'board', | ||
| message: '12E31님과의 게시글에 대한 공유요청을 보냈어요.', | ||
| isRead: false, | ||
| }, | ||
| ]; | ||
|
|
||
| const NotificationsPage = () => { | ||
| const navigate = useNavigate(); | ||
|
|
||
| const [noti, setNoti] = useState<Noti[]>([]); | ||
|
|
||
| const [isOpenWarningModal, setIsOpenWarningModal] = useState(false); | ||
|
|
||
| const handleClickItem = (type: string) => { | ||
| if (type === 'warning') { | ||
| const [adminText, setAdmintext] = useState<string>(''); | ||
|
|
||
| // MEMO : 편지 데이터 전송중 데이터도 추가될건데 나중에 데이터 추가되면 코드 업데이트 하긔 | ||
| const handleClickItem = (alarmType: string, content?: string | number) => { | ||
| if (alarmType === 'LETTER') { | ||
| navigate(`/letter/${content}`); | ||
| } | ||
| if (alarmType === 'REPORT') { | ||
| setIsOpenWarningModal(true); | ||
| if (typeof content === 'string') setAdmintext(content); | ||
| } | ||
| if (alarmType === 'SHARE') { | ||
| navigate(`/board/letter/${content}`, { state: { isShareLetterPreview: true } }); | ||
| } | ||
| if (alarmType === 'POSTED') { | ||
| navigate(`/board/letter/${content}`); | ||
| } | ||
| }; | ||
|
|
||
| const handleGetTimeLines = async () => { | ||
| const res = await getTimeLines(); | ||
| if (res?.status === 200) { | ||
| console.log(res); | ||
| setNoti(res.data.data.content); | ||
| } | ||
| }; | ||
|
|
||
| const handlePatchReadNotification = async (timelineId: number) => { | ||
| const res = await patchReadNotification(timelineId); | ||
| if (res?.status !== 200) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이것도 아마 api 호출 catch 문에서 잡힐 것 같습니다! |
||
| console.log('읽음처리 에러 발생'); | ||
| } | ||
| }; | ||
|
|
||
| const handlePatchReadNotificationAll = async () => { | ||
| const res = await patchReadNotificationAll(); | ||
| if (res?.status !== 200) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 얘도 잡힐 것 같습니다! |
||
| console.log('모두 읽음처리 에러 발생'); | ||
| } | ||
| }; | ||
|
|
||
| useEffect(() => { | ||
| handleGetTimeLines(); | ||
| }, []); | ||
|
|
||
| return ( | ||
| <> | ||
| <WarningModal isOpen={isOpenWarningModal} onClose={() => setIsOpenWarningModal(false)} /> | ||
| <WarningModal | ||
| isOpen={isOpenWarningModal} | ||
| adminText={adminText} | ||
| onClose={() => setIsOpenWarningModal(false)} | ||
| /> | ||
| <main className="flex grow flex-col items-center px-5 pt-20 pb-9"> | ||
| <PageTitle className="mb-10">알림</PageTitle> | ||
| <button type="button" className="body-sb text-gray-60 place-self-end"> | ||
| <button | ||
| type="button" | ||
| className="body-sb text-gray-60 place-self-end" | ||
| onClick={() => { | ||
| handlePatchReadNotificationAll(); | ||
| }} | ||
| > | ||
| 모두 읽음 | ||
| </button> | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| <ul className="mt-2 flex h-full w-full flex-col gap-2 pb-10"> | ||
| {DUMMY_NOTI.map((notification) => ( | ||
| <li key={notification.id}> | ||
| {noti.map((notification) => ( | ||
| <li key={notification.timelineId}> | ||
| <NotificationItem | ||
| type={notification.type} | ||
| message={notification.message} | ||
| isRead={notification.isRead} | ||
| onClick={() => handleClickItem(notification.type)} | ||
| type={notification.alarmType} | ||
| title={notification.title} | ||
| read={notification.read} | ||
| onClick={() => { | ||
| handleClickItem(notification.alarmType, notification.content); | ||
| handlePatchReadNotification(notification.timelineId); | ||
| }} | ||
| /> | ||
| </li> | ||
| ))} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!res대신res.status를 체크하는 게 좋아보입니당!throw error로 에러를 다시 던져주는 게 좋을 것 같아요patchReadNotification와patchReadNotificationAll의 에러 메시지가 같아서, 구분하기 쉽도록 수정하면 어떨까 합니다