diff --git a/dev/Common/UtilsUser.js b/dev/Common/UtilsUser.js index f9e896eef..613b82ef9 100644 --- a/dev/Common/UtilsUser.js +++ b/dev/Common/UtilsUser.js @@ -1,5 +1,4 @@ import { ComposeType, FolderType } from 'Common/Enums'; -import { pString } from 'Common/Utils'; const tpl = document.createElement('template'), @@ -303,13 +302,13 @@ export function folderListOptionsBuilder( bSep = true; aList.forEach(oItem => { - // if (oItem.subScribed() || !oItem.existen || bBuildUnvisible) + // if (oItem.subscribed() || !oItem.exists || bBuildUnvisible) if ( - (oItem.subScribed() || !oItem.existen || bBuildUnvisible) && - (oItem.selectable || oItem.hasSubScribedSubfolders()) + (oItem.subscribed() || !oItem.exists || bBuildUnvisible) && + (oItem.selectable || oItem.hasSubscribedSubfolders()) ) { if (fVisibleCallback ? fVisibleCallback(oItem) : true) { - if (FolderType.User === oItem.type() || !bSystem || oItem.hasSubScribedSubfolders()) { + if (FolderType.User === oItem.type() || !bSystem || oItem.hasSubscribedSubfolders()) { aResult.push({ id: oItem.fullNameRaw, name: @@ -327,7 +326,7 @@ export function folderListOptionsBuilder( } } - if (oItem.subScribed() && oItem.subFolders().length) { + if (oItem.subscribed() && oItem.subFolders().length) { aResult = aResult.concat( folderListOptionsBuilder( [], @@ -349,17 +348,14 @@ export function folderListOptionsBuilder( } /** + * Call the Model/CollectionModel onDestroy() to clear knockout functions/objects * @param {Object|Array} objectOrObjects * @returns {void} */ export function delegateRunOnDestroy(objectOrObjects) { - if (objectOrObjects) { - if (isArray(objectOrObjects)) { - objectOrObjects.forEach(item => delegateRunOnDestroy(item)); - } else { - objectOrObjects.onDestroy && objectOrObjects.onDestroy(); - } - } + objectOrObjects && (isArray(objectOrObjects) ? objectOrObjects : [objectOrObjects]).forEach( + obj => obj.onDestroy && obj.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 {Function} PopupComposeViewModel @@ -524,8 +512,8 @@ export function mailToHelper(mailToUrl, PopupComposeViewModel) { to, cc, bcc, - null == params.subject ? null : pString(decodeURIComponent(params.subject)), - null == params.body ? null : plainToHtml(pString(decodeURIComponent(params.body))) + null == params.subject ? null : decodeURIComponent(params.subject), + null == params.body ? null : plainToHtml(decodeURIComponent(params.body)) ]); return true; diff --git a/dev/External/ko.js b/dev/External/ko.js index c98bff8f1..03f94965e 100644 --- a/dev/External/ko.js +++ b/dev/External/ko.js @@ -262,4 +262,19 @@ ko.observable.fn.deleteAccessHelper = function() { 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; diff --git a/dev/Knoin/AbstractModel.js b/dev/Knoin/AbstractModel.js index ca74afc25..4ab6ad20a 100644 --- a/dev/Knoin/AbstractModel.js +++ b/dev/Knoin/AbstractModel.js @@ -1,5 +1,5 @@ -function disposeOne(disposable) { +function dispose(disposable) { if (disposable && 'function' === typeof disposable.dispose) { disposable.dispose(); } @@ -22,7 +22,7 @@ function typeCast(curValue, newValue) { } export class AbstractModel { - disposables = []; + subscribables = []; constructor() { /* @@ -32,27 +32,30 @@ export class AbstractModel { */ } - addObservables(obj) { - Object.entries(obj).forEach(([key, value]) => this[key] = ko.observable(value) ); -/* - Object.entries(obj).forEach(([key, value]) => - this[key] = Array.isArray(value) ? ko.observableArray(value) : ko.observable(value) - ); -*/ + addObservables(observables) { + ko.addObservablesTo(this, observables); } - addComputables(obj) { - Object.entries(obj).forEach(([key, fn]) => this[key] = ko.computed(fn) ); + addComputables(computables) { + ko.addComputablesTo(this, computables); } - addSubscribables(obj) { - Object.entries(obj).forEach(([key, fn]) => this.disposables.push( this[key].subscribe(fn) ) ); + addSubscribables(subscribables) { + Object.entries(subscribables).forEach(([key, fn]) => this.subscribables.push( this[key].subscribe(fn) ) ); } + /** Called by delegateRunOnDestroy */ onDestroy() { - this.disposables.forEach(disposeOne); + /** clear ko.subscribe */ + this.subscribables.forEach(dispose); + /** clear object entries */ 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; }); } diff --git a/dev/Knoin/AbstractViewNext.js b/dev/Knoin/AbstractViewNext.js index d0be60624..6f485c514 100644 --- a/dev/Knoin/AbstractViewNext.js +++ b/dev/Knoin/AbstractViewNext.js @@ -56,4 +56,16 @@ export class AbstractViewNext { return this.viewModelDom.querySelector(selectors); } + addObservables(observables) { + ko.addObservablesTo(this, observables); + } + + addComputables(computables) { + ko.addComputablesTo(this, computables); + } + + addSubscribables(subscribables) { + ko.addSubscribablesTo(this, subscribables); + } + } diff --git a/dev/Model/AbstractCollection.js b/dev/Model/AbstractCollection.js index 285ec275f..ae84b6334 100644 --- a/dev/Model/AbstractCollection.js +++ b/dev/Model/AbstractCollection.js @@ -10,6 +10,10 @@ export class AbstractCollectionModel extends Array super(); } + onDestroy() { + this.forEach(item => item.onDestroy && item.onDestroy()); + } + /** * @static * @param {FetchJson} json diff --git a/dev/Model/Folder.js b/dev/Model/Folder.js index fae1b8e80..7580bd467 100644 --- a/dev/Model/Folder.js +++ b/dev/Model/Folder.js @@ -20,7 +20,7 @@ class FolderModel extends AbstractModel { this.interval = 0; this.selectable = false; - this.existen = true; + this.exists = true; this.addObservables({ name: '', @@ -29,7 +29,7 @@ class FolderModel extends AbstractModel { focused: false, selected: false, edited: false, - subScribed: true, + subscribed: true, checkable: false, deleteAccess: false, @@ -54,10 +54,6 @@ class FolderModel extends AbstractModel { const folder = super.reviveFromJson(json); if (folder) { 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({ read: folder.privateMessageCountAll, @@ -87,26 +83,26 @@ class FolderModel extends AbstractModel { isInbox: () => FolderType.Inbox === folder.type(), - hasSubScribedSubfolders: + hasSubscribedSubfolders: () => !!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: () => { - const isSubScribed = folder.subScribed(), - isSubFolders = folder.hasSubScribedSubfolders(); + const isSubscribed = folder.subscribed(), + isSubFolders = folder.hasSubscribedSubfolders(); - return isSubScribed || (isSubFolders && (!folder.existen || !folder.selectable)); + return isSubscribed || (isSubFolders && (!folder.exists || !folder.selectable)); }, isSystemFolder: () => FolderType.User !== folder.type(), hidden: () => { const isSystem = folder.isSystemFolder(), - isSubFolders = folder.hasSubScribedSubfolders(); + isSubFolders = folder.hasSubscribedSubfolders(); return (isSystem && !isSubFolders) || (!folder.selectable && !isSubFolders); }, @@ -137,7 +133,7 @@ class FolderModel extends AbstractModel { selectableForFolderList: () => !folder.isSystemFolder() && folder.selectable, - canBeSubScribed: () => !folder.isSystemFolder() && folder.selectable, + canBeSubscribed: () => !folder.isSystemFolder() && folder.selectable, canBeChecked: () => !folder.isSystemFolder() && folder.selectable, @@ -221,9 +217,9 @@ class FolderModel extends AbstractModel { hasUnreadMessages: () => 0 < folder.messageCountUnread() && folder.printableUnreadCount(), - hasSubScribedUnreadMessagesSubfolders: () => + hasSubscribedUnreadMessagesSubfolders: () => !!folder.subFolders().find( - folder => folder.hasUnreadMessages() || folder.hasSubScribedUnreadMessagesSubfolders() + folder => folder.hasUnreadMessages() || folder.hasSubscribedUnreadMessagesSubfolders() ) }); @@ -246,7 +242,7 @@ class FolderModel extends AbstractModel { * @returns {string} */ collapsedCss() { - return 'e-collapsed-sign ' + (this.hasSubScribedSubfolders() + return 'e-collapsed-sign ' + (this.hasSubscribedSubfolders() ? (this.collapsed() ? 'icon-right-mini' : 'icon-down-mini') : 'icon-none' ); diff --git a/dev/Settings/User/Folders.js b/dev/Settings/User/Folders.js index e3e9f2380..fb48e6ea3 100644 --- a/dev/Settings/User/Folders.js +++ b/dev/Settings/User/Folders.js @@ -120,13 +120,13 @@ class FoldersUserSettings { subscribeFolder(folder) { Local.set(ClientSideKeyName.FoldersLashHash, ''); Remote.folderSetSubscribe(()=>{}, folder.fullNameRaw, true); - folder.subScribed(true); + folder.subscribed(true); } unSubscribeFolder(folder) { Local.set(ClientSideKeyName.FoldersLashHash, ''); Remote.folderSetSubscribe(()=>{}, folder.fullNameRaw, false); - folder.subScribed(false); + folder.subscribed(false); } checkableTrueFolder(folder) { diff --git a/dev/Settings/User/Themes.js b/dev/Settings/User/Themes.js index 1de837358..18c9e8c64 100644 --- a/dev/Settings/User/Themes.js +++ b/dev/Settings/User/Themes.js @@ -26,9 +26,6 @@ class ThemesUserSettings { this.themeTrigger = ko.observable(SaveSettingsStep.Idle).extend({ throttle: 100 }); - this.iTimer = 0; - this.oThemeAjaxRequest = null; - this.theme.subscribe((value) => { this.themesObjects().forEach(theme => { theme.selected(value === theme.name); diff --git a/dev/Stores/User/Folder.js b/dev/Stores/User/Folder.js index 1d358d39d..d6485fa66 100644 --- a/dev/Stores/User/Folder.js +++ b/dev/Stores/User/Folder.js @@ -160,9 +160,9 @@ class FolderUserStore { folder && inboxFolderName !== folder.fullNameRaw && folder.selectable && - folder.existen && + folder.exists && timeout > folder.interval && - (folder.isSystemFolder() || (folder.subScribed() && folder.checkable())) + (folder.isSystemFolder() || (folder.subscribed() && folder.checkable())) ) { timeouts.push([folder.interval, folder.fullNameRaw]); } diff --git a/dev/View/Admin/Login.js b/dev/View/Admin/Login.js index b8c607d1a..78cf527b3 100644 --- a/dev/View/Admin/Login.js +++ b/dev/View/Admin/Login.js @@ -23,29 +23,33 @@ class LoginAdminView extends AbstractViewNext { this.hideSubmitButton = appSettingsGet('hideSubmitButton'); - this.login = ko.observable(''); - this.password = ko.observable(''); + this.addObservables({ + login: '', + password: '', - this.loginError = ko.observable(false); - this.passwordError = ko.observable(false); + loginError: false, + passwordError: false, + + formHidden: false, + + submitRequest: false, + submitError: '' + }); this.loginErrorAnimation = 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.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)); - - this.submitRequest = ko.observable(false); - this.submitError = ko.observable(''); + passwordError: v => this.passwordErrorAnimation(!!v) + }); } @command((self) => !self.submitRequest()) diff --git a/dev/View/Popup/Account.js b/dev/View/Popup/Account.js index 94fb9637f..61b8bb6a7 100644 --- a/dev/View/Popup/Account.js +++ b/dev/View/Popup/Account.js @@ -1,5 +1,3 @@ -import ko from 'ko'; - import { StorageResultType, Notification } from 'Common/Enums'; import { getNotification } from 'Common/Translator'; @@ -16,27 +14,25 @@ class AccountPopupView extends AbstractViewNext { constructor() { super(); - this.isNew = ko.observable(true); + this.addObservables({ + isNew: true, - this.email = ko.observable(''); - this.password = ko.observable(''); + email: '', + password: '', - this.emailError = ko.observable(false); - this.passwordError = ko.observable(false); + emailError: false, + passwordError: false, - this.email.subscribe(() => { - this.emailError(false); + submitRequest: false, + submitError: '', + submitErrorAdditional: '', + + emailFocus: false }); - this.password.subscribe(() => { - this.passwordError(false); - }); + this.email.subscribe(() => this.emailError(false)); - this.submitRequest = ko.observable(false); - this.submitError = ko.observable(''); - this.submitErrorAdditional = ko.observable(''); - - this.emailFocus = ko.observable(false); + this.password.subscribe(() => this.passwordError(false)); } @command((self) => !self.submitRequest()) diff --git a/dev/View/Popup/AddOpenPgpKey.js b/dev/View/Popup/AddOpenPgpKey.js index 9d833604c..1a8449f25 100644 --- a/dev/View/Popup/AddOpenPgpKey.js +++ b/dev/View/Popup/AddOpenPgpKey.js @@ -1,5 +1,3 @@ -import ko from 'ko'; - import PgpStore from 'Stores/User/Pgp'; import { popup, command } from 'Knoin/Knoin'; @@ -13,13 +11,15 @@ class AddOpenPgpKeyPopupView extends AbstractViewNext { constructor() { super(); - this.key = ko.observable(''); - this.key.error = ko.observable(false); - this.key.errorMessage = ko.observable(''); + this.addObservables({ + key: '', + keyError: false, + keyErrorMessage: '' + }); this.key.subscribe(() => { - this.key.error(false); - this.key.errorMessage(''); + this.keyError(false); + this.keyErrorMessage(''); }); } @@ -35,10 +35,10 @@ class AddOpenPgpKeyPopupView extends AbstractViewNext { keyTrimmed = keyTrimmed.replace(/[\r]+/g, '').replace(/[\n]{2,}/g, '\n\n'); } - this.key.error(!keyTrimmed); - this.key.errorMessage(''); + this.keyError(!keyTrimmed); + this.keyErrorMessage(''); - if (!openpgpKeyring || this.key.error()) { + if (!openpgpKeyring || this.keyError()) { return false; } @@ -58,8 +58,8 @@ class AddOpenPgpKeyPopupView extends AbstractViewNext { } if (err) { - this.key.error(true); - this.key.errorMessage(err && err[0] ? '' + err[0] : ''); + this.keyError(true); + this.keyErrorMessage(err && err[0] ? '' + err[0] : ''); console.log(err); } } @@ -75,7 +75,7 @@ class AddOpenPgpKeyPopupView extends AbstractViewNext { rl.app.reloadOpenPgpKeys(); - if (this.key.error()) { + if (this.keyError()) { return false; } @@ -85,8 +85,8 @@ class AddOpenPgpKeyPopupView extends AbstractViewNext { clearPopup() { this.key(''); - this.key.error(false); - this.key.errorMessage(''); + this.keyError(false); + this.keyErrorMessage(''); } onShow() { diff --git a/dev/View/Popup/AdvancedSearch.js b/dev/View/Popup/AdvancedSearch.js index e8956bf01..27a002fe5 100644 --- a/dev/View/Popup/AdvancedSearch.js +++ b/dev/View/Popup/AdvancedSearch.js @@ -15,17 +15,19 @@ class AdvancedSearchPopupView extends AbstractViewNext { constructor() { super(); - this.fromFocus = ko.observable(false); + this.addObservables({ + fromFocus: false, - this.from = ko.observable(''); - this.to = ko.observable(''); - this.subject = ko.observable(''); - this.text = ko.observable(''); - this.selectedDateValue = ko.observable(-1); + from: '', + to: '', + subject: '', + text: '', + selectedDateValue: -1, - this.hasAttachment = ko.observable(false); - this.starred = ko.observable(false); - this.unseen = ko.observable(false); + hasAttachment: false, + starred: false, + unseen: false + }); this.selectedDates = ko.computed(() => { translatorTrigger(); diff --git a/dev/View/Popup/Ask.js b/dev/View/Popup/Ask.js index 911929cf9..44007e887 100644 --- a/dev/View/Popup/Ask.js +++ b/dev/View/Popup/Ask.js @@ -1,5 +1,3 @@ -import ko from 'ko'; - import { KeyState } from 'Common/Enums'; import { i18n } from 'Common/Translator'; @@ -14,9 +12,11 @@ class AskPopupView extends AbstractViewNext { constructor() { super(); - this.askDesc = ko.observable(''); - this.yesButton = ko.observable(''); - this.noButton = ko.observable(''); + this.addObservables({ + askDesc: '', + yesButton: '', + noButton: '' + }); this.fYesAction = null; this.fNoAction = null; diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index 8bc879785..5bc22585b 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -132,150 +132,86 @@ class ComposePopupView extends AbstractViewNext { this.capaOpenPGP = PgpStore.capaOpenPGP; - this.identitiesDropdownTrigger = ko.observable(false); + this.identities = IdentityStore.identities; - this.to = ko.observable(''); - this.to.focused = ko.observable(false); - this.cc = ko.observable(''); - this.cc.focused = ko.observable(false); - this.bcc = ko.observable(''); - this.bcc.focused = ko.observable(false); - this.replyTo = ko.observable(''); - this.replyTo.focused = ko.observable(false); + this.addObservables({ + identitiesDropdownTrigger: false, + + to: '', + toFocused: false, + cc: '', + ccFocused: 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)); ko.computed(() => { switch (true) { - case this.to.focused(): + case this.toFocused(): this.sLastFocusedField = 'to'; break; - case this.cc.focused(): + case this.ccFocused(): this.sLastFocusedField = 'cc'; break; - case this.bcc.focused(): + case this.bccFocused(): this.sLastFocusedField = 'bcc'; break; // no default } }).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.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.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({ toggleSubscribe: [ @@ -291,24 +227,105 @@ class ComposePopupView extends AbstractViewNext { ] }); - this.currentIdentityView = ko.computed(() => { - const item = this.currentIdentity(); - return item ? item.formattedName() : 'unknown'; + 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(); + return item ? item.formattedName() : 'unknown'; + }, + + canBeSentOrSaved: () => !this.sending() && !this.saving() + }); - this.to.subscribe((value) => { - if (this.emptyToError() && value.length) { - this.emptyToError(false); + 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) { + this.emptyToError(false); + } + }, + + attachmentsInProcess: value => { + if (this.attachmentsInProcessError() && Array.isNotEmpty(value)) { + this.attachmentsInProcessError(false); + } } }); - this.attachmentsInProcess.subscribe(value => { - if (this.attachmentsInProcessError() && Array.isNotEmpty(value)) { - this.attachmentsInProcessError(false); - } - }); - - this.canBeSentOrSaved = ko.computed(() => !this.sending() && !this.saving()); + this.resizeObserver = new ResizeObserver(this.resizerTrigger.throttle(50).bind(this)); setInterval(() => { if ( @@ -323,15 +340,6 @@ class ComposePopupView extends AbstractViewNext { this.saveCommand(); } }, 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) @@ -677,7 +685,7 @@ class ComposePopupView extends AbstractViewNext { this.bSkipNextHide = false; - this.to.focused(false); + this.toFocused(false); rl.route.on(); @@ -1120,9 +1128,9 @@ class ComposePopupView extends AbstractViewNext { // rl.settings.app('mobile') || setTimeout(() => { if (!this.to()) { - this.to.focused(true); + this.toFocused(true); } else if (this.oEditor) { - if (!this.to.focused()) { + if (!this.toFocused()) { this.oEditor.focus(); } } @@ -1222,10 +1230,7 @@ class ComposePopupView extends AbstractViewNext { if (attachment) { this.attachments.remove(attachment); delegateRunOnDestroy(attachment); - - if (oJua) { - oJua.cancel(id); - } + oJua && oJua.cancel(id); } }; } diff --git a/dev/View/Popup/ComposeOpenPgp.js b/dev/View/Popup/ComposeOpenPgp.js index 70984499a..48839e601 100644 --- a/dev/View/Popup/ComposeOpenPgp.js +++ b/dev/View/Popup/ComposeOpenPgp.js @@ -25,57 +25,59 @@ class ComposeOpenPgpPopupView extends AbstractViewNext { this.publicKeysOptionsCaption = i18n('PGP_NOTIFICATIONS/ADD_A_PUBLICK_KEY'); this.privateKeysOptionsCaption = i18n('PGP_NOTIFICATIONS/SELECT_A_PRIVATE_KEY'); - this.notification = ko.observable(''); + this.addObservables({ + notification: '', - this.sign = ko.observable(false); - this.encrypt = ko.observable(false); + sign: false, + encrypt: false, - this.password = ko.observable(''); + password: '', - this.text = ko.observable(''); - this.selectedPrivateKey = ko.observable(null); - this.selectedPublicKey = ko.observable(null); + text: '', + selectedPrivateKey: null, + selectedPublicKey: null, - this.signKey = ko.observable(null); + signKey: null, + + submitRequest: false + }); this.encryptKeys = ko.observableArray([]); - this.encryptKeysView = ko.computed( - () => this.encryptKeys().map(oKey => (oKey ? oKey.key : null)).filter(v => v) - ); + this.addComputables({ + encryptKeysView: () => this.encryptKeys().map(oKey => (oKey ? oKey.key : null)).filter(v => v), - this.privateKeysOptions = ko.computed(() => { - const opts = PgpStore.openpgpkeysPrivate().map((oKey, iIndex) => { - if (this.signKey() && this.signKey().key.id === oKey.id) { - return null; - } - return oKey.users.map(user => ({ - 'id': oKey.guid, - 'name': '(' + oKey.id.substr(KEY_NAME_SUBSTR).toUpperCase() + ') ' + user, - 'key': oKey, - 'class': iIndex % 2 ? 'odd' : 'even' - })); - }); + privateKeysOptions: () => { + const opts = PgpStore.openpgpkeysPrivate().map((oKey, iIndex) => { + if (this.signKey() && this.signKey().key.id === oKey.id) { + return null; + } + return oKey.users.map(user => ({ + 'id': oKey.guid, + 'name': '(' + oKey.id.substr(KEY_NAME_SUBSTR).toUpperCase() + ') ' + user, + 'key': oKey, + 'class': iIndex % 2 ? 'odd' : 'even' + })); + }); - return opts.flat().filter(v => v); + return opts.flat().filter(v => v); + }, + + publicKeysOptions: () => { + const opts = PgpStore.openpgpkeysPublic().map((oKey, index) => { + if (this.encryptKeysView().includes(oKey)) { + return null; + } + return oKey.users.map(user => ({ + 'id': oKey.guid, + 'name': '(' + oKey.id.substr(KEY_NAME_SUBSTR).toUpperCase() + ') ' + user, + 'key': oKey, + 'class': index % 2 ? 'odd' : 'even' + })); + }); + return opts.flat().filter(v => v); + } }); - this.publicKeysOptions = ko.computed(() => { - const opts = PgpStore.openpgpkeysPublic().map((oKey, index) => { - if (this.encryptKeysView().includes(oKey)) { - return null; - } - return oKey.users.map(user => ({ - 'id': oKey.guid, - 'name': '(' + oKey.id.substr(KEY_NAME_SUBSTR).toUpperCase() + ') ' + user, - 'key': oKey, - 'class': index % 2 ? 'odd' : 'even' - })); - }); - return opts.flat().filter(v => v); - }); - - this.submitRequest = ko.observable(false); - this.resultCallback = null; this.selectedPrivateKey.subscribe((value) => { diff --git a/dev/View/Popup/Contacts.js b/dev/View/Popup/Contacts.js index 803eb01f2..eca63b4ff 100644 --- a/dev/View/Popup/Contacts.js +++ b/dev/View/Popup/Contacts.js @@ -46,81 +46,36 @@ class ContactsPopupView extends AbstractViewNext { this.allowContactsSync = ContactStore.allowContactsSync; this.enableContactsSync = ContactStore.enableContactsSync; - this.search = ko.observable(''); - this.contactsCount = ko.observable(0); - this.contacts = ContactStore.contacts; + this.addObservables({ + search: '', + contactsCount: 0, - this.currentContact = ko.observable(null); + currentContact: null, - this.importUploaderButton = ko.observable(null); + importUploaderButton: null, - this.contactsPage = ko.observable(1); - this.contactsPageCount = ko.computed(() => - Math.max(1, Math.ceil(this.contactsCount() / CONTACTS_PER_PAGE)) - ); + contactsPage: 1, - this.contactsPaginator = ko.computed(computedPaginatorHelper(this.contactsPage, this.contactsPageCount)); + emptySelection: true, + viewClearSearch: false, - this.emptySelection = ko.observable(true); - this.viewClearSearch = ko.observable(false); + viewID: '', + viewReadOnly: false, - this.viewID = ko.observable(''); - this.viewReadOnly = ko.observable(false); - this.viewProperties = ko.observableArray([]); + viewSaveTrigger: SaveSettingsStep.Idle, - this.viewSaveTrigger = ko.observable(SaveSettingsStep.Idle); + viewSaving: false, - this.viewPropertiesNames = ko.computed(() => - this.viewProperties().filter( - 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)); + watchDirty: false, + watchHash: false }); - this.viewPropertiesPhones = ko.computed(() => - this.viewProperties().filter(property => ContactPropertyType.Phone === property.type()) - ); + this.contacts = ContactStore.contacts; - this.viewPropertiesEmailsNonEmpty = ko.computed(() => - this.viewPropertiesNames().filter(property => !!trim(property.value())) - ); + this.viewProperties = ko.observableArray([]); 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 const fFastClearEmptyListHelper = list => { @@ -129,33 +84,18 @@ class ContactsPopupView extends AbstractViewNext { delegateRunOnDestroy(list); } }; - this.viewPropertiesEmailsEmptyAndOnFocused.subscribe(fFastClearEmptyListHelper); - this.viewPropertiesPhonesEmptyAndOnFocused.subscribe(fFastClearEmptyListHelper); - this.viewPropertiesWebEmptyAndOnFocused.subscribe(fFastClearEmptyListHelper); - this.viewPropertiesOtherEmptyAndOnFocused.subscribe(fFastClearEmptyListHelper); + this.addSubscribables({ + viewPropertiesEmailsEmptyAndOnFocused: fFastClearEmptyListHelper, + viewPropertiesPhonesEmptyAndOnFocused: fFastClearEmptyListHelper, + viewPropertiesWebEmptyAndOnFocused: fFastClearEmptyListHelper, + viewPropertiesOtherEmptyAndOnFocused: fFastClearEmptyListHelper + }); */ - this.viewSaving = ko.observable(false); - this.useCheckboxesInList = SettingsStore.useCheckboxesInList; 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.contacts, this.currentContact, @@ -177,11 +117,6 @@ class ContactsPopupView extends AbstractViewNext { 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.viewHash.subscribe(() => { @@ -191,6 +126,61 @@ class ContactsPopupView extends AbstractViewNext { }); 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() diff --git a/dev/View/Popup/Domain.js b/dev/View/Popup/Domain.js index 53e57e5ce..a46f783a0 100644 --- a/dev/View/Popup/Domain.js +++ b/dev/View/Popup/Domain.js @@ -1,5 +1,3 @@ -import ko from 'ko'; - import { StorageResultType, ServerSecure, Notification } from 'Common/Enums'; import { pInt, pString } from 'Common/Utils'; import { i18n } from 'Common/Translator'; @@ -19,154 +17,155 @@ class DomainPopupView extends AbstractViewNext { constructor() { super(); - this.edit = ko.observable(false); - this.saving = ko.observable(false); - this.savingError = ko.observable(''); - this.page = ko.observable('main'); - this.sieveSettings = ko.observable(false); + this.addObservables({ + edit: false, + saving: false, + savingError: '', + page: 'main', + sieveSettings: false, - this.testing = ko.observable(false); - this.testingDone = ko.observable(false); - this.testingImapError = ko.observable(false); - this.testingSieveError = ko.observable(false); - this.testingSmtpError = ko.observable(false); - this.testingImapErrorDesc = ko.observable(''); - this.testingSieveErrorDesc = ko.observable(''); - this.testingSmtpErrorDesc = ko.observable(''); + testing: false, + testingDone: false, + testingImapError: false, + testingSieveError: false, + testingSmtpError: false, + testingImapErrorDesc: '', + testingSieveErrorDesc: '', + 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); - this.sieveServerFocus = ko.observable(false); - this.smtpServerFocus = ko.observable(false); + enableSmartPorts: false + }); - this.name = ko.observable(''); + this.addSubscribables({ + testingImapError: value => value || this.testingImapErrorDesc(''), + testingSieveError: value => value || this.testingSieveErrorDesc(''), + testingSmtpError: value => value || this.testingSmtpErrorDesc(''), - 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(''); + page: () => this.sieveSettings(false), - this.enableSmartPorts = ko.observable(false); + // smart form improvements + imapServerFocus: value => + value && this.name() && !this.imapServer() && this.imapServer(this.name().replace(/[.]?[*][.]?/g, '')), - this.allowSieve = ko.computed(() => CapaAdminStore.filters() && CapaAdminStore.sieve()); + sieveServerFocus: value => + value && this.imapServer() && !this.sieveServer() && this.sieveServer(this.imapServer()), - this.headerText = ko.computed(() => { - const name = this.name(), - aliasName = this.aliasName(); + smtpServerFocus: value => value && this.imapServer() && !this.smtpServer() + && this.smtpServer(this.imapServer().replace(/imap/gi, 'smtp')), - let result = ''; - - if (this.edit()) { - result = i18n('POPUPS_DOMAIN/TITLE_EDIT_DOMAIN', { 'NAME': name }); - if (aliasName) { - result += ' ← ' + aliasName; + imapSecure: value => { + if (this.enableSmartPorts()) { + const port = pInt(this.imapPort()); + switch (pString(value)) { + case '0': + case '2': + if (993 === port) { + this.imapPort('143'); + } + break; + case '1': + if (143 === port) { + this.imapPort('993'); + } + break; + // no default + } } - } 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(() => { - const name = this.name(); - return !this.edit() && name ? i18n('POPUPS_DOMAIN/NEW_DOMAIN_DESC', { 'NAME': '*@' + name }) : ''; - }); - - this.domainIsComputed = ko.computed(() => { - 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 - this.imapServerFocus.subscribe(value => - value && this.name() && !this.imapServer() && this.imapServer(this.name().replace(/[.]?[*][.]?/g, '')) - ); - - this.sieveServerFocus.subscribe(value => - value && this.imapServer() && !this.sieveServer() && this.sieveServer(this.imapServer()) - ); - - this.smtpServerFocus.subscribe(value => - value && this.imapServer() && !this.smtpServer() && this.smtpServer(this.imapServer().replace(/imap/gi, 'smtp')) - ); - - this.imapSecure.subscribe(value => { - if (this.enableSmartPorts()) { - const port = pInt(this.imapPort()); - switch (pString(value)) { - case '0': - case '2': - if (993 === port) { - this.imapPort('143'); - } - break; - case '1': - if (143 === port) { - this.imapPort('993'); - } - break; - // no default + smtpSecure: value => { + if (this.enableSmartPorts()) { + const port = pInt(this.smtpPort()); + switch (pString(value)) { + case '0': + if (465 === port || 587 === port) { + this.smtpPort('25'); + } + break; + case '1': + if (25 === port || 587 === port) { + this.smtpPort('465'); + } + break; + case '2': + if (25 === port || 465 === port) { + this.smtpPort('587'); + } + break; + // no default + } } } }); - this.smtpSecure.subscribe(value => { - if (this.enableSmartPorts()) { - const port = pInt(this.smtpPort()); - switch (pString(value)) { - case '0': - if (465 === port || 587 === port) { - this.smtpPort('25'); - } - break; - case '1': - if (25 === port || 587 === port) { - this.smtpPort('465'); - } - break; - case '2': - if (25 === port || 465 === port) { - this.smtpPort('587'); - } - break; - // 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() }); } diff --git a/dev/View/Popup/DomainAlias.js b/dev/View/Popup/DomainAlias.js index 8728b1e8f..3ce1fe97d 100644 --- a/dev/View/Popup/DomainAlias.js +++ b/dev/View/Popup/DomainAlias.js @@ -18,18 +18,18 @@ class DomainAliasPopupView extends AbstractViewNext { constructor() { super(); - this.saving = ko.observable(false); - this.savingError = ko.observable(''); + this.addObservables({ + saving: false, + savingError: '', - this.name = ko.observable(''); + name: '', - this.alias = ko.observable(''); + alias: '' + }); this.domains = DomainStore.domainsWithoutAliases; - this.domainsOptions = ko.computed(() => - this.domains().map(item => ({ optValue: item.name, optText: item.name })) - ); + this.domainsOptions = ko.computed(() => this.domains().map(item => ({ optValue: item.name, optText: item.name }))); this.canBeSaved = ko.computed(() => !this.saving() && this.name() && this.alias()); diff --git a/dev/View/Popup/Filter.js b/dev/View/Popup/Filter.js index 0813f769c..eac9655b6 100644 --- a/dev/View/Popup/Filter.js +++ b/dev/View/Popup/Filter.js @@ -18,24 +18,21 @@ class FilterPopupView extends AbstractViewNext { constructor() { super(); - this.isNew = ko.observable(true); + this.addObservables({ + isNew: true, + filter: null, + allowMarkAsRead: false, + selectedFolderValue: '' + }); this.modules = FilterStore.modules; this.fTrueCallback = null; - this.filter = ko.observable(null); - - this.allowMarkAsRead = ko.observable(false); this.defaultOptionsAfterRender = defaultOptionsAfterRender; this.folderSelectList = FolderStore.folderMenuForFilters; - this.selectedFolderValue = ko.observable(''); - this.selectedFolderValue.subscribe(() => { - if (this.filter()) { - this.filter().actionValueError(false); - } - }); + this.selectedFolderValue.subscribe(() => this.filter() && this.filter().actionValueError(false)); this.actionTypeOptions = ko.observableArray([]); this.fieldOptions = ko.observableArray([]); diff --git a/dev/View/Popup/FolderClear.js b/dev/View/Popup/FolderClear.js index 4b368da06..2b2c50ba9 100644 --- a/dev/View/Popup/FolderClear.js +++ b/dev/View/Popup/FolderClear.js @@ -1,5 +1,3 @@ -import ko from 'ko'; - import { StorageResultType, Notification } from 'Common/Enums'; import { i18n, getNotification } from 'Common/Translator'; import { setFolderHash } from 'Common/Cache'; @@ -19,23 +17,25 @@ class FolderClearPopupView extends AbstractViewNext { constructor() { super(); - this.selectedFolder = ko.observable(null); - this.clearingProcess = ko.observable(false); - this.clearingError = ko.observable(''); - - this.folderFullNameForClear = ko.computed(() => { - const folder = this.selectedFolder(); - return folder ? folder.printableFullName() : ''; + this.addObservables({ + selectedFolder: null, + clearingProcess: false, + clearingError: '' }); - this.folderNameForClear = ko.computed(() => { - const folder = this.selectedFolder(); - return folder ? folder.localName() : ''; - }); + this.addComputables({ + folderFullNameForClear: () => { + const folder = this.selectedFolder(); + return folder ? folder.printableFullName() : ''; + }, - this.dangerDescHtml = ko.computed(() => - i18n('POPUPS_CLEAR_FOLDER/DANGER_DESC_HTML_1', { 'FOLDER': this.folderNameForClear() }) - ); + folderNameForClear: () => { + const folder = this.selectedFolder(); + return folder ? folder.localName() : ''; + }, + + dangerDescHtml: () => i18n('POPUPS_CLEAR_FOLDER/DANGER_DESC_HTML_1', { 'FOLDER': this.folderNameForClear() }) + }); } @command((self) => { diff --git a/dev/View/Popup/FolderCreate.js b/dev/View/Popup/FolderCreate.js index 989f32a84..0edc46fde 100644 --- a/dev/View/Popup/FolderCreate.js +++ b/dev/View/Popup/FolderCreate.js @@ -20,10 +20,12 @@ class FolderCreateView extends AbstractViewNext { constructor() { super(); - this.folderName = ko.observable(''); - this.folderName.focused = ko.observable(false); + this.addObservables({ + folderName: '', + folderNameFocused: false, - this.selectedParentValue = ko.observable(UNUSED_OPTION_VALUE); + selectedParentValue: UNUSED_OPTION_VALUE + }); this.parentFolderSelectList = ko.computed(() => { const top = [], @@ -66,7 +68,7 @@ class FolderCreateView extends AbstractViewNext { clearPopup() { this.folderName(''); this.selectedParentValue(''); - this.folderName.focused(false); + this.folderNameFocused(false); } onShow() { @@ -75,7 +77,7 @@ class FolderCreateView extends AbstractViewNext { onShowWithDelay() { // rl.settings.app('mobile') || - this.folderName.focused(true); + this.folderNameFocused(true); } } diff --git a/dev/View/Popup/FolderSystem.js b/dev/View/Popup/FolderSystem.js index 2d723ed0e..3c684250e 100644 --- a/dev/View/Popup/FolderSystem.js +++ b/dev/View/Popup/FolderSystem.js @@ -79,11 +79,13 @@ class FolderSystemPopupView extends AbstractViewNext { fSaveSystemFolders(); }; - FolderStore.sentFolder.subscribe(fCallback); - FolderStore.draftFolder.subscribe(fCallback); - FolderStore.spamFolder.subscribe(fCallback); - FolderStore.trashFolder.subscribe(fCallback); - FolderStore.archiveFolder.subscribe(fCallback); + ko.addSubscribablesTo(FolderStore, { + sentFolder: fCallback, + draftFolder: fCallback, + spamFolder: fCallback, + trashFolder: fCallback, + archiveFolder: fCallback + }); this.defaultOptionsAfterRender = defaultOptionsAfterRender; } diff --git a/dev/View/Popup/Identity.js b/dev/View/Popup/Identity.js index ab98e11e2..822377abc 100644 --- a/dev/View/Popup/Identity.js +++ b/dev/View/Popup/Identity.js @@ -26,25 +26,27 @@ class IdentityPopupView extends AbstractViewNext { super(); this.id = ''; - this.edit = ko.observable(false); - this.owner = ko.observable(false); + this.addObservables({ + 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.focused = ko.observable(false); - this.name = ko.observable(''); this.replyTo = ko.observable('').validateEmail(); - this.replyTo.focused = ko.observable(false); 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) => { if (false === this.showBcc() && value.length) { @@ -71,19 +73,19 @@ class IdentityPopupView extends AbstractViewNext { if (this.email.hasError()) { if (!this.owner()) { - this.email.focused(true); + this.emailFocused(true); } return false; } if (this.replyTo.hasError()) { - this.replyTo.focused(true); + this.replyToFocused(true); return false; } if (this.bcc.hasError()) { - this.bcc.focused(true); + this.bccFocused(true); return false; } @@ -163,7 +165,7 @@ class IdentityPopupView extends AbstractViewNext { onShowWithDelay() { if (!this.owner()/* && !rl.settings.app('mobile')*/) { - this.email.focused(true); + this.emailFocused(true); } } diff --git a/dev/View/Popup/Languages.js b/dev/View/Popup/Languages.js index 6f2c8dec5..35a966c3c 100644 --- a/dev/View/Popup/Languages.js +++ b/dev/View/Popup/Languages.js @@ -29,9 +29,7 @@ class LanguagesPopupView extends AbstractViewNext { })); }); - this.langs.subscribe(() => { - this.setLanguageSelection(); - }); + this.langs.subscribe(() => this.setLanguageSelection()); } languageTooltipName(language) { diff --git a/dev/View/Popup/MessageOpenPgp.js b/dev/View/Popup/MessageOpenPgp.js index eb6e4b09c..464cafabf 100644 --- a/dev/View/Popup/MessageOpenPgp.js +++ b/dev/View/Popup/MessageOpenPgp.js @@ -14,17 +14,16 @@ class MessageOpenPgpPopupView extends AbstractViewNext { constructor() { super(); - this.notification = ko.observable(''); - - this.selectedKey = ko.observable(null); + this.addObservables({ + notification: '', + selectedKey: null, + password: '', + submitRequest: false + }); this.privateKeys = ko.observableArray([]); - this.password = ko.observable(''); - this.resultCallback = null; - this.submitRequest = ko.observable(false); - this.sDefaultKeyScope = KeyState.PopupMessageOpenPGP; } diff --git a/dev/View/Popup/NewOpenPgpKey.js b/dev/View/Popup/NewOpenPgpKey.js index babc62acc..eaf4bb7ad 100644 --- a/dev/View/Popup/NewOpenPgpKey.js +++ b/dev/View/Popup/NewOpenPgpKey.js @@ -1,5 +1,3 @@ -import ko from 'ko'; - import { pInt } from 'Common/Utils'; import PgpStore from 'Stores/User/Pgp'; @@ -15,20 +13,20 @@ class NewOpenPgpKeyPopupView extends AbstractViewNext { constructor() { super(); - this.email = ko.observable(''); - this.email.focus = ko.observable(''); - this.email.error = ko.observable(false); + this.addObservables({ + email: '', + emailFocus: '', + emailError: false, - this.name = ko.observable(''); - this.password = ko.observable(''); - this.keyBitLength = ko.observable(2048); + name: '', + password: '', + keyBitLength: 2048, - this.submitRequest = ko.observable(false); - this.submitError = ko.observable(''); - - this.email.subscribe(() => { - this.email.error(false); + submitRequest: false, + submitError: '' }); + + this.email.subscribe(() => this.emailError(false)); } @command() @@ -36,8 +34,8 @@ class NewOpenPgpKeyPopupView extends AbstractViewNext { const userId = {}, openpgpKeyring = PgpStore.openpgpKeyring; - this.email.error(!this.email().trim()); - if (!openpgpKeyring || this.email.error()) { + this.emailError(!this.email().trim()); + if (!openpgpKeyring || this.emailError()) { return false; } @@ -95,7 +93,7 @@ class NewOpenPgpKeyPopupView extends AbstractViewNext { this.password(''); this.email(''); - this.email.error(false); + this.emailError(false); this.keyBitLength(2048); this.submitError(''); @@ -106,7 +104,7 @@ class NewOpenPgpKeyPopupView extends AbstractViewNext { } onShowWithDelay() { - this.email.focus(true); + this.emailFocus(true); } } diff --git a/dev/View/Popup/Plugin.js b/dev/View/Popup/Plugin.js index 42d6027b8..5bd9a9ac2 100644 --- a/dev/View/Popup/Plugin.js +++ b/dev/View/Popup/Plugin.js @@ -18,10 +18,11 @@ class PluginPopupView extends AbstractViewNext { this.onPluginSettingsUpdateResponse = this.onPluginSettingsUpdateResponse.bind(this); - this.saveError = ko.observable(''); - - this.name = ko.observable(''); - this.readme = ko.observable(''); + this.addObservables({ + saveError: '', + name: '', + readme: '' + }); this.configures = ko.observableArray([]); diff --git a/dev/View/Popup/Template.js b/dev/View/Popup/Template.js index 2791e1b6e..b95b4078d 100644 --- a/dev/View/Popup/Template.js +++ b/dev/View/Popup/Template.js @@ -1,5 +1,3 @@ -import ko from 'ko'; - import { StorageResultType, Notification } from 'Common/Enums'; import { getNotification } from 'Common/Translator'; import { HtmlEditor } from 'Common/HtmlEditor'; @@ -19,38 +17,37 @@ class TemplatePopupView extends AbstractViewNext { super(); this.editor = null; - this.signatureDom = ko.observable(null); - this.id = ko.observable(''); + this.addObservables({ + signatureDom: null, - this.name = ko.observable(''); - this.name.error = ko.observable(false); - this.name.focus = ko.observable(false); + id: '', - this.body = ko.observable(''); - this.body.loading = ko.observable(false); - this.body.error = ko.observable(false); + name: '', + nameError: false, + nameFocus: false, - this.name.subscribe(() => { - this.name.error(false); + body: '', + bodyLoading: false, + bodyError: false, + + submitRequest: false, + submitError: '' }); - this.body.subscribe(() => { - this.body.error(false); - }); + this.name.subscribe(() => this.nameError(false)); - this.submitRequest = ko.observable(false); - this.submitError = ko.observable(''); + this.body.subscribe(() => this.bodyError(false)); } @command((self) => !self.submitRequest()) addTemplateCommand() { this.populateBodyFromEditor(); - this.name.error(!this.name().trim()); - this.body.error(!this.body().trim() || ':HTML:' === this.body().trim()); + this.nameError(!this.name().trim()); + this.bodyError(!this.body().trim() || ':HTML:' === this.body().trim()); - if (this.name.error() || this.body.error()) { + if (this.nameError() || this.bodyError()) { return false; } @@ -82,11 +79,11 @@ class TemplatePopupView extends AbstractViewNext { this.id(''); this.name(''); - this.name.error(false); + this.nameError(false); this.body(''); - this.body.loading(false); - this.body.error(false); + this.bodyLoading(false); + this.bodyError(false); this.submitRequest(false); this.submitError(''); @@ -125,11 +122,11 @@ class TemplatePopupView extends AbstractViewNext { if (template.populated) { this.editorSetBody(this.body()); } else { - this.body.loading(true); - this.body.error(false); + this.bodyLoading(true); + this.bodyError(false); Remote.templateGetById((result, data) => { - this.body.loading(false); + this.bodyLoading(false); if ( StorageResultType.Success === result && @@ -141,10 +138,10 @@ class TemplatePopupView extends AbstractViewNext { template.populated = true; this.body(template.body); - this.body.error(false); + this.bodyError(false); } else { this.body(''); - this.body.error(true); + this.bodyError(true); } this.editorSetBody(this.body()); @@ -156,7 +153,7 @@ class TemplatePopupView extends AbstractViewNext { } onShowWithDelay() { - this.name.focus(true); + this.nameFocus(true); } } diff --git a/dev/View/Popup/TwoFactorConfiguration.js b/dev/View/Popup/TwoFactorConfiguration.js index e6143a7c0..ff310ad35 100644 --- a/dev/View/Popup/TwoFactorConfiguration.js +++ b/dev/View/Popup/TwoFactorConfiguration.js @@ -1,5 +1,3 @@ -import ko from 'ko'; - import { Capa, StorageResultType } from 'Common/Enums'; import { pString } from 'Common/Utils'; import { i18n, trigger as translatorTrigger } from 'Common/Translator'; @@ -17,68 +15,72 @@ class TwoFactorConfigurationPopupView extends AbstractViewNext { constructor() { 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.processing = ko.observable(false); - this.clearing = ko.observable(false); - 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_, - write: (value) => { - value = !!value; - if (value && this.twoFactorTested()) { - this.viewEnable_(value); - Remote.enableTwoFactor((result, data) => { - if (StorageResultType.Success !== result || !data || !data.Result) { - this.viewEnable_(false); - } - }, true); - } else { - if (!value) { + this.addComputables({ + viewEnable: { + read: this.viewEnable_, + write: (value) => { + value = !!value; + if (value && this.twoFactorTested()) { this.viewEnable_(value); - } - - Remote.enableTwoFactor((result, data) => { - if (StorageResultType.Success !== result || !data || !data.Result) { - this.viewEnable_(false); + Remote.enableTwoFactor((result, data) => { + if (StorageResultType.Success !== result || !data || !data.Result) { + this.viewEnable_(false); + } + }, true); + } else { + if (!value) { + this.viewEnable_(value); } - }, false); + + Remote.enableTwoFactor((result, data) => { + if (StorageResultType.Success !== result || !data || !data.Result) { + this.viewEnable_(false); + } + }, false); + } } - } - }); + }, - this.viewTwoFactorEnableTooltip = ko.computed(() => { - translatorTrigger(); - return this.twoFactorTested() || this.viewEnable_() - ? '' - : i18n('POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_SECRET_TEST_BEFORE_DESC'); - }); + viewTwoFactorEnableTooltip: () => { + translatorTrigger(); + return this.twoFactorTested() || this.viewEnable_() + ? '' + : i18n('POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_SECRET_TEST_BEFORE_DESC'); + }, - this.viewTwoFactorStatus = ko.computed(() => { - translatorTrigger(); - return i18n( - this.twoFactorStatus() - ? 'POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_SECRET_CONFIGURED_DESC' - : 'POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC' - ); - }); + viewTwoFactorStatus: () => { + translatorTrigger(); + return i18n( + this.twoFactorStatus() + ? 'POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_SECRET_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.onShowSecretResult = this.onShowSecretResult.bind(this); diff --git a/dev/View/Popup/TwoFactorTest.js b/dev/View/Popup/TwoFactorTest.js index 80809177f..e9802dc0e 100644 --- a/dev/View/Popup/TwoFactorTest.js +++ b/dev/View/Popup/TwoFactorTest.js @@ -1,5 +1,3 @@ -import ko from 'ko'; - import { StorageResultType } from 'Common/Enums'; import Remote from 'Remote/User/Fetch'; @@ -15,13 +13,15 @@ class TwoFactorTestPopupView extends AbstractViewNext { constructor() { super(); - this.code = ko.observable(''); - this.code.focused = ko.observable(false); - this.code.status = ko.observable(null); + this.addObservables({ + code: '', + codeFocused: false, + codeStatus: null, + + testing: false + }); this.koTestedTrigger = null; - - this.testing = ko.observable(false); } @command((self) => self.code() && !self.testing()) @@ -29,9 +29,9 @@ class TwoFactorTestPopupView extends AbstractViewNext { this.testing(true); Remote.testTwoFactor((result, data) => { 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.code()); @@ -39,8 +39,8 @@ class TwoFactorTestPopupView extends AbstractViewNext { clearPopup() { this.code(''); - this.code.focused(false); - this.code.status(null); + this.codeFocused(false); + this.codeStatus(null); this.testing(false); this.koTestedTrigger = null; @@ -54,7 +54,7 @@ class TwoFactorTestPopupView extends AbstractViewNext { onShowWithDelay() { // rl.settings.app('mobile') || - this.code.focused(true); + this.codeFocused(true); } } diff --git a/dev/View/Popup/ViewOpenPgpKey.js b/dev/View/Popup/ViewOpenPgpKey.js index aa3bf8230..9eadfd9e3 100644 --- a/dev/View/Popup/ViewOpenPgpKey.js +++ b/dev/View/Popup/ViewOpenPgpKey.js @@ -1,5 +1,3 @@ -import ko from 'ko'; - import { KeyState } from 'Common/Enums'; import { popup } from 'Knoin/Knoin'; @@ -13,8 +11,10 @@ class ViewOpenPgpKeyPopupView extends AbstractViewNext { constructor() { super(); - this.key = ko.observable(''); - this.keyDom = ko.observable(null); + this.addObservables({ + key: '', + keyDom: null + }); this.sDefaultKeyScope = KeyState.PopupViewOpenPGP; } diff --git a/dev/View/User/Login.js b/dev/View/User/Login.js index 964c50133..d8eca88d2 100644 --- a/dev/View/User/Login.js +++ b/dev/View/User/Login.js @@ -37,17 +37,31 @@ class LoginUserView extends AbstractViewNext { 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(''); - this.password = ko.observable(''); - this.signMe = ko.observable(false); + emailError: false, + passwordError: false, - this.additionalCode = ko.observable(''); - this.additionalCode.error = ko.observable(false); - this.additionalCode.errorAnimation = ko.observable(false).extend({ falseTimeout: 500 }); - this.additionalCode.visibility = ko.observable(false); - this.additionalCodeSignMe = ko.observable(false); + formHidden: false, + + submitRequest: false, + submitError: '', + 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.loginDescription = (Settings.get('LoginDescription')||'').trim(); @@ -58,61 +72,50 @@ class LoginUserView extends AbstractViewNext { this.forgotPasswordLinkUrl = Settings.app('forgotPasswordLinkUrl'); this.registrationLinkUrl = Settings.app('registrationLinkUrl'); - this.emailError = ko.observable(false); - this.passwordError = ko.observable(false); - this.emailErrorAnimation = ko.observable(false).extend({ falseTimeout: 500 }); this.passwordErrorAnimation = ko.observable(false).extend({ falseTimeout: 500 }); - this.formHidden = ko.observable(false); + this.addComputables({ + formError: + () => + this.emailErrorAnimation() || + this.passwordErrorAnimation() || + (this.additionalCodeVisibility() && this.additionalCodeErrorAnimation()), - this.formError = ko.computed( - () => - this.emailErrorAnimation() || - this.passwordErrorAnimation() || - (this.additionalCode.visibility() && this.additionalCode.errorAnimation()) - ); + languageFullName: () => convertLangName(this.language()), - this.email.subscribe(() => { - this.emailError(false); - this.additionalCode(''); - this.additionalCode.visibility(false); + signMeVisibility: () => LoginSignMeType.Unused !== this.signMeType() }); - 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); - this.submitError = ko.observable(''); - this.submitErrorAddidional = ko.observable(''); - - this.submitError.subscribe(value => value || this.submitErrorAddidional('')); + signMeType: iValue => this.signMe(LoginSignMeType.DefaultOn === iValue) + }); this.allowLanguagesOnLogin = !!Settings.get('AllowLanguagesOnLogin'); - this.langRequest = ko.observable(false); this.language = LanguageStore.language; this.languages = LanguageStore.languages; 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()) { this.submitError(Settings.get('AdditionalLoginError')); } @@ -128,10 +131,10 @@ class LoginUserView extends AbstractViewNext { this.passwordError(false); let error; - if (this.additionalCode.visibility()) { - this.additionalCode.error(false); + if (this.additionalCodeVisibility()) { + this.additionalCodeError(false); if (!this.additionalCode().trim()) { - this.additionalCode.error(true); + this.additionalCodeError(true); error = '.inputAdditionalCode'; } } @@ -157,7 +160,7 @@ class LoginUserView extends AbstractViewNext { if (oData.Result) { if (oData.TwoFactorAuth) { this.additionalCode(''); - this.additionalCode.visibility(true); + this.additionalCodeVisibility(true); this.submitRequest(false); setTimeout(() => this.querySelector('.inputAdditionalCode').focus(), 100); @@ -192,8 +195,8 @@ class LoginUserView extends AbstractViewNext { sLoginPassword, !!this.signMe(), this.bSendLanguage ? this.language() : '', - this.additionalCode.visibility() ? this.additionalCode() : '', - this.additionalCode.visibility() ? !!this.additionalCodeSignMe() : false + this.additionalCodeVisibility() ? this.additionalCode() : '', + this.additionalCodeVisibility() ? !!this.additionalCodeSignMe() : false ); Local.set(ClientSideKeyName.LastSignMe, this.signMe() ? '-1-' : '-0-'); diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index f0627fbad..36b784e5e 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -108,97 +108,87 @@ class MessageListMailBoxUserView extends AbstractViewNext { this.userUsageSize = QuotaStore.usage; this.userUsageProc = QuotaStore.percentage; - this.moveDropdownTrigger = ko.observable(false); - this.moreDropdownTrigger = ko.observable(false); + this.addObservables({ + moveDropdownTrigger: false, + moreDropdownTrigger: false, + + dragOverArea: null, + dragOverBodyArea: null, + + inputMessageListSearchFocus: false + }); // append drag and drop this.dragOver = 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.mobile || Layout.SidePreview === SettingsStore.layout() - ? 'MailMessageListItem' - : 'MailMessageListItemNoPreviewPane' - ); - - this.messageListSearchDesc = ko.computed(() => { - const value = MessageStore.messageListEndSearch(); - return value ? i18n('MESSAGE_LIST/SEARCH_RESULT_FOR', { 'SEARCH': value }) : ''; - }); - - this.messageListPaginator = ko.computed( - computedPaginatorHelper(MessageStore.messageListPage, MessageStore.messageListPageCount) - ); - - this.checkAll = ko.computed({ - read: () => 0 < MessageStore.messageListChecked().length, - write: (value) => { - value = !!value; - MessageStore.messageList().forEach(message => message.checked(value)); - } - }); - - this.inputMessageListSearchFocus = ko.observable(false); this.sLastSearchValue = ''; - this.inputProxyMessageListSearch = ko.computed({ - read: this.mainMessageListSearch, - write: value => this.sLastSearchValue = value + + this.addComputables({ + messageListItemTemplate: () => + this.mobile || Layout.SidePreview === SettingsStore.layout() + ? 'MailMessageListItem' + : 'MailMessageListItemNoPreviewPane', + + messageListSearchDesc: () => { + const value = MessageStore.messageListEndSearch(); + return value ? i18n('MESSAGE_LIST/SEARCH_RESULT_FOR', { 'SEARCH': value }) : '' + }, + + messageListPaginator: computedPaginatorHelper(MessageStore.messageListPage, MessageStore.messageListPageCount), + + checkAll: { + read: () => 0 < MessageStore.messageListChecked().length, + write: (value) => { + value = !!value; + MessageStore.messageList().forEach(message => message.checked(value)); + } + }, + + inputProxyMessageListSearch: { + read: this.mainMessageListSearch, + write: value => this.sLastSearchValue = value + }, + + isIncompleteChecked: () => { + const c = MessageStore.messageListChecked().length; + 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.isIncompleteChecked = ko.computed(() => { - const c = MessageStore.messageListChecked().length; - return c && MessageStore.messageList().length > c; - }); - - 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.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; diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 303482f1f..40e7ba270 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -16,7 +16,7 @@ import { import { $htmlCL, leftPanelDisabled, keyScopeReal, moveAction } from 'Common/Globals'; import { inFocus } from 'Common/Utils'; -import { mailToHelper, isTransparent } from 'Common/UtilsUser'; +import { mailToHelper } from 'Common/UtilsUser'; import Audio from 'Common/Audio'; @@ -40,6 +40,10 @@ import { AbstractViewNext } from 'Knoin/AbstractViewNext'; const Settings = rl.settings; +function isTransparent(color) { + return 'rgba(0, 0, 0, 0)' === color || 'transparent' === color; +} + @view({ name: 'View/User/MailBox/MessageView', type: ViewType.Right, @@ -67,7 +71,14 @@ class MessageViewMailBoxUserView extends AbstractViewNext { this.oHeaderDom = 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; @@ -103,50 +114,12 @@ class MessageViewMailBoxUserView extends AbstractViewNext { this.messageListOfThreadsLoading = ko.observable(false).extend({ rateLimit: 1 }); this.highlightUnselectedAttachments = ko.observable(false).extend({ falseTimeout: 2000 }); - this.showAttachmnetControls = ko.observable(false); - 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.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.messageVisibility = ko.computed(() => !this.messageLoadingThrottle() && !!this.message()); - - this.message.subscribe(message => (!message) && MessageStore.selectorMessageSelected(null)); - - this.canBeRepliedOrForwarded = ko.computed(() => !this.isDraftFolder() && this.messageVisibility()); - // commands this.replyCommand = createCommandReplyHelper(ComposeType.Reply); this.replyAllCommand = createCommandReplyHelper(ComposeType.ReplyAll); @@ -162,97 +135,85 @@ class MessageViewMailBoxUserView extends AbstractViewNext { // viewer - this.viewBodyTopValue = ko.observable(0); - this.viewFolder = ''; this.viewUid = ''; this.viewHash = ''; - this.viewSubject = ko.observable(''); - this.viewFromShort = ko.observable(''); - this.viewFromDkimData = ko.observable(['none', '']); - this.viewToShort = ko.observable(''); - this.viewFrom = ko.observable(''); - this.viewTo = ko.observable(''); - this.viewCc = ko.observable(''); - this.viewBcc = ko.observable(''); - this.viewReplyTo = ko.observable(''); - this.viewTimeStamp = ko.observable(0); - this.viewSize = ko.observable(''); - this.viewLineAsCss = ko.observable(''); - this.viewViewLink = ko.observable(''); - this.viewUnsubscribeLink = ko.observable(''); - this.viewDownloadLink = ko.observable(''); - this.viewIsImportant = ko.observable(false); - this.viewIsFlagged = ko.observable(false); - - this.viewFromDkimVisibility = ko.computed(() => 'none' !== this.viewFromDkimData()[0]); - - 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.addObservables({ + viewBodyTopValue: 0, + viewSubject: '', + viewFromShort: '', + viewFromDkimData: ['none', ''], + viewToShort: '', + viewFrom: '', + viewTo: '', + viewCc: '', + viewBcc: '', + viewReplyTo: '', + viewTimeStamp: 0, + viewSize: '', + viewLineAsCss: '', + viewViewLink: '', + viewUnsubscribeLink: '', + viewDownloadLink: '', + viewIsImportant: false, + viewIsFlagged: false }); - this.viewFromDkimStatusTitle = ko.computed(() => { - const status = this.viewFromDkimData(); - if (Array.isNotEmpty(status)) { - if (status[0]) { - return status[1] || 'DKIM: ' + status[0]; - } - } + this.addSubscribables({ + showAttachmnetControls: v => this.message() + && this.message().attachments().forEach(item => item && item.checked(!!v)), - 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) => { - this.messageActiveDom(null); + message: message => { + this.messageActiveDom(null); - if (message) { - this.showAttachmnetControls(false); - if (Local.get(ClientSideKeyName.MessageAttachmentControls)) { - setTimeout(() => { - this.showAttachmnetControls(true); - }, 50); - } + if (message) { + this.showAttachmnetControls(false); + if (Local.get(ClientSideKeyName.MessageAttachmentControls)) { + setTimeout(() => { + this.showAttachmnetControls(true); + }, 50); + } + + if (this.viewHash !== message.hash) { + this.scrollMessageToTop(); + } + + this.viewFolder = message.folder; + this.viewUid = message.uid; + this.viewHash = message.hash; + this.viewSubject(message.subject()); + this.viewFromShort(message.fromToLine(true, true)); + this.viewFromDkimData(message.fromDkimData()); + this.viewToShort(message.toToLine(true, true)); + this.viewFrom(message.fromToLine(false)); + this.viewTo(message.toToLine(false)); + this.viewCc(message.ccToLine(false)); + this.viewBcc(message.bccToLine(false)); + this.viewReplyTo(message.replyToToLine(false)); + this.viewTimeStamp(message.dateTimeStampInUTC()); + this.viewSize(message.friendlySize()); + this.viewLineAsCss(message.lineAsCss()); + this.viewViewLink(message.viewLink()); + this.viewUnsubscribeLink(message.getFirstUnsubsribeLink()); + this.viewDownloadLink(message.downloadLink()); + this.viewIsImportant(message.isImportant()); + this.viewIsFlagged(message.isFlagged()); + } else { + MessageStore.selectorMessageSelected(null); + + this.viewFolder = ''; + this.viewUid = ''; + this.viewHash = ''; - if (this.viewHash !== message.hash) { this.scrollMessageToTop(); } + }, - this.viewFolder = message.folder; - this.viewUid = message.uid; - this.viewHash = message.hash; - this.viewSubject(message.subject()); - this.viewFromShort(message.fromToLine(true, true)); - this.viewFromDkimData(message.fromDkimData()); - this.viewToShort(message.toToLine(true, true)); - this.viewFrom(message.fromToLine(false)); - this.viewTo(message.toToLine(false)); - this.viewCc(message.ccToLine(false)); - this.viewBcc(message.bccToLine(false)); - this.viewReplyTo(message.replyToToLine(false)); - this.viewTimeStamp(message.dateTimeStampInUTC()); - this.viewSize(message.friendlySize()); - this.viewLineAsCss(message.lineAsCss()); - this.viewViewLink(message.viewLink()); - this.viewUnsubscribeLink(message.getFirstUnsubsribeLink()); - this.viewDownloadLink(message.downloadLink()); - this.viewIsImportant(message.isImportant()); - this.viewIsFlagged(message.isFlagged()); - } else { - this.viewFolder = ''; - this.viewUid = ''; - this.viewHash = ''; - - this.scrollMessageToTop(); - } + fullScreenMode: value => $htmlCL.toggle('rl-message-fullscreen', value) }); this.message.viewTrigger.subscribe(() => { @@ -260,13 +221,55 @@ class MessageViewMailBoxUserView extends AbstractViewNext { 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( - () => MessageStore.messageListCompleteLoadingThrottle() || MessageStore.messageLoadingThrottle() - ); + 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() + }); + + this.lastReplyAction(Local.get(ClientSideKeyName.LastReplyAction) || ComposeType.Reply); addEventListener('mailbox.message-view.toggle-full-screen', () => this.toggleFullScreen()); diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Folder.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Folder.php index 3faedf643..4c8864259 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Folder.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Folder.php @@ -30,7 +30,7 @@ class Folder implements \JsonSerializable /** * @var bool */ - private $bExisten; + private $bExists; /** * @var bool @@ -50,7 +50,7 @@ class Folder implements \JsonSerializable /** * @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->oSubFolders = null; @@ -66,7 +66,7 @@ class Folder implements \JsonSerializable } $this->bSubscribed = $bSubscribed; - $this->bExisten = $bExisten; + $this->bExists = $bExists; } /** @@ -164,12 +164,12 @@ class Folder implements \JsonSerializable public function IsExists() : bool { - return $this->bExisten; + return $this->bExists; } 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(), 'Delimiter' => (string) $this->Delimiter(), 'HasVisibleSubFolders' => $this->HasVisibleSubFolders(), - 'IsSubscribed' => $this->IsSubscribed(), - 'IsExists' => $this->IsExists(), - 'IsSelectable' => $this->IsSelectable(), + 'Subscribed' => $this->bSubscribed, + 'Exists' => $this->bExists, + 'Selectable' => $this->IsSelectable(), 'Flags' => $this->FlagsLowerCase() ); } diff --git a/snappymail/v/0.0.0/app/templates/Views/User/Login.html b/snappymail/v/0.0.0/app/templates/Views/User/Login.html index dfa5aa405..8d9a3d612 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/Login.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/Login.html @@ -59,7 +59,7 @@
+ data-bind="visible: additionalCodeVisibility(), css: {'error': additionalCodeError, 'animated': additionalCodeErrorAnimation}">
+ data-bind="visible: additionalCodeVisibility()">
@@ -143,7 +143,7 @@
- +
diff --git a/snappymail/v/0.0.0/app/templates/Views/User/PopupsFolderCreate.html b/snappymail/v/0.0.0/app/templates/Views/User/PopupsFolderCreate.html index f38ecea76..86f4a5f8d 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/PopupsFolderCreate.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/PopupsFolderCreate.html @@ -15,7 +15,7 @@
+ data-bind="textInput: folderName, hasfocus: folderNameFocused, onEnter: createFolderCommand" />
diff --git a/snappymail/v/0.0.0/app/templates/Views/User/PopupsIdentity.html b/snappymail/v/0.0.0/app/templates/Views/User/PopupsIdentity.html index a7fc6c868..6c0ef3b3e 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/PopupsIdentity.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/PopupsIdentity.html @@ -19,7 +19,7 @@
+ data-bind="value: email, onEnter: addOrEditIdentityCommand, hasfocus: emailFocused" />
@@ -41,7 +41,7 @@
+ data-bind="value: replyTo, onEnter: addOrEditIdentityCommand, hasfocus: replyToFocused" />
@@ -49,7 +49,7 @@
+ data-bind="value: bcc, onEnter: addOrEditIdentityCommand, hasfocus: bccFocused" />
diff --git a/snappymail/v/0.0.0/app/templates/Views/User/PopupsNewOpenPgpKey.html b/snappymail/v/0.0.0/app/templates/Views/User/PopupsNewOpenPgpKey.html index 4212e6883..c13e167cf 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/PopupsNewOpenPgpKey.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/PopupsNewOpenPgpKey.html @@ -13,12 +13,12 @@

-
+
+ data-bind="value: email, hasfocus: emailFocus" />
diff --git a/snappymail/v/0.0.0/app/templates/Views/User/PopupsTemplate.html b/snappymail/v/0.0.0/app/templates/Views/User/PopupsTemplate.html index b3cdb80d1..3af91854f 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/PopupsTemplate.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/PopupsTemplate.html @@ -14,17 +14,17 @@

-
+
+ data-bind="textInput: name, onEnter: addTemplateCommand, hasfocus: nameFocus" />

-
+
diff --git a/snappymail/v/0.0.0/app/templates/Views/User/PopupsTwoFactorTest.html b/snappymail/v/0.0.0/app/templates/Views/User/PopupsTwoFactorTest.html index 8667821b6..04ac958bd 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/PopupsTwoFactorTest.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/PopupsTwoFactorTest.html @@ -16,14 +16,14 @@
+ data-bind="textInput: code, hasfocus: codeFocused, onEnter: testCodeCommand" />