Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 24 additions & 16 deletions hooks/use-push-notification.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import * as Sentry from "@sentry/react-native";
import { api } from '@/lib/api';
import { getApp } from '@react-native-firebase/app';
import { getMessaging, getToken } from '@react-native-firebase/messaging';
import { getMessaging, getToken, onMessage } from '@react-native-firebase/messaging';
import * as Device from 'expo-device';
import * as Notifications from 'expo-notifications';
import { useRouter } from 'expo-router';
import { useEffect } from 'react';
import { Platform } from 'react-native';

// 트래킹 전용 타입 — use-tracking-fcm.ts에서 처리하므로 여기서 제외
const TRACKING_TYPES = new Set(['TRACKING_PHOTO_MILESTONE', 'TRACKING_SUMMIT_REACHED']);

/**
* 앱 시작 시 FCM 토큰을 서버에 등록하고
* 포어그라운드 알림 수신 및 알림 탭 이벤트를 처리하는 훅
*
* 역할 분리:
* - 이 훅: 트래킹 외 모든 FCM 포어그라운드 수신 → 시스템 배너 표시
* - use-tracking-fcm.ts: TRACKING_PHOTO_MILESTONE → 인앱 PhotoWindowBanner
*/
export function usePushNotification(enabled = true): void {
const router = useRouter();
Expand All @@ -20,20 +26,22 @@ export function usePushNotification(enabled = true): void {
if (!enabled) return;
registerFcmToken();

// 포어그라운드 알림 수신 — Firebase SDK 충돌로 배너가 안 뜨는 경우 로컬 알림으로 재표시
// TRACKING_PHOTO_MILESTONE은 use-tracking-fcm.ts에서 별도 처리하므로 제외 (무한 루프 방지)
const foregroundSub = Notifications.addNotificationReceivedListener(
async (notification) => {
const data = notification.request.content.data as PushData | null;
if (data?.type === 'TRACKING_PHOTO_MILESTONE') return;
const { title, body } = notification.request.content;
if (!title && !body) return;
await Notifications.scheduleNotificationAsync({
content: { title: title ?? 'SEMOSAN', body: body ?? '' },
trigger: null,
});
},
);
// 포어그라운드 FCM 수신 — Firebase SDK가 expo-notifications보다 우선하므로 onMessage 사용
// 트래킹 타입은 use-tracking-fcm.ts에서 처리하므로 제외
const fcmMessaging = getMessaging(getApp());
const unsubscribeFcm = onMessage(fcmMessaging, async (remoteMessage) => {
const type = typeof remoteMessage.data?.type === 'string' ? remoteMessage.data.type : undefined;
if (type && TRACKING_TYPES.has(type)) return;

const title = remoteMessage.notification?.title ?? (typeof remoteMessage.data?.title === 'string' ? remoteMessage.data.title : undefined);
const body = remoteMessage.notification?.body ?? (typeof remoteMessage.data?.body === 'string' ? remoteMessage.data.body : undefined);
if (!title && !body) return;

await Notifications.scheduleNotificationAsync({
content: { title: title ?? 'SEMOSAN', body: body ?? '' },
trigger: null,
});
});

// 알림 탭 → 화면 이동
const tapSub = Notifications.addNotificationResponseReceivedListener(
Expand All @@ -51,7 +59,7 @@ export function usePushNotification(enabled = true): void {
});

return () => {
foregroundSub.remove();
unsubscribeFcm();
tapSub.remove();
};
}, [router, enabled]);
Expand Down
Loading