Skip to content

Commit

Permalink
#11 feat: 실시간 알람 기능 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
sinamong0620 committed Sep 21, 2024
1 parent 8d3850d commit be26fe1
Show file tree
Hide file tree
Showing 6 changed files with 336 additions and 150 deletions.
24 changes: 19 additions & 5 deletions src/api/MyPageApi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NotificationResponse, ProfileInfo, ReturnData } from '../types/MyPage';
import { axiosInstance } from '../utils/apiConfig';

// * 마이페이지 프로필 조회
export const fetchData = async (): Promise<ProfileInfo | undefined> => {
try {
const res = await axiosInstance.get('/members/mypage');
Expand All @@ -10,19 +11,23 @@ export const fetchData = async (): Promise<ProfileInfo | undefined> => {
}
};

//마이페이지에서 팀블록 챌린지 블록 페이지네이션 데이터 받아오기
export const fetchBlockData = async (pageNum: number): Promise<ReturnData | undefined> => {
// * 마이페이지 개인,팀,챌린지 블록 데이터 받아오기
export const fetchBlockData = async (
pageNum: number,
email: string
): Promise<ReturnData | undefined> => {
try {
const res = await axiosInstance.get(
`/members/mypage/dashboard-challenges?page=${pageNum}&size=6`
`/members/mypage/dashboard-challenges?requestEmail=${email}&page=${pageNum}&size=6`
);
console.log(res);
return res.data as ReturnData;
} catch (error) {
console.error('Error fetching data:', error);
}
};

//알람 전체 리스트 조회
// * 알람 리스트 전체 조회
export const getAlarmList = async (): Promise<NotificationResponse | undefined> => {
try {
const res = await axiosInstance.get('/notifications');
Expand All @@ -32,7 +37,7 @@ export const getAlarmList = async (): Promise<NotificationResponse | undefined>
}
};

//사용자 닉네임,자기소개 수정
//* 사용자 닉네임,자기소개 수정
export const updateUserInfo = async (nickname: string, introduction: string) => {
try {
const data = await axiosInstance.patch('/members/mypage', {
Expand All @@ -44,3 +49,12 @@ export const updateUserInfo = async (nickname: string, introduction: string) =>
console.error('Error fetching data :', error);
}
};

//* 알림 아이콘 클릭시 모든 알람 읽어진 것으로 수정
export const updateAlarmIsRead = async () => {
try {
await axiosInstance.patch('/notifications');
} catch (error) {
console.error('Error fetching data :', error);
}
};
12 changes: 11 additions & 1 deletion src/api/TeamDashBoardApi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ export const deleteTeamDashboard = async (id: string): Promise<void> => {
const response = await axiosInstance.delete(`/dashboards/team/${id}`);
console.log(response);
} catch (error) {
console.log('error');
console.error('error');
}
};

// * 팀 대시보드 참여 수락
export const postTeamDashboard = async (dashboardId: string) => {
try {
const response = await axiosInstance.post(`/dashboards/team/${dashboardId}/join`);
console.log(response.data);
} catch (error) {
console.error('error');
}
};
63 changes: 63 additions & 0 deletions src/components/AlarmBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import styled from 'styled-components';
import Flex from './Flex';
import theme from '../styles/Theme/Theme';
import { postTeamDashboard } from '../api/TeamDashBoardApi';

type Props = {
message: string;
isRead: boolean;
};
const AlarmBlock = ({ message, isRead }: Props) => {
const nameMatch = message.match(/([-]+)(?=)/);

const dashboardMatch = message.match(/\s(.+?)\s/);

const numberMatch = message.match(/\d+$/);

const name = nameMatch ? nameMatch[0] : null;
const dashboard = dashboardMatch ? dashboardMatch[0] : null;
const number = numberMatch ? numberMatch[0] : '';

const onAcceptHandler = () => {
postTeamDashboard(number);
};
return (
<AlarmContainer isRead={isRead}>
<Flex alignItems="center">
<UserInfoContainer>
<h6>{name}</h6>
<p>{dashboard} 초대</p>
</UserInfoContainer>
{number !== '' ? <button onClick={onAcceptHandler}>수락</button> : ''}
</Flex>
</AlarmContainer>
);
};
export default AlarmBlock;

const AlarmContainer = styled.div<{ isRead: boolean }>`
padding: 2rem;
background: ${props => (props.isRead ? 'white' : '#fffdcc')};
font-size: 0.8rem;
border-bottom: 1px solid ${theme.color.stroke2};
h6 {
margin-bottom: 0.2rem;
}
button {
background: ${theme.color.lightGray};
border-radius: 0.5rem;
padding: 0.2rem 0.5rem;
color: white;
font-size: 0.8rem;
font-weight: 600;
}
button:hover {
background: ${theme.color.gradation};
}
`;

const UserInfoContainer = styled.div`
flex: 1;
`;
30 changes: 22 additions & 8 deletions src/hooks/useSSE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@ import { atom, useAtom } from 'jotai';
import { EventSourcePolyfill } from 'event-source-polyfill';
import { unreadCount } from '../contexts/sseAtom';
import { customErrToast } from '../utils/customErrorToast';
import { NotificationResponse } from '../types/MyPage';

// 1. Jotai atom 정의 (SSE 상태와 메시지 관리)
const sseConnectedAtom = atom(false); // SSE 연결 상태
const sseMessagesAtom = atom<string[]>([]); // SSE 메시지 상태

// 2. useSSE 훅 정의
export const useSSE = () => {
export const useSSE = (
setAlarmNoti?: React.Dispatch<React.SetStateAction<NotificationResponse | undefined>>
) => {
const [, setConnected] = useAtom(sseConnectedAtom);
const [, setMessages] = useAtom(sseMessagesAtom);
const [unReadCount, setUnReadCount] = useAtom(unreadCount);

const eventSource = useRef<EventSourcePolyfill | null>(null);
const reconnectTimeout = useRef<NodeJS.Timeout | null>(null); // 재연결 타임아웃 관리
// 재연결 타임아웃 관리
const reconnectTimeout = useRef<NodeJS.Timeout | null>(null);

// SSE 연결 설정 함수
const connectToSSE = useCallback(() => {
Expand All @@ -39,10 +41,22 @@ export const useSSE = () => {

// 메시지 수신 처리
eventSource.current.onmessage = event => {
console.log('SSE 메시지 수신:', event.data);
if (event.data.includes('대시보드')) {
customErrToast(event.data);
if (!event.data.includes('연결')) {
const modifiedMessage = event.data.replace(/\d+$/, '');
customErrToast(modifiedMessage);
setUnReadCount(prev => prev + 1);
if (setAlarmNoti)
setAlarmNoti(prev => {
if (prev) {
return {
...prev,
data: {
...prev.data,
notificationInfoResDto: [modifiedMessage, ...prev.data.notificationInfoResDto],
},
};
}
});
}
};

Expand All @@ -64,7 +78,7 @@ export const useSSE = () => {
connectToSSE();
}, 3000); // 3초 후 재연결
};
}, [setConnected, setMessages]);
}, [setConnected, setMessages, setUnReadCount]);

useEffect(() => {
// 첫 연결 시도
Expand Down
Loading

0 comments on commit be26fe1

Please sign in to comment.