mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Improved knockout observables management to prevent memory leaks
This commit is contained in:
parent
b165a1de4f
commit
3eb6ab1ef7
46 changed files with 1020 additions and 1013 deletions
|
|
@ -1,5 +1,4 @@
|
||||||
import { ComposeType, FolderType } from 'Common/Enums';
|
import { ComposeType, FolderType } from 'Common/Enums';
|
||||||
import { pString } from 'Common/Utils';
|
|
||||||
|
|
||||||
const
|
const
|
||||||
tpl = document.createElement('template'),
|
tpl = document.createElement('template'),
|
||||||
|
|
@ -303,13 +302,13 @@ export function folderListOptionsBuilder(
|
||||||
|
|
||||||
bSep = true;
|
bSep = true;
|
||||||
aList.forEach(oItem => {
|
aList.forEach(oItem => {
|
||||||
// if (oItem.subScribed() || !oItem.existen || bBuildUnvisible)
|
// if (oItem.subscribed() || !oItem.exists || bBuildUnvisible)
|
||||||
if (
|
if (
|
||||||
(oItem.subScribed() || !oItem.existen || bBuildUnvisible) &&
|
(oItem.subscribed() || !oItem.exists || bBuildUnvisible) &&
|
||||||
(oItem.selectable || oItem.hasSubScribedSubfolders())
|
(oItem.selectable || oItem.hasSubscribedSubfolders())
|
||||||
) {
|
) {
|
||||||
if (fVisibleCallback ? fVisibleCallback(oItem) : true) {
|
if (fVisibleCallback ? fVisibleCallback(oItem) : true) {
|
||||||
if (FolderType.User === oItem.type() || !bSystem || oItem.hasSubScribedSubfolders()) {
|
if (FolderType.User === oItem.type() || !bSystem || oItem.hasSubscribedSubfolders()) {
|
||||||
aResult.push({
|
aResult.push({
|
||||||
id: oItem.fullNameRaw,
|
id: oItem.fullNameRaw,
|
||||||
name:
|
name:
|
||||||
|
|
@ -327,7 +326,7 @@ export function folderListOptionsBuilder(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oItem.subScribed() && oItem.subFolders().length) {
|
if (oItem.subscribed() && oItem.subFolders().length) {
|
||||||
aResult = aResult.concat(
|
aResult = aResult.concat(
|
||||||
folderListOptionsBuilder(
|
folderListOptionsBuilder(
|
||||||
[],
|
[],
|
||||||
|
|
@ -349,17 +348,14 @@ export function folderListOptionsBuilder(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Call the Model/CollectionModel onDestroy() to clear knockout functions/objects
|
||||||
* @param {Object|Array} objectOrObjects
|
* @param {Object|Array} objectOrObjects
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
export function delegateRunOnDestroy(objectOrObjects) {
|
export function delegateRunOnDestroy(objectOrObjects) {
|
||||||
if (objectOrObjects) {
|
objectOrObjects && (isArray(objectOrObjects) ? objectOrObjects : [objectOrObjects]).forEach(
|
||||||
if (isArray(objectOrObjects)) {
|
obj => obj.onDestroy && obj.onDestroy()
|
||||||
objectOrObjects.forEach(item => delegateRunOnDestroy(item));
|
);
|
||||||
} else {
|
|
||||||
objectOrObjects.onDestroy && objectOrObjects.onDestroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -448,14 +444,6 @@ export function computedPaginatorHelper(koCurrentPage, koPageCount) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} color
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
export function isTransparent(color) {
|
|
||||||
return 'rgba(0, 0, 0, 0)' === color || 'transparent' === color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} mailToUrl
|
* @param {string} mailToUrl
|
||||||
* @param {Function} PopupComposeViewModel
|
* @param {Function} PopupComposeViewModel
|
||||||
|
|
@ -524,8 +512,8 @@ export function mailToHelper(mailToUrl, PopupComposeViewModel) {
|
||||||
to,
|
to,
|
||||||
cc,
|
cc,
|
||||||
bcc,
|
bcc,
|
||||||
null == params.subject ? null : pString(decodeURIComponent(params.subject)),
|
null == params.subject ? null : decodeURIComponent(params.subject),
|
||||||
null == params.body ? null : plainToHtml(pString(decodeURIComponent(params.body)))
|
null == params.body ? null : plainToHtml(decodeURIComponent(params.body))
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
15
dev/External/ko.js
vendored
15
dev/External/ko.js
vendored
|
|
@ -262,4 +262,19 @@ ko.observable.fn.deleteAccessHelper = function() {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ko.addObservablesTo = (target, observables) => {
|
||||||
|
Object.entries(observables).forEach(([key, value]) => target[key] = ko.observable(value) );
|
||||||
|
/*
|
||||||
|
Object.entries(observables).forEach(([key, value]) =>
|
||||||
|
target[key] = Array.isArray(value) ? ko.observableArray(value) : ko.observable(value)
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
|
ko.addComputablesTo = (target, computables) =>
|
||||||
|
Object.entries(computables).forEach(([key, fn]) => target[key] = ko.computed(fn));
|
||||||
|
|
||||||
|
ko.addSubscribablesTo = (target, subscribables) =>
|
||||||
|
Object.entries(subscribables).forEach(([key, fn]) => target[key].subscribe(fn));
|
||||||
|
|
||||||
export default ko;
|
export default ko;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
function disposeOne(disposable) {
|
function dispose(disposable) {
|
||||||
if (disposable && 'function' === typeof disposable.dispose) {
|
if (disposable && 'function' === typeof disposable.dispose) {
|
||||||
disposable.dispose();
|
disposable.dispose();
|
||||||
}
|
}
|
||||||
|
|
@ -22,7 +22,7 @@ function typeCast(curValue, newValue) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AbstractModel {
|
export class AbstractModel {
|
||||||
disposables = [];
|
subscribables = [];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
/*
|
/*
|
||||||
|
|
@ -32,27 +32,30 @@ export class AbstractModel {
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
addObservables(obj) {
|
addObservables(observables) {
|
||||||
Object.entries(obj).forEach(([key, value]) => this[key] = ko.observable(value) );
|
ko.addObservablesTo(this, observables);
|
||||||
/*
|
|
||||||
Object.entries(obj).forEach(([key, value]) =>
|
|
||||||
this[key] = Array.isArray(value) ? ko.observableArray(value) : ko.observable(value)
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addComputables(obj) {
|
addComputables(computables) {
|
||||||
Object.entries(obj).forEach(([key, fn]) => this[key] = ko.computed(fn) );
|
ko.addComputablesTo(this, computables);
|
||||||
}
|
}
|
||||||
|
|
||||||
addSubscribables(obj) {
|
addSubscribables(subscribables) {
|
||||||
Object.entries(obj).forEach(([key, fn]) => this.disposables.push( this[key].subscribe(fn) ) );
|
Object.entries(subscribables).forEach(([key, fn]) => this.subscribables.push( this[key].subscribe(fn) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Called by delegateRunOnDestroy */
|
||||||
onDestroy() {
|
onDestroy() {
|
||||||
this.disposables.forEach(disposeOne);
|
/** clear ko.subscribe */
|
||||||
|
this.subscribables.forEach(dispose);
|
||||||
|
/** clear object entries */
|
||||||
Object.entries(this).forEach(([key, value]) => {
|
Object.entries(this).forEach(([key, value]) => {
|
||||||
disposeOne(value);
|
/** clear CollectionModel */
|
||||||
|
let arr = ko.isObservableArray(value) ? value() : value;
|
||||||
|
arr && arr.onDestroy && value.onDestroy();
|
||||||
|
/** destroy ko.observable/ko.computed? */
|
||||||
|
dispose(value);
|
||||||
|
/** clear object value */
|
||||||
this[key] = null;
|
this[key] = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,4 +56,16 @@ export class AbstractViewNext {
|
||||||
return this.viewModelDom.querySelector(selectors);
|
return this.viewModelDom.querySelector(selectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addObservables(observables) {
|
||||||
|
ko.addObservablesTo(this, observables);
|
||||||
|
}
|
||||||
|
|
||||||
|
addComputables(computables) {
|
||||||
|
ko.addComputablesTo(this, computables);
|
||||||
|
}
|
||||||
|
|
||||||
|
addSubscribables(subscribables) {
|
||||||
|
ko.addSubscribablesTo(this, subscribables);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,10 @@ export class AbstractCollectionModel extends Array
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDestroy() {
|
||||||
|
this.forEach(item => item.onDestroy && item.onDestroy());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @static
|
* @static
|
||||||
* @param {FetchJson} json
|
* @param {FetchJson} json
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ class FolderModel extends AbstractModel {
|
||||||
this.interval = 0;
|
this.interval = 0;
|
||||||
|
|
||||||
this.selectable = false;
|
this.selectable = false;
|
||||||
this.existen = true;
|
this.exists = true;
|
||||||
|
|
||||||
this.addObservables({
|
this.addObservables({
|
||||||
name: '',
|
name: '',
|
||||||
|
|
@ -29,7 +29,7 @@ class FolderModel extends AbstractModel {
|
||||||
focused: false,
|
focused: false,
|
||||||
selected: false,
|
selected: false,
|
||||||
edited: false,
|
edited: false,
|
||||||
subScribed: true,
|
subscribed: true,
|
||||||
checkable: false,
|
checkable: false,
|
||||||
deleteAccess: false,
|
deleteAccess: false,
|
||||||
|
|
||||||
|
|
@ -54,10 +54,6 @@ class FolderModel extends AbstractModel {
|
||||||
const folder = super.reviveFromJson(json);
|
const folder = super.reviveFromJson(json);
|
||||||
if (folder) {
|
if (folder) {
|
||||||
folder.deep = json.FullNameRaw.split(folder.delimiter).length - 1;
|
folder.deep = json.FullNameRaw.split(folder.delimiter).length - 1;
|
||||||
folder.selectable = !!json.IsSelectable;
|
|
||||||
folder.existen = !!json.IsExists;
|
|
||||||
|
|
||||||
folder.subScribed(!!json.IsSubscribed);
|
|
||||||
|
|
||||||
folder.messageCountAll = ko.computed({
|
folder.messageCountAll = ko.computed({
|
||||||
read: folder.privateMessageCountAll,
|
read: folder.privateMessageCountAll,
|
||||||
|
|
@ -87,26 +83,26 @@ class FolderModel extends AbstractModel {
|
||||||
|
|
||||||
isInbox: () => FolderType.Inbox === folder.type(),
|
isInbox: () => FolderType.Inbox === folder.type(),
|
||||||
|
|
||||||
hasSubScribedSubfolders:
|
hasSubscribedSubfolders:
|
||||||
() =>
|
() =>
|
||||||
!!folder.subFolders().find(
|
!!folder.subFolders().find(
|
||||||
oFolder => (oFolder.subScribed() || oFolder.hasSubScribedSubfolders()) && !oFolder.isSystemFolder()
|
oFolder => (oFolder.subscribed() || oFolder.hasSubscribedSubfolders()) && !oFolder.isSystemFolder()
|
||||||
),
|
),
|
||||||
|
|
||||||
canBeEdited: () => FolderType.User === folder.type() && folder.existen && folder.selectable,
|
canBeEdited: () => FolderType.User === folder.type() && folder.exists && folder.selectable,
|
||||||
|
|
||||||
visible: () => {
|
visible: () => {
|
||||||
const isSubScribed = folder.subScribed(),
|
const isSubscribed = folder.subscribed(),
|
||||||
isSubFolders = folder.hasSubScribedSubfolders();
|
isSubFolders = folder.hasSubscribedSubfolders();
|
||||||
|
|
||||||
return isSubScribed || (isSubFolders && (!folder.existen || !folder.selectable));
|
return isSubscribed || (isSubFolders && (!folder.exists || !folder.selectable));
|
||||||
},
|
},
|
||||||
|
|
||||||
isSystemFolder: () => FolderType.User !== folder.type(),
|
isSystemFolder: () => FolderType.User !== folder.type(),
|
||||||
|
|
||||||
hidden: () => {
|
hidden: () => {
|
||||||
const isSystem = folder.isSystemFolder(),
|
const isSystem = folder.isSystemFolder(),
|
||||||
isSubFolders = folder.hasSubScribedSubfolders();
|
isSubFolders = folder.hasSubscribedSubfolders();
|
||||||
|
|
||||||
return (isSystem && !isSubFolders) || (!folder.selectable && !isSubFolders);
|
return (isSystem && !isSubFolders) || (!folder.selectable && !isSubFolders);
|
||||||
},
|
},
|
||||||
|
|
@ -137,7 +133,7 @@ class FolderModel extends AbstractModel {
|
||||||
|
|
||||||
selectableForFolderList: () => !folder.isSystemFolder() && folder.selectable,
|
selectableForFolderList: () => !folder.isSystemFolder() && folder.selectable,
|
||||||
|
|
||||||
canBeSubScribed: () => !folder.isSystemFolder() && folder.selectable,
|
canBeSubscribed: () => !folder.isSystemFolder() && folder.selectable,
|
||||||
|
|
||||||
canBeChecked: () => !folder.isSystemFolder() && folder.selectable,
|
canBeChecked: () => !folder.isSystemFolder() && folder.selectable,
|
||||||
|
|
||||||
|
|
@ -221,9 +217,9 @@ class FolderModel extends AbstractModel {
|
||||||
|
|
||||||
hasUnreadMessages: () => 0 < folder.messageCountUnread() && folder.printableUnreadCount(),
|
hasUnreadMessages: () => 0 < folder.messageCountUnread() && folder.printableUnreadCount(),
|
||||||
|
|
||||||
hasSubScribedUnreadMessagesSubfolders: () =>
|
hasSubscribedUnreadMessagesSubfolders: () =>
|
||||||
!!folder.subFolders().find(
|
!!folder.subFolders().find(
|
||||||
folder => folder.hasUnreadMessages() || folder.hasSubScribedUnreadMessagesSubfolders()
|
folder => folder.hasUnreadMessages() || folder.hasSubscribedUnreadMessagesSubfolders()
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -246,7 +242,7 @@ class FolderModel extends AbstractModel {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
collapsedCss() {
|
collapsedCss() {
|
||||||
return 'e-collapsed-sign ' + (this.hasSubScribedSubfolders()
|
return 'e-collapsed-sign ' + (this.hasSubscribedSubfolders()
|
||||||
? (this.collapsed() ? 'icon-right-mini' : 'icon-down-mini')
|
? (this.collapsed() ? 'icon-right-mini' : 'icon-down-mini')
|
||||||
: 'icon-none'
|
: 'icon-none'
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -120,13 +120,13 @@ class FoldersUserSettings {
|
||||||
subscribeFolder(folder) {
|
subscribeFolder(folder) {
|
||||||
Local.set(ClientSideKeyName.FoldersLashHash, '');
|
Local.set(ClientSideKeyName.FoldersLashHash, '');
|
||||||
Remote.folderSetSubscribe(()=>{}, folder.fullNameRaw, true);
|
Remote.folderSetSubscribe(()=>{}, folder.fullNameRaw, true);
|
||||||
folder.subScribed(true);
|
folder.subscribed(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
unSubscribeFolder(folder) {
|
unSubscribeFolder(folder) {
|
||||||
Local.set(ClientSideKeyName.FoldersLashHash, '');
|
Local.set(ClientSideKeyName.FoldersLashHash, '');
|
||||||
Remote.folderSetSubscribe(()=>{}, folder.fullNameRaw, false);
|
Remote.folderSetSubscribe(()=>{}, folder.fullNameRaw, false);
|
||||||
folder.subScribed(false);
|
folder.subscribed(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkableTrueFolder(folder) {
|
checkableTrueFolder(folder) {
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,6 @@ class ThemesUserSettings {
|
||||||
|
|
||||||
this.themeTrigger = ko.observable(SaveSettingsStep.Idle).extend({ throttle: 100 });
|
this.themeTrigger = ko.observable(SaveSettingsStep.Idle).extend({ throttle: 100 });
|
||||||
|
|
||||||
this.iTimer = 0;
|
|
||||||
this.oThemeAjaxRequest = null;
|
|
||||||
|
|
||||||
this.theme.subscribe((value) => {
|
this.theme.subscribe((value) => {
|
||||||
this.themesObjects().forEach(theme => {
|
this.themesObjects().forEach(theme => {
|
||||||
theme.selected(value === theme.name);
|
theme.selected(value === theme.name);
|
||||||
|
|
|
||||||
|
|
@ -160,9 +160,9 @@ class FolderUserStore {
|
||||||
folder &&
|
folder &&
|
||||||
inboxFolderName !== folder.fullNameRaw &&
|
inboxFolderName !== folder.fullNameRaw &&
|
||||||
folder.selectable &&
|
folder.selectable &&
|
||||||
folder.existen &&
|
folder.exists &&
|
||||||
timeout > folder.interval &&
|
timeout > folder.interval &&
|
||||||
(folder.isSystemFolder() || (folder.subScribed() && folder.checkable()))
|
(folder.isSystemFolder() || (folder.subscribed() && folder.checkable()))
|
||||||
) {
|
) {
|
||||||
timeouts.push([folder.interval, folder.fullNameRaw]);
|
timeouts.push([folder.interval, folder.fullNameRaw]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,29 +23,33 @@ class LoginAdminView extends AbstractViewNext {
|
||||||
|
|
||||||
this.hideSubmitButton = appSettingsGet('hideSubmitButton');
|
this.hideSubmitButton = appSettingsGet('hideSubmitButton');
|
||||||
|
|
||||||
this.login = ko.observable('');
|
this.addObservables({
|
||||||
this.password = ko.observable('');
|
login: '',
|
||||||
|
password: '',
|
||||||
|
|
||||||
this.loginError = ko.observable(false);
|
loginError: false,
|
||||||
this.passwordError = ko.observable(false);
|
passwordError: false,
|
||||||
|
|
||||||
|
formHidden: false,
|
||||||
|
|
||||||
|
submitRequest: false,
|
||||||
|
submitError: ''
|
||||||
|
});
|
||||||
|
|
||||||
this.loginErrorAnimation = ko.observable(false).extend({ 'falseTimeout': 500 });
|
this.loginErrorAnimation = ko.observable(false).extend({ 'falseTimeout': 500 });
|
||||||
this.passwordErrorAnimation = ko.observable(false).extend({ 'falseTimeout': 500 });
|
this.passwordErrorAnimation = ko.observable(false).extend({ 'falseTimeout': 500 });
|
||||||
|
|
||||||
this.formHidden = ko.observable(false);
|
|
||||||
|
|
||||||
this.formError = ko.computed(() => this.loginErrorAnimation() || this.passwordErrorAnimation());
|
this.formError = ko.computed(() => this.loginErrorAnimation() || this.passwordErrorAnimation());
|
||||||
|
|
||||||
this.login.subscribe(() => this.loginError(false));
|
this.addSubscribables({
|
||||||
|
login: () => this.loginError(false),
|
||||||
|
|
||||||
this.password.subscribe(() => this.passwordError(false));
|
password: () => this.passwordError(false),
|
||||||
|
|
||||||
this.loginError.subscribe(v => this.loginErrorAnimation(!!v));
|
loginError: v => this.loginErrorAnimation(!!v),
|
||||||
|
|
||||||
this.passwordError.subscribe(v => this.passwordErrorAnimation(!!v));
|
passwordError: v => this.passwordErrorAnimation(!!v)
|
||||||
|
});
|
||||||
this.submitRequest = ko.observable(false);
|
|
||||||
this.submitError = ko.observable('');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@command((self) => !self.submitRequest())
|
@command((self) => !self.submitRequest())
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import ko from 'ko';
|
|
||||||
|
|
||||||
import { StorageResultType, Notification } from 'Common/Enums';
|
import { StorageResultType, Notification } from 'Common/Enums';
|
||||||
import { getNotification } from 'Common/Translator';
|
import { getNotification } from 'Common/Translator';
|
||||||
|
|
||||||
|
|
@ -16,27 +14,25 @@ class AccountPopupView extends AbstractViewNext {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.isNew = ko.observable(true);
|
this.addObservables({
|
||||||
|
isNew: true,
|
||||||
|
|
||||||
this.email = ko.observable('');
|
email: '',
|
||||||
this.password = ko.observable('');
|
password: '',
|
||||||
|
|
||||||
this.emailError = ko.observable(false);
|
emailError: false,
|
||||||
this.passwordError = ko.observable(false);
|
passwordError: false,
|
||||||
|
|
||||||
this.email.subscribe(() => {
|
submitRequest: false,
|
||||||
this.emailError(false);
|
submitError: '',
|
||||||
|
submitErrorAdditional: '',
|
||||||
|
|
||||||
|
emailFocus: false
|
||||||
});
|
});
|
||||||
|
|
||||||
this.password.subscribe(() => {
|
this.email.subscribe(() => this.emailError(false));
|
||||||
this.passwordError(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.submitRequest = ko.observable(false);
|
this.password.subscribe(() => this.passwordError(false));
|
||||||
this.submitError = ko.observable('');
|
|
||||||
this.submitErrorAdditional = ko.observable('');
|
|
||||||
|
|
||||||
this.emailFocus = ko.observable(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@command((self) => !self.submitRequest())
|
@command((self) => !self.submitRequest())
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import ko from 'ko';
|
|
||||||
|
|
||||||
import PgpStore from 'Stores/User/Pgp';
|
import PgpStore from 'Stores/User/Pgp';
|
||||||
|
|
||||||
import { popup, command } from 'Knoin/Knoin';
|
import { popup, command } from 'Knoin/Knoin';
|
||||||
|
|
@ -13,13 +11,15 @@ class AddOpenPgpKeyPopupView extends AbstractViewNext {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.key = ko.observable('');
|
this.addObservables({
|
||||||
this.key.error = ko.observable(false);
|
key: '',
|
||||||
this.key.errorMessage = ko.observable('');
|
keyError: false,
|
||||||
|
keyErrorMessage: ''
|
||||||
|
});
|
||||||
|
|
||||||
this.key.subscribe(() => {
|
this.key.subscribe(() => {
|
||||||
this.key.error(false);
|
this.keyError(false);
|
||||||
this.key.errorMessage('');
|
this.keyErrorMessage('');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,10 +35,10 @@ class AddOpenPgpKeyPopupView extends AbstractViewNext {
|
||||||
keyTrimmed = keyTrimmed.replace(/[\r]+/g, '').replace(/[\n]{2,}/g, '\n\n');
|
keyTrimmed = keyTrimmed.replace(/[\r]+/g, '').replace(/[\n]{2,}/g, '\n\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.key.error(!keyTrimmed);
|
this.keyError(!keyTrimmed);
|
||||||
this.key.errorMessage('');
|
this.keyErrorMessage('');
|
||||||
|
|
||||||
if (!openpgpKeyring || this.key.error()) {
|
if (!openpgpKeyring || this.keyError()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,8 +58,8 @@ class AddOpenPgpKeyPopupView extends AbstractViewNext {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
this.key.error(true);
|
this.keyError(true);
|
||||||
this.key.errorMessage(err && err[0] ? '' + err[0] : '');
|
this.keyErrorMessage(err && err[0] ? '' + err[0] : '');
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +75,7 @@ class AddOpenPgpKeyPopupView extends AbstractViewNext {
|
||||||
|
|
||||||
rl.app.reloadOpenPgpKeys();
|
rl.app.reloadOpenPgpKeys();
|
||||||
|
|
||||||
if (this.key.error()) {
|
if (this.keyError()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,8 +85,8 @@ class AddOpenPgpKeyPopupView extends AbstractViewNext {
|
||||||
|
|
||||||
clearPopup() {
|
clearPopup() {
|
||||||
this.key('');
|
this.key('');
|
||||||
this.key.error(false);
|
this.keyError(false);
|
||||||
this.key.errorMessage('');
|
this.keyErrorMessage('');
|
||||||
}
|
}
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,19 @@ class AdvancedSearchPopupView extends AbstractViewNext {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.fromFocus = ko.observable(false);
|
this.addObservables({
|
||||||
|
fromFocus: false,
|
||||||
|
|
||||||
this.from = ko.observable('');
|
from: '',
|
||||||
this.to = ko.observable('');
|
to: '',
|
||||||
this.subject = ko.observable('');
|
subject: '',
|
||||||
this.text = ko.observable('');
|
text: '',
|
||||||
this.selectedDateValue = ko.observable(-1);
|
selectedDateValue: -1,
|
||||||
|
|
||||||
this.hasAttachment = ko.observable(false);
|
hasAttachment: false,
|
||||||
this.starred = ko.observable(false);
|
starred: false,
|
||||||
this.unseen = ko.observable(false);
|
unseen: false
|
||||||
|
});
|
||||||
|
|
||||||
this.selectedDates = ko.computed(() => {
|
this.selectedDates = ko.computed(() => {
|
||||||
translatorTrigger();
|
translatorTrigger();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import ko from 'ko';
|
|
||||||
|
|
||||||
import { KeyState } from 'Common/Enums';
|
import { KeyState } from 'Common/Enums';
|
||||||
import { i18n } from 'Common/Translator';
|
import { i18n } from 'Common/Translator';
|
||||||
|
|
||||||
|
|
@ -14,9 +12,11 @@ class AskPopupView extends AbstractViewNext {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.askDesc = ko.observable('');
|
this.addObservables({
|
||||||
this.yesButton = ko.observable('');
|
askDesc: '',
|
||||||
this.noButton = ko.observable('');
|
yesButton: '',
|
||||||
|
noButton: ''
|
||||||
|
});
|
||||||
|
|
||||||
this.fYesAction = null;
|
this.fYesAction = null;
|
||||||
this.fNoAction = null;
|
this.fNoAction = null;
|
||||||
|
|
|
||||||
|
|
@ -132,150 +132,86 @@ class ComposePopupView extends AbstractViewNext {
|
||||||
|
|
||||||
this.capaOpenPGP = PgpStore.capaOpenPGP;
|
this.capaOpenPGP = PgpStore.capaOpenPGP;
|
||||||
|
|
||||||
this.identitiesDropdownTrigger = ko.observable(false);
|
this.identities = IdentityStore.identities;
|
||||||
|
|
||||||
this.to = ko.observable('');
|
this.addObservables({
|
||||||
this.to.focused = ko.observable(false);
|
identitiesDropdownTrigger: false,
|
||||||
this.cc = ko.observable('');
|
|
||||||
this.cc.focused = ko.observable(false);
|
to: '',
|
||||||
this.bcc = ko.observable('');
|
toFocused: false,
|
||||||
this.bcc.focused = ko.observable(false);
|
cc: '',
|
||||||
this.replyTo = ko.observable('');
|
ccFocused: false,
|
||||||
this.replyTo.focused = ko.observable(false);
|
bcc: '',
|
||||||
|
bccFocused: false,
|
||||||
|
replyTo: '',
|
||||||
|
replyToFocused: false,
|
||||||
|
|
||||||
|
subject: '',
|
||||||
|
subjectFocused: false,
|
||||||
|
|
||||||
|
isHtml: false,
|
||||||
|
|
||||||
|
requestDsn: false,
|
||||||
|
requestReadReceipt: false,
|
||||||
|
markAsImportant: false,
|
||||||
|
|
||||||
|
sendError: false,
|
||||||
|
sendSuccessButSaveError: false,
|
||||||
|
savedError: false,
|
||||||
|
|
||||||
|
sendErrorDesc: '',
|
||||||
|
savedErrorDesc: '',
|
||||||
|
|
||||||
|
savedTime: 0,
|
||||||
|
|
||||||
|
emptyToError: false,
|
||||||
|
|
||||||
|
attachmentsInProcessError: false,
|
||||||
|
attachmentsInErrorError: false,
|
||||||
|
|
||||||
|
showCc: false,
|
||||||
|
showBcc: false,
|
||||||
|
showReplyTo: false,
|
||||||
|
|
||||||
|
draftFolder: '',
|
||||||
|
draftUid: '',
|
||||||
|
sending: false,
|
||||||
|
saving: false,
|
||||||
|
|
||||||
|
attachmentsPlace: false,
|
||||||
|
|
||||||
|
composeUploaderButton: null,
|
||||||
|
composeUploaderDropPlace: null,
|
||||||
|
dragAndDropEnabled: false,
|
||||||
|
attacheMultipleAllowed: false,
|
||||||
|
addAttachmentEnabled: false,
|
||||||
|
|
||||||
|
composeEditorArea: null,
|
||||||
|
|
||||||
|
currentIdentity: this.identities()[0] ? this.identities()[0] : null
|
||||||
|
});
|
||||||
|
|
||||||
// this.to.subscribe((v) => console.log(v));
|
// this.to.subscribe((v) => console.log(v));
|
||||||
|
|
||||||
ko.computed(() => {
|
ko.computed(() => {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case this.to.focused():
|
case this.toFocused():
|
||||||
this.sLastFocusedField = 'to';
|
this.sLastFocusedField = 'to';
|
||||||
break;
|
break;
|
||||||
case this.cc.focused():
|
case this.ccFocused():
|
||||||
this.sLastFocusedField = 'cc';
|
this.sLastFocusedField = 'cc';
|
||||||
break;
|
break;
|
||||||
case this.bcc.focused():
|
case this.bccFocused():
|
||||||
this.sLastFocusedField = 'bcc';
|
this.sLastFocusedField = 'bcc';
|
||||||
break;
|
break;
|
||||||
// no default
|
// no default
|
||||||
}
|
}
|
||||||
}).extend({ notify: 'always' });
|
}).extend({ notify: 'always' });
|
||||||
|
|
||||||
this.subject = ko.observable('');
|
|
||||||
this.subject.focused = ko.observable(false);
|
|
||||||
|
|
||||||
this.isHtml = ko.observable(false);
|
|
||||||
|
|
||||||
this.requestDsn = ko.observable(false);
|
|
||||||
this.requestReadReceipt = ko.observable(false);
|
|
||||||
this.markAsImportant = ko.observable(false);
|
|
||||||
|
|
||||||
this.sendError = ko.observable(false);
|
|
||||||
this.sendSuccessButSaveError = ko.observable(false);
|
|
||||||
this.savedError = ko.observable(false);
|
|
||||||
|
|
||||||
this.sendButtonSuccess = ko.computed(() => !this.sendError() && !this.sendSuccessButSaveError());
|
|
||||||
|
|
||||||
this.sendErrorDesc = ko.observable('');
|
|
||||||
this.savedErrorDesc = ko.observable('');
|
|
||||||
|
|
||||||
this.sendError.subscribe(value => !value && this.sendErrorDesc(''));
|
|
||||||
|
|
||||||
this.savedError.subscribe(value => !value && this.savedErrorDesc(''));
|
|
||||||
|
|
||||||
this.sendSuccessButSaveError.subscribe(value => !value && this.savedErrorDesc(''));
|
|
||||||
|
|
||||||
this.savedTime = ko.observable(0);
|
|
||||||
this.savedTimeText = ko.computed(() =>
|
|
||||||
this.savedTime()
|
|
||||||
? i18n('COMPOSE/SAVED_TIME', { 'TIME': this.savedTime().format('LT') })
|
|
||||||
: ''
|
|
||||||
);
|
|
||||||
|
|
||||||
this.emptyToError = ko.observable(false);
|
|
||||||
this.emptyToErrorTooltip = ko.computed(() => (this.emptyToError() ? i18n('COMPOSE/EMPTY_TO_ERROR_DESC') : ''));
|
|
||||||
|
|
||||||
this.attachmentsInProcessError = ko.observable(false);
|
|
||||||
this.attachmentsInErrorError = ko.observable(false);
|
|
||||||
|
|
||||||
this.attachmentsErrorTooltip = ko.computed(() => {
|
|
||||||
let result = '';
|
|
||||||
switch (true) {
|
|
||||||
case this.attachmentsInProcessError():
|
|
||||||
result = i18n('COMPOSE/ATTACHMENTS_UPLOAD_ERROR_DESC');
|
|
||||||
break;
|
|
||||||
case this.attachmentsInErrorError():
|
|
||||||
result = i18n('COMPOSE/ATTACHMENTS_ERROR_DESC');
|
|
||||||
break;
|
|
||||||
// no default
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.showCc = ko.observable(false);
|
|
||||||
this.showBcc = ko.observable(false);
|
|
||||||
this.showReplyTo = ko.observable(false);
|
|
||||||
|
|
||||||
this.cc.subscribe((value) => {
|
|
||||||
if (false === this.showCc() && value.length) {
|
|
||||||
this.showCc(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.bcc.subscribe((value) => {
|
|
||||||
if (false === this.showBcc() && value.length) {
|
|
||||||
this.showBcc(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.replyTo.subscribe((value) => {
|
|
||||||
if (false === this.showReplyTo() && value.length) {
|
|
||||||
this.showReplyTo(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.draftFolder = ko.observable('');
|
|
||||||
this.draftUid = ko.observable('');
|
|
||||||
this.sending = ko.observable(false);
|
|
||||||
this.saving = ko.observable(false);
|
|
||||||
this.attachments = ko.observableArray([]);
|
this.attachments = ko.observableArray([]);
|
||||||
|
|
||||||
this.attachmentsInProcess = ko.computed(() => this.attachments().filter(item => item && !item.complete()));
|
|
||||||
this.attachmentsInReady = ko.computed(() => this.attachments().filter(item => item && item.complete()));
|
|
||||||
this.attachmentsInError = ko.computed(() => this.attachments().filter(item => item && item.error()));
|
|
||||||
|
|
||||||
this.attachmentsCount = ko.computed(() => this.attachments().length);
|
|
||||||
this.attachmentsInErrorCount = ko.computed(() => this.attachmentsInError().length);
|
|
||||||
this.attachmentsInProcessCount = ko.computed(() => this.attachmentsInProcess().length);
|
|
||||||
this.isDraftFolderMessage = ko.computed(() => this.draftFolder() && this.draftUid());
|
|
||||||
|
|
||||||
this.attachmentsPlace = ko.observable(false);
|
|
||||||
|
|
||||||
this.attachmentsInErrorCount.subscribe((value) => {
|
|
||||||
if (0 === value) {
|
|
||||||
this.attachmentsInErrorError(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.composeUploaderButton = ko.observable(null);
|
|
||||||
this.composeUploaderDropPlace = ko.observable(null);
|
|
||||||
this.dragAndDropEnabled = ko.observable(false);
|
|
||||||
this.dragAndDropOver = ko.observable(false).extend({ throttle: 1 });
|
this.dragAndDropOver = ko.observable(false).extend({ throttle: 1 });
|
||||||
this.dragAndDropVisible = ko.observable(false).extend({ throttle: 1 });
|
this.dragAndDropVisible = ko.observable(false).extend({ throttle: 1 });
|
||||||
this.attacheMultipleAllowed = ko.observable(false);
|
|
||||||
this.addAttachmentEnabled = ko.observable(false);
|
|
||||||
|
|
||||||
this.composeEditorArea = ko.observable(null);
|
|
||||||
|
|
||||||
this.identities = IdentityStore.identities;
|
|
||||||
this.identitiesOptions = ko.computed(() =>
|
|
||||||
IdentityStore.identities().map(item => ({
|
|
||||||
'item': item,
|
|
||||||
'optValue': item.id(),
|
|
||||||
'optText': item.formattedName()
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
|
|
||||||
this.currentIdentity = ko.observable(this.identities()[0] ? this.identities()[0] : null);
|
|
||||||
|
|
||||||
this.currentIdentity.extend({
|
this.currentIdentity.extend({
|
||||||
toggleSubscribe: [
|
toggleSubscribe: [
|
||||||
|
|
@ -291,24 +227,105 @@ class ComposePopupView extends AbstractViewNext {
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
this.currentIdentityView = ko.computed(() => {
|
this.bDisabeCloseOnEsc = true;
|
||||||
|
this.sDefaultKeyScope = KeyState.Compose;
|
||||||
|
|
||||||
|
this.tryToClosePopup = this.tryToClosePopup.debounce(200);
|
||||||
|
|
||||||
|
this.iTimer = 0;
|
||||||
|
|
||||||
|
this.addComputables({
|
||||||
|
sendButtonSuccess: () => !this.sendError() && !this.sendSuccessButSaveError(),
|
||||||
|
|
||||||
|
savedTimeText: () =>
|
||||||
|
this.savedTime() ? i18n('COMPOSE/SAVED_TIME', { 'TIME': this.savedTime().format('LT') }) : '',
|
||||||
|
|
||||||
|
emptyToErrorTooltip: () => (this.emptyToError() ? i18n('COMPOSE/EMPTY_TO_ERROR_DESC') : ''),
|
||||||
|
|
||||||
|
attachmentsErrorTooltip: () => {
|
||||||
|
let result = '';
|
||||||
|
switch (true) {
|
||||||
|
case this.attachmentsInProcessError():
|
||||||
|
result = i18n('COMPOSE/ATTACHMENTS_UPLOAD_ERROR_DESC');
|
||||||
|
break;
|
||||||
|
case this.attachmentsInErrorError():
|
||||||
|
result = i18n('COMPOSE/ATTACHMENTS_ERROR_DESC');
|
||||||
|
break;
|
||||||
|
// no default
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
attachmentsInProcess: () => this.attachments().filter(item => item && !item.complete()),
|
||||||
|
attachmentsInReady: () => this.attachments().filter(item => item && item.complete()),
|
||||||
|
attachmentsInError: () => this.attachments().filter(item => item && item.error()),
|
||||||
|
|
||||||
|
attachmentsCount: () => this.attachments().length,
|
||||||
|
attachmentsInErrorCount: () => this.attachmentsInError().length,
|
||||||
|
attachmentsInProcessCount: () => this.attachmentsInProcess().length,
|
||||||
|
isDraftFolderMessage: () => this.draftFolder() && this.draftUid(),
|
||||||
|
|
||||||
|
identitiesOptions: () =>
|
||||||
|
IdentityStore.identities().map(item => ({
|
||||||
|
'item': item,
|
||||||
|
'optValue': item.id(),
|
||||||
|
'optText': item.formattedName()
|
||||||
|
})),
|
||||||
|
|
||||||
|
currentIdentityView: () => {
|
||||||
const item = this.currentIdentity();
|
const item = this.currentIdentity();
|
||||||
return item ? item.formattedName() : 'unknown';
|
return item ? item.formattedName() : 'unknown';
|
||||||
|
},
|
||||||
|
|
||||||
|
canBeSentOrSaved: () => !this.sending() && !this.saving()
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.to.subscribe((value) => {
|
this.addSubscribables({
|
||||||
|
sendError: value => !value && this.sendErrorDesc(''),
|
||||||
|
|
||||||
|
savedError: value => !value && this.savedErrorDesc(''),
|
||||||
|
|
||||||
|
sendSuccessButSaveError: value => !value && this.savedErrorDesc(''),
|
||||||
|
|
||||||
|
cc: value => {
|
||||||
|
if (false === this.showCc() && value.length) {
|
||||||
|
this.showCc(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
bcc: value => {
|
||||||
|
if (false === this.showBcc() && value.length) {
|
||||||
|
this.showBcc(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
replyTo: value => {
|
||||||
|
if (false === this.showReplyTo() && value.length) {
|
||||||
|
this.showReplyTo(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
attachmentsInErrorCount: value => {
|
||||||
|
if (0 === value) {
|
||||||
|
this.attachmentsInErrorError(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
to: value => {
|
||||||
if (this.emptyToError() && value.length) {
|
if (this.emptyToError() && value.length) {
|
||||||
this.emptyToError(false);
|
this.emptyToError(false);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
|
||||||
this.attachmentsInProcess.subscribe(value => {
|
attachmentsInProcess: value => {
|
||||||
if (this.attachmentsInProcessError() && Array.isNotEmpty(value)) {
|
if (this.attachmentsInProcessError() && Array.isNotEmpty(value)) {
|
||||||
this.attachmentsInProcessError(false);
|
this.attachmentsInProcessError(false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.canBeSentOrSaved = ko.computed(() => !this.sending() && !this.saving());
|
this.resizeObserver = new ResizeObserver(this.resizerTrigger.throttle(50).bind(this));
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
if (
|
if (
|
||||||
|
|
@ -323,15 +340,6 @@ class ComposePopupView extends AbstractViewNext {
|
||||||
this.saveCommand();
|
this.saveCommand();
|
||||||
}
|
}
|
||||||
}, 120000);
|
}, 120000);
|
||||||
|
|
||||||
this.bDisabeCloseOnEsc = true;
|
|
||||||
this.sDefaultKeyScope = KeyState.Compose;
|
|
||||||
|
|
||||||
this.tryToClosePopup = this.tryToClosePopup.debounce(200);
|
|
||||||
|
|
||||||
this.iTimer = 0;
|
|
||||||
|
|
||||||
this.resizeObserver = new ResizeObserver(this.resizerTrigger.throttle(50).bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getMessageRequestParams(sSaveFolder)
|
getMessageRequestParams(sSaveFolder)
|
||||||
|
|
@ -677,7 +685,7 @@ class ComposePopupView extends AbstractViewNext {
|
||||||
|
|
||||||
this.bSkipNextHide = false;
|
this.bSkipNextHide = false;
|
||||||
|
|
||||||
this.to.focused(false);
|
this.toFocused(false);
|
||||||
|
|
||||||
rl.route.on();
|
rl.route.on();
|
||||||
|
|
||||||
|
|
@ -1120,9 +1128,9 @@ class ComposePopupView extends AbstractViewNext {
|
||||||
// rl.settings.app('mobile') ||
|
// rl.settings.app('mobile') ||
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!this.to()) {
|
if (!this.to()) {
|
||||||
this.to.focused(true);
|
this.toFocused(true);
|
||||||
} else if (this.oEditor) {
|
} else if (this.oEditor) {
|
||||||
if (!this.to.focused()) {
|
if (!this.toFocused()) {
|
||||||
this.oEditor.focus();
|
this.oEditor.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1222,10 +1230,7 @@ class ComposePopupView extends AbstractViewNext {
|
||||||
if (attachment) {
|
if (attachment) {
|
||||||
this.attachments.remove(attachment);
|
this.attachments.remove(attachment);
|
||||||
delegateRunOnDestroy(attachment);
|
delegateRunOnDestroy(attachment);
|
||||||
|
oJua && oJua.cancel(id);
|
||||||
if (oJua) {
|
|
||||||
oJua.cancel(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,25 +25,28 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
|
||||||
this.publicKeysOptionsCaption = i18n('PGP_NOTIFICATIONS/ADD_A_PUBLICK_KEY');
|
this.publicKeysOptionsCaption = i18n('PGP_NOTIFICATIONS/ADD_A_PUBLICK_KEY');
|
||||||
this.privateKeysOptionsCaption = i18n('PGP_NOTIFICATIONS/SELECT_A_PRIVATE_KEY');
|
this.privateKeysOptionsCaption = i18n('PGP_NOTIFICATIONS/SELECT_A_PRIVATE_KEY');
|
||||||
|
|
||||||
this.notification = ko.observable('');
|
this.addObservables({
|
||||||
|
notification: '',
|
||||||
|
|
||||||
this.sign = ko.observable(false);
|
sign: false,
|
||||||
this.encrypt = ko.observable(false);
|
encrypt: false,
|
||||||
|
|
||||||
this.password = ko.observable('');
|
password: '',
|
||||||
|
|
||||||
this.text = ko.observable('');
|
text: '',
|
||||||
this.selectedPrivateKey = ko.observable(null);
|
selectedPrivateKey: null,
|
||||||
this.selectedPublicKey = ko.observable(null);
|
selectedPublicKey: null,
|
||||||
|
|
||||||
this.signKey = ko.observable(null);
|
signKey: null,
|
||||||
|
|
||||||
|
submitRequest: false
|
||||||
|
});
|
||||||
this.encryptKeys = ko.observableArray([]);
|
this.encryptKeys = ko.observableArray([]);
|
||||||
|
|
||||||
this.encryptKeysView = ko.computed(
|
this.addComputables({
|
||||||
() => this.encryptKeys().map(oKey => (oKey ? oKey.key : null)).filter(v => v)
|
encryptKeysView: () => this.encryptKeys().map(oKey => (oKey ? oKey.key : null)).filter(v => v),
|
||||||
);
|
|
||||||
|
|
||||||
this.privateKeysOptions = ko.computed(() => {
|
privateKeysOptions: () => {
|
||||||
const opts = PgpStore.openpgpkeysPrivate().map((oKey, iIndex) => {
|
const opts = PgpStore.openpgpkeysPrivate().map((oKey, iIndex) => {
|
||||||
if (this.signKey() && this.signKey().key.id === oKey.id) {
|
if (this.signKey() && this.signKey().key.id === oKey.id) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -57,9 +60,9 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
|
||||||
});
|
});
|
||||||
|
|
||||||
return opts.flat().filter(v => v);
|
return opts.flat().filter(v => v);
|
||||||
});
|
},
|
||||||
|
|
||||||
this.publicKeysOptions = ko.computed(() => {
|
publicKeysOptions: () => {
|
||||||
const opts = PgpStore.openpgpkeysPublic().map((oKey, index) => {
|
const opts = PgpStore.openpgpkeysPublic().map((oKey, index) => {
|
||||||
if (this.encryptKeysView().includes(oKey)) {
|
if (this.encryptKeysView().includes(oKey)) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -72,10 +75,9 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
return opts.flat().filter(v => v);
|
return opts.flat().filter(v => v);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.submitRequest = ko.observable(false);
|
|
||||||
|
|
||||||
this.resultCallback = null;
|
this.resultCallback = null;
|
||||||
|
|
||||||
this.selectedPrivateKey.subscribe((value) => {
|
this.selectedPrivateKey.subscribe((value) => {
|
||||||
|
|
|
||||||
|
|
@ -46,81 +46,36 @@ class ContactsPopupView extends AbstractViewNext {
|
||||||
this.allowContactsSync = ContactStore.allowContactsSync;
|
this.allowContactsSync = ContactStore.allowContactsSync;
|
||||||
this.enableContactsSync = ContactStore.enableContactsSync;
|
this.enableContactsSync = ContactStore.enableContactsSync;
|
||||||
|
|
||||||
this.search = ko.observable('');
|
this.addObservables({
|
||||||
this.contactsCount = ko.observable(0);
|
search: '',
|
||||||
this.contacts = ContactStore.contacts;
|
contactsCount: 0,
|
||||||
|
|
||||||
this.currentContact = ko.observable(null);
|
currentContact: null,
|
||||||
|
|
||||||
this.importUploaderButton = ko.observable(null);
|
importUploaderButton: null,
|
||||||
|
|
||||||
this.contactsPage = ko.observable(1);
|
contactsPage: 1,
|
||||||
this.contactsPageCount = ko.computed(() =>
|
|
||||||
Math.max(1, Math.ceil(this.contactsCount() / CONTACTS_PER_PAGE))
|
|
||||||
);
|
|
||||||
|
|
||||||
this.contactsPaginator = ko.computed(computedPaginatorHelper(this.contactsPage, this.contactsPageCount));
|
emptySelection: true,
|
||||||
|
viewClearSearch: false,
|
||||||
|
|
||||||
this.emptySelection = ko.observable(true);
|
viewID: '',
|
||||||
this.viewClearSearch = ko.observable(false);
|
viewReadOnly: false,
|
||||||
|
|
||||||
this.viewID = ko.observable('');
|
viewSaveTrigger: SaveSettingsStep.Idle,
|
||||||
this.viewReadOnly = ko.observable(false);
|
|
||||||
this.viewProperties = ko.observableArray([]);
|
|
||||||
|
|
||||||
this.viewSaveTrigger = ko.observable(SaveSettingsStep.Idle);
|
viewSaving: false,
|
||||||
|
|
||||||
this.viewPropertiesNames = ko.computed(() =>
|
watchDirty: false,
|
||||||
this.viewProperties().filter(
|
watchHash: false
|
||||||
property => [ContactPropertyType.FirstName, ContactPropertyType.LastName].includes(property.type())
|
|
||||||
)
|
|
||||||
);
|
|
||||||
this.viewPropertiesOther = ko.computed(() =>
|
|
||||||
this.viewProperties().filter(property => [ContactPropertyType.Nick].includes(property.type()))
|
|
||||||
);
|
|
||||||
|
|
||||||
this.viewPropertiesEmails = ko.computed(() =>
|
|
||||||
this.viewProperties().filter(property => ContactPropertyType.Email === property.type())
|
|
||||||
);
|
|
||||||
|
|
||||||
this.viewPropertiesWeb = ko.computed(() =>
|
|
||||||
this.viewProperties().filter(property => ContactPropertyType.Web === property.type())
|
|
||||||
);
|
|
||||||
|
|
||||||
this.viewHasNonEmptyRequiredProperties = ko.computed(() => {
|
|
||||||
const names = this.viewPropertiesNames(),
|
|
||||||
emails = this.viewPropertiesEmails(),
|
|
||||||
fFilter = property => !!trim(property.value());
|
|
||||||
|
|
||||||
return !!(names.find(fFilter) || emails.find(fFilter));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.viewPropertiesPhones = ko.computed(() =>
|
this.contacts = ContactStore.contacts;
|
||||||
this.viewProperties().filter(property => ContactPropertyType.Phone === property.type())
|
|
||||||
);
|
|
||||||
|
|
||||||
this.viewPropertiesEmailsNonEmpty = ko.computed(() =>
|
this.viewProperties = ko.observableArray([]);
|
||||||
this.viewPropertiesNames().filter(property => !!trim(property.value()))
|
|
||||||
);
|
|
||||||
|
|
||||||
const propertyFocused = property => !trim(property.value()) && !property.focused();
|
const propertyFocused = property => !trim(property.value()) && !property.focused();
|
||||||
|
|
||||||
this.viewPropertiesEmailsEmptyAndOnFocused = ko.computed(() =>
|
|
||||||
this.viewPropertiesEmails().filter(propertyFocused)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.viewPropertiesPhonesEmptyAndOnFocused = ko.computed(() =>
|
|
||||||
this.viewPropertiesPhones().filter(propertyFocused)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.viewPropertiesWebEmptyAndOnFocused = ko.computed(() =>
|
|
||||||
this.viewPropertiesWeb().filter(propertyFocused)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.viewPropertiesOtherEmptyAndOnFocused = ko.computed(() =>
|
|
||||||
this.viewPropertiesOther().filter(propertyFocused)
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// Somehow this is broken now when calling addNewProperty
|
// Somehow this is broken now when calling addNewProperty
|
||||||
const fFastClearEmptyListHelper = list => {
|
const fFastClearEmptyListHelper = list => {
|
||||||
|
|
@ -129,33 +84,18 @@ class ContactsPopupView extends AbstractViewNext {
|
||||||
delegateRunOnDestroy(list);
|
delegateRunOnDestroy(list);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.viewPropertiesEmailsEmptyAndOnFocused.subscribe(fFastClearEmptyListHelper);
|
this.addSubscribables({
|
||||||
this.viewPropertiesPhonesEmptyAndOnFocused.subscribe(fFastClearEmptyListHelper);
|
viewPropertiesEmailsEmptyAndOnFocused: fFastClearEmptyListHelper,
|
||||||
this.viewPropertiesWebEmptyAndOnFocused.subscribe(fFastClearEmptyListHelper);
|
viewPropertiesPhonesEmptyAndOnFocused: fFastClearEmptyListHelper,
|
||||||
this.viewPropertiesOtherEmptyAndOnFocused.subscribe(fFastClearEmptyListHelper);
|
viewPropertiesWebEmptyAndOnFocused: fFastClearEmptyListHelper,
|
||||||
|
viewPropertiesOtherEmptyAndOnFocused: fFastClearEmptyListHelper
|
||||||
|
});
|
||||||
*/
|
*/
|
||||||
|
|
||||||
this.viewSaving = ko.observable(false);
|
|
||||||
|
|
||||||
this.useCheckboxesInList = SettingsStore.useCheckboxesInList;
|
this.useCheckboxesInList = SettingsStore.useCheckboxesInList;
|
||||||
|
|
||||||
this.search.subscribe(() => this.reloadContactList());
|
this.search.subscribe(() => this.reloadContactList());
|
||||||
|
|
||||||
this.contactsChecked = ko.computed(() => this.contacts().filter(item => item.checked()));
|
|
||||||
|
|
||||||
this.contactsCheckedOrSelected = ko.computed(() => {
|
|
||||||
const checked = this.contactsChecked(),
|
|
||||||
selected = this.currentContact();
|
|
||||||
|
|
||||||
return selected
|
|
||||||
? checked.concat([selected]).unique()
|
|
||||||
: checked;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.contactsCheckedOrSelectedUids = ko.computed(() =>
|
|
||||||
this.contactsCheckedOrSelected().map(contact => contact.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.selector = new Selector(
|
this.selector = new Selector(
|
||||||
this.contacts,
|
this.contacts,
|
||||||
this.currentContact,
|
this.currentContact,
|
||||||
|
|
@ -177,11 +117,6 @@ class ContactsPopupView extends AbstractViewNext {
|
||||||
|
|
||||||
this.bDropPageAfterDelete = false;
|
this.bDropPageAfterDelete = false;
|
||||||
|
|
||||||
this.watchDirty = ko.observable(false);
|
|
||||||
this.watchHash = ko.observable(false);
|
|
||||||
|
|
||||||
this.viewHash = ko.computed(() => '' + this.viewProperties().map(oItem => oItem.value()).join(''));
|
|
||||||
|
|
||||||
// this.saveCommandDebounce = _.debounce(this.saveCommand.bind(this), 1000);
|
// this.saveCommandDebounce = _.debounce(this.saveCommand.bind(this), 1000);
|
||||||
|
|
||||||
this.viewHash.subscribe(() => {
|
this.viewHash.subscribe(() => {
|
||||||
|
|
@ -191,6 +126,61 @@ class ContactsPopupView extends AbstractViewNext {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.sDefaultKeyScope = KeyState.ContactList;
|
this.sDefaultKeyScope = KeyState.ContactList;
|
||||||
|
|
||||||
|
this.addComputables({
|
||||||
|
contactsPageCount: () => Math.max(1, Math.ceil(this.contactsCount() / CONTACTS_PER_PAGE)),
|
||||||
|
|
||||||
|
contactsPaginator: computedPaginatorHelper(this.contactsPage, this.contactsPageCount),
|
||||||
|
|
||||||
|
viewPropertiesNames: () =>
|
||||||
|
this.viewProperties().filter(
|
||||||
|
property => [ContactPropertyType.FirstName, ContactPropertyType.LastName].includes(property.type())
|
||||||
|
),
|
||||||
|
|
||||||
|
viewPropertiesOther: () =>
|
||||||
|
this.viewProperties().filter(property => [ContactPropertyType.Nick].includes(property.type())),
|
||||||
|
|
||||||
|
viewPropertiesEmails: () =>
|
||||||
|
this.viewProperties().filter(property => ContactPropertyType.Email === property.type()),
|
||||||
|
|
||||||
|
viewPropertiesWeb: () => this.viewProperties().filter(property => ContactPropertyType.Web === property.type()),
|
||||||
|
|
||||||
|
viewHasNonEmptyRequiredProperties: () => {
|
||||||
|
const names = this.viewPropertiesNames(),
|
||||||
|
emails = this.viewPropertiesEmails(),
|
||||||
|
fFilter = property => !!trim(property.value());
|
||||||
|
|
||||||
|
return !!(names.find(fFilter) || emails.find(fFilter));
|
||||||
|
},
|
||||||
|
|
||||||
|
viewPropertiesPhones: () =>
|
||||||
|
this.viewProperties().filter(property => ContactPropertyType.Phone === property.type()),
|
||||||
|
|
||||||
|
viewPropertiesEmailsNonEmpty: () => this.viewPropertiesNames().filter(property => !!trim(property.value())),
|
||||||
|
|
||||||
|
viewPropertiesEmailsEmptyAndOnFocused: () => this.viewPropertiesEmails().filter(propertyFocused),
|
||||||
|
|
||||||
|
viewPropertiesPhonesEmptyAndOnFocused: () => this.viewPropertiesPhones().filter(propertyFocused),
|
||||||
|
|
||||||
|
viewPropertiesWebEmptyAndOnFocused: () => this.viewPropertiesWeb().filter(propertyFocused),
|
||||||
|
|
||||||
|
viewPropertiesOtherEmptyAndOnFocused: () => this.viewPropertiesOther().filter(propertyFocused),
|
||||||
|
|
||||||
|
contactsChecked: () => this.contacts().filter(item => item.checked()),
|
||||||
|
|
||||||
|
contactsCheckedOrSelected: () => {
|
||||||
|
const checked = this.contactsChecked(),
|
||||||
|
selected = this.currentContact();
|
||||||
|
|
||||||
|
return selected
|
||||||
|
? checked.concat([selected]).unique()
|
||||||
|
: checked;
|
||||||
|
},
|
||||||
|
|
||||||
|
contactsCheckedOrSelectedUids: () => this.contactsCheckedOrSelected().map(contact => contact.id),
|
||||||
|
|
||||||
|
viewHash: () => '' + this.viewProperties().map(oItem => oItem.value()).join('')
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@command()
|
@command()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import ko from 'ko';
|
|
||||||
|
|
||||||
import { StorageResultType, ServerSecure, Notification } from 'Common/Enums';
|
import { StorageResultType, ServerSecure, Notification } from 'Common/Enums';
|
||||||
import { pInt, pString } from 'Common/Utils';
|
import { pInt, pString } from 'Common/Utils';
|
||||||
import { i18n } from 'Common/Translator';
|
import { i18n } from 'Common/Translator';
|
||||||
|
|
@ -19,113 +17,67 @@ class DomainPopupView extends AbstractViewNext {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.edit = ko.observable(false);
|
this.addObservables({
|
||||||
this.saving = ko.observable(false);
|
edit: false,
|
||||||
this.savingError = ko.observable('');
|
saving: false,
|
||||||
this.page = ko.observable('main');
|
savingError: '',
|
||||||
this.sieveSettings = ko.observable(false);
|
page: 'main',
|
||||||
|
sieveSettings: false,
|
||||||
|
|
||||||
this.testing = ko.observable(false);
|
testing: false,
|
||||||
this.testingDone = ko.observable(false);
|
testingDone: false,
|
||||||
this.testingImapError = ko.observable(false);
|
testingImapError: false,
|
||||||
this.testingSieveError = ko.observable(false);
|
testingSieveError: false,
|
||||||
this.testingSmtpError = ko.observable(false);
|
testingSmtpError: false,
|
||||||
this.testingImapErrorDesc = ko.observable('');
|
testingImapErrorDesc: '',
|
||||||
this.testingSieveErrorDesc = ko.observable('');
|
testingSieveErrorDesc: '',
|
||||||
this.testingSmtpErrorDesc = ko.observable('');
|
testingSmtpErrorDesc: '',
|
||||||
|
|
||||||
this.testingImapError.subscribe(value => value || this.testingImapErrorDesc(''));
|
imapServerFocus: false,
|
||||||
|
sieveServerFocus: false,
|
||||||
|
smtpServerFocus: false,
|
||||||
|
|
||||||
this.testingSieveError.subscribe(value => value || this.testingSieveErrorDesc(''));
|
name: '',
|
||||||
|
|
||||||
this.testingSmtpError.subscribe(value => value || this.testingSmtpErrorDesc(''));
|
imapServer: '',
|
||||||
|
imapPort: '143',
|
||||||
|
imapSecure: ServerSecure.None,
|
||||||
|
imapShortLogin: false,
|
||||||
|
useSieve: false,
|
||||||
|
sieveAllowRaw: false,
|
||||||
|
sieveServer: '',
|
||||||
|
sievePort: '4190',
|
||||||
|
sieveSecure: ServerSecure.None,
|
||||||
|
smtpServer: '',
|
||||||
|
smtpPort: '25',
|
||||||
|
smtpSecure: ServerSecure.None,
|
||||||
|
smtpShortLogin: false,
|
||||||
|
smtpAuth: true,
|
||||||
|
smtpPhpMail: false,
|
||||||
|
whiteList: '',
|
||||||
|
aliasName: '',
|
||||||
|
|
||||||
this.imapServerFocus = ko.observable(false);
|
enableSmartPorts: false
|
||||||
this.sieveServerFocus = ko.observable(false);
|
|
||||||
this.smtpServerFocus = ko.observable(false);
|
|
||||||
|
|
||||||
this.name = ko.observable('');
|
|
||||||
|
|
||||||
this.imapServer = ko.observable('');
|
|
||||||
this.imapPort = ko.observable('143');
|
|
||||||
this.imapSecure = ko.observable(ServerSecure.None);
|
|
||||||
this.imapShortLogin = ko.observable(false);
|
|
||||||
this.useSieve = ko.observable(false);
|
|
||||||
this.sieveAllowRaw = ko.observable(false);
|
|
||||||
this.sieveServer = ko.observable('');
|
|
||||||
this.sievePort = ko.observable('4190');
|
|
||||||
this.sieveSecure = ko.observable(ServerSecure.None);
|
|
||||||
this.smtpServer = ko.observable('');
|
|
||||||
this.smtpPort = ko.observable('25');
|
|
||||||
this.smtpSecure = ko.observable(ServerSecure.None);
|
|
||||||
this.smtpShortLogin = ko.observable(false);
|
|
||||||
this.smtpAuth = ko.observable(true);
|
|
||||||
this.smtpPhpMail = ko.observable(false);
|
|
||||||
this.whiteList = ko.observable('');
|
|
||||||
this.aliasName = ko.observable('');
|
|
||||||
|
|
||||||
this.enableSmartPorts = ko.observable(false);
|
|
||||||
|
|
||||||
this.allowSieve = ko.computed(() => CapaAdminStore.filters() && CapaAdminStore.sieve());
|
|
||||||
|
|
||||||
this.headerText = ko.computed(() => {
|
|
||||||
const name = this.name(),
|
|
||||||
aliasName = this.aliasName();
|
|
||||||
|
|
||||||
let result = '';
|
|
||||||
|
|
||||||
if (this.edit()) {
|
|
||||||
result = i18n('POPUPS_DOMAIN/TITLE_EDIT_DOMAIN', { 'NAME': name });
|
|
||||||
if (aliasName) {
|
|
||||||
result += ' ← ' + aliasName;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = name
|
|
||||||
? i18n('POPUPS_DOMAIN/TITLE_ADD_DOMAIN_WITH_NAME', { 'NAME': name })
|
|
||||||
: i18n('POPUPS_DOMAIN/TITLE_ADD_DOMAIN');
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.domainDesc = ko.computed(() => {
|
this.addSubscribables({
|
||||||
const name = this.name();
|
testingImapError: value => value || this.testingImapErrorDesc(''),
|
||||||
return !this.edit() && name ? i18n('POPUPS_DOMAIN/NEW_DOMAIN_DESC', { 'NAME': '*@' + name }) : '';
|
testingSieveError: value => value || this.testingSieveErrorDesc(''),
|
||||||
});
|
testingSmtpError: value => value || this.testingSmtpErrorDesc(''),
|
||||||
|
|
||||||
this.domainIsComputed = ko.computed(() => {
|
page: () => this.sieveSettings(false),
|
||||||
const usePhpMail = this.smtpPhpMail(),
|
|
||||||
allowSieve = this.allowSieve(),
|
|
||||||
useSieve = this.useSieve();
|
|
||||||
|
|
||||||
return (
|
|
||||||
this.name() &&
|
|
||||||
this.imapServer() &&
|
|
||||||
this.imapPort() &&
|
|
||||||
(allowSieve && useSieve ? this.sieveServer() && this.sievePort() : true) &&
|
|
||||||
((this.smtpServer() && this.smtpPort()) || usePhpMail)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.canBeTested = ko.computed(() => !this.testing() && this.domainIsComputed());
|
|
||||||
this.canBeSaved = ko.computed(() => !this.saving() && this.domainIsComputed());
|
|
||||||
|
|
||||||
this.page.subscribe(() => this.sieveSettings(false));
|
|
||||||
|
|
||||||
// smart form improvements
|
// smart form improvements
|
||||||
this.imapServerFocus.subscribe(value =>
|
imapServerFocus: value =>
|
||||||
value && this.name() && !this.imapServer() && this.imapServer(this.name().replace(/[.]?[*][.]?/g, ''))
|
value && this.name() && !this.imapServer() && this.imapServer(this.name().replace(/[.]?[*][.]?/g, '')),
|
||||||
);
|
|
||||||
|
|
||||||
this.sieveServerFocus.subscribe(value =>
|
sieveServerFocus: value =>
|
||||||
value && this.imapServer() && !this.sieveServer() && this.sieveServer(this.imapServer())
|
value && this.imapServer() && !this.sieveServer() && this.sieveServer(this.imapServer()),
|
||||||
);
|
|
||||||
|
|
||||||
this.smtpServerFocus.subscribe(value =>
|
smtpServerFocus: value => value && this.imapServer() && !this.smtpServer()
|
||||||
value && this.imapServer() && !this.smtpServer() && this.smtpServer(this.imapServer().replace(/imap/gi, 'smtp'))
|
&& this.smtpServer(this.imapServer().replace(/imap/gi, 'smtp')),
|
||||||
);
|
|
||||||
|
|
||||||
this.imapSecure.subscribe(value => {
|
imapSecure: value => {
|
||||||
if (this.enableSmartPorts()) {
|
if (this.enableSmartPorts()) {
|
||||||
const port = pInt(this.imapPort());
|
const port = pInt(this.imapPort());
|
||||||
switch (pString(value)) {
|
switch (pString(value)) {
|
||||||
|
|
@ -143,9 +95,9 @@ class DomainPopupView extends AbstractViewNext {
|
||||||
// no default
|
// no default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
|
||||||
this.smtpSecure.subscribe(value => {
|
smtpSecure: value => {
|
||||||
if (this.enableSmartPorts()) {
|
if (this.enableSmartPorts()) {
|
||||||
const port = pInt(this.smtpPort());
|
const port = pInt(this.smtpPort());
|
||||||
switch (pString(value)) {
|
switch (pString(value)) {
|
||||||
|
|
@ -167,6 +119,53 @@ class DomainPopupView extends AbstractViewNext {
|
||||||
// no default
|
// no default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addComputables({
|
||||||
|
allowSieve: () => CapaAdminStore.filters() && CapaAdminStore.sieve(),
|
||||||
|
|
||||||
|
headerText: () => {
|
||||||
|
const name = this.name(),
|
||||||
|
aliasName = this.aliasName();
|
||||||
|
|
||||||
|
let result = '';
|
||||||
|
|
||||||
|
if (this.edit()) {
|
||||||
|
result = i18n('POPUPS_DOMAIN/TITLE_EDIT_DOMAIN', { 'NAME': name });
|
||||||
|
if (aliasName) {
|
||||||
|
result += ' ← ' + aliasName;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = name
|
||||||
|
? i18n('POPUPS_DOMAIN/TITLE_ADD_DOMAIN_WITH_NAME', { 'NAME': name })
|
||||||
|
: i18n('POPUPS_DOMAIN/TITLE_ADD_DOMAIN');
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
domainDesc: () => {
|
||||||
|
const name = this.name();
|
||||||
|
return !this.edit() && name ? i18n('POPUPS_DOMAIN/NEW_DOMAIN_DESC', { 'NAME': '*@' + name }) : '';
|
||||||
|
},
|
||||||
|
|
||||||
|
domainIsComputed: () => {
|
||||||
|
const usePhpMail = this.smtpPhpMail(),
|
||||||
|
allowSieve = this.allowSieve(),
|
||||||
|
useSieve = this.useSieve();
|
||||||
|
|
||||||
|
return (
|
||||||
|
this.name() &&
|
||||||
|
this.imapServer() &&
|
||||||
|
this.imapPort() &&
|
||||||
|
(allowSieve && useSieve ? this.sieveServer() && this.sievePort() : true) &&
|
||||||
|
((this.smtpServer() && this.smtpPort()) || usePhpMail)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
canBeTested: () => !this.testing() && this.domainIsComputed(),
|
||||||
|
canBeSaved: () => !this.saving() && this.domainIsComputed()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,18 @@ class DomainAliasPopupView extends AbstractViewNext {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.saving = ko.observable(false);
|
this.addObservables({
|
||||||
this.savingError = ko.observable('');
|
saving: false,
|
||||||
|
savingError: '',
|
||||||
|
|
||||||
this.name = ko.observable('');
|
name: '',
|
||||||
|
|
||||||
this.alias = ko.observable('');
|
alias: ''
|
||||||
|
});
|
||||||
|
|
||||||
this.domains = DomainStore.domainsWithoutAliases;
|
this.domains = DomainStore.domainsWithoutAliases;
|
||||||
|
|
||||||
this.domainsOptions = ko.computed(() =>
|
this.domainsOptions = ko.computed(() => this.domains().map(item => ({ optValue: item.name, optText: item.name })));
|
||||||
this.domains().map(item => ({ optValue: item.name, optText: item.name }))
|
|
||||||
);
|
|
||||||
|
|
||||||
this.canBeSaved = ko.computed(() => !this.saving() && this.name() && this.alias());
|
this.canBeSaved = ko.computed(() => !this.saving() && this.name() && this.alias());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,24 +18,21 @@ class FilterPopupView extends AbstractViewNext {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.isNew = ko.observable(true);
|
this.addObservables({
|
||||||
|
isNew: true,
|
||||||
|
filter: null,
|
||||||
|
allowMarkAsRead: false,
|
||||||
|
selectedFolderValue: ''
|
||||||
|
});
|
||||||
|
|
||||||
this.modules = FilterStore.modules;
|
this.modules = FilterStore.modules;
|
||||||
|
|
||||||
this.fTrueCallback = null;
|
this.fTrueCallback = null;
|
||||||
this.filter = ko.observable(null);
|
|
||||||
|
|
||||||
this.allowMarkAsRead = ko.observable(false);
|
|
||||||
|
|
||||||
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
|
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
|
||||||
this.folderSelectList = FolderStore.folderMenuForFilters;
|
this.folderSelectList = FolderStore.folderMenuForFilters;
|
||||||
this.selectedFolderValue = ko.observable('');
|
|
||||||
|
|
||||||
this.selectedFolderValue.subscribe(() => {
|
this.selectedFolderValue.subscribe(() => this.filter() && this.filter().actionValueError(false));
|
||||||
if (this.filter()) {
|
|
||||||
this.filter().actionValueError(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.actionTypeOptions = ko.observableArray([]);
|
this.actionTypeOptions = ko.observableArray([]);
|
||||||
this.fieldOptions = ko.observableArray([]);
|
this.fieldOptions = ko.observableArray([]);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import ko from 'ko';
|
|
||||||
|
|
||||||
import { StorageResultType, Notification } from 'Common/Enums';
|
import { StorageResultType, Notification } from 'Common/Enums';
|
||||||
import { i18n, getNotification } from 'Common/Translator';
|
import { i18n, getNotification } from 'Common/Translator';
|
||||||
import { setFolderHash } from 'Common/Cache';
|
import { setFolderHash } from 'Common/Cache';
|
||||||
|
|
@ -19,23 +17,25 @@ class FolderClearPopupView extends AbstractViewNext {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.selectedFolder = ko.observable(null);
|
this.addObservables({
|
||||||
this.clearingProcess = ko.observable(false);
|
selectedFolder: null,
|
||||||
this.clearingError = ko.observable('');
|
clearingProcess: false,
|
||||||
|
clearingError: ''
|
||||||
|
});
|
||||||
|
|
||||||
this.folderFullNameForClear = ko.computed(() => {
|
this.addComputables({
|
||||||
|
folderFullNameForClear: () => {
|
||||||
const folder = this.selectedFolder();
|
const folder = this.selectedFolder();
|
||||||
return folder ? folder.printableFullName() : '';
|
return folder ? folder.printableFullName() : '';
|
||||||
});
|
},
|
||||||
|
|
||||||
this.folderNameForClear = ko.computed(() => {
|
folderNameForClear: () => {
|
||||||
const folder = this.selectedFolder();
|
const folder = this.selectedFolder();
|
||||||
return folder ? folder.localName() : '';
|
return folder ? folder.localName() : '';
|
||||||
});
|
},
|
||||||
|
|
||||||
this.dangerDescHtml = ko.computed(() =>
|
dangerDescHtml: () => i18n('POPUPS_CLEAR_FOLDER/DANGER_DESC_HTML_1', { 'FOLDER': this.folderNameForClear() })
|
||||||
i18n('POPUPS_CLEAR_FOLDER/DANGER_DESC_HTML_1', { 'FOLDER': this.folderNameForClear() })
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@command((self) => {
|
@command((self) => {
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,12 @@ class FolderCreateView extends AbstractViewNext {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.folderName = ko.observable('');
|
this.addObservables({
|
||||||
this.folderName.focused = ko.observable(false);
|
folderName: '',
|
||||||
|
folderNameFocused: false,
|
||||||
|
|
||||||
this.selectedParentValue = ko.observable(UNUSED_OPTION_VALUE);
|
selectedParentValue: UNUSED_OPTION_VALUE
|
||||||
|
});
|
||||||
|
|
||||||
this.parentFolderSelectList = ko.computed(() => {
|
this.parentFolderSelectList = ko.computed(() => {
|
||||||
const top = [],
|
const top = [],
|
||||||
|
|
@ -66,7 +68,7 @@ class FolderCreateView extends AbstractViewNext {
|
||||||
clearPopup() {
|
clearPopup() {
|
||||||
this.folderName('');
|
this.folderName('');
|
||||||
this.selectedParentValue('');
|
this.selectedParentValue('');
|
||||||
this.folderName.focused(false);
|
this.folderNameFocused(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
|
|
@ -75,7 +77,7 @@ class FolderCreateView extends AbstractViewNext {
|
||||||
|
|
||||||
onShowWithDelay() {
|
onShowWithDelay() {
|
||||||
// rl.settings.app('mobile') ||
|
// rl.settings.app('mobile') ||
|
||||||
this.folderName.focused(true);
|
this.folderNameFocused(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,11 +79,13 @@ class FolderSystemPopupView extends AbstractViewNext {
|
||||||
fSaveSystemFolders();
|
fSaveSystemFolders();
|
||||||
};
|
};
|
||||||
|
|
||||||
FolderStore.sentFolder.subscribe(fCallback);
|
ko.addSubscribablesTo(FolderStore, {
|
||||||
FolderStore.draftFolder.subscribe(fCallback);
|
sentFolder: fCallback,
|
||||||
FolderStore.spamFolder.subscribe(fCallback);
|
draftFolder: fCallback,
|
||||||
FolderStore.trashFolder.subscribe(fCallback);
|
spamFolder: fCallback,
|
||||||
FolderStore.archiveFolder.subscribe(fCallback);
|
trashFolder: fCallback,
|
||||||
|
archiveFolder: fCallback
|
||||||
|
});
|
||||||
|
|
||||||
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
|
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,25 +26,27 @@ class IdentityPopupView extends AbstractViewNext {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.id = '';
|
this.id = '';
|
||||||
this.edit = ko.observable(false);
|
this.addObservables({
|
||||||
this.owner = ko.observable(false);
|
edit: false,
|
||||||
|
owner: false,
|
||||||
|
emailFocused: false,
|
||||||
|
name: '',
|
||||||
|
replyToFocused: false,
|
||||||
|
bccFocused: false,
|
||||||
|
|
||||||
|
signature: '',
|
||||||
|
signatureInsertBefore: false,
|
||||||
|
|
||||||
|
showBcc: false,
|
||||||
|
showReplyTo: false,
|
||||||
|
|
||||||
|
submitRequest: false,
|
||||||
|
submitError: ''
|
||||||
|
});
|
||||||
|
|
||||||
this.email = ko.observable('').validateEmail();
|
this.email = ko.observable('').validateEmail();
|
||||||
this.email.focused = ko.observable(false);
|
|
||||||
this.name = ko.observable('');
|
|
||||||
this.replyTo = ko.observable('').validateEmail();
|
this.replyTo = ko.observable('').validateEmail();
|
||||||
this.replyTo.focused = ko.observable(false);
|
|
||||||
this.bcc = ko.observable('').validateEmail();
|
this.bcc = ko.observable('').validateEmail();
|
||||||
this.bcc.focused = ko.observable(false);
|
|
||||||
|
|
||||||
this.signature = ko.observable('');
|
|
||||||
this.signatureInsertBefore = ko.observable(false);
|
|
||||||
|
|
||||||
this.showBcc = ko.observable(false);
|
|
||||||
this.showReplyTo = ko.observable(false);
|
|
||||||
|
|
||||||
this.submitRequest = ko.observable(false);
|
|
||||||
this.submitError = ko.observable('');
|
|
||||||
|
|
||||||
this.bcc.subscribe((value) => {
|
this.bcc.subscribe((value) => {
|
||||||
if (false === this.showBcc() && value.length) {
|
if (false === this.showBcc() && value.length) {
|
||||||
|
|
@ -71,19 +73,19 @@ class IdentityPopupView extends AbstractViewNext {
|
||||||
|
|
||||||
if (this.email.hasError()) {
|
if (this.email.hasError()) {
|
||||||
if (!this.owner()) {
|
if (!this.owner()) {
|
||||||
this.email.focused(true);
|
this.emailFocused(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.replyTo.hasError()) {
|
if (this.replyTo.hasError()) {
|
||||||
this.replyTo.focused(true);
|
this.replyToFocused(true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.bcc.hasError()) {
|
if (this.bcc.hasError()) {
|
||||||
this.bcc.focused(true);
|
this.bccFocused(true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,7 +165,7 @@ class IdentityPopupView extends AbstractViewNext {
|
||||||
|
|
||||||
onShowWithDelay() {
|
onShowWithDelay() {
|
||||||
if (!this.owner()/* && !rl.settings.app('mobile')*/) {
|
if (!this.owner()/* && !rl.settings.app('mobile')*/) {
|
||||||
this.email.focused(true);
|
this.emailFocused(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,7 @@ class LanguagesPopupView extends AbstractViewNext {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
this.langs.subscribe(() => {
|
this.langs.subscribe(() => this.setLanguageSelection());
|
||||||
this.setLanguageSelection();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
languageTooltipName(language) {
|
languageTooltipName(language) {
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,16 @@ class MessageOpenPgpPopupView extends AbstractViewNext {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.notification = ko.observable('');
|
this.addObservables({
|
||||||
|
notification: '',
|
||||||
this.selectedKey = ko.observable(null);
|
selectedKey: null,
|
||||||
|
password: '',
|
||||||
|
submitRequest: false
|
||||||
|
});
|
||||||
this.privateKeys = ko.observableArray([]);
|
this.privateKeys = ko.observableArray([]);
|
||||||
|
|
||||||
this.password = ko.observable('');
|
|
||||||
|
|
||||||
this.resultCallback = null;
|
this.resultCallback = null;
|
||||||
|
|
||||||
this.submitRequest = ko.observable(false);
|
|
||||||
|
|
||||||
this.sDefaultKeyScope = KeyState.PopupMessageOpenPGP;
|
this.sDefaultKeyScope = KeyState.PopupMessageOpenPGP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import ko from 'ko';
|
|
||||||
|
|
||||||
import { pInt } from 'Common/Utils';
|
import { pInt } from 'Common/Utils';
|
||||||
|
|
||||||
import PgpStore from 'Stores/User/Pgp';
|
import PgpStore from 'Stores/User/Pgp';
|
||||||
|
|
@ -15,20 +13,20 @@ class NewOpenPgpKeyPopupView extends AbstractViewNext {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.email = ko.observable('');
|
this.addObservables({
|
||||||
this.email.focus = ko.observable('');
|
email: '',
|
||||||
this.email.error = ko.observable(false);
|
emailFocus: '',
|
||||||
|
emailError: false,
|
||||||
|
|
||||||
this.name = ko.observable('');
|
name: '',
|
||||||
this.password = ko.observable('');
|
password: '',
|
||||||
this.keyBitLength = ko.observable(2048);
|
keyBitLength: 2048,
|
||||||
|
|
||||||
this.submitRequest = ko.observable(false);
|
submitRequest: false,
|
||||||
this.submitError = ko.observable('');
|
submitError: ''
|
||||||
|
|
||||||
this.email.subscribe(() => {
|
|
||||||
this.email.error(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.email.subscribe(() => this.emailError(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@command()
|
@command()
|
||||||
|
|
@ -36,8 +34,8 @@ class NewOpenPgpKeyPopupView extends AbstractViewNext {
|
||||||
const userId = {},
|
const userId = {},
|
||||||
openpgpKeyring = PgpStore.openpgpKeyring;
|
openpgpKeyring = PgpStore.openpgpKeyring;
|
||||||
|
|
||||||
this.email.error(!this.email().trim());
|
this.emailError(!this.email().trim());
|
||||||
if (!openpgpKeyring || this.email.error()) {
|
if (!openpgpKeyring || this.emailError()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,7 +93,7 @@ class NewOpenPgpKeyPopupView extends AbstractViewNext {
|
||||||
this.password('');
|
this.password('');
|
||||||
|
|
||||||
this.email('');
|
this.email('');
|
||||||
this.email.error(false);
|
this.emailError(false);
|
||||||
this.keyBitLength(2048);
|
this.keyBitLength(2048);
|
||||||
|
|
||||||
this.submitError('');
|
this.submitError('');
|
||||||
|
|
@ -106,7 +104,7 @@ class NewOpenPgpKeyPopupView extends AbstractViewNext {
|
||||||
}
|
}
|
||||||
|
|
||||||
onShowWithDelay() {
|
onShowWithDelay() {
|
||||||
this.email.focus(true);
|
this.emailFocus(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,11 @@ class PluginPopupView extends AbstractViewNext {
|
||||||
|
|
||||||
this.onPluginSettingsUpdateResponse = this.onPluginSettingsUpdateResponse.bind(this);
|
this.onPluginSettingsUpdateResponse = this.onPluginSettingsUpdateResponse.bind(this);
|
||||||
|
|
||||||
this.saveError = ko.observable('');
|
this.addObservables({
|
||||||
|
saveError: '',
|
||||||
this.name = ko.observable('');
|
name: '',
|
||||||
this.readme = ko.observable('');
|
readme: ''
|
||||||
|
});
|
||||||
|
|
||||||
this.configures = ko.observableArray([]);
|
this.configures = ko.observableArray([]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import ko from 'ko';
|
|
||||||
|
|
||||||
import { StorageResultType, Notification } from 'Common/Enums';
|
import { StorageResultType, Notification } from 'Common/Enums';
|
||||||
import { getNotification } from 'Common/Translator';
|
import { getNotification } from 'Common/Translator';
|
||||||
import { HtmlEditor } from 'Common/HtmlEditor';
|
import { HtmlEditor } from 'Common/HtmlEditor';
|
||||||
|
|
@ -19,38 +17,37 @@ class TemplatePopupView extends AbstractViewNext {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.editor = null;
|
this.editor = null;
|
||||||
this.signatureDom = ko.observable(null);
|
|
||||||
|
|
||||||
this.id = ko.observable('');
|
this.addObservables({
|
||||||
|
signatureDom: null,
|
||||||
|
|
||||||
this.name = ko.observable('');
|
id: '',
|
||||||
this.name.error = ko.observable(false);
|
|
||||||
this.name.focus = ko.observable(false);
|
|
||||||
|
|
||||||
this.body = ko.observable('');
|
name: '',
|
||||||
this.body.loading = ko.observable(false);
|
nameError: false,
|
||||||
this.body.error = ko.observable(false);
|
nameFocus: false,
|
||||||
|
|
||||||
this.name.subscribe(() => {
|
body: '',
|
||||||
this.name.error(false);
|
bodyLoading: false,
|
||||||
|
bodyError: false,
|
||||||
|
|
||||||
|
submitRequest: false,
|
||||||
|
submitError: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
this.body.subscribe(() => {
|
this.name.subscribe(() => this.nameError(false));
|
||||||
this.body.error(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.submitRequest = ko.observable(false);
|
this.body.subscribe(() => this.bodyError(false));
|
||||||
this.submitError = ko.observable('');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@command((self) => !self.submitRequest())
|
@command((self) => !self.submitRequest())
|
||||||
addTemplateCommand() {
|
addTemplateCommand() {
|
||||||
this.populateBodyFromEditor();
|
this.populateBodyFromEditor();
|
||||||
|
|
||||||
this.name.error(!this.name().trim());
|
this.nameError(!this.name().trim());
|
||||||
this.body.error(!this.body().trim() || ':HTML:' === this.body().trim());
|
this.bodyError(!this.body().trim() || ':HTML:' === this.body().trim());
|
||||||
|
|
||||||
if (this.name.error() || this.body.error()) {
|
if (this.nameError() || this.bodyError()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,11 +79,11 @@ class TemplatePopupView extends AbstractViewNext {
|
||||||
this.id('');
|
this.id('');
|
||||||
|
|
||||||
this.name('');
|
this.name('');
|
||||||
this.name.error(false);
|
this.nameError(false);
|
||||||
|
|
||||||
this.body('');
|
this.body('');
|
||||||
this.body.loading(false);
|
this.bodyLoading(false);
|
||||||
this.body.error(false);
|
this.bodyError(false);
|
||||||
|
|
||||||
this.submitRequest(false);
|
this.submitRequest(false);
|
||||||
this.submitError('');
|
this.submitError('');
|
||||||
|
|
@ -125,11 +122,11 @@ class TemplatePopupView extends AbstractViewNext {
|
||||||
if (template.populated) {
|
if (template.populated) {
|
||||||
this.editorSetBody(this.body());
|
this.editorSetBody(this.body());
|
||||||
} else {
|
} else {
|
||||||
this.body.loading(true);
|
this.bodyLoading(true);
|
||||||
this.body.error(false);
|
this.bodyError(false);
|
||||||
|
|
||||||
Remote.templateGetById((result, data) => {
|
Remote.templateGetById((result, data) => {
|
||||||
this.body.loading(false);
|
this.bodyLoading(false);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
StorageResultType.Success === result &&
|
StorageResultType.Success === result &&
|
||||||
|
|
@ -141,10 +138,10 @@ class TemplatePopupView extends AbstractViewNext {
|
||||||
template.populated = true;
|
template.populated = true;
|
||||||
|
|
||||||
this.body(template.body);
|
this.body(template.body);
|
||||||
this.body.error(false);
|
this.bodyError(false);
|
||||||
} else {
|
} else {
|
||||||
this.body('');
|
this.body('');
|
||||||
this.body.error(true);
|
this.bodyError(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.editorSetBody(this.body());
|
this.editorSetBody(this.body());
|
||||||
|
|
@ -156,7 +153,7 @@ class TemplatePopupView extends AbstractViewNext {
|
||||||
}
|
}
|
||||||
|
|
||||||
onShowWithDelay() {
|
onShowWithDelay() {
|
||||||
this.name.focus(true);
|
this.nameFocus(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import ko from 'ko';
|
|
||||||
|
|
||||||
import { Capa, StorageResultType } from 'Common/Enums';
|
import { Capa, StorageResultType } from 'Common/Enums';
|
||||||
import { pString } from 'Common/Utils';
|
import { pString } from 'Common/Utils';
|
||||||
import { i18n, trigger as translatorTrigger } from 'Common/Translator';
|
import { i18n, trigger as translatorTrigger } from 'Common/Translator';
|
||||||
|
|
@ -17,27 +15,30 @@ class TwoFactorConfigurationPopupView extends AbstractViewNext {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.lock = ko.observable(false);
|
this.addObservables({
|
||||||
|
lock: false,
|
||||||
|
|
||||||
|
processing: false,
|
||||||
|
clearing: false,
|
||||||
|
secreting: false,
|
||||||
|
|
||||||
|
viewUser: '',
|
||||||
|
twoFactorStatus: false,
|
||||||
|
|
||||||
|
twoFactorTested: false,
|
||||||
|
|
||||||
|
viewSecret: '',
|
||||||
|
viewBackupCodes: '',
|
||||||
|
viewUrlTitle: '',
|
||||||
|
viewUrl: '',
|
||||||
|
|
||||||
|
viewEnable_: false
|
||||||
|
});
|
||||||
|
|
||||||
this.capaTwoFactor = rl.settings.capa(Capa.TwoFactor);
|
this.capaTwoFactor = rl.settings.capa(Capa.TwoFactor);
|
||||||
|
|
||||||
this.processing = ko.observable(false);
|
this.addComputables({
|
||||||
this.clearing = ko.observable(false);
|
viewEnable: {
|
||||||
this.secreting = ko.observable(false);
|
|
||||||
|
|
||||||
this.viewUser = ko.observable('');
|
|
||||||
this.twoFactorStatus = ko.observable(false);
|
|
||||||
|
|
||||||
this.twoFactorTested = ko.observable(false);
|
|
||||||
|
|
||||||
this.viewSecret = ko.observable('');
|
|
||||||
this.viewBackupCodes = ko.observable('');
|
|
||||||
this.viewUrlTitle = ko.observable('');
|
|
||||||
this.viewUrl = ko.observable('');
|
|
||||||
|
|
||||||
this.viewEnable_ = ko.observable(false);
|
|
||||||
|
|
||||||
this.viewEnable = ko.computed({
|
|
||||||
read: this.viewEnable_,
|
read: this.viewEnable_,
|
||||||
write: (value) => {
|
write: (value) => {
|
||||||
value = !!value;
|
value = !!value;
|
||||||
|
|
@ -60,25 +61,26 @@ class TwoFactorConfigurationPopupView extends AbstractViewNext {
|
||||||
}, false);
|
}, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
|
||||||
this.viewTwoFactorEnableTooltip = ko.computed(() => {
|
viewTwoFactorEnableTooltip: () => {
|
||||||
translatorTrigger();
|
translatorTrigger();
|
||||||
return this.twoFactorTested() || this.viewEnable_()
|
return this.twoFactorTested() || this.viewEnable_()
|
||||||
? ''
|
? ''
|
||||||
: i18n('POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_SECRET_TEST_BEFORE_DESC');
|
: i18n('POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_SECRET_TEST_BEFORE_DESC');
|
||||||
});
|
},
|
||||||
|
|
||||||
this.viewTwoFactorStatus = ko.computed(() => {
|
viewTwoFactorStatus: () => {
|
||||||
translatorTrigger();
|
translatorTrigger();
|
||||||
return i18n(
|
return i18n(
|
||||||
this.twoFactorStatus()
|
this.twoFactorStatus()
|
||||||
? 'POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_SECRET_CONFIGURED_DESC'
|
? 'POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_SECRET_CONFIGURED_DESC'
|
||||||
: 'POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC'
|
: 'POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC'
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
|
||||||
this.twoFactorAllowedEnable = ko.computed(() => this.viewEnable() || this.twoFactorTested());
|
twoFactorAllowedEnable: () => this.viewEnable() || this.twoFactorTested()
|
||||||
|
});
|
||||||
|
|
||||||
this.onResult = this.onResult.bind(this);
|
this.onResult = this.onResult.bind(this);
|
||||||
this.onShowSecretResult = this.onShowSecretResult.bind(this);
|
this.onShowSecretResult = this.onShowSecretResult.bind(this);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import ko from 'ko';
|
|
||||||
|
|
||||||
import { StorageResultType } from 'Common/Enums';
|
import { StorageResultType } from 'Common/Enums';
|
||||||
|
|
||||||
import Remote from 'Remote/User/Fetch';
|
import Remote from 'Remote/User/Fetch';
|
||||||
|
|
@ -15,13 +13,15 @@ class TwoFactorTestPopupView extends AbstractViewNext {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.code = ko.observable('');
|
this.addObservables({
|
||||||
this.code.focused = ko.observable(false);
|
code: '',
|
||||||
this.code.status = ko.observable(null);
|
codeFocused: false,
|
||||||
|
codeStatus: null,
|
||||||
|
|
||||||
|
testing: false
|
||||||
|
});
|
||||||
|
|
||||||
this.koTestedTrigger = null;
|
this.koTestedTrigger = null;
|
||||||
|
|
||||||
this.testing = ko.observable(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@command((self) => self.code() && !self.testing())
|
@command((self) => self.code() && !self.testing())
|
||||||
|
|
@ -29,9 +29,9 @@ class TwoFactorTestPopupView extends AbstractViewNext {
|
||||||
this.testing(true);
|
this.testing(true);
|
||||||
Remote.testTwoFactor((result, data) => {
|
Remote.testTwoFactor((result, data) => {
|
||||||
this.testing(false);
|
this.testing(false);
|
||||||
this.code.status(StorageResultType.Success === result && data && !!data.Result);
|
this.codeStatus(StorageResultType.Success === result && data && !!data.Result);
|
||||||
|
|
||||||
if (this.koTestedTrigger && this.code.status()) {
|
if (this.koTestedTrigger && this.codeStatus()) {
|
||||||
this.koTestedTrigger(true);
|
this.koTestedTrigger(true);
|
||||||
}
|
}
|
||||||
}, this.code());
|
}, this.code());
|
||||||
|
|
@ -39,8 +39,8 @@ class TwoFactorTestPopupView extends AbstractViewNext {
|
||||||
|
|
||||||
clearPopup() {
|
clearPopup() {
|
||||||
this.code('');
|
this.code('');
|
||||||
this.code.focused(false);
|
this.codeFocused(false);
|
||||||
this.code.status(null);
|
this.codeStatus(null);
|
||||||
this.testing(false);
|
this.testing(false);
|
||||||
|
|
||||||
this.koTestedTrigger = null;
|
this.koTestedTrigger = null;
|
||||||
|
|
@ -54,7 +54,7 @@ class TwoFactorTestPopupView extends AbstractViewNext {
|
||||||
|
|
||||||
onShowWithDelay() {
|
onShowWithDelay() {
|
||||||
// rl.settings.app('mobile') ||
|
// rl.settings.app('mobile') ||
|
||||||
this.code.focused(true);
|
this.codeFocused(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import ko from 'ko';
|
|
||||||
|
|
||||||
import { KeyState } from 'Common/Enums';
|
import { KeyState } from 'Common/Enums';
|
||||||
|
|
||||||
import { popup } from 'Knoin/Knoin';
|
import { popup } from 'Knoin/Knoin';
|
||||||
|
|
@ -13,8 +11,10 @@ class ViewOpenPgpKeyPopupView extends AbstractViewNext {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.key = ko.observable('');
|
this.addObservables({
|
||||||
this.keyDom = ko.observable(null);
|
key: '',
|
||||||
|
keyDom: null
|
||||||
|
});
|
||||||
|
|
||||||
this.sDefaultKeyScope = KeyState.PopupViewOpenPGP;
|
this.sDefaultKeyScope = KeyState.PopupViewOpenPGP;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,17 +37,31 @@ class LoginUserView extends AbstractViewNext {
|
||||||
|
|
||||||
this.hideSubmitButton = Settings.app('hideSubmitButton');
|
this.hideSubmitButton = Settings.app('hideSubmitButton');
|
||||||
|
|
||||||
this.welcome = ko.observable(!!Settings.get('UseLoginWelcomePage'));
|
this.addObservables({
|
||||||
|
welcome: !!Settings.get('UseLoginWelcomePage'),
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
signMe: false,
|
||||||
|
additionalCode: '',
|
||||||
|
|
||||||
this.email = ko.observable('');
|
emailError: false,
|
||||||
this.password = ko.observable('');
|
passwordError: false,
|
||||||
this.signMe = ko.observable(false);
|
|
||||||
|
|
||||||
this.additionalCode = ko.observable('');
|
formHidden: false,
|
||||||
this.additionalCode.error = ko.observable(false);
|
|
||||||
this.additionalCode.errorAnimation = ko.observable(false).extend({ falseTimeout: 500 });
|
submitRequest: false,
|
||||||
this.additionalCode.visibility = ko.observable(false);
|
submitError: '',
|
||||||
this.additionalCodeSignMe = ko.observable(false);
|
submitErrorAddidional: '',
|
||||||
|
|
||||||
|
langRequest: false,
|
||||||
|
|
||||||
|
additionalCodeError: false,
|
||||||
|
additionalCodeSignMe: false,
|
||||||
|
additionalCodeVisibility: false,
|
||||||
|
signMeType: LoginSignMeType.Unused
|
||||||
|
});
|
||||||
|
|
||||||
|
this.additionalCodeErrorAnimation = ko.observable(false).extend({ falseTimeout: 500 });
|
||||||
|
|
||||||
this.logoImg = (Settings.get('LoginLogo')||'').trim();
|
this.logoImg = (Settings.get('LoginLogo')||'').trim();
|
||||||
this.loginDescription = (Settings.get('LoginDescription')||'').trim();
|
this.loginDescription = (Settings.get('LoginDescription')||'').trim();
|
||||||
|
|
@ -58,61 +72,50 @@ class LoginUserView extends AbstractViewNext {
|
||||||
this.forgotPasswordLinkUrl = Settings.app('forgotPasswordLinkUrl');
|
this.forgotPasswordLinkUrl = Settings.app('forgotPasswordLinkUrl');
|
||||||
this.registrationLinkUrl = Settings.app('registrationLinkUrl');
|
this.registrationLinkUrl = Settings.app('registrationLinkUrl');
|
||||||
|
|
||||||
this.emailError = ko.observable(false);
|
|
||||||
this.passwordError = ko.observable(false);
|
|
||||||
|
|
||||||
this.emailErrorAnimation = ko.observable(false).extend({ falseTimeout: 500 });
|
this.emailErrorAnimation = ko.observable(false).extend({ falseTimeout: 500 });
|
||||||
this.passwordErrorAnimation = ko.observable(false).extend({ falseTimeout: 500 });
|
this.passwordErrorAnimation = ko.observable(false).extend({ falseTimeout: 500 });
|
||||||
|
|
||||||
this.formHidden = ko.observable(false);
|
this.addComputables({
|
||||||
|
formError:
|
||||||
this.formError = ko.computed(
|
|
||||||
() =>
|
() =>
|
||||||
this.emailErrorAnimation() ||
|
this.emailErrorAnimation() ||
|
||||||
this.passwordErrorAnimation() ||
|
this.passwordErrorAnimation() ||
|
||||||
(this.additionalCode.visibility() && this.additionalCode.errorAnimation())
|
(this.additionalCodeVisibility() && this.additionalCodeErrorAnimation()),
|
||||||
);
|
|
||||||
|
|
||||||
this.email.subscribe(() => {
|
languageFullName: () => convertLangName(this.language()),
|
||||||
this.emailError(false);
|
|
||||||
this.additionalCode('');
|
signMeVisibility: () => LoginSignMeType.Unused !== this.signMeType()
|
||||||
this.additionalCode.visibility(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.password.subscribe(() => this.passwordError(false));
|
this.addSubscribables({
|
||||||
|
email: () => {
|
||||||
|
this.emailError(false);
|
||||||
|
this.additionalCode('');
|
||||||
|
this.additionalCodeVisibility(false);
|
||||||
|
},
|
||||||
|
|
||||||
this.additionalCode.subscribe(() => this.additionalCode.error(false));
|
password: () => this.passwordError(false),
|
||||||
|
|
||||||
this.additionalCode.visibility.subscribe(() => this.additionalCode.error(false));
|
additionalCode: () => this.additionalCodeError(false),
|
||||||
|
additionalCodeError: bV => this.additionalCodeErrorAnimation(!!bV),
|
||||||
|
additionalCodeVisibility: () => this.additionalCodeError(false),
|
||||||
|
|
||||||
this.emailError.subscribe(bV => this.emailErrorAnimation(!!bV));
|
emailError: bV => this.emailErrorAnimation(!!bV),
|
||||||
|
|
||||||
this.passwordError.subscribe(bV => this.passwordErrorAnimation(!!bV));
|
passwordError: bV => this.passwordErrorAnimation(!!bV),
|
||||||
|
|
||||||
this.additionalCode.error.subscribe(bV => this.additionalCode.errorAnimation(!!bV));
|
submitError: value => value || this.submitErrorAddidional(''),
|
||||||
|
|
||||||
this.submitRequest = ko.observable(false);
|
signMeType: iValue => this.signMe(LoginSignMeType.DefaultOn === iValue)
|
||||||
this.submitError = ko.observable('');
|
});
|
||||||
this.submitErrorAddidional = ko.observable('');
|
|
||||||
|
|
||||||
this.submitError.subscribe(value => value || this.submitErrorAddidional(''));
|
|
||||||
|
|
||||||
this.allowLanguagesOnLogin = !!Settings.get('AllowLanguagesOnLogin');
|
this.allowLanguagesOnLogin = !!Settings.get('AllowLanguagesOnLogin');
|
||||||
|
|
||||||
this.langRequest = ko.observable(false);
|
|
||||||
this.language = LanguageStore.language;
|
this.language = LanguageStore.language;
|
||||||
this.languages = LanguageStore.languages;
|
this.languages = LanguageStore.languages;
|
||||||
|
|
||||||
this.bSendLanguage = false;
|
this.bSendLanguage = false;
|
||||||
|
|
||||||
this.languageFullName = ko.computed(() => convertLangName(this.language()));
|
|
||||||
|
|
||||||
this.signMeType = ko.observable(LoginSignMeType.Unused);
|
|
||||||
|
|
||||||
this.signMeType.subscribe(iValue => this.signMe(LoginSignMeType.DefaultOn === iValue));
|
|
||||||
|
|
||||||
this.signMeVisibility = ko.computed(() => LoginSignMeType.Unused !== this.signMeType());
|
|
||||||
|
|
||||||
if (Settings.get('AdditionalLoginError') && !this.submitError()) {
|
if (Settings.get('AdditionalLoginError') && !this.submitError()) {
|
||||||
this.submitError(Settings.get('AdditionalLoginError'));
|
this.submitError(Settings.get('AdditionalLoginError'));
|
||||||
}
|
}
|
||||||
|
|
@ -128,10 +131,10 @@ class LoginUserView extends AbstractViewNext {
|
||||||
this.passwordError(false);
|
this.passwordError(false);
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
if (this.additionalCode.visibility()) {
|
if (this.additionalCodeVisibility()) {
|
||||||
this.additionalCode.error(false);
|
this.additionalCodeError(false);
|
||||||
if (!this.additionalCode().trim()) {
|
if (!this.additionalCode().trim()) {
|
||||||
this.additionalCode.error(true);
|
this.additionalCodeError(true);
|
||||||
error = '.inputAdditionalCode';
|
error = '.inputAdditionalCode';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -157,7 +160,7 @@ class LoginUserView extends AbstractViewNext {
|
||||||
if (oData.Result) {
|
if (oData.Result) {
|
||||||
if (oData.TwoFactorAuth) {
|
if (oData.TwoFactorAuth) {
|
||||||
this.additionalCode('');
|
this.additionalCode('');
|
||||||
this.additionalCode.visibility(true);
|
this.additionalCodeVisibility(true);
|
||||||
this.submitRequest(false);
|
this.submitRequest(false);
|
||||||
|
|
||||||
setTimeout(() => this.querySelector('.inputAdditionalCode').focus(), 100);
|
setTimeout(() => this.querySelector('.inputAdditionalCode').focus(), 100);
|
||||||
|
|
@ -192,8 +195,8 @@ class LoginUserView extends AbstractViewNext {
|
||||||
sLoginPassword,
|
sLoginPassword,
|
||||||
!!this.signMe(),
|
!!this.signMe(),
|
||||||
this.bSendLanguage ? this.language() : '',
|
this.bSendLanguage ? this.language() : '',
|
||||||
this.additionalCode.visibility() ? this.additionalCode() : '',
|
this.additionalCodeVisibility() ? this.additionalCode() : '',
|
||||||
this.additionalCode.visibility() ? !!this.additionalCodeSignMe() : false
|
this.additionalCodeVisibility() ? !!this.additionalCodeSignMe() : false
|
||||||
);
|
);
|
||||||
|
|
||||||
Local.set(ClientSideKeyName.LastSignMe, this.signMe() ? '-1-' : '-0-');
|
Local.set(ClientSideKeyName.LastSignMe, this.signMe() ? '-1-' : '-0-');
|
||||||
|
|
|
||||||
|
|
@ -108,97 +108,87 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||||
this.userUsageSize = QuotaStore.usage;
|
this.userUsageSize = QuotaStore.usage;
|
||||||
this.userUsageProc = QuotaStore.percentage;
|
this.userUsageProc = QuotaStore.percentage;
|
||||||
|
|
||||||
this.moveDropdownTrigger = ko.observable(false);
|
this.addObservables({
|
||||||
this.moreDropdownTrigger = ko.observable(false);
|
moveDropdownTrigger: false,
|
||||||
|
moreDropdownTrigger: false,
|
||||||
|
|
||||||
|
dragOverArea: null,
|
||||||
|
dragOverBodyArea: null,
|
||||||
|
|
||||||
|
inputMessageListSearchFocus: false
|
||||||
|
});
|
||||||
|
|
||||||
// append drag and drop
|
// append drag and drop
|
||||||
this.dragOver = ko.observable(false).extend({ 'throttle': 1 });
|
this.dragOver = ko.observable(false).extend({ 'throttle': 1 });
|
||||||
this.dragOverEnter = ko.observable(false).extend({ 'throttle': 1 });
|
this.dragOverEnter = ko.observable(false).extend({ 'throttle': 1 });
|
||||||
this.dragOverArea = ko.observable(null);
|
|
||||||
this.dragOverBodyArea = ko.observable(null);
|
|
||||||
|
|
||||||
this.messageListItemTemplate = ko.computed(() =>
|
this.sLastSearchValue = '';
|
||||||
|
|
||||||
|
this.addComputables({
|
||||||
|
messageListItemTemplate: () =>
|
||||||
this.mobile || Layout.SidePreview === SettingsStore.layout()
|
this.mobile || Layout.SidePreview === SettingsStore.layout()
|
||||||
? 'MailMessageListItem'
|
? 'MailMessageListItem'
|
||||||
: 'MailMessageListItemNoPreviewPane'
|
: 'MailMessageListItemNoPreviewPane',
|
||||||
);
|
|
||||||
|
|
||||||
this.messageListSearchDesc = ko.computed(() => {
|
messageListSearchDesc: () => {
|
||||||
const value = MessageStore.messageListEndSearch();
|
const value = MessageStore.messageListEndSearch();
|
||||||
return value ? i18n('MESSAGE_LIST/SEARCH_RESULT_FOR', { 'SEARCH': value }) : '';
|
return value ? i18n('MESSAGE_LIST/SEARCH_RESULT_FOR', { 'SEARCH': value }) : ''
|
||||||
});
|
},
|
||||||
|
|
||||||
this.messageListPaginator = ko.computed(
|
messageListPaginator: computedPaginatorHelper(MessageStore.messageListPage, MessageStore.messageListPageCount),
|
||||||
computedPaginatorHelper(MessageStore.messageListPage, MessageStore.messageListPageCount)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.checkAll = ko.computed({
|
checkAll: {
|
||||||
read: () => 0 < MessageStore.messageListChecked().length,
|
read: () => 0 < MessageStore.messageListChecked().length,
|
||||||
write: (value) => {
|
write: (value) => {
|
||||||
value = !!value;
|
value = !!value;
|
||||||
MessageStore.messageList().forEach(message => message.checked(value));
|
MessageStore.messageList().forEach(message => message.checked(value));
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
|
||||||
this.inputMessageListSearchFocus = ko.observable(false);
|
inputProxyMessageListSearch: {
|
||||||
|
|
||||||
this.sLastSearchValue = '';
|
|
||||||
this.inputProxyMessageListSearch = ko.computed({
|
|
||||||
read: this.mainMessageListSearch,
|
read: this.mainMessageListSearch,
|
||||||
write: value => this.sLastSearchValue = value
|
write: value => this.sLastSearchValue = value
|
||||||
});
|
},
|
||||||
|
|
||||||
this.isIncompleteChecked = ko.computed(() => {
|
isIncompleteChecked: () => {
|
||||||
const c = MessageStore.messageListChecked().length;
|
const c = MessageStore.messageListChecked().length;
|
||||||
return c && MessageStore.messageList().length > c;
|
return c && MessageStore.messageList().length > c;
|
||||||
|
},
|
||||||
|
|
||||||
|
hasMessages: () => 0 < this.messageList().length,
|
||||||
|
|
||||||
|
hasCheckedOrSelectedLines: () => 0 < this.messageListCheckedOrSelected().length,
|
||||||
|
|
||||||
|
isSpamFolder: () => FolderStore.spamFolder() === this.messageListEndFolder() && FolderStore.spamFolder(),
|
||||||
|
|
||||||
|
isSpamDisabled: () => UNUSED_OPTION_VALUE === FolderStore.spamFolder(),
|
||||||
|
|
||||||
|
isTrashFolder: () => FolderStore.trashFolder() === this.messageListEndFolder() && FolderStore.trashFolder(),
|
||||||
|
|
||||||
|
isDraftFolder: () => FolderStore.draftFolder() === this.messageListEndFolder() && FolderStore.draftFolder(),
|
||||||
|
|
||||||
|
isSentFolder: () => FolderStore.sentFolder() === this.messageListEndFolder() && FolderStore.sentFolder(),
|
||||||
|
|
||||||
|
isArchiveFolder: () => FolderStore.archiveFolder() === this.messageListEndFolder() && FolderStore.archiveFolder(),
|
||||||
|
|
||||||
|
isArchiveDisabled: () => UNUSED_OPTION_VALUE === FolderStore.archiveFolder(),
|
||||||
|
|
||||||
|
isArchiveVisible: () => !this.isArchiveFolder() && !this.isArchiveDisabled() && !this.isDraftFolder(),
|
||||||
|
|
||||||
|
isSpamVisible: () =>
|
||||||
|
!this.isSpamFolder() && !this.isSpamDisabled() && !this.isDraftFolder() && !this.isSentFolder(),
|
||||||
|
|
||||||
|
isUnSpamVisible: () =>
|
||||||
|
this.isSpamFolder() && !this.isSpamDisabled() && !this.isDraftFolder() && !this.isSentFolder(),
|
||||||
|
|
||||||
|
mobileCheckedStateShow: () => this.mobile ? 0 < MessageStore.messageListChecked().length : true,
|
||||||
|
|
||||||
|
mobileCheckedStateHide: () => this.mobile ? !MessageStore.messageListChecked().length : true,
|
||||||
|
|
||||||
|
messageListFocused: () => Focused.MessageList === AppStore.focusedState()
|
||||||
});
|
});
|
||||||
|
|
||||||
this.hasMessages = ko.computed(() => 0 < this.messageList().length);
|
|
||||||
|
|
||||||
this.hasCheckedOrSelectedLines = ko.computed(() => 0 < this.messageListCheckedOrSelected().length);
|
|
||||||
|
|
||||||
this.isSpamFolder = ko.computed(
|
|
||||||
() => FolderStore.spamFolder() === this.messageListEndFolder() && FolderStore.spamFolder()
|
|
||||||
);
|
|
||||||
|
|
||||||
this.isSpamDisabled = ko.computed(() => UNUSED_OPTION_VALUE === FolderStore.spamFolder());
|
|
||||||
|
|
||||||
this.isTrashFolder = ko.computed(
|
|
||||||
() => FolderStore.trashFolder() === this.messageListEndFolder() && FolderStore.trashFolder()
|
|
||||||
);
|
|
||||||
|
|
||||||
this.isDraftFolder = ko.computed(
|
|
||||||
() => FolderStore.draftFolder() === this.messageListEndFolder() && FolderStore.draftFolder()
|
|
||||||
);
|
|
||||||
|
|
||||||
this.isSentFolder = ko.computed(
|
|
||||||
() => FolderStore.sentFolder() === this.messageListEndFolder() && FolderStore.sentFolder()
|
|
||||||
);
|
|
||||||
|
|
||||||
this.isArchiveFolder = ko.computed(
|
|
||||||
() => FolderStore.archiveFolder() === this.messageListEndFolder() && FolderStore.archiveFolder()
|
|
||||||
);
|
|
||||||
|
|
||||||
this.isArchiveDisabled = ko.computed(() => UNUSED_OPTION_VALUE === FolderStore.archiveFolder());
|
|
||||||
|
|
||||||
this.isArchiveVisible = ko.computed(
|
|
||||||
() => !this.isArchiveFolder() && !this.isArchiveDisabled() && !this.isDraftFolder()
|
|
||||||
);
|
|
||||||
|
|
||||||
this.isSpamVisible = ko.computed(
|
|
||||||
() => !this.isSpamFolder() && !this.isSpamDisabled() && !this.isDraftFolder() && !this.isSentFolder()
|
|
||||||
);
|
|
||||||
|
|
||||||
this.isUnSpamVisible = ko.computed(
|
|
||||||
() => this.isSpamFolder() && !this.isSpamDisabled() && !this.isDraftFolder() && !this.isSentFolder()
|
|
||||||
);
|
|
||||||
|
|
||||||
// this.messageListChecked = MessageStore.messageListChecked;
|
// this.messageListChecked = MessageStore.messageListChecked;
|
||||||
this.mobileCheckedStateShow = ko.computed(() => this.mobile ? 0 < MessageStore.messageListChecked().length : true);
|
|
||||||
|
|
||||||
this.mobileCheckedStateHide = ko.computed(() => this.mobile ? !MessageStore.messageListChecked().length : true);
|
|
||||||
|
|
||||||
this.messageListFocused = ko.computed(() => Focused.MessageList === AppStore.focusedState());
|
|
||||||
|
|
||||||
this.canBeMoved = this.hasCheckedOrSelectedLines;
|
this.canBeMoved = this.hasCheckedOrSelectedLines;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import {
|
||||||
import { $htmlCL, leftPanelDisabled, keyScopeReal, moveAction } from 'Common/Globals';
|
import { $htmlCL, leftPanelDisabled, keyScopeReal, moveAction } from 'Common/Globals';
|
||||||
|
|
||||||
import { inFocus } from 'Common/Utils';
|
import { inFocus } from 'Common/Utils';
|
||||||
import { mailToHelper, isTransparent } from 'Common/UtilsUser';
|
import { mailToHelper } from 'Common/UtilsUser';
|
||||||
|
|
||||||
import Audio from 'Common/Audio';
|
import Audio from 'Common/Audio';
|
||||||
|
|
||||||
|
|
@ -40,6 +40,10 @@ import { AbstractViewNext } from 'Knoin/AbstractViewNext';
|
||||||
|
|
||||||
const Settings = rl.settings;
|
const Settings = rl.settings;
|
||||||
|
|
||||||
|
function isTransparent(color) {
|
||||||
|
return 'rgba(0, 0, 0, 0)' === color || 'transparent' === color;
|
||||||
|
}
|
||||||
|
|
||||||
@view({
|
@view({
|
||||||
name: 'View/User/MailBox/MessageView',
|
name: 'View/User/MailBox/MessageView',
|
||||||
type: ViewType.Right,
|
type: ViewType.Right,
|
||||||
|
|
@ -67,7 +71,14 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
this.oHeaderDom = null;
|
this.oHeaderDom = null;
|
||||||
this.oMessageScrollerDom = null;
|
this.oMessageScrollerDom = null;
|
||||||
|
|
||||||
this.bodyBackgroundColor = ko.observable('');
|
this.addObservables({
|
||||||
|
bodyBackgroundColor: '',
|
||||||
|
showAttachmnetControls: false,
|
||||||
|
downloadAsZipLoading: false,
|
||||||
|
lastReplyAction_: '',
|
||||||
|
showFullInfo: '1' === Local.get(ClientSideKeyName.MessageHeaderFullInfo),
|
||||||
|
moreDropdownTrigger: false
|
||||||
|
});
|
||||||
|
|
||||||
this.pswp = null;
|
this.pswp = null;
|
||||||
|
|
||||||
|
|
@ -103,50 +114,12 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
this.messageListOfThreadsLoading = ko.observable(false).extend({ rateLimit: 1 });
|
this.messageListOfThreadsLoading = ko.observable(false).extend({ rateLimit: 1 });
|
||||||
this.highlightUnselectedAttachments = ko.observable(false).extend({ falseTimeout: 2000 });
|
this.highlightUnselectedAttachments = ko.observable(false).extend({ falseTimeout: 2000 });
|
||||||
|
|
||||||
this.showAttachmnetControls = ko.observable(false);
|
|
||||||
|
|
||||||
this.showAttachmnetControlsState = v => Local.set(ClientSideKeyName.MessageAttachmentControls, !!v);
|
this.showAttachmnetControlsState = v => Local.set(ClientSideKeyName.MessageAttachmentControls, !!v);
|
||||||
|
|
||||||
this.allowAttachmnetControls = ko.computed(
|
|
||||||
() => this.attachmentsActions().length && Settings.capa(Capa.AttachmentsActions)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.downloadAsZipAllowed = ko.computed(
|
|
||||||
() => this.attachmentsActions().includes('zip') && this.allowAttachmnetControls()
|
|
||||||
);
|
|
||||||
|
|
||||||
this.downloadAsZipLoading = ko.observable(false);
|
|
||||||
this.downloadAsZipError = ko.observable(false).extend({ falseTimeout: 7000 });
|
this.downloadAsZipError = ko.observable(false).extend({ falseTimeout: 7000 });
|
||||||
|
|
||||||
this.showAttachmnetControls.subscribe(v => this.message()
|
|
||||||
&& this.message().attachments().forEach(item => item && item.checked(!!v))
|
|
||||||
);
|
|
||||||
|
|
||||||
this.lastReplyAction_ = ko.observable('');
|
|
||||||
this.lastReplyAction = ko.computed({
|
|
||||||
read: this.lastReplyAction_,
|
|
||||||
write: value => this.lastReplyAction_(
|
|
||||||
[ComposeType.Reply, ComposeType.ReplyAll, ComposeType.Forward].includes(value)
|
|
||||||
? ComposeType.Reply
|
|
||||||
: value
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
this.lastReplyAction(Local.get(ClientSideKeyName.LastReplyAction) || ComposeType.Reply);
|
|
||||||
|
|
||||||
this.lastReplyAction_.subscribe(value => Local.set(ClientSideKeyName.LastReplyAction, value));
|
|
||||||
|
|
||||||
this.showFullInfo = ko.observable('1' === Local.get(ClientSideKeyName.MessageHeaderFullInfo));
|
|
||||||
|
|
||||||
this.moreDropdownTrigger = ko.observable(false);
|
|
||||||
this.messageDomFocused = ko.observable(false).extend({ rateLimit: 0 });
|
this.messageDomFocused = ko.observable(false).extend({ rateLimit: 0 });
|
||||||
|
|
||||||
this.messageVisibility = ko.computed(() => !this.messageLoadingThrottle() && !!this.message());
|
|
||||||
|
|
||||||
this.message.subscribe(message => (!message) && MessageStore.selectorMessageSelected(null));
|
|
||||||
|
|
||||||
this.canBeRepliedOrForwarded = ko.computed(() => !this.isDraftFolder() && this.messageVisibility());
|
|
||||||
|
|
||||||
// commands
|
// commands
|
||||||
this.replyCommand = createCommandReplyHelper(ComposeType.Reply);
|
this.replyCommand = createCommandReplyHelper(ComposeType.Reply);
|
||||||
this.replyAllCommand = createCommandReplyHelper(ComposeType.ReplyAll);
|
this.replyAllCommand = createCommandReplyHelper(ComposeType.ReplyAll);
|
||||||
|
|
@ -162,56 +135,39 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
|
|
||||||
// viewer
|
// viewer
|
||||||
|
|
||||||
this.viewBodyTopValue = ko.observable(0);
|
|
||||||
|
|
||||||
this.viewFolder = '';
|
this.viewFolder = '';
|
||||||
this.viewUid = '';
|
this.viewUid = '';
|
||||||
this.viewHash = '';
|
this.viewHash = '';
|
||||||
this.viewSubject = ko.observable('');
|
this.addObservables({
|
||||||
this.viewFromShort = ko.observable('');
|
viewBodyTopValue: 0,
|
||||||
this.viewFromDkimData = ko.observable(['none', '']);
|
viewSubject: '',
|
||||||
this.viewToShort = ko.observable('');
|
viewFromShort: '',
|
||||||
this.viewFrom = ko.observable('');
|
viewFromDkimData: ['none', ''],
|
||||||
this.viewTo = ko.observable('');
|
viewToShort: '',
|
||||||
this.viewCc = ko.observable('');
|
viewFrom: '',
|
||||||
this.viewBcc = ko.observable('');
|
viewTo: '',
|
||||||
this.viewReplyTo = ko.observable('');
|
viewCc: '',
|
||||||
this.viewTimeStamp = ko.observable(0);
|
viewBcc: '',
|
||||||
this.viewSize = ko.observable('');
|
viewReplyTo: '',
|
||||||
this.viewLineAsCss = ko.observable('');
|
viewTimeStamp: 0,
|
||||||
this.viewViewLink = ko.observable('');
|
viewSize: '',
|
||||||
this.viewUnsubscribeLink = ko.observable('');
|
viewLineAsCss: '',
|
||||||
this.viewDownloadLink = ko.observable('');
|
viewViewLink: '',
|
||||||
this.viewIsImportant = ko.observable(false);
|
viewUnsubscribeLink: '',
|
||||||
this.viewIsFlagged = ko.observable(false);
|
viewDownloadLink: '',
|
||||||
|
viewIsImportant: false,
|
||||||
this.viewFromDkimVisibility = ko.computed(() => 'none' !== this.viewFromDkimData()[0]);
|
viewIsFlagged: false
|
||||||
|
|
||||||
this.viewFromDkimStatusIconClass = ko.computed(() => {
|
|
||||||
switch (this.viewFromDkimData()[0]) {
|
|
||||||
case 'none':
|
|
||||||
return 'icon-none iconcolor-display-none';
|
|
||||||
case 'pass':
|
|
||||||
return 'icon-ok iconcolor-green';
|
|
||||||
default:
|
|
||||||
return 'icon-warning-alt iconcolor-red';
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.viewFromDkimStatusTitle = ko.computed(() => {
|
this.addSubscribables({
|
||||||
const status = this.viewFromDkimData();
|
showAttachmnetControls: v => this.message()
|
||||||
if (Array.isNotEmpty(status)) {
|
&& this.message().attachments().forEach(item => item && item.checked(!!v)),
|
||||||
if (status[0]) {
|
|
||||||
return status[1] || 'DKIM: ' + status[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
lastReplyAction_: value => Local.set(ClientSideKeyName.LastReplyAction, value),
|
||||||
});
|
|
||||||
|
|
||||||
this.messageActiveDom.subscribe(dom => this.bodyBackgroundColor(this.detectDomBackgroundColor(dom)), this);
|
messageActiveDom: dom => this.bodyBackgroundColor(this.detectDomBackgroundColor(dom)),
|
||||||
|
|
||||||
this.message.subscribe((message) => {
|
message: message => {
|
||||||
this.messageActiveDom(null);
|
this.messageActiveDom(null);
|
||||||
|
|
||||||
if (message) {
|
if (message) {
|
||||||
|
|
@ -247,12 +203,17 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
this.viewIsImportant(message.isImportant());
|
this.viewIsImportant(message.isImportant());
|
||||||
this.viewIsFlagged(message.isFlagged());
|
this.viewIsFlagged(message.isFlagged());
|
||||||
} else {
|
} else {
|
||||||
|
MessageStore.selectorMessageSelected(null);
|
||||||
|
|
||||||
this.viewFolder = '';
|
this.viewFolder = '';
|
||||||
this.viewUid = '';
|
this.viewUid = '';
|
||||||
this.viewHash = '';
|
this.viewHash = '';
|
||||||
|
|
||||||
this.scrollMessageToTop();
|
this.scrollMessageToTop();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
fullScreenMode: value => $htmlCL.toggle('rl-message-fullscreen', value)
|
||||||
});
|
});
|
||||||
|
|
||||||
this.message.viewTrigger.subscribe(() => {
|
this.message.viewTrigger.subscribe(() => {
|
||||||
|
|
@ -260,13 +221,55 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
message ? this.viewIsFlagged(message.isFlagged()) : this.viewIsFlagged(false);
|
message ? this.viewIsFlagged(message.isFlagged()) : this.viewIsFlagged(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.fullScreenMode.subscribe(value => $htmlCL.toggle('rl-message-fullscreen', value));
|
this.addComputables({
|
||||||
|
allowAttachmnetControls: () => this.attachmentsActions().length && Settings.capa(Capa.AttachmentsActions),
|
||||||
|
|
||||||
this.messageFocused = ko.computed(() => Focused.MessageView === AppStore.focusedState());
|
downloadAsZipAllowed: () => this.attachmentsActions().includes('zip') && this.allowAttachmnetControls(),
|
||||||
|
|
||||||
this.messageListAndMessageViewLoading = ko.computed(
|
lastReplyAction: {
|
||||||
|
read: this.lastReplyAction_,
|
||||||
|
write: value => this.lastReplyAction_(
|
||||||
|
[ComposeType.Reply, ComposeType.ReplyAll, ComposeType.Forward].includes(value)
|
||||||
|
? ComposeType.Reply
|
||||||
|
: value
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
messageVisibility: () => !this.messageLoadingThrottle() && !!this.message(),
|
||||||
|
|
||||||
|
canBeRepliedOrForwarded: () => !this.isDraftFolder() && this.messageVisibility(),
|
||||||
|
|
||||||
|
viewFromDkimVisibility: () => 'none' !== this.viewFromDkimData()[0],
|
||||||
|
|
||||||
|
viewFromDkimStatusIconClass:() => {
|
||||||
|
switch (this.viewFromDkimData()[0]) {
|
||||||
|
case 'none':
|
||||||
|
return 'icon-none iconcolor-display-none';
|
||||||
|
case 'pass':
|
||||||
|
return 'icon-ok iconcolor-green';
|
||||||
|
default:
|
||||||
|
return 'icon-warning-alt iconcolor-red';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
viewFromDkimStatusTitle:() => {
|
||||||
|
const status = this.viewFromDkimData();
|
||||||
|
if (Array.isNotEmpty(status)) {
|
||||||
|
if (status[0]) {
|
||||||
|
return status[1] || 'DKIM: ' + status[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
|
||||||
|
messageFocused: () => Focused.MessageView === AppStore.focusedState(),
|
||||||
|
|
||||||
|
messageListAndMessageViewLoading:
|
||||||
() => MessageStore.messageListCompleteLoadingThrottle() || MessageStore.messageLoadingThrottle()
|
() => MessageStore.messageListCompleteLoadingThrottle() || MessageStore.messageLoadingThrottle()
|
||||||
);
|
});
|
||||||
|
|
||||||
|
this.lastReplyAction(Local.get(ClientSideKeyName.LastReplyAction) || ComposeType.Reply);
|
||||||
|
|
||||||
addEventListener('mailbox.message-view.toggle-full-screen', () => this.toggleFullScreen());
|
addEventListener('mailbox.message-view.toggle-full-screen', () => this.toggleFullScreen());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ class Folder implements \JsonSerializable
|
||||||
/**
|
/**
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private $bExisten;
|
private $bExists;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool
|
* @var bool
|
||||||
|
|
@ -50,7 +50,7 @@ class Folder implements \JsonSerializable
|
||||||
/**
|
/**
|
||||||
* @throws \MailSo\Base\Exceptions\InvalidArgumentException
|
* @throws \MailSo\Base\Exceptions\InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
function __construct(\MailSo\Imap\Folder $oImapFolder, bool $bSubscribed = true, bool $bExisten = true)
|
function __construct(\MailSo\Imap\Folder $oImapFolder, bool $bSubscribed = true, bool $bExists = true)
|
||||||
{
|
{
|
||||||
$this->oImapFolder = $oImapFolder;
|
$this->oImapFolder = $oImapFolder;
|
||||||
$this->oSubFolders = null;
|
$this->oSubFolders = null;
|
||||||
|
|
@ -66,7 +66,7 @@ class Folder implements \JsonSerializable
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->bSubscribed = $bSubscribed;
|
$this->bSubscribed = $bSubscribed;
|
||||||
$this->bExisten = $bExisten;
|
$this->bExists = $bExists;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -164,12 +164,12 @@ class Folder implements \JsonSerializable
|
||||||
|
|
||||||
public function IsExists() : bool
|
public function IsExists() : bool
|
||||||
{
|
{
|
||||||
return $this->bExisten;
|
return $this->bExists;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function IsSelectable() : bool
|
public function IsSelectable() : bool
|
||||||
{
|
{
|
||||||
return $this->IsExists() && $this->oImapFolder->IsSelectable();
|
return $this->bExists && $this->oImapFolder->IsSelectable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -236,9 +236,9 @@ class Folder implements \JsonSerializable
|
||||||
'FullNameRaw' => $this->FullNameRaw(),
|
'FullNameRaw' => $this->FullNameRaw(),
|
||||||
'Delimiter' => (string) $this->Delimiter(),
|
'Delimiter' => (string) $this->Delimiter(),
|
||||||
'HasVisibleSubFolders' => $this->HasVisibleSubFolders(),
|
'HasVisibleSubFolders' => $this->HasVisibleSubFolders(),
|
||||||
'IsSubscribed' => $this->IsSubscribed(),
|
'Subscribed' => $this->bSubscribed,
|
||||||
'IsExists' => $this->IsExists(),
|
'Exists' => $this->bExists,
|
||||||
'IsSelectable' => $this->IsSelectable(),
|
'Selectable' => $this->IsSelectable(),
|
||||||
'Flags' => $this->FlagsLowerCase()
|
'Flags' => $this->FlagsLowerCase()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="controls"
|
<div class="controls"
|
||||||
data-bind="visible: additionalCode.visibility(), css: {'error': additionalCode.error, 'animated': additionalCode.errorAnimation}">
|
data-bind="visible: additionalCodeVisibility(), css: {'error': additionalCodeError, 'animated': additionalCodeErrorAnimation}">
|
||||||
<div class="input-append">
|
<div class="input-append">
|
||||||
<input type="text" class="i18n input-block-level inputAdditionalCode" autocomplete="off"
|
<input type="text" class="i18n input-block-level inputAdditionalCode" autocomplete="off"
|
||||||
autocorrect="off" autocapitalize="off" spellcheck="false" style="padding-right: 35px;"
|
autocorrect="off" autocapitalize="off" spellcheck="false" style="padding-right: 35px;"
|
||||||
|
|
@ -71,7 +71,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="controls plugin-mark-Login-BottomControlGroup"
|
<div class="controls plugin-mark-Login-BottomControlGroup"
|
||||||
data-bind="visible: additionalCode.visibility()">
|
data-bind="visible: additionalCodeVisibility()">
|
||||||
<div class="additionalCodeSignMeLabel" data-bind="component: {
|
<div class="additionalCodeSignMeLabel" data-bind="component: {
|
||||||
name: 'CheckboxSimple',
|
name: 'CheckboxSimple',
|
||||||
params: {
|
params: {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="e-item" data-bind="visible: visible, css: { 'i-am-inbox-wrapper': isInbox }">
|
<div class="e-item" data-bind="visible: visible, css: { 'i-am-inbox-wrapper': isInbox }">
|
||||||
<a class="e-link" data-bind="dropmessages: $data,
|
<a class="e-link" data-bind="dropmessages: $data,
|
||||||
css: { 'i-am-inbox': isInbox, 'selected': selected() && !isSystemFolder(), 'selectable': selectableForFolderList, 'hidden' : hidden, 'print-count': hasUnreadMessages, 'unread-sub': hasSubScribedUnreadMessagesSubfolders, 'system': isSystemFolder, 'anim-action-class': actionBlink }">
|
css: { 'i-am-inbox': isInbox, 'selected': selected() && !isSystemFolder(), 'selectable': selectableForFolderList, 'hidden' : hidden, 'print-count': hasUnreadMessages, 'unread-sub': hasSubscribedUnreadMessagesSubfolders, 'system': isSystemFolder, 'anim-action-class': actionBlink }">
|
||||||
<span class="badge pull-right count" data-bind="text: printableUnreadCount"></span>
|
<span class="badge pull-right count" data-bind="text: printableUnreadCount"></span>
|
||||||
<i data-bind="css: collapsedCss()"></i>
|
<i data-bind="css: collapsedCss()"></i>
|
||||||
<span class="focused-poiner"></span>
|
<span class="focused-poiner"></span>
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="alert" data-bind="visible: key.error() && key.errorMessage(), text: key.errorMessage"></div>
|
<div class="alert" data-bind="visible: keyError() && keyErrorMessage(), text: keyErrorMessage"></div>
|
||||||
<div class="form-horizontal">
|
<div class="form-horizontal">
|
||||||
<div class="control-group" data-bind="css: {'error': key.error}">
|
<div class="control-group" data-bind="css: {'error': keyError}">
|
||||||
<textarea class="inputKey input-xxlarge" rows="14" autofocus="" autocomplete="off" data-bind="value: key"></textarea>
|
<textarea class="inputKey input-xxlarge" rows="14" autofocus="" autocomplete="off" data-bind="value: key"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="e-cell e-value">
|
<div class="e-cell e-value">
|
||||||
<input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" data-bind="emailsTags: to, emailsTagsFocus: to.focused, autoCompleteSource: emailsSource" />
|
<input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" data-bind="emailsTags: to, emailsTagsFocus: toFocused, autoCompleteSource: emailsSource" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="e-row cc-row" data-bind="visible: showCc">
|
<div class="e-row cc-row" data-bind="visible: showCc">
|
||||||
|
|
@ -143,7 +143,7 @@
|
||||||
<span class="i18n" data-i18n="COMPOSE/TITLE_SUBJECT"></span>
|
<span class="i18n" data-i18n="COMPOSE/TITLE_SUBJECT"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="e-cell e-value">
|
<div class="e-cell e-value">
|
||||||
<input type="text" size="70" autocomplete="off" data-bind="textInput: subject, hasFocus: subject.focused" />
|
<input type="text" size="70" autocomplete="off" data-bind="textInput: subject, hasFocus: subjectFocused" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="e-row">
|
<div class="e-row">
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
</label>
|
</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="text" class="uiInput inputName" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
<input type="text" class="uiInput inputName" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||||
data-bind="textInput: folderName, hasfocus: folderName.focused, onEnter: createFolderCommand" />
|
data-bind="textInput: folderName, hasfocus: folderNameFocused, onEnter: createFolderCommand" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="email" class="inputEmail input-xlarge" autofocus=""
|
<input type="email" class="inputEmail input-xlarge" autofocus=""
|
||||||
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||||
data-bind="value: email, onEnter: addOrEditIdentityCommand, hasfocus: email.focused" />
|
data-bind="value: email, onEnter: addOrEditIdentityCommand, hasfocus: emailFocused" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group" data-bind="visible: owner">
|
<div class="control-group" data-bind="visible: owner">
|
||||||
|
|
@ -41,7 +41,7 @@
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="text" class="inputReplyTo input-xlarge"
|
<input type="text" class="inputReplyTo input-xlarge"
|
||||||
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||||
data-bind="value: replyTo, onEnter: addOrEditIdentityCommand, hasfocus: replyTo.focused" />
|
data-bind="value: replyTo, onEnter: addOrEditIdentityCommand, hasfocus: replyToFocused" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group" data-bind="visible: showBcc, css: {'error': bcc.hasError}">
|
<div class="control-group" data-bind="visible: showBcc, css: {'error': bcc.hasError}">
|
||||||
|
|
@ -49,7 +49,7 @@
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="text" class="inputBcc input-xlarge"
|
<input type="text" class="inputBcc input-xlarge"
|
||||||
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||||
data-bind="value: bcc, onEnter: addOrEditIdentityCommand, hasfocus: bcc.focused" />
|
data-bind="value: bcc, onEnter: addOrEditIdentityCommand, hasfocus: bccFocused" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group" data-bind="visible: !showReplyTo() || !showBcc()">
|
<div class="control-group" data-bind="visible: !showReplyTo() || !showBcc()">
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,12 @@
|
||||||
<span data-bind="text: submitError"></span>
|
<span data-bind="text: submitError"></span>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div class="control-group" data-bind="css: {'error': email.error}">
|
<div class="control-group" data-bind="css: {'error': emailError}">
|
||||||
<label class="i18n control-label" data-i18n="POPUPS_GENERATE_OPEN_PGP_KEYS/LABEL_EMAIL"></label>
|
<label class="i18n control-label" data-i18n="POPUPS_GENERATE_OPEN_PGP_KEYS/LABEL_EMAIL"></label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="email" class="inputEmail input-large"
|
<input type="email" class="inputEmail input-large"
|
||||||
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||||
data-bind="value: email, hasfocus: email.focus" />
|
data-bind="value: email, hasfocus: emailFocus" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,17 @@
|
||||||
<span data-bind="text: submitError"></span>
|
<span data-bind="text: submitError"></span>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div class="control-group" data-bind="css: {'error': name.error}">
|
<div class="control-group" data-bind="css: {'error': nameError}">
|
||||||
<label class="i18n control-label" data-i18n="POPUPS_ADD_TEMPLATE/LABEL_NAME"></label>
|
<label class="i18n control-label" data-i18n="POPUPS_ADD_TEMPLATE/LABEL_NAME"></label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="text" class="inputName input-xlarge" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
<input type="text" class="inputName input-xlarge" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||||
data-bind="textInput: name, onEnter: addTemplateCommand, hasfocus: name.focus" />
|
data-bind="textInput: name, onEnter: addTemplateCommand, hasfocus: nameFocus" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<div class="form-horizontal">
|
<div class="form-horizontal">
|
||||||
<div class="control-group" data-bind="css: {'error': body.error}">
|
<div class="control-group" data-bind="css: {'error': bodyError}">
|
||||||
<div class="e-template-place" data-bind="initDom: signatureDom"></div>
|
<div class="e-template-place" data-bind="initDom: signatureDom"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,14 @@
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="text" class="uiInput inputName"
|
<input type="text" class="uiInput inputName"
|
||||||
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||||
data-bind="textInput: code, hasfocus: code.focused, onEnter: testCodeCommand" />
|
data-bind="textInput: code, hasfocus: codeFocused, onEnter: testCodeCommand" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<a class="btn" data-bind="command: testCodeCommand, css: { 'btn-success': true === code.status(), 'btn-danger': false === code.status() }">
|
<a class="btn" data-bind="command: testCodeCommand, css: { 'btn-success': true === codeStatus(), 'btn-danger': false === codeStatus() }">
|
||||||
<i data-bind="css: {'icon-ok': !testing(), 'icon-spinner animated': testing(), 'icon-white': true === code.status() || false === code.status() }"></i>
|
<i data-bind="css: {'icon-ok': !testing(), 'icon-spinner animated': testing(), 'icon-white': true === codeStatus() || false === codeStatus() }"></i>
|
||||||
|
|
||||||
<span class="i18n" data-i18n="POPUPS_TWO_FACTOR_TEST/BUTTON_TEST"></span>
|
<span class="i18n" data-i18n="POPUPS_TWO_FACTOR_TEST/BUTTON_TEST"></span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,18 @@
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="subscribe-folder-parent">
|
<td class="subscribe-folder-parent">
|
||||||
<span class="unsubscribe-folder" data-bind="visible: canBeSubScribed() && !subScribed(), click: function(oFolder) { $root.subscribeFolder(oFolder); }">
|
<span class="unsubscribe-folder" data-bind="visible: canBeSubscribed() && !subscribed(), click: function(oFolder) { $root.subscribeFolder(oFolder); }">
|
||||||
<i class="icon-eye"></i>
|
<i class="icon-eye"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="subscribe-folder" data-bind="visible: canBeSubScribed() && subScribed(), click: function(oFolder) { $root.unSubscribeFolder(oFolder); }">
|
<span class="subscribe-folder" data-bind="visible: canBeSubscribed() && subscribed(), click: function(oFolder) { $root.unSubscribeFolder(oFolder); }">
|
||||||
<i class="icon-eye"></i>
|
<i class="icon-eye"></i>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="check-folder-parent" data-bind="visible: $root.displaySpecSetting">
|
<td class="check-folder-parent" data-bind="visible: $root.displaySpecSetting">
|
||||||
<span class="uncheck-folder" data-bind="visible: canBeChecked() && subScribed() && !checkable(), click: function(oFolder) { $root.checkableTrueFolder(oFolder); }">
|
<span class="uncheck-folder" data-bind="visible: canBeChecked() && subscribed() && !checkable(), click: function(oFolder) { $root.checkableTrueFolder(oFolder); }">
|
||||||
<i class="icon-check-mark-circle-two"></i>
|
<i class="icon-check-mark-circle-two"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="check-folder" data-bind="visible: canBeChecked() && subScribed() && checkable(), click: function(oFolder) { $root.checkableFalseFolder(oFolder); }">
|
<span class="check-folder" data-bind="visible: canBeChecked() && subscribed() && checkable(), click: function(oFolder) { $root.checkableFalseFolder(oFolder); }">
|
||||||
<i class="icon-check-mark-circle-two"></i>
|
<i class="icon-check-mark-circle-two"></i>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue