Skip to content

Commit

Permalink
#68 fix : 실시간 알람 렌더링 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
sinamong0620 committed Sep 24, 2024
1 parent 9e5f1db commit ed7adf9
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 98 deletions.
4 changes: 2 additions & 2 deletions src/contexts/sseAtom.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { atom } from 'jotai';
import { EventSourcePolyfill } from 'event-source-polyfill';
import { NotificationResponse } from '../types/MyPage';

// Jotai atom 정의
export const sseConnectedAtom = atom(false); // SSE 연결 상태
export const sseMessagesAtom = atom<string[]>([]); // 수신된 알림 메시지
export const unreadCount = atom<number>(0); // 읽지 않은 알림 수
export const notifications = atom<string[]>([]); // 알림 목록 배열
export const notifications = atom<NotificationResponse>(); // 알림 목록
37 changes: 20 additions & 17 deletions src/hooks/useSSE.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { useEffect, useCallback, useRef } from 'react';
import { atom, useAtom } from 'jotai';
import { EventSourcePolyfill } from 'event-source-polyfill';
import { unreadCount } from '../contexts/sseAtom';
import { notifications, unreadCount } from '../contexts/sseAtom';
import { customErrToast } from '../utils/customErrorToast';
import { NotificationResponse } from '../types/MyPage';
import { apiBaseUrl } from '../utils/apiConfig';
import { getAlarmList } from '../api/MyPageApi';
import { useQuery } from '@tanstack/react-query';

const sseConnectedAtom = atom(false); // SSE 연결 상태
const sseMessagesAtom = atom<string[]>([]); // SSE 메시지 상태

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

const eventSource = useRef<EventSourcePolyfill | null>(null);
// 재연결 타임아웃 관리
Expand Down Expand Up @@ -42,22 +42,25 @@ export const useSSE = (

// 메시지 수신 처리
eventSource.current.onmessage = event => {
console.log(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],
},
};
}
});

// if (setAlarmNoti)
// setAlarmNoti(prev => {
// if (prev) {
// return {
// ...prev,
// data: {
// ...prev.data,
// notificationInfoResDto: [modifiedMessage, ...prev.data.notificationInfoResDto],
// },
// };
// }
// });
}
};

Expand Down
211 changes: 132 additions & 79 deletions src/pages/MyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ import { fetchBlockData, fetchData, getAlarmList, updateAlarmIsRead } from '../a
import { useAtom } from 'jotai';
import { useQuery } from '@tanstack/react-query';
import Pagination from '@mui/material/Pagination';
import { unreadCount } from '../contexts/sseAtom';
import { useSSE } from '../hooks/useSSE';
import { notifications, unreadCount } from '../contexts/sseAtom';
import useTeamDashBoard from '../hooks/useTeamDashBoard';
import { postTeamDashboard } from '../api/TeamDashBoardApi';

const MyPage = () => {
const navigate = useNavigate();
const { data, refetch } = useQuery({ queryKey: ['profile'], queryFn: fetchData });
const { data: alarmNoti } = useQuery({ queryKey: ['alarmNoti'], queryFn: getAlarmList });
const { data: alarmNoti, refetch: AlarmRefetch } = useQuery({
queryKey: ['alarmNoti'],
queryFn: getAlarmList,
});

console.log(alarmNoti);
const [teamBool, setTeamBool] = useState<string>('personal'); // 기본값으로 팀 탭을 보여줌
Expand All @@ -37,15 +41,16 @@ const MyPage = () => {

//* 알람 관련 변수
const [unReadCount, setUnReadCount] = useAtom(unreadCount);
const [alarmList, setAlarmList] = useAtom(notifications);
const [visibleAlarm, setAlarmVisible] = useState(false);
const [visibleModal, setModalVisible] = useState(false);
const [alarmList, setAlarmList] = useState(alarmNoti);

useSSE(setAlarmList); //스트림 연결

const onAlarmVisibleFunc = () => {
setAlarmVisible(prev => !prev);
setUnReadCount(0);
if (unReadCount !== 0) {
// unReadCount가 0이 아닐 때만 업데이트
setUnReadCount(0);
}
updateAlarmIsRead();
};

Expand Down Expand Up @@ -94,6 +99,13 @@ const MyPage = () => {
}
};

//로그아웃
const onLogoutHandler = () => {
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
navigate('/login');
};

// 처음 렌더링 시 팀 탭 데이터 호출
useEffect(() => {
const fetchInitialBlockData = async () => {
Expand All @@ -120,88 +132,112 @@ const MyPage = () => {
}
}, [alarmNoti]);

useEffect(() => {
AlarmRefetch();
setAlarmList(alarmNoti);
}, [unReadCount]);

let content;
if (teamBool === 'personal') {
content = (
<S.DashboardContainer>
<S.ChanllengeBlockContainer>
{personalBlockData?.personalDashboardInfoResDto.map((item, idx) => {
const { dashboardId, title, description } = item;
return (
<S.TeamBlockWrapper
key={idx}
onClick={() => {
navigate(`/${dashboardId}`);
}}
>
<ChallengeBlock title={title} description={description} />
</S.TeamBlockWrapper>
);
})}
</S.ChanllengeBlockContainer>
<S.PagenateBox>
<Pagination
page={pageNumber}
count={personalBlockData?.pageInfoResDto.totalPages}
defaultPage={0}
onChange={onChangePageNation}
/>
</S.PagenateBox>
{personalBlockData?.personalDashboardInfoResDto.length === 0 ? (
NoContentComponent
) : (
<div>
<S.GridContainer>
{personalBlockData?.personalDashboardInfoResDto.map((item, idx) => {
const { dashboardId, title, description } = item;
return (
<S.TeamBlockWrapper
key={idx}
onClick={() => {
navigate(`/${dashboardId}`);
}}
>
<ChallengeBlock title={title} description={description} />
</S.TeamBlockWrapper>
);
})}
</S.GridContainer>

<S.PagenateBox>
<Pagination
page={pageNumber}
count={personalBlockData?.pageInfoResDto.totalPages}
defaultPage={0}
onChange={onChangePageNation}
/>
</S.PagenateBox>
</div>
)}
</S.DashboardContainer>
);
} else if (teamBool === 'team') {
content = (
<S.DashboardContainer>
<S.ChanllengeBlockContainer>
{teamBlockData?.teamDashboardInfoResDto.map((item, idx) => {
const { dashboardId, title, joinMembers, description } = item;
return (
<S.TeamBlockWrapper
key={idx}
onClick={() => {
navigate(`/${dashboardId}`);
}}
>
<ChallengeBlock
title={title}
joinMembers={joinMembers ?? 0}
description={description}
/>
</S.TeamBlockWrapper>
);
})}
</S.ChanllengeBlockContainer>
<S.PagenateBox>
<Pagination
page={pageNumber}
count={teamBlockData?.pageInfoResDto.totalPages}
defaultPage={0}
onChange={onChangePageNation}
/>
</S.PagenateBox>
{teamBlockData?.teamDashboardInfoResDto.length === 0 ? (
NoContentComponent
) : (
<div>
<S.GridContainer>
{teamBlockData?.teamDashboardInfoResDto.map((item, idx) => {
const { dashboardId, title, joinMembers, description } = item;
return (
<S.TeamBlockWrapper
key={idx}
onClick={() => {
navigate(`/${dashboardId}`);
}}
>
<ChallengeBlock
title={title}
joinMembers={joinMembers ?? 0}
description={description}
/>
</S.TeamBlockWrapper>
);
})}
</S.GridContainer>
<S.PagenateBox>
<Pagination
page={pageNumber}
count={teamBlockData?.pageInfoResDto.totalPages}
defaultPage={0}
onChange={onChangePageNation}
/>
</S.PagenateBox>
</div>
)}
</S.DashboardContainer>
);
} else if (teamBool === 'challenge') {
content = (
<S.DashboardContainer>
<S.ChanllengeBlockContainer>
{challengeBlockData?.challengeInfoResDto.map((item, idx) => {
const { title, cycle } = item;
return (
<div key={idx}>
<ChallengeBlock key={idx} />
</div>
);
})}
</S.ChanllengeBlockContainer>
<S.PagenateBox>
<Pagination
page={pageNumber}
count={challengeBlockData?.pageInfoResDto.totalPages}
defaultPage={0}
onChange={onChangePageNation}
/>
</S.PagenateBox>
{challengeBlockData?.challengeInfoResDto.length === 0 ? (
NoContentComponent
) : (
<div>
<S.GridContainer>
{challengeBlockData?.challengeInfoResDto.map((item, idx) => {
const { title, cycle } = item;
return (
<div key={idx}>
<ChallengeBlock key={idx} />
</div>
);
})}
</S.GridContainer>
<S.PagenateBox>
<Pagination
page={pageNumber}
count={challengeBlockData?.pageInfoResDto.totalPages}
defaultPage={0}
onChange={onChangePageNation}
/>
</S.PagenateBox>
</div>
)}
</S.DashboardContainer>
);
}
Expand Down Expand Up @@ -239,7 +275,7 @@ const MyPage = () => {

<Flex flexDirection="column" gap="0.75rem">
<S.ButtonWrapper onClick={onModalVisibleFunc}>프로필 수정</S.ButtonWrapper>
<S.ButtonWrapper> 로그아웃 </S.ButtonWrapper>
<S.ButtonWrapper onClick={onLogoutHandler}> 로그아웃 </S.ButtonWrapper>
</Flex>
</Flex>

Expand All @@ -249,9 +285,18 @@ const MyPage = () => {
</S.ImageWrapper>
{visibleAlarm && (
<S.AlarmDataContainer>
{alarmNoti?.data.notificationInfoResDto.map((item, idx) => (
<AlarmBlock key={idx} message={item.message} isRead={item.isRead} />
))}
{alarmList?.data.notificationInfoResDto.length === 0 ? (
<Flex height={96} justifyContent="center" alignItems="center">
<span>생성된 알림이 없어요</span>
</Flex>
) : (
alarmList?.data.notificationInfoResDto
.slice()
.reverse()
.map((item, idx) => (
<AlarmBlock key={idx} message={item.message} isRead={item.isRead} />
))
)}
</S.AlarmDataContainer>
)}
</Flex>
Expand All @@ -273,3 +318,11 @@ const MyPage = () => {
};

export default MyPage;

const NoContentComponent = (
<S.NoContentComponent>
<Flex height={500} alignItems="center" justifyContent="center">
생성된 내용이 없어요
</Flex>
</S.NoContentComponent>
);

0 comments on commit ed7adf9

Please sign in to comment.