From 80ab02363eccbc9af297f79a621c26ba135f0182 Mon Sep 17 00:00:00 2001 From: RainLoop Team Date: Fri, 17 Jun 2016 02:23:49 +0300 Subject: [PATCH] Code refactoring (v2) --- dev/App/Abstract.jsx | 4 +- dev/App/Admin.jsx | 9 +- dev/App/User.jsx | 22 +- dev/Common/Booter.jsx | 68 ++- dev/Common/Cache.jsx | 635 ++++++++++++++------------- dev/Common/Enums.jsx | 3 +- dev/Common/Momentor.jsx | 6 +- dev/Common/Selector.jsx | 13 +- dev/Common/Translator.jsx | 608 +++++++++++++------------ dev/Common/Utils.jsx | 4 +- dev/Component/Select.jsx | 4 +- dev/Knoin/AbstractScreen.js | 2 +- dev/Model/ComposeAttachment.js | 2 +- dev/Promises/User/Populator.js | 2 +- dev/Settings/Admin/General.js | 12 +- dev/Settings/Admin/Plugins.js | 2 +- dev/Settings/Admin/Security.js | 6 +- dev/Settings/Admin/Social.js | 14 +- dev/Settings/User/Folders.js | 8 +- dev/Settings/User/General.js | 12 +- dev/Storage/Client.jsx | 45 +- dev/Storage/RainLoop.jsx | 157 +++---- dev/Stores/Language.js | 8 +- dev/View/Popup/FolderSystem.js | 2 +- dev/View/User/Login.js | 4 +- dev/View/User/MailBox/MessageList.js | 11 +- dev/View/User/MailBox/MessageView.js | 25 +- dev/bootstrap.jsx | 4 +- jsconfig.json | 23 + 29 files changed, 858 insertions(+), 857 deletions(-) create mode 100644 jsconfig.json diff --git a/dev/App/Abstract.jsx b/dev/App/Abstract.jsx index b5149bceb..1d6b15cb6 100644 --- a/dev/App/Abstract.jsx +++ b/dev/App/Abstract.jsx @@ -12,7 +12,7 @@ import {noop, isNormal, pString, inArray, microtime, timestamp, detectDropdownVi import * as Links from 'Common/Links'; import * as Settings from 'Storage/Settings'; import * as Events from 'Common/Events'; -import Translator from 'Common/Translator'; +import {initOnStartOrLangChange, initNotificationLanguage} from 'Common/Translator'; import {AbstractBoot} from 'Knoin/AbstractBoot'; @@ -307,7 +307,7 @@ class AbstractApp extends AbstractBoot ko.components.register('CheckboxSimple', require('Component/Checkbox')); } - Translator.initOnStartOrLangChange(Translator.initNotificationLanguage, Translator); + initOnStartOrLangChange(initNotificationLanguage); _.delay(windowResizeCallback, 1000); diff --git a/dev/App/Admin.jsx b/dev/App/Admin.jsx index 03fc70f12..6aa21b39e 100644 --- a/dev/App/Admin.jsx +++ b/dev/App/Admin.jsx @@ -4,7 +4,7 @@ import ko from 'ko'; import progressJs from 'progressJs'; import * as Links from 'Common/Links'; -import Translator from 'Common/Translator'; +import {getNotification} from 'Common/Translator'; import {StorageResultType, Notification} from 'Common/Enums'; import {pInt, isNormal, isArray, inArray, isUnd} from 'Common/Utils'; @@ -186,14 +186,14 @@ class AdminApp extends AbstractApp Notification.LicensingExpired ])) { - LicenseStore.licenseError(Translator.getNotification(pInt(data.ErrorCode))); + LicenseStore.licenseError(getNotification(pInt(data.ErrorCode))); LicenseStore.licensing(true); } else { if (StorageResultType.Abort === result) { - LicenseStore.licenseError(Translator.getNotification(Notification.LicensingServerIsUnavailable)); + LicenseStore.licenseError(getNotification(Notification.LicensingServerIsUnavailable)); LicenseStore.licensing(true); } else @@ -256,5 +256,4 @@ class AdminApp extends AbstractApp } } -const App = new AdminApp(); -export default App; +export default new AdminApp(); diff --git a/dev/App/User.jsx b/dev/App/User.jsx index 138fa5585..2fcee73d8 100644 --- a/dev/App/User.jsx +++ b/dev/App/User.jsx @@ -24,8 +24,8 @@ import * as Plugins from 'Common/Plugins'; import * as Links from 'Common/Links'; import * as Events from 'Common/Events'; import * as Momentor from 'Common/Momentor'; -import Translator from 'Common/Translator'; -import Cache from 'Common/Cache'; +import * as Cache from 'Common/Cache'; +import {getNotification, i18n} from 'Common/Translator'; import SocialStore from 'Stores/Social'; import SettingsStore from 'Stores/User/Settings'; @@ -37,9 +37,9 @@ import PgpStore from 'Stores/User/Pgp'; import MessageStore from 'Stores/User/Message'; import ContactStore from 'Stores/User/Contact'; -import Local from 'Storage/Client'; +import * as Local from 'Storage/Client'; import * as Settings from 'Storage/Settings'; -import RainLoopStorage from 'Storage/RainLoop'; +import {checkTimestamp} from 'Storage/RainLoop'; import Remote from 'Remote/User/Ajax'; import Promises from 'Promises/User/Ajax'; @@ -81,7 +81,7 @@ class AppUser extends AbstractApp window.setTimeout(() => window.setInterval(() => Events.pub('interval.10m-after5m'), 60000 * 10), 60000 * 5); $.wakeUp(() => { - if (RainLoopStorage.checkTimestamp()) + if (checkTimestamp()) { this.reload(); } @@ -95,7 +95,7 @@ class AppUser extends AbstractApp }, {}, 60 * 60 * 1000); - if (RainLoopStorage.checkTimestamp()) + if (checkTimestamp()) { this.reload(); } @@ -189,7 +189,7 @@ class AppUser extends AbstractApp MessageStore.messageList([]); MessageStore.messageListLoading(false); MessageStore.messageListError(oData && oData.ErrorCode ? - Translator.getNotification(oData.ErrorCode) : Translator.i18n('NOTIFICATIONS/CANT_GET_MESSAGE_LIST') + getNotification(oData.ErrorCode) : i18n('NOTIFICATIONS/CANT_GET_MESSAGE_LIST') ); } @@ -298,7 +298,7 @@ class AppUser extends AbstractApp if (oData && -1 < inArray(oData.ErrorCode, [Notification.CantMoveMessage, Notification.CantCopyMessage])) { - window.alert(Translator.getNotification(oData.ErrorCode)); + window.alert(getNotification(oData.ErrorCode)); } } @@ -366,7 +366,7 @@ class AppUser extends AbstractApp else if (!bUseFolder || (FolderType.Trash === iDeleteType && (sFromFolderFullNameRaw === FolderStore.spamFolder() || sFromFolderFullNameRaw === FolderStore.trashFolder()))) { - kn.showScreenPopup(require('View/Popup/Ask'), [Translator.i18n('POPUPS_ASK/DESC_WANT_DELETE_MESSAGES'), () => { + kn.showScreenPopup(require('View/Popup/Ask'), [i18n('POPUPS_ASK/DESC_WANT_DELETE_MESSAGES'), () => { this.messagesDeleteHelper(sFromFolderFullNameRaw, aUidForRemove); MessageStore.removeMessagesFromList(sFromFolderFullNameRaw, aUidForRemove); }]); @@ -437,7 +437,7 @@ class AppUser extends AbstractApp .fastResolve(true) .then(() => promise) .catch((errorCode) => { - FolderStore.folderList.error(Translator.getNotification(errorCode, '', errorDefCode)); + FolderStore.folderList.error(getNotification(errorCode, '', errorDefCode)); }).fin(() => { Promises.foldersReloadWithTimeout(FolderStore.foldersLoading); }).done() @@ -1296,7 +1296,7 @@ class AppUser extends AbstractApp } else { - this.setWindowTitle(Translator.i18n('TITLES/LOADING')); + this.setWindowTitle(i18n('TITLES/LOADING')); // require.ensure([], function() { // require code splitting diff --git a/dev/Common/Booter.jsx b/dev/Common/Booter.jsx index ab72aa09a..55fef7a77 100644 --- a/dev/Common/Booter.jsx +++ b/dev/Common/Booter.jsx @@ -2,36 +2,23 @@ import window from 'window'; import progressJs from 'progressJs'; -import RainLoopStorage from 'Storage/RainLoop'; +import STYLES_CSS from 'Styles/@Boot.css'; +import LAYOUT_HTML from 'Html/Layout.html'; -let rlAppDataStorage = null; +import {getHash, setHash, clearHash} from 'Storage/RainLoop'; -window.__rlah = () => { - return RainLoopStorage ? RainLoopStorage.getHash() : null; -}; +let RL_APP_DATA_STORAGE = null; -window.__rlah_data = () => { - return rlAppDataStorage; -}; - -window.__rlah_set = () => { - if (RainLoopStorage) - { - RainLoopStorage.setHash(); - } -}; - -window.__rlah_clear = () => { - if (RainLoopStorage) - { - RainLoopStorage.clearHash(); - } -}; +window.__rlah = () => getHash(); +window.__rlah_set = () => setHash(); +window.__rlah_clear = () => clearHash(); +window.__rlah_data = () => RL_APP_DATA_STORAGE; function getComputedStyle(id, name) { var element = window.document.getElementById(id); - return element.currentStyle ? element.currentStyle[name] : (window.getComputedStyle ? window.getComputedStyle(element, null).getPropertyValue(name) : null); + return element.currentStyle ? element.currentStyle[name] : + (window.getComputedStyle ? window.getComputedStyle(element, null).getPropertyValue(name) : null); } function includeStyle(styles) @@ -46,20 +33,16 @@ function includeScr(src) function includeLayout() { - const - css = require('Styles/@Boot.css'), - layout = require('Html/Layout.html'), - app = window.document.getElementById('rl-app') - ; + const app = window.document.getElementById('rl-app'); - if (css) + if (STYLES_CSS) { - includeStyle(css); + includeStyle(STYLES_CSS); } - if (app && layout) + if (app && LAYOUT_HTML) { - app.innerHTML = layout.replace(/[\r\n\t]+/g, ''); + app.innerHTML = LAYOUT_HTML.replace(/[\r\n\t]+/g, ''); return true; } @@ -221,21 +204,24 @@ function runApp() window.__initAppData = function(data) { - rlAppDataStorage = data; + RL_APP_DATA_STORAGE = data; window.__rlah_set(); - if (rlAppDataStorage.NewThemeLink) + if (RL_APP_DATA_STORAGE) { - (window.document.getElementById('app-theme-link') || {}).href = rlAppDataStorage.NewThemeLink; - } + if (RL_APP_DATA_STORAGE.NewThemeLink) + { + (window.document.getElementById('app-theme-link') || {}).href = RL_APP_DATA_STORAGE.NewThemeLink; + } - if (rlAppDataStorage.IncludeCss) - { - includeStyle(rlAppDataStorage.IncludeCss); - } + if (RL_APP_DATA_STORAGE.IncludeCss) + { + includeStyle(RL_APP_DATA_STORAGE.IncludeCss); + } - showDescriptionAndLoading(rlAppDataStorage ? (rlAppDataStorage.LoadingDescriptionEsc || '') : ''); + showDescriptionAndLoading(RL_APP_DATA_STORAGE.LoadingDescriptionEsc || ''); + } runApp(); }; diff --git a/dev/Common/Cache.jsx b/dev/Common/Cache.jsx index 0a61431d4..52821aa70 100644 --- a/dev/Common/Cache.jsx +++ b/dev/Common/Cache.jsx @@ -5,324 +5,339 @@ import {trim, pInt, isArray} from 'Common/Utils'; import * as Links from 'Common/Links'; import * as Settings from 'Storage/Settings'; -class CacheUserStorage +let FOLDERS_CACHE = {}; +let FOLDERS_NAME_CACHE = {}; +let FOLDERS_HASH_CACHE = {}; +let FOLDERS_UID_NEXT_CACHE = {}; +let MESSAGE_FLAGS_CACHE = {}; + +let NEW_MESSAGE_CACHE = {}; +let REQUESTED_MESSAGE_CACHE = {}; + +const capaGravatar = Settings.capa(Capa.Gravatar); +let inboxFolderName = ''; + +export function clear() { - oFoldersCache = {}; - oFoldersNamesCache = {}; - oFolderHashCache = {}; - oFolderUidNextCache = {}; - oMessageListHashCache = {}; - oMessageFlagsCache = {}; - oNewMessage = {}; - oRequestedMessage = {}; - bCapaGravatar = false; - inboxFolderName = ''; + FOLDERS_CACHE = {}; + FOLDERS_NAME_CACHE = {}; + FOLDERS_HASH_CACHE = {}; + FOLDERS_UID_NEXT_CACHE = {}; + MESSAGE_FLAGS_CACHE = {}; +} - constructor() +/** + * @param {string} email + * @param {Function} callback + * @return {string} + */ +export function getUserPic(email, callback) +{ + email = trim(email); + callback(capaGravatar && '' !== email ? Links.avatarLink(email) : '', email); +} + +/** + * @param {string} folderFullNameRaw + * @param {string} uid + * @return {string} + */ +export function getMessageKey(folderFullNameRaw, uid) +{ + return `${folderFullNameRaw}#${uid}`; +} + +/** + * @param {string} folder + * @param {string} uid + */ +export function addRequestedMessage(folder, uid) +{ + REQUESTED_MESSAGE_CACHE[getMessageKey(folder, uid)] = true; +} + +/** + * @param {string} folder + * @param {string} uid + * @return {boolean} + */ +export function hasRequestedMessage(folder, uid) +{ + return true === REQUESTED_MESSAGE_CACHE[getMessageKey(folder, uid)]; +} + +/** + * @param {string} folderFullNameRaw + * @param {string} uid + */ +export function addNewMessageCache(folderFullNameRaw, uid) +{ + NEW_MESSAGE_CACHE[getMessageKey(folderFullNameRaw, uid)] = true; +} + +/** + * @param {string} folderFullNameRaw + * @param {string} uid + */ +export function hasNewMessageAndRemoveFromCache(folderFullNameRaw, uid) +{ + if (NEW_MESSAGE_CACHE[getMessageKey(folderFullNameRaw, uid)]) { - this.bCapaGravatar = Settings.capa(Capa.Gravatar); + NEW_MESSAGE_CACHE[getMessageKey(folderFullNameRaw, uid)] = null; + return true; } + return false; +} - clear() { - this.oFoldersCache = {}; - this.oFoldersNamesCache = {}; - this.oFolderHashCache = {}; - this.oFolderUidNextCache = {}; - this.oMessageListHashCache = {}; - this.oMessageFlagsCache = {}; - } +export function clearNewMessageCache() +{ + NEW_MESSAGE_CACHE = {}; +} - /** - * @param {string} email - * @param {Function} callback - * @return {string} - */ - getUserPic(email, callback) { - email = trim(email); - callback(this.bCapaGravatar && '' !== email ? Links.avatarLink(email) : '', email); - } +/** + * @return {string} + */ +export function getFolderInboxName() +{ + return '' === inboxFolderName ? 'INBOX' : inboxFolderName; +} - /** - * @param {string} folderFullNameRaw - * @param {string} uid - * @return {string} - */ - getMessageKey(folderFullNameRaw, uid) { - return `${folderFullNameRaw}#${uid}`; - } +/** + * @param {string} folderHash + * @return {string} + */ +export function getFolderFullNameRaw(folderHash) +{ + return '' !== folderHash && FOLDERS_NAME_CACHE[folderHash] ? FOLDERS_NAME_CACHE[folderHash] : ''; +} - /** - * @param {string} folder - * @param {string} uid - */ - addRequestedMessage(folder, uid) { - this.oRequestedMessage[this.getMessageKey(folder, uid)] = true; - } - - /** - * @param {string} folder - * @param {string} uid - * @return {boolean} - */ - hasRequestedMessage(folder, uid) { - return true === this.oRequestedMessage[this.getMessageKey(folder, uid)]; - } - - /** - * @param {string} folderFullNameRaw - * @param {string} uid - */ - addNewMessageCache(folderFullNameRaw, uid) { - this.oNewMessage[this.getMessageKey(folderFullNameRaw, uid)] = true; - } - - /** - * @param {string} folderFullNameRaw - * @param {string} uid - */ - hasNewMessageAndRemoveFromCache(folderFullNameRaw, uid) { - if (this.oNewMessage[this.getMessageKey(folderFullNameRaw, uid)]) - { - this.oNewMessage[this.getMessageKey(folderFullNameRaw, uid)] = null; - return true; - } - return false; - } - - clearNewMessageCache() { - this.oNewMessage = {}; - } - - /** - * @return {string} - */ - getFolderInboxName() { - return '' === this.inboxFolderName ? 'INBOX' : this.inboxFolderName; - } - - /** - * @param {string} folderHash - * @return {string} - */ - getFolderFullNameRaw(folderHash) { - return '' !== folderHash && this.oFoldersNamesCache[folderHash] ? this.oFoldersNamesCache[folderHash] : ''; - } - - /** - * @param {string} folderHash - * @param {string} folderFullNameRaw - */ - setFolderFullNameRaw(folderHash, folderFullNameRaw) { - this.oFoldersNamesCache[folderHash] = folderFullNameRaw; - if ('INBOX' === folderFullNameRaw || '' === this.inboxFolderName) - { - this.inboxFolderName = folderFullNameRaw; - } - } - - /** - * @param {string} folderFullNameRaw - * @return {string} - */ - getFolderHash(folderFullNameRaw) { - return '' !== folderFullNameRaw && this.oFolderHashCache[folderFullNameRaw] ? this.oFolderHashCache[folderFullNameRaw] : ''; - } - - /** - * @param {string} folderFullNameRaw - * @param {string} folderHash - */ - setFolderHash(folderFullNameRaw, folderHash) { - if ('' !== folderFullNameRaw) - { - this.oFolderHashCache[folderFullNameRaw] = folderHash; - } - } - - /** - * @param {string} folderFullNameRaw - * @return {string} - */ - getFolderUidNext(folderFullNameRaw) { - return '' !== folderFullNameRaw && this.oFolderUidNextCache[folderFullNameRaw] ? this.oFolderUidNextCache[folderFullNameRaw] : ''; - } - - /** - * @param {string} folderFullNameRaw - * @param {string} uidNext - */ - setFolderUidNext(folderFullNameRaw, uidNext) { - this.oFolderUidNextCache[folderFullNameRaw] = uidNext; - } - - /** - * @param {string} folderFullNameRaw - * @return {?FolderModel} - */ - getFolderFromCacheList(folderFullNameRaw) { - return '' !== folderFullNameRaw && this.oFoldersCache[folderFullNameRaw] ? this.oFoldersCache[folderFullNameRaw] : null; - } - - /** - * @param {string} folderFullNameRaw - * @param {?FolderModel} folder - */ - setFolderToCacheList(folderFullNameRaw, folder) { - this.oFoldersCache[folderFullNameRaw] = folder; - } - - /** - * @param {string} folderFullNameRaw - */ - removeFolderFromCacheList(folderFullNameRaw) { - this.setFolderToCacheList(folderFullNameRaw, null); - } - - /** - * @param {string} folderFullName - * @param {string} uid - * @return {?Array} - */ - getMessageFlagsFromCache(folderFullName, uid) { - return this.oMessageFlagsCache[folderFullName] && this.oMessageFlagsCache[folderFullName][uid] ? - this.oMessageFlagsCache[folderFullName][uid] : null; - } - - /** - * @param {string} folderFullName - * @param {string} uid - * @param {Array} flagsCache - */ - setMessageFlagsToCache(folderFullName, uid, flagsCache) { - if (!this.oMessageFlagsCache[folderFullName]) - { - this.oMessageFlagsCache[folderFullName] = {}; - } - - this.oMessageFlagsCache[folderFullName][uid] = flagsCache; - } - - /** - * @param {string} folderFullName - */ - clearMessageFlagsFromCacheByFolder(folderFullName) { - this.oMessageFlagsCache[folderFullName] = {}; - } - - /** - * @param {(MessageModel|null)} message - */ - initMessageFlagsFromCache(message) { - - if (message) - { - const - uid = message.uid, - flags = this.getMessageFlagsFromCache(message.folderFullNameRaw, uid) - ; - - if (flags && 0 < flags.length) - { - message.flagged(!!flags[1]); - - if (!message.__simple_message__) - { - message.unseen(!!flags[0]); - message.answered(!!flags[2]); - message.forwarded(!!flags[3]); - message.isReadReceipt(!!flags[4]); - message.deletedMark(!!flags[5]); - } - } - - if (0 < message.threads().length) - { - const unseenSubUid = _.find(message.threads(), (sSubUid) => { - if (uid !== sSubUid) { - const subFlags = this.getMessageFlagsFromCache(message.folderFullNameRaw, sSubUid); - return subFlags && 0 < subFlags.length && !!subFlags[0]; - } - return false; - }); - - const flaggedSubUid = _.find(message.threads(), (sSubUid) => { - if (uid !== sSubUid) { - const subFlags = this.getMessageFlagsFromCache(message.folderFullNameRaw, sSubUid); - return subFlags && 0 < subFlags.length && !!subFlags[1]; - } - return false; - }); - - message.hasUnseenSubMessage(unseenSubUid && 0 < pInt(unseenSubUid)); - message.hasFlaggedSubMessage(flaggedSubUid && 0 < pInt(flaggedSubUid)); - } - } - } - - /** - * @param {(MessageModel|null)} message - */ - storeMessageFlagsToCache(message) { - if (message) - { - this.setMessageFlagsToCache( - message.folderFullNameRaw, message.uid, - [message.unseen(), message.flagged(), message.answered(), message.forwarded(), - message.isReadReceipt(), message.deletedMark()] - ); - } - } - - /** - * @param {string} folder - * @param {string} uid - * @param {Array} flags - */ - storeMessageFlagsToCacheByFolderAndUid(folder, uid, flags) { - if (isArray(flags) && 0 < flags.length) - { - this.setMessageFlagsToCache(folder, uid, flags); - } - } - - /** - * @param {string} folder - * @param {string} uid - * @param {number} setAction - */ - storeMessageFlagsToCacheBySetAction(folder, uid, setAction) { - - let unread = 0; - const flags = this.getMessageFlagsFromCache(folder, uid); - - if (isArray(flags) && 0 < flags.length) - { - if (flags[0]) - { - unread = 1; - } - - switch (setAction) - { - case MessageSetAction.SetSeen: - flags[0] = false; - break; - case MessageSetAction.UnsetSeen: - flags[0] = true; - break; - case MessageSetAction.SetFlag: - flags[1] = true; - break; - case MessageSetAction.UnsetFlag: - flags[1] = false; - break; - } - - this.setMessageFlagsToCache(folder, uid, flags); - } - - return unread; +/** + * @param {string} folderHash + * @param {string} folderFullNameRaw + */ +export function setFolderFullNameRaw(folderHash, folderFullNameRaw) +{ + FOLDERS_NAME_CACHE[folderHash] = folderFullNameRaw; + if ('INBOX' === folderFullNameRaw || '' === inboxFolderName) + { + inboxFolderName = folderFullNameRaw; } } -module.exports = new CacheUserStorage(); +/** + * @param {string} folderFullNameRaw + * @return {string} + */ +export function getFolderHash(folderFullNameRaw) +{ + return '' !== folderFullNameRaw && FOLDERS_HASH_CACHE[folderFullNameRaw] ? FOLDERS_HASH_CACHE[folderFullNameRaw] : ''; +} + +/** + * @param {string} folderFullNameRaw + * @param {string} folderHash + */ +export function setFolderHash(folderFullNameRaw, folderHash) +{ + if ('' !== folderFullNameRaw) + { + FOLDERS_HASH_CACHE[folderFullNameRaw] = folderHash; + } +} + +/** + * @param {string} folderFullNameRaw + * @return {string} + */ +export function getFolderUidNext(folderFullNameRaw) +{ + return '' !== folderFullNameRaw && FOLDERS_UID_NEXT_CACHE[folderFullNameRaw] ? FOLDERS_UID_NEXT_CACHE[folderFullNameRaw] : ''; +} + +/** + * @param {string} folderFullNameRaw + * @param {string} uidNext + */ +export function setFolderUidNext(folderFullNameRaw, uidNext) +{ + FOLDERS_UID_NEXT_CACHE[folderFullNameRaw] = uidNext; +} + +/** + * @param {string} folderFullNameRaw + * @return {?FolderModel} + */ +export function getFolderFromCacheList(folderFullNameRaw) +{ + return '' !== folderFullNameRaw && FOLDERS_CACHE[folderFullNameRaw] ? FOLDERS_CACHE[folderFullNameRaw] : null; +} + +/** + * @param {string} folderFullNameRaw + * @param {?FolderModel} folder + */ +export function setFolderToCacheList(folderFullNameRaw, folder) +{ + FOLDERS_CACHE[folderFullNameRaw] = folder; +} + +/** + * @param {string} folderFullNameRaw + */ +export function removeFolderFromCacheList(folderFullNameRaw) +{ + setFolderToCacheList(folderFullNameRaw, null); +} + +/** + * @param {string} folderFullName + * @param {string} uid + * @return {?Array} + */ +export function getMessageFlagsFromCache(folderFullName, uid) +{ + return MESSAGE_FLAGS_CACHE[folderFullName] && MESSAGE_FLAGS_CACHE[folderFullName][uid] ? + MESSAGE_FLAGS_CACHE[folderFullName][uid] : null; +} + +/** + * @param {string} folderFullName + * @param {string} uid + * @param {Array} flagsCache + */ +export function setMessageFlagsToCache(folderFullName, uid, flagsCache) +{ + if (!MESSAGE_FLAGS_CACHE[folderFullName]) + { + MESSAGE_FLAGS_CACHE[folderFullName] = {}; + } + + MESSAGE_FLAGS_CACHE[folderFullName][uid] = flagsCache; +} + +/** + * @param {string} folderFullName + */ +export function clearMessageFlagsFromCacheByFolder(folderFullName) +{ + MESSAGE_FLAGS_CACHE[folderFullName] = {}; +} + +/** + * @param {(MessageModel|null)} message + */ +export function initMessageFlagsFromCache(message) +{ + + if (message) + { + const + uid = message.uid, + flags = getMessageFlagsFromCache(message.folderFullNameRaw, uid) + ; + + if (flags && 0 < flags.length) + { + message.flagged(!!flags[1]); + + if (!message.__simple_message__) + { + message.unseen(!!flags[0]); + message.answered(!!flags[2]); + message.forwarded(!!flags[3]); + message.isReadReceipt(!!flags[4]); + message.deletedMark(!!flags[5]); + } + } + + if (0 < message.threads().length) + { + const unseenSubUid = _.find(message.threads(), (sSubUid) => { + if (uid !== sSubUid) { + const subFlags = getMessageFlagsFromCache(message.folderFullNameRaw, sSubUid); + return subFlags && 0 < subFlags.length && !!subFlags[0]; + } + return false; + }); + + const flaggedSubUid = _.find(message.threads(), (sSubUid) => { + if (uid !== sSubUid) { + const subFlags = getMessageFlagsFromCache(message.folderFullNameRaw, sSubUid); + return subFlags && 0 < subFlags.length && !!subFlags[1]; + } + return false; + }); + + message.hasUnseenSubMessage(unseenSubUid && 0 < pInt(unseenSubUid)); + message.hasFlaggedSubMessage(flaggedSubUid && 0 < pInt(flaggedSubUid)); + } + } +} + +/** + * @param {(MessageModel|null)} message + */ +export function storeMessageFlagsToCache(message) +{ + if (message) + { + setMessageFlagsToCache( + message.folderFullNameRaw, message.uid, + [message.unseen(), message.flagged(), message.answered(), message.forwarded(), + message.isReadReceipt(), message.deletedMark()] + ); + } +} + +/** + * @param {string} folder + * @param {string} uid + * @param {Array} flags + */ +export function storeMessageFlagsToCacheByFolderAndUid(folder, uid, flags) +{ + if (isArray(flags) && 0 < flags.length) + { + setMessageFlagsToCache(folder, uid, flags); + } +} + +/** + * @param {string} folder + * @param {string} uid + * @param {number} setAction + */ +export function storeMessageFlagsToCacheBySetAction(folder, uid, setAction) +{ + + let unread = 0; + const flags = getMessageFlagsFromCache(folder, uid); + + if (isArray(flags) && 0 < flags.length) + { + if (flags[0]) + { + unread = 1; + } + + switch (setAction) + { + case MessageSetAction.SetSeen: + flags[0] = false; + break; + case MessageSetAction.UnsetSeen: + flags[0] = true; + break; + case MessageSetAction.SetFlag: + flags[1] = true; + break; + case MessageSetAction.UnsetFlag: + flags[1] = false; + break; + } + + setMessageFlagsToCache(folder, uid, flags); + } + + return unread; +} diff --git a/dev/Common/Enums.jsx b/dev/Common/Enums.jsx index 48089eb58..68594a2f2 100644 --- a/dev/Common/Enums.jsx +++ b/dev/Common/Enums.jsx @@ -209,7 +209,8 @@ export const ClientSideKeyName = { 'MessageListSize': 5, 'LastReplyAction': 6, 'LastSignMe': 7, - 'ComposeLastIdentityID': 8 + 'ComposeLastIdentityID': 8, + 'MessageHeaderFullInfo': 9 }; /** diff --git a/dev/Common/Momentor.jsx b/dev/Common/Momentor.jsx index ae08c0ace..ee021161a 100644 --- a/dev/Common/Momentor.jsx +++ b/dev/Common/Momentor.jsx @@ -1,6 +1,6 @@ import {window, $, _, moment} from 'common'; -import Translator from 'Common/Translator'; +import {i18n} from 'Common/Translator'; let _moment = null; let _momentNow = 0; @@ -48,11 +48,11 @@ function formatCustomShortDate(m) case 4 >= now.diff(m, 'hours'): return m.fromNow(); case now.format('L') === m.format('L'): - return Translator.i18n('MESSAGE_LIST/TODAY_AT', { + return i18n('MESSAGE_LIST/TODAY_AT', { TIME: m.format('LT') }); case now.clone().subtract('days', 1).format('L') === m.format('L'): - return Translator.i18n('MESSAGE_LIST/YESTERDAY_AT', { + return i18n('MESSAGE_LIST/YESTERDAY_AT', { TIME: m.format('LT') }); case now.year() === m.year(): diff --git a/dev/Common/Selector.jsx b/dev/Common/Selector.jsx index 23c7766c1..1c4103900 100644 --- a/dev/Common/Selector.jsx +++ b/dev/Common/Selector.jsx @@ -2,7 +2,7 @@ import {$, _, key} from 'common'; import ko from 'ko'; import {EventKeyCode} from 'Common/Enums'; -import {isArray, inArray} from 'Common/Utils'; +import {isArray, inArray, noop, noopTrue} from 'Common/Utils'; class Selector { @@ -118,9 +118,6 @@ class Selector this.sLastUid = ''; this.oCallbacks = {}; - this.emptyFunction = () => {}; - this.emptyTrueFunction = () => true; - this.focusedItem.subscribe((item) => { if (item) { @@ -291,14 +288,14 @@ class Selector { if (!item) { - (this.oCallbacks.onItemSelect || this.emptyFunction)(item || null); + (this.oCallbacks.onItemSelect || noop)(item || null); } } else { if (item) { - (this.oCallbacks.onItemSelect || this.emptyFunction)(item); + (this.oCallbacks.onItemSelect || noop)(item); } } } @@ -416,14 +413,14 @@ class Selector * @return {boolean} */ autoSelect() { - return !!(this.oCallbacks.onAutoSelect || this.emptyTrueFunction)(); + return !!(this.oCallbacks.onAutoSelect || noopTrue)(); } /** * @param {boolean} up */ doUpUpOrDownDown(up) { - (this.oCallbacks.onUpUpOrDownDown || this.emptyTrueFunction)(!!up); + (this.oCallbacks.onUpUpOrDownDown || noopTrue)(!!up); } /** diff --git a/dev/Common/Translator.jsx b/dev/Common/Translator.jsx index b1295606f..4eb7779ed 100644 --- a/dev/Common/Translator.jsx +++ b/dev/Common/Translator.jsx @@ -2,342 +2,332 @@ import {window, $, _} from 'common'; import ko from 'ko'; import {Notification, UploadErrorCode} from 'Common/Enums'; -import {pInt, isUnd, isNull, has, bind, microtime, noop, inArray} from 'Common/Utils'; +import {pInt, isUnd, isNull, has, microtime, inArray} from 'Common/Utils'; import {$html, bAnimationSupported} from 'Common/Globals'; +import {reload as momentorReload} from 'Common/Momentor'; +import {langLink} from 'Common/Links'; +import Promise from 'Promise'; -class Translator +let I18N_DATA = window.rainloopI18N || {}; + +const I18N_NOTIFICATION_DATA = {}; +const I18N_NOTIFICATION_MAP = [ + [Notification.InvalidToken, 'NOTIFICATIONS/INVALID_TOKEN'], + [Notification.InvalidToken, 'NOTIFICATIONS/INVALID_TOKEN'], + [Notification.AuthError, 'NOTIFICATIONS/AUTH_ERROR'], + [Notification.AccessError, 'NOTIFICATIONS/ACCESS_ERROR'], + [Notification.ConnectionError, 'NOTIFICATIONS/CONNECTION_ERROR'], + [Notification.CaptchaError, 'NOTIFICATIONS/CAPTCHA_ERROR'], + [Notification.SocialFacebookLoginAccessDisable, 'NOTIFICATIONS/SOCIAL_FACEBOOK_LOGIN_ACCESS_DISABLE'], + [Notification.SocialTwitterLoginAccessDisable, 'NOTIFICATIONS/SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE'], + [Notification.SocialGoogleLoginAccessDisable, 'NOTIFICATIONS/SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE'], + [Notification.DomainNotAllowed, 'NOTIFICATIONS/DOMAIN_NOT_ALLOWED'], + [Notification.AccountNotAllowed, 'NOTIFICATIONS/ACCOUNT_NOT_ALLOWED'], + + [Notification.AccountTwoFactorAuthRequired, 'NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_REQUIRED'], + [Notification.AccountTwoFactorAuthError, 'NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_ERROR'], + + [Notification.CouldNotSaveNewPassword, 'NOTIFICATIONS/COULD_NOT_SAVE_NEW_PASSWORD'], + [Notification.CurrentPasswordIncorrect, 'NOTIFICATIONS/CURRENT_PASSWORD_INCORRECT'], + [Notification.NewPasswordShort, 'NOTIFICATIONS/NEW_PASSWORD_SHORT'], + [Notification.NewPasswordWeak, 'NOTIFICATIONS/NEW_PASSWORD_WEAK'], + [Notification.NewPasswordForbidden, 'NOTIFICATIONS/NEW_PASSWORD_FORBIDDENT'], + + [Notification.ContactsSyncError, 'NOTIFICATIONS/CONTACTS_SYNC_ERROR'], + + [Notification.CantGetMessageList, 'NOTIFICATIONS/CANT_GET_MESSAGE_LIST'], + [Notification.CantGetMessage, 'NOTIFICATIONS/CANT_GET_MESSAGE'], + [Notification.CantDeleteMessage, 'NOTIFICATIONS/CANT_DELETE_MESSAGE'], + [Notification.CantMoveMessage, 'NOTIFICATIONS/CANT_MOVE_MESSAGE'], + [Notification.CantCopyMessage, 'NOTIFICATIONS/CANT_MOVE_MESSAGE'], + + [Notification.CantSaveMessage, 'NOTIFICATIONS/CANT_SAVE_MESSAGE'], + [Notification.CantSendMessage, 'NOTIFICATIONS/CANT_SEND_MESSAGE'], + [Notification.InvalidRecipients, 'NOTIFICATIONS/INVALID_RECIPIENTS'], + + [Notification.CantSaveFilters, 'NOTIFICATIONS/CANT_SAVE_FILTERS'], + [Notification.CantGetFilters, 'NOTIFICATIONS/CANT_GET_FILTERS'], + [Notification.FiltersAreNotCorrect, 'NOTIFICATIONS/FILTERS_ARE_NOT_CORRECT'], + + [Notification.CantCreateFolder, 'NOTIFICATIONS/CANT_CREATE_FOLDER'], + [Notification.CantRenameFolder, 'NOTIFICATIONS/CANT_RENAME_FOLDER'], + [Notification.CantDeleteFolder, 'NOTIFICATIONS/CANT_DELETE_FOLDER'], + [Notification.CantDeleteNonEmptyFolder, 'NOTIFICATIONS/CANT_DELETE_NON_EMPTY_FOLDER'], + [Notification.CantSubscribeFolder, 'NOTIFICATIONS/CANT_SUBSCRIBE_FOLDER'], + [Notification.CantUnsubscribeFolder, 'NOTIFICATIONS/CANT_UNSUBSCRIBE_FOLDER'], + + [Notification.CantSaveSettings, 'NOTIFICATIONS/CANT_SAVE_SETTINGS'], + [Notification.CantSavePluginSettings, 'NOTIFICATIONS/CANT_SAVE_PLUGIN_SETTINGS'], + + [Notification.DomainAlreadyExists, 'NOTIFICATIONS/DOMAIN_ALREADY_EXISTS'], + + [Notification.CantInstallPackage, 'NOTIFICATIONS/CANT_INSTALL_PACKAGE'], + [Notification.CantDeletePackage, 'NOTIFICATIONS/CANT_DELETE_PACKAGE'], + [Notification.InvalidPluginPackage, 'NOTIFICATIONS/INVALID_PLUGIN_PACKAGE'], + [Notification.UnsupportedPluginPackage, 'NOTIFICATIONS/UNSUPPORTED_PLUGIN_PACKAGE'], + + [Notification.LicensingServerIsUnavailable, 'NOTIFICATIONS/LICENSING_SERVER_IS_UNAVAILABLE'], + [Notification.LicensingExpired, 'NOTIFICATIONS/LICENSING_EXPIRED'], + [Notification.LicensingBanned, 'NOTIFICATIONS/LICENSING_BANNED'], + + [Notification.DemoSendMessageError, 'NOTIFICATIONS/DEMO_SEND_MESSAGE_ERROR'], + [Notification.DemoAccountError, 'NOTIFICATIONS/DEMO_ACCOUNT_ERROR'], + + [Notification.AccountAlreadyExists, 'NOTIFICATIONS/ACCOUNT_ALREADY_EXISTS'], + [Notification.AccountDoesNotExist, 'NOTIFICATIONS/ACCOUNT_DOES_NOT_EXIST'], + + [Notification.MailServerError, 'NOTIFICATIONS/MAIL_SERVER_ERROR'], + [Notification.InvalidInputArgument, 'NOTIFICATIONS/INVALID_INPUT_ARGUMENT'], + [Notification.UnknownNotification, 'NOTIFICATIONS/UNKNOWN_ERROR'], + [Notification.UnknownError, 'NOTIFICATIONS/UNKNOWN_ERROR'] +]; + +export const trigger = ko.observable(false); + +/** + * @param {string} key + * @param {Object=} valueList + * @param {string=} defaulValue + * @return {string} + */ +export function i18n(key, valueList, defaulValue) { - data = {}; - notificationI18N = {}; + let + valueName = '', + result = I18N_DATA[key] + ; - constructor() { - this.data = window.rainloopI18N || {}; - this.trigger = ko.observable(false); - this.i18n = bind(this.i18n, this); - this.init(); + if (isUnd(result)) + { + result = isUnd(defaulValue) ? key : defaulValue; } - /** - * @param {string} key - * @param {Object=} valueList - * @param {string=} defaulValue - * @return {string} - */ - i18n(key, valueList, defaulValue) { - - let - valueName = '', - result = this.data[key] - ; - - if (isUnd(result)) + if (!isUnd(valueList) && !isNull(valueList)) + { + for (valueName in valueList) { - result = isUnd(defaulValue) ? key : defaulValue; - } - - if (!isUnd(valueList) && !isNull(valueList)) - { - for (valueName in valueList) + if (has(valueList, valueName)) { - if (has(valueList, valueName)) - { - result = result.replace('%' + valueName + '%', valueList[valueName]); - } - } - } - - return result; - } - - /** - * @param {Object} element - */ - i18nToNode(element) { - - const - $el = $(element), - key = $el.data('i18n') - ; - - if (key) - { - if ('[' === key.substr(0, 1)) - { - switch (key.substr(0, 6)) - { - case '[html]': - $el.html(this.i18n(key.substr(6))); - break; - case '[place': - $el.attr('placeholder', this.i18n(key.substr(13))); - break; - case '[title': - $el.attr('title', this.i18n(key.substr(7))); - break; - } - } - else - { - $el.text(this.i18n(key)); + result = result.replace('%' + valueName + '%', valueList[valueName]); } } } - /** - * @param {Object} elements - * @param {boolean=} animate = false - */ - i18nToNodes(elements, animate = false) { - _.defer(() => { + return result; +} - $('[data-i18n]', elements).each((index, item) => { - this.i18nToNode(item); - }); +const i18nToNode = (element) => { - if (animate && bAnimationSupported) + const + $el = $(element), + key = $el.data('i18n') + ; + + if (key) + { + if ('[' === key.substr(0, 1)) + { + switch (key.substr(0, 6)) { - $('.i18n-animation[data-i18n]', elements).letterfx({ - fx: 'fall fade', - backwards: false, - timing: 50, - fx_duration: '50ms', - letter_end: 'restore', - element_end: 'restore' - }); + case '[html]': + $el.html(i18n(key.substr(6))); + break; + case '[place': + $el.attr('placeholder', i18n(key.substr(13))); + break; + case '[title': + $el.attr('title', i18n(key.substr(7))); + break; } + } + else + { + $el.text(i18n(key)); + } + } +}; + +/** + * @param {Object} elements + * @param {boolean=} animate = false + */ +export function i18nToNodes(elements, animate = false) +{ + _.defer(() => { + + $('[data-i18n]', elements).each((index, item) => { + i18nToNode(item); }); - } - reloadData() { - if (window.rainloopI18N) + if (animate && bAnimationSupported) { - this.data = window.rainloopI18N || {}; - - this.i18nToNodes(window.document, true); - - require('Common/Momentor').reload(); - this.trigger(!this.trigger()); - } - - window.rainloopI18N = null; - } - - initNotificationLanguage() { - - const map = [ - [Notification.InvalidToken, 'NOTIFICATIONS/INVALID_TOKEN'], - [Notification.InvalidToken, 'NOTIFICATIONS/INVALID_TOKEN'], - [Notification.AuthError, 'NOTIFICATIONS/AUTH_ERROR'], - [Notification.AccessError, 'NOTIFICATIONS/ACCESS_ERROR'], - [Notification.ConnectionError, 'NOTIFICATIONS/CONNECTION_ERROR'], - [Notification.CaptchaError, 'NOTIFICATIONS/CAPTCHA_ERROR'], - [Notification.SocialFacebookLoginAccessDisable, 'NOTIFICATIONS/SOCIAL_FACEBOOK_LOGIN_ACCESS_DISABLE'], - [Notification.SocialTwitterLoginAccessDisable, 'NOTIFICATIONS/SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE'], - [Notification.SocialGoogleLoginAccessDisable, 'NOTIFICATIONS/SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE'], - [Notification.DomainNotAllowed, 'NOTIFICATIONS/DOMAIN_NOT_ALLOWED'], - [Notification.AccountNotAllowed, 'NOTIFICATIONS/ACCOUNT_NOT_ALLOWED'], - - [Notification.AccountTwoFactorAuthRequired, 'NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_REQUIRED'], - [Notification.AccountTwoFactorAuthError, 'NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_ERROR'], - - [Notification.CouldNotSaveNewPassword, 'NOTIFICATIONS/COULD_NOT_SAVE_NEW_PASSWORD'], - [Notification.CurrentPasswordIncorrect, 'NOTIFICATIONS/CURRENT_PASSWORD_INCORRECT'], - [Notification.NewPasswordShort, 'NOTIFICATIONS/NEW_PASSWORD_SHORT'], - [Notification.NewPasswordWeak, 'NOTIFICATIONS/NEW_PASSWORD_WEAK'], - [Notification.NewPasswordForbidden, 'NOTIFICATIONS/NEW_PASSWORD_FORBIDDENT'], - - [Notification.ContactsSyncError, 'NOTIFICATIONS/CONTACTS_SYNC_ERROR'], - - [Notification.CantGetMessageList, 'NOTIFICATIONS/CANT_GET_MESSAGE_LIST'], - [Notification.CantGetMessage, 'NOTIFICATIONS/CANT_GET_MESSAGE'], - [Notification.CantDeleteMessage, 'NOTIFICATIONS/CANT_DELETE_MESSAGE'], - [Notification.CantMoveMessage, 'NOTIFICATIONS/CANT_MOVE_MESSAGE'], - [Notification.CantCopyMessage, 'NOTIFICATIONS/CANT_MOVE_MESSAGE'], - - [Notification.CantSaveMessage, 'NOTIFICATIONS/CANT_SAVE_MESSAGE'], - [Notification.CantSendMessage, 'NOTIFICATIONS/CANT_SEND_MESSAGE'], - [Notification.InvalidRecipients, 'NOTIFICATIONS/INVALID_RECIPIENTS'], - - [Notification.CantSaveFilters, 'NOTIFICATIONS/CANT_SAVE_FILTERS'], - [Notification.CantGetFilters, 'NOTIFICATIONS/CANT_GET_FILTERS'], - [Notification.FiltersAreNotCorrect, 'NOTIFICATIONS/FILTERS_ARE_NOT_CORRECT'], - - [Notification.CantCreateFolder, 'NOTIFICATIONS/CANT_CREATE_FOLDER'], - [Notification.CantRenameFolder, 'NOTIFICATIONS/CANT_RENAME_FOLDER'], - [Notification.CantDeleteFolder, 'NOTIFICATIONS/CANT_DELETE_FOLDER'], - [Notification.CantDeleteNonEmptyFolder, 'NOTIFICATIONS/CANT_DELETE_NON_EMPTY_FOLDER'], - [Notification.CantSubscribeFolder, 'NOTIFICATIONS/CANT_SUBSCRIBE_FOLDER'], - [Notification.CantUnsubscribeFolder, 'NOTIFICATIONS/CANT_UNSUBSCRIBE_FOLDER'], - - [Notification.CantSaveSettings, 'NOTIFICATIONS/CANT_SAVE_SETTINGS'], - [Notification.CantSavePluginSettings, 'NOTIFICATIONS/CANT_SAVE_PLUGIN_SETTINGS'], - - [Notification.DomainAlreadyExists, 'NOTIFICATIONS/DOMAIN_ALREADY_EXISTS'], - - [Notification.CantInstallPackage, 'NOTIFICATIONS/CANT_INSTALL_PACKAGE'], - [Notification.CantDeletePackage, 'NOTIFICATIONS/CANT_DELETE_PACKAGE'], - [Notification.InvalidPluginPackage, 'NOTIFICATIONS/INVALID_PLUGIN_PACKAGE'], - [Notification.UnsupportedPluginPackage, 'NOTIFICATIONS/UNSUPPORTED_PLUGIN_PACKAGE'], - - [Notification.LicensingServerIsUnavailable, 'NOTIFICATIONS/LICENSING_SERVER_IS_UNAVAILABLE'], - [Notification.LicensingExpired, 'NOTIFICATIONS/LICENSING_EXPIRED'], - [Notification.LicensingBanned, 'NOTIFICATIONS/LICENSING_BANNED'], - - [Notification.DemoSendMessageError, 'NOTIFICATIONS/DEMO_SEND_MESSAGE_ERROR'], - [Notification.DemoAccountError, 'NOTIFICATIONS/DEMO_ACCOUNT_ERROR'], - - [Notification.AccountAlreadyExists, 'NOTIFICATIONS/ACCOUNT_ALREADY_EXISTS'], - [Notification.AccountDoesNotExist, 'NOTIFICATIONS/ACCOUNT_DOES_NOT_EXIST'], - - [Notification.MailServerError, 'NOTIFICATIONS/MAIL_SERVER_ERROR'], - [Notification.InvalidInputArgument, 'NOTIFICATIONS/INVALID_INPUT_ARGUMENT'], - [Notification.UnknownNotification, 'NOTIFICATIONS/UNKNOWN_ERROR'], - [Notification.UnknownError, 'NOTIFICATIONS/UNKNOWN_ERROR'] - ]; - - this.notificationI18N = this.notificationI18N || {}; - - map.forEach((item) => { - this.notificationI18N[item[0]] = this.i18n(item[1]); - }); - } - - /** - * @param {Function} callback - * @param {Object} scope - * @param {Function=} langCallback - */ - initOnStartOrLangChange(callback, scope, langCallback = null) { - if (callback) - { - callback.call(scope); - } - - if (langCallback) - { - this.trigger.subscribe(() => { - if (callback) - { - callback.call(scope); - } - - langCallback.call(scope); + $('.i18n-animation[data-i18n]', elements).letterfx({ + fx: 'fall fade', + backwards: false, + timing: 50, + fx_duration: '50ms', + letter_end: 'restore', + element_end: 'restore' }); } - else if (callback) - { - this.trigger.subscribe(callback, scope); - } + }); +} + +const reloadData = () => { + if (window.rainloopI18N) + { + I18N_DATA = window.rainloopI18N || {}; + + i18nToNodes(window.document, true); + + momentorReload(); + trigger(!trigger()); } - /** - * @param {number} code - * @param {*=} message = '' - * @param {*=} defCode = null - * @return {string} - */ - getNotification(code, message = '', defCode = null) { - code = window.parseInt(code, 10) || 0; - if (Notification.ClientViewError === code && message) - { - return message; - } + window.rainloopI18N = null; +}; - defCode = defCode ? (window.parseInt(defCode, 10)) || 0 : 0; - return isUnd(this.notificationI18N[code]) ? ( - defCode && isUnd(this.notificationI18N[defCode]) ? this.notificationI18N[defCode] : '' - ) : this.notificationI18N[code]; +export function initNotificationLanguage() +{ + I18N_NOTIFICATION_MAP.forEach((item) => { + I18N_NOTIFICATION_DATA[item[0]] = i18n(item[1]); + }); +} + +/** + * @param {Function} callback + * @param {Object} scope + * @param {Function=} langCallback = null + */ +export function initOnStartOrLangChange(callback, scope, langCallback = null) +{ + if (callback) + { + callback.call(scope); } - /** - * @param {object} response - * @param {number} defCode = Notification.UnknownNotification - * @return {string} - */ - getNotificationFromResponse(response, defCode = Notification.UnknownNotification) { - return response && response.ErrorCode ? - this.getNotification(pInt(response.ErrorCode), response.ErrorMessage || '') : - this.getNotification(defCode); + if (langCallback) + { + trigger.subscribe(() => { + if (callback) + { + callback.call(scope); + } + + langCallback.call(scope); + }); } - - /** - * @param {*} code - * @return {string} - */ - getUploadErrorDescByCode(code) { - let result = ''; - switch (window.parseInt(code, 10) || 0) { - case UploadErrorCode.FileIsTooBig: - result = this.i18n('UPLOAD/ERROR_FILE_IS_TOO_BIG'); - break; - case UploadErrorCode.FilePartiallyUploaded: - result = this.i18n('UPLOAD/ERROR_FILE_PARTIALLY_UPLOADED'); - break; - case UploadErrorCode.FileNoUploaded: - result = this.i18n('UPLOAD/ERROR_NO_FILE_UPLOADED'); - break; - case UploadErrorCode.MissingTempFolder: - result = this.i18n('UPLOAD/ERROR_MISSING_TEMP_FOLDER'); - break; - case UploadErrorCode.FileOnSaveingError: - result = this.i18n('UPLOAD/ERROR_ON_SAVING_FILE'); - break; - case UploadErrorCode.FileType: - result = this.i18n('UPLOAD/ERROR_FILE_TYPE'); - break; - default: - result = this.i18n('UPLOAD/ERROR_UNKNOWN'); - break; - } - - return result; - } - - /** - * @param {boolean} admin - * @param {string} language - * @param {Function=} done - * @param {Function=} fail - */ - reload(admin, language, done, fail) { - - const - self = this, - start = microtime() - ; - - $html.addClass('rl-changing-language'); - - $.ajax({ - url: require('Common/Links').langLink(language, admin), - dataType: 'script', - cache: true - }) - .fail(fail || noop) - .done(function () { - _.delay(function () { - - self.reloadData(); - - (done || noop)(); - - const isRtl = -1 < inArray(language, ['ar', 'ar_sa', 'he', 'he_he', 'ur', 'ur_ir']); - - $html - .removeClass('rl-changing-language') - .removeClass('rl-rtl rl-ltr') - .addClass(isRtl ? 'rl-rtl' : 'rl-ltr') - // .attr('dir', isRtl ? 'rtl' : 'ltr') - ; - - }, 500 < microtime() - start ? 1 : 500); - }) - ; - } - - init() { - $html.addClass('rl-' + ($html.attr('dir') || 'ltr')); + else if (callback) + { + trigger.subscribe(callback, scope); } } -module.exports = new Translator(); +/** + * @param {number} code + * @param {*=} message = '' + * @param {*=} defCode = null + * @return {string} + */ +export function getNotification(code, message = '', defCode = null) +{ + code = window.parseInt(code, 10) || 0; + if (Notification.ClientViewError === code && message) + { + return message; + } + + defCode = defCode ? (window.parseInt(defCode, 10)) || 0 : 0; + return isUnd(I18N_NOTIFICATION_DATA[code]) ? ( + defCode && isUnd(I18N_NOTIFICATION_DATA[defCode]) ? I18N_NOTIFICATION_DATA[defCode] : '' + ) : I18N_NOTIFICATION_DATA[code]; +} + +/** + * @param {object} response + * @param {number} defCode = Notification.UnknownNotification + * @return {string} + */ +export function getNotificationFromResponse(response, defCode = Notification.UnknownNotification) +{ + return response && response.ErrorCode ? + getNotification(pInt(response.ErrorCode), response.ErrorMessage || '') : getNotification(defCode); +} + +/** + * @param {*} code + * @return {string} + */ +export function getUploadErrorDescByCode(code) +{ + let result = ''; + switch (window.parseInt(code, 10) || 0) + { + case UploadErrorCode.FileIsTooBig: + result = i18n('UPLOAD/ERROR_FILE_IS_TOO_BIG'); + break; + case UploadErrorCode.FilePartiallyUploaded: + result = i18n('UPLOAD/ERROR_FILE_PARTIALLY_UPLOADED'); + break; + case UploadErrorCode.FileNoUploaded: + result = i18n('UPLOAD/ERROR_NO_FILE_UPLOADED'); + break; + case UploadErrorCode.MissingTempFolder: + result = i18n('UPLOAD/ERROR_MISSING_TEMP_FOLDER'); + break; + case UploadErrorCode.FileOnSaveingError: + result = i18n('UPLOAD/ERROR_ON_SAVING_FILE'); + break; + case UploadErrorCode.FileType: + result = i18n('UPLOAD/ERROR_FILE_TYPE'); + break; + default: + result = i18n('UPLOAD/ERROR_UNKNOWN'); + break; + } + + return result; +} + +/** + * @param {boolean} admin + * @param {string} language + */ +export function reload(admin, language) +{ + const start = microtime(); + + $html.addClass('rl-changing-language'); + + return new Promise((resolve, reject) => { + $.ajax({ + url: langLink(language, admin), + dataType: 'script', + cache: true + }).then(() => { + _.delay(() => { + + reloadData(); + + const isRtl = -1 < inArray(language, ['ar', 'ar_sa', 'he', 'he_he', 'ur', 'ur_ir']); + + $html + .removeClass('rl-changing-language') + .removeClass('rl-rtl rl-ltr') + .addClass(isRtl ? 'rl-rtl' : 'rl-ltr') +// .attr('dir', isRtl ? 'rtl' : 'ltr') + ; + + resolve(); + + }, 500 < microtime() - start ? 1 : 500); + }, () => { + $html.removeClass('rl-changing-language'); + window.rainloopI18N = null; + reject(); + }); + }); +} + +// init section +$html.addClass('rl-' + ($html.attr('dir') || 'ltr')); diff --git a/dev/Common/Utils.jsx b/dev/Common/Utils.jsx index 0ba3b83a1..4b05af1a0 100644 --- a/dev/Common/Utils.jsx +++ b/dev/Common/Utils.jsx @@ -20,8 +20,10 @@ const isNull = _.isNull; const has = _.has; const bind = _.bind; const noop = () => {}; +const noopTrue = () => true; +const noopFalse = () => false; -export {trim, inArray, isArray, isObject, isFunc, isUnd, isNull, has, bind, noop, noop as emptyFunction}; +export {trim, inArray, isArray, isObject, isFunc, isUnd, isNull, has, bind, noop, noopTrue, noopFalse}; /** * @param {Function} callback diff --git a/dev/Component/Select.jsx b/dev/Component/Select.jsx index 93893ed5e..3a315c105 100644 --- a/dev/Component/Select.jsx +++ b/dev/Component/Select.jsx @@ -1,5 +1,5 @@ -import Translator from 'Common/Translator'; +import {i18n} from 'Common/Translator'; import {defautOptionsAfterRender} from 'Common/Utils'; import {componentExportHelper} from 'Component/Abstract'; import {AbstractInput} from 'Component/AbstractInput'; @@ -21,7 +21,7 @@ class SelectComponent extends AbstractInput if (this.optionsCaption) { - this.optionsCaption = Translator.i18n(this.optionsCaption); + this.optionsCaption = i18n(this.optionsCaption); } this.defautOptionsAfterRender = defautOptionsAfterRender; diff --git a/dev/Knoin/AbstractScreen.js b/dev/Knoin/AbstractScreen.js index 89d2493b9..429c90f2c 100644 --- a/dev/Knoin/AbstractScreen.js +++ b/dev/Knoin/AbstractScreen.js @@ -75,7 +75,7 @@ if (Utils.isNonEmptyArray(aRoutes)) { - fMatcher = _.bind(this.onRoute || Utils.emptyFunction, this); + fMatcher = _.bind(this.onRoute || Utils.noop, this); oRoute = crossroads.create(); _.each(aRoutes, function (aItem) { diff --git a/dev/Model/ComposeAttachment.js b/dev/Model/ComposeAttachment.js index 48d42e9d0..3507db7e9 100644 --- a/dev/Model/ComposeAttachment.js +++ b/dev/Model/ComposeAttachment.js @@ -85,7 +85,7 @@ ComposeAttachmentModel.prototype.CID = ''; ComposeAttachmentModel.prototype.contentLocation = ''; ComposeAttachmentModel.prototype.fromMessage = false; - ComposeAttachmentModel.prototype.cancel = Utils.emptyFunction; + ComposeAttachmentModel.prototype.cancel = Utils.noop; /** * @param {AjaxJsonComposeAttachment} oJsonAttachment diff --git a/dev/Promises/User/Populator.js b/dev/Promises/User/Populator.js index 713cec419..c738cd6cb 100644 --- a/dev/Promises/User/Populator.js +++ b/dev/Promises/User/Populator.js @@ -194,7 +194,7 @@ if (bUpdate) { - require('Remote/User/Ajax').saveSystemFolders(Utils.emptyFunction, { + require('Remote/User/Ajax').saveSystemFolders(Utils.noop, { 'SentFolder': FolderStore.sentFolder(), 'DraftFolder': FolderStore.draftFolder(), 'SpamFolder': FolderStore.spamFolder(), diff --git a/dev/Settings/Admin/General.js b/dev/Settings/Admin/General.js index ca000b854..d793d08f5 100644 --- a/dev/Settings/Admin/General.js +++ b/dev/Settings/Admin/General.js @@ -113,13 +113,15 @@ self.languageAdminTrigger(Enums.SaveSettingsStep.Animate); - Translator.reload(true, sValue, + Translator.reload(true, sValue).then( fReloadLanguageHelper(Enums.SaveSettingsStep.TrueResult), - fReloadLanguageHelper(Enums.SaveSettingsStep.FalseResult)); - - Remote.saveAdminConfig(null, { - 'LanguageAdmin': Utils.trim(sValue) + fReloadLanguageHelper(Enums.SaveSettingsStep.FalseResult) + ).then(function() { + Remote.saveAdminConfig(null, { + 'LanguageAdmin': Utils.trim(sValue) + }); }); + }); self.theme.subscribe(function (sValue) { diff --git a/dev/Settings/Admin/Plugins.js b/dev/Settings/Admin/Plugins.js index d09bd70a7..57deda684 100644 --- a/dev/Settings/Admin/Plugins.js +++ b/dev/Settings/Admin/Plugins.js @@ -74,7 +74,7 @@ ; this.enabledPlugins.subscribe(function (bValue) { - Remote.saveAdminConfig(Utils.emptyFunction, { + Remote.saveAdminConfig(Utils.noop, { 'EnabledPlugins': bValue ? '1' : '0' }); }); diff --git a/dev/Settings/Admin/Security.js b/dev/Settings/Admin/Security.js index f8bdaa220..91df64018 100644 --- a/dev/Settings/Admin/Security.js +++ b/dev/Settings/Admin/Security.js @@ -131,19 +131,19 @@ SecurityAdminSettings.prototype.onBuild = function () { this.capaOpenPGP.subscribe(function (bValue) { - Remote.saveAdminConfig(Utils.emptyFunction, { + Remote.saveAdminConfig(Utils.noop, { 'CapaOpenPGP': bValue ? '1' : '0' }); }); this.capaTwoFactorAuth.subscribe(function (bValue) { - Remote.saveAdminConfig(Utils.emptyFunction, { + Remote.saveAdminConfig(Utils.noop, { 'CapaTwoFactorAuth': bValue ? '1' : '0' }); }); this.capaTwoFactorAuthForce.subscribe(function (bValue) { - Remote.saveAdminConfig(Utils.emptyFunction, { + Remote.saveAdminConfig(Utils.noop, { 'CapaTwoFactorAuthForce': bValue ? '1' : '0' }); }); diff --git a/dev/Settings/Admin/Social.js b/dev/Settings/Admin/Social.js index 8a6890cc5..35a40d25a 100644 --- a/dev/Settings/Admin/Social.js +++ b/dev/Settings/Admin/Social.js @@ -79,7 +79,7 @@ self.facebookEnable.subscribe(function (bValue) { if (self.facebookSupported()) { - Remote.saveAdminConfig(Utils.emptyFunction, { + Remote.saveAdminConfig(Utils.noop, { 'FacebookEnable': bValue ? '1' : '0' }); } @@ -104,7 +104,7 @@ }); self.twitterEnable.subscribe(function (bValue) { - Remote.saveAdminConfig(Utils.emptyFunction, { + Remote.saveAdminConfig(Utils.noop, { 'TwitterEnable': bValue ? '1' : '0' }); }); @@ -122,25 +122,25 @@ }); self.googleEnable.subscribe(function (bValue) { - Remote.saveAdminConfig(Utils.emptyFunction, { + Remote.saveAdminConfig(Utils.noop, { 'GoogleEnable': bValue ? '1' : '0' }); }); self.googleEnableAuth.subscribe(function (bValue) { - Remote.saveAdminConfig(Utils.emptyFunction, { + Remote.saveAdminConfig(Utils.noop, { 'GoogleEnableAuth': bValue ? '1' : '0' }); }); self.googleEnableDrive.subscribe(function (bValue) { - Remote.saveAdminConfig(Utils.emptyFunction, { + Remote.saveAdminConfig(Utils.noop, { 'GoogleEnableDrive': bValue ? '1' : '0' }); }); self.googleEnablePreview.subscribe(function (bValue) { - Remote.saveAdminConfig(Utils.emptyFunction, { + Remote.saveAdminConfig(Utils.noop, { 'GoogleEnablePreview': bValue ? '1' : '0' }); }); @@ -164,7 +164,7 @@ }); self.dropboxEnable.subscribe(function (bValue) { - Remote.saveAdminConfig(Utils.emptyFunction, { + Remote.saveAdminConfig(Utils.noop, { 'DropboxEnable': bValue ? '1' : '0' }); }); diff --git a/dev/Settings/User/Folders.js b/dev/Settings/User/Folders.js index 9c3ed765a..4f84080ac 100644 --- a/dev/Settings/User/Folders.js +++ b/dev/Settings/User/Folders.js @@ -169,7 +169,7 @@ FoldersUserSettings.prototype.subscribeFolder = function (oFolder) { Local.set(Enums.ClientSideKeyName.FoldersLashHash, ''); - Remote.folderSetSubscribe(Utils.emptyFunction, oFolder.fullNameRaw, true); + Remote.folderSetSubscribe(Utils.noop, oFolder.fullNameRaw, true); oFolder.subScribed(true); }; @@ -177,21 +177,21 @@ FoldersUserSettings.prototype.unSubscribeFolder = function (oFolder) { Local.set(Enums.ClientSideKeyName.FoldersLashHash, ''); - Remote.folderSetSubscribe(Utils.emptyFunction, oFolder.fullNameRaw, false); + Remote.folderSetSubscribe(Utils.noop, oFolder.fullNameRaw, false); oFolder.subScribed(false); }; FoldersUserSettings.prototype.checkableTrueFolder = function (oFolder) { - Remote.folderSetCheckable(Utils.emptyFunction, oFolder.fullNameRaw, true); + Remote.folderSetCheckable(Utils.noop, oFolder.fullNameRaw, true); oFolder.checkable(true); }; FoldersUserSettings.prototype.checkableFalseFolder = function (oFolder) { - Remote.folderSetCheckable(Utils.emptyFunction, oFolder.fullNameRaw, false); + Remote.folderSetCheckable(Utils.noop, oFolder.fullNameRaw, false); oFolder.checkable(false); }; diff --git a/dev/Settings/User/General.js b/dev/Settings/User/General.js index ff43bb8d5..c6ce30d43 100644 --- a/dev/Settings/User/General.js +++ b/dev/Settings/User/General.js @@ -135,13 +135,15 @@ self.languageTrigger(Enums.SaveSettingsStep.Animate); - Translator.reload(false, sValue, + Translator.reload(false, sValue).then( fReloadLanguageHelper(Enums.SaveSettingsStep.TrueResult), - fReloadLanguageHelper(Enums.SaveSettingsStep.FalseResult)); - - Remote.saveSettings(null, { - 'Language': sValue + fReloadLanguageHelper(Enums.SaveSettingsStep.FalseResult) + ).then(function() { + Remote.saveSettings(null, { + 'Language': sValue + }); }); + }); self.editorDefaultType.subscribe(function (sValue) { diff --git a/dev/Storage/Client.jsx b/dev/Storage/Client.jsx index 719dabec0..9d6afdec7 100644 --- a/dev/Storage/Client.jsx +++ b/dev/Storage/Client.jsx @@ -3,30 +3,27 @@ import {_} from 'common'; import {CookieDriver} from 'Common/ClientStorageDriver/Cookie'; import {LocalStorageDriver} from 'Common/ClientStorageDriver/LocalStorage'; -class ClientStorage +const SupportedStorageDriver = _.find( + [LocalStorageDriver, CookieDriver], (StorageDriver) => StorageDriver && StorageDriver.supported() +); + +const driver = SupportedStorageDriver ? new SupportedStorageDriver() : null; + +/** + * @param {number} key + * @param {*} data + * @return {boolean} + */ +export function set(key, data) { - constructor() { - const SupportedStorageDriver = _.find([LocalStorageDriver, CookieDriver], - (StorageDriver) => StorageDriver && StorageDriver.supported()); - this.driver = SupportedStorageDriver ? new SupportedStorageDriver() : null; - } - - /** - * @param {number} key - * @param {*} data - * @return {boolean} - */ - set(key, data) { - return this.driver ? this.driver.set('p' + key, data) : false; - } - - /** - * @param {number} key - * @return {*} - */ - get(key) { - return this.driver ? this.driver.get('p' + key) : null; - } + return driver ? driver.set('p' + key, data) : false; } -module.exports = new ClientStorage(); +/** + * @param {number} key + * @return {*} + */ +export function get(key) +{ + return driver ? driver.get('p' + key) : null; +} diff --git a/dev/Storage/RainLoop.jsx b/dev/Storage/RainLoop.jsx index d89f4659f..e86769f64 100644 --- a/dev/Storage/RainLoop.jsx +++ b/dev/Storage/RainLoop.jsx @@ -1,105 +1,90 @@ import window from 'window'; +import JSON from 'JSON'; -const - STORAGE_KEY = '__rlA', - TIME_KEY = '__rlT' -; +const STORAGE_KEY = '__rlA'; +const TIME_KEY = '__rlT'; -class RainLoopStorage -{ - s = null; - t = null; +const SESS_STORAGE = window.sessionStorage || null; +const WIN_STORAGE = window.top || window || null; - constructor() +const __get = (key) => { + + let result = null; + if (SESS_STORAGE) { - this.s = window.sessionStorage || null; - this.t = window.top || window; - - this.init(); + result = SESS_STORAGE.getItem(key) || null; + } + else if (WIN_STORAGE && JSON) + { + const data = WIN_STORAGE.name && '{' === WIN_STORAGE.name.toString().substr(0, 1) ? JSON.parse(WIN_STORAGE.name.toString()) : null; + result = data ? (data[key] || null) : null; } - __get(key) { + return result; +}; - let result = null; - if (this.s) - { - result = this.s.getItem(key) || null; - } - else if (this.t && JSON) - { - const data = this.t.name && '{' === this.t.name.toString().substr(0, 1) ? JSON.parse(this.t.name.toString()) : null; - result = data ? (data[key] || null) : null; - } +const __set = (key, value) => { - return result; + if (SESS_STORAGE) + { + SESS_STORAGE.setItem(key, value); } + else if (WIN_STORAGE && JSON) + { + let data = WIN_STORAGE.name && '{' === WIN_STORAGE.name.toString().substr(0, 1) ? JSON.parse(WIN_STORAGE.name.toString()) : null; + data = data || {}; + data[key] = value; - __set(key, value) { - if (this.s) - { - this.s.setItem(key, value); - } - else if (this.t && JSON) - { - let data = this.t.name && '{' === this.t.name.toString().substr(0, 1) ? JSON.parse(this.t.name.toString()) : null; - data = data || {}; - data[key] = value; - - this.t.name = JSON.stringify(data); - } + WIN_STORAGE.name = JSON.stringify(data); } +}; - timestamp() { - return window.Math.round((new Date()).getTime() / 1000); - } +const timestamp = () => window.Math.round((new window.Date()).getTime() / 1000); - checkTimestamp() { +const setTimestamp = () => __set(TIME_KEY, timestamp()); - if (this.timestamp() > this.getTimestamp() + 1000 * 60 * 60) // 60m - { - this.clearHash(); - return true; - } +const getTimestamp = () => { + let time = __get(TIME_KEY, 0); + return time ? (window.parseInt(time, 10) || 0) : 0; +}; - return false; - } - - init() { - window.setInterval(() => { - this.setTimestamp(); - }, 1000 * 60); // 1m - } - - getHash() { - return this.__get(STORAGE_KEY); - } - - setHash() { - - const - key = 'AuthAccountHash', - appData = window.__rlah_data() - ; - - this.__set(STORAGE_KEY, appData && appData[key] ? appData[key] : ''); - this.setTimestamp(); - } - - - setTimestamp() { - this.__set(TIME_KEY, this.timestamp()); - } - - getTimestamp() { - let time = this.__get(TIME_KEY, 0); - return time ? (window.parseInt(time, 10) | 0) : 0; - } - - clearHash() { - this.__set(STORAGE_KEY, ''); - this.setTimestamp(); - } +/** + * @return {string} + */ +export function getHash() +{ + return __get(STORAGE_KEY); } -module.exports = new RainLoopStorage(); +export function setHash() +{ + const + key = 'AuthAccountHash', + appData = window.__rlah_data() + ; + + __set(STORAGE_KEY, appData && appData[key] ? appData[key] : ''); + setTimestamp(); +} + +export function clearHash() +{ + __set(STORAGE_KEY, ''); + setTimestamp(); +} + +export function checkTimestamp() +{ + if (timestamp() > getTimestamp() + 1000 * 60 * 60) // 60m + { + clearHash(); + return true; + } + return false; +} + +// init section +window.setInterval(() => { + setTimestamp(); +}, 1000 * 60); // 1m diff --git a/dev/Stores/Language.js b/dev/Stores/Language.js index 005583aa8..b4f9783ec 100644 --- a/dev/Stores/Language.js +++ b/dev/Stores/Language.js @@ -20,10 +20,14 @@ this.languagesAdmin = ko.observableArray([]); this.language = ko.observable('') - .extend({'limitedList': this.languages}); + .extend({'limitedList': this.languages}) + .extend({'reversible': true}) + ; this.languageAdmin = ko.observable('') - .extend({'limitedList': this.languagesAdmin}); + .extend({'limitedList': this.languagesAdmin}) + .extend({'reversible': true}) + ; this.userLanguage = ko.observable(''); this.userLanguageAdmin = ko.observable(''); diff --git a/dev/View/Popup/FolderSystem.js b/dev/View/Popup/FolderSystem.js index de879d66c..83d3d046b 100644 --- a/dev/View/Popup/FolderSystem.js +++ b/dev/View/Popup/FolderSystem.js @@ -62,7 +62,7 @@ Settings.settingsSet('TrashFolder', FolderStore.trashFolder()); Settings.settingsSet('ArchiveFolder', FolderStore.archiveFolder()); - Remote.saveSystemFolders(Utils.emptyFunction, { + Remote.saveSystemFolders(Utils.noop, { 'SentFolder': FolderStore.sentFolder(), 'DraftFolder': FolderStore.draftFolder(), 'SpamFolder': FolderStore.spamFolder(), diff --git a/dev/View/User/Login.js b/dev/View/User/Login.js index 63c101f20..935619e98 100644 --- a/dev/View/User/Login.js +++ b/dev/View/User/Login.js @@ -469,11 +469,12 @@ } _.delay(function () { + LanguageStore.language.subscribe(function (sValue) { self.langRequest(true); - Translator.reload(false, sValue, function() { + Translator.reload(false, sValue).then(function() { self.langRequest(false); self.bSendLanguage = true; }, function() { @@ -481,6 +482,7 @@ }); }); + }, 50); Utils.triggerAutocompleteInputChange(true); diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index 27c1dd08c..3a1631f83 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -258,7 +258,7 @@ MessageStore.messageListCheckedOrSelectedUidsWithSubMails(), true); }, this.canBeMoved); - this.moveCommand = Utils.createCommand(this, Utils.emptyFunction, this.canBeMoved); + this.moveCommand = Utils.createCommand(this, Utils.noop, this.canBeMoved); this.reloadCommand = Utils.createCommand(this, function () { if (!MessageStore.messageListCompleteLoadingThrottleForAnimation() && this.allowReload) @@ -558,7 +558,7 @@ Cache.clearMessageFlagsFromCacheByFolder(sFolderFullNameRaw); } - Remote.messageSetSeenToAll(Utils.emptyFunction, sFolderFullNameRaw, true); + Remote.messageSetSeenToAll(Utils.noop, sFolderFullNameRaw, true); break; case Enums.MessageSetAction.UnsetSeen: oFolder = Cache.getFolderFromCacheList(sFolderFullNameRaw); @@ -571,7 +571,7 @@ oFolder.messageCountUnread(oFolder.messageCountAll()); Cache.clearMessageFlagsFromCacheByFolder(sFolderFullNameRaw); } - Remote.messageSetSeenToAll(Utils.emptyFunction, sFolderFullNameRaw, false); + Remote.messageSetSeenToAll(Utils.noop, sFolderFullNameRaw, false); break; } @@ -784,11 +784,6 @@ { var self = this; - // disable print - key('ctrl+p, command+p', Enums.KeyState.MessageList, function () { - return false; - }); - key('enter', Enums.KeyState.MessageList, function () { if (self.message() && self.useAutoSelect()) { diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 415eebb33..4ceef95f6 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -186,7 +186,7 @@ Local.set(Enums.ClientSideKeyName.LastReplyAction, sValue); }); - this.showFullInfo = ko.observable(false); + this.showFullInfo = ko.observable('1' === Local.get(Enums.ClientSideKeyName.MessageHeaderFullInfo)); this.moreDropdownTrigger = ko.observable(false); this.messageDomFocused = ko.observable(false).extend({'rateLimit': 0}); @@ -217,7 +217,7 @@ this.forwardAsAttachmentCommand = createCommandHelper(Enums.ComposeType.ForwardAsAttachment); this.editAsNewCommand = createCommandHelper(Enums.ComposeType.EditAsNew); - this.messageVisibilityCommand = Utils.createCommand(this, Utils.emptyFunction, this.messageVisibility); + this.messageVisibilityCommand = Utils.createCommand(this, Utils.noop, this.messageVisibility); this.messageEditCommand = Utils.createCommand(this, function () { this.editMessage(); @@ -672,9 +672,10 @@ _.delay(fCheckHeaderHeight, 500); }, 50)); - this.showFullInfo.subscribe(function () { + this.showFullInfo.subscribe(function (value) { Utils.windowResize(); Utils.windowResize(250); + Local.set(Enums.ClientSideKeyName.MessageHeaderFullInfo, value ? '1' : '0'); }); if (this.dropboxEnabled() && this.dropboxApiKey() && !window.Dropbox) @@ -873,13 +874,13 @@ }); // message information - // key('i', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () { - // if (MessageStore.message()) - // { - // self.showFullInfo(!self.showFullInfo()); - // return false; - // } - // }); + key('ctrl+i, command+i', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () { + if (MessageStore.message()) + { + self.showFullInfo(!self.showFullInfo()); + } + return false; + }); // toggle message blockquotes key('b', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () { @@ -901,7 +902,7 @@ }); // print - key('ctrl+p, command+p', Enums.KeyState.MessageView, function () { + key('ctrl+p, command+p', [Enums.KeyState.MessageView, Enums.KeyState.MessageList], function () { if (self.message()) { self.message().printMessage(); @@ -1199,7 +1200,7 @@ { if (oMessage && '' !== oMessage.readReceipt()) { - Remote.sendReadReceiptMessage(Utils.emptyFunction, oMessage.folderFullNameRaw, oMessage.uid, + Remote.sendReadReceiptMessage(Utils.noop, oMessage.folderFullNameRaw, oMessage.uid, oMessage.readReceipt(), Translator.i18n('READ_RECEIPT/SUBJECT', {'SUBJECT': oMessage.subject()}), Translator.i18n('READ_RECEIPT/BODY', {'READ-RECEIPT': AccountStore.email()})); diff --git a/dev/bootstrap.jsx b/dev/bootstrap.jsx index d39778df5..bc10405ec 100644 --- a/dev/bootstrap.jsx +++ b/dev/bootstrap.jsx @@ -5,7 +5,7 @@ import {kill_CtrlA_CtrlS, detectDropdownVisibility, createCommand} from 'Common/ import {$win, $html, data as GlobalsData, bMobileDevice} from 'Common/Globals'; import * as Enums from 'Common/Enums'; import * as Plugins from 'Common/Plugins'; -import Translator from 'Common/Translator'; +import {i18n} from 'Common/Translator'; import EmailModel from 'Model/Email'; export default (App) => { @@ -28,7 +28,7 @@ export default (App) => { const rl = window.rl || {}; - rl.i18n = Translator.i18n; + rl.i18n = i18n; rl.createCommand = createCommand; rl.addSettingsViewModel = Plugins.addSettingsViewModel; diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 000000000..efbc4ea97 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,23 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=759670 + // for the documentation about the jsconfig.json format + "compilerOptions": { + "target": "es2015", + "module": "commonjs", + "allowSyntheticDefaultImports": true + }, + "exclude": [ + "build", + "data", + "vendors", + "rainloop", + "plugins", + "node_modules", + "bower_components", + "jspm_packages", + "nbproject", + "tests", + "tmp", + "temp" + ] +}