Merge remote-tracking branch 'origin/master' into addressbook

This commit is contained in:
djmaze 2022-09-02 15:15:01 +02:00
commit 1a5b8819fa
204 changed files with 2050 additions and 2773 deletions

View file

@ -38,7 +38,7 @@ export class AccountPopupView extends AbstractViewPopup {
this.submitRequest(false);
if (iError) {
this.submitError(getNotification(iError));
this.submitErrorAdditional((data && data.ErrorMessageAdditional) || '');
this.submitErrorAdditional(data?.ErrorMessageAdditional);
} else {
rl.app.accountsAndIdentities();
this.close();
@ -54,7 +54,7 @@ export class AccountPopupView extends AbstractViewPopup {
}
onShow(account) {
if (account && account.isAdditional()) {
if (account?.isAdditional()) {
this.isNew(false);
this.email(account.email);
} else {

View file

@ -1,6 +1,7 @@
import { koComputable } from 'External/ko';
import { i18n, trigger as translatorTrigger } from 'Common/Translator';
import { pString } from 'Common/Utils';
import { MessagelistUserStore } from 'Stores/User/Messagelist';
@ -16,6 +17,7 @@ export class AdvancedSearchPopupView extends AbstractViewPopup {
to: '',
subject: '',
text: '',
repliedValue: -1,
selectedDateValue: -1,
selectedTreeValue: '',
@ -26,9 +28,18 @@ export class AdvancedSearchPopupView extends AbstractViewPopup {
this.showMultisearch = koComputable(() => FolderUserStore.hasCapability('MULTISEARCH'));
this.repliedOptions = koComputable(() => {
translatorTrigger();
return [
{ id: -1, name: '' },
{ id: 1, name: i18n('GLOBAL/YES') },
{ id: 0, name: i18n('GLOBAL/NO') }
];
});
this.selectedDates = koComputable(() => {
translatorTrigger();
let prefix = 'SEARCH/LABEL_ADV_DATE_';
let prefix = 'SEARCH/LABEL_DATE_';
return [
{ id: -1, name: i18n(prefix + 'ALL') },
{ id: 3, name: i18n(prefix + '3_DAYS') },
@ -42,7 +53,7 @@ export class AdvancedSearchPopupView extends AbstractViewPopup {
this.selectedTree = koComputable(() => {
translatorTrigger();
let prefix = 'SEARCH/LABEL_ADV_SUBFOLDERS_';
let prefix = 'SEARCH/LABEL_SUBFOLDERS_';
return [
{ id: '', name: i18n(prefix + 'NONE') },
{ id: 'subtree-one', name: i18n(prefix + 'SUBTREE_ONE') },
@ -60,66 +71,61 @@ export class AdvancedSearchPopupView extends AbstractViewPopup {
this.close();
}
parseSearchStringValue(search) {
const parts = (search || '').split(/[\s]+/g);
parts.forEach(part => {
switch (part) {
case 'has:attachment':
this.hasAttachment(true);
break;
case 'is:unseen,flagged':
this.starred(true);
/* falls through */
case 'is:unseen':
this.unseen(true);
break;
// no default
}
});
}
buildSearchString() {
const
self = this,
data = new FormData(),
append = (key, value) => value.length && data.append(key, value);
append('from', this.from().trim());
append('to', this.to().trim());
append('subject', this.subject().trim());
append('text', this.text().trim());
append('in', this.selectedTreeValue());
if (-1 < this.selectedDateValue()) {
append('from', self.from().trim());
append('to', self.to().trim());
append('subject', self.subject().trim());
append('text', self.text().trim());
append('in', self.selectedTreeValue());
if (-1 < self.selectedDateValue()) {
let d = new Date();
d.setDate(d.getDate() - this.selectedDateValue());
d.setDate(d.getDate() - self.selectedDateValue());
append('since', d.toISOString().split('T')[0]);
}
let result = new URLSearchParams(data).toString();
if (this.hasAttachment()) {
if (self.hasAttachment()) {
result += '&attachment';
}
if (this.unseen()) {
if (self.unseen()) {
result += '&unseen';
}
if (this.starred()) {
if (self.starred()) {
result += '&flagged';
}
if (1 == self.repliedValue()) {
result += '&answered';
}
if (0 == self.repliedValue()) {
result += '&unanswered';
}
return result.replace(/^&+/, '');
}
onShow(search) {
this.from('');
this.to('');
this.subject('');
this.text('');
this.selectedDateValue(-1);
this.hasAttachment(false);
this.starred(false);
this.unseen(false);
this.parseSearchStringValue(search);
const self = this,
params = new URLSearchParams('?'+search);
self.from(pString(params.get('from')));
self.to(pString(params.get('to')));
self.subject(pString(params.get('subject')));
self.text(pString(params.get('text')));
self.selectedTreeValue(pString(params.get('in')));
// self.selectedDateValue(params.get('since'));
self.selectedDateValue(-1);
self.hasAttachment(params.has('attachment'));
self.starred(params.has('flagged'));
self.unseen(params.has('unseen'));
if (params.has('answered')) {
self.repliedValue(1);
} else if (params.has('unanswered')) {
self.repliedValue(0);
}
}
}

View file

@ -11,6 +11,8 @@ export class AskPopupView extends AbstractViewPopup {
askDesc: '',
yesButton: '',
noButton: '',
username: '',
askUsername: false,
passphrase: '',
askPass: false
});
@ -40,15 +42,19 @@ export class AskPopupView extends AbstractViewPopup {
* @param {boolean=} focusOnShow = true
* @returns {void}
*/
onShow(sAskDesc, fYesFunc = null, fNoFunc = null, focusOnShow = true, askPass = false, btnText = '') {
onShow(sAskDesc, fYesFunc = null, fNoFunc = null, focusOnShow = true, ask = 0, btnText = '') {
this.askDesc(sAskDesc || '');
this.askPass(askPass);
this.askUsername(ask & 2);
this.askPass(ask & 1);
this.username('');
this.passphrase('');
this.yesButton(i18n(btnText || 'POPUPS_ASK/BUTTON_YES'));
this.noButton(i18n(askPass ? 'GLOBAL/CANCEL' : 'POPUPS_ASK/BUTTON_NO'));
this.yesButton(i18n(btnText || 'GLOBAL/YES'));
this.noButton(i18n(ask ? 'GLOBAL/CANCEL' : 'GLOBAL/NO'));
this.fYesAction = fYesFunc;
this.fNoAction = fNoFunc;
this.focusOnShow = focusOnShow ? (askPass ? 'input[type="password"]' : '.buttonYes') : '';
this.focusOnShow = focusOnShow
? (ask ? 'input[type="'+(ask&2?'text':'password')+'"]' : '.buttonYes')
: '';
}
afterShow() {
@ -63,12 +69,15 @@ export class AskPopupView extends AbstractViewPopup {
onBuild() {
// shortcuts.add('tab', 'shift', 'Ask', () => {
shortcuts.add('tab,arrowright,arrowleft', '', 'Ask', () => {
let btn = this.querySelector('.buttonYes');
if (btn.matches(':focus')) {
btn = this.querySelector('.buttonNo');
let yes = this.querySelector('.buttonYes'),
no = this.querySelector('.buttonNo');
if (yes.matches(':focus')) {
no.focus();
return false;
} else if (no.matches(':focus')) {
yes.focus();
return false;
}
btn.focus();
return false;
});
}
}
@ -80,7 +89,20 @@ AskPopupView.password = function(sAskDesc, btnText) {
() => resolve(this.__vm.passphrase()),
() => resolve(null),
true,
true,
1,
btnText
]);
});
}
AskPopupView.credentials = function(sAskDesc, btnText) {
return new Promise(resolve => {
this.showModal([
sAskDesc,
() => resolve({username:this.__vm.username(), password:this.__vm.passphrase()}),
() => resolve(null),
true,
3,
btnText
]);
});

View file

@ -16,11 +16,11 @@ import { encodeHtml, HtmlEditor, htmlToPlain } from 'Common/Html';
import { koArrayWithDestroy } from 'External/ko';
import { UNUSED_OPTION_VALUE } from 'Common/Consts';
import { messagesDeleteHelper } from 'Common/Folders';
import { folderInformation, messagesDeleteHelper } from 'Common/Folders';
import { serverRequest } from 'Common/Links';
import { i18n, getNotification, getUploadErrorDescByCode, timestampToString } from 'Common/Translator';
import { MessageFlagsCache, setFolderHash } from 'Common/Cache';
import { Settings, SettingsCapa, SettingsGet, elementById, addShortcut } from 'Common/Globals';
import { Settings, SettingsCapa, SettingsGet, elementById, addShortcut, createElement } from 'Common/Globals';
//import { exitFullscreen, isFullscreen, toggleFullscreen } from 'Common/Fullscreen';
import { AppUserStore } from 'Stores/User/App';
@ -50,9 +50,13 @@ import { ThemeStore } from 'Stores/Theme';
let alreadyFullscreen;
*/
let oLastMessage;
const
ScopeCompose = 'Compose',
tpl = createElement('template'),
base64_encode = text => btoa(unescape(encodeURIComponent(text))).match(/.{1,76}/g).join('\r\n'),
email = new EmailModel(),
@ -77,7 +81,7 @@ const
if (FolderUserStore.currentFolderFullName() === draftsFolder) {
MessagelistUserStore.reload(true);
} else {
rl.app.folderInformation(draftsFolder);
folderInformation(draftsFolder);
}
}
},
@ -209,8 +213,8 @@ export class ComposePopupView extends AbstractViewPopup {
super('Compose');
const fEmailOutInHelper = (context, identity, name, isIn) => {
if (identity && context && identity[name]() && (isIn ? true : context[name]())) {
const identityEmail = identity[name]();
const identityEmail = context && identity?.[name]();
if (identityEmail && (isIn ? true : context[name]())) {
let list = context[name]().trim().split(',');
list = list.filter(email => {
@ -218,15 +222,12 @@ export class ComposePopupView extends AbstractViewPopup {
return email && identityEmail.trim() !== email;
});
if (isIn) {
list.push(identityEmail);
}
isIn && list.push(identityEmail);
context[name](list.join(','));
}
};
this.oLastMessage = null;
this.oEditor = null;
this.aDraftInfo = null;
this.sInReplyTo = '';
@ -352,7 +353,7 @@ export class ComposePopupView extends AbstractViewPopup {
},
attachmentsInProcess: () => this.attachments.filter(item => item && !item.complete()),
attachmentsInError: () => this.attachments.filter(item => item && item.error()),
attachmentsInError: () => this.attachments.filter(item => item?.error()),
attachmentsCount: () => this.attachments.length,
attachmentsInErrorCount: () => this.attachmentsInError.length,
@ -508,7 +509,7 @@ export class ComposePopupView extends AbstractViewPopup {
alternative.children.push(data);
data = alternative;
}
if (sign && !draft && sign[1]) {
if (!draft && sign?.[1]) {
if ('openpgp' == sign[0]) {
// Doesn't sign attachments
params.Html = params.Text = '';
@ -608,7 +609,7 @@ export class ComposePopupView extends AbstractViewPopup {
this.savedErrorDesc(i18n('COMPOSE/SAVED_ERROR_ON_SEND').trim());
} else {
this.sendError(true);
this.sendErrorDesc(getNotification(iError, data && data.ErrorMessage)
this.sendErrorDesc(getNotification(iError, data?.ErrorMessage)
|| getNotification(Notification.CantSendMessage));
}
} else {
@ -755,11 +756,11 @@ export class ComposePopupView extends AbstractViewPopup {
// getAutocomplete
emailsSource(value, fResponse) {
Remote.request('Suggestions',
Remote.abort('Suggestions').request('Suggestions',
(iError, data) => {
if (!iError && isArray(data.Result)) {
fResponse(
data.Result.map(item => (item && item[0] ? (new EmailModel(item[0], item[1])).toLine() : null))
data.Result.map(item => (item?.[0] ? (new EmailModel(item[0], item[1])).toLine() : null))
.filter(v => v)
);
} else if (Notification.RequestAborted !== iError) {
@ -769,15 +770,12 @@ export class ComposePopupView extends AbstractViewPopup {
{
Query: value
// ,Page: 1
},
null,
'',
['Suggestions']
}
);
}
selectIdentity(identity) {
identity = identity && identity.item;
identity = identity?.item;
if (identity) {
this.currentIdentity(identity);
this.setSignatureFromIdentity(identity);
@ -828,7 +826,7 @@ export class ComposePopupView extends AbstractViewPopup {
this.editor(editor => {
let signature = identity.signature() || '',
isHtml = ':HTML:' === signature.slice(0, 6),
fromLine = this.oLastMessage ? emailArrayToStringLineHelper(this.oLastMessage.from, true) : '';
fromLine = oLastMessage ? emailArrayToStringLineHelper(oLastMessage.from, true) : '';
if (fromLine) {
signature = signature.replace(/{{FROM-FULL}}/g, fromLine);
if (!fromLine.includes(' ') && 0 < fromLine.indexOf('@')) {
@ -909,24 +907,20 @@ export class ComposePopupView extends AbstractViewPopup {
sText = '',
identity = null,
aDraftInfo = null,
message = null;
message = 1 === arrayLength(oMessageOrArray)
? oMessageOrArray[0]
: (isArray(oMessageOrArray) ? null : oMessageOrArray);
const excludeEmail = {},
const
// excludeEmail = new Set(),
excludeEmail = {},
mEmail = AccountUserStore.email(),
lineComposeType = sType || ComposeType.Empty;
if (oMessageOrArray) {
message =
1 === arrayLength(oMessageOrArray)
? oMessageOrArray[0]
: isArray(oMessageOrArray)
? null
: oMessageOrArray;
}
oLastMessage = message;
this.oLastMessage = message;
if (null !== mEmail) {
if (mEmail) {
// excludeEmail.add(mEmail);
excludeEmail[mEmail] = true;
}
@ -934,6 +928,7 @@ export class ComposePopupView extends AbstractViewPopup {
identity = findIdentityByMessage(lineComposeType, message);
if (identity) {
// excludeEmail.add(identity.email());
excludeEmail[identity.email()] = true;
}
@ -1030,16 +1025,22 @@ export class ComposePopupView extends AbstractViewPopup {
// no default
}
sText = message.bodyAsHTML();
let encrypted;
// https://github.com/the-djmaze/snappymail/issues/491
tpl.innerHTML = message.bodyAsHTML();
tpl.content.querySelectorAll('img').forEach(img =>
img.dataset.xSrcCid || img.dataset.xSrc || img.replaceWith(img.alt || img.title)
);
sText = tpl.innerHTML.trim();
switch (lineComposeType) {
case ComposeType.Reply:
case ComposeType.ReplyAll:
sFrom = message.fromToLine(false, true);
sText = '<br><br><p>' + i18n('COMPOSE/REPLY_MESSAGE_TITLE', { DATETIME: sDate, EMAIL: sFrom })
+ ':</p><blockquote>'
+ sText.replace(/<img[^>]+>/g, '').replace(/<a\s[^>]+><\/a>/g, '').trim()
+ sText.trim()
+ '</blockquote>';
break;
@ -1068,6 +1069,7 @@ export class ComposePopupView extends AbstractViewPopup {
case ComposeType.ForwardAsAttachment:
sText = '';
break;
default:
encrypted = PgpUserStore.isEncrypted(sText);
if (encrypted) {
@ -1131,7 +1133,8 @@ export class ComposePopupView extends AbstractViewPopup {
this.setFocusInPopup();
}
const downloads = this.getAttachmentsDownloadsForUpload();
// item.CID item.isInline item.isLinked
const downloads = this.attachments.filter(item => item && !item.tempName()).map(item => item.id);
if (arrayLength(downloads)) {
Remote.request('MessageUploadAttachments',
(iError, oData) => {
@ -1148,7 +1151,7 @@ export class ComposePopupView extends AbstractViewPopup {
});
} else {
this.attachments.forEach(attachment => {
if (attachment && attachment.fromMessage) {
if (attachment?.fromMessage) {
attachment
.waiting(false)
.uploading(false)
@ -1175,7 +1178,7 @@ export class ComposePopupView extends AbstractViewPopup {
if (!this.to()) {
this.to.focused(true);
} else if (!this.to.focused()) {
this.oEditor && this.oEditor.focus();
this.oEditor?.focus();
}
}, 100);
}
@ -1261,7 +1264,7 @@ export class ComposePopupView extends AbstractViewPopup {
})
.on('onComplete', (id, result, data) => {
const attachment = this.getAttachmentById(id),
response = (data && data.Result) || {},
response = data?.Result || {},
errorCode = response.ErrorCode,
attachmentJson = result && response.Attachment;
@ -1349,7 +1352,7 @@ export class ComposePopupView extends AbstractViewPopup {
prepareAttachmentsForSendOrSave() {
const result = {};
this.attachments.forEach(item => {
if (item && item.complete() && item.tempName() && item.enabled()) {
if (item?.complete() && item?.tempName() && item?.enabled()) {
result[item.tempName()] = [item.fileName(), item.isInline ? '1' : '0', item.CID, item.contentLocation];
}
});
@ -1376,7 +1379,7 @@ export class ComposePopupView extends AbstractViewPopup {
oJua || attachment.waiting(false).uploading(true);
attachment.cancel = () => {
this.attachments.remove(attachment);
oJua && oJua.cancel(attachment.id);
oJua?.cancel(attachment.id);
};
this.attachments.push(attachment);
view && this.attachmentsArea();
@ -1402,35 +1405,22 @@ export class ComposePopupView extends AbstractViewPopup {
if (message) {
if (ComposeType.ForwardAsAttachment === type) {
this.addMessageAsAttachment(message);
} else {
} else if ([
ComposeType.Reply, ComposeType.ReplyAll,
ComposeType.Forward, ComposeType.Draft, ComposeType.EditAsNew
].includes(type)) {
message.attachments.forEach(item => {
let add = false;
switch (type) {
case ComposeType.Reply:
case ComposeType.ReplyAll:
break;
case ComposeType.Forward:
case ComposeType.Draft:
case ComposeType.EditAsNew:
add = true;
break;
// no default
}
if (add) {
const attachment = new ComposeAttachmentModel(
item.download,
item.fileName,
item.estimatedSize,
item.isInline(),
item.isLinked(),
item.cid,
item.contentLocation
);
attachment.fromMessage = true;
this.addAttachment(attachment);
}
const attachment = new ComposeAttachmentModel(
item.download,
item.fileName,
item.estimatedSize,
item.isInline(),
item.isLinked(),
item.cid,
item.contentLocation
);
attachment.fromMessage = true;
this.addAttachment(attachment);
});
}
}
@ -1443,7 +1433,7 @@ export class ComposePopupView extends AbstractViewPopup {
isEmptyForm(includeAttachmentInProgress = true) {
const withoutAttachment = includeAttachmentInProgress
? !this.attachments.length
: !this.attachments.some(item => item && item.complete());
: !this.attachments.some(item => item?.complete());
return (
!this.to.length &&
@ -1463,8 +1453,8 @@ export class ComposePopupView extends AbstractViewPopup {
this.replyTo('');
this.subject('');
this.requestDsn(false);
this.requestReadReceipt(false);
this.requestDsn(SettingsUserStore.requestDsn());
this.requestReadReceipt(SettingsUserStore.requestReadReceipt());
this.markAsImportant(false);
this.bodyArea();
@ -1485,8 +1475,8 @@ export class ComposePopupView extends AbstractViewPopup {
this.showBcc(false);
this.showReplyTo(false);
this.pgpSign(false);
this.pgpEncrypt(false);
this.pgpSign(SettingsUserStore.pgpSign());
this.pgpEncrypt(SettingsUserStore.pgpEncrypt());
this.attachments([]);
@ -1499,20 +1489,11 @@ export class ComposePopupView extends AbstractViewPopup {
this.sending(false);
this.saving(false);
this.oEditor && this.oEditor.clear();
this.oEditor?.clear();
this.dropMailvelope();
}
/**
* @returns {Array}
*/
getAttachmentsDownloadsForUpload() {
return this.attachments.filter(item => item && !item.tempName()).map(
item => item.id
);
}
mailvelopeArea() {
if (!this.mailvelope) {
/**

View file

@ -78,7 +78,7 @@ export class ContactsPopupView extends AbstractViewPopup {
contactsPaginator: computedPaginatorHelper(this.contactsPage, pagecount),
contactsCheckedOrSelected: () => {
const checked = ContactUserStore.filter(item => item.checked && item.checked()),
const checked = ContactUserStore.filter(item => item.checked?.()),
selected = this.selectorContact();
return selected
@ -128,7 +128,7 @@ export class ContactsPopupView extends AbstractViewPopup {
const data = oItem.getNameAndEmailHelper(),
email = data ? new EmailModel(data[0], data[1]) : null;
if (email && email.validate()) {
if (email?.validate()) {
return email;
}
}
@ -235,7 +235,7 @@ export class ContactsPopupView extends AbstractViewPopup {
if (this.contactsCheckedOrSelected().length) {
Remote.request('ContactsDelete',
(iError, oData) => {
if (500 < (!iError && oData && oData.Time ? pInt(oData.Time) : 0)) {
if (500 < (!iError && oData?.Time) ? pInt(oData.Time) : 0) {
this.reloadContactList(this.bDropPageAfterDelete);
} else {
setTimeout(() => this.reloadContactList(this.bDropPageAfterDelete), 500);
@ -274,7 +274,7 @@ export class ContactsPopupView extends AbstractViewPopup {
}
ContactUserStore.loading(true);
Remote.request('Contacts',
Remote.abort('Contacts').request('Contacts',
(iError, data) => {
let count = 0,
list = [];
@ -299,10 +299,7 @@ export class ContactsPopupView extends AbstractViewPopup {
Offset: offset,
Limit: CONTACTS_PER_PAGE,
Search: this.search()
},
null,
'',
['Contacts']
}
);
}

View file

@ -8,6 +8,8 @@ import { AbstractViewPopup } from 'Knoin/AbstractViews';
import { DomainAdminStore } from 'Stores/Admin/Domain';
import { AskPopupView } from 'View/Popup/Ask';
const domainToParams = oDomain => ({
Name: oDomain.name(),
@ -179,39 +181,46 @@ export class DomainPopupView extends AbstractViewPopup {
testConnectionCommand() {
this.clearTesting(false);
this.testing(true);
// https://github.com/the-djmaze/snappymail/issues/477
AskPopupView.credentials('IMAP', 'GLOBAL/TEST').then(credentials => {
if (credentials) {
this.testing(true);
const params = domainToParams(this);
params.username = credentials.username;
params.password = credentials.password;
Remote.request('AdminDomainTest',
(iError, oData) => {
this.testing(false);
if (iError) {
this.testingImapError(true);
this.testingSieveError(true);
this.testingSmtpError(true);
} else {
this.testingDone(true);
this.testingImapError(true !== oData.Result.Imap);
this.testingSieveError(true !== oData.Result.Sieve);
this.testingSmtpError(true !== oData.Result.Smtp);
Remote.request('AdminDomainTest',
(iError, oData) => {
this.testing(false);
if (iError) {
this.testingImapError(true);
this.testingSieveError(true);
this.testingSmtpError(true);
} else {
this.testingDone(true);
this.testingImapError(true !== oData.Result.Imap);
this.testingSieveError(true !== oData.Result.Sieve);
this.testingSmtpError(true !== oData.Result.Smtp);
if (this.testingImapError() && oData.Result.Imap) {
this.testingImapErrorDesc('');
this.testingImapErrorDesc(oData.Result.Imap);
}
if (this.testingImapError() && oData.Result.Imap) {
this.testingImapErrorDesc('');
this.testingImapErrorDesc(oData.Result.Imap);
}
if (this.testingSieveError() && oData.Result.Sieve) {
this.testingSieveErrorDesc('');
this.testingSieveErrorDesc(oData.Result.Sieve);
}
if (this.testingSieveError() && oData.Result.Sieve) {
this.testingSieveErrorDesc('');
this.testingSieveErrorDesc(oData.Result.Sieve);
}
if (this.testingSmtpError() && oData.Result.Smtp) {
this.testingSmtpErrorDesc('');
this.testingSmtpErrorDesc(oData.Result.Smtp);
}
}
},
domainToParams(this)
);
if (this.testingSmtpError() && oData.Result.Smtp) {
this.testingSmtpErrorDesc('');
this.testingSmtpErrorDesc(oData.Result.Smtp);
}
}
},
params
);
}
});
}
onDomainCreateOrSaveResponse(iError) {
@ -239,7 +248,7 @@ export class DomainPopupView extends AbstractViewPopup {
if (oDomain) {
this.enableSmartPorts(false);
this.edit(true);
forEachObjectEntry(oDomain, (key, value) => this[key] && this[key](value));
forEachObjectEntry(oDomain, (key, value) => this[key]?.(value));
this.enableSmartPorts(true);
}
}

View file

@ -19,7 +19,7 @@ export class FolderSystemPopupView extends AbstractViewPopup {
this.folderSelectList = koComputable(() =>
folderListOptionsBuilder(
FolderUserStore.folderListSystemNames(),
FolderUserStore.systemFoldersNames(),
[
['', i18n('POPUPS_SYSTEM_FOLDERS/SELECT_CHOOSE_ONE')],
[UNUSED_OPTION_VALUE, i18n('POPUPS_SYSTEM_FOLDERS/SELECT_UNUSE_NAME')]

View file

@ -63,9 +63,7 @@ export class IdentityPopupView extends AbstractViewPopup {
submitForm() {
if (!this.submitRequest()) {
if (this.signature && this.signature.__fetchEditorValue) {
this.signature.__fetchEditorValue();
}
this.signature?.__fetchEditorValue?.();
if (!this.emailHasError()) {
this.emailHasError(!this.email().trim());

View file

@ -51,7 +51,7 @@ export class LanguagesPopupView extends AbstractViewPopup {
}
changeLanguage(lang) {
this.fLang && this.fLang(lang);
this.fLang?.(lang);
this.close();
}
}

View file

@ -94,7 +94,7 @@ export class OpenPgpGeneratePopupView extends AbstractViewPopup {
showError(e) {
console.log(e);
if (e && e.message) {
if (e?.message) {
this.submitError(e.message);
}
}

View file

@ -114,9 +114,9 @@ export class LoginUserView extends AbstractViewLogin {
if (Notification.InvalidInputArgument == iError) {
iError = Notification.AuthError;
}
this.submitError(getNotification(iError, (oData ? oData.ErrorMessage : ''),
this.submitError(getNotification(iError, oData?.ErrorMessage,
Notification.UnknownNotification));
this.submitErrorAdditional((oData && oData.ErrorMessageAdditional) || '');
this.submitErrorAdditional(oData?.ErrorMessageAdditional);
} else {
rl.setData(oData.Result);
}

View file

@ -31,9 +31,7 @@ export class MailFolderList extends AbstractViewLeft {
this.composeInEdit = AppUserStore.composeInEdit;
this.folderList = FolderUserStore.folderList;
this.folderListSystem = FolderUserStore.folderListSystem;
this.foldersChanging = FolderUserStore.foldersChanging;
this.systemFolders = FolderUserStore.systemFolders;
this.moveAction = moveAction;
@ -84,7 +82,7 @@ export class MailFolderList extends AbstractViewLeft {
}
el = eqs(event, 'a');
if (el && el.matches('.selectable')) {
if (el?.matches('.selectable')) {
event.preventDefault();
const folder = ko.dataFor(el);
if (folder) {
@ -172,10 +170,10 @@ export class MailFolderList extends AbstractViewLeft {
AppUserStore.focusedState.subscribe(value => {
let el = qs('li a.focused');
el && el.classList.remove('focused');
el?.classList.remove('focused');
if (Scope.FolderList === value) {
el = qs('li a.selected');
el && el.classList.add('focused');
el?.classList.add('focused');
}
});
}

View file

@ -20,7 +20,7 @@ import { isFullscreen, toggleFullscreen } from 'Common/Fullscreen';
import { mailBox, serverRequest } from 'Common/Links';
import { Selector } from 'Common/Selector';
import { i18n, initOnStartOrLangChange } from 'Common/Translator';
import { i18n } from 'Common/Translator';
import {
getFolderFromCacheList,
@ -55,14 +55,14 @@ const
*/
listAction = (...args) => MessagelistUserStore.setAction(...args);
let
iGoToUpOrDownTimeout = 0,
sLastSearchValue = '';
export class MailMessageList extends AbstractViewRight {
constructor() {
super();
this.emptySubjectValue = '';
this.iGoToUpOrDownTimeout = 0;
this.newMoveToFolder = !!SettingsGet('NewMoveToFolder');
this.allowSearch = SettingsCapa('Search');
@ -76,19 +76,10 @@ export class MailMessageList extends AbstractViewRight {
this.isMobile = ThemeStore.isMobile;
this.leftPanelDisabled = leftPanelDisabled;
this.messageListSearch = MessagelistUserStore.listSearch;
this.messageListError = MessagelistUserStore.error;
this.popupVisibility = arePopupsVisible;
this.useCheckboxesInList = SettingsUserStore.useCheckboxesInList;
this.messageListThreadUid = MessagelistUserStore.endThreadUid;
this.messageListIsLoading = MessagelistUserStore.isLoading;
initOnStartOrLangChange(() => this.emptySubjectValue = i18n('MESSAGE_LIST/EMPTY_SUBJECT_TEXT'));
this.userUsageProc = FolderUserStore.quotaPercentage;
this.addObservables({
@ -99,15 +90,13 @@ export class MailMessageList extends AbstractViewRight {
dragOverArea: null,
dragOverBodyArea: null,
inputMessageListSearchFocus: false
focusSearch: false
});
// append drag and drop
this.dragOver = ko.observable(false).extend({ throttle: 1 });
this.dragOverEnter = ko.observable(false).extend({ throttle: 1 });
this.sLastSearchValue = '';
this.addComputables({
sortSupported: () =>
@ -136,9 +125,9 @@ export class MailMessageList extends AbstractViewRight {
}
},
inputProxyMessageListSearch: {
inputSearch: {
read: MessagelistUserStore.mainSearch,
write: value => this.sLastSearchValue = value
write: value => sLastSearchValue = value
},
isIncompleteChecked: () => {
@ -146,33 +135,28 @@ export class MailMessageList extends AbstractViewRight {
return c && MessagelistUserStore().length > c;
},
hasMessages: () => 0 < MessagelistUserStore().length,
isSpamFolder: () => (FolderUserStore.spamFolder() || 0) === MessagelistUserStore().Folder,
isSpamDisabled: () => UNUSED_OPTION_VALUE === FolderUserStore.spamFolder(),
isTrashFolder: () => (FolderUserStore.trashFolder() || 0) === MessagelistUserStore().Folder,
isDraftFolder: () => (FolderUserStore.draftsFolder() || 0) === MessagelistUserStore().Folder,
isSentFolder: () => (FolderUserStore.sentFolder() || 0) === MessagelistUserStore().Folder,
archiveAllowed: () =>
(FolderUserStore.archiveFolder() || 0) !== MessagelistUserStore().Folder
&& UNUSED_OPTION_VALUE !== FolderUserStore.archiveFolder()
&& !this.isDraftFolder(),
isArchiveFolder: () => (FolderUserStore.archiveFolder() || 0) === MessagelistUserStore().Folder,
spamAllowed: () => UNUSED_OPTION_VALUE !== FolderUserStore.spamFolder()
&& (FolderUserStore.sentFolder() || 0) !== MessagelistUserStore().Folder
&& !this.isDraftFolder(),
isArchiveDisabled: () => UNUSED_OPTION_VALUE === FolderUserStore.archiveFolder(),
isSpamVisible: () => !this.isSpamFolder() && this.spamAllowed(),
isArchiveVisible: () => !this.isArchiveFolder() && !this.isArchiveDisabled() && !this.isDraftFolder(),
isUnSpamVisible: () => this.isSpamFolder() && this.spamAllowed(),
isSpamVisible: () =>
!this.isSpamFolder() && !this.isSpamDisabled() && !this.isDraftFolder() && !this.isSentFolder(),
mobileCheckedStateShow: () => ThemeStore.isMobile() ? MessagelistUserStore.listChecked().length : 1,
isUnSpamVisible: () =>
this.isSpamFolder() && !this.isSpamDisabled() && !this.isDraftFolder() && !this.isSentFolder(),
mobileCheckedStateShow: () => ThemeStore.isMobile() ? 0 < MessagelistUserStore.listChecked().length : true,
mobileCheckedStateHide: () => ThemeStore.isMobile() ? !MessagelistUserStore.listChecked().length : true,
mobileCheckedStateHide: () => ThemeStore.isMobile() ? !MessagelistUserStore.listChecked().length : 1,
sortText: () => {
let mode = FolderUserStore.sortMode(),
@ -188,8 +172,6 @@ export class MailMessageList extends AbstractViewRight {
}
});
this.hasCheckedOrSelectedLines = MessagelistUserStore.hasCheckedOrSelected,
this.selector = new Selector(
MessagelistUserStore,
MessagelistUserStore.selectedMessage,
@ -228,7 +210,7 @@ export class MailMessageList extends AbstractViewRight {
const sFolder = e.detail.Folder, iUid = e.detail.Uid;
const message = MessagelistUserStore.find(
item => item && sFolder === item.folder && iUid == item.uid
item => sFolder === item?.folder && iUid == item?.uid
);
if ('INBOX' === sFolder) {
@ -344,7 +326,7 @@ export class MailMessageList extends AbstractViewRight {
moveNewCommand(vm, event) {
if (this.newMoveToFolder && this.mobileCheckedStateShow()) {
if (vm && event && event.preventDefault) {
if (vm && event?.preventDefault) {
event.preventDefault();
event.stopPropagation();
}
@ -364,8 +346,8 @@ export class MailMessageList extends AbstractViewRight {
return false;
}
clearTimeout(this.iGoToUpOrDownTimeout);
this.iGoToUpOrDownTimeout = setTimeout(() => {
clearTimeout(iGoToUpOrDownTimeout);
iGoToUpOrDownTimeout = setTimeout(() => {
let prev, next, temp, current;
this.messageListPaginator().find(item => {
@ -412,7 +394,7 @@ export class MailMessageList extends AbstractViewRight {
cancelSearch() {
MessagelistUserStore.mainSearch('');
this.inputMessageListSearchFocus(false);
this.focusSearch(false);
}
cancelThreadUid() {
@ -446,7 +428,7 @@ export class MailMessageList extends AbstractViewRight {
getDragData(event) {
const item = ko.dataFor(doc.elementFromPoint(event.clientX, event.clientY));
item && item.checked && item.checked(true);
item?.checked?.(true);
const uids = MessagelistUserStore.listCheckedOrSelectedUidsWithSubMails();
item && !uids.includes(item.uid) && uids.push(item.uid);
return uids.length ? {
@ -589,7 +571,7 @@ export class MailMessageList extends AbstractViewRight {
}
gotoThread(message) {
if (message && 0 < message.threadsLen()) {
if (0 < message?.threadsLen()) {
MessagelistUserStore.pageBeforeThread(MessagelistUserStore.page());
hasher.setHash(
@ -643,7 +625,7 @@ export class MailMessageList extends AbstractViewRight {
el && this.gotoThread(ko.dataFor(el));
},
dblclick: event => {
let el = eqs(event, '.actionHandle');
let el = eqs(event, '.actionHandle');
el && this.gotoThread(ko.dataFor(el));
}
});
@ -684,7 +666,7 @@ export class MailMessageList extends AbstractViewRight {
addShortcut('enter,open', '', Scope.MessageList, () => {
if (formFieldFocused()) {
MessagelistUserStore.mainSearch(this.sLastSearchValue);
MessagelistUserStore.mainSearch(sLastSearchValue);
return false;
}
if (MessageUserStore.message() && this.useAutoSelect()) {
@ -735,15 +717,10 @@ export class MailMessageList extends AbstractViewRight {
});
registerShortcut('t', '', [Scope.MessageList], () => {
let message = MessagelistUserStore.selectedMessage();
if (!message) {
message = MessagelistUserStore.focusedMessage();
}
if (message && 0 < message.threadsLen()) {
let message = MessagelistUserStore.selectedMessage() || MessagelistUserStore.focusedMessage();
if (0 < message?.threadsLen()) {
this.gotoThread(message);
}
return false;
});
@ -778,7 +755,7 @@ export class MailMessageList extends AbstractViewRight {
if (SettingsCapa('Search')) {
// search input focus
addShortcut('/', '', [Scope.MessageList, Scope.MessageView], () => {
this.inputMessageListSearchFocus(true);
this.focusSearch(true);
return false;
});
}

View file

@ -7,7 +7,6 @@ import { Scope } from 'Common/Enums';
import {
ComposeType,
ClientSideKeyNameLastReplyAction,
ClientSideKeyNameMessageHeaderFullInfo,
ClientSideKeyNameMessageAttachmentControls,
FolderType,
@ -94,10 +93,11 @@ export class MailMessageView extends AbstractViewRight {
}
}, this.messageVisibility);
this.msgDefaultAction = SettingsUserStore.msgDefaultAction;
this.addObservables({
showAttachmentControls: !!Local.get(ClientSideKeyNameMessageAttachmentControls),
downloadAsZipLoading: false,
lastReplyAction_: '',
showFullInfo: '1' === Local.get(ClientSideKeyNameMessageHeaderFullInfo),
moreDropdownTrigger: false,
@ -134,19 +134,10 @@ export class MailMessageView extends AbstractViewRight {
allowAttachmentControls: () => arrayLength(attachmentsActions) && SettingsCapa('AttachmentsActions'),
downloadAsZipAllowed: () => this.attachmentsActions.includes('zip')
&& (currentMessage() ? currentMessage().attachments : [])
.filter(item => item && !item.isLinked() && item.checked() && item.download)
&& (currentMessage()?.attachments || [])
.filter(item => item?.download && !item?.isLinked() && item?.checked())
.length,
lastReplyAction: {
read: this.lastReplyAction_,
write: value => this.lastReplyAction_(
[ComposeType.Reply, ComposeType.ReplyAll, ComposeType.Forward].includes(value)
? ComposeType.Reply
: value
)
},
tagsAllowed: () => FolderUserStore.currentFolder() ? FolderUserStore.currentFolder().tagsAllowed() : false,
messageVisibility: () => !MessageUserStore.loading() && !!currentMessage(),
@ -182,8 +173,6 @@ export class MailMessageView extends AbstractViewRight {
});
this.addSubscribables({
lastReplyAction_: value => Local.set(ClientSideKeyNameLastReplyAction, value),
message: message => {
MessageUserStore.activeDom(null);
@ -207,8 +196,6 @@ export class MailMessageView extends AbstractViewRight {
showFullInfo: value => Local.set(ClientSideKeyNameMessageHeaderFullInfo, value ? '1' : '0')
});
this.lastReplyAction(Local.get(ClientSideKeyNameLastReplyAction) || ComposeType.Reply);
// commands
this.replyCommand = createCommandReplyHelper(ComposeType.Reply);
this.replyAllCommand = createCommandReplyHelper(ComposeType.ReplyAll);
@ -256,7 +243,6 @@ export class MailMessageView extends AbstractViewRight {
* @returns {void}
*/
replyOrforward(sType) {
this.lastReplyAction(sType);
showMessageComposer([sType, currentMessage()]);
}
@ -300,7 +286,7 @@ export class MailMessageView extends AbstractViewRight {
el = eqs(event, '.attachmentsPlace .attachmentItem .attachmentNameParent');
if (el) {
const attachment = ko.dataFor(el);
if (attachment && attachment.linkDownload()) {
if (attachment?.linkDownload()) {
if ('message/rfc822' == attachment.mimeType) {
// TODO
rl.fetch(attachment.linkDownload()).then(response => {
@ -407,7 +393,7 @@ export class MailMessageView extends AbstractViewRight {
// toggle message blockquotes
registerShortcut('b', '', [Scope.MessageList, Scope.MessageView], () => {
const message = currentMessage();
if (message && message.body) {
if (message?.body) {
message.body.querySelectorAll('.rlBlockquoteSwitcher').forEach(node => node.click());
return false;
}
@ -425,7 +411,7 @@ export class MailMessageView extends AbstractViewRight {
// print
addShortcut('p,printscreen', 'meta', [Scope.MessageView, Scope.MessageList], () => {
currentMessage() && currentMessage().printMessage();
currentMessage()?.printMessage();
return false;
});
@ -524,7 +510,7 @@ export class MailMessageView extends AbstractViewRight {
downloadAsZip() {
const hashes = (currentMessage() ? currentMessage().attachments : [])
.map(item => (item && !item.isLinked() && item.checked() ? item.download : ''))
.map(item => item?.checked() && !item?.isLinked() ? item.download : '')
.filter(v => v);
if (hashes.length) {
Remote.post('AttachmentsActions', this.downloadAsZipLoading, {
@ -532,7 +518,7 @@ export class MailMessageView extends AbstractViewRight {
Hashes: hashes
})
.then(result => {
let hash = result && result.Result && result.Result.FileHash;
let hash = result?.Result?.FileHash;
if (hash) {
download(attachmentDownload(hash), hash+'.zip');
} else {
@ -591,7 +577,7 @@ export class MailMessageView extends AbstractViewRight {
if (result.data) {
MimeToMessage(result.data, oMessage);
oMessage.html() ? oMessage.viewHtml() : oMessage.viewPlain();
if (result.signatures && result.signatures.length) {
if (result.signatures?.length) {
oMessage.pgpSigned(true);
oMessage.pgpVerified({
signatures: result.signatures,
@ -610,7 +596,7 @@ export class MailMessageView extends AbstractViewRight {
oMessage.pgpVerified(result);
}
/*
if (result && result.success) {
if (result?.success) {
i18n('OPENPGP/GOOD_SIGNATURE', {
USER: validKey.user + ' (' + validKey.id + ')'
});
@ -618,7 +604,7 @@ export class MailMessageView extends AbstractViewRight {
} else {
const keyIds = arrayLength(signingKeyIds) ? signingKeyIds : null,
additional = keyIds
? keyIds.map(item => (item && item.toHex ? item.toHex() : null)).filter(v => v).join(', ')
? keyIds.map(item => item?.toHex?.()).filter(v => v).join(', ')
: '';
i18n('OPENPGP/ERROR', {

View file

@ -52,7 +52,7 @@ export class SystemDropDownUserView extends AbstractViewRight {
}
accountClick(account, event) {
let email = account && account.email;
let email = account?.email;
if (email && 0 === event.button && AccountUserStore.email() != email) {
AccountUserStore.loading(true);
event.preventDefault();