mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-02-22 15:15:12 +08:00
When Slate fails, include the shape (not content) of the offending draft in the error report and recover
This commit is contained in:
parent
50d61d36fa
commit
8edc9f0fc6
2 changed files with 55 additions and 13 deletions
|
@ -405,3 +405,27 @@ export function convertToPlainText(value: Value) {
|
|||
};
|
||||
return serializeNode(value.document);
|
||||
}
|
||||
|
||||
/* This is a utility method that converts the value to JSON and strips every node
|
||||
of it's sensitive bigts, replacing text, links, images, etc. with "XXX" characers
|
||||
of the same length. This allows us to log exceptions with the document's structure
|
||||
so we can debug challenging problems but not leak PII. */
|
||||
export function convertToShapeWithoutContent(value: Value) {
|
||||
// Sidenote: this uses JSON.stringify to "walk" every key-value pair
|
||||
// in the entire JSON tree and have the opportunity to replace the values.
|
||||
let json: object = { error: 'toJSON failed' };
|
||||
try {
|
||||
json = value.toJSON();
|
||||
} catch (err) {
|
||||
// pass
|
||||
}
|
||||
return JSON.stringify(json, (key, value) => {
|
||||
if (
|
||||
typeof value === 'string' &&
|
||||
['text', 'href', 'html', 'contentId', 'className'].includes(key)
|
||||
) {
|
||||
return 'X'.repeat(value.length);
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import { SyncbackDraftTask } from '../tasks/syncback-draft-task';
|
|||
|
||||
export type MessageWithEditorState = Message & { bodyEditorState: any };
|
||||
|
||||
const { convertFromHTML, convertToHTML } = Conversion;
|
||||
const { convertFromHTML, convertToHTML, convertToShapeWithoutContent } = Conversion;
|
||||
const MetadataChangePrefix = 'metadata.';
|
||||
let DraftStore = null;
|
||||
|
||||
|
@ -44,19 +44,37 @@ function hotwireDraftBodyState(draft: any, session: DraftEditingSession): Messag
|
|||
_bodyHTMLValue = inHTML;
|
||||
|
||||
if (session._mountedEditor) {
|
||||
// compute it now and apply it, preserving the document history
|
||||
_bodyEditorValue = session._mountedEditor
|
||||
.moveToRangeOfDocument()
|
||||
.delete()
|
||||
.insertFragment(convertFromHTML(inHTML).document)
|
||||
.moveToRangeOfDocument()
|
||||
.moveToStart().value;
|
||||
const inHTMLEditorValue = convertFromHTML(inHTML);
|
||||
try {
|
||||
// try to apply the new value to the existing document to preserve undo history.
|
||||
_bodyEditorValue = session._mountedEditor
|
||||
.moveToRangeOfDocument()
|
||||
.delete()
|
||||
.insertFragment(inHTMLEditorValue.document)
|
||||
.moveToRangeOfDocument()
|
||||
.moveToStart().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;
|
||||
// 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;
|
||||
}
|
||||
} catch (err) {
|
||||
// deleting and re-inserting the whole document seems to push Slate pretty hard and it
|
||||
// sometimes fails with odd schema issues (undefined node, invalid range.) Just fall
|
||||
// back to blowing away undo rather than getting the editor stuck.
|
||||
|
||||
// These errors are "downstream" from the actual problem (an invalid document model).
|
||||
// To debug this, we convert the doc to JSON and replace all the actual text / html
|
||||
// with "XXXX". Sending this home means we can replay the failing transform with an
|
||||
// equivalent document of the same shape.
|
||||
AppEnv.reportError(new Error(`Unable to insert fragment into existing document.`), {
|
||||
underlyingError: err,
|
||||
existingSlateShape: convertToShapeWithoutContent(session._mountedEditor.value),
|
||||
incomingSlateShape: convertToShapeWithoutContent(inHTMLEditorValue),
|
||||
});
|
||||
_bodyEditorValue = inHTMLEditorValue;
|
||||
}
|
||||
} else {
|
||||
// compute it again when it's asked for
|
||||
|
|
Loading…
Reference in a new issue