diff --git a/.gitignore b/.gitignore
index dac9fcceb..2e09257cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
/.idea
/nbproject
/npm-debug.log
+/rainloop.sublime-project
+/rainloop.sublime-workspace
/rainloop/v/0.0.0/static/css/*.css
/rainloop/v/0.0.0/static/js/*.js
/rainloop/v/0.0.0/static/js/**/*.js
diff --git a/dev/App/User.js b/dev/App/User.js
index 154819268..2509c1d8d 100644
--- a/dev/App/User.js
+++ b/dev/App/User.js
@@ -75,6 +75,20 @@
Events.pub('interval.10m');
}, 60000 * 10);
+ window.setInterval(function () {
+ Events.pub('interval.15m');
+ }, 60000 * 15);
+
+ window.setInterval(function () {
+ Events.pub('interval.20m');
+ }, 60000 * 15);
+
+ window.setTimeout(function () {
+ window.setInterval(function () {
+ Events.pub('interval.5m-after5m');
+ }, 60000 * 5);
+ }, 60000 * 5);
+
window.setTimeout(function () {
window.setInterval(function () {
Events.pub('interval.10m-after5m');
@@ -476,8 +490,32 @@
}
};
- AppUser.prototype.accountsAndIdentities = function ()
+ AppUser.prototype.accountsCounts = function ()
{
+ Remote.accountsCounts(function (sResult, oData) {
+ if (Enums.StorageResultType.Success === sResult && oData.Result && oData.Result['Counts'])
+ {
+ var aAcounts = Data.accounts();
+
+ _.each(oData.Result['Counts'], function (oItem) {
+
+ var oAccount = _.find(aAcounts, function (oAccount) {
+ return oAccount && oItem[0] === oAccount.email;
+ });
+
+ if (oAccount)
+ {
+ oAccount.count(Utils.pInt(oItem[1]));
+ }
+ });
+ }
+ });
+ };
+
+ AppUser.prototype.accountsAndIdentities = function (bBoot)
+ {
+ var self = this;
+
Data.accountsLoading(true);
Data.identitiesLoading(true);
@@ -489,6 +527,7 @@
if (Enums.StorageResultType.Success === sResult && oData.Result)
{
var
+ aCounts = {},
sParentEmail = Settings.settingsGet('ParentEmail'),
sAccountEmail = Data.accountEmail()
;
@@ -497,12 +536,22 @@
if (Utils.isArray(oData.Result['Accounts']))
{
+ _.each(Data.accounts(), function (oAccount) {
+ aCounts[oAccount.email] = oAccount.count();
+ });
+
Utils.delegateRunOnDestroy(Data.accounts());
+
Data.accounts(_.map(oData.Result['Accounts'], function (sValue) {
- return new AccountModel(sValue, sValue !== sParentEmail);
+ return new AccountModel(sValue, sValue !== sParentEmail, aCounts[sValue] || 0);
}));
}
+ if (Utils.isUnd(bBoot) ? false : !!bBoot)
+ {
+ self.accountsCounts();
+ }
+
if (Utils.isArray(oData.Result['Identities']))
{
Utils.delegateRunOnDestroy(Data.identities());
@@ -723,8 +772,9 @@
});
if (bBoot)
- {
- self.folderInformationMultiply(true);
+ { _.delay(function () {
+ self.folderInformationMultiply(true);
+ }, 2000);
}
}
}
@@ -1342,7 +1392,7 @@
self.folderInformation(Cache.getFolderInboxName());
});
- Events.sub('interval.2m', function () {
+ Events.sub('interval.3m', function () {
var sF = Data.currentFolderFullNameRaw();
if (Cache.getFolderInboxName() !== sF)
{
@@ -1350,15 +1400,15 @@
}
});
- Events.sub('interval.3m', function () {
+ Events.sub('interval.5m-after5m', function () {
self.folderInformationMultiply();
});
- Events.sub('interval.5m', function () {
+ Events.sub('interval.15m', function () {
self.quota();
});
- Events.sub('interval.10m', function () {
+ Events.sub('interval.20m', function () {
self.folders();
});
@@ -1379,7 +1429,7 @@
if (Settings.capa(Enums.Capa.AdditionalAccounts) || Settings.capa(Enums.Capa.AdditionalIdentities))
{
- self.accountsAndIdentities();
+ self.accountsAndIdentities(true);
}
_.delay(function () {
diff --git a/dev/Common/Utils.js b/dev/Common/Utils.js
index fbc764f5e..178d8d8f4 100644
--- a/dev/Common/Utils.js
+++ b/dev/Common/Utils.js
@@ -1253,9 +1253,11 @@
return sResult;
};
- Utils.draggeblePlace = function ()
+ Utils.draggablePlace = function ()
{
- return $('
').appendTo('#rl-hidden');
+ return $('' +
+ ' ' +
+ '
').appendTo('#rl-hidden');
};
Utils.defautOptionsAfterRender = function (oDomOption, oItem)
diff --git a/dev/External/ko.js b/dev/External/ko.js
index 58b9570c8..8d3514f49 100644
--- a/dev/External/ko.js
+++ b/dev/External/ko.js
@@ -793,6 +793,30 @@
return oTarget;
};
+ ko.extenders.toggleSubscribeProperty = function (oTarget, oOptions)
+ {
+ var sProp = oOptions[1];
+
+ if (sProp)
+ {
+ oTarget.subscribe(function (oPrev) {
+ if (oPrev && oPrev[sProp])
+ {
+ oPrev[sProp](false);
+ }
+ }, oOptions[0], 'beforeChange');
+
+ oTarget.subscribe(function (oNext) {
+ if (oNext && oNext[sProp])
+ {
+ oNext[sProp](true);
+ }
+ }, oOptions[0]);
+ }
+
+ return oTarget;
+ };
+
ko.extenders.falseTimeout = function (oTarget, iOption)
{
var Utils = require('Common/Utils');
diff --git a/dev/Model/Account.js b/dev/Model/Account.js
index 51bb44643..ea8c11270 100644
--- a/dev/Model/Account.js
+++ b/dev/Model/Account.js
@@ -17,13 +17,16 @@
*
* @param {string} sEmail
* @param {boolean=} bCanBeDelete = true
+ * @param {number=} iCount = 0
*/
- function AccountModel(sEmail, bCanBeDelete)
+ function AccountModel(sEmail, bCanBeDelete, iCount)
{
AbstractModel.call(this, 'AccountModel');
this.email = sEmail;
+ this.count = ko.observable(iCount || 0);
+
this.deleteAccess = ko.observable(false);
this.canBeDalete = ko.observable(Utils.isUnd(bCanBeDelete) ? true : !!bCanBeDelete);
this.canBeEdit = this.canBeDalete;
diff --git a/dev/Model/Filter.js b/dev/Model/Filter.js
index c1901a09e..4845ce01b 100644
--- a/dev/Model/Filter.js
+++ b/dev/Model/Filter.js
@@ -21,42 +21,20 @@
{
AbstractModel.call(this, 'FilterModel');
- this.isNew = ko.observable(true);
this.enabled = ko.observable(true);
this.name = ko.observable('');
-
- this.conditionsType = ko.observable(Enums.FilterRulesType.All);
+ this.name.error = ko.observable(false);
+ this.name.focused = ko.observable(false);
this.conditions = ko.observableArray([]);
+ this.conditionsType = ko.observable(Enums.FilterRulesType.All);
// Actions
- this.actionMarkAsRead = ko.observable(false);
- this.actionSkipOtherFilters = ko.observable(true);
this.actionValue = ko.observable('');
+ this.actionMarkAsRead = ko.observable(false);
this.actionType = ko.observable(Enums.FiltersAction.Move);
- this.actionTypeOptions = [ // TODO i18n
- {'id': Enums.FiltersAction.None, 'name': 'None'},
- {'id': Enums.FiltersAction.Move, 'name': ' Move to'},
- // {'id': Enums.FiltersAction.Forward, 'name': 'Forward to'},
- {'id': Enums.FiltersAction.Discard, 'name': 'Discard'}
- ];
-
- this.enableSkipOtherFilters = ko.computed(function () {
- return -1 === Utils.inArray(this.actionType(), [
- Enums.FiltersAction.Move, Enums.FiltersAction.Forward, Enums.FiltersAction.Discard
- ]);
- }, this);
-
- this.actionSkipOtherFiltersResult = ko.computed({
- 'read': function () {
- return this.actionSkipOtherFilters() ||
- !this.enableSkipOtherFilters();
- },
- 'write': this.actionSkipOtherFilters,
- 'owner': this
- });
this.actionTemplate = ko.computed(function () {
@@ -84,14 +62,42 @@
Utils.windowResize();
}));
- this.regDisposables([this.enableSkipOtherFilters, this.actionSkipOtherFiltersResult, this.actionTemplate]);
+ this.regDisposables(this.name.subscribe(function (sValue) {
+ this.name.error('' === sValue);
+ }, this));
+
+ this.regDisposables([this.actionTemplate]);
+
+ this.deleteAccess = ko.observable(false);
+ this.canBeDalete = ko.observable(true);
}
_.extend(FilterModel.prototype, AbstractModel.prototype);
+ FilterModel.prototype.toJson = function ()
+ {
+ return {
+ 'Enabled': this.enabled(),
+ 'Name': this.name(),
+ 'ConditionsType': this.conditionsType(),
+ 'Conditions': _.map(this.conditions(), function (oItem) {
+ return oItem.toJson();
+ }),
+ 'ActionMarkAsRead': this.actionMarkAsRead(),
+ 'ActionValue': this.actionValue(),
+ 'ActionType': this.actionType()
+ };
+ };
+
FilterModel.prototype.addCondition = function ()
{
- this.conditions.push(new FilterConditionModel(this.conditions));
+ this.conditions.push(new FilterConditionModel());
+ };
+
+ FilterModel.prototype.removeCondition = function (oConditionToDelete)
+ {
+ this.conditions.remove(oConditionToDelete);
+ Utils.delegateRunOnDestroy(oConditionToDelete);
};
FilterModel.prototype.parse = function (oItem)
@@ -107,6 +113,29 @@
return bResult;
};
+ FilterModel.prototype.cloneSelf = function ()
+ {
+ var oClone = new FilterModel();
+
+ oClone.enabled(this.enabled());
+
+ oClone.name(this.name());
+ oClone.name.error(this.name.error());
+
+ oClone.conditionsType(this.conditionsType());
+
+ oClone.actionMarkAsRead(this.actionMarkAsRead());
+ oClone.actionValue(this.actionValue());
+
+ oClone.actionType(this.actionType());
+
+ oClone.conditions(_.map(this.conditions(), function (oCondition) {
+ return oCondition.cloneSelf();
+ }));
+
+ return oClone;
+ };
+
module.exports = FilterModel;
}());
\ No newline at end of file
diff --git a/dev/Model/FilterCondition.js b/dev/Model/FilterCondition.js
index 821aeabee..4f6660d5c 100644
--- a/dev/Model/FilterCondition.js
+++ b/dev/Model/FilterCondition.js
@@ -16,29 +16,12 @@
* @param {*} oKoList
* @constructor
*/
- function FilterConditionModel(oKoList)
+ function FilterConditionModel()
{
AbstractModel.call(this, 'FilterConditionModel');
- this.parentList = oKoList;
-
this.field = ko.observable(Enums.FilterConditionField.From);
-
- this.fieldOptions = [ // TODO i18n
- {'id': Enums.FilterConditionField.From, 'name': 'From'},
- {'id': Enums.FilterConditionField.Recipient, 'name': 'Recipient (To or CC)'},
- {'id': Enums.FilterConditionField.Subject, 'name': 'Subject'}
- ];
-
this.type = ko.observable(Enums.FilterConditionType.EqualTo);
-
- this.typeOptions = [ // TODO i18n
- {'id': Enums.FilterConditionType.EqualTo, 'name': 'Equal To'},
- {'id': Enums.FilterConditionType.NotEqualTo, 'name': 'Not Equal To'},
- {'id': Enums.FilterConditionType.Contains, 'name': 'Contains'},
- {'id': Enums.FilterConditionType.NotContains, 'name': 'Not Contains'}
- ];
-
this.value = ko.observable('');
this.template = ko.computed(function () {
@@ -60,9 +43,24 @@
_.extend(FilterConditionModel.prototype, AbstractModel.prototype);
- FilterConditionModel.prototype.removeSelf = function ()
+ FilterConditionModel.prototype.toJson = function ()
{
- this.parentList.remove(this);
+ return {
+ 'Field': this.field(),
+ 'Type': this.type(),
+ 'Value': this.value()
+ };
+ };
+
+ FilterConditionModel.prototype.cloneSelf = function ()
+ {
+ var oClone = new FilterConditionModel();
+
+ oClone.field(this.field());
+ oClone.type(this.type());
+ oClone.value(this.value());
+
+ return oClone;
};
module.exports = FilterConditionModel;
diff --git a/dev/Settings/User/Filters.js b/dev/Settings/User/Filters.js
index ea677c5e8..0e1ceb99c 100644
--- a/dev/Settings/User/Filters.js
+++ b/dev/Settings/User/Filters.js
@@ -6,7 +6,10 @@
var
ko = require('ko'),
- Utils = require('Common/Utils')
+ Enums = require('Common/Enums'),
+ Utils = require('Common/Utils'),
+
+ Remote = require('Storage/User/Remote')
;
/**
@@ -14,14 +17,80 @@
*/
function FiltersUserSettings()
{
+ var self = this;
+
+ this.haveChanges = ko.observable(false);
+
+ this.processText = ko.observable('');
+ this.visibility = ko.observable(false);
+
this.filters = ko.observableArray([]);
- this.filters.loading = ko.observable(false);
+ this.filters.loading = ko.observable(false).extend({'throttle': 200});
+ this.filters.saving = ko.observable(false).extend({'throttle': 200});
this.filters.subscribe(function () {
Utils.windowResize();
});
+
+ this.processText = ko.computed(function () {
+ return this.filters.loading() ? Utils.i18n('SETTINGS_FILTERS/LOADING_PROCESS') : '';
+ }, this);
+
+ this.visibility = ko.computed(function () {
+ return '' === this.processText() ? 'hidden' : 'visible';
+ }, this);
+
+ this.filterForDeletion = ko.observable(null).extend({'falseTimeout': 3000}).extend(
+ {'toggleSubscribeProperty': [this, 'deleteAccess']});
+
+ this.saveChanges = Utils.createCommand(this, function () {
+
+ if (!this.filters.saving())
+ {
+ this.filters.saving(true);
+
+ Remote.filtersSave(function (sResult, oData) {
+
+ self.filters.saving(false);
+
+ if (Enums.StorageResultType.Success === sResult && oData && oData.Result)
+ {
+
+ self.haveChanges(false);
+ self.updateList();
+ }
+
+ }, this.filters());
+ }
+
+ return true;
+
+ }, function () {
+ return this.haveChanges();
+ });
+
+ this.filters.subscribe(function () {
+ this.haveChanges(true);
+ }, this);
}
+ FiltersUserSettings.prototype.scrollableOptions = function ()
+ {
+ return {
+ // handle: '.drag-handle'
+ };
+ };
+
+ FiltersUserSettings.prototype.updateList = function ()
+ {
+ var self = this;
+
+ this.filters.loading(true);
+ // Remote.filtersGet(function (sResult, oData) {
+ self.filters.loading(false);
+ // });
+ };
+
FiltersUserSettings.prototype.deleteFilter = function (oFilter)
{
this.filters.remove(oFilter);
@@ -31,11 +100,60 @@
FiltersUserSettings.prototype.addFilter = function ()
{
var
- FilterModel = require('Model/Filter')
+ self = this,
+ FilterModel = require('Model/Filter'),
+ oNew = new FilterModel()
;
require('Knoin/Knoin').showScreenPopup(
- require('View/Popup/Filter'), [new FilterModel()]);
+ require('View/Popup/Filter'), [oNew, function () {
+ self.filters.push(oNew);
+ self.haveChanges(true);
+ }, false]);
+ };
+
+ FiltersUserSettings.prototype.editFilter = function (oEdit)
+ {
+ var
+ self = this,
+ oCloned = oEdit.cloneSelf()
+ ;
+
+ require('Knoin/Knoin').showScreenPopup(
+ require('View/Popup/Filter'), [oCloned, function () {
+
+ var
+ oFilters = self.filters(),
+ iIndex = oFilters.indexOf(oEdit)
+ ;
+
+ if (-1 < iIndex && oFilters[iIndex])
+ {
+ Utils.delegateRunOnDestroy(oFilters[iIndex]);
+ oFilters[iIndex] = oCloned;
+
+ self.filters(oFilters);
+ self.haveChanges(true);
+ }
+
+ }, false]);
+ };
+
+ FiltersUserSettings.prototype.onBuild = function (oDom)
+ {
+ var self = this;
+
+ oDom
+ .on('click', '.filter-item .e-action', function () {
+ var oFilterItem = ko.dataFor(this);
+ if (oFilterItem)
+ {
+ self.editFilter(oFilterItem);
+ }
+ })
+ ;
+
+ this.updateList();
};
module.exports = FiltersUserSettings;
diff --git a/dev/Settings/User/Identities.js b/dev/Settings/User/Identities.js
index eba18b614..2022b1bee 100644
--- a/dev/Settings/User/Identities.js
+++ b/dev/Settings/User/Identities.js
@@ -129,15 +129,11 @@
{
this.identityForDeletion(null);
- var
- fRemoveFolder = function (oIdentity) {
- return oIdentityToRemove === oIdentity;
- }
- ;
-
if (oIdentityToRemove)
{
- this.identities.remove(fRemoveFolder);
+ this.identities.remove(function (oIdentity) {
+ return oIdentityToRemove === oIdentity;
+ });
Remote.identityDelete(function () {
require('App/User').accountsAndIdentities();
diff --git a/dev/Storage/User/Data.js b/dev/Storage/User/Data.js
index 57a140d41..e9bcaa729 100644
--- a/dev/Storage/User/Data.js
+++ b/dev/Storage/User/Data.js
@@ -631,8 +631,8 @@
iUtc = moment().unix(),
iTimeout = iUtc - 60 * 5,
aTimeouts = [],
+ sInboxFolderName = Cache.getFolderInboxName(),
fSearchFunction = function (aList) {
- var sInboxFolderName = Cache.getFolderInboxName();
_.each(aList, function (oFolder) {
if (oFolder && sInboxFolderName !== oFolder.fullNameRaw &&
oFolder.selectable && oFolder.existen &&
@@ -653,12 +653,9 @@
fSearchFunction(this.folderList());
aTimeouts.sort(function(a, b) {
- if (a[0] < b[0])
- {
+ if (a[0] < b[0]) {
return -1;
- }
- else if (a[0] > b[0])
- {
+ } else if (a[0] > b[0]) {
return 1;
}
diff --git a/dev/Storage/User/Remote.js b/dev/Storage/User/Remote.js
index c7373a0ae..9ab486695 100644
--- a/dev/Storage/User/Remote.js
+++ b/dev/Storage/User/Remote.js
@@ -221,6 +221,35 @@
this.defaultRequest(fCallback, 'AccountsAndIdentities');
};
+ /**
+ * @param {?Function} fCallback
+ */
+ RemoteUserStorage.prototype.accountsCounts = function (fCallback)
+ {
+ this.defaultRequest(fCallback, 'AccountsCounts');
+ };
+
+ /**
+ * @param {?Function} fCallback
+ */
+ RemoteUserStorage.prototype.filtersSave = function (fCallback, aFilters)
+ {
+ this.defaultRequest(fCallback, 'FiltersSave', {
+ 'Filters': _.map(aFilters, function (oItem) {
+ return oItem.toJson();
+ })
+ });
+ };
+
+ /**
+ * @param {?Function} fCallback
+ */
+ RemoteUserStorage.prototype.filtersGet = function (fCallback)
+ {
+ this.defaultRequest(fCallback, 'Filters', {
+ });
+ };
+
/**
* @param {?Function} fCallback
* @param {string} sFolderFullNameRaw
diff --git a/dev/Styles/SettingsFilters.less b/dev/Styles/SettingsFilters.less
index a1c07b5a4..9bcf8e558 100644
--- a/dev/Styles/SettingsFilters.less
+++ b/dev/Styles/SettingsFilters.less
@@ -55,5 +55,11 @@
cursor: pointer;
opacity: 0.5;
}
+
+ &.ui-sortable-helper {
+ .delete-filter {
+ display: none;
+ }
+ }
}
}
diff --git a/dev/Styles/SystemDropDown.less b/dev/Styles/SystemDropDown.less
index 404be4649..8aa437661 100644
--- a/dev/Styles/SystemDropDown.less
+++ b/dev/Styles/SystemDropDown.less
@@ -1,34 +1,60 @@
-
-.b-system-drop-down {
-
- .b-toolbar {
- position: absolute;
- top: 0;
- right: 0;
- height: 30px;
- padding: 10px @rlLowMargin;
- z-index: 103;
- }
-
- .e-facebook-name {
- display: inline-block;
- padding-top: 4px;
- }
-
- .btn.system-dropdown {
- padding-left: 10px;
- padding-right: 10px;
- }
-
- .button-fb-logout {
- margin: 5px;
- }
-
- .email-title {
- display: inline-block;
- max-width: 200px;
- text-align: left;
- text-overflow: ellipsis;
- overflow: hidden;
- }
+
+.b-system-drop-down {
+
+ .b-toolbar {
+ position: absolute;
+ top: 0;
+ right: 0;
+ height: 30px;
+ padding: 10px @rlLowMargin;
+ z-index: 103;
+ }
+
+ .e-facebook-name {
+ display: inline-block;
+ padding-top: 4px;
+ }
+
+ .btn.system-dropdown {
+ padding-left: 10px;
+ padding-right: 10px;
+ }
+
+ .button-fb-logout {
+ margin: 5px;
+ }
+
+ .email-title {
+ display: inline-block;
+ max-width: 200px;
+ text-align: left;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ margin-right: 28px;
+ vertical-align: middle;
+ }
+
+ .account-item {
+
+ .icon-ok {
+ display: none;
+ }
+
+ &.current {
+ .icon-ok {
+ display: inline-block;
+ }
+ .icon-user {
+ display: none;
+ }
+ }
+ }
+
+ .counter {
+ display: inline-block;
+ }
+
+ .g-ui-menu .e-link.account-item {
+ padding-right: 5px;
+ }
}
\ No newline at end of file
diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js
index e0ef5a387..ed03d56c7 100644
--- a/dev/View/Popup/Compose.js
+++ b/dev/View/Popup/Compose.js
@@ -1180,14 +1180,13 @@
if (window.google && window.google.picker)
{
var drivePicker = new window.google.picker.PickerBuilder()
- .addView(
- new window.google.picker.DocsView()
- .setIncludeFolders(true)
- )
+ // .addView(window.google.picker.ViewId.FOLDERS)
+ .addView(window.google.picker.ViewId.DOCS)
.setAppId(Settings.settingsGet('GoogleClientID'))
.setOAuthToken(oOauthToken.access_token)
.setCallback(_.bind(self.driveCallback, self, oOauthToken.access_token))
.enableFeature(window.google.picker.Feature.NAV_HIDDEN)
+ // .setOrigin(window.location.protocol + '//' + window.location.host)
.build()
;
@@ -1205,14 +1204,9 @@
window.gapi.load('auth', {'callback': function () {
- var oAuthToken = window.gapi.auth.getToken();
- if (!oAuthToken)
- {
- window.gapi.auth.authorize({
- 'client_id': Settings.settingsGet('GoogleClientID'),
- 'scope': 'https://www.googleapis.com/auth/drive.readonly',
- 'immediate': true
- }, function (oAuthResult) {
+ var
+ oAuthToken = window.gapi.auth.getToken(),
+ fResult = function (oAuthResult) {
if (oAuthResult && !oAuthResult.error)
{
var oAuthToken = window.gapi.auth.getToken();
@@ -1220,23 +1214,29 @@
{
self.driveCreatePiker(oAuthToken);
}
+
+ return true;
}
- else
+
+ return false;
+ }
+ ;
+
+ if (!oAuthToken)
+ {
+ window.gapi.auth.authorize({
+ 'client_id': Settings.settingsGet('GoogleClientID'),
+ 'scope': 'https://www.googleapis.com/auth/drive.readonly',
+ 'immediate': true
+ }, function (oAuthResult) {
+
+ if (!fResult(oAuthResult))
{
window.gapi.auth.authorize({
'client_id': Settings.settingsGet('GoogleClientID'),
'scope': 'https://www.googleapis.com/auth/drive.readonly',
'immediate': false
- }, function (oAuthResult) {
- if (oAuthResult && !oAuthResult.error)
- {
- var oAuthToken = window.gapi.auth.getToken();
- if (oAuthToken)
- {
- self.driveCreatePiker(oAuthToken);
- }
- }
- });
+ }, fResult);
}
});
}
diff --git a/dev/View/Popup/Filter.js b/dev/View/Popup/Filter.js
index ede447e58..1f4570261 100644
--- a/dev/View/Popup/Filter.js
+++ b/dev/View/Popup/Filter.js
@@ -8,6 +8,7 @@
ko = require('ko'),
Consts = require('Common/Consts'),
+ Enums = require('Common/Enums'),
Utils = require('Common/Utils'),
Data = require('Storage/User/Data'),
@@ -24,28 +25,102 @@
{
AbstractView.call(this, 'Popups', 'PopupsFilter');
+ this.isNew = ko.observable(true);
+
+ this.fTrueCallback = null;
this.filter = ko.observable(null);
this.selectedFolderValue = ko.observable(Consts.Values.UnuseOptionValue);
this.folderSelectList = Data.folderMenuForMove;
this.defautOptionsAfterRender = Utils.defautOptionsAfterRender;
+ this.saveFilter = Utils.createCommand(this, function () {
+
+ if (this.filter())
+ {
+ if ('' === this.filter().name())
+ {
+ this.filter().name.error(true);
+ return false;
+ }
+
+ if (this.fTrueCallback)
+ {
+ this.fTrueCallback(this.filter());
+ }
+
+ if (this.modalVisibility())
+ {
+ Utils.delegateRun(this, 'closeCommand');
+ }
+ }
+
+ return true;
+ });
+
+ this.actionTypeOptions = [
+ {'id': Enums.FiltersAction.None, 'name': 'None @i18n'},
+ {'id': Enums.FiltersAction.Move, 'name': ' Move to @i18n'},
+// {'id': Enums.FiltersAction.Forward, 'name': 'Forward to @i18n'},
+ {'id': Enums.FiltersAction.Discard, 'name': 'Discard @i18n'}
+ ];
+
+ this.fieldOptions = [
+ {'id': Enums.FilterConditionField.From, 'name': 'From @i18n'},
+ {'id': Enums.FilterConditionField.Recipient, 'name': 'Recipient (To or CC) @i18n'},
+ {'id': Enums.FilterConditionField.Subject, 'name': 'Subject @i18n'}
+ ];
+
+ this.typeOptions = [
+ {'id': Enums.FilterConditionType.EqualTo, 'name': 'Equal To @i18n'},
+ {'id': Enums.FilterConditionType.NotEqualTo, 'name': 'Not Equal To @i18n'},
+ {'id': Enums.FilterConditionType.Contains, 'name': 'Contains @i18n'},
+ {'id': Enums.FilterConditionType.NotContains, 'name': 'Not Contains @i18n'}
+ ];
+
kn.constructorEnd(this);
}
kn.extendAsViewModel(['View/Popup/Filter', 'PopupsFilterViewModel'], FilterPopupView);
_.extend(FilterPopupView.prototype, AbstractView.prototype);
- FilterPopupView.prototype.clearPopup = function ()
+ FilterPopupView.prototype.removeCondition = function (oConditionToDelete)
{
- // TODO
+ if (this.filter())
+ {
+ this.filter().removeCondition(oConditionToDelete);
+ }
};
- FilterPopupView.prototype.onShow = function (oFilter)
+ FilterPopupView.prototype.clearPopup = function ()
+ {
+ this.isNew(true);
+
+ this.fTrueCallback = null;
+ this.filter(null);
+ };
+
+ FilterPopupView.prototype.onShow = function (oFilter, fTrueCallback, bEdit)
{
this.clearPopup();
+ this.fTrueCallback = fTrueCallback;
this.filter(oFilter);
+
+ this.isNew(!bEdit);
+
+ if (!bEdit && oFilter)
+ {
+ oFilter.name.focused(true);
+ }
+ };
+
+ FilterPopupView.prototype.onFocus = function ()
+ {
+ if (this.isNew() && this.filter())
+ {
+ this.filter().name.focused(true);
+ }
};
module.exports = FilterPopupView;
diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js
index fb92be532..ebea101b0 100644
--- a/dev/View/User/MailBox/MessageList.js
+++ b/dev/View/User/MailBox/MessageList.js
@@ -319,7 +319,7 @@
}
var
- oEl = Utils.draggeblePlace(),
+ oEl = Utils.draggablePlace(),
aUids = Data.messageListCheckedOrSelectedUidsWithSubMails()
;
diff --git a/gulpfile.js b/gulpfile.js
index c6a8e3d70..788788133 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -144,6 +144,7 @@ cfg.paths.js = {
'vendors/underscore/1.6.0/underscore-min.js',
'vendors/jquery/jquery-1.11.1.min.js',
'vendors/jquery-ui/js/jquery-ui-1.10.3.custom.min.js',
+// 'vendors/jquery-ui.touch-punch/jquery.ui.touch-punch.min.js',
'vendors/jquery-cookie/jquery.cookie-1.4.0.min.js',
'vendors/jquery-finger/jquery.finger.min.js',
'vendors/jquery-mousewheel/jquery.mousewheel-3.1.4.min.js',
@@ -161,6 +162,7 @@ cfg.paths.js = {
'vendors/routes/crossroads.min.js',
'vendors/knockout/knockout-3.2.0.js',
'vendors/knockout-projections/knockout-projections-1.0.0.min.js',
+ 'vendors/knockout-sortable/knockout-sortable.min.js',
'vendors/ssm/ssm.min.js',
'vendors/jua/jua.min.js',
'vendors/Autolinker/Autolinker.min.js',
@@ -169,7 +171,6 @@ cfg.paths.js = {
'vendors/jsencrypt/jsencrypt.min.js',
'vendors/keymaster/keymaster.min.js',
'vendors/ifvisible/ifvisible.min.js',
-// 'vendors/jquery-magnific-popup/jquery.magnific-popup.min.js',
'vendors/bootstrap/js/bootstrap.min.js'
]
},
diff --git a/package.json b/package.json
index 6e52b4a3a..f9d61a4f0 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "RainLoop",
"title": "RainLoop Webmail",
"version": "1.7.0",
- "release": "210",
+ "release": "211",
"description": "Simple, modern & fast web-based email client",
"homepage": "http://rainloop.net",
"main": "gulpfile.js",
diff --git a/rainloop/v/0.0.0/app/src/RainLoop/Actions.php b/rainloop/v/0.0.0/app/src/RainLoop/Actions.php
index 42d42470d..1ccab3ea0 100644
--- a/rainloop/v/0.0.0/app/src/RainLoop/Actions.php
+++ b/rainloop/v/0.0.0/app/src/RainLoop/Actions.php
@@ -2031,6 +2031,28 @@ class Actions
return false;
}
+ /**
+ * @return array
+ *
+ * @throws \MailSo\Base\Exceptions\Exception
+ */
+ public function DoFilters()
+ {
+ return $this->TrueResponse(__FUNCTION__);
+ }
+
+
+ /**
+ * @return array
+ *
+ * @throws \MailSo\Base\Exceptions\Exception
+ */
+ public function DoFiltersSave()
+ {
+ sleep(2);
+ return $this->TrueResponse(__FUNCTION__);
+ }
+
/**
* @return array
*
@@ -2246,6 +2268,35 @@ class Actions
));
}
+ /**
+ * @return array
+ *
+ * @throws \MailSo\Base\Exceptions\Exception
+ */
+ public function DoAccountsCounts()
+ {
+ $oAccount = $this->getAccountFromToken();
+
+ $aCounts = array();
+ if ($this->Config()->Get('webmail', 'allow_additional_accounts', true))
+ {
+ $mAccounts = $this->GetAccounts($oAccount);
+ foreach ($mAccounts as $sEmail => $sHash)
+ {
+ $aCounts[] = array(\MailSo\Base\Utils::IdnToUtf8($sEmail), 0);
+ }
+ }
+ else
+ {
+ $aCounts[] = array(\MailSo\Base\Utils::IdnToUtf8($oAccount->Email()), 0);
+ }
+
+ return $this->DefaultResponse(__FUNCTION__, array(
+ 'Complete' => true,
+ 'Counts' => $aCounts
+ ));
+ }
+
/**
* @return array
*
diff --git a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsLicensing.html b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsLicensing.html
index ff6f28910..8c7b89320 100644
--- a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsLicensing.html
+++ b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsLicensing.html
@@ -65,8 +65,8 @@
- Lifetime (Premium)
- Premium
+ Premium
+ (Lifetime)
Subscription expires:
diff --git a/rainloop/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html b/rainloop/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html
index f6e549ca0..55eb77abd 100644
--- a/rainloop/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html
+++ b/rainloop/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html
@@ -21,7 +21,7 @@
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/PopupsFilter.html b/rainloop/v/0.0.0/app/templates/Views/User/PopupsFilter.html
index f7316b194..7a261e52b 100644
--- a/rainloop/v/0.0.0/app/templates/Views/User/PopupsFilter.html
+++ b/rainloop/v/0.0.0/app/templates/Views/User/PopupsFilter.html
@@ -4,29 +4,34 @@
-
-
-
-
+
+
+
- Conditions
+ Conditions @i18n
- Matching all of the following rules
+ Matching all of the following rules @i18n
- Matching any of the following rules
+ Matching any of the following rules @i18n
@@ -34,7 +39,7 @@
- All incoming messages
+ All incoming messages @i18n
@@ -49,32 +54,23 @@
Actions
-
-
-
-
+
+
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFilters.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFilters.html
index e23d49d31..53d2846d5 100644
--- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFilters.html
+++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFilters.html
@@ -5,15 +5,57 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
\ No newline at end of file
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionNoValue.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionNoValue.html
index c70956749..7ca0fc7c3 100644
--- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionNoValue.html
+++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionNoValue.html
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionValueAsFolders.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionValueAsFolders.html
index 5f6aa0c29..e4addf7af 100644
--- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionValueAsFolders.html
+++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionValueAsFolders.html
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionWithValue.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionWithValue.html
index 48f742fd0..25c6e505f 100644
--- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionWithValue.html
+++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersActionWithValue.html
@@ -1,3 +1,3 @@
-
+
\ No newline at end of file
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersConditionDefault.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersConditionDefault.html
index 1a203ca2d..61f6978de 100644
--- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersConditionDefault.html
+++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsFiltersConditionDefault.html
@@ -1,9 +1,9 @@
-
+
-
+
-
+
\ No newline at end of file
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SystemDropDown.html b/rainloop/v/0.0.0/app/templates/Views/User/SystemDropDown.html
index 7338db65d..c7bc2aca3 100644
--- a/rainloop/v/0.0.0/app/templates/Views/User/SystemDropDown.html
+++ b/rainloop/v/0.0.0/app/templates/Views/User/SystemDropDown.html
@@ -11,19 +11,26 @@