1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-12 08:54:00 +02:00
joplin/ReactNativeClient/lib/reducer.js
2017-11-17 18:57:27 +00:00

326 lines
7.6 KiB
JavaScript

const { Note } = require('lib/models/note.js');
const { Folder } = require('lib/models/folder.js');
const defaultState = {
notes: [],
notesSource: '',
notesParentType: null,
folders: [],
tags: [],
searches: [],
selectedNoteId: null,
selectedFolderId: null,
selectedTagId: null,
selectedSearchId: null,
selectedItemType: 'note',
showSideMenu: false,
screens: {},
historyCanGoBack: false,
notesOrder: [
{ by: 'user_updated_time', dir: 'DESC' },
],
syncStarted: false,
syncReport: {},
searchQuery: '',
settings: {},
appState: 'starting',
windowContentSize: { width: 0, height: 0 },
};
// When deleting a note, tag or folder
function handleItemDelete(state, action) {
let newState = Object.assign({}, state);
const map = {
'FOLDER_DELETE': ['folders', 'selectedFolderId'],
'NOTE_DELETE': ['notes', 'selectedNoteId'],
'TAG_DELETE': ['tags', 'selectedTagId'],
'SEARCH_DELETE': ['searches', 'selectedSearchId'],
};
const listKey = map[action.type][0];
const selectedItemKey = map[action.type][1];
let previousIndex = 0;
let newItems = [];
const items = state[listKey];
for (let i = 0; i < items.length; i++) {
let item = items[i];
if (item.id == action.id) {
previousIndex = i;
continue;
}
newItems.push(item);
}
newState = Object.assign({}, state);
newState[listKey] = newItems;
if (previousIndex >= newItems.length) {
previousIndex = newItems.length - 1;
}
const newIndex = previousIndex >= 0 ? newItems[previousIndex].id : null;
newState[selectedItemKey] = newIndex;
if (!newIndex && newState.notesParentType !== 'Folder') {
newState.notesParentType = 'Folder';
}
return newState;
}
function updateOneTagOrFolder(state, action) {
let newItems = action.type === 'TAG_UPDATE_ONE' ? state.tags.splice(0) : state.folders.splice(0);
let item = action.type === 'TAG_UPDATE_ONE' ? action.tag : action.folder;
var found = false;
for (let i = 0; i < newItems.length; i++) {
let n = newItems[i];
if (n.id == item.id) {
newItems[i] = Object.assign(newItems[i], item);
found = true;
break;
}
}
if (!found) newItems.push(item);
let newState = Object.assign({}, state);
if (action.type === 'TAG_UPDATE_ONE') {
newState.tags = newItems;
} else {
newState.folders = newItems;
}
return newState;
}
function defaultNotesParentType(state, exclusion) {
let newNotesParentType = null;
if (exclusion !== 'Folder' && state.selectedFolderId) {
newNotesParentType = 'Folder';
} else if (exclusion !== 'Tag' && state.selectedTagId) {
newNotesParentType = 'Tag';
} else if (exclusion !== 'Search' && state.selectedSearchId) {
newNotesParentType = 'Search';
}
return newNotesParentType;
}
const reducer = (state = defaultState, action) => {
let newState = state;
try {
switch (action.type) {
case 'NOTE_SELECT':
newState = Object.assign({}, state);
newState.selectedNoteId = action.id;
break;
case 'FOLDER_SELECT':
newState = Object.assign({}, state);
newState.selectedFolderId = action.id;
if (!action.id) {
newState.notesParentType = defaultNotesParentType(state, 'Folder');
} else {
newState.notesParentType = 'Folder';
}
break;
case 'SETTING_UPDATE_ALL':
newState = Object.assign({}, state);
newState.settings = action.settings;
break;
case 'SETTING_UPDATE_ONE':
newState = Object.assign({}, state);
let newSettings = Object.assign({}, state.settings);
newSettings[action.key] = action.value;
newState.settings = newSettings;
break;
// Replace all the notes with the provided array
case 'NOTE_UPDATE_ALL':
newState = Object.assign({}, state);
newState.notes = action.notes;
newState.notesSource = action.notesSource;
break;
// Insert the note into the note list if it's new, or
// update it within the note array if it already exists.
case 'NOTE_UPDATE_ONE':
const modNote = action.note;
const noteIsInFolder = function(note, folderId) {
if (note.is_conflict) return folderId === Folder.conflictFolderId();
if (!('parent_id' in modNote) || note.parent_id == folderId) return true;
return false;
}
let noteFolderHasChanged = false;
let newNotes = state.notes.slice();
var found = false;
for (let i = 0; i < newNotes.length; i++) {
let n = newNotes[i];
if (n.id == modNote.id) {
// Note is still in the same folder
if (noteIsInFolder(modNote, n.parent_id)) {
// Merge the properties that have changed (in modNote) into
// the object we already have.
newNotes[i] = Object.assign({}, newNotes[i]);
for (let n in modNote) {
if (!modNote.hasOwnProperty(n)) continue;
newNotes[i][n] = modNote[n];
}
} else { // Note has moved to a different folder
newNotes.splice(i, 1);
noteFolderHasChanged = true;
}
found = true;
break;
}
}
// Note was not found - if the current folder is the same as the note folder,
// add it to it.
if (!found) {
if (noteIsInFolder(modNote, state.selectedFolderId)) {
newNotes.push(modNote);
}
}
newNotes = Note.sortNotes(newNotes, state.notesOrder, newState.settings.uncompletedTodosOnTop);
newState = Object.assign({}, state);
newState.notes = newNotes;
if (noteFolderHasChanged) {
newState.selectedNoteId = newNotes.length ? newNotes[0].id : null;
}
break;
case 'NOTE_DELETE':
newState = handleItemDelete(state, action);
break;
case 'TAG_DELETE':
newState = handleItemDelete(state, action);
break;
case 'FOLDER_UPDATE_ALL':
newState = Object.assign({}, state);
newState.folders = action.folders;
break;
case 'TAG_UPDATE_ALL':
newState = Object.assign({}, state);
newState.tags = action.tags;
break;
case 'TAG_SELECT':
newState = Object.assign({}, state);
newState.selectedTagId = action.id;
if (!action.id) {
newState.notesParentType = defaultNotesParentType(state, 'Tag');
} else {
newState.notesParentType = 'Tag';
}
break;
case 'TAG_UPDATE_ONE':
newState = updateOneTagOrFolder(state, action);
break;
case 'FOLDER_UPDATE_ONE':
newState = updateOneTagOrFolder(state, action);
break;
case 'FOLDER_DELETE':
newState = handleItemDelete(state, action);
break;
case 'SYNC_STARTED':
newState = Object.assign({}, state);
newState.syncStarted = true;
break;
case 'SYNC_COMPLETED':
newState = Object.assign({}, state);
newState.syncStarted = false;
break;
case 'SYNC_REPORT_UPDATE':
newState = Object.assign({}, state);
newState.syncReport = action.report;
break;
case 'SEARCH_QUERY':
newState = Object.assign({}, state);
newState.searchQuery = action.query.trim();
break;
case 'SEARCH_ADD':
newState = Object.assign({}, state);
let searches = newState.searches.slice();
searches.push(action.search);
newState.searches = searches;
break;
case 'SEARCH_DELETE':
newState = handleItemDelete(state, action);
break;
case 'SEARCH_SELECT':
newState = Object.assign({}, state);
newState.selectedSearchId = action.id;
if (!action.id) {
newState.notesParentType = defaultNotesParentType(state, 'Search');
} else {
newState.notesParentType = 'Search';
}
break;
case 'APP_STATE_SET':
newState = Object.assign({}, state);
newState.appState = action.state;
break;
}
} catch (error) {
error.message = 'In reducer: ' + error.message + ' Action: ' + JSON.stringify(action);
throw error;
}
return newState;
}
module.exports = { reducer, defaultState };