import * as React from 'react'; import { createContext, useEffect, useMemo, useRef, useState } from 'react'; import { Alert, Platform, StyleSheet } from 'react-native'; import { Button, Dialog, Portal, Text } from 'react-native-paper'; import Modal from './Modal'; import { _ } from '@joplin/lib/locale'; import shim from '@joplin/lib/shim'; import makeShowMessageBox from '../utils/makeShowMessageBox'; export interface PromptButton { text: string; onPress?: ()=> void; style?: 'cancel'|'default'|'destructive'; } interface PromptOptions { cancelable?: boolean; } export interface DialogControl { prompt(title: string, message: string, buttons?: PromptButton[], options?: PromptOptions): void; } export const DialogContext = createContext(null); interface Props { children: React.ReactNode; } interface PromptDialogData { key: string; title: string; message: string; buttons: PromptButton[]; onDismiss: (()=> void)|null; } const styles = StyleSheet.create({ dialogContainer: { maxWidth: 400, minWidth: '50%', alignSelf: 'center', }, modalContainer: { marginTop: 'auto', marginBottom: 'auto', }, }); const DialogManager: React.FC = props => { const [dialogModels, setPromptDialogs] = useState([]); const nextDialogIdRef = useRef(0); const dialogControl: DialogControl = useMemo(() => { const defaultButtons = [{ text: _('OK') }]; return { prompt: (title: string, message: string, buttons: PromptButton[] = defaultButtons, options?: PromptOptions) => { if (Platform.OS !== 'web') { // Alert.alert provides a more native style on iOS. Alert.alert(title, message, buttons, options); // Alert.alert doesn't work on web. } else { const onDismiss = () => { setPromptDialogs(dialogs => dialogs.filter(d => d !== dialog)); }; const cancelable = options?.cancelable ?? true; const dialog: PromptDialogData = { key: `dialog-${nextDialogIdRef.current++}`, title, message, buttons: buttons.map(button => ({ ...button, onPress: () => { onDismiss(); button.onPress?.(); }, })), onDismiss: cancelable ? onDismiss : null, }; setPromptDialogs(dialogs => { return [ ...dialogs, dialog, ]; }); } }, }; }, []); const dialogControlRef = useRef(dialogControl); dialogControlRef.current = dialogControl; useEffect(() => { shim.showMessageBox = makeShowMessageBox(dialogControlRef); return () => { dialogControlRef.current = null; }; }, []); const dialogComponents: React.ReactNode[] = []; for (const dialog of dialogModels) { const buttons = dialog.buttons.map((button, index) => { return ( ); }); dialogComponents.push( {dialog.title} {dialog.message} {buttons} , ); } // Web: Use a wrapper for better keyboard focus handling. return <> {props.children} {dialogComponents} ; }; export default DialogManager;