-
Notifications
You must be signed in to change notification settings - Fork 2
[Feature] FCM 연결 및 모달 생성, API 연결 #88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
a97775b
feat: FCM 연결 및 모달 생성, API 연결
yeonjin719 d63ffd1
fix: navigate 삭제
yeonjin719 b8e138a
fix: 안쓰는 import 삭제
yeonjin719 ea72f93
fix: 오타 수정
yeonjin719 3535bc4
fix: 코드 리뷰 피드백 반영
yeonjin719 0fa5d08
fix: 빌드 에러 수정
yeonjin719 d9c050b
fix: 에러 페이지 import 명 변경, default 값 통일
yeonjin719 aa782d9
Merge branch 'develop' into feature/#87
yeonjin719 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| /// <reference lib="webworker" /> | ||
| /* eslint-env serviceworker */ | ||
| /* global firebase importScripts clients */ | ||
| importScripts('https://www.gstatic.com/firebasejs/10.13.2/firebase-app-compat.js'); | ||
| importScripts('https://www.gstatic.com/firebasejs/10.13.2/firebase-messaging-compat.js'); | ||
|
|
||
| firebase.initializeApp({ | ||
| apiKey: 'AIzaSyAjZqK2lhCOeX_P2Sf-_2IGEFlORchcO5w', | ||
| authDomain: 'withtime-ff471.firebaseapp.com', | ||
| projectId: 'withtime-ff471', | ||
| storageBucket: 'withtime-ff471.firebasestorage.app', | ||
| messagingSenderId: '47995224236', | ||
| appId: '1:47995224236:web:85371605ce4a6659529f09', | ||
| measurementId: 'G-5E8Q23LL4H', | ||
| }); | ||
|
|
||
| const messaging = firebase.messaging(); | ||
|
|
||
| self.addEventListener('push', function (event) { | ||
| try { | ||
| const payload = event.data.json(); | ||
| const title = payload.notification.title; | ||
|
|
||
| const options = { | ||
| body: payload.notification.body, | ||
| icon: payload.notification.icon, | ||
| data: payload.notification.click_action, | ||
| }; | ||
|
|
||
| event.waitUntil(self.registration.showNotification(title, options)); | ||
| } catch (error) { | ||
| console.error('Push event error:', error); | ||
| } | ||
| }); | ||
|
|
||
| self.addEventListener('notificationclick', function (event) { | ||
| console.log(event.notification); | ||
|
|
||
| event.notification.close(); | ||
|
|
||
| event.waitUntil(clients.openWindow(event.notification.data).catch((error) => console.error('Failed to open window:', error))); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import type { TRequestGetAlarm, TRequestPostDeviceToken, TResponseGetAlarm, TResponsePostDeviceToken } from '@/types/alarm/alarm'; | ||
|
|
||
| import { axiosInstance } from '../axiosInstance'; | ||
|
|
||
| export const getAlarm = async ({ size = 5, cursor }: TRequestGetAlarm): Promise<TResponseGetAlarm> => { | ||
| const { data } = await axiosInstance.get('/api/v1/alarms', { | ||
| params: { | ||
| size, | ||
| cursor, | ||
| }, | ||
| }); | ||
| return data; | ||
| }; | ||
|
|
||
| export const postDeviceToken = async ({ deviceToken }: TRequestPostDeviceToken): Promise<TResponsePostDeviceToken> => { | ||
| const { data } = await axiosInstance.post('/api/v1/alarms/device-tokens', { deviceToken }); | ||
| return data; | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import type { TAlarm } from '@/types/alarm/alarm'; | ||
|
|
||
| import ChevronForward from '@/assets/icons/default_arrows/chevron_forward.svg?react'; | ||
|
|
||
| function Alarm({ title }: TAlarm) { | ||
yeonjin719 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return ( | ||
| <div className="flex items-center justify-between w-full py-[24px] border-b-[2px] border-b-default-gray-400"> | ||
| <div className="text-default-gray-800 text-[22px] sm:w-[500px] w-[200px] whitespace-nowrap overflow-hidden text-ellipsis">{title}</div> | ||
| <ChevronForward className="self-center" /> | ||
| </div> | ||
| ); | ||
yeonjin719 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| export default Alarm; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import { useEffect } from 'react'; | ||
| import { useInView } from 'react-intersection-observer'; | ||
| import ClipLoader from 'react-spinners/ClipLoader'; | ||
|
|
||
| import { useGetAlarm } from '@/hooks/alarm/useGetAlarm'; | ||
|
|
||
| import Alarm from '../alarmModal/alarm'; | ||
| import Modal from '../common/modal'; | ||
|
|
||
| import ErrorComponent from '@/pages/common/Error'; | ||
|
|
||
| type TAlarmModalProps = { | ||
| onClose: () => void; | ||
| }; | ||
|
|
||
| function AlarmModal({ onClose }: TAlarmModalProps) { | ||
| const { data, fetchNextPage, isFetching, hasNextPage, error } = useGetAlarm({ size: 5 }); | ||
| const { ref, inView } = useInView({ | ||
| threshold: 0, | ||
| }); | ||
|
|
||
| useEffect(() => { | ||
| if (inView) { | ||
| if (!isFetching && hasNextPage) { | ||
| fetchNextPage(); | ||
| } | ||
| } | ||
| }, [inView, isFetching, hasNextPage, fetchNextPage]); | ||
|
|
||
| if (error) { | ||
| return <ErrorComponent />; | ||
| } | ||
|
|
||
| return ( | ||
| <Modal title="알림" onClose={onClose} position="main"> | ||
| <div className="mt-[5px] flex flex-col items-center justify-center sm:w-[600px] w-[300px] px-[28px] max-h-[300px] overflow-y-scroll"> | ||
| {(!data?.pages?.length || data.pages.every((page) => page.result.alarmList.length === 0)) && ( | ||
| <div className="text-center flex text-default-gray-700 font-heading3 py-8 h-fit">아직 알림이 없습니다</div> | ||
| )} | ||
| {data?.pages.map((alarmList) => alarmList.result.alarmList.map((alarm) => <Alarm key={alarm.id} {...alarm} />))} | ||
| <div ref={ref} className="flex w-full justify-center mt-[10px] h-[1px]"> | ||
| {isFetching && hasNextPage && <ClipLoader />} | ||
| </div> | ||
| </div> | ||
| </Modal> | ||
| ); | ||
| } | ||
|
|
||
| export default AlarmModal; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| // src/firebase/firebase.ts | ||
| import { initializeApp } from 'firebase/app'; | ||
| import { getMessaging, getToken } from 'firebase/messaging'; | ||
|
|
||
| const firebaseConfig = { | ||
| apiKey: import.meta.env.VITE_FIREBASE_API_KEY, | ||
| authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, | ||
| projectId: import.meta.env.VITE_PROJECT_ID, | ||
| storageBucket: import.meta.env.VITE_STORAGE_BUCKET, | ||
| messagingSenderId: import.meta.env.VITE_MESSAGE_SENDER_ID, | ||
| appId: import.meta.env.VITE_APP_ID, | ||
| measurementId: import.meta.env.VITE_MEASUREMENT_ID, | ||
| }; | ||
yeonjin719 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| const app = initializeApp(firebaseConfig); | ||
| const messaging = getMessaging(app); | ||
|
|
||
| export const generateToken = async () => { | ||
| try { | ||
| const token = await getToken(messaging, { | ||
| vapidKey: import.meta.env.VITE_FIREBASE_VAPID_KEY, | ||
| }); | ||
| if (!token) { | ||
| console.warn('FCM 토큰 생성에 실패했습니다. 알림 권한을 확인해주세요.'); | ||
| } | ||
| return token; | ||
| } catch (error) { | ||
| console.error('FCM 토큰 생성 중 오류 발생:', error); | ||
| return null; | ||
| } | ||
| }; | ||
|
|
||
| export const registerServiceWorker = async () => { | ||
| try { | ||
| if ('serviceWorker' in navigator) { | ||
| await navigator.serviceWorker.register('/firebase-messaging-sw.js'); | ||
| } | ||
| } catch (err) { | ||
| console.error('Service Worker registration failed:', err); | ||
| } | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| // src/hooks/alarm/useDeviceToken.ts | ||
| import { useEffect } from 'react'; | ||
| import { isSupported } from 'firebase/messaging'; | ||
|
|
||
| import { postDeviceToken } from '@/api/alarm/alarm'; // 서버에 FCM 토큰 전송하는 API 함수 | ||
| import { generateToken, registerServiceWorker } from '@/firebase/firebase'; | ||
|
|
||
| export const useDeviceToken = () => { | ||
| useEffect(() => { | ||
| const setupFCM = async () => { | ||
| if (!(await isSupported())) { | ||
| console.warn('FCM은 현재 브라우저에서 지원되지 않습니다.'); | ||
| return; | ||
| } | ||
|
|
||
| await registerServiceWorker(); | ||
| const token = await generateToken(); | ||
|
|
||
| if (token) { | ||
| try { | ||
| await postDeviceToken({ deviceToken: token }); // 서버에 전송 | ||
| } catch (err) { | ||
| console.error('디바이스 토큰 서버 전송 실패:', err); | ||
| } | ||
| } | ||
yeonjin719 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| const handleClick = () => { | ||
| setupFCM(); | ||
| window.removeEventListener('click', handleClick); | ||
| }; | ||
|
|
||
| window.addEventListener('click', handleClick); | ||
|
|
||
| return () => { | ||
| window.removeEventListener('click', handleClick); | ||
| }; | ||
| }, []); | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import { useInfiniteQuery } from '@tanstack/react-query'; | ||
|
|
||
| import type { TRequestGetAlarm } from '@/types/alarm/alarm'; | ||
|
|
||
| import { getAlarm } from '@/api/alarm/alarm'; | ||
| import { alarmKeys } from '@/queryKey/queryKey'; | ||
|
|
||
| export const useGetAlarm = ({ cursor, size }: TRequestGetAlarm) => { | ||
| return useInfiniteQuery({ | ||
| queryKey: alarmKeys.getAlarm(size ?? 5, cursor).queryKey, | ||
| queryFn: ({ pageParam = cursor }) => getAlarm({ cursor: pageParam, size: size ?? 5 }), | ||
| initialPageParam: cursor, | ||
| getNextPageParam: (lastPage) => lastPage.result.cursor ?? undefined, | ||
| }); | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.