"use client"; // Capacitor 네이티브 앱 자동 푸시 등록 — UI 없음, 백그라운드 동작. // 로그인 후 첫 페이지 로드 시 자동으로: // 1. 안드로이드 시스템 알림 권한 prompt (POST_NOTIFICATIONS) // 2. 권한 허용되면 FCM 토큰 발급 // 3. 백엔드 /api/m/push/fcm-token 에 등록 // 4. 새 상품/공지 알림이 자동 도착 시작 // // 일반 웹 브라우저(Capacitor 아님)에서는 아무것도 안 함 — 회원정보의 PushOptIn 카드 사용. import { useEffect, useRef } from "react"; // Capacitor 글로벌 타입은 push-optin.tsx 의 declare global 과 동일 — 한 쪽만 선언해도 충분. // 여기서는 import 한 PushOptIn 없으므로 별도 type 만 정의. interface PN { requestPermissions: () => Promise<{ receive: "granted" | "denied" | "prompt" }>; register: () => Promise; addListener: (event: string, cb: (data: PNData) => void) => Promise<{ remove: () => void }>; } interface PNData { value?: string; error?: string; notification?: { title?: string; body?: string; data?: Record }; actionId?: string; } function getCap(): { isNativePlatform?: () => boolean; Plugins?: { PushNotifications?: PN } } | undefined { if (typeof window === "undefined") return undefined; return (window as unknown as { Capacitor?: { isNativePlatform?: () => boolean; Plugins?: { PushNotifications?: PN } } }).Capacitor; } const SESSION_KEY = "momo-native-push-tried"; export function NativePushAutoRegister() { const ranRef = useRef(false); useEffect(() => { if (ranRef.current) return; ranRef.current = true; const cap = getCap(); const PN = cap?.Plugins?.PushNotifications; if (!cap?.isNativePlatform?.() || !PN) return; // 같은 세션 내 중복 호출 방지 (페이지 이동 마다 다시 묻지 않음) try { if (sessionStorage.getItem(SESSION_KEY)) return; } catch { /* ignore */ } try { sessionStorage.setItem(SESSION_KEY, "1"); } catch { /* ignore */ } // 알림 탭 시 (앱 백그라운드/종료 → 사용자가 알림 누름) → webview 에서 해당 URL 로 이동. // FCM payload 의 data.url 사용. 절대경로 또는 상대경로 둘 다 허용. PN.addListener("pushNotificationActionPerformed", (d) => { const url = d.notification?.data?.url; if (!url) return; try { if (/^https?:\/\//i.test(url)) { window.location.href = url; } else { window.location.assign(url); } } catch (err) { console.error("[native-push] navigate 실패:", err); } }).catch(() => {}); // 포그라운드 알림 도착 — 콘솔 로그만 (Capacitor 가 자동으로 알림 표시 안 함, OS 알림으로 옴) PN.addListener("pushNotificationReceived", (d) => { console.log("[native-push] foreground:", d.notification?.title); }).catch(() => {}); (async () => { try { // 1) 권한 요청 (이미 granted 면 즉시 granted 반환) const perm = await PN.requestPermissions(); if (perm.receive !== "granted") { console.log("[native-push] 권한 거부:", perm.receive); return; } // 2) FCM 토큰 받기 (10초 안에) const token = await new Promise((resolve) => { let resolved = false; const timeout = setTimeout(() => { if (!resolved) { resolved = true; resolve(null); } }, 10000); PN.addListener("registration", (d) => { if (resolved) return; resolved = true; clearTimeout(timeout); resolve(d.value || null); }); PN.addListener("registrationError", (d) => { if (resolved) return; resolved = true; clearTimeout(timeout); console.error("[native-push] registrationError:", d); resolve(null); }); PN.register(); }); if (!token) { console.warn("[native-push] FCM 토큰 발급 실패 — 다음 로그인에서 재시도"); return; } // 3) 백엔드 등록 — 401 이면 로그인 안 됐다는 뜻, 다음 로그인 시 재시도 const r = await fetch("/api/m/push/fcm-token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ token, userAgent: navigator.userAgent }), }); if (r.ok) { console.log("[native-push] ✔ FCM 토큰 등록 완료"); } else if (r.status === 401) { // 로그인 전이면 세션 키 제거 → 로그인 후 다시 시도되도록 try { sessionStorage.removeItem(SESSION_KEY); } catch { /* ignore */ } } else { console.warn(`[native-push] 백엔드 등록 실패 (HTTP ${r.status})`); } } catch (err) { console.error("[native-push] 자동 등록 예외:", err); } })(); }, []); return null; }