1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-07-13 00:10:37 +02:00

Desktop: Resolves #11687: Plugins: Allow editor plugins to support multiple windows (#12041)

This commit is contained in:
Henry Heino
2025-06-06 02:00:47 -07:00
committed by GitHub
parent 291ba88224
commit 608dbab453
46 changed files with 1022 additions and 195 deletions

View File

@ -68,6 +68,8 @@ import getActivePluginEditorView from '@joplin/lib/services/plugins/utils/getAct
import EditorPluginHandler from '@joplin/lib/services/plugins/EditorPluginHandler';
import AudioRecordingBanner from '../../voiceTyping/AudioRecordingBanner';
import SpeechToTextBanner from '../../voiceTyping/SpeechToTextBanner';
import { defaultWindowId } from '@joplin/lib/reducer';
import useVisiblePluginEditorViewIds from '@joplin/lib/hooks/plugins/useVisiblePluginEditorViewIds';
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const emptyArray: any[] = [];
@ -86,6 +88,7 @@ interface NoteNavigation {
}
interface Props extends BaseProps {
windowId: string;
provisionalNoteIds: string[];
navigation: NoteNavigation;
dispatch: Dispatch;
@ -102,13 +105,13 @@ interface Props extends BaseProps {
highlightedWords: string[];
noteHash: string;
toolbarEnabled: boolean;
'plugins.shownEditorViewIds': string[];
pluginHtmlContents: PluginHtmlContents;
editorNoteReloadTimeRequest: number;
}
interface ComponentProps extends Props {
dialogs: DialogControl;
visibleEditorPluginIds: string[];
}
interface State {
@ -170,7 +173,9 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public dialogbox: any;
private commandRegistration_: RegisteredRuntime|null = null;
private editorPluginHandler_ = new EditorPluginHandler(PluginService.instance());
private editorPluginHandler_ = new EditorPluginHandler(PluginService.instance(), saveEvent => {
return shared.noteComponent_change(this, 'body', saveEvent.body);
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public static navigationOptions(): any {
@ -561,10 +566,13 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
}, 100);
}
await this.editorPluginHandler_.emitActivationCheck();
await this.editorPluginHandler_.emitActivationCheck({
noteId: this.props.noteId,
parentWindowId: defaultWindowId,
});
setTimeout(() => {
this.editorPluginHandler_.emitUpdate(this.props['plugins.shownEditorViewIds']);
this.emitEditorPluginUpdate_();
}, 300);
}
@ -578,7 +586,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
await ResourceFetcher.instance().markForDownload(resourceIds);
}
public componentDidUpdate(prevProps: Props, prevState: State) {
public componentDidUpdate(prevProps: ComponentProps, prevState: State) {
if (this.doFocusUpdate_) {
this.doFocusUpdate_ = false;
this.scheduleFocusUpdate();
@ -626,15 +634,22 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
});
}
if (this.props['plugins.shownEditorViewIds'] !== prevProps['plugins.shownEditorViewIds']) {
const { editorPlugin } = getShownPluginEditorView(this.props.plugins, this.props['plugins.shownEditorViewIds']);
if (this.props.visibleEditorPluginIds !== prevProps.visibleEditorPluginIds) {
const { editorPlugin } = getShownPluginEditorView(this.props.plugins, this.props.windowId);
if (!editorPlugin && this.props.editorNoteReloadTimeRequest > this.state.noteLastLoadTime) {
void shared.reloadNote(this);
}
}
if (prevProps.noteId && this.props.noteId && prevProps.noteId !== this.props.noteId) {
void this.editorPluginHandler_.emitActivationCheck();
void this.editorPluginHandler_.emitActivationCheck({
noteId: this.props.noteId,
parentWindowId: defaultWindowId,
});
}
if (prevState.note.body !== this.state.note.body) {
this.emitEditorPluginUpdate_();
}
}
@ -659,6 +674,13 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
this.setState({ newAndNoTitleChangeNoteId: null });
}
private emitEditorPluginUpdate_() {
this.editorPluginHandler_.emitUpdate({
noteId: this.props.noteId,
newBody: this.state.note.body,
}, this.props.visibleEditorPluginIds);
}
private onPlainEditorTextChange = (text: string) => {
if (!this.undoRedoService_.canUndo) {
this.undoRedoService_.push(this.undoState());
@ -1463,7 +1485,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
// multiple times.
this.registerCommands();
const { editorPlugin, editorView } = getShownPluginEditorView(this.props.plugins, this.props['plugins.shownEditorViewIds']);
const { editorPlugin, editorView } = getShownPluginEditorView(this.props.plugins, this.props.windowId);
if (this.state.isLoading) {
return (
@ -1494,6 +1516,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
}
const renderPluginEditor = () => {
this.editorPluginHandler_.onEditorPluginShown(editorView.id);
return <PluginUserWebView
viewInfo={{ plugin: editorPlugin, view: editorView }}
themeId={this.props.themeId}
@ -1604,6 +1627,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
const voiceTypingDialogShown = this.state.showSpeechToTextDialog || this.state.showAudioRecorder;
const renderActionButton = () => {
if (voiceTypingDialogShown) return null;
if (editorView) return null;
if (!this.state.note || !!this.state.note.deleted_time) return null;
const editButton = {
@ -1670,7 +1694,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
return result;
};
const { editorPlugin: activeEditorPlugin } = getActivePluginEditorView(this.props.plugins);
const { editorPlugin: activeEditorPlugin } = getActivePluginEditorView(this.props.plugins, this.props.windowId);
return (
<View style={this.rootStyle(this.props.themeId).root}>
@ -1709,13 +1733,16 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
// how the new note should be rendered
const NoteScreenWrapper = (props: Props) => {
const dialogs = useContext(DialogContext);
const visibleEditorPluginIds = useVisiblePluginEditorViewIds(props.plugins, props.windowId);
return (
<NoteScreenComponent key={props.noteId} dialogs={dialogs} {...props} />
<NoteScreenComponent key={props.noteId} dialogs={dialogs} visibleEditorPluginIds={visibleEditorPluginIds} {...props} />
);
};
const NoteScreen = connect((state: AppState) => {
return {
windowId: state.windowId,
noteId: state.selectedNoteIds.length ? state.selectedNoteIds[0] : null,
noteHash: state.selectedNoteHash,
itemType: state.selectedItemType,
@ -1732,7 +1759,6 @@ const NoteScreen = connect((state: AppState) => {
provisionalNoteIds: state.provisionalNoteIds,
highlightedWords: state.highlightedWords,
plugins: state.pluginService.plugins,
'plugins.shownEditorViewIds': state.settings['plugins.shownEditorViewIds'] || [],
pluginHtmlContents: state.pluginService.pluginHtmlContents,
editorNoteReloadTimeRequest: state.editorNoteReloadTimeRequest,