Files
aliasvault/apps/mobile-app/context/DialogContext.tsx

218 lines
5.3 KiB
TypeScript

import React, { createContext, useContext, useState, useCallback, useMemo } from 'react';
import { Alert, Platform } from 'react-native';
import { ConfirmDialog, type IConfirmDialogButton } from '@/components/common/ConfirmDialog';
interface DialogConfig {
title: string;
message: string;
buttons: IConfirmDialogButton[];
}
interface DialogContextValue {
/**
* Show a simple alert with an OK button.
*/
showAlert: (title: string, message: string, onOk?: () => void) => void;
/**
* Show a confirm dialog with Cancel and a custom action button.
*/
showConfirm: (
title: string,
message: string,
confirmText: string,
onConfirm: () => void | Promise<void>,
options?: {
cancelText?: string;
confirmStyle?: 'default' | 'destructive';
}
) => void;
/**
* Show a custom dialog with any buttons.
*/
showDialog: (title: string, message: string, buttons: IConfirmDialogButton[]) => void;
/**
* Hide the current dialog.
*/
hideDialog: () => void;
}
const DialogContext = createContext<DialogContextValue | null>(null);
/**
* Hook to access the dialog service.
*
* Usage:
* ```tsx
* const { showAlert, showConfirm } = useDialog();
*
* // Simple alert
* showAlert(t('common.error'), t('auth.errors.enterPassword'));
*
* // Confirm dialog
* showConfirm(
* t('items.deleteItem'),
* t('items.deleteConfirm'),
* t('common.delete'),
* async () => { await deleteItem(); },
* { confirmStyle: 'destructive' }
* );
* ```
*/
export function useDialog(): DialogContextValue {
const context = useContext(DialogContext);
if (!context) {
throw new Error('useDialog must be used within a DialogProvider');
}
return context;
}
interface DialogProviderProps {
children: React.ReactNode;
}
/**
* Provider component that enables the dialog service.
* Add this near the root of your app (e.g., in _layout.tsx).
*/
export function DialogProvider({ children }: DialogProviderProps): React.ReactNode {
const [dialogConfig, setDialogConfig] = useState<DialogConfig | null>(null);
/**
* Hide the dialog.
*/
const hideDialog = useCallback((): void => {
setDialogConfig(null);
}, []);
/**
* Show a simple alert with an OK button.
*/
const showAlert = useCallback((title: string, message: string, onOk?: () => void): void => {
// On iOS, use native Alert for simple alerts too
if (Platform.OS === 'ios') {
Alert.alert(title, message, [{
text: 'OK',
style: 'default',
onPress: onOk,
}]);
return;
}
setDialogConfig({
title,
message,
buttons: [{
text: 'OK',
style: 'default',
onPress: (): void => {
onOk?.();
setDialogConfig(null);
},
}],
});
}, []);
/**
* Show a confirm dialog with Cancel and action button.
*/
const showConfirm = useCallback((
title: string,
message: string,
confirmText: string,
onConfirm: () => void | Promise<void>,
options?: {
cancelText?: string;
confirmStyle?: 'default' | 'destructive';
}
): void => {
// On iOS, use native Alert
if (Platform.OS === 'ios') {
Alert.alert(title, message, [
{ text: options?.cancelText ?? 'Cancel', style: 'cancel' },
{
text: confirmText,
style: options?.confirmStyle ?? 'default',
onPress: () => { onConfirm(); },
},
]);
return;
}
setDialogConfig({
title,
message,
buttons: [
{
text: options?.cancelText ?? 'Cancel',
style: 'cancel',
onPress: (): void => setDialogConfig(null),
},
{
text: confirmText,
style: options?.confirmStyle ?? 'default',
onPress: async (): Promise<void> => {
await onConfirm();
setDialogConfig(null);
},
},
],
});
}, []);
/**
* Show a custom dialog with any buttons.
*/
const showDialog = useCallback((title: string, message: string, buttons: IConfirmDialogButton[]): void => {
// On iOS, use native Alert
if (Platform.OS === 'ios') {
const alertButtons = buttons.map(btn => ({
text: btn.text,
style: btn.style,
onPress: () => { btn.onPress?.(); },
}));
Alert.alert(title, message, alertButtons);
return;
}
// Wrap button onPress to auto-close dialog
const wrappedButtons = buttons.map(btn => ({
...btn,
onPress: async (): Promise<void> => {
await btn.onPress?.();
setDialogConfig(null);
},
}));
setDialogConfig({ title, message, buttons: wrappedButtons });
}, []);
const contextValue = useMemo(() => ({
showAlert,
showConfirm,
showDialog,
hideDialog,
}), [showAlert, showConfirm, showDialog, hideDialog]);
return (
<DialogContext.Provider value={contextValue}>
{children}
{/* Only render on Android since iOS uses native Alert */}
{Platform.OS !== 'ios' && (
<ConfirmDialog
isVisible={dialogConfig !== null}
title={dialogConfig?.title ?? ''}
message={dialogConfig?.message ?? ''}
buttons={dialogConfig?.buttons ?? []}
onClose={hideDialog}
/>
)}
</DialogContext.Provider>
);
}
export default DialogProvider;