1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-07-16 00:14:34 +02:00

Chore: Refactor mobile plugin logic into locations more consistent with other parts of the app (#10636)

This commit is contained in:
Henry Heino
2024-06-25 05:59:59 -07:00
committed by GitHub
parent 801d36c41f
commit c7116b135f
34 changed files with 155 additions and 91 deletions

View File

@ -0,0 +1,53 @@
import { useMemo, RefObject } from 'react';
import { DialogMainProcessApi, DialogWebViewApi } from '../../types';
import Logger from '@joplin/utils/Logger';
import { WebViewControl } from '../../../../components/ExtendedWebView';
import createOnLogHander from '../../utils/createOnLogHandler';
import RNToWebViewMessenger from '../../../../utils/ipc/RNToWebViewMessenger';
import { SerializableData } from '@joplin/lib/utils/ipc/types';
import PostMessageService, { ResponderComponentType } from '@joplin/lib/services/PostMessageService';
import PluginService from '@joplin/lib/services/plugins/PluginService';
interface Props {
pluginId: string;
viewId: string;
webviewRef: RefObject<WebViewControl>;
messageChannelId: string;
}
const useDialogMessenger = (props: Props) => {
const { pluginId, webviewRef, viewId, messageChannelId } = props;
return useMemo(() => {
const plugin = PluginService.instance().pluginById(pluginId);
const logger = Logger.create(`PluginDialogWebView(${pluginId})`);
const dialogApi: DialogMainProcessApi = {
postMessage: async (message: SerializableData) => {
return await plugin.viewController(viewId).emitMessage({ message });
},
onMessage: async (callback) => {
PostMessageService.instance().registerViewMessageHandler(
ResponderComponentType.UserWebview,
viewId,
(message: SerializableData) => {
// For compatibility with desktop, the message needs to be wrapped in
// an object.
return callback({ message });
},
);
},
onError: async (error: string) => {
logger.error(`Unhandled error: ${error}`);
plugin.hasErrors = true;
},
onLog: createOnLogHander(plugin, logger),
};
return new RNToWebViewMessenger<DialogMainProcessApi, DialogWebViewApi>(
messageChannelId, webviewRef, dialogApi,
);
}, [webviewRef, pluginId, viewId, messageChannelId]);
};
export default useDialogMessenger;

View File

@ -0,0 +1,52 @@
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
import { useRef, useState } from 'react';
import { DialogContentSize, DialogWebViewApi } from '../../types';
import { PixelRatio } from 'react-native';
interface Props {
dialogControl: DialogWebViewApi;
webViewLoadCount: number;
watchForSizeChanges: boolean;
}
const useDialogSize = (props: Props) => {
const { dialogControl, webViewLoadCount } = props;
const [dialogSize, setDialogSize] = useState<DialogContentSize|null>(null);
const lastSizeRef = useRef(dialogSize);
lastSizeRef.current = dialogSize;
useAsyncEffect(async event => {
if (!dialogControl) {
// May happen if the webview is still loading.
return;
}
while (!event.cancelled) {
const contentSize = await dialogControl.getContentSize();
if (event.cancelled) return;
const lastSize = lastSizeRef.current;
if (contentSize.width !== lastSize?.width || contentSize.height !== lastSize?.height) {
// We use 1000 here because getPixelSizeForLayoutSize is guaranteed to return
// an integer.
const pixelToDpRatio = 1000 / PixelRatio.getPixelSizeForLayoutSize(1000);
setDialogSize({
width: contentSize.width * pixelToDpRatio,
height: contentSize.height * pixelToDpRatio,
});
}
if (!props.watchForSizeChanges) return;
await new Promise<void>(resolve => {
setTimeout(() => {
resolve();
}, 500);
});
}
}, [dialogControl, setDialogSize, webViewLoadCount]);
return dialogSize;
};
export default useDialogSize;

View File

@ -0,0 +1,10 @@
import { PluginStates, utils as pluginUtils } from '@joplin/lib/services/plugins/reducer';
import { useMemo } from 'react';
const useViewInfos = (pluginStates: PluginStates) => {
return useMemo(() => {
return pluginUtils.viewInfosByType(pluginStates, 'webview');
}, [pluginStates]);
};
export default useViewInfos;

View File

@ -0,0 +1,43 @@
import { useEffect } from 'react';
import { DialogWebViewApi } from '../../types';
import shim from '@joplin/lib/shim';
import { themeStyle } from '../../../../components/global-style';
import themeToCss from '@joplin/lib/services/style/themeToCss';
interface Props {
themeId: number;
scriptPaths: string[];
dialogControl: DialogWebViewApi;
pluginBaseDir: string;
// Whenever the WebView reloads, we need to re-inject CSS and JavaScript.
webViewLoadCount: number;
}
const useWebViewSetup = (props: Props) => {
const { scriptPaths, dialogControl, pluginBaseDir, themeId } = props;
useEffect(() => {
const jsPaths = [];
const cssPaths = [];
for (const rawPath of scriptPaths) {
const resolvedPath = shim.fsDriver().resolveRelativePathWithinDir(pluginBaseDir, rawPath);
if (resolvedPath.match(/\.css$/i)) {
cssPaths.push(resolvedPath);
} else {
jsPaths.push(resolvedPath);
}
}
void dialogControl.includeCssFiles(cssPaths);
void dialogControl.includeJsFiles(jsPaths);
}, [dialogControl, scriptPaths, props.webViewLoadCount, pluginBaseDir]);
useEffect(() => {
const theme = themeStyle(themeId);
const themeVariableCss = themeToCss(theme);
void dialogControl.setThemeCss(themeVariableCss);
}, [dialogControl, themeId, props.webViewLoadCount]);
};
export default useWebViewSetup;