From ce3fcfb91ebdcf80f34d7562336d145d9b8d4e2e Mon Sep 17 00:00:00 2001 From: HOOOO98 Date: Thu, 25 Jan 2024 16:16:34 +0900 Subject: [PATCH 1/9] Feat: connect alarm with home #165 --- src/components/Alarm/Alarm.module.scss | 4 +- src/components/Home/TabBar/TabBar.tsx | 9 ++-- src/pages/Home/Home.tsx | 68 ++++++++++++++------------ 3 files changed, 45 insertions(+), 36 deletions(-) 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/Home/TabBar/TabBar.tsx b/src/components/Home/TabBar/TabBar.tsx index 0bf9c875..7adfae5b 100644 --- a/src/components/Home/TabBar/TabBar.tsx +++ b/src/components/Home/TabBar/TabBar.tsx @@ -4,7 +4,10 @@ import {Link} from 'react-router-dom'; import styles from './TabBar.module.scss'; -function TabBar() { +interface Alarmprop { + onAlarmOpen: () => void; +} +function TabBar({onAlarmOpen}: Alarmprop) { const news = localStorage.getItem('news'); return ( @@ -13,10 +16,10 @@ function TabBar() { - + ); 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 && } -
+ + ); } From f56164a5a54955a8adf6adb4eddfd8683cd64be3 Mon Sep 17 00:00:00 2001 From: HOOOO98 Date: Thu, 25 Jan 2024 20:33:23 +0900 Subject: [PATCH 2/9] Feat: get token and post token server connect FCM #165 --- public/firebase-messaging-sw.js | 2 +- src/firebase/messaging-init-in-sw.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/public/firebase-messaging-sw.js b/public/firebase-messaging-sw.js index 16740290..43d71b0d 100644 --- a/public/firebase-messaging-sw.js +++ b/public/firebase-messaging-sw.js @@ -22,7 +22,7 @@ self.addEventListener('push', function (e) { tag: resultData.tag, ...resultData, }; - console.log('출력'); + console.log('출력', new Date()); // GET 알림 불러오기 갱신 self.registration.showNotification(notificationTitle, notificationOptions); }); diff --git a/src/firebase/messaging-init-in-sw.ts b/src/firebase/messaging-init-in-sw.ts index dc71bfa6..8c56f4a5 100644 --- a/src/firebase/messaging-init-in-sw.ts +++ b/src/firebase/messaging-init-in-sw.ts @@ -1,3 +1,4 @@ +import axios from 'axios'; import {initializeApp} from 'firebase/app'; import {getMessaging, getToken, onMessage} from 'firebase/messaging'; @@ -26,10 +27,13 @@ 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]알림 토큰을 얻지 못했습니다'); + + await axios.post('/api/notifications/subscribe', {isGlobal: false, spaceId: 1}, {withCredentials: true}); onMessage(messaging, (payload) => { console.log('푸시 알람 메세지 출력', payload); }); From 9fd291cd0eeb13959f8a32cb49e06c1daea3072c Mon Sep 17 00:00:00 2001 From: HOOOO98 Date: Fri, 26 Jan 2024 21:20:53 +0900 Subject: [PATCH 3/9] Feat: add connect alarm api --- public/firebase-messaging-sw.js | 4 +- src/components/User/MypageList/MypageList.tsx | 52 ++++++++----------- src/firebase/messaging-init-in-sw.ts | 8 +-- 3 files changed, 26 insertions(+), 38 deletions(-) diff --git a/public/firebase-messaging-sw.js b/public/firebase-messaging-sw.js index 43d71b0d..0384755a 100644 --- a/public/firebase-messaging-sw.js +++ b/public/firebase-messaging-sw.js @@ -14,11 +14,11 @@ self.addEventListener('push', function (e) { // 필수! 알람 제목 const notificationTitle = resultData.title; const notificationOptions = { + // 푸시 알람 받아오는 데이터 파싱 (코드에 따라서) //필수! 알람 설명 body: resultData.body, - //필수! 알람 이미지(프로필, 로고) + //필수! 알림 메세지 파싱 icon: resultData.image, - //필수! 알람 분류태그 (전체, 투표, 나가기...) tag: resultData.tag, ...resultData, }; diff --git a/src/components/User/MypageList/MypageList.tsx b/src/components/User/MypageList/MypageList.tsx index 7fcbc998..0f9ab63d 100644 --- a/src/components/User/MypageList/MypageList.tsx +++ b/src/components/User/MypageList/MypageList.tsx @@ -1,67 +1,61 @@ -import { useState } from "react"; -import { RiArrowRightSLine } from "react-icons/ri"; -import { useNavigate } from "react-router-dom"; +import axios from 'axios'; +import {useState} from 'react'; +import {RiArrowRightSLine} from 'react-icons/ri'; +import {useNavigate} from 'react-router-dom'; -import styles from "./MypageList.module.scss"; +import styles from './MypageList.module.scss'; -import AlertIcon from "@/assets/icons/error-warning-line.svg?react"; +import AlertIcon from '@/assets/icons/error-warning-line.svg?react'; function MypageList() { - const [alertOn, setAlertOn] = useState( - Notification.permission === "granted" ? true : false, - ); + const [alertOn, setAlertOn] = useState(Notification.permission === 'granted' ? true : false); const navigate = useNavigate(); - const onClickAlert = () => { - if (Notification.permission === "denied") { + const onClickAlert = async () => { + if (Notification.permission === 'denied') { + console.log(Notification.permission); return; } - - setAlertOn(!alertOn); + if (alertOn) { + setAlertOn(false); + await axios.post('/api/notifications/subscribe'); + } else { + setAlertOn(true); + await axios.post('/api/notifications/unsubscribe'); + } }; return (
  • { - navigate("/user/privacy"); + navigate('/user/privacy'); }} >
    계정 관리
    - +
  • 알림
    -
    • - - 구글 크롬(Chrome) 브라우저에 최적화 되어 있어 크롬 브라우저 - 사용을 권장합니다. - + 구글 크롬(Chrome) 브라우저에 최적화 되어 있어 크롬 브라우저 사용을 권장합니다.
    • - - 브라우저의 알림 설정을 켜주셔야 서비스 알림을 받을 수 - 있습니다. - + 브라우저의 알림 설정을 켜주셔야 서비스 알림을 받을 수 있습니다.
    -
  • diff --git a/src/firebase/messaging-init-in-sw.ts b/src/firebase/messaging-init-in-sw.ts index 8c56f4a5..48839afb 100644 --- a/src/firebase/messaging-init-in-sw.ts +++ b/src/firebase/messaging-init-in-sw.ts @@ -1,6 +1,5 @@ -import axios from 'axios'; import {initializeApp} from 'firebase/app'; -import {getMessaging, getToken, onMessage} from 'firebase/messaging'; +import {getMessaging, getToken} from 'firebase/messaging'; import {sendNotificationToken} from '@/api/notification'; @@ -32,11 +31,6 @@ async function requestPermission() { await sendNotificationToken({token}); console.log('[FCM]알림 토큰을 전송했습니다'); } else console.log('[FCM]알림 토큰을 얻지 못했습니다'); - - await axios.post('/api/notifications/subscribe', {isGlobal: false, spaceId: 1}, {withCredentials: true}); - onMessage(messaging, (payload) => { - console.log('푸시 알람 메세지 출력', payload); - }); } requestPermission(); From 7140a3dd68e7424890cb9e954cc1c6631efdcbdb Mon Sep 17 00:00:00 2001 From: HOOOO98 Date: Sat, 27 Jan 2024 01:22:13 +0900 Subject: [PATCH 4/9] Feat: connect alarm api #165 --- public/firebase-messaging-sw.js | 94 ++++++++++++++++--- .../SideBar/TravelList/TravelList.tsx | 4 +- src/components/User/MypageList/MypageList.tsx | 4 +- 3 files changed, 88 insertions(+), 14 deletions(-) diff --git a/public/firebase-messaging-sw.js b/public/firebase-messaging-sw.js index 0384755a..525a9ab2 100644 --- a/public/firebase-messaging-sw.js +++ b/public/firebase-messaging-sw.js @@ -1,3 +1,79 @@ +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; + 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 = `여행지가 []에서 []으로 변경되었습니다.`; + break; + case 'SPACE_SCHEDULE_CHANGED': + notificationBody = `여행날짜가 []에서 []으로 변경되었습니다.`; + break; + default: + notificationBody = '알 수 없는 이벤트입니다.'; + } + + return notificationBody; +} + self.addEventListener('install', function () { console.log('[FCM SW] 설치중..'); self.skipWaiting(); @@ -9,20 +85,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('출력', new Date()); // GET 알림 불러오기 갱신 self.registration.showNotification(notificationTitle, notificationOptions); }); diff --git a/src/components/SideBar/TravelList/TravelList.tsx b/src/components/SideBar/TravelList/TravelList.tsx index 32bcb0d3..9eb70dba 100644 --- a/src/components/SideBar/TravelList/TravelList.tsx +++ b/src/components/SideBar/TravelList/TravelList.tsx @@ -50,7 +50,9 @@ function TravelList({isSideOpen}: TravelListProp) { {spaces.data.spaces.map((item, index) => (
); diff --git a/src/components/Alarm/TabCapsule/TabCapsule.tsx b/src/components/Alarm/TabCapsule/TabCapsule.tsx index be2feeee..3ade6745 100644 --- a/src/components/Alarm/TabCapsule/TabCapsule.tsx +++ b/src/components/Alarm/TabCapsule/TabCapsule.tsx @@ -1,7 +1,11 @@ import {Tab, TabIndicator, TabList, TabPanel, TabPanels, Tabs} from '@chakra-ui/react'; +import {useNavigate} from 'react-router-dom'; +// import {useNavigate} from 'react-router-dom'; import styles from './TabCapsule.module.scss'; +import {useGetAlarm} from '@/hooks/Notification/useNotification'; + import Content from './Content/Content'; const AllContents = [ @@ -50,7 +54,17 @@ const SpaceTravelContents = [ }, ]; -function TabCapsule() { +function TabCapsule({isAlarmOpen}: {isAlarmOpen: boolean}) { + const {data: Alarm} = useGetAlarm(isAlarmOpen); + console.log(Alarm); + const navigate = useNavigate(); + + if (Alarm?.status === 403 || Alarm?.status === 401) { + navigate('/auth/login', { + replace: true, + }); + } + return ( diff --git a/src/components/SideBar/TravelList/TravelList.tsx b/src/components/SideBar/TravelList/TravelList.tsx index 9eb70dba..668b1ba7 100644 --- a/src/components/SideBar/TravelList/TravelList.tsx +++ b/src/components/SideBar/TravelList/TravelList.tsx @@ -50,9 +50,7 @@ function TravelList({isSideOpen}: TravelListProp) { {spaces.data.spaces.map((item, index) => (