diff --git a/dev/App/User.js b/dev/App/User.js index 2e0892ea6..075deffb7 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -29,16 +29,16 @@ TemplateStore = require('Stores/User/Template'), FolderStore = require('Stores/User/Folder'), PgpStore = require('Stores/User/Pgp'), + MessageStore = require('Stores/User/Message'), + ContactStore = require('Stores/User/Contact'), Local = require('Storage/Client'), Settings = require('Storage/Settings'), - Data = require('Storage/User/Data'), Cache = require('Storage/User/Cache'), Remote = require('Storage/User/Remote'), EmailModel = require('Model/Email'), FolderModel = require('Model/Folder'), - MessageModel = require('Model/Message'), AccountModel = require('Model/Account'), IdentityModel = require('Model/Identity'), TemplateModel = require('Model/Template'), @@ -146,11 +146,11 @@ AppUser.prototype.reloadFlagsCurrentMessageListAndMessageFromCache = function () { - _.each(Data.messageList(), function (oMessage) { + _.each(MessageStore.messageList(), function (oMessage) { Cache.initMessageFlagsFromCache(oMessage); }); - Cache.initMessageFlagsFromCache(Data.message()); + Cache.initMessageFlagsFromCache(MessageStore.message()); }; /** @@ -160,45 +160,45 @@ AppUser.prototype.reloadMessageList = function (bDropPagePosition, bDropCurrenFolderCache) { var - self = this, - iOffset = (Data.messageListPage() - 1) * SettingsStore.messagesPerPage() + iOffset = (MessageStore.messageListPage() - 1) * SettingsStore.messagesPerPage() ; if (Utils.isUnd(bDropCurrenFolderCache) ? false : !!bDropCurrenFolderCache) { - Cache.setFolderHash(Data.currentFolderFullNameRaw(), ''); + Cache.setFolderHash(FolderStore.currentFolderFullNameRaw(), ''); } if (Utils.isUnd(bDropPagePosition) ? false : !!bDropPagePosition) { - Data.messageListPage(1); + MessageStore.messageListPage(1); iOffset = 0; } - Data.messageListLoading(true); + MessageStore.messageListLoading(true); Remote.messageList(function (sResult, oData, bCached) { if (Enums.StorageResultType.Success === sResult && oData && oData.Result) { - Data.messageListError(''); - Data.messageListLoading(false); - self.setMessageList(oData, bCached); + MessageStore.messageListError(''); + MessageStore.messageListLoading(false); + + MessageStore.setMessageList(oData, bCached); } else if (Enums.StorageResultType.Unload === sResult) { - Data.messageListError(''); - Data.messageListLoading(false); + MessageStore.messageListError(''); + MessageStore.messageListLoading(false); } else if (Enums.StorageResultType.Abort !== sResult) { - Data.messageList([]); - Data.messageListLoading(false); - Data.messageListError(oData && oData.ErrorCode ? + MessageStore.messageList([]); + MessageStore.messageListLoading(false); + MessageStore.messageListError(oData && oData.ErrorCode ? Translator.getNotification(oData.ErrorCode) : Translator.i18n('NOTIFICATIONS/CANT_GET_MESSAGE_LIST') ); } - }, Data.currentFolderFullNameRaw(), iOffset, SettingsStore.messagesPerPage(), Data.messageListSearch()); + }, FolderStore.currentFolderFullNameRaw(), iOffset, SettingsStore.messagesPerPage(), MessageStore.messageListSearch()); }; AppUser.prototype.recacheInboxMessageList = function () @@ -217,8 +217,8 @@ */ AppUser.prototype.contactsSync = function (fResultFunc) { - var oContacts = Data.contacts; - if (oContacts.importing() || oContacts.syncing() || !Data.enableContactsSync() || !Data.allowContactsSync()) + var oContacts = ContactStore.contacts; + if (oContacts.importing() || oContacts.syncing() || !ContactStore.enableContactsSync() || !ContactStore.allowContactsSync()) { return false; } @@ -296,7 +296,7 @@ AppUser.prototype.moveOrDeleteResponseHelper = function (sResult, oData) { - if (Enums.StorageResultType.Success === sResult && Data.currentFolder()) + if (Enums.StorageResultType.Success === sResult && FolderStore.currentFolder()) { if (oData && Utils.isArray(oData.Result) && 2 === oData.Result.length) { @@ -304,7 +304,7 @@ } else { - Cache.setFolderHash(Data.currentFolderFullNameRaw(), ''); + Cache.setFolderHash(FolderStore.currentFolderFullNameRaw(), ''); if (oData && -1 < Utils.inArray(oData.ErrorCode, [Enums.Notification.CantMoveMessage, Enums.Notification.CantCopyMessage])) @@ -313,7 +313,7 @@ } } - this.reloadMessageListHelper(0 === Data.messageList().length); + this.reloadMessageListHelper(0 === MessageStore.messageList().length); this.quotaDebounce(); } }; @@ -325,7 +325,7 @@ AppUser.prototype.deleteMessagesFromFolderWithoutCheck = function (sFromFolderFullNameRaw, aUidForRemove) { this.messagesDeleteHelper(sFromFolderFullNameRaw, aUidForRemove); - Data.removeMessagesFromList(sFromFolderFullNameRaw, aUidForRemove); + MessageStore.removeMessagesFromList(sFromFolderFullNameRaw, aUidForRemove); }; /** @@ -382,14 +382,14 @@ kn.showScreenPopup(require('View/Popup/Ask'), [Translator.i18n('POPUPS_ASK/DESC_WANT_DELETE_MESSAGES'), function () { self.messagesDeleteHelper(sFromFolderFullNameRaw, aUidForRemove); - Data.removeMessagesFromList(sFromFolderFullNameRaw, aUidForRemove); + MessageStore.removeMessagesFromList(sFromFolderFullNameRaw, aUidForRemove); }]); } else if (oMoveFolder) { this.messagesMoveHelper(sFromFolderFullNameRaw, oMoveFolder.fullNameRaw, aUidForRemove); - Data.removeMessagesFromList(sFromFolderFullNameRaw, aUidForRemove, oMoveFolder.fullNameRaw); + MessageStore.removeMessagesFromList(sFromFolderFullNameRaw, aUidForRemove, oMoveFolder.fullNameRaw); } }; @@ -419,7 +419,7 @@ this.messagesMoveHelper(oFromFolder.fullNameRaw, oToFolder.fullNameRaw, aUidForMove); } - Data.removeMessagesFromList(oFromFolder.fullNameRaw, aUidForMove, oToFolder.fullNameRaw, bCopy); + MessageStore.removeMessagesFromList(oFromFolder.fullNameRaw, aUidForMove, oToFolder.fullNameRaw, bCopy); return true; } } @@ -432,12 +432,12 @@ */ AppUser.prototype.folders = function (fCallback) { - Data.foldersLoading(true); + FolderStore.foldersLoading(true); Remote.folders(_.bind(function (sResult, oData) { var bResult = false; - Data.foldersLoading(false); + FolderStore.foldersLoading(false); if (Enums.StorageResultType.Success === sResult && oData && oData.Result) { @@ -707,11 +707,11 @@ } } - Data.initUidNextAndNewMessages(oFolder.fullNameRaw, oData.Result.UidNext, oData.Result.NewMessages); + MessageStore.initUidNextAndNewMessages(oFolder.fullNameRaw, oData.Result.UidNext, oData.Result.NewMessages); if (oData.Result.Hash !== sHash || '' === sHash) { - if (oFolder.fullNameRaw === Data.currentFolderFullNameRaw()) + if (oFolder.fullNameRaw === FolderStore.currentFolderFullNameRaw()) { self.reloadMessageList(); } @@ -722,9 +722,9 @@ } else if (bUnreadCountChange) { - if (oFolder.fullNameRaw === Data.currentFolderFullNameRaw()) + if (oFolder.fullNameRaw === FolderStore.currentFolderFullNameRaw()) { - aList = Data.messageList(); + aList = MessageStore.messageList(); if (Utils.isNonEmptyArray(aList)) { self.folderInformation(oFolder.fullNameRaw, aList); @@ -748,7 +748,7 @@ var self = this, iUtc = moment().unix(), - aFolders = Data.getNextFolderNames(bBoot) + aFolders = FolderStore.getNextFolderNames(bBoot) ; if (Utils.isNonEmptyArray(aFolders)) @@ -798,16 +798,16 @@ if (oItem.Hash !== sHash || '' === sHash) { - if (oFolder.fullNameRaw === Data.currentFolderFullNameRaw()) + if (oFolder.fullNameRaw === FolderStore.currentFolderFullNameRaw()) { self.reloadMessageList(); } } else if (bUnreadCountChange) { - if (oFolder.fullNameRaw === Data.currentFolderFullNameRaw()) + if (oFolder.fullNameRaw === FolderStore.currentFolderFullNameRaw()) { - aList = Data.messageList(); + aList = MessageStore.messageList(); if (Utils.isNonEmptyArray(aList)) { self.folderInformation(oFolder.fullNameRaw, aList); @@ -941,136 +941,6 @@ }, sQuery); }; - AppUser.prototype.setMessageList = function (oData, bCached) - { - if (oData && oData.Result && 'Collection/MessageCollection' === oData.Result['@Object'] && - oData.Result['@Collection'] && Utils.isArray(oData.Result['@Collection'])) - { - var - mLastCollapsedThreadUids = null, - iIndex = 0, - iLen = 0, - iCount = 0, - iOffset = 0, - aList = [], - iUtc = moment().unix(), - aStaticList = Data.staticMessageList, - oJsonMessage = null, - oMessage = null, - oFolder = null, - iNewCount = 0, - bUnreadCountChange = false - ; - - iCount = Utils.pInt(oData.Result.MessageResultCount); - iOffset = Utils.pInt(oData.Result.Offset); - - if (Utils.isNonEmptyArray(oData.Result.LastCollapsedThreadUids)) - { - mLastCollapsedThreadUids = oData.Result.LastCollapsedThreadUids; - } - - oFolder = Cache.getFolderFromCacheList( - Utils.isNormal(oData.Result.Folder) ? oData.Result.Folder : ''); - - if (oFolder && !bCached) - { - oFolder.interval = iUtc; - - Cache.setFolderHash(oData.Result.Folder, oData.Result.FolderHash); - - if (Utils.isNormal(oData.Result.MessageCount)) - { - oFolder.messageCountAll(oData.Result.MessageCount); - } - - if (Utils.isNormal(oData.Result.MessageUnseenCount)) - { - if (Utils.pInt(oFolder.messageCountUnread()) !== Utils.pInt(oData.Result.MessageUnseenCount)) - { - bUnreadCountChange = true; - } - - oFolder.messageCountUnread(oData.Result.MessageUnseenCount); - } - - Data.initUidNextAndNewMessages(oFolder.fullNameRaw, oData.Result.UidNext, oData.Result.NewMessages); - } - - if (bUnreadCountChange && oFolder) - { - Cache.clearMessageFlagsFromCacheByFolder(oFolder.fullNameRaw); - } - - for (iIndex = 0, iLen = oData.Result['@Collection'].length; iIndex < iLen; iIndex++) - { - oJsonMessage = oData.Result['@Collection'][iIndex]; - if (oJsonMessage && 'Object/Message' === oJsonMessage['@Object']) - { - oMessage = aStaticList[iIndex]; - if (!oMessage || !oMessage.initByJson(oJsonMessage)) - { - oMessage = MessageModel.newInstanceFromJson(oJsonMessage); - } - - if (oMessage) - { - if (Cache.hasNewMessageAndRemoveFromCache(oMessage.folderFullNameRaw, oMessage.uid) && 5 >= iNewCount) - { - iNewCount++; - oMessage.newForAnimation(true); - } - - oMessage.deleted(false); - - if (bCached) - { - Cache.initMessageFlagsFromCache(oMessage); - } - else - { - Cache.storeMessageFlagsToCache(oMessage); - } - - oMessage.lastInCollapsedThread(mLastCollapsedThreadUids && -1 < Utils.inArray(Utils.pInt(oMessage.uid), mLastCollapsedThreadUids) ? true : false); - - aList.push(oMessage); - } - } - } - - Data.messageListCount(iCount); - Data.messageListSearch(Utils.isNormal(oData.Result.Search) ? oData.Result.Search : ''); - Data.messageListPage(window.Math.ceil((iOffset / SettingsStore.messagesPerPage()) + 1)); - Data.messageListEndFolder(Utils.isNormal(oData.Result.Folder) ? oData.Result.Folder : ''); - Data.messageListEndSearch(Utils.isNormal(oData.Result.Search) ? oData.Result.Search : ''); - Data.messageListEndPage(Data.messageListPage()); - - Data.messageList(aList); - Data.messageListIsNotCompleted(false); - - if (aStaticList.length < aList.length) - { - Data.staticMessageList = aList; - } - - Cache.clearNewMessageCache(); - - if (oFolder && (bCached || bUnreadCountChange || SettingsStore.useThreads())) - { - this.folderInformation(oFolder.fullNameRaw, aList); - } - } - else - { - Data.messageListCount(0); - Data.messageList([]); - Data.messageListError(Translator.getNotification( - oData && oData.ErrorCode ? oData.ErrorCode : Enums.Notification.CantGetMessageList - )); - } - }; - /** * @param {string} sNamespace * @param {Array} aFolders @@ -1163,13 +1033,13 @@ { if (!Utils.isUnd(oData.Result.Namespace)) { - Data.namespace = oData.Result.Namespace; + FolderStore.namespace = oData.Result.Namespace; } AppStore.threadsAllowed(!!Settings.settingsGet('UseImapThread') && oData.Result.IsThreadsSupported && true); - Data.folderList(this.folderResponseParseRec(Data.namespace, oData.Result['@Collection'])); + FolderStore.folderList(this.folderResponseParseRec(FolderStore.namespace, oData.Result['@Collection'])); if (oData.Result['SystemFolders'] && '' === '' + Settings.settingsGet('SentFolder') + @@ -1525,7 +1395,7 @@ }); Events.sub('interval.3m', function () { - var sF = Data.currentFolderFullNameRaw(); + var sF = FolderStore.currentFolderFullNameRaw(); if (Cache.getFolderInboxName() !== sF) { self.folderInformation(sF); @@ -1562,7 +1432,7 @@ self.accountsAndIdentities(true); _.delay(function () { - var sF = Data.currentFolderFullNameRaw(); + var sF = FolderStore.currentFolderFullNameRaw(); if (Cache.getFolderInboxName() !== sF) { self.folderInformation(sF); diff --git a/dev/Model/Message.js b/dev/Model/Message.js index 30a01c860..e1f960f5a 100644 --- a/dev/Model/Message.js +++ b/dev/Model/Message.js @@ -1205,9 +1205,8 @@ var aRes = [], mPgpMessage = null, - Data = require('Storage/User/Data'), sFrom = this.from && this.from[0] && this.from[0].email ? this.from[0].email : '', - aPublicKeys = Data.findPublicKeysByEmail(sFrom), + aPublicKeys = PgpStore.findPublicKeysByEmail(sFrom), oValidKey = null, oValidSysKey = null, sPlain = '' @@ -1233,7 +1232,7 @@ if (oValidKey) { - oValidSysKey = Data.findPublicKeyByHex(oValidKey.keyid.toHex()); + oValidSysKey = PgpStore.findPublicKeyByHex(oValidKey.keyid.toHex()); if (oValidSysKey) { sPlain = mPgpMessage.getText(); @@ -1269,10 +1268,9 @@ aRes = [], mPgpMessage = null, mPgpMessageDecrypted = null, - Data = require('Storage/User/Data'), sFrom = this.from && this.from[0] && this.from[0].email ? this.from[0].email : '', - aPublicKey = Data.findPublicKeysByEmail(sFrom), - oPrivateKey = Data.findSelfPrivateKey(sPassword), + aPublicKey = PgpStore.findPublicKeysByEmail(sFrom), + oPrivateKey = PgpStore.findSelfPrivateKey(sPassword), oValidKey = null, oValidSysKey = null, sPlain = '' @@ -1305,7 +1303,7 @@ if (oValidKey) { - oValidSysKey = Data.findPublicKeyByHex(oValidKey.keyid.toHex()); + oValidSysKey = PgpStore.findPublicKeyByHex(oValidKey.keyid.toHex()); if (oValidSysKey) { this.pgpSignedVerifyStatus(Enums.SignedVerifyStatus.Success); diff --git a/dev/Screen/User/MailBox.js b/dev/Screen/User/MailBox.js index 7a8d073c5..f690b1446 100644 --- a/dev/Screen/User/MailBox.js +++ b/dev/Screen/User/MailBox.js @@ -14,8 +14,9 @@ AccountStore = require('Stores/User/Account'), SettingsStore = require('Stores/User/Settings'), + FolderStore = require('Stores/User/Folder'), + MessageStore = require('Stores/User/Message'), - Data = require('Storage/User/Data'), Cache = require('Storage/User/Cache'), AbstractScreen = require('Knoin/AbstractScreen') @@ -48,7 +49,7 @@ { var sEmail = AccountStore.email(), - nFoldersInboxUnreadCount = Data.foldersInboxUnreadCount() + nFoldersInboxUnreadCount = FolderStore.foldersInboxUnreadCount() ; require('App/User').setTitle(('' === sEmail ? '' : @@ -72,7 +73,7 @@ { if (Utils.isUnd(bPreview) ? false : !!bPreview) { - if (Enums.Layout.NoPreview === SettingsStore.layout() && !Data.message()) + if (Enums.Layout.NoPreview === SettingsStore.layout() && !MessageStore.message()) { require('App/User').historyBack(); } @@ -86,15 +87,13 @@ if (oFolder) { - Data - .currentFolder(oFolder) - .messageListPage(iPage) - .messageListSearch(sSearch) - ; + FolderStore.currentFolder(oFolder); + MessageStore.messageListPage(iPage); + MessageStore.messageListSearch(sSearch); - if (Enums.Layout.NoPreview === SettingsStore.layout() && Data.message()) + if (Enums.Layout.NoPreview === SettingsStore.layout() && MessageStore.message()) { - Data.message(null); + MessageStore.message(null); } require('App/User').reloadMessageList(); @@ -104,9 +103,10 @@ MailBoxUserScreen.prototype.onStart = function () { - Data.folderList.subscribe(Utils.windowResizeCallback); - Data.messageList.subscribe(Utils.windowResizeCallback); - Data.message.subscribe(Utils.windowResizeCallback); + FolderStore.folderList.subscribe(Utils.windowResizeCallback); + + MessageStore.messageList.subscribe(Utils.windowResizeCallback); + MessageStore.message.subscribe(Utils.windowResizeCallback); _.delay(function () { SettingsStore.layout.valueHasMutated(); @@ -114,7 +114,7 @@ Events.sub('mailbox.inbox-unread-count', function (iCount) { - Data.foldersInboxUnreadCount(iCount); + FolderStore.foldersInboxUnreadCount(iCount); var sEmail = AccountStore.email(); @@ -126,7 +126,7 @@ }); }); - Data.foldersInboxUnreadCount.subscribe(function () { + FolderStore.foldersInboxUnreadCount.subscribe(function () { this.setNewTitle(); }, this); diff --git a/dev/Settings/User/Contacts.js b/dev/Settings/User/Contacts.js index 523a5deb2..35b6d51e4 100644 --- a/dev/Settings/User/Contacts.js +++ b/dev/Settings/User/Contacts.js @@ -7,9 +7,9 @@ ko = require('ko'), AppStore = require('Stores/User/App'), + ContactStore = require('Stores/User/Contact'), - Remote = require('Storage/User/Remote'), - Data = require('Storage/User/Data') + Remote = require('Storage/User/Remote') ; /** @@ -19,11 +19,11 @@ { this.contactsAutosave = AppStore.contactsAutosave; - this.allowContactsSync = Data.allowContactsSync; - this.enableContactsSync = Data.enableContactsSync; - this.contactsSyncUrl = Data.contactsSyncUrl; - this.contactsSyncUser = Data.contactsSyncUser; - this.contactsSyncPass = Data.contactsSyncPass; + this.allowContactsSync = ContactStore.allowContactsSync; + this.enableContactsSync = ContactStore.enableContactsSync; + this.contactsSyncUrl = ContactStore.contactsSyncUrl; + this.contactsSyncUser = ContactStore.contactsSyncUser; + this.contactsSyncPass = ContactStore.contactsSyncPass; this.saveTrigger = ko.computed(function () { return [ diff --git a/dev/Settings/User/Folders.js b/dev/Settings/User/Folders.js index 7a131ba08..bcebce92f 100644 --- a/dev/Settings/User/Folders.js +++ b/dev/Settings/User/Folders.js @@ -10,8 +10,9 @@ Utils = require('Common/Utils'), Translator = require('Common/Translator'), + FolderStore = require('Stores/User/Folder'), + Settings = require('Storage/Settings'), - Data = require('Storage/User/Data'), Cache = require('Storage/User/Cache'), Remote = require('Storage/User/Remote'), Local = require('Storage/Client') @@ -22,15 +23,15 @@ */ function FoldersUserSettings() { - this.folderList = Data.folderList; + this.folderList = FolderStore.folderList; this.loading = ko.computed(function () { var - bLoading = Data.foldersLoading(), - bCreating = Data.foldersCreating(), - bDeleting = Data.foldersDeleting(), - bRenaming = Data.foldersRenaming() + bLoading = FolderStore.foldersLoading(), + bCreating = FolderStore.foldersCreating(), + bDeleting = FolderStore.foldersDeleting(), + bRenaming = FolderStore.foldersRenaming() ; return bLoading || bCreating || bDeleting || bRenaming; @@ -66,13 +67,13 @@ { Local.set(Enums.ClientSideKeyName.FoldersLashHash, ''); - Data.foldersRenaming(true); + FolderStore.foldersRenaming(true); Remote.folderRename(function (sResult, oData) { - Data.foldersRenaming(false); + FolderStore.foldersRenaming(false); if (Enums.StorageResultType.Success !== sResult || !oData || !oData.Result) { - Data.folderList.error( + FolderStore.folderList.error( oData && oData.ErrorCode ? Translator.getNotification(oData.ErrorCode) : Translator.i18n('NOTIFICATIONS/CANT_RENAME_FOLDER')); } @@ -98,7 +99,7 @@ FoldersUserSettings.prototype.onShow = function () { - Data.folderList.error(''); + FolderStore.folderList.error(''); }; FoldersUserSettings.prototype.createFolder = function () @@ -135,15 +136,15 @@ { Local.set(Enums.ClientSideKeyName.FoldersLashHash, ''); - Data.folderList.remove(fRemoveFolder); + FolderStore.folderList.remove(fRemoveFolder); - Data.foldersDeleting(true); + FolderStore.foldersDeleting(true); Remote.folderDelete(function (sResult, oData) { - Data.foldersDeleting(false); + FolderStore.foldersDeleting(false); if (Enums.StorageResultType.Success !== sResult || !oData || !oData.Result) { - Data.folderList.error( + FolderStore.folderList.error( oData && oData.ErrorCode ? Translator.getNotification(oData.ErrorCode) : Translator.i18n('NOTIFICATIONS/CANT_DELETE_FOLDER')); } @@ -156,7 +157,7 @@ } else if (0 < oFolderToRemove.privateMessageCountAll()) { - Data.folderList.error(Translator.getNotification(Enums.Notification.CantDeleteNonEmptyFolder)); + FolderStore.folderList.error(Translator.getNotification(Enums.Notification.CantDeleteNonEmptyFolder)); } }; diff --git a/dev/Settings/User/General.js b/dev/Settings/User/General.js index 9739a2679..345b5acce 100644 --- a/dev/Settings/User/General.js +++ b/dev/Settings/User/General.js @@ -17,8 +17,8 @@ LanguageStore = require('Stores/Language'), SettingsStore = require('Stores/User/Settings'), NotificationStore = require('Stores/User/Notification'), + MessageStore = require('Stores/User/Message'), - Data = require('Storage/User/Data'), Remote = require('Storage/User/Remote') ; @@ -163,7 +163,7 @@ self.useThreads.subscribe(function (bValue) { - Data.messageList([]); + MessageStore.messageList([]); Remote.saveSettings(null, { 'UseThreads': bValue ? '1' : '0' @@ -172,7 +172,7 @@ self.layout.subscribe(function (nValue) { - Data.messageList([]); + MessageStore.messageList([]); Remote.saveSettings(f2, { 'Layout': nValue diff --git a/dev/Storage/User/Data.js b/dev/Storage/User/Data.js index 363836b68..61f21970a 100644 --- a/dev/Storage/User/Data.js +++ b/dev/Storage/User/Data.js @@ -3,894 +3,14 @@ 'use strict'; - var - window = require('window'), - _ = require('_'), - $ = require('$'), - ko = require('ko'), - moment = require('moment'), - - Consts = require('Common/Consts'), - Enums = require('Common/Enums'), - Globals = require('Common/Globals'), - Utils = require('Common/Utils'), - Links = require('Common/Links'), - Translator = require('Common/Translator'), - - SettingsStore = require('Stores/User/Settings'), - - Settings = require('Storage/Settings'), - Cache = require('Storage/User/Cache'), - - kn = require('Knoin/Knoin'), - - MessageModel = require('Model/Message') - ; - /** * @constructor */ function DataUserStorage() { - // contacts - this.contacts = ko.observableArray([]); - this.contacts.loading = ko.observable(false).extend({'throttle': 200}); - this.contacts.importing = ko.observable(false).extend({'throttle': 200}); - this.contacts.syncing = ko.observable(false).extend({'throttle': 200}); - this.contacts.exportingVcf = ko.observable(false).extend({'throttle': 200}); - this.contacts.exportingCsv = ko.observable(false).extend({'throttle': 200}); - this.allowContactsSync = ko.observable(false); - this.enableContactsSync = ko.observable(false); - this.contactsSyncUrl = ko.observable(''); - this.contactsSyncUser = ko.observable(''); - this.contactsSyncPass = ko.observable(''); - - this.allowContactsSync = ko.observable(!!Settings.settingsGet('ContactsSyncIsAllowed')); - this.enableContactsSync = ko.observable(!!Settings.settingsGet('EnableContactsSync')); - this.contactsSyncUrl = ko.observable(Settings.settingsGet('ContactsSyncUrl')); - this.contactsSyncUser = ko.observable(Settings.settingsGet('ContactsSyncUser')); - this.contactsSyncPass = ko.observable(Settings.settingsGet('ContactsSyncPassword')); - - // folders - this.namespace = ''; - this.folderList = ko.observableArray([]); - this.folderList.focused = ko.observable(false); - this.folderList.optimized = ko.observable(false); - this.folderList.error = ko.observable(''); - - this.foldersLoading = ko.observable(false); - this.foldersCreating = ko.observable(false); - this.foldersDeleting = ko.observable(false); - this.foldersRenaming = ko.observable(false); - - this.foldersListWithSingleInboxRootFolder = ko.computed(function () { - var aList = this.folderList(); - return !_.find(aList, function (oFolder) { - return oFolder && !oFolder.isSystemFolder() && oFolder.visible(); - }); - }, this); - - this.foldersChanging = ko.computed(function () { - var - bLoading = this.foldersLoading(), - bCreating = this.foldersCreating(), - bDeleting = this.foldersDeleting(), - bRenaming = this.foldersRenaming() - ; - return bLoading || bCreating || bDeleting || bRenaming; - }, this); - - this.foldersInboxUnreadCount = ko.observable(0); - - this.currentFolder = ko.observable(null).extend({'toggleSubscribe': [null, - function (oPrev) { - if (oPrev) - { - oPrev.selected(false); - } - }, function (oNext) { - if (oNext) - { - oNext.selected(true); - } - } - ]}); - - this.currentFolderFullNameRaw = ko.computed(function () { - return this.currentFolder() ? this.currentFolder().fullNameRaw : ''; - }, this); - - this.currentFolderFullName = ko.computed(function () { - return this.currentFolder() ? this.currentFolder().fullName : ''; - }, this); - - this.currentFolderFullNameHash = ko.computed(function () { - return this.currentFolder() ? this.currentFolder().fullNameHash : ''; - }, this); - - this.currentFolderName = ko.computed(function () { - return this.currentFolder() ? this.currentFolder().name() : ''; - }, this); - - this.folderListSystemNames = ko.computed(function () { - - var - FolderStore = require('Stores/User/Folder'), - - aList = [Cache.getFolderInboxName()], - aFolders = this.folderList(), - sSentFolder = FolderStore.sentFolder(), - sDraftFolder = FolderStore.draftFolder(), - sSpamFolder = FolderStore.spamFolder(), - sTrashFolder = FolderStore.trashFolder(), - sArchiveFolder = FolderStore.archiveFolder() - ; - - if (Utils.isArray(aFolders) && 0 < aFolders.length) - { - if ('' !== sSentFolder && Consts.Values.UnuseOptionValue !== sSentFolder) - { - aList.push(sSentFolder); - } - if ('' !== sDraftFolder && Consts.Values.UnuseOptionValue !== sDraftFolder) - { - aList.push(sDraftFolder); - } - if ('' !== sSpamFolder && Consts.Values.UnuseOptionValue !== sSpamFolder) - { - aList.push(sSpamFolder); - } - if ('' !== sTrashFolder && Consts.Values.UnuseOptionValue !== sTrashFolder) - { - aList.push(sTrashFolder); - } - if ('' !== sArchiveFolder && Consts.Values.UnuseOptionValue !== sArchiveFolder) - { - aList.push(sArchiveFolder); - } - } - - return aList; - - }, this); - - this.folderListSystem = ko.computed(function () { - return _.compact(_.map(this.folderListSystemNames(), function (sName) { - return Cache.getFolderFromCacheList(sName); - })); - }, this); - - this.folderMenuForMove = ko.computed(function () { - return Utils.folderListOptionsBuilder(this.folderListSystem(), this.folderList(), [ - this.currentFolderFullNameRaw() - ], null, null, null, null, function (oItem) { - return oItem ? oItem.localName() : ''; - }); - }, this); - - this.folderMenuForFilters = ko.computed(function () { - return Utils.folderListOptionsBuilder(this.folderListSystem(), this.folderList(), - ['INBOX'], [['', '']], null, null, null, function (oItem) { - return oItem ? oItem.localName() : ''; - } - ); - }, this); - - // message list - this.staticMessageList = []; - - this.messageList = ko.observableArray([]).extend({'rateLimit': 0}); - - this.messageListCount = ko.observable(0); - this.messageListSearch = ko.observable(''); - this.messageListPage = ko.observable(1); - - this.messageListThreadFolder = ko.observable(''); - this.messageListThreadUids = ko.observableArray([]); - - this.messageListThreadFolder.subscribe(function () { - this.messageListThreadUids([]); - }, this); - - this.messageListEndFolder = ko.observable(''); - this.messageListEndSearch = ko.observable(''); - this.messageListEndPage = ko.observable(1); - - this.messageListEndHash = ko.computed(function () { - return this.messageListEndFolder() + '|' + this.messageListEndSearch() + '|' + this.messageListEndPage(); - }, this); - - this.messageListPageCount = ko.computed(function () { - var iPage = window.Math.ceil(this.messageListCount() / - SettingsStore.messagesPerPage()); - return 0 >= iPage ? 1 : iPage; - }, this); - - this.mainMessageListSearch = ko.computed({ - 'read': this.messageListSearch, - 'write': function (sValue) { - kn.setHash(Links.mailBox( - this.currentFolderFullNameHash(), 1, Utils.trim(sValue.toString()) - )); - }, - 'owner': this - }); - - this.messageListError = ko.observable(''); - - this.messageListLoading = ko.observable(false); - this.messageListIsNotCompleted = ko.observable(false); - this.messageListCompleteLoadingThrottle = ko.observable(false).extend({'throttle': 200}); - - this.messageListCompleteLoading = ko.computed(function () { - var - bOne = this.messageListLoading(), - bTwo = this.messageListIsNotCompleted() - ; - return bOne || bTwo; - }, this); - - this.messageListCompleteLoading.subscribe(function (bValue) { - this.messageListCompleteLoadingThrottle(bValue); - }, this); - - this.messageList.subscribe(_.debounce(function (aList) { - _.each(aList, function (oItem) { - if (oItem.newForAnimation()) - { - oItem.newForAnimation(false); - } - }); - }, 500)); - - // message preview - this.staticMessageList = new MessageModel(); - this.message = ko.observable(null); - this.messageLoading = ko.observable(false); - this.messageLoadingThrottle = ko.observable(false).extend({'throttle': 50}); - - this.message.focused = ko.observable(false); - - this.message.subscribe(function (oMessage) { - if (!oMessage) - { - this.message.focused(false); - this.messageFullScreenMode(false); - this.hideMessageBodies(); - - if (Enums.Layout.NoPreview === SettingsStore.layout() && - -1 < window.location.hash.indexOf('message-preview')) - { - require('App/User').historyBack(); - } - } - else if (Enums.Layout.NoPreview === SettingsStore.layout()) - { - this.message.focused(true); - } - }, this); - - this.message.focused.subscribe(function (bValue) { - if (bValue) - { - this.folderList.focused(false); - Globals.keyScope(Enums.KeyState.MessageView); - } - else if (Enums.KeyState.MessageView === Globals.keyScope()) - { - if (Enums.Layout.NoPreview === SettingsStore.layout() && this.message()) - { - Globals.keyScope(Enums.KeyState.MessageView); - } - else - { - Globals.keyScope(Enums.KeyState.MessageList); - } - } - }, this); - - this.folderList.focused.subscribe(function (bValue) { - if (bValue) - { - Globals.keyScope(Enums.KeyState.FolderList); - } - else if (Enums.KeyState.FolderList === Globals.keyScope()) - { - Globals.keyScope(Enums.KeyState.MessageList); - } - }); - - this.messageLoading.subscribe(function (bValue) { - this.messageLoadingThrottle(bValue); - }, this); - - this.messageFullScreenMode = ko.observable(false); - - this.messageError = ko.observable(''); - - this.messagesBodiesDom = ko.observable(null); - - this.messagesBodiesDom.subscribe(function (oDom) { - if (oDom && !(oDom instanceof $)) - { - this.messagesBodiesDom($(oDom)); - } - }, this); - - this.messageActiveDom = ko.observable(null); - - this.isMessageSelected = ko.computed(function () { - return null !== this.message(); - }, this); - - this.currentMessage = ko.observable(null); - - this.messageListChecked = ko.computed(function () { - return _.filter(this.messageList(), function (oItem) { - return oItem.checked(); - }); - }, this).extend({'rateLimit': 0}); - - this.hasCheckedMessages = ko.computed(function () { - return 0 < this.messageListChecked().length; - }, this).extend({'rateLimit': 0}); - - this.messageListCheckedOrSelected = ko.computed(function () { - - var - aChecked = this.messageListChecked(), - oSelectedMessage = this.currentMessage() - ; - - return _.union(aChecked, oSelectedMessage ? [oSelectedMessage] : []); - - }, this); - - this.messageListCheckedOrSelectedUidsWithSubMails = ko.computed(function () { - var aList = []; - _.each(this.messageListCheckedOrSelected(), function (oMessage) { - if (oMessage) - { - aList.push(oMessage.uid); - if (0 < oMessage.threadsLen() && 0 === oMessage.parentUid() && oMessage.lastInCollapsedThread()) - { - aList = _.union(aList, oMessage.threads()); - } - } - }); - return aList; - }, this); - - // other - this.composeInEdit = ko.observable(false); - - this.purgeMessageBodyCacheThrottle = _.throttle(this.purgeMessageBodyCache, 1000 * 30); } - DataUserStorage.prototype.purgeMessageBodyCache = function() - { - var - iCount = 0, - oMessagesBodiesDom = null, - iEnd = Globals.iMessageBodyCacheCount - Consts.Values.MessageBodyCacheLimit - ; - - if (0 < iEnd) - { - oMessagesBodiesDom = this.messagesBodiesDom(); - if (oMessagesBodiesDom) - { - oMessagesBodiesDom.find('.rl-cache-class').each(function () { - var oItem = $(this); - if (iEnd > oItem.data('rl-cache-count')) - { - oItem.addClass('rl-cache-purge'); - iCount++; - } - }); - - if (0 < iCount) - { - _.delay(function () { - oMessagesBodiesDom.find('.rl-cache-purge').remove(); - }, 300); - } - } - } - }; - - DataUserStorage.prototype.initUidNextAndNewMessages = function (sFolder, sUidNext, aNewMessages) - { - if (Cache.getFolderInboxName() === sFolder && Utils.isNormal(sUidNext) && sUidNext !== '') - { - if (Utils.isArray(aNewMessages) && 0 < aNewMessages.length) - { - var - iIndex = 0, - iLen = aNewMessages.length, - NotificationStore = require('Stores/User/Notification') - ; - - _.each(aNewMessages, function (oItem) { - Cache.addNewMessageCache(sFolder, oItem.Uid); - }); - - NotificationStore.playSoundNotification(); - - if (3 < iLen) - { - NotificationStore.displayDesktopNotification( - Links.notificationMailIcon(), - require('Stores/User/Account').email(), - Translator.i18n('MESSAGE_LIST/NEW_MESSAGE_NOTIFICATION', { - 'COUNT': iLen - }) - ); - } - else - { - for (; iIndex < iLen; iIndex++) - { - NotificationStore.displayDesktopNotification( - Links.notificationMailIcon(), - MessageModel.emailsToLine(MessageModel.initEmailsFromJson(aNewMessages[iIndex].From), false), - aNewMessages[iIndex].Subject - ); - } - } - } - - Cache.setFolderUidNext(sFolder, sUidNext); - } - }; - - DataUserStorage.prototype.hideMessageBodies = function () - { - var oMessagesBodiesDom = this.messagesBodiesDom(); - if (oMessagesBodiesDom) - { - oMessagesBodiesDom.find('.b-text-part').hide(); - } - }; - - /** - * @param {boolean=} bBoot = false - * @returns {Array} - */ - DataUserStorage.prototype.getNextFolderNames = function (bBoot) - { - bBoot = Utils.isUnd(bBoot) ? false : !!bBoot; - - var - aResult = [], - iLimit = 5, - iUtc = moment().unix(), - iTimeout = iUtc - 60 * 5, - aTimeouts = [], - sInboxFolderName = Cache.getFolderInboxName(), - fSearchFunction = function (aList) { - _.each(aList, function (oFolder) { - if (oFolder && sInboxFolderName !== oFolder.fullNameRaw && - oFolder.selectable && oFolder.existen && - iTimeout > oFolder.interval && - (!bBoot || oFolder.subScribed())) - { - aTimeouts.push([oFolder.interval, oFolder.fullNameRaw]); - } - - if (oFolder && 0 < oFolder.subFolders().length) - { - fSearchFunction(oFolder.subFolders()); - } - }); - } - ; - - fSearchFunction(this.folderList()); - - aTimeouts.sort(function(a, b) { - if (a[0] < b[0]) { - return -1; - } else if (a[0] > b[0]) { - return 1; - } - - return 0; - }); - - _.find(aTimeouts, function (aItem) { - var oFolder = Cache.getFolderFromCacheList(aItem[1]); - if (oFolder) - { - oFolder.interval = iUtc; - aResult.push(aItem[1]); - } - - return iLimit <= aResult.length; - }); - - return _.uniq(aResult); - }; - - /** - * @param {string} sFromFolderFullNameRaw - * @param {Array} aUidForRemove - * @param {string=} sToFolderFullNameRaw = '' - * @param {bCopy=} bCopy = false - */ - DataUserStorage.prototype.removeMessagesFromList = function ( - sFromFolderFullNameRaw, aUidForRemove, sToFolderFullNameRaw, bCopy) - { - sToFolderFullNameRaw = Utils.isNormal(sToFolderFullNameRaw) ? sToFolderFullNameRaw : ''; - bCopy = Utils.isUnd(bCopy) ? false : !!bCopy; - - aUidForRemove = _.map(aUidForRemove, function (mValue) { - return Utils.pInt(mValue); - }); - - var - self = this, - iUnseenCount = 0, - aMessageList = this.messageList(), - oFromFolder = Cache.getFolderFromCacheList(sFromFolderFullNameRaw), - oToFolder = '' === sToFolderFullNameRaw ? null : Cache.getFolderFromCacheList(sToFolderFullNameRaw || ''), - sCurrentFolderFullNameRaw = this.currentFolderFullNameRaw(), - oCurrentMessage = this.message(), - aMessages = sCurrentFolderFullNameRaw === sFromFolderFullNameRaw ? _.filter(aMessageList, function (oMessage) { - return oMessage && -1 < Utils.inArray(Utils.pInt(oMessage.uid), aUidForRemove); - }) : [] - ; - - _.each(aMessages, function (oMessage) { - if (oMessage && oMessage.unseen()) - { - iUnseenCount++; - } - }); - - if (oFromFolder && !bCopy) - { - oFromFolder.messageCountAll(0 <= oFromFolder.messageCountAll() - aUidForRemove.length ? - oFromFolder.messageCountAll() - aUidForRemove.length : 0); - - if (0 < iUnseenCount) - { - oFromFolder.messageCountUnread(0 <= oFromFolder.messageCountUnread() - iUnseenCount ? - oFromFolder.messageCountUnread() - iUnseenCount : 0); - } - } - - if (oToFolder) - { - oToFolder.messageCountAll(oToFolder.messageCountAll() + aUidForRemove.length); - if (0 < iUnseenCount) - { - oToFolder.messageCountUnread(oToFolder.messageCountUnread() + iUnseenCount); - } - - oToFolder.actionBlink(true); - } - - if (0 < aMessages.length) - { - if (bCopy) - { - _.each(aMessages, function (oMessage) { - oMessage.checked(false); - }); - } - else - { - this.messageListIsNotCompleted(true); - - _.each(aMessages, function (oMessage) { - if (oCurrentMessage && oCurrentMessage.hash === oMessage.hash) - { - oCurrentMessage = null; - self.message(null); - } - - oMessage.deleted(true); - }); - - _.delay(function () { - _.each(aMessages, function (oMessage) { - self.messageList.remove(oMessage); - }); - }, 400); - } - } - - if ('' !== sFromFolderFullNameRaw) - { - Cache.setFolderHash(sFromFolderFullNameRaw, ''); - } - - if ('' !== sToFolderFullNameRaw) - { - Cache.setFolderHash(sToFolderFullNameRaw, ''); - } - }; - - /** - * @param {Object} oMessageTextBody - */ - DataUserStorage.prototype.initBlockquoteSwitcher = function (oMessageTextBody) - { - if (oMessageTextBody) - { - var $oList = $('blockquote:not(.rl-bq-switcher)', oMessageTextBody).filter(function () { - return 0 === $(this).parent().closest('blockquote', oMessageTextBody).length; - }); - - if ($oList && 0 < $oList.length) - { - _.delay(function () { - $oList.each(function () { - var $self = $(this), iH = $self.height(); - if (0 === iH || 150 < iH) - { - $self.addClass('rl-bq-switcher hidden-bq'); - $('') - .insertBefore($self) - .click(function () { - $self.toggleClass('hidden-bq'); - Utils.windowResize(); - }) - .after('
') - .before('
') - ; - } - }); - }, 100); - } - } - }; - - DataUserStorage.prototype.setMessage = function (oData, bCached) - { - var - bIsHtml = false, - bHasExternals = false, - bHasInternals = false, - oBody = null, - oTextBody = null, - sId = '', - sResultHtml = '', - bPgpSigned = false, - bPgpEncrypted = false, - oMessagesBodiesDom = this.messagesBodiesDom(), - oMessage = this.message() - ; - - if (oData && oMessage && oData.Result && 'Object/Message' === oData.Result['@Object'] && - oMessage.folderFullNameRaw === oData.Result.Folder && oMessage.uid === oData.Result.Uid) - { - this.messageError(''); - - oMessage.initUpdateByMessageJson(oData.Result); - Cache.addRequestedMessage(oMessage.folderFullNameRaw, oMessage.uid); - - if (!bCached) - { - oMessage.initFlagsByJson(oData.Result); - } - - oMessagesBodiesDom = oMessagesBodiesDom && oMessagesBodiesDom[0] ? oMessagesBodiesDom : null; - if (oMessagesBodiesDom) - { - sId = 'rl-mgs-' + oMessage.hash.replace(/[^a-zA-Z0-9]/g, ''); - oTextBody = oMessagesBodiesDom.find('#' + sId); - if (!oTextBody || !oTextBody[0]) - { - bHasExternals = !!oData.Result.HasExternals; - bHasInternals = !!oData.Result.HasInternals; - - oBody = $('
').hide().addClass('rl-cache-class'); - oBody.data('rl-cache-count', ++Globals.iMessageBodyCacheCount); - - if (Utils.isNormal(oData.Result.Html) && '' !== oData.Result.Html) - { - bIsHtml = true; - sResultHtml = oData.Result.Html.toString(); - } - else if (Utils.isNormal(oData.Result.Plain) && '' !== oData.Result.Plain) - { - bIsHtml = false; - sResultHtml = Utils.plainToHtml(oData.Result.Plain.toString(), false); - - if ((oMessage.isPgpSigned() || oMessage.isPgpEncrypted()) && require('Stores/User/Pgp').capaOpenPGP()) - { - oMessage.plainRaw = Utils.pString(oData.Result.Plain); - - bPgpEncrypted = /---BEGIN PGP MESSAGE---/.test(oMessage.plainRaw); - if (!bPgpEncrypted) - { - bPgpSigned = /-----BEGIN PGP SIGNED MESSAGE-----/.test(oMessage.plainRaw) && - /-----BEGIN PGP SIGNATURE-----/.test(oMessage.plainRaw); - } - - Globals.$div.empty(); - if (bPgpSigned && oMessage.isPgpSigned()) - { - sResultHtml = - Globals.$div.append( - $('
').text(oMessage.plainRaw)
-									).html()
-								;
-							}
-							else if (bPgpEncrypted && oMessage.isPgpEncrypted())
-							{
-								sResultHtml =
-									Globals.$div.append(
-										$('
').text(oMessage.plainRaw)
-									).html()
-								;
-							}
-
-							Globals.$div.empty();
-
-							oMessage.isPgpSigned(bPgpSigned);
-							oMessage.isPgpEncrypted(bPgpEncrypted);
-						}
-					}
-					else
-					{
-						bIsHtml = false;
-					}
-
-					oBody
-						.html(Utils.findEmailAndLinks(sResultHtml))
-						.addClass('b-text-part ' + (bIsHtml ? 'html' : 'plain'))
-					;
-
-					oMessage.isHtml(!!bIsHtml);
-					oMessage.hasImages(!!bHasExternals);
-					oMessage.pgpSignedVerifyStatus(Enums.SignedVerifyStatus.None);
-					oMessage.pgpSignedVerifyUser('');
-
-					oMessage.body = oBody;
-					if (oMessage.body)
-					{
-						oMessagesBodiesDom.append(oMessage.body);
-					}
-
-					oMessage.storeDataToDom();
-
-					if (bHasInternals)
-					{
-						oMessage.showInternalImages(true);
-					}
-
-					if (oMessage.hasImages() && SettingsStore.showImages())
-					{
-						oMessage.showExternalImages(true);
-					}
-
-					this.purgeMessageBodyCacheThrottle();
-				}
-				else
-				{
-					oMessage.body = oTextBody;
-					if (oMessage.body)
-					{
-						oMessage.body.data('rl-cache-count', ++Globals.iMessageBodyCacheCount);
-						oMessage.fetchDataToDom();
-					}
-				}
-
-				this.messageActiveDom(oMessage.body);
-
-				this.hideMessageBodies();
-				oMessage.body.show();
-
-				if (oBody)
-				{
-					this.initBlockquoteSwitcher(oBody);
-				}
-			}
-
-			Cache.initMessageFlagsFromCache(oMessage);
-			if (oMessage.unseen())
-			{
-				require('App/User').setMessageSeen(oMessage);
-			}
-
-			Utils.windowResize();
-		}
-	};
-
-	/**
-	 * @param {Array} aList
-	 * @returns {string}
-	 */
-	DataUserStorage.prototype.calculateMessageListHash = function (aList)
-	{
-		return _.map(aList, function (oMessage) {
-			return '' + oMessage.hash + '_' + oMessage.threadsLen() + '_' + oMessage.flagHash();
-		}).join('|');
-	};
-
-	DataUserStorage.prototype.findPublicKeyByHex = function (sHash)
-	{
-		return _.find(require('Stores/User/Pgp').openpgpkeysPublic(), function (oItem) {
-			return oItem && sHash === oItem.id;
-		});
-	};
-
-	DataUserStorage.prototype.findPublicKeysByEmail = function (sEmail)
-	{
-		var PgpStore = require('Stores/User/Pgp');
-		return _.compact(_.map(PgpStore.openpgpkeysPublic(), function (oItem) {
-
-			var oKey = null;
-			if (oItem && sEmail === oItem.email)
-			{
-				try
-				{
-					oKey = PgpStore.openpgp.key.readArmored(oItem.armor);
-					if (oKey && !oKey.err && oKey.keys && oKey.keys[0])
-					{
-						return oKey.keys[0];
-					}
-				}
-				catch (e) {}
-			}
-
-			return null;
-
-		}));
-	};
-
-	/**
-	 * @param {string} sEmail
-	 * @param {string=} sPassword
-	 * @returns {?}
-	 */
-	DataUserStorage.prototype.findPrivateKeyByEmail = function (sEmail, sPassword)
-	{
-		var
-			PgpStore = require('Stores/User/Pgp'),
-			oPrivateKey = null,
-			oKey = _.find(PgpStore.openpgpkeysPrivate(), function (oItem) {
-				return oItem && sEmail === oItem.email;
-			})
-		;
-
-		if (oKey)
-		{
-			try
-			{
-				oPrivateKey = PgpStore.openpgp.key.readArmored(oKey.armor);
-				if (oPrivateKey && !oPrivateKey.err && oPrivateKey.keys && oPrivateKey.keys[0])
-				{
-					oPrivateKey = oPrivateKey.keys[0];
-					oPrivateKey.decrypt(Utils.pString(sPassword));
-				}
-				else
-				{
-					oPrivateKey = null;
-				}
-			}
-			catch (e)
-			{
-				oPrivateKey = null;
-			}
-		}
-
-		return oPrivateKey;
-	};
-
-	/**
-	 * @param {string=} sPassword
-	 * @returns {?}
-	 */
-	DataUserStorage.prototype.findSelfPrivateKey = function (sPassword)
-	{
-		return this.findPrivateKeyByEmail(require('Stores/User/Account').email(), sPassword);
-	};
-
 	module.exports = new DataUserStorage();
 
 }());
diff --git a/dev/Storage/User/Remote.js b/dev/Storage/User/Remote.js
index 005cfea8b..891c98474 100644
--- a/dev/Storage/User/Remote.js
+++ b/dev/Storage/User/Remote.js
@@ -12,10 +12,10 @@
 
 		AppStore = require('Stores/User/App'),
 		SettingsStore = require('Stores/User/Settings'),
+		MessageStore = require('Stores/User/Message'),
 
 		Settings = require('Storage/Settings'),
 		Cache = require('Storage/User/Cache'),
-		Data = require('Storage/User/Data'),
 
 		AbstractRemoteStorage = require('Storage/AbstractRemote')
 	;
@@ -348,7 +348,7 @@
 					sFolderHash,
 					Cache.getFolderInboxName() === sFolderFullNameRaw ? Cache.getFolderUidNext(sFolderFullNameRaw) : '',
 					AppStore.threadsAllowed() && SettingsStore.useThreads() ? '1' : '0',
-					AppStore.threadsAllowed() && sFolderFullNameRaw === Data.messageListThreadFolder() ? Data.messageListThreadUids().join(',') : ''
+					AppStore.threadsAllowed() && sFolderFullNameRaw === MessageStore.messageListThreadFolder() ? MessageStore.messageListThreadUids().join(',') : ''
 				].join(String.fromCharCode(0))), bSilent ? [] : ['MessageList']);
 		}
 		else
@@ -360,7 +360,7 @@
 				'Search': sSearch,
 				'UidNext': Cache.getFolderInboxName() === sFolderFullNameRaw ? Cache.getFolderUidNext(sFolderFullNameRaw) : '',
 				'UseThreads': AppStore.threadsAllowed() && SettingsStore.useThreads() ? '1' : '0',
-				'ExpandedThreadUid': AppStore.threadsAllowed() && sFolderFullNameRaw === Data.messageListThreadFolder() ? Data.messageListThreadUids().join(',') : ''
+				'ExpandedThreadUid': AppStore.threadsAllowed() && sFolderFullNameRaw === MessageStore.messageListThreadFolder() ? MessageStore.messageListThreadUids().join(',') : ''
 			}, '' === sSearch ? Consts.Defaults.DefaultAjaxTimeout : Consts.Defaults.SearchAjaxTimeout, '', bSilent ? [] : ['MessageList']);
 		}
 	};
diff --git a/dev/Stores/User/App.js b/dev/Stores/User/App.js
index 74f31df59..71e25c390 100644
--- a/dev/Stores/User/App.js
+++ b/dev/Stores/User/App.js
@@ -21,6 +21,8 @@
 		this.projectHash = ko.observable('');
 		this.threadsAllowed = ko.observable(false);
 
+		this.composeInEdit = ko.observable(false);
+
 		this.contactsAutosave = ko.observable(false);
 		this.useLocalProxyForExternalImages = ko.observable(false);
 
diff --git a/dev/Stores/User/Contact.js b/dev/Stores/User/Contact.js
new file mode 100644
index 000000000..7af3fd388
--- /dev/null
+++ b/dev/Stores/User/Contact.js
@@ -0,0 +1,43 @@
+
+(function () {
+
+	'use strict';
+
+	var
+		ko = require('ko'),
+
+		Settings = require('Storage/Settings')
+	;
+
+	/**
+	 * @constructor
+	 */
+	function ContactUserStore()
+	{
+		this.contacts = ko.observableArray([]);
+		this.contacts.loading = ko.observable(false).extend({'throttle': 200});
+		this.contacts.importing = ko.observable(false).extend({'throttle': 200});
+		this.contacts.syncing = ko.observable(false).extend({'throttle': 200});
+		this.contacts.exportingVcf = ko.observable(false).extend({'throttle': 200});
+		this.contacts.exportingCsv = ko.observable(false).extend({'throttle': 200});
+
+		this.allowContactsSync = ko.observable(false);
+		this.enableContactsSync = ko.observable(false);
+		this.contactsSyncUrl = ko.observable('');
+		this.contactsSyncUser = ko.observable('');
+		this.contactsSyncPass = ko.observable('');
+	}
+
+	ContactUserStore.prototype.populate = function ()
+	{
+		this.allowContactsSync(!!Settings.settingsGet('ContactsSyncIsAllowed'));
+		this.enableContactsSync(!!Settings.settingsGet('EnableContactsSync'));
+
+		this.contactsSyncUrl(Settings.settingsGet('ContactsSyncUrl'));
+		this.contactsSyncUser(Settings.settingsGet('ContactsSyncUser'));
+		this.contactsSyncPass(Settings.settingsGet('ContactsSyncPassword'));
+	};
+
+	module.exports = new ContactUserStore();
+
+}());
\ No newline at end of file
diff --git a/dev/Stores/User/Folder.js b/dev/Stores/User/Folder.js
index 6c6fa5894..d033dbaad 100644
--- a/dev/Stores/User/Folder.js
+++ b/dev/Stores/User/Folder.js
@@ -4,10 +4,14 @@
 	'use strict';
 
 	var
+		_ = require('_'),
 		ko = require('ko'),
+		moment = require('moment'),
 
 		Enums = require('Common/Enums'),
 		Consts = require('Common/Consts'),
+		Globals = require('Common/Globals'),
+		Utils = require('Common/Utils'),
 
 		Cache = require('Storage/User/Cache')
 	;
@@ -23,6 +27,25 @@
 		this.trashFolder = ko.observable('');
 		this.archiveFolder = ko.observable('');
 
+		this.namespace = '';
+
+		this.folderList = ko.observableArray([]);
+		this.folderList.focused = ko.observable(false);
+		this.folderList.optimized = ko.observable(false);
+		this.folderList.error = ko.observable('');
+
+		this.foldersLoading = ko.observable(false);
+		this.foldersCreating = ko.observable(false);
+		this.foldersDeleting = ko.observable(false);
+		this.foldersRenaming = ko.observable(false);
+
+		this.foldersInboxUnreadCount = ko.observable(0);
+
+		this.currentFolder = ko.observable(null).extend({'toggleSubscribe': [null,
+			function (oPrev) { if (oPrev) { oPrev.selected(false); }},
+			function (oNext) { if (oNext) { oNext.selected(true); }}
+		]});
+
 		this.computers();
 		this.subscribers();
 	}
@@ -32,6 +55,96 @@
 		this.draftFolderNotEnabled = ko.computed(function () {
 			return '' === this.draftFolder() || Consts.Values.UnuseOptionValue === this.draftFolder();
 		}, this);
+
+		this.foldersListWithSingleInboxRootFolder = ko.computed(function () {
+			return !_.find(this.folderList(), function (oFolder) {
+				return oFolder && !oFolder.isSystemFolder() && oFolder.visible();
+			});
+		}, this);
+
+		this.currentFolderFullNameRaw = ko.computed(function () {
+			return this.currentFolder() ? this.currentFolder().fullNameRaw : '';
+		}, this);
+
+		this.currentFolderFullName = ko.computed(function () {
+			return this.currentFolder() ? this.currentFolder().fullName : '';
+		}, this);
+
+		this.currentFolderFullNameHash = ko.computed(function () {
+			return this.currentFolder() ? this.currentFolder().fullNameHash : '';
+		}, this);
+
+		this.foldersChanging = ko.computed(function () {
+			var
+				bLoading = this.foldersLoading(),
+				bCreating = this.foldersCreating(),
+				bDeleting = this.foldersDeleting(),
+				bRenaming = this.foldersRenaming()
+			;
+			return bLoading || bCreating || bDeleting || bRenaming;
+		}, this);
+
+		this.folderListSystemNames = ko.computed(function () {
+
+			var
+				aList = [Cache.getFolderInboxName()],
+				aFolders = this.folderList(),
+				sSentFolder = this.sentFolder(),
+				sDraftFolder = this.draftFolder(),
+				sSpamFolder = this.spamFolder(),
+				sTrashFolder = this.trashFolder(),
+				sArchiveFolder = this.archiveFolder()
+			;
+
+			if (Utils.isArray(aFolders) && 0 < aFolders.length)
+			{
+				if ('' !== sSentFolder && Consts.Values.UnuseOptionValue !== sSentFolder)
+				{
+					aList.push(sSentFolder);
+				}
+				if ('' !== sDraftFolder && Consts.Values.UnuseOptionValue !== sDraftFolder)
+				{
+					aList.push(sDraftFolder);
+				}
+				if ('' !== sSpamFolder && Consts.Values.UnuseOptionValue !== sSpamFolder)
+				{
+					aList.push(sSpamFolder);
+				}
+				if ('' !== sTrashFolder && Consts.Values.UnuseOptionValue !== sTrashFolder)
+				{
+					aList.push(sTrashFolder);
+				}
+				if ('' !== sArchiveFolder && Consts.Values.UnuseOptionValue !== sArchiveFolder)
+				{
+					aList.push(sArchiveFolder);
+				}
+			}
+
+			return aList;
+
+		}, this);
+
+		this.folderListSystem = ko.computed(function () {
+			return _.compact(_.map(this.folderListSystemNames(), function (sName) {
+				return Cache.getFolderFromCacheList(sName);
+			}));
+		}, this);
+
+		this.folderMenuForMove = ko.computed(function () {
+			return Utils.folderListOptionsBuilder(this.folderListSystem(), this.folderList(), [
+				this.currentFolderFullNameRaw()
+			], null, null, null, null, function (oItem) {
+				return oItem ? oItem.localName() : '';
+			});
+		}, this);
+
+		this.folderMenuForFilters = ko.computed(function () {
+			return Utils.folderListOptionsBuilder(this.folderListSystem(), this.folderList(),
+				['INBOX'], [['', '']], null, null, null, function (oItem) {
+					return oItem ? oItem.localName() : '';
+				}
+			);
+		}, this);
 	};
 
 	FolderUserStore.prototype.subscribers = function ()
@@ -68,6 +181,76 @@
 		this.spamFolder.subscribe(fSetSystemFolderType(Enums.FolderType.Spam), this);
 		this.trashFolder.subscribe(fSetSystemFolderType(Enums.FolderType.Trash), this);
 		this.archiveFolder.subscribe(fSetSystemFolderType(Enums.FolderType.Archive), this);
+
+		this.folderList.focused.subscribe(function (bValue) {
+			if (bValue)
+			{
+				Globals.keyScope(Enums.KeyState.FolderList);
+			}
+			else if (Enums.KeyState.FolderList === Globals.keyScope())
+			{
+				Globals.keyScope(Enums.KeyState.MessageList);
+			}
+		});
+	};
+
+	/**
+	 * @param {boolean=} bBoot = false
+	 * @returns {Array}
+	 */
+	FolderUserStore.prototype.getNextFolderNames = function (bBoot)
+	{
+		bBoot = Utils.isUnd(bBoot) ? false : !!bBoot;
+
+		var
+			aResult = [],
+			iLimit = 5,
+			iUtc = moment().unix(),
+			iTimeout = iUtc - 60 * 5,
+			aTimeouts = [],
+			sInboxFolderName = Cache.getFolderInboxName(),
+			fSearchFunction = function (aList) {
+				_.each(aList, function (oFolder) {
+					if (oFolder && sInboxFolderName !== oFolder.fullNameRaw &&
+						oFolder.selectable && oFolder.existen &&
+						iTimeout > oFolder.interval &&
+						(!bBoot || oFolder.subScribed()))
+					{
+						aTimeouts.push([oFolder.interval, oFolder.fullNameRaw]);
+					}
+
+					if (oFolder && 0 < oFolder.subFolders().length)
+					{
+						fSearchFunction(oFolder.subFolders());
+					}
+				});
+			}
+		;
+
+		fSearchFunction(this.folderList());
+
+		aTimeouts.sort(function(a, b) {
+			if (a[0] < b[0]) {
+				return -1;
+			} else if (a[0] > b[0]) {
+				return 1;
+			}
+
+			return 0;
+		});
+
+		_.find(aTimeouts, function (aItem) {
+			var oFolder = Cache.getFolderFromCacheList(aItem[1]);
+			if (oFolder)
+			{
+				oFolder.interval = iUtc;
+				aResult.push(aItem[1]);
+			}
+
+			return iLimit <= aResult.length;
+		});
+
+		return _.uniq(aResult);
 	};
 
 	module.exports = new FolderUserStore();
diff --git a/dev/Stores/User/Message.js b/dev/Stores/User/Message.js
new file mode 100644
index 000000000..b75326f3d
--- /dev/null
+++ b/dev/Stores/User/Message.js
@@ -0,0 +1,745 @@
+
+(function () {
+
+	'use strict';
+
+	var
+		_ = require('_'),
+		ko = require('ko'),
+		window = require('window'),
+		moment = require('moment'),
+		$ = require('$'),
+
+		kn = require('Knoin/Knoin'),
+
+		Enums = require('Common/Enums'),
+		Consts = require('Common/Consts'),
+		Globals = require('Common/Globals'),
+		Utils = require('Common/Utils'),
+		Links = require('Common/Links'),
+		Translator = require('Common/Translator'),
+
+		FolderStore = require('Stores/User/Folder'),
+		SettingsStore = require('Stores/User/Settings'),
+
+		MessageModel = require('Model/Message'),
+
+		Cache = require('Storage/User/Cache')
+	;
+
+	/**
+	 * @constructor
+	 */
+	function MessageUserStore()
+	{
+		this.staticMessage = new MessageModel();
+		this.staticMessageList = [];
+
+		this.messageList = ko.observableArray([]).extend({'rateLimit': 0});
+
+		this.messageListCount = ko.observable(0);
+		this.messageListSearch = ko.observable('');
+		this.messageListPage = ko.observable(1);
+		this.messageListError = ko.observable('');
+
+		this.messageListThreadFolder = ko.observable('');
+		this.messageListThreadUids = ko.observableArray([]);
+
+		this.messageListEndFolder = ko.observable('');
+		this.messageListEndSearch = 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});
+
+		// message viewer
+		this.message = ko.observable(null);
+		this.currentMessage = ko.observable(null);
+
+		this.message.focused = ko.observable(false);
+
+		this.messageError = ko.observable('');
+		this.messageLoading = ko.observable(false);
+		this.messageLoadingThrottle = ko.observable(false).extend({'throttle': 50});
+
+		this.messageFullScreenMode = ko.observable(false);
+
+		this.messagesBodiesDom = ko.observable(null);
+		this.messageActiveDom = ko.observable(null);
+
+		this.messageListChecked = ko.computed(function () {
+			return _.filter(this.messageList(), function (oItem) {
+				return oItem.checked();
+			});
+		}, this).extend({'rateLimit': 0});
+
+		this.hasCheckedMessages = ko.computed(function () {
+			return 0 < this.messageListChecked().length;
+		}, this).extend({'rateLimit': 0});
+
+		this.messageListCheckedOrSelected = ko.computed(function () {
+
+			var
+				aChecked = this.messageListChecked(),
+				oSelectedMessage = this.currentMessage()
+			;
+
+			return _.union(aChecked, oSelectedMessage ? [oSelectedMessage] : []);
+
+		}, this);
+
+		this.messageListCheckedOrSelectedUidsWithSubMails = ko.computed(function () {
+			var aList = [];
+			_.each(this.messageListCheckedOrSelected(), function (oMessage) {
+				if (oMessage)
+				{
+					aList.push(oMessage.uid);
+					if (0 < oMessage.threadsLen() && 0 === oMessage.parentUid() && oMessage.lastInCollapsedThread())
+					{
+						aList = _.union(aList, oMessage.threads());
+					}
+				}
+			});
+			return aList;
+		}, this);
+
+
+		this.computers();
+		this.subscribers();
+
+		this.purgeMessageBodyCacheThrottle = _.throttle(this.purgeMessageBodyCache, 1000 * 30);
+	}
+
+	MessageUserStore.prototype.computers = function ()
+	{
+		this.messageListEndHash = ko.computed(function () {
+			return this.messageListEndFolder() + '|' + this.messageListEndSearch() + '|' + this.messageListEndPage();
+		}, this);
+
+		this.messageListPageCount = ko.computed(function () {
+			var iPage = window.Math.ceil(this.messageListCount() /
+				SettingsStore.messagesPerPage());
+			return 0 >= iPage ? 1 : iPage;
+		}, this);
+
+		this.mainMessageListSearch = ko.computed({
+			'read': this.messageListSearch,
+			'write': function (sValue) {
+				kn.setHash(Links.mailBox(
+					FolderStore.currentFolderFullNameHash(), 1, Utils.trim(sValue.toString())
+				));
+			},
+			'owner': this
+		});
+
+		this.messageListCompleteLoading = ko.computed(function () {
+			var
+				bOne = this.messageListLoading(),
+				bTwo = this.messageListIsNotCompleted()
+			;
+			return bOne || bTwo;
+		}, this);
+
+		this.isMessageSelected = ko.computed(function () {
+			return null !== this.message();
+		}, this);
+	};
+
+	MessageUserStore.prototype.subscribers = function ()
+	{
+		this.messageListThreadFolder.subscribe(function () {
+			this.messageListThreadUids([]);
+		}, this);
+
+		this.messageListCompleteLoading.subscribe(function (bValue) {
+			this.messageListCompleteLoadingThrottle(bValue);
+		}, this);
+
+		this.messageList.subscribe(_.debounce(function (aList) {
+			_.each(aList, function (oItem) {
+				if (oItem && oItem.newForAnimation())
+				{
+					oItem.newForAnimation(false);
+				}
+			});
+		}, 500));
+
+		this.message.subscribe(function (oMessage) {
+			if (!oMessage)
+			{
+				this.message.focused(false);
+				this.messageFullScreenMode(false);
+				this.hideMessageBodies();
+
+				if (Enums.Layout.NoPreview === SettingsStore.layout() &&
+					-1 < window.location.hash.indexOf('message-preview'))
+				{
+					require('App/User').historyBack();
+				}
+			}
+			else if (Enums.Layout.NoPreview === SettingsStore.layout())
+			{
+				this.message.focused(true);
+			}
+		}, this);
+
+		this.message.focused.subscribe(function (bValue) {
+			if (bValue)
+			{
+				FolderStore.folderList.focused(false);
+				Globals.keyScope(Enums.KeyState.MessageView);
+			}
+			else if (Enums.KeyState.MessageView === Globals.keyScope())
+			{
+				if (Enums.Layout.NoPreview === SettingsStore.layout() && this.message())
+				{
+					Globals.keyScope(Enums.KeyState.MessageView);
+				}
+				else
+				{
+					Globals.keyScope(Enums.KeyState.MessageList);
+				}
+			}
+		}, this);
+
+		this.messageLoading.subscribe(function (bValue) {
+			this.messageLoadingThrottle(bValue);
+		}, this);
+
+		this.messagesBodiesDom.subscribe(function (oDom) {
+			if (oDom && !(oDom instanceof $))
+			{
+				this.messagesBodiesDom($(oDom));
+			}
+		}, this);
+
+		this.messageListEndFolder.subscribe(function (sFolder) {
+			var oMessage = this.message();
+			if (oMessage && sFolder && sFolder !== oMessage.folderFullNameRaw)
+			{
+				this.message(null);
+			}
+		}, this);
+	};
+
+	MessageUserStore.prototype.purgeMessageBodyCache = function()
+	{
+		var
+			iCount = 0,
+			oMessagesBodiesDom = null,
+			iEnd = Globals.iMessageBodyCacheCount - Consts.Values.MessageBodyCacheLimit
+		;
+
+		if (0 < iEnd)
+		{
+			oMessagesBodiesDom = this.messagesBodiesDom();
+			if (oMessagesBodiesDom)
+			{
+				oMessagesBodiesDom.find('.rl-cache-class').each(function () {
+					var oItem = $(this);
+					if (iEnd > oItem.data('rl-cache-count'))
+					{
+						oItem.addClass('rl-cache-purge');
+						iCount++;
+					}
+				});
+
+				if (0 < iCount)
+				{
+					_.delay(function () {
+						oMessagesBodiesDom.find('.rl-cache-purge').remove();
+					}, 300);
+				}
+			}
+		}
+	};
+
+	MessageUserStore.prototype.initUidNextAndNewMessages = function (sFolder, sUidNext, aNewMessages)
+	{
+		if (Cache.getFolderInboxName() === sFolder && Utils.isNormal(sUidNext) && sUidNext !== '')
+		{
+			if (Utils.isArray(aNewMessages) && 0 < aNewMessages.length)
+			{
+				var
+					iIndex = 0,
+					iLen = aNewMessages.length,
+					NotificationStore = require('Stores/User/Notification')
+				;
+
+				_.each(aNewMessages, function (oItem) {
+					Cache.addNewMessageCache(sFolder, oItem.Uid);
+				});
+
+				NotificationStore.playSoundNotification();
+
+				if (3 < iLen)
+				{
+					NotificationStore.displayDesktopNotification(
+						Links.notificationMailIcon(),
+						require('Stores/User/Account').email(),
+						Translator.i18n('MESSAGE_LIST/NEW_MESSAGE_NOTIFICATION', {
+							'COUNT': iLen
+						})
+					);
+				}
+				else
+				{
+					for (; iIndex < iLen; iIndex++)
+					{
+						NotificationStore.displayDesktopNotification(
+							Links.notificationMailIcon(),
+							MessageModel.emailsToLine(MessageModel.initEmailsFromJson(aNewMessages[iIndex].From), false),
+							aNewMessages[iIndex].Subject
+						);
+					}
+				}
+			}
+
+			Cache.setFolderUidNext(sFolder, sUidNext);
+		}
+	};
+
+	MessageUserStore.prototype.hideMessageBodies = function ()
+	{
+		var oMessagesBodiesDom = this.messagesBodiesDom();
+		if (oMessagesBodiesDom)
+		{
+			oMessagesBodiesDom.find('.b-text-part').hide();
+		}
+	};
+
+	/**
+	 * @param {string} sFromFolderFullNameRaw
+	 * @param {Array} aUidForRemove
+	 * @param {string=} sToFolderFullNameRaw = ''
+	 * @param {bCopy=} bCopy = false
+	 */
+	MessageUserStore.prototype.removeMessagesFromList = function (
+		sFromFolderFullNameRaw, aUidForRemove, sToFolderFullNameRaw, bCopy)
+	{
+		sToFolderFullNameRaw = Utils.isNormal(sToFolderFullNameRaw) ? sToFolderFullNameRaw : '';
+		bCopy = Utils.isUnd(bCopy) ? false : !!bCopy;
+
+		aUidForRemove = _.map(aUidForRemove, function (mValue) {
+			return Utils.pInt(mValue);
+		});
+
+		var
+			self = this,
+			iUnseenCount = 0,
+			aMessageList = this.messageList(),
+			oFromFolder = Cache.getFolderFromCacheList(sFromFolderFullNameRaw),
+			oToFolder = '' === sToFolderFullNameRaw ? null : Cache.getFolderFromCacheList(sToFolderFullNameRaw || ''),
+			sCurrentFolderFullNameRaw = FolderStore.currentFolderFullNameRaw(),
+			oCurrentMessage = this.message(),
+			aMessages = sCurrentFolderFullNameRaw === sFromFolderFullNameRaw ? _.filter(aMessageList, function (oMessage) {
+				return oMessage && -1 < Utils.inArray(Utils.pInt(oMessage.uid), aUidForRemove);
+			}) : []
+		;
+
+		_.each(aMessages, function (oMessage) {
+			if (oMessage && oMessage.unseen())
+			{
+				iUnseenCount++;
+			}
+		});
+
+		if (oFromFolder && !bCopy)
+		{
+			oFromFolder.messageCountAll(0 <= oFromFolder.messageCountAll() - aUidForRemove.length ?
+				oFromFolder.messageCountAll() - aUidForRemove.length : 0);
+
+			if (0 < iUnseenCount)
+			{
+				oFromFolder.messageCountUnread(0 <= oFromFolder.messageCountUnread() - iUnseenCount ?
+					oFromFolder.messageCountUnread() - iUnseenCount : 0);
+			}
+		}
+
+		if (oToFolder)
+		{
+			oToFolder.messageCountAll(oToFolder.messageCountAll() + aUidForRemove.length);
+			if (0 < iUnseenCount)
+			{
+				oToFolder.messageCountUnread(oToFolder.messageCountUnread() + iUnseenCount);
+			}
+
+			oToFolder.actionBlink(true);
+		}
+
+		if (0 < aMessages.length)
+		{
+			if (bCopy)
+			{
+				_.each(aMessages, function (oMessage) {
+					oMessage.checked(false);
+				});
+			}
+			else
+			{
+				this.messageListIsNotCompleted(true);
+
+				_.each(aMessages, function (oMessage) {
+					if (oCurrentMessage && oCurrentMessage.hash === oMessage.hash)
+					{
+						oCurrentMessage = null;
+						self.message(null);
+					}
+
+					oMessage.deleted(true);
+				});
+
+				_.delay(function () {
+					_.each(aMessages, function (oMessage) {
+						self.messageList.remove(oMessage);
+					});
+				}, 400);
+			}
+		}
+
+		if ('' !== sFromFolderFullNameRaw)
+		{
+			Cache.setFolderHash(sFromFolderFullNameRaw, '');
+		}
+
+		if ('' !== sToFolderFullNameRaw)
+		{
+			Cache.setFolderHash(sToFolderFullNameRaw, '');
+		}
+	};
+
+	/**
+	 * @param {Object} oMessageTextBody
+	 */
+	MessageUserStore.prototype.initBlockquoteSwitcher = function (oMessageTextBody)
+	{
+		if (oMessageTextBody)
+		{
+			var $oList = $('blockquote:not(.rl-bq-switcher)', oMessageTextBody).filter(function () {
+				return 0 === $(this).parent().closest('blockquote', oMessageTextBody).length;
+			});
+
+			if ($oList && 0 < $oList.length)
+			{
+				_.delay(function () {
+					$oList.each(function () {
+						var $self = $(this), iH = $self.height();
+						if (0 === iH || 150 < iH)
+						{
+							$self.addClass('rl-bq-switcher hidden-bq');
+							$('')
+								.insertBefore($self)
+								.click(function () {
+									$self.toggleClass('hidden-bq');
+									Utils.windowResize();
+								})
+								.after('
') + .before('
') + ; + } + }); + }, 100); + } + } + }; + + MessageUserStore.prototype.setMessage = function (oData, bCached) + { + var + bIsHtml = false, + bHasExternals = false, + bHasInternals = false, + oBody = null, + oTextBody = null, + sId = '', + sResultHtml = '', + bPgpSigned = false, + bPgpEncrypted = false, + oMessagesBodiesDom = this.messagesBodiesDom(), + oMessage = this.message() + ; + + if (oData && oMessage && oData.Result && 'Object/Message' === oData.Result['@Object'] && + oMessage.folderFullNameRaw === oData.Result.Folder && oMessage.uid === oData.Result.Uid) + { + this.messageError(''); + + oMessage.initUpdateByMessageJson(oData.Result); + Cache.addRequestedMessage(oMessage.folderFullNameRaw, oMessage.uid); + + if (!bCached) + { + oMessage.initFlagsByJson(oData.Result); + } + + oMessagesBodiesDom = oMessagesBodiesDom && oMessagesBodiesDom[0] ? oMessagesBodiesDom : null; + if (oMessagesBodiesDom) + { + sId = 'rl-mgs-' + oMessage.hash.replace(/[^a-zA-Z0-9]/g, ''); + oTextBody = oMessagesBodiesDom.find('#' + sId); + if (!oTextBody || !oTextBody[0]) + { + bHasExternals = !!oData.Result.HasExternals; + bHasInternals = !!oData.Result.HasInternals; + + oBody = $('
').hide().addClass('rl-cache-class'); + oBody.data('rl-cache-count', ++Globals.iMessageBodyCacheCount); + + if (Utils.isNormal(oData.Result.Html) && '' !== oData.Result.Html) + { + bIsHtml = true; + sResultHtml = oData.Result.Html.toString(); + } + else if (Utils.isNormal(oData.Result.Plain) && '' !== oData.Result.Plain) + { + bIsHtml = false; + sResultHtml = Utils.plainToHtml(oData.Result.Plain.toString(), false); + + if ((oMessage.isPgpSigned() || oMessage.isPgpEncrypted()) && require('Stores/User/Pgp').capaOpenPGP()) + { + oMessage.plainRaw = Utils.pString(oData.Result.Plain); + + bPgpEncrypted = /---BEGIN PGP MESSAGE---/.test(oMessage.plainRaw); + if (!bPgpEncrypted) + { + bPgpSigned = /-----BEGIN PGP SIGNED MESSAGE-----/.test(oMessage.plainRaw) && + /-----BEGIN PGP SIGNATURE-----/.test(oMessage.plainRaw); + } + + Globals.$div.empty(); + if (bPgpSigned && oMessage.isPgpSigned()) + { + sResultHtml = + Globals.$div.append( + $('
').text(oMessage.plainRaw)
+									).html()
+								;
+							}
+							else if (bPgpEncrypted && oMessage.isPgpEncrypted())
+							{
+								sResultHtml =
+									Globals.$div.append(
+										$('
').text(oMessage.plainRaw)
+									).html()
+								;
+							}
+
+							Globals.$div.empty();
+
+							oMessage.isPgpSigned(bPgpSigned);
+							oMessage.isPgpEncrypted(bPgpEncrypted);
+						}
+					}
+					else
+					{
+						bIsHtml = false;
+					}
+
+					oBody
+						.html(Utils.findEmailAndLinks(sResultHtml))
+						.addClass('b-text-part ' + (bIsHtml ? 'html' : 'plain'))
+					;
+
+					oMessage.isHtml(!!bIsHtml);
+					oMessage.hasImages(!!bHasExternals);
+					oMessage.pgpSignedVerifyStatus(Enums.SignedVerifyStatus.None);
+					oMessage.pgpSignedVerifyUser('');
+
+					oMessage.body = oBody;
+					if (oMessage.body)
+					{
+						oMessagesBodiesDom.append(oMessage.body);
+					}
+
+					oMessage.storeDataToDom();
+
+					if (bHasInternals)
+					{
+						oMessage.showInternalImages(true);
+					}
+
+					if (oMessage.hasImages() && SettingsStore.showImages())
+					{
+						oMessage.showExternalImages(true);
+					}
+
+					this.purgeMessageBodyCacheThrottle();
+				}
+				else
+				{
+					oMessage.body = oTextBody;
+					if (oMessage.body)
+					{
+						oMessage.body.data('rl-cache-count', ++Globals.iMessageBodyCacheCount);
+						oMessage.fetchDataToDom();
+					}
+				}
+
+				this.messageActiveDom(oMessage.body);
+
+				this.hideMessageBodies();
+				oMessage.body.show();
+
+				if (oBody)
+				{
+					this.initBlockquoteSwitcher(oBody);
+				}
+			}
+
+			Cache.initMessageFlagsFromCache(oMessage);
+			if (oMessage.unseen())
+			{
+				require('App/User').setMessageSeen(oMessage);
+			}
+
+			Utils.windowResize();
+		}
+	};
+
+	/**
+	 * @param {Array} aList
+	 * @returns {string}
+	 */
+	MessageUserStore.prototype.calculateMessageListHash = function (aList)
+	{
+		return _.map(aList, function (oMessage) {
+			return '' + oMessage.hash + '_' + oMessage.threadsLen() + '_' + oMessage.flagHash();
+		}).join('|');
+	};
+
+	MessageUserStore.prototype.setMessageList = function (oData, bCached)
+	{
+		if (oData && oData.Result && 'Collection/MessageCollection' === oData.Result['@Object'] &&
+			oData.Result['@Collection'] && Utils.isArray(oData.Result['@Collection']))
+		{
+			var
+				mLastCollapsedThreadUids = null,
+				iIndex = 0,
+				iLen = 0,
+				iCount = 0,
+				iOffset = 0,
+				aList = [],
+				iUtc = moment().unix(),
+				aStaticList = this.staticMessageList,
+				oJsonMessage = null,
+				oMessage = null,
+				oFolder = null,
+				iNewCount = 0,
+				bUnreadCountChange = false
+			;
+
+			iCount = Utils.pInt(oData.Result.MessageResultCount);
+			iOffset = Utils.pInt(oData.Result.Offset);
+
+			if (Utils.isNonEmptyArray(oData.Result.LastCollapsedThreadUids))
+			{
+				mLastCollapsedThreadUids = oData.Result.LastCollapsedThreadUids;
+			}
+
+			oFolder = Cache.getFolderFromCacheList(
+				Utils.isNormal(oData.Result.Folder) ? oData.Result.Folder : '');
+
+			if (oFolder && !bCached)
+			{
+				oFolder.interval = iUtc;
+
+				Cache.setFolderHash(oData.Result.Folder, oData.Result.FolderHash);
+
+				if (Utils.isNormal(oData.Result.MessageCount))
+				{
+					oFolder.messageCountAll(oData.Result.MessageCount);
+				}
+
+				if (Utils.isNormal(oData.Result.MessageUnseenCount))
+				{
+					if (Utils.pInt(oFolder.messageCountUnread()) !== Utils.pInt(oData.Result.MessageUnseenCount))
+					{
+						bUnreadCountChange = true;
+					}
+
+					oFolder.messageCountUnread(oData.Result.MessageUnseenCount);
+				}
+
+				this.initUidNextAndNewMessages(oFolder.fullNameRaw, oData.Result.UidNext, oData.Result.NewMessages);
+			}
+
+			if (bUnreadCountChange && oFolder)
+			{
+				Cache.clearMessageFlagsFromCacheByFolder(oFolder.fullNameRaw);
+			}
+
+			for (iIndex = 0, iLen = oData.Result['@Collection'].length; iIndex < iLen; iIndex++)
+			{
+				oJsonMessage = oData.Result['@Collection'][iIndex];
+				if (oJsonMessage && 'Object/Message' === oJsonMessage['@Object'])
+				{
+					oMessage = aStaticList[iIndex];
+					if (!oMessage || !oMessage.initByJson(oJsonMessage))
+					{
+						oMessage = MessageModel.newInstanceFromJson(oJsonMessage);
+					}
+
+					if (oMessage)
+					{
+						if (Cache.hasNewMessageAndRemoveFromCache(oMessage.folderFullNameRaw, oMessage.uid) && 5 >= iNewCount)
+						{
+							iNewCount++;
+							oMessage.newForAnimation(true);
+						}
+
+						oMessage.deleted(false);
+
+						if (bCached)
+						{
+							Cache.initMessageFlagsFromCache(oMessage);
+						}
+						else
+						{
+							Cache.storeMessageFlagsToCache(oMessage);
+						}
+
+						oMessage.lastInCollapsedThread(mLastCollapsedThreadUids && -1 < Utils.inArray(Utils.pInt(oMessage.uid), mLastCollapsedThreadUids) ? true : false);
+
+						aList.push(oMessage);
+					}
+				}
+			}
+
+			this.messageListCount(iCount);
+			this.messageListSearch(Utils.isNormal(oData.Result.Search) ? oData.Result.Search : '');
+			this.messageListPage(window.Math.ceil((iOffset / SettingsStore.messagesPerPage()) + 1));
+
+			this.messageListEndFolder(Utils.isNormal(oData.Result.Folder) ? oData.Result.Folder : '');
+			this.messageListEndSearch(Utils.isNormal(oData.Result.Search) ? oData.Result.Search : '');
+			this.messageListEndPage(this.messageListPage());
+
+			this.messageList(aList);
+			this.messageListIsNotCompleted(false);
+
+			if (aStaticList.length < aList.length)
+			{
+				this.staticMessageList = aList;
+			}
+
+			Cache.clearNewMessageCache();
+
+			if (oFolder && (bCached || bUnreadCountChange || SettingsStore.useThreads()))
+			{
+				require('App/User').folderInformation(oFolder.fullNameRaw, aList);
+			}
+		}
+		else
+		{
+			this.messageListCount(0);
+			this.messageList([]);
+			this.messageListError(Translator.getNotification(
+				oData && oData.ErrorCode ? oData.ErrorCode : Enums.Notification.CantGetMessageList
+			));
+		}
+	};
+
+	module.exports = new MessageUserStore();
+
+}());
+
diff --git a/dev/Stores/User/Pgp.js b/dev/Stores/User/Pgp.js
index a910e1641..979ab8976 100644
--- a/dev/Stores/User/Pgp.js
+++ b/dev/Stores/User/Pgp.js
@@ -3,7 +3,10 @@
 	'use strict';
 
 	var
-		ko = require('ko')
+		_ = require('_'),
+		ko = require('ko'),
+
+		Utils = require('Common/Utils')
 	;
 
 	/**
@@ -35,6 +38,84 @@
 		return !!this.openpgp;
 	};
 
+	PgpUserStore.prototype.findPublicKeyByHex = function (sHash)
+	{
+		return _.find(this.openpgpkeysPublic(), function (oItem) {
+			return oItem && sHash === oItem.id;
+		});
+	};
+
+	PgpUserStore.prototype.findPublicKeysByEmail = function (sEmail)
+	{
+		var self = this;
+		return _.compact(_.map(this.openpgpkeysPublic(), function (oItem) {
+
+			var oKey = null;
+			if (oItem && sEmail === oItem.email)
+			{
+				try
+				{
+					oKey = self.openpgp.key.readArmored(oItem.armor);
+					if (oKey && !oKey.err && oKey.keys && oKey.keys[0])
+					{
+						return oKey.keys[0];
+					}
+				}
+				catch (e) {}
+			}
+
+			return null;
+
+		}));
+	};
+
+	/**
+	 * @param {string} sEmail
+	 * @param {string=} sPassword
+	 * @returns {?}
+	 */
+	PgpUserStore.prototype.findPrivateKeyByEmail = function (sEmail, sPassword)
+	{
+		var
+			oPrivateKey = null,
+			oKey = _.find(this.openpgpkeysPrivate(), function (oItem) {
+				return oItem && sEmail === oItem.email;
+			})
+		;
+
+		if (oKey)
+		{
+			try
+			{
+				oPrivateKey = this.openpgp.key.readArmored(oKey.armor);
+				if (oPrivateKey && !oPrivateKey.err && oPrivateKey.keys && oPrivateKey.keys[0])
+				{
+					oPrivateKey = oPrivateKey.keys[0];
+					oPrivateKey.decrypt(Utils.pString(sPassword));
+				}
+				else
+				{
+					oPrivateKey = null;
+				}
+			}
+			catch (e)
+			{
+				oPrivateKey = null;
+			}
+		}
+
+		return oPrivateKey;
+	};
+
+	/**
+	 * @param {string=} sPassword
+	 * @returns {?}
+	 */
+	PgpUserStore.prototype.findSelfPrivateKey = function (sPassword)
+	{
+		return this.findPrivateKeyByEmail(require('Stores/User/Account').email(), sPassword);
+	};
+
 	module.exports = new PgpUserStore();
 
 }());
diff --git a/dev/Styles/Compose.less b/dev/Styles/Compose.less
index 94106d0ae..647ba6570 100644
--- a/dev/Styles/Compose.less
+++ b/dev/Styles/Compose.less
@@ -56,6 +56,9 @@
 			opacity: 1;
 			color: #fff;
 			border-color: #eee;
+
+			font-size: 28px;
+			line-height: 28px;
 		}
 
 		.btn.disabled {
diff --git a/dev/Styles/Layout.less b/dev/Styles/Layout.less
index 59ccf85be..d183aded1 100644
--- a/dev/Styles/Layout.less
+++ b/dev/Styles/Layout.less
@@ -139,7 +139,7 @@ html.ssm-state-tablet, html.ssm-state-mobile {
 	}
 
 	.b-compose.modal {
-		width: 700px;
+		width: 720px;
 	}
 
 	.b-contacts-content.modal {
@@ -156,7 +156,7 @@ html.ssm-state-tablet, html.ssm-state-mobile {
 html.ssm-state-tablet {
 
 	.b-compose.modal {
-		width: 700px;
+		width: 720px;
 	}
 
 	.b-contacts-content.modal {
diff --git a/dev/Styles/_CkeFix.less b/dev/Styles/_CkeFix.less
index b72455f0a..d022d8a71 100644
--- a/dev/Styles/_CkeFix.less
+++ b/dev/Styles/_CkeFix.less
@@ -110,6 +110,8 @@
 
 	blockquote p {
 		margin: 0 0 10px;
+		font-size: 14px;
+		line-height: 20px;
 	}
 
 	img {
diff --git a/dev/View/Popup/AdvancedSearch.js b/dev/View/Popup/AdvancedSearch.js
index ed8d00dd5..6bf1f0921 100644
--- a/dev/View/Popup/AdvancedSearch.js
+++ b/dev/View/Popup/AdvancedSearch.js
@@ -10,7 +10,7 @@
 
 		Utils = require('Common/Utils'),
 
-		Data = require('Storage/User/Data'),
+		MessageStore = require('Stores/User/Message'),
 
 		kn = require('Knoin/Knoin'),
 		AbstractView = require('Knoin/AbstractView')
@@ -41,7 +41,7 @@
 			var sSearch = this.buildSearchString();
 			if ('' !== sSearch)
 			{
-				Data.mainMessageListSearch(sSearch);
+				MessageStore.mainMessageListSearch(sSearch);
 			}
 
 			this.cancelCommand();
diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js
index c72aec14b..e90c9aad2 100644
--- a/dev/View/Popup/Compose.js
+++ b/dev/View/Popup/Compose.js
@@ -27,10 +27,10 @@
 		AccountStore = require('Stores/User/Account'),
 		FolderStore = require('Stores/User/Folder'),
 		PgpStore = require('Stores/User/Pgp'),
+		MessageStore = require('Stores/User/Message'),
 		SocialStore = require('Stores/Social'),
 
 		Settings = require('Storage/Settings'),
-		Data = require('Storage/User/Data'),
 		Cache = require('Storage/User/Cache'),
 		Remote = require('Storage/User/Remote'),
 
@@ -87,7 +87,7 @@
 		this.allowContacts = !!AppStore.contactsIsAllowed();
 
 		this.bSkipNextHide = false;
-		this.composeInEdit = Data.composeInEdit;
+		this.composeInEdit = AppStore.composeInEdit;
 		this.editorDefaultType = SettingsStore.editorDefaultType;
 
 		this.capaOpenPGP = PgpStore.capaOpenPGP;
@@ -433,8 +433,6 @@
 
 				var self = this;
 
-				window.console.log(this.sLastFocusedField);
-
 				_.delay(function () {
 					kn.showScreenPopup(require('View/Popup/Contacts'),
 						[true, self.sLastFocusedField]);
@@ -578,7 +576,7 @@
 		if ('' !== sDraftFolder)
 		{
 			Cache.setFolderHash(sDraftFolder, '');
-			if (Data.currentFolderFullNameRaw() === sDraftFolder)
+			if (FolderStore.currentFolderFullNameRaw() === sDraftFolder)
 			{
 				require('App/User').reloadMessageList(true);
 			}
@@ -701,10 +699,10 @@
 
 				if (this.bFromDraft)
 				{
-					oMessage = Data.message();
+					oMessage = MessageStore.message();
 					if (oMessage && this.draftFolder() === oMessage.folderFullNameRaw && this.draftUid() === oMessage.uid)
 					{
-						Data.message(null);
+						MessageStore.message(null);
 					}
 				}
 
@@ -741,7 +739,7 @@
 
 		if (!this.bSkipNextHide)
 		{
-			this.composeInEdit(false);
+			AppStore.composeInEdit(false);
 			this.reset();
 		}
 
@@ -883,7 +881,7 @@
 
 		this.autosaveStart();
 
-		if (this.composeInEdit())
+		if (AppStore.composeInEdit())
 		{
 			sType = sType || Enums.ComposeType.Empty;
 
@@ -969,7 +967,7 @@
 	ComposePopupView.prototype.initOnShow = function (sType, oMessageOrArray,
 		aToEmails, aCcEmails, aBccEmails, sCustomSubject, sCustomPlainText)
 	{
-		this.composeInEdit(true);
+		AppStore.composeInEdit(true);
 
 		var
 			self = this,
diff --git a/dev/View/Popup/ComposeOpenPgp.js b/dev/View/Popup/ComposeOpenPgp.js
index 828c49bad..e77a82f18 100644
--- a/dev/View/Popup/ComposeOpenPgp.js
+++ b/dev/View/Popup/ComposeOpenPgp.js
@@ -12,7 +12,6 @@
 		Enums = require('Common/Enums'),
 		Translator = require('Common/Translator'),
 
-		Data = require('Storage/User/Data'),
 		PgpStore = require('Stores/User/Pgp'),
 
 		EmailModel = require('Model/Email'),
@@ -66,7 +65,7 @@
 
 			if (bResult && this.sign())
 			{
-				oPrivateKey = Data.findPrivateKeyByEmail(this.from(), this.password());
+				oPrivateKey = PgpStore.findPrivateKeyByEmail(this.from(), this.password());
 				if (!oPrivateKey)
 				{
 					this.notification(Translator.i18n('PGP_NOTIFICATIONS/NO_PRIVATE_KEY_FOUND_FOR', {
@@ -87,7 +86,7 @@
 			{
 				aPublicKeys = [];
 				_.each(this.to(), function (sEmail) {
-					var aKeys = Data.findPublicKeysByEmail(sEmail);
+					var aKeys = PgpStore.findPublicKeysByEmail(sEmail);
 					if (0 === aKeys.length && bResult)
 					{
 						self.notification(Translator.i18n('PGP_NOTIFICATIONS/NO_PUBLIC_KEYS_FOUND_FOR', {
diff --git a/dev/View/Popup/Contacts.js b/dev/View/Popup/Contacts.js
index 5ba47fcd5..275292441 100644
--- a/dev/View/Popup/Contacts.js
+++ b/dev/View/Popup/Contacts.js
@@ -19,8 +19,8 @@
 		Translator = require('Common/Translator'),
 
 		SettingsStore = require('Stores/User/Settings'),
+		ContactStore = require('Stores/User/Contact'),
 
-		Data = require('Storage/User/Data'),
 		Remote = require('Storage/User/Remote'),
 
 		EmailModel = require('Model/Email'),
@@ -52,13 +52,13 @@
 		this.bBackToCompose = false;
 		this.sLastComposeFocusedField = '';
 
-		this.allowContactsSync = Data.allowContactsSync;
-		this.enableContactsSync = Data.enableContactsSync;
+		this.allowContactsSync = ContactStore.allowContactsSync;
+		this.enableContactsSync = ContactStore.enableContactsSync;
 		this.allowExport = !Globals.bMobileDevice;
 
 		this.search = ko.observable('');
 		this.contactsCount = ko.observable(0);
-		this.contacts = Data.contacts;
+		this.contacts = ContactStore.contacts;
 
 		this.currentContact = ko.observable(null);
 
diff --git a/dev/View/Popup/Filter.js b/dev/View/Popup/Filter.js
index 44dc15353..c5f5ad428 100644
--- a/dev/View/Popup/Filter.js
+++ b/dev/View/Popup/Filter.js
@@ -12,9 +12,8 @@
 		Utils = require('Common/Utils'),
 		Translator = require('Common/Translator'),
 
-		Data = require('Storage/User/Data'),
-
 		FilterStore = require('Stores/User/Filter'),
+		FolderStore = require('Stores/User/Folder'),
 
 		kn = require('Knoin/Knoin'),
 		AbstractView = require('Knoin/AbstractView')
@@ -38,7 +37,7 @@
 		this.allowMarkAsRead = ko.observable(false);
 
 		this.defautOptionsAfterRender = Utils.defautOptionsAfterRender;
-		this.folderSelectList = Data.folderMenuForFilters;
+		this.folderSelectList = FolderStore.folderMenuForFilters;
 		this.selectedFolderValue = ko.observable('');
 
 		this.selectedFolderValue.subscribe(function() {
diff --git a/dev/View/Popup/FolderClear.js b/dev/View/Popup/FolderClear.js
index c842cbd53..137cdfcdf 100644
--- a/dev/View/Popup/FolderClear.js
+++ b/dev/View/Popup/FolderClear.js
@@ -11,7 +11,8 @@
 		Utils = require('Common/Utils'),
 		Translator = require('Common/Translator'),
 
-		Data = require('Storage/User/Data'),
+		MessageStore = require('Stores/User/Message'),
+
 		Cache = require('Storage/User/Cache'),
 		Remote = require('Storage/User/Remote'),
 
@@ -56,8 +57,8 @@
 
 			if (oFolderToClear)
 			{
-				Data.message(null);
-				Data.messageList([]);
+				MessageStore.message(null);
+				MessageStore.messageList([]);
 
 				this.clearingProcess(true);
 
diff --git a/dev/View/Popup/FolderCreate.js b/dev/View/Popup/FolderCreate.js
index 500f032df..32af85e15 100644
--- a/dev/View/Popup/FolderCreate.js
+++ b/dev/View/Popup/FolderCreate.js
@@ -13,7 +13,8 @@
 		Utils = require('Common/Utils'),
 		Translator = require('Common/Translator'),
 
-		Data = require('Storage/User/Data'),
+		FolderStore = require('Stores/User/Folder'),
+
 		Remote = require('Storage/User/Remote'),
 
 		kn = require('Knoin/Knoin'),
@@ -43,7 +44,7 @@
 				aTop = [],
 				fDisableCallback = null,
 				fVisibleCallback = null,
-				aList = Data.folderList(),
+				aList = FolderStore.folderList(),
 				fRenameCallback = function (oItem) {
 					return oItem ? (oItem.isSystemFolder() ? oItem.name() + ' ' + oItem.manageFolderSystemName() : oItem.name()) : '';
 				}
@@ -51,11 +52,11 @@
 
 			aTop.push(['', this.sNoParentText]);
 
-			if ('' !== Data.namespace)
+			if ('' !== FolderStore.namespace)
 			{
 				fDisableCallback = function (oItem)
 				{
-					return Data.namespace !== oItem.fullNameRaw.substr(0, Data.namespace.length);
+					return FolderStore.namespace !== oItem.fullNameRaw.substr(0, FolderStore.namespace.length);
 				};
 			}
 
@@ -70,22 +71,22 @@
 				sParentFolderName = this.selectedParentValue()
 			;
 
-			if ('' === sParentFolderName && 1 < Data.namespace.length)
+			if ('' === sParentFolderName && 1 < FolderStore.namespace.length)
 			{
-				sParentFolderName = Data.namespace.substr(0, Data.namespace.length - 1);
+				sParentFolderName = FolderStore.namespace.substr(0, FolderStore.namespace.length - 1);
 			}
 
-			Data.foldersCreating(true);
+			FolderStore.foldersCreating(true);
 			Remote.folderCreate(function (sResult, oData) {
 
-				Data.foldersCreating(false);
+				FolderStore.foldersCreating(false);
 				if (Enums.StorageResultType.Success === sResult && oData && oData.Result)
 				{
 					require('App/User').folders();
 				}
 				else
 				{
-					Data.folderList.error(
+					FolderStore.folderList.error(
 						oData && oData.ErrorCode ? Translator.getNotification(oData.ErrorCode) : Translator.i18n('NOTIFICATIONS/CANT_CREATE_FOLDER'));
 				}
 
diff --git a/dev/View/Popup/FolderSystem.js b/dev/View/Popup/FolderSystem.js
index b39f806a5..a8914faca 100644
--- a/dev/View/Popup/FolderSystem.js
+++ b/dev/View/Popup/FolderSystem.js
@@ -15,7 +15,6 @@
 		FolderStore = require('Stores/User/Folder'),
 
 		Settings = require('Storage/Settings'),
-		Data = require('Storage/User/Data'),
 		Remote = require('Storage/User/Remote'),
 
 		kn = require('Knoin/Knoin'),
@@ -38,7 +37,7 @@
 		this.notification = ko.observable('');
 
 		this.folderSelectList = ko.computed(function () {
-			return Utils.folderListOptionsBuilder([], Data.folderList(), Data.folderListSystemNames(), [
+			return Utils.folderListOptionsBuilder([], FolderStore.folderList(), FolderStore.folderListSystemNames(), [
 				['', this.sChooseOnText],
 				[Consts.Values.UnuseOptionValue, this.sUnuseText]
 			], null, null, null, null, null, true);
diff --git a/dev/View/User/MailBox/FolderList.js b/dev/View/User/MailBox/FolderList.js
index bf4060b92..c484eec73 100644
--- a/dev/View/User/MailBox/FolderList.js
+++ b/dev/View/User/MailBox/FolderList.js
@@ -17,9 +17,10 @@
 
 		AppStore = require('Stores/User/App'),
 		SettingsStore = require('Stores/User/Settings'),
+		FolderStore = require('Stores/User/Folder'),
+		MessageStore = require('Stores/User/Message'),
 
 		Cache = require('Storage/User/Cache'),
-		Data = require('Storage/User/Data'),
 
 		kn = require('Knoin/Knoin'),
 		AbstractView = require('Knoin/AbstractView')
@@ -36,14 +37,14 @@
 		this.oContentVisible = null;
 		this.oContentScrollable = null;
 
-		this.composeInEdit = Data.composeInEdit;
+		this.composeInEdit = AppStore.composeInEdit;
 
-		this.messageList = Data.messageList;
-		this.folderList = Data.folderList;
-		this.folderListSystem = Data.folderListSystem;
-		this.foldersChanging = Data.foldersChanging;
+		this.messageList = MessageStore.messageList;
+		this.folderList = FolderStore.folderList;
+		this.folderListSystem = FolderStore.folderListSystem;
+		this.foldersChanging = FolderStore.foldersChanging;
 
-		this.foldersListWithSingleInboxRootFolder = Data.foldersListWithSingleInboxRootFolder;
+		this.foldersListWithSingleInboxRootFolder = FolderStore.foldersListWithSingleInboxRootFolder;
 
 		this.leftPanelDisabled = Globals.leftPanelDisabled;
 
@@ -94,10 +95,10 @@
 				{
 					if (Enums.Layout.NoPreview === SettingsStore.layout())
 					{
-						Data.message(null);
+						MessageStore.message(null);
 					}
 
-					if (oFolder.fullNameRaw === Data.currentFolderFullNameRaw())
+					if (oFolder.fullNameRaw === FolderStore.currentFolderFullNameRaw())
 					{
 						Cache.setFolderHash(oFolder.fullNameRaw, '');
 					}
@@ -143,7 +144,7 @@
 			var $items = $('.b-folders .e-item .e-link:not(.hidden).focused', oDom);
 			if ($items.length && $items[0])
 			{
-				self.folderList.focused(false);
+				FolderStore.folderList.focused(false);
 				$items.click();
 			}
 
@@ -167,11 +168,11 @@
 		});
 
 		key('esc, tab, shift+tab, right', Enums.KeyState.FolderList, function () {
-			self.folderList.focused(false);
+			FolderStore.folderList.focused(false);
 			return false;
 		});
 
-		self.folderList.focused.subscribe(function (bValue) {
+		FolderStore.folderList.focused.subscribe(function (bValue) {
 			$('.b-folders .e-item .e-link.focused', oDom).removeClass('focused');
 			if (bValue)
 			{
diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js
index 8a9ab8ccd..9ae318f0a 100644
--- a/dev/View/User/MailBox/MessageList.js
+++ b/dev/View/User/MailBox/MessageList.js
@@ -23,10 +23,10 @@
 		QuotaStore = require('Stores/User/Quota'),
 		SettingsStore = require('Stores/User/Settings'),
 		FolderStore = require('Stores/User/Folder'),
+		MessageStore = require('Stores/User/Message'),
 
 		Settings = require('Storage/Settings'),
 		Cache = require('Storage/User/Cache'),
-		Data = require('Storage/User/Data'),
 		Remote = require('Storage/User/Remote'),
 
 		kn = require('Knoin/Knoin'),
@@ -49,24 +49,24 @@
 
 		this.popupVisibility = Globals.popupVisibility;
 
-		this.message = Data.message;
-		this.messageList = Data.messageList;
-		this.folderList = Data.folderList;
-		this.currentMessage = Data.currentMessage;
-		this.isMessageSelected = Data.isMessageSelected;
-		this.messageListSearch = Data.messageListSearch;
-		this.messageListError = Data.messageListError;
-		this.folderMenuForMove = Data.folderMenuForMove;
+		this.message = MessageStore.message;
+		this.messageList = MessageStore.messageList;
+		this.folderList = FolderStore.folderList;
+		this.currentMessage = MessageStore.currentMessage;
+		this.isMessageSelected = MessageStore.isMessageSelected;
+		this.messageListSearch = MessageStore.messageListSearch;
+		this.messageListError = MessageStore.messageListError;
+		this.folderMenuForMove = FolderStore.folderMenuForMove;
 
 		this.useCheckboxesInList = SettingsStore.useCheckboxesInList;
 
-		this.mainMessageListSearch = Data.mainMessageListSearch;
-		this.messageListEndFolder = Data.messageListEndFolder;
+		this.mainMessageListSearch = MessageStore.mainMessageListSearch;
+		this.messageListEndFolder = MessageStore.messageListEndFolder;
 
-		this.messageListChecked = Data.messageListChecked;
-		this.messageListCheckedOrSelected = Data.messageListCheckedOrSelected;
-		this.messageListCheckedOrSelectedUidsWithSubMails = Data.messageListCheckedOrSelectedUidsWithSubMails;
-		this.messageListCompleteLoadingThrottle = Data.messageListCompleteLoadingThrottle;
+		this.messageListChecked = MessageStore.messageListChecked;
+		this.messageListCheckedOrSelected = MessageStore.messageListCheckedOrSelected;
+		this.messageListCheckedOrSelectedUidsWithSubMails = MessageStore.messageListCheckedOrSelectedUidsWithSubMails;
+		this.messageListCompleteLoadingThrottle = MessageStore.messageListCompleteLoadingThrottle;
 
 		Translator.initOnStartOrLangChange(function () {
 			this.emptySubjectValue = Translator.i18n('MESSAGE_LIST/EMPTY_SUBJECT_TEXT');
@@ -91,20 +91,21 @@
 		});
 
 		this.messageListSearchDesc = ko.computed(function () {
-			var sValue = Data.messageListEndSearch();
+			var sValue = MessageStore.messageListEndSearch();
 			return '' === sValue ? '' : Translator.i18n('MESSAGE_LIST/SEARCH_RESULT_FOR', {'SEARCH': sValue});
 		});
 
-		this.messageListPagenator = ko.computed(Utils.computedPagenatorHelper(Data.messageListPage, Data.messageListPageCount));
+		this.messageListPagenator = ko.computed(Utils.computedPagenatorHelper(
+			MessageStore.messageListPage, MessageStore.messageListPageCount));
 
 		this.checkAll = ko.computed({
 			'read': function () {
-				return 0 < Data.messageListChecked().length;
+				return 0 < MessageStore.messageListChecked().length;
 			},
 
 			'write': function (bValue) {
 				bValue = !!bValue;
-				_.each(Data.messageList(), function (oMessage) {
+				_.each(MessageStore.messageList(), function (oMessage) {
 					oMessage.checked(bValue);
 				});
 			}
@@ -123,8 +124,8 @@
 
 		this.isIncompleteChecked = ko.computed(function () {
 			var
-				iM = Data.messageList().length,
-				iC = Data.messageListChecked().length
+				iM = MessageStore.messageList().length,
+				iC = MessageStore.messageListChecked().length
 			;
 			return 0 < iM && 0 < iC && iM > iC;
 		}, this);
@@ -173,48 +174,48 @@
 		this.canBeMoved = this.hasCheckedOrSelectedLines;
 
 		this.clearCommand = Utils.createCommand(this, function () {
-			kn.showScreenPopup(require('View/Popup/FolderClear'), [Data.currentFolder()]);
+			kn.showScreenPopup(require('View/Popup/FolderClear'), [FolderStore.currentFolder()]);
 		});
 
 		this.multyForwardCommand = Utils.createCommand(this, function () {
 			kn.showScreenPopup(require('View/Popup/Compose'), [
-				Enums.ComposeType.ForwardAsAttachment, Data.messageListCheckedOrSelected()]);
+				Enums.ComposeType.ForwardAsAttachment, MessageStore.messageListCheckedOrSelected()]);
 		}, this.canBeMoved);
 
 		this.deleteWithoutMoveCommand = Utils.createCommand(this, function () {
 			require('App/User').deleteMessagesFromFolder(Enums.FolderType.Trash,
-				Data.currentFolderFullNameRaw(),
-				Data.messageListCheckedOrSelectedUidsWithSubMails(), false);
+				FolderStore.currentFolderFullNameRaw(),
+				MessageStore.messageListCheckedOrSelectedUidsWithSubMails(), false);
 		}, this.canBeMoved);
 
 		this.deleteCommand = Utils.createCommand(this, function () {
 			require('App/User').deleteMessagesFromFolder(Enums.FolderType.Trash,
-				Data.currentFolderFullNameRaw(),
-				Data.messageListCheckedOrSelectedUidsWithSubMails(), true);
+				FolderStore.currentFolderFullNameRaw(),
+				MessageStore.messageListCheckedOrSelectedUidsWithSubMails(), true);
 		}, this.canBeMoved);
 
 		this.archiveCommand = Utils.createCommand(this, function () {
 			require('App/User').deleteMessagesFromFolder(Enums.FolderType.Archive,
-				Data.currentFolderFullNameRaw(),
-				Data.messageListCheckedOrSelectedUidsWithSubMails(), true);
+				FolderStore.currentFolderFullNameRaw(),
+				MessageStore.messageListCheckedOrSelectedUidsWithSubMails(), true);
 		}, this.canBeMoved);
 
 		this.spamCommand = Utils.createCommand(this, function () {
 			require('App/User').deleteMessagesFromFolder(Enums.FolderType.Spam,
-				Data.currentFolderFullNameRaw(),
-				Data.messageListCheckedOrSelectedUidsWithSubMails(), true);
+				FolderStore.currentFolderFullNameRaw(),
+				MessageStore.messageListCheckedOrSelectedUidsWithSubMails(), true);
 		}, this.canBeMoved);
 
 		this.notSpamCommand = Utils.createCommand(this, function () {
 			require('App/User').deleteMessagesFromFolder(Enums.FolderType.NotSpam,
-				Data.currentFolderFullNameRaw(),
-				Data.messageListCheckedOrSelectedUidsWithSubMails(), true);
+				FolderStore.currentFolderFullNameRaw(),
+				MessageStore.messageListCheckedOrSelectedUidsWithSubMails(), true);
 		}, this.canBeMoved);
 
 		this.moveCommand = Utils.createCommand(this, Utils.emptyFunction, this.canBeMoved);
 
 		this.reloadCommand = Utils.createCommand(this, function () {
-			if (!Data.messageListCompleteLoadingThrottle())
+			if (!MessageStore.messageListCompleteLoadingThrottle())
 			{
 				require('App/User').reloadMessageList(false, true);
 			}
@@ -229,18 +230,19 @@
 		this.selector.on('onItemSelect', _.bind(function (oMessage) {
 			if (oMessage)
 			{
-				Data.message(Data.staticMessageList.populateByMessageListItem(oMessage));
-				this.populateMessageBody(Data.message());
+				MessageStore.message(MessageStore.staticMessage.populateByMessageListItem(oMessage));
+
+				this.populateMessageBody(MessageStore.message());
 
 				if (Enums.Layout.NoPreview === SettingsStore.layout())
 				{
 					kn.setHash(Links.messagePreview(), true);
-					Data.message.focused(true);
+					MessageStore.message.focused(true);
 				}
 			}
 			else
 			{
-				Data.message(null);
+				MessageStore.message(null);
 			}
 		}, this));
 
@@ -248,23 +250,14 @@
 			return oMessage ? oMessage.generateUid() : '';
 		});
 
-		Data.messageListEndHash.subscribe(function () {
+		MessageStore.messageListEndHash.subscribe(function () {
 			this.selector.scrollToTop();
 		}, this);
 
-		Data.messageListEndFolder.subscribe(function (sFolder) {
-			var oMessage = Data.message();
-			if (oMessage && sFolder && sFolder !== oMessage.folderFullNameRaw)
-			{
-				Data.message(null);
-			}
-		}, this);
-
 		SettingsStore.layout.subscribe(function (mValue) {
 			this.selector.autoSelect(Enums.Layout.NoPreview !== mValue);
 		}, this);
 
-
 		SettingsStore.layout.valueHasMutated();
 
 		Events
@@ -318,8 +311,8 @@
 		if (this.canBeMoved())
 		{
 			require('App/User').moveMessagesToFolder(
-				Data.currentFolderFullNameRaw(),
-				Data.messageListCheckedOrSelectedUidsWithSubMails(), sToFolderFullNameRaw, bCopy);
+				FolderStore.currentFolderFullNameRaw(),
+				MessageStore.messageListCheckedOrSelectedUidsWithSubMails(), sToFolderFullNameRaw, bCopy);
 		}
 
 		return false;
@@ -334,15 +327,15 @@
 
 		var
 			oEl = Utils.draggablePlace(),
-			aUids = Data.messageListCheckedOrSelectedUidsWithSubMails()
+			aUids = MessageStore.messageListCheckedOrSelectedUidsWithSubMails()
 		;
 
-		oEl.data('rl-folder', Data.currentFolderFullNameRaw());
+		oEl.data('rl-folder', FolderStore.currentFolderFullNameRaw());
 		oEl.data('rl-uids', aUids);
 		oEl.find('.text').text('' + aUids.length);
 
 		_.defer(function () {
-			var aUids = Data.messageListCheckedOrSelectedUidsWithSubMails();
+			var aUids = MessageStore.messageListCheckedOrSelectedUidsWithSubMails();
 
 			oEl.data('rl-uids', aUids);
 			oEl.find('.text').text('' + aUids.length);
@@ -358,22 +351,22 @@
 	 */
 	MessageListMailBoxUserView.prototype.onMessageResponse = function (sResult, oData, bCached)
 	{
-		Data.hideMessageBodies();
-		Data.messageLoading(false);
+		MessageStore.hideMessageBodies();
+		MessageStore.messageLoading(false);
 
 		if (Enums.StorageResultType.Success === sResult && oData && oData.Result)
 		{
-			Data.setMessage(oData, bCached);
+			MessageStore.setMessage(oData, bCached);
 		}
 		else if (Enums.StorageResultType.Unload === sResult)
 		{
-			Data.message(null);
-			Data.messageError('');
+			MessageStore.message(null);
+			MessageStore.messageError('');
 		}
 		else if (Enums.StorageResultType.Abort !== sResult)
 		{
-			Data.message(null);
-			Data.messageError((oData && oData.ErrorCode ?
+			MessageStore.message(null);
+			MessageStore.messageError((oData && oData.ErrorCode ?
 				Translator.getNotification(oData.ErrorCode) :
 				Translator.getNotification(Enums.Notification.UnknownError)));
 		}
@@ -385,7 +378,7 @@
 		{
 			if (Remote.message(this.onMessageResponse, oMessage.folderFullNameRaw, oMessage.uid))
 			{
-				Data.messageLoading(true);
+				MessageStore.messageLoading(true);
 			}
 			else
 			{
@@ -409,7 +402,7 @@
 
 		if (Utils.isUnd(aMessages))
 		{
-			aMessages = Data.messageListChecked();
+			aMessages = MessageStore.messageListChecked();
 		}
 
 		aUids = _.map(aMessages, function (oMessage) {
@@ -484,7 +477,7 @@
 	{
 		var
 			oFolder = null,
-			aMessages = Data.messageList()
+			aMessages = MessageStore.messageList()
 		;
 
 		if ('' !== sFolderFullNameRaw)
@@ -530,27 +523,31 @@
 
 	MessageListMailBoxUserView.prototype.listSetSeen = function ()
 	{
-		this.setAction(Data.currentFolderFullNameRaw(), Enums.MessageSetAction.SetSeen, Data.messageListCheckedOrSelected());
+		this.setAction(FolderStore.currentFolderFullNameRaw(), Enums.MessageSetAction.SetSeen,
+			MessageStore.messageListCheckedOrSelected());
 	};
 
 	MessageListMailBoxUserView.prototype.listSetAllSeen = function ()
 	{
-		this.setActionForAll(Data.currentFolderFullNameRaw(), Enums.MessageSetAction.SetSeen);
+		this.setActionForAll(FolderStore.currentFolderFullNameRaw(), Enums.MessageSetAction.SetSeen);
 	};
 
 	MessageListMailBoxUserView.prototype.listUnsetSeen = function ()
 	{
-		this.setAction(Data.currentFolderFullNameRaw(), Enums.MessageSetAction.UnsetSeen, Data.messageListCheckedOrSelected());
+		this.setAction(FolderStore.currentFolderFullNameRaw(), Enums.MessageSetAction.UnsetSeen,
+			MessageStore.messageListCheckedOrSelected());
 	};
 
 	MessageListMailBoxUserView.prototype.listSetFlags = function ()
 	{
-		this.setAction(Data.currentFolderFullNameRaw(), Enums.MessageSetAction.SetFlag, Data.messageListCheckedOrSelected());
+		this.setAction(FolderStore.currentFolderFullNameRaw(), Enums.MessageSetAction.SetFlag,
+			MessageStore.messageListCheckedOrSelected());
 	};
 
 	MessageListMailBoxUserView.prototype.listUnsetFlags = function ()
 	{
-		this.setAction(Data.currentFolderFullNameRaw(), Enums.MessageSetAction.UnsetFlag, Data.messageListCheckedOrSelected());
+		this.setAction(FolderStore.currentFolderFullNameRaw(), Enums.MessageSetAction.UnsetFlag,
+			MessageStore.messageListCheckedOrSelected());
 	};
 
 	MessageListMailBoxUserView.prototype.flagMessages = function (oCurrentMessage)
@@ -649,9 +646,9 @@
 
 			if (oMessage && !oMessage.lastInCollapsedThreadLoading())
 			{
-				Data.messageListThreadFolder(oMessage.folderFullNameRaw);
+				MessageStore.messageListThreadFolder(oMessage.folderFullNameRaw);
 
-				aList = Data.messageListThreadUids();
+				aList = MessageStore.messageListThreadUids();
 
 				if (oMessage.lastInCollapsedThread())
 				{
@@ -662,7 +659,7 @@
 					aList = _.without(aList, 0 < oMessage.parentUid() ? oMessage.parentUid() : oMessage.uid);
 				}
 
-				Data.messageListThreadUids(_.uniq(aList));
+				MessageStore.messageListThreadUids(_.uniq(aList));
 
 				oMessage.lastInCollapsedThreadLoading(true);
 				oMessage.lastInCollapsedThread(!oMessage.lastInCollapsedThread());
@@ -687,9 +684,9 @@
 				if (oPage)
 				{
 					kn.setHash(Links.mailBox(
-						Data.currentFolderFullNameHash(),
+						FolderStore.currentFolderFullNameHash(),
 						oPage.value,
-						Data.messageListSearch()
+						MessageStore.messageListSearch()
 					));
 				}
 			})
@@ -733,7 +730,7 @@
 		key('delete, shift+delete, shift+3', Enums.KeyState.MessageList, function (event, handler) {
 			if (event)
 			{
-				if (0 < Data.messageListCheckedOrSelected().length)
+				if (0 < MessageStore.messageListCheckedOrSelected().length)
 				{
 					if (handler && 'shift+delete' === handler.shortcut)
 					{
@@ -815,7 +812,7 @@
 		key('tab, shift+tab, left, right', Enums.KeyState.MessageList, function (event, handler) {
 			if (event && handler && ('shift+tab' === handler.shortcut || 'left' === handler.shortcut))
 			{
-				self.folderList.focused(true);
+				FolderStore.folderList.focused(true);
 			}
 			else if (self.message())
 			{
@@ -904,7 +901,7 @@
 				'disableFolderDragAndDrop': true,
 				'hidden': {
 					'Folder': function () {
-						return Data.currentFolderFullNameRaw();
+						return FolderStore.currentFolderFullNameRaw();
 					}
 				},
 				'dragAndDropElement': this.dragOverArea(),
@@ -933,13 +930,15 @@
 				this.dragOver(false);
 			}, this))
 			.on('onSelect', _.bind(function (sUid, oData) {
+
 				if (sUid && oData && 'message/rfc822' === oData['Type'])
 				{
-					Data.messageListLoading(true);
+					MessageStore.messageListLoading(true);
 					return true;
 				}
 
 				return false;
+
 			}, this))
 			.on('onComplete', _.bind(function () {
 				require('App/User').reloadMessageList(true, true);
diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js
index 980f7ada8..9494f5ca4 100644
--- a/dev/View/User/MailBox/MessageView.js
+++ b/dev/View/User/MailBox/MessageView.js
@@ -22,10 +22,10 @@
 		SettingsStore = require('Stores/User/Settings'),
 		AccountStore = require('Stores/User/Account'),
 		FolderStore = require('Stores/User/Folder'),
+		MessageStore = require('Stores/User/Message'),
 
 		Local = require('Storage/Client'),
 		Cache = require('Storage/User/Cache'),
-		Data = require('Storage/User/Data'),
 		Remote = require('Storage/User/Remote'),
 
 		kn = require('Knoin/Knoin'),
@@ -56,23 +56,23 @@
 
 		this.pswp = null;
 
-		this.message = Data.message;
-		this.currentMessage = Data.currentMessage;
-		this.messageListChecked = Data.messageListChecked;
-		this.hasCheckedMessages = Data.hasCheckedMessages;
-		this.messageListCheckedOrSelectedUidsWithSubMails = Data.messageListCheckedOrSelectedUidsWithSubMails;
-		this.messageLoading = Data.messageLoading;
-		this.messageLoadingThrottle = Data.messageLoadingThrottle;
-		this.messagesBodiesDom = Data.messagesBodiesDom;
+		this.message = MessageStore.message;
+		this.currentMessage = MessageStore.currentMessage;
+		this.messageListChecked = MessageStore.messageListChecked;
+		this.hasCheckedMessages = MessageStore.hasCheckedMessages;
+		this.messageListCheckedOrSelectedUidsWithSubMails = MessageStore.messageListCheckedOrSelectedUidsWithSubMails;
+		this.messageLoading = MessageStore.messageLoading;
+		this.messageLoadingThrottle = MessageStore.messageLoadingThrottle;
+		this.messagesBodiesDom = MessageStore.messagesBodiesDom;
 		this.useThreads = SettingsStore.useThreads;
 		this.replySameFolder = SettingsStore.replySameFolder;
 		this.layout = SettingsStore.layout;
 		this.usePreviewPane = SettingsStore.usePreviewPane;
-		this.isMessageSelected = Data.isMessageSelected;
-		this.messageActiveDom = Data.messageActiveDom;
-		this.messageError = Data.messageError;
+		this.isMessageSelected = MessageStore.isMessageSelected;
+		this.messageActiveDom = MessageStore.messageActiveDom;
+		this.messageError = MessageStore.messageError;
 
-		this.fullScreenMode = Data.messageFullScreenMode;
+		this.fullScreenMode = MessageStore.messageFullScreenMode;
 
 		this.lastReplyAction_ = ko.observable('');
 		this.lastReplyAction = ko.computed({
@@ -113,7 +113,7 @@
 
 		// commands
 		this.closeMessage = Utils.createCommand(this, function () {
-			Data.message(null);
+			MessageStore.message(null);
 		});
 
 		this.replyCommand = createCommandHelper(Enums.ComposeType.Reply);
@@ -141,7 +141,7 @@
 			if (this.message())
 			{
 				require('App/User').deleteMessagesFromFolder(Enums.FolderType.Trash,
-					Data.currentFolderFullNameRaw(),
+					FolderStore.currentFolderFullNameRaw(),
 					[this.message().uid], false);
 			}
 		}, this.messageVisibility);
@@ -395,7 +395,7 @@
 	 */
 	MessageViewMailBoxUserView.prototype.replyOrforward = function (sType)
 	{
-		kn.showScreenPopup(require('View/Popup/Compose'), [sType, Data.message()]);
+		kn.showScreenPopup(require('View/Popup/Compose'), [sType, MessageStore.message()]);
 	};
 
 	MessageViewMailBoxUserView.prototype.onBuild = function (oDom)
@@ -619,7 +619,7 @@
 
 		// reply
 		key('r', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
-			if (Data.message())
+			if (MessageStore.message())
 			{
 				self.replyCommand();
 				return false;
@@ -628,7 +628,7 @@
 
 		// replaAll
 		key('a', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
-			if (Data.message())
+			if (MessageStore.message())
 			{
 				self.replyAllCommand();
 				return false;
@@ -637,7 +637,7 @@
 
 		// forward
 		key('f', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
-			if (Data.message())
+			if (MessageStore.message())
 			{
 				self.forwardCommand();
 				return false;
@@ -646,7 +646,7 @@
 
 		// message information
 	//	key('i', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
-	//		if (oData.message())
+	//		if (MessageStore.message())
 	//		{
 	//			self.showFullInfo(!self.showFullInfo());
 	//			return false;
@@ -655,9 +655,9 @@
 
 		// toggle message blockquotes
 		key('b', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
-			if (Data.message() && Data.message().body)
+			if (MessageStore.message() && MessageStore.message().body)
 			{
-				Data.message().body.find('.rlBlockquoteSwitcher').click();
+				MessageStore.message().body.find('.rlBlockquoteSwitcher').click();
 				return false;
 			}
 		});
@@ -731,7 +731,7 @@
 	 */
 	MessageViewMailBoxUserView.prototype.isDraftFolder = function ()
 	{
-		return Data.message() && FolderStore.draftFolder() === Data.message().folderFullNameRaw;
+		return MessageStore.message() && FolderStore.draftFolder() === MessageStore.message().folderFullNameRaw;
 	};
 
 	/**
@@ -739,7 +739,7 @@
 	 */
 	MessageViewMailBoxUserView.prototype.isSentFolder = function ()
 	{
-		return Data.message() && FolderStore.sentFolder() === Data.message().folderFullNameRaw;
+		return MessageStore.message() && FolderStore.sentFolder() === MessageStore.message().folderFullNameRaw;
 	};
 
 	/**
@@ -747,7 +747,7 @@
 	 */
 	MessageViewMailBoxUserView.prototype.isSpamFolder = function ()
 	{
-		return Data.message() && FolderStore.spamFolder() === Data.message().folderFullNameRaw;
+		return MessageStore.message() && FolderStore.spamFolder() === MessageStore.message().folderFullNameRaw;
 	};
 
 	/**
@@ -755,7 +755,7 @@
 	 */
 	MessageViewMailBoxUserView.prototype.isSpamDisabled = function ()
 	{
-		return Data.message() && FolderStore.spamFolder() === Consts.Values.UnuseOptionValue;
+		return MessageStore.message() && FolderStore.spamFolder() === Consts.Values.UnuseOptionValue;
 	};
 
 	/**
@@ -763,7 +763,7 @@
 	 */
 	MessageViewMailBoxUserView.prototype.isArchiveFolder = function ()
 	{
-		return Data.message() && FolderStore.archiveFolder() === Data.message().folderFullNameRaw;
+		return MessageStore.message() && FolderStore.archiveFolder() === MessageStore.message().folderFullNameRaw;
 	};
 
 	/**
@@ -771,7 +771,7 @@
 	 */
 	MessageViewMailBoxUserView.prototype.isArchiveDisabled = function ()
 	{
-		return Data.message() && FolderStore.archiveFolder() === Consts.Values.UnuseOptionValue;
+		return MessageStore.message() && FolderStore.archiveFolder() === Consts.Values.UnuseOptionValue;
 	};
 
 	/**
@@ -789,9 +789,9 @@
 
 	MessageViewMailBoxUserView.prototype.editMessage = function ()
 	{
-		if (Data.message())
+		if (MessageStore.message())
 		{
-			kn.showScreenPopup(require('View/Popup/Compose'), [Enums.ComposeType.Draft, Data.message()]);
+			kn.showScreenPopup(require('View/Popup/Compose'), [Enums.ComposeType.Draft, MessageStore.message()]);
 		}
 	};
 
diff --git a/dev/View/User/Settings/Pane.js b/dev/View/User/Settings/Pane.js
index 129710a83..845044cdc 100644
--- a/dev/View/User/Settings/Pane.js
+++ b/dev/View/User/Settings/Pane.js
@@ -6,11 +6,6 @@
 	var
 		_ = require('_'),
 
-		Links = require('Common/Links'),
-
-		Data = require('Storage/User/Data'),
-		Cache = require('Storage/User/Cache'),
-
 		kn = require('Knoin/Knoin'),
 		AbstractView = require('Knoin/AbstractView')
 	;
@@ -31,12 +26,13 @@
 
 	PaneSettingsUserView.prototype.onShow = function ()
 	{
-		Data.message(null);
+		require('Stores/User/Message').message(null);
 	};
 
 	PaneSettingsUserView.prototype.backToMailBoxClick = function ()
 	{
-		kn.setHash(Links.inbox(Cache.getFolderInboxName()));
+		kn.setHash(require('Common/Links').inbox(
+			require('Storage/User/Cache').getFolderInboxName()));
 	};
 
 	module.exports = PaneSettingsUserView;
diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php b/rainloop/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php
index 0758c0079..846f5df1a 100644
--- a/rainloop/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php
+++ b/rainloop/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php
@@ -306,13 +306,14 @@ class SmtpClient extends \MailSo\Net\NetClient
 	/**
 	 * @param string $sFrom
 	 * @param string $sSizeIfSupported = ''
+	 * @param bool $bDsn = false
 	 *
 	 * @return \MailSo\Smtp\SmtpClient
 	 *
 	 * @throws \MailSo\Net\Exceptions\Exception
 	 * @throws \MailSo\Smtp\Exceptions\Exception
 	 */
-	public function MailFrom($sFrom, $sSizeIfSupported = '')
+	public function MailFrom($sFrom, $sSizeIfSupported = '', $bDsn = false)
 	{
 		$sFrom = \MailSo\Base\Utils::IdnToAscii($sFrom, true);
 		$sCmd = 'FROM:<'.$sFrom.'>';
@@ -323,6 +324,11 @@ class SmtpClient extends \MailSo\Net\NetClient
 			$sCmd .= ' SIZE='.$sSizeIfSupported;
 		}
 
+		if ($bDsn && $this->IsSupported('DSN'))
+		{
+			$sCmd .= ' RET=HDRS';
+		}
+
 		$this->sendRequestWithCheck('MAIL', 250, $sCmd);
 
 		$this->bMail = true;
@@ -334,13 +340,14 @@ class SmtpClient extends \MailSo\Net\NetClient
 
 	/**
 	 * @param string $sTo
+	 * @param bool $bDsn = false
 	 *
 	 * @return \MailSo\Smtp\SmtpClient
 	 *
 	 * @throws \MailSo\Net\Exceptions\Exception
 	 * @throws \MailSo\Smtp\Exceptions\Exception
 	 */
-	public function Rcpt($sTo)
+	public function Rcpt($sTo, $bDsn = false)
 	{
 		if (!$this->bMail)
 		{
@@ -350,7 +357,14 @@ class SmtpClient extends \MailSo\Net\NetClient
 		}
 
 		$sTo = \MailSo\Base\Utils::IdnToAscii($sTo, true);
-		$this->sendRequestWithCheck('RCPT', array(250, 251), 'TO:<'.$sTo.'>');
+		$sCmd = 'TO:<'.$sTo.'>';
+
+		if ($bDsn && $this->IsSupported('DSN'))
+		{
+			$sCmd .= ' NOTIFY=SUCCESS,FAILURE';
+		}
+
+		$this->sendRequestWithCheck('RCPT', array(250, 251), $sCmd);
 
 		$this->bRcpt = true;
 
diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php
index 532e4b534..2f48ed040 100644
--- a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php
+++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php
@@ -5752,12 +5752,14 @@ class Actions
 	 * @param \RainLoop\Model\Account $oAccount
 	 * @param \MailSo\Mime\Message $oMessage
 	 * @param resource $rMessageStream
+	 * @param bool $bDsn = true
 	 * @param bool $bAddHiddenRcpt = true
 	 *
 	 * @throws \RainLoop\Exceptions\ClientException
 	 * @throws \MailSo\Net\Exceptions\ConnectionException
 	 */
-	private function smtpSendMessage($oAccount, $oMessage, &$rMessageStream, &$iMessageStreamSize, $bAddHiddenRcpt = true)
+	private function smtpSendMessage($oAccount, $oMessage,
+		&$rMessageStream, &$iMessageStreamSize, $bDsn = false, $bAddHiddenRcpt = true)
 	{
 		$oRcpt = $oMessage->GetRcpt();
 		if ($oRcpt && 0 < $oRcpt->Count())
@@ -5773,9 +5775,9 @@ class Actions
 				$sFrom = $oFrom instanceof \MailSo\Mime\Email ? $oFrom->GetEmail() : '';
 				$sFrom = empty($sFrom) ? $oAccount->Email() : $sFrom;
 
-				$aHiddenRcpt = array();
 				$this->Plugins()->RunHook('filter.smtp-from', array($oAccount, $oMessage, &$sFrom));
 
+				$aHiddenRcpt = array();
 				if ($bAddHiddenRcpt)
 				{
 					$this->Plugins()->RunHook('filter.smtp-hidden-rcpt', array($oAccount, $oMessage, &$aHiddenRcpt));
@@ -5806,9 +5808,9 @@ class Actions
 								list($sMailHeaders, $sMailBody) = \explode("\r\n\r\n", $sRawBody, 2);
 								unset($sRawBody);
 
-								$this->Logger()->WriteDump(array(
-									$sMailTo, $sMailSubject, $sMailBody, $sMailHeaders
-								));
+//								$this->Logger()->WriteDump(array(
+//									$sMailTo, $sMailSubject, $sMailBody, $sMailHeaders
+//								));
 
 								if (!\mail($sMailTo, $sMailSubject, $sMailBody, $sMailHeaders/*, '-f'.$oFrom->GetEmail()*/))
 								{
@@ -5824,15 +5826,17 @@ class Actions
 				}
 				else if ($oSmtpClient->IsConnected())
 				{
+					$bDsn = false;
+
 					if (!empty($sFrom))
 					{
-						$oSmtpClient->MailFrom($sFrom);
+						$oSmtpClient->MailFrom($sFrom, '', $bDsn);
 					}
 
 					$aRcpt =& $oRcpt->GetAsArray();
 					foreach ($aRcpt as /* @var $oEmail \MailSo\Mime\Email */ $oEmail)
 					{
-						$oSmtpClient->Rcpt($oEmail->GetEmail());
+						$oSmtpClient->Rcpt($oEmail->GetEmail(), $bDsn);
 					}
 
 					if ($bAddHiddenRcpt && \is_array($aHiddenRcpt) && 0 < \count($aHiddenRcpt))
@@ -5902,6 +5906,7 @@ class Actions
 		$sDraftUid = $this->GetActionParam('MessageUid', '');
 		$sSentFolder = $this->GetActionParam('SentFolder', '');
 		$aDraftInfo = $this->GetActionParam('DraftInfo', null);
+		$bDsn = '1' === $this->GetActionParam('Dsn', '0');
 
 		$oMessage = $this->buildMessage($oAccount, false);
 
@@ -5922,7 +5927,7 @@ class Actions
 
 				if (false !== $iMessageStreamSize)
 				{
-					$this->smtpSendMessage($oAccount, $oMessage, $rMessageStream, $iMessageStreamSize);
+					$this->smtpSendMessage($oAccount, $oMessage, $rMessageStream, $iMessageStreamSize, $bDsn, true);
 
 					$this->deleteMessageAttachmnets($oAccount);
 
@@ -6096,7 +6101,7 @@ class Actions
 
 				if (false !== $iMessageStreamSize)
 				{
-					$this->smtpSendMessage($oAccount, $oMessage, $rMessageStream, $iMessageStreamSize);
+					$this->smtpSendMessage($oAccount, $oMessage, $rMessageStream, $iMessageStreamSize, false, false);
 
 					if (\is_resource($rMessageStream))
 					{
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/PopupsCompose.html b/rainloop/v/0.0.0/app/templates/Views/User/PopupsCompose.html
index 60318b591..f04a2842e 100644
--- a/rainloop/v/0.0.0/app/templates/Views/User/PopupsCompose.html
+++ b/rainloop/v/0.0.0/app/templates/Views/User/PopupsCompose.html
@@ -192,7 +192,7 @@
 					
-
+
    @@ -204,7 +204,7 @@
    + data-bind="visible: !attachmentsPlace(), initDom: composeEditorArea, initResizeTrigger: [resizer(), 200, 40]">
    diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFilters.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFilters.html index 03c72b1d6..967d1563d 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFilters.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFilters.html @@ -7,7 +7,7 @@