feat: FCM 포그라운드 알림 처리 강화

- type별 토스트 스타일 분기 (error/warning/success/info)
- 포그라운드 사운드 재생 (sound_key 기반)
- URL 클릭 이동 지원
- 앱 상태 감지 (포그라운드/백그라운드)
- sounds/ 디렉토리 추가
This commit is contained in:
2025-12-18 23:01:13 +09:00
parent dd225d59af
commit 43e469b444
2 changed files with 121 additions and 4 deletions

View File

@@ -1,20 +1,30 @@
/**
* FCM (Firebase Cloud Messaging) Push Notification Handler
* Capacitor 앱에서만 동작, 웹 브라우저에서는 무시됨
*
* Payload data 스키마:
* - type: 알림 타입 (invoice_failed, order_completed 등)
* - url: 클릭 시 이동 URL
* - sound_key: 사운드 파일 키 (sounds/{sound_key}.wav)
*/
(function () {
'use strict';
console.log('FCM.js LOADED');
console.log('[FCM] fcm.js LOADED v2');
const CONFIG = {
apiBaseUrl: window.SAM_CONFIG?.apiBaseUrl || 'https://api.codebridge-x.com',
fcmTokenKey: 'fcm_token',
apiTokenKey: 'api_access_token',
apiKeyHeader: window.SAM_CONFIG?.apiKey || '',
soundBasePath: '/sounds/',
defaultSound: 'default',
};
// 앱 상태 (포그라운드 여부)
let isAppForeground = true;
// DOM 준비되면 실행 (이미 로드됐으면 즉시)
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', bootstrap);
@@ -28,6 +38,16 @@
return;
}
// 앱 상태 리스너 (포그라운드/백그라운드)
if (window.Capacitor?.Plugins?.App) {
const { App } = Capacitor.Plugins;
App.addListener('appStateChange', ({ isActive }) => {
isAppForeground = isActive;
console.log('[FCM] App state:', isActive ? 'foreground' : 'background');
});
}
await initializeFCM();
}
@@ -51,9 +71,7 @@
PushNotifications.addListener('pushNotificationReceived', (notification) => {
console.log('[FCM] Push received (foreground):', notification);
if (typeof showToast === 'function') {
showToast(notification.body || notification.title, 'info');
}
handleForegroundNotification(notification);
});
PushNotifications.addListener('pushNotificationActionPerformed', (action) => {
@@ -182,9 +200,108 @@
return window.SAM_CONFIG?.appVersion || null;
}
/**
* 포그라운드 알림 처리
*/
function handleForegroundNotification(notification) {
const data = notification.data || {};
const type = data.type || 'default';
const url = data.url;
const soundKey = data.sound_key;
console.log('[FCM] Notification data:', { type, url, soundKey });
// 1. 포그라운드에서만 사운드 재생 (백그라운드는 OS 채널 사운드)
if (isAppForeground && soundKey) {
playNotificationSound(soundKey);
}
// 2. 타입별 토스트 메시지 표시
const title = notification.title || '알림';
const body = notification.body || '';
const toastType = getToastTypeByNotificationType(type);
if (typeof showToast === 'function') {
showToast(`${title}: ${body}`, toastType);
}
// 3. 클릭 가능한 토스트 (URL이 있는 경우)
if (url && typeof showClickableToast === 'function') {
showClickableToast(title, body, () => {
window.location.href = url;
});
}
}
/**
* 알림 사운드 재생 (포그라운드 전용)
*/
function playNotificationSound(soundKey) {
try {
const soundPath = `${CONFIG.soundBasePath}${soundKey}.wav`;
const audio = new Audio(soundPath);
audio.volume = 0.5;
audio.play().catch(err => {
console.warn('[FCM] Sound play failed, trying default:', err.message);
// 기본 사운드 시도
if (soundKey !== CONFIG.defaultSound) {
const defaultAudio = new Audio(`${CONFIG.soundBasePath}${CONFIG.defaultSound}.wav`);
defaultAudio.volume = 0.5;
defaultAudio.play().catch(() => {});
}
});
} catch (err) {
console.warn('[FCM] Sound error:', err);
}
}
/**
* 알림 타입별 토스트 스타일 결정
*/
function getToastTypeByNotificationType(type) {
const typeMap = {
// 긴급/에러
'invoice_failed': 'error',
'payment_failed': 'error',
'order_cancelled': 'error',
// 경고
'approval_required': 'warning',
'stock_low': 'warning',
// 성공
'order_completed': 'success',
'payment_completed': 'success',
'approval_approved': 'success',
// 기본
'default': 'info',
};
return typeMap[type] || 'info';
}
/**
* 클릭 가능한 토스트 (URL 이동용)
* showClickableToast가 없으면 기본 토스트 사용
*/
if (typeof window.showClickableToast === 'undefined') {
window.showClickableToast = function(title, body, onClick) {
// 기본 구현: 일반 토스트 + 클릭 핸들러는 무시
if (typeof showToast === 'function') {
showToast(`${title}: ${body}`, 'info');
}
console.log('[FCM] showClickableToast not implemented, URL click ignored');
};
}
window.FCM = {
unregisterToken,
reinitialize: initializeFCM,
// 디버그/테스트용
playSound: playNotificationSound,
testForeground: (data) => handleForegroundNotification({ title: 'Test', body: 'Test notification', data }),
};
})();

0
public/sounds/.gitkeep Normal file
View File