mirror of
https://github.com/the-djmaze/snappymail.git
synced 2024-09-20 07:35:55 +08:00
Split Sieve/Filters code from app.js so that i can work on the new Sieve GUI
This commit is contained in:
parent
ec6a79d9d6
commit
d6dc4d291c
16
README.md
16
README.md
|
@ -139,23 +139,25 @@ RainLoop 1.15 vs SnappyMail
|
|||
|
||||
|js/* |RainLoop |Snappy |
|
||||
|--------------- |--------: |--------: |
|
||||
|admin.js |2.158.025 | 79.040 |
|
||||
|app.js |4.215.733 | 431.641 |
|
||||
|admin.js |2.158.025 | 79.018 |
|
||||
|app.js |4.215.733 | 407.680 |
|
||||
|boot.js | 672.433 | 1.996 |
|
||||
|libs.js | 647.679 | 200.131 |
|
||||
|sieve.js | 0 | 30.809 |
|
||||
|polyfills.js | 325.908 | 0 |
|
||||
|serviceworker.js | 0 | 285 |
|
||||
|TOTAL |8.019.778 | 713.093 |
|
||||
|TOTAL |8.019.778 | 719.919 |
|
||||
|
||||
|js/min/* |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli |
|
||||
|--------------- |--------: |--------: |------: |------: |--------: |--------: |
|
||||
|admin.min.js | 255.514 | 39.272 | 73.899 | 13.076 | 60.674 | 11.727 |
|
||||
|app.min.js | 516.000 | 207.038 |140.430 | 66.265 |110.657 | 56.788 |
|
||||
|admin.min.js | 255.514 | 39.256 | 73.899 | 13.076 | 60.674 | 11.702 |
|
||||
|app.min.js | 516.000 | 194.277 |140.430 | 62.348 |110.657 | 53.485 |
|
||||
|boot.min.js | 66.456 | 1.230 | 22.553 | 768 | 20.043 | 619 |
|
||||
|libs.min.js | 574.626 | 96.201 |177.280 | 35.522 |151.855 | 31.746 |
|
||||
|sieve.min.js | 0 | 15.009 | 0 | 5.228 | 0 | 4.702 |
|
||||
|polyfills.min.js | 32.608 | 0 | 11.315 | 0 | 10.072 | 0 |
|
||||
|TOTAL |1.445.204 | 343.741 |425.477 |115.631 |353.301 |100.880 |
|
||||
|TOTAL (no admin) |1.189.690 | 304.469 |351.061 |102.555 |292.627 | 89.153 |
|
||||
|TOTAL |1.445.204 | 345.973 |425.477 |116.942 |353.301 |102.254 |
|
||||
|TOTAL (no admin) |1.189.690 | 306.717 |351.061 |103.866 |292.627 | 90.552 |
|
||||
|
||||
For a user its around 69% smaller and faster than traditional RainLoop.
|
||||
|
||||
|
|
|
@ -67,8 +67,8 @@ fetchFolderInformation = (fCallback, folder, list = []) => {
|
|||
/**
|
||||
* @param {Array=} aDisabled
|
||||
* @param {Array=} aHeaderLines
|
||||
* @param {Function=} fDisableCallback
|
||||
* @param {Function=} fRenameCallback
|
||||
* @param {Function=} fDisableCallback
|
||||
* @param {boolean=} bNoSelectSelectable Used in FolderCreatePopupView
|
||||
* @returns {Array}
|
||||
*/
|
||||
|
|
|
@ -29,7 +29,7 @@ class AbstractView {
|
|||
|
||||
/*
|
||||
onBuild() {}
|
||||
onBeforeShow() {} // Happens before: hidden = false
|
||||
beforeShow() {} // Happens before: hidden = false
|
||||
onShow() {} // Happens after: hidden = false
|
||||
onHide() {}
|
||||
*/
|
||||
|
@ -73,7 +73,7 @@ export class AbstractViewPopup extends AbstractView
|
|||
onClose() {}
|
||||
|
||||
/*
|
||||
onBeforeShow() {} // Happens before showModal()
|
||||
beforeShow() {} // Happens before showModal()
|
||||
onShow() {} // Happens after showModal()
|
||||
afterShow() {} // Happens after showModal() animation transitionend
|
||||
onHide() {} // Happens before animation transitionend
|
||||
|
@ -112,7 +112,7 @@ export class AbstractViewSettings
|
|||
{
|
||||
/*
|
||||
onBuild(viewModelDom) {}
|
||||
onBeforeShow() {}
|
||||
beforeShow() {}
|
||||
onShow() {}
|
||||
onHide() {}
|
||||
viewModelDom
|
||||
|
|
|
@ -214,7 +214,7 @@ const
|
|||
vmScreen.onShow && vmScreen.onShow();
|
||||
|
||||
forEachViewModel(vmScreen, (vm, dom) => {
|
||||
vm.onBeforeShow && vm.onBeforeShow();
|
||||
vm.beforeShow && vm.beforeShow();
|
||||
dom.hidden = false;
|
||||
vm.onShow && vm.onShow();
|
||||
autofocus(dom);
|
||||
|
@ -243,7 +243,7 @@ export const
|
|||
if (vm) {
|
||||
params = params || [];
|
||||
|
||||
vm.onBeforeShow && vm.onBeforeShow(...params);
|
||||
vm.beforeShow && vm.beforeShow(...params);
|
||||
|
||||
vm.modalVisible(true);
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ export class AbstractSettingsScreen extends AbstractScreen {
|
|||
this.oCurrentSubScreen = settingsScreen;
|
||||
|
||||
// show
|
||||
settingsScreen.onBeforeShow && settingsScreen.onBeforeShow();
|
||||
settingsScreen.beforeShow && settingsScreen.beforeShow();
|
||||
settingsScreen.viewModelDom.hidden = false;
|
||||
settingsScreen.onShow && settingsScreen.onShow();
|
||||
|
||||
|
|
|
@ -21,8 +21,6 @@ import { SystemDropDownUserView } from 'View/User/SystemDropDown';
|
|||
import { SettingsMenuUserView } from 'View/User/Settings/Menu';
|
||||
import { SettingsPaneUserView } from 'View/User/Settings/Pane';
|
||||
|
||||
//import { staticLink } from 'Common/Links';
|
||||
|
||||
export class SettingsUserScreen extends AbstractSettingsScreen {
|
||||
constructor() {
|
||||
super([SystemDropDownUserView, SettingsMenuUserView, SettingsPaneUserView]);
|
||||
|
@ -41,7 +39,6 @@ export class SettingsUserScreen extends AbstractSettingsScreen {
|
|||
|
||||
if (SettingsCapa('Sieve')) {
|
||||
views.push(UserSettingsFilters);
|
||||
// rl.loadScript(staticLink('js/sieve.js')).then(() => 0).catch(e => console.error(e));
|
||||
}
|
||||
|
||||
if (SettingsCapa('AutoLogout') || SettingsCapa('OpenPGP') || SettingsCapa('GnuPG')) {
|
||||
|
|
|
@ -9,7 +9,7 @@ export class AdminSettingsConfig /*extends AbstractViewSettings*/ {
|
|||
this.config = ko.observableArray();
|
||||
}
|
||||
|
||||
onBeforeShow() {
|
||||
beforeShow() {
|
||||
Remote.request('AdminSettingsGet', (iError, data) => {
|
||||
if (!iError) {
|
||||
const cfg = [],
|
||||
|
|
|
@ -1,97 +1,44 @@
|
|||
import { getNotification } from 'Common/Translator';
|
||||
import { forEachObjectValue } from 'Common/Utils';
|
||||
import { addObservablesTo } from 'External/ko';
|
||||
import { staticLink } from 'Common/Links';
|
||||
import { FolderUserStore } from 'Stores/User/Folder';
|
||||
|
||||
import { SieveUserStore } from 'Stores/User/Sieve';
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
|
||||
import { SieveScriptModel } from 'Model/SieveScript';
|
||||
|
||||
import { showScreenPopup } from 'Knoin/Knoin';
|
||||
|
||||
import { SieveScriptPopupView } from 'View/Popup/SieveScript';
|
||||
|
||||
//export class UserSettingsFilters /*extends AbstractViewSettings*/ {
|
||||
export class UserSettingsFilters /*extends AbstractViewSettings*/ {
|
||||
constructor() {
|
||||
this.scripts = SieveUserStore.scripts;
|
||||
this.loading = ko.observable(false).extend({ debounce: 200 });
|
||||
|
||||
this.scripts = ko.observableArray();
|
||||
this.loading = ko.observable(true).extend({ debounce: 200 });
|
||||
addObservablesTo(this, {
|
||||
serverError: false,
|
||||
serverErrorDesc: ''
|
||||
});
|
||||
|
||||
rl.loadScript(staticLink('js/sieve.js')).then(() => {
|
||||
const Sieve = window.Sieve;
|
||||
Sieve.folderList = FolderUserStore.folderList;
|
||||
Sieve.serverError.subscribe(value => this.serverError(value));
|
||||
Sieve.serverErrorDesc.subscribe(value => this.serverErrorDesc(value));
|
||||
Sieve.loading.subscribe(value => this.loading(value));
|
||||
Sieve.scripts.subscribe(value => this.scripts(value));
|
||||
Sieve.updateList();
|
||||
}).catch(e => console.error(e));
|
||||
|
||||
this.scriptForDeletion = ko.observable(null).askDeleteHelper();
|
||||
}
|
||||
|
||||
setError(text) {
|
||||
this.serverError(true);
|
||||
this.serverErrorDesc(text);
|
||||
}
|
||||
|
||||
updateList() {
|
||||
if (!this.loading()) {
|
||||
this.loading(true);
|
||||
this.serverError(false);
|
||||
|
||||
Remote.request('Filters', (iError, data) => {
|
||||
this.loading(false);
|
||||
this.scripts([]);
|
||||
|
||||
if (iError) {
|
||||
SieveUserStore.capa([]);
|
||||
this.setError(getNotification(iError));
|
||||
} else {
|
||||
SieveUserStore.capa(data.Result.Capa);
|
||||
/*
|
||||
this.scripts(
|
||||
data.Result.Scripts.map(aItem => SieveScriptModel.reviveFromJson(aItem)).filter(v => v)
|
||||
);
|
||||
*/
|
||||
forEachObjectValue(data.Result.Scripts, value => {
|
||||
value = SieveScriptModel.reviveFromJson(value);
|
||||
value && this.scripts.push(value)
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
addScript() {
|
||||
showScreenPopup(SieveScriptPopupView, [new SieveScriptModel()]);
|
||||
this.editScript();
|
||||
}
|
||||
|
||||
editScript(script) {
|
||||
showScreenPopup(SieveScriptPopupView, [script]);
|
||||
window.Sieve.ScriptView.showModal(script ? [script] : null);
|
||||
}
|
||||
|
||||
deleteScript(script) {
|
||||
this.serverError(false);
|
||||
Remote.request('FiltersScriptDelete',
|
||||
(iError, data) => {
|
||||
if (iError) {
|
||||
this.setError((data && data.ErrorMessageAdditional) || getNotification(iError));
|
||||
} else {
|
||||
this.scripts.remove(script);
|
||||
}
|
||||
},
|
||||
{name:script.name()}
|
||||
);
|
||||
window.Sieve.deleteScript(script);
|
||||
}
|
||||
|
||||
toggleScript(script) {
|
||||
let name = script.active() ? '' : script.name();
|
||||
this.serverError(false);
|
||||
Remote.request('FiltersScriptActivate',
|
||||
(iError, data) => {
|
||||
if (iError) {
|
||||
this.setError((data && data.ErrorMessageAdditional) || iError)
|
||||
} else {
|
||||
this.scripts.forEach(script => script.active(script.name() === name));
|
||||
}
|
||||
},
|
||||
{name:name}
|
||||
);
|
||||
window.Sieve.toggleScript(script);
|
||||
}
|
||||
|
||||
onBuild(oDom) {
|
||||
|
@ -103,6 +50,6 @@ export class UserSettingsFilters /*extends AbstractViewSettings*/ {
|
|||
}
|
||||
|
||||
onShow() {
|
||||
this.updateList();
|
||||
window.Sieve && window.Sieve.updateList();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
import { getNotification } from 'Common/Translator';
|
||||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
|
||||
//export class UserSettingsFilters /*extends AbstractViewSettings*/ {
|
||||
export class UserSettingsSieve /*extends AbstractViewSettings*/ {
|
||||
constructor() {
|
||||
const Sieve = window.Sieve;
|
||||
this.scripts = Sieve.scripts;
|
||||
this.loading = Sieve.loading;
|
||||
this.serverError = Sieve.serverError;
|
||||
this.serverErrorDesc = Sieve.serverErrorDesc;
|
||||
this.scriptForDeletion = ko.observable(null).askDeleteHelper();
|
||||
}
|
||||
|
||||
setError(text) {
|
||||
this.serverError(true);
|
||||
this.serverErrorDesc(text);
|
||||
}
|
||||
|
||||
updateList() {
|
||||
window.Sieve.updateList();
|
||||
}
|
||||
|
||||
addScript() {
|
||||
window.Sieve.ScriptView.showModal();
|
||||
}
|
||||
|
||||
editScript(script) {
|
||||
window.Sieve.ScriptView.showModal([script]);
|
||||
}
|
||||
|
||||
deleteScript(script) {
|
||||
this.serverError(false);
|
||||
Remote.request('FiltersScriptDelete',
|
||||
(iError, data) => {
|
||||
if (iError) {
|
||||
this.setError((data && data.ErrorMessageAdditional) || getNotification(iError));
|
||||
} else {
|
||||
this.scripts.remove(script);
|
||||
}
|
||||
},
|
||||
{name:script.name()}
|
||||
);
|
||||
}
|
||||
|
||||
toggleScript(script) {
|
||||
let name = script.active() ? '' : script.name();
|
||||
this.serverError(false);
|
||||
Remote.request('FiltersScriptActivate',
|
||||
(iError, data) => {
|
||||
if (iError) {
|
||||
this.setError((data && data.ErrorMessageAdditional) || iError)
|
||||
} else {
|
||||
this.scripts.forEach(script => script.active(script.name() === name));
|
||||
}
|
||||
},
|
||||
{name:name}
|
||||
);
|
||||
}
|
||||
|
||||
onBuild(oDom) {
|
||||
oDom.addEventListener('click', event => {
|
||||
const el = event.target.closestWithin('.script-item .e-action', oDom),
|
||||
script = el && ko.dataFor(el);
|
||||
script && this.editScript(script);
|
||||
});
|
||||
}
|
||||
|
||||
onShow() {
|
||||
this.updateList();
|
||||
}
|
||||
}
|
121
dev/Sieve/Model/Abstract.js
Normal file
121
dev/Sieve/Model/Abstract.js
Normal file
|
@ -0,0 +1,121 @@
|
|||
import { forEachObjectValue, forEachObjectEntry, koComputable } from 'Sieve/Utils';
|
||||
|
||||
function typeCast(curValue, newValue) {
|
||||
if (null != curValue) {
|
||||
switch (typeof curValue)
|
||||
{
|
||||
case 'boolean': return 0 != newValue && !!newValue;
|
||||
case 'number': return isFinite(newValue) ? parseFloat(newValue) : 0;
|
||||
case 'string': return null != newValue ? '' + newValue : '';
|
||||
case 'object':
|
||||
if (curValue.constructor.reviveFromJson) {
|
||||
return curValue.constructor.reviveFromJson(newValue);
|
||||
}
|
||||
if (Array.isArray(curValue) && !Array.isArray(newValue))
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
export class AbstractModel {
|
||||
constructor() {
|
||||
/*
|
||||
if (new.target === AbstractModel) {
|
||||
throw new Error("Can't instantiate AbstractModel!");
|
||||
}
|
||||
*/
|
||||
this.disposables = [];
|
||||
}
|
||||
|
||||
addObservables(observables) {
|
||||
forEachObjectEntry(observables, (key, value) =>
|
||||
this[key] || (this[key] = /*isArray(value) ? ko.observableArray(value) :*/ ko.observable(value))
|
||||
);
|
||||
}
|
||||
|
||||
addComputables(computables) {
|
||||
forEachObjectEntry(computables, (key, fn) => this[key] = koComputable(fn));
|
||||
}
|
||||
|
||||
addSubscribables(subscribables) {
|
||||
forEachObjectEntry(subscribables, (key, fn) => this.disposables.push( this[key].subscribe(fn) ) );
|
||||
}
|
||||
|
||||
/** Called by delegateRunOnDestroy */
|
||||
onDestroy() {
|
||||
/** dispose ko subscribables */
|
||||
this.disposables.forEach(disposable => {
|
||||
disposable && typeof disposable.dispose === 'function' && disposable.dispose();
|
||||
});
|
||||
/** clear object entries */
|
||||
// forEachObjectEntry(this, (key, value) => {
|
||||
forEachObjectValue(this, value => {
|
||||
/** clear CollectionModel */
|
||||
let arr = ko.isObservableArray(value) ? value() : value;
|
||||
arr && arr.onDestroy && arr.onDestroy();
|
||||
/** destroy ko.observable/ko.computed? */
|
||||
// dispose(value);
|
||||
/** clear object value */
|
||||
// this[key] = null; // TODO: issue with Contacts view
|
||||
});
|
||||
// this.disposables = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @param {FetchJson} json
|
||||
* @returns {boolean}
|
||||
*/
|
||||
static validJson(json) {
|
||||
return !!(json && ('Object/'+this.name.replace('Model', '') === json['@Object']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @param {FetchJson} json
|
||||
* @returns {*Model}
|
||||
*/
|
||||
static reviveFromJson(json) {
|
||||
let obj = this.validJson(json) ? new this() : null;
|
||||
obj && obj.revivePropertiesFromJson(json);
|
||||
return obj;
|
||||
}
|
||||
|
||||
revivePropertiesFromJson(json) {
|
||||
let model = this.constructor;
|
||||
if (!model.validJson(json)) {
|
||||
return false;
|
||||
}
|
||||
forEachObjectEntry(json, (key, value) => {
|
||||
if ('@' !== key[0]) try {
|
||||
key = key[0].toLowerCase() + key.slice(1);
|
||||
switch (typeof this[key])
|
||||
{
|
||||
case 'function':
|
||||
if (ko.isObservable(this[key])) {
|
||||
this[key](typeCast(this[key](), value));
|
||||
// console.log('Observable ' + (typeof this[key]()) + ' ' + (model.name) + '.' + key + ' revived');
|
||||
}
|
||||
// else console.log(model.name + '.' + key + ' is a function');
|
||||
break;
|
||||
case 'boolean':
|
||||
case 'number':
|
||||
case 'object':
|
||||
case 'string':
|
||||
this[key] = typeCast(this[key], value);
|
||||
break;
|
||||
// fall through
|
||||
case 'undefined':
|
||||
default:
|
||||
// console.log((typeof this[key])+' '+(model.name)+'.'+key+' not revived');
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(model.name + '.' + key);
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,13 +1,11 @@
|
|||
import { koArrayWithDestroy } from 'External/ko';
|
||||
import { koArrayWithDestroy } from 'Sieve/Utils';
|
||||
|
||||
import { arrayLength, pString } from 'Common/Utils';
|
||||
import { i18n } from 'Common/Translator';
|
||||
import { getFolderFromCacheList } from 'Common/Cache';
|
||||
//import { getFolderFromCacheList } from 'Common/Cache';
|
||||
|
||||
import { AccountUserStore } from 'Stores/User/Account';
|
||||
//import { AccountUserStore } from 'Stores/User/Account';
|
||||
|
||||
import { FilterConditionModel } from 'Model/FilterCondition';
|
||||
import { AbstractModel } from 'Knoin/AbstractModel';
|
||||
import { FilterConditionModel } from 'Sieve/Model/FilterCondition';
|
||||
import { AbstractModel } from 'Sieve/Model/Abstract';
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
|
@ -66,9 +64,10 @@ export class FilterModel extends AbstractModel {
|
|||
|
||||
this.conditions = koArrayWithDestroy();
|
||||
|
||||
const fGetRealFolderName = (folderFullName) => {
|
||||
const folder = getFolderFromCacheList(folderFullName);
|
||||
return folder ? folder.fullName.replace('.' === folder.delimiter ? /\./ : /[\\/]+/, ' / ') : folderFullName;
|
||||
const fGetRealFolderName = folderFullName => {
|
||||
// const folder = getFolderFromCacheList(folderFullName);
|
||||
// return folder ? folder.fullName.replace('.' === folder.delimiter ? /\./ : /[\\/]+/, ' / ') : folderFullName;
|
||||
return folderFullName;
|
||||
};
|
||||
|
||||
this.addComputables({
|
||||
|
@ -78,23 +77,23 @@ export class FilterModel extends AbstractModel {
|
|||
|
||||
switch (this.actionType()) {
|
||||
case FilterAction.MoveTo:
|
||||
result = i18n(root + 'MOVE_TO', {
|
||||
result = rl.i18n(root + 'MOVE_TO', {
|
||||
FOLDER: fGetRealFolderName(actionValue)
|
||||
});
|
||||
break;
|
||||
case FilterAction.Forward:
|
||||
result = i18n(root + 'FORWARD_TO', {
|
||||
result = rl.i18n(root + 'FORWARD_TO', {
|
||||
EMAIL: actionValue
|
||||
});
|
||||
break;
|
||||
case FilterAction.Vacation:
|
||||
result = i18n(root + 'VACATION_MESSAGE');
|
||||
result = rl.i18n(root + 'VACATION_MESSAGE');
|
||||
break;
|
||||
case FilterAction.Reject:
|
||||
result = i18n(root + 'REJECT');
|
||||
result = rl.i18n(root + 'REJECT');
|
||||
break;
|
||||
case FilterAction.Discard:
|
||||
result = i18n(root + 'DISCARD');
|
||||
result = rl.i18n(root + 'DISCARD');
|
||||
break;
|
||||
// no default
|
||||
}
|
||||
|
@ -213,7 +212,7 @@ export class FilterModel extends AbstractModel {
|
|||
}
|
||||
|
||||
setRecipients() {
|
||||
this.actionValueFourth(AccountUserStore.getEmailAddresses().join(', '));
|
||||
// this.actionValueFourth(AccountUserStore.getEmailAddresses().join(', '));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -224,16 +223,10 @@ export class FilterModel extends AbstractModel {
|
|||
static reviveFromJson(json) {
|
||||
const filter = super.reviveFromJson(json);
|
||||
if (filter) {
|
||||
filter.id = pString(json.ID);
|
||||
|
||||
filter.conditions([]);
|
||||
|
||||
if (arrayLength(json.Conditions)) {
|
||||
filter.conditions(
|
||||
json.Conditions.map(aData => FilterConditionModel.reviveFromJson(aData)).filter(v => v)
|
||||
);
|
||||
}
|
||||
|
||||
filter.id = filter.id ? '' + filter.id : '';
|
||||
filter.conditions(
|
||||
json.Conditions ? json.Conditions.map(aData => FilterConditionModel.reviveFromJson(aData)).filter(v => v) : []
|
||||
);
|
||||
filter.actionKeep(0 != json.Keep);
|
||||
filter.actionNoStop(0 == json.Stop);
|
||||
filter.actionMarkAsRead(1 == json.MarkAsRead);
|
|
@ -1,6 +1,6 @@
|
|||
import { koComputable } from 'External/ko';
|
||||
import { koComputable } from 'Sieve/Utils';
|
||||
|
||||
import { AbstractModel } from 'Knoin/AbstractModel';
|
||||
import { AbstractModel } from 'Sieve/Model/Abstract';
|
||||
|
||||
/**
|
||||
* @enum {string}
|
|
@ -1,7 +1,6 @@
|
|||
import { AbstractModel } from 'Knoin/AbstractModel';
|
||||
import { FilterModel } from 'Model/Filter';
|
||||
import { arrayLength, pString, b64EncodeJSON } from 'Common/Utils';
|
||||
import { koArrayWithDestroy } from 'External/ko';
|
||||
import { AbstractModel } from 'Sieve/Model/Abstract';
|
||||
import { FilterModel } from 'Sieve/Model/Filter';
|
||||
import { koArrayWithDestroy } from 'Sieve/Utils';
|
||||
|
||||
const SIEVE_FILE_NAME = 'rainloop.user';
|
||||
|
||||
|
@ -153,12 +152,12 @@ function filtersToSieveScript(filters)
|
|||
subject = ':subject ' + quote(StripSpaces(paramValue)) + ' ';
|
||||
}
|
||||
|
||||
paramValue = pString(filter.actionValueThird()).trim();
|
||||
paramValue = (filter.actionValueThird() || '').trim();
|
||||
if (paramValue.length) {
|
||||
days = Math.max(1, parseInt(paramValue, 10));
|
||||
}
|
||||
|
||||
paramValue = pString(filter.actionValueFourth()).trim()
|
||||
paramValue = (filter.actionValueFourth() || '').trim()
|
||||
if (paramValue.length) {
|
||||
paramValue = paramValue.split(',').map(email =>
|
||||
email.trim().length ? quote(email) : ''
|
||||
|
@ -214,7 +213,7 @@ function filtersToSieveScript(filters)
|
|||
'/*',
|
||||
'BEGIN:FILTER:' + filter.id,
|
||||
'BEGIN:HEADER',
|
||||
b64EncodeJSON(filter.toJson()).match(split).join(eol) + 'END:HEADER',
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(filter.toJson())))).match(split).join(eol) + 'END:HEADER',
|
||||
'*/',
|
||||
filter.enabled() ? '' : '/* @Filter is disabled ',
|
||||
filterToString(filter, require),
|
||||
|
@ -317,7 +316,7 @@ export class SieveScriptModel extends AbstractModel
|
|||
if (script) {
|
||||
if (script.allowFilters()) {
|
||||
script.filters(
|
||||
arrayLength(json.filters)
|
||||
Array.isArray(json.filters) && json.filters.length
|
||||
? json.filters.map(aData => FilterModel.reviveFromJson(aData)).filter(v => v)
|
||||
: sieveScriptToFilters(script.body())
|
||||
);
|
|
@ -1,4 +1,89 @@
|
|||
import { SieveScriptModel } from 'Sieve/Model/Script';
|
||||
|
||||
export const
|
||||
// import { i18n } from 'Common/Translator';
|
||||
i18n = rl.i18n,
|
||||
|
||||
// import { forEachObjectValue, forEachObjectEntry } from 'Common/Utils';
|
||||
forEachObjectValue = (obj, fn) => Object.values(obj).forEach(fn),
|
||||
forEachObjectEntry = (obj, fn) => Object.entries(obj).forEach(([key, value]) => fn(key, value)),
|
||||
|
||||
// import { koArrayWithDestroy } from 'External/ko';
|
||||
// With this we don't need delegateRunOnDestroy
|
||||
koArrayWithDestroy = data => {
|
||||
data = ko.observableArray(data);
|
||||
data.subscribe(changes =>
|
||||
changes.forEach(item =>
|
||||
'deleted' === item.status && null == item.moved && item.value.onDestroy && item.value.onDestroy()
|
||||
)
|
||||
, data, 'arrayChange');
|
||||
return data;
|
||||
},
|
||||
|
||||
// import { koComputable } from 'External/ko';
|
||||
koComputable = fn => ko.computed(fn, {'pure':true}),
|
||||
|
||||
arrayToString = (arr, separator) =>
|
||||
arr.map(item => item.toString ? item.toString() : item).join(separator);
|
||||
arr.map(item => item.toString ? item.toString() : item).join(separator),
|
||||
/*
|
||||
getNotificationMessage = code => {
|
||||
let key = getKeyByValue(Notification, code);
|
||||
return key ? I18N_DATA.NOTIFICATIONS[i18nKey(key).replace('_NOTIFICATION', '_ERROR')] : '';
|
||||
rl.i18n('NOTIFICATIONS/')
|
||||
},
|
||||
getNotification = (code, message = '', defCode = 0) => {
|
||||
code = parseInt(code, 10) || 0;
|
||||
if (Notification.ClientViewError === code && message) {
|
||||
return message;
|
||||
}
|
||||
|
||||
return getNotificationMessage(code)
|
||||
|| getNotificationMessage(parseInt(defCode, 10))
|
||||
|| '';
|
||||
},
|
||||
*/
|
||||
getNotification = code => 'ERROR ' + code,
|
||||
|
||||
Remote = rl.app.Remote,
|
||||
|
||||
// capabilities
|
||||
capa = ko.observableArray(),
|
||||
|
||||
// Sieve scripts SieveScriptModel
|
||||
scripts = koArrayWithDestroy(),
|
||||
|
||||
loading = ko.observable(false),
|
||||
serverError = ko.observable(false),
|
||||
serverErrorDesc = ko.observable(''),
|
||||
setError = text => {
|
||||
serverError(true);
|
||||
serverErrorDesc(text);
|
||||
},
|
||||
|
||||
updateList = () => {
|
||||
if (!loading()) {
|
||||
loading(true);
|
||||
serverError(false);
|
||||
|
||||
Remote.request('Filters', (iError, data) => {
|
||||
loading(false);
|
||||
scripts([]);
|
||||
|
||||
if (iError) {
|
||||
capa([]);
|
||||
setError(getNotification(iError));
|
||||
} else {
|
||||
capa(data.Result.Capa);
|
||||
/*
|
||||
scripts(
|
||||
data.Result.Scripts.map(aItem => SieveScriptModel.reviveFromJson(aItem)).filter(v => v)
|
||||
);
|
||||
*/
|
||||
forEachObjectValue(data.Result.Scripts, value => {
|
||||
value = SieveScriptModel.reviveFromJson(value);
|
||||
value && scripts.push(value)
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,19 +1,77 @@
|
|||
import ko from 'ko';
|
||||
import { koComputable } from 'External/ko';
|
||||
import { FilterAction } from 'Sieve/Model/Filter';
|
||||
import { FilterConditionField, FilterConditionType } from 'Sieve/Model/FilterCondition';
|
||||
/*
|
||||
import { SettingsUserStore } from 'Stores/User/Settings';
|
||||
*/
|
||||
|
||||
import { FilterAction } from 'Model/Filter';
|
||||
import { FilterConditionField, FilterConditionType } from 'Model/FilterCondition';
|
||||
import { SettingsGet } from 'Common/Globals';
|
||||
import { defaultOptionsAfterRender } from 'Common/Utils';
|
||||
import { i18n, initOnStartOrLangChange } from 'Common/Translator';
|
||||
import {
|
||||
capa,
|
||||
i18n,
|
||||
koComputable
|
||||
} from 'Sieve/Utils';
|
||||
|
||||
import { SieveUserStore } from 'Stores/User/Sieve';
|
||||
const
|
||||
// import { defaultOptionsAfterRender } from 'Common/Utils';
|
||||
defaultOptionsAfterRender = (domItem, item) =>
|
||||
domItem && item && undefined !== item.disabled
|
||||
&& domItem.classList.toggle('disabled', domItem.disabled = item.disabled),
|
||||
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
// import { folderListOptionsBuilder } from 'Common/Folders';
|
||||
/**
|
||||
* @param {Array=} aDisabled
|
||||
* @param {Array=} aHeaderLines
|
||||
* @param {Function=} fRenameCallback
|
||||
* @returns {Array}
|
||||
*/
|
||||
folderListOptionsBuilder = (
|
||||
aDisabled,
|
||||
aHeaderLines,
|
||||
fRenameCallback
|
||||
) => {
|
||||
const
|
||||
aResult = [],
|
||||
sDeepPrefix = '\u00A0\u00A0\u00A0',
|
||||
showUnsubscribed = true/*!SettingsUserStore.hideUnsubscribed()*/,
|
||||
|
||||
import { folderListOptionsBuilder } from 'Common/Folders';
|
||||
foldersWalk = folders => {
|
||||
folders.forEach(oItem => {
|
||||
if (showUnsubscribed || oItem.hasSubscriptions() || !oItem.exists) {
|
||||
aResult.push({
|
||||
id: oItem.fullName,
|
||||
name:
|
||||
sDeepPrefix.repeat(oItem.deep) +
|
||||
fRenameCallback(oItem),
|
||||
system: false,
|
||||
disabled: !oItem.selectable() || aDisabled.includes(oItem.fullName)
|
||||
});
|
||||
}
|
||||
|
||||
export class FilterPopupView extends AbstractViewPopup {
|
||||
if (oItem.subFolders.length) {
|
||||
foldersWalk(oItem.subFolders());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
fRenameCallback = fRenameCallback || (oItem => oItem.name());
|
||||
Array.isArray(aDisabled) || (aDisabled = []);
|
||||
|
||||
Array.isArray(aHeaderLines) && aHeaderLines.forEach(line =>
|
||||
aResult.push({
|
||||
id: line[0],
|
||||
name: line[1],
|
||||
system: false,
|
||||
disabled: false
|
||||
})
|
||||
);
|
||||
|
||||
// FolderUserStore.folderList()
|
||||
foldersWalk(window.Sieve.folderList() || []);
|
||||
|
||||
return aResult;
|
||||
};
|
||||
|
||||
export class FilterPopupView extends rl.pluginPopupView {
|
||||
constructor() {
|
||||
super('Filter');
|
||||
|
||||
|
@ -27,7 +85,7 @@ export class FilterPopupView extends AbstractViewPopup {
|
|||
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
|
||||
this.folderSelectList = koComputable(() =>
|
||||
folderListOptionsBuilder(
|
||||
[SettingsGet('SieveAllowFileintoInbox') ? '' : 'INBOX'],
|
||||
[rl.settings.get('SieveAllowFileintoInbox') ? '' : 'INBOX'],
|
||||
[['', '']],
|
||||
item => item ? item.localName() : ''
|
||||
)
|
||||
|
@ -39,9 +97,7 @@ export class FilterPopupView extends AbstractViewPopup {
|
|||
key => this[key] = ko.observableArray()
|
||||
);
|
||||
|
||||
initOnStartOrLangChange(this.populateOptions.bind(this));
|
||||
|
||||
SieveUserStore.capa.subscribe(this.populateOptions, this);
|
||||
this.populateOptions();
|
||||
}
|
||||
|
||||
saveFilter() {
|
||||
|
@ -77,11 +133,10 @@ export class FilterPopupView extends AbstractViewPopup {
|
|||
|
||||
// this.actionTypeOptions.push({id: FilterAction.None,
|
||||
// name: i18n('GLOBAL/NONE')});
|
||||
const modules = SieveUserStore.capa;
|
||||
if (modules) {
|
||||
this.allowMarkAsRead(modules.includes('imap4flags'));
|
||||
if (capa) {
|
||||
this.allowMarkAsRead(capa.includes('imap4flags'));
|
||||
|
||||
if (modules.includes('fileinto')) {
|
||||
if (capa.includes('fileinto')) {
|
||||
this.actionTypeOptions.push({
|
||||
id: FilterAction.MoveTo,
|
||||
name: i18nFilter('ACTION_MOVE_TO')
|
||||
|
@ -92,22 +147,22 @@ export class FilterPopupView extends AbstractViewPopup {
|
|||
});
|
||||
}
|
||||
|
||||
if (modules.includes('reject')) {
|
||||
if (capa.includes('reject')) {
|
||||
this.actionTypeOptions.push({ id: FilterAction.Reject, name: i18nFilter('ACTION_REJECT') });
|
||||
}
|
||||
|
||||
if (modules.includes('vacation')) {
|
||||
if (capa.includes('vacation')) {
|
||||
this.actionTypeOptions.push({
|
||||
id: FilterAction.Vacation,
|
||||
name: i18nFilter('ACTION_VACATION_MESSAGE')
|
||||
});
|
||||
}
|
||||
|
||||
if (modules.includes('body')) {
|
||||
if (capa.includes('body')) {
|
||||
this.fieldOptions.push({ id: FilterConditionField.Body, name: i18nFilter('FIELD_BODY') });
|
||||
}
|
||||
|
||||
if (modules.includes('regex')) {
|
||||
if (capa.includes('regex')) {
|
||||
this.typeOptions.push({ id: FilterConditionType.Regex, name: 'Regex' });
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +184,8 @@ export class FilterPopupView extends AbstractViewPopup {
|
|||
this.filter().removeCondition(oConditionToDelete);
|
||||
}
|
||||
|
||||
onShow(oFilter, fTrueCallback, bEdit) {
|
||||
beforeShow(oFilter, fTrueCallback, bEdit) {
|
||||
// onShow(oFilter, fTrueCallback, bEdit) {
|
||||
this.isNew(!bEdit);
|
||||
|
||||
this.fTrueCallback = fTrueCallback;
|
||||
|
@ -138,6 +194,8 @@ export class FilterPopupView extends AbstractViewPopup {
|
|||
this.selectedFolderValue(oFilter.actionValue());
|
||||
|
||||
bEdit || oFilter.nameFocused(true);
|
||||
|
||||
this.populateOptions();
|
||||
}
|
||||
|
||||
afterShow() {
|
|
@ -1,24 +1,22 @@
|
|||
import ko from 'ko';
|
||||
import { FilterModel } from 'Sieve/Model/Filter';
|
||||
import { SieveScriptModel } from 'Sieve/Model/Script';
|
||||
|
||||
import { getNotification, i18nToNodes } from 'Common/Translator';
|
||||
import { addObservablesTo } from 'External/ko';
|
||||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
import { FilterModel } from 'Model/Filter';
|
||||
import { SieveUserStore } from 'Stores/User/Sieve';
|
||||
|
||||
import { showScreenPopup } from 'Knoin/Knoin';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
import { FilterPopupView } from 'View/Popup/Filter';
|
||||
import { FilterPopupView } from 'Sieve/View/Filter';
|
||||
|
||||
//import { parseScript } from 'Sieve/Parser';
|
||||
|
||||
export class SieveScriptPopupView extends AbstractViewPopup {
|
||||
import {
|
||||
capa,
|
||||
scripts,
|
||||
getNotification,
|
||||
Remote
|
||||
} from 'Sieve/Utils';
|
||||
|
||||
export class SieveScriptPopupView extends rl.pluginPopupView {
|
||||
constructor() {
|
||||
super('SieveScript');
|
||||
|
||||
addObservablesTo(this, {
|
||||
this.addObservables({
|
||||
saveError: false,
|
||||
saveErrorText: '',
|
||||
rawActive: false,
|
||||
|
@ -26,7 +24,7 @@ export class SieveScriptPopupView extends AbstractViewPopup {
|
|||
script: null
|
||||
});
|
||||
|
||||
this.sieveCapabilities = SieveUserStore.capa.join(' ');
|
||||
this.sieveCapabilities = capa.join(' ');
|
||||
this.saving = false;
|
||||
|
||||
this.filterForDeletion = ko.observable(null).askDeleteHelper();
|
||||
|
@ -40,7 +38,7 @@ export class SieveScriptPopupView extends AbstractViewPopup {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!script.exists() && SieveUserStore.scripts.find(item => item.name() === script.name())) {
|
||||
if (!script.exists() && scripts.find(item => item.name() === script.name())) {
|
||||
script.nameError(true);
|
||||
return;
|
||||
}
|
||||
|
@ -60,7 +58,7 @@ export class SieveScriptPopupView extends AbstractViewPopup {
|
|||
self.saveError(true);
|
||||
self.saveErrorText((data && data.ErrorMessageAdditional) || getNotification(iError));
|
||||
} else {
|
||||
script.exists() || SieveUserStore.scripts.push(script);
|
||||
script.exists() || scripts.push(script);
|
||||
script.exists(true);
|
||||
script.hasChanges(false);
|
||||
}
|
||||
|
@ -78,7 +76,7 @@ export class SieveScriptPopupView extends AbstractViewPopup {
|
|||
/* this = SieveScriptModel */
|
||||
const filter = new FilterModel();
|
||||
filter.generateID();
|
||||
showScreenPopup(FilterPopupView, [
|
||||
FilterPopupView.showModal([
|
||||
filter,
|
||||
() => this.filters.push(filter)
|
||||
]);
|
||||
|
@ -86,13 +84,14 @@ export class SieveScriptPopupView extends AbstractViewPopup {
|
|||
|
||||
editFilter(filter) {
|
||||
const clonedFilter = filter.cloneSelf();
|
||||
showScreenPopup(FilterPopupView, [
|
||||
FilterPopupView.showModal([
|
||||
clonedFilter,
|
||||
() => {
|
||||
const script = this.script(),
|
||||
filters = script.filters(),
|
||||
index = filters.indexOf(filter);
|
||||
if (-1 < index) {
|
||||
// script.filters.splice(index, 1, clonedFilter);
|
||||
filters[index] = clonedFilter;
|
||||
script.filters(filters);
|
||||
}
|
||||
|
@ -118,7 +117,9 @@ export class SieveScriptPopupView extends AbstractViewPopup {
|
|||
});
|
||||
}
|
||||
|
||||
onShow(oScript) {
|
||||
beforeShow(oScript) {
|
||||
// onShow(oScript) {
|
||||
oScript = oScript || new SieveScriptModel();
|
||||
let raw = !oScript.allowFilters();
|
||||
this.script(oScript);
|
||||
this.rawActive(raw);
|
||||
|
@ -132,9 +133,4 @@ export class SieveScriptPopupView extends AbstractViewPopup {
|
|||
console.log(tree.join('\r\n'));
|
||||
*/
|
||||
}
|
||||
|
||||
afterShow() {
|
||||
// Sometimes not everything is translated, try again
|
||||
i18nToNodes(this.viewModelDom);
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ export class LanguagesPopupView extends AbstractViewPopup {
|
|||
this.languages().forEach(item => item.selected(item.key === currentLang));
|
||||
}
|
||||
|
||||
onBeforeShow() {
|
||||
beforeShow() {
|
||||
this.fLang = null;
|
||||
this.userLanguage('');
|
||||
|
||||
|
|
246
dev/sieve.js
246
dev/sieve.js
|
@ -1,206 +1,56 @@
|
|||
import { parseScript } from 'Sieve/Parser';
|
||||
import {
|
||||
capa,
|
||||
scripts,
|
||||
loading,
|
||||
serverError,
|
||||
serverErrorDesc,
|
||||
setError,
|
||||
updateList,
|
||||
getNotification,
|
||||
Remote
|
||||
} from 'Sieve/Utils';
|
||||
|
||||
import { FilterModel } from 'Model/Filter';
|
||||
import { SieveScriptModel } from 'Model/SieveScript';
|
||||
import { FilterPopupView } from 'View/Popup/Filter';
|
||||
|
||||
//import { getNotification, i18nToNodes } from 'Common/Translator';
|
||||
import { forEachObjectValue } from 'Common/Utils';
|
||||
import { koArrayWithDestroy } from 'External/ko';
|
||||
import { SieveScriptPopupView } from 'Sieve/View/Script';
|
||||
|
||||
// SieveUserStore
|
||||
const
|
||||
/*
|
||||
getNotificationMessage = code => {
|
||||
let key = getKeyByValue(Notification, code);
|
||||
return key ? I18N_DATA.NOTIFICATIONS[i18nKey(key).replace('_NOTIFICATION', '_ERROR')] : '';
|
||||
rl.i18n('NOTIFICATIONS/')
|
||||
},
|
||||
getNotification = (code, message = '', defCode = 0) => {
|
||||
code = parseInt(code, 10) || 0;
|
||||
if (Notification.ClientViewError === code && message) {
|
||||
return message;
|
||||
}
|
||||
window.Sieve = {
|
||||
capa: capa,
|
||||
scripts: scripts,
|
||||
setError: setError,
|
||||
updateList: updateList,
|
||||
loading: loading,
|
||||
serverError: serverError,
|
||||
serverErrorDesc: serverErrorDesc,
|
||||
ScriptView: SieveScriptPopupView,
|
||||
|
||||
return getNotificationMessage(code)
|
||||
|| getNotificationMessage(parseInt(defCode, 10))
|
||||
|| '';
|
||||
},
|
||||
*/
|
||||
getNotification = code => 'ERROR ' + code,
|
||||
folderList: null,
|
||||
|
||||
Remote = rl.app.Remote,
|
||||
|
||||
Sieve = {
|
||||
// capabilities
|
||||
capa: ko.observableArray(),
|
||||
|
||||
// Sieve scripts SieveScriptModel
|
||||
scripts: koArrayWithDestroy(),
|
||||
|
||||
parseScript: parseScript,
|
||||
|
||||
setError: text => {
|
||||
Sieve.serverError(true);
|
||||
Sieve.serverErrorDesc(text);
|
||||
},
|
||||
updateList: () => {
|
||||
if (!Sieve.loading()) {
|
||||
Sieve.loading(true);
|
||||
Sieve.serverError(false);
|
||||
|
||||
Remote.request('Filters', (iError, data) => {
|
||||
Sieve.loading(false);
|
||||
Sieve.scripts([]);
|
||||
|
||||
if (iError) {
|
||||
Sieve.capa([]);
|
||||
Sieve.setError(getNotification(iError));
|
||||
} else {
|
||||
Sieve.capa(data.Result.Capa);
|
||||
/*
|
||||
Sieve.scripts(
|
||||
data.Result.Scripts.map(aItem => SieveScriptModel.reviveFromJson(aItem)).filter(v => v)
|
||||
);
|
||||
*/
|
||||
forEachObjectValue(data.Result.Scripts, value => {
|
||||
value = SieveScriptModel.reviveFromJson(value);
|
||||
value && Sieve.scripts.push(value)
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
loading: ko.observable(false).extend({ debounce: 200 }),
|
||||
serverError: ko.observable(false),
|
||||
serverErrorDesc: ko.observable('')
|
||||
};
|
||||
|
||||
Sieve.ScriptView = class SieveScriptPopupView extends rl.pluginPopupView {
|
||||
constructor() {
|
||||
super('SieveScript');
|
||||
|
||||
this.addObservables({
|
||||
saveError: false,
|
||||
saveErrorText: '',
|
||||
rawActive: false,
|
||||
allowToggle: false,
|
||||
script: null
|
||||
});
|
||||
|
||||
this.sieveCapabilities = Sieve.capa.join(' ');
|
||||
this.saving = false;
|
||||
|
||||
this.filterForDeletion = ko.observable(null).askDeleteHelper();
|
||||
}
|
||||
|
||||
saveScript() {
|
||||
let self = this,
|
||||
script = self.script();
|
||||
if (!self.saving/* && script.hasChanges()*/) {
|
||||
if (!script.verify()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!script.exists() && Sieve.scripts.find(item => item.name() === script.name())) {
|
||||
script.nameError(true);
|
||||
return;
|
||||
}
|
||||
|
||||
self.saving = true;
|
||||
self.saveError(false);
|
||||
|
||||
if (self.allowToggle()) {
|
||||
script.body(script.filtersToRaw());
|
||||
}
|
||||
|
||||
Remote.request('FiltersScriptSave',
|
||||
(iError, data) => {
|
||||
self.saving = false;
|
||||
|
||||
if (iError) {
|
||||
self.saveError(true);
|
||||
self.saveErrorText((data && data.ErrorMessageAdditional) || getNotification(iError));
|
||||
} else {
|
||||
script.exists() || Sieve.scripts.push(script);
|
||||
script.exists(true);
|
||||
script.hasChanges(false);
|
||||
}
|
||||
},
|
||||
script.toJson()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
deleteFilter(filter) {
|
||||
this.script().filters.remove(filter);
|
||||
}
|
||||
|
||||
addFilter() {
|
||||
/* this = SieveScriptModel */
|
||||
const filter = new FilterModel();
|
||||
filter.generateID();
|
||||
FilterPopupView.showModal([
|
||||
filter,
|
||||
() => this.filters.push(filter)
|
||||
]);
|
||||
}
|
||||
|
||||
editFilter(filter) {
|
||||
const clonedFilter = filter.cloneSelf();
|
||||
FilterPopupView.showModal([
|
||||
clonedFilter,
|
||||
() => {
|
||||
const script = this.script(),
|
||||
filters = script.filters(),
|
||||
index = filters.indexOf(filter);
|
||||
if (-1 < index) {
|
||||
filters[index] = clonedFilter;
|
||||
script.filters(filters);
|
||||
deleteScript: script => {
|
||||
serverError(false);
|
||||
Remote.request('FiltersScriptDelete',
|
||||
(iError, data) => {
|
||||
if (iError) {
|
||||
setError((data && data.ErrorMessageAdditional) || getNotification(iError));
|
||||
} else {
|
||||
scripts.remove(script);
|
||||
}
|
||||
},
|
||||
true
|
||||
]);
|
||||
{name:script.name()}
|
||||
);
|
||||
},
|
||||
|
||||
toggleScript(script) {
|
||||
let name = script.active() ? '' : script.name();
|
||||
serverError(false);
|
||||
Remote.request('FiltersScriptActivate',
|
||||
(iError, data) => {
|
||||
if (iError) {
|
||||
setError((data && data.ErrorMessageAdditional) || iError)
|
||||
} else {
|
||||
scripts.forEach(script => script.active(script.name() === name));
|
||||
}
|
||||
},
|
||||
{name:name}
|
||||
);
|
||||
}
|
||||
|
||||
toggleFiltersRaw() {
|
||||
let script = this.script(), notRaw = !this.rawActive();
|
||||
if (notRaw) {
|
||||
script.body(script.filtersToRaw());
|
||||
script.hasChanges(script.hasChanges());
|
||||
}
|
||||
this.rawActive(notRaw);
|
||||
}
|
||||
|
||||
onBuild(oDom) {
|
||||
oDom.addEventListener('click', event => {
|
||||
const el = event.target.closestWithin('td.e-action', oDom),
|
||||
filter = el && ko.dataFor(el);
|
||||
filter && this.editFilter(filter);
|
||||
});
|
||||
}
|
||||
|
||||
onShow(oScript) {
|
||||
oScript = oScript || new SieveScriptModel();
|
||||
let raw = !oScript.allowFilters();
|
||||
this.script(oScript);
|
||||
this.rawActive(raw);
|
||||
this.allowToggle(!raw);
|
||||
this.saveError(false);
|
||||
|
||||
/*
|
||||
// TODO: Sieve GUI
|
||||
let tree = parseScript(oScript.body(), oScript.name());
|
||||
console.dir(tree);
|
||||
console.log(tree.join('\r\n'));
|
||||
*/
|
||||
}
|
||||
|
||||
afterShow() {
|
||||
// Sometimes not everything is translated, try again
|
||||
// i18nToNodes(this.viewModelDom);
|
||||
}
|
||||
}
|
||||
|
||||
window.Sieve = Sieve;
|
||||
};
|
||||
|
|
|
@ -21,7 +21,7 @@ class PluginPopupView extends rl.pluginPopupView
|
|||
onBuild(dom) {}
|
||||
|
||||
// Happens before showModal()
|
||||
onBeforeShow(...params) {}
|
||||
beforeShow(...params) {}
|
||||
// Happens after showModal()
|
||||
onShow(...params) {}
|
||||
// Happens after showModal() animation transitionend
|
||||
|
|
|
@ -5,44 +5,39 @@
|
|||
<span data-i18n="POPUPS_FILTER/TITLE_EDIT_FILTER" data-bind="visible: !isNew()"></span>
|
||||
</h3>
|
||||
</header>
|
||||
<div class="modal-body">
|
||||
<div class="filter" data-bind="with: filter, i18nInit: filter">
|
||||
<div data-bind="i18nInit: true">
|
||||
<div class="modal-body" data-bind="with: filter, i18nInit: filter">
|
||||
<div class="control-group" data-bind="css: {'error': nameError}">
|
||||
<input type="text" class="span5"
|
||||
data-bind="value: name, hasfocus: nameFocused"
|
||||
autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
data-i18n="[placeholder]GLOBAL/NAME">
|
||||
</div>
|
||||
|
||||
<div class="control-group" data-bind="css: {'error': nameError}">
|
||||
<input type="text" class="span5"
|
||||
data-bind="value: name, hasfocus: nameFocused"
|
||||
autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
data-i18n="[placeholder]GLOBAL/NAME">
|
||||
</div>
|
||||
|
||||
<div class="legend" data-i18n="POPUPS_FILTER/LEGEND_CONDITIONS"></div>
|
||||
<div class="control-group" data-bind="visible: 1 < conditions().length">
|
||||
<select class="span4" data-bind="value: conditionsType">
|
||||
<option value="Any"
|
||||
data-i18n="POPUPS_FILTER/SELECT_MATCH_ANY"></option>
|
||||
<option value="All"
|
||||
data-i18n="POPUPS_FILTER/SELECT_MATCH_ALL"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div data-bind="visible: conditions().length, foreach: conditions">
|
||||
<div class="control-group" data-bind="css: {'error': valueError}" style="display:flex">
|
||||
<div style="flex-grow:1" data-bind="css: {'error': valueError}, template: {'name': template(), 'data': $data}"></div>
|
||||
<span class="delete-action button-delete fontastic" style="margin-top: 5px;"
|
||||
data-bind="click: function (oCondition) { $root.removeCondition(oCondition); }">🗑</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group" data-bind="visible: 0 === conditions().length"
|
||||
data-i18n="POPUPS_FILTER/ALL_INCOMING_MESSAGES_DESC"></div>
|
||||
<div class="control-group">
|
||||
<a class="btn" data-bind="click: addCondition, i18nInit: true" data-icon="✚" data-i18n="POPUPS_FILTER/BUTTON_ADD_CONDITION"></a>
|
||||
</div>
|
||||
|
||||
<div class="legend" data-i18n="POPUPS_FILTER/LEGEND_ACTIONS"></div>
|
||||
<select class="span3" data-bind="options: $root.actionTypeOptions, value: actionType, optionsText: 'name', optionsValue: 'id'"></select>
|
||||
<div data-bind="template: {'name': actionTemplate()}, i18nUpdate: actionTemplate"></div>
|
||||
<div class="legend" data-i18n="POPUPS_FILTER/LEGEND_CONDITIONS"></div>
|
||||
<div class="control-group" data-bind="visible: 1 < conditions().length">
|
||||
<select class="span4" data-bind="value: conditionsType">
|
||||
<option value="Any"
|
||||
data-i18n="POPUPS_FILTER/SELECT_MATCH_ANY"></option>
|
||||
<option value="All"
|
||||
data-i18n="POPUPS_FILTER/SELECT_MATCH_ALL"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div data-bind="visible: conditions().length, foreach: conditions">
|
||||
<div class="control-group" data-bind="css: {'error': valueError}" style="display:flex">
|
||||
<div style="flex-grow:1" data-bind="css: {'error': valueError}, template: {'name': template(), 'data': $data}"></div>
|
||||
<span class="delete-action button-delete fontastic" style="margin-top: 5px;"
|
||||
data-bind="click: function (oCondition) { $root.removeCondition(oCondition); }">🗑</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group" data-bind="visible: 0 === conditions().length"
|
||||
data-i18n="POPUPS_FILTER/ALL_INCOMING_MESSAGES_DESC"></div>
|
||||
<div class="control-group">
|
||||
<a class="btn" data-bind="click: addCondition, i18nInit: true" data-icon="✚" data-i18n="POPUPS_FILTER/BUTTON_ADD_CONDITION"></a>
|
||||
</div>
|
||||
|
||||
<div class="legend" data-i18n="POPUPS_FILTER/LEGEND_ACTIONS"></div>
|
||||
<select class="span3" data-bind="options: $root.actionTypeOptions, value: actionType, optionsText: 'name', optionsValue: 'id'"></select>
|
||||
<div data-bind="template: {'name': actionTemplate()}, i18nUpdate: actionTemplate"></div>
|
||||
</div>
|
||||
<footer>
|
||||
<a class="btn buttonSave" data-bind="click: saveFilter" data-icon="✔" data-i18n="GLOBAL/DONE"></a>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!-- ko with: script -->
|
||||
<header>
|
||||
<header data-bind="i18nInit: true">
|
||||
<a href="#" class="close" data-bind="click: $root.close">×</a>
|
||||
<h3>
|
||||
<span data-i18n="POPUPS_SIEVE_SCRIPT/TITLE_CREATE" data-bind="visible: !exists()"></span>
|
||||
|
@ -7,7 +7,7 @@
|
|||
<span data-bind="visible: exists">: <!-- ko text: name--><!-- /ko --></span>
|
||||
</h3>
|
||||
</header>
|
||||
<div class="modal-body">
|
||||
<div class="modal-body" data-bind="i18nInit: true">
|
||||
|
||||
<div class="control-group" data-bind="css: {'error': nameError}, hidden: exists">
|
||||
<input type="text" class="span5"
|
||||
|
@ -60,7 +60,7 @@
|
|||
<a class="btn" data-bind="click: $root.addFilter" data-icon="✚" data-i18n="POPUPS_SIEVE_SCRIPT/BUTTON_ADD_FILTER"></a>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<footer data-bind="i18nInit: true">
|
||||
<a class="btn" data-bind="visible: $root.allowToggle, click: function() { $root.toggleFiltersRaw(); }, css: {'active': $root.rawActive }" data-i18n="[title]POPUPS_SIEVE_SCRIPT/BUTTON_RAW_SCRIPT">
|
||||
<i class="icon-file-code"></i>
|
||||
</a>
|
||||
|
|
|
@ -30,4 +30,4 @@
|
|||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a class="btn" data-bind="click: $root.addScript" data-icon="✚" data-i18n="SETTINGS_FILTERS/BUTTON_ADD_SCRIPT"></a>
|
||||
<a class="btn" data-bind="click: addScript" data-icon="✚" data-i18n="SETTINGS_FILTERS/BUTTON_ADD_SCRIPT"></a>
|
||||
|
|
|
@ -133,6 +133,6 @@ exports.jsLint = jsLint;
|
|||
exports.js = gulp.series(
|
||||
jsClean,
|
||||
jsLint,
|
||||
gulp.parallel(jsBoot, jsServiceWorker, jsOpenPGP, jsLibs/*, jsSieve*/, jsApp, jsAdmin),
|
||||
gulp.parallel(jsBoot, jsServiceWorker, jsOpenPGP, jsLibs, jsSieve, jsApp, jsAdmin),
|
||||
jsMin
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue