diff --git a/dev/App/User.js b/dev/App/User.js index cd2aa9422..5386e9666 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -528,10 +528,9 @@ class AppUser extends AbstractApp { delegateRunOnDestroy(TemplateStore.templates()); TemplateStore.templates( - data.Result.Templates.map(templateData => { - const template = new TemplateModel(); - return template.parse(templateData) ? template : null; - }).filter(v => v) + data.Result.Templates.map(templateData => + TemplateModel.reviveFromJson(templateData) + ).filter(v => v) ); } }); diff --git a/dev/Knoin/AbstractModel.js b/dev/Knoin/AbstractModel.js index ed16993a3..1895703c4 100644 --- a/dev/Knoin/AbstractModel.js +++ b/dev/Knoin/AbstractModel.js @@ -12,7 +12,14 @@ export class AbstractModel { * @param {string} modelName = '' */ constructor() { -// this.sModelName = new.target.name; +/* + constructor(props) { + if (new.target === Parent) { + throw new Error("Can't instantiate abstract class!"); + } + this.sModelName = new.target.name; + props && Object.entries(props).forEach(([key, value]) => '@' !== key[0] && (this[key] = value)); +*/ } regDisposables(value) { @@ -29,4 +36,30 @@ export class AbstractModel { } Object.values(this).forEach(disposeOne); } + + /** + * @static + * @param {FetchJson} json + * @returns {boolean} + */ + static validJson(json) { + return !!(json && ('Object/'+this.name.replace('Model', '') === json['@Object'])); + } + + /** + * @static + * @param {FetchJson} json + * @returns {*Model} + */ + static reviveFromJson(json) { + // Object/Attachment + // Object/Contact + // Object/Email + // Object/Filter + // Object/Folder + // Object/Message + // Object/Template + return this.validJson(json) ? new this(json) : null; + } + } diff --git a/dev/Model/Attachment.js b/dev/Model/Attachment.js index 511deafaa..235bc4950 100644 --- a/dev/Model/Attachment.js +++ b/dev/Model/Attachment.js @@ -41,48 +41,36 @@ class AttachmentModel extends AbstractModel { /** * @static - * @param {AjaxJsonAttachment} json + * @param {FetchJsonAttachment} json * @returns {?AttachmentModel} */ - static newInstanceFromJson(json) { - const attachment = new AttachmentModel(); - return attachment.initByJson(json) ? attachment : null; - } - - /** - * @param {AjaxJsonAttachment} json - * @returns {boolean} - */ - initByJson(json) { - let bResult = false; - if (json && 'Object/Attachment' === json['@Object']) { - this.mimeType = ((json.MimeType || '').toLowerCase()).trim(); - this.fileName = json.FileName.trim(); + static reviveFromJson(json) { + const attachment = super.reviveFromJson(json); + if (attachment) { + attachment.mimeType = ((json.MimeType || '').toLowerCase()).trim(); + attachment.fileName = json.FileName.trim(); // if it is inline - this.isInline = !!json.IsInline; + attachment.isInline = !!json.IsInline; // if inline image is linked with CID in html // and 'src="cid:' or background-image:url(cid:) - this.isLinked = !!json.IsLinked; - this.isThumbnail = !!json.IsThumbnail; - this.cid = json.CID; - this.contentLocation = json.ContentLocation; - this.download = json.Download; + attachment.isLinked = !!json.IsLinked; + attachment.isThumbnail = !!json.IsThumbnail; + attachment.cid = json.CID; + attachment.contentLocation = json.ContentLocation; + attachment.download = json.Download; - this.folder = json.Folder; - this.uid = json.Uid; - this.mimeIndex = json.MimeIndex; - this.framed = !!json.Framed; + attachment.folder = json.Folder; + attachment.uid = json.Uid; + attachment.mimeIndex = json.MimeIndex; + attachment.framed = !!json.Framed; - this.friendlySize = File.friendlySize(json.EstimatedSize); - this.cidWithoutTags = this.cid.replace(/^<+/, '').replace(/>+$/, ''); + attachment.friendlySize = File.friendlySize(json.EstimatedSize); + attachment.cidWithoutTags = attachment.cid.replace(/^<+/, '').replace(/>+$/, ''); - this.fileNameExt = File.getExtension(this.fileName); - this.fileType = File.getType(this.fileNameExt, this.mimeType); - - bResult = true; + attachment.fileNameExt = File.getExtension(attachment.fileName); + attachment.fileType = File.getType(attachment.fileNameExt, attachment.mimeType); } - - return bResult; + return attachment; } /** diff --git a/dev/Model/AttachmentCollection.js b/dev/Model/AttachmentCollection.js index 11d70deb7..9fe193e10 100644 --- a/dev/Model/AttachmentCollection.js +++ b/dev/Model/AttachmentCollection.js @@ -10,7 +10,7 @@ export class AttachmentCollectionModel extends AbstractCollectionModel * @returns {AttachmentCollectionModel} */ static reviveFromJson(items) { - let cb = attachment => AttachmentModel.newInstanceFromJson(attachment), + let cb = attachment => AttachmentModel.reviveFromJson(attachment), result = super.reviveFromJson(items, cb); if (!result) { result = new AttachmentCollectionModel; diff --git a/dev/Model/ComposeAttachment.js b/dev/Model/ComposeAttachment.js index 0e4fe069e..531308e55 100644 --- a/dev/Model/ComposeAttachment.js +++ b/dev/Model/ComposeAttachment.js @@ -83,7 +83,7 @@ class ComposeAttachmentModel extends AbstractModel { } /** - * @param {AjaxJsonComposeAttachment} json + * @param {FetchJsonComposeAttachment} json * @returns {boolean} */ initByUploadJson(json) { diff --git a/dev/Model/Contact.js b/dev/Model/Contact.js index 3162e5c65..39242db31 100644 --- a/dev/Model/Contact.js +++ b/dev/Model/Contact.js @@ -46,28 +46,26 @@ class ContactModel extends AbstractModel { } /** - * @param {Object} oItem - * @returns {boolean} + * @static + * @param {FetchJsonContact} json + * @returns {?ContactModel} */ - parse(json) { - let result = false; - if (json && 'Object/Contact' === json['@Object']) { - this.idContact = pInt(json.IdContact); - this.display = pString(json.Display); - this.readOnly = !!json.ReadOnly; + static reviveFromJson(json) { + const contact = super.reviveFromJson(json); + if (contact) { + contact.idContact = pInt(json.IdContact); + contact.display = pString(json.Display); + contact.readOnly = !!json.ReadOnly; if (Array.isNotEmpty(json.Properties)) { json.Properties.forEach(property => { if (property && property.Type && null != property.Value && null != property.TypeStr) { - this.properties.push([pInt(property.Type), pString(property.Value), pString(property.TypeStr)]); + contact.properties.push([pInt(property.Type), pString(property.Value), pString(property.TypeStr)]); } }); } - - result = true; } - - return result; + return contact; } /** diff --git a/dev/Model/Email.js b/dev/Model/Email.js index 1711704b7..167b9712c 100644 --- a/dev/Model/Email.js +++ b/dev/Model/Email.js @@ -1,5 +1,7 @@ import { encodeHtml } from 'Common/UtilsUser'; +import { AbstractModel } from 'Knoin/AbstractModel'; + 'use strict'; /** @@ -258,7 +260,7 @@ class Tokenizer } } -class EmailModel { +class EmailModel extends AbstractModel { email = ''; name = ''; dkimStatus = ''; @@ -271,6 +273,7 @@ class EmailModel { * @param {string=} dkimValue = '' */ constructor(email = '', name = '', dkimStatus = 'none', dkimValue = '') { + super(); this.email = email; this.name = name; this.dkimStatus = dkimStatus; @@ -281,12 +284,20 @@ class EmailModel { /** * @static - * @param {AjaxJsonEmail} json + * @param {FetchJsonEmail} json * @returns {?EmailModel} */ - static newInstanceFromJson(json) { - const email = new EmailModel(); - return email.initByJson(json) ? email : null; + static reviveFromJson(json) { + const email = super.reviveFromJson(json); + if (email && email.email) { + email.name = json.Name.trim(); + email.email = json.Email.trim(); + email.dkimStatus = (json.DkimStatus || '').trim(); + email.dkimValue = (json.DkimValue || '').trim(); + email.clearDuplicateName(); + return email; + } + return null; } /** @@ -332,25 +343,6 @@ class EmailModel { return (this.name + ' ' + this.email).toLowerCase().includes(query.toLowerCase()); } - /** - * @param {AjaxJsonEmail} oJsonEmail - * @returns {boolean} - */ - initByJson(json) { - let result = false; - if (json && 'Object/Email' === json['@Object']) { - this.name = json.Name.trim(); - this.email = json.Email.trim(); - this.dkimStatus = (json.DkimStatus || '').trim(); - this.dkimValue = (json.DkimValue || '').trim(); - - result = !!this.email; - this.clearDuplicateName(); - } - - return result; - } - /** * @param {boolean} friendlyView = false * @param {boolean=} wrapWithLink = false diff --git a/dev/Model/EmailCollection.js b/dev/Model/EmailCollection.js index ac194cf4c..cd3b28782 100644 --- a/dev/Model/EmailCollection.js +++ b/dev/Model/EmailCollection.js @@ -12,7 +12,7 @@ export class EmailCollectionModel extends AbstractCollectionModel static reviveFromJson(items) { let result = new EmailCollectionModel; Array.isArray(items) && items.forEach(email => { - email = EmailModel.newInstanceFromJson(email); + email = EmailModel.reviveFromJson(email); email && result.push(email); }); return result; diff --git a/dev/Model/Filter.js b/dev/Model/Filter.js index f4b75e31d..4ae6374f3 100644 --- a/dev/Model/Filter.js +++ b/dev/Model/Filter.js @@ -207,19 +207,24 @@ class FilterModel extends AbstractModel { this.actionValueFourth(AccountStore.getEmailAddresses().join(', ')); } - parse(json) { - let result = false; - if (json && 'Object/Filter' === json['@Object']) { - this.id = pString(json.ID); - this.name(pString(json.Name)); - this.enabled(!!json.Enabled); + /** + * @static + * @param {FetchJsonFilter} json + * @returns {?FilterModel} + */ + static reviveFromJson(json) { + const filter = super.reviveFromJson(json); + if (filter) { + filter.id = pString(json.ID); + filter.name(pString(json.Name)); + filter.enabled(!!json.Enabled); - this.conditionsType(pString(json.ConditionsType)); + filter.conditionsType(pString(json.ConditionsType)); - this.conditions([]); + filter.conditions([]); if (Array.isNotEmpty(json.Conditions)) { - this.conditions( + filter.conditions( json.Conditions.map(aData => { const filterCondition = new FilterConditionModel(); return filterCondition && filterCondition.parse(aData) ? filterCondition : null; @@ -227,21 +232,18 @@ class FilterModel extends AbstractModel { ); } - this.actionType(pString(json.ActionType)); + filter.actionType(pString(json.ActionType)); - this.actionValue(pString(json.ActionValue)); - this.actionValueSecond(pString(json.ActionValueSecond)); - this.actionValueThird(pString(json.ActionValueThird)); - this.actionValueFourth(pString(json.ActionValueFourth)); + filter.actionValue(pString(json.ActionValue)); + filter.actionValueSecond(pString(json.ActionValueSecond)); + filter.actionValueThird(pString(json.ActionValueThird)); + filter.actionValueFourth(pString(json.ActionValueFourth)); - this.actionNoStop(!json.Stop); - this.actionKeep(!!json.Keep); - this.actionMarkAsRead(!!json.MarkAsRead); - - result = true; + filter.actionNoStop(!json.Stop); + filter.actionKeep(!!json.Keep); + filter.actionMarkAsRead(!!json.MarkAsRead); } - - return result; + return filter; } cloneSelf() { diff --git a/dev/Model/Folder.js b/dev/Model/Folder.js index fd1b52006..43394571d 100644 --- a/dev/Model/Folder.js +++ b/dev/Model/Folder.js @@ -43,241 +43,220 @@ class FolderModel extends AbstractModel { /** * @static - * @param {AjaxJsonFolder} json + * @param {FetchJsonFolder} json * @returns {?FolderModel} */ - static newInstanceFromJson(json) { - const folder = new FolderModel(); - return folder.initByJson(json) ? folder.initComputed() : null; - } + static reviveFromJson(json) { + const folder = super.reviveFromJson(json); + if (folder) { + folder.name(json.Name); + folder.delimiter = json.Delimiter; + folder.fullName = json.FullName; + folder.fullNameRaw = json.FullNameRaw; + folder.fullNameHash = json.FullNameHash; + folder.deep = json.FullNameRaw.split(folder.delimiter).length - 1; + folder.selectable = !!json.IsSelectable; + folder.existen = !!json.IsExists; - /** - * @returns {FolderModel} - */ - initComputed() { - this.isInbox = ko.computed(() => FolderType.Inbox === this.type()); + folder.subScribed(!!json.IsSubscribed); + folder.checkable(!!json.Checkable); - this.hasSubScribedSubfolders = ko.computed( - () => - !!this.subFolders().find( - oFolder => (oFolder.subScribed() || oFolder.hasSubScribedSubfolders()) && !oFolder.isSystemFolder() - ) - ); + folder.isInbox = ko.computed(() => FolderType.Inbox === folder.type()); - this.canBeEdited = ko.computed(() => FolderType.User === this.type() && this.existen && this.selectable); + folder.hasSubScribedSubfolders = ko.computed( + () => + !!folder.subFolders().find( + oFolder => (oFolder.subScribed() || oFolder.hasSubScribedSubfolders()) && !oFolder.isSystemFolder() + ) + ); - this.visible = ko.computed(() => { - const isSubScribed = this.subScribed(), - isSubFolders = this.hasSubScribedSubfolders(); + folder.canBeEdited = ko.computed(() => FolderType.User === folder.type() && folder.existen && folder.selectable); - return isSubScribed || (isSubFolders && (!this.existen || !this.selectable)); - }); + folder.visible = ko.computed(() => { + const isSubScribed = folder.subScribed(), + isSubFolders = folder.hasSubScribedSubfolders(); - this.isSystemFolder = ko.computed(() => FolderType.User !== this.type()); + return isSubScribed || (isSubFolders && (!folder.existen || !folder.selectable)); + }); - this.hidden = ko.computed(() => { - const isSystem = this.isSystemFolder(), - isSubFolders = this.hasSubScribedSubfolders(); + folder.isSystemFolder = ko.computed(() => FolderType.User !== folder.type()); - return (isSystem && !isSubFolders) || (!this.selectable && !isSubFolders); - }); + folder.hidden = ko.computed(() => { + const isSystem = folder.isSystemFolder(), + isSubFolders = folder.hasSubScribedSubfolders(); - this.selectableForFolderList = ko.computed(() => !this.isSystemFolder() && this.selectable); + return (isSystem && !isSubFolders) || (!folder.selectable && !isSubFolders); + }); - this.messageCountAll = ko - .computed({ - read: this.privateMessageCountAll, - write: (iValue) => { - if (isPosNumeric(iValue, true)) { - this.privateMessageCountAll(iValue); - } else { - this.privateMessageCountAll.valueHasMutated(); + folder.selectableForFolderList = ko.computed(() => !folder.isSystemFolder() && folder.selectable); + + folder.messageCountAll = ko + .computed({ + read: folder.privateMessageCountAll, + write: (iValue) => { + if (isPosNumeric(iValue, true)) { + folder.privateMessageCountAll(iValue); + } else { + folder.privateMessageCountAll.valueHasMutated(); + } + } + }) + .extend({ notify: 'always' }); + + folder.messageCountUnread = ko + .computed({ + read: folder.privateMessageCountUnread, + write: (value) => { + if (isPosNumeric(value, true)) { + folder.privateMessageCountUnread(value); + } else { + folder.privateMessageCountUnread.valueHasMutated(); + } + } + }) + .extend({ notify: 'always' }); + + folder.printableUnreadCount = ko.computed(() => { + const count = folder.messageCountAll(), + unread = folder.messageCountUnread(), + type = folder.type(); + + if (0 < count) { + if (FolderType.Draft === type) { + return '' + count; + } + if ( + 0 < unread && + FolderType.Trash !== type && + FolderType.Archive !== type && + FolderType.SentItems !== type + ) { + return '' + unread; } } - }) - .extend({ notify: 'always' }); - this.messageCountUnread = ko - .computed({ - read: this.privateMessageCountUnread, + return ''; + }); + + folder.canBeDeleted = ko.computed( + () => !folder.isSystemFolder() && !folder.subFolders().length + ); + + folder.canBeSubScribed = ko.computed( + () => !folder.isSystemFolder() && folder.selectable + ); + + folder.canBeChecked = folder.canBeSubScribed; + + folder.localName = ko.computed(() => { + translatorTrigger(); + + let name = folder.name(); + const type = folder.type(); + + if (folder.isSystemFolder()) { + switch (type) { + case FolderType.Inbox: + name = i18n('FOLDER_LIST/INBOX_NAME'); + break; + case FolderType.SentItems: + name = i18n('FOLDER_LIST/SENT_NAME'); + break; + case FolderType.Draft: + name = i18n('FOLDER_LIST/DRAFTS_NAME'); + break; + case FolderType.Spam: + name = i18n('FOLDER_LIST/SPAM_NAME'); + break; + case FolderType.Trash: + name = i18n('FOLDER_LIST/TRASH_NAME'); + break; + case FolderType.Archive: + name = i18n('FOLDER_LIST/ARCHIVE_NAME'); + break; + // no default + } + } + + return name; + }); + + folder.manageFolderSystemName = ko.computed(() => { + translatorTrigger(); + + let suffix = ''; + const type = folder.type(), + name = folder.name(); + + if (folder.isSystemFolder()) { + switch (type) { + case FolderType.Inbox: + suffix = '(' + i18n('FOLDER_LIST/INBOX_NAME') + ')'; + break; + case FolderType.SentItems: + suffix = '(' + i18n('FOLDER_LIST/SENT_NAME') + ')'; + break; + case FolderType.Draft: + suffix = '(' + i18n('FOLDER_LIST/DRAFTS_NAME') + ')'; + break; + case FolderType.Spam: + suffix = '(' + i18n('FOLDER_LIST/SPAM_NAME') + ')'; + break; + case FolderType.Trash: + suffix = '(' + i18n('FOLDER_LIST/TRASH_NAME') + ')'; + break; + case FolderType.Archive: + suffix = '(' + i18n('FOLDER_LIST/ARCHIVE_NAME') + ')'; + break; + // no default + } + } + + if ((suffix && '(' + name + ')' === suffix) || '(inbox)' === suffix.toLowerCase()) { + suffix = ''; + } + + return suffix; + }); + + folder.collapsed = ko.computed({ + read: () => !folder.hidden() && folder.collapsedPrivate(), write: (value) => { - if (isPosNumeric(value, true)) { - this.privateMessageCountUnread(value); - } else { - this.privateMessageCountUnread.valueHasMutated(); - } + folder.collapsedPrivate(value); } - }) - .extend({ notify: 'always' }); + }); - this.printableUnreadCount = ko.computed(() => { - const count = this.messageCountAll(), - unread = this.messageCountUnread(), - type = this.type(); + folder.hasUnreadMessages = ko.computed(() => 0 < folder.messageCountUnread() && folder.printableUnreadCount()); - if (0 < count) { - if (FolderType.Draft === type) { - return '' + count; - } else if ( - 0 < unread && - FolderType.Trash !== type && - FolderType.Archive !== type && - FolderType.SentItems !== type - ) { - return '' + unread; + folder.hasSubScribedUnreadMessagesSubfolders = ko.computed( + () => + !!folder.subFolders().find( + folder => folder.hasUnreadMessages() || folder.hasSubScribedUnreadMessagesSubfolders() + ) + ); + + // subscribe + folder.name.subscribe(value => folder.nameForEdit(value)); + + folder.edited.subscribe(value => value && folder.nameForEdit(folder.name())); + + folder.messageCountUnread.subscribe((unread) => { + if (FolderType.Inbox === folder.type()) { + dispatchEvent(new CustomEvent('mailbox.inbox-unread-count', {detail:unread})); } - } - - return ''; - }); - - this.canBeDeleted = ko.computed(() => { - const bSystem = this.isSystemFolder(); - return !bSystem && !this.subFolders().length; - }); - - this.canBeSubScribed = ko.computed( - () => !this.isSystemFolder() && this.selectable - ); - - this.canBeChecked = this.canBeSubScribed; - - this.localName = ko.computed(() => { - translatorTrigger(); - - let name = this.name(); - const type = this.type(); - - if (this.isSystemFolder()) { - switch (type) { - case FolderType.Inbox: - name = i18n('FOLDER_LIST/INBOX_NAME'); - break; - case FolderType.SentItems: - name = i18n('FOLDER_LIST/SENT_NAME'); - break; - case FolderType.Draft: - name = i18n('FOLDER_LIST/DRAFTS_NAME'); - break; - case FolderType.Spam: - name = i18n('FOLDER_LIST/SPAM_NAME'); - break; - case FolderType.Trash: - name = i18n('FOLDER_LIST/TRASH_NAME'); - break; - case FolderType.Archive: - name = i18n('FOLDER_LIST/ARCHIVE_NAME'); - break; - // no default - } - } - - return name; - }); - - this.manageFolderSystemName = ko.computed(() => { - translatorTrigger(); - - let suffix = ''; - const type = this.type(), - name = this.name(); - - if (this.isSystemFolder()) { - switch (type) { - case FolderType.Inbox: - suffix = '(' + i18n('FOLDER_LIST/INBOX_NAME') + ')'; - break; - case FolderType.SentItems: - suffix = '(' + i18n('FOLDER_LIST/SENT_NAME') + ')'; - break; - case FolderType.Draft: - suffix = '(' + i18n('FOLDER_LIST/DRAFTS_NAME') + ')'; - break; - case FolderType.Spam: - suffix = '(' + i18n('FOLDER_LIST/SPAM_NAME') + ')'; - break; - case FolderType.Trash: - suffix = '(' + i18n('FOLDER_LIST/TRASH_NAME') + ')'; - break; - case FolderType.Archive: - suffix = '(' + i18n('FOLDER_LIST/ARCHIVE_NAME') + ')'; - break; - // no default - } - } - - if ((suffix && '(' + name + ')' === suffix) || '(inbox)' === suffix.toLowerCase()) { - suffix = ''; - } - - return suffix; - }); - - this.collapsed = ko.computed({ - read: () => !this.hidden() && this.collapsedPrivate(), - write: (value) => { - this.collapsedPrivate(value); - } - }); - - this.hasUnreadMessages = ko.computed(() => 0 < this.messageCountUnread() && this.printableUnreadCount()); - - this.hasSubScribedUnreadMessagesSubfolders = ko.computed( - () => - !!this.subFolders().find( - folder => folder.hasUnreadMessages() || folder.hasSubScribedUnreadMessagesSubfolders() - ) - ); - - // subscribe - this.name.subscribe(value => this.nameForEdit(value)); - - this.edited.subscribe(value => value && this.nameForEdit(this.name())); - - this.messageCountUnread.subscribe((unread) => { - if (FolderType.Inbox === this.type()) { - dispatchEvent(new CustomEvent('mailbox.inbox-unread-count', {detail:unread})); - } - }); - - return this; + }); + } + return folder; } /** * @returns {string} */ collapsedCss() { - return this.hasSubScribedSubfolders() - ? this.collapsed() - ? 'icon-right-mini e-collapsed-sign' - : 'icon-down-mini e-collapsed-sign' - : 'icon-none e-collapsed-sign'; - } - - /** - * @param {AjaxJsonFolder} json - * @returns {boolean} - */ - initByJson(json) { - let bResult = false; - - if (json && 'Object/Folder' === json['@Object']) { - this.name(json.Name); - this.delimiter = json.Delimiter; - this.fullName = json.FullName; - this.fullNameRaw = json.FullNameRaw; - this.fullNameHash = json.FullNameHash; - this.deep = json.FullNameRaw.split(this.delimiter).length - 1; - this.selectable = !!json.IsSelectable; - this.existen = !!json.IsExists; - - this.subScribed(!!json.IsSubscribed); - this.checkable(!!json.Checkable); - - bResult = true; - } - - return bResult; + return 'e-collapsed-sign ' + (this.hasSubScribedSubfolders() + ? (this.collapsed() ? 'icon-right-mini' : 'icon-down-mini') + : 'icon-none' + ); } /** diff --git a/dev/Model/FolderCollection.js b/dev/Model/FolderCollection.js index 35c8b9716..2f6f3b804 100644 --- a/dev/Model/FolderCollection.js +++ b/dev/Model/FolderCollection.js @@ -47,7 +47,7 @@ export class FolderCollectionModel extends AbstractCollectionModel bDisplaySpecSetting = FolderStore.displaySpecSetting(); return super.reviveFromJson(object, (oFolder, self) => { let oCacheFolder = Cache.getFolderFromCacheList(oFolder.FullNameRaw); - if (!oCacheFolder && (oCacheFolder = FolderModel.newInstanceFromJson(oFolder))) { + if (!oCacheFolder && (oCacheFolder = FolderModel.reviveFromJson(oFolder))) { if (oFolder.FullNameRaw == self.SystemFolders[ServerFolderType.INBOX]) { oCacheFolder.type(FolderType.Inbox); Cache.setFolderInboxName(oFolder.FullNameRaw); diff --git a/dev/Model/Message.js b/dev/Model/Message.js index 093789745..a6301008c 100644 --- a/dev/Model/Message.js +++ b/dev/Model/Message.js @@ -93,12 +93,49 @@ class MessageModel extends AbstractModel { /** * @static - * @param {AjaxJsonMessage} oJsonMessage + * @param {FetchJsonMessage} json * @returns {?MessageModel} */ - static newInstanceFromJson(json) { - const oMessageModel = new MessageModel(); - return oMessageModel.initByJson(json) ? oMessageModel : null; + static reviveFromJson(json) { + const oMessageModel = super.reviveFromJson(json); + if (oMessageModel) { + oMessageModel.folderFullNameRaw = json.Folder; + oMessageModel.uid = json.Uid; + oMessageModel.hash = json.Hash; + oMessageModel.requestHash = json.RequestHash; + + oMessageModel.size(pInt(json.Size)); + + oMessageModel.from = EmailCollectionModel.reviveFromJson(json.From); + oMessageModel.to = EmailCollectionModel.reviveFromJson(json.To); + oMessageModel.cc = EmailCollectionModel.reviveFromJson(json.Cc); + oMessageModel.bcc = EmailCollectionModel.reviveFromJson(json.Bcc); + oMessageModel.replyTo = EmailCollectionModel.reviveFromJson(json.ReplyTo); + oMessageModel.deliveredTo = EmailCollectionModel.reviveFromJson(json.DeliveredTo); + oMessageModel.unsubsribeLinks = Array.isNotEmpty(json.UnsubsribeLinks) ? json.UnsubsribeLinks : []; + + oMessageModel.subject(json.Subject); + if (isArray(json.SubjectParts)) { + oMessageModel.subjectPrefix(json.SubjectParts[0]); + oMessageModel.subjectSuffix(json.SubjectParts[1]); + } else { + oMessageModel.subjectPrefix(''); + oMessageModel.subjectSuffix(oMessageModel.subject()); + } + + oMessageModel.dateTimeStampInUTC(pInt(json.DateTimeStampInUTC)); + + oMessageModel.fromEmailString(oMessageModel.from.toString(true)); + oMessageModel.fromClearEmailString(oMessageModel.from.toStringClear()); + oMessageModel.toEmailsString(oMessageModel.to.toString(true)); + oMessageModel.toClearEmailsString(oMessageModel.to.toStringClear()); + + oMessageModel.threads(isArray(json.Threads) ? json.Threads : []); + + oMessageModel.initFlagsByJson(json); + oMessageModel.computeSenderEmail(); + } + return oMessageModel; } _reset() { @@ -206,7 +243,8 @@ class MessageModel extends AbstractModel { } setFromJson(json) { - if (json && 'Object/Message' === json['@Object']) { + let result = MessageModel.validJson(json); + if (result) { let priority = pInt(json.Priority); this.priority( [MessagePriority.High, MessagePriority.Low].includes(priority) ? priority : MessagePriority.Normal @@ -216,62 +254,12 @@ class MessageModel extends AbstractModel { this.hasAttachments(!!json.HasAttachments); this.attachmentsSpecData(isArray(json.AttachmentsSpecData) ? json.AttachmentsSpecData : []); - - return true; } - - return false; - } - - /** - * @param {AjaxJsonMessage} json - * @returns {boolean} - */ - initByJson(json) { - let result = this.setFromJson(json); - if (result) { - this.folderFullNameRaw = json.Folder; - this.uid = json.Uid; - this.hash = json.Hash; - this.requestHash = json.RequestHash; - - this.size(pInt(json.Size)); - - this.from = EmailCollectionModel.reviveFromJson(json.From); - this.to = EmailCollectionModel.reviveFromJson(json.To); - this.cc = EmailCollectionModel.reviveFromJson(json.Cc); - this.bcc = EmailCollectionModel.reviveFromJson(json.Bcc); - this.replyTo = EmailCollectionModel.reviveFromJson(json.ReplyTo); - this.deliveredTo = EmailCollectionModel.reviveFromJson(json.DeliveredTo); - this.unsubsribeLinks = Array.isNotEmpty(json.UnsubsribeLinks) ? json.UnsubsribeLinks : []; - - this.subject(json.Subject); - if (isArray(json.SubjectParts)) { - this.subjectPrefix(json.SubjectParts[0]); - this.subjectSuffix(json.SubjectParts[1]); - } else { - this.subjectPrefix(''); - this.subjectSuffix(this.subject()); - } - - this.dateTimeStampInUTC(pInt(json.DateTimeStampInUTC)); - - this.fromEmailString(this.from.toString(true)); - this.fromClearEmailString(this.from.toStringClear()); - this.toEmailsString(this.to.toString(true)); - this.toClearEmailsString(this.to.toStringClear()); - - this.threads(isArray(json.Threads) ? json.Threads : []); - - this.initFlagsByJson(json); - this.computeSenderEmail(); - } - return result; } /** - * @param {AjaxJsonMessage} json + * @param {FetchJsonMessage} json * @returns {boolean} */ initUpdateByMessageJson(json) { @@ -314,22 +302,19 @@ class MessageModel extends AbstractModel { } /** - * @param {AjaxJsonMessage} json + * @param {FetchJsonMessage} json * @returns {boolean} */ initFlagsByJson(json) { - let result = false; - if (json && 'Object/Message' === json['@Object']) { + let result = MessageModel.validJson(json); + if (result) { this.unseen(!json.IsSeen); this.flagged(!!json.IsFlagged); this.answered(!!json.IsAnswered); this.forwarded(!!json.IsForwarded); this.isReadReceipt(!!json.IsReadReceipt); this.deletedMark(!!json.IsDeleted); - - result = true; } - return result; } diff --git a/dev/Model/MessageCollection.js b/dev/Model/MessageCollection.js index bfd4cf022..de5b4b384 100644 --- a/dev/Model/MessageCollection.js +++ b/dev/Model/MessageCollection.js @@ -36,19 +36,17 @@ export class MessageCollectionModel extends AbstractCollectionModel static reviveFromJson(object, cached) { let newCount = 0; return super.reviveFromJson(object, message => { - if (message && 'Object/Message' === message['@Object']) { - message = MessageModel.newInstanceFromJson(message); - if (message) { - if (hasNewMessageAndRemoveFromCache(message.folderFullNameRaw, message.uid) && 5 >= newCount) { - ++newCount; - message.newForAnimation(true); - } - - message.deleted(false); - - cached ? initMessageFlagsFromCache(message) : storeMessageFlagsToCache(message); - return message; + message = MessageModel.reviveFromJson(message); + if (message) { + if (hasNewMessageAndRemoveFromCache(message.folderFullNameRaw, message.uid) && 5 >= newCount) { + ++newCount; + message.newForAnimation(true); } + + message.deleted(false); + + cached ? initMessageFlagsFromCache(message) : storeMessageFlagsToCache(message); + return message; } }); } diff --git a/dev/Model/Template.js b/dev/Model/Template.js index 4bf5e0689..e2427c278 100644 --- a/dev/Model/Template.js +++ b/dev/Model/Template.js @@ -22,20 +22,19 @@ class TemplateModel extends AbstractModel { } /** - * @returns {boolean} + * @static + * @param {FetchJsonTemplate} json + * @returns {?TemplateModel} */ - parse(json) { - let result = false; - if (json && 'Object/Template' === json['@Object']) { - this.id = pString(json.ID); - this.name = pString(json.Name); - this.body = pString(json.Body); - this.populated = !!json.Populated; - - result = true; + static reviveFromJson(json) { + const template = super.reviveFromJson(json); + if (template) { + template.id = pString(json.ID); + template.name = pString(json.Name); + template.body = pString(json.Body); + template.populated = !!json.Populated; } - - return result; + return template; } } diff --git a/dev/Settings/User/Filters.js b/dev/Settings/User/Filters.js index 86df16741..3f17b37ee 100644 --- a/dev/Settings/User/Filters.js +++ b/dev/Settings/User/Filters.js @@ -102,10 +102,7 @@ class FiltersUserSettings { this.serverError(false); this.filters( - data.Result.Filters.map(aItem => { - const filter = new FilterModel(); - return filter && filter.parse(aItem) ? filter : null; - }).filter(v => v) + data.Result.Filters.map(aItem => FilterModel.reviveFromJson(aItem)).filter(v => v) ); this.modules(data.Result.Modules ? data.Result.Modules : {}); diff --git a/dev/Stores/User/Message.js b/dev/Stores/User/Message.js index 1667a0ae1..52ace5a40 100644 --- a/dev/Stores/User/Message.js +++ b/dev/Stores/User/Message.js @@ -452,14 +452,13 @@ class MessageUserStore { if ( data && + MessageModel.validJson(data.Result) && message && - data.Result && - 'Object/Message' === data.Result['@Object'] && message.folderFullNameRaw === data.Result.Folder ) { const threads = message.threads(); if (message.uid !== data.Result.Uid && 1 < threads.length && threads.includes(data.Result.Uid)) { - message = MessageModel.newInstanceFromJson(data.Result); + message = MessageModel.reviveFromJson(data.Result); if (message) { message.threads(threads); initMessageFlagsFromCache(message); @@ -641,7 +640,7 @@ class MessageUserStore { /** * @param {string} sResult - * @param {AjaxJsonDefaultResponse} oData + * @param {FetchJsonDefaultResponse} oData * @param {boolean} bCached */ onMessageResponse(sResult, oData, bCached) { diff --git a/dev/View/Popup/Contacts.js b/dev/View/Popup/Contacts.js index 6de440c8c..e97e74cb2 100644 --- a/dev/View/Popup/Contacts.js +++ b/dev/View/Popup/Contacts.js @@ -468,7 +468,7 @@ class ContactsPopupView extends AbstractViewNext { /** * @param {string} sResult - * @param {AjaxJsonDefaultResponse} oData + * @param {FetchJsonDefaultResponse} oData */ deleteResponse(sResult, oData) { if (500 < (StorageResultType.Success === sResult && oData && oData.Time ? pInt(oData.Time) : 0)) { @@ -570,13 +570,11 @@ class ContactsPopupView extends AbstractViewNext { if (StorageResultType.Success === result && data && data.Result && data.Result.List) { if (Array.isNotEmpty(data.Result.List)) { - list = data.Result.List.map(item => { - const contact = new ContactModel(); - return contact.parse(item) ? contact : null; + data.Result.List.forEach(item => { + item = ContactModel.reviveFromJson(item); + item && list.push(item); }); - list = list.filter(v => v); - count = pInt(data.Result.Count); count = 0 < count ? count : 0; } diff --git a/dev/View/Popup/Template.js b/dev/View/Popup/Template.js index 895f49463..2791e1b6e 100644 --- a/dev/View/Popup/Template.js +++ b/dev/View/Popup/Template.js @@ -8,6 +8,7 @@ import Remote from 'Remote/User/Fetch'; import { popup, command } from 'Knoin/Knoin'; import { AbstractViewNext } from 'Knoin/AbstractViewNext'; +import { TemplateModel } from 'Model/Template'; @popup({ name: 'View/Popup/Template', @@ -133,8 +134,7 @@ class TemplatePopupView extends AbstractViewNext { if ( StorageResultType.Success === result && data && - data.Result && - 'Object/Template' === data.Result['@Object'] && + TemplateModel.validJson(data.Result) && null != data.Result.Body ) { template.body = data.Result.Body;