diff --git a/public/firebase-messaging-sw.js b/public/firebase-messaging-sw.js index 16740290..29fa7bf2 100644 --- a/public/firebase-messaging-sw.js +++ b/public/firebase-messaging-sw.js @@ -1,3 +1,83 @@ +function parseNestedJson(jsonString) { + try { + let formattedString = jsonString.trim().replace(/\n/g, '').replace(/ {2}/g, ''); + let outerJson = JSON.parse(formattedString); + let innerJsonString = outerJson.body; + let correctedInnerJsonString = innerJsonString.replace(/\\"/g, '"'); + let innerJson = JSON.parse(correctedInnerJsonString); + return innerJson; + } catch (e) { + return `Error parsing JSON: ${e.message}`; + } +} + +function createNotificationBody(data) { + if (!data || !data.spaceEventInfo || !data.memberEventInfo) { + return '필요한 데이터가 누락되었습니다.'; + } + + const spaceTitle = data.spaceEventInfo.spaceTitle ? data.spaceEventInfo.spaceTitle : '여행지 미정'; + const memberNickname = data.memberEventInfo.memberNickname; + let notificationBody = ''; + + switch (data.eventType) { + case 'VOTE_CREATED': + if (!data.voteEventInfo) { + return `[${spaceTitle}] ${memberNickname} 님이 새로운 투표를 생성했습니다.`; + } + // eslint-disable-next-line no-case-declarations + const voteTitle = data.voteEventInfo.voteTitle; + notificationBody = `[${spaceTitle}] ${memberNickname} 님이 새로운 투표 “${voteTitle}”를 생성했습니다.`; + break; + case 'VOTE_DONE': + if (!data.voteEventInfo) { + return `[${spaceTitle}] 투표가 최종 확정되었습니다.`; + } + // eslint-disable-next-line no-case-declarations + const voteTitleDone = data.voteEventInfo.voteTitle; + notificationBody = `[${spaceTitle}] “${voteTitleDone}” 투표가 최종 확정되었습니다.`; + break; + case 'VOTE_DELETED': + if (!data.voteEventInfo) { + return `[${spaceTitle}] ${memberNickname} 님이 투표를 삭제했습니다.`; + } + // eslint-disable-next-line no-case-declarations + const voteTitleDeleted = data.voteEventInfo.voteTitle; + notificationBody = `[${spaceTitle}] ${memberNickname} 님이 “${voteTitleDeleted}”의 투표를 삭제했습니다.`; + break; + case 'MEMBER_INVITED': + notificationBody = `[${spaceTitle}] 에 ${memberNickname} 님이 초대되었습니다.`; + break; + case 'MEMBER_EXIT': + notificationBody = `[${spaceTitle}] ${memberNickname} 님이 여행 스페이스를 나갔습니다.`; + break; + case 'NEW_JOURNEY_ADDED': + // eslint-disable-next-line no-case-declarations + const voteTitleCandidateAdd = data.voteEventInfo.voteTitle; + notificationBody = `[${spaceTitle}] ${voteTitleCandidateAdd} 의 후보가 일정에 추가 되었습니다.`; + break; + case 'CANDIDATE_ADDED': + // eslint-disable-next-line no-case-declarations + const voteTitleAdd = data.voteEventInfo.voteTitle; + notificationBody = `[${spaceTitle}] ${memberNickname} 님이 "${voteTitleAdd}"에 새로운 후보를 등록했습니다.`; + break; + case 'SPACE_LOCATION_CHANGED': + notificationBody = `여행지가 [${data.spaceEventInfo.oldTitle || '여행지 미정'}]에서 [${ + data.spaceEventInfo.spaceTitle + }]으로 변경되었습니다.`; + break; + case 'SPACE_SCHEDULE_CHANGED': + notificationBody = `여행날짜가 [${data.spaceEventInfo.oldDates || '날짜 미정'}]에서 [${ + data.spaceEventInfo.changeDate + }]으로 변경되었습니다.`; + break; + default: + notificationBody = '알 수 없는 이벤트입니다.'; + } + + return notificationBody; +} + self.addEventListener('install', function () { console.log('[FCM SW] 설치중..'); self.skipWaiting(); @@ -9,20 +89,16 @@ self.addEventListener('activate', function () { self.addEventListener('push', function (e) { if (!e.data.json()) return; - const resultData = e.data.json().notification; - // 필수! 알람 제목 - const notificationTitle = resultData.title; + const resultDataString = typeof resultData === 'string' ? resultData : JSON.stringify(resultData); + const parsedResultData = parseNestedJson(resultDataString); + console.log(parsedResultData); + + const notificationTitle = 'TRIPVOTE'; const notificationOptions = { - //필수! 알람 설명 - body: resultData.body, - //필수! 알람 이미지(프로필, 로고) - icon: resultData.image, - //필수! 알람 분류태그 (전체, 투표, 나가기...) - tag: resultData.tag, - ...resultData, + body: createNotificationBody(parsedResultData), + ...parsedResultData, }; - console.log('출력'); self.registration.showNotification(notificationTitle, notificationOptions); }); diff --git a/src/api/notification.ts b/src/api/notification.ts index a29c93a6..848d6d64 100644 --- a/src/api/notification.ts +++ b/src/api/notification.ts @@ -19,3 +19,27 @@ export const GetNotification = async () => { console.log('[notification]알림내용을 가져오지 못했습니다'); } }; + +export const GetAlarm = async () => { + try { + const response = await axios.get('/api/notifications', {withCredentials: true}); + return response; + } catch (error) { + if (axios.isAxiosError(error)) { + console.log(error); + return error.response; + } + } +}; + +export const PostReadAlarm = async (notiId: number) => { + try { + const response = await axios.patch(`/api/notifications/${notiId}/read`, {withCredentials: true}); + return response; + } catch (error) { + if (axios.isAxiosError(error)) { + console.log(error); + return error.response; + } + } +}; diff --git a/src/components/Alarm/Alarm.module.scss b/src/components/Alarm/Alarm.module.scss index eb6f31ad..b79fe437 100644 --- a/src/components/Alarm/Alarm.module.scss +++ b/src/components/Alarm/Alarm.module.scss @@ -1,11 +1,11 @@ -@use "@/sass" as *; +@use '@/sass' as *; .page { position: absolute; top: 0; width: 100%; height: 100vh; - z-index: 9; + z-index: 11; .slide { position: absolute; diff --git a/src/components/Alarm/Alarm.tsx b/src/components/Alarm/Alarm.tsx index 85c8ccf3..90dba224 100644 --- a/src/components/Alarm/Alarm.tsx +++ b/src/components/Alarm/Alarm.tsx @@ -48,7 +48,6 @@ function Alarm({isAlarmOpen, alarmClose}: AlarmProps) { useEffect(() => { if (isAlarmOpen === true) { localStorage.removeItem('news'); - console.log('비움'); } }, [isAlarmOpen]); @@ -69,7 +68,7 @@ function Alarm({isAlarmOpen, alarmClose}: AlarmProps) { }} > - + ); diff --git a/src/components/Alarm/TabCapsule/Content/Content.module.scss b/src/components/Alarm/TabCapsule/Content/Content.module.scss index 16bbfce3..3b009a5a 100644 --- a/src/components/Alarm/TabCapsule/Content/Content.module.scss +++ b/src/components/Alarm/TabCapsule/Content/Content.module.scss @@ -1,4 +1,4 @@ -@use "@/sass" as *; +@use '@/sass' as *; .page { height: calc(100vh - 86px); diff --git a/src/components/Alarm/TabCapsule/Content/Content.tsx b/src/components/Alarm/TabCapsule/Content/Content.tsx index 32585e16..50708f93 100644 --- a/src/components/Alarm/TabCapsule/Content/Content.tsx +++ b/src/components/Alarm/TabCapsule/Content/Content.tsx @@ -1,20 +1,23 @@ -import styles from "./Content.module.scss"; +import styles from './Content.module.scss'; -import formatTimeAgo from "@/utils/formatTimeAgo"; +import DefaultProfile from '@/assets/profile_default.svg?react'; +import formatTimeAgo from '@/utils/formatTimeAgo'; -import { ContentProps } from "@/types/alarm"; +import {ContentProps} from '@/types/alarm'; -function Content({ contents }: ContentProps) { +function Content({contents}: ContentProps) { return (
{contents.map((content, index) => (
- + {content.url ? ( + + ) : ( + + )}

{content.title}

-

- {formatTimeAgo(new Date(content.time))} -

+

{formatTimeAgo(new Date(content.time))}

))} diff --git a/src/components/Alarm/TabCapsule/TabCapsule.tsx b/src/components/Alarm/TabCapsule/TabCapsule.tsx index be2feeee..ddc74b8b 100644 --- a/src/components/Alarm/TabCapsule/TabCapsule.tsx +++ b/src/components/Alarm/TabCapsule/TabCapsule.tsx @@ -1,56 +1,30 @@ import {Tab, TabIndicator, TabList, TabPanel, TabPanels, Tabs} from '@chakra-ui/react'; +import {useNavigate} from 'react-router-dom'; import styles from './TabCapsule.module.scss'; +import {useGetAlarm} from '@/hooks/Notification/useNotification'; + +import {PostReadAlarm} from '@/api/notification'; +import {parsingAlarmTravel} from '@/utils/parsingAlarm'; + import Content from './Content/Content'; -const AllContents = [ - { - url: 'https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg', - title: `[강릉 여행]강자밭님이 "밥집 어디갈꺼야" 투표를 만들었어요.`, - time: '2024-01-11T23:09:00', - }, - { - url: 'https://github.com/Strong-Potato/TripVote-FE/assets/120024673/089a8673-405e-4a06-b173-4cf3e985b466', - title: '개인정보 보호정책이 변경되었습니다.', - time: '2024-01-11T20:00:00', - }, - { - url: 'https://github.com/Strong-Potato/TripVote-FE/assets/120024673/089a8673-405e-4a06-b173-4cf3e985b466', - title: '개인정보 보호정책이 변경되었습니다.', - time: '2024-01-10T10:00:00', - }, - { - url: 'https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg', - title: `[강릉 여행]강자밭님이 "카페 어디갈꺼야" 투표를 만들었어요.`, - time: '2024-01-03T12:00:00', - }, - { - url: 'https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg', - title: `[강릉 여행]강자밭님이 "숙소 어디갈꺼야" 투표를 만들었어요.`, - time: '2023-12-31T12:00:00', - }, -]; +function TabCapsule({isAlarmOpen}: {isAlarmOpen: boolean}) { + const navigate = useNavigate(); + const {data: Alarm} = useGetAlarm(isAlarmOpen); + localStorage.removeItem('news'); + + Alarm && PostReadAlarm(Alarm?.data.data.notificationDetail[0].id); + if (Alarm?.status === 403 || Alarm?.status === 401) { + navigate('/auth/login', { + replace: true, + }); + } -const SpaceTravelContents = [ - { - url: 'https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg', - title: `[강릉 여행]강자밭님이 "밥집 어디갈꺼야" 투표를 만들었어요.`, - time: '2024-01-11T23:05:00', - }, - { - url: 'https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg', - title: `[강릉 여행]강자밭님이 "카페 어디갈꺼야" 투표를 만들었어요.`, - time: '2024-01-03T12:00:00', - }, - { - url: 'https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg', - title: `[강릉 여행]강자밭님이 "숙소 어디갈꺼야" 투표를 만들었어요.`, - time: '2023-12-31T12:00:00', - }, -]; + const AllContents = parsingAlarmTravel(Alarm?.data.data.notificationDetail); + const SpaceTravelContents = parsingAlarmTravel(Alarm?.data.data.notificationDetail); -function TabCapsule() { return ( diff --git a/src/components/Home/TabBar/TabBar.tsx b/src/components/Home/TabBar/TabBar.tsx index 0bf9c875..6b4eb1c0 100644 --- a/src/components/Home/TabBar/TabBar.tsx +++ b/src/components/Home/TabBar/TabBar.tsx @@ -4,8 +4,16 @@ import {Link} from 'react-router-dom'; import styles from './TabBar.module.scss'; -function TabBar() { +import {useGetAlarm} from '@/hooks/Notification/useNotification'; + +import {Alarmprop} from '@/types/alarm'; + +function TabBar({onAlarmOpen}: Alarmprop) { const news = localStorage.getItem('news'); + const {data: Alarm} = useGetAlarm(true); + if (Alarm?.data.data.notificationDetail[0].isRead === false) { + localStorage.setItem('news', 'true'); + } return (
@@ -13,10 +21,10 @@ function TabBar() { - +
); diff --git a/src/components/Modal/Invitation/Invitation.tsx b/src/components/Modal/Invitation/Invitation.tsx index 6c153eaa..4fc0868b 100644 --- a/src/components/Modal/Invitation/Invitation.tsx +++ b/src/components/Modal/Invitation/Invitation.tsx @@ -16,7 +16,7 @@ import {InvitationProps} from '@/types/Invitation'; function Invitation({inviteCode, modal}: InvitationProps) { const [isFull, setIsFull] = useRecoilState(isFullMember); - const [, , removeCookie] = useCookies(['join_space_token']); + const [, setcookie] = useCookies(['join_space_token']); const {data} = useGetMyInfo(true); const navigate = useNavigate(); const parsedInviteCode = parseInviteCode(inviteCode); @@ -33,10 +33,10 @@ function Invitation({inviteCode, modal}: InvitationProps) { const handleJoin = async () => { const response = await postJoin(parsedInviteCode!.space_id); if (response.status === 200) { - removeCookie('join_space_token', {path: '/'}); + setcookie('join_space_token', ''); navigate(`/trip/${parsedInviteCode!.space_id}`); } else if (response.status === 400) { - removeCookie('join_space_token', {path: '/'}); + setcookie('join_space_token', ''); modal(false); setIsFull(true); } @@ -62,7 +62,7 @@ function Invitation({inviteCode, modal}: InvitationProps) { className={styles.wrapperInvalidButton__confirm} onClick={() => { modal(false); - removeCookie('join_space_token', {path: '/'}); + setcookie('join_space_token', ''); }} > 확인 @@ -87,7 +87,7 @@ function Invitation({inviteCode, modal}: InvitationProps) { className={styles.wrapperButton__cancel} onClick={() => { modal(false); - removeCookie('join_space_token', {path: '/'}); + setcookie('join_space_token', ''); }} > 취소 @@ -114,7 +114,7 @@ function Invitation({inviteCode, modal}: InvitationProps) { className={styles.wrapperButton__cancel} onClick={() => { modal(false); - removeCookie('join_space_token', {path: '/'}); + setcookie('join_space_token', ''); }} > 취소 diff --git a/src/components/SideBar/TravelList/TravelList.tsx b/src/components/SideBar/TravelList/TravelList.tsx index 32bcb0d3..668b1ba7 100644 --- a/src/components/SideBar/TravelList/TravelList.tsx +++ b/src/components/SideBar/TravelList/TravelList.tsx @@ -50,7 +50,7 @@ function TravelList({isSideOpen}: TravelListProp) { {spaces.data.spaces.map((item, index) => (
    • - - 구글 크롬(Chrome) 브라우저에 최적화 되어 있어 크롬 브라우저 - 사용을 권장합니다. - + 구글 크롬(Chrome) 브라우저에 최적화 되어 있어 크롬 브라우저 사용을 권장합니다.
    • - - 브라우저의 알림 설정을 켜주셔야 서비스 알림을 받을 수 - 있습니다. - + 브라우저의 알림 설정을 켜주셔야 서비스 알림을 받을 수 있습니다.
    -
  • diff --git a/src/firebase/messaging-init-in-sw.ts b/src/firebase/messaging-init-in-sw.ts index dc71bfa6..48839afb 100644 --- a/src/firebase/messaging-init-in-sw.ts +++ b/src/firebase/messaging-init-in-sw.ts @@ -1,5 +1,5 @@ import {initializeApp} from 'firebase/app'; -import {getMessaging, getToken, onMessage} from 'firebase/messaging'; +import {getMessaging, getToken} from 'firebase/messaging'; import {sendNotificationToken} from '@/api/notification'; @@ -26,13 +26,11 @@ async function requestPermission() { const token = await getToken(messaging, { vapidKey: import.meta.env.VITE_VAPID_KEY, }); + console.log(token); if (token) { await sendNotificationToken({token}); console.log('[FCM]알림 토큰을 전송했습니다'); } else console.log('[FCM]알림 토큰을 얻지 못했습니다'); - onMessage(messaging, (payload) => { - console.log('푸시 알람 메세지 출력', payload); - }); } requestPermission(); diff --git a/src/hooks/Notification/useNotification.ts b/src/hooks/Notification/useNotification.ts new file mode 100644 index 00000000..bd0da73e --- /dev/null +++ b/src/hooks/Notification/useNotification.ts @@ -0,0 +1,14 @@ +import {useQuery} from '@tanstack/react-query'; + +import {GetAlarm} from '@/api/notification'; + +function useGetAlarm(enabled: boolean) { + return useQuery({ + queryKey: ['Alarm'], + queryFn: GetAlarm, + enabled, + retry: 0, + }); +} + +export {useGetAlarm}; diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 8be6a94e..0048ba87 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -1,3 +1,4 @@ +import {useDisclosure} from '@chakra-ui/react'; import {useEffect, useState} from 'react'; import {useCookies} from 'react-cookie'; @@ -5,6 +6,7 @@ import styles from './Home.module.scss'; import useLockBodyScroll from '@/hooks/useLockBodyScroll'; +import Alarm from '@/components/Alarm/Alarm'; import Onboarding from '@/components/Home/Onboarding/Onboarding'; import RecommendedItemList from '@/components/Home/RecommendedItemList/RecommendedItemList'; import RecommendedLocationList from '@/components/Home/RecommendedLocationList/RecommendedLocationList'; @@ -15,6 +17,7 @@ import VoteAtHome from '@/components/Home/VoteAtHome/VoteAtHome'; import Invitation from '@/components/Modal/Invitation/Invitation'; function Home() { + const {isOpen: isAlarmOpen, onOpen: onAlarmOpen, onClose: onAlarmClose} = useDisclosure(); const [onboarding, setOnboarding] = useState(true); const [cookies] = useCookies(['join_space_token']); const [modal, setModal] = useState(false); @@ -35,40 +38,43 @@ function Home() { }, [cookies.join_space_token]); return ( -
    - - - -
    -

    내 여행 일정

    - -
    -
    -
    -

    - 지금 인기있는 여행지는? - {window.innerWidth > 450 ? : 🔥} -

    - -
    -
    -

    - 겨울에 어울리는 포근한 숙소 - {window.innerWidth > 450 ? : } -

    - + <> +
    + + + +
    +

    내 여행 일정

    +
    -
    -

    - 겨울을 신나게 즐기는 쌈박한 방법! - {window.innerWidth > 450 ? : 🎵} -

    - +
    +
    +

    + 지금 인기있는 여행지는? + {window.innerWidth > 450 ? : 🔥} +

    + +
    +
    +

    + 겨울에 어울리는 포근한 숙소 + {window.innerWidth > 450 ? : } +

    + +
    +
    +

    + 겨울을 신나게 즐기는 쌈박한 방법! + {window.innerWidth > 450 ? : 🎵} +

    + +
    + {!onboarding && } + {modal && }
    - {!onboarding && } - {modal && } -
    + + ); } diff --git a/src/pages/Trip/Trip.tsx b/src/pages/Trip/Trip.tsx index b2740e33..457a8778 100644 --- a/src/pages/Trip/Trip.tsx +++ b/src/pages/Trip/Trip.tsx @@ -17,6 +17,7 @@ import {useNavigate, useParams} from 'react-router-dom'; import styles from './Trip.module.scss'; +import {useGetAlarm} from '@/hooks/Notification/useNotification'; import {useGetJourneys, useGetSpace} from '@/hooks/Spaces/space'; import Alarm from '@/components/Alarm/Alarm'; @@ -50,6 +51,10 @@ function Trip() { const [center, setCenter] = useState(getMapCenter(journeysData.data)); const navigate = useNavigate(); const users = spaceData?.data?.members; + const {data: AlarmData} = useGetAlarm(true); + if (AlarmData?.data.data.notificationDetail[0].isRead === false) { + localStorage.setItem('news', 'true'); + } useEffect(() => { console.log('아임센터', center); diff --git a/src/types/alarm.ts b/src/types/alarm.ts index 9da9d9d6..52209cfe 100644 --- a/src/types/alarm.ts +++ b/src/types/alarm.ts @@ -16,3 +16,7 @@ export type AlarmProps = { export type BackProps = { alarmClose: () => void; }; + +export interface Alarmprop { + onAlarmOpen: () => void; +} diff --git a/src/types/notification.ts b/src/types/notification.ts index 346dd5fb..63fa0f1e 100644 --- a/src/types/notification.ts +++ b/src/types/notification.ts @@ -1,3 +1,56 @@ export interface Token { token: string; } + +// interface MemberEventInfo { +// memberId: number; +// memberNickname: string; +// memberImageUrl: string; +// } + +// interface SpaceEventInfo { +// spaceId: number; +// spaceTitle: string; +// oldTitle: string | null; +// oldDates: string | null; +// changeDate: string | null; +// } + +// interface VoteEventInfo { +// voteId?: number; +// voteTitle?: string; +// } + +// interface NotificationInformation { +// topicId: number; +// memberEventInfo: MemberEventInfo; +// spaceEventInfo: SpaceEventInfo; +// voteEventInfo?: VoteEventInfo; +// eventType: NotificationEventType; +// createdAt: string; +// } +type NotificationEventType = + | 'VOTE_CREATED' + | 'VOTE_DONE' + | 'VOTE_DELETED' + | 'MEMBER_INVITED' + | 'MEMBER_EXIT' + | 'NEW_JOURNEY_ADDED' + | 'CANDIDATE_ADDED' + | 'SPACE_LOCATION_CHANGED' + | 'SPACE_SCHEDULE_CHANGED'; + +export interface NotificationDetails { + isRead: boolean; + id: number; + type: NotificationEventType; + notificationInformation: string; + receiverId: number; + createdAt: string; +} + +export interface NotificationData { + url: string; + title: string; + time: string; +} diff --git a/src/utils/parsingAlarm.ts b/src/utils/parsingAlarm.ts new file mode 100644 index 00000000..211d687f --- /dev/null +++ b/src/utils/parsingAlarm.ts @@ -0,0 +1,81 @@ +import {NotificationData, NotificationDetails} from '@/types/notification'; + +export function parsingAlarmTravel(notificationDetails: NotificationDetails[]): NotificationData[] { + const travelNoti: NotificationData[] = []; + + try { + notificationDetails.forEach((event) => { + const notificationInformation = JSON.parse(event.notificationInformation); + const eventType = event.type; + const spaceEventInfo = notificationInformation.spaceEventInfo; + const memberEventInfo = notificationInformation.memberEventInfo; + const voteEventInfo = notificationInformation.voteEventInfo; + const createdAt = notificationInformation.createdAt; + const notificationData = { + url: memberEventInfo.memberImageUrl || '', + title: '', + time: createdAt, + }; + + switch (eventType) { + case 'VOTE_CREATED': + notificationData.title = `[${spaceEventInfo?.spaceTitle || '여행지 미정'}] ${ + memberEventInfo?.memberNickname || '알 수 없음' + } 님이 새로운 투표를 생성했습니다.`; + break; + case 'VOTE_DONE': + notificationData.title = `[${spaceEventInfo?.spaceTitle || '여행지 미정'}] "${ + voteEventInfo?.voteTitle || '' + }" 투표가 최종 확정되었습니다.`; + break; + case 'VOTE_DELETED': + notificationData.title = `[${spaceEventInfo?.spaceTitle || '여행지 미정'}] ${ + memberEventInfo?.memberNickname || '알 수 없음' + } 님이 투표를 삭제했습니다.`; + break; + case 'MEMBER_INVITED': + notificationData.title = `[${spaceEventInfo?.spaceTitle || '여행지 미정'}] 에 ${ + memberEventInfo?.memberNickname || '알 수 없음' + } 님이 초대되었습니다.`; + break; + case 'MEMBER_EXIT': + notificationData.title = `[${spaceEventInfo?.spaceTitle || '여행지 미정'}] 에 ${ + memberEventInfo?.memberNickname || '알 수 없음' + } 님이 여행 스페이스를 나갔습니다.`; + break; + case 'NEW_JOURNEY_ADDED': + notificationData.title = `[${spaceEventInfo?.spaceTitle || '여행지 미정'}] ${ + voteEventInfo?.voteTitle || '' + } 의 후보가 일정에 추가 되었습니다.`; + break; + case 'CANDIDATE_ADDED': + notificationData.title = `[${spaceEventInfo?.spaceTitle || '여행지 미정'}] ${ + memberEventInfo?.memberNickname || '알 수 없음' + } 님이 "${voteEventInfo?.voteTitle || ''}"에 새로운 후보를 등록했습니다.`; + break; + case 'SPACE_LOCATION_CHANGED': + // eslint-disable-next-line no-case-declarations + const oldLocation = spaceEventInfo?.oldTitle || '여행지 미정'; + notificationData.title = `여행지가 [${oldLocation}]에서 [${ + spaceEventInfo?.spaceTitle || '여행지 미정' + }]으로 변경되었습니다.`; + break; + case 'SPACE_SCHEDULE_CHANGED': + // eslint-disable-next-line no-case-declarations + const oldDates = spaceEventInfo?.oldDates || '날짜 미정'; + // eslint-disable-next-line no-case-declarations + const newDates = spaceEventInfo?.changDate || '날짜 미정'; + notificationData.title = `여행날짜가 [${oldDates}]에서 [${newDates}]으로 변경되었습니다.`; + break; + default: + break; + } + + travelNoti.push(notificationData); + }); + } catch (error) { + console.error(`Error parsing notifications: ${error}`); + } + + return travelNoti; +}