-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #150 from ALLREVA/ARV-207-feat/alarm
[ARV-207] Web Push 알림 및 PWA 구현
- Loading branch information
Showing
14 changed files
with
859 additions
and
2 deletions.
There are no files selected for viewing
This file contains 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 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 @@ | ||
{ | ||
"short_name": "ALLREVA", | ||
"name": "ALLREVA: 차량 대절 서비스", | ||
"icons": [ | ||
{ "src": "/favicon.png", "sizes": "96x96", "type": "image/x-icon" }, | ||
{ "src": "/logo192x192.png", "sizes": "192x192", "type": "image/png" }, | ||
{ "src": "/logo512x512.png", "sizes": "512x512", "type": "image/png" } | ||
], | ||
"start_url": "/", | ||
"display": "standalone", | ||
"orientation": "portrait", | ||
"theme_color": "#000000", | ||
"background_color": "#ffffff" | ||
} |
This file contains 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 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,32 @@ | ||
self.addEventListener('install', function (e) { | ||
console.log('fcm sw install..'); | ||
self.skipWaiting(); | ||
}); | ||
|
||
self.addEventListener('activate', function (e) { | ||
console.log('fcm sw activate..'); | ||
}); | ||
|
||
self.addEventListener('push', function (e) { | ||
console.log('push: ', e.data.json()); | ||
if (!e.data.json()) return; | ||
|
||
const resultData = e.data.json().notification; | ||
const notificationTitle = resultData.title; | ||
const notificationOptions = { | ||
body: resultData.body, | ||
icon: resultData.image, | ||
tag: resultData.tag, | ||
...resultData, | ||
}; | ||
console.log('push: ', { resultData, notificationTitle, notificationOptions }); | ||
|
||
self.registration.showNotification(notificationTitle, notificationOptions); | ||
}); | ||
|
||
self.addEventListener('notificationclick', function (event) { | ||
console.log('notification click'); | ||
const url = '/'; | ||
event.notification.close(); | ||
event.waitUntil(clients.openWindow(url)); | ||
}); |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains 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 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 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,62 @@ | ||
import { initializeApp } from 'firebase/app'; | ||
import { getMessaging, getToken, onMessage } from 'firebase/messaging'; | ||
|
||
import { endPoint } from 'constants/endPoint'; | ||
import { tokenAxios } from 'utils/axios'; | ||
|
||
const firebaseConfig = { | ||
apiKey: import.meta.env.VITE_API_KEY, | ||
authDomain: 'allreva-86be8.firebaseapp.com', | ||
projectId: 'allreva-86be8', | ||
storageBucket: 'allreva-86be8.firebasestorage.app', | ||
messagingSenderId: import.meta.env.VITE_MESSAGING_SENDER_ID, | ||
appId: import.meta.env.VITE_APP_ID, | ||
measurementId: import.meta.env.VITE_MEASUREMENT_ID, | ||
}; | ||
|
||
// Initialize Firebase | ||
const app = initializeApp(firebaseConfig); | ||
const messaging = getMessaging(app); | ||
|
||
if ('serviceWorker' in navigator) { | ||
navigator.serviceWorker | ||
.register('/firebase-messaging-sw.js') | ||
.then((registration) => { | ||
console.log('Service Worker registered with scope:', registration.scope); | ||
}) | ||
.catch((err) => { | ||
console.error('Service Worker registration failed:', err); | ||
}); | ||
} | ||
|
||
export async function requestPermission() { | ||
console.log('권한 요청 중...'); | ||
|
||
const permission = await Notification.requestPermission(); | ||
if (permission === 'denied') { | ||
console.log('알림 권한이 허용되지 않았습니다'); | ||
return; | ||
} | ||
|
||
console.log('알림 권한이 허용되었습니다'); | ||
|
||
const token = await getToken(messaging, { | ||
vapidKey: import.meta.env.VITE_VAPID_KEY, | ||
}); | ||
|
||
if (token) { | ||
console.log('token: ', token); | ||
|
||
try { | ||
await tokenAxios.post(endPoint.GET_NOTIFICATION_TOKEN, { deviceToken: token }); | ||
} catch (error) { | ||
console.error('토큰 전송에 실패했습니다', error); | ||
} | ||
} else { | ||
console.log('token을 얻을 수 없습니다'); | ||
} | ||
|
||
onMessage(messaging, (payload) => { | ||
console.log('메시지가 도착했습니다.', payload); | ||
}); | ||
} |
This file contains 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,54 @@ | ||
import styled from '@emotion/styled'; | ||
import { useQuery } from '@tanstack/react-query'; | ||
|
||
import NotificationItem from './components/NotificationItem'; | ||
|
||
import { endPoint } from 'constants/endPoint'; | ||
import { tokenAxios } from 'utils'; | ||
|
||
export interface Notification { | ||
createdAt: string; | ||
updatedAt: string; | ||
deletedAt: string; | ||
id: number; | ||
title: string; | ||
message: string; | ||
recipientId: number; | ||
read: boolean; | ||
} | ||
|
||
interface NotificationResult { | ||
timeStamp: string; | ||
code: string; | ||
message: string; | ||
result: Notification[]; | ||
} | ||
|
||
const Notification = () => { | ||
const getNotification = async () => { | ||
const response = await tokenAxios.get(endPoint.GET_NOTIFICATIONS); | ||
|
||
return response.data.result; | ||
}; | ||
|
||
const { data } = useQuery<Notification[]>({ | ||
queryKey: ['notifications'], | ||
queryFn: () => getNotification(), | ||
}); | ||
console.log(data); | ||
return ( | ||
<NotificationContainer> | ||
{data?.map((item) => <NotificationItem key={item.id} notification={item} />)} | ||
</NotificationContainer> | ||
); | ||
}; | ||
|
||
const NotificationContainer = styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
padding: 2.4rem; | ||
gap: 1.6rem; | ||
height: 100%; | ||
`; | ||
|
||
export default Notification; |
This file contains 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,96 @@ | ||
import styled from '@emotion/styled'; | ||
import { useMutation, useQueryClient } from '@tanstack/react-query'; | ||
import { TbBell } from 'react-icons/tb'; | ||
|
||
import type { Notification } from '../Notification'; | ||
|
||
import { endPoint } from 'constants/endPoint'; | ||
import { BodyRegularText, ChipText, TitleText2 } from 'styles/Typography'; | ||
import { formatFromNowDate, tokenAxios } from 'utils'; | ||
|
||
interface NotificationItemProps { | ||
notification: Notification; | ||
} | ||
|
||
const NotificationItem = ({ notification }: NotificationItemProps) => { | ||
const queryClient = useQueryClient(); | ||
|
||
const { mutate: patchRead } = useMutation({ | ||
mutationFn: (notificationId: number) => | ||
tokenAxios.patch(`${endPoint.PATCH_NOTIFICATION_READ}`, { | ||
id: notificationId, | ||
}), | ||
onSuccess: () => { | ||
void queryClient.invalidateQueries({ queryKey: ['notifications'] }); | ||
}, | ||
}); | ||
|
||
const handlePatchRead = () => { | ||
if (!notification.read) { | ||
patchRead(notification.id); | ||
console.log(notification.id); | ||
} | ||
}; | ||
|
||
return ( | ||
<NotificationItemContainer onClick={handlePatchRead}> | ||
<NotificationIcon> | ||
<TbBellIcon size={28} /> | ||
</NotificationIcon> | ||
<ContentWrapper> | ||
<NotificationTitle> | ||
<TitleText2>{notification.title}</TitleText2> | ||
{!notification.read && <NotificationDot />} | ||
</NotificationTitle> | ||
<BodyRegularText>{notification.message}</BodyRegularText> | ||
<ChipText>{formatFromNowDate(notification.createdAt)}</ChipText> | ||
</ContentWrapper> | ||
</NotificationItemContainer> | ||
); | ||
}; | ||
|
||
const NotificationItemContainer = styled.div` | ||
display: flex; | ||
align-items: center; | ||
gap: 1.6rem; | ||
min-height: 14rem; | ||
border-radius: 16px; | ||
background-color: ${({ theme }) => theme.colors.dark[700]}; | ||
padding: 1.2rem 2.2rem; | ||
cursor: pointer; | ||
`; | ||
|
||
const NotificationIcon = styled.div` | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
width: 5rem; | ||
height: 5rem; | ||
border-radius: 14px; | ||
background-color: ${({ theme }) => theme.colors.dark[100]}; | ||
`; | ||
|
||
const TbBellIcon = styled(TbBell)` | ||
fill: ${({ theme }) => theme.colors.dark[300]}; | ||
color: ${({ theme }) => theme.colors.dark[300]}; | ||
`; | ||
|
||
const NotificationTitle = styled.div` | ||
display: flex; | ||
gap: 0.6rem; | ||
align-items: center; | ||
`; | ||
|
||
const NotificationDot = styled.div` | ||
width: 0.8rem; | ||
height: 0.8rem; | ||
border-radius: 100%; | ||
background-color: ${({ theme }) => theme.colors.red}; | ||
`; | ||
|
||
const ContentWrapper = styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
`; | ||
|
||
export default NotificationItem; |
This file contains 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 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.