import React, { useState, useEffect } from 'react'; import { StyleSheet, View, Text, TouchableOpacity, Alert, Clipboard, Platform, ScrollView } from 'react-native'; import { Klaviyo } from 'klaviyo-react-native-sdk'; import * as Notifications from 'expo-notifications'; import { useNavigation } from 'expo-router'; // Add type declaration for global variable declare global { var lastBackgroundNotification: any; } export default function PushScreen() { const [pushPermissionStatus, setPushPermissionStatus] = useState(null); const [pushToken, setPushToken] = useState(null); const [lastNotification, setLastNotification] = useState(null); const [lastSilentNotification, setLastSilentNotification] = useState(null); const [lastBackgroundNotification, setLastBackgroundNotification] = useState(null); const navigation = useNavigation(); useEffect(() => { setupNotifications(); // Check for any background notifications that were received if (global.lastBackgroundNotification) { console.log('Found background notification in global:', global.lastBackgroundNotification); setLastBackgroundNotification(global.lastBackgroundNotification); } // Add a listener for when the screen comes into focus const unsubscribe = navigation.addListener('focus', () => { console.log('Push screen focused, checking for background notifications'); if (global.lastBackgroundNotification) { console.log('Found background notification on focus:', global.lastBackgroundNotification); setLastBackgroundNotification(global.lastBackgroundNotification); } }); // Cleanup function return () => { unsubscribe(); }; }, []); const setupNotifications = async () => { console.log('Setting up notifications...'); // Request permissions const { status: existingStatus } = await Notifications.getPermissionsAsync(); console.log('Existing permission status:', existingStatus); let finalStatus = existingStatus; if (existingStatus !== 'granted') { console.log('Requesting permissions...'); const { status } = await Notifications.requestPermissionsAsync(); finalStatus = status; console.log('New permission status:', status); } setPushPermissionStatus(finalStatus); if (finalStatus !== 'granted') { console.log('Permission not granted, stopping setup'); Alert.alert('Permission Required', 'Push notifications are required for this app to function properly.'); return; } console.log('Permissions granted, proceeding to get push token'); handleGetPushToken(); }; const handleGetPushToken = async () => { try { console.log('Getting push token...'); console.log('Platform:', Platform.OS); // Add timeout promise with longer timeout for iOS const timeoutPromise = new Promise((_, reject) => { setTimeout(() => { reject(new Error(`getDevicePushTokenAsync timed out after ${Platform.OS === 'ios' ? '30' : '10'} seconds`)); }, Platform.OS === 'ios' ? 30000 : 10000); }); const tokenPromise = Notifications.getDevicePushTokenAsync(); // Race between the actual call and the timeout const tokenResponse = await Promise.race([tokenPromise, timeoutPromise]) as Notifications.DevicePushToken; const token = tokenResponse.data; console.log('Extracted token:', token); setPushToken(token); console.log('Setting token in Klaviyo...'); try { Klaviyo.setPushToken(token); console.log('Token set in Klaviyo successfully'); } catch (klaviyoError) { console.error('Error setting token in Klaviyo:', klaviyoError); Alert.alert('Warning', 'Token was retrieved but could not be set in Klaviyo. Please try again.'); } } catch (error) { console.error('Error getting push token:', error); if (error instanceof Error) { console.error('Error details:', { message: error.message, stack: error.stack, name: error.name }); } Alert.alert('Error', `Failed to get push token: ${error instanceof Error ? error.message : 'Unknown error'}`); } }; const renderNotificationPayload = (notification: any, type: 'regular' | 'silent' | 'background') => { if (!notification) return null; console.log('Rendering notification:', { type, notification }); const content = notification.request?.content || notification; const data = content.data || {}; const isSilent = content.data?.aps?.contentAvailable === 1; // Helper function to safely stringify objects const safeStringify = (obj: any) => { try { return JSON.stringify(obj, null, 2); } catch (e) { return String(obj); } }; return ( {type === 'background' ? 'Last Background Notification:' : type === 'silent' ? 'Last Silent Push:' : 'Last Notification:'} {content.title && ( Title: {content.title} )} {content.body && ( Body: {typeof content.body === 'object' ? safeStringify(content.body) : content.body} )} Data: {safeStringify(data)} {notification.key_value_pairs && ( Key-Value Pairs: {safeStringify(notification.key_value_pairs)} )} Received: {new Date(notification.receivedAt || notification.date).toLocaleString()} {type === 'background' && ( Source: Background Task )} ); }; return ( Push Notifications {pushPermissionStatus === 'granted' ? 'Push Enabled' : 'Enable Push Notifications'} {pushPermissionStatus && ( Status: {pushPermissionStatus} )} {lastNotification && renderNotificationPayload(lastNotification, 'regular')} {lastSilentNotification && renderNotificationPayload(lastSilentNotification, 'silent')} {lastBackgroundNotification && renderNotificationPayload(lastBackgroundNotification, 'background')} { <> {pushToken ? 'Update Push Token' : 'Get Push Token'} {pushToken && ( Current Token: {pushToken} { Clipboard.setString(pushToken); Alert.alert('Copied', 'Token copied to clipboard'); }} > Copy )} } ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', }, section: { padding: 20, gap: 15, }, sectionTitle: { fontSize: 18, fontWeight: 'bold', marginBottom: 10, }, button: { backgroundColor: '#007AFF', paddingHorizontal: 20, paddingVertical: 10, borderRadius: 8, alignItems: 'center', }, fullWidthButton: { width: '100%', }, buttonText: { color: '#fff', fontSize: 16, fontWeight: 'bold', }, buttonSuccess: { backgroundColor: '#34C759', }, permissionStatus: { textAlign: 'center', marginTop: 8, color: '#666', }, tokenContainer: { marginTop: 10, padding: 10, backgroundColor: '#f5f5f5', borderRadius: 8, }, tokenLabel: { fontSize: 14, color: '#666', marginBottom: 5, }, tokenRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 10, }, tokenText: { fontSize: 12, fontFamily: 'monospace', flex: 1, }, copyButton: { backgroundColor: '#007AFF', paddingHorizontal: 12, paddingVertical: 6, borderRadius: 6, }, copyButtonText: { color: '#fff', fontSize: 12, fontWeight: 'bold', }, notificationContainer: { marginTop: 10, padding: 10, backgroundColor: '#f0f0f0', borderRadius: 8, borderWidth: 1, borderColor: '#ddd', }, notificationTitle: { fontSize: 14, fontWeight: 'bold', marginBottom: 5, color: '#333', }, notificationText: { fontSize: 12, color: '#666', marginBottom: 3, fontFamily: 'monospace', }, });