mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-21 09:38:01 +02:00
cb8dca747b
Refactor note editor using React Hooks and TypeScript and moved editor-specific code to separate files. Moved business logic into more maintainable custom hooks. Squashed commit of the following: commit f243d9bf89bdcfa1849ee26df5c0dd3e33405010 Author: Laurent Cozic <laurent@cozic.net> Date: Sat May 2 16:04:14 2020 +0100 Fixed saving issue commit 055f68d2e8b6cf6f130336c38ac2ab480887583d Author: Laurent Cozic <laurent@cozic.net> Date: Sat May 2 15:43:38 2020 +0100 Fixed HTML notes commit 99a3cf71f58d2fedcdf3001bf4110b6e8e3993da Merge: 9be85c45f2b16ebbbf7a
Author: Laurent Cozic <laurent@cozic.net> Date: Sat May 2 12:54:42 2020 +0100 Merge branch 'master' into refactor_note_text commit 9be85c45f23e5cb1ecd612b0ee631947871ada6f Author: Laurent Cozic <laurent@cozic.net> Date: Sat May 2 12:21:01 2020 +0100 Ident to space commit 848dde1869c010fe5851f493ef7287ada5f2991e Author: Laurent Cozic <laurent@cozic.net> Date: Sat May 2 11:28:50 2020 +0100 Refactor prop types commit 13c3bbe2b4f9a522ea3f8a25e7e5e7bb026dfd4f Author: Laurent Cozic <laurent@cozic.net> Date: Sat May 2 11:15:45 2020 +0100 Fixed resource loading issue commit 50cb38e3f00ef40ea8b6a468eadd66728a3ec332 Author: Laurent Cozic <laurent@cozic.net> Date: Fri May 1 23:46:58 2020 +0100 Fixed resource loading logic commit bc42ed03735f50c8394d597bb9e67312e55752fe Author: Laurent Cozic <laurent@cozic.net> Date: Fri May 1 23:08:41 2020 +0100 Various fixes commit 03c038e6d6cbde03bd474798b96c4eb120fd1647 Author: Laurent Cozic <laurent@cozic.net> Date: Wed Apr 29 23:22:49 2020 +0100 Fixed resource handling commit dc6c15302fac094c4e7dec5a20c9fcc4edb3d132 Author: Laurent Cozic <laurent@cozic.net> Date: Wed Apr 29 22:55:13 2020 +0100 Moved more code to files commit 398d5121e53df34de89b4148ef2cfd3a7bbe4feb Author: Laurent Cozic <laurent@cozic.net> Date: Wed Apr 29 00:22:43 2020 +0000 More fixes commit 3ebbb80147d7d502fd955776c7fedb743400597f Author: Laurent Cozic <laurent@cozic.net> Date: Wed Apr 29 00:12:44 2020 +0000 Various improvements and bug fixes commit 52a65ed3875e0709117ca93ba723e20624577d05 Author: Laurent Cozic <laurent@cozic.net> Date: Tue Apr 28 23:51:07 2020 +0000 Move more code to sub-files commit 33ccf530fb442d7ddae0852cbab2c335efdbbf33 Author: Laurent Cozic <laurent@cozic.net> Date: Tue Apr 28 23:25:12 2020 +0100 Moved code to sub-files commit ba3ad2cf9fcc1d7809df4afe93cd9737585a9960 Merge: 445acdab73150ee14de6
Author: Laurent Cozic <laurent@cozic.net> Date: Tue Apr 28 22:28:56 2020 +0100 Merge branch 'master' into refactor_note_text commit 445acdab7368345369d7f69b9becd1e77c8383dc Author: Laurent Cozic <laurent@cozic.net> Date: Tue Apr 28 19:01:41 2020 +0100 Imported more code commit 772481d3a3ac7f0b0b00e86394c0f4fd2f3a9fa7 Author: Laurent Cozic <laurent@cozic.net> Date: Mon Apr 27 23:43:17 2020 +0000 Handle save/load state commit b3b92192ae3a1a30e3018810346cebfad47ac5e3 Author: Laurent Cozic <laurent@cozic.net> Date: Mon Apr 27 23:11:11 2020 +0000 Clean up and added back scroll commit 7a19ecfd0cb7fef1d58ece2e024099c7e40986da Author: Laurent Cozic <laurent@cozic.net> Date: Mon Apr 27 22:29:39 2020 +0100 More refactoring commit ac388afd381eaecfa4582b3566d032c9d953c4dc Author: Laurent Cozic <laurent@cozic.net> Date: Sun Apr 26 17:07:01 2020 +0100 Restored print commit 1d2c0ed389a5398dacc584d24922c5ea0dda861a Author: Laurent Cozic <laurent@cozic.net> Date: Sun Apr 26 12:03:15 2020 +0100 Put back search commit c618cb59d43fa3bb507dbd0b757b302ecfe907b3 Author: Laurent Cozic <laurent@cozic.net> Date: Sat Apr 25 18:21:11 2020 +0100 Restore scrolling behaviour commit 324e6ea79ebafab1d2bca246ef030751147a47eb Author: Laurent Cozic <laurent@cozic.net> Date: Sat Apr 25 10:22:31 2020 +0100 Simplified saving notes commit ef089aaf2289193bf275d94c1f2785f6d88657e4 Author: Laurent Cozic <laurent@cozic.net> Date: Sat Apr 25 10:12:16 2020 +0100 More refactoring commit 61b102307d5a98d2c1502d7bf073592da21af720 Author: Laurent Cozic <laurent@cozic.net> Date: Fri Apr 24 18:04:44 2020 +0100 Added back note revisions commit 7d5e3694d0df044b8493d9114e89e2d81c9b69ad Author: Laurent Cozic <laurent@cozic.net> Date: Thu Apr 23 22:51:52 2020 +0000 More note toolbar refactoring commit a56d58e7c80d91f29afadaffaaa004f3254482f7 Author: Laurent Cozic <laurent@cozic.net> Date: Thu Apr 23 20:54:37 2020 +0100 Finished toolbar refactoring commit 6c8ef9f44f880a9569eed5c54c9c47dca2251e5e Author: Laurent Cozic <laurent@cozic.net> Date: Thu Apr 23 19:17:44 2020 +0100 More refactoring commit 7de8057158a9256e2e0dcf948081e10a6a642216 Author: Laurent Cozic <laurent@cozic.net> Date: Wed Apr 22 23:48:42 2020 +0100 Started refactoring commands commit 177263c85e7d17d8ddc01b583738c2ab14b3acd7 Merge: f58f1a06e07ceb68d835
Author: Laurent Cozic <laurent@cozic.net> Date: Wed Apr 22 20:26:19 2020 +0100 Merge branch 'master' into refactor_note_text commit f58f1a06e08b3cf80e2ac7a794b15f4b5caf8932 Author: Laurent Cozic <laurent@cozic.net> Date: Wed Apr 22 20:25:43 2020 +0100 Moving Ace Editor to separate component commit a83d3a220515137985c0f334f5848c91b8539138 Author: Laurent Cozic <laurent@cozic.net> Date: Mon Apr 20 20:33:21 2020 +0000 Cleaned up directory structure for note editor commit c6f2e609c9443bac21de5033bbedf86ac6f12cc0 Author: Laurent Cozic <laurent@cozic.net> Date: Mon Apr 20 19:23:06 2020 +0100 Added "note" menu to move note-related items to it commit 1219465318ae5a7a2c777ae2ec15d3357e1499df Author: Laurent Cozic <laurent@cozic.net> Date: Mon Apr 20 19:05:04 2020 +0100 Moved note related toolbar to separate component
209 lines
6.2 KiB
TypeScript
209 lines
6.2 KiB
TypeScript
import { useState, useEffect, useCallback } from 'react';
|
|
import { FormNote, defaultFormNote, ResourceInfos } from './types';
|
|
import { clearResourceCache, attachedResources } from './resourceHandling';
|
|
const { MarkupToHtml } = require('lib/joplin-renderer');
|
|
const HtmlToHtml = require('lib/joplin-renderer/HtmlToHtml');
|
|
import AsyncActionQueue from '../../../lib/AsyncActionQueue';
|
|
import { handleResourceDownloadMode } from './resourceHandling';
|
|
const usePrevious = require('lib/hooks/usePrevious').default;
|
|
const Note = require('lib/models/Note');
|
|
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');
|
|
|
|
export interface OnLoadEvent {
|
|
formNote: FormNote,
|
|
}
|
|
|
|
interface HookDependencies {
|
|
syncStarted: boolean,
|
|
noteId: string,
|
|
isProvisional: boolean,
|
|
titleInputRef: any,
|
|
editorRef: any,
|
|
onBeforeLoad(event:OnLoadEvent):void,
|
|
onAfterLoad(event:OnLoadEvent):void,
|
|
}
|
|
|
|
function installResourceChangeHandler(onResourceChangeHandler: Function) {
|
|
ResourceFetcher.instance().on('downloadComplete', onResourceChangeHandler);
|
|
ResourceFetcher.instance().on('downloadStarted', onResourceChangeHandler);
|
|
DecryptionWorker.instance().on('resourceDecrypted', onResourceChangeHandler);
|
|
}
|
|
|
|
function uninstallResourceChangeHandler(onResourceChangeHandler: Function) {
|
|
ResourceFetcher.instance().off('downloadComplete', onResourceChangeHandler);
|
|
ResourceFetcher.instance().off('downloadStarted', onResourceChangeHandler);
|
|
DecryptionWorker.instance().off('resourceDecrypted', onResourceChangeHandler);
|
|
}
|
|
|
|
export default function useFormNote(dependencies:HookDependencies) {
|
|
const { syncStarted, noteId, isProvisional, titleInputRef, editorRef, onBeforeLoad, onAfterLoad } = dependencies;
|
|
|
|
const [formNote, setFormNote] = useState<FormNote>(defaultFormNote());
|
|
const [isNewNote, setIsNewNote] = useState(false);
|
|
const prevSyncStarted = usePrevious(syncStarted);
|
|
const previousNoteId = usePrevious(formNote.id);
|
|
const [resourceInfos, setResourceInfos] = useState<ResourceInfos>({});
|
|
|
|
async function initNoteState(n: any) {
|
|
let originalCss = '';
|
|
|
|
if (n.markup_language === MarkupToHtml.MARKUP_LANGUAGE_HTML) {
|
|
const htmlToHtml = new HtmlToHtml();
|
|
const splitted = htmlToHtml.splitHtml(n.body);
|
|
originalCss = splitted.css;
|
|
}
|
|
|
|
const newFormNote = {
|
|
id: n.id,
|
|
title: n.title,
|
|
body: n.body,
|
|
is_todo: n.is_todo,
|
|
parent_id: n.parent_id,
|
|
bodyWillChangeId: 0,
|
|
bodyChangeId: 0,
|
|
markup_language: n.markup_language,
|
|
saveActionQueue: new AsyncActionQueue(300),
|
|
originalCss: originalCss,
|
|
hasChanged: false,
|
|
user_updated_time: n.user_updated_time,
|
|
encryption_applied: n.encryption_applied,
|
|
};
|
|
|
|
// Note that for performance reason,the call to setResourceInfos should
|
|
// be first because it loads the resource infos in an async way. If we
|
|
// swap them, the formNote will be updated first and rendered, then the
|
|
// the resources will load, and the note will be re-rendered.
|
|
setResourceInfos(await attachedResources(n.body));
|
|
setFormNote(newFormNote);
|
|
|
|
await handleResourceDownloadMode(n.body);
|
|
|
|
return newFormNote;
|
|
}
|
|
|
|
useEffect(() => {
|
|
// Check that synchronisation has just finished - and
|
|
// if the note has never been changed, we reload it.
|
|
// If the note has already been changed, it's a conflict
|
|
// that's already been handled by the synchronizer.
|
|
|
|
if (!prevSyncStarted) return () => {};
|
|
if (syncStarted) return () => {};
|
|
if (formNote.hasChanged) return () => {};
|
|
|
|
reg.logger().debug('Sync has finished and note has never been changed - reloading it');
|
|
|
|
let cancelled = false;
|
|
|
|
const loadNote = async () => {
|
|
const n = await Note.load(noteId);
|
|
if (cancelled) return;
|
|
|
|
// Normally should not happened because if the note has been deleted via sync
|
|
// it would not have been loaded in the editor (due to note selection changing
|
|
// on delete)
|
|
if (!n) {
|
|
reg.logger().warn('Trying to reload note that has been deleted:', noteId);
|
|
return;
|
|
}
|
|
|
|
await initNoteState(n);
|
|
};
|
|
|
|
loadNote();
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [prevSyncStarted, syncStarted, formNote]);
|
|
|
|
useEffect(() => {
|
|
if (!noteId) return () => {};
|
|
|
|
if (formNote.id === noteId) return () => {};
|
|
|
|
let cancelled = false;
|
|
|
|
reg.logger().debug('Loading existing note', noteId);
|
|
|
|
function handleAutoFocus(noteIsTodo: boolean) {
|
|
if (!isProvisional) return;
|
|
|
|
const focusSettingName = noteIsTodo ? 'newTodoFocus' : 'newNoteFocus';
|
|
|
|
requestAnimationFrame(() => {
|
|
if (Setting.value(focusSettingName) === 'title') {
|
|
if (titleInputRef.current) titleInputRef.current.focus();
|
|
} else {
|
|
if (editorRef.current) editorRef.current.execCommand({ name: 'focus' });
|
|
}
|
|
});
|
|
}
|
|
|
|
async function loadNote() {
|
|
const n = await Note.load(noteId);
|
|
if (cancelled) return;
|
|
if (!n) throw new Error(`Cannot find note with ID: ${noteId}`);
|
|
reg.logger().debug('Loaded note:', n);
|
|
|
|
await onBeforeLoad({ formNote });
|
|
|
|
const newFormNote = await initNoteState(n);
|
|
|
|
setIsNewNote(isProvisional);
|
|
|
|
await onAfterLoad({ formNote: newFormNote });
|
|
|
|
handleAutoFocus(!!n.is_todo);
|
|
}
|
|
|
|
loadNote();
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [noteId, isProvisional, formNote]);
|
|
|
|
const onResourceChange = useCallback(async function(event:any = null) {
|
|
const resourceIds = await Note.linkedResourceIds(formNote.body);
|
|
if (!event || resourceIds.indexOf(event.id) >= 0) {
|
|
clearResourceCache();
|
|
setResourceInfos(await attachedResources(formNote.body));
|
|
}
|
|
}, [formNote.body]);
|
|
|
|
useEffect(() => {
|
|
installResourceChangeHandler(onResourceChange);
|
|
return () => {
|
|
uninstallResourceChangeHandler(onResourceChange);
|
|
};
|
|
}, [onResourceChange]);
|
|
|
|
useEffect(() => {
|
|
if (previousNoteId !== formNote.id) {
|
|
onResourceChange();
|
|
}
|
|
}, [previousNoteId, formNote.id, onResourceChange]);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
async function runEffect() {
|
|
const r = await attachedResources(formNote.body);
|
|
if (cancelled) return;
|
|
setResourceInfos(r);
|
|
}
|
|
|
|
runEffect();
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [formNote.body]);
|
|
|
|
return { isNewNote, formNote, setFormNote, resourceInfos };
|
|
}
|