Code refactoring (promises for folders management)

This commit is contained in:
RainLoop Team 2015-03-17 18:12:19 +04:00
parent 9bec1d0a8f
commit bab64a7cea
19 changed files with 467 additions and 333 deletions

View file

@ -26,7 +26,6 @@
Cache = require('Common/Cache'), Cache = require('Common/Cache'),
SocialStore = require('Stores/Social'), SocialStore = require('Stores/Social'),
AppStore = require('Stores/User/App'),
SettingsStore = require('Stores/User/Settings'), SettingsStore = require('Stores/User/Settings'),
AccountStore = require('Stores/User/Account'), AccountStore = require('Stores/User/Account'),
IdentityStore = require('Stores/User/Identity'), IdentityStore = require('Stores/User/Identity'),
@ -40,9 +39,9 @@
Settings = require('Storage/Settings'), Settings = require('Storage/Settings'),
Remote = require('Remote/User/Ajax'), Remote = require('Remote/User/Ajax'),
Promises = require('Promises/User/Ajax'),
EmailModel = require('Model/Email'), EmailModel = require('Model/Email'),
FolderModel = require('Model/Folder'),
AccountModel = require('Model/Account'), AccountModel = require('Model/Account'),
IdentityModel = require('Model/Identity'), IdentityModel = require('Model/Identity'),
TemplateModel = require('Model/Template'), TemplateModel = require('Model/Template'),
@ -434,27 +433,35 @@
/** /**
* @param {Function=} fCallback * @param {Function=} fCallback
*/ */
AppUser.prototype.folders = function (fCallback) AppUser.prototype.foldersReload = function (fCallback)
{ {
FolderStore.foldersLoading(true); Promises.foldersReload(FolderStore.foldersLoading).then(function (bValue) {
Remote.folders(_.bind(function (sResult, oData) {
var bResult = false;
FolderStore.foldersLoading(false);
if (Enums.StorageResultType.Success === sResult && oData && oData.Result)
{
bResult = true;
this.setFolders(oData);
}
if (fCallback) if (fCallback)
{ {
fCallback(bResult); fCallback(!!bValue);
} }
}).fail(function () {
if (fCallback)
{
fCallback(false);
}
});
};
}, this)); AppUser.prototype.foldersPromisesActionHelper = function (oPromise, iErrorDefCode)
{
Promises
.abort('Folders')
.fastResolve(true)
.then(function () {
return oPromise;
})
.fail(function (iErrorCode) {
FolderStore.folderList.error(Translator.getNotification(iErrorCode, '', iErrorDefCode));
}).fin(function () {
Promises.foldersReloadWithTimeout(FolderStore.foldersLoading);
}).done()
;
}; };
AppUser.prototype.reloadOpenPgpKeys = function () AppUser.prototype.reloadOpenPgpKeys = function ()
@ -1026,157 +1033,6 @@
}, sQuery); }, sQuery);
}; };
/**
* @param {string} sNamespace
* @param {Array} aFolders
* @return {Array}
*/
AppUser.prototype.folderResponseParseRec = function (sNamespace, aFolders)
{
var
self = this,
iIndex = 0,
iLen = 0,
oFolder = null,
oCacheFolder = null,
sFolderFullNameRaw = '',
aSubFolders = [],
aList = []
;
for (iIndex = 0, iLen = aFolders.length; iIndex < iLen; iIndex++)
{
oFolder = aFolders[iIndex];
if (oFolder)
{
sFolderFullNameRaw = oFolder.FullNameRaw;
oCacheFolder = Cache.getFolderFromCacheList(sFolderFullNameRaw);
if (!oCacheFolder)
{
oCacheFolder = FolderModel.newInstanceFromJson(oFolder);
if (oCacheFolder)
{
Cache.setFolderToCacheList(sFolderFullNameRaw, oCacheFolder);
Cache.setFolderFullNameRaw(oCacheFolder.fullNameHash, sFolderFullNameRaw, oCacheFolder);
}
}
if (oCacheFolder)
{
oCacheFolder.collapsed(!self.isFolderExpanded(oCacheFolder.fullNameHash));
if (oFolder.Extended)
{
if (oFolder.Extended.Hash)
{
Cache.setFolderHash(oCacheFolder.fullNameRaw, oFolder.Extended.Hash);
}
if (Utils.isNormal(oFolder.Extended.MessageCount))
{
oCacheFolder.messageCountAll(oFolder.Extended.MessageCount);
}
if (Utils.isNormal(oFolder.Extended.MessageUnseenCount))
{
oCacheFolder.messageCountUnread(oFolder.Extended.MessageUnseenCount);
}
}
aSubFolders = oFolder['SubFolders'];
if (aSubFolders && 'Collection/FolderCollection' === aSubFolders['@Object'] &&
aSubFolders['@Collection'] && Utils.isArray(aSubFolders['@Collection']))
{
oCacheFolder.subFolders(
this.folderResponseParseRec(sNamespace, aSubFolders['@Collection']));
}
aList.push(oCacheFolder);
}
}
}
return aList;
};
/**
* @param {*} oData
*/
AppUser.prototype.setFolders = function (oData)
{
var
bUpdate = false,
fNormalizeFolder = function (sFolderFullNameRaw) {
return ('' === sFolderFullNameRaw || Consts.Values.UnuseOptionValue === sFolderFullNameRaw ||
null !== Cache.getFolderFromCacheList(sFolderFullNameRaw)) ? sFolderFullNameRaw : '';
}
;
if (oData && oData.Result && 'Collection/FolderCollection' === oData.Result['@Object'] &&
oData.Result['@Collection'] && Utils.isArray(oData.Result['@Collection']))
{
if (!Utils.isUnd(oData.Result.Namespace))
{
FolderStore.namespace = oData.Result.Namespace;
}
AppStore.threadsAllowed(!!Settings.settingsGet('UseImapThread') &&
oData.Result.IsThreadsSupported && true);
FolderStore.folderList(this.folderResponseParseRec(FolderStore.namespace, oData.Result['@Collection']));
FolderStore.folderList.optimized(!!oData.Result.Optimized);
if (oData.Result['SystemFolders'] && '' === '' +
Settings.settingsGet('SentFolder') +
Settings.settingsGet('DraftFolder') +
Settings.settingsGet('SpamFolder') +
Settings.settingsGet('TrashFolder') +
Settings.settingsGet('ArchiveFolder') +
Settings.settingsGet('NullFolder'))
{
// TODO Magic Numbers
Settings.settingsSet('SentFolder', oData.Result['SystemFolders'][2] || null);
Settings.settingsSet('DraftFolder', oData.Result['SystemFolders'][3] || null);
Settings.settingsSet('SpamFolder', oData.Result['SystemFolders'][4] || null);
Settings.settingsSet('TrashFolder', oData.Result['SystemFolders'][5] || null);
Settings.settingsSet('ArchiveFolder', oData.Result['SystemFolders'][12] || null);
bUpdate = true;
}
FolderStore.sentFolder(fNormalizeFolder(Settings.settingsGet('SentFolder')));
FolderStore.draftFolder(fNormalizeFolder(Settings.settingsGet('DraftFolder')));
FolderStore.spamFolder(fNormalizeFolder(Settings.settingsGet('SpamFolder')));
FolderStore.trashFolder(fNormalizeFolder(Settings.settingsGet('TrashFolder')));
FolderStore.archiveFolder(fNormalizeFolder(Settings.settingsGet('ArchiveFolder')));
if (bUpdate)
{
Remote.saveSystemFolders(Utils.emptyFunction, {
'SentFolder': FolderStore.sentFolder(),
'DraftFolder': FolderStore.draftFolder(),
'SpamFolder': FolderStore.spamFolder(),
'TrashFolder': FolderStore.trashFolder(),
'ArchiveFolder': FolderStore.archiveFolder(),
'NullFolder': 'NullFolder'
});
}
Local.set(Enums.ClientSideKeyName.FoldersLashHash, oData.Result.FoldersHash);
}
};
/**
* @param {string} sFullNameHash
* @return {boolean}
*/
AppUser.prototype.isFolderExpanded = function (sFullNameHash)
{
var aExpandedList = Local.get(Enums.ClientSideKeyName.ExpandedFolders);
return Utils.isArray(aExpandedList) && -1 !== _.indexOf(aExpandedList, sFullNameHash);
};
/** /**
* @param {string} sFullNameHash * @param {string} sFullNameHash
* @param {boolean} bExpanded * @param {boolean} bExpanded
@ -1429,7 +1285,7 @@
//require.ensure([], function() { // require code splitting //require.ensure([], function() { // require code splitting
self.folders(_.bind(function (bValue) { self.foldersReload(_.bind(function (bValue) {
kn.hideLoading(); kn.hideLoading();
@ -1498,7 +1354,7 @@
}); });
Events.sub('interval.20m', function () { Events.sub('interval.20m', function () {
self.folders(); self.foldersReload();
}); });
iContactsSyncInterval = 5 <= iContactsSyncInterval ? iContactsSyncInterval : 20; iContactsSyncInterval = 5 <= iContactsSyncInterval ? iContactsSyncInterval : 20;

View file

@ -235,9 +235,10 @@
/** /**
* @param {number} iCode * @param {number} iCode
* @param {*=} mMessage = '' * @param {*=} mMessage = ''
* @param {*=} mDefCode = null
* @return {string} * @return {string}
*/ */
Translator.prototype.getNotification = function (iCode, mMessage) Translator.prototype.getNotification = function (iCode, mMessage, mDefCode)
{ {
iCode = window.parseInt(iCode, 10) || 0; iCode = window.parseInt(iCode, 10) || 0;
if (Enums.Notification.ClientViewError === iCode && mMessage) if (Enums.Notification.ClientViewError === iCode && mMessage)
@ -245,7 +246,9 @@
return mMessage; return mMessage;
} }
return _.isUndefined(this.notificationI18N[iCode]) ? '' : this.notificationI18N[iCode]; return _.isUndefined(this.notificationI18N[iCode]) ? (
mDefCode && _.isUndefined(this.notificationI18N[mDefCode]) ? this.notificationI18N[mDefCode] : ''
) : this.notificationI18N[iCode];
}; };
/** /**

View file

@ -20,30 +20,18 @@
{ {
AbstractModel.call(this, 'MessageSimpleModel'); AbstractModel.call(this, 'MessageSimpleModel');
this.selected = false; this.clear();
this.folderFullNameRaw = '';
this.uid = '';
this.size = 0;
this.sender = '';
this.subject = '';
this.dateUTC = 0;
} }
_.extend(MessageSimpleModel.prototype, AbstractModel.prototype); _.extend(MessageSimpleModel.prototype, AbstractModel.prototype);
/** MessageSimpleModel.prototype.selected = false;
* @static MessageSimpleModel.prototype.folderFullNameRaw = '';
* @param {AjaxJsonMessage} oJsonMessage MessageSimpleModel.prototype.uid = '';
* @return {?MessageSimpleModel} MessageSimpleModel.prototype.size = 0;
*/ MessageSimpleModel.prototype.sender = '';
MessageSimpleModel.newInstanceFromJson = function (oJsonMessage) MessageSimpleModel.prototype.subject = '';
{ MessageSimpleModel.prototype.dateInUTC = 0;
var oMessageModel = new MessageSimpleModel();
return oMessageModel.initByJson(oJsonMessage) ? oMessageModel : null;
};
MessageSimpleModel.prototype.clear = function () MessageSimpleModel.prototype.clear = function ()
{ {
@ -56,7 +44,7 @@
this.sender = ''; this.sender = '';
this.subject = ''; this.subject = '';
this.dateUTC = 0; this.dateInUTC = 0;
}; };
/** /**
@ -80,7 +68,7 @@
this.subject = oJsonMessage.Subject; this.subject = oJsonMessage.Subject;
this.dateUTC = Utils.pInt(oJsonMessage.DateTimeStampInUTC); this.dateInUTC = Utils.pInt(oJsonMessage.DateTimeStampInUTC);
bResult = true; bResult = true;
} }
@ -88,6 +76,17 @@
return bResult; return bResult;
}; };
/**
* @static
* @param {AjaxJsonMessage} oJsonMessage
* @return {?MessageSimpleModel}
*/
MessageSimpleModel.newInstanceFromJson = function (oJsonMessage)
{
var oMessageModel = new MessageSimpleModel();
return oMessageModel.initByJson(oJsonMessage) ? oMessageModel : null;
};
module.exports = MessageSimpleModel; module.exports = MessageSimpleModel;
}()); }());

View file

@ -5,6 +5,7 @@
var var
$ = require('$'), $ = require('$'),
_ = require('_'),
Q = require('Q'), Q = require('Q'),
Consts = require('Common/Consts'), Consts = require('Common/Consts'),
@ -12,7 +13,9 @@
Utils = require('Common/Utils'), Utils = require('Common/Utils'),
Links = require('Common/Links'), Links = require('Common/Links'),
Settings = require('Storage/Settings') Settings = require('Storage/Settings'),
AbstractBasicPromises = require('Promises/AbstractBasic')
; ;
/** /**
@ -20,21 +23,18 @@
*/ */
function AbstractAjaxPromises() function AbstractAjaxPromises()
{ {
this.oRequests = {}; AbstractBasicPromises.call(this);
this.clear();
} }
AbstractAjaxPromises.prototype.func = function (fFunc) _.extend(AbstractAjaxPromises.prototype, AbstractBasicPromises.prototype);
{
fFunc();
return this; AbstractAjaxPromises.prototype.oRequests = {};
};
AbstractAjaxPromises.prototype.fastResolve = function (mData) AbstractAjaxPromises.prototype.clear = function ()
{ {
var oDeferred = Q.defer(); this.oRequests = {};
oDeferred.resolve(mData);
return oDeferred.promise;
}; };
AbstractAjaxPromises.prototype.abort = function (sAction, bClearOnly) AbstractAjaxPromises.prototype.abort = function (sAction, bClearOnly)
@ -43,7 +43,7 @@
{ {
if (!bClearOnly && this.oRequests[sAction].abort) if (!bClearOnly && this.oRequests[sAction].abort)
{ {
this.oRequests[sAction].__aborted = true; this.oRequests[sAction].__aborted__ = true;
this.oRequests[sAction].abort(); this.oRequests[sAction].abort();
} }
@ -71,10 +71,7 @@
oParameters['XToken'] = Settings.settingsGet('Token'); oParameters['XToken'] = Settings.settingsGet('Token');
} }
if (fTrigger) this.setTrigger(fTrigger, true);
{
fTrigger(true);
}
oH = $.ajax({ oH = $.ajax({
'type': bPost ? 'POST' : 'GET', 'type': bPost ? 'POST' : 'GET',
@ -96,8 +93,8 @@
{ {
if (oData && oData.Result && sAction === oData.Action) if (oData && oData.Result && sAction === oData.Action)
{ {
oData.Result.__cached__ = bCached; oData.__cached__ = bCached;
oDeferred.resolve(oData.Result); oDeferred.resolve(oData);
} }
else if (oData && oData.Action) else if (oData && oData.Action)
{ {
@ -114,7 +111,7 @@
} }
else if ('abort' === sTextStatus) else if ('abort' === sTextStatus)
{ {
if (!oData || !oData.__aborted) if (!oData || !oData.__aborted__)
{ {
oDeferred.reject(Enums.Notification.AjaxAbort); oDeferred.reject(Enums.Notification.AjaxAbort);
} }
@ -130,10 +127,7 @@
delete self.oRequests[sAction]; delete self.oRequests[sAction];
} }
if (fTrigger) self.setTrigger(fTrigger, false);
{
fTrigger(false);
}
}); });
if (oH) if (oH)

View file

@ -0,0 +1,56 @@
(function () {
'use strict';
var
_ = require('_'),
Q = require('Q'),
Utils = require('Common/Utils')
;
/**
* @constructor
*/
function AbstractBasicPromises()
{
this.oPromisesStack = {};
}
AbstractBasicPromises.prototype.func = function (fFunc)
{
fFunc();
return this;
};
AbstractBasicPromises.prototype.fastResolve = function (mData)
{
var oDeferred = Q.defer();
oDeferred.resolve(mData);
return oDeferred.promise;
};
AbstractBasicPromises.prototype.fastReject = function (mData)
{
var oDeferred = Q.defer();
oDeferred.reject(mData);
return oDeferred.promise;
};
AbstractBasicPromises.prototype.setTrigger = function (mTrigger, bValue)
{
if (mTrigger)
{
_.each(Utils.isArray(mTrigger) ? mTrigger : [mTrigger], function (fTrigger) {
if (fTrigger)
{
fTrigger(!!bValue);
}
});
}
};
module.exports = AbstractBasicPromises;
}());

View file

@ -4,8 +4,20 @@
'use strict'; 'use strict';
var var
window = require('window'),
_ = require('_'), _ = require('_'),
// Enums = require('Common/Enums'),
// Utils = require('Common/Utils'),
// Base64 = require('Common/Base64'),
// Cache = require('Common/Cache'),
//
// AppStore = require('Stores/User/App'),
// SettingsStore = require('Stores/User/Settings'),
MessageSimpleModel = require('Model/MessageSimple'),
PromisesPopulator = require('Promises/User/Populator'),
AbstractAjaxPromises = require('Promises/AbstractAjax') AbstractAjaxPromises = require('Promises/AbstractAjax')
; ;
@ -13,17 +25,19 @@
* @constructor * @constructor
* @extends AbstractAjaxPromises * @extends AbstractAjaxPromises
*/ */
function PromisesUserAjax() function UserAjaxUserPromises()
{ {
AbstractAjaxPromises.call(this); AbstractAjaxPromises.call(this);
this.messageListSimpleHash = ''; this.messageListSimpleHash = '';
this.messageListSimpleCache = null; this.messageListSimpleCache = null;
this.sSubSubQuery = '&ss=/';
} }
_.extend(PromisesUserAjax.prototype, AbstractAjaxPromises.prototype); _.extend(UserAjaxUserPromises.prototype, AbstractAjaxPromises.prototype);
PromisesUserAjax.prototype.messageListSimple = function (sFolder, aUids, fTrigger) UserAjaxUserPromises.prototype.messageListSimple = function (sFolder, aUids, fTrigger)
{ {
var self = this, sHash = sFolder + '~' + aUids.join('/'); var self = this, sHash = sFolder + '~' + aUids.join('/');
if (sHash === this.messageListSimpleHash && this.messageListSimpleCache) if (sHash === this.messageListSimpleHash && this.messageListSimpleCache)
@ -35,23 +49,93 @@
.postRequest('MessageListSimple', fTrigger, { .postRequest('MessageListSimple', fTrigger, {
'Folder': sFolder, 'Folder': sFolder,
'Uids': aUids 'Uids': aUids
}).then(function (aData) { }).then(function (oData) {
var
MessageSimpleModel = require('Model/MessageSimple'),
aResult = _.compact(_.map(aData, function (aItem) {
return MessageSimpleModel.newInstanceFromJson(aItem);
}))
;
self.messageListSimpleHash = sHash; self.messageListSimpleHash = sHash;
self.messageListSimpleCache = aResult; self.messageListSimpleCache = _.compact(_.map(oData.Result, function (aItem) {
return MessageSimpleModel.newInstanceFromJson(aItem);
}));
return aResult; return self.messageListSimpleCache;
}, function (iError) {
self.messageListSimpleHash = '';
self.messageListSimpleCache = null;
return self.fastReject(iError);
}) })
; ;
}; };
module.exports = new PromisesUserAjax(); UserAjaxUserPromises.prototype.foldersReload = function (fTrigger)
{
return this.abort('Folders')
.postRequest('Folders', fTrigger).then(function (oData) {
PromisesPopulator.foldersList(oData.Result);
PromisesPopulator.foldersAdditionalParameters(oData.Result);
return true;
});
};
UserAjaxUserPromises.prototype._folders_timeout_ = 0;
UserAjaxUserPromises.prototype.foldersReloadWithTimeout = function (fTrigger)
{
this.setTrigger(fTrigger, true);
var self = this;
window.clearTimeout(this._folders_timeout_);
this._folders_timeout_ = window.setTimeout(function () {
self.foldersReload(fTrigger);
}, 500);
};
UserAjaxUserPromises.prototype.folderDelete = function (sFolderFullNameRaw, fTrigger)
{
return this.postRequest('FolderDelete', fTrigger, {
'Folder': sFolderFullNameRaw
});
};
UserAjaxUserPromises.prototype.folderCreate = function (sNewFolderName, sParentName, fTrigger)
{
return this.postRequest('FolderCreate', fTrigger, {
'Folder': sNewFolderName,
'Parent': sParentName
});
};
UserAjaxUserPromises.prototype.folderRename = function (sPrevFolderFullNameRaw, sNewFolderName, fTrigger)
{
return this.postRequest('FolderRename', fTrigger, {
'Folder': sPrevFolderFullNameRaw,
'NewFolderName': sNewFolderName
});
};
// UserAjaxUserPromises.prototype.message = function (sFolderFullNameRaw, iUid, fTrigger)
// {
// sFolderFullNameRaw = Utils.pString(sFolderFullNameRaw);
// iUid = Utils.pInt(iUid);
//
// if (Cache.getFolderFromCacheList(sFolderFullNameRaw) && 0 >= iUid)
// {
// return this.abort('Message')
// .getRequest('Message', fTrigger,
// this.sSubSubQuery + Base64.urlsafe_encode([
// sFolderFullNameRaw, iUid,
// AppStore.projectHash(),
// AppStore.threadsAllowed() && SettingsStore.useThreads() ? '1' : '0'
// ].join(String.fromCharCode(0))))
// .then(function (oData) {
// return oData;
// });
// }
//
// return this.fastReject(Enums.Notification.UnknownError);
// };
module.exports = new UserAjaxUserPromises();
}()); }());

View file

@ -0,0 +1,199 @@
(function () {
'use strict';
var
_ = require('_'),
Consts = require('Common/Consts'),
Enums = require('Common/Enums'),
Utils = require('Common/Utils'),
Cache = require('Common/Cache'),
AppStore = require('Stores/User/App'),
FolderStore = require('Stores/User/Folder'),
Settings = require('Storage/Settings'),
Local = require('Storage/Client'),
FolderModel = require('Model/Folder'),
AbstractBasicPromises = require('Promises/AbstractBasic')
;
/**
* @constructor
*/
function PromisesUserPopulator()
{
AbstractBasicPromises.call(this);
}
_.extend(PromisesUserPopulator.prototype, AbstractBasicPromises.prototype);
/**
* @param {string} sFullNameHash
* @return {boolean}
*/
PromisesUserPopulator.prototype.isFolderExpanded = function (sFullNameHash)
{
var aExpandedList = Local.get(Enums.ClientSideKeyName.ExpandedFolders);
return Utils.isArray(aExpandedList) && -1 !== _.indexOf(aExpandedList, sFullNameHash);
};
/**
* @param {string} sFolderFullNameRaw
* @return {string}
*/
PromisesUserPopulator.prototype.normalizeFolder = function (sFolderFullNameRaw)
{
return ('' === sFolderFullNameRaw || Consts.Values.UnuseOptionValue === sFolderFullNameRaw ||
null !== Cache.getFolderFromCacheList(sFolderFullNameRaw)) ? sFolderFullNameRaw : '';
};
/**
* @param {string} sNamespace
* @param {Array} aFolders
* @return {Array}
*/
PromisesUserPopulator.prototype.folderResponseParseRec = function (sNamespace, aFolders)
{
var
self = this,
iIndex = 0,
iLen = 0,
oFolder = null,
oCacheFolder = null,
sFolderFullNameRaw = '',
aSubFolders = [],
aList = []
;
for (iIndex = 0, iLen = aFolders.length; iIndex < iLen; iIndex++)
{
oFolder = aFolders[iIndex];
if (oFolder)
{
sFolderFullNameRaw = oFolder.FullNameRaw;
oCacheFolder = Cache.getFolderFromCacheList(sFolderFullNameRaw);
if (!oCacheFolder)
{
oCacheFolder = FolderModel.newInstanceFromJson(oFolder);
if (oCacheFolder)
{
Cache.setFolderToCacheList(sFolderFullNameRaw, oCacheFolder);
Cache.setFolderFullNameRaw(oCacheFolder.fullNameHash, sFolderFullNameRaw, oCacheFolder);
}
}
if (oCacheFolder)
{
oCacheFolder.collapsed(!self.isFolderExpanded(oCacheFolder.fullNameHash));
if (oFolder.Extended)
{
if (oFolder.Extended.Hash)
{
Cache.setFolderHash(oCacheFolder.fullNameRaw, oFolder.Extended.Hash);
}
if (Utils.isNormal(oFolder.Extended.MessageCount))
{
oCacheFolder.messageCountAll(oFolder.Extended.MessageCount);
}
if (Utils.isNormal(oFolder.Extended.MessageUnseenCount))
{
oCacheFolder.messageCountUnread(oFolder.Extended.MessageUnseenCount);
}
}
aSubFolders = oFolder['SubFolders'];
if (aSubFolders && 'Collection/FolderCollection' === aSubFolders['@Object'] &&
aSubFolders['@Collection'] && Utils.isArray(aSubFolders['@Collection']))
{
oCacheFolder.subFolders(
this.folderResponseParseRec(sNamespace, aSubFolders['@Collection']));
}
aList.push(oCacheFolder);
}
}
}
return aList;
};
PromisesUserPopulator.prototype.foldersList = function (oData)
{
if (oData && 'Collection/FolderCollection' === oData['@Object'] &&
oData['@Collection'] && Utils.isArray(oData['@Collection']))
{
FolderStore.folderList(this.folderResponseParseRec(
Utils.isUnd(oData.Namespace) ? '' : oData.Namespace, oData['@Collection']));
}
};
PromisesUserPopulator.prototype.foldersAdditionalParameters = function (oData)
{
if (oData && oData && 'Collection/FolderCollection' === oData['@Object'] &&
oData['@Collection'] && Utils.isArray(oData['@Collection']))
{
if (!Utils.isUnd(oData.Namespace))
{
FolderStore.namespace = oData.Namespace;
}
AppStore.threadsAllowed(
!!Settings.settingsGet('UseImapThread') &&
oData.IsThreadsSupported && true);
FolderStore.folderList.optimized(!!oData.Optimized);
var bUpdate = false;
if (oData['SystemFolders'] && '' === '' +
Settings.settingsGet('SentFolder') +
Settings.settingsGet('DraftFolder') +
Settings.settingsGet('SpamFolder') +
Settings.settingsGet('TrashFolder') +
Settings.settingsGet('ArchiveFolder') +
Settings.settingsGet('NullFolder'))
{
// TODO Magic Numbers
Settings.settingsSet('SentFolder', oData['SystemFolders'][2] || null);
Settings.settingsSet('DraftFolder', oData['SystemFolders'][3] || null);
Settings.settingsSet('SpamFolder', oData['SystemFolders'][4] || null);
Settings.settingsSet('TrashFolder', oData['SystemFolders'][5] || null);
Settings.settingsSet('ArchiveFolder', oData['SystemFolders'][12] || null);
bUpdate = true;
}
FolderStore.sentFolder(this.normalizeFolder(Settings.settingsGet('SentFolder')));
FolderStore.draftFolder(this.normalizeFolder(Settings.settingsGet('DraftFolder')));
FolderStore.spamFolder(this.normalizeFolder(Settings.settingsGet('SpamFolder')));
FolderStore.trashFolder(this.normalizeFolder(Settings.settingsGet('TrashFolder')));
FolderStore.archiveFolder(this.normalizeFolder(Settings.settingsGet('ArchiveFolder')));
if (bUpdate)
{
require('Remote/User/Ajax').saveSystemFolders(Utils.emptyFunction, {
'SentFolder': FolderStore.sentFolder(),
'DraftFolder': FolderStore.draftFolder(),
'SpamFolder': FolderStore.spamFolder(),
'TrashFolder': FolderStore.trashFolder(),
'ArchiveFolder': FolderStore.archiveFolder(),
'NullFolder': 'NullFolder'
});
}
Local.set(Enums.ClientSideKeyName.FoldersLashHash, oData.FoldersHash);
}
};
module.exports = new PromisesUserPopulator();
}());

View file

@ -721,43 +721,6 @@
}); });
}; };
/**
* @param {?Function} fCallback
* @param {string} sNewFolderName
* @param {string} sParentName
*/
RemoteUserAjax.prototype.folderCreate = function (fCallback, sNewFolderName, sParentName)
{
this.defaultRequest(fCallback, 'FolderCreate', {
'Folder': sNewFolderName,
'Parent': sParentName
}, null, '', ['Folders']);
};
/**
* @param {?Function} fCallback
* @param {string} sFolderFullNameRaw
*/
RemoteUserAjax.prototype.folderDelete = function (fCallback, sFolderFullNameRaw)
{
this.defaultRequest(fCallback, 'FolderDelete', {
'Folder': sFolderFullNameRaw
}, null, '', ['Folders']);
};
/**
* @param {?Function} fCallback
* @param {string} sPrevFolderFullNameRaw
* @param {string} sNewFolderName
*/
RemoteUserAjax.prototype.folderRename = function (fCallback, sPrevFolderFullNameRaw, sNewFolderName)
{
this.defaultRequest(fCallback, 'FolderRename', {
'Folder': sPrevFolderFullNameRaw,
'NewFolderName': sNewFolderName
}, null, '', ['Folders']);
};
/** /**
* @param {?Function} fCallback * @param {?Function} fCallback
* @param {string} sFolderFullNameRaw * @param {string} sFolderFullNameRaw

View file

@ -17,6 +17,7 @@
FolderStore = require('Stores/User/Folder'), FolderStore = require('Stores/User/Folder'),
Promises = require('Promises/User/Ajax'),
Remote = require('Remote/User/Ajax') Remote = require('Remote/User/Ajax')
; ;
@ -69,19 +70,9 @@
{ {
Local.set(Enums.ClientSideKeyName.FoldersLashHash, ''); Local.set(Enums.ClientSideKeyName.FoldersLashHash, '');
FolderStore.foldersRenaming(true); require('App/User').foldersPromisesActionHelper(
Remote.folderRename(function (sResult, oData) { Promises.folderRename(oFolder.fullNameRaw, sEditName, FolderStore.foldersRenaming),
Enums.Notification.CantRenameFolder);
FolderStore.foldersRenaming(false);
if (Enums.StorageResultType.Success !== sResult || !oData || !oData.Result)
{
FolderStore.folderList.error(
oData && oData.ErrorCode ? Translator.getNotification(oData.ErrorCode) : Translator.i18n('NOTIFICATIONS/CANT_RENAME_FOLDER'));
}
require('App/User').folders();
}, oFolder.fullNameRaw, sEditName);
Cache.removeFolderFromCacheList(oFolder.fullNameRaw); Cache.removeFolderFromCacheList(oFolder.fullNameRaw);
@ -140,19 +131,9 @@
FolderStore.folderList.remove(fRemoveFolder); FolderStore.folderList.remove(fRemoveFolder);
FolderStore.foldersDeleting(true); require('App/User').foldersPromisesActionHelper(
Remote.folderDelete(function (sResult, oData) { Promises.folderDelete(oFolderToRemove.fullNameRaw, FolderStore.foldersDeleting),
Enums.Notification.CantDeleteFolder);
FolderStore.foldersDeleting(false);
if (Enums.StorageResultType.Success !== sResult || !oData || !oData.Result)
{
FolderStore.folderList.error(
oData && oData.ErrorCode ? Translator.getNotification(oData.ErrorCode) : Translator.i18n('NOTIFICATIONS/CANT_DELETE_FOLDER'));
}
require('App/User').folders();
}, oFolderToRemove.fullNameRaw);
Cache.removeFolderFromCacheList(oFolderToRemove.fullNameRaw); Cache.removeFolderFromCacheList(oFolderToRemove.fullNameRaw);
} }

View file

@ -4,7 +4,6 @@
'use strict'; 'use strict';
var var
_ = require('_'),
ko = require('ko'), ko = require('ko'),
Utils = require('Common/Utils'), Utils = require('Common/Utils'),

View file

@ -685,10 +685,6 @@
{ {
this.messageThreadLoading(true); this.messageThreadLoading(true);
} }
else
{
Utils.log('Error: Unknown message request: ' + sFolder + ' ~ ' + sUid + ' [e-102]');
}
if (Enums.Layout.NoPreview === SettingsStore.layout()) if (Enums.Layout.NoPreview === SettingsStore.layout())
{ {
@ -705,10 +701,6 @@
{ {
this.messageCurrentLoading(true); this.messageCurrentLoading(true);
} }
else
{
Utils.log('Error: Unknown message request: ' + oMessage.folderFullNameRaw + ' ~ ' + oMessage.uid + ' [e-101]');
}
} }
}; };

View file

@ -398,7 +398,7 @@ html.rl-no-preview-pane {
background-color: #999; background-color: #999;
color: #fff; color: #fff;
border-radius: 6px; border-radius: 6px;
padding: 0px 4px; padding: 1px 6px 1px 5px;
margin-right: 5px; margin-right: 5px;
} }
} }

View file

@ -15,7 +15,7 @@
FolderStore = require('Stores/User/Folder'), FolderStore = require('Stores/User/Folder'),
Remote = require('Remote/User/Ajax'), Promises = require('Promises/User/Ajax'),
kn = require('Knoin/Knoin'), kn = require('Knoin/Knoin'),
AbstractView = require('Knoin/AbstractView') AbstractView = require('Knoin/AbstractView')
@ -76,21 +76,9 @@
sParentFolderName = FolderStore.namespace.substr(0, FolderStore.namespace.length - 1); sParentFolderName = FolderStore.namespace.substr(0, FolderStore.namespace.length - 1);
} }
FolderStore.foldersCreating(true); require('App/User').foldersPromisesActionHelper(
Remote.folderCreate(function (sResult, oData) { Promises.folderCreate(this.folderName(), sParentFolderName, FolderStore.foldersCreating),
Enums.Notification.CantCreateFolder);
FolderStore.foldersCreating(false);
if (Enums.StorageResultType.Success === sResult && oData && oData.Result)
{
require('App/User').folders();
}
else
{
FolderStore.folderList.error(
oData && oData.ErrorCode ? Translator.getNotification(oData.ErrorCode) : Translator.i18n('NOTIFICATIONS/CANT_CREATE_FOLDER'));
}
}, this.folderName(), sParentFolderName);
this.cancelCommand(); this.cancelCommand();

View file

@ -212,7 +212,11 @@
this.viewThreads.trigger = ko.observable(false); this.viewThreads.trigger = ko.observable(false);
this.viewThreadMessages = ko.observableArray([]); this.viewThreadMessages = ko.observableArray([]);
this.viewThreadMessages.hash = ''; this.viewThreadMessages.error = ko.observable('');
this.viewThreadMessages.subscribe(function () {
this.viewThreadMessages.error('');
}, this);
MessageStore.messageLastThreadUidsData.subscribe(function (oData) { MessageStore.messageLastThreadUidsData.subscribe(function (oData) {
if (oData && oData['Uids']) if (oData && oData['Uids'])
@ -320,11 +324,11 @@
self.viewThreadMessages(aList); self.viewThreadMessages(aList);
}, function (iErrorCode) { }).fail(function (iErrorCode) {
self.viewThreadMessages([]);
window.alert(Translator.getNotification(iErrorCode)); self.viewThreadMessages.error(Translator.getNotification(
iErrorCode, '', Enums.Notification.CantGetMessageList));
}); }).done();
} }
}, function () { }, function () {

View file

@ -2,7 +2,7 @@
"name": "RainLoop", "name": "RainLoop",
"title": "RainLoop Webmail", "title": "RainLoop Webmail",
"version": "1.8.2", "version": "1.8.2",
"release": "288", "release": "289",
"description": "Simple, modern & fast web-based email client", "description": "Simple, modern & fast web-based email client",
"homepage": "http://rainloop.net", "homepage": "http://rainloop.net",
"main": "gulpfile.js", "main": "gulpfile.js",
@ -67,5 +67,9 @@
"gulp-beautify": "*", "gulp-beautify": "*",
"gulp-plumber": "*", "gulp-plumber": "*",
"gulp-concat-util": "*" "gulp-concat-util": "*"
},
"scripts": {
"gulp": "gulp",
"w": "gulp w"
} }
} }

View file

@ -5009,6 +5009,9 @@ class Actions
*/ */
public function DoFolders() public function DoFolders()
{ {
// \sleep(1);
// throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::MailServerError);
$sRawKey = (string) $this->GetActionParam('RawKey', ''); $sRawKey = (string) $this->GetActionParam('RawKey', '');
if (0 < \strlen($sRawKey)) if (0 < \strlen($sRawKey))
{ {
@ -5153,6 +5156,9 @@ class Actions
*/ */
public function DoFolderCreate() public function DoFolderCreate()
{ {
// \sleep(1);
// throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::CantCreateFolder);
$this->initMailClientConnection(); $this->initMailClientConnection();
try try
@ -5232,6 +5238,9 @@ class Actions
*/ */
public function DoFolderDelete() public function DoFolderDelete()
{ {
// \sleep(1);
// throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::CantRenameFolder);
$this->initMailClientConnection(); $this->initMailClientConnection();
$sFolderFullNameRaw = $this->GetActionParam('Folder', ''); $sFolderFullNameRaw = $this->GetActionParam('Folder', '');

View file

@ -62,6 +62,8 @@
<tbody> <tbody>
<tr><td class="i18n" data-i18n-text="SHORTCUTS_HELP/LABEL_FULLSCREEN_TOGGLE">Toggle fullscreen mode</td><td>Enter</td></tr> <tr><td class="i18n" data-i18n-text="SHORTCUTS_HELP/LABEL_FULLSCREEN_TOGGLE">Toggle fullscreen mode</td><td>Enter</td></tr>
<tr><td class="i18n" data-i18n-text="SHORTCUTS_HELP/LABEL_BLOCKQUOTES_TOGGLE">Toggle message blockquotes</td><td>B</td></tr> <tr><td class="i18n" data-i18n-text="SHORTCUTS_HELP/LABEL_BLOCKQUOTES_TOGGLE">Toggle message blockquotes</td><td>B</td></tr>
<tr><td class="i18n" data-i18n-text="SHORTCUTS_HELP/LABEL_THREAD_NEXT">Next message in thread</td><td>Ctrl + &larr;, Command + &larr;</td></tr>
<tr><td class="i18n" data-i18n-text="SHORTCUTS_HELP/LABEL_THREAD_PREV">Previous message in thread</td><td>Ctrl + &rarr;, Command + &rarr;</td></tr>
<tr><td class="i18n" data-i18n-text="SHORTCUTS_HELP/LABEL_PRINT">Print</td><td>Ctrl + P, Command + P</td></tr> <tr><td class="i18n" data-i18n-text="SHORTCUTS_HELP/LABEL_PRINT">Print</td><td>Ctrl + P, Command + P</td></tr>
<tr><td class="i18n" data-i18n-text="SHORTCUTS_HELP/LABEL_EXIT_FULLSCREEN">Exit fullscreen mode</td><td>Esc</td></tr> <tr><td class="i18n" data-i18n-text="SHORTCUTS_HELP/LABEL_EXIT_FULLSCREEN">Exit fullscreen mode</td><td>Esc</td></tr>
<tr><td class="i18n" data-i18n-text="SHORTCUTS_HELP/LABEL_CLOSE_MESSAGE">Close message (No preview pane layout)</td><td>Esc</td></tr> <tr><td class="i18n" data-i18n-text="SHORTCUTS_HELP/LABEL_CLOSE_MESSAGE">Close message (No preview pane layout)</td><td>Esc</td></tr>

View file

@ -1,8 +1,6 @@
<div style="margin: 30px"> <div style="margin: 30px">
<h1 style="display: inline; color: #fff; text-shadow: 1px 2px 0px rgba(0, 0, 0, 0.5);">RainLoop Webmail</h1> <h1 style="display: inline; color: #fff; text-shadow: 1px 2px 0px rgba(0, 0, 0, 0.5);">RainLoop Webmail</h1>
&nbsp; &nbsp;&nbsp;&nbsp;
&nbsp;
&nbsp;
<h3 style="display: inline; color: #fff; text-shadow: 1px 2px 0px rgba(0, 0, 0, 0.5);">(<span data-bind="text: version"></span>)</h3> <h3 style="display: inline; color: #fff; text-shadow: 1px 2px 0px rgba(0, 0, 0, 0.5);">(<span data-bind="text: version"></span>)</h3>
<h3><a href="http://rainloop.net/" target="_blank">http://rainloop.net</a></h3> <h3><a href="http://rainloop.net/" target="_blank">http://rainloop.net</a></h3>
</div> </div>

View file

@ -206,13 +206,16 @@
</a> </a>
<ul class="dropdown-menu pull-right g-ui-menu thread-list" role="menu" aria-labelledby="thread-list-view-dropdown-id" <ul class="dropdown-menu pull-right g-ui-menu thread-list" role="menu" aria-labelledby="thread-list-view-dropdown-id"
style="min-width: 400px; max-width: 400px; width: 400px;"> style="min-width: 400px; max-width: 400px; width: 400px;">
<div data-bind="visible: '' !== viewThreadMessages.error()" style="color: red; text-align: center; padding: 10px">
<spam data-bind="text: viewThreadMessages.error"></spam>
</div >
<div data-bind="visible: messageListOfThreadsLoading" style="text-align: center; padding: 10px"> <div data-bind="visible: messageListOfThreadsLoading" style="text-align: center; padding: 10px">
<i class="icon-spinner animated" /> <i class="icon-spinner animated" />
</div > </div >
<div data-bind="foreach: viewThreadMessages, visible: !messageListOfThreadsLoading()"> <div data-bind="foreach: viewThreadMessages, visible: !messageListOfThreadsLoading()">
<li class="e-item thread-list-message" role="presentation" data-bind="css: {'selected': selected}"> <li class="e-item thread-list-message" role="presentation" data-bind="css: {'selected': selected}">
<a class="e-link menuitem" href="#" tabindex="-1" onclick="return false;"> <a class="e-link menuitem" href="#" tabindex="-1" onclick="return false;">
<span class="thread-date pull-right" data-moment-format="SHORT" data-bind="moment: dateUTC"></span> <span class="thread-date pull-right" data-moment-format="SHORT" data-bind="moment: dateInUTC"></span>
<div style="text-overflow: ellipsis; overflow: hidden;"> <div style="text-overflow: ellipsis; overflow: hidden;">
<span class="thread-from" data-bind="text: sender"></span> <span class="thread-from" data-bind="text: sender"></span>
&nbsp; &nbsp;