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) => (