1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-11-29 22:48:10 +02:00

Desktop: Fixes #8661: Fix note editor blank after syncing an encrypted note with remote changes (#8666)

This commit is contained in:
Henry Heino
2023-08-18 01:31:45 -07:00
committed by GitHub
parent 4804c1c0c3
commit e7014492c5
8 changed files with 136 additions and 25 deletions

View File

@@ -78,6 +78,7 @@ function NoteEditor(props: NoteEditorProps) {
const { formNote, setFormNote, isNewNote, resourceInfos } = useFormNote({
syncStarted: props.syncStarted,
decryptionStarted: props.decryptionStarted,
noteId: effectiveNoteId,
isProvisional: props.isProvisional,
titleInputRef: titleInputRef,
@@ -633,6 +634,7 @@ const mapStateToProps = (state: AppState) => {
isProvisional: state.provisionalNoteIds.includes(noteId),
editorNoteStatuses: state.editorNoteStatuses,
syncStarted: state.syncStarted,
decryptionStarted: state.decryptionWorker?.state !== 'idle',
themeId: state.settings.theme,
richTextBannerDismissed: state.settings.richTextBannerDismissed,
watchedNoteFiles: state.watchedNoteFiles,

View File

@@ -27,6 +27,7 @@ export interface NoteEditorProps {
isProvisional: boolean;
editorNoteStatuses: any;
syncStarted: boolean;
decryptionStarted: boolean;
bodyEditor: string;
notesParentType: string;
selectedNoteTags: any[];

View File

@@ -0,0 +1,73 @@
import Note from '@joplin/lib/models/Note';
import { setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
import { renderHook } from '@testing-library/react-hooks';
import useFormNote, { HookDependencies } from './useFormNote';
describe('useFormNote', () => {
beforeEach(async () => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
});
it('should update note when decryption completes', async () => {
const testNote = await Note.save({ title: 'Test Note!' });
const makeFormNoteProps = (syncStarted: boolean, decryptionStarted: boolean): HookDependencies => {
return {
syncStarted,
decryptionStarted,
noteId: testNote.id,
isProvisional: false,
titleInputRef: null,
editorRef: null,
onBeforeLoad: ()=>{},
onAfterLoad: ()=>{},
};
};
const formNote = renderHook(props => useFormNote(props), {
initialProps: makeFormNoteProps(true, false),
});
await formNote.waitFor(() => {
expect(formNote.result.current.formNote).toMatchObject({
encryption_applied: 0,
title: testNote.title,
});
});
await Note.save({
id: testNote.id,
encryption_cipher_text: 'cipher_text',
encryption_applied: 1,
});
// Sync starting should cause a re-render
formNote.rerender(makeFormNoteProps(false, false));
await formNote.waitFor(() => {
expect(formNote.result.current.formNote).toMatchObject({
encryption_applied: 1,
});
});
formNote.rerender(makeFormNoteProps(false, true));
await Note.save({
id: testNote.id,
encryption_applied: 0,
title: 'Test Note!',
});
// Ending decryption should also cause a re-render
formNote.rerender(makeFormNoteProps(false, false));
await formNote.waitFor(() => {
expect(formNote.result.current.formNote).toMatchObject({
encryption_applied: 0,
title: 'Test Note!',
});
});
});
});

View File

@@ -18,8 +18,9 @@ export interface OnLoadEvent {
formNote: FormNote;
}
interface HookDependencies {
export interface HookDependencies {
syncStarted: boolean;
decryptionStarted: boolean;
noteId: string;
isProvisional: boolean;
titleInputRef: any;
@@ -61,15 +62,21 @@ function resourceInfosChanged(a: ResourceInfos, b: ResourceInfos): boolean {
}
export default function useFormNote(dependencies: HookDependencies) {
const { syncStarted, noteId, isProvisional, titleInputRef, editorRef, onBeforeLoad, onAfterLoad } = dependencies;
const {
syncStarted, decryptionStarted, noteId, isProvisional, titleInputRef, editorRef, onBeforeLoad, onAfterLoad,
} = dependencies;
const [formNote, setFormNote] = useState<FormNote>(defaultFormNote());
const [formNoteRefeshScheduled, setFormNoteRefreshScheduled] = useState<boolean>(false);
const [isNewNote, setIsNewNote] = useState(false);
const prevSyncStarted = usePrevious(syncStarted);
const prevDecryptionStarted = usePrevious(decryptionStarted);
const previousNoteId = usePrevious(formNote.id);
const [resourceInfos, setResourceInfos] = useState<ResourceInfos>({});
// Increasing the value of this counter cancels any ongoing note refreshes and starts
// a new refresh.
const [formNoteRefeshScheduled, setFormNoteRefreshScheduled] = useState<number>(0);
async function initNoteState(n: any) {
let originalCss = '';
@@ -107,7 +114,7 @@ export default function useFormNote(dependencies: HookDependencies) {
}
useEffect(() => {
if (!formNoteRefeshScheduled) return () => {};
if (formNoteRefeshScheduled <= 0) return () => {};
reg.logger().info('Sync has finished and note has never been changed - reloading it');
@@ -126,7 +133,7 @@ export default function useFormNote(dependencies: HookDependencies) {
}
await initNoteState(n);
setFormNoteRefreshScheduled(false);
setFormNoteRefreshScheduled(0);
};
void loadNote();
@@ -136,21 +143,32 @@ export default function useFormNote(dependencies: HookDependencies) {
};
}, [formNoteRefeshScheduled, noteId]);
const refreshFormNote = useCallback(() => {
// Increase the counter to cancel any ongoing refresh attempts
// and start a new one.
setFormNoteRefreshScheduled(formNoteRefeshScheduled + 1);
}, [formNoteRefeshScheduled]);
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.
const decryptionJustEnded = prevDecryptionStarted && !decryptionStarted;
const syncJustEnded = prevSyncStarted && !syncStarted;
if (!prevSyncStarted) return;
if (syncStarted) return;
if (!decryptionJustEnded && !syncJustEnded) return;
if (formNote.hasChanged) return;
// Refresh the form note.
// This is kept separate from the above logic so that when prevSyncStarted is changed
// from true to false, it doesn't cancel the note from loading.
setFormNoteRefreshScheduled(true);
}, [prevSyncStarted, syncStarted, formNote.hasChanged]);
refreshFormNote();
}, [
prevSyncStarted, syncStarted,
prevDecryptionStarted, decryptionStarted,
formNote.hasChanged, refreshFormNote,
]);
useEffect(() => {
if (!noteId) {