Skip to content

Commit

Permalink
Improve logic
Browse files Browse the repository at this point in the history
  • Loading branch information
laurent22 committed Nov 9, 2024
1 parent 5204b8c commit 560c519
Show file tree
Hide file tree
Showing 16 changed files with 202 additions and 13 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/restoreNote.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/revealResourceFile.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/search.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/setTags.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showEditorPlugin.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showModalMessage.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showNoteContentProperties.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showNoteProperties.js
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/restoreNote.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/revealResourceFile.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/search.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/setTags.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showEditorPlugin.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showModalMessage.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showNoteContentProperties.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showNoteProperties.js
Expand Down
1 change: 1 addition & 0 deletions packages/app-desktop/gui/MainScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,7 @@ class MainScreenComponent extends React.Component<Props, State> {
<NoteEditor
windowId={defaultWindowId}
key={key}
startupPluginsLoaded={this.props.startupPluginsLoaded}
/>
</div>;
},
Expand Down
3 changes: 3 additions & 0 deletions packages/app-desktop/gui/NoteEditor/EditorWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface Props {
newWindow: boolean;
windowId: string;
activeWindowId: string;
startupPluginsLoaded: boolean;
}

const emptyCallback = () => {};
Expand All @@ -45,6 +46,7 @@ const SecondaryWindow: React.FC<Props> = props => {
<NoteEditor
windowId={props.windowId}
onTitleChange={onNoteTitleChange}
startupPluginsLoaded={props.startupPluginsLoaded}
/>
</div>;

Expand Down Expand Up @@ -121,5 +123,6 @@ export default connect((state: AppState, ownProps: ConnectProps) => {
codeView: windowState?.editorCodeView ?? state.settings['editor.codeView'],
legacyMarkdown: state.settings['editor.legacyMarkdown'],
activeWindowId: stateUtils.activeWindowId(state),
startupPluginsLoaded: state.startupPluginsLoaded,
};
})(SecondaryWindow);
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ import { ContextMenuParams, Event } from 'electron';
import { useEffect, RefObject } from 'react';
import { _ } from '@joplin/lib/locale';
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
import { MenuItemLocation } from '@joplin/lib/services/plugins/api/types';
import { EditContextMenuFilterObject, MenuItemLocation } from '@joplin/lib/services/plugins/api/types';
import MenuUtils from '@joplin/lib/services/commands/MenuUtils';
import CommandService from '@joplin/lib/services/CommandService';
import SpellCheckerService from '@joplin/lib/services/spellChecker/SpellCheckerService';
import { EditContextMenuFilterObject } from '@joplin/lib/services/plugins/api/JoplinWorkspace';
import type CodeMirrorControl from '@joplin/editor/CodeMirror/CodeMirrorControl';
import eventManager from '@joplin/lib/eventManager';
import bridge from '../../../../../services/bridge';
Expand Down
38 changes: 37 additions & 1 deletion packages/app-desktop/gui/NoteEditor/NoteEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ import Logger from '@joplin/utils/Logger';
import usePluginEditorView from './utils/usePluginEditorView';
import { stateUtils } from '@joplin/lib/reducer';
import { WindowIdContext } from '../NewWindowOrIFrame';
import { EditorActivationCheckFilterObject } from '@joplin/lib/services/plugins/api/types';
import PluginService from '@joplin/lib/services/plugins/PluginService';
import WebviewController from '@joplin/lib/services/plugins/WebviewController';
import AsyncActionQueue, { IntervalType } from '@joplin/lib/AsyncActionQueue';

const debounce = require('debounce');

Expand All @@ -69,6 +73,15 @@ const toolbarButtonUtils = new ToolbarButtonUtils(CommandService.instance());
const onDragOver: React.DragEventHandler = event => event.preventDefault();
let editorIdCounter = 0;

const makeNoteUpdateAction = (shownEditorViewIds: string[]) => {
return async () => {
for (const viewId of shownEditorViewIds) {
const controller = PluginService.instance().viewControllerByViewId(viewId) as WebviewController;
if (controller) controller.emitUpdate();
}
};
};

function NoteEditorContent(props: NoteEditorProps) {
const [showRevisions, setShowRevisions] = useState(false);
const [titleHasBeenManuallyChanged, setTitleHasBeenManuallyChanged] = useState(false);
Expand All @@ -78,6 +91,9 @@ function NoteEditorContent(props: NoteEditorProps) {
const titleInputRef = useRef<HTMLInputElement>();
const isMountedRef = useRef(true);
const noteSearchBarRef = useRef(null);
const viewUpdateAsyncQueue_ = useRef<AsyncActionQueue>(new AsyncActionQueue(100, IntervalType.Fixed));

const shownEditorViewIds = props['plugins.shownEditorViewIds'];

// Should be constant and unique to this instance of the editor.
const editorId = useMemo(() => {
Expand All @@ -99,7 +115,27 @@ function NoteEditorContent(props: NoteEditorProps) {

const effectiveNoteId = useEffectiveNoteId(props);

const { editorPlugin, editorView } = usePluginEditorView(props.plugins, props['plugins.shownEditorViewIds']);
useAsyncEffect(async (event) => {
if (!props.startupPluginsLoaded) return;

let filterObject: EditorActivationCheckFilterObject = {
activatedEditors: [],
};
filterObject = await eventManager.filterEmit('editorActivationCheck', filterObject);
if (event.cancelled) return;

for (const editor of filterObject.activatedEditors) {
const controller = PluginService.instance().pluginById(editor.pluginId).viewController(editor.viewId) as WebviewController;
controller.setActive(editor.isActive);
}
}, [effectiveNoteId, props.startupPluginsLoaded]);

useEffect(() => {
if (!props.startupPluginsLoaded) return;
viewUpdateAsyncQueue_.current.push(makeNoteUpdateAction(shownEditorViewIds));
}, [effectiveNoteId, shownEditorViewIds, props.startupPluginsLoaded]);

const { editorPlugin, editorView } = usePluginEditorView(props.plugins, shownEditorViewIds);
const builtInEditorVisible = !editorPlugin;

const { formNote, setFormNote, isNewNote, resourceInfos } = useFormNote({
Expand Down
1 change: 1 addition & 0 deletions packages/app-desktop/gui/NoteEditor/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export interface NoteEditorProps {
'plugins.shownEditorViewIds': string[];
onTitleChange?: (title: string)=> void;
bodyEditor: string;
startupPluginsLoaded: boolean;
}

export interface NoteBodyEditorRef {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import * as restoreNote from './restoreNote';
import * as revealResourceFile from './revealResourceFile';
import * as search from './search';
import * as setTags from './setTags';
import * as showEditorPlugin from './showEditorPlugin';
import * as showModalMessage from './showModalMessage';
import * as showNoteContentProperties from './showNoteContentProperties';
import * as showNoteProperties from './showNoteProperties';
Expand Down Expand Up @@ -77,6 +78,7 @@ const index: any[] = [
revealResourceFile,
search,
setTags,
showEditorPlugin,
showModalMessage,
showNoteContentProperties,
showNoteProperties,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { CommandContext, CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
import Setting from '@joplin/lib/models/Setting';
import getActivePluginEditorView from '@joplin/lib/services/plugins/utils/getActivePluginEditorView';
import Logger from '@joplin/utils/Logger';

const logger = Logger.create('showEditorPlugin');

export const declaration: CommandDeclaration = {
name: 'showEditorPlugin',
label: () => 'Show editor plugin',
iconName: 'fas fa-eye',
};

export const runtime = (): CommandRuntime => {
return {
execute: async (context: CommandContext, editorViewId = '', show = true) => {
logger.info('View:', editorViewId, 'Show:', show);

const shownEditorViewIds = Setting.value('plugins.shownEditorViewIds');

if (!editorViewId) {
const { editorPlugin, editorView } = getActivePluginEditorView(context.state.pluginService.plugins);

if (!editorPlugin) {
logger.warn('No editor plugin to toggle to');
return;
}

editorViewId = editorView.id;
}

const idx = shownEditorViewIds.indexOf(editorViewId);

if (show) {
if (idx >= 0) {
logger.info(`Editor is already visible: ${editorViewId}`);
return;
}

shownEditorViewIds.push(editorViewId);
} else {
if (idx < 0) {
logger.info(`Editor is already hidden: ${editorViewId}`);
return;
}

shownEditorViewIds.splice(idx, 1);
}

Setting.setValue('plugins.shownEditorViewIds', shownEditorViewIds);
},
};
};
4 changes: 4 additions & 0 deletions packages/lib/services/plugins/Plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,10 @@ export default class Plugin {
this.viewControllers_[v.handle] = v;
}

public hasViewController(handle: ViewHandle) {
return !!this.viewControllers_[handle];
}

public viewController(handle: ViewHandle): ViewController {
if (!this.viewControllers_[handle]) throw new Error(`View not found: ${handle}`);
return this.viewControllers_[handle];
Expand Down
8 changes: 8 additions & 0 deletions packages/lib/services/plugins/PluginService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import isCompatible from './utils/isCompatible';
import { AppType } from './api/types';
import minVersionForPlatform from './utils/isCompatible/minVersionForPlatform';
import { _ } from '../../locale';
import ViewController from './ViewController';
const uslug = require('@joplin/fork-uslug');

const logger = Logger.create('PluginService');
Expand Down Expand Up @@ -202,6 +203,13 @@ export default class PluginService extends BaseService {
return this.plugins_[id];
}

public viewControllerByViewId(id: string): ViewController|null {
for (const [, plugin] of Object.entries(this.plugins_)) {
if (plugin.hasViewController(id)) return plugin.viewController(id);
}
return null;
}

public unserializePluginSettings(settings: SerializedPluginSettings): PluginSettings {
const output = { ...settings };

Expand Down
41 changes: 39 additions & 2 deletions packages/lib/services/plugins/WebviewController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ const { toSystemSlashes } = require('../../path-utils');
import PostMessageService, { MessageParticipant } from '../PostMessageService';
import { PluginViewState } from './reducer';
import { defaultWindowId } from '../../reducer';
import Logger from '@joplin/utils/Logger';
import CommandService from '../CommandService';

const logger = Logger.create('WebviewController');

export enum ContainerType {
Panel = 'panel',
Expand Down Expand Up @@ -50,12 +54,15 @@ export default class WebviewController extends ViewController {
private baseDir_: string;
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
private messageListener_: Function = null;
private updateListener_: ()=> void = null;
private closeResponse_: CloseResponse = null;
private containerType_: ContainerType = null;

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public constructor(handle: ViewHandle, pluginId: string, store: any, baseDir: string, containerType: ContainerType) {
super(handle, pluginId, store);
this.baseDir_ = toSystemSlashes(baseDir, 'linux');
this.containerType_ = containerType;

const view: PluginViewState = {
id: this.handle,
Expand Down Expand Up @@ -138,16 +145,38 @@ export default class WebviewController extends ViewController {

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async emitMessage(event: EmitMessageEvent): Promise<any> {

if (!this.messageListener_) return;

if (this.containerType_ === ContainerType.Editor && !this.isActive()) {
logger.info('emitMessage: Not emitting message because editor is disabled:', this.pluginId, this.handle);
return;
}

return this.messageListener_(event.message);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public emitUpdate() {
if (!this.updateListener_) return;

if (this.containerType_ === ContainerType.Editor && (!this.isActive() || !this.isVisible())) {
logger.info('emitMessage: Not emitting update because editor is disabled or hidden:', this.pluginId, this.handle, this.isActive(), this.isVisible());
return;
}

this.updateListener_();
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public onMessage(callback: any) {
this.messageListener_ = callback;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public onUpdate(callback: any) {
this.updateListener_ = callback;
}

// ---------------------------------------------
// Specific to panels
// ---------------------------------------------
Expand Down Expand Up @@ -244,14 +273,22 @@ export default class WebviewController extends ViewController {
// Specific to editors
// ---------------------------------------------

public async setActive(active: boolean): Promise<void> {
public setActive(active: boolean) {
this.setStoreProp('opened', active);
}

public isActive(): boolean {
return this.storeView.opened;
}

public async isVisible(): Promise<boolean> {
if (!this.storeView.opened) return false;
const shownEditorViewIds: string[] = this.store.getState().settings['plugins.shownEditorViewIds'];
return shownEditorViewIds.includes(this.handle);
}

public async setVisible(visible: boolean) {
await CommandService.instance().execute('showEditorPlugin', this.handle, visible);
}

}
27 changes: 26 additions & 1 deletion packages/lib/services/plugins/api/JoplinViewsEditor.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/* eslint-disable multiline-comment-style */

import eventManager from '../../../eventManager';
import Plugin from '../Plugin';
import createViewHandle from '../utils/createViewHandle';
import WebviewController, { ContainerType } from '../WebviewController';
import { ViewHandle } from './types';
import { ActivationCheckCallback, EditorActivationCheckFilterObject, FilterHandler, ViewHandle, UpdateCallback } from './types';

/**
* Allows creating alternative note editors. When `setActive` is called, this view is going to
Expand All @@ -27,6 +28,7 @@ export default class JoplinViewsEditors {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
private store: any;
private plugin: Plugin;
private activationCheckHandlers_: Record<string, FilterHandler<EditorActivationCheckFilterObject>> = {};

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public constructor(plugin: Plugin, store: any) {
Expand Down Expand Up @@ -70,6 +72,29 @@ export default class JoplinViewsEditors {
return this.controller(handle).onMessage(callback);
}

public async onActivationCheck(handle: ViewHandle, callback: ActivationCheckCallback): Promise<void> {
const handler: FilterHandler<EditorActivationCheckFilterObject> = async (object) => {
const isActive = await callback();
object.activatedEditors.push({
pluginId: this.plugin.id,
viewId: handle,
isActive: isActive,
});
return object;
};

this.activationCheckHandlers_[handle] = handler;

eventManager.filterOn('editorActivationCheck', this.activationCheckHandlers_[handle]);
this.plugin.addOnUnloadListener(() => {
eventManager.filterOff('editorActivationCheck', this.activationCheckHandlers_[handle]);
});
}

public async onUpdate(handle: ViewHandle, callback: UpdateCallback): Promise<void> {
this.controller(handle).onUpdate(callback);
}

/**
* See [[JoplinViewPanels]]
*/
Expand Down
4 changes: 4 additions & 0 deletions packages/lib/services/plugins/api/JoplinViewsPanels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,8 @@ export default class JoplinViewsPanels {
return this.controller(handle).visible;
}

public async isActive(handle: ViewHandle): Promise<boolean> {
return this.controller(handle).isActive();
}

}
Loading

0 comments on commit 560c519

Please sign in to comment.