Improved knockout observables management to prevent memory leaks

This commit is contained in:
djmaze 2020-10-26 12:54:03 +01:00
parent b165a1de4f
commit 3eb6ab1ef7
46 changed files with 1020 additions and 1013 deletions

View file

@ -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())

View file

@ -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())

View file

@ -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() {

View file

@ -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();

View file

@ -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;

View file

@ -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);
}
};
}

View file

@ -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) => {

View file

@ -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()

View file

@ -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()
});
}

View file

@ -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());

View file

@ -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([]);

View file

@ -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) => {

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -29,9 +29,7 @@ class LanguagesPopupView extends AbstractViewNext {
}));
});
this.langs.subscribe(() => {
this.setLanguageSelection();
});
this.langs.subscribe(() => this.setLanguageSelection());
}
languageTooltipName(language) {

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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([]);

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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-');

View file

@ -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;

View file

@ -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());