// apps/mobile/lib/push.ts // Expo Push token 등록 — 앱 진입 시 호출하면 Supabase push_tokens 테이블에 upsert. import * as Notifications from 'expo-notifications' import * as Device from 'expo-device' import { Platform } from 'react-native' import Constants from 'expo-constants' import { supabase } from './supabase' Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: true, shouldSetBadge: false, shouldShowBanner: true, shouldShowList: true }) }) /** * Expo push token을 받아 Supabase push_tokens 테이블에 upsert. * 시뮬레이터/에뮬레이터에서는 token이 발급되지 않으므로 조용히 return. */ export async function registerPushToken(userId: string): Promise { if (!Device.isDevice) { return } try { // 권한 요청 const { status: existingStatus } = await Notifications.getPermissionsAsync() let finalStatus = existingStatus if (existingStatus !== 'granted') { const { status } = await Notifications.requestPermissionsAsync() finalStatus = status } if (finalStatus !== 'granted') { return } // EAS projectId는 app.json/eas.json에서 const projectId = (Constants.expoConfig?.extra?.eas?.projectId as string | undefined) ?? (Constants.easConfig?.projectId as string | undefined) const tokenData = await Notifications.getExpoPushTokenAsync(projectId ? { projectId } : undefined) const token = tokenData.data if (!token) return const platform: 'ios' | 'android' | 'web' = Platform.OS === 'ios' ? 'ios' : Platform.OS === 'android' ? 'android' : 'web' // Android 채널 설정 if (Platform.OS === 'android') { await Notifications.setNotificationChannelAsync('default', { name: 'default', importance: Notifications.AndroidImportance.MAX, vibrationPattern: [0, 250, 250, 250], lightColor: '#f25b29' }) } // Supabase upsert await supabase.from('push_tokens').upsert( { user_id: userId, token, platform, device_name: Device.deviceName ?? null }, { onConflict: 'token' } ) } catch { // push 등록 실패는 앱 진행 차단하지 않음 } }