- FCMProvider 컨텍스트 및 useFCM 훅 추가 - Capacitor FCM 플러그인 통합 - 알림 사운드 파일 추가 (default.wav, push_notification.wav) - Firebase 메시징 패키지 의존성 추가
138 lines
3.3 KiB
TypeScript
138 lines
3.3 KiB
TypeScript
'use client';
|
|
|
|
/**
|
|
* FCM 푸시 알림 훅
|
|
*
|
|
* Capacitor 네이티브 앱에서 FCM 푸시 알림을 처리합니다.
|
|
* - 로그인 상태에서 자동으로 FCM 초기화
|
|
* - 포그라운드 알림을 sonner 토스트로 표시
|
|
* - 로그아웃 시 FCM 토큰 해제
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* // FCMProvider에서 사용
|
|
* function FCMProvider({ children }) {
|
|
* useFCM();
|
|
* return <>{children}</>;
|
|
* }
|
|
* ```
|
|
*/
|
|
|
|
import { useEffect, useRef, useCallback } from 'react';
|
|
import { toast } from 'sonner';
|
|
import {
|
|
initializeFCM,
|
|
unregisterFCMToken,
|
|
isCapacitorNative,
|
|
getToastTypeByNotificationType,
|
|
type FCMNotification,
|
|
type ToastType,
|
|
} from '@/lib/capacitor/fcm';
|
|
import { hasAuthToken } from '@/lib/api/auth-headers';
|
|
|
|
export function useFCM() {
|
|
const initialized = useRef(false);
|
|
|
|
/**
|
|
* 포그라운드 알림 핸들러 (sonner 토스트)
|
|
*/
|
|
const handleForegroundNotification = useCallback((notification: FCMNotification) => {
|
|
const { title, body, data } = notification;
|
|
const type = data?.type;
|
|
const url = data?.url;
|
|
|
|
// 타입별 토스트 스타일 결정
|
|
const toastType: ToastType = getToastTypeByNotificationType(type);
|
|
|
|
// 토스트 옵션
|
|
const toastOptions = {
|
|
description: body,
|
|
duration: 5000,
|
|
action: url
|
|
? {
|
|
label: '보기',
|
|
onClick: () => {
|
|
window.location.href = url;
|
|
},
|
|
}
|
|
: undefined,
|
|
};
|
|
|
|
// 타입별 토스트 표시
|
|
const toastTitle = title || '알림';
|
|
|
|
switch (toastType) {
|
|
case 'error':
|
|
toast.error(toastTitle, toastOptions);
|
|
break;
|
|
case 'warning':
|
|
toast.warning(toastTitle, toastOptions);
|
|
break;
|
|
case 'success':
|
|
toast.success(toastTitle, toastOptions);
|
|
break;
|
|
default:
|
|
toast.info(toastTitle, toastOptions);
|
|
}
|
|
}, []);
|
|
|
|
/**
|
|
* FCM 초기화
|
|
*/
|
|
useEffect(() => {
|
|
// 네이티브 환경이 아니면 무시
|
|
if (!isCapacitorNative()) {
|
|
console.log('[useFCM] Not in native environment, skipping');
|
|
return;
|
|
}
|
|
|
|
// 이미 초기화됐으면 무시
|
|
if (initialized.current) {
|
|
return;
|
|
}
|
|
|
|
// 로그인 상태가 아니면 무시
|
|
if (!hasAuthToken()) {
|
|
console.log('[useFCM] No auth token, skipping FCM initialization');
|
|
return;
|
|
}
|
|
|
|
// FCM 초기화
|
|
initialized.current = true;
|
|
console.log('[useFCM] Initializing FCM...');
|
|
|
|
initializeFCM(handleForegroundNotification).then((success) => {
|
|
if (success) {
|
|
console.log('[useFCM] FCM initialized successfully');
|
|
} else {
|
|
console.log('[useFCM] FCM initialization failed or skipped');
|
|
initialized.current = false;
|
|
}
|
|
});
|
|
}, [handleForegroundNotification]);
|
|
|
|
/**
|
|
* FCM 토큰 해제 (로그아웃 시 호출)
|
|
*/
|
|
const cleanup = useCallback(async () => {
|
|
console.log('[useFCM] Cleaning up FCM...');
|
|
await unregisterFCMToken();
|
|
initialized.current = false;
|
|
}, []);
|
|
|
|
return { cleanup };
|
|
}
|
|
|
|
/**
|
|
* FCM cleanup 함수만 반환하는 훅
|
|
* (로그아웃 로직에서 사용)
|
|
*/
|
|
export function useFCMCleanup() {
|
|
const cleanup = useCallback(async () => {
|
|
if (!isCapacitorNative()) return;
|
|
await unregisterFCMToken();
|
|
}, []);
|
|
|
|
return { cleanup };
|
|
}
|