diff --git a/packages/client-app/internal_packages/message-list/lib/inline-image-listeners.es6 b/packages/client-app/internal_packages/message-list/lib/inline-image-listeners.es6 index 15c86b155..f6ef3a3c6 100644 --- a/packages/client-app/internal_packages/message-list/lib/inline-image-listeners.es6 +++ b/packages/client-app/internal_packages/message-list/lib/inline-image-listeners.es6 @@ -11,7 +11,7 @@ function safeDecode(str) { function _runOnImageNode(node) { if (node.src && node.dataset.nylasFile) { node.addEventListener('error', () => { - const file = JSON.parse(safeDecode(node.dataset.nylasFile), Utils.registeredObjectReviver); + const file = Utils.convertToModel(JSON.parse(safeDecode(node.dataset.nylasFile))); const initialDisplay = node.style.display; const downloadButton = document.createElement('a'); downloadButton.classList.add('inline-download-prompt') @@ -28,7 +28,7 @@ function _runOnImageNode(node) { }); node.addEventListener('load', () => { - const file = JSON.parse(safeDecode(node.dataset.nylasFile), Utils.registeredObjectReviver); + const file = Utils.convertToModel(JSON.parse(safeDecode(node.dataset.nylasFile))); node.addEventListener('dblclick', () => { Actions.fetchAndOpenFile(file); }); @@ -37,7 +37,7 @@ function _runOnImageNode(node) { } export function encodedAttributeForFile(file) { - return safeEncode(JSON.stringify(file, Utils.registeredObjectReplacer)); + return safeEncode(JSON.stringify(file)); } export function addInlineImageListeners(doc) { diff --git a/packages/client-app/spec/utils/utils-spec.coffee b/packages/client-app/spec/utils/utils-spec.coffee index 4db6e69fa..02df0dd0f 100644 --- a/packages/client-app/spec/utils/utils-spec.coffee +++ b/packages/client-app/spec/utils/utils-spec.coffee @@ -18,7 +18,7 @@ class Bar extends Foo describe 'Utils', -> - describe "registeredObjectReviver / registeredObjectReplacer", -> + describe "modelTypesReviver", -> beforeEach -> @testThread = new Thread id: 'local-1' @@ -33,18 +33,18 @@ describe 'Utils', -> it "should serialize and de-serialize models correctly", -> expectedString = '[{"client_id":"local-1","account_id":"1","metadata":[],"subject":"Test 1234","participants":[{"client_id":"local-a","account_id":"1","name":"Juan","email":"juan@nylas.com","thirdPartyData":{},"is_search_indexed":false,"id":"local-a"},{"client_id":"local-b","account_id":"1","name":"Ben","email":"ben@nylas.com","thirdPartyData":{},"is_search_indexed":false,"id":"local-b"}],"in_all_mail":true,"is_search_indexed":false,"id":"local-1","__cls":"Thread"}]' - jsonString = JSON.stringify([@testThread], Utils.registeredObjectReplacer) + jsonString = JSON.stringify([@testThread]) expect(jsonString).toEqual(expectedString) - revived = JSON.parse(jsonString, Utils.registeredObjectReviver) + revived = JSON.parse(jsonString, Utils.modelTypesReviver) expect(revived).toEqual([@testThread]) it "should re-inflate Models in places they're not explicitly declared types", -> b = new JSONBlob({id: "ThreadsToProcess", json: [@testThread]}) - jsonString = JSON.stringify(b, Utils.registeredObjectReplacer) + jsonString = JSON.stringify(b) expectedString = '{"client_id":"ThreadsToProcess","server_id":"ThreadsToProcess","json":[{"client_id":"local-1","account_id":"1","metadata":[],"subject":"Test 1234","participants":[{"client_id":"local-a","account_id":"1","name":"Juan","email":"juan@nylas.com","thirdPartyData":{},"is_search_indexed":false,"id":"local-a"},{"client_id":"local-b","account_id":"1","name":"Ben","email":"ben@nylas.com","thirdPartyData":{},"is_search_indexed":false,"id":"local-b"}],"in_all_mail":true,"is_search_indexed":false,"id":"local-1","__cls":"Thread"}],"id":"ThreadsToProcess","__cls":"JSONBlob"}' expect(jsonString).toEqual(expectedString) - revived = JSON.parse(jsonString, Utils.registeredObjectReviver) + revived = JSON.parse(jsonString, Utils.modelTypesReviver) expect(revived).toEqual(b) expect(revived.json[0] instanceof Thread).toBe(true) expect(revived.json[0].participants[0] instanceof Contact).toBe(true) diff --git a/packages/client-app/src/components/tokenizing-text-field.jsx b/packages/client-app/src/components/tokenizing-text-field.jsx index 41f524976..f950b7f04 100644 --- a/packages/client-app/src/components/tokenizing-text-field.jsx +++ b/packages/client-app/src/components/tokenizing-text-field.jsx @@ -430,7 +430,7 @@ export default class TokenizingTextField extends React.Component { let items = null; try { - items = JSON.parse(json, Utils.registeredObjectReviver); + items = JSON.parse(json).map(Utils.convertToModel); } catch (err) { console.error(err) items = null; @@ -663,7 +663,7 @@ export default class TokenizingTextField extends React.Component { if (tokens.length === 0) { tokens = [token]; } - const json = JSON.stringify(tokens, Utils.registeredObjectReplacer); + const json = JSON.stringify(tokens); event.dataTransfer.setData('nylas-token-items', json); event.dataTransfer.setData('text/plain', tokens.map(t => t.toString()).join(', ')); event.dataTransfer.dropEffect = "move"; @@ -755,7 +755,7 @@ export default class TokenizingTextField extends React.Component { _onAttachToClipboard = (event) => { const text = this.state.selectedKeys.join(', ') if (event.clipboardData) { - const json = JSON.stringify(this._selectedTokens(), Utils.registeredObjectReplacer); + const json = JSON.stringify(this._selectedTokens()); event.clipboardData.setData('text/plain', text); event.clipboardData.setData('nylas-token-items', json); diff --git a/packages/client-app/src/flux/models/query.es6 b/packages/client-app/src/flux/models/query.es6 index 79d58dc59..38eb686ef 100644 --- a/packages/client-app/src/flux/models/query.es6 +++ b/packages/client-app/src/flux/models/query.es6 @@ -296,7 +296,7 @@ export default class ModelQuery { try { return result.map((row) => { - const object = JSON.parse(row.data, Utils.registeredObjectReviver) + const object = Utils.convertToModel(JSON.parse(row.data)); for (const attrName of Object.keys(this._klass.attributes)) { const attr = this._klass.attributes[attrName]; if (!attr.needsColumn() || !attr.loadFromColumn) { diff --git a/packages/client-app/src/flux/models/utils.coffee b/packages/client-app/src/flux/models/utils.coffee index 89dbd3ea0..aae68a802 100644 --- a/packages/client-app/src/flux/models/utils.coffee +++ b/packages/client-app/src/flux/models/utils.coffee @@ -32,7 +32,7 @@ Utils = html = html.slice(0, maxLength) (new DOMParser()).parseFromString(html, "text/html").body.innerText - registeredObjectReviver: (k,v) -> + modelTypesReviver: (k,v) -> type = v?.__cls return v unless type @@ -40,13 +40,15 @@ Utils = return DatabaseObjectRegistry.deserialize(type, v) return v - - registeredObjectReplacer: (k, v) -> - if _.isObject(v) - type = this[k].constructor.name - if DatabaseObjectRegistry.isInRegistry(type) - v.__cls = type - return v + + convertToModel: (json) -> + if not json + return null + if not json.__cls + throw new Error("convertToModel: no __cls found on object.") + if not DatabaseObjectRegistry.isInRegistry(json.__cls) + throw new Error("convertToModel: __cls is not a known class.") + return DatabaseObjectRegistry.deserialize(json.__cls, json) fastOmit: (props, without) -> otherProps = Object.assign({}, props) diff --git a/packages/client-app/src/flux/stores/database-change-record.es6 b/packages/client-app/src/flux/stores/database-change-record.es6 index 853d75530..b6cedda4e 100644 --- a/packages/client-app/src/flux/stores/database-change-record.es6 +++ b/packages/client-app/src/flux/stores/database-change-record.es6 @@ -1,5 +1,3 @@ -import Utils from '../models/utils'; - /* DatabaseChangeRecord is the object emitted from the DatabaseStore when it triggers. The DatabaseChangeRecord contains information about what type of model changed, @@ -8,27 +6,13 @@ change records. */ export default class DatabaseChangeRecord { - constructor(options) { - this.options = options; - this._objects = options.objects - this._objectsString = options.objectsString; - this._objects = this._objects || JSON.parse(this._objectsString, Utils.registeredObjectReviver); - - Object.defineProperty(this, 'type', { - get: () => options.type, - }) - Object.defineProperty(this, 'objectClass', { - get: () => options.objectClass, - }) - Object.defineProperty(this, 'objects', { - get: () => { - return this._objects; - }, - }) + constructor({type, objectClass, objects}) { + this.objects = objects; + this.type = type; + this.objectClass = objectClass; } toJSON() { - this._objectsString = this._objectsString || JSON.stringify(this._objects, Utils.registeredObjectReplacer); return { type: this.type, objectClass: this.objectClass, diff --git a/packages/client-app/src/mailbox-perspective.coffee b/packages/client-app/src/mailbox-perspective.coffee index 5f47a2175..3e3f3bd29 100644 --- a/packages/client-app/src/mailbox-perspective.coffee +++ b/packages/client-app/src/mailbox-perspective.coffee @@ -61,10 +61,10 @@ class MailboxPerspective @fromJSON: (json) => try if json.type is CategoryMailboxPerspective.name - categories = JSON.parse(json.serializedCategories, Utils.registeredObjectReviver) + categories = JSON.parse(json.serializedCategories).map(Utils.convertToModel) return @forCategories(categories) else if json.type is UnreadMailboxPerspective.name - categories = JSON.parse(json.serializedCategories, Utils.registeredObjectReviver) + categories = JSON.parse(json.serializedCategories).map(Utils.convertToModel) return @forUnread(categories) else if json.type is StarredMailboxPerspective.name return @forStarred(json.accountIds) @@ -262,7 +262,7 @@ class CategoryMailboxPerspective extends MailboxPerspective toJSON: => json = super - json.serializedCategories = JSON.stringify(@_categories, Utils.registeredObjectReplacer) + json.serializedCategories = JSON.stringify(@_categories) json isEqual: (other) => diff --git a/packages/client-app/src/mailsync-process.es6 b/packages/client-app/src/mailsync-process.es6 index 4acfcd83a..0be4ebb08 100644 --- a/packages/client-app/src/mailsync-process.es6 +++ b/packages/client-app/src/mailsync-process.es6 @@ -105,7 +105,7 @@ export default class MailsyncProcess extends EventEmitter { sendMessage(json) { if (!Utils) { Utils = require('nylas-exports').Utils; } - const msg = `${JSON.stringify(json, Utils.registeredObjectReplacer)}\n`; + const msg = `${JSON.stringify(json)}\n`; const contentBuffer = Buffer.from(msg); this._proc.stdin.write(contentBuffer); }