mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-08-25 18:19:22 +03:00
Merge branch 'master' into UserMailTemplates
# Conflicts: # dev/App/User.js # dev/Common/Enums.js # dev/Screen/User/Settings.js # dev/Settings/Admin/General.js # snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php # snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php # snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php
This commit is contained in:
commit
f7f56b3789
794 changed files with 96444 additions and 74188 deletions
|
|
@ -1,18 +1,14 @@
|
|||
import ko from 'ko';
|
||||
|
||||
import { Settings } from 'Common/Globals';
|
||||
import { fireEvent } from 'Common/Globals';
|
||||
import { getNotification } from 'Common/Translator';
|
||||
|
||||
import Remote from 'Remote/Admin/Fetch';
|
||||
|
||||
import { decorateKoCommands } from 'Knoin/Knoin';
|
||||
import { AbstractViewCenter } from 'Knoin/AbstractViews';
|
||||
import { AbstractViewLogin } from 'Knoin/AbstractViews';
|
||||
|
||||
class LoginAdminView extends AbstractViewCenter {
|
||||
export class AdminLoginView extends AbstractViewLogin {
|
||||
constructor() {
|
||||
super('Admin/Login', 'AdminLogin');
|
||||
|
||||
this.hideSubmitButton = Settings.app('hideSubmitButton');
|
||||
super('AdminLogin');
|
||||
|
||||
this.addObservables({
|
||||
login: '',
|
||||
|
|
@ -26,8 +22,6 @@ class LoginAdminView extends AbstractViewCenter {
|
|||
submitError: ''
|
||||
});
|
||||
|
||||
this.formError = ko.observable(false).extend({ falseTimeout: 500 });
|
||||
|
||||
this.addSubscribables({
|
||||
login: () => this.loginError(false),
|
||||
password: () => this.passwordError(false)
|
||||
|
|
@ -39,42 +33,34 @@ class LoginAdminView extends AbstractViewCenter {
|
|||
}
|
||||
|
||||
submitCommand(self, event) {
|
||||
const valid = event.target.form.reportValidity(),
|
||||
name = this.login().trim(),
|
||||
pass = this.password();
|
||||
let form = event.target.form,
|
||||
data = new FormData(form),
|
||||
valid = form.reportValidity() && fireEvent('sm-admin-login', data);
|
||||
|
||||
this.loginError(!name);
|
||||
this.passwordError(!pass);
|
||||
this.loginError(!this.login());
|
||||
this.passwordError(!this.password());
|
||||
this.formError(!valid);
|
||||
|
||||
if (valid) {
|
||||
this.submitRequest(true);
|
||||
|
||||
Remote.adminLogin(
|
||||
iError => {
|
||||
Remote.request('AdminLogin',
|
||||
(iError, oData) => {
|
||||
fireEvent('sm-admin-login-response', {
|
||||
error: iError,
|
||||
data: oData
|
||||
});
|
||||
if (iError) {
|
||||
this.submitRequest(false);
|
||||
this.submitError(getNotification(iError));
|
||||
} else {
|
||||
rl.route.reload();
|
||||
rl.setData(oData.Result);
|
||||
}
|
||||
},
|
||||
name,
|
||||
pass,
|
||||
this.totp()
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
onShow() {
|
||||
rl.route.off();
|
||||
}
|
||||
|
||||
submitForm() {
|
||||
// return false;
|
||||
}
|
||||
}
|
||||
|
||||
export { LoginAdminView };
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ class MenuSettingsAdminView extends AbstractViewLeft {
|
|||
* @param {?} screen
|
||||
*/
|
||||
constructor(screen) {
|
||||
super('Admin/Settings/Menu', 'AdminMenu');
|
||||
super('AdminMenu');
|
||||
|
||||
this.menu = screen.menu;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,16 @@
|
|||
import ko from 'ko';
|
||||
|
||||
import Remote from 'Remote/Admin/Fetch';
|
||||
|
||||
import { PackageAdminStore } from 'Stores/Admin/Package';
|
||||
|
||||
import { AbstractViewRight } from 'Knoin/AbstractViews';
|
||||
|
||||
import { leftPanelDisabled, Settings } from 'Common/Globals';
|
||||
import { leftPanelDisabled } from 'Common/Globals';
|
||||
|
||||
class PaneSettingsAdminView extends AbstractViewRight {
|
||||
export class PaneSettingsAdminView extends AbstractViewRight {
|
||||
constructor() {
|
||||
super('Admin/Settings/Pane', 'AdminPane');
|
||||
|
||||
this.version = ko.observable(Settings.app('version'));
|
||||
|
||||
super('AdminPane');
|
||||
this.leftPanelDisabled = leftPanelDisabled;
|
||||
|
||||
this.adminManLoadingVisibility = ko
|
||||
.computed(() => PackageAdminStore.loading() ? 'visible' : 'hidden');
|
||||
}
|
||||
|
||||
toggleLeft(item, event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
leftPanelDisabled(!leftPanelDisabled());
|
||||
}
|
||||
|
||||
logoutClick() {
|
||||
Remote.adminLogout(() => rl.logoutReload());
|
||||
Remote.request('AdminLogout', () => rl.logoutReload());
|
||||
}
|
||||
}
|
||||
|
||||
export { PaneSettingsAdminView };
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@ import { getNotification } from 'Common/Translator';
|
|||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
|
||||
import { decorateKoCommands } from 'Knoin/Knoin';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
class AccountPopupView extends AbstractViewPopup {
|
||||
export class AccountPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('Account');
|
||||
|
||||
|
|
@ -26,45 +25,42 @@ class AccountPopupView extends AbstractViewPopup {
|
|||
this.email.subscribe(() => this.emailError(false));
|
||||
|
||||
this.password.subscribe(() => this.passwordError(false));
|
||||
|
||||
decorateKoCommands(this, {
|
||||
addAccountCommand: self => !self.submitRequest()
|
||||
});
|
||||
}
|
||||
|
||||
addAccountCommand() {
|
||||
this.emailError(!this.email().trim());
|
||||
this.passwordError(!this.password().trim());
|
||||
|
||||
if (this.emailError() || this.passwordError()) {
|
||||
return false;
|
||||
submitForm() {
|
||||
if (!this.submitRequest()) {
|
||||
const email = this.email().trim(), pass = this.password();
|
||||
this.emailError(!email);
|
||||
this.passwordError(!pass);
|
||||
if (!this.emailError() && pass) {
|
||||
this.submitRequest(true);
|
||||
Remote.request('AccountSetup', (iError, data) => {
|
||||
this.submitRequest(false);
|
||||
if (iError) {
|
||||
this.submitError(getNotification(iError));
|
||||
this.submitErrorAdditional((data && data.ErrorMessageAdditional) || '');
|
||||
} else {
|
||||
rl.app.accountsAndIdentities();
|
||||
this.close();
|
||||
}
|
||||
}, {
|
||||
Email: email,
|
||||
Password: pass,
|
||||
New: this.isNew() ? 1 : 0
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.submitRequest(true);
|
||||
|
||||
Remote.accountSetup(
|
||||
(iError, data) => {
|
||||
this.submitRequest(false);
|
||||
if (iError) {
|
||||
this.submitError(getNotification(iError));
|
||||
this.submitErrorAdditional((data && data.ErrorMessageAdditional) || '');
|
||||
} else {
|
||||
rl.app.accountsAndIdentities();
|
||||
this.cancelCommand();
|
||||
}
|
||||
},
|
||||
this.email(),
|
||||
this.password(),
|
||||
this.isNew()
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
clearPopup() {
|
||||
this.isNew(true);
|
||||
|
||||
this.email('');
|
||||
onShow(account) {
|
||||
if (account && account.isAdditional()) {
|
||||
this.isNew(false);
|
||||
this.email(account.email);
|
||||
} else {
|
||||
this.isNew(true);
|
||||
this.email('');
|
||||
}
|
||||
this.password('');
|
||||
|
||||
this.emailError(false);
|
||||
|
|
@ -74,14 +70,4 @@ class AccountPopupView extends AbstractViewPopup {
|
|||
this.submitError('');
|
||||
this.submitErrorAdditional('');
|
||||
}
|
||||
|
||||
onShow(account) {
|
||||
this.clearPopup();
|
||||
if (account && account.canBeEdit()) {
|
||||
this.isNew(false);
|
||||
this.email(account.email);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { AccountPopupView, AccountPopupView as default };
|
||||
|
|
|
|||
|
|
@ -1,96 +0,0 @@
|
|||
import { PgpUserStore } from 'Stores/User/Pgp';
|
||||
|
||||
import { decorateKoCommands } from 'Knoin/Knoin';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
class AddOpenPgpKeyPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('AddOpenPgpKey');
|
||||
|
||||
this.addObservables({
|
||||
key: '',
|
||||
keyError: false,
|
||||
keyErrorMessage: ''
|
||||
});
|
||||
|
||||
this.key.subscribe(() => {
|
||||
this.keyError(false);
|
||||
this.keyErrorMessage('');
|
||||
});
|
||||
|
||||
decorateKoCommands(this, {
|
||||
addOpenPgpKeyCommand: 1
|
||||
});
|
||||
}
|
||||
|
||||
addOpenPgpKeyCommand() {
|
||||
// eslint-disable-next-line max-len
|
||||
const reg = /[-]{3,6}BEGIN[\s]PGP[\s](PRIVATE|PUBLIC)[\s]KEY[\s]BLOCK[-]{3,6}[\s\S]+?[-]{3,6}END[\s]PGP[\s](PRIVATE|PUBLIC)[\s]KEY[\s]BLOCK[-]{3,6}/gi,
|
||||
openpgpKeyring = PgpUserStore.openpgpKeyring;
|
||||
|
||||
let keyTrimmed = this.key().trim();
|
||||
|
||||
if (/[\n]/.test(keyTrimmed)) {
|
||||
keyTrimmed = keyTrimmed.replace(/[\r]+/g, '').replace(/[\n]{2,}/g, '\n\n');
|
||||
}
|
||||
|
||||
this.keyError(!keyTrimmed);
|
||||
this.keyErrorMessage('');
|
||||
|
||||
if (!openpgpKeyring || this.keyError()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let match = null,
|
||||
count = 30,
|
||||
done = false;
|
||||
|
||||
do {
|
||||
match = reg.exec(keyTrimmed);
|
||||
if (match && 0 < count) {
|
||||
if (match[0] && match[1] && match[2] && match[1] === match[2]) {
|
||||
let err = null;
|
||||
if ('PRIVATE' === match[1]) {
|
||||
err = openpgpKeyring.privateKeys.importKey(match[0]);
|
||||
} else if ('PUBLIC' === match[1]) {
|
||||
err = openpgpKeyring.publicKeys.importKey(match[0]);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
this.keyError(true);
|
||||
this.keyErrorMessage(err && err[0] ? '' + err[0] : '');
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
--count;
|
||||
done = false;
|
||||
} else {
|
||||
done = true;
|
||||
}
|
||||
} while (!done);
|
||||
|
||||
openpgpKeyring.store();
|
||||
|
||||
rl.app.reloadOpenPgpKeys();
|
||||
|
||||
if (this.keyError()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.cancelCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
clearPopup() {
|
||||
this.key('');
|
||||
this.keyError(false);
|
||||
this.keyErrorMessage('');
|
||||
}
|
||||
|
||||
onShow() {
|
||||
this.clearPopup();
|
||||
}
|
||||
}
|
||||
|
||||
export { AddOpenPgpKeyPopupView, AddOpenPgpKeyPopupView as default };
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
import ko from 'ko';
|
||||
import { koComputable } from 'External/ko';
|
||||
|
||||
import { i18n, trigger as translatorTrigger } from 'Common/Translator';
|
||||
|
||||
import { MessageUserStore } from 'Stores/User/Message';
|
||||
import { MessagelistUserStore } from 'Stores/User/Messagelist';
|
||||
|
||||
import { decorateKoCommands } from 'Knoin/Knoin';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
import { FolderUserStore } from 'Stores/User/Folder';
|
||||
|
||||
class AdvancedSearchPopupView extends AbstractViewPopup {
|
||||
export class AdvancedSearchPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('AdvancedSearch');
|
||||
|
||||
|
|
@ -17,15 +17,18 @@ class AdvancedSearchPopupView extends AbstractViewPopup {
|
|||
subject: '',
|
||||
text: '',
|
||||
selectedDateValue: -1,
|
||||
selectedTreeValue: '',
|
||||
|
||||
hasAttachment: false,
|
||||
starred: false,
|
||||
unseen: false
|
||||
});
|
||||
|
||||
let prefix = 'SEARCH/LABEL_ADV_DATE_';
|
||||
this.selectedDates = ko.computed(() => {
|
||||
this.showMultisearch = koComputable(() => FolderUserStore.hasCapability('MULTISEARCH'));
|
||||
|
||||
this.selectedDates = koComputable(() => {
|
||||
translatorTrigger();
|
||||
let prefix = 'SEARCH/LABEL_ADV_DATE_';
|
||||
return [
|
||||
{ id: -1, name: i18n(prefix + 'ALL') },
|
||||
{ id: 3, name: i18n(prefix + '3_DAYS') },
|
||||
|
|
@ -37,18 +40,24 @@ class AdvancedSearchPopupView extends AbstractViewPopup {
|
|||
];
|
||||
});
|
||||
|
||||
decorateKoCommands(this, {
|
||||
searchCommand: 1
|
||||
this.selectedTree = koComputable(() => {
|
||||
translatorTrigger();
|
||||
let prefix = 'SEARCH/LABEL_ADV_SUBFOLDERS_';
|
||||
return [
|
||||
{ id: '', name: i18n(prefix + 'NONE') },
|
||||
{ id: 'subtree-one', name: i18n(prefix + 'SUBTREE_ONE') },
|
||||
{ id: 'subtree', name: i18n(prefix + 'SUBTREE') }
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
searchCommand() {
|
||||
submitForm() {
|
||||
const search = this.buildSearchString();
|
||||
if (search) {
|
||||
MessageUserStore.mainMessageListSearch(search);
|
||||
MessagelistUserStore.mainSearch(search);
|
||||
}
|
||||
|
||||
this.cancelCommand();
|
||||
this.close();
|
||||
}
|
||||
|
||||
parseSearchStringValue(search) {
|
||||
|
|
@ -69,68 +78,38 @@ class AdvancedSearchPopupView extends AbstractViewPopup {
|
|||
});
|
||||
}
|
||||
|
||||
buildSearchStringValue(value) {
|
||||
if (value.includes(' ')) {
|
||||
value = '"' + value + '"';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
buildSearchString() {
|
||||
const result = [],
|
||||
from_ = this.from().trim(),
|
||||
to = this.to().trim(),
|
||||
subject = this.subject().trim(),
|
||||
text = this.text().trim(),
|
||||
isPart = [],
|
||||
hasPart = [];
|
||||
|
||||
if (from_) {
|
||||
result.push('from:' + this.buildSearchStringValue(from_));
|
||||
}
|
||||
|
||||
if (to) {
|
||||
result.push('to:' + this.buildSearchStringValue(to));
|
||||
}
|
||||
|
||||
if (subject) {
|
||||
result.push('subject:' + this.buildSearchStringValue(subject));
|
||||
}
|
||||
|
||||
if (this.hasAttachment()) {
|
||||
hasPart.push('attachment');
|
||||
}
|
||||
|
||||
if (this.unseen()) {
|
||||
isPart.push('unseen');
|
||||
}
|
||||
|
||||
if (this.starred()) {
|
||||
isPart.push('flagged');
|
||||
}
|
||||
|
||||
if (hasPart.length) {
|
||||
result.push('has:' + hasPart.join(','));
|
||||
}
|
||||
|
||||
if (isPart.length) {
|
||||
result.push('is:' + isPart.join(','));
|
||||
}
|
||||
const
|
||||
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()) {
|
||||
let d = new Date();
|
||||
d.setDate(d.getDate() - this.selectedDateValue());
|
||||
result.push('date:' + d.format('Y.m.d') + '/');
|
||||
append('date', d.format('Y.m.d') + '/');
|
||||
}
|
||||
|
||||
if (text) {
|
||||
result.push('text:' + this.buildSearchStringValue(text));
|
||||
let result = new URLSearchParams(data).toString();
|
||||
|
||||
if (this.hasAttachment()) {
|
||||
result += '&attachment';
|
||||
}
|
||||
if (this.unseen()) {
|
||||
result += '&unseen';
|
||||
}
|
||||
if (this.starred()) {
|
||||
result += '&flagged';
|
||||
}
|
||||
|
||||
return result.join(' ').trim();
|
||||
return result.replace(/^&+/, '');
|
||||
}
|
||||
|
||||
clearPopup() {
|
||||
onShow(search) {
|
||||
this.from('');
|
||||
this.to('');
|
||||
this.subject('');
|
||||
|
|
@ -140,12 +119,7 @@ class AdvancedSearchPopupView extends AbstractViewPopup {
|
|||
this.hasAttachment(false);
|
||||
this.starred(false);
|
||||
this.unseen(false);
|
||||
}
|
||||
|
||||
onShow(search) {
|
||||
this.clearPopup();
|
||||
this.parseSearchStringValue(search);
|
||||
}
|
||||
}
|
||||
|
||||
export { AdvancedSearchPopupView, AdvancedSearchPopupView as default };
|
||||
|
|
|
|||
|
|
@ -1,84 +1,68 @@
|
|||
import { Scope } from 'Common/Enums';
|
||||
import { i18n } from 'Common/Translator';
|
||||
import { isFunction } from 'Common/Utils';
|
||||
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
class AskPopupView extends AbstractViewPopup {
|
||||
export class AskPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('Ask');
|
||||
|
||||
this.addObservables({
|
||||
askDesc: '',
|
||||
yesButton: '',
|
||||
noButton: ''
|
||||
noButton: '',
|
||||
passphrase: '',
|
||||
askPass: false
|
||||
});
|
||||
|
||||
this.fYesAction = null;
|
||||
this.fNoAction = null;
|
||||
|
||||
this.bFocusYesOnShow = true;
|
||||
this.bDisabeCloseOnEsc = true;
|
||||
}
|
||||
|
||||
clearPopup() {
|
||||
this.askDesc('');
|
||||
this.yesButton(i18n('POPUPS_ASK/BUTTON_YES'));
|
||||
this.noButton(i18n('POPUPS_ASK/BUTTON_NO'));
|
||||
|
||||
this.fYesAction = null;
|
||||
this.fNoAction = null;
|
||||
this.focusOnShow = true;
|
||||
}
|
||||
|
||||
yesClick() {
|
||||
this.cancelCommand();
|
||||
this.close();
|
||||
|
||||
isFunction(this.fYesAction) && this.fYesAction.call(null);
|
||||
isFunction(this.fYesAction) && this.fYesAction();
|
||||
}
|
||||
|
||||
noClick() {
|
||||
this.cancelCommand();
|
||||
this.close();
|
||||
|
||||
isFunction(this.fNoAction) && this.fNoAction.call(null);
|
||||
isFunction(this.fNoAction) && this.fNoAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} sAskDesc
|
||||
* @param {Function=} fYesFunc
|
||||
* @param {Function=} fNoFunc
|
||||
* @param {string=} sYesButton
|
||||
* @param {string=} sNoButton
|
||||
* @param {boolean=} bFocusYesOnShow = true
|
||||
* @param {boolean=} focusOnShow = true
|
||||
* @returns {void}
|
||||
*/
|
||||
onShow(askDesc, fYesFunc = null, fNoFunc = null, yesButton = '', noButton = '', isFocusYesOnShow = true) {
|
||||
this.clearPopup();
|
||||
|
||||
this.fYesAction = fYesFunc || null;
|
||||
this.fNoAction = fNoFunc || null;
|
||||
|
||||
this.askDesc(askDesc || '');
|
||||
|
||||
if (yesButton) {
|
||||
this.yesButton(yesButton);
|
||||
}
|
||||
|
||||
if (noButton) {
|
||||
this.noButton(noButton);
|
||||
}
|
||||
|
||||
this.bFocusYesOnShow = !!isFocusYesOnShow;
|
||||
onShow(sAskDesc, fYesFunc = null, fNoFunc = null, focusOnShow = true, askPass = false, btnText = '') {
|
||||
this.askDesc(sAskDesc || '');
|
||||
this.askPass(askPass);
|
||||
this.passphrase('');
|
||||
this.yesButton(i18n(btnText || 'POPUPS_ASK/BUTTON_YES'));
|
||||
this.noButton(i18n(askPass ? 'GLOBAL/CANCEL' : 'POPUPS_ASK/BUTTON_NO'));
|
||||
this.fYesAction = fYesFunc;
|
||||
this.fNoAction = fNoFunc;
|
||||
this.focusOnShow = focusOnShow ? (askPass ? 'input[type="password"]' : '.buttonYes') : '';
|
||||
}
|
||||
|
||||
onShowWithDelay() {
|
||||
if (this.bFocusYesOnShow) {
|
||||
this.querySelector('.buttonYes').focus();
|
||||
}
|
||||
afterShow() {
|
||||
this.focusOnShow && this.querySelector(this.focusOnShow).focus();
|
||||
}
|
||||
|
||||
onClose() {
|
||||
this.noClick();
|
||||
return false;
|
||||
}
|
||||
|
||||
onBuild() {
|
||||
// shortcuts.add('tab', 'shift', Scope.Ask, () => {
|
||||
shortcuts.add('tab,arrowright,arrowleft', '', Scope.Ask, () => {
|
||||
// shortcuts.add('tab', 'shift', 'Ask', () => {
|
||||
shortcuts.add('tab,arrowright,arrowleft', '', 'Ask', () => {
|
||||
let btn = this.querySelector('.buttonYes');
|
||||
if (btn.matches(':focus')) {
|
||||
btn = this.querySelector('.buttonNo');
|
||||
|
|
@ -86,12 +70,18 @@ class AskPopupView extends AbstractViewPopup {
|
|||
btn.focus();
|
||||
return false;
|
||||
});
|
||||
|
||||
shortcuts.add('escape', '', Scope.Ask, () => {
|
||||
this.noClick();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { AskPopupView, AskPopupView as default };
|
||||
AskPopupView.password = function(sAskDesc, btnText) {
|
||||
return new Promise(resolve => {
|
||||
this.showModal([
|
||||
sAskDesc,
|
||||
() => resolve(this.__vm.passphrase()),
|
||||
() => resolve(null),
|
||||
true,
|
||||
true,
|
||||
btnText
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,385 +0,0 @@
|
|||
import ko from 'ko';
|
||||
|
||||
import { pString, defaultOptionsAfterRender } from 'Common/Utils';
|
||||
|
||||
import { Scope } from 'Common/Enums';
|
||||
import { i18n } from 'Common/Translator';
|
||||
|
||||
import { PgpUserStore } from 'Stores/User/Pgp';
|
||||
|
||||
import { EmailModel } from 'Model/Email';
|
||||
|
||||
import { decorateKoCommands } from 'Knoin/Knoin';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
const KEY_NAME_SUBSTR = -8,
|
||||
i18nPGP = (key, params) => i18n('PGP_NOTIFICATIONS/' + key, params);
|
||||
|
||||
class ComposeOpenPgpPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('ComposeOpenPgp');
|
||||
|
||||
this.publicKeysOptionsCaption = i18nPGP('ADD_A_PUBLICK_KEY');
|
||||
this.privateKeysOptionsCaption = i18nPGP('SELECT_A_PRIVATE_KEY');
|
||||
|
||||
this.addObservables({
|
||||
notification: '',
|
||||
|
||||
sign: false,
|
||||
encrypt: false,
|
||||
|
||||
password: '',
|
||||
|
||||
text: '',
|
||||
selectedPrivateKey: null,
|
||||
selectedPublicKey: null,
|
||||
|
||||
signKey: null,
|
||||
|
||||
submitRequest: false
|
||||
});
|
||||
this.encryptKeys = ko.observableArray();
|
||||
|
||||
this.addComputables({
|
||||
encryptKeysView: () => this.encryptKeys.map(oKey => (oKey ? oKey.key : null)).filter(v => v),
|
||||
|
||||
privateKeysOptions: () => {
|
||||
const opts = PgpUserStore.openpgpkeysPrivate().map(oKey => {
|
||||
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
|
||||
}));
|
||||
});
|
||||
|
||||
return opts.flat().filter(v => v);
|
||||
},
|
||||
|
||||
publicKeysOptions: () => {
|
||||
const opts = PgpUserStore.openpgpkeysPublic().map(oKey => {
|
||||
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
|
||||
}));
|
||||
});
|
||||
return opts.flat().filter(v => v);
|
||||
}
|
||||
});
|
||||
|
||||
this.resultCallback = null;
|
||||
|
||||
this.selectedPrivateKey.subscribe((value) => {
|
||||
if (value) {
|
||||
this.selectCommand();
|
||||
this.updateCommand();
|
||||
}
|
||||
});
|
||||
|
||||
this.selectedPublicKey.subscribe((value) => {
|
||||
if (value) {
|
||||
this.addCommand();
|
||||
}
|
||||
});
|
||||
|
||||
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
|
||||
|
||||
this.deletePublickKey = this.deletePublickKey.bind(this);
|
||||
|
||||
decorateKoCommands(this, {
|
||||
doCommand: self => !self.submitRequest() && (self.sign() || self.encrypt()),
|
||||
selectCommand: 1,
|
||||
addCommand: 1,
|
||||
updateCommand: 1,
|
||||
});
|
||||
}
|
||||
|
||||
doCommand() {
|
||||
let result = true,
|
||||
privateKey = null,
|
||||
aPublicKeys = [];
|
||||
|
||||
this.submitRequest(true);
|
||||
|
||||
if (result && this.sign()) {
|
||||
if (!this.signKey()) {
|
||||
this.notification(i18nPGP('NO_PRIVATE_KEY_FOUND'));
|
||||
result = false;
|
||||
} else if (!this.signKey().key) {
|
||||
this.notification(
|
||||
i18nPGP('NO_PRIVATE_KEY_FOUND_FOR', {
|
||||
EMAIL: this.signKey().email
|
||||
})
|
||||
);
|
||||
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
const privateKeys = this.signKey().key.getNativeKeys();
|
||||
privateKey = privateKeys[0] || null;
|
||||
|
||||
try {
|
||||
if (privateKey) {
|
||||
privateKey.decrypt(pString(this.password()));
|
||||
}
|
||||
} catch (e) {
|
||||
privateKey = null;
|
||||
}
|
||||
|
||||
if (!privateKey) {
|
||||
this.notification(i18nPGP('NO_PRIVATE_KEY_FOUND'));
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result && this.encrypt()) {
|
||||
if (this.encryptKeys.length) {
|
||||
aPublicKeys = [];
|
||||
|
||||
this.encryptKeys.forEach(oKey => {
|
||||
if (oKey && oKey.key) {
|
||||
aPublicKeys = aPublicKeys.concat(oKey.key.getNativeKeys().flat(Infinity).filter(v => v));
|
||||
} else if (oKey && oKey.email) {
|
||||
this.notification(
|
||||
i18nPGP('NO_PUBLIC_KEYS_FOUND_FOR', {
|
||||
EMAIL: oKey.email
|
||||
})
|
||||
);
|
||||
|
||||
result = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (result && (!aPublicKeys.length || this.encryptKeys.length !== aPublicKeys.length)) {
|
||||
result = false;
|
||||
}
|
||||
} else {
|
||||
this.notification(i18nPGP('NO_PUBLIC_KEYS_FOUND'));
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (result && this.resultCallback) {
|
||||
setTimeout(() => {
|
||||
let pgpPromise = null;
|
||||
|
||||
try {
|
||||
if (aPublicKeys.length) {
|
||||
if (privateKey) {
|
||||
pgpPromise = PgpUserStore.openpgp.encrypt({
|
||||
data: this.text(),
|
||||
publicKeys: aPublicKeys,
|
||||
privateKeys: [privateKey]
|
||||
});
|
||||
} else {
|
||||
pgpPromise = PgpUserStore.openpgp.encrypt({
|
||||
data: this.text(),
|
||||
publicKeys: aPublicKeys
|
||||
});
|
||||
}
|
||||
} else if (privateKey) {
|
||||
pgpPromise = PgpUserStore.openpgp.sign({
|
||||
data: this.text(),
|
||||
privateKeys: [privateKey]
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
|
||||
this.notification(
|
||||
i18nPGP('PGP_ERROR', {
|
||||
ERROR: '' + e
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (pgpPromise) {
|
||||
try {
|
||||
pgpPromise
|
||||
.then((mData) => {
|
||||
this.resultCallback(mData.data);
|
||||
this.cancelCommand();
|
||||
})
|
||||
.catch((e) => {
|
||||
this.notification(
|
||||
i18nPGP('PGP_ERROR', {
|
||||
ERROR: '' + e
|
||||
})
|
||||
);
|
||||
});
|
||||
} catch (e) {
|
||||
this.notification(
|
||||
i18nPGP('PGP_ERROR', {ERROR: '' + e})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.submitRequest(false);
|
||||
}, 20);
|
||||
} else {
|
||||
this.submitRequest(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
selectCommand() {
|
||||
const keyId = this.selectedPrivateKey(),
|
||||
option = keyId ? this.privateKeysOptions().find(item => item && keyId === item.id) : null;
|
||||
|
||||
if (option) {
|
||||
this.signKey({
|
||||
empty: !option.key,
|
||||
selected: ko.observable(!!option.key),
|
||||
users: option.key.users,
|
||||
hash: option.key.id.substr(KEY_NAME_SUBSTR).toUpperCase(),
|
||||
key: option.key
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
addCommand() {
|
||||
const keyId = this.selectedPublicKey(),
|
||||
option = keyId ? this.publicKeysOptions().find(item => item && keyId === item.id) : null;
|
||||
|
||||
if (option) {
|
||||
this.encryptKeys.push({
|
||||
empty: !option.key,
|
||||
selected: ko.observable(!!option.key),
|
||||
removable: ko.observable(!this.sign() || !this.signKey() || this.signKey().key.id !== option.key.id),
|
||||
users: option.key.users,
|
||||
hash: option.key.id.substr(KEY_NAME_SUBSTR).toUpperCase(),
|
||||
key: option.key
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateCommand() {
|
||||
this.encryptKeys.forEach(oKey =>
|
||||
oKey.removable(!this.sign() || !this.signKey() || this.signKey().key.id !== oKey.key.id)
|
||||
);
|
||||
}
|
||||
|
||||
deletePublickKey(publicKey) {
|
||||
this.encryptKeys.remove(publicKey);
|
||||
}
|
||||
|
||||
clearPopup() {
|
||||
this.notification('');
|
||||
|
||||
this.sign(false);
|
||||
this.encrypt(false);
|
||||
|
||||
this.password('');
|
||||
|
||||
this.signKey(null);
|
||||
this.encryptKeys([]);
|
||||
this.text('');
|
||||
|
||||
this.resultCallback = null;
|
||||
}
|
||||
|
||||
onBuild() {
|
||||
// shortcuts.add('tab', 'shift', Scope.ComposeOpenPgp, () => {
|
||||
shortcuts.add('tab', '', Scope.ComposeOpenPgp, () => {
|
||||
let btn = this.querySelector('.inputPassword');
|
||||
if (btn.matches(':focus')) {
|
||||
btn = this.querySelector('.buttonDo');
|
||||
}
|
||||
btn.focus();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
onHideWithDelay() {
|
||||
this.clearPopup();
|
||||
}
|
||||
|
||||
onShowWithDelay() {
|
||||
this.querySelector(this.sign() ? '.inputPassword' : '.buttonDo').focus();
|
||||
}
|
||||
|
||||
onShow(fCallback, sText, identity, sTo, sCc, sBcc) {
|
||||
this.clearPopup();
|
||||
|
||||
let rec = [],
|
||||
emailLine = '';
|
||||
|
||||
const email = new EmailModel();
|
||||
|
||||
this.resultCallback = fCallback;
|
||||
|
||||
if (sTo) {
|
||||
rec.push(sTo);
|
||||
}
|
||||
|
||||
if (sCc) {
|
||||
rec.push(sCc);
|
||||
}
|
||||
|
||||
if (sBcc) {
|
||||
rec.push(sBcc);
|
||||
}
|
||||
|
||||
rec = rec.join(', ').split(',');
|
||||
rec = rec.map(value => {
|
||||
email.clear();
|
||||
email.parse(value.trim());
|
||||
return email.email || false;
|
||||
}).filter(v => v);
|
||||
|
||||
if (identity && identity.email()) {
|
||||
emailLine = identity.email();
|
||||
rec.unshift(emailLine);
|
||||
|
||||
const keys = PgpUserStore.findAllPrivateKeysByEmailNotNative(emailLine);
|
||||
if (keys && keys[0]) {
|
||||
this.signKey({
|
||||
users: keys[0].users || [emailLine],
|
||||
hash: keys[0].id.substr(KEY_NAME_SUBSTR).toUpperCase(),
|
||||
key: keys[0]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.signKey()) {
|
||||
this.sign(true);
|
||||
}
|
||||
|
||||
if (rec.length) {
|
||||
this.encryptKeys(
|
||||
rec.map(recEmail => {
|
||||
const keys = PgpUserStore.findAllPublicKeysByEmailNotNative(recEmail);
|
||||
return keys
|
||||
? keys.map(publicKey => ({
|
||||
empty: !publicKey,
|
||||
selected: ko.observable(!!publicKey),
|
||||
removable: ko.observable(
|
||||
!this.sign() || !this.signKey() || this.signKey().key.id !== publicKey.id
|
||||
),
|
||||
users: publicKey ? publicKey.users || [recEmail] : [recEmail],
|
||||
hash: publicKey ? publicKey.id.substr(KEY_NAME_SUBSTR).toUpperCase() : '',
|
||||
key: publicKey
|
||||
}))
|
||||
: [];
|
||||
}).flat().validUnique(encryptKey => encryptKey.hash)
|
||||
);
|
||||
|
||||
if (this.encryptKeys.length) {
|
||||
this.encrypt(true);
|
||||
}
|
||||
}
|
||||
|
||||
this.text(sText);
|
||||
}
|
||||
}
|
||||
|
||||
export { ComposeOpenPgpPopupView, ComposeOpenPgpPopupView as default };
|
||||
|
|
@ -1,14 +1,10 @@
|
|||
import ko from 'ko';
|
||||
|
||||
import {
|
||||
SaveSettingsStep,
|
||||
Scope
|
||||
} from 'Common/Enums';
|
||||
import { koArrayWithDestroy } from 'External/ko';
|
||||
|
||||
import { SaveSettingsStep } from 'Common/Enums';
|
||||
import { ComposeType } from 'Common/EnumsUser';
|
||||
|
||||
import { registerShortcut } from 'Common/Globals';
|
||||
import { arrayLength, pInt } from 'Common/Utils';
|
||||
import { delegateRunOnDestroy, computedPaginatorHelper, showMessageComposer } from 'Common/UtilsUser';
|
||||
import { download, computedPaginatorHelper, showMessageComposer } from 'Common/UtilsUser';
|
||||
|
||||
import { Selector } from 'Common/Selector';
|
||||
import { serverRequestRaw, serverRequest } from 'Common/Links';
|
||||
|
|
@ -23,14 +19,18 @@ import { EmailModel } from 'Model/Email';
|
|||
import { ContactModel } from 'Model/Contact';
|
||||
import { ContactPropertyModel, ContactPropertyType } from 'Model/ContactProperty';
|
||||
|
||||
import { decorateKoCommands, hideScreenPopup } from 'Knoin/Knoin';
|
||||
import { decorateKoCommands, showScreenPopup } from 'Knoin/Knoin';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
const CONTACTS_PER_PAGE = 50,
|
||||
import { AskPopupView } from 'View/Popup/Ask';
|
||||
|
||||
const
|
||||
CONTACTS_PER_PAGE = 50,
|
||||
ScopeContacts = 'Contacts',
|
||||
propertyIsMail = prop => prop.isType(ContactPropertyType.Email),
|
||||
propertyIsName = prop => prop.isType(ContactPropertyType.FirstName) || prop.isType(ContactPropertyType.LastName);
|
||||
|
||||
class ContactsPopupView extends AbstractViewPopup {
|
||||
export class ContactsPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('Contacts');
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ class ContactsPopupView extends AbstractViewPopup {
|
|||
|
||||
this.contacts = ContactUserStore;
|
||||
|
||||
this.viewProperties = ko.observableArray();
|
||||
this.viewProperties = koArrayWithDestroy();
|
||||
|
||||
this.useCheckboxesInList = SettingsUserStore.useCheckboxesInList;
|
||||
|
||||
|
|
@ -136,17 +136,16 @@ class ContactsPopupView extends AbstractViewPopup {
|
|||
});
|
||||
|
||||
decorateKoCommands(this, {
|
||||
newCommand: 1,
|
||||
// close: self => !self.watchDirty(),
|
||||
deleteCommand: self => 0 < self.contactsCheckedOrSelected().length,
|
||||
newMessageCommand: self => 0 < self.contactsCheckedOrSelected().length,
|
||||
clearCommand: 1,
|
||||
saveCommand: self => !self.viewSaving() && !self.viewReadOnly()
|
||||
&& (self.contactHasValidName() || self.viewProperties.find(prop => propertyIsMail(prop) && prop.isValid())),
|
||||
syncCommand: self => !self.contacts.syncing() && !self.contacts.importing()
|
||||
});
|
||||
}
|
||||
|
||||
newCommand() {
|
||||
newContact() {
|
||||
this.populateViewContact(null);
|
||||
this.currentContact(null);
|
||||
}
|
||||
|
|
@ -183,7 +182,7 @@ class ContactsPopupView extends AbstractViewPopup {
|
|||
if (arrayLength(aE)) {
|
||||
this.bBackToCompose = false;
|
||||
|
||||
hideScreenPopup(ContactsPopupView);
|
||||
this.close();
|
||||
|
||||
switch (this.sLastComposeFocusedField) {
|
||||
case 'cc':
|
||||
|
|
@ -207,7 +206,7 @@ class ContactsPopupView extends AbstractViewPopup {
|
|||
return true;
|
||||
}
|
||||
|
||||
clearCommand() {
|
||||
clearSearch() {
|
||||
this.search('');
|
||||
}
|
||||
|
||||
|
|
@ -217,7 +216,7 @@ class ContactsPopupView extends AbstractViewPopup {
|
|||
|
||||
const requestUid = Jua.randomId();
|
||||
|
||||
Remote.contactSave(
|
||||
Remote.request('ContactSave',
|
||||
(iError, oData) => {
|
||||
let res = false;
|
||||
this.viewSaving(false);
|
||||
|
|
@ -243,15 +242,16 @@ class ContactsPopupView extends AbstractViewPopup {
|
|||
this.watchDirty(false);
|
||||
setTimeout(() => this.viewSaveTrigger(SaveSettingsStep.Idle), 1000);
|
||||
}
|
||||
},
|
||||
requestUid,
|
||||
this.viewID(),
|
||||
this.viewProperties.map(oItem => oItem.toJSON())
|
||||
}, {
|
||||
RequestUid: requestUid,
|
||||
Uid: this.viewID(),
|
||||
Properties: this.viewProperties.map(oItem => oItem.toJSON())
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
syncCommand() {
|
||||
rl.app.contactsSync(iError => {
|
||||
ContactUserStore.sync(iError => {
|
||||
iError && alert(getNotification(iError));
|
||||
|
||||
this.reloadContactList(true);
|
||||
|
|
@ -316,11 +316,11 @@ class ContactsPopupView extends AbstractViewPopup {
|
|||
}
|
||||
|
||||
exportVcf() {
|
||||
rl.app.download(serverRequestRaw('ContactsVcf'));
|
||||
download(serverRequestRaw('ContactsVcf'), 'contacts.vcf');
|
||||
}
|
||||
|
||||
exportCsv() {
|
||||
rl.app.download(serverRequestRaw('ContactsCsv'));
|
||||
download(serverRequestRaw('ContactsCsv'), 'contacts.csv');
|
||||
}
|
||||
|
||||
removeCheckedOrSelectedContactsFromList() {
|
||||
|
|
@ -345,31 +345,30 @@ class ContactsPopupView extends AbstractViewPopup {
|
|||
}
|
||||
|
||||
setTimeout(() => {
|
||||
contacts.forEach(contact => {
|
||||
ContactUserStore.remove(contact);
|
||||
delegateRunOnDestroy(contact);
|
||||
});
|
||||
contacts.forEach(contact => ContactUserStore.remove(contact));
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
deleteSelectedContacts() {
|
||||
if (this.contactsCheckedOrSelected().length) {
|
||||
Remote.contactsDelete((iError, oData) => {
|
||||
if (500 < (!iError && oData && oData.Time ? pInt(oData.Time) : 0)) {
|
||||
this.reloadContactList(this.bDropPageAfterDelete);
|
||||
} else {
|
||||
setTimeout(() => this.reloadContactList(this.bDropPageAfterDelete), 500);
|
||||
Remote.request('ContactsDelete',
|
||||
(iError, oData) => {
|
||||
if (500 < (!iError && oData && oData.Time ? pInt(oData.Time) : 0)) {
|
||||
this.reloadContactList(this.bDropPageAfterDelete);
|
||||
} else {
|
||||
setTimeout(() => this.reloadContactList(this.bDropPageAfterDelete), 500);
|
||||
}
|
||||
}, {
|
||||
Uids: this.contactsCheckedOrSelectedUids().join(',')
|
||||
}
|
||||
}, this.contactsCheckedOrSelectedUids());
|
||||
|
||||
);
|
||||
this.removeCheckedOrSelectedContactsFromList();
|
||||
}
|
||||
}
|
||||
|
||||
removeProperty(oProp) {
|
||||
this.viewProperties.remove(oProp);
|
||||
delegateRunOnDestroy(oProp);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -393,7 +392,6 @@ class ContactsPopupView extends AbstractViewPopup {
|
|||
|
||||
this.viewID(id);
|
||||
|
||||
// delegateRunOnDestroy(this.viewProperties());
|
||||
// this.viewProperties([]);
|
||||
this.viewProperties(contact.properties);
|
||||
|
||||
|
|
@ -415,7 +413,7 @@ class ContactsPopupView extends AbstractViewPopup {
|
|||
}
|
||||
|
||||
ContactUserStore.loading(true);
|
||||
Remote.contacts(
|
||||
Remote.request('Contacts',
|
||||
(iError, data) => {
|
||||
let count = 0,
|
||||
list = [];
|
||||
|
|
@ -432,27 +430,31 @@ class ContactsPopupView extends AbstractViewPopup {
|
|||
|
||||
this.contactsCount(count);
|
||||
|
||||
delegateRunOnDestroy(ContactUserStore());
|
||||
ContactUserStore(list);
|
||||
|
||||
ContactUserStore.loading(false);
|
||||
this.viewClearSearch(!!this.search());
|
||||
},
|
||||
offset,
|
||||
CONTACTS_PER_PAGE,
|
||||
this.search()
|
||||
{
|
||||
Offset: offset,
|
||||
Limit: CONTACTS_PER_PAGE,
|
||||
Search: this.search()
|
||||
},
|
||||
null,
|
||||
'',
|
||||
['Contacts']
|
||||
);
|
||||
}
|
||||
|
||||
onBuild(dom) {
|
||||
this.selector.init(dom.querySelector('.b-list-content'), Scope.Contacts);
|
||||
this.selector.init(dom.querySelector('.b-list-content'), ScopeContacts);
|
||||
|
||||
shortcuts.add('delete', '', Scope.Contacts, () => {
|
||||
registerShortcut('delete', '', ScopeContacts, () => {
|
||||
this.deleteCommand();
|
||||
return false;
|
||||
});
|
||||
|
||||
shortcuts.add('c,w', '', Scope.Contacts, () => {
|
||||
registerShortcut('c,w', '', ScopeContacts, () => {
|
||||
this.newMessageCommand();
|
||||
return false;
|
||||
});
|
||||
|
|
@ -460,7 +462,7 @@ class ContactsPopupView extends AbstractViewPopup {
|
|||
const self = this;
|
||||
|
||||
dom.addEventListener('click', event => {
|
||||
let el = event.target.closestWithin('.e-paginator .e-page', dom);
|
||||
let el = event.target.closestWithin('.e-paginator a', dom);
|
||||
if (el && ko.dataFor(el)) {
|
||||
self.contactsPage(pInt(ko.dataFor(el).value));
|
||||
self.reloadContactList();
|
||||
|
|
@ -491,23 +493,28 @@ class ContactsPopupView extends AbstractViewPopup {
|
|||
}
|
||||
}
|
||||
|
||||
onClose() {
|
||||
if (this.watchDirty() && AskPopupView.hidden()) {
|
||||
showScreenPopup(AskPopupView, [
|
||||
i18n('POPUPS_ASK/DESC_WANT_CLOSE_THIS_WINDOW'),
|
||||
() => this.close()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
onShow(bBackToCompose, sLastComposeFocusedField) {
|
||||
this.bBackToCompose = !!bBackToCompose;
|
||||
this.sLastComposeFocusedField = sLastComposeFocusedField;
|
||||
|
||||
rl.route.off();
|
||||
this.reloadContactList(true);
|
||||
}
|
||||
|
||||
onHide() {
|
||||
rl.route.on();
|
||||
|
||||
this.currentContact(null);
|
||||
this.emptySelection(true);
|
||||
this.search('');
|
||||
this.contactsCount(0);
|
||||
|
||||
delegateRunOnDestroy(ContactUserStore());
|
||||
ContactUserStore([]);
|
||||
|
||||
this.sLastComposeFocusedField = '';
|
||||
|
|
@ -519,5 +526,3 @@ class ContactsPopupView extends AbstractViewPopup {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { ContactsPopupView, ContactsPopupView as default };
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { pInt, pString } from 'Common/Utils';
|
||||
import { pInt, forEachObjectEntry } from 'Common/Utils';
|
||||
import { i18n, getNotification } from 'Common/Translator';
|
||||
|
||||
import Remote from 'Remote/Admin/Fetch';
|
||||
|
|
@ -8,7 +8,26 @@ import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
|||
|
||||
import { DomainAdminStore } from 'Stores/Admin/Domain';
|
||||
|
||||
class DomainPopupView extends AbstractViewPopup {
|
||||
const domainToParams = oDomain => ({
|
||||
Name: oDomain.name(),
|
||||
|
||||
IncHost: oDomain.imapHost(),
|
||||
IncPort: oDomain.imapPort(),
|
||||
IncSecure: oDomain.imapSecure(),
|
||||
|
||||
UseSieve: oDomain.useSieve() ? 1 : 0,
|
||||
SieveHost: oDomain.sieveHost(),
|
||||
SievePort: oDomain.sievePort(),
|
||||
SieveSecure: oDomain.sieveSecure(),
|
||||
|
||||
OutHost: oDomain.smtpHost(),
|
||||
OutPort: oDomain.smtpPort(),
|
||||
OutSecure: oDomain.smtpSecure(),
|
||||
OutAuth: oDomain.smtpAuth() ? 1 : 0,
|
||||
OutUsePhpMail: oDomain.smtpPhpMail() ? 1 : 0
|
||||
});
|
||||
|
||||
export class DomainPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('Domain');
|
||||
|
||||
|
|
@ -27,9 +46,9 @@ class DomainPopupView extends AbstractViewPopup {
|
|||
testingSieveErrorDesc: '',
|
||||
testingSmtpErrorDesc: '',
|
||||
|
||||
imapServerFocus: false,
|
||||
sieveServerFocus: false,
|
||||
smtpServerFocus: false,
|
||||
imapHostFocus: false,
|
||||
sieveHostFocus: false,
|
||||
smtpHostFocus: false,
|
||||
});
|
||||
|
||||
this.addComputables({
|
||||
|
|
@ -42,7 +61,7 @@ class DomainPopupView extends AbstractViewPopup {
|
|||
if (this.edit()) {
|
||||
result = i18n('POPUPS_DOMAIN/TITLE_EDIT_DOMAIN', { NAME: name });
|
||||
if (aliasName) {
|
||||
result += ' ← ' + aliasName;
|
||||
result += ' ⫘ ' + aliasName;
|
||||
}
|
||||
} else {
|
||||
result = name
|
||||
|
|
@ -64,10 +83,10 @@ class DomainPopupView extends AbstractViewPopup {
|
|||
|
||||
return (
|
||||
this.name() &&
|
||||
this.imapServer() &&
|
||||
this.imapHost() &&
|
||||
this.imapPort() &&
|
||||
(useSieve ? this.sieveServer() && this.sievePort() : true) &&
|
||||
((this.smtpServer() && this.smtpPort()) || usePhpMail)
|
||||
(useSieve ? this.sieveHost() && this.sievePort() : true) &&
|
||||
((this.smtpHost() && this.smtpPort()) || usePhpMail)
|
||||
);
|
||||
},
|
||||
|
||||
|
|
@ -81,28 +100,28 @@ class DomainPopupView extends AbstractViewPopup {
|
|||
testingSmtpError: value => value || this.testingSmtpErrorDesc(''),
|
||||
|
||||
// smart form improvements
|
||||
imapServerFocus: value =>
|
||||
value && this.name() && !this.imapServer() && this.imapServer(this.name().replace(/[.]?[*][.]?/g, '')),
|
||||
imapHostFocus: value =>
|
||||
value && this.name() && !this.imapHost() && this.imapHost(this.name().replace(/[.]?[*][.]?/g, '')),
|
||||
|
||||
sieveServerFocus: value =>
|
||||
value && this.imapServer() && !this.sieveServer() && this.sieveServer(this.imapServer()),
|
||||
sieveHostFocus: value =>
|
||||
value && this.imapHost() && !this.sieveHost() && this.sieveHost(this.imapHost()),
|
||||
|
||||
smtpServerFocus: value => value && this.imapServer() && !this.smtpServer()
|
||||
&& this.smtpServer(this.imapServer().replace(/imap/gi, 'smtp')),
|
||||
smtpHostFocus: value => value && this.imapHost() && !this.smtpHost()
|
||||
&& this.smtpHost(this.imapHost().replace(/imap/gi, 'smtp')),
|
||||
|
||||
imapSecure: value => {
|
||||
if (this.enableSmartPorts()) {
|
||||
const port = pInt(this.imapPort());
|
||||
switch (pString(value)) {
|
||||
case '0':
|
||||
case '2':
|
||||
switch (pInt(value)) {
|
||||
case 0:
|
||||
case 2:
|
||||
if (993 === port) {
|
||||
this.imapPort('143');
|
||||
this.imapPort(143);
|
||||
}
|
||||
break;
|
||||
case '1':
|
||||
case 1:
|
||||
if (143 === port) {
|
||||
this.imapPort('993');
|
||||
this.imapPort(993);
|
||||
}
|
||||
break;
|
||||
// no default
|
||||
|
|
@ -113,20 +132,20 @@ class DomainPopupView extends AbstractViewPopup {
|
|||
smtpSecure: value => {
|
||||
if (this.enableSmartPorts()) {
|
||||
const port = pInt(this.smtpPort());
|
||||
switch (pString(value)) {
|
||||
case '0':
|
||||
switch (pInt(value)) {
|
||||
case 0:
|
||||
if (465 === port || 587 === port) {
|
||||
this.smtpPort('25');
|
||||
this.smtpPort(25);
|
||||
}
|
||||
break;
|
||||
case '1':
|
||||
case 1:
|
||||
if (25 === port || 587 === port) {
|
||||
this.smtpPort('465');
|
||||
this.smtpPort(465);
|
||||
}
|
||||
break;
|
||||
case '2':
|
||||
case 2:
|
||||
if (25 === port || 465 === port) {
|
||||
this.smtpPort('587');
|
||||
this.smtpPort(587);
|
||||
}
|
||||
break;
|
||||
// no default
|
||||
|
|
@ -143,20 +162,26 @@ class DomainPopupView extends AbstractViewPopup {
|
|||
|
||||
createOrAddCommand() {
|
||||
this.saving(true);
|
||||
Remote.createOrUpdateDomain(
|
||||
Remote.request('AdminDomainSave',
|
||||
this.onDomainCreateOrSaveResponse.bind(this),
|
||||
this
|
||||
Object.assign(domainToParams(this), {
|
||||
Create: this.edit() ? 0 : 1,
|
||||
|
||||
IncShortLogin: this.imapShortLogin() ? 1 : 0,
|
||||
|
||||
OutShortLogin: this.smtpShortLogin() ? 1 : 0,
|
||||
OutSetSender: this.smtpSetSender() ? 1 : 0,
|
||||
|
||||
WhiteList: this.whiteList()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
testConnectionCommand() {
|
||||
this.testingDone(false);
|
||||
this.testingImapError(false);
|
||||
this.testingSieveError(false);
|
||||
this.testingSmtpError(false);
|
||||
this.clearTesting(false);
|
||||
this.testing(true);
|
||||
|
||||
Remote.testConnectionForDomain(
|
||||
Remote.request('AdminDomainTest',
|
||||
(iError, oData) => {
|
||||
this.testing(false);
|
||||
if (iError) {
|
||||
|
|
@ -185,7 +210,7 @@ class DomainPopupView extends AbstractViewPopup {
|
|||
}
|
||||
}
|
||||
},
|
||||
this
|
||||
domainToParams(this)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +220,7 @@ class DomainPopupView extends AbstractViewPopup {
|
|||
this.savingError(getNotification(iError));
|
||||
} else {
|
||||
DomainAdminStore.fetch();
|
||||
this.closeCommand();
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -209,56 +234,36 @@ class DomainPopupView extends AbstractViewPopup {
|
|||
|
||||
onShow(oDomain) {
|
||||
this.saving(false);
|
||||
|
||||
this.clearTesting();
|
||||
|
||||
this.clearForm();
|
||||
if (oDomain) {
|
||||
this.enableSmartPorts(false);
|
||||
|
||||
this.edit(true);
|
||||
|
||||
this.name(oDomain.Name);
|
||||
this.imapServer(oDomain.IncHost);
|
||||
this.imapPort('' + pInt(oDomain.IncPort));
|
||||
this.imapSecure(oDomain.IncSecure);
|
||||
this.imapShortLogin(!!oDomain.IncShortLogin);
|
||||
this.useSieve(!!oDomain.UseSieve);
|
||||
this.sieveServer(oDomain.SieveHost);
|
||||
this.sievePort('' + pInt(oDomain.SievePort));
|
||||
this.sieveSecure(oDomain.SieveSecure);
|
||||
this.smtpServer(oDomain.OutHost);
|
||||
this.smtpPort('' + pInt(oDomain.OutPort));
|
||||
this.smtpSecure(oDomain.OutSecure);
|
||||
this.smtpShortLogin(!!oDomain.OutShortLogin);
|
||||
this.smtpAuth(!!oDomain.OutAuth);
|
||||
this.smtpSetSender(!!oDomain.OutSetSender);
|
||||
this.smtpPhpMail(!!oDomain.OutUsePhpMail);
|
||||
this.whiteList(oDomain.WhiteList);
|
||||
this.aliasName(oDomain.AliasName);
|
||||
|
||||
forEachObjectEntry(oDomain, (key, value) => this[key] && this[key](value));
|
||||
this.enableSmartPorts(true);
|
||||
}
|
||||
}
|
||||
|
||||
getDefaults() {
|
||||
return {
|
||||
enableSmartPorts: false,
|
||||
|
||||
savingError: '',
|
||||
|
||||
name: '',
|
||||
|
||||
imapServer: '',
|
||||
imapPort: '143',
|
||||
imapHost: '',
|
||||
imapPort: 143,
|
||||
imapSecure: 0,
|
||||
imapShortLogin: false,
|
||||
|
||||
useSieve: false,
|
||||
sieveServer: '',
|
||||
sievePort: '4190',
|
||||
sieveHost: '',
|
||||
sievePort: 4190,
|
||||
sieveSecure: 0,
|
||||
|
||||
smtpServer: '',
|
||||
smtpPort: '25',
|
||||
smtpHost: '',
|
||||
smtpPort: 25,
|
||||
smtpSecure: 0,
|
||||
smtpShortLogin: false,
|
||||
smtpAuth: true,
|
||||
|
|
@ -266,17 +271,13 @@ class DomainPopupView extends AbstractViewPopup {
|
|||
smtpPhpMail: false,
|
||||
|
||||
whiteList: '',
|
||||
aliasName: '',
|
||||
|
||||
enableSmartPorts: false
|
||||
aliasName: ''
|
||||
};
|
||||
}
|
||||
|
||||
clearForm() {
|
||||
this.edit(false);
|
||||
Object.entries(this.getDefaults()).forEach(([key, value]) => this[key](value));
|
||||
forEachObjectEntry(this.getDefaults(), (key, value) => this[key](value));
|
||||
this.enableSmartPorts(true);
|
||||
}
|
||||
}
|
||||
|
||||
export { DomainPopupView, DomainPopupView as default };
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import Remote from 'Remote/Admin/Fetch';
|
|||
import { decorateKoCommands } from 'Knoin/Knoin';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
class DomainAliasPopupView extends AbstractViewPopup {
|
||||
export class DomainAliasPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('DomainAlias');
|
||||
|
||||
|
|
@ -35,15 +35,19 @@ class DomainAliasPopupView extends AbstractViewPopup {
|
|||
|
||||
createCommand() {
|
||||
this.saving(true);
|
||||
Remote.createDomainAlias(iError => {
|
||||
this.saving(false);
|
||||
if (iError) {
|
||||
this.savingError(getNotification(iError));
|
||||
} else {
|
||||
DomainAdminStore.fetch();
|
||||
this.closeCommand();
|
||||
}
|
||||
}, this.name(), this.alias());
|
||||
Remote.request('AdminDomainAliasSave',
|
||||
iError => {
|
||||
this.saving(false);
|
||||
if (iError) {
|
||||
this.savingError(getNotification(iError));
|
||||
} else {
|
||||
DomainAdminStore.fetch();
|
||||
this.close();
|
||||
}
|
||||
}, {
|
||||
Name: this.name(),
|
||||
Alias: this.alias()
|
||||
});
|
||||
}
|
||||
|
||||
onShow() {
|
||||
|
|
@ -59,5 +63,3 @@ class DomainAliasPopupView extends AbstractViewPopup {
|
|||
this.alias('');
|
||||
}
|
||||
}
|
||||
|
||||
export { DomainAliasPopupView, DomainAliasPopupView as default };
|
||||
|
|
|
|||
|
|
@ -1,179 +0,0 @@
|
|||
import ko from 'ko';
|
||||
|
||||
import { FilterAction } from 'Model/Filter';
|
||||
import { FilterConditionField, FilterConditionType } from 'Model/FilterCondition';
|
||||
import { defaultOptionsAfterRender } from 'Common/Utils';
|
||||
import { i18n, initOnStartOrLangChange } from 'Common/Translator';
|
||||
|
||||
import { FolderUserStore } from 'Stores/User/Folder';
|
||||
import { SieveUserStore } from 'Stores/User/Sieve';
|
||||
|
||||
import { decorateKoCommands } from 'Knoin/Knoin';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
import { folderListOptionsBuilder } from 'Common/UtilsUser';
|
||||
|
||||
class FilterPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('Filter');
|
||||
|
||||
this.addObservables({
|
||||
isNew: true,
|
||||
filter: null,
|
||||
allowMarkAsRead: false,
|
||||
selectedFolderValue: ''
|
||||
});
|
||||
|
||||
this.fTrueCallback = null;
|
||||
|
||||
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
|
||||
this.folderSelectList = ko.computed(() =>
|
||||
folderListOptionsBuilder(
|
||||
[FolderUserStore.sieveAllowFileintoInbox ? '' : 'INBOX'],
|
||||
[['', '']],
|
||||
item => item ? item.localName() : ''
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
this.selectedFolderValue.subscribe(() => this.filter() && this.filter().actionValueError(false));
|
||||
|
||||
['actionTypeOptions','fieldOptions','typeOptions','typeOptionsSize','typeOptionsBody'].forEach(
|
||||
key => this[key] = ko.observableArray()
|
||||
);
|
||||
|
||||
initOnStartOrLangChange(this.populateOptions.bind(this));
|
||||
|
||||
SieveUserStore.capa.subscribe(this.populateOptions, this);
|
||||
|
||||
decorateKoCommands(this, {
|
||||
saveFilterCommand: 1
|
||||
});
|
||||
}
|
||||
|
||||
saveFilterCommand() {
|
||||
if (this.filter()) {
|
||||
if (FilterAction.MoveTo === this.filter().actionType()) {
|
||||
this.filter().actionValue(this.selectedFolderValue());
|
||||
}
|
||||
|
||||
if (!this.filter().verify()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.fTrueCallback && this.fTrueCallback(this.filter());
|
||||
|
||||
this.modalVisibility() && this.closeCommand();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
populateOptions() {
|
||||
this.actionTypeOptions([]);
|
||||
|
||||
let i18nFilter = key => i18n('POPUPS_FILTER/SELECT_' + key);
|
||||
|
||||
this.fieldOptions([
|
||||
{ id: FilterConditionField.From, name: i18n('GLOBAL/FROM') },
|
||||
{ id: FilterConditionField.Recipient, name: i18nFilter('FIELD_RECIPIENTS') },
|
||||
{ id: FilterConditionField.Subject, name: i18n('GLOBAL/SUBJECT') },
|
||||
{ id: FilterConditionField.Size, name: i18nFilter('FIELD_SIZE') },
|
||||
{ id: FilterConditionField.Header, name: i18nFilter('FIELD_HEADER') }
|
||||
]);
|
||||
|
||||
this.typeOptions([
|
||||
{ id: FilterConditionType.Contains, name: i18nFilter('TYPE_CONTAINS') },
|
||||
{ id: FilterConditionType.NotContains, name: i18nFilter('TYPE_NOT_CONTAINS') },
|
||||
{ id: FilterConditionType.EqualTo, name: i18nFilter('TYPE_EQUAL_TO') },
|
||||
{ id: FilterConditionType.NotEqualTo, name: i18nFilter('TYPE_NOT_EQUAL_TO') }
|
||||
]);
|
||||
|
||||
// this.actionTypeOptions.push({id: FilterAction.None,
|
||||
// name: i18n('GLOBAL/NONE')});
|
||||
const modules = SieveUserStore.capa;
|
||||
if (modules) {
|
||||
if (modules.includes('imap4flags')) {
|
||||
this.allowMarkAsRead(true);
|
||||
}
|
||||
|
||||
if (modules.includes('fileinto')) {
|
||||
this.actionTypeOptions.push({
|
||||
id: FilterAction.MoveTo,
|
||||
name: i18nFilter('ACTION_MOVE_TO')
|
||||
});
|
||||
this.actionTypeOptions.push({
|
||||
id: FilterAction.Forward,
|
||||
name: i18nFilter('ACTION_FORWARD_TO')
|
||||
});
|
||||
}
|
||||
|
||||
if (modules.includes('reject')) {
|
||||
this.actionTypeOptions.push({ id: FilterAction.Reject, name: i18nFilter('ACTION_REJECT') });
|
||||
}
|
||||
|
||||
if (modules.includes('vacation')) {
|
||||
this.actionTypeOptions.push({
|
||||
id: FilterAction.Vacation,
|
||||
name: i18nFilter('ACTION_VACATION_MESSAGE')
|
||||
});
|
||||
}
|
||||
|
||||
if (modules.includes('body')) {
|
||||
this.fieldOptions.push({ id: FilterConditionField.Body, name: i18nFilter('FIELD_BODY') });
|
||||
}
|
||||
|
||||
if (modules.includes('regex')) {
|
||||
this.typeOptions.push({ id: FilterConditionType.Regex, name: 'Regex' });
|
||||
}
|
||||
}
|
||||
|
||||
this.actionTypeOptions.push({ id: FilterAction.Discard, name: i18nFilter('ACTION_DISCARD') });
|
||||
|
||||
this.typeOptionsSize([
|
||||
{ id: FilterConditionType.Over, name: i18nFilter('TYPE_OVER') },
|
||||
{ id: FilterConditionType.Under, name: i18nFilter('TYPE_UNDER') }
|
||||
]);
|
||||
|
||||
this.typeOptionsBody([
|
||||
{ id: FilterConditionType.Text, name: i18nFilter('TYPE_TEXT') },
|
||||
{ id: FilterConditionType.Raw, name: i18nFilter('TYPE_RAW') }
|
||||
]);
|
||||
}
|
||||
|
||||
removeCondition(oConditionToDelete) {
|
||||
if (this.filter()) {
|
||||
this.filter().removeCondition(oConditionToDelete);
|
||||
}
|
||||
}
|
||||
|
||||
clearPopup() {
|
||||
this.isNew(true);
|
||||
|
||||
this.fTrueCallback = null;
|
||||
this.filter(null);
|
||||
}
|
||||
|
||||
onShow(oFilter, fTrueCallback, bEdit) {
|
||||
this.clearPopup();
|
||||
|
||||
this.fTrueCallback = fTrueCallback;
|
||||
this.filter(oFilter);
|
||||
|
||||
if (oFilter) {
|
||||
this.selectedFolderValue(oFilter.actionValue());
|
||||
}
|
||||
|
||||
this.isNew(!bEdit);
|
||||
|
||||
if (!bEdit && oFilter) {
|
||||
oFilter.nameFocused(true);
|
||||
}
|
||||
}
|
||||
|
||||
onShowWithDelay() {
|
||||
this.isNew() && this.filter() && this.filter().nameFocused(true);
|
||||
}
|
||||
}
|
||||
|
||||
export { FilterPopupView, FilterPopupView as default };
|
||||
|
|
@ -2,13 +2,14 @@ import { i18n, getNotification } from 'Common/Translator';
|
|||
import { setFolderHash } from 'Common/Cache';
|
||||
|
||||
import { MessageUserStore } from 'Stores/User/Message';
|
||||
import { MessagelistUserStore } from 'Stores/User/Messagelist';
|
||||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
|
||||
import { decorateKoCommands } from 'Knoin/Knoin';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
class FolderClearPopupView extends AbstractViewPopup {
|
||||
export class FolderClearPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('FolderClear');
|
||||
|
||||
|
|
@ -19,17 +20,11 @@ class FolderClearPopupView extends AbstractViewPopup {
|
|||
});
|
||||
|
||||
this.addComputables({
|
||||
folderFullNameForClear: () => {
|
||||
dangerDescHtml: () => {
|
||||
const folder = this.selectedFolder();
|
||||
return folder ? folder.printableFullName() : '';
|
||||
},
|
||||
|
||||
folderNameForClear: () => {
|
||||
const folder = this.selectedFolder();
|
||||
return folder ? folder.localName() : '';
|
||||
},
|
||||
|
||||
dangerDescHtml: () => i18n('POPUPS_CLEAR_FOLDER/DANGER_DESC_HTML_1', { FOLDER: this.folderNameForClear() })
|
||||
// return i18n('POPUPS_CLEAR_FOLDER/DANGER_DESC_HTML_1', { FOLDER: folder ? folder.fullName.replace(folder.delimiter, ' / ') : '' });
|
||||
return i18n('POPUPS_CLEAR_FOLDER/DANGER_DESC_HTML_1', { FOLDER: folder ? folder.localName() : '' });
|
||||
}
|
||||
});
|
||||
|
||||
decorateKoCommands(this, {
|
||||
|
|
@ -44,38 +39,31 @@ class FolderClearPopupView extends AbstractViewPopup {
|
|||
const folderToClear = this.selectedFolder();
|
||||
if (folderToClear) {
|
||||
MessageUserStore.message(null);
|
||||
MessageUserStore.list([]);
|
||||
MessagelistUserStore([]);
|
||||
|
||||
this.clearingProcess(true);
|
||||
|
||||
folderToClear.messageCountAll(0);
|
||||
folderToClear.messageCountUnread(0);
|
||||
|
||||
setFolderHash(folderToClear.fullNameRaw, '');
|
||||
setFolderHash(folderToClear.fullName, '');
|
||||
|
||||
Remote.folderClear(iError => {
|
||||
Remote.request('FolderClear', iError => {
|
||||
this.clearingProcess(false);
|
||||
if (iError) {
|
||||
this.clearingError(getNotification(iError));
|
||||
} else {
|
||||
rl.app.reloadMessageList(true);
|
||||
this.cancelCommand();
|
||||
MessagelistUserStore.reload(true);
|
||||
this.close();
|
||||
}
|
||||
}, folderToClear.fullNameRaw);
|
||||
}, {
|
||||
Folder: folderToClear.fullName
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
clearPopup() {
|
||||
this.clearingProcess(false);
|
||||
this.selectedFolder(null);
|
||||
}
|
||||
|
||||
onShow(folder) {
|
||||
this.clearPopup();
|
||||
if (folder) {
|
||||
this.selectedFolder(folder);
|
||||
}
|
||||
this.clearingProcess(false);
|
||||
this.selectedFolder(folder || null);
|
||||
}
|
||||
}
|
||||
|
||||
export { FolderClearPopupView, FolderClearPopupView as default };
|
||||
|
|
|
|||
|
|
@ -1,73 +1,86 @@
|
|||
import ko from 'ko';
|
||||
import { koComputable } from 'External/ko';
|
||||
|
||||
import { Notification } from 'Common/Enums';
|
||||
import { UNUSED_OPTION_VALUE } from 'Common/Consts';
|
||||
import { defaultOptionsAfterRender } from 'Common/Utils';
|
||||
import { folderListOptionsBuilder } from 'Common/UtilsUser';
|
||||
import { folderListOptionsBuilder, sortFolders } from 'Common/Folders';
|
||||
import { getNotification } from 'Common/Translator';
|
||||
|
||||
import { FolderUserStore } from 'Stores/User/Folder';
|
||||
import { SettingsUserStore } from 'Stores/User/Settings';
|
||||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
|
||||
import { decorateKoCommands } from 'Knoin/Knoin';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
class FolderCreatePopupView extends AbstractViewPopup {
|
||||
import { setFolder, getFolderFromCacheList } from 'Common/Cache';
|
||||
import { FolderModel } from 'Model/FolderCollection';
|
||||
|
||||
export class FolderCreatePopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('FolderCreate');
|
||||
|
||||
this.addObservables({
|
||||
folderName: '',
|
||||
folderSubscribe: SettingsUserStore.hideUnsubscribed(),
|
||||
|
||||
selectedParentValue: UNUSED_OPTION_VALUE
|
||||
});
|
||||
|
||||
this.parentFolderSelectList = ko.computed(() =>
|
||||
this.parentFolderSelectList = koComputable(() =>
|
||||
folderListOptionsBuilder(
|
||||
[],
|
||||
[['', '']],
|
||||
oItem =>
|
||||
oItem ? (oItem.isSystemFolder() ? oItem.name() + ' ' + oItem.manageFolderSystemName() : oItem.name()) : '',
|
||||
FolderUserStore.namespace
|
||||
? item => FolderUserStore.namespace !== item.fullNameRaw.substr(0, FolderUserStore.namespace.length)
|
||||
? item => FolderUserStore.namespace !== item.fullName.slice(0, FolderUserStore.namespace.length)
|
||||
: null,
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
|
||||
|
||||
decorateKoCommands(this, {
|
||||
createFolderCommand: self => self.simpleFolderNameValidation(self.folderName())
|
||||
});
|
||||
}
|
||||
|
||||
createFolderCommand() {
|
||||
let parentFolderName = this.selectedParentValue();
|
||||
if (!parentFolderName && 1 < FolderUserStore.namespace.length) {
|
||||
parentFolderName = FolderUserStore.namespace.substr(0, FolderUserStore.namespace.length - 1);
|
||||
submitForm() {
|
||||
if (/^[^\\/]+$/g.test(this.folderName())) {
|
||||
let parentFolderName = this.selectedParentValue();
|
||||
if (!parentFolderName && 1 < FolderUserStore.namespace.length) {
|
||||
parentFolderName = FolderUserStore.namespace.slice(0, FolderUserStore.namespace.length - 1);
|
||||
}
|
||||
|
||||
Remote.abort('Folders').post('FolderCreate', FolderUserStore.foldersCreating, {
|
||||
Folder: this.folderName(),
|
||||
Parent: parentFolderName,
|
||||
Subscribe: this.folderSubscribe() ? 1 : 0
|
||||
})
|
||||
.then(
|
||||
data => {
|
||||
const folder = getFolderFromCacheList(parentFolderName),
|
||||
subFolder = FolderModel.reviveFromJson(data.Result),
|
||||
folders = (folder ? folder.subFolders : FolderUserStore.folderList);
|
||||
setFolder(subFolder);
|
||||
folders.push(subFolder);
|
||||
sortFolders(folders);
|
||||
/*
|
||||
var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
|
||||
console.log((folder ? folder.subFolders : FolderUserStore.folderList).sort(collator.compare));
|
||||
*/
|
||||
},
|
||||
error => {
|
||||
FolderUserStore.folderListError(
|
||||
getNotification(error.code, '', Notification.CantCreateFolder)
|
||||
+ '.\n' + error.message);
|
||||
}
|
||||
);
|
||||
|
||||
this.close();
|
||||
}
|
||||
|
||||
rl.app.foldersPromisesActionHelper(
|
||||
Remote.folderCreate(this.folderName(), parentFolderName),
|
||||
Notification.CantCreateFolder
|
||||
);
|
||||
|
||||
this.cancelCommand();
|
||||
}
|
||||
|
||||
simpleFolderNameValidation(sName) {
|
||||
return /^[^\\/]+$/g.test(sName);
|
||||
}
|
||||
|
||||
clearPopup() {
|
||||
this.folderName('');
|
||||
this.selectedParentValue('');
|
||||
}
|
||||
|
||||
onShow() {
|
||||
this.clearPopup();
|
||||
this.folderName('');
|
||||
this.selectedParentValue('');
|
||||
}
|
||||
}
|
||||
|
||||
export { FolderCreatePopupView, FolderCreatePopupView as default };
|
||||
|
|
|
|||
|
|
@ -1,41 +1,34 @@
|
|||
import ko from 'ko';
|
||||
import { koComputable, addSubscribablesTo } from 'External/ko';
|
||||
|
||||
import { SetSystemFoldersNotification } from 'Common/EnumsUser';
|
||||
import { UNUSED_OPTION_VALUE } from 'Common/Consts';
|
||||
import { defaultOptionsAfterRender, addSubscribablesTo } from 'Common/Utils';
|
||||
import { folderListOptionsBuilder } from 'Common/UtilsUser';
|
||||
import { initOnStartOrLangChange, i18n } from 'Common/Translator';
|
||||
import { defaultOptionsAfterRender } from 'Common/Utils';
|
||||
import { folderListOptionsBuilder } from 'Common/Folders';
|
||||
import { i18n } from 'Common/Translator';
|
||||
|
||||
import { FolderUserStore } from 'Stores/User/Folder';
|
||||
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
class FolderSystemPopupView extends AbstractViewPopup {
|
||||
export class FolderSystemPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('FolderSystem');
|
||||
|
||||
this.sChooseOnText = '';
|
||||
this.sUnuseText = '';
|
||||
|
||||
initOnStartOrLangChange(() => {
|
||||
this.sChooseOnText = i18n('POPUPS_SYSTEM_FOLDERS/SELECT_CHOOSE_ONE');
|
||||
this.sUnuseText = i18n('POPUPS_SYSTEM_FOLDERS/SELECT_UNUSE_NAME');
|
||||
});
|
||||
|
||||
this.notification = ko.observable('');
|
||||
|
||||
this.folderSelectList = ko.computed(() =>
|
||||
this.folderSelectList = koComputable(() =>
|
||||
folderListOptionsBuilder(
|
||||
FolderUserStore.folderListSystemNames(),
|
||||
[
|
||||
['', this.sChooseOnText],
|
||||
[UNUSED_OPTION_VALUE, this.sUnuseText]
|
||||
['', i18n('POPUPS_SYSTEM_FOLDERS/SELECT_CHOOSE_ONE')],
|
||||
[UNUSED_OPTION_VALUE, i18n('POPUPS_SYSTEM_FOLDERS/SELECT_UNUSE_NAME')]
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
this.sentFolder = FolderUserStore.sentFolder;
|
||||
this.draftFolder = FolderUserStore.draftFolder;
|
||||
this.draftsFolder = FolderUserStore.draftsFolder;
|
||||
this.spamFolder = FolderUserStore.spamFolder;
|
||||
this.trashFolder = FolderUserStore.trashFolder;
|
||||
this.archiveFolder = FolderUserStore.archiveFolder;
|
||||
|
|
@ -44,7 +37,7 @@ class FolderSystemPopupView extends AbstractViewPopup {
|
|||
|
||||
addSubscribablesTo(FolderUserStore, {
|
||||
sentFolder: fSaveSystemFolders,
|
||||
draftFolder: fSaveSystemFolders,
|
||||
draftsFolder: fSaveSystemFolders,
|
||||
spamFolder: fSaveSystemFolders,
|
||||
trashFolder: fSaveSystemFolders,
|
||||
archiveFolder: fSaveSystemFolders
|
||||
|
|
@ -80,5 +73,3 @@ class FolderSystemPopupView extends AbstractViewPopup {
|
|||
this.notification(notification);
|
||||
}
|
||||
}
|
||||
|
||||
export { FolderSystemPopupView, FolderSystemPopupView as default };
|
||||
|
|
|
|||
|
|
@ -2,12 +2,11 @@ import { getNotification } from 'Common/Translator';
|
|||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
|
||||
import { decorateKoCommands } from 'Knoin/Knoin';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
const reEmail = /^[^@\s]+@[^@\s]+$/;
|
||||
|
||||
class IdentityPopupView extends AbstractViewPopup {
|
||||
export class IdentityPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('Identity');
|
||||
|
||||
|
|
@ -60,60 +59,57 @@ class IdentityPopupView extends AbstractViewPopup {
|
|||
this.replyTo.valueHasMutated();
|
||||
this.bcc.valueHasMutated();
|
||||
*/
|
||||
decorateKoCommands(this, {
|
||||
addOrEditIdentityCommand: self => !self.submitRequest()
|
||||
});
|
||||
}
|
||||
|
||||
addOrEditIdentityCommand() {
|
||||
if (this.signature && this.signature.__fetchEditorValue) {
|
||||
this.signature.__fetchEditorValue();
|
||||
}
|
||||
|
||||
if (!this.emailHasError()) {
|
||||
this.emailHasError(!this.email().trim());
|
||||
}
|
||||
|
||||
if (this.emailHasError()) {
|
||||
if (!this.owner()) {
|
||||
this.emailFocused(true);
|
||||
submitForm() {
|
||||
if (!this.submitRequest()) {
|
||||
if (this.signature && this.signature.__fetchEditorValue) {
|
||||
this.signature.__fetchEditorValue();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!this.emailHasError()) {
|
||||
this.emailHasError(!this.email().trim());
|
||||
}
|
||||
|
||||
if (this.replyToHasError()) {
|
||||
this.replyToFocused(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.bccHasError()) {
|
||||
this.bccFocused(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.submitRequest(true);
|
||||
|
||||
Remote.identityUpdate(
|
||||
iError => {
|
||||
this.submitRequest(false);
|
||||
if (iError) {
|
||||
this.submitError(getNotification(iError));
|
||||
} else {
|
||||
rl.app.accountsAndIdentities();
|
||||
this.cancelCommand();
|
||||
if (this.emailHasError()) {
|
||||
if (!this.owner()) {
|
||||
this.emailFocused(true);
|
||||
}
|
||||
},
|
||||
this.id,
|
||||
this.email(),
|
||||
this.name(),
|
||||
this.replyTo(),
|
||||
this.bcc(),
|
||||
this.signature(),
|
||||
this.signatureInsertBefore()
|
||||
);
|
||||
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.replyToHasError()) {
|
||||
this.replyToFocused(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.bccHasError()) {
|
||||
this.bccFocused(true);
|
||||
return;
|
||||
}
|
||||
|
||||
this.submitRequest(true);
|
||||
|
||||
Remote.request('IdentityUpdate', iError => {
|
||||
this.submitRequest(false);
|
||||
if (iError) {
|
||||
this.submitError(getNotification(iError));
|
||||
} else {
|
||||
rl.app.accountsAndIdentities();
|
||||
this.close();
|
||||
}
|
||||
}, {
|
||||
Id: this.id,
|
||||
Email: this.email(),
|
||||
Name: this.name(),
|
||||
ReplyTo: this.replyTo(),
|
||||
Bcc: this.bcc(),
|
||||
Signature: this.signature(),
|
||||
SignatureInsertBefore: this.signatureInsertBefore() ? 1 : 0
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
clearPopup() {
|
||||
|
|
@ -162,13 +158,11 @@ class IdentityPopupView extends AbstractViewPopup {
|
|||
}
|
||||
}
|
||||
|
||||
onShowWithDelay() {
|
||||
afterShow() {
|
||||
this.owner() || this.emailFocused(true);
|
||||
}
|
||||
|
||||
onHideWithDelay() {
|
||||
afterHide() {
|
||||
this.clearPopup();
|
||||
}
|
||||
}
|
||||
|
||||
export { IdentityPopupView, IdentityPopupView as default };
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { Scope } from 'Common/Enums';
|
||||
|
||||
import { addShortcut } from 'Common/Globals';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
export class KeyboardShortcutsHelpPopupView extends AbstractViewPopup {
|
||||
|
|
@ -12,9 +11,9 @@ export class KeyboardShortcutsHelpPopupView extends AbstractViewPopup {
|
|||
const tabs = dom.querySelectorAll('.tabs input'),
|
||||
last = tabs.length - 1;
|
||||
|
||||
// shortcuts.add('tab', 'shift',
|
||||
shortcuts.add('tab,arrowleft,arrowright', '',
|
||||
Scope.KeyboardShortcutsHelp,
|
||||
// addShortcut('tab', 'shift',
|
||||
addShortcut('tab,arrowleft,arrowright', '',
|
||||
'KeyboardShortcutsHelp',
|
||||
event => {
|
||||
let next = 0;
|
||||
tabs.forEach((node, index) => {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import ko from 'ko';
|
||||
import { koComputable } from 'External/ko';
|
||||
|
||||
import { convertLangName } from 'Common/Translator';
|
||||
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
class LanguagesPopupView extends AbstractViewPopup {
|
||||
export class LanguagesPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('Languages');
|
||||
|
||||
|
|
@ -13,7 +14,7 @@ class LanguagesPopupView extends AbstractViewPopup {
|
|||
|
||||
this.langs = ko.observableArray();
|
||||
|
||||
this.languages = ko.computed(() => {
|
||||
this.languages = koComputable(() => {
|
||||
const userLanguage = this.userLanguage();
|
||||
return this.langs.map(language => ({
|
||||
key: language,
|
||||
|
|
@ -35,7 +36,7 @@ class LanguagesPopupView extends AbstractViewPopup {
|
|||
this.languages().forEach(item => item.selected(item.key === currentLang));
|
||||
}
|
||||
|
||||
onBeforeShow() {
|
||||
beforeShow() {
|
||||
this.fLang = null;
|
||||
this.userLanguage('');
|
||||
|
||||
|
|
@ -51,8 +52,6 @@ class LanguagesPopupView extends AbstractViewPopup {
|
|||
|
||||
changeLanguage(lang) {
|
||||
this.fLang && this.fLang(lang);
|
||||
this.cancelCommand();
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
export { LanguagesPopupView, LanguagesPopupView as default };
|
||||
|
|
|
|||
|
|
@ -1,121 +0,0 @@
|
|||
import ko from 'ko';
|
||||
|
||||
import { pString } from 'Common/Utils';
|
||||
import { Scope } from 'Common/Enums';
|
||||
|
||||
import { decorateKoCommands } from 'Knoin/Knoin';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
class MessageOpenPgpPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('MessageOpenPgp');
|
||||
|
||||
this.addObservables({
|
||||
notification: '',
|
||||
selectedKey: null,
|
||||
password: '',
|
||||
submitRequest: false
|
||||
});
|
||||
this.privateKeys = ko.observableArray();
|
||||
|
||||
this.resultCallback = null;
|
||||
|
||||
decorateKoCommands(this, {
|
||||
doCommand: self => !self.submitRequest()
|
||||
});
|
||||
}
|
||||
|
||||
doCommand() {
|
||||
this.submitRequest(true);
|
||||
|
||||
setTimeout(() => {
|
||||
let privateKey = null;
|
||||
|
||||
try {
|
||||
if (this.resultCallback && this.selectedKey()) {
|
||||
const privateKeys = this.selectedKey().getNativeKeys();
|
||||
privateKey = privateKeys && privateKeys[0] ? privateKeys[0] : null;
|
||||
|
||||
if (privateKey) {
|
||||
try {
|
||||
if (!privateKey.decrypt(pString(this.password()))) {
|
||||
console.log('Error: Private key cannot be decrypted');
|
||||
privateKey = null;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
privateKey = null;
|
||||
}
|
||||
} else {
|
||||
console.log('Error: Private key cannot be found');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
privateKey = null;
|
||||
}
|
||||
|
||||
this.submitRequest(false);
|
||||
|
||||
this.cancelCommand();
|
||||
this.resultCallback(privateKey);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
clearPopup() {
|
||||
this.notification('');
|
||||
|
||||
this.password('');
|
||||
|
||||
this.selectedKey(false);
|
||||
this.submitRequest(false);
|
||||
|
||||
this.resultCallback = null;
|
||||
this.privateKeys([]);
|
||||
}
|
||||
|
||||
onBuild(oDom) {
|
||||
// shortcuts.add('tab', 'shift', Scope.MessageOpenPgp, () => {
|
||||
shortcuts.add('tab', '', Scope.MessageOpenPgp, () => {
|
||||
let btn = this.querySelector('.inputPassword');
|
||||
if (btn.matches(':focus')) {
|
||||
btn = this.querySelector('.buttonDo');
|
||||
}
|
||||
btn.focus();
|
||||
return false;
|
||||
});
|
||||
|
||||
const self = this;
|
||||
|
||||
oDom.addEventListener('click', event => {
|
||||
const el = event.target.closestWithin('.key-list__item', oDom);
|
||||
if (el) {
|
||||
oDom.querySelectorAll('.key-list__item .key-list__item__radio').forEach(node =>
|
||||
node.textContent = el === node ? '⦿' : '○'
|
||||
);
|
||||
|
||||
self.selectedKey(ko.dataFor(el));
|
||||
|
||||
// this.querySelector('.inputPassword').focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onHideWithDelay() {
|
||||
this.clearPopup();
|
||||
}
|
||||
|
||||
onShow(fCallback, privateKeys) {
|
||||
this.clearPopup();
|
||||
|
||||
this.resultCallback = fCallback;
|
||||
this.privateKeys(privateKeys);
|
||||
|
||||
if (this.viewModelDom) {
|
||||
const el = this.viewModelDom.querySelector('.key-list__item');
|
||||
el && el.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { MessageOpenPgpPopupView, MessageOpenPgpPopupView as default };
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
import { pInt } from 'Common/Utils';
|
||||
|
||||
import { PgpUserStore } from 'Stores/User/Pgp';
|
||||
|
||||
import { decorateKoCommands } from 'Knoin/Knoin';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
class NewOpenPgpKeyPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('NewOpenPgpKey');
|
||||
|
||||
this.addObservables({
|
||||
email: '',
|
||||
emailError: false,
|
||||
|
||||
name: '',
|
||||
password: '',
|
||||
keyBitLength: 4096,
|
||||
|
||||
submitRequest: false,
|
||||
submitError: ''
|
||||
});
|
||||
|
||||
this.email.subscribe(() => this.emailError(false));
|
||||
|
||||
decorateKoCommands(this, {
|
||||
generateOpenPgpKeyCommand: 1
|
||||
});
|
||||
}
|
||||
|
||||
generateOpenPgpKeyCommand() {
|
||||
const userId = {},
|
||||
openpgpKeyring = PgpUserStore.openpgpKeyring;
|
||||
|
||||
this.emailError(!this.email().trim());
|
||||
if (!openpgpKeyring || this.emailError()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
userId.email = this.email();
|
||||
if (this.name()) {
|
||||
userId.name = this.name();
|
||||
}
|
||||
|
||||
this.submitRequest(true);
|
||||
this.submitError('');
|
||||
|
||||
setTimeout(() => {
|
||||
try {
|
||||
PgpUserStore.openpgp
|
||||
.generateKey({
|
||||
userIds: [userId],
|
||||
numBits: pInt(this.keyBitLength()),
|
||||
passphrase: this.password().trim()
|
||||
})
|
||||
.then((keyPair) => {
|
||||
this.submitRequest(false);
|
||||
|
||||
if (keyPair && keyPair.privateKeyArmored) {
|
||||
openpgpKeyring.privateKeys.importKey(keyPair.privateKeyArmored);
|
||||
openpgpKeyring.publicKeys.importKey(keyPair.publicKeyArmored);
|
||||
|
||||
openpgpKeyring.store();
|
||||
|
||||
rl.app.reloadOpenPgpKeys();
|
||||
this.cancelCommand();
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
this.submitRequest(false);
|
||||
this.showError(e);
|
||||
});
|
||||
} catch (e) {
|
||||
this.submitRequest(false);
|
||||
this.showError(e);
|
||||
}
|
||||
}, 100);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
showError(e) {
|
||||
console.log(e);
|
||||
if (e && e.message) {
|
||||
this.submitError(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
clearPopup() {
|
||||
this.name('');
|
||||
this.password('');
|
||||
|
||||
this.email('');
|
||||
this.emailError(false);
|
||||
this.keyBitLength(4096);
|
||||
|
||||
this.submitError('');
|
||||
}
|
||||
|
||||
onShow() {
|
||||
this.clearPopup();
|
||||
}
|
||||
}
|
||||
|
||||
export { NewOpenPgpKeyPopupView, NewOpenPgpKeyPopupView as default };
|
||||
109
dev/View/Popup/OpenPgpGenerate.js
Normal file
109
dev/View/Popup/OpenPgpGenerate.js
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
//import { pInt } from 'Common/Utils';
|
||||
|
||||
import { GnuPGUserStore } from 'Stores/User/GnuPG';
|
||||
import { OpenPGPUserStore } from 'Stores/User/OpenPGP';
|
||||
|
||||
import { IdentityUserStore } from 'Stores/User/Identity';
|
||||
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
import { SettingsCapa } from 'Common/Globals';
|
||||
|
||||
export class OpenPgpGeneratePopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('OpenPgpGenerate');
|
||||
|
||||
this.identities = IdentityUserStore;
|
||||
|
||||
this.addObservables({
|
||||
email: '',
|
||||
emailError: false,
|
||||
|
||||
name: '',
|
||||
password: '',
|
||||
keyType: 'ECC',
|
||||
|
||||
submitRequest: false,
|
||||
submitError: '',
|
||||
|
||||
backupPublicKey: true,
|
||||
backupPrivateKey: false,
|
||||
|
||||
saveGnuPGPublic: true,
|
||||
saveGnuPGPrivate: false
|
||||
});
|
||||
|
||||
this.canGnuPG = SettingsCapa('GnuPG');
|
||||
|
||||
this.email.subscribe(() => this.emailError(false));
|
||||
}
|
||||
|
||||
submitForm() {
|
||||
const type = this.keyType().toLowerCase(),
|
||||
userId = {
|
||||
name: this.name(),
|
||||
email: this.email()
|
||||
},
|
||||
cfg = {
|
||||
type: type,
|
||||
userIDs: [userId],
|
||||
passphrase: this.password().trim()
|
||||
// format: 'armored' // output key format, defaults to 'armored' (other options: 'binary' or 'object')
|
||||
}
|
||||
/*
|
||||
if ('ecc' === type) {
|
||||
cfg.curve = 'curve25519';
|
||||
} else {
|
||||
cfg.rsaBits = pInt(this.keyBitLength());
|
||||
}
|
||||
*/
|
||||
this.emailError(!this.email().trim());
|
||||
if (this.emailError()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.submitRequest(true);
|
||||
this.submitError('');
|
||||
|
||||
openpgp.generateKey(cfg).then(keyPair => {
|
||||
if (keyPair) {
|
||||
const fn = () => {
|
||||
this.submitRequest(false);
|
||||
this.close();
|
||||
};
|
||||
|
||||
OpenPGPUserStore.storeKeyPair(keyPair);
|
||||
|
||||
keyPair.onServer = (this.backupPublicKey() ? 1 : 0) + (this.backupPrivateKey() ? 2 : 0);
|
||||
keyPair.inGnuPG = (this.saveGnuPGPublic() ? 1 : 0) + (this.saveGnuPGPrivate() ? 2 : 0);
|
||||
if (keyPair.onServer || keyPair.inGnuPG) {
|
||||
if (!this.backupPrivateKey() && !this.saveGnuPGPrivate()) {
|
||||
delete keyPair.privateKey;
|
||||
}
|
||||
GnuPGUserStore.storeKeyPair(keyPair, fn);
|
||||
} else {
|
||||
fn();
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
this.submitRequest(false);
|
||||
this.showError(e);
|
||||
});
|
||||
}
|
||||
|
||||
showError(e) {
|
||||
console.log(e);
|
||||
if (e && e.message) {
|
||||
this.submitError(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
onShow() {
|
||||
this.name(''/*IdentityUserStore()[0].name()*/);
|
||||
this.password('');
|
||||
this.email(''/*IdentityUserStore()[0].email()*/);
|
||||
this.emailError(false);
|
||||
this.submitError('');
|
||||
}
|
||||
}
|
||||
74
dev/View/Popup/OpenPgpImport.js
Normal file
74
dev/View/Popup/OpenPgpImport.js
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import { GnuPGUserStore } from 'Stores/User/GnuPG';
|
||||
import { OpenPGPUserStore } from 'Stores/User/OpenPGP';
|
||||
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
export class OpenPgpImportPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('OpenPgpImport');
|
||||
|
||||
this.addObservables({
|
||||
key: '',
|
||||
keyError: false,
|
||||
keyErrorMessage: '',
|
||||
|
||||
saveGnuPG: true,
|
||||
saveServer: false
|
||||
});
|
||||
|
||||
this.canGnuPG = GnuPGUserStore.isSupported();
|
||||
|
||||
this.key.subscribe(() => {
|
||||
this.keyError(false);
|
||||
this.keyErrorMessage('');
|
||||
});
|
||||
}
|
||||
|
||||
submitForm() {
|
||||
let keyTrimmed = this.key().trim();
|
||||
|
||||
if (/\n/.test(keyTrimmed)) {
|
||||
keyTrimmed = keyTrimmed.replace(/\r+/g, '').replace(/\n{2,}/g, '\n\n');
|
||||
}
|
||||
|
||||
this.keyError(!keyTrimmed);
|
||||
this.keyErrorMessage('');
|
||||
|
||||
if (!keyTrimmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
let match = null,
|
||||
count = 30,
|
||||
done = false;
|
||||
// eslint-disable-next-line max-len
|
||||
const reg = /[-]{3,6}BEGIN[\s]PGP[\s](PRIVATE|PUBLIC)[\s]KEY[\s]BLOCK[-]{3,6}[\s\S]+?[-]{3,6}END[\s]PGP[\s](PRIVATE|PUBLIC)[\s]KEY[\s]BLOCK[-]{3,6}/gi;
|
||||
|
||||
do {
|
||||
match = reg.exec(keyTrimmed);
|
||||
if (match && 0 < count) {
|
||||
if (match[0] && match[1] && match[2] && match[1] === match[2]) {
|
||||
this.saveGnuPG() && GnuPGUserStore.isSupported() && GnuPGUserStore.importKey(this.key());
|
||||
OpenPGPUserStore.isSupported() && OpenPGPUserStore.importKey(this.key());
|
||||
}
|
||||
|
||||
--count;
|
||||
done = false;
|
||||
} else {
|
||||
done = true;
|
||||
}
|
||||
} while (!done);
|
||||
|
||||
if (this.keyError()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.close();
|
||||
}
|
||||
|
||||
onShow() {
|
||||
this.key('');
|
||||
this.keyError(false);
|
||||
this.keyErrorMessage('');
|
||||
}
|
||||
}
|
||||
126
dev/View/Popup/OpenPgpKey.js
Normal file
126
dev/View/Popup/OpenPgpKey.js
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
import { doc, addShortcut } from 'Common/Globals';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
export class OpenPgpKeyPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('OpenPgpKey');
|
||||
|
||||
this.addObservables({
|
||||
key: '',
|
||||
keyDom: null
|
||||
});
|
||||
}
|
||||
|
||||
selectKey() {
|
||||
const el = this.keyDom();
|
||||
if (el) {
|
||||
let sel = getSelection(),
|
||||
range = doc.createRange();
|
||||
sel.removeAllRanges();
|
||||
range.selectNodeContents(el);
|
||||
sel.addRange(range);
|
||||
}
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(this.key()).then(
|
||||
() => console.log('Copied to clipboard'),
|
||||
err => console.error(err)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onShow(openPgpKey) {
|
||||
// TODO: show more info
|
||||
this.key(openPgpKey ? openPgpKey.armor : '');
|
||||
/*
|
||||
this.key = key;
|
||||
const aEmails = [];
|
||||
if (key.users) {
|
||||
key.users.forEach(user => user.userID.email && aEmails.push(user.userID.email));
|
||||
}
|
||||
this.id = key.getKeyID().toHex();
|
||||
this.fingerprint = key.getFingerprint();
|
||||
this.can_encrypt = !!key.getEncryptionKey();
|
||||
this.can_sign = !!key.getSigningKey();
|
||||
this.emails = aEmails;
|
||||
this.armor = armor;
|
||||
this.askDelete = ko.observable(false);
|
||||
this.openForDeletion = ko.observable(null).askDeleteHelper();
|
||||
|
||||
key.id = key.subkeys[0].keyid;
|
||||
key.fingerprint = key.subkeys[0].fingerprint;
|
||||
key.uids.forEach(uid => uid.email && aEmails.push(uid.email));
|
||||
key.emails = aEmails;
|
||||
"disabled": false,
|
||||
"expired": false,
|
||||
"revoked": false,
|
||||
"is_secret": true,
|
||||
"can_sign": true,
|
||||
"can_decrypt": true
|
||||
"can_verify": true
|
||||
"can_encrypt": true,
|
||||
"uids": [
|
||||
{
|
||||
"name": "demo",
|
||||
"comment": "",
|
||||
"email": "demo@snappymail.eu",
|
||||
"uid": "demo <demo@snappymail.eu>",
|
||||
"revoked": false,
|
||||
"invalid": false
|
||||
}
|
||||
],
|
||||
"subkeys": [
|
||||
{
|
||||
"fingerprint": "2C223F20EA2ADB4CB68F81D95F3A5CDC09AD8AE3",
|
||||
"keyid": "5F3A5CDC09AD8AE3",
|
||||
"timestamp": 1643381672,
|
||||
"expires": 0,
|
||||
"is_secret": false,
|
||||
"invalid": false,
|
||||
"can_encrypt": false,
|
||||
"can_sign": true,
|
||||
"disabled": false,
|
||||
"expired": false,
|
||||
"revoked": false,
|
||||
"can_certify": true,
|
||||
"can_authenticate": false,
|
||||
"is_qualified": false,
|
||||
"is_de_vs": false,
|
||||
"pubkey_algo": 303,
|
||||
"length": 256,
|
||||
"keygrip": "5A1A6C7310D0508C68E8E74F15068301E83FD1AE",
|
||||
"is_cardkey": false,
|
||||
"curve": "ed25519"
|
||||
},
|
||||
{
|
||||
"fingerprint": "3CD720549D8833872C267D08F1230DCE2A561ADE",
|
||||
"keyid": "F1230DCE2A561ADE",
|
||||
"timestamp": 1643381672,
|
||||
"expires": 0,
|
||||
"is_secret": false,
|
||||
"invalid": false,
|
||||
"can_encrypt": true,
|
||||
"can_sign": false,
|
||||
"disabled": false,
|
||||
"expired": false,
|
||||
"revoked": false,
|
||||
"can_certify": false,
|
||||
"can_authenticate": false,
|
||||
"is_qualified": false,
|
||||
"is_de_vs": false,
|
||||
"pubkey_algo": 302,
|
||||
"length": 256,
|
||||
"keygrip": "886921A7E06BE56F8E8C51797BB476BB26DF21BF",
|
||||
"is_cardkey": false,
|
||||
"curve": "cv25519"
|
||||
}
|
||||
]
|
||||
*/
|
||||
}
|
||||
|
||||
onBuild() {
|
||||
addShortcut('a', 'meta', 'OpenPgpKey', () => {
|
||||
this.selectKey();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,15 @@
|
|||
import ko from 'ko';
|
||||
|
||||
import { Scope } from 'Common/Enums';
|
||||
import { getNotification, i18n } from 'Common/Translator';
|
||||
import { arrayLength } from 'Common/Utils';
|
||||
|
||||
import Remote from 'Remote/Admin/Fetch';
|
||||
|
||||
import { decorateKoCommands, isPopupVisible, showScreenPopup } from 'Knoin/Knoin';
|
||||
import { decorateKoCommands, showScreenPopup } from 'Knoin/Knoin';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
import { AskPopupView } from 'View/Popup/Ask';
|
||||
|
||||
class PluginPopupView extends AbstractViewPopup {
|
||||
export class PluginPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('Plugin');
|
||||
|
||||
|
|
@ -21,17 +20,14 @@ class PluginPopupView extends AbstractViewPopup {
|
|||
readme: ''
|
||||
});
|
||||
|
||||
this.configures = ko.observableArray();
|
||||
this.config = ko.observableArray();
|
||||
|
||||
this.addComputables({
|
||||
hasReadme: () => !!this.readme(),
|
||||
hasConfiguration: () => 0 < this.configures().length
|
||||
hasConfiguration: () => 0 < this.config().length
|
||||
});
|
||||
|
||||
this.bDisabeCloseOnEsc = true;
|
||||
this.sDefaultScope = Scope.All;
|
||||
|
||||
this.tryToClosePopup = this.tryToClosePopup.debounce(200);
|
||||
this.keyScope.scope = 'all';
|
||||
|
||||
decorateKoCommands(this, {
|
||||
saveCommand: self => self.hasConfiguration()
|
||||
|
|
@ -39,32 +35,40 @@ class PluginPopupView extends AbstractViewPopup {
|
|||
}
|
||||
|
||||
saveCommand() {
|
||||
const list = {
|
||||
const oConfig = {
|
||||
Id: this.id(),
|
||||
Settings: {}
|
||||
};
|
||||
|
||||
this.configures.forEach(oItem => {
|
||||
let value = oItem.value();
|
||||
},
|
||||
setItem = item => {
|
||||
let value = item.value();
|
||||
if (false === value || true === value) {
|
||||
value = value ? 1 : 0;
|
||||
}
|
||||
list.Settings[oItem.Name] = value;
|
||||
oConfig.Settings[item.Name] = value;
|
||||
};
|
||||
|
||||
this.config.forEach(oItem => {
|
||||
if (7 == oItem.Type) {
|
||||
// Group
|
||||
oItem.config.forEach(oSubItem => setItem(oSubItem));
|
||||
} else {
|
||||
setItem(oItem);
|
||||
}
|
||||
});
|
||||
|
||||
this.saveError('');
|
||||
Remote.pluginSettingsUpdate(iError =>
|
||||
iError
|
||||
Remote.request('AdminPluginSettingsUpdate',
|
||||
iError => iError
|
||||
? this.saveError(getNotification(iError))
|
||||
: this.cancelCommand()
|
||||
, list);
|
||||
: this.close(),
|
||||
oConfig);
|
||||
}
|
||||
|
||||
onShow(oPlugin) {
|
||||
this.id('');
|
||||
this.name('');
|
||||
this.readme('');
|
||||
this.configures([]);
|
||||
this.config([]);
|
||||
|
||||
if (oPlugin) {
|
||||
this.id(oPlugin.Id);
|
||||
|
|
@ -73,38 +77,30 @@ class PluginPopupView extends AbstractViewPopup {
|
|||
|
||||
const config = oPlugin.Config;
|
||||
if (arrayLength(config)) {
|
||||
this.configures(
|
||||
config.map(item => ({
|
||||
value: ko.observable(item[0]),
|
||||
placeholder: ko.observable(item[6]),
|
||||
Name: item[1],
|
||||
Type: item[2],
|
||||
Label: item[3],
|
||||
Default: item[4],
|
||||
Desc: item[5]
|
||||
}))
|
||||
this.config(
|
||||
config.map(item => {
|
||||
if (7 == item.Type) {
|
||||
// Group
|
||||
item.config.forEach(subItem => {
|
||||
subItem.value = ko.observable(subItem.value);
|
||||
});
|
||||
} else {
|
||||
item.value = ko.observable(item.value);
|
||||
}
|
||||
return item;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tryToClosePopup() {
|
||||
if (!isPopupVisible(AskPopupView)) {
|
||||
onClose() {
|
||||
if (AskPopupView.hidden()) {
|
||||
showScreenPopup(AskPopupView, [
|
||||
i18n('POPUPS_ASK/DESC_WANT_CLOSE_THIS_WINDOW'),
|
||||
() => this.modalVisibility() && this.cancelCommand()
|
||||
() => this.close()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
onBuild() {
|
||||
shortcuts.add('escape', '', Scope.All, () => {
|
||||
if (this.modalVisibility()) {
|
||||
this.tryToClosePopup();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export { PluginPopupView, PluginPopupView as default };
|
||||
|
|
|
|||
|
|
@ -1,146 +0,0 @@
|
|||
import ko from 'ko';
|
||||
|
||||
import { getNotification, i18nToNodes } from 'Common/Translator';
|
||||
import { addObservablesTo } from 'Common/Utils';
|
||||
import { delegateRunOnDestroy } from 'Common/UtilsUser';
|
||||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
import { FilterModel } from 'Model/Filter';
|
||||
import { SieveUserStore } from 'Stores/User/Sieve';
|
||||
|
||||
import { showScreenPopup/*, decorateKoCommands*/ } from 'Knoin/Knoin';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
import { FilterPopupView } from 'View/Popup/Filter';
|
||||
|
||||
class SieveScriptPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('SieveScript');
|
||||
|
||||
addObservablesTo(this, {
|
||||
saveError: false,
|
||||
saveErrorText: '',
|
||||
rawActive: false,
|
||||
allowToggle: false,
|
||||
script: null
|
||||
});
|
||||
|
||||
this.sieveCapabilities = SieveUserStore.capa.join(' ');
|
||||
this.saving = false;
|
||||
|
||||
this.filterForDeletion = ko.observable(null).deleteAccessHelper();
|
||||
/*
|
||||
decorateKoCommands(this, {
|
||||
saveScriptCommand: 1
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
saveScriptCommand() {
|
||||
let self = this,
|
||||
script = self.script();
|
||||
if (!self.saving/* && script.hasChanges()*/) {
|
||||
if (!script.verify()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!script.exists() && SieveUserStore.scripts.find(item => item.name() === script.name())) {
|
||||
script.nameError(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
self.saving = true;
|
||||
self.saveError(false);
|
||||
|
||||
if (self.allowToggle()) {
|
||||
script.body(script.filtersToRaw());
|
||||
}
|
||||
|
||||
Remote.filtersScriptSave(
|
||||
(iError, data) => {
|
||||
self.saving = false;
|
||||
|
||||
if (iError) {
|
||||
self.saveError(true);
|
||||
self.saveErrorText((data && data.ErrorMessageAdditional) || getNotification(iError));
|
||||
} else {
|
||||
script.exists() || SieveUserStore.scripts.push(script);
|
||||
script.exists(true);
|
||||
script.hasChanges(false);
|
||||
}
|
||||
},
|
||||
script
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
deleteFilter(filter) {
|
||||
this.script().filters.remove(filter);
|
||||
delegateRunOnDestroy(filter);
|
||||
}
|
||||
|
||||
addFilter() {
|
||||
/* this = SieveScriptModel */
|
||||
const filter = new FilterModel();
|
||||
filter.generateID();
|
||||
showScreenPopup(FilterPopupView, [
|
||||
filter,
|
||||
() => {
|
||||
this.filters.push(filter);
|
||||
},
|
||||
false
|
||||
]);
|
||||
}
|
||||
|
||||
editFilter(filter) {
|
||||
const clonedFilter = filter.cloneSelf();
|
||||
showScreenPopup(FilterPopupView, [
|
||||
clonedFilter,
|
||||
() => {
|
||||
const script = this.script(),
|
||||
filters = script.filters(),
|
||||
index = filters.indexOf(filter);
|
||||
if (-1 < index) {
|
||||
delegateRunOnDestroy(filters[index]);
|
||||
filters[index] = clonedFilter;
|
||||
script.filters(filters);
|
||||
}
|
||||
},
|
||||
true
|
||||
]);
|
||||
}
|
||||
|
||||
toggleFiltersRaw() {
|
||||
if (!this.rawActive()) {
|
||||
let script = this.script(),
|
||||
changed = script.hasChanges();
|
||||
script.body(script.filtersToRaw());
|
||||
script.hasChanges(changed);
|
||||
}
|
||||
this.rawActive(!this.rawActive());
|
||||
}
|
||||
|
||||
onBuild(oDom) {
|
||||
oDom.addEventListener('click', event => {
|
||||
const el = event.target.closestWithin('td.e-action', oDom),
|
||||
filter = el && ko.dataFor(el);
|
||||
filter && this.editFilter(filter);
|
||||
});
|
||||
}
|
||||
|
||||
onShow(oScript) {
|
||||
this.script(oScript);
|
||||
this.rawActive(!oScript.allowFilters());
|
||||
this.allowToggle(oScript.allowFilters());
|
||||
this.saveError(false);
|
||||
}
|
||||
|
||||
onShowWithDelay() {
|
||||
// Sometimes not everything is translated, try again
|
||||
i18nToNodes(this.viewModelDom);
|
||||
}
|
||||
}
|
||||
|
||||
export { SieveScriptPopupView, SieveScriptPopupView as default };
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
import { Scope } from 'Common/Enums';
|
||||
import { doc } from 'Common/Globals';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
class ViewOpenPgpKeyPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('ViewOpenPgpKey');
|
||||
|
||||
this.addObservables({
|
||||
key: '',
|
||||
keyDom: null
|
||||
});
|
||||
}
|
||||
|
||||
clearPopup() {
|
||||
this.key('');
|
||||
}
|
||||
|
||||
selectKey() {
|
||||
const el = this.keyDom();
|
||||
if (el) {
|
||||
let sel = getSelection(),
|
||||
range = doc.createRange();
|
||||
sel.removeAllRanges();
|
||||
range.selectNodeContents(el);
|
||||
sel.addRange(range);
|
||||
}
|
||||
}
|
||||
|
||||
onShow(openPgpKey) {
|
||||
this.clearPopup();
|
||||
|
||||
if (openPgpKey) {
|
||||
this.key(openPgpKey.armor);
|
||||
}
|
||||
}
|
||||
|
||||
onBuild() {
|
||||
shortcuts.add('a', 'meta', Scope.ViewOpenPgpKey, () => {
|
||||
this.selectKey();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { ViewOpenPgpKeyPopupView, ViewOpenPgpKeyPopupView as default };
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
import { AppUserStore } from 'Stores/User/App';
|
||||
import { AccountUserStore } from 'Stores/User/Account';
|
||||
import { MessageUserStore } from 'Stores/User/Message';
|
||||
|
||||
import { Capa, Scope } from 'Common/Enums';
|
||||
import { settings } from 'Common/Links';
|
||||
|
||||
import { showScreenPopup } from 'Knoin/Knoin';
|
||||
import { AbstractViewRight } from 'Knoin/AbstractViews';
|
||||
|
||||
import { KeyboardShortcutsHelpPopupView } from 'View/Popup/KeyboardShortcutsHelp';
|
||||
import { AccountPopupView } from 'View/Popup/Account';
|
||||
import { ContactsPopupView } from 'View/Popup/Contacts';
|
||||
|
||||
import { doc, Settings, leftPanelDisabled } from 'Common/Globals';
|
||||
|
||||
import { ThemeStore } from 'Stores/Theme';
|
||||
|
||||
export class AbstractSystemDropDownUserView extends AbstractViewRight {
|
||||
constructor(name) {
|
||||
super(name, 'SystemDropDown');
|
||||
|
||||
this.allowAccounts = Settings.capa(Capa.AdditionalAccounts);
|
||||
this.allowSettings = Settings.capa(Capa.Settings);
|
||||
this.allowHelp = Settings.capa(Capa.Help);
|
||||
|
||||
this.accountEmail = AccountUserStore.email;
|
||||
|
||||
this.accounts = AccountUserStore.accounts;
|
||||
this.accountsLoading = AccountUserStore.loading;
|
||||
this.accountsUnreadCount = AccountUserStore.accountsUnreadCount;
|
||||
|
||||
this.addObservables({
|
||||
currentAudio: '',
|
||||
accountMenuDropdownTrigger: false
|
||||
});
|
||||
|
||||
this.allowContacts = AppUserStore.allowContacts();
|
||||
|
||||
addEventListener('audio.stop', () => this.currentAudio(''));
|
||||
addEventListener('audio.start', e => this.currentAudio(e.detail));
|
||||
}
|
||||
|
||||
stopPlay() {
|
||||
dispatchEvent(new CustomEvent('audio.api.stop'));
|
||||
}
|
||||
|
||||
accountClick(account, event) {
|
||||
if (account && 0 === event.button) {
|
||||
AccountUserStore.loading(true);
|
||||
setTimeout(() => AccountUserStore.loading(false), 1000);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
emailTitle() {
|
||||
return AccountUserStore.email();
|
||||
}
|
||||
|
||||
settingsClick() {
|
||||
this.allowSettings && rl.route.setHash(settings());
|
||||
}
|
||||
|
||||
settingsHelp() {
|
||||
this.allowHelp && showScreenPopup(KeyboardShortcutsHelpPopupView);
|
||||
}
|
||||
|
||||
addAccountClick() {
|
||||
this.allowAccounts && showScreenPopup(AccountPopupView);
|
||||
}
|
||||
|
||||
contactsClick() {
|
||||
this.allowContacts && showScreenPopup(ContactsPopupView);
|
||||
}
|
||||
|
||||
toggleLayout()
|
||||
{
|
||||
const mobile = !ThemeStore.isMobile();
|
||||
doc.cookie = 'rllayout=' + (mobile ? 'mobile' : 'desktop');
|
||||
ThemeStore.isMobile(mobile);
|
||||
leftPanelDisabled(mobile);
|
||||
// location.reload();
|
||||
}
|
||||
|
||||
logoutClick() {
|
||||
rl.app.logout();
|
||||
}
|
||||
|
||||
onBuild() {
|
||||
shortcuts.add('m,contextmenu', '', [Scope.MessageList, Scope.MessageView, Scope.Settings], () => {
|
||||
if (this.viewModelVisible) {
|
||||
MessageUserStore.messageFullScreenMode(false);
|
||||
this.accountMenuDropdownTrigger(true);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// shortcuts help
|
||||
shortcuts.add('?,f1,help', '', [Scope.MessageList, Scope.MessageView, Scope.Settings], () => {
|
||||
if (this.viewModelVisible) {
|
||||
showScreenPopup(KeyboardShortcutsHelpPopupView);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
import ko from 'ko';
|
||||
|
||||
import { Notification } from 'Common/Enums';
|
||||
import { ClientSideKeyName } from 'Common/EnumsUser';
|
||||
import { Settings, SettingsGet } from 'Common/Globals';
|
||||
import { getNotification, reload as translatorReload, convertLangName } from 'Common/Translator';
|
||||
import { ClientSideKeyNameLastSignMe } from 'Common/EnumsUser';
|
||||
import { SettingsGet, fireEvent } from 'Common/Globals';
|
||||
import { getNotification, translatorReload, convertLangName } from 'Common/Translator';
|
||||
|
||||
import { LanguageStore } from 'Stores/Language';
|
||||
|
||||
|
|
@ -12,20 +10,19 @@ import * as Local from 'Storage/Client';
|
|||
import Remote from 'Remote/User/Fetch';
|
||||
|
||||
import { decorateKoCommands, showScreenPopup } from 'Knoin/Knoin';
|
||||
import { AbstractViewCenter } from 'Knoin/AbstractViews';
|
||||
import { AbstractViewLogin } from 'Knoin/AbstractViews';
|
||||
|
||||
import { LanguagesPopupView } from 'View/Popup/Languages';
|
||||
|
||||
const SignMeOff = 0,
|
||||
const
|
||||
SignMeOff = 0,
|
||||
SignMeOn = 1,
|
||||
SignMeUnused = 2;
|
||||
|
||||
|
||||
class LoginUserView extends AbstractViewCenter {
|
||||
export class LoginUserView extends AbstractViewLogin {
|
||||
constructor() {
|
||||
super('User/Login', 'Login');
|
||||
|
||||
this.hideSubmitButton = Settings.app('hideSubmitButton');
|
||||
super();
|
||||
|
||||
this.addObservables({
|
||||
loadingDesc: SettingsGet('LoadingDescription'),
|
||||
|
|
@ -46,8 +43,6 @@ class LoginUserView extends AbstractViewCenter {
|
|||
signMeType: SignMeUnused
|
||||
});
|
||||
|
||||
this.formError = ko.observable(false).extend({ falseTimeout: 500 });
|
||||
|
||||
this.allowLanguagesOnLogin = !!SettingsGet('AllowLanguagesOnLogin');
|
||||
|
||||
this.language = LanguageStore.language;
|
||||
|
|
@ -95,7 +90,7 @@ class LoginUserView extends AbstractViewCenter {
|
|||
submitCommand(self, event) {
|
||||
let form = event.target.form,
|
||||
data = new FormData(form),
|
||||
valid = form.reportValidity();
|
||||
valid = form.reportValidity() && fireEvent('sm-user-login', data);
|
||||
|
||||
this.emailError(!this.email());
|
||||
this.passwordError(!this.password());
|
||||
|
|
@ -105,8 +100,12 @@ class LoginUserView extends AbstractViewCenter {
|
|||
this.submitRequest(true);
|
||||
data.set('Language', this.bSendLanguage ? this.language() : '');
|
||||
data.set('SignMe', this.signMe() ? 1 : 0);
|
||||
Remote.login(
|
||||
Remote.request('Login',
|
||||
(iError, oData) => {
|
||||
fireEvent('sm-user-login-response', {
|
||||
error: iError,
|
||||
data: oData
|
||||
});
|
||||
if (iError) {
|
||||
this.submitRequest(false);
|
||||
if (Notification.InvalidInputArgument == iError) {
|
||||
|
|
@ -116,25 +115,22 @@ class LoginUserView extends AbstractViewCenter {
|
|||
Notification.UnknownNotification));
|
||||
this.submitErrorAddidional((oData && oData.ErrorMessageAdditional) || '');
|
||||
} else {
|
||||
rl.route.reload();
|
||||
rl.setData(oData.Result);
|
||||
}
|
||||
},
|
||||
data
|
||||
);
|
||||
|
||||
Local.set(ClientSideKeyName.LastSignMe, this.signMe() ? '-1-' : '-0-');
|
||||
Local.set(ClientSideKeyNameLastSignMe, this.signMe() ? '-1-' : '-0-');
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
onShow() {
|
||||
rl.route.off();
|
||||
}
|
||||
onBuild(dom) {
|
||||
super.onBuild(dom);
|
||||
|
||||
onBuild() {
|
||||
const signMeLocal = Local.get(ClientSideKeyName.LastSignMe),
|
||||
signMe = (SettingsGet('SignMe') || '').toLowerCase();
|
||||
const signMe = (SettingsGet('SignMe') || '').toLowerCase();
|
||||
|
||||
switch (signMe) {
|
||||
case 'defaultoff':
|
||||
|
|
@ -143,7 +139,7 @@ class LoginUserView extends AbstractViewCenter {
|
|||
'defaulton' === signMe ? SignMeOn : SignMeOff
|
||||
);
|
||||
|
||||
switch (signMeLocal) {
|
||||
switch (Local.get(ClientSideKeyNameLastSignMe)) {
|
||||
case '-1-':
|
||||
this.signMeType(SignMeOn);
|
||||
break;
|
||||
|
|
@ -160,13 +156,7 @@ class LoginUserView extends AbstractViewCenter {
|
|||
}
|
||||
}
|
||||
|
||||
submitForm() {
|
||||
// return false;
|
||||
}
|
||||
|
||||
selectLanguage() {
|
||||
showScreenPopup(LanguagesPopupView, [this.language, this.languages(), LanguageStore.userLanguage()]);
|
||||
}
|
||||
}
|
||||
|
||||
export { LoginUserView };
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
import ko from 'ko';
|
||||
|
||||
import { Capa, Scope } from 'Common/Enums';
|
||||
import { leftPanelDisabled, moveAction, Settings } from 'Common/Globals';
|
||||
import { Scope } from 'Common/Enums';
|
||||
import { moveAction, addShortcut } from 'Common/Globals';
|
||||
import { mailBox, settings } from 'Common/Links';
|
||||
import { setFolderHash } from 'Common/Cache';
|
||||
import { addComputablesTo } from 'External/ko';
|
||||
|
||||
import { AppUserStore } from 'Stores/User/App';
|
||||
import { SettingsUserStore } from 'Stores/User/Settings';
|
||||
import { FolderUserStore } from 'Stores/User/Folder';
|
||||
import { MessageUserStore } from 'Stores/User/Message';
|
||||
import { ThemeStore } from 'Stores/Theme';
|
||||
import { MessagelistUserStore } from 'Stores/User/Messagelist';
|
||||
|
||||
import { showScreenPopup } from 'Knoin/Knoin';
|
||||
import { AbstractViewLeft } from 'Knoin/AbstractViews';
|
||||
|
|
@ -18,9 +19,13 @@ import { showMessageComposer } from 'Common/UtilsUser';
|
|||
import { FolderCreatePopupView } from 'View/Popup/FolderCreate';
|
||||
import { ContactsPopupView } from 'View/Popup/Contacts';
|
||||
|
||||
export class FolderListMailBoxUserView extends AbstractViewLeft {
|
||||
import { moveMessagesToFolder } from 'Common/Folders';
|
||||
|
||||
import { setExpandedFolder } from 'Model/FolderCollection';
|
||||
|
||||
export class MailFolderList extends AbstractViewLeft {
|
||||
constructor() {
|
||||
super('User/MailBox/FolderList', 'MailFolderList');
|
||||
super();
|
||||
|
||||
this.oContentScrollable = null;
|
||||
|
||||
|
|
@ -32,14 +37,29 @@ export class FolderListMailBoxUserView extends AbstractViewLeft {
|
|||
|
||||
this.moveAction = moveAction;
|
||||
|
||||
this.foldersListWithSingleInboxRootFolder = FolderUserStore.singleRootFolder;
|
||||
this.foldersListWithSingleInboxRootFolder = ko.observable(false);
|
||||
|
||||
this.leftPanelDisabled = leftPanelDisabled;
|
||||
|
||||
this.allowComposer = Settings.capa(Capa.Composer);
|
||||
this.allowContacts = AppUserStore.allowContacts();
|
||||
|
||||
this.folderListFocused = ko.computed(() => Scope.FolderList === AppUserStore.focusedState());
|
||||
addComputablesTo(this, {
|
||||
folderListVisible: () => {
|
||||
let multiple = false,
|
||||
inbox, visible,
|
||||
result = FolderUserStore.folderList().filter(folder => {
|
||||
if (folder.isInbox()) {
|
||||
inbox = folder;
|
||||
}
|
||||
visible = folder.visible();
|
||||
multiple |= visible && !folder.isInbox();
|
||||
return visible;
|
||||
});
|
||||
if (inbox && !multiple) {
|
||||
inbox.collapsed(false);
|
||||
}
|
||||
this.foldersListWithSingleInboxRootFolder(!multiple);
|
||||
return result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onBuild(dom) {
|
||||
|
|
@ -54,7 +74,7 @@ export class FolderListMailBoxUserView extends AbstractViewLeft {
|
|||
const folder = ko.dataFor(el);
|
||||
if (folder) {
|
||||
const collapsed = folder.collapsed();
|
||||
rl.app.setExpandedFolder(folder.fullNameHash, collapsed);
|
||||
setExpandedFolder(folder.fullName, collapsed);
|
||||
|
||||
folder.collapsed(!collapsed);
|
||||
event.preventDefault();
|
||||
|
|
@ -65,16 +85,15 @@ export class FolderListMailBoxUserView extends AbstractViewLeft {
|
|||
|
||||
el = eqs(event, 'a');
|
||||
if (el && el.matches('.selectable')) {
|
||||
ThemeStore.isMobile() && leftPanelDisabled(true);
|
||||
event.preventDefault();
|
||||
const folder = ko.dataFor(el);
|
||||
if (folder) {
|
||||
if (moveAction()) {
|
||||
moveAction(false);
|
||||
rl.app.moveMessagesToFolder(
|
||||
FolderUserStore.currentFolderFullNameRaw(),
|
||||
MessageUserStore.listCheckedOrSelectedUidsWithSubMails(),
|
||||
folder.fullNameRaw,
|
||||
moveMessagesToFolder(
|
||||
FolderUserStore.currentFolderFullName(),
|
||||
MessagelistUserStore.listCheckedOrSelectedUidsWithSubMails(),
|
||||
folder.fullName,
|
||||
event.ctrlKey
|
||||
);
|
||||
} else {
|
||||
|
|
@ -82,13 +101,13 @@ export class FolderListMailBoxUserView extends AbstractViewLeft {
|
|||
MessageUserStore.message(null);
|
||||
}
|
||||
|
||||
if (folder.fullNameRaw === FolderUserStore.currentFolderFullNameRaw()) {
|
||||
setFolderHash(folder.fullNameRaw, '');
|
||||
if (folder.fullName === FolderUserStore.currentFolderFullName()) {
|
||||
setFolderHash(folder.fullName, '');
|
||||
}
|
||||
|
||||
rl.route.setHash(
|
||||
hasher.setHash(
|
||||
mailBox(folder.fullNameHash, 1,
|
||||
(event.target.matches('.flag-icon') && !folder.isFlagged()) ? 'is:flagged' : ''
|
||||
(event.target.matches('.flag-icon') && !folder.isFlagged()) ? 'flagged' : ''
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
@ -98,9 +117,9 @@ export class FolderListMailBoxUserView extends AbstractViewLeft {
|
|||
}
|
||||
});
|
||||
|
||||
shortcuts.add('arrowup,arrowdown', '', Scope.FolderList, event => {
|
||||
addShortcut('arrowup,arrowdown', '', Scope.FolderList, event => {
|
||||
let items = [], index = 0;
|
||||
dom.querySelectorAll('.b-folders li a:not(.hidden)').forEach(node => {
|
||||
dom.querySelectorAll('li a:not(.hidden)').forEach(node => {
|
||||
if (node.offsetHeight || node.getClientRects().length) {
|
||||
items.push(node);
|
||||
if (node.matches('.focused')) {
|
||||
|
|
@ -122,8 +141,8 @@ export class FolderListMailBoxUserView extends AbstractViewLeft {
|
|||
return false;
|
||||
});
|
||||
|
||||
shortcuts.add('enter,open', '', Scope.FolderList, () => {
|
||||
const item = qs('.b-folders li a:not(.hidden).focused');
|
||||
addShortcut('enter,open', '', Scope.FolderList, () => {
|
||||
const item = qs('li a:not(.hidden).focused');
|
||||
if (item) {
|
||||
AppUserStore.focusedState(Scope.MessageList);
|
||||
item.click();
|
||||
|
|
@ -132,30 +151,30 @@ export class FolderListMailBoxUserView extends AbstractViewLeft {
|
|||
return false;
|
||||
});
|
||||
|
||||
shortcuts.add('space', '', Scope.FolderList, () => {
|
||||
const item = qs('.b-folders li a:not(.hidden).focused'),
|
||||
addShortcut('space', '', Scope.FolderList, () => {
|
||||
const item = qs('li a:not(.hidden).focused'),
|
||||
folder = item && ko.dataFor(item);
|
||||
if (folder) {
|
||||
const collapsed = folder.collapsed();
|
||||
rl.app.setExpandedFolder(folder.fullNameHash, collapsed);
|
||||
setExpandedFolder(folder.fullName, collapsed);
|
||||
folder.collapsed(!collapsed);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// shortcuts.add('tab', 'shift', Scope.FolderList, () => {
|
||||
shortcuts.add('escape,tab,arrowright', '', Scope.FolderList, () => {
|
||||
// addShortcut('tab', 'shift', Scope.FolderList, () => {
|
||||
addShortcut('escape,tab,arrowright', '', Scope.FolderList, () => {
|
||||
AppUserStore.focusedState(Scope.MessageList);
|
||||
moveAction(false);
|
||||
return false;
|
||||
});
|
||||
|
||||
AppUserStore.focusedState.subscribe(value => {
|
||||
let el = qs('.b-folders li a.focused');
|
||||
let el = qs('li a.focused');
|
||||
el && el.classList.remove('focused');
|
||||
if (Scope.FolderList === value) {
|
||||
el = qs('.b-folders li a.selected');
|
||||
el = qs('li a.selected');
|
||||
el && el.classList.add('focused');
|
||||
}
|
||||
});
|
||||
|
|
@ -187,7 +206,7 @@ export class FolderListMailBoxUserView extends AbstractViewLeft {
|
|||
}
|
||||
|
||||
configureFolders() {
|
||||
rl.route.setHash(settings('folders'));
|
||||
hasher.setHash(settings('folders'));
|
||||
}
|
||||
|
||||
contactsClick() {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,23 +1,36 @@
|
|||
import ko from 'ko';
|
||||
import { koComputable } from 'External/ko';
|
||||
|
||||
import { UNUSED_OPTION_VALUE } from 'Common/Consts';
|
||||
|
||||
import {
|
||||
Capa,
|
||||
Scope
|
||||
} from 'Common/Enums';
|
||||
import { Scope } from 'Common/Enums';
|
||||
|
||||
import {
|
||||
ComposeType,
|
||||
ClientSideKeyName,
|
||||
ClientSideKeyNameLastReplyAction,
|
||||
ClientSideKeyNameMessageHeaderFullInfo,
|
||||
ClientSideKeyNameMessageAttachmentControls,
|
||||
FolderType,
|
||||
MessageSetAction
|
||||
} from 'Common/EnumsUser';
|
||||
|
||||
import { $htmlCL, leftPanelDisabled, keyScopeReal, moveAction, Settings, getFullscreenElement, exitFullscreen } from 'Common/Globals';
|
||||
import {
|
||||
elementById,
|
||||
$htmlCL,
|
||||
leftPanelDisabled,
|
||||
keyScopeReal,
|
||||
moveAction,
|
||||
Settings,
|
||||
SettingsCapa,
|
||||
getFullscreenElement,
|
||||
exitFullscreen,
|
||||
fireEvent,
|
||||
addShortcut,
|
||||
registerShortcut
|
||||
} from 'Common/Globals';
|
||||
|
||||
import { arrayLength, inFocus } from 'Common/Utils';
|
||||
import { mailToHelper, showMessageComposer } from 'Common/UtilsUser';
|
||||
import { arrayLength } from 'Common/Utils';
|
||||
import { download, mailToHelper, showMessageComposer, initFullscreen } from 'Common/UtilsUser';
|
||||
|
||||
import { SMAudio } from 'Common/Audio';
|
||||
|
||||
|
|
@ -31,120 +44,101 @@ import { SettingsUserStore } from 'Stores/User/Settings';
|
|||
import { AccountUserStore } from 'Stores/User/Account';
|
||||
import { FolderUserStore } from 'Stores/User/Folder';
|
||||
import { MessageUserStore } from 'Stores/User/Message';
|
||||
import { MessagelistUserStore } from 'Stores/User/Messagelist';
|
||||
import { ThemeStore } from 'Stores/Theme';
|
||||
|
||||
import * as Local from 'Storage/Client';
|
||||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
|
||||
import { decorateKoCommands, createCommand } from 'Knoin/Knoin';
|
||||
import { decorateKoCommands } from 'Knoin/Knoin';
|
||||
import { AbstractViewRight } from 'Knoin/AbstractViews';
|
||||
|
||||
class MessageViewMailBoxUserView extends AbstractViewRight {
|
||||
import { PgpUserStore } from 'Stores/User/Pgp';
|
||||
|
||||
import { MimeToMessage } from 'Mime/Utils';
|
||||
|
||||
const
|
||||
oMessageScrollerDom = () => elementById('messageItem') || {},
|
||||
|
||||
currentMessage = () => MessageUserStore.message();
|
||||
|
||||
export class MailMessageView extends AbstractViewRight {
|
||||
constructor() {
|
||||
super('User/MailBox/MessageView', 'MailMessageView');
|
||||
super();
|
||||
|
||||
const
|
||||
/**
|
||||
* @param {Function} fExecute
|
||||
* @param {Function} fCanExecute = true
|
||||
* @returns {Function}
|
||||
*/
|
||||
createCommand = (fExecute, fCanExecute) => {
|
||||
let fResult = () => {
|
||||
fResult.canExecute() && fExecute.call(null);
|
||||
return false;
|
||||
};
|
||||
fResult.canExecute = koComputable(() => fCanExecute());
|
||||
return fResult;
|
||||
},
|
||||
|
||||
createCommandReplyHelper = type =>
|
||||
createCommand(() => {
|
||||
this.lastReplyAction(type);
|
||||
this.replyOrforward(type);
|
||||
}, this.canBeRepliedOrForwarded),
|
||||
createCommand(() => this.replyOrforward(type), this.canBeRepliedOrForwarded),
|
||||
|
||||
createCommandActionHelper = (folderType, useFolder) =>
|
||||
createCommand(() => {
|
||||
const message = MessageUserStore.message();
|
||||
if (message && this.allowMessageListActions) {
|
||||
const message = currentMessage();
|
||||
if (message) {
|
||||
MessageUserStore.message(null);
|
||||
rl.app.deleteMessagesFromFolder(folderType, message.folder, [message.uid], useFolder);
|
||||
}
|
||||
}, this.messageVisibility);
|
||||
|
||||
this.oHeaderDom = null;
|
||||
this.oMessageScrollerDom = null;
|
||||
|
||||
this.addObservables({
|
||||
showAttachmnetControls: false,
|
||||
showAttachmentControls: false,
|
||||
downloadAsZipLoading: false,
|
||||
lastReplyAction_: '',
|
||||
showFullInfo: '1' === Local.get(ClientSideKeyName.MessageHeaderFullInfo),
|
||||
moreDropdownTrigger: false
|
||||
showFullInfo: '1' === Local.get(ClientSideKeyNameMessageHeaderFullInfo),
|
||||
moreDropdownTrigger: false,
|
||||
|
||||
// viewer
|
||||
viewFromShort: '',
|
||||
viewFromDkimData: ['none', ''],
|
||||
viewToShort: ''
|
||||
});
|
||||
|
||||
this.moveAction = moveAction;
|
||||
|
||||
this.allowComposer = Settings.capa(Capa.Composer);
|
||||
this.allowMessageActions = Settings.capa(Capa.MessageActions);
|
||||
this.allowMessageListActions = Settings.capa(Capa.MessageListActions);
|
||||
this.allowMessageActions = SettingsCapa('MessageActions');
|
||||
|
||||
const attachmentsActions = Settings.app('attachmentsActions');
|
||||
this.attachmentsActions = ko.observableArray(arrayLength(attachmentsActions) ? attachmentsActions : []);
|
||||
|
||||
this.message = MessageUserStore.message;
|
||||
this.hasCheckedMessages = MessageUserStore.hasCheckedMessages;
|
||||
this.messageLoadingThrottle = MessageUserStore.messageLoading;
|
||||
this.messagesBodiesDom = MessageUserStore.messagesBodiesDom;
|
||||
this.isMessageSelected = MessageUserStore.isMessageSelected;
|
||||
this.messageActiveDom = MessageUserStore.messageActiveDom;
|
||||
this.messageError = MessageUserStore.messageError;
|
||||
this.hasCheckedMessages = MessagelistUserStore.hasCheckedMessages;
|
||||
this.messageLoadingThrottle = MessageUserStore.loading;
|
||||
this.messagesBodiesDom = MessageUserStore.bodiesDom;
|
||||
this.messageError = MessageUserStore.error;
|
||||
|
||||
this.fullScreenMode = MessageUserStore.messageFullScreenMode;
|
||||
this.fullScreenMode = MessageUserStore.fullScreen;
|
||||
this.toggleFullScreen = MessageUserStore.toggleFullScreen;
|
||||
|
||||
this.messageListOfThreadsLoading = ko.observable(false).extend({ rateLimit: 1 });
|
||||
this.highlightUnselectedAttachments = ko.observable(false).extend({ falseTimeout: 2000 });
|
||||
|
||||
this.showAttachmnetControlsState = v => Local.set(ClientSideKeyName.MessageAttachmentControls, !!v);
|
||||
this.showAttachmentControlsState = v => Local.set(ClientSideKeyNameMessageAttachmentControls, !!v);
|
||||
|
||||
this.downloadAsZipError = ko.observable(false).extend({ falseTimeout: 7000 });
|
||||
|
||||
this.messageDomFocused = ko.observable(false).extend({ rateLimit: 0 });
|
||||
|
||||
// commands
|
||||
this.replyCommand = createCommandReplyHelper(ComposeType.Reply);
|
||||
this.replyAllCommand = createCommandReplyHelper(ComposeType.ReplyAll);
|
||||
this.forwardCommand = createCommandReplyHelper(ComposeType.Forward);
|
||||
this.forwardAsAttachmentCommand = createCommandReplyHelper(ComposeType.ForwardAsAttachment);
|
||||
this.editAsNewCommand = createCommandReplyHelper(ComposeType.EditAsNew);
|
||||
|
||||
this.deleteCommand = createCommandActionHelper(FolderType.Trash, true);
|
||||
this.deleteWithoutMoveCommand = createCommandActionHelper(FolderType.Trash, false);
|
||||
this.archiveCommand = createCommandActionHelper(FolderType.Archive, true);
|
||||
this.spamCommand = createCommandActionHelper(FolderType.Spam, true);
|
||||
this.notSpamCommand = createCommandActionHelper(FolderType.NotSpam, true);
|
||||
|
||||
// viewer
|
||||
|
||||
this.viewFolder = '';
|
||||
this.viewUid = '';
|
||||
this.viewHash = '';
|
||||
this.addObservables({
|
||||
viewBodyTopValue: 0,
|
||||
viewSubject: '',
|
||||
viewFromShort: '',
|
||||
viewFromDkimData: ['none', ''],
|
||||
viewToShort: '',
|
||||
viewFrom: '',
|
||||
viewTo: '',
|
||||
viewCc: '',
|
||||
viewBcc: '',
|
||||
viewReplyTo: '',
|
||||
viewTimeStamp: 0,
|
||||
viewSize: '',
|
||||
viewSpamScore: 0,
|
||||
viewSpamStatus: '',
|
||||
viewLineAsCss: '',
|
||||
viewViewLink: '',
|
||||
viewUnsubscribeLink: '',
|
||||
viewDownloadLink: '',
|
||||
viewIsImportant: false,
|
||||
viewIsFlagged: false,
|
||||
hasVirus: null
|
||||
});
|
||||
|
||||
this.addComputables({
|
||||
allowAttachmnetControls: () => this.attachmentsActions.length && Settings.capa(Capa.AttachmentsActions),
|
||||
allowAttachmentControls: () => this.attachmentsActions.length && SettingsCapa('AttachmentsActions'),
|
||||
|
||||
downloadAsZipAllowed: () => this.attachmentsActions.includes('zip') && this.allowAttachmnetControls(),
|
||||
downloadAsZipAllowed: () => this.attachmentsActions.includes('zip') && this.allowAttachmentControls(),
|
||||
|
||||
lastReplyAction: {
|
||||
read: this.lastReplyAction_,
|
||||
|
|
@ -155,7 +149,7 @@ class MessageViewMailBoxUserView extends AbstractViewRight {
|
|||
)
|
||||
},
|
||||
|
||||
messageVisibility: () => !MessageUserStore.messageLoading() && !!MessageUserStore.message(),
|
||||
messageVisibility: () => !MessageUserStore.loading() && !!currentMessage(),
|
||||
|
||||
canBeRepliedOrForwarded: () => !this.isDraftFolder() && this.messageVisibility(),
|
||||
|
||||
|
|
@ -181,26 +175,26 @@ class MessageViewMailBoxUserView extends AbstractViewRight {
|
|||
return '';
|
||||
},
|
||||
|
||||
messageFocused: () => Scope.MessageView === AppUserStore.focusedState(),
|
||||
pgpSupported: () => currentMessage() && PgpUserStore.isSupported(),
|
||||
|
||||
messageListAndMessageViewLoading:
|
||||
() => MessageUserStore.listCompleteLoading() || MessageUserStore.messageLoading()
|
||||
messageListOrViewLoading:
|
||||
() => MessagelistUserStore.isLoading() | MessageUserStore.loading()
|
||||
});
|
||||
|
||||
this.addSubscribables({
|
||||
showAttachmnetControls: v => MessageUserStore.message()
|
||||
&& MessageUserStore.message().attachments.forEach(item => item && item.checked(!!v)),
|
||||
showAttachmentControls: v => currentMessage()
|
||||
&& currentMessage().attachments.forEach(item => item && item.checked(!!v)),
|
||||
|
||||
lastReplyAction_: value => Local.set(ClientSideKeyName.LastReplyAction, value),
|
||||
lastReplyAction_: value => Local.set(ClientSideKeyNameLastReplyAction, value),
|
||||
|
||||
message: message => {
|
||||
this.messageActiveDom(null);
|
||||
MessageUserStore.activeDom(null);
|
||||
|
||||
if (message) {
|
||||
this.showAttachmnetControls(false);
|
||||
if (Local.get(ClientSideKeyName.MessageAttachmentControls)) {
|
||||
this.showAttachmentControls(false);
|
||||
if (Local.get(ClientSideKeyNameMessageAttachmentControls)) {
|
||||
setTimeout(() => {
|
||||
this.showAttachmnetControls(true);
|
||||
this.showAttachmentControls(true);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
|
|
@ -208,36 +202,13 @@ class MessageViewMailBoxUserView extends AbstractViewRight {
|
|||
this.scrollMessageToTop();
|
||||
}
|
||||
|
||||
let spam = message.spamResult();
|
||||
|
||||
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());
|
||||
this.viewTo(message.toToLine());
|
||||
this.viewCc(message.ccToLine());
|
||||
this.viewBcc(message.bccToLine());
|
||||
this.viewReplyTo(message.replyToToLine());
|
||||
this.viewTimeStamp(message.dateTimeStampInUTC());
|
||||
this.viewSize(message.friendlySize());
|
||||
this.viewSpamScore(message.spamScore());
|
||||
this.viewSpamStatus(spam ? i18n(message.isSpam() ? 'GLOBAL/SPAM' : 'GLOBAL/NOT_SPAM') + ': ' + spam : '');
|
||||
this.viewLineAsCss(message.lineAsCss());
|
||||
this.viewViewLink(message.viewLink());
|
||||
this.viewUnsubscribeLink(message.getFirstUnsubsribeLink());
|
||||
this.viewDownloadLink(message.downloadLink());
|
||||
this.viewIsImportant(message.isImportant());
|
||||
this.viewIsFlagged(message.isFlagged());
|
||||
this.hasVirus(message.hasVirus());
|
||||
} else {
|
||||
MessageUserStore.selectorMessageSelected(null);
|
||||
MessagelistUserStore.selectedMessage(null);
|
||||
|
||||
this.viewFolder = '';
|
||||
this.viewUid = '';
|
||||
this.viewHash = '';
|
||||
|
||||
this.scrollMessageToTop();
|
||||
|
|
@ -245,57 +216,59 @@ class MessageViewMailBoxUserView extends AbstractViewRight {
|
|||
},
|
||||
|
||||
fullScreenMode: value => {
|
||||
value && currentMessage() && AppUserStore.focusedState(Scope.MessageView);
|
||||
if (this.oContent) {
|
||||
value ? this.oContent.requestFullscreen() : exitFullscreen();
|
||||
} else {
|
||||
$htmlCL.toggle('rl-message-fullscreen', value);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
showFullInfo: value => Local.set(ClientSideKeyNameMessageHeaderFullInfo, value ? '1' : '0')
|
||||
});
|
||||
|
||||
MessageUserStore.messageViewTrigger.subscribe(() => {
|
||||
const message = MessageUserStore.message();
|
||||
this.viewIsFlagged(message ? message.isFlagged() : false);
|
||||
});
|
||||
this.lastReplyAction(Local.get(ClientSideKeyNameLastReplyAction) || ComposeType.Reply);
|
||||
|
||||
this.lastReplyAction(Local.get(ClientSideKeyName.LastReplyAction) || ComposeType.Reply);
|
||||
// commands
|
||||
this.replyCommand = createCommandReplyHelper(ComposeType.Reply);
|
||||
this.replyAllCommand = createCommandReplyHelper(ComposeType.ReplyAll);
|
||||
this.forwardCommand = createCommandReplyHelper(ComposeType.Forward);
|
||||
this.forwardAsAttachmentCommand = createCommandReplyHelper(ComposeType.ForwardAsAttachment);
|
||||
this.editAsNewCommand = createCommandReplyHelper(ComposeType.EditAsNew);
|
||||
|
||||
addEventListener('mailbox.message-view.toggle-full-screen', () => this.toggleFullScreen());
|
||||
this.deleteCommand = createCommandActionHelper(FolderType.Trash, true);
|
||||
this.deleteWithoutMoveCommand = createCommandActionHelper(FolderType.Trash, false);
|
||||
this.archiveCommand = createCommandActionHelper(FolderType.Archive, true);
|
||||
this.spamCommand = createCommandActionHelper(FolderType.Spam, true);
|
||||
this.notSpamCommand = createCommandActionHelper(FolderType.NotSpam, true);
|
||||
|
||||
decorateKoCommands(this, {
|
||||
closeMessageCommand: 1,
|
||||
messageEditCommand: self => self.messageVisibility(),
|
||||
goUpCommand: self => !self.messageListAndMessageViewLoading(),
|
||||
goDownCommand: self => !self.messageListAndMessageViewLoading()
|
||||
goUpCommand: self => !self.messageListOrViewLoading(),
|
||||
goDownCommand: self => !self.messageListOrViewLoading()
|
||||
});
|
||||
}
|
||||
|
||||
closeMessageCommand() {
|
||||
closeMessage() {
|
||||
MessageUserStore.message(null);
|
||||
}
|
||||
|
||||
messageEditCommand() {
|
||||
this.editMessage();
|
||||
if (currentMessage()) {
|
||||
showMessageComposer([ComposeType.Draft, currentMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
goUpCommand() {
|
||||
dispatchEvent(new CustomEvent('mailbox.message-list.selector.go-up',
|
||||
{detail:SettingsUserStore.usePreviewPane() || !!MessageUserStore.message()} // bForceSelect
|
||||
));
|
||||
fireEvent('mailbox.message-list.selector.go-up',
|
||||
SettingsUserStore.usePreviewPane() || !!currentMessage() // bForceSelect
|
||||
);
|
||||
}
|
||||
|
||||
goDownCommand() {
|
||||
dispatchEvent(new CustomEvent('mailbox.message-list.selector.go-down',
|
||||
{detail:SettingsUserStore.usePreviewPane() || !!MessageUserStore.message()} // bForceSelect
|
||||
));
|
||||
}
|
||||
|
||||
toggleFullScreen() {
|
||||
try {
|
||||
getSelection().removeAllRanges();
|
||||
} catch (e) {} // eslint-disable-line no-empty
|
||||
|
||||
this.fullScreenMode(!this.fullScreenMode());
|
||||
fireEvent('mailbox.message-list.selector.go-down',
|
||||
SettingsUserStore.usePreviewPane() || !!currentMessage() // bForceSelect
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -303,44 +276,13 @@ class MessageViewMailBoxUserView extends AbstractViewRight {
|
|||
* @returns {void}
|
||||
*/
|
||||
replyOrforward(sType) {
|
||||
showMessageComposer([sType, MessageUserStore.message()]);
|
||||
}
|
||||
|
||||
checkHeaderHeight() {
|
||||
this.oHeaderDom && this.viewBodyTopValue(MessageUserStore.message() ? this.oHeaderDom.offsetHeight : 0);
|
||||
this.lastReplyAction(sType);
|
||||
showMessageComposer([sType, currentMessage()]);
|
||||
}
|
||||
|
||||
onBuild(dom) {
|
||||
this.oMessageScrollerDom = dom.querySelector('.messageItem');
|
||||
|
||||
this.fullScreenMode.subscribe(value =>
|
||||
value && MessageUserStore.message() && AppUserStore.focusedState(Scope.MessageView));
|
||||
|
||||
this.showFullInfo.subscribe(value => Local.set(ClientSideKeyName.MessageHeaderFullInfo, value ? '1' : '0'));
|
||||
|
||||
let el = dom.querySelector('.messageItemHeader');
|
||||
this.oHeaderDom = el;
|
||||
if (el) {
|
||||
if (!this.resizeObserver) {
|
||||
this.resizeObserver = new ResizeObserver(this.checkHeaderHeight.debounce(50).bind(this));
|
||||
}
|
||||
this.resizeObserver.observe(el);
|
||||
} else if (this.resizeObserver) {
|
||||
this.resizeObserver.disconnect();
|
||||
}
|
||||
|
||||
let event = 'fullscreenchange';
|
||||
el = dom.querySelector('.b-message-view-wrapper');
|
||||
if (!el.requestFullscreen && el.webkitRequestFullscreen) {
|
||||
el.requestFullscreen = el.webkitRequestFullscreen;
|
||||
event = 'webkit'+event;
|
||||
}
|
||||
if (el.requestFullscreen) {
|
||||
this.oContent = el;
|
||||
el.addEventListener(event, () =>
|
||||
this.fullScreenMode(getFullscreenElement() === el)
|
||||
);
|
||||
}
|
||||
const el = dom.querySelector('.b-content');
|
||||
this.oContent = initFullscreen(el, () => MessageUserStore.fullScreen(getFullscreenElement() === el));
|
||||
|
||||
const eqs = (ev, s) => ev.target.closestWithin(s, dom);
|
||||
dom.addEventListener('click', event => {
|
||||
|
|
@ -381,50 +323,36 @@ class MessageViewMailBoxUserView extends AbstractViewRight {
|
|||
el = eqs(event, '.attachmentsPlace .attachmentItem .attachmentNameParent');
|
||||
if (el) {
|
||||
const attachment = ko.dataFor(el);
|
||||
attachment && attachment.download && rl.app.download(attachment.linkDownload());
|
||||
attachment && attachment.linkDownload() && download(attachment.linkDownload(), attachment.fileName);
|
||||
}
|
||||
|
||||
if (eqs(event, '.messageItemHeader .subjectParent .flagParent')) {
|
||||
const message = MessageUserStore.message();
|
||||
message && rl.app.messageListAction(
|
||||
const message = currentMessage();
|
||||
message && MessagelistUserStore.setAction(
|
||||
message.folder,
|
||||
message.isFlagged() ? MessageSetAction.UnsetFlag : MessageSetAction.SetFlag,
|
||||
[message]
|
||||
);
|
||||
}
|
||||
|
||||
el = eqs(event, '.thread-list .flagParent');
|
||||
if (el) {
|
||||
const message = ko.dataFor(el);
|
||||
message && message.folder && message.uid && rl.app.messageListAction(
|
||||
message.folder,
|
||||
message.isFlagged() ? MessageSetAction.UnsetFlag : MessageSetAction.SetFlag,
|
||||
[message]
|
||||
);
|
||||
|
||||
this.threadsDropdownTrigger(true);
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
AppUserStore.focusedState.subscribe((value) => {
|
||||
AppUserStore.focusedState.subscribe(value => {
|
||||
if (Scope.MessageView !== value) {
|
||||
this.scrollMessageToTop();
|
||||
this.scrollMessageToLeft();
|
||||
}
|
||||
});
|
||||
|
||||
keyScopeReal.subscribe(value => this.messageDomFocused(Scope.MessageView === value && !inFocus()));
|
||||
keyScopeReal.subscribe(value => this.messageDomFocused(Scope.MessageView === value));
|
||||
|
||||
// initShortcuts
|
||||
|
||||
// exit fullscreen, back
|
||||
shortcuts.add('escape,backspace', '', Scope.MessageView, () => {
|
||||
if (this.viewModelVisible && MessageUserStore.message()) {
|
||||
addShortcut('escape', '', Scope.MessageView, () => {
|
||||
if (!this.viewModelDom.hidden && currentMessage()) {
|
||||
const preview = SettingsUserStore.usePreviewPane();
|
||||
if (this.fullScreenMode()) {
|
||||
this.fullScreenMode(false);
|
||||
if (MessageUserStore.fullScreen()) {
|
||||
MessageUserStore.fullScreen(false);
|
||||
|
||||
if (preview) {
|
||||
AppUserStore.focusedState(Scope.MessageList);
|
||||
|
|
@ -440,14 +368,14 @@ class MessageViewMailBoxUserView extends AbstractViewRight {
|
|||
});
|
||||
|
||||
// fullscreen
|
||||
shortcuts.add('enter,open', '', Scope.MessageView, () => {
|
||||
this.toggleFullScreen();
|
||||
addShortcut('enter,open', '', Scope.MessageView, () => {
|
||||
MessageUserStore.toggleFullScreen();
|
||||
return false;
|
||||
});
|
||||
|
||||
// reply
|
||||
shortcuts.add('r,mailreply', '', [Scope.MessageList, Scope.MessageView], () => {
|
||||
if (MessageUserStore.message()) {
|
||||
registerShortcut('r,mailreply', '', [Scope.MessageList, Scope.MessageView], () => {
|
||||
if (currentMessage()) {
|
||||
this.replyCommand();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -455,80 +383,80 @@ class MessageViewMailBoxUserView extends AbstractViewRight {
|
|||
});
|
||||
|
||||
// replyAll
|
||||
shortcuts.add('a', '', [Scope.MessageList, Scope.MessageView], () => {
|
||||
if (MessageUserStore.message()) {
|
||||
registerShortcut('a', '', [Scope.MessageList, Scope.MessageView], () => {
|
||||
if (currentMessage()) {
|
||||
this.replyAllCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
shortcuts.add('mailreply', 'shift', [Scope.MessageList, Scope.MessageView], () => {
|
||||
if (MessageUserStore.message()) {
|
||||
registerShortcut('mailreply', 'shift', [Scope.MessageList, Scope.MessageView], () => {
|
||||
if (currentMessage()) {
|
||||
this.replyAllCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// forward
|
||||
shortcuts.add('f,mailforward', '', [Scope.MessageList, Scope.MessageView], () => {
|
||||
if (MessageUserStore.message()) {
|
||||
registerShortcut('f,mailforward', '', [Scope.MessageList, Scope.MessageView], () => {
|
||||
if (currentMessage()) {
|
||||
this.forwardCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// message information
|
||||
shortcuts.add('i', 'meta', [Scope.MessageList, Scope.MessageView], () => {
|
||||
if (MessageUserStore.message()) {
|
||||
registerShortcut('i', 'meta', [Scope.MessageList, Scope.MessageView], () => {
|
||||
if (currentMessage()) {
|
||||
this.showFullInfo(!this.showFullInfo());
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// toggle message blockquotes
|
||||
shortcuts.add('b', '', [Scope.MessageList, Scope.MessageView], () => {
|
||||
const message = MessageUserStore.message();
|
||||
registerShortcut('b', '', [Scope.MessageList, Scope.MessageView], () => {
|
||||
const message = currentMessage();
|
||||
if (message && message.body) {
|
||||
message.body.querySelectorAll('.rlBlockquoteSwitcher').forEach(node => node.click());
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
shortcuts.add('arrowup,arrowleft', 'meta', [Scope.MessageList, Scope.MessageView], () => {
|
||||
addShortcut('arrowup,arrowleft', 'meta', [Scope.MessageList, Scope.MessageView], () => {
|
||||
this.goUpCommand();
|
||||
return false;
|
||||
});
|
||||
|
||||
shortcuts.add('arrowdown,arrowright', 'meta', [Scope.MessageList, Scope.MessageView], () => {
|
||||
addShortcut('arrowdown,arrowright', 'meta', [Scope.MessageList, Scope.MessageView], () => {
|
||||
this.goDownCommand();
|
||||
return false;
|
||||
});
|
||||
|
||||
// print
|
||||
shortcuts.add('p,printscreen', 'meta', [Scope.MessageView, Scope.MessageList], () => {
|
||||
MessageUserStore.message() && MessageUserStore.message().printMessage();
|
||||
addShortcut('p,printscreen', 'meta', [Scope.MessageView, Scope.MessageList], () => {
|
||||
currentMessage() && currentMessage().printMessage();
|
||||
return false;
|
||||
});
|
||||
|
||||
// delete
|
||||
shortcuts.add('delete', '', Scope.MessageView, () => {
|
||||
addShortcut('delete', '', Scope.MessageView, () => {
|
||||
this.deleteCommand();
|
||||
return false;
|
||||
});
|
||||
shortcuts.add('delete', 'shift', Scope.MessageView, () => {
|
||||
addShortcut('delete', 'shift', Scope.MessageView, () => {
|
||||
this.deleteWithoutMoveCommand();
|
||||
return false;
|
||||
});
|
||||
|
||||
// change focused state
|
||||
shortcuts.add('arrowleft', '', Scope.MessageView, () => {
|
||||
if (!this.fullScreenMode() && MessageUserStore.message() && SettingsUserStore.usePreviewPane()
|
||||
&& !this.oMessageScrollerDom.scrollLeft) {
|
||||
addShortcut('arrowleft', '', Scope.MessageView, () => {
|
||||
if (!MessageUserStore.fullScreen() && currentMessage() && SettingsUserStore.usePreviewPane()
|
||||
&& !oMessageScrollerDom().scrollLeft) {
|
||||
AppUserStore.focusedState(Scope.MessageList);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
shortcuts.add('tab', 'shift', Scope.MessageView, () => {
|
||||
if (!this.fullScreenMode() && MessageUserStore.message() && SettingsUserStore.usePreviewPane()) {
|
||||
addShortcut('tab', 'shift', Scope.MessageView, () => {
|
||||
if (!MessageUserStore.fullScreen() && currentMessage() && SettingsUserStore.usePreviewPane()) {
|
||||
AppUserStore.focusedState(Scope.MessageList);
|
||||
}
|
||||
return false;
|
||||
|
|
@ -539,42 +467,42 @@ class MessageViewMailBoxUserView extends AbstractViewRight {
|
|||
* @returns {boolean}
|
||||
*/
|
||||
isDraftFolder() {
|
||||
return MessageUserStore.message() && FolderUserStore.draftFolder() === MessageUserStore.message().folder;
|
||||
return currentMessage() && FolderUserStore.draftsFolder() === currentMessage().folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isSentFolder() {
|
||||
return MessageUserStore.message() && FolderUserStore.sentFolder() === MessageUserStore.message().folder;
|
||||
return currentMessage() && FolderUserStore.sentFolder() === currentMessage().folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isSpamFolder() {
|
||||
return MessageUserStore.message() && FolderUserStore.spamFolder() === MessageUserStore.message().folder;
|
||||
return currentMessage() && FolderUserStore.spamFolder() === currentMessage().folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isSpamDisabled() {
|
||||
return MessageUserStore.message() && FolderUserStore.spamFolder() === UNUSED_OPTION_VALUE;
|
||||
return currentMessage() && FolderUserStore.spamFolder() === UNUSED_OPTION_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isArchiveFolder() {
|
||||
return MessageUserStore.message() && FolderUserStore.archiveFolder() === MessageUserStore.message().folder;
|
||||
return currentMessage() && FolderUserStore.archiveFolder() === currentMessage().folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isArchiveDisabled() {
|
||||
return MessageUserStore.message() && FolderUserStore.archiveFolder() === UNUSED_OPTION_VALUE;
|
||||
return currentMessage() && FolderUserStore.archiveFolder() === UNUSED_OPTION_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -588,34 +516,32 @@ class MessageViewMailBoxUserView extends AbstractViewRight {
|
|||
showMessageComposer();
|
||||
}
|
||||
|
||||
editMessage() {
|
||||
if (MessageUserStore.message()) {
|
||||
showMessageComposer([ComposeType.Draft, MessageUserStore.message()]);
|
||||
}
|
||||
}
|
||||
|
||||
scrollMessageToTop() {
|
||||
this.oMessageScrollerDom.scrollTop = (50 < this.oMessageScrollerDom.scrollTop) ? 50 : 0;
|
||||
oMessageScrollerDom().scrollTop = (50 < oMessageScrollerDom().scrollTop) ? 50 : 0;
|
||||
}
|
||||
|
||||
scrollMessageToLeft() {
|
||||
this.oMessageScrollerDom.scrollLeft = 0;
|
||||
oMessageScrollerDom().scrollLeft = 0;
|
||||
}
|
||||
|
||||
downloadAsZip() {
|
||||
const hashes = (MessageUserStore.message() ? MessageUserStore.message().attachments : [])
|
||||
.map(item => (item && !item.isLinked && item.checked() ? item.download : ''))
|
||||
const hashes = (currentMessage() ? currentMessage().attachments : [])
|
||||
.map(item => (item && !item.isLinked() && item.checked() ? item.download : ''))
|
||||
.filter(v => v);
|
||||
if (hashes.length) {
|
||||
Remote.attachmentsActions('Zip', hashes, this.downloadAsZipLoading)
|
||||
.then(result => {
|
||||
if (result && result.Result && result.Result.FileHash) {
|
||||
rl.app.download(attachmentDownload(result.Result.FileHash));
|
||||
} else {
|
||||
this.downloadAsZipError(true);
|
||||
}
|
||||
})
|
||||
.catch(() => this.downloadAsZipError(true));
|
||||
Remote.post('AttachmentsActions', this.downloadAsZipLoading, {
|
||||
Do: 'Zip',
|
||||
Hashes: hashes
|
||||
})
|
||||
.then(result => {
|
||||
let hash = result && result.Result && result.Result.FileHash;
|
||||
if (hash) {
|
||||
download(attachmentDownload(hash), hash+'.zip');
|
||||
} else {
|
||||
this.downloadAsZipError(true);
|
||||
}
|
||||
})
|
||||
.catch(() => this.downloadAsZipError(true));
|
||||
} else {
|
||||
this.highlightUnselectedAttachments(true);
|
||||
}
|
||||
|
|
@ -626,14 +552,14 @@ class MessageViewMailBoxUserView extends AbstractViewRight {
|
|||
* @returns {void}
|
||||
*/
|
||||
showImages() {
|
||||
MessageUserStore.message() && MessageUserStore.message().showExternalImages();
|
||||
currentMessage().showExternalImages();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
printableCheckedMessageCount() {
|
||||
const cnt = MessageUserStore.listCheckedOrSelectedUidsWithSubMails().length;
|
||||
const cnt = MessagelistUserStore.listCheckedOrSelectedUidsWithSubMails().length;
|
||||
return 0 < cnt ? (100 > cnt ? cnt : '99+') : '';
|
||||
}
|
||||
|
||||
|
|
@ -642,24 +568,69 @@ class MessageViewMailBoxUserView extends AbstractViewRight {
|
|||
* @returns {void}
|
||||
*/
|
||||
readReceipt() {
|
||||
let oMessage = MessageUserStore.message()
|
||||
if (oMessage && oMessage.readReceipt()) {
|
||||
Remote.sendReadReceiptMessage(
|
||||
()=>0,
|
||||
oMessage.folder,
|
||||
oMessage.uid,
|
||||
oMessage.readReceipt(),
|
||||
i18n('READ_RECEIPT/SUBJECT', { SUBJECT: oMessage.subject() }),
|
||||
i18n('READ_RECEIPT/BODY', { 'READ-RECEIPT': AccountUserStore.email() })
|
||||
);
|
||||
let oMessage = currentMessage()
|
||||
if (oMessage.readReceipt()) {
|
||||
Remote.request('SendReadReceiptMessage', null, {
|
||||
MessageFolder: oMessage.folder,
|
||||
MessageUid: oMessage.uid,
|
||||
ReadReceipt: oMessage.readReceipt(),
|
||||
Subject: i18n('READ_RECEIPT/SUBJECT', { SUBJECT: oMessage.subject() }),
|
||||
Text: i18n('READ_RECEIPT/BODY', { 'READ-RECEIPT': AccountUserStore.email() })
|
||||
});
|
||||
|
||||
oMessage.isReadReceipt(true);
|
||||
oMessage.flags.push('$mdnsent');
|
||||
// oMessage.flags.valueHasMutated();
|
||||
|
||||
MessageFlagsCache.store(oMessage);
|
||||
|
||||
rl.app.reloadFlagsCurrentMessageListAndMessageFromCache();
|
||||
MessagelistUserStore.reloadFlagsAndCachedMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { MessageViewMailBoxUserView };
|
||||
pgpDecrypt() {
|
||||
const oMessage = currentMessage();
|
||||
PgpUserStore.decrypt(oMessage).then(result => {
|
||||
if (result) {
|
||||
oMessage.pgpDecrypted(true);
|
||||
if (result.data) {
|
||||
MimeToMessage(result.data, oMessage);
|
||||
oMessage.html() ? oMessage.viewHtml() : oMessage.viewPlain();
|
||||
if (result.signatures && result.signatures.length) {
|
||||
oMessage.pgpSigned(true);
|
||||
oMessage.pgpVerified({
|
||||
signatures: result.signatures,
|
||||
success: !!result.signatures.length
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pgpVerify(/*self, event*/) {
|
||||
const oMessage = currentMessage()/*, ctrl = event.target.closest('.openpgp-control')*/;
|
||||
PgpUserStore.verify(oMessage).then(result => {
|
||||
if (result) {
|
||||
oMessage.pgpVerified(result);
|
||||
}
|
||||
/*
|
||||
if (result && result.success) {
|
||||
i18n('OPENPGP/GOOD_SIGNATURE', {
|
||||
USER: validKey.user + ' (' + validKey.id + ')'
|
||||
});
|
||||
message.getText()
|
||||
} else {
|
||||
const keyIds = arrayLength(signingKeyIds) ? signingKeyIds : null,
|
||||
additional = keyIds
|
||||
? keyIds.map(item => (item && item.toHex ? item.toHex() : null)).filter(v => v).join(', ')
|
||||
: '';
|
||||
|
||||
i18n('OPENPGP/ERROR', {
|
||||
ERROR: 'message'
|
||||
}) + (additional ? ' (' + additional + ')' : '');
|
||||
}
|
||||
*/
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
import { AbstractSystemDropDownUserView } from 'View/User/AbstractSystemDropDown';
|
||||
|
||||
class SystemDropDownMailBoxUserView extends AbstractSystemDropDownUserView
|
||||
{
|
||||
constructor() {
|
||||
super('User/MailBox/SystemDropDown');
|
||||
}
|
||||
}
|
||||
|
||||
export { SystemDropDownMailBoxUserView };
|
||||
|
|
@ -1,36 +1,23 @@
|
|||
import { leftPanelDisabled } from 'Common/Globals';
|
||||
import { settings, mailbox } from 'Common/Links';
|
||||
import { getFolderInboxName } from 'Common/Cache';
|
||||
|
||||
import { AbstractViewLeft } from 'Knoin/AbstractViews';
|
||||
|
||||
import { ThemeStore } from 'Stores/Theme';
|
||||
|
||||
export class MenuSettingsUserView extends AbstractViewLeft {
|
||||
export class SettingsMenuUserView extends AbstractViewLeft {
|
||||
/**
|
||||
* @param {Object} screen
|
||||
*/
|
||||
constructor(screen) {
|
||||
super('User/Settings/Menu', 'SettingsMenu');
|
||||
|
||||
this.leftPanelDisabled = leftPanelDisabled;
|
||||
super();
|
||||
|
||||
this.menu = screen.menu;
|
||||
}
|
||||
|
||||
onBuild(dom) {
|
||||
dom.addEventListener('click', event =>
|
||||
ThemeStore.isMobile()
|
||||
&& event.target.closestWithin('.b-settins-left nav a', dom)
|
||||
&& leftPanelDisabled(true)
|
||||
);
|
||||
}
|
||||
|
||||
link(route) {
|
||||
return settings(route);
|
||||
}
|
||||
|
||||
backToMailBoxClick() {
|
||||
rl.route.setHash(mailbox(getFolderInboxName()));
|
||||
hasher.setHash(mailbox(getFolderInboxName()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@ import { ThemeStore } from 'Stores/Theme';
|
|||
|
||||
import { AbstractViewRight } from 'Knoin/AbstractViews';
|
||||
|
||||
export class PaneSettingsUserView extends AbstractViewRight {
|
||||
export class SettingsPaneUserView extends AbstractViewRight {
|
||||
constructor() {
|
||||
super('User/Settings/Pane', 'SettingsPane');
|
||||
super();
|
||||
|
||||
this.isMobile = ThemeStore.isMobile;
|
||||
this.leftPanelDisabled = leftPanelDisabled;
|
||||
}
|
||||
|
||||
|
|
@ -18,25 +19,13 @@ export class PaneSettingsUserView extends AbstractViewRight {
|
|||
MessageUserStore.message(null);
|
||||
}
|
||||
|
||||
hideLeft(item, event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
leftPanelDisabled(true);
|
||||
}
|
||||
|
||||
showLeft(item, event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
leftPanelDisabled(false);
|
||||
}
|
||||
|
||||
onBuild(dom) {
|
||||
dom.addEventListener('click', () => ThemeStore.isMobile() && leftPanelDisabled(true));
|
||||
dom.addEventListener('click', () =>
|
||||
ThemeStore.isMobile() && !event.target.closestWithin('.toggleLeft', dom) && leftPanelDisabled(true)
|
||||
);
|
||||
}
|
||||
|
||||
backToMailBoxClick() {
|
||||
rl.route.setHash(mailbox(getFolderInboxName()));
|
||||
hasher.setHash(mailbox(getFolderInboxName()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
import { AbstractSystemDropDownUserView } from 'View/User/AbstractSystemDropDown';
|
||||
|
||||
class SystemDropDownSettingsUserView extends AbstractSystemDropDownUserView
|
||||
{
|
||||
constructor() {
|
||||
super('User/Settings/SystemDropDown');
|
||||
}
|
||||
}
|
||||
|
||||
export { SystemDropDownSettingsUserView };
|
||||
143
dev/View/User/SystemDropDown.js
Normal file
143
dev/View/User/SystemDropDown.js
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
import { AppUserStore } from 'Stores/User/App';
|
||||
import { AccountUserStore } from 'Stores/User/Account';
|
||||
import { MessageUserStore } from 'Stores/User/Message';
|
||||
//import { FolderUserStore } from 'Stores/User/Folder';
|
||||
|
||||
import { Scope } from 'Common/Enums';
|
||||
import { settings } from 'Common/Links';
|
||||
|
||||
import { showScreenPopup } from 'Knoin/Knoin';
|
||||
import { AbstractViewRight } from 'Knoin/AbstractViews';
|
||||
|
||||
import { KeyboardShortcutsHelpPopupView } from 'View/Popup/KeyboardShortcutsHelp';
|
||||
import { AccountPopupView } from 'View/Popup/Account';
|
||||
import { ContactsPopupView } from 'View/Popup/Contacts';
|
||||
|
||||
import { doc, leftPanelDisabled, fireEvent, SettingsCapa, registerShortcut } from 'Common/Globals';
|
||||
|
||||
import { ThemeStore } from 'Stores/Theme';
|
||||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
import { getNotification } from 'Common/Translator';
|
||||
//import { clearCache } from 'Common/Cache';
|
||||
//import { koComputable } from 'External/ko';
|
||||
|
||||
export class SystemDropDownUserView extends AbstractViewRight {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.allowAccounts = SettingsCapa('AdditionalAccounts');
|
||||
|
||||
this.accountEmail = AccountUserStore.email;
|
||||
|
||||
this.accounts = AccountUserStore.accounts;
|
||||
this.accountsLoading = AccountUserStore.loading;
|
||||
/*
|
||||
this.accountsUnreadCount = : koComputable(() => 0);
|
||||
this.accountsUnreadCount = : koComputable(() => AccountUserStore.accounts().reduce((result, item) => result + item.count(), 0));
|
||||
*/
|
||||
|
||||
this.addObservables({
|
||||
currentAudio: '',
|
||||
accountMenuDropdownTrigger: false
|
||||
});
|
||||
|
||||
this.allowContacts = AppUserStore.allowContacts();
|
||||
|
||||
addEventListener('audio.stop', () => this.currentAudio(''));
|
||||
addEventListener('audio.start', e => this.currentAudio(e.detail));
|
||||
}
|
||||
|
||||
stopPlay() {
|
||||
fireEvent('audio.api.stop');
|
||||
}
|
||||
|
||||
accountClick(account, event) {
|
||||
let email = account && account.email;
|
||||
if (email && 0 === event.button && AccountUserStore.email() != email) {
|
||||
AccountUserStore.loading(true);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
Remote.request('AccountSwitch',
|
||||
(iError/*, oData*/) => {
|
||||
if (iError) {
|
||||
AccountUserStore.loading(false);
|
||||
alert(getNotification(iError).replace('%EMAIL%', email));
|
||||
if (account.isAdditional()) {
|
||||
showScreenPopup(AccountPopupView, [account]);
|
||||
}
|
||||
} else {
|
||||
/* // Not working yet
|
||||
forEachObjectEntry(oData.Result, (key, value) => rl.settings.set(key, value));
|
||||
clearCache();
|
||||
// MessageUserStore.setMessage();
|
||||
// MessageUserStore.purgeMessageBodyCache();
|
||||
// MessageUserStore.hideMessageBodies();
|
||||
MessagelistUserStore([]);
|
||||
// FolderUserStore.folderList([]);
|
||||
loadFolders(value => {
|
||||
if (value) {
|
||||
// 4. Change to INBOX = reload MessageList
|
||||
// MessagelistUserStore.setMessageList();
|
||||
}
|
||||
});
|
||||
AccountUserStore.loading(false);
|
||||
*/
|
||||
rl.route.reload();
|
||||
}
|
||||
}, {Email:email}
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
emailTitle() {
|
||||
return AccountUserStore.email();
|
||||
}
|
||||
|
||||
settingsClick() {
|
||||
hasher.setHash(settings());
|
||||
}
|
||||
|
||||
settingsHelp() {
|
||||
showScreenPopup(KeyboardShortcutsHelpPopupView);
|
||||
}
|
||||
|
||||
addAccountClick() {
|
||||
this.allowAccounts && showScreenPopup(AccountPopupView);
|
||||
}
|
||||
|
||||
contactsClick() {
|
||||
this.allowContacts && showScreenPopup(ContactsPopupView);
|
||||
}
|
||||
|
||||
toggleLayout()
|
||||
{
|
||||
const mobile = !ThemeStore.isMobile();
|
||||
doc.cookie = 'rllayout=' + (mobile ? 'mobile' : 'desktop') + '; samesite=strict';
|
||||
ThemeStore.isMobile(mobile);
|
||||
leftPanelDisabled(mobile);
|
||||
}
|
||||
|
||||
logoutClick() {
|
||||
rl.app.logout();
|
||||
}
|
||||
|
||||
onBuild() {
|
||||
registerShortcut('m', '', [Scope.MessageList, Scope.MessageView, Scope.Settings], () => {
|
||||
if (!this.viewModelDom.hidden) {
|
||||
MessageUserStore.fullScreen(false);
|
||||
this.accountMenuDropdownTrigger(true);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// shortcuts help
|
||||
registerShortcut('?,f1,help', '', [Scope.MessageList, Scope.MessageView, Scope.Settings], () => {
|
||||
if (!this.viewModelDom.hidden) {
|
||||
showScreenPopup(KeyboardShortcutsHelpPopupView);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue