2024-03-14 21:04:32 +02:00
|
|
|
import * as React from 'react';
|
|
|
|
import { useCallback, useMemo, useState } from 'react';
|
|
|
|
import { PluginHtmlContents, PluginStates, ViewInfo } from '@joplin/lib/services/plugins/reducer';
|
|
|
|
import { StyleSheet, View, useWindowDimensions } from 'react-native';
|
2024-06-25 14:59:59 +02:00
|
|
|
import usePlugin from '@joplin/lib/hooks/usePlugin';
|
2024-03-14 21:04:32 +02:00
|
|
|
import { DialogContentSize, DialogWebViewApi } from '../types';
|
|
|
|
import { Button } from 'react-native-paper';
|
|
|
|
import { themeStyle } from '@joplin/lib/theme';
|
|
|
|
import { ButtonSpec, DialogResult } from '@joplin/lib/services/plugins/api/types';
|
|
|
|
import { _ } from '@joplin/lib/locale';
|
|
|
|
import WebviewController, { ContainerType } from '@joplin/lib/services/plugins/WebviewController';
|
|
|
|
import { Theme } from '@joplin/lib/themes/type';
|
|
|
|
import useDialogSize from './hooks/useDialogSize';
|
|
|
|
import PluginUserWebView from './PluginUserWebView';
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
themeId: number;
|
|
|
|
pluginHtmlContents: PluginHtmlContents;
|
|
|
|
pluginStates: PluginStates;
|
|
|
|
viewInfo: ViewInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
const useStyles = (
|
|
|
|
themeId: number,
|
|
|
|
dialogContentSize: DialogContentSize|null,
|
|
|
|
fitToContent: boolean,
|
|
|
|
) => {
|
|
|
|
const windowSize = useWindowDimensions();
|
|
|
|
|
|
|
|
return useMemo(() => {
|
|
|
|
const theme: Theme = themeStyle(themeId);
|
|
|
|
|
|
|
|
const useDialogSize = fitToContent && dialogContentSize;
|
|
|
|
const dialogHasLoaded = !!dialogContentSize;
|
|
|
|
|
2024-04-03 19:56:54 +02:00
|
|
|
const maxWidth = windowSize.width * 0.97;
|
|
|
|
const maxHeight = windowSize.height * 0.95;
|
|
|
|
const dialogWidth = useDialogSize ? dialogContentSize.width : maxWidth;
|
|
|
|
const dialogHeight = useDialogSize ? dialogContentSize.height : maxHeight;
|
|
|
|
|
2024-03-14 21:04:32 +02:00
|
|
|
return StyleSheet.create({
|
|
|
|
webView: {
|
|
|
|
backgroundColor: 'transparent',
|
|
|
|
display: 'flex',
|
|
|
|
},
|
|
|
|
webViewContainer: {
|
|
|
|
flexGrow: 1,
|
|
|
|
flexShrink: 1,
|
2024-04-03 19:56:54 +02:00
|
|
|
|
|
|
|
maxWidth,
|
|
|
|
maxHeight,
|
|
|
|
width: dialogWidth,
|
|
|
|
height: dialogHeight,
|
2024-03-14 21:04:32 +02:00
|
|
|
},
|
|
|
|
dialog: {
|
|
|
|
backgroundColor: theme.backgroundColor,
|
|
|
|
borderRadius: 12,
|
|
|
|
|
2024-04-03 19:56:54 +02:00
|
|
|
maxWidth,
|
|
|
|
maxHeight,
|
2024-03-14 21:04:32 +02:00
|
|
|
opacity: dialogHasLoaded ? 1 : 0.1,
|
|
|
|
|
|
|
|
// Center
|
|
|
|
marginLeft: 'auto',
|
|
|
|
marginRight: 'auto',
|
|
|
|
},
|
|
|
|
buttonRow: {
|
|
|
|
flexDirection: 'row',
|
2024-04-03 19:56:54 +02:00
|
|
|
flexShrink: 0,
|
2024-03-14 21:04:32 +02:00
|
|
|
padding: 12,
|
|
|
|
justifyContent: 'flex-end',
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}, [themeId, dialogContentSize, fitToContent, windowSize.width, windowSize.height]);
|
|
|
|
};
|
|
|
|
|
|
|
|
const defaultButtonSpecs: ButtonSpec[] = [
|
|
|
|
{ id: 'ok' }, { id: 'cancel' },
|
|
|
|
];
|
|
|
|
|
|
|
|
const PluginDialogWebView: React.FC<Props> = props => {
|
|
|
|
const viewInfo = props.viewInfo;
|
|
|
|
const view = viewInfo.view;
|
|
|
|
const pluginId = viewInfo.plugin.id;
|
|
|
|
const viewId = view.id;
|
|
|
|
const plugin = usePlugin(pluginId);
|
|
|
|
const [webViewLoadCount, setWebViewLoadCount] = useState(0);
|
|
|
|
const [dialogControl, setDialogControl] = useState<DialogWebViewApi|null>(null);
|
|
|
|
|
|
|
|
const dialogSize = useDialogSize({
|
|
|
|
dialogControl,
|
|
|
|
webViewLoadCount,
|
2024-04-03 19:56:54 +02:00
|
|
|
watchForSizeChanges: view.fitToContent,
|
2024-03-14 21:04:32 +02:00
|
|
|
});
|
|
|
|
const styles = useStyles(props.themeId, dialogSize, view.fitToContent);
|
|
|
|
|
|
|
|
const onButtonPress = useCallback(async (button: ButtonSpec) => {
|
|
|
|
const closeWithResponse = (response?: DialogResult|null) => {
|
|
|
|
const viewController = plugin.viewController(viewId) as WebviewController;
|
|
|
|
if (view.containerType === ContainerType.Dialog) {
|
|
|
|
viewController.closeWithResponse(response);
|
|
|
|
} else {
|
|
|
|
viewController.close();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let formData = undefined;
|
|
|
|
if (button.id !== 'cancel') {
|
|
|
|
formData = await dialogControl.getFormData();
|
|
|
|
}
|
|
|
|
|
|
|
|
closeWithResponse({ id: button.id, formData });
|
|
|
|
button.onClick?.();
|
|
|
|
}, [dialogControl, plugin, viewId, view.containerType]);
|
|
|
|
|
|
|
|
const buttonComponents: React.ReactElement[] = [];
|
|
|
|
const buttonSpecs = view.buttons ?? defaultButtonSpecs;
|
|
|
|
|
|
|
|
for (const button of buttonSpecs) {
|
|
|
|
let iconName = undefined;
|
|
|
|
let buttonTitle = button.title ?? button.id;
|
|
|
|
|
|
|
|
if (button.id === 'cancel') {
|
|
|
|
iconName = 'close-outline';
|
|
|
|
buttonTitle = button.title ?? _('Cancel');
|
|
|
|
} else if (button.id === 'ok') {
|
|
|
|
iconName = 'check';
|
|
|
|
buttonTitle = button.title ?? _('OK');
|
|
|
|
}
|
|
|
|
|
|
|
|
buttonComponents.push(
|
|
|
|
<Button
|
|
|
|
key={button.id}
|
|
|
|
icon={iconName}
|
|
|
|
mode='text'
|
|
|
|
onPress={() => onButtonPress(button)}
|
|
|
|
>
|
|
|
|
{buttonTitle}
|
|
|
|
</Button>,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const onWebViewLoaded = useCallback(() => {
|
|
|
|
setWebViewLoadCount(webViewLoadCount + 1);
|
|
|
|
}, [setWebViewLoadCount, webViewLoadCount]);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<View style={styles.dialog}>
|
|
|
|
<View style={styles.webViewContainer}>
|
|
|
|
<PluginUserWebView
|
|
|
|
style={styles.webView}
|
|
|
|
themeId={props.themeId}
|
|
|
|
viewInfo={props.viewInfo}
|
|
|
|
pluginHtmlContents={props.pluginHtmlContents}
|
|
|
|
onLoadEnd={onWebViewLoaded}
|
|
|
|
setDialogControl={setDialogControl}
|
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
<View style={styles.buttonRow}>
|
|
|
|
{buttonComponents}
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default PluginDialogWebView;
|