import { Alert, Platform } from 'react-native'; // Track active alerts to prevent stacking const activeAlerts = new Set(); export interface AlertButton { text?: string; onPress?: () => void; style?: 'default' | 'cancel' | 'destructive'; } export interface ShowAlertOptions { title: string; message?: string; buttons?: AlertButton[]; cancelable?: boolean; onDismiss?: () => void; alertId?: string; } /** * Cross-platform alert that works on web, iOS, and Android * On web, uses window.confirm() for confirmation dialogs and window.alert() for simple alerts * On native platforms, uses React Native's Alert.alert() */ export const showAlert = (options: ShowAlertOptions) => { const { title, message, buttons, cancelable, onDismiss, alertId, } = options; // Generate unique ID for this alert const id = alertId || `${title}-${message || ''}`; // Prevent stacking if (activeAlerts.has(id)) { return; } activeAlerts.add(id); if (Platform.OS === 'web') { // Web implementation handleWebAlert(id, title, message, buttons, onDismiss); } else { // Native implementation handleNativeAlert(id, title, message, buttons, cancelable, onDismiss); } }; /** * Handle alerts on web using window.confirm/alert */ const handleWebAlert = ( id: string, title: string, message?: string, buttons?: AlertButton[], onDismiss?: () => void ) => { const fullMessage = message ? `${title}\n\n${message}` : title; // Use setTimeout to ensure the alert runs after current execution setTimeout(() => { try { if (!buttons || buttons.length === 0) { // Simple alert with OK button window.alert(fullMessage); activeAlerts.delete(id); onDismiss?.(); } else if (buttons.length === 1) { // Single button - use alert window.alert(fullMessage); activeAlerts.delete(id); buttons[0].onPress?.(); onDismiss?.(); } else if (buttons.length === 2) { // Two buttons - use confirm // Find cancel and confirm buttons const cancelButton = buttons.find(b => b.style === 'cancel'); const confirmButton = buttons.find(b => b.style !== 'cancel') || buttons[1]; const result = window.confirm(fullMessage); activeAlerts.delete(id); if (result) { // User clicked OK - trigger the non-cancel button confirmButton?.onPress?.(); } else { // User clicked Cancel cancelButton?.onPress?.(); } onDismiss?.(); } else { // More than 2 buttons - use confirm for first two, warn about limitation console.warn('Web alerts only support up to 2 buttons. Additional buttons will be ignored.'); const cancelButton = buttons.find(b => b.style === 'cancel'); const confirmButton = buttons.find(b => b.style === 'destructive') || buttons.find(b => b.style !== 'cancel') || buttons[0]; const result = window.confirm(fullMessage); activeAlerts.delete(id); if (result) { confirmButton?.onPress?.(); } else { cancelButton?.onPress?.(); } onDismiss?.(); } } catch (error) { activeAlerts.delete(id); console.error('Error showing web alert:', error); } }, 0); }; /** * Handle alerts on native platforms using React Native Alert */ const handleNativeAlert = ( id: string, title: string, message?: string, buttons?: AlertButton[], cancelable?: boolean, onDismiss?: () => void ) => { // Wrap button callbacks to clear the alert ID const wrappedButtons = buttons?.map(button => ({ ...button, onPress: () => { activeAlerts.delete(id); button.onPress?.(); }, })); // Wrap onDismiss to clear the alert ID const wrappedOptions = { cancelable, onDismiss: () => { activeAlerts.delete(id); onDismiss?.(); }, }; Alert.alert(title, message, wrappedButtons, wrappedOptions); }; /** * Simple alert with just an OK button * Works on all platforms */ export const alertOk = (title: string, message?: string, onOk?: () => void) => { showAlert({ title, message, buttons: [{ text: 'OK', onPress: onOk }], }); }; /** * Confirmation dialog with Cancel and Confirm buttons * Works on all platforms */ export const alertConfirm = ( title: string, message: string, onConfirm: () => void, onCancel?: () => void, confirmText: string = 'Confirm', cancelText: string = 'Cancel', destructive: boolean = false ) => { showAlert({ title, message, buttons: [ { text: cancelText, style: 'cancel', onPress: onCancel }, { text: confirmText, style: destructive ? 'destructive' : 'default', onPress: onConfirm }, ], }); }; /** * Clear a specific alert from the active set (useful for manual cleanup) */ export const clearAlert = (alertId: string) => { activeAlerts.delete(alertId); }; /** * Clear all active alerts (useful for cleanup on unmount) */ export const clearAllAlerts = () => { activeAlerts.clear(); };