mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-26 18:58:21 +02:00
All: Added concept of provisional note to simplify creation and handling of newly created notes
This commit is contained in:
parent
6542a60d61
commit
6ca0e6adcc
@ -96,15 +96,15 @@ class MainScreenComponent extends React.Component {
|
||||
const folderId = Setting.value('activeFolderId');
|
||||
if (!folderId) return;
|
||||
|
||||
const newNote = {
|
||||
const newNote = await Note.save({
|
||||
parent_id: folderId,
|
||||
template: template,
|
||||
is_todo: isTodo ? 1 : 0,
|
||||
};
|
||||
template: template,
|
||||
}, { provisional: true });
|
||||
|
||||
this.props.dispatch({
|
||||
type: 'NOTE_SET_NEW_ONE',
|
||||
item: newNote,
|
||||
type: 'NOTE_SELECT',
|
||||
id: newNote.id,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -159,7 +159,7 @@ class NoteListComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
let style = Object.assign({ width: width }, this.style().listItem);
|
||||
let style = Object.assign({ width: width, opacity: this.props.provisionalNoteIds.includes(item.id) ? 0.5 : 1 }, this.style().listItem);
|
||||
|
||||
if (this.props.selectedNoteIds.indexOf(item.id) >= 0) {
|
||||
style = Object.assign(style, this.style().listItemSelected);
|
||||
@ -465,6 +465,7 @@ const mapStateToProps = state => {
|
||||
selectedSearchId: state.selectedSearchId,
|
||||
watchedNoteFiles: state.watchedNoteFiles,
|
||||
windowCommand: state.windowCommand,
|
||||
provisionalNoteIds: state.provisionalNoteIds,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -101,7 +101,6 @@ class NoteTextComponent extends React.Component {
|
||||
webviewReady: false,
|
||||
scrollHeight: null,
|
||||
editorScrollTop: 0,
|
||||
newNote: null,
|
||||
noteTags: [],
|
||||
showRevisions: false,
|
||||
loading: false,
|
||||
@ -416,9 +415,8 @@ class NoteTextComponent extends React.Component {
|
||||
async UNSAFE_componentWillMount() {
|
||||
let note = null;
|
||||
let noteTags = [];
|
||||
if (this.props.newNote) {
|
||||
note = Object.assign({}, this.props.newNote);
|
||||
} else if (this.props.noteId) {
|
||||
|
||||
if (this.props.noteId) {
|
||||
note = await Note.load(this.props.noteId);
|
||||
noteTags = this.props.noteTags || [];
|
||||
}
|
||||
@ -537,35 +535,24 @@ class NoteTextComponent extends React.Component {
|
||||
this.setState({ loading: true });
|
||||
|
||||
const stateNoteId = this.state.note ? this.state.note.id : null;
|
||||
let noteId = null;
|
||||
let note = null;
|
||||
let loadingNewNote = true;
|
||||
let noteId = props.noteId;
|
||||
let parentFolder = null;
|
||||
let scrollPercent = 0;
|
||||
const isProvisionalNote = this.props.provisionalNoteIds.includes(noteId);
|
||||
|
||||
if (props.newNote) {
|
||||
// assign new note and prevent body from being null
|
||||
note = Object.assign({}, props.newNote, { body: '' });
|
||||
this.lastLoadedNoteId_ = null;
|
||||
if (note.template) note.body = TemplateUtils.render(note.template);
|
||||
} else {
|
||||
noteId = props.noteId;
|
||||
let scrollPercent = this.props.lastEditorScrollPercents[noteId];
|
||||
if (!scrollPercent) scrollPercent = 0;
|
||||
|
||||
scrollPercent = this.props.lastEditorScrollPercents[noteId];
|
||||
if (!scrollPercent) scrollPercent = 0;
|
||||
let loadingNewNote = stateNoteId !== noteId;
|
||||
this.lastLoadedNoteId_ = noteId;
|
||||
let note = noteId ? await Note.load(noteId) : null;
|
||||
if (noteId !== this.lastLoadedNoteId_) return defer(); // Race condition - current note was changed while this one was loading
|
||||
if (options.noReloadIfLocalChanges && this.isModified()) return defer();
|
||||
|
||||
loadingNewNote = stateNoteId !== noteId;
|
||||
this.lastLoadedNoteId_ = noteId;
|
||||
note = noteId ? await Note.load(noteId) : null;
|
||||
if (noteId !== this.lastLoadedNoteId_) return defer(); // Race condition - current note was changed while this one was loading
|
||||
if (options.noReloadIfLocalChanges && this.isModified()) return defer();
|
||||
|
||||
// If the note hasn't been changed, exit now
|
||||
if (this.state.note && note) {
|
||||
let diff = Note.diffObjects(this.state.note, note);
|
||||
delete diff.type_;
|
||||
if (!Object.getOwnPropertyNames(diff).length) return defer();
|
||||
}
|
||||
// If the note hasn't been changed, exit now
|
||||
if (this.state.note && note) {
|
||||
let diff = Note.diffObjects(this.state.note, note);
|
||||
delete diff.type_;
|
||||
if (!Object.getOwnPropertyNames(diff).length) return defer();
|
||||
}
|
||||
|
||||
this.markupToHtml_ = null;
|
||||
@ -573,7 +560,7 @@ class NoteTextComponent extends React.Component {
|
||||
// If we are loading nothing (noteId == null), make sure to
|
||||
// set webviewReady to false too because the webview component
|
||||
// is going to be removed in render().
|
||||
const webviewReady = !!this.webviewRef_.current && this.state.webviewReady && (!!noteId || !!props.newNote);
|
||||
const webviewReady = !!this.webviewRef_.current && this.state.webviewReady && !!noteId;
|
||||
|
||||
// Scroll back to top when loading new note
|
||||
if (loadingNewNote) {
|
||||
@ -589,7 +576,7 @@ class NoteTextComponent extends React.Component {
|
||||
this.restoreScrollTop_ = 0;
|
||||
|
||||
// Only force focus on notes when creating a new note/todo
|
||||
if (this.props.newNote) {
|
||||
if (isProvisionalNote) {
|
||||
const focusSettingName = note.is_todo ? 'newTodoFocus' : 'newNoteFocus';
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
@ -661,9 +648,7 @@ class NoteTextComponent extends React.Component {
|
||||
}
|
||||
|
||||
async UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
if (this.props.newNote !== nextProps.newNote && nextProps.newNote) {
|
||||
await this.scheduleReloadNote(nextProps);
|
||||
} else if ('noteId' in nextProps && nextProps.noteId !== this.props.noteId) {
|
||||
if ('noteId' in nextProps && nextProps.noteId !== this.props.noteId) {
|
||||
await this.scheduleReloadNote(nextProps);
|
||||
} else if ('noteTags' in nextProps && this.areNoteTagsModified(nextProps.noteTags, this.state.noteTags)) {
|
||||
this.setState({
|
||||
@ -1941,7 +1926,6 @@ class NoteTextComponent extends React.Component {
|
||||
if (this.props.selectedNoteIds.length > 1) {
|
||||
return this.renderMultiNotes(rootStyle);
|
||||
} else if (!note || !!note.encryption_applied) {
|
||||
// || (note && !this.props.newNote && this.props.noteId && note.id !== this.props.noteId)) { // note.id !== props.noteId is when the note has not been loaded yet, and the previous one is still in the state
|
||||
return this.renderNoNotes(rootStyle);
|
||||
}
|
||||
|
||||
@ -2101,7 +2085,7 @@ class NoteTextComponent extends React.Component {
|
||||
this.title_changeText(event);
|
||||
}}
|
||||
onKeyDown={this.titleField_keyDown}
|
||||
placeholder={this.props.newNote ? _('Creating new %s...', isTodo ? _('to-do') : _('note')) : ''}
|
||||
placeholder={this.props.provisionalNoteIds.includes(note.id) ? _('Creating new %s...', isTodo ? _('to-do') : _('note')) : ''}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -2229,7 +2213,6 @@ const mapStateToProps = state => {
|
||||
folders: state.folders,
|
||||
theme: state.settings.theme,
|
||||
syncStarted: state.syncStarted,
|
||||
newNote: state.newNote,
|
||||
windowCommand: state.windowCommand,
|
||||
notesParentType: state.notesParentType,
|
||||
searches: state.searches,
|
||||
@ -2239,6 +2222,7 @@ const mapStateToProps = state => {
|
||||
lastEditorScrollPercents: state.lastEditorScrollPercents,
|
||||
historyNotes: state.historyNotes,
|
||||
templates: state.templates,
|
||||
provisionalNoteIds: state.provisionalNoteIds,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
const Setting = require('lib/models/Setting');
|
||||
const Tag = require('lib/models/Tag');
|
||||
const Note = require('lib/models/Note');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const ResourceFetcher = require('lib/services/ResourceFetcher');
|
||||
|
||||
@ -28,16 +29,22 @@ const reduxSharedMiddleware = async function(store, next, action) {
|
||||
refreshTags = true;
|
||||
}
|
||||
|
||||
if (action.type === 'NOTE_SELECT') {
|
||||
const noteIds = newState.provisionalNoteIds.slice();
|
||||
for (const noteId of noteIds) {
|
||||
if (action.id === noteId) continue;
|
||||
await Note.delete(noteId);
|
||||
}
|
||||
}
|
||||
|
||||
if (action.type === 'NOTE_DELETE' ||
|
||||
action.type === 'NOTE_SELECT' ||
|
||||
action.type === 'NOTE_SELECT_TOGGLE' ||
|
||||
action.type === 'NOTE_SET_NEW_ONE') {
|
||||
action.type === 'NOTE_SELECT_TOGGLE') {
|
||||
let noteTags = [];
|
||||
|
||||
// We don't need to show tags unless only one note is selected.
|
||||
// For new notes, the old note is still selected, but we don't want to show any tags.
|
||||
if (action.type !== 'NOTE_SET_NEW_ONE' &&
|
||||
newState.selectedNoteIds &&
|
||||
if (newState.selectedNoteIds &&
|
||||
newState.selectedNoteIds.length === 1) {
|
||||
noteTags = await Tag.tagsByNoteId(newState.selectedNoteIds[0]);
|
||||
}
|
||||
|
@ -561,6 +561,7 @@ class Note extends BaseItem {
|
||||
this.dispatch({
|
||||
type: 'NOTE_UPDATE_ONE',
|
||||
note: note,
|
||||
provisional: !!options.provisional,
|
||||
});
|
||||
|
||||
if ('todo_due' in o || 'todo_completed' in o || 'is_todo' in o || 'is_conflict' in o) {
|
||||
|
@ -32,7 +32,6 @@ const defaultState = {
|
||||
sharedData: null,
|
||||
appState: 'starting',
|
||||
hasDisabledSyncItems: false,
|
||||
newNote: null,
|
||||
customCss: '',
|
||||
templates: [],
|
||||
collapsedFolderIds: [],
|
||||
@ -51,6 +50,7 @@ const defaultState = {
|
||||
},
|
||||
historyNotes: [],
|
||||
plugins: {},
|
||||
provisionalNoteIds: [],
|
||||
};
|
||||
|
||||
const stateUtils = {};
|
||||
@ -291,19 +291,15 @@ function changeSelectedNotes(state, action, options = null) {
|
||||
if (action.ids) noteIds = action.ids;
|
||||
if (action.noteId) noteIds = [action.noteId];
|
||||
|
||||
// const noteIds = 'id' in action ? (action.id ? [action.id] : []) : action.ids;
|
||||
|
||||
let newState = Object.assign({}, state);
|
||||
|
||||
if (action.type === 'NOTE_SELECT') {
|
||||
if (JSON.stringify(newState.selectedNoteIds) === JSON.stringify(noteIds)) return state;
|
||||
newState.selectedNoteIds = noteIds;
|
||||
newState.newNote = null;
|
||||
newState.selectedNoteHash = action.hash ? action.hash : '';
|
||||
} else if (action.type === 'NOTE_SELECT_ADD') {
|
||||
if (!noteIds.length) return state;
|
||||
newState.selectedNoteIds = ArrayUtils.unique(newState.selectedNoteIds.concat(noteIds));
|
||||
newState.newNote = null;
|
||||
} else if (action.type === 'NOTE_SELECT_REMOVE') {
|
||||
if (!noteIds.length) return state; // Nothing to unselect
|
||||
if (state.selectedNoteIds.length <= 1) return state; // Cannot unselect the last note
|
||||
@ -315,7 +311,6 @@ function changeSelectedNotes(state, action, options = null) {
|
||||
newSelectedNoteIds.push(id);
|
||||
}
|
||||
newState.selectedNoteIds = newSelectedNoteIds;
|
||||
newState.newNote = null;
|
||||
} else if (action.type === 'NOTE_SELECT_TOGGLE') {
|
||||
if (!noteIds.length) return state;
|
||||
|
||||
@ -324,8 +319,6 @@ function changeSelectedNotes(state, action, options = null) {
|
||||
} else {
|
||||
newState = changeSelectedNotes(state, { type: 'NOTE_SELECT_ADD', id: noteIds[0] });
|
||||
}
|
||||
|
||||
newState.newNote = null;
|
||||
} else {
|
||||
throw new Error('Unreachable');
|
||||
}
|
||||
@ -512,11 +505,32 @@ const reducer = (state = defaultState, action) => {
|
||||
if (!newNotes.length) newIndex = -1;
|
||||
newState.selectedNoteIds = newIndex >= 0 ? [newNotes[newIndex].id] : [];
|
||||
}
|
||||
|
||||
if (action.provisional) {
|
||||
newState.provisionalNoteIds.push(modNote.id);
|
||||
} else {
|
||||
const idx = newState.provisionalNoteIds.indexOf(modNote.id);
|
||||
if (idx >= 0) {
|
||||
const t = newState.provisionalNoteIds.slice();
|
||||
t.splice(idx, 1);
|
||||
newState.provisionalNoteIds = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'NOTE_DELETE':
|
||||
newState = handleItemDelete(state, action);
|
||||
|
||||
{
|
||||
newState = handleItemDelete(state, action);
|
||||
|
||||
const idx = newState.provisionalNoteIds.indexOf(action.id);
|
||||
if (idx >= 0) {
|
||||
const t = newState.provisionalNoteIds.slice();
|
||||
t.splice(idx, 1);
|
||||
newState.provisionalNoteIds = t;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'TAG_DELETE':
|
||||
@ -700,15 +714,6 @@ const reducer = (state = defaultState, action) => {
|
||||
newState.hasDisabledSyncItems = true;
|
||||
break;
|
||||
|
||||
case 'NOTE_SET_NEW_ONE':
|
||||
newState = Object.assign({}, state);
|
||||
newState.newNote = action.item;
|
||||
if (newState.selectedNoteIds.length > 1) {
|
||||
newState.selectedNoteIds = newState.selectedNoteIds.slice();
|
||||
newState.selectedNoteIds = [newState.selectedNoteIds[0]];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'CLIPPER_SERVER_SET':
|
||||
{
|
||||
newState = Object.assign({}, state);
|
||||
|
Loading…
x
Reference in New Issue
Block a user