mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-02-23 07:36:12 +08:00
Improve performance of body <> bodyEditorValue by making both mutations lazy
This commit is contained in:
parent
60c613fe7d
commit
cfef571559
1 changed files with 49 additions and 24 deletions
|
@ -26,61 +26,86 @@ let DraftStore = null;
|
||||||
|
|
||||||
function hotwireDraftBodyState(draft: any, session: DraftEditingSession): MessageWithEditorState {
|
function hotwireDraftBodyState(draft: any, session: DraftEditingSession): MessageWithEditorState {
|
||||||
// Populate the bodyEditorState and override the draft properties
|
// Populate the bodyEditorState and override the draft properties
|
||||||
// so that they're kept in sync with minimal recomputation
|
// so that they're kept in sync with minimal recomputation.
|
||||||
let _bodyHTMLCache = draft.body;
|
let _bodyHTMLValue = draft.body;
|
||||||
let _bodyEditorState = null;
|
let _bodyEditorValue = null;
|
||||||
|
|
||||||
draft.__bodyPropDescriptor = {
|
draft.__bodyPropDescriptor = {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
get: function() {
|
get: function() {
|
||||||
if (!_bodyHTMLCache) {
|
if (!_bodyHTMLValue) {
|
||||||
console.log('building HTML body cache');
|
_bodyHTMLValue = convertToHTML(_bodyEditorValue);
|
||||||
_bodyHTMLCache = convertToHTML(_bodyEditorState);
|
|
||||||
}
|
}
|
||||||
return _bodyHTMLCache;
|
return _bodyHTMLValue;
|
||||||
},
|
},
|
||||||
set: function(inHTML) {
|
set: function(inHTML) {
|
||||||
let nextValue = convertFromHTML(inHTML);
|
if (_bodyHTMLValue === inHTML) return;
|
||||||
|
|
||||||
|
_bodyHTMLValue = inHTML;
|
||||||
|
|
||||||
if (session._mountedEditor) {
|
if (session._mountedEditor) {
|
||||||
nextValue = session._mountedEditor
|
// compute it now and apply it, preserving the document history
|
||||||
|
_bodyEditorValue = session._mountedEditor
|
||||||
.moveToRangeOfDocument()
|
.moveToRangeOfDocument()
|
||||||
|
.delete()
|
||||||
.insertFragment(convertFromHTML(inHTML).document)
|
.insertFragment(convertFromHTML(inHTML).document)
|
||||||
.moveToRangeOfDocument()
|
.moveToRangeOfDocument()
|
||||||
.moveToStart()
|
.moveToStart().value;
|
||||||
.deleteForward(1).value;
|
|
||||||
|
// occasionally inserting the new document adds a new line at the beginning of the value.
|
||||||
|
// It's unclaer why this happens...
|
||||||
|
const firstBlock = _bodyEditorValue.document.getBlocks().first();
|
||||||
|
if (firstBlock.text === '') {
|
||||||
|
_bodyEditorValue = session._mountedEditor.removeNodeByKey(firstBlock.key).value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// compute it again when it's asked for
|
||||||
|
_bodyEditorValue = null;
|
||||||
}
|
}
|
||||||
_bodyEditorState = nextValue;
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
draft.__bodyEditorStatePropDescriptor = {
|
draft.__bodyEditorValuePropDescriptor = {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
get: function() {
|
get: function() {
|
||||||
return _bodyEditorState;
|
if (!_bodyEditorValue) {
|
||||||
},
|
_bodyEditorValue = convertFromHTML(_bodyHTMLValue);
|
||||||
set: function(next) {
|
|
||||||
if (_bodyEditorState !== next) {
|
|
||||||
_bodyHTMLCache = null;
|
|
||||||
}
|
}
|
||||||
_bodyEditorState = next;
|
return _bodyEditorValue;
|
||||||
|
},
|
||||||
|
set: function(inValue) {
|
||||||
|
if (_bodyEditorValue === inValue) return;
|
||||||
|
_bodyHTMLValue = null;
|
||||||
|
_bodyEditorValue = inValue;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.defineProperty(draft, 'body', draft.__bodyPropDescriptor);
|
Object.defineProperty(draft, 'body', draft.__bodyPropDescriptor);
|
||||||
Object.defineProperty(draft, 'bodyEditorState', draft.__bodyEditorStatePropDescriptor);
|
Object.defineProperty(draft, 'bodyEditorState', draft.__bodyEditorValuePropDescriptor);
|
||||||
draft.body = _bodyHTMLCache;
|
draft.body = _bodyHTMLValue;
|
||||||
|
|
||||||
return draft as MessageWithEditorState;
|
return draft as MessageWithEditorState;
|
||||||
}
|
}
|
||||||
|
|
||||||
function fastCloneDraft(draft) {
|
/**
|
||||||
|
* Note: This method is intended to return a new Mesasge object so that lazy people doing
|
||||||
|
* shallow equals get the correct basic behavior as the draft is modified in the session.
|
||||||
|
*
|
||||||
|
* However, this method does not deep clone array values (To:, etc.) and the hot-wired body
|
||||||
|
* and bodyEditorValue are linked through the same internal state. Changing the body of
|
||||||
|
* the cloned draft changes the body of the old draft too.
|
||||||
|
*
|
||||||
|
* At the moment these tradeoffs seem OK because we're really just trying to make
|
||||||
|
* "props.draft !== nextProps.draft" work.
|
||||||
|
*/
|
||||||
|
function fastCloneDraft(draft: MessageWithEditorState) {
|
||||||
const next = new Message({});
|
const next = new Message({});
|
||||||
for (const key of Object.getOwnPropertyNames(draft)) {
|
for (const key of Object.getOwnPropertyNames(draft)) {
|
||||||
if (key === 'body' || key === 'bodyEditorState') continue;
|
if (key === 'body' || key === 'bodyEditorState') continue;
|
||||||
next[key] = draft[key];
|
next[key] = draft[key];
|
||||||
}
|
}
|
||||||
Object.defineProperty(next, 'body', (next as any).__bodyPropDescriptor);
|
Object.defineProperty(next, 'body', (next as any).__bodyPropDescriptor);
|
||||||
Object.defineProperty(next, 'bodyEditorState', (next as any).__bodyEditorStatePropDescriptor);
|
Object.defineProperty(next, 'bodyEditorState', (next as any).__bodyEditorValuePropDescriptor);
|
||||||
return next as MessageWithEditorState;
|
return next as MessageWithEditorState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,7 +367,6 @@ export class DraftEditingSession extends MailspringStore {
|
||||||
// be made through the editing session and we don't want to overwrite the user's
|
// be made through the editing session and we don't want to overwrite the user's
|
||||||
// work under any scenario.
|
// work under any scenario.
|
||||||
const lockedFields = this.changes.dirtyFields();
|
const lockedFields = this.changes.dirtyFields();
|
||||||
|
|
||||||
let changed = false;
|
let changed = false;
|
||||||
for (const [key] of Object.entries(Message.attributes)) {
|
for (const [key] of Object.entries(Message.attributes)) {
|
||||||
if (key === 'headerMessageId') continue;
|
if (key === 'headerMessageId') continue;
|
||||||
|
@ -356,6 +380,7 @@ export class DraftEditingSession extends MailspringStore {
|
||||||
}
|
}
|
||||||
this._draft[key] = nextDraft[key];
|
this._draft[key] = nextDraft[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
this.trigger();
|
this.trigger();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue