diff --git a/dev/Model/Message.js b/dev/Model/Message.js index 798cfdb94..c7808dfa9 100644 --- a/dev/Model/Message.js +++ b/dev/Model/Message.js @@ -25,7 +25,7 @@ import { LanguageStore } from 'Stores/Language'; import Remote from 'Remote/User/Fetch'; const - hcont = Element.fromHTML('
'), + hcont = Element.fromHTML('
'), getRealHeight = el => { hcont.innerHTML = el.outerHTML; const result = hcont.clientHeight; @@ -133,22 +133,12 @@ export class MessageModel extends AbstractModel { attachmentIconClass: () => this.encrypted() ? 'icon-lock' : FileInfo.getAttachmentsIconClass(this.attachments()), threadsLen: () => this.threads().length, - listAttachments: () => this.attachments() - .filter(item => SettingsUserStore.listInlineAttachments() || !item.isLinked()), - hasAttachments: () => this.listAttachments().length, isUnseen: () => !this.flags().includes('\\seen'), isFlagged: () => this.flags().includes('\\flagged'), - isReadReceipt: () => this.flags().includes('$mdnsent'), // isJunk: () => this.flags().includes('$junk') && !this.flags().includes('$nonjunk'), // isPhishing: () => this.flags().includes('$phishing'), - tagsToHTML: () => this.flags().map(value => - isAllowedKeyword(value) - ? '' + i18n('MESSAGE_TAGS/'+value,0,value) + '' - : '' - ).join(' '), - tagOptions: () => { const tagOptions = []; FolderUserStore.currentFolder().permanentFlags.forEach(value => { @@ -209,61 +199,6 @@ export class MessageModel extends AbstractModel { } } - /** - * @returns {boolean} - */ - hasUnsubsribeLinks() { - return this.unsubsribeLinks().length; - } - - /** - * @returns {string} - */ - getFirstUnsubsribeLink() { - return this.unsubsribeLinks()[0] || ''; - } - - /** - * @param {boolean} friendlyView - * @param {boolean=} wrapWithLink - * @returns {string} - */ - fromToLine(friendlyView, wrapWithLink) { - return this.from.toString(friendlyView, wrapWithLink); - } - - /** - * @param {boolean} friendlyView - * @param {boolean=} wrapWithLink - * @returns {string} - */ - toToLine(friendlyView, wrapWithLink) { - return this.to.toString(friendlyView, wrapWithLink); - } - - /** - * @param {boolean} friendlyView - * @param {boolean=} wrapWithLink - * @returns {string} - */ - ccToLine(friendlyView, wrapWithLink) { - return this.cc.toString(friendlyView, wrapWithLink); - } - - /** - * @returns {string} - */ - bccToLine() { - return this.bcc.toString(); - } - - /** - * @returns {string} - */ - replyToToLine() { - return this.replyTo.toString(); - } - /** * @return string */ @@ -288,7 +223,7 @@ export class MessageModel extends AbstractModel { /** * @returns {string} */ - viewLink() { + viewRaw() { return serverRequestRaw('ViewAsPlain', this.requestHash); } @@ -390,7 +325,7 @@ export class MessageModel extends AbstractModel { viewPopupMessage(print) { const timeStampInUTC = this.dateTimeStampInUTC() || 0, - ccLine = this.ccToLine(), + ccLine = this.cc.toString(), m = 0 < timeStampInUTC ? new Date(timeStampInUTC * 1000) : null, win = open(''), sdoc = win.document; @@ -402,7 +337,7 @@ export class MessageModel extends AbstractModel { sdoc.write(PreviewHTML .replace('', '<title>'+subject) // eslint-disable-next-line max-len - .replace('<body>', `<body style="background-color:${prop('background-color')};color:${prop('color')}"><header><h1>${subject}</h1><time>${encodeHtml(m ? m.format('LLL',0,LanguageStore.hourCycle()) : '')}</time><div>${encodeHtml(this.fromToLine())}</div><div>${encodeHtml(i18n('GLOBAL/TO'))}: ${encodeHtml(this.toToLine())}</div>${cc}</header><${mode}>${this.bodyAsHTML()}</${mode}>`) + .replace('<body>', `<body style="background-color:${prop('background-color')};color:${prop('color')}"><header><h1>${subject}</h1><time>${encodeHtml(m ? m.format('LLL',0,LanguageStore.hourCycle()) : '')}</time><div>${encodeHtml(this.from)}</div><div>${encodeHtml(i18n('GLOBAL/TO'))}: ${encodeHtml(this.to)}</div>${cc}</header><${mode}>${this.bodyAsHTML()}</${mode}>`) ); sdoc.close(); @@ -435,7 +370,6 @@ export class MessageModel extends AbstractModel { */ static fromMessageListItem(message) { let self = new MessageModel(); - if (message) { // Clone message values forEachObjectEntry(message, (key, value) => { @@ -445,10 +379,8 @@ export class MessageModel extends AbstractModel { self[key] = value; } }); + self.computeSenderEmail(); } - - self.computeSenderEmail(); - return self; } diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index 0526d3330..283a8c9e0 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -900,7 +900,7 @@ export class ComposePopupView extends AbstractViewPopup { switch (msgComposeType) { case ComposeType.Reply: case ComposeType.ReplyAll: - sFrom = message.fromToLine(false, true); + sFrom = message.from.toString(false, true); sText = '<br><br><p>' + i18n('COMPOSE/REPLY_MESSAGE_TITLE', { DATETIME: sDate, EMAIL: sFrom }) + ':</p><blockquote>' + sText.trim() @@ -908,9 +908,9 @@ export class ComposePopupView extends AbstractViewPopup { break; case ComposeType.Forward: - sFrom = message.fromToLine(false, true); - sTo = message.toToLine(false, true); - sCc = message.ccToLine(false, true); + sFrom = message.from.toString(false, true); + sTo = message.to.toString(false, true); + sCc = message.cc.toString(false, true); sText = '<br><br><p>' + i18n('COMPOSE/FORWARD_MESSAGE_TOP_TITLE') + '</p><div>' + i18n('GLOBAL/FROM') + ': ' + sFrom + '<br>' diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 47d9511fc..30a5e31b0 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -130,7 +130,7 @@ export class MailMessageView extends AbstractViewRight { this.isDraftFolder = MessagelistUserStore.isDraftFolder; this.isSpamFolder = MessagelistUserStore.isSpamFolder; - this.message = MessageUserStore.message; + this.message = currentMessage; this.messageLoadingThrottle = MessageUserStore.loading; this.messagesBodiesDom = MessageUserStore.bodiesDom; this.messageError = MessageUserStore.error; @@ -157,6 +157,21 @@ export class MailMessageView extends AbstractViewRight { messageVisibility: () => !MessageUserStore.loading() && !!currentMessage(), + tagsToHTML: () => currentMessage()?.flags().map(value => + isAllowedKeyword(value) + ? '<span class="focused msgflag-'+value+'">' + i18n('MESSAGE_TAGS/'+value,0,value) + '</span>' + : '' + ).join(' '), + + askReadReceipt: () => + (MessagelistUserStore.isDraftFolder() || MessagelistUserStore.isSentFolder()) + && currentMessage()?.readReceipt() + && currentMessage()?.flags().includes('$mdnsent'), + + listAttachments: () => currentMessage()?.attachments() + .filter(item => SettingsUserStore.listInlineAttachments() || !item.isLinked()), + hasAttachments: () => this.listAttachments().length, + canBeRepliedOrForwarded: () => !MessagelistUserStore.isDraftFolder() && this.messageVisibility(), viewFromDkimVisibility: () => 'none' !== this.viewFromDkimData()[0], @@ -181,6 +196,8 @@ export class MailMessageView extends AbstractViewRight { return ''; }, + firstUnsubsribeLink: () => currentMessage()?.unsubsribeLinks()[0] || '', + pgpSupported: () => currentMessage() && PgpUserStore.isSupported(), messageListOrViewLoading: @@ -195,11 +212,11 @@ export class MailMessageView extends AbstractViewRight { } this.viewHash = message.hash; // TODO: make first param a user setting #683 - this.viewFromShort(message.fromToLine(false, true)); + this.viewFromShort(message.from.toString(false, true)); let dkim = 1 === arrayLength(message.from) && message.dkim && message.dkim.find(dkim => message.from[0].email.includes(dkim[1])); this.viewFromDkimData(dkim ? [dkim[0], dkim[2]] : ['none', '']); - this.viewToShort(message.toToLine(true, true)); + this.viewToShort(message.to.toString(true, true)); } else { MessagelistUserStore.selectedMessage(null); @@ -453,13 +470,6 @@ export class MailMessageView extends AbstractViewRight { }); } - /** - * @returns {boolean} - */ - isDraftOrSentFolder() { - return MessagelistUserStore.isDraftFolder() || MessagelistUserStore.isSentFolder(); - } - scrollMessageToTop() { oMessageScrollerDom().scrollTop = (50 < oMessageScrollerDom().scrollTop) ? 50 : 0; } diff --git a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html index 885f5067d..24349cf33 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html @@ -86,8 +86,8 @@ <li role="presentation"> <a href="#" tabindex="-1" data-bind="command: deleteWithoutMoveCommand" data-icon="🗑" data-i18n="MESSAGE_LIST/BUTTON_DELETE_WITHOUT_MOVE"></a> </li> - <li class="dividerbar" role="presentation" data-bind="visible: message().hasUnsubsribeLinks()"> - <a target="_blank" href="#" tabindex="-1" data-bind="attr: { href: message().getFirstUnsubsribeLink() }" data-icon="✖" data-i18n="MESSAGE/BUTTON_UNSUBSCRIBE"></a> + <li class="dividerbar" role="presentation" data-bind="visible: firstUnsubsribeLink"> + <a target="_blank" href="#" tabindex="-1" data-bind="attr: { href: firstUnsubsribeLink }" data-icon="✖" data-i18n="MESSAGE/BUTTON_UNSUBSCRIBE"></a> </li> </div> <div data-bind="visible: allowMessageActions, with: message" class="dividerbar"> @@ -110,7 +110,7 @@ <a href="#" tabindex="-1" data-bind="click: viewPlain" data-icon="👁" data-i18n="MESSAGE/PLAIN_VIEW"></a> </li> <li class="dividerbar" role="presentation"> - <a target="_blank" href="#" tabindex="-1" data-bind="attr: { href: viewLink() }"> + <a target="_blank" href="#" tabindex="-1" data-bind="attr: { href: viewRaw() }"> <i class="icon-file-code"></i> <span data-i18n="MESSAGE/MENU_VIEW_ORIGINAL"></span> </a> @@ -128,36 +128,36 @@ <div class="subjectParent"> <span class="infoParent g-ui-user-select-none fontastic" data-bind="click: toggleFullInfo">ℹ</span> <span class="flagParent g-ui-user-select-none flagOff fontastic" data-bind="text: message().isFlagged() ? '★' : '☆', css: {'flagOn': message().isFlagged(), 'flagOff': !message().isFlagged()}"></span> - <span class="subject" data-bind="text: message().subject, title: message().subject"></span> + <span class="subject" data-bind="text: message().subject"></span> <a href="#" class="close" data-bind="click: closeMessage" style="margin-top: -8px;">×</a> </div> <div class="informationShort" data-bind="hidden: showFullInfo"> - <span class="from" data-bind="html: viewFromShort, title: message().fromToLine()"></span> + <span class="from" data-bind="html: viewFromShort, title: message().from"></span> <i data-bind="visible: viewFromDkimVisibility, css: viewFromDkimStatusIconClass, title: viewFromDkimStatusTitle"></i> </div> <div class="informationFull" data-bind="visible: showFullInfo, with: message"> <table> - <tr data-bind="visible: fromToLine()"> + <tr data-bind="visible: from.length"> <td data-i18n="GLOBAL/FROM"></td> - <td><span data-bind="text: fromToLine()"></span> + <td><span data-bind="text: from"></span> <i data-bind="visible: $parent.viewFromDkimVisibility, css: $parent.viewFromDkimStatusIconClass, title: $parent.viewFromDkimStatusTitle"></i> </td> </tr> - <tr data-bind="visible: toToLine()"> + <tr data-bind="visible: to.length"> <td data-i18n="GLOBAL/TO"></td> - <td data-bind="text: toToLine(), title: toToLine()"></td> + <td data-bind="text: to"></td> </tr> - <tr data-bind="visible: ccToLine()"> + <tr data-bind="visible: cc.length"> <td data-i18n="GLOBAL/CC"></td> - <td data-bind="text: ccToLine(), title: ccToLine()"></td> + <td data-bind="text: cc"></td> </tr> - <tr data-bind="visible: bccToLine()"> + <tr data-bind="visible: bcc.length"> <td data-i18n="GLOBAL/BCC"></td> - <td data-bind="text: bccToLine(), title: bccToLine()"></td> + <td data-bind="text: bcc"></td> </tr> - <tr data-bind="visible: replyToToLine()"> + <tr data-bind="visible: replyTo.length"> <td data-i18n="GLOBAL/REPLY_TO"></td> - <td data-bind="text: replyToToLine(), title: replyToToLine()"></td> + <td data-bind="text: replyTo"></td> </tr> <tr data-bind="visible: dateTimeStampInUTC"> <td data-i18n="MESSAGE/LABEL_DATE"></td> @@ -183,13 +183,13 @@ <div data-bind="hidden: showFullInfo"> <time class="date" data-moment-format="FULL" data-bind="visible: 0 < message().dateTimeStampInUTC(), moment: message().dateTimeStampInUTC()"></time> <div class="informationShortWrp"> - <div class="informationShort" data-bind="visible: message().toToLine()"> + <div class="informationShort" data-bind="visible: message().to.length"> <span data-i18n="GLOBAL/TO"></span>: - <span data-bind="text: message().toToLine()"></span> + <span data-bind="text: message().to"></span> </div> - <div class="informationShort" data-bind="visible: message().ccToLine()"> + <div class="informationShort" data-bind="visible: message().cc.length"> <span data-i18n="GLOBAL/CC"></span>: - <span data-bind="text: message().ccToLine()"></span> + <span data-bind="text: message().cc"></span> </div> </div> <div class="informationShort" data-bind="visible: message().spamResult()"> @@ -201,7 +201,7 @@ <!-- ko if: tagsAllowed --> <div class="messageTags"> <span data-i18n="MESSAGE/TAGS"></span>: - <span class="messageAssignedTags" data-bind="html: message().tagsToHTML()"></span> + <span class="messageAssignedTags" data-bind="html: tagsToHTML"></span> <div class="btn-group" data-bind="registerBootstrapDropdown: true" style="display: inline-block"> <a class="btn btn-thin btn-transparent dropdown-toggle fontastic" id="tags-dropdown-id" href="#" tabindex="-1">🏷</a> <menu class="dropdown-menu right-edge" role="menu" aria-labelledby="tags-dropdown-id"> @@ -227,12 +227,12 @@ <div data-bind="hidden: messageLoadingThrottle"> <div class="bodySubHeader"> - <div class="readReceipt" data-bind="visible: !isDraftOrSentFolder() && message().readReceipt() && !message().isReadReceipt(), click: readReceipt" + <div class="readReceipt" data-bind="visible: askReadReceipt, click: readReceipt" data-icon="✉" data-i18n="MESSAGE/BUTTON_NOTIFY_READ_RECEIPT"></div> - <details class="attachmentsPlace" data-bind="visible: message().hasAttachments(), css: {'selection-mode' : showAttachmentControls}"> + <details class="attachmentsPlace" data-bind="visible: hasAttachments, css: {'selection-mode' : showAttachmentControls}"> <summary data-i18n="MESSAGE/PRINT_LABEL_ATTACHMENTS"></summary> <!-- ko ifnot: simpleAttachmentsList --> - <ul class="attachmentList" data-bind="foreach: message().listAttachments()"> + <ul class="attachmentList" data-bind="foreach: listAttachments"> <li class="attachmentItem" draggable="true" data-bind="event: { 'dragstart': eventDragStart }, attr: { 'title': fileName }, css: {'checked': checked}"> <div class="attachmentIconParent" data-bind="css: { 'hasPreview': hasPreview(), 'hasPreplay': hasPreplay(), 'isImage': isImage() }"> @@ -259,7 +259,7 @@ </ul> <!-- /ko --> <!-- ko if: simpleAttachmentsList --> - <ul class="attachmentListSimple" data-bind="foreach: message().listAttachments()"> + <ul class="attachmentListSimple" data-bind="foreach: listAttachments"> <li class="attachmentItem" data-bind="attr: { 'title': fileName }, css: {'checked': checked}"> <span class="attachmentName" data-bind="text: fileName"></span> <i class="checkboxAttachment fontastic" @@ -271,7 +271,7 @@ </details> <div class="attachmentsControls" - data-bind="visible: showAttachmentControls() && message().hasAttachments()"> + data-bind="visible: showAttachmentControls() && hasAttachments()"> <span data-bind="visible: downloadAsZipAllowed"> <i class="fontastic iconcolor-red" data-bind="visible: downloadAsZipError">✖</i>