1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-18 09:35:20 +02:00
joplin/packages/app-desktop/services/plugins/UserWebview.tsx

157 lines
3.9 KiB
TypeScript
Raw Normal View History

import * as React from 'react';
import { useRef, useImperativeHandle, forwardRef, useEffect } from 'react';
import useViewIsReady from './hooks/useViewIsReady';
import useThemeCss from './hooks/useThemeCss';
import useContentSize from './hooks/useContentSize';
import useSubmitHandler from './hooks/useSubmitHandler';
import useHtmlLoader from './hooks/useHtmlLoader';
import useWebviewToPluginMessages from './hooks/useWebviewToPluginMessages';
import useScriptLoader from './hooks/useScriptLoader';
import Logger from '@joplin/utils/Logger';
import styled from 'styled-components';
const logger = Logger.create('UserWebview');
export interface Props {
html: string;
scripts: string[];
pluginId: string;
viewId: string;
themeId: number;
minWidth?: number;
minHeight?: number;
fitToContent?: boolean;
borderBottom?: boolean;
theme?: any;
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
onSubmit?: Function;
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
onDismiss?: Function;
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
onReady?: Function;
}
const StyledFrame = styled.iframe<{ fitToContent: boolean; borderBottom: boolean }>`
padding: 0;
margin: 0;
width: ${(props: any) => props.fitToContent ? `${props.width}px` : '100%'};
height: ${(props: any) => props.fitToContent ? `${props.height}px` : '100%'};
border: none;
border-bottom: ${(props: any) => props.borderBottom ? `1px solid ${props.theme.dividerColor}` : 'none'};
`;
function serializeForm(form: any) {
const output: any = {};
const formData = new FormData(form);
for (const key of formData.keys()) {
output[key] = formData.get(key);
}
return output;
}
function serializeForms(document: any) {
const forms = document.getElementsByTagName('form');
const output: any = {};
let untitledIndex = 0;
for (const form of forms) {
const name = `${form.getAttribute('name')}` || (`form${untitledIndex++}`);
output[name] = serializeForm(form);
}
return output;
}
function UserWebview(props: Props, ref: any) {
const minWidth = props.minWidth ? props.minWidth : 200;
const minHeight = props.minHeight ? props.minHeight : 20;
const viewRef = useRef(null);
const isReady = useViewIsReady(viewRef);
const cssFilePath = useThemeCss({ pluginId: props.pluginId, themeId: props.themeId });
useEffect(() => {
if (isReady && props.onReady) props.onReady();
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, [isReady]);
function frameWindow() {
if (!viewRef.current) return null;
return viewRef.current.contentWindow;
}
function postMessage(name: string, args: any = null) {
const win = frameWindow();
if (!win) return;
logger.debug('Got message', name, args);
win.postMessage({ target: 'webview', name, args }, '*');
}
useImperativeHandle(ref, () => {
return {
formData: function() {
if (viewRef.current) {
return serializeForms(frameWindow().document);
} else {
return null;
}
},
focus: function() {
if (viewRef.current) viewRef.current.focus();
},
};
});
const htmlHash = useHtmlLoader(
frameWindow(),
isReady,
postMessage,
props.html,
);
const contentSize = useContentSize(
frameWindow(),
htmlHash,
minWidth,
minHeight,
props.fitToContent,
isReady,
);
useSubmitHandler(
frameWindow(),
props.onSubmit,
props.onDismiss,
htmlHash,
);
useWebviewToPluginMessages(
frameWindow(),
isReady,
props.pluginId,
props.viewId,
postMessage,
);
useScriptLoader(
postMessage,
isReady,
props.scripts,
cssFilePath,
);
return <StyledFrame
id={props.viewId}
width={contentSize.width}
height={contentSize.height}
fitToContent={props.fitToContent}
ref={viewRef}
src="services/plugins/UserWebviewIndex.html"
borderBottom={props.borderBottom}
></StyledFrame>;
}
export default forwardRef(UserWebview);