mirror of
https://github.com/laurent22/joplin.git
synced 2025-02-13 19:42:36 +02:00
This commit is contained in:
parent
6220267abb
commit
ac154ee1e8
@ -295,6 +295,7 @@ packages/app-desktop/gui/NoteEditor/utils/useNoteSearchBar.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/usePluginEditorView.test.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/usePluginEditorView.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/usePluginServiceRegistration.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useResourceUnwatcher.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useScheduleSaveCallbacks.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useScrollWhenReadyOptions.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useSearchMarkers.js
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -270,6 +270,7 @@ packages/app-desktop/gui/NoteEditor/utils/useNoteSearchBar.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/usePluginEditorView.test.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/usePluginEditorView.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/usePluginServiceRegistration.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useResourceUnwatcher.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useScheduleSaveCallbacks.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useScrollWhenReadyOptions.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useSearchMarkers.js
|
||||
|
@ -40,6 +40,8 @@ export interface AppWindowState extends WindowState {
|
||||
visibleDialogs: VisibleDialogs;
|
||||
dialogs: AppStateDialog[];
|
||||
devToolsVisible: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
watchedResources: any;
|
||||
}
|
||||
|
||||
interface BackgroundWindowStates {
|
||||
@ -62,8 +64,6 @@ export interface AppState extends State, AppWindowState {
|
||||
modalOverlayMessage: string|null;
|
||||
|
||||
// Extra reducer keys go here
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
watchedResources: any;
|
||||
mainLayout: LayoutItem;
|
||||
isResettingLayout: boolean;
|
||||
}
|
||||
@ -76,6 +76,7 @@ export const createAppDefaultWindowState = (): AppWindowState => {
|
||||
noteVisiblePanes: ['editor', 'viewer'],
|
||||
editorCodeView: true,
|
||||
devToolsVisible: false,
|
||||
watchedResources: {},
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -653,6 +653,7 @@ class Application extends BaseApplication {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
(action: any) => { this.store().dispatch(action); },
|
||||
(path: string) => bridge().openItem(path),
|
||||
() => this.store().getState().windowId,
|
||||
);
|
||||
|
||||
// Forwards the local event to the global event manager, so that it can
|
||||
|
@ -59,6 +59,7 @@ import { EditorActivationCheckFilterObject } from '@joplin/lib/services/plugins/
|
||||
import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
import WebviewController from '@joplin/lib/services/plugins/WebviewController';
|
||||
import AsyncActionQueue, { IntervalType } from '@joplin/lib/AsyncActionQueue';
|
||||
import useResourceUnwatcher from './utils/useResourceUnwatcher';
|
||||
|
||||
const debounce = require('debounce');
|
||||
|
||||
@ -358,6 +359,8 @@ function NoteEditorContent(props: NoteEditorProps) {
|
||||
const windowId = useContext(WindowIdContext);
|
||||
const onMessage = useMessageHandler(scrollWhenReady, clearScrollWhenReady, windowId, editorRef, setLocalSearchResultCount, props.dispatch, formNote, htmlToMarkdown, markupToHtml);
|
||||
|
||||
useResourceUnwatcher({ noteId: formNote.id, windowId });
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
const externalEditWatcher_noteChange = useCallback((event: any) => {
|
||||
if (event.id === formNote.id) {
|
||||
@ -729,7 +732,7 @@ const mapStateToProps = (state: AppState, ownProps: ConnectProps) => {
|
||||
selectedSearchId: windowState.selectedSearchId,
|
||||
customCss: state.customViewerCss,
|
||||
noteVisiblePanes: windowState.noteVisiblePanes,
|
||||
watchedResources: state.watchedResources,
|
||||
watchedResources: windowState.watchedResources,
|
||||
highlightedWords: state.highlightedWords,
|
||||
plugins: state.pluginService.plugins,
|
||||
pluginHtmlContents: state.pluginService.pluginHtmlContents,
|
||||
|
@ -0,0 +1,21 @@
|
||||
import ResourceEditWatcher from '@joplin/lib/services/ResourceEditWatcher';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
interface Props {
|
||||
noteId: string;
|
||||
windowId: string;
|
||||
}
|
||||
|
||||
const useResourceUnwatcher = ({ noteId, windowId }: Props) => {
|
||||
useEffect(() => {
|
||||
// All resources associated with the current window should no longer be watched after:
|
||||
// 1. The editor unloads, or
|
||||
// 2. The note shown in the editor changes.
|
||||
// Unwatching in a cleanup callback handles both cases.
|
||||
return () => {
|
||||
void ResourceEditWatcher.instance().stopWatchingAll(windowId);
|
||||
};
|
||||
}, [noteId, windowId]);
|
||||
};
|
||||
|
||||
export default useResourceUnwatcher;
|
@ -1,7 +1,6 @@
|
||||
import { RefObject, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { NoteBodyEditorRef, ScrollOptions, ScrollOptionTypes } from './types';
|
||||
import usePrevious from '@joplin/lib/hooks/usePrevious';
|
||||
import ResourceEditWatcher from '@joplin/lib/services/ResourceEditWatcher';
|
||||
import type { EditorScrollPercents } from '../../../app.reducer';
|
||||
|
||||
interface Props {
|
||||
@ -30,8 +29,6 @@ const useScrollWhenReadyOptions = ({ noteId, selectedNoteHash, lastEditorScrollP
|
||||
type: selectedNoteHash ? ScrollOptionTypes.Hash : ScrollOptionTypes.Percent,
|
||||
value: selectedNoteHash ? selectedNoteHash : lastScrollPercent,
|
||||
});
|
||||
|
||||
void ResourceEditWatcher.instance().stopWatchingAll();
|
||||
}, [noteId, previousNoteId, selectedNoteHash, editorRef]);
|
||||
|
||||
const clearScrollWhenReady = useCallback(() => {
|
||||
|
@ -9,10 +9,14 @@ import { ResourceEntity } from '../database/types';
|
||||
const EventEmitter = require('events');
|
||||
const chokidar = require('chokidar');
|
||||
|
||||
type WindowId = string;
|
||||
|
||||
interface WatchedItem {
|
||||
resourceId: string;
|
||||
title: string;
|
||||
lastFileUpdatedTime: number;
|
||||
lastResourceUpdatedTime: number;
|
||||
watchedByWindows: WindowId[];
|
||||
path: string;
|
||||
asyncSaveQueue: AsyncActionQueue;
|
||||
size: number;
|
||||
@ -23,6 +27,7 @@ interface WatchedItems {
|
||||
}
|
||||
|
||||
type OpenItemFn = (path: string)=> void;
|
||||
type GetWindowIdFn = ()=> string;
|
||||
|
||||
export default class ResourceEditWatcher {
|
||||
|
||||
@ -41,6 +46,7 @@ export default class ResourceEditWatcher {
|
||||
private eventEmitter_: any;
|
||||
private tempDir_ = '';
|
||||
private openItem_: OpenItemFn;
|
||||
private getActiveWindowId_: GetWindowIdFn;
|
||||
|
||||
public constructor() {
|
||||
this.logger_ = new Logger();
|
||||
@ -51,10 +57,11 @@ export default class ResourceEditWatcher {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any -- Old code before rule was applied, Old code before rule was applied
|
||||
public initialize(logger: any, dispatch: Function, openItem: OpenItemFn) {
|
||||
public initialize(logger: any, dispatch: Function, openItem: OpenItemFn, getWindowId: GetWindowIdFn) {
|
||||
this.logger_ = logger;
|
||||
this.dispatch = dispatch;
|
||||
this.openItem_ = openItem;
|
||||
this.getActiveWindowId_ = getWindowId;
|
||||
}
|
||||
|
||||
public static instance() {
|
||||
@ -239,6 +246,7 @@ export default class ResourceEditWatcher {
|
||||
}
|
||||
|
||||
private async watch(resourceId: string): Promise<WatchedItem> {
|
||||
const sourceWindowId = this.getActiveWindowId_();
|
||||
let watchedItem = this.watchedItemByResourceId(resourceId);
|
||||
|
||||
if (!watchedItem) {
|
||||
@ -246,8 +254,10 @@ export default class ResourceEditWatcher {
|
||||
|
||||
watchedItem = {
|
||||
resourceId: resourceId,
|
||||
title: '',
|
||||
lastFileUpdatedTime: 0,
|
||||
lastResourceUpdatedTime: 0,
|
||||
watchedByWindows: [sourceWindowId],
|
||||
asyncSaveQueue: new AsyncActionQueue(1000),
|
||||
path: '',
|
||||
size: -1,
|
||||
@ -261,6 +271,10 @@ export default class ResourceEditWatcher {
|
||||
watchedItem.lastFileUpdatedTime = stat.mtime.getTime();
|
||||
watchedItem.lastResourceUpdatedTime = resource.updated_time;
|
||||
watchedItem.size = stat.size;
|
||||
watchedItem.title = resource.title;
|
||||
// Reset the watching window list to handle the case where the active window
|
||||
// was changed while loading the resource.
|
||||
watchedItem.watchedByWindows = [this.getActiveWindowId_()];
|
||||
|
||||
this.watchFile(editFilePath);
|
||||
|
||||
@ -269,6 +283,18 @@ export default class ResourceEditWatcher {
|
||||
id: resource.id,
|
||||
title: resource.title,
|
||||
});
|
||||
} else if (!watchedItem.watchedByWindows.includes(sourceWindowId)) {
|
||||
watchedItem = {
|
||||
...this.watchedItems_[resourceId],
|
||||
watchedByWindows: [...watchedItem.watchedByWindows, sourceWindowId],
|
||||
};
|
||||
this.watchedItems_[resourceId] = watchedItem;
|
||||
|
||||
this.dispatch({
|
||||
type: 'RESOURCE_EDIT_WATCHER_SET',
|
||||
id: watchedItem.resourceId,
|
||||
title: watchedItem.title,
|
||||
});
|
||||
}
|
||||
|
||||
this.logger().info(`ResourceEditWatcher: Started watching ${watchedItem.path}`);
|
||||
@ -318,16 +344,26 @@ export default class ResourceEditWatcher {
|
||||
this.logger().info(`ResourceEditWatcher: Stopped watching ${item.path}`);
|
||||
}
|
||||
|
||||
public async stopWatchingAll() {
|
||||
public async stopWatchingAll(sourceWindow: string) {
|
||||
const promises = [];
|
||||
for (const resourceId in this.watchedItems_) {
|
||||
const item = this.watchedItems_[resourceId];
|
||||
promises.push(this.stopWatching(item.resourceId));
|
||||
let item = this.watchedItems_[resourceId];
|
||||
|
||||
if (item.watchedByWindows.includes(sourceWindow)) {
|
||||
const otherWatchingWindows = item.watchedByWindows.filter(id => id !== sourceWindow);
|
||||
item = { ...item, watchedByWindows: otherWatchingWindows };
|
||||
this.watchedItems_[resourceId] = item;
|
||||
}
|
||||
|
||||
if (item.watchedByWindows.length === 0) {
|
||||
promises.push(this.stopWatching(item.resourceId));
|
||||
}
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
this.dispatch({
|
||||
type: 'RESOURCE_EDIT_WATCHER_CLEAR',
|
||||
windowId: sourceWindow,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import produce, { Draft } from 'immer';
|
||||
import { defaultWindowId, stateUtils } from '../../reducer';
|
||||
|
||||
export const defaultState = {
|
||||
watchedResources: {},
|
||||
@ -20,14 +21,28 @@ const reducer = produce((draft: Draft<any>, action: any) => {
|
||||
break;
|
||||
|
||||
case 'RESOURCE_EDIT_WATCHER_REMOVE':
|
||||
// RESOURCE_EDIT_WATCHER_REMOVE signals that a resource is no longer being watched.
|
||||
// As such, it should be removed from all windows' resource lists:
|
||||
for (const windowId in draft.backgroundWindows) {
|
||||
// watchedResources is per-window only on desktop:
|
||||
if ('watchedResources' in draft.backgroundWindows[windowId]) {
|
||||
delete draft.backgroundWindows[windowId].watchedResources[action.id];
|
||||
}
|
||||
}
|
||||
|
||||
delete draft.watchedResources[action.id];
|
||||
break;
|
||||
|
||||
case 'RESOURCE_EDIT_WATCHER_CLEAR':
|
||||
case 'RESOURCE_EDIT_WATCHER_CLEAR': {
|
||||
const windowState = stateUtils.windowStateById(draft, action.windowId ?? defaultWindowId);
|
||||
|
||||
draft.watchedResources = {};
|
||||
// The window may have already been closed.
|
||||
const windowExists = !!windowState;
|
||||
if (windowExists) {
|
||||
windowState.watchedResources = {};
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
|
@ -155,4 +155,5 @@ tablist
|
||||
Edubirdie
|
||||
Useviral
|
||||
ldaps
|
||||
Bluesky
|
||||
Bluesky
|
||||
unwatcher
|
||||
|
Loading…
x
Reference in New Issue
Block a user