mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-11 18:24:43 +02:00
This commit is contained in:
parent
ecc50790ed
commit
b3f32ffc59
455
CliClient/tests/feature_NoteHistory.js
Normal file
455
CliClient/tests/feature_NoteHistory.js
Normal file
@ -0,0 +1,455 @@
|
||||
require('app-module-path').addPath(__dirname);
|
||||
const { asyncTest, id, ids, createNTestFolders, sortedIds, createNTestNotes, TestApp } = require('test-utils.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { uuid } = require('lib/uuid.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
|
||||
const { ALL_NOTES_FILTER_ID } = require('lib/reserved-ids.js');
|
||||
|
||||
let testApp = null;
|
||||
|
||||
const goBackWard = (state) => {
|
||||
if (!state.backwardHistoryNotes.length) return;
|
||||
testApp.dispatch({ type: 'HISTORY_BACKWARD' });
|
||||
};
|
||||
|
||||
const goForward = (state) => {
|
||||
if (!state.forwardHistoryNotes.length) return;
|
||||
testApp.dispatch({ type: 'HISTORY_FORWARD' });
|
||||
};
|
||||
|
||||
const goToNote = (testApp, note) => {
|
||||
testApp.dispatch({ type: 'NOTE_SELECT', id: note.id });
|
||||
};
|
||||
|
||||
describe('integration_ForwardBackwardNoteHistory', function() {
|
||||
beforeEach(async (done) => {
|
||||
testApp = new TestApp();
|
||||
await testApp.start(['--no-welcome']);
|
||||
done();
|
||||
});
|
||||
|
||||
afterEach(async (done) => {
|
||||
if (testApp !== null) await testApp.destroy();
|
||||
testApp = null;
|
||||
done();
|
||||
});
|
||||
|
||||
it('should save history when navigating through notes', asyncTest(async () => {
|
||||
// setup
|
||||
const folders = await createNTestFolders(2);
|
||||
await testApp.wait();
|
||||
const notes0 = await createNTestNotes(5, folders[0]);
|
||||
// let notes1 = await createNTestNotes(5, folders[1]);
|
||||
await testApp.wait();
|
||||
|
||||
testApp.dispatch({ type: 'FOLDER_SELECT', id: id(folders[0]) });
|
||||
await testApp.wait();
|
||||
|
||||
testApp.dispatch({ type: 'NOTE_SELECT', id: notes0[3].id });
|
||||
await testApp.wait();
|
||||
testApp.dispatch({ type: 'NOTE_SELECT', id: notes0[2].id });
|
||||
await testApp.wait();
|
||||
testApp.dispatch({ type: 'NOTE_SELECT', id: notes0[1].id });
|
||||
await testApp.wait();
|
||||
testApp.dispatch({ type: 'NOTE_SELECT', id: notes0[0].id });
|
||||
await testApp.wait();
|
||||
|
||||
let state = testApp.store().getState();
|
||||
|
||||
goBackWard(state);
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[1].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
|
||||
goBackWard(state);
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[2].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
|
||||
goForward(state);
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[1].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
|
||||
testApp.dispatch({ type: 'NOTE_SELECT', id: notes0[4].id });
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[4].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
}));
|
||||
|
||||
|
||||
it('should save history when navigating through notebooks', asyncTest(async () => {
|
||||
const folders = await createNTestFolders(2);
|
||||
await testApp.wait();
|
||||
const notes0 = await createNTestNotes(5, folders[0]);
|
||||
const notes1 = await createNTestNotes(5, folders[1]);
|
||||
await testApp.wait();
|
||||
|
||||
testApp.dispatch({ type: 'FOLDER_SELECT', id: id(folders[0]) });
|
||||
await testApp.wait();
|
||||
|
||||
testApp.dispatch({ type: 'FOLDER_SELECT', id: id(folders[1]) });
|
||||
await testApp.wait();
|
||||
|
||||
let state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes1[4].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[1].id);
|
||||
|
||||
testApp.dispatch({ type: 'FOLDER_SELECT', id: id(folders[0]) });
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[4].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
|
||||
goBackWard(state);
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes1[4].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[1].id);
|
||||
|
||||
goForward(state);
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[4].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
}));
|
||||
|
||||
|
||||
it('should save history when searching for a note', asyncTest(async () => {
|
||||
const folders = await createNTestFolders(2);
|
||||
await testApp.wait();
|
||||
const notes0 = await createNTestNotes(5, folders[0]);
|
||||
const notes1 = await createNTestNotes(5, folders[1]);
|
||||
await testApp.wait();
|
||||
|
||||
testApp.dispatch({ type: 'FOLDER_SELECT', id: id(folders[0]) });
|
||||
await testApp.wait();
|
||||
|
||||
testApp.dispatch({ type: 'FOLDER_SELECT', id: id(folders[1]) });
|
||||
await testApp.wait();
|
||||
|
||||
let state = testApp.store().getState();
|
||||
|
||||
expect(state.selectedNoteIds).toEqual([notes1[4].id]); // notes1[4]
|
||||
expect(state.selectedFolderId).toEqual(folders[1].id);
|
||||
|
||||
const searchId = uuid.create();
|
||||
testApp.dispatch({
|
||||
type: 'SEARCH_UPDATE',
|
||||
search: {
|
||||
id: searchId,
|
||||
title: notes0[0].title,
|
||||
query_pattern: notes0[0].title,
|
||||
query_folder_id: null,
|
||||
type: BaseModel.TYPE_SEARCH,
|
||||
},
|
||||
});
|
||||
await testApp.wait();
|
||||
|
||||
testApp.dispatch({
|
||||
type: 'SEARCH_SELECT',
|
||||
id: searchId,
|
||||
});
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(ids(state.backwardHistoryNotes)).toEqual(ids([notes0[4], notes1[4]]));
|
||||
expect(ids(state.forwardHistoryNotes)).toEqual([]);
|
||||
}));
|
||||
|
||||
it('should ensure no adjacent duplicates', asyncTest(async () => {
|
||||
const folders = await createNTestFolders(2);
|
||||
await testApp.wait();
|
||||
const notes0 = await createNTestNotes(5, folders[0]);
|
||||
await testApp.wait();
|
||||
|
||||
testApp.dispatch({ type: 'FOLDER_SELECT', id: id(folders[0]) });
|
||||
await testApp.wait();
|
||||
|
||||
testApp.dispatch({ type: 'NOTE_SELECT', id: notes0[0].id });
|
||||
await testApp.wait();
|
||||
|
||||
goToNote(testApp, notes0[1]);
|
||||
await testApp.wait();
|
||||
goToNote(testApp, notes0[2]);
|
||||
await testApp.wait();
|
||||
goToNote(testApp, notes0[3]);
|
||||
await testApp.wait();
|
||||
goToNote(testApp, notes0[2]);
|
||||
await testApp.wait();
|
||||
goToNote(testApp, notes0[3]);
|
||||
await testApp.wait();
|
||||
goToNote(testApp, notes0[2]);
|
||||
await testApp.wait();
|
||||
goToNote(testApp, notes0[3]);
|
||||
await testApp.wait();
|
||||
goToNote(testApp, notes0[2]);
|
||||
await testApp.wait();
|
||||
goToNote(testApp, notes0[3]);
|
||||
await testApp.wait();
|
||||
goToNote(testApp, notes0[2]);
|
||||
await testApp.wait();
|
||||
goToNote(testApp, notes0[3]);
|
||||
await testApp.wait();
|
||||
|
||||
let state = testApp.store().getState();
|
||||
|
||||
goBackWard(state);
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[2].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
|
||||
goBackWard(state);
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[3].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
|
||||
goBackWard(state);
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[2].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
|
||||
goBackWard(state);
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[3].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
|
||||
testApp.dispatch({ type: 'NOTE_DELETE', id: notes0[2].id });
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[3].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
}));
|
||||
|
||||
it('should ensure history is not corrupted when notes get deleted.', asyncTest(async () => {
|
||||
const folders = await createNTestFolders(2);
|
||||
await testApp.wait();
|
||||
const notes0 = await createNTestNotes(5, folders[0]);
|
||||
await testApp.wait();
|
||||
|
||||
testApp.dispatch({ type: 'FOLDER_SELECT', id: id(folders[0]) });
|
||||
await testApp.wait();
|
||||
|
||||
testApp.dispatch({ type: 'NOTE_SELECT', id: notes0[0].id });
|
||||
await testApp.wait();
|
||||
|
||||
goToNote(testApp, notes0[1]);
|
||||
await testApp.wait();
|
||||
|
||||
goToNote(testApp, notes0[2]);
|
||||
await testApp.wait();
|
||||
|
||||
testApp.dispatch({ type: 'NOTE_DELETE', id: notes0[1].id });
|
||||
await testApp.wait();
|
||||
|
||||
let state = testApp.store().getState();
|
||||
goBackWard(state);
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[0].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
}));
|
||||
|
||||
it('should ensure history is not corrupted when notes get created.', asyncTest(async () => {
|
||||
const folders = await createNTestFolders(2);
|
||||
await testApp.wait();
|
||||
const notes0 = await createNTestNotes(5, folders[0]);
|
||||
await testApp.wait();
|
||||
|
||||
testApp.dispatch({ type: 'FOLDER_SELECT', id: id(folders[0]) });
|
||||
await testApp.wait();
|
||||
|
||||
goToNote(testApp, notes0[0]);
|
||||
await testApp.wait();
|
||||
|
||||
goToNote(testApp, notes0[1]);
|
||||
await testApp.wait();
|
||||
|
||||
const newNote = await Note.save({
|
||||
parent_id: folders[0].id,
|
||||
is_todo: 0,
|
||||
body: 'test',
|
||||
});
|
||||
await testApp.wait();
|
||||
|
||||
goToNote(testApp, newNote);
|
||||
await testApp.wait();
|
||||
|
||||
let state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([newNote.id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
|
||||
goToNote(testApp, notes0[2]);
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[2].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
|
||||
goBackWard(state);
|
||||
await testApp.wait();
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([newNote.id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
|
||||
goBackWard(state);
|
||||
await testApp.wait();
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[1].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
|
||||
goForward(state);
|
||||
await testApp.wait();
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([newNote.id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
|
||||
goForward(state);
|
||||
await testApp.wait();
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[2].id]);
|
||||
expect(state.selectedFolderId).toEqual(folders[0].id);
|
||||
}));
|
||||
|
||||
it('should ensure history works when traversing all notes', asyncTest(async () => {
|
||||
const folders = await createNTestFolders(2);
|
||||
await testApp.wait();
|
||||
const notes0 = await createNTestNotes(5, folders[0]);
|
||||
const notes1 = await createNTestNotes(5, folders[1]);
|
||||
await testApp.wait();
|
||||
|
||||
testApp.dispatch({ type: 'FOLDER_SELECT', id: id(folders[0]) });
|
||||
await testApp.wait();
|
||||
|
||||
goToNote(testApp, notes0[0]);
|
||||
await testApp.wait();
|
||||
|
||||
testApp.dispatch({ type: 'SMART_FILTER_SELECT', id: ALL_NOTES_FILTER_ID });
|
||||
await testApp.wait();
|
||||
|
||||
let state = testApp.store().getState();
|
||||
expect(sortedIds(state.notes)).toEqual(sortedIds(notes0.concat(notes1)));
|
||||
expect(state.selectedNoteIds).toEqual(ids([notes0[0]]));
|
||||
|
||||
goToNote(testApp, notes0[2]);
|
||||
await testApp.wait();
|
||||
|
||||
goToNote(testApp, notes0[4]);
|
||||
await testApp.wait();
|
||||
|
||||
goToNote(testApp, notes1[2]);
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes1[2].id]);
|
||||
|
||||
goBackWard(state);
|
||||
await testApp.wait();
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[4].id]);
|
||||
|
||||
goBackWard(state);
|
||||
await testApp.wait();
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[2].id]);
|
||||
|
||||
goBackWard(state);
|
||||
await testApp.wait();
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[0].id]);
|
||||
|
||||
goForward(state);
|
||||
await testApp.wait();
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[2].id]);
|
||||
|
||||
goForward(state);
|
||||
await testApp.wait();
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedNoteIds).toEqual([notes0[4].id]);
|
||||
}));
|
||||
|
||||
it('should ensure history works when traversing through conflict notes', asyncTest(async () => {
|
||||
const folders = await createNTestFolders(1);
|
||||
await testApp.wait();
|
||||
const notes0 = await createNTestNotes(5, folders[0]);
|
||||
await testApp.wait();
|
||||
|
||||
// create two conflict notes with parent_id folder 1
|
||||
const note1 = await Note.save({ title: 'note 1', parent_id: folders[0].id, is_conflict: 1 });
|
||||
await testApp.wait();
|
||||
const note2 = await Note.save({ title: 'note 2', parent_id: folders[0].id, is_conflict: 1 });
|
||||
await testApp.wait();
|
||||
|
||||
// Testing history between conflict notes
|
||||
testApp.dispatch({ type: 'FOLDER_SELECT', id: Folder.conflictFolderId() });
|
||||
await testApp.wait();
|
||||
|
||||
goToNote(testApp, note1);
|
||||
await testApp.wait();
|
||||
|
||||
goToNote(testApp, note2);
|
||||
await testApp.wait();
|
||||
|
||||
let state = testApp.store().getState();
|
||||
expect(state.selectedFolderId).toBe(Folder.conflictFolderId());
|
||||
expect(state.selectedNoteIds[0]).toBe(note2.id);
|
||||
|
||||
goBackWard(state);
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedFolderId).toBe(Folder.conflictFolderId());
|
||||
expect(state.selectedNoteIds[0]).toBe(note1.id);
|
||||
|
||||
goForward(state);
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedFolderId).toBe(Folder.conflictFolderId());
|
||||
expect(state.selectedNoteIds[0]).toBe(note2.id);
|
||||
|
||||
// Testing history between conflict and non conflict notes.
|
||||
testApp.dispatch({ type: 'FOLDER_SELECT', id: folders[0].id });
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedFolderId).toBe(folders[0].id);
|
||||
expect(state.selectedNoteIds[0]).toBe(notes0[4].id);
|
||||
|
||||
goBackWard(state);
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedFolderId).toBe(Folder.conflictFolderId());
|
||||
expect(state.selectedNoteIds[0]).toBe(note2.id);
|
||||
|
||||
goForward(state);
|
||||
await testApp.wait();
|
||||
|
||||
state = testApp.store().getState();
|
||||
expect(state.selectedFolderId).toBe(folders[0].id);
|
||||
expect(state.selectedNoteIds[0]).toBe(notes0[4].id);
|
||||
}));
|
||||
|
||||
});
|
@ -5,7 +5,7 @@ const { setupDatabaseAndSynchronizer, switchClient, asyncTest, createNTestNotes,
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const { reducer, defaultState, stateUtils } = require('lib/reducer.js');
|
||||
const { reducer, defaultState, stateUtils, MAX_HISTORY } = require('lib/reducer.js');
|
||||
|
||||
function initTestState(folders, selectedFolderIndex, notes, selectedNoteIndexes, tags = null, selectedTagIndex = null) {
|
||||
let state = defaultState;
|
||||
@ -36,6 +36,33 @@ function initTestState(folders, selectedFolderIndex, notes, selectedNoteIndexes,
|
||||
return state;
|
||||
}
|
||||
|
||||
function goToNote(notes, selectedNoteIndexes, state) {
|
||||
if (selectedNoteIndexes != null) {
|
||||
const selectedIds = [];
|
||||
for (let i = 0; i < selectedNoteIndexes.length; i++) {
|
||||
selectedIds.push(notes[selectedNoteIndexes[i]].id);
|
||||
}
|
||||
state = reducer(state, { type: 'NOTE_SELECT', ids: selectedIds });
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
function goBackWard(state) {
|
||||
if (!state.backwardHistoryNotes.length) return state;
|
||||
state = reducer(state, {
|
||||
type: 'HISTORY_BACKWARD',
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
function goForward(state) {
|
||||
if (!state.forwardHistoryNotes.length) return state;
|
||||
state = reducer(state, {
|
||||
type: 'HISTORY_FORWARD',
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
function createExpectedState(items, keepIndexes, selectedIndexes) {
|
||||
const expected = { items: [], selectedIds: [] };
|
||||
|
||||
@ -345,4 +372,213 @@ describe('Reducer', function() {
|
||||
expect(state.selectedNoteIds).toEqual(expected.selectedIds);
|
||||
}));
|
||||
|
||||
it('should remove deleted note from history', asyncTest(async () => {
|
||||
|
||||
// create 1 folder
|
||||
const folders = await createNTestFolders(1);
|
||||
// create 5 notes
|
||||
const notes = await createNTestNotes(5, folders[0]);
|
||||
// select the 1st folder and the 1st note
|
||||
let state = initTestState(folders, 0, notes, [0]);
|
||||
|
||||
// select second note
|
||||
state = goToNote(notes, [1], state);
|
||||
// select third note
|
||||
state = goToNote(notes, [2], state);
|
||||
// select fourth note
|
||||
state = goToNote(notes, [3], state);
|
||||
|
||||
// expect history to contain first, second and third note
|
||||
expect(state.backwardHistoryNotes.length).toEqual(3);
|
||||
expect(getIds(state.backwardHistoryNotes)).toEqual(getIds(notes.slice(0, 3)));
|
||||
|
||||
// delete third note
|
||||
state = reducer(state, { type: 'NOTE_DELETE', id: notes[2].id });
|
||||
|
||||
// expect history to not contain third note
|
||||
expect(getIds(state.backwardHistoryNotes)).not.toContain(notes[2].id);
|
||||
}));
|
||||
|
||||
it('should remove all notes of a deleted notebook from history', asyncTest(async () => {
|
||||
const folders = await createNTestFolders(2);
|
||||
const notes = [];
|
||||
for (let i = 0; i < folders.length; i++) {
|
||||
notes.push(...await createNTestNotes(3, folders[i]));
|
||||
}
|
||||
|
||||
let state = initTestState(folders, 0, notes.slice(0,3), [0]);
|
||||
state = goToNote(notes, [1], state);
|
||||
state = goToNote(notes, [2], state);
|
||||
|
||||
|
||||
// go to second folder
|
||||
state = reducer(state, { type: 'FOLDER_SELECT', id: folders[1].id });
|
||||
expect(getIds(state.backwardHistoryNotes)).toEqual(getIds(notes.slice(0, 3)));
|
||||
|
||||
// delete the first folder
|
||||
state = reducer(state, { type: 'FOLDER_DELETE', id: folders[0].id });
|
||||
|
||||
expect(getIds(state.backwardHistoryNotes)).toEqual([]);
|
||||
}));
|
||||
|
||||
it('should maintain history correctly when going backward and forward', asyncTest(async () => {
|
||||
const folders = await createNTestFolders(2);
|
||||
const notes = [];
|
||||
for (let i = 0; i < folders.length; i++) {
|
||||
notes.push(...await createNTestNotes(5, folders[i]));
|
||||
}
|
||||
|
||||
let state = initTestState(folders, 0, notes.slice(0,5), [0]);
|
||||
state = goToNote(notes, [1], state);
|
||||
state = goToNote(notes, [2], state);
|
||||
state = goToNote(notes, [3], state);
|
||||
state = goToNote(notes, [4], state);
|
||||
|
||||
expect(getIds(state.backwardHistoryNotes)).toEqual(getIds(notes.slice(0, 4)));
|
||||
|
||||
state = goBackWard(state);
|
||||
expect(getIds(state.backwardHistoryNotes)).toEqual(getIds(notes.slice(0,3)));
|
||||
expect(getIds(state.forwardHistoryNotes)).toEqual(getIds(notes.slice(4, 5)));
|
||||
|
||||
state = goBackWard(state);
|
||||
expect(getIds(state.backwardHistoryNotes)).toEqual(getIds(notes.slice(0,2)));
|
||||
// because we push the last seen note to stack.
|
||||
expect(getIds(state.forwardHistoryNotes)).toEqual(getIds([notes[4], notes[3]]));
|
||||
|
||||
state = goForward(state);
|
||||
expect(getIds(state.backwardHistoryNotes)).toEqual(getIds(notes.slice(0,3)));
|
||||
expect(getIds(state.forwardHistoryNotes)).toEqual(getIds([notes[4]]));
|
||||
|
||||
state = goForward(state);
|
||||
expect(getIds(state.backwardHistoryNotes)).toEqual(getIds(notes.slice(0,4)));
|
||||
expect(getIds(state.forwardHistoryNotes)).toEqual([]);
|
||||
}));
|
||||
|
||||
it('should remember the last seen note of a notebook', asyncTest(async () => {
|
||||
const folders = await createNTestFolders(2);
|
||||
const notes = [];
|
||||
for (let i = 0; i < folders.length; i++) {
|
||||
notes.push(...await createNTestNotes(5, folders[i]));
|
||||
}
|
||||
|
||||
let state = initTestState(folders, 0, notes.slice(0,5), [0]);
|
||||
|
||||
state = goToNote(notes, [1], state);
|
||||
state = goToNote(notes, [2], state);
|
||||
state = goToNote(notes, [3], state);
|
||||
state = goToNote(notes, [4], state); // last seen note is notes[4]
|
||||
// go to second folder
|
||||
state = reducer(state, { type: 'FOLDER_SELECT', id: folders[1].id });
|
||||
state = goToNote(notes, [5], state);
|
||||
state = goToNote(notes, [6], state);
|
||||
|
||||
// return to first folder
|
||||
state = reducer(state, { type: 'FOLDER_SELECT', id: folders[0].id });
|
||||
|
||||
expect(state.lastSelectedNotesIds.Folder[folders[0].id]).toEqual([notes[4].id]);
|
||||
|
||||
// return to second folder
|
||||
state = reducer(state, { type: 'FOLDER_SELECT', id: folders[1].id });
|
||||
expect(state.lastSelectedNotesIds.Folder[folders[1].id]).toEqual([notes[6].id]);
|
||||
|
||||
}));
|
||||
|
||||
it('should ensure that history is free of adjacent duplicates', asyncTest(async () => {
|
||||
// create 1 folder
|
||||
const folders = await createNTestFolders(1);
|
||||
// create 5 notes
|
||||
const notes = await createNTestNotes(5, folders[0]);
|
||||
// select the 1st folder and the 1st note
|
||||
let state = initTestState(folders, 0, notes, [0]);
|
||||
|
||||
// backward = 0 1 2 3 2 3 2 3 2 3 2
|
||||
// forward =
|
||||
// current = 3
|
||||
state = goToNote(notes, [1], state);
|
||||
state = goToNote(notes, [2], state);
|
||||
state = goToNote(notes, [3], state);
|
||||
state = goToNote(notes, [2], state);
|
||||
state = goToNote(notes, [3], state);
|
||||
state = goToNote(notes, [2], state);
|
||||
state = goToNote(notes, [3], state);
|
||||
state = goToNote(notes, [2], state);
|
||||
state = goToNote(notes, [3], state);
|
||||
state = goToNote(notes, [2], state);
|
||||
state = goToNote(notes, [3], state);
|
||||
|
||||
// backward = 0 1 2 3 2 3 2 3 2 3
|
||||
// forward = 3
|
||||
// current = 2
|
||||
state = goBackWard(state);
|
||||
|
||||
// backward = 0 1 2 3 2 3 2 3 2
|
||||
// forward = 3 2
|
||||
// current = 3
|
||||
state = goBackWard(state);
|
||||
|
||||
// backward = 0 1 2 3 2 3 2 3
|
||||
// forward = 3 2 3
|
||||
// current = 2
|
||||
state = goBackWard(state);
|
||||
|
||||
// backward = 0 1 2 3 2 3 2
|
||||
// forward = 3 2 3 2
|
||||
// current = 3
|
||||
state = goBackWard(state);
|
||||
|
||||
expect(state.backwardHistoryNotes.map(n=>n.id)).toEqual([notes[0], notes[1], notes[2], notes[3], notes[2], notes[3], notes[2]].map(n=>n.id));
|
||||
expect(state.forwardHistoryNotes.map(n=>n.id)).toEqual([notes[3], notes[2], notes[3], notes[2]].map(n=>n.id));
|
||||
expect(state.selectedNoteIds).toEqual([notes[3].id]);
|
||||
|
||||
// delete third note
|
||||
state = reducer(state, { type: 'NOTE_DELETE', id: notes[2].id });
|
||||
|
||||
// if adjacent duplicates not removed
|
||||
// backward = 0 1 3 3
|
||||
// forward = 3 3
|
||||
// current = 3
|
||||
|
||||
// if adjacent duplicates are removed
|
||||
// backward = 0 1 3
|
||||
// forward = 3
|
||||
// current = 3
|
||||
|
||||
// Expected: adjacent duplicates are removed and latest history does not contain current note
|
||||
// backward = 0 1
|
||||
// forward =
|
||||
// current = 3
|
||||
expect(state.backwardHistoryNotes.map(x => x.id)).toEqual([notes[0].id, notes[1].id]);
|
||||
expect(state.forwardHistoryNotes.map(x => x.id)).toEqual([]);
|
||||
expect(state.selectedNoteIds).toEqual([notes[3].id]);
|
||||
}));
|
||||
|
||||
it('should ensure history max limit is maintained', asyncTest(async () => {
|
||||
const folders = await createNTestFolders(1);
|
||||
// create 5 notes
|
||||
const notes = await createNTestNotes(5, folders[0]);
|
||||
// select the 1st folder and the 1st note
|
||||
let state = initTestState(folders, 0, notes, [0]);
|
||||
|
||||
const idx = 0;
|
||||
for (let i = 0; i < 2 * MAX_HISTORY; i++) {
|
||||
state = goToNote(notes, [i % 5], state);
|
||||
}
|
||||
|
||||
expect(state.backwardHistoryNotes.length).toEqual(MAX_HISTORY);
|
||||
expect(state.forwardHistoryNotes.map(x => x.id)).toEqual([]);
|
||||
|
||||
for (let i = 0; i < 2 * MAX_HISTORY; i++) {
|
||||
state = goBackWard(state);
|
||||
}
|
||||
|
||||
expect(state.backwardHistoryNotes).toEqual([]);
|
||||
expect(state.forwardHistoryNotes.length).toEqual(MAX_HISTORY);
|
||||
|
||||
for (let i = 0; i < 2 * MAX_HISTORY; i++) {
|
||||
state = goForward(state);
|
||||
}
|
||||
|
||||
expect(state.backwardHistoryNotes.length).toEqual(MAX_HISTORY);
|
||||
expect(state.forwardHistoryNotes.map(x => x.id)).toEqual([]);
|
||||
}));
|
||||
});
|
||||
|
@ -554,7 +554,6 @@ const mapStateToProps = (state: any) => {
|
||||
watchedNoteFiles: state.watchedNoteFiles,
|
||||
windowCommand: state.windowCommand,
|
||||
notesParentType: state.notesParentType,
|
||||
historyNotes: state.historyNotes,
|
||||
selectedNoteTags: state.selectedNoteTags,
|
||||
lastEditorScrollPercents: state.lastEditorScrollPercents,
|
||||
selectedNoteHash: state.selectedNoteHash,
|
||||
|
@ -16,7 +16,6 @@ export interface NoteEditorProps {
|
||||
windowCommand: any;
|
||||
folders: any[];
|
||||
notesParentType: string;
|
||||
historyNotes: any[];
|
||||
selectedNoteTags: any[];
|
||||
lastEditorScrollPercents: any;
|
||||
selectedNoteHash: string;
|
||||
|
@ -68,10 +68,6 @@ export default function useMessageHandler(scrollWhenReady:any, setScrollWhenRead
|
||||
folderId: item.parent_id,
|
||||
noteId: item.id,
|
||||
hash: resourceUrlInfo.hash,
|
||||
historyNoteAction: {
|
||||
id: formNote.id,
|
||||
parent_id: formNote.parent_id,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
throw new Error(`Unsupported item type: ${item.type_}`);
|
||||
|
@ -18,11 +18,12 @@ interface NoteToolbarProps {
|
||||
selectedFolderId: string,
|
||||
folders: any[],
|
||||
watchedNoteFiles: string[],
|
||||
backwardHistoryNotes: any[],
|
||||
forwardHistoryNotes: any[],
|
||||
notesParentType: string,
|
||||
note: any,
|
||||
dispatch: Function,
|
||||
onButtonClick(event:ButtonClickEvent):void,
|
||||
historyNotes: any[],
|
||||
}
|
||||
|
||||
function styles_(props:NoteToolbarProps) {
|
||||
@ -37,12 +38,37 @@ function styles_(props:NoteToolbarProps) {
|
||||
}
|
||||
|
||||
function useToolbarItems(props:NoteToolbarProps) {
|
||||
const { note, selectedFolderId, folders, watchedNoteFiles, notesParentType, dispatch, onButtonClick, historyNotes } = props;
|
||||
const { note, selectedFolderId, folders, watchedNoteFiles, notesParentType, dispatch
|
||||
, onButtonClick, backwardHistoryNotes, forwardHistoryNotes } = props;
|
||||
|
||||
const toolbarItems = [];
|
||||
|
||||
const folder = Folder.byId(folders, selectedFolderId);
|
||||
|
||||
toolbarItems.push({
|
||||
tooltip: _('Back'),
|
||||
iconName: 'fa-arrow-left',
|
||||
enabled: (backwardHistoryNotes.length > 0),
|
||||
onClick: () => {
|
||||
if (!backwardHistoryNotes.length) return;
|
||||
props.dispatch({
|
||||
type: 'HISTORY_BACKWARD',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
toolbarItems.push({
|
||||
tooltip: _('Front'),
|
||||
iconName: 'fa-arrow-right',
|
||||
enabled: (forwardHistoryNotes.length > 0),
|
||||
onClick: () => {
|
||||
if (!forwardHistoryNotes.length) return;
|
||||
props.dispatch({
|
||||
type: 'HISTORY_FORWARD',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
if (folder && ['Search', 'Tag', 'SmartFilter'].includes(notesParentType)) {
|
||||
toolbarItems.push({
|
||||
title: _('In: %s', substrWithEllipsis(folder.title, 0, 16)),
|
||||
@ -58,25 +84,6 @@ function useToolbarItems(props:NoteToolbarProps) {
|
||||
});
|
||||
}
|
||||
|
||||
if (historyNotes.length) {
|
||||
toolbarItems.push({
|
||||
tooltip: _('Back'),
|
||||
iconName: 'fa-arrow-left',
|
||||
onClick: () => {
|
||||
if (!historyNotes.length) return;
|
||||
|
||||
const lastItem = historyNotes[historyNotes.length - 1];
|
||||
|
||||
dispatch({
|
||||
type: 'FOLDER_AND_NOTE_SELECT',
|
||||
folderId: lastItem.parent_id,
|
||||
noteId: lastItem.id,
|
||||
historyNoteAction: 'pop',
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (watchedNoteFiles.indexOf(note.id) >= 0) {
|
||||
toolbarItems.push({
|
||||
tooltip: _('Click to stop external editing'),
|
||||
@ -149,7 +156,8 @@ const mapStateToProps = (state:any) => {
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
folders: state.folders,
|
||||
watchedNoteFiles: state.watchedNoteFiles,
|
||||
historyNotes: state.historyNotes,
|
||||
backwardHistoryNotes: state.backwardHistoryNotes,
|
||||
forwardHistoryNotes: state.forwardHistoryNotes,
|
||||
notesParentType: state.notesParentType,
|
||||
};
|
||||
};
|
||||
|
@ -443,6 +443,11 @@ class BaseApplication {
|
||||
refreshFolders = true;
|
||||
}
|
||||
|
||||
if (action.type == 'HISTORY_BACKWARD' || action.type == 'HISTORY_FORWARD') {
|
||||
refreshNotes = true;
|
||||
refreshNotesUseSelectedNoteId = true;
|
||||
}
|
||||
|
||||
if (action.type == 'FOLDER_SELECT' || action.type === 'FOLDER_DELETE' || action.type === 'FOLDER_AND_NOTE_SELECT' || (action.type === 'SEARCH_UPDATE' && newState.notesParentType === 'Folder')) {
|
||||
Setting.setValue('activeFolderId', newState.selectedFolderId);
|
||||
this.currentFolder_ = newState.selectedFolderId ? await Folder.load(newState.selectedFolderId) : null;
|
||||
|
@ -53,12 +53,15 @@ const defaultState = {
|
||||
resourceFetcher: {
|
||||
toFetchCount: 0,
|
||||
},
|
||||
historyNotes: [],
|
||||
backwardHistoryNotes: [],
|
||||
forwardHistoryNotes: [],
|
||||
plugins: {},
|
||||
provisionalNoteIds: [],
|
||||
editorNoteStatuses: {},
|
||||
};
|
||||
|
||||
const MAX_HISTORY = 200;
|
||||
|
||||
const stateUtils = {};
|
||||
|
||||
const derivedStateCache_ = {};
|
||||
@ -115,6 +118,27 @@ stateUtils.lastSelectedNoteIds = function(state) {
|
||||
return output ? output : [];
|
||||
};
|
||||
|
||||
stateUtils.getCurrentNote = function(state) {
|
||||
const selectedNoteIds = state.selectedNoteIds;
|
||||
const notes = state.notes;
|
||||
if (selectedNoteIds != null && selectedNoteIds.length > 0) {
|
||||
const currNote = notes.find(note => note.id === selectedNoteIds[0]);
|
||||
if (currNote != null) {
|
||||
return {
|
||||
id: currNote.id,
|
||||
parent_id: currNote.parent_id,
|
||||
notesParentType: state.notesParentType,
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
selectedTagId: state.selectedTagId,
|
||||
selectedSearchId: state.selectedSearchId,
|
||||
searches: state.searches,
|
||||
selectedSmartFilterId: state.selectedSmartFilterId,
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
function arrayHasEncryptedItems(array) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (array[i].encryption_applied) return true;
|
||||
@ -146,6 +170,10 @@ function folderSetCollapsed(state, action) {
|
||||
return newState;
|
||||
}
|
||||
|
||||
function removeAdjacentDuplicates(items) {
|
||||
return items.filter((item, idx) => (idx >= 1) ? items[idx - 1].id !== item.id : true);
|
||||
}
|
||||
|
||||
// When deleting a note, tag or folder
|
||||
function handleItemDelete(state, action) {
|
||||
const map = {
|
||||
@ -264,8 +292,6 @@ function defaultNotesParentType(state, exclusion) {
|
||||
|
||||
function changeSelectedFolder(state, action, options = null) {
|
||||
if (!options) options = {};
|
||||
if (!('clearNoteHistory' in options)) options.clearNoteHistory = true;
|
||||
|
||||
const newState = Object.assign({}, state);
|
||||
newState.selectedFolderId = 'folderId' in action ? action.folderId : action.id;
|
||||
if (!newState.selectedFolderId) {
|
||||
@ -276,7 +302,6 @@ function changeSelectedFolder(state, action, options = null) {
|
||||
|
||||
if (newState.selectedFolderId === state.selectedFolderId && newState.notesParentType === state.notesParentType) return state;
|
||||
|
||||
if (options.clearNoteHistory) newState.historyNotes = [];
|
||||
if (options.clearSelectedNoteIds) newState.selectedNoteIds = [];
|
||||
|
||||
return newState;
|
||||
@ -296,7 +321,6 @@ function recordLastSelectedNoteIds(state, noteIds) {
|
||||
|
||||
function changeSelectedNotes(state, action, options = null) {
|
||||
if (!options) options = {};
|
||||
if (!('clearNoteHistory' in options)) options.clearNoteHistory = true;
|
||||
|
||||
let noteIds = [];
|
||||
if (action.id) noteIds = [action.id];
|
||||
@ -337,8 +361,6 @@ function changeSelectedNotes(state, action, options = null) {
|
||||
|
||||
newState = recordLastSelectedNoteIds(newState, newState.selectedNoteIds);
|
||||
|
||||
if (options.clearNoteHistory) newState.historyNotes = [];
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
@ -353,20 +375,158 @@ function removeItemFromArray(array, property, value) {
|
||||
return array;
|
||||
}
|
||||
|
||||
const getContextFromHistory = (ctx) => {
|
||||
const result = {};
|
||||
result.notesParentType = ctx.notesParentType;
|
||||
if (result.notesParentType === 'Folder') {
|
||||
result.selectedFolderId = ctx.selectedFolderId;
|
||||
} else if (result.notesParentType === 'Tag') {
|
||||
result.selectedTagId = ctx.selectedTagId;
|
||||
} else if (result.notesParentType === 'Search') {
|
||||
result.selectedSearchId = ctx.selectedSearchId;
|
||||
result.searches = ctx.searches;
|
||||
} else if (result.notesParentType === 'SmartFilter') {
|
||||
result.selectedSmartFilterId = ctx.selectedSmartFilterId;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
function handleHistory(state, action) {
|
||||
let newState = Object.assign({}, state);
|
||||
let backwardHistoryNotes = newState.backwardHistoryNotes.slice();
|
||||
let forwardHistoryNotes = newState.forwardHistoryNotes.slice();
|
||||
const currentNote = stateUtils.getCurrentNote(state);
|
||||
switch (action.type) {
|
||||
case 'HISTORY_BACKWARD': {
|
||||
const note = backwardHistoryNotes[backwardHistoryNotes.length - 1];
|
||||
if (currentNote != null && (forwardHistoryNotes.length === 0 || currentNote.id != forwardHistoryNotes[forwardHistoryNotes.length - 1].id)) {
|
||||
forwardHistoryNotes = forwardHistoryNotes.concat(currentNote).slice(-MAX_HISTORY);
|
||||
}
|
||||
|
||||
newState = changeSelectedFolder(newState, Object.assign({}, action, { type: 'FOLDER_SELECT', folderId: note.parent_id }));
|
||||
newState = changeSelectedNotes(newState, Object.assign({}, action, { type: 'NOTE_SELECT', noteId: note.id }));
|
||||
|
||||
const ctx = backwardHistoryNotes[backwardHistoryNotes.length - 1];
|
||||
newState = Object.assign(newState, getContextFromHistory(ctx));
|
||||
|
||||
backwardHistoryNotes.pop();
|
||||
break;
|
||||
}
|
||||
case 'HISTORY_FORWARD': {
|
||||
const note = forwardHistoryNotes[forwardHistoryNotes.length - 1];
|
||||
|
||||
if (currentNote != null && (backwardHistoryNotes.length === 0 || currentNote.id != backwardHistoryNotes[backwardHistoryNotes.length - 1].id)) {
|
||||
backwardHistoryNotes = backwardHistoryNotes.concat(currentNote).slice(-MAX_HISTORY);
|
||||
}
|
||||
|
||||
newState = changeSelectedFolder(newState, Object.assign({}, action, { type: 'FOLDER_SELECT', folderId: note.parent_id }));
|
||||
newState = changeSelectedNotes(newState, Object.assign({}, action, { type: 'NOTE_SELECT', noteId: note.id }));
|
||||
|
||||
const ctx = forwardHistoryNotes[forwardHistoryNotes.length - 1];
|
||||
newState = Object.assign(newState, getContextFromHistory(ctx));
|
||||
|
||||
|
||||
forwardHistoryNotes.pop();
|
||||
break;
|
||||
}
|
||||
case 'NOTE_SELECT':
|
||||
if (currentNote != null && action.id != currentNote.id) {
|
||||
forwardHistoryNotes = [];
|
||||
backwardHistoryNotes = backwardHistoryNotes.concat(currentNote).slice(-MAX_HISTORY);
|
||||
}
|
||||
// History should be free from duplicates.
|
||||
if (backwardHistoryNotes != null && backwardHistoryNotes.length > 0 &&
|
||||
action.id === backwardHistoryNotes[backwardHistoryNotes.length - 1].id) {
|
||||
backwardHistoryNotes.pop();
|
||||
}
|
||||
break;
|
||||
case 'TAG_SELECT':
|
||||
case 'FOLDER_AND_NOTE_SELECT':
|
||||
case 'FOLDER_SELECT':
|
||||
if (currentNote != null) {
|
||||
forwardHistoryNotes = [];
|
||||
backwardHistoryNotes = backwardHistoryNotes.concat(currentNote).slice(-MAX_HISTORY);
|
||||
}
|
||||
break;
|
||||
case 'NOTE_UPDATE_ONE': {
|
||||
const modNote = action.note;
|
||||
|
||||
backwardHistoryNotes = backwardHistoryNotes.map(note => {
|
||||
if (note.id === modNote.id) {
|
||||
return Object.assign(note, { parent_id: modNote.parent_id, selectedFolderId: modNote.parent_id });
|
||||
}
|
||||
return note;
|
||||
});
|
||||
|
||||
forwardHistoryNotes = forwardHistoryNotes.map(note => {
|
||||
if (note.id === modNote.id) {
|
||||
return Object.assign(note, { parent_id: modNote.parent_id, selectedFolderId: modNote.parent_id });
|
||||
}
|
||||
return note;
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case 'SEARCH_UPDATE':
|
||||
if (currentNote != null && (backwardHistoryNotes.length === 0 ||
|
||||
backwardHistoryNotes[backwardHistoryNotes.length - 1].id != currentNote.id)) {
|
||||
forwardHistoryNotes = [];
|
||||
backwardHistoryNotes = backwardHistoryNotes.concat(currentNote).slice(-MAX_HISTORY);
|
||||
}
|
||||
break;
|
||||
case 'FOLDER_DELETE':
|
||||
backwardHistoryNotes = backwardHistoryNotes.filter(note => note.parent_id != action.id);
|
||||
forwardHistoryNotes = forwardHistoryNotes.filter(note => note.parent_id != action.id);
|
||||
|
||||
backwardHistoryNotes = removeAdjacentDuplicates(backwardHistoryNotes);
|
||||
forwardHistoryNotes = removeAdjacentDuplicates(forwardHistoryNotes);
|
||||
break;
|
||||
case 'NOTE_DELETE': {
|
||||
backwardHistoryNotes = backwardHistoryNotes.filter(note => note.id != action.id);
|
||||
forwardHistoryNotes = forwardHistoryNotes.filter(note => note.id != action.id);
|
||||
|
||||
backwardHistoryNotes = removeAdjacentDuplicates(backwardHistoryNotes);
|
||||
forwardHistoryNotes = removeAdjacentDuplicates(forwardHistoryNotes);
|
||||
|
||||
// Fix the case where after deletion the currently selected note is also the latest in history
|
||||
const selectedNoteIds = newState.selectedNoteIds;
|
||||
if (selectedNoteIds.length && backwardHistoryNotes.length && backwardHistoryNotes[backwardHistoryNotes.length - 1].id === selectedNoteIds[0]) {
|
||||
backwardHistoryNotes = backwardHistoryNotes.slice(0, backwardHistoryNotes.length - 1);
|
||||
}
|
||||
if (selectedNoteIds.length && forwardHistoryNotes.length && forwardHistoryNotes[forwardHistoryNotes.length - 1].id === selectedNoteIds[0]) {
|
||||
forwardHistoryNotes = forwardHistoryNotes.slice(0, forwardHistoryNotes.length - 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// console.log('Unknown action in history reducer.' ,action.type);
|
||||
return state;
|
||||
}
|
||||
|
||||
newState.backwardHistoryNotes = backwardHistoryNotes;
|
||||
newState.forwardHistoryNotes = forwardHistoryNotes;
|
||||
return newState;
|
||||
}
|
||||
|
||||
const reducer = (state = defaultState, action) => {
|
||||
// if (!['SIDE_MENU_OPEN_PERCENT'].includes(action.type)) console.info('Action', action.type);
|
||||
|
||||
let newState = state;
|
||||
|
||||
// NOTE_DELETE requires post processing
|
||||
if (action.type !== 'NOTE_DELETE') {
|
||||
newState = handleHistory(newState, action);
|
||||
}
|
||||
|
||||
try {
|
||||
switch (action.type) {
|
||||
|
||||
case 'NOTE_SELECT':
|
||||
case 'NOTE_SELECT_ADD':
|
||||
case 'NOTE_SELECT_REMOVE':
|
||||
case 'NOTE_SELECT_TOGGLE':
|
||||
newState = changeSelectedNotes(state, action);
|
||||
newState = changeSelectedNotes(newState, action);
|
||||
break;
|
||||
|
||||
case 'NOTE_SELECT_EXTEND':
|
||||
{
|
||||
newState = Object.assign({}, state);
|
||||
@ -424,29 +584,14 @@ const reducer = (state = defaultState, action) => {
|
||||
break;
|
||||
|
||||
case 'FOLDER_SELECT':
|
||||
newState = changeSelectedFolder(state, action, { clearSelectedNoteIds: true });
|
||||
newState = changeSelectedFolder(newState, action, { clearSelectedNoteIds: true });
|
||||
break;
|
||||
|
||||
case 'FOLDER_AND_NOTE_SELECT':
|
||||
{
|
||||
newState = changeSelectedFolder(state, action, { clearNoteHistory: false });
|
||||
newState = changeSelectedFolder(newState, action);
|
||||
const noteSelectAction = Object.assign({}, action, { type: 'NOTE_SELECT' });
|
||||
newState = changeSelectedNotes(newState, noteSelectAction, { clearNoteHistory: false });
|
||||
|
||||
if (action.historyNoteAction) {
|
||||
const historyNotes = newState.historyNotes.slice();
|
||||
if (typeof action.historyNoteAction === 'object') {
|
||||
historyNotes.push(Object.assign({}, action.historyNoteAction));
|
||||
} else if (action.historyNoteAction === 'pop') {
|
||||
historyNotes.pop();
|
||||
}
|
||||
newState.historyNotes = historyNotes;
|
||||
} else if (newState !== state) {
|
||||
// Clear the note history if folder and selected note have actually been changed. For example
|
||||
// they won't change if they are already selected. That way, the "Back" button to go to the
|
||||
// previous note wll stay.
|
||||
newState.historyNotes = [];
|
||||
}
|
||||
newState = changeSelectedNotes(newState, noteSelectAction);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -543,6 +688,7 @@ const reducer = (state = defaultState, action) => {
|
||||
newState.selectedNoteIds = newIndex >= 0 ? [newNotes[newIndex].id] : [];
|
||||
}
|
||||
|
||||
|
||||
if (action.provisional) {
|
||||
newState.provisionalNoteIds.push(modNote.id);
|
||||
} else {
|
||||
@ -603,7 +749,6 @@ const reducer = (state = defaultState, action) => {
|
||||
break;
|
||||
|
||||
case 'TAG_SELECT':
|
||||
newState = Object.assign({}, state);
|
||||
newState.selectedTagId = action.id;
|
||||
if (!action.id) {
|
||||
newState.notesParentType = defaultNotesParentType(state, 'Tag');
|
||||
@ -654,7 +799,7 @@ const reducer = (state = defaultState, action) => {
|
||||
break;
|
||||
|
||||
case 'FOLDER_DELETE':
|
||||
newState = handleItemDelete(state, action);
|
||||
newState = handleItemDelete(newState, action);
|
||||
break;
|
||||
|
||||
case 'MASTERKEY_UPDATE_ALL':
|
||||
@ -725,7 +870,7 @@ const reducer = (state = defaultState, action) => {
|
||||
|
||||
case 'SEARCH_UPDATE':
|
||||
{
|
||||
newState = Object.assign({}, state);
|
||||
newState = handleHistory(state, action);
|
||||
const searches = newState.searches.slice();
|
||||
let found = false;
|
||||
for (let i = 0; i < searches.length; i++) {
|
||||
@ -846,7 +991,11 @@ const reducer = (state = defaultState, action) => {
|
||||
newState.hasEncryptedItems = stateHasEncryptedItems(newState);
|
||||
}
|
||||
|
||||
if (action.type === 'NOTE_DELETE') {
|
||||
newState = handleHistory(newState, action);
|
||||
}
|
||||
|
||||
return newState;
|
||||
};
|
||||
|
||||
module.exports = { reducer, defaultState, stateUtils };
|
||||
module.exports = { reducer, defaultState, stateUtils, MAX_HISTORY };
|
||||
|
Loading…
Reference in New Issue
Block a user