mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Desktop: Fixes #3407: In some cases, changes made to an attachment would not be saved. Also added banner to show that an attachment is being edited
This commit is contained in:
parent
f3dc3602c8
commit
4bef79cd71
@ -151,7 +151,8 @@ ReactNativeClient/lib/services/keychain/KeychainServiceDriver.dummy.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.mobile.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.node.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriverBase.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/index.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/reducer.js
|
||||
ReactNativeClient/lib/services/rest/actionApi.desktop.js
|
||||
ReactNativeClient/lib/services/rest/errors.js
|
||||
ReactNativeClient/lib/services/SettingUtils.js
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -142,7 +142,8 @@ ReactNativeClient/lib/services/keychain/KeychainServiceDriver.dummy.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.mobile.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.node.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriverBase.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/index.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/reducer.js
|
||||
ReactNativeClient/lib/services/rest/actionApi.desktop.js
|
||||
ReactNativeClient/lib/services/rest/errors.js
|
||||
ReactNativeClient/lib/services/SettingUtils.js
|
||||
|
@ -21,7 +21,7 @@ const InteropServiceHelper = require('./InteropServiceHelper.js');
|
||||
const ResourceService = require('lib/services/ResourceService');
|
||||
const ClipperServer = require('lib/ClipperServer');
|
||||
const ExternalEditWatcher = require('lib/services/ExternalEditWatcher');
|
||||
const ResourceEditWatcher = require('lib/services/ResourceEditWatcher').default;
|
||||
const ResourceEditWatcher = require('lib/services/ResourceEditWatcher/index').default;
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const { shell, webFrame, clipboard } = require('electron');
|
||||
const Menu = bridge().Menu;
|
||||
@ -1265,7 +1265,7 @@ class Application extends BaseApplication {
|
||||
ExternalEditWatcher.instance().setLogger(reg.logger());
|
||||
ExternalEditWatcher.instance().dispatch = this.store().dispatch;
|
||||
|
||||
ResourceEditWatcher.instance().initialize(reg.logger(), this.store().dispatch);
|
||||
ResourceEditWatcher.instance().initialize(reg.logger(), (action) => { console.info('ACTION', action); this.store().dispatch(action); });
|
||||
|
||||
RevisionService.instance().runInBackground();
|
||||
|
||||
|
@ -17,7 +17,7 @@ import useMarkupToHtml from './utils/useMarkupToHtml';
|
||||
import useFormNote, { OnLoadEvent } from './utils/useFormNote';
|
||||
import styles_ from './styles';
|
||||
import { NoteEditorProps, FormNote, ScrollOptions, ScrollOptionTypes, OnChangeEvent, NoteBodyEditorProps } from './utils/types';
|
||||
import ResourceEditWatcher from '../../lib/services/ResourceEditWatcher';
|
||||
import ResourceEditWatcher from '../../lib/services/ResourceEditWatcher/index';
|
||||
import CommandService from '../../lib/services/CommandService';
|
||||
|
||||
const { themeStyle } = require('lib/theme');
|
||||
@ -480,6 +480,17 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function renderResourceWatchingNotification() {
|
||||
if (!Object.keys(props.watchedResources).length) return null;
|
||||
const resourceTitles = Object.keys(props.watchedResources).map(id => props.watchedResources[id].title);
|
||||
return (
|
||||
<div style={styles.resourceWatchBanner}>
|
||||
<p style={styles.resourceWatchBannerLine}>{_('The following attachments are being watched for changes:')} <strong>{resourceTitles.join(', ')}</strong></p>
|
||||
<p style={{ ...styles.resourceWatchBannerLine, marginBottom: 0 }}>{_('The attachments will no longer be watched when you switch to a different note.')}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (formNote.encryption_applied || !formNote.id || !props.noteId) {
|
||||
return renderNoNotes(styles.root);
|
||||
}
|
||||
@ -487,6 +498,7 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
return (
|
||||
<div style={styles.root} onDrop={onDrop}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
||||
{renderResourceWatchingNotification()}
|
||||
{renderTitleBar()}
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
{renderNoteToolbar()}{renderTagBar()}
|
||||
@ -528,6 +540,7 @@ const mapStateToProps = (state: any) => {
|
||||
selectedSearchId: state.selectedSearchId,
|
||||
customCss: state.customCss,
|
||||
noteVisiblePanes: state.noteVisiblePanes,
|
||||
watchedResources: state.watchedResources,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -53,6 +53,18 @@ export default function styles(props: NoteEditorProps) {
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
},
|
||||
resourceWatchBanner: {
|
||||
...theme.textStyle,
|
||||
padding: 10,
|
||||
marginLeft: 5,
|
||||
marginBottom: 10,
|
||||
color: theme.colorWarn,
|
||||
backgroundColor: theme.warningBackgroundColor,
|
||||
},
|
||||
resourceWatchBannerLine: {
|
||||
marginTop: 0,
|
||||
marginBottom: 10,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import ResourceEditWatcher from '../../../lib/services/ResourceEditWatcher';
|
||||
import ResourceEditWatcher from '../../../lib/services/ResourceEditWatcher/index';
|
||||
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const Menu = bridge().Menu;
|
||||
|
@ -22,6 +22,7 @@ export interface NoteEditorProps {
|
||||
selectedSearchId: string,
|
||||
customCss: string,
|
||||
noteVisiblePanes: string[],
|
||||
watchedResources: any,
|
||||
}
|
||||
|
||||
export interface NoteBodyEditorProps {
|
||||
|
@ -11,7 +11,7 @@ const Setting = require('lib/models/Setting');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const ResourceFetcher = require('lib/services/ResourceFetcher.js');
|
||||
const DecryptionWorker = require('lib/services/DecryptionWorker.js');
|
||||
const ResourceEditWatcher = require('lib/services/ResourceEditWatcher.js').default;
|
||||
const ResourceEditWatcher = require('lib/services/ResourceEditWatcher/index').default;
|
||||
|
||||
export interface OnLoadEvent {
|
||||
formNote: FormNote,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useCallback } from 'react';
|
||||
import { FormNote } from './types';
|
||||
import contextMenu from './contextMenu';
|
||||
import ResourceEditWatcher from '../../../lib/services/ResourceEditWatcher';
|
||||
import ResourceEditWatcher from '../../../lib/services/ResourceEditWatcher/index';
|
||||
const BaseItem = require('lib/models/BaseItem');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
|
57
ElectronClient/package-lock.json
generated
57
ElectronClient/package-lock.json
generated
@ -1482,15 +1482,13 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
|
||||
"integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"is-glob": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
|
||||
"integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"is-extglob": "^1.0.0"
|
||||
}
|
||||
@ -2032,8 +2030,7 @@
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.1.tgz",
|
||||
"integrity": "sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"boxen": {
|
||||
"version": "4.2.0",
|
||||
@ -5095,15 +5092,13 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
|
||||
"integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"is-glob": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
|
||||
"integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"is-extglob": "^1.0.0"
|
||||
}
|
||||
@ -5271,8 +5266,7 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@ -5321,8 +5315,7 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
@ -5335,8 +5328,7 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@ -5467,8 +5459,7 @@
|
||||
"version": "2.0.4",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@ -5482,7 +5473,6 @@
|
||||
"resolved": false,
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@ -5508,15 +5498,13 @@
|
||||
"version": "0.0.8",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.9.0",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
@ -5537,7 +5525,6 @@
|
||||
"resolved": false,
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@ -5636,8 +5623,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@ -5651,7 +5637,6 @@
|
||||
"resolved": false,
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@ -5747,8 +5732,7 @@
|
||||
"version": "5.1.2",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@ -5790,7 +5774,6 @@
|
||||
"resolved": false,
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@ -5812,7 +5795,6 @@
|
||||
"resolved": false,
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@ -5861,15 +5843,13 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -6490,6 +6470,11 @@
|
||||
"file-type": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"immer": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-7.0.5.tgz",
|
||||
"integrity": "sha512-TtRAKZyuqld2eYjvWgXISLJ0ZlOl1OOTzRmrmiY8SlB0dnAhZ1OiykIDL5KDFNaPHDXiLfGQFNJGtet8z8AEmg=="
|
||||
},
|
||||
"import-fresh": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
|
||||
@ -8833,8 +8818,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
|
||||
"integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"is-glob": {
|
||||
"version": "2.0.1",
|
||||
@ -10047,11 +10031,6 @@
|
||||
"through2": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"remove-markdown": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/remove-markdown/-/remove-markdown-0.3.0.tgz",
|
||||
"integrity": "sha1-XktmdJOpNXlyjz1S7MHbnKUF3Jg="
|
||||
},
|
||||
"remove-trailing-separator": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
|
||||
|
@ -125,6 +125,7 @@
|
||||
"html-minifier": "^4.0.0",
|
||||
"htmlparser2": "^4.1.0",
|
||||
"image-type": "^3.0.0",
|
||||
"immer": "^7.0.5",
|
||||
"joplin-turndown": "^4.0.28",
|
||||
"joplin-turndown-plugin-gfm": "^1.0.12",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
|
@ -632,7 +632,13 @@ class BaseApplication {
|
||||
SyncTargetRegistry.addClass(SyncTargetDropbox);
|
||||
SyncTargetRegistry.addClass(SyncTargetAmazonS3);
|
||||
|
||||
try {
|
||||
await shim.fsDriver().remove(tempDir);
|
||||
} catch (error) {
|
||||
// Can't do anything in this case, not even log, since the logger
|
||||
// is not yet ready. But normally it's not an issue if the temp
|
||||
// dir cannot be deleted.
|
||||
}
|
||||
|
||||
await fs.mkdirp(profileDir, 0o755);
|
||||
await fs.mkdirp(resourceDir, 0o755);
|
||||
|
@ -3,6 +3,7 @@ const Folder = require('lib/models/Folder.js');
|
||||
const ArrayUtils = require('lib/ArrayUtils.js');
|
||||
const { ALL_NOTES_FILTER_ID } = require('lib/reserved-ids');
|
||||
const CommandService = require('lib/services/CommandService').default;
|
||||
const resourceEditWatcherReducer = require('lib/services/ResourceEditWatcher/reducer').default;
|
||||
|
||||
const defaultState = {
|
||||
notes: [],
|
||||
@ -1057,6 +1058,8 @@ const reducer = (state = defaultState, action) => {
|
||||
newState = handleHistory(newState, action);
|
||||
}
|
||||
|
||||
newState = resourceEditWatcherReducer(newState, action);
|
||||
|
||||
CommandService.instance().scheduleMapStateToProps(newState);
|
||||
|
||||
return newState;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AsyncActionQueue from '../AsyncActionQueue';
|
||||
import AsyncActionQueue from '../../AsyncActionQueue';
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Setting = require('lib/models/Setting');
|
||||
const Resource = require('lib/models/Resource');
|
||||
@ -25,7 +25,7 @@ export default class ResourceEditWatcher {
|
||||
private static instance_:ResourceEditWatcher;
|
||||
|
||||
private logger_:any;
|
||||
// private dispatch:Function;
|
||||
private dispatch:Function;
|
||||
private watcher_:any;
|
||||
private chokidar_:any;
|
||||
private watchedItems_:WatchedItems = {};
|
||||
@ -34,15 +34,15 @@ export default class ResourceEditWatcher {
|
||||
|
||||
constructor() {
|
||||
this.logger_ = new Logger();
|
||||
// this.dispatch = () => {};
|
||||
this.dispatch = () => {};
|
||||
this.watcher_ = null;
|
||||
this.chokidar_ = chokidar;
|
||||
this.eventEmitter_ = new EventEmitter();
|
||||
}
|
||||
|
||||
initialize(logger:any/* , dispatch:Function*/) {
|
||||
initialize(logger:any, dispatch:Function) {
|
||||
this.logger_ = logger;
|
||||
// this.dispatch = dispatch;
|
||||
this.dispatch = dispatch;
|
||||
}
|
||||
|
||||
static instance() {
|
||||
@ -95,6 +95,41 @@ export default class ResourceEditWatcher {
|
||||
};
|
||||
};
|
||||
|
||||
const handleChangeEvent = async (path:string) => {
|
||||
this.logger().debug('ResourceEditWatcher: handleChangeEvent: ' + path);
|
||||
|
||||
const watchedItem = this.watchedItemByPath(path);
|
||||
|
||||
if (!watchedItem) {
|
||||
// The parent directory of the edited resource often gets a change event too
|
||||
// and ends up here. Print a warning, but most likely it's nothing important.
|
||||
this.logger().debug(`ResourceEditWatcher: could not find resource ID from path: ${path}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const resourceId = watchedItem.resourceId;
|
||||
const stat = await shim.fsDriver().stat(path);
|
||||
const editedFileUpdatedTime = stat.mtime.getTime();
|
||||
|
||||
if (watchedItem.lastFileUpdatedTime === editedFileUpdatedTime) {
|
||||
// chokidar is buggy and emits "change" events even when nothing has changed
|
||||
// so double-check the modified time and skip processing if there's no change.
|
||||
// In particular it emits two such events just after the file has been copied
|
||||
// in openAndWatch().
|
||||
//
|
||||
// We also need this because some events are handled twice - once in the "all" event
|
||||
// handle and once in the "raw" event handler, due to a bug in chokidar. So having
|
||||
// this check means we don't unecessarily save the resource twice when the file is
|
||||
// modified by the user.
|
||||
this.logger().debug(`ResourceEditWatcher: No timestamp change - skip: ${resourceId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger().debug(`ResourceEditWatcher: Queuing save action: ${resourceId}`);
|
||||
watchedItem.asyncSaveQueue.push(makeSaveAction(resourceId, path));
|
||||
watchedItem.lastFileUpdatedTime = editedFileUpdatedTime;
|
||||
}
|
||||
|
||||
if (!this.watcher_) {
|
||||
this.watcher_ = this.chokidar_.watch(fileToWatch);
|
||||
this.watcher_.on('all', async (event:any, path:string) => {
|
||||
@ -108,41 +143,27 @@ export default class ResourceEditWatcher {
|
||||
// See: https://github.com/laurent22/joplin/issues/710#issuecomment-420997167
|
||||
// this.watcher_.unwatch(path);
|
||||
} else if (event === 'change') {
|
||||
const watchedItem = this.watchedItemByPath(path);
|
||||
const resourceId = watchedItem.resourceId;
|
||||
|
||||
if (!watchedItem) {
|
||||
this.logger().error(`ResourceEditWatcher: could not find resource ID from path: ${path}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const stat = await shim.fsDriver().stat(path);
|
||||
const editedFileUpdatedTime = stat.mtime.getTime();
|
||||
|
||||
if (watchedItem.lastFileUpdatedTime === editedFileUpdatedTime) {
|
||||
// chokidar is buggy and emits "change" events even when nothing has changed
|
||||
// so double-check the modified time and skip processing if there's no change.
|
||||
// In particular it emits two such events just after the file has been copied
|
||||
// in openAndWatch().
|
||||
this.logger().debug(`ResourceEditWatcher: No timestamp change - skip: ${resourceId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger().debug(`ResourceEditWatcher: Queuing save action: ${resourceId}`);
|
||||
|
||||
watchedItem.asyncSaveQueue.push(makeSaveAction(resourceId, path));
|
||||
watchedItem.lastFileUpdatedTime = editedFileUpdatedTime;
|
||||
handleChangeEvent(path);
|
||||
} else if (event === 'error') {
|
||||
this.logger().error('ResourceEditWatcher: error');
|
||||
}
|
||||
});
|
||||
|
||||
// Hack to support external watcher on some linux applications (gedit, gvim, etc)
|
||||
// taken from https://github.com/paulmillr/chokidar/issues/591
|
||||
//
|
||||
// 2020-07-22: It also applies when editing Excel files, which copy the new file
|
||||
// then rename, so handling the "change" event alone is not enough as sometimes
|
||||
// that event is not event triggered.
|
||||
// https://github.com/laurent22/joplin/issues/3407
|
||||
//
|
||||
// @ts-ignore Leave unused path variable
|
||||
this.watcher_.on('raw', async (event:string, path:string, options:any) => {
|
||||
this.logger().debug(`ResourceEditWatcher: Raw event: ${event}: ${options.watchedPath}`);
|
||||
if (event === 'rename') {
|
||||
this.watcher_.unwatch(options.watchedPath);
|
||||
this.watcher_.add(options.watchedPath);
|
||||
handleChangeEvent(options.watchedPath);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -181,6 +202,12 @@ export default class ResourceEditWatcher {
|
||||
watchedItem.lastResourceUpdatedTime = resource.updated_time;
|
||||
|
||||
this.watch(editFilePath);
|
||||
|
||||
this.dispatch({
|
||||
type: 'RESOURCE_EDIT_WATCHER_SET',
|
||||
id: resource.id,
|
||||
title: resource.title,
|
||||
});
|
||||
}
|
||||
|
||||
bridge().openItem(watchedItem.path);
|
||||
@ -199,9 +226,20 @@ export default class ResourceEditWatcher {
|
||||
|
||||
await item.asyncSaveQueue.waitForAllDone();
|
||||
|
||||
try {
|
||||
if (this.watcher_) this.watcher_.unwatch(item.path);
|
||||
await shim.fsDriver().remove(item.path);
|
||||
} catch (error) {
|
||||
this.logger().warn(`ResourceEditWatcher: There was an error unwatching resource ${resourceId}. Joplin will ignore the file regardless.`, error);
|
||||
}
|
||||
|
||||
delete this.watchedItems_[resourceId];
|
||||
|
||||
this.dispatch({
|
||||
type: 'RESOURCE_EDIT_WATCHER_REMOVE',
|
||||
id: resourceId,
|
||||
});
|
||||
|
||||
this.logger().info(`ResourceEditWatcher: Stopped watching ${item.path}`);
|
||||
}
|
||||
|
||||
@ -211,7 +249,11 @@ export default class ResourceEditWatcher {
|
||||
const item = this.watchedItems_[resourceId];
|
||||
promises.push(this.stopWatching(item.resourceId));
|
||||
}
|
||||
return Promise.all(promises);
|
||||
await Promise.all(promises);
|
||||
|
||||
this.dispatch({
|
||||
type: 'RESOURCE_EDIT_WATCHER_CLEAR',
|
||||
});
|
||||
}
|
||||
|
||||
private watchedItemByResourceId(resourceId:string):WatchedItem {
|
@ -0,0 +1,38 @@
|
||||
import produce, { Draft } from 'immer';
|
||||
|
||||
export const defaultState = {
|
||||
watchedResources: {},
|
||||
};
|
||||
|
||||
const reducer = produce((draft: Draft<any>, action:any) => {
|
||||
if (action.type.indexOf('RESOURCE_EDIT_WATCHER_') !== 0) return;
|
||||
|
||||
try {
|
||||
switch (action.type) {
|
||||
|
||||
case 'RESOURCE_EDIT_WATCHER_SET':
|
||||
|
||||
draft.watchedResources[action.id] = {
|
||||
id: action.id,
|
||||
title: action.title,
|
||||
};
|
||||
break;
|
||||
|
||||
case 'RESOURCE_EDIT_WATCHER_REMOVE':
|
||||
|
||||
delete draft.watchedResources[action.id];
|
||||
break;
|
||||
|
||||
case 'RESOURCE_EDIT_WATCHER_CLEAR':
|
||||
|
||||
draft.watchedResources = {};
|
||||
break;
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
error.message = `In plugin reducer: ${error.message} Action: ${JSON.stringify(action)}`;
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
export default reducer;
|
@ -354,7 +354,12 @@
|
||||
"ElectronClient/commands/stopExternalEditing.js": true,
|
||||
"ElectronClient/gui/NoteEditor/commands/showRevisions.js": true,
|
||||
"ReactNativeClient/lib/commands/historyBackward.js": true,
|
||||
"ReactNativeClient/lib/commands/historyForward.js": true
|
||||
"ReactNativeClient/lib/commands/historyForward.js": true,
|
||||
"CliClient/tests/support/amazon-s3-auth.json": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinMode.js": true,
|
||||
"ReactNativeClient/lib/hooks/useEffectDebugger.js": true,
|
||||
"ReactNativeClient/lib/services/ResourceEditWatcher/index.js": true,
|
||||
"ReactNativeClient/lib/services/ResourceEditWatcher/reducer.js": true
|
||||
},
|
||||
"spellright.language": [
|
||||
"en"
|
||||
|
46
package-lock.json
generated
46
package-lock.json
generated
@ -2829,8 +2829,7 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@ -2851,14 +2850,12 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@ -2873,20 +2870,17 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@ -3003,8 +2997,7 @@
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@ -3016,7 +3009,6 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@ -3031,7 +3023,6 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@ -3039,14 +3030,12 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.9.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
@ -3065,7 +3054,6 @@
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@ -3155,8 +3143,7 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@ -3168,7 +3155,6 @@
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@ -3254,8 +3240,7 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@ -3291,7 +3276,6 @@
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@ -3311,7 +3295,6 @@
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@ -3355,14 +3338,12 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -3799,6 +3780,11 @@
|
||||
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
|
||||
"dev": true
|
||||
},
|
||||
"immer": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-7.0.5.tgz",
|
||||
"integrity": "sha512-TtRAKZyuqld2eYjvWgXISLJ0ZlOl1OOTzRmrmiY8SlB0dnAhZ1OiykIDL5KDFNaPHDXiLfGQFNJGtet8z8AEmg=="
|
||||
},
|
||||
"import-fresh": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz",
|
||||
|
@ -38,6 +38,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.11.0",
|
||||
"immer": "^7.0.5",
|
||||
"joplin-turndown": "^4.0.28",
|
||||
"joplin-turndown-plugin-gfm": "^1.0.12",
|
||||
"relative": "^3.0.2"
|
||||
|
Loading…
Reference in New Issue
Block a user