Skip to content

Commit

Permalink
FCM 에러 해결 (#469)
Browse files Browse the repository at this point in the history
* refactor: friebase FCM 관리 모듈로 분리

* refactor: Notification 제거 및 구독 함수 수정
  • Loading branch information
hozzijeong committed Oct 20, 2023
1 parent a10fa4f commit 808c947
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 99 deletions.
22 changes: 22 additions & 0 deletions frontend/src/components/mypage/PushAlert/PushToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Toggle from 'components/@common/Toggle';
import usePushAlert from 'hooks/@common/usePushAlert';
import PushStatus from 'models/PushStatus';

const PushToggle = () => {
const pushSupport = PushStatus.getIsSupport();
const notificationDenied = PushStatus.getPermission();
const { currentSubscribe, subscribeAlert, unSubscribeAlert } = usePushAlert();

return (
<Toggle
width={45}
height={20}
toggleOnCallback={subscribeAlert}
toggleOffCallback={unSubscribeAlert}
state={currentSubscribe}
disabled={!pushSupport || notificationDenied === 'denied'}
/>
);
};

export default PushToggle;
17 changes: 5 additions & 12 deletions frontend/src/components/mypage/PushAlert/index.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
import Toggle from 'components/@common/Toggle';
import { Suspense } from 'react';
import { PushAlertContent, PushAlertWrapper, WarnParagraph } from './PushAlert.style';
import usePushAlert from 'hooks/@common/usePushAlert';
import PushStatus from 'models/PushStatus';
import PushToggle from './PushToggle';

const PushAlert = () => {
const { currentSubscribe, subscribeAlert, unSubscribeAlert } = usePushAlert();

const pushSupport = PushStatus.getIsSupport();
const notificationDenied = PushStatus.getPermission();

return (
<PushAlertWrapper>
<PushAlertContent>
<p>리마인더 알림 받기</p>
<Toggle
width={45}
height={20}
toggleOnCallback={subscribeAlert}
toggleOffCallback={unSubscribeAlert}
state={currentSubscribe}
disabled={!pushSupport || notificationDenied === 'denied'}
/>
<Suspense fallback={<div>로딩중잉ㅂ니다?</div>}>
<PushToggle />
</Suspense>
</PushAlertContent>
{!pushSupport && <WarnParagraph>지원하지 않는 브라우저 또는 os입니다.</WarnParagraph>}
{notificationDenied === 'denied' && (
Expand Down
9 changes: 5 additions & 4 deletions frontend/src/hooks/@common/usePushAlert.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import useWebPush from 'hooks/queries/auth/useWebPush';
import FCMMessaging from 'models/FCMMessaging';
import PushStatus from 'models/PushStatus';
import { getCurrentToken } from 'utils/firebase';
import useAddToast from './useAddToast';

const usePushAlert = () => {
Expand All @@ -14,21 +14,22 @@ const usePushAlert = () => {
}

// subscribe를 하지 않으려면 해당 토큰을 제거해야 한다.

const permission = await Notification.requestPermission();
PushStatus.setPermission(permission);

if (permission !== 'granted') {
addToast({ type: 'info', message: '알림이 허용되지 않았습니다', time: 3000 });
PushStatus.setPermission(permission);
return;
}

// TODO: 사용자가 처음 알림 설정을 한 것인지 아니면 기존에
try {
let token = PushStatus.getCurrentToken();

// 이중 throw... 이게 괜찮은걸까?
if (token === null) {
token = await getCurrentToken();
// TODO: 여기서 시간이 좀 걸림;;
token = await FCMMessaging.getCurrentToken();

if (token === null) throw new Error();
}
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/hooks/queries/auth/useWebPush.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useMutation, useQueryClient, useSuspenseQuery } from '@tanstack/react-query';
import useAddToast from 'hooks/@common/useAddToast';
import FCMMessaging from 'models/FCMMessaging';
import PushStatus from 'models/PushStatus';
import WebPushSubscribeAPI, { SUBSCRIBE_URL } from 'apis/webPush';
import { deleteCurrentToken, getCurrentToken } from 'utils/firebase';
import noRetryIfUnauthorized from 'utils/noRetryIfUnauthorized';
import throwOnInvalidStatus from 'utils/throwOnInvalidStatus';

Expand Down Expand Up @@ -34,8 +34,8 @@ const useWebPush = () => {
queryClient.setQueryData([SUBSCRIBE_URL], context?.prevData);
addToast({ type: 'error', message: error.message });

await deleteCurrentToken();
const currentToken = await getCurrentToken();
await FCMMessaging.deleteCurrentToken();
const currentToken = await FCMMessaging.getCurrentToken();
PushStatus.setCurrentToken(currentToken);
},
onSettled: () => {
Expand All @@ -59,8 +59,8 @@ const useWebPush = () => {
return { prevData };
},
onSuccess: async () => {
await deleteCurrentToken();
const currentToken = await getCurrentToken();
await FCMMessaging.deleteCurrentToken();
const currentToken = await FCMMessaging.getCurrentToken();
PushStatus.setCurrentToken(currentToken);
},
onError: (error, __, context) => {
Expand Down
70 changes: 70 additions & 0 deletions frontend/src/models/FCMMessaging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Analytics, getAnalytics } from 'firebase/analytics';
import { FirebaseApp, FirebaseOptions, initializeApp } from 'firebase/app';
import { Messaging, deleteToken, getMessaging, getToken, onMessage } from 'firebase/messaging';

const firebaseConfig = {
apiKey: 'AIzaSyAOEUhyDZ1FQ2Ly77t-TNEqzb-686teUKU',
authDomain: 'pium-7ddfe.firebaseapp.com',
projectId: 'pium-7ddfe',
storageBucket: 'pium-7ddfe.appspot.com',
messagingSenderId: '66938335591',
appId: '1:66938335591:web:88ebf4f7f9dba08031ffc2',
measurementId: 'G-8SL2D547VW',
};

class FCMMessaging {
private app: FirebaseApp | null = null;
private messaging: Messaging | null = null;
private analytics: Analytics | null = null;

constructor(config: FirebaseOptions) {
this.app = initializeApp(config);
this.analytics = getAnalytics(this.app);
}

checkMessaging() {
return this.messaging ? true : false;
}

registerMessaging() {
if (!this.app) throw new Error('메세지 등록을 위해서는 FCM 초기화가 필요합니다.');
this.messaging = getMessaging(this.app);
}

setOnMessaging() {
if (!this.messaging) return;
onMessage(this.messaging, (payload) => {
const { notification } = payload;

const notificationTitle = notification?.title ?? '피움 알림';
const notificationOptions = {
body: notification?.body ?? '내용이 없습니다',
icon: notification?.icon ?? './assets/favicon-32x32.png',
};
const foregroundNotification = new Notification(notificationTitle, notificationOptions);

foregroundNotification.onclick = function (event) {
event.preventDefault();
foregroundNotification.close();
};
});
}

async getCurrentToken() {
if (!this.messaging) throw new Error('등록된 메세지가 없어서 토큰을 반환할 수 없습니다');
const permission = Notification.permission;

if (permission !== 'granted') return null;

return await getToken(this.messaging, {
vapidKey: process.env.VAPID_PUBLIC_KEY ?? '',
});
}

async deleteCurrentToken() {
if (!this.messaging) throw new Error('등록된 메세지가 없어서 토큰을 삭제할 수 없습니다');
return await deleteToken(this.messaging);
}
}

export default new FCMMessaging(firebaseConfig);
8 changes: 4 additions & 4 deletions frontend/src/models/PushStatus.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// TODO: 클래스로 변환해서 사용해보기
import { deleteCurrentToken } from 'utils/firebase';
import FCMMessaging from './FCMMessaging';

type NotificationPermission = 'granted' | 'default' | 'denied';

Expand All @@ -17,7 +17,7 @@ export const isSupported =
const initialPushStatus: PushStatusState = {
pushSupport: isSupported,
currentToken: null,
notificationPermission: Notification.permission,
notificationPermission: 'default',
};

class PushStatus {
Expand All @@ -28,7 +28,7 @@ class PushStatus {

if (pushStatus.notificationPermission !== 'granted') {
this.pushStatus.currentToken = null;
await deleteCurrentToken();
await FCMMessaging.deleteCurrentToken();
}
}

Expand All @@ -47,7 +47,7 @@ class PushStatus {
async setPermission(permission: NotificationPermission) {
if (permission !== 'granted') {
this.pushStatus.currentToken = null;
await deleteCurrentToken();
await FCMMessaging.deleteCurrentToken();
}

this.pushStatus.notificationPermission = permission;
Expand Down
22 changes: 11 additions & 11 deletions frontend/src/registerServiceWork.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import PushStatus, { isSupported } from 'models/PushStatus';
import { getCurrentToken } from 'utils/firebase';
import { isSupported } from 'firebase/messaging';
import FCMMessaging from 'models/FCMMessaging';
import PushStatus, { isSupported as isBrowserSupport } from 'models/PushStatus';

const registerPwaServiceWorker = async (workerPath: string) => {
// 지원하지 않는 브라우저라면 return;
if (!isSupported) {
const FCMSupported = await isSupported();

if (!isBrowserSupport || !FCMSupported) {
return;
}
// 기존에 있던 서비스 워커를 가져옴
Expand All @@ -22,16 +25,13 @@ const registerPwaServiceWorker = async (workerPath: string) => {
}
}

// 새로운 서비스워커로 업데이트
// 초기에 시작할 때 currentToken을 일단 받음
// permission을 물어보지 않아서 getCurrentToken을 받아올 수 없음.

const currentToken = await getCurrentToken();
FCMMessaging.registerMessaging();
FCMMessaging.setOnMessaging();

PushStatus.updatePushStatus({
notificationPermission: Notification.permission,
currentToken,
pushSupport: isSupported,
notificationPermission: 'default',
currentToken: null,
pushSupport: FCMSupported && isBrowserSupport,
});
};

Expand Down
63 changes: 0 additions & 63 deletions frontend/src/utils/firebase.ts

This file was deleted.

0 comments on commit 808c947

Please sign in to comment.