import window from 'window'; import _ from '_'; import $ from '$'; import ko from 'ko'; import key from 'key'; import Jua from 'Jua'; import { Capa, Magics, KeyState, ComposeType, StorageResultType, EditorDefaultType, Notification, SetSystemFoldersNotification, UploadErrorCode } from 'Common/Enums'; import { trim, isArray, isNormal, delegateRun, isNonEmptyArray, clearBqSwitcher, replySubjectAdd, encodeHtml, noopFalse, inFocus, delegateRunOnDestroy, pInt, isUnd } from 'Common/Utils'; import {UNUSED_OPTION_VALUE} from 'Common/Consts'; import {bXMLHttpRequestSupported, bMobileDevice} from 'Common/Globals'; import {upload} from 'Common/Links'; import {i18n, getNotification, getUploadErrorDescByCode} from 'Common/Translator'; import {format as momentorFormat} from 'Common/Momentor'; import {getMessageFlagsFromCache, setMessageFlagsToCache, setFolderHash} from 'Common/Cache'; import {HtmlEditor} from 'Common/HtmlEditor'; import AppStore from 'Stores/User/App'; import SettingsStore from 'Stores/User/Settings'; import IdentityStore from 'Stores/User/Identity'; import AccountStore from 'Stores/User/Account'; import FolderStore from 'Stores/User/Folder'; import PgpStore from 'Stores/User/Pgp'; import MessageStore from 'Stores/User/Message'; import SocialStore from 'Stores/Social'; import Remote from 'Remote/User/Ajax'; import * as Settings from 'Storage/Settings'; import * as Events from 'Common/Events'; import {ComposeAttachmentModel} from 'Model/ComposeAttachment'; import {getApp} from 'Helper/Apps/User'; import {popup, command, isPopupVisible, showScreenPopup, hideScreenPopup, routeOn, routeOff} from 'Knoin/Knoin'; import {AbstractViewNext} from 'Knoin/AbstractViewNext'; @popup({ name: 'View/Popup/Compose', templateID: 'PopupsCompose' }) class ComposePopupView extends AbstractViewNext { constructor() { super(); const fEmailOutInHelper = (context, identity, name, isIn) => { if (identity && context && identity[name]() && (isIn ? true : context[name]())) { const identityEmail = identity[name](); let list = trim(context[name]()).split(/[,]/); list = _.filter(list, (email) => { email = trim(email); return email && trim(identityEmail) !== email; }); if (isIn) { list.push(identityEmail); } context[name](list.join(',')); } }; this.oLastMessage = null; this.oEditor = null; this.aDraftInfo = null; this.sInReplyTo = ''; this.bFromDraft = false; this.sReferences = ''; this.sLastFocusedField = 'to'; this.resizerTrigger = _.bind(this.resizerTrigger, this); this.allowContacts = !!AppStore.contactsIsAllowed(); this.allowFolders = !!Settings.capa(Capa.Folders); this.bSkipNextHide = false; this.composeInEdit = AppStore.composeInEdit; this.editorDefaultType = SettingsStore.editorDefaultType; this.capaOpenPGP = PgpStore.capaOpenPGP; this.identitiesDropdownTrigger = ko.observable(false); this.to = ko.observable(''); this.to.focused = ko.observable(false); this.cc = ko.observable(''); this.cc.focused = ko.observable(false); this.bcc = ko.observable(''); this.bcc.focused = ko.observable(false); this.replyTo = ko.observable(''); this.replyTo.focused = ko.observable(false); ko.computed(() => { switch (true) { case this.to.focused(): this.sLastFocusedField = 'to'; break; case this.cc.focused(): this.sLastFocusedField = 'cc'; break; case this.bcc.focused(): this.sLastFocusedField = 'bcc'; break; // no default } }).extend({notify: 'always'}); this.subject = ko.observable(''); this.subject.focused = ko.observable(false); this.isHtml = ko.observable(false); this.requestDsn = ko.observable(false); this.requestReadReceipt = ko.observable(false); this.markAsImportant = ko.observable(false); this.sendError = ko.observable(false); this.sendSuccessButSaveError = ko.observable(false); this.savedError = ko.observable(false); this.sendButtonSuccess = ko.computed( () => !this.sendError() && !this.sendSuccessButSaveError() ); this.sendErrorDesc = ko.observable(''); this.savedErrorDesc = ko.observable(''); this.sendError.subscribe((value) => { if (!value) { this.sendErrorDesc(''); } }); this.savedError.subscribe((value) => { if (!value) { this.savedErrorDesc(''); } }); this.sendSuccessButSaveError.subscribe((value) => { if (!value) { this.savedErrorDesc(''); } }); this.savedTime = ko.observable(0); this.savedTimeText = ko.computed( () => (0 < this.savedTime() ? i18n('COMPOSE/SAVED_TIME', {'TIME': momentorFormat(this.savedTime() - 1, 'LT')}) : '') ); this.emptyToError = ko.observable(false); this.emptyToErrorTooltip = ko.computed( () => (this.emptyToError() ? i18n('COMPOSE/EMPTY_TO_ERROR_DESC') : '') ); this.attachmentsInProcessError = ko.observable(false); this.attachmentsInErrorError = ko.observable(false); this.attachmentsErrorTooltip = ko.computed(() => { let result = ''; switch (true) { case this.attachmentsInProcessError(): result = i18n('COMPOSE/ATTACHMENTS_UPLOAD_ERROR_DESC'); break; case this.attachmentsInErrorError(): result = i18n('COMPOSE/ATTACHMENTS_ERROR_DESC'); break; // no default } return result; }); this.showCc = ko.observable(false); this.showBcc = ko.observable(false); this.showReplyTo = ko.observable(false); this.cc.subscribe((value) => { if (false === this.showCc() && 0 < value.length) { this.showCc(true); } }); this.bcc.subscribe((value) => { if (false === this.showBcc() && 0 < value.length) { this.showBcc(true); } }); this.replyTo.subscribe((value) => { if (false === this.showReplyTo() && 0 < value.length) { this.showReplyTo(true); } }); this.draftFolder = ko.observable(''); this.draftUid = ko.observable(''); this.sending = ko.observable(false); this.saving = ko.observable(false); this.attachments = ko.observableArray([]); this.attachmentsInProcess = this.attachments.filter((item) => item && !item.complete()); this.attachmentsInReady = this.attachments.filter((item) => item && item.complete()); this.attachmentsInError = this.attachments.filter((item) => item && '' !== item.error()); this.attachmentsCount = ko.computed(() => this.attachments().length); this.attachmentsInErrorCount = ko.computed(() => this.attachmentsInError().length); this.attachmentsInProcessCount = ko.computed(() => this.attachmentsInProcess().length); this.isDraftFolderMessage = ko.computed(() => '' !== this.draftFolder() && '' !== this.draftUid()); this.attachmentsPlace = ko.observable(false); this.attachments.subscribe(this.resizerTrigger); this.attachmentsPlace.subscribe(this.resizerTrigger); this.attachmentsInErrorCount.subscribe((value) => { if (0 === value) { this.attachmentsInErrorError(false); } }); this.composeUploaderButton = ko.observable(null); this.composeUploaderDropPlace = ko.observable(null); this.dragAndDropEnabled = ko.observable(false); this.dragAndDropOver = ko.observable(false).extend({throttle: 1}); this.dragAndDropVisible = ko.observable(false).extend({throttle: 1}); this.attacheMultipleAllowed = ko.observable(false); this.addAttachmentEnabled = ko.observable(false); this.composeEditorArea = ko.observable(null); this.identities = IdentityStore.identities; this.identitiesOptions = ko.computed( () => _.map(IdentityStore.identities(), (item) => ({ 'item': item, 'optValue': item.id(), 'optText': item.formattedName() })) ); this.currentIdentity = ko.observable( this.identities()[0] ? this.identities()[0] : null); this.currentIdentity.extend({toggleSubscribe: [ this, (identity) => { fEmailOutInHelper(this, identity, 'bcc'); fEmailOutInHelper(this, identity, 'replyTo'); }, (identity) => { fEmailOutInHelper(this, identity, 'bcc', true); fEmailOutInHelper(this, identity, 'replyTo', true); } ]}); this.currentIdentityView = ko.computed(() => { const item = this.currentIdentity(); return item ? item.formattedName() : 'unknown'; }); this.to.subscribe((value) => { if (this.emptyToError() && 0 < value.length) { this.emptyToError(false); } }); this.attachmentsInProcess.subscribe((value) => { if (this.attachmentsInProcessError() && isArray(value) && 0 === value.length) { this.attachmentsInProcessError(false); } }); this.resizer = ko.observable(false).extend({throttle: 50}); this.resizer.subscribe(() => { if (this.oEditor) { this.oEditor.resize(); } }); this.canBeSentOrSaved = ko.computed(() => !this.sending() && !this.saving()); this.sendMessageResponse = _.bind(this.sendMessageResponse, this); this.saveMessageResponse = _.bind(this.saveMessageResponse, this); Events.sub('interval.2m', () => { if (this.modalVisibility() && !FolderStore.draftFolderNotEnabled() && SettingsStore.allowDraftAutosave() && !this.isEmptyForm(false) && !this.saving() && !this.sending() && !this.savedError()) { this.saveCommand(); } }); this.showCc.subscribe(this.resizerTrigger); this.showBcc.subscribe(this.resizerTrigger); this.showReplyTo.subscribe(this.resizerTrigger); this.dropboxEnabled = SocialStore.dropbox.enabled; this.dropboxApiKey = SocialStore.dropbox.apiKey; this.driveEnabled = ko.observable(bXMLHttpRequestSupported && !!Settings.settingsGet('AllowGoogleSocial') && !!Settings.settingsGet('AllowGoogleSocialDrive') && !!Settings.settingsGet('GoogleClientID') && !!Settings.settingsGet('GoogleApiKey')); this.driveVisible = ko.observable(false); this.driveCallback = _.bind(this.driveCallback, this); this.onMessageUploadAttachments = _.bind(this.onMessageUploadAttachments, this); this.bDisabeCloseOnEsc = true; this.sDefaultKeyScope = KeyState.Compose; this.tryToClosePopup = _.debounce(_.bind(this.tryToClosePopup, this), Magics.Time200ms); this.emailsSource = _.bind(this.emailsSource, this); this.autosaveFunction = _.bind(this.autosaveFunction, this); this.iTimer = 0; } @command((self) => self.canBeSentOrSaved()) sendCommand() { const sTo = trim(this.to()), sCc = trim(this.cc()), sBcc = trim(this.bcc()); let sSentFolder = FolderStore.sentFolder(); this.attachmentsInProcessError(false); this.attachmentsInErrorError(false); this.emptyToError(false); if (0 < this.attachmentsInProcess().length) { this.attachmentsInProcessError(true); this.attachmentsPlace(true); } else if (0 < this.attachmentsInError().length) { this.attachmentsInErrorError(true); this.attachmentsPlace(true); } if ('' === sTo && '' === sCc && '' === sBcc) { this.emptyToError(true); } if (!this.emptyToError() && !this.attachmentsInErrorError() && !this.attachmentsInProcessError()) { if (SettingsStore.replySameFolder()) { if (isArray(this.aDraftInfo) && 3 === this.aDraftInfo.length && isNormal(this.aDraftInfo[2]) && 0 < this.aDraftInfo[2].length) { sSentFolder = this.aDraftInfo[2]; } } if (!this.allowFolders) { sSentFolder = UNUSED_OPTION_VALUE; } if ('' === sSentFolder) { showScreenPopup(require('View/Popup/FolderSystem'), [SetSystemFoldersNotification.Sent]); } else { this.sendError(false); this.sending(true); if (isArray(this.aDraftInfo) && 3 === this.aDraftInfo.length) { const flagsCache = getMessageFlagsFromCache(this.aDraftInfo[2], this.aDraftInfo[1]); if (flagsCache) { if ('forward' === this.aDraftInfo[0]) { flagsCache[3] = true; } else { flagsCache[2] = true; } setMessageFlagsToCache(this.aDraftInfo[2], this.aDraftInfo[1], flagsCache); getApp().reloadFlagsCurrentMessageListAndMessageFromCache(); setFolderHash(this.aDraftInfo[2], ''); } } sSentFolder = UNUSED_OPTION_VALUE === sSentFolder ? '' : sSentFolder; setFolderHash(this.draftFolder(), ''); setFolderHash(sSentFolder, ''); Remote.sendMessage( this.sendMessageResponse, this.currentIdentity() ? this.currentIdentity().id() : '', this.draftFolder(), this.draftUid(), sSentFolder, sTo, this.cc(), this.bcc(), this.replyTo(), this.subject(), this.oEditor ? this.oEditor.isHtml() : false, this.oEditor ? this.oEditor.getData(true) : '', this.prepearAttachmentsForSendOrSave(), this.aDraftInfo, this.sInReplyTo, this.sReferences, this.requestDsn(), this.requestReadReceipt(), this.markAsImportant() ); } } } @command((self) => self.canBeSentOrSaved()) saveCommand() { if (!this.allowFolders) { return false; } if (FolderStore.draftFolderNotEnabled()) { showScreenPopup(require('View/Popup/FolderSystem'), [SetSystemFoldersNotification.Draft]); } else { this.savedError(false); this.saving(true); this.autosaveStart(); setFolderHash(FolderStore.draftFolder(), ''); Remote.saveMessage( this.saveMessageResponse, this.currentIdentity() ? this.currentIdentity().id() : '', this.draftFolder(), this.draftUid(), FolderStore.draftFolder(), this.to(), this.cc(), this.bcc(), this.replyTo(), this.subject(), this.oEditor ? this.oEditor.isHtml() : false, this.oEditor ? this.oEditor.getData(true) : '', this.prepearAttachmentsForSendOrSave(), this.aDraftInfo, this.sInReplyTo, this.sReferences, this.markAsImportant() ); } return true; } @command((self) => self.isDraftFolderMessage()) deleteCommand() { const PopupsAskViewModel = require('View/Popup/Ask'); if (!isPopupVisible(PopupsAskViewModel) && this.modalVisibility()) { showScreenPopup(PopupsAskViewModel, [i18n('POPUPS_ASK/DESC_WANT_DELETE_MESSAGES'), () => { if (this.modalVisibility()) { getApp().deleteMessagesFromFolderWithoutCheck(this.draftFolder(), [this.draftUid()]); hideScreenPopup(ComposePopupView); } }]); } } @command((self) => self.canBeSentOrSaved()) skipCommand() { this.bSkipNextHide = true; if (this.modalVisibility() && !this.saving() && !this.sending() && !FolderStore.draftFolderNotEnabled() && SettingsStore.allowDraftAutosave()) { this.saveCommand(); } this.tryToClosePopup(); } @command((self) => self.allowContacts) contactsCommand() { if (this.allowContacts) { this.skipCommand(); _.delay(() => { showScreenPopup(require('View/Popup/Contacts'), [true, this.sLastFocusedField]); }, Magics.Time200ms); } } @command((self) => self.dropboxEnabled()) dropboxCommand() { if (window.Dropbox) { window.Dropbox.choose({ success: (files) => { if (files && files[0] && files[0].link) { this.addDropboxAttachment(files[0]); } }, linkType: 'direct', multiselect: false }); } return true; } @command((self) => self.driveEnabled()) driveCommand() { this.driveOpenPopup(); return true; } autosaveFunction() { if (this.modalVisibility() && !FolderStore.draftFolderNotEnabled() && SettingsStore.allowDraftAutosave() && !this.isEmptyForm(false) && !this.saving() && !this.sending() && !this.savedError()) { this.saveCommand(); } this.autosaveStart(); } autosaveStart() { window.clearTimeout(this.iTimer); this.iTimer = window.setTimeout(this.autosaveFunction, Magics.Time1m); } autosaveStop() { window.clearTimeout(this.iTimer); } emailsSource(oData, fResponse) { getApp().getAutocomplete(oData.term, (aData) => { fResponse(_.map(aData, (oEmailItem) => oEmailItem.toLine(false))); }); } openOpenPgpPopup() { if (PgpStore.capaOpenPGP() && this.oEditor && !this.oEditor.isHtml()) { showScreenPopup(require('View/Popup/ComposeOpenPgp'), [ (result) => { this.editor((editor) => { editor.setPlain(result); }); }, this.oEditor.getData(false), this.currentIdentity(), this.to(), this.cc(), this.bcc() ]); } } reloadDraftFolder() { const draftFolder = FolderStore.draftFolder(); if ('' !== draftFolder && UNUSED_OPTION_VALUE !== draftFolder) { setFolderHash(draftFolder, ''); if (FolderStore.currentFolderFullNameRaw() === draftFolder) { getApp().reloadMessageList(true); } else { getApp().folderInformation(draftFolder); } } } findIdentityByMessage(composeType, message) { let resultIndex = 1000, resultIdentity = null; const identities = IdentityStore.identities(), identitiesCache = {}, fEachHelper = (item) => { if (item && item.email && identitiesCache[item.email]) { if (!resultIdentity || resultIndex > identitiesCache[item.email][1]) { resultIdentity = identitiesCache[item.email][0]; resultIndex = identitiesCache[item.email][1]; } } }; _.each(identities, (item, index) => { identitiesCache[item.email()] = [item, index]; }); if (message) { switch (composeType) { case ComposeType.Empty: break; case ComposeType.Reply: case ComposeType.ReplyAll: case ComposeType.Forward: case ComposeType.ForwardAsAttachment: _.each(_.union(message.to, message.cc, message.bcc), fEachHelper); if (!resultIdentity) { _.each(message.deliveredTo, fEachHelper); } break; case ComposeType.Draft: _.each(_.union(message.from, message.replyTo), fEachHelper); break; // no default } } return resultIdentity || identities[0] || null; } selectIdentity(identity) { if (identity && identity.item) { this.currentIdentity(identity.item); this.setSignatureFromIdentity(identity.item); } } sendMessageResponse(statusResult, data) { let result = false, message = ''; this.sending(false); if (StorageResultType.Success === statusResult && data && data.Result) { result = true; if (this.modalVisibility()) { delegateRun(this, 'closeCommand'); } } if (this.modalVisibility() && !result) { if (data && Notification.CantSaveMessage === data.ErrorCode) { this.sendSuccessButSaveError(true); this.savedErrorDesc(trim(i18n('COMPOSE/SAVED_ERROR_ON_SEND'))); } else { message = getNotification(data && data.ErrorCode ? data.ErrorCode : Notification.CantSendMessage, data && data.ErrorMessage ? data.ErrorMessage : ''); this.sendError(true); this.sendErrorDesc(message || getNotification(Notification.CantSendMessage)); } } this.reloadDraftFolder(); } saveMessageResponse(statusResult, oData) { let result = false; this.saving(false); if (StorageResultType.Success === statusResult && oData && oData.Result) { if (oData.Result.NewFolder && oData.Result.NewUid) { result = true; if (this.bFromDraft) { const message = MessageStore.message(); if (message && this.draftFolder() === message.folderFullNameRaw && this.draftUid() === message.uid) { MessageStore.message(null); } } this.draftFolder(oData.Result.NewFolder); this.draftUid(oData.Result.NewUid); this.savedTime(window.Math.round((new window.Date()).getTime() / 1000)); if (this.bFromDraft) { setFolderHash(this.draftFolder(), ''); } } } if (!result) { this.savedError(true); this.savedErrorDesc(getNotification(Notification.CantSaveMessage)); } this.reloadDraftFolder(); } onHide() { this.autosaveStop(); if (!this.bSkipNextHide) { AppStore.composeInEdit(false); this.reset(); } this.bSkipNextHide = false; this.to.focused(false); routeOn(); } editor(fOnInit) { if (fOnInit) { if (!this.oEditor && this.composeEditorArea()) { // _.delay(() => { this.oEditor = new HtmlEditor(this.composeEditorArea(), null, () => { fOnInit(this.oEditor); this.resizerTrigger(); }, (bHtml) => { this.isHtml(!!bHtml); }); // }, 1000); } else if (this.oEditor) { fOnInit(this.oEditor); this.resizerTrigger(); } } } converSignature(signature) { let limit = 10, fromLine = ''; const moments = [], momentRegx = /{{MOMENT:([^}]+)}}/g; signature = signature.replace(/[\r]/g, ''); fromLine = this.oLastMessage ? this.emailArrayToStringLineHelper(this.oLastMessage.from, true) : ''; if ('' !== fromLine) { signature = signature.replace(/{{FROM-FULL}}/g, fromLine); if (-1 === fromLine.indexOf(' ') && 0 < fromLine.indexOf('@')) { fromLine = fromLine.replace(/@[\S]+/, ''); } signature = signature.replace(/{{FROM}}/g, fromLine); } signature = signature.replace(/[\s]{1,2}{{FROM}}/g, '{{FROM}}'); signature = signature.replace(/[\s]{1,2}{{FROM-FULL}}/g, '{{FROM-FULL}}'); signature = signature.replace(/{{FROM}}/g, ''); signature = signature.replace(/{{FROM-FULL}}/g, ''); if (-1 < signature.indexOf('{{DATE}}')) { signature = signature.replace(/{{DATE}}/g, momentorFormat(0, 'llll')); } if (-1 < signature.indexOf('{{TIME}}')) { signature = signature.replace(/{{TIME}}/g, momentorFormat(0, 'LT')); } if (-1 < signature.indexOf('{{MOMENT:')) { try { let match = null; while (null !== (match = momentRegx.exec(signature))) // eslint-disable-line no-cond-assign { if (match && match[0] && match[1]) { moments.push([match[0], match[1]]); } limit -= 1; if (0 === limit) { break; } } if (moments && 0 < moments.length) { _.each(moments, (data) => { signature = signature.replace(data[0], momentorFormat(0, data[1])); }); } signature = signature.replace(/{{MOMENT:[^}]+}}/g, ''); } catch (e) {} // eslint-disable-line no-empty } return signature; } setSignatureFromIdentity(identity) { if (identity) { this.editor((editor) => { let isHtml = false, signature = identity.signature(); if ('' !== signature) { if (':HTML:' === signature.substr(0, 6)) { isHtml = true; signature = signature.substr(6); } } editor.setSignature(this.converSignature(signature), isHtml, !!identity.signatureInsertBefore()); }); } } /** * @param {string=} type = ComposeType.Empty * @param {?MessageModel|Array=} oMessageOrArray = null * @param {Array=} aToEmails = null * @param {Array=} aCcEmails = null * @param {Array=} aBccEmails = null * @param {string=} sCustomSubject = null * @param {string=} sCustomPlainText = null */ onShow(type, oMessageOrArray, aToEmails, aCcEmails, aBccEmails, sCustomSubject, sCustomPlainText) { routeOff(); this.autosaveStart(); if (AppStore.composeInEdit()) { type = type || ComposeType.Empty; if (ComposeType.Empty !== type) { showScreenPopup(require('View/Popup/Ask'), [i18n('COMPOSE/DISCARD_UNSAVED_DATA'), () => { this.initOnShow(type, oMessageOrArray, aToEmails, aCcEmails, aBccEmails, sCustomSubject, sCustomPlainText); }, null, null, null, false]); } else { this.addEmailsTo(this.to, aToEmails); this.addEmailsTo(this.cc, aCcEmails); this.addEmailsTo(this.bcc, aBccEmails); if (isNormal(sCustomSubject) && '' !== sCustomSubject && '' === this.subject()) { this.subject(sCustomSubject); } } } else { this.initOnShow(type, oMessageOrArray, aToEmails, aCcEmails, aBccEmails, sCustomSubject, sCustomPlainText); } } onWarmUp() { if (this.modalVisibility && !this.modalVisibility()) { this.editor((editor) => editor.modeToggle(false)); } } /** * @param {Function} fKoValue * @param {Array} emails */ addEmailsTo(fKoValue, emails) { if (isNonEmptyArray(emails)) { const value = trim(fKoValue()), values = _.uniq(_.compact(_.map(emails, (item) => (item ? item.toLine(false) : null)))); fKoValue(value + ('' === value ? '' : ', ') + trim(values.join(', '))); } } /** * * @param {Array} aList * @param {boolean} bFriendly * @returns {string} */ emailArrayToStringLineHelper(aList, bFriendly) { bFriendly = !!bFriendly; return _.map(aList, (item) => item.toLine(bFriendly)).join(', '); } /** * @param {string=} sType = ComposeType.Empty * @param {?MessageModel|Array=} oMessageOrArray = null * @param {Array=} aToEmails = null * @param {Array=} aCcEmails = null * @param {Array=} aBccEmails = null * @param {string=} sCustomSubject = null * @param {string=} sCustomPlainText = null */ initOnShow(sType, oMessageOrArray, aToEmails, aCcEmails, aBccEmails, sCustomSubject, sCustomPlainText) { AppStore.composeInEdit(true); let sFrom = '', sTo = '', sCc = '', sDate = '', sSubject = '', sText = '', sReplyTitle = '', identity = null, aDraftInfo = null, message = null; const excludeEmail = {}, mEmail = AccountStore.email(), lineComposeType = sType || ComposeType.Empty; oMessageOrArray = oMessageOrArray || null; if (oMessageOrArray && isNormal(oMessageOrArray)) { message = isArray(oMessageOrArray) && 1 === oMessageOrArray.length ? oMessageOrArray[0] : (!isArray(oMessageOrArray) ? oMessageOrArray : null); } this.oLastMessage = message; if (null !== mEmail) { excludeEmail[mEmail] = true; } this.reset(); identity = this.findIdentityByMessage(lineComposeType, message); if (identity) { excludeEmail[identity.email()] = true; } if (isNonEmptyArray(aToEmails)) { this.to(this.emailArrayToStringLineHelper(aToEmails)); } if (isNonEmptyArray(aCcEmails)) { this.cc(this.emailArrayToStringLineHelper(aCcEmails)); } if (isNonEmptyArray(aBccEmails)) { this.bcc(this.emailArrayToStringLineHelper(aBccEmails)); } if ('' !== lineComposeType && message) { sDate = momentorFormat(message.dateTimeStampInUTC(), 'FULL'); sSubject = message.subject(); aDraftInfo = message.aDraftInfo; const clonedText = $(message.body).clone(); if (clonedText) { clearBqSwitcher(clonedText); sText = clonedText.html(); } let resplyAllParts = null; switch (lineComposeType) { case ComposeType.Empty: break; case ComposeType.Reply: this.to(this.emailArrayToStringLineHelper(message.replyEmails(excludeEmail))); this.subject(replySubjectAdd('Re', sSubject)); this.prepearMessageAttachments(message, lineComposeType); this.aDraftInfo = ['reply', message.uid, message.folderFullNameRaw]; this.sInReplyTo = message.sMessageId; this.sReferences = trim(this.sInReplyTo + ' ' + message.sReferences); break; case ComposeType.ReplyAll: resplyAllParts = message.replyAllEmails(excludeEmail); this.to(this.emailArrayToStringLineHelper(resplyAllParts[0])); this.cc(this.emailArrayToStringLineHelper(resplyAllParts[1])); this.subject(replySubjectAdd('Re', sSubject)); this.prepearMessageAttachments(message, lineComposeType); this.aDraftInfo = ['reply', message.uid, message.folderFullNameRaw]; this.sInReplyTo = message.sMessageId; this.sReferences = trim(this.sInReplyTo + ' ' + message.references()); break; case ComposeType.Forward: this.subject(replySubjectAdd('Fwd', sSubject)); this.prepearMessageAttachments(message, lineComposeType); this.aDraftInfo = ['forward', message.uid, message.folderFullNameRaw]; this.sInReplyTo = message.sMessageId; this.sReferences = trim(this.sInReplyTo + ' ' + message.sReferences); break; case ComposeType.ForwardAsAttachment: this.subject(replySubjectAdd('Fwd', sSubject)); this.prepearMessageAttachments(message, lineComposeType); this.aDraftInfo = ['forward', message.uid, message.folderFullNameRaw]; this.sInReplyTo = message.sMessageId; this.sReferences = trim(this.sInReplyTo + ' ' + message.sReferences); break; case ComposeType.Draft: this.to(this.emailArrayToStringLineHelper(message.to)); this.cc(this.emailArrayToStringLineHelper(message.cc)); this.bcc(this.emailArrayToStringLineHelper(message.bcc)); this.replyTo(this.emailArrayToStringLineHelper(message.replyTo)); this.bFromDraft = true; this.draftFolder(message.folderFullNameRaw); this.draftUid(message.uid); this.subject(sSubject); this.prepearMessageAttachments(message, lineComposeType); this.aDraftInfo = isNonEmptyArray(aDraftInfo) && 3 === aDraftInfo.length ? aDraftInfo : null; this.sInReplyTo = message.sInReplyTo; this.sReferences = message.sReferences; break; case ComposeType.EditAsNew: this.to(this.emailArrayToStringLineHelper(message.to)); this.cc(this.emailArrayToStringLineHelper(message.cc)); this.bcc(this.emailArrayToStringLineHelper(message.bcc)); this.replyTo(this.emailArrayToStringLineHelper(message.replyTo)); this.subject(sSubject); this.prepearMessageAttachments(message, lineComposeType); this.aDraftInfo = isNonEmptyArray(aDraftInfo) && 3 === aDraftInfo.length ? aDraftInfo : null; this.sInReplyTo = message.sInReplyTo; this.sReferences = message.sReferences; break; // no default } switch (lineComposeType) { case ComposeType.Reply: case ComposeType.ReplyAll: sFrom = message.fromToLine(false, true); sReplyTitle = i18n('COMPOSE/REPLY_MESSAGE_TITLE', { 'DATETIME': sDate, 'EMAIL': sFrom }); sText = '

' + sReplyTitle + ':' + '

' + '
' + trim(sText) + '
'; break; case ComposeType.Forward: sFrom = message.fromToLine(false, true); sTo = message.toToLine(false, true); sCc = message.ccToLine(false, true); sText = '

' + i18n('COMPOSE/FORWARD_MESSAGE_TOP_TITLE') + '
' + i18n('COMPOSE/FORWARD_MESSAGE_TOP_FROM') + ': ' + sFrom + '
' + i18n('COMPOSE/FORWARD_MESSAGE_TOP_TO') + ': ' + sTo + (0 < sCc.length ? '
' + i18n('COMPOSE/FORWARD_MESSAGE_TOP_CC') + ': ' + sCc : '') + '
' + i18n('COMPOSE/FORWARD_MESSAGE_TOP_SENT') + ': ' + encodeHtml(sDate) + '
' + i18n('COMPOSE/FORWARD_MESSAGE_TOP_SUBJECT') + ': ' + encodeHtml(sSubject) + '

' + trim(sText) + '

'; break; case ComposeType.ForwardAsAttachment: sText = ''; break; // no default } this.editor((editor) => { editor.setHtml(sText, false); if (EditorDefaultType.PlainForced === this.editorDefaultType() || (!message.isHtml() && EditorDefaultType.HtmlForced !== this.editorDefaultType())) { editor.modeToggle(false); } if (identity && ComposeType.Draft !== lineComposeType && ComposeType.EditAsNew !== lineComposeType) { this.setSignatureFromIdentity(identity); } this.setFocusInPopup(); }); } else if (ComposeType.Empty === lineComposeType) { this.subject(isNormal(sCustomSubject) ? '' + sCustomSubject : ''); sText = isNormal(sCustomPlainText) ? '' + sCustomPlainText : ''; this.editor((editor) => { editor.setHtml(sText, false); if (EditorDefaultType.Html !== this.editorDefaultType() && EditorDefaultType.HtmlForced !== this.editorDefaultType()) { editor.modeToggle(false); } if (identity) { this.setSignatureFromIdentity(identity); } this.setFocusInPopup(); }); } else if (isNonEmptyArray(oMessageOrArray)) { _.each(oMessageOrArray, (item) => { this.addMessageAsAttachment(item); }); this.editor((editor) => { editor.setHtml('', false); if (EditorDefaultType.Html !== this.editorDefaultType() && EditorDefaultType.HtmlForced !== this.editorDefaultType()) { editor.modeToggle(false); } if (identity && ComposeType.Draft !== lineComposeType && ComposeType.EditAsNew !== lineComposeType) { this.setSignatureFromIdentity(identity); } this.setFocusInPopup(); }); } else { this.setFocusInPopup(); } const downloads = this.getAttachmentsDownloadsForUpload(); if (isNonEmptyArray(downloads)) { Remote.messageUploadAttachments(this.onMessageUploadAttachments, downloads); } if (identity) { this.currentIdentity(identity); } this.resizerTrigger(); } onMessageUploadAttachments(sResult, oData) { if (StorageResultType.Success === sResult && oData && oData.Result) { if (!this.viewModelVisibility()) { _.each(oData.Result, (id, tempName) => { const attachment = this.getAttachmentById(id); if (attachment) { attachment.tempName(tempName); attachment.waiting(false).uploading(false).complete(true); } }); } } else { this.setMessageAttachmentFailedDownloadText(); } } setFocusInPopup() { if (!bMobileDevice) { _.delay(() => { if ('' === this.to()) { this.to.focused(true); } else if (this.oEditor) { if (!this.to.focused()) { this.oEditor.focus(); } } }, Magics.Time100ms); } } onShowWithDelay() { this.resizerTrigger(); } tryToClosePopup() { const PopupsAskViewModel = require('View/Popup/Ask'); if (!isPopupVisible(PopupsAskViewModel) && this.modalVisibility()) { if (this.bSkipNextHide || (this.isEmptyForm() && !this.draftUid())) { delegateRun(this, 'closeCommand'); } else { showScreenPopup(PopupsAskViewModel, [i18n('POPUPS_ASK/DESC_WANT_CLOSE_THIS_WINDOW'), () => { if (this.modalVisibility()) { delegateRun(this, 'closeCommand'); } }]); } } } onBuild() { this.initUploader(); key('ctrl+q, command+q, ctrl+w, command+w', KeyState.Compose, noopFalse); key('`', KeyState.Compose, () => { if (this.oEditor && !this.oEditor.hasFocus() && !inFocus()) { this.identitiesDropdownTrigger(true); return false; } return true; }); key('ctrl+`', KeyState.Compose, () => { this.identitiesDropdownTrigger(true); return false; }); key('esc, ctrl+down, command+down', KeyState.Compose, () => { this.skipCommand(); return false; }); if (this.allowFolders) { key('ctrl+s, command+s', KeyState.Compose, () => { this.saveCommand(); return false; }); } if (Settings.appSettingsGet('allowCtrlEnterOnCompose')) { key('ctrl+enter, command+enter', KeyState.Compose, () => { this.sendCommand(); return false; }); } key('shift+esc', KeyState.Compose, () => { if (this.modalVisibility()) { this.tryToClosePopup(); } return false; }); Events.sub('window.resize.real', this.resizerTrigger); Events.sub('window.resize.real', _.debounce(this.resizerTrigger, Magics.Time50ms)); SocialStore.appendDropbox(); if (this.driveEnabled()) { $.getScript('https://apis.google.com/js/api.js', () => { if (window.gapi) { this.driveVisible(true); } }); } window.setInterval(() => { if (this.modalVisibility() && this.oEditor) { this.oEditor.resize(); } }, Magics.Time5s); } driveCallback(accessToken, data) { if (data && window.XMLHttpRequest && window.google && data[window.google.picker.Response.ACTION] === window.google.picker.Action.PICKED && data[window.google.picker.Response.DOCUMENTS] && data[window.google.picker.Response.DOCUMENTS][0] && data[window.google.picker.Response.DOCUMENTS][0].id) { const request = new window.XMLHttpRequest(); request.open('GET', 'https://www.googleapis.com/drive/v2/files/' + data[window.google.picker.Response.DOCUMENTS][0].id); request.setRequestHeader('Authorization', 'Bearer ' + accessToken); request.addEventListener('load', () => { if (request && request.responseText) { const response = window.JSON.parse(request.responseText), fExport = (item, mimeType, ext) => { if (item && item.exportLinks) { if (item.exportLinks[mimeType]) { response.downloadUrl = item.exportLinks[mimeType]; response.title = item.title + '.' + ext; response.mimeType = mimeType; } else if (item.exportLinks['application/pdf']) { response.downloadUrl = item.exportLinks['application/pdf']; response.title = item.title + '.pdf'; response.mimeType = 'application/pdf'; } } }; if (response && !response.downloadUrl && response.mimeType && response.exportLinks) { switch (response.mimeType.toString().toLowerCase()) { case 'application/vnd.google-apps.document': fExport(response, 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'docx'); break; case 'application/vnd.google-apps.spreadsheet': fExport(response, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'xlsx'); break; case 'application/vnd.google-apps.drawing': fExport(response, 'image/png', 'png'); break; case 'application/vnd.google-apps.presentation': fExport(response, 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'pptx'); break; default: fExport(response, 'application/pdf', 'pdf'); break; } } if (response && response.downloadUrl) { this.addDriveAttachment(response, accessToken); } } }); request.send(); } } driveCreatePiker(authToken) { if (window.gapi && authToken && authToken.access_token) { window.gapi.load('picker', { callback: () => { if (window.google && window.google.picker) { const drivePicker = new window.google.picker.PickerBuilder() // .addView(window.google.picker.ViewId.FOLDERS) .addView(window.google.picker.ViewId.DOCS) .setAppId(Settings.settingsGet('GoogleClientID')) .setOAuthToken(authToken.access_token) .setCallback(_.bind(this.driveCallback, this, authToken.access_token)) .enableFeature(window.google.picker.Feature.NAV_HIDDEN) // .setOrigin(window.location.protocol + '//' + window.location.host) .build(); drivePicker.setVisible(true); } } }); } } driveOpenPopup() { if (window.gapi) { window.gapi.load('auth', { callback: () => { const authToken = window.gapi.auth.getToken(), fResult = (authResult) => { if (authResult && !authResult.error) { const token = window.gapi.auth.getToken(); if (token) { this.driveCreatePiker(token); } return true; } return false; }; if (!authToken) { window.gapi.auth.authorize({ 'client_id': Settings.settingsGet('GoogleClientID'), 'scope': 'https://www.googleapis.com/auth/drive.readonly', 'immediate': true }, (authResult) => { if (!fResult(authResult)) { window.gapi.auth.authorize({ 'client_id': Settings.settingsGet('GoogleClientID'), 'scope': 'https://www.googleapis.com/auth/drive.readonly', 'immediate': false }, fResult); } }); } else { this.driveCreatePiker(authToken); } } }); } } /** * @param {string} id * @returns {?Object} */ getAttachmentById(id) { return _.find(this.attachments(), (item) => item && id === item.id); } cancelAttachmentHelper(id, oJua) { return () => { const attachment = _.find(this.attachments(), (item) => item && item.id === id); if (attachment) { this.attachments.remove(attachment); delegateRunOnDestroy(attachment); if (oJua) { oJua.cancel(id); } } }; } initUploader() { if (this.composeUploaderButton()) { const uploadCache = {}, attachmentSizeLimit = pInt(Settings.settingsGet('AttachmentLimit')), oJua = new Jua({ 'action': upload(), 'name': 'uploader', 'queueSize': 2, 'multipleSizeLimit': 50, 'clickElement': this.composeUploaderButton(), 'dragAndDropElement': this.composeUploaderDropPlace() }); if (oJua) { oJua // .on('onLimitReached', (limit) => { // alert(limit); // }) .on('onDragEnter', () => { this.dragAndDropOver(true); }) .on('onDragLeave', () => { this.dragAndDropOver(false); }) .on('onBodyDragEnter', () => { this.attachmentsPlace(true); this.dragAndDropVisible(true); }) .on('onBodyDragLeave', () => { this.dragAndDropVisible(false); }) .on('onProgress', (id, loaded, total) => { let item = uploadCache[id]; if (!item) { item = this.getAttachmentById(id); if (item) { uploadCache[id] = item; } } if (item) { item.progress(window.Math.floor(loaded / total * 100)); } }) .on('onSelect', (sId, oData) => { this.dragAndDropOver(false); const fileName = isUnd(oData.FileName) ? '' : oData.FileName.toString(), size = isNormal(oData.Size) ? pInt(oData.Size) : null, attachment = new ComposeAttachmentModel(sId, fileName, size); attachment.cancel = this.cancelAttachmentHelper(sId, oJua); this.attachments.push(attachment); this.attachmentsPlace(true); if (0 < size && 0 < attachmentSizeLimit && attachmentSizeLimit < size) { attachment .waiting(false).uploading(true).complete(true) .error(i18n('UPLOAD/ERROR_FILE_IS_TOO_BIG')); return false; } return true; }) .on('onStart', (id) => { let item = uploadCache[id]; if (!item) { item = this.getAttachmentById(id); if (item) { uploadCache[id] = item; } } if (item) { item.waiting(false).uploading(true).complete(false); } }) .on('onComplete', (id, result, data) => { const attachment = this.getAttachmentById(id), errorCode = data && data.Result && data.Result.ErrorCode ? data.Result.ErrorCode : null, attachmentJson = result && data && data.Result && data.Result.Attachment ? data.Result.Attachment : null; let error = ''; if (null !== errorCode) { error = getUploadErrorDescByCode(errorCode); } else if (!attachmentJson) { error = i18n('UPLOAD/ERROR_UNKNOWN'); } if (attachment) { if ('' !== error && 0 < error.length) { attachment .waiting(false) .uploading(false) .complete(true) .error(error); } else if (attachmentJson) { attachment .waiting(false) .uploading(false) .complete(true); attachment.initByUploadJson(attachmentJson); } if (isUnd(uploadCache[id])) { delete (uploadCache[id]); } } }); this .addAttachmentEnabled(true) .dragAndDropEnabled(oJua.isDragAndDropSupported()); } else { this .addAttachmentEnabled(false) .dragAndDropEnabled(false); } } } /** * @returns {Object} */ prepearAttachmentsForSendOrSave() { const result = {}; _.each(this.attachmentsInReady(), (item) => { if (item && '' !== item.tempName() && item.enabled()) { result[item.tempName()] = [ item.fileName(), item.isInline ? '1' : '0', item.CID, item.contentLocation ]; } }); return result; } /** * @param {MessageModel} message */ addMessageAsAttachment(message) { if (message) { let temp = message.subject(); temp = '.eml' === temp.substr(-4).toLowerCase() ? temp : temp + '.eml'; const attachment = new ComposeAttachmentModel( message.requestHash, temp, message.size() ); attachment.fromMessage = true; attachment.cancel = this.cancelAttachmentHelper(message.requestHash); attachment.waiting(false).uploading(true).complete(true); this.attachments.push(attachment); } } /** * @param {string} url * @param {string} name * @param {number} size * @returns {ComposeAttachmentModel} */ addAttachmentHelper(url, name, size) { const attachment = new ComposeAttachmentModel(url, name, size); attachment.fromMessage = false; attachment.cancel = this.cancelAttachmentHelper(url); attachment.waiting(false).uploading(true).complete(false); this.attachments.push(attachment); this.attachmentsPlace(true); return attachment; } /** * @param {Object} dropboxFile * @returns {boolean} */ addDropboxAttachment(dropboxFile) { const attachmentSizeLimit = pInt(Settings.settingsGet('AttachmentLimit')), mSize = dropboxFile.bytes, attachment = this.addAttachmentHelper(dropboxFile.link, dropboxFile.name, mSize); if (0 < mSize && 0 < attachmentSizeLimit && attachmentSizeLimit < mSize) { attachment.uploading(false).complete(true); attachment.error(i18n('UPLOAD/ERROR_FILE_IS_TOO_BIG')); return false; } Remote.composeUploadExternals((statusResult, data) => { let result = false; attachment.uploading(false).complete(true); if (StorageResultType.Success === statusResult && data && data.Result) { if (data.Result[attachment.id]) { result = true; attachment.tempName(data.Result[attachment.id]); } } if (!result) { attachment.error(getUploadErrorDescByCode(UploadErrorCode.FileNoUploaded)); } }, [dropboxFile.link]); return true; } /** * @param {Object} driveFile * @param {string} accessToken * @returns {boolean} */ addDriveAttachment(driveFile, accessToken) { const attachmentSizeLimit = pInt(Settings.settingsGet('AttachmentLimit')), size = driveFile.fileSize ? pInt(driveFile.fileSize) : 0, attachment = this.addAttachmentHelper(driveFile.downloadUrl, driveFile.title, size); if (0 < size && 0 < attachmentSizeLimit && attachmentSizeLimit < size) { attachment.uploading(false).complete(true); attachment.error(i18n('UPLOAD/ERROR_FILE_IS_TOO_BIG')); return false; } Remote.composeUploadDrive((statusResult, data) => { let result = false; attachment.uploading(false).complete(true); if (StorageResultType.Success === statusResult && data && data.Result) { if (data.Result[attachment.id]) { result = true; attachment.tempName(data.Result[attachment.id][0]); attachment.size(pInt(data.Result[attachment.id][1])); } } if (!result) { attachment.error(getUploadErrorDescByCode(UploadErrorCode.FileNoUploaded)); } }, driveFile.downloadUrl, accessToken); return true; } /** * @param {MessageModel} message * @param {string} type */ prepearMessageAttachments(message, type) { if (message) { if (ComposeType.ForwardAsAttachment === type) { this.addMessageAsAttachment(message); } else { const attachments = message.attachments(); _.each(isNonEmptyArray(attachments) ? attachments : [], (item) => { let add = false; switch (type) { case ComposeType.Reply: case ComposeType.ReplyAll: add = item.isLinked; break; case ComposeType.Forward: case ComposeType.Draft: case ComposeType.EditAsNew: add = true; break; // no default } if (add) { const attachment = new ComposeAttachmentModel( item.download, item.fileName, item.estimatedSize, item.isInline, item.isLinked, item.cid, item.contentLocation ); attachment.fromMessage = true; attachment.cancel = this.cancelAttachmentHelper(item.download); attachment.waiting(false).uploading(true).complete(false); this.attachments.push(attachment); } }); } } } removeLinkedAttachments() { const arrachment = _.find(this.attachments(), (item) => item && item.isLinked); if (arrachment) { this.attachments.remove(arrachment); delegateRunOnDestroy(arrachment); } } setMessageAttachmentFailedDownloadText() { _.each(this.attachments(), (attachment) => { if (attachment && attachment.fromMessage) { attachment .waiting(false) .uploading(false) .complete(true) .error(getUploadErrorDescByCode(UploadErrorCode.FileNoUploaded)); } }); } /** * @param {boolean=} includeAttachmentInProgress = true * @returns {boolean} */ isEmptyForm(includeAttachmentInProgress = true) { const withoutAttachment = includeAttachmentInProgress ? 0 === this.attachments().length : 0 === this.attachmentsInReady().length; return 0 === this.to().length && 0 === this.cc().length && 0 === this.bcc().length && 0 === this.replyTo().length && 0 === this.subject().length && withoutAttachment && (!this.oEditor || '' === this.oEditor.getData()); } reset() { this.to(''); this.cc(''); this.bcc(''); this.replyTo(''); this.subject(''); this.requestDsn(false); this.requestReadReceipt(false); this.markAsImportant(false); this.attachmentsPlace(false); this.aDraftInfo = null; this.sInReplyTo = ''; this.bFromDraft = false; this.sReferences = ''; this.sendError(false); this.sendSuccessButSaveError(false); this.savedError(false); this.savedTime(0); this.emptyToError(false); this.attachmentsInProcessError(false); this.showCc(false); this.showBcc(false); this.showReplyTo(false); delegateRunOnDestroy(this.attachments()); this.attachments([]); this.dragAndDropOver(false); this.dragAndDropVisible(false); this.draftFolder(''); this.draftUid(''); this.sending(false); this.saving(false); if (this.oEditor) { this.oEditor.clear(false); } } /** * @returns {Array} */ getAttachmentsDownloadsForUpload() { return _.map(_.filter( this.attachments(), (item) => item && '' === item.tempName(), ), (item) => item.id); } resizerTrigger() { this.resizer(!this.resizer()); } } export {ComposePopupView, ComposePopupView as default};