import window from 'window';
import ko from 'ko';
import $ from '$';
import { Magics, Layout, Focused, MessageSetAction, StorageResultType, Notification } from 'Common/Enums';
import {
isNormal,
pInt,
pString,
plainToHtml,
windowResize,
findEmailAndLinks,
getRealHeight
} from 'Common/Utils';
import {
getFolderInboxName,
addNewMessageCache,
setFolderUidNext,
getFolderFromCacheList,
setFolderHash,
initMessageFlagsFromCache,
addRequestedMessage,
clearMessageFlagsFromCacheByFolder,
hasNewMessageAndRemoveFromCache,
storeMessageFlagsToCache,
clearNewMessageCache
} from 'Common/Cache';
import { MESSAGE_BODY_CACHE_LIMIT } from 'Common/Consts';
import { data as GlobalsData, $div } from 'Common/Globals';
import { mailBox, notificationMailIcon } from 'Common/Links';
import { i18n, getNotification } from 'Common/Translator';
import { momentNowUnix } from 'Common/Momentor';
import * as MessageHelper from 'Helper/Message';
import { MessageModel } from 'Model/Message';
import { setHash } from 'Knoin/Knoin';
import AppStore from 'Stores/User/App';
import AccountStore from 'Stores/User/Account';
import FolderStore from 'Stores/User/Folder';
import PgpStore from 'Stores/User/Pgp';
import SettingsStore from 'Stores/User/Settings';
import NotificationStore from 'Stores/User/Notification';
import { getApp } from 'Helper/Apps/User';
import Remote from 'Remote/User/Ajax';
class MessageUserStore {
constructor() {
this.staticMessage = new MessageModel();
this.messageList = ko.observableArray([]).extend({ rateLimit: 0 });
this.messageListCount = ko.observable(0);
this.messageListSearch = ko.observable('');
this.messageListThreadUid = ko.observable('');
this.messageListPage = ko.observable(1);
this.messageListPageBeforeThread = ko.observable(1);
this.messageListError = ko.observable('');
this.messageListEndFolder = ko.observable('');
this.messageListEndSearch = ko.observable('');
this.messageListEndThreadUid = ko.observable('');
this.messageListEndPage = ko.observable(1);
this.messageListLoading = ko.observable(false);
this.messageListIsNotCompleted = ko.observable(false);
this.messageListCompleteLoadingThrottle = ko.observable(false).extend({ throttle: 200 });
this.messageListCompleteLoadingThrottleForAnimation = ko.observable(false).extend({ specialThrottle: 700 });
this.messageListDisableAutoSelect = ko.observable(false).extend({ falseTimeout: 500 });
this.selectorMessageSelected = ko.observable(null);
this.selectorMessageFocused = ko.observable(null);
// message viewer
this.message = ko.observable(null);
this.message.viewTrigger = ko.observable(false);
this.messageError = ko.observable('');
this.messageCurrentLoading = ko.observable(false);
this.messageLoadingThrottle = ko.observable(false).extend({ throttle: Magics.Time50ms });
this.messageFullScreenMode = ko.observable(false);
this.messagesBodiesDom = ko.observable(null);
this.messageActiveDom = ko.observable(null);
this.computers();
this.subscribers();
this.onMessageResponse = this.onMessageResponse.bind(this);
// throttle
var t, o = this;
this.purgeMessageBodyCacheThrottle = ()=>{
if (!t) {
t = setTimeout(()=>{
o.purgeMessageBodyCache();
t = 0;
}, Magics.Time30s);
}
};
}
computers() {
this.messageLoading = ko.computed(() => this.messageCurrentLoading());
this.messageListEndHash = ko.computed(
() =>
this.messageListEndFolder() +
'|' +
this.messageListEndSearch() +
'|' +
this.messageListEndThreadUid() +
'|' +
this.messageListEndPage()
);
this.messageListPageCount = ko.computed(() => {
const page = window.Math.ceil(this.messageListCount() / SettingsStore.messagesPerPage());
return 0 >= page ? 1 : page;
});
this.mainMessageListSearch = ko.computed({
read: this.messageListSearch,
write: (value) => {
setHash(
mailBox(FolderStore.currentFolderFullNameHash(), 1, value.toString().trim(), this.messageListThreadUid())
);
}
});
this.messageListCompleteLoading = ko.computed(() => {
const one = this.messageListLoading(),
two = this.messageListIsNotCompleted();
return one || two;
});
this.isMessageSelected = ko.computed(() => null !== this.message());
this.messageListChecked = ko
.computed(() => this.messageList().filter(item => item.checked()))
.extend({ rateLimit: 0 });
this.hasCheckedMessages = ko.computed(() => 0 < this.messageListChecked().length).extend({ rateLimit: 0 });
this.messageListCheckedOrSelected = ko.computed(() => {
const checked = this.messageListChecked(),
selectedMessage = this.selectorMessageSelected(),
focusedMessage = this.selectorMessageFocused();
if (checked.length) {
return selectedMessage
? checked.concat([selectedMessage]).filter((value, index, self) => self.indexOf(value) == index)
: checked;
} else if (selectedMessage) {
return [selectedMessage];
}
return focusedMessage ? [focusedMessage] : [];
});
this.messageListCheckedOrSelectedUidsWithSubMails = ko.computed(() => {
let result = [];
this.messageListCheckedOrSelected().forEach(message => {
if (message) {
result.push(message.uid);
if (1 < message.threadsLen()) {
result = result.concat(message.threads()).filter((value, index, self) => self.indexOf(value) == index);
}
}
});
return result;
});
}
subscribers() {
this.messageListCompleteLoading.subscribe((value) => {
value = !!value;
this.messageListCompleteLoadingThrottle(value);
this.messageListCompleteLoadingThrottleForAnimation(value);
});
var d;
this.messageList.subscribe(
(list)=>{
// debounce
d && clearTimeout(d);
d = setTimeout(()=>list.forEach(item => {
if (item && item.newForAnimation()) {
item.newForAnimation(false);
}
}), Magics.Time500ms);
}
);
this.message.subscribe((message) => {
if (message) {
if (Layout.NoPreview === SettingsStore.layout()) {
AppStore.focusedState(Focused.MessageView);
}
} else {
AppStore.focusedState(Focused.MessageList);
this.messageFullScreenMode(false);
this.hideMessageBodies();
}
});
this.messageLoading.subscribe((value) => {
this.messageLoadingThrottle(value);
});
this.messagesBodiesDom.subscribe((dom) => {
if (dom && !(dom instanceof $)) {
this.messagesBodiesDom($(dom));
}
});
this.messageListEndFolder.subscribe((folder) => {
const message = this.message();
if (message && folder && folder !== message.folderFullNameRaw) {
this.message(null);
}
});
}
purgeMessageBodyCache() {
let count = 0;
const end = GlobalsData.iMessageBodyCacheCount - MESSAGE_BODY_CACHE_LIMIT;
if (0 < end) {
const messagesDom = this.messagesBodiesDom();
if (messagesDom) {
messagesDom.find('.rl-cache-class').each(function() {
const item = $(this); // eslint-disable-line no-invalid-this
if (end > item.data('rl-cache-count')) {
item.addClass('rl-cache-purge');
count += 1;
}
});
if (0 < count) {
setTimeout(() => messagesDom.find('.rl-cache-purge').remove(), Magics.Time350ms);
}
}
}
}
initUidNextAndNewMessages(folder, uidNext, newMessages) {
if (getFolderInboxName() === folder && isNormal(uidNext) && uidNext) {
if (Array.isArray(newMessages) && newMessages.length) {
newMessages.forEach(item => {
addNewMessageCache(folder, item.Uid);
});
NotificationStore.playSoundNotification();
const len = newMessages.length;
if (3 < len) {
NotificationStore.displayDesktopNotification(
notificationMailIcon(),
AccountStore.email(),
i18n('MESSAGE_LIST/NEW_MESSAGE_NOTIFICATION', {
'COUNT': len
}),
{ 'Folder': '', 'Uid': '' }
);
} else {
newMessages.forEach(item => {
NotificationStore.displayDesktopNotification(
notificationMailIcon(),
MessageHelper.emailArrayToString(MessageHelper.emailArrayFromJson(item.From), false),
item.Subject,
{ 'Folder': item.Folder, 'Uid': item.Uid }
);
});
}
}
setFolderUidNext(folder, uidNext);
}
}
hideMessageBodies() {
const messagesDom = this.messagesBodiesDom();
if (messagesDom) {
messagesDom.find('.b-text-part').hide();
}
}
/**
* @param {string} fromFolderFullNameRaw
* @param {Array} uidForRemove
* @param {string=} toFolderFullNameRaw = ''
* @param {boolean=} copy = false
*/
removeMessagesFromList(fromFolderFullNameRaw, uidForRemove, toFolderFullNameRaw = '', copy = false) {
uidForRemove = uidForRemove.map(mValue => pInt(mValue));
let unseenCount = 0,
messageList = this.messageList(),
currentMessage = this.message();
const trashFolder = FolderStore.trashFolder(),
spamFolder = FolderStore.spamFolder(),
fromFolder = getFolderFromCacheList(fromFolderFullNameRaw),
toFolder = toFolderFullNameRaw ? getFolderFromCacheList(toFolderFullNameRaw) : null,
currentFolderFullNameRaw = FolderStore.currentFolderFullNameRaw(),
messages =
currentFolderFullNameRaw === fromFolderFullNameRaw
? messageList.filter(item => item && uidForRemove.includes(pInt(item.uid)))
: [];
messages.forEach(item => {
if (item && item.unseen()) {
unseenCount += 1;
}
});
if (fromFolder && !copy) {
fromFolder.messageCountAll(
0 <= fromFolder.messageCountAll() - uidForRemove.length ? fromFolder.messageCountAll() - uidForRemove.length : 0
);
if (0 < unseenCount) {
fromFolder.messageCountUnread(
0 <= fromFolder.messageCountUnread() - unseenCount ? fromFolder.messageCountUnread() - unseenCount : 0
);
}
}
if (toFolder) {
if (trashFolder === toFolder.fullNameRaw || spamFolder === toFolder.fullNameRaw) {
unseenCount = 0;
}
toFolder.messageCountAll(toFolder.messageCountAll() + uidForRemove.length);
if (0 < unseenCount) {
toFolder.messageCountUnread(toFolder.messageCountUnread() + unseenCount);
}
toFolder.actionBlink(true);
}
if (messages.length) {
if (copy) {
messages.forEach(item => {
item.checked(false);
});
} else {
this.messageListIsNotCompleted(true);
messages.forEach(item => {
if (currentMessage && currentMessage.hash === item.hash) {
currentMessage = null;
this.message(null);
}
item.deleted(true);
});
setTimeout(() => {
messages.forEach(item => {
this.messageList.remove(item);
});
}, Magics.Time350ms);
}
}
if (fromFolderFullNameRaw) {
setFolderHash(fromFolderFullNameRaw, '');
}
if (toFolderFullNameRaw) {
setFolderHash(toFolderFullNameRaw, '');
}
if (this.messageListThreadUid()) {
messageList = this.messageList();
if (
messageList &&
messageList.length &&
!!messageList.find(item => !!(item && item.deleted() && item.uid === this.messageListThreadUid()))
) {
const message = messageList.find(item => item && !item.deleted());
if (message && this.messageListThreadUid() !== pString(message.uid)) {
this.messageListThreadUid(pString(message.uid));
setHash(
mailBox(
FolderStore.currentFolderFullNameHash(),
this.messageListPage(),
this.messageListSearch(),
this.messageListThreadUid()
),
true,
true
);
} else if (!message) {
if (1 < this.messageListPage()) {
this.messageListPage(this.messageListPage() - 1);
setHash(
mailBox(
FolderStore.currentFolderFullNameHash(),
this.messageListPage(),
this.messageListSearch(),
this.messageListThreadUid()
),
true,
true
);
} else {
this.messageListThreadUid('');
setHash(
mailBox(
FolderStore.currentFolderFullNameHash(),
this.messageListPageBeforeThread(),
this.messageListSearch()
),
true,
true
);
}
}
}
}
}
/**
* @param {Object} messageTextBody
*/
initBlockquoteSwitcher(messageTextBody) {
if (messageTextBody) {
const $oList = $('blockquote:not(.rl-bq-switcher)', messageTextBody).filter(function() {
return !$(this).parent().closest('blockquote', messageTextBody).length;
});
if ($oList && $oList.length) {
$oList.each(function() {
const $this = $(this); // eslint-disable-line no-invalid-this
let h = $this.height();
if (0 === h) {
h = getRealHeight($this);
}
if ($this.text().trim() && (0 === h || 100 < h)) {
$this.addClass('rl-bq-switcher hidden-bq');
$('')
.insertBefore($this)
.on('click.rlBlockquoteSwitcher', () => {
$this.toggleClass('hidden-bq');
windowResize();
})
.after('
')
.before('
');
}
});
}
}
}
/**
* @param {Object} messageTextBody
* @param {Object} message
*/
initOpenPgpControls(messageTextBody, message) {
if (messageTextBody && messageTextBody.find) {
messageTextBody.find('.b-plain-openpgp:not(.inited)').each(function() {
PgpStore.initMessageBodyControls($(this), message); // eslint-disable-line no-invalid-this
});
}
}
setMessage(data, cached) {
let isNew = false,
body = null,
id = '',
plain = '',
resultHtml = '',
pgpSigned = false,
messagesDom = this.messagesBodiesDom(),
selectedMessage = this.selectorMessageSelected(),
message = this.message();
if (
data &&
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);
if (message) {
message.threads(threads);
initMessageFlagsFromCache(message);
this.message(this.staticMessage.populateByMessageListItem(message));
message = this.message();
isNew = true;
}
}
if (message && message.uid === data.Result.Uid) {
this.messageError('');
message.initUpdateByMessageJson(data.Result);
addRequestedMessage(message.folderFullNameRaw, message.uid);
if (!cached) {
message.initFlagsByJson(data.Result);
}
messagesDom = messagesDom && messagesDom[0] ? messagesDom : null;
if (messagesDom) {
id = 'rl-mgs-' + message.hash.replace(/[^a-zA-Z0-9]/g, '');
const textBody = messagesDom.find('#' + id);
if (!textBody || !textBody[0]) {
let isHtml = false;
if (isNormal(data.Result.Html) && data.Result.Html) {
isHtml = true;
resultHtml = data.Result.Html.toString();
} else if (isNormal(data.Result.Plain) && data.Result.Plain) {
isHtml = false;
resultHtml = plainToHtml(data.Result.Plain.toString(), false);
if ((message.isPgpSigned() || message.isPgpEncrypted()) && PgpStore.capaOpenPGP()) {
plain = pString(data.Result.Plain);
const isPgpEncrypted = /---BEGIN PGP MESSAGE---/.test(plain);
if (!isPgpEncrypted) {
pgpSigned =
/-----BEGIN PGP SIGNED MESSAGE-----/.test(plain) && /-----BEGIN PGP SIGNATURE-----/.test(plain);
}
$div.empty();
if (pgpSigned && message.isPgpSigned()) {
resultHtml = $div.append($('
' + resultHtml + ''; } $div.empty(); message.isPgpSigned(pgpSigned); message.isPgpEncrypted(isPgpEncrypted); } else { resultHtml = '
' + resultHtml + ''; } } else { isHtml = false; resultHtml = '
' + resultHtml + ''; } GlobalsData.iMessageBodyCacheCount += 1; body = $('') .hide() .addClass('rl-cache-class'); body.data('rl-cache-count', GlobalsData.iMessageBodyCacheCount); body.html(findEmailAndLinks(resultHtml)).addClass('b-text-part ' + (isHtml ? 'html' : 'plain')); message.isHtml(!!isHtml); message.hasImages(!!data.Result.HasExternals); message.body = body; if (message.body) { messagesDom.append(message.body); } message.storeDataInDom(); if (data.Result.HasInternals) { message.showInternalImages(true); } if (message.hasImages() && SettingsStore.showImages()) { message.showExternalImages(true); } this.purgeMessageBodyCacheThrottle(); } else { message.body = textBody; if (message.body) { GlobalsData.iMessageBodyCacheCount += 1; message.body.data('rl-cache-count', GlobalsData.iMessageBodyCacheCount); message.fetchDataFromDom(); } } this.messageActiveDom(message.body); this.hideMessageBodies(); if (body) { this.initOpenPgpControls(body, message); this.initBlockquoteSwitcher(body); } message.body.show(); } initMessageFlagsFromCache(message); if (message.unseen() || message.hasUnseenSubMessage()) { getApp().messageListAction(message.folderFullNameRaw, MessageSetAction.SetSeen, [message]); } if (isNew) { message = this.message(); if ( selectedMessage && message && (message.folderFullNameRaw !== selectedMessage.folderFullNameRaw || message.uid !== selectedMessage.uid) ) { this.selectorMessageSelected(null); if (1 === this.messageList().length) { this.selectorMessageFocused(null); } } else if (!selectedMessage && message) { selectedMessage = this.messageList().find( subMessage => subMessage && subMessage.folderFullNameRaw === message.folderFullNameRaw && subMessage.uid === message.uid ); if (selectedMessage) { this.selectorMessageSelected(selectedMessage); this.selectorMessageFocused(selectedMessage); } } } windowResize(); } } } selectMessage(oMessage) { if (oMessage) { this.message(this.staticMessage.populateByMessageListItem(oMessage)); this.populateMessageBody(this.message()); } else { this.message(null); } } selectMessageByFolderAndUid(sFolder, sUid) { if (sFolder && sUid) { this.message(this.staticMessage.populateByMessageListItem(null)); this.message().folderFullNameRaw = sFolder; this.message().uid = sUid; this.populateMessageBody(this.message()); } else { this.message(null); } } populateMessageBody(oMessage) { if (oMessage) { if (Remote.message(this.onMessageResponse, oMessage.folderFullNameRaw, oMessage.uid)) { this.messageCurrentLoading(true); } } } /** * @param {string} sResult * @param {AjaxJsonDefaultResponse} oData * @param {boolean} bCached */ onMessageResponse(sResult, oData, bCached) { this.hideMessageBodies(); this.messageCurrentLoading(false); if (StorageResultType.Success === sResult && oData && oData.Result) { this.setMessage(oData, bCached); } else if (StorageResultType.Unload === sResult) { this.message(null); this.messageError(''); } else if (StorageResultType.Abort !== sResult) { this.message(null); this.messageError( oData && oData.ErrorCode ? getNotification(oData.ErrorCode) : getNotification(Notification.UnknownError) ); } } /** * @param {Array} list * @returns {string} */ calculateMessageListHash(list) { return list.map(message => '' + message.hash + '_' + message.threadsLen() + '_' + message.flagHash()).join( '|' ); } setMessageList(data, cached) { if ( data && data.Result && 'Collection/MessageCollection' === data.Result['@Object'] && data.Result['@Collection'] && Array.isArray(data.Result['@Collection']) ) { let newCount = 0, unreadCountChange = false; const list = [], utc = momentNowUnix(), iCount = pInt(data.Result.MessageResultCount), iOffset = pInt(data.Result.Offset); const folder = getFolderFromCacheList(isNormal(data.Result.Folder) ? data.Result.Folder : ''); if (folder && !cached) { folder.interval = utc; setFolderHash(data.Result.Folder, data.Result.FolderHash); if (isNormal(data.Result.MessageCount)) { folder.messageCountAll(data.Result.MessageCount); } if (isNormal(data.Result.MessageUnseenCount)) { if (pInt(folder.messageCountUnread()) !== pInt(data.Result.MessageUnseenCount)) { unreadCountChange = true; } folder.messageCountUnread(data.Result.MessageUnseenCount); } this.initUidNextAndNewMessages(folder.fullNameRaw, data.Result.UidNext, data.Result.NewMessages); } if (unreadCountChange && folder) { clearMessageFlagsFromCacheByFolder(folder.fullNameRaw); } data.Result['@Collection'].forEach(jsonMessage => { if (jsonMessage && 'Object/Message' === jsonMessage['@Object']) { const message = MessageModel.newInstanceFromJson(jsonMessage); if (message) { if (hasNewMessageAndRemoveFromCache(message.folderFullNameRaw, message.uid) && 5 >= newCount) { newCount += 1; message.newForAnimation(true); } message.deleted(false); if (cached) { initMessageFlagsFromCache(message); } else { storeMessageFlagsToCache(message); } list.push(message); } } }); this.messageListCount(iCount); this.messageListSearch(isNormal(data.Result.Search) ? data.Result.Search : ''); this.messageListPage(window.Math.ceil(iOffset / SettingsStore.messagesPerPage() + 1)); this.messageListThreadUid(isNormal(data.Result.ThreadUid) ? pString(data.Result.ThreadUid) : ''); this.messageListEndFolder(isNormal(data.Result.Folder) ? data.Result.Folder : ''); this.messageListEndSearch(this.messageListSearch()); this.messageListEndThreadUid(this.messageListThreadUid()); this.messageListEndPage(this.messageListPage()); this.messageListDisableAutoSelect(true); this.messageList(list); this.messageListIsNotCompleted(false); clearNewMessageCache(); if (folder && (cached || unreadCountChange || SettingsStore.useThreads())) { getApp().folderInformation(folder.fullNameRaw, list); } } else { this.messageListCount(0); this.messageList([]); this.messageListError(getNotification(data && data.ErrorCode ? data.ErrorCode : Notification.CantGetMessageList)); } } } export default new MessageUserStore();