This version uses Rollup instead of WebPack.

Due to that the code is smaller and has changes to prevent Circular Dependencies
This commit is contained in:
djmaze 2021-01-25 22:00:13 +01:00
parent 5e63ade9dd
commit ad8fd8879b
49 changed files with 595 additions and 577 deletions

View file

@ -13,13 +13,22 @@ import { initOnStartOrLangChange } from 'Common/Translator';
import LanguageStore from 'Stores/Language';
import ThemeStore from 'Stores/Theme';
import SaveTriggerComponent from 'Component/SaveTrigger';
import InputComponent from 'Component/Input';
import SelectComponent from 'Component/Select';
import TextAreaComponent from 'Component/TextArea';
import CheckboxMaterialDesignComponent from 'Component/MaterialDesign/Checkbox';
import CheckboxComponent from 'Component/Checkbox';
const Settings = rl.settings, doc = document;
export class AbstractApp {
/**
* @param {RemoteStorage|AdminRemoteStorage} Remote
*/
constructor() {
constructor(Remote) {
this.Remote = Remote;
const refresh = (()=>dispatchEvent(new CustomEvent('rl.auto-logout-refresh'))).debounce(5000),
fn = (ev=>{
$htmlCL.toggle('rl-ctrl-key-pressed', ev.ctrlKey);
@ -81,17 +90,15 @@ export class AbstractApp {
bootstart() {
const mobile = Settings.app('mobile');
ko.components.register('SaveTrigger', require('Component/SaveTrigger').default);
ko.components.register('Input', require('Component/Input').default);
ko.components.register('Select', require('Component/Select').default);
ko.components.register('TextArea', require('Component/TextArea').default);
ko.components.register('SaveTrigger', SaveTriggerComponent);
ko.components.register('Input', InputComponent);
ko.components.register('Select', SelectComponent);
ko.components.register('TextArea', TextAreaComponent);
ko.components.register('CheckboxSimple', CheckboxComponent);
if (Settings.app('materialDesign') && !rl.settings.app('mobile')) {
ko.components.register('Checkbox', require('Component/MaterialDesign/Checkbox').default);
ko.components.register('CheckboxSimple', require('Component/Checkbox').default);
ko.components.register('Checkbox', CheckboxMaterialDesignComponent);
} else {
ko.components.register('Checkbox', require('Component/Checkbox').default);
ko.components.register('CheckboxSimple', require('Component/Checkbox').default);
ko.components.register('Checkbox', CheckboxComponent);
}
initOnStartOrLangChange();

View file

@ -1,3 +1,5 @@
import ko from 'External/User/ko'; // eslint-disable-line no-unused-vars
import { pInt, pString } from 'Common/Utils';
import { isPosNumeric, delegateRunOnDestroy, mailToHelper } from 'Common/UtilsUser';
@ -67,7 +69,10 @@ import { startScreens, showScreenPopup } from 'Knoin/Knoin';
import { AbstractApp } from 'App/Abstract';
require('External/User/ko');
import { ComposePopupView } from 'View/Popup/Compose';
import { FolderSystemPopupView } from 'View/Popup/FolderSystem';
import { AskPopupView } from 'View/Popup/Ask';
import { TwoFactorConfigurationPopupView } from 'View/Popup/TwoFactorConfiguration';
const doc = document,
Settings = rl.settings;
@ -329,13 +334,13 @@ class AppUser extends AbstractApp {
}
if (!oMoveFolder && bUseFolder) {
showScreenPopup(require('View/Popup/FolderSystem'), [nSetSystemFoldersNotification]);
showScreenPopup(FolderSystemPopupView, [nSetSystemFoldersNotification]);
} else if (
!bUseFolder ||
(FolderType.Trash === iDeleteType &&
(sFromFolderFullNameRaw === FolderStore.spamFolder() || sFromFolderFullNameRaw === FolderStore.trashFolder()))
) {
showScreenPopup(require('View/Popup/Ask'), [
showScreenPopup(AskPopupView, [
i18n('POPUPS_ASK/DESC_WANT_DELETE_MESSAGES'),
() => {
this.messagesDeleteHelper(sFromFolderFullNameRaw, aUidForRemove);
@ -918,7 +923,7 @@ class AppUser extends AbstractApp {
Settings.get('RequireTwoFactor')
) {
this.bootend();
showScreenPopup(require('View/Popup/TwoFactorConfiguration'), [true]);
showScreenPopup(TwoFactorConfigurationPopupView, [true]);
} else {
rl.setWindowTitle(i18n('GLOBAL/LOADING'));
@ -1022,7 +1027,7 @@ class AppUser extends AbstractApp {
} catch (e) {} // eslint-disable-line no-empty
if (Settings.get('MailToEmail')) {
mailToHelper(Settings.get('MailToEmail'), require('View/Popup/Compose'));
mailToHelper(Settings.get('MailToEmail'), ComposePopupView);
}
}, 500);
}
@ -1047,6 +1052,11 @@ class AppUser extends AbstractApp {
setInterval(() => dispatchEvent(new CustomEvent('reload-time')), 60000);
}
showComposePopupView(params = [])
{
showScreenPopup(ComposePopupView, params);
}
}
export default new AppUser();

View file

@ -1,5 +1,23 @@
import { createCKEditor } from 'External/CKEditor.js';
const
htmlre = /[&<>"']/g,
htmlmap = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;'
};
/**
* @param {string} text
* @returns {string}
*/
export function encodeHtml(text) {
return (text && text.toString ? text.toString() : ''+text).replace(htmlre, m => htmlmap[m]);
}
class HtmlEditor {
editor;
blurTimer = 0;

View file

@ -1,3 +1,5 @@
import * as Knoin from 'Knoin/Knoin';
const USER_VIEW_MODELS_HOOKS = [],
ADMIN_VIEW_MODELS_HOOKS = [];
@ -45,9 +47,8 @@ export function addSettingsViewModelForAdmin(SettingsViewModelClass, template, l
* @param {boolean} admin
*/
export function runSettingsViewModelHooks(admin) {
const Knoin = require('Knoin/Knoin');
(admin ? ADMIN_VIEW_MODELS_HOOKS : USER_VIEW_MODELS_HOOKS).forEach(view => {
Knoin.addSettingsViewModel(view[0], view[1], view[2], view[3]);
Knoin.settingsAddViewModel(view[0], view[1], view[2], view[3]);
});
}

View file

@ -160,3 +160,17 @@ export function reload(admin, language) {
doc.head.append(script);
});
}
/**
*
* @param {string} language
* @param {boolean=} isEng = false
* @returns {string}
*/
export function convertLangName(language, isEng = false) {
return i18n(
'LANGS_NAMES' + (true === isEng ? '_EN' : '') + '/LANG_' + language.toUpperCase().replace(/[^a-zA-Z0-9]+/g, '_'),
null,
language
);
}

View file

@ -49,20 +49,6 @@ export const convertThemeName = theme => {
.trim();
};
/**
*
* @param {string} language
* @param {boolean=} isEng = false
* @returns {string}
*/
export function convertLangName(language, isEng = false) {
return require('Common/Translator').i18n(
'LANGS_NAMES' + (true === isEng ? '_EN' : '') + '/LANG_' + language.toUpperCase().replace(/[^a-zA-Z0-9]+/g, '_'),
null,
language
);
}
/**
* @param {object} domOption
* @param {object} item

View file

@ -1,16 +1,11 @@
import { ComposeType, FolderType } from 'Common/EnumsUser';
import { EmailModel } from 'Model/Email';
import { showScreenPopup } from 'Knoin/Knoin';
import { encodeHtml } from 'Common/Html';
const
tpl = document.createElement('template'),
isArray = Array.isArray,
htmlmap = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;'
},
htmlspecialchars = str => (''+str).replace(/[&<>"']/g, m => htmlmap[m]);
isArray = Array.isArray;
/**
* @param {(string|number)} value
@ -21,14 +16,6 @@ export function isPosNumeric(value, includeZero = true) {
return null != value && (includeZero ? /^[0-9]*$/ : /^[1-9]+[0-9]*$/).test(value.toString());
}
/**
* @param {string} text
* @returns {string}
*/
export function encodeHtml(text) {
return null != text ? htmlspecialchars(text.toString()) : '';
}
/**
* @param {string} text
* @param {number=} len = 100
@ -101,7 +88,7 @@ export function htmlToPlain(html) {
.replace(/[\n]/gm, '<br />')
.replace(/[\r]/gm, '')
: '',
fixAttibuteValue = (...args) => (args && 1 < args.length ? '' + args[1] + htmlspecialchars(args[2]) : ''),
fixAttibuteValue = (...args) => (args && 1 < args.length ? '' + args[1] + encodeHtml(args[2]) : ''),
convertLinks = (...args) => (args && 1 < args.length ? args[1].trim() : '');
tpl.innerHTML = html
@ -470,8 +457,7 @@ export function mailToHelper(mailToUrl, PopupComposeViewModel) {
params = {};
const email = mailToUrl.replace(/\?.+$/, ''),
query = mailToUrl.replace(/^[^?]*\?/, ''),
EmailModel = require('Model/Email').default;
query = mailToUrl.replace(/^[^?]*\?/, '');
query.split('&').forEach(temp => {
temp = temp.split('=');
@ -506,7 +492,7 @@ export function mailToHelper(mailToUrl, PopupComposeViewModel) {
bcc = EmailModel.parseEmailLine(decodeURIComponent(params.bcc));
}
require('Knoin/Knoin').showScreenPopup(PopupComposeViewModel, [
showScreenPopup(PopupComposeViewModel, [
ComposeType.Empty,
null,
to,

View file

@ -1,6 +1,6 @@
import { SaveSettingsStep } from 'Common/Enums';
const ko = window.ko;
import { pInt } from 'Common/Utils';
import ko from 'External/ko';
ko.bindingHandlers.saveTrigger = {
init: (element) => {
@ -38,11 +38,10 @@ ko.extenders.idleTrigger = (target) => {
};
ko.extenders.posInterer = (target, defaultVal) => {
const Utils = require('Common/Utils'),
result = ko.computed({
const result = ko.computed({
read: target,
write: newValue => {
let val = Utils.pInt(newValue.toString(), defaultVal);
let val = pInt(newValue.toString(), defaultVal);
if (0 >= val) {
val = defaultVal;
}

View file

@ -1,6 +1,9 @@
const ko = window.ko,
import ko from 'External/ko';
import { HtmlEditor } from 'Common/Html';
import { timeToNode } from 'Common/Momentor';
import { EmailModel } from 'Model/Email';
rlContentType = 'snappymail/action',
const rlContentType = 'snappymail/action',
// In Chrome we have no access to dataTransfer.getData unless it's the 'drop' event
// In Chrome Mobile dataTransfer.types.includes(rlContentType) fails, only text/plain is set
@ -30,7 +33,6 @@ ko.bindingHandlers.editor = {
let editor = null;
const fValue = fValueAccessor(),
HtmlEditor = require('Common/HtmlEditor').default,
fUpdateEditorValue = () => fValue && fValue.__editor && fValue.__editor.setHtmlOrPlain(fValue()),
fUpdateKoValue = () => fValue && fValue.__editor && fValue(fValue.__editor.getDataWithHtmlMark()),
fOnReady = () => {
@ -51,7 +53,7 @@ ko.bindingHandlers.editor = {
}
};
let ttn = (element, fValueAccessor) => require('Common/Momentor').timeToNode(element, ko.unwrap(fValueAccessor()));
let ttn = (element, fValueAccessor) => timeToNode(element, ko.unwrap(fValueAccessor()));
ko.bindingHandlers.moment = {
init: ttn,
update: ttn
@ -59,8 +61,7 @@ ko.bindingHandlers.moment = {
ko.bindingHandlers.emailsTags = {
init: (element, fValueAccessor, fAllBindingsAccessor) => {
const EmailModel = require('Model/Email').default,
fValue = fValueAccessor(),
const fValue = fValueAccessor(),
fAllBindings = fAllBindingsAccessor(),
inputDelimiters = [',', ';', '\n'];

23
dev/External/ko.js vendored
View file

@ -1,32 +1,31 @@
import { i18n, i18nToNodes, trigger } from 'Common/Translator';
import { dropdownVisibility } from 'Common/Globals';
const
doc = document,
ko = window.ko,
Translator = () => require('Common/Translator'),
Globals = () => require('Common/Globals'),
isFunction = v => typeof v === 'function',
koValue = value => !ko.isObservable(value) && isFunction(value) ? value() : ko.unwrap(value);
ko.bindingHandlers.tooltip = {
init: (element, fValueAccessor) => {
const Global = Globals();
const sValue = koValue(fValueAccessor());
if ('off' === element.dataset.tooltipI18n) {
element.title = sValue;
} else {
element.title = Translator().i18n(sValue);
Translator().trigger.subscribe(() =>
element.title = Translator().i18n(sValue)
element.title = i18n(sValue);
trigger.subscribe(() =>
element.title = i18n(sValue)
);
Global.dropdownVisibility.subscribe(() =>
element.title = Translator().i18n(sValue)
dropdownVisibility.subscribe(() =>
element.title = i18n(sValue)
);
}
},
update: (element, fValueAccessor) => {
const sValue = koValue(fValueAccessor());
if (sValue) {
element.title = 'off' === element.dataset.tooltipI18n ? sValue : Translator().i18n(sValue);
element.title = 'off' === element.dataset.tooltipI18n ? sValue : i18n(sValue);
} else {
element.title = '';
}
@ -111,13 +110,13 @@ ko.bindingHandlers.modal = {
};
ko.bindingHandlers.i18nInit = {
init: element => Translator().i18nToNodes(element)
init: element => i18nToNodes(element)
};
ko.bindingHandlers.i18nUpdate = {
update: (element, fValueAccessor) => {
ko.unwrap(fValueAccessor());
Translator().i18nToNodes(element);
i18nToNodes(element);
}
};

View file

@ -58,7 +58,7 @@ export function createCommand(fExecute, fCanExecute = true) {
* @param {boolean=} isDefault = false
* @returns {void}
*/
export function addSettingsViewModel(SettingsViewModelClass, template, labelName, route, isDefault = false) {
export function settingsAddViewModel(SettingsViewModelClass, template, labelName, route, isDefault = false) {
SettingsViewModelClass.__rlSettingsData = {
Label: labelName,
Template: template,

View file

@ -1,4 +1,4 @@
import { encodeHtml } from 'Common/UtilsUser';
import { encodeHtml } from 'Common/Html';
import { AbstractModel } from 'Knoin/AbstractModel';

View file

@ -1,227 +0,0 @@
import ko from 'ko';
import { FolderType } from 'Common/EnumsUser';
import { isPosNumeric } from 'Common/UtilsUser';
import { i18n, trigger as translatorTrigger } from 'Common/Translator';
import { AbstractModel } from 'Knoin/AbstractModel';
import { FolderCollectionModel } from 'Model/FolderCollection';
function getSystemFolderName(type, def)
{
switch (type) {
case FolderType.Inbox:
return i18n('FOLDER_LIST/INBOX_NAME');
case FolderType.SentItems:
return i18n('FOLDER_LIST/SENT_NAME');
case FolderType.Draft:
return i18n('FOLDER_LIST/DRAFTS_NAME');
case FolderType.Spam:
return i18n('GLOBAL/SPAM');
case FolderType.Trash:
return i18n('FOLDER_LIST/TRASH_NAME');
case FolderType.Archive:
return i18n('FOLDER_LIST/ARCHIVE_NAME');
// no default
}
return def;
}
export class FolderModel extends AbstractModel {
constructor() {
super();
this.fullName = '';
this.fullNameRaw = '';
this.fullNameHash = '';
this.delimiter = '';
this.namespace = '';
this.deep = 0;
this.interval = 0;
this.selectable = false;
this.exists = true;
this.addObservables({
name: '',
type: FolderType.User,
focused: false,
selected: false,
edited: false,
subscribed: true,
checkable: false,
deleteAccess: false,
nameForEdit: '',
privateMessageCountAll: 0,
privateMessageCountUnread: 0,
collapsedPrivate: true
});
this.subFolders = ko.observableArray(new FolderCollectionModel);
this.actionBlink = ko.observable(false).extend({ falseTimeout: 1000 });
}
/**
* @static
* @param {FetchJsonFolder} json
* @returns {?FolderModel}
*/
static reviveFromJson(json) {
const folder = super.reviveFromJson(json);
if (folder) {
folder.deep = json.FullNameRaw.split(folder.delimiter).length - 1;
folder.messageCountAll = ko.computed({
read: folder.privateMessageCountAll,
write: (iValue) => {
if (isPosNumeric(iValue, true)) {
folder.privateMessageCountAll(iValue);
} else {
folder.privateMessageCountAll.valueHasMutated();
}
}
})
.extend({ notify: 'always' });
folder.messageCountUnread = ko.computed({
read: folder.privateMessageCountUnread,
write: (value) => {
if (isPosNumeric(value, true)) {
folder.privateMessageCountUnread(value);
} else {
folder.privateMessageCountUnread.valueHasMutated();
}
}
})
.extend({ notify: 'always' });
folder.addComputables({
isInbox: () => FolderType.Inbox === folder.type(),
hasSubscribedSubfolders:
() =>
!!folder.subFolders.find(
oFolder => (oFolder.subscribed() || oFolder.hasSubscribedSubfolders()) && !oFolder.isSystemFolder()
),
canBeEdited: () => FolderType.User === folder.type() && folder.exists && folder.selectable,
visible: () => {
const isSubscribed = folder.subscribed(),
isSubFolders = folder.hasSubscribedSubfolders();
return isSubscribed || (isSubFolders && (!folder.exists || !folder.selectable));
},
isSystemFolder: () => FolderType.User !== folder.type(),
hidden: () => {
const isSystem = folder.isSystemFolder(),
isSubFolders = folder.hasSubscribedSubfolders();
return (isSystem && !isSubFolders) || (!folder.selectable && !isSubFolders);
},
printableUnreadCount: () => {
const count = folder.messageCountAll(),
unread = folder.messageCountUnread(),
type = folder.type();
if (0 < count) {
if (FolderType.Draft === type) {
return '' + count;
}
if (
0 < unread &&
FolderType.Trash !== type &&
FolderType.Archive !== type &&
FolderType.SentItems !== type
) {
return '' + unread;
}
}
return '';
},
canBeDeleted: () => !folder.isSystemFolder() && !folder.subFolders.length,
selectableForFolderList: () => !folder.isSystemFolder() && folder.selectable,
canBeSubscribed: () => !folder.isSystemFolder() && folder.selectable,
canBeChecked: () => !folder.isSystemFolder() && folder.selectable,
localName: () => {
let name = folder.name();
if (folder.isSystemFolder()) {
translatorTrigger();
name = getSystemFolderName(folder.type(), name);
}
return name;
},
manageFolderSystemName: () => {
if (folder.isSystemFolder()) {
translatorTrigger();
let suffix = getSystemFolderName(folder.type(), '');
if (folder.name() !== suffix && 'inbox' !== suffix.toLowerCase()) {
return '(' + suffix + ')';
}
}
return '';
},
collapsed: {
read: () => !folder.hidden() && folder.collapsedPrivate(),
write: (value) => {
folder.collapsedPrivate(value);
}
},
hasUnreadMessages: () => 0 < folder.messageCountUnread() && folder.printableUnreadCount(),
hasSubscribedUnreadMessagesSubfolders: () =>
!!folder.subFolders.find(
folder => folder.hasUnreadMessages() || folder.hasSubscribedUnreadMessagesSubfolders()
)
});
folder.addSubscribables({
name: value => folder.nameForEdit(value),
edited: value => value && folder.nameForEdit(folder.name()),
messageCountUnread: unread => {
if (FolderType.Inbox === folder.type()) {
dispatchEvent(new CustomEvent('mailbox.inbox-unread-count', {detail:unread}));
}
}
});
}
return folder;
}
/**
* @returns {string}
*/
collapsedCss() {
return 'e-collapsed-sign ' + (this.hasSubscribedSubfolders()
? (this.collapsed() ? 'icon-right-mini' : 'icon-down-mini')
: 'icon-none'
);
}
/**
* @returns {string}
*/
printableFullName() {
return this.fullName.split(this.delimiter).join(' / ');
}
}

View file

@ -10,11 +10,12 @@ import * as Local from 'Storage/Client';
import AppStore from 'Stores/User/App';
import FolderStore from 'Stores/User/Folder';
import { FolderModel } from 'Model/Folder';
import ko from 'ko';
import Remote from 'Remote/User/Fetch';
import { isPosNumeric } from 'Common/UtilsUser';
import { i18n, trigger as translatorTrigger } from 'Common/Translator';
'use strict';
import { AbstractModel } from 'Knoin/AbstractModel';
const Settings = rl.settings,
@ -141,7 +142,7 @@ export class FolderCollectionModel extends AbstractCollectionModel
FolderStore.archiveFolder(normalizeFolder(Settings.get('ArchiveFolder')));
if (update) {
Remote.saveSystemFolders(()=>{}, {
rl.app.Remote.saveSystemFolders(()=>{}, {
SentFolder: FolderStore.sentFolder(),
DraftFolder: FolderStore.draftFolder(),
SpamFolder: FolderStore.spamFolder(),
@ -155,3 +156,222 @@ export class FolderCollectionModel extends AbstractCollectionModel
}
}
function getSystemFolderName(type, def)
{
switch (type) {
case FolderType.Inbox:
return i18n('FOLDER_LIST/INBOX_NAME');
case FolderType.SentItems:
return i18n('FOLDER_LIST/SENT_NAME');
case FolderType.Draft:
return i18n('FOLDER_LIST/DRAFTS_NAME');
case FolderType.Spam:
return i18n('GLOBAL/SPAM');
case FolderType.Trash:
return i18n('FOLDER_LIST/TRASH_NAME');
case FolderType.Archive:
return i18n('FOLDER_LIST/ARCHIVE_NAME');
// no default
}
return def;
}
export class FolderModel extends AbstractModel {
constructor() {
super();
this.fullName = '';
this.fullNameRaw = '';
this.fullNameHash = '';
this.delimiter = '';
this.namespace = '';
this.deep = 0;
this.interval = 0;
this.selectable = false;
this.exists = true;
this.addObservables({
name: '',
type: FolderType.User,
focused: false,
selected: false,
edited: false,
subscribed: true,
checkable: false,
deleteAccess: false,
nameForEdit: '',
privateMessageCountAll: 0,
privateMessageCountUnread: 0,
collapsedPrivate: true
});
this.subFolders = ko.observableArray(new FolderCollectionModel);
this.actionBlink = ko.observable(false).extend({ falseTimeout: 1000 });
}
/**
* @static
* @param {FetchJsonFolder} json
* @returns {?FolderModel}
*/
static reviveFromJson(json) {
const folder = super.reviveFromJson(json);
if (folder) {
folder.deep = json.FullNameRaw.split(folder.delimiter).length - 1;
folder.messageCountAll = ko.computed({
read: folder.privateMessageCountAll,
write: (iValue) => {
if (isPosNumeric(iValue, true)) {
folder.privateMessageCountAll(iValue);
} else {
folder.privateMessageCountAll.valueHasMutated();
}
}
})
.extend({ notify: 'always' });
folder.messageCountUnread = ko.computed({
read: folder.privateMessageCountUnread,
write: (value) => {
if (isPosNumeric(value, true)) {
folder.privateMessageCountUnread(value);
} else {
folder.privateMessageCountUnread.valueHasMutated();
}
}
})
.extend({ notify: 'always' });
folder.addComputables({
isInbox: () => FolderType.Inbox === folder.type(),
hasSubscribedSubfolders:
() =>
!!folder.subFolders.find(
oFolder => (oFolder.subscribed() || oFolder.hasSubscribedSubfolders()) && !oFolder.isSystemFolder()
),
canBeEdited: () => FolderType.User === folder.type() && folder.exists && folder.selectable,
visible: () => {
const isSubscribed = folder.subscribed(),
isSubFolders = folder.hasSubscribedSubfolders();
return isSubscribed || (isSubFolders && (!folder.exists || !folder.selectable));
},
isSystemFolder: () => FolderType.User !== folder.type(),
hidden: () => {
const isSystem = folder.isSystemFolder(),
isSubFolders = folder.hasSubscribedSubfolders();
return (isSystem && !isSubFolders) || (!folder.selectable && !isSubFolders);
},
printableUnreadCount: () => {
const count = folder.messageCountAll(),
unread = folder.messageCountUnread(),
type = folder.type();
if (0 < count) {
if (FolderType.Draft === type) {
return '' + count;
}
if (
0 < unread &&
FolderType.Trash !== type &&
FolderType.Archive !== type &&
FolderType.SentItems !== type
) {
return '' + unread;
}
}
return '';
},
canBeDeleted: () => !folder.isSystemFolder() && !folder.subFolders.length,
selectableForFolderList: () => !folder.isSystemFolder() && folder.selectable,
canBeSubscribed: () => !folder.isSystemFolder() && folder.selectable,
canBeChecked: () => !folder.isSystemFolder() && folder.selectable,
localName: () => {
let name = folder.name();
if (folder.isSystemFolder()) {
translatorTrigger();
name = getSystemFolderName(folder.type(), name);
}
return name;
},
manageFolderSystemName: () => {
if (folder.isSystemFolder()) {
translatorTrigger();
let suffix = getSystemFolderName(folder.type(), '');
if (folder.name() !== suffix && 'inbox' !== suffix.toLowerCase()) {
return '(' + suffix + ')';
}
}
return '';
},
collapsed: {
read: () => !folder.hidden() && folder.collapsedPrivate(),
write: (value) => {
folder.collapsedPrivate(value);
}
},
hasUnreadMessages: () => 0 < folder.messageCountUnread() && folder.printableUnreadCount(),
hasSubscribedUnreadMessagesSubfolders: () =>
!!folder.subFolders.find(
folder => folder.hasUnreadMessages() || folder.hasSubscribedUnreadMessagesSubfolders()
)
});
folder.addSubscribables({
name: value => folder.nameForEdit(value),
edited: value => value && folder.nameForEdit(folder.name()),
messageCountUnread: unread => {
if (FolderType.Inbox === folder.type()) {
dispatchEvent(new CustomEvent('mailbox.inbox-unread-count', {detail:unread}));
}
}
});
}
return folder;
}
/**
* @returns {string}
*/
collapsedCss() {
return 'e-collapsed-sign ' + (this.hasSubscribedSubfolders()
? (this.collapsed() ? 'icon-right-mini' : 'icon-down-mini')
: 'icon-none'
);
}
/**
* @returns {string}
*/
printableFullName() {
return this.fullName.split(this.delimiter).join(' / ');
}
}

View file

@ -3,7 +3,7 @@ import ko from 'ko';
import { MessagePriority } from 'Common/EnumsUser';
import { i18n } from 'Common/Translator';
import { encodeHtml } from 'Common/UtilsUser';
import { encodeHtml } from 'Common/Html';
import { messageViewLink, messageDownloadLink } from 'Common/Links';
@ -14,6 +14,8 @@ import { AttachmentCollectionModel } from 'Model/AttachmentCollection';
import { EmailCollectionModel } from 'Model/EmailCollection';
import { AbstractModel } from 'Knoin/AbstractModel';
import PreviewHTML from 'Html/PreviewMessage.html';
const isArray = Array.isArray,
SignedVerifyStatus = {
@ -379,9 +381,8 @@ export class MessageModel extends AbstractModel {
ccLine = this.ccToLine(false),
m = 0 < timeStampInUTC ? new Date(timeStampInUTC * 1000) : null,
win = open(''),
doc = win.document,
html = require('Html/PreviewMessage.html');
doc.write((html.default)
doc = win.document;
doc.write(PreviewHTML
.replace(/{{subject}}/g, encodeHtml(this.subject()))
.replace('{{date}}', encodeHtml(m ? m.format('LLL') : ''))
.replace('{{fromCreds}}', encodeHtml(this.fromToLine(false)))

View file

@ -1,4 +1,4 @@
import { addSettingsViewModel } from 'Knoin/Knoin';
import { settingsAddViewModel } from 'Knoin/Knoin';
import { runSettingsViewModelHooks } from 'Common/Plugins';
import { AbstractSettingsScreen } from 'Screen/AbstractSettings';
@ -25,7 +25,7 @@ export class SettingsAdminScreen extends AbstractSettingsScreen {
* @param {Function=} fCallback = null
*/
setupSettings(fCallback = null) {
addSettingsViewModel(
settingsAddViewModel(
GeneralAdminSettings,
'AdminSettingsGeneral',
'TABS_LABELS/LABEL_GENERAL_NAME',
@ -43,7 +43,7 @@ export class SettingsAdminScreen extends AbstractSettingsScreen {
[PackagesAdminSettings, 'Packages'],
[AboutAdminSettings, 'About'],
].forEach(item =>
addSettingsViewModel(
settingsAddViewModel(
item[0],
'AdminSettings'+item[1],
'TABS_LABELS/LABEL_'+item[1].toUpperCase()+'_NAME',

View file

@ -20,6 +20,8 @@ import { warmUpScreenPopup } from 'Knoin/Knoin';
import { AbstractScreen } from 'Knoin/AbstractScreen';
import { ComposePopupView } from 'View/Popup/Compose';
const Settings = rl.settings;
export class MailBoxUserScreen extends AbstractScreen {
@ -101,7 +103,7 @@ export class MailBoxUserScreen extends AbstractScreen {
*/
onStart() {
setTimeout(() => SettingsStore.layout.valueHasMutated(), 50);
setTimeout(() => warmUpScreenPopup(require('View/Popup/Compose')), 500);
setTimeout(() => warmUpScreenPopup(ComposePopupView), 500);
addEventListener('mailbox.inbox-unread-count', e => {
FolderStore.foldersInboxUnreadCount(e.detail);

View file

@ -6,7 +6,7 @@ import { initOnStartOrLangChange, i18n } from 'Common/Translator';
import AppStore from 'Stores/User/App';
import AccountStore from 'Stores/User/Account';
import { addSettingsViewModel } from 'Knoin/Knoin';
import { settingsAddViewModel } from 'Knoin/Knoin';
import { AbstractSettingsScreen } from 'Screen/AbstractSettings';
@ -48,14 +48,14 @@ export class SettingsUserScreen extends AbstractSettingsScreen {
return false;
}
addSettingsViewModel(GeneralUserSettings, 'SettingsGeneral', 'SETTINGS_LABELS/LABEL_GENERAL_NAME', 'general', true);
settingsAddViewModel(GeneralUserSettings, 'SettingsGeneral', 'SETTINGS_LABELS/LABEL_GENERAL_NAME', 'general', true);
if (AppStore.contactsIsAllowed()) {
addSettingsViewModel(ContactsUserSettings, 'SettingsContacts', 'SETTINGS_LABELS/LABEL_CONTACTS_NAME', 'contacts');
settingsAddViewModel(ContactsUserSettings, 'SettingsContacts', 'SETTINGS_LABELS/LABEL_CONTACTS_NAME', 'contacts');
}
if (Settings.capa(Capa.AdditionalAccounts) || Settings.capa(Capa.Identities)) {
addSettingsViewModel(
settingsAddViewModel(
AccountsUserSettings,
'SettingsAccounts',
Settings.capa(Capa.AdditionalAccounts)
@ -66,15 +66,15 @@ export class SettingsUserScreen extends AbstractSettingsScreen {
}
if (Settings.capa(Capa.Sieve)) {
addSettingsViewModel(FiltersUserSettings, 'SettingsFilters', 'SETTINGS_LABELS/LABEL_FILTERS_NAME', 'filters');
settingsAddViewModel(FiltersUserSettings, 'SettingsFilters', 'SETTINGS_LABELS/LABEL_FILTERS_NAME', 'filters');
}
if (Settings.capa(Capa.AutoLogout) || Settings.capa(Capa.TwoFactor)) {
addSettingsViewModel(SecurityUserSettings, 'SettingsSecurity', 'SETTINGS_LABELS/LABEL_SECURITY_NAME', 'security');
settingsAddViewModel(SecurityUserSettings, 'SettingsSecurity', 'SETTINGS_LABELS/LABEL_SECURITY_NAME', 'security');
}
if (Settings.capa(Capa.Templates)) {
addSettingsViewModel(
settingsAddViewModel(
TemplatesUserSettings,
'SettingsTemplates',
'SETTINGS_LABELS/LABEL_TEMPLATES_NAME',
@ -83,15 +83,15 @@ export class SettingsUserScreen extends AbstractSettingsScreen {
}
if (Settings.capa(Capa.Folders)) {
addSettingsViewModel(FoldersUserSettings, 'SettingsFolders', 'SETTINGS_LABELS/LABEL_FOLDERS_NAME', 'folders');
settingsAddViewModel(FoldersUserSettings, 'SettingsFolders', 'SETTINGS_LABELS/LABEL_FOLDERS_NAME', 'folders');
}
if (Settings.capa(Capa.Themes)) {
addSettingsViewModel(ThemesUserSettings, 'SettingsThemes', 'SETTINGS_LABELS/LABEL_THEMES_NAME', 'themes');
settingsAddViewModel(ThemesUserSettings, 'SettingsThemes', 'SETTINGS_LABELS/LABEL_THEMES_NAME', 'themes');
}
if (Settings.capa(Capa.OpenPGP)) {
addSettingsViewModel(OpenPgpUserSettings, 'SettingsOpenPGP', 'OpenPGP', 'openpgp');
settingsAddViewModel(OpenPgpUserSettings, 'SettingsOpenPGP', 'OpenPGP', 'openpgp');
}
runSettingsViewModelHooks(false);

View file

@ -6,6 +6,9 @@ import { showScreenPopup } from 'Knoin/Knoin';
import DomainStore from 'Stores/Admin/Domain';
import Remote from 'Remote/Admin/Fetch';
import { DomainPopupView } from 'View/Popup/Domain';
import { DomainAliasPopupView } from 'View/Popup/DomainAlias';
export class DomainsAdminSettings {
constructor() {
this.domains = DomainStore.domains;
@ -19,11 +22,11 @@ export class DomainsAdminSettings {
}
createDomain() {
showScreenPopup(require('View/Popup/Domain'));
showScreenPopup(DomainPopupView);
}
createDomainAlias() {
showScreenPopup(require('View/Popup/DomainAlias'));
showScreenPopup(DomainAliasPopupView);
}
deleteDomain(domain) {
@ -47,7 +50,7 @@ export class DomainsAdminSettings {
onDomainLoadRequest(sResult, oData) {
if (StorageResultType.Success === sResult && oData && oData.Result) {
showScreenPopup(require('View/Popup/Domain'), [oData.Result]);
showScreenPopup(DomainPopupView, [oData.Result]);
}
}

View file

@ -4,12 +4,11 @@ import {
pInt,
settingsSaveHelperSimpleFunction,
changeTheme,
convertThemeName,
convertLangName
convertThemeName
} from 'Common/Utils';
import { SaveSettingsStep } from 'Common/Enums';
import { reload as translatorReload } from 'Common/Translator';
import { reload as translatorReload, convertLangName } from 'Common/Translator';
import { showScreenPopup } from 'Knoin/Knoin';
@ -19,6 +18,7 @@ import ThemeStore from 'Stores/Theme';
import LanguageStore from 'Stores/Language';
import AppAdminStore from 'Stores/Admin/App';
import CapaAdminStore from 'Stores/Admin/Capa';
import LanguagesPopupView from 'View/Popup/Languages';
const settingsGet = rl.settings.get;
@ -167,11 +167,11 @@ export class GeneralAdminSettings {
}
selectLanguage() {
showScreenPopup(require('View/Popup/Languages'), [this.language, this.languages(), LanguageStore.userLanguage()]);
showScreenPopup(LanguagesPopupView, [this.language, this.languages(), LanguageStore.userLanguage()]);
}
selectLanguageAdmin() {
showScreenPopup(require('View/Popup/Languages'), [
showScreenPopup(LanguagesPopupView, [
this.languageAdmin,
this.languagesAdmin(),
LanguageStore.userLanguageAdmin()

View file

@ -9,6 +9,8 @@ import PluginStore from 'Stores/Admin/Plugin';
import Remote from 'Remote/Admin/Fetch';
import { PluginPopupView } from 'View/Popup/Plugin';
export class PluginsAdminSettings {
constructor() {
this.enabledPlugins = ko.observable(!!rl.settings.get('EnabledPlugins'));
@ -54,7 +56,7 @@ export class PluginsAdminSettings {
onPluginLoadRequest(result, data) {
if (StorageResultType.Success === result && data && data.Result) {
showScreenPopup(require('View/Popup/Plugin'), [data.Result]);
showScreenPopup(PluginPopupView, [data.Result]);
}
}

View file

@ -8,6 +8,9 @@ import Remote from 'Remote/User/Fetch';
import { showScreenPopup } from 'Knoin/Knoin';
import { AccountPopupView } from 'View/Popup/Account';
import { IdentityPopupView } from 'View/Popup/Identity';
export class AccountsUserSettings {
constructor() {
this.allowAdditionalAccount = rl.settings.capa(Capa.AdditionalAccounts);
@ -21,21 +24,21 @@ export class AccountsUserSettings {
}
addNewAccount() {
showScreenPopup(require('View/Popup/Account'));
showScreenPopup(AccountPopupView);
}
editAccount(account) {
if (account && account.canBeEdit()) {
showScreenPopup(require('View/Popup/Account'), [account]);
showScreenPopup(AccountPopupView, [account]);
}
}
addNewIdentity() {
showScreenPopup(require('View/Popup/Identity'));
showScreenPopup(IdentityPopupView);
}
editIdentity(identity) {
showScreenPopup(require('View/Popup/Identity'), [identity]);
showScreenPopup(IdentityPopupView, [identity]);
}
/**

View file

@ -11,6 +11,8 @@ import { SieveScriptModel } from 'Model/SieveScript';
import { showScreenPopup } from 'Knoin/Knoin';
import { SieveScriptPopupView } from 'View/Popup/SieveScript';
export class FiltersUserSettings {
constructor() {
this.scripts = SieveStore.scripts;
@ -60,11 +62,11 @@ export class FiltersUserSettings {
}
addScript() {
showScreenPopup(require('View/Popup/SieveScript'), [new SieveScriptModel()]);
showScreenPopup(SieveScriptPopupView, [new SieveScriptModel()]);
}
editScript(script) {
showScreenPopup(require('View/Popup/SieveScript'), [script]);
showScreenPopup(SieveScriptPopupView, [script]);
}
deleteScript(script) {

View file

@ -14,6 +14,9 @@ import Remote from 'Remote/User/Fetch';
import { showScreenPopup } from 'Knoin/Knoin';
import { FolderCreatePopupView } from 'View/Popup/FolderCreate';
import { FolderSystemPopupView } from 'View/Popup/FolderSystem';
export class FoldersUserSettings {
constructor() {
this.displaySpecSetting = FolderStore.displaySpecSetting;
@ -79,11 +82,11 @@ export class FoldersUserSettings {
}
createFolder() {
showScreenPopup(require('View/Popup/FolderCreate'));
showScreenPopup(FolderCreatePopupView);
}
systemFolder() {
showScreenPopup(require('View/Popup/FolderSystem'));
showScreenPopup(FolderSystemPopupView);
}
deleteFolder(folderToRemove) {

View file

@ -5,9 +5,9 @@ import { MESSAGES_PER_PAGE_VALUES } from 'Common/Consts';
import { SaveSettingsStep } from 'Common/Enums';
import { EditorDefaultType, Layout } from 'Common/EnumsUser';
import { settingsSaveHelperSimpleFunction, convertLangName } from 'Common/Utils';
import { settingsSaveHelperSimpleFunction } from 'Common/Utils';
import { i18n, trigger as translatorTrigger, reload as translatorReload } from 'Common/Translator';
import { i18n, trigger as translatorTrigger, reload as translatorReload, convertLangName } from 'Common/Translator';
import { showScreenPopup } from 'Knoin/Knoin';
@ -20,6 +20,9 @@ import MessageStore from 'Stores/User/Message';
import Remote from 'Remote/User/Fetch';
import { IdentityPopupView } from 'View/Popup/Identity';
import { LanguagesPopupView } from 'View/Popup/Languages';
export class GeneralUserSettings {
constructor() {
this.language = LanguageStore.language;
@ -87,7 +90,7 @@ export class GeneralUserSettings {
editMainIdentity() {
const identity = this.identityMain();
if (identity) {
showScreenPopup(require('View/Popup/Identity'), [identity]);
showScreenPopup(IdentityPopupView, [identity]);
}
}
@ -161,6 +164,6 @@ export class GeneralUserSettings {
}
selectLanguage() {
showScreenPopup(require('View/Popup/Languages'), [this.language, this.languages(), LanguageStore.userLanguage()]);
showScreenPopup(LanguagesPopupView, [this.language, this.languages(), LanguageStore.userLanguage()]);
}
}

View file

@ -9,6 +9,10 @@ import Remote from 'Remote/User/Fetch';
import { showScreenPopup } from 'Knoin/Knoin';
import { AddOpenPgpKeyPopupView } from 'View/Popup/AddOpenPgpKey';
import { NewOpenPgpKeyPopupView } from 'View/Popup/NewOpenPgpKey';
import { ViewOpenPgpKeyPopupView } from 'View/Popup/ViewOpenPgpKey';
export class OpenPgpUserSettings {
constructor() {
this.openpgpkeys = PgpStore.openpgpkeys;
@ -21,16 +25,16 @@ export class OpenPgpUserSettings {
}
addOpenPgpKey() {
showScreenPopup(require('View/Popup/AddOpenPgpKey'));
showScreenPopup(AddOpenPgpKeyPopupView);
}
generateOpenPgpKey() {
showScreenPopup(require('View/Popup/NewOpenPgpKey'));
showScreenPopup(NewOpenPgpKeyPopupView);
}
viewOpenPgpKey(openPgpKey) {
if (openPgpKey) {
showScreenPopup(require('View/Popup/ViewOpenPgpKey'), [openPgpKey]);
showScreenPopup(ViewOpenPgpKeyPopupView, [openPgpKey]);
}
}

View file

@ -10,6 +10,8 @@ import SettinsStore from 'Stores/User/Settings';
import Remote from 'Remote/User/Fetch';
import { TwoFactorConfigurationPopupView } from 'View/Popup/TwoFactorConfiguration';
export class SecurityUserSettings {
constructor() {
this.capaAutoLogout = rl.settings.capa(Capa.AutoLogout);
@ -34,7 +36,7 @@ export class SecurityUserSettings {
}
configureTwoFactor() {
showScreenPopup(require('View/Popup/TwoFactorConfiguration'));
showScreenPopup(TwoFactorConfigurationPopupView);
}
onBuild() {

View file

@ -7,6 +7,8 @@ import Remote from 'Remote/User/Fetch';
import { showScreenPopup } from 'Knoin/Knoin';
import { TemplatePopupView } from 'View/Popup/Template';
export class TemplatesUserSettings {
constructor() {
this.templates = TemplateStore.templates;
@ -20,12 +22,12 @@ export class TemplatesUserSettings {
}
addNewTemplate() {
showScreenPopup(require('View/Popup/Template'));
showScreenPopup(TemplatePopupView);
}
editTemplate(oTemplateItem) {
if (oTemplateItem) {
showScreenPopup(require('View/Popup/Template'), [oTemplateItem]);
showScreenPopup(TemplatePopupView, [oTemplateItem]);
}
}

View file

@ -7,6 +7,8 @@ import AccountStore from 'Stores/User/Account';
import { showScreenPopup } from 'Knoin/Knoin';
import { MessageOpenPgpPopupView } from 'View/Popup/MessageOpenPgp';
function controlsHelper(dom, verControl, success, title, text)
{
dom.classList.toggle('error', !success);
@ -264,7 +266,7 @@ class PgpUserStore {
if (message && message.getEncryptionKeyIds) {
const privateKeys = this.findPrivateKeysByEncryptionKeyIds(message.getEncryptionKeyIds(), recipients, true);
if (privateKeys && privateKeys.length) {
showScreenPopup(require('View/Popup/MessageOpenPgp'), [
showScreenPopup(MessageOpenPgpPopupView, [
(decryptedKey) => {
if (decryptedKey) {
message.decrypt(decryptedKey).then(

View file

@ -15,7 +15,8 @@ import {
} from 'Common/EnumsUser';
import { inFocus, pInt } from 'Common/Utils';
import { encodeHtml, delegateRunOnDestroy } from 'Common/UtilsUser';
import { delegateRunOnDestroy } from 'Common/UtilsUser';
import { encodeHtml } from 'Common/Html';
import { UNUSED_OPTION_VALUE } from 'Common/Consts';
import { upload } from 'Common/Links';
@ -23,7 +24,7 @@ import { i18n, getNotification, getUploadErrorDescByCode } from 'Common/Translat
import { format as momentorFormat } from 'Common/Momentor';
import { MessageFlagsCache, setFolderHash } from 'Common/Cache';
import { HtmlEditor } from 'Common/HtmlEditor';
import { HtmlEditor } from 'Common/Html';
import AppStore from 'Stores/User/App';
import SettingsStore from 'Stores/User/Settings';
@ -40,6 +41,11 @@ import { ComposeAttachmentModel } from 'Model/ComposeAttachment';
import { command, isPopupVisible, showScreenPopup, hideScreenPopup } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
import { FolderSystemPopupView } from 'View/Popup/FolderSystem';
import { AskPopupView } from 'View/Popup/Ask';
import { ContactsPopupView } from 'View/Popup/Contacts';
import { ComposeOpenPgpPopupView } from 'View/Popup/ComposeOpenPgp';
const Settings = rl.settings,
/**
* @param {string} prefix
@ -403,7 +409,7 @@ class ComposePopupView extends AbstractViewPopup {
}
if (!sSentFolder) {
showScreenPopup(require('View/Popup/FolderSystem'), [SetSystemFoldersNotification.Sent]);
showScreenPopup(FolderSystemPopupView, [SetSystemFoldersNotification.Sent]);
} else {
this.sendError(false);
this.sending(true);
@ -443,7 +449,7 @@ class ComposePopupView extends AbstractViewPopup {
}
if (FolderStore.draftFolderNotEnabled()) {
showScreenPopup(require('View/Popup/FolderSystem'), [SetSystemFoldersNotification.Draft]);
showScreenPopup(FolderSystemPopupView, [SetSystemFoldersNotification.Draft]);
} else {
this.savedError(false);
this.saving(true);
@ -463,9 +469,8 @@ class ComposePopupView extends AbstractViewPopup {
@command((self) => self.isDraftFolderMessage())
deleteCommand() {
const PopupsAskViewModel = require('View/Popup/Ask');
if (!isPopupVisible(PopupsAskViewModel) && this.modalVisibility()) {
showScreenPopup(PopupsAskViewModel, [
if (!isPopupVisible(AskPopupView) && this.modalVisibility()) {
showScreenPopup(AskPopupView, [
i18n('POPUPS_ASK/DESC_WANT_DELETE_MESSAGES'),
() => {
if (this.modalVisibility()) {
@ -499,7 +504,7 @@ class ComposePopupView extends AbstractViewPopup {
if (this.allowContacts) {
this.skipCommand();
setTimeout(() => {
showScreenPopup(require('View/Popup/Contacts'), [true, this.sLastFocusedField]);
showScreenPopup(ContactsPopupView, [true, this.sLastFocusedField]);
}, 200);
}
}
@ -535,7 +540,7 @@ class ComposePopupView extends AbstractViewPopup {
openOpenPgpPopup() {
if (PgpStore.capaOpenPGP() && this.oEditor && !this.oEditor.isHtml()) {
showScreenPopup(require('View/Popup/ComposeOpenPgp'), [
showScreenPopup(ComposeOpenPgpPopupView, [
(result) => {
this.editor((editor) => {
editor.setPlain(result);
@ -780,7 +785,7 @@ class ComposePopupView extends AbstractViewPopup {
if (AppStore.composeInEdit()) {
type = type || ComposeType.Empty;
if (ComposeType.Empty !== type) {
showScreenPopup(require('View/Popup/Ask'), [
showScreenPopup(AskPopupView, [
i18n('COMPOSE/DISCARD_UNSAVED_DATA'),
() => {
this.initOnShow(type, oMessageOrArray, aToEmails, aCcEmails, aBccEmails, sCustomSubject, sCustomPlainText);
@ -1137,12 +1142,11 @@ class ComposePopupView extends AbstractViewPopup {
}
tryToClosePopup() {
const PopupsAskViewModel = require('View/Popup/Ask');
if (!isPopupVisible(PopupsAskViewModel) && this.modalVisibility()) {
if (!isPopupVisible(AskPopupView) && this.modalVisibility()) {
if (this.bSkipNextHide || (this.isEmptyForm() && !this.draftUid())) {
this.closeCommand && this.closeCommand();
} else {
showScreenPopup(PopupsAskViewModel, [
showScreenPopup(AskPopupView, [
i18n('POPUPS_ASK/DESC_WANT_CLOSE_THIS_WINDOW'),
() => {
if (this.modalVisibility()) {

View file

@ -26,9 +26,10 @@ import { EmailModel } from 'Model/Email';
import { ContactModel } from 'Model/Contact';
import { ContactPropertyModel, ContactPropertyType } from 'Model/ContactProperty';
import { command, showScreenPopup, hideScreenPopup } from 'Knoin/Knoin';
import { command, hideScreenPopup } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
const CONTACTS_PER_PAGE = 50,
propertyIsMail = prop => prop.isType(ContactPropertyType.Email),
propertyIsName = prop => prop.isType(ContactPropertyType.FirstName) || prop.isType(ContactPropertyType.LastName);
@ -225,7 +226,7 @@ class ContactsPopupView extends AbstractViewPopup {
this.sLastComposeFocusedField = '';
setTimeout(() => {
showScreenPopup(require('View/Popup/Compose'), [ComposeType.Empty, null, toEmails, ccEmails, bccEmails]);
rl.app.showComposePopupView([ComposeType.Empty, null, toEmails, ccEmails, bccEmails]);
}, 200);
}
@ -567,7 +568,7 @@ class ContactsPopupView extends AbstractViewPopup {
this.bBackToCompose = false;
if (rl.settings.capa(Capa.Composer)) {
showScreenPopup(require('View/Popup/Compose'));
rl.app.showComposePopupView();
}
}
}

View file

@ -12,7 +12,7 @@ import Remote from 'Remote/User/Fetch';
import { command } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
class FolderCreateView extends AbstractViewPopup {
class FolderCreatePopupView extends AbstractViewPopup {
constructor() {
super('FolderCreate');
@ -77,4 +77,4 @@ class FolderCreateView extends AbstractViewPopup {
}
}
export { FolderCreateView, FolderCreateView as default };
export { FolderCreatePopupView, FolderCreatePopupView as default };

View file

@ -1,6 +1,6 @@
import ko from 'ko';
import { convertLangName } from 'Common/Utils';
import { convertLangName } from 'Common/Translator';
import { AbstractViewPopup } from 'Knoin/AbstractViews';

View file

@ -7,6 +7,7 @@ import Remote from 'Remote/Admin/Fetch';
import { command, isPopupVisible, showScreenPopup } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
import { AskPopupView } from 'View/Popup/Ask';
class PluginPopupView extends AbstractViewPopup {
constructor() {
@ -88,9 +89,8 @@ class PluginPopupView extends AbstractViewPopup {
}
tryToClosePopup() {
const PopupsAskViewModel = require('View/Popup/Ask');
if (!isPopupVisible(PopupsAskViewModel)) {
showScreenPopup(PopupsAskViewModel, [
if (!isPopupVisible(AskPopupView)) {
showScreenPopup(AskPopupView, [
i18n('POPUPS_ASK/DESC_WANT_CLOSE_THIS_WINDOW'),
() => this.modalVisibility() && this.cancelCommand && this.cancelCommand()
]);

View file

@ -12,6 +12,8 @@ import SieveStore from 'Stores/User/Sieve';
import { showScreenPopup/*, command*/ } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
import { FilterPopupView } from 'View/Popup/Filter';
class SieveScriptPopupView extends AbstractViewPopup {
constructor() {
super('SieveScript');
@ -83,7 +85,7 @@ class SieveScriptPopupView extends AbstractViewPopup {
/* this = SieveScriptModel */
const filter = new FilterModel();
filter.generateID();
showScreenPopup(require('View/Popup/Filter'), [
showScreenPopup(FilterPopupView, [
filter,
() => {
this.filters.push(filter);
@ -94,7 +96,7 @@ class SieveScriptPopupView extends AbstractViewPopup {
editFilter(filter) {
const clonedFilter = filter.cloneSelf();
showScreenPopup(require('View/Popup/Filter'), [
showScreenPopup(FilterPopupView, [
clonedFilter,
() => {
const script = this.script(),

View file

@ -1,6 +1,6 @@
import { StorageResultType, Notification } from 'Common/Enums';
import { getNotification } from 'Common/Translator';
import { HtmlEditor } from 'Common/HtmlEditor';
import { HtmlEditor } from 'Common/Html';
import Remote from 'Remote/User/Fetch';

View file

@ -7,6 +7,8 @@ import Remote from 'Remote/User/Fetch';
import { showScreenPopup } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
import { TwoFactorTestPopupView } from 'View/Popup/TwoFactorTest';
class TwoFactorConfigurationPopupView extends AbstractViewPopup {
constructor() {
super('TwoFactorConfiguration');
@ -104,7 +106,7 @@ class TwoFactorConfigurationPopupView extends AbstractViewPopup {
}
testTwoFactor() {
showScreenPopup(require('View/Popup/TwoFactorTest'), [this.twoFactorTested]);
showScreenPopup(TwoFactorTestPopupView, [this.twoFactorTested]);
}
clearTwoFactor() {

View file

@ -8,6 +8,9 @@ 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';
const Settings = rl.settings;
export class AbstractSystemDropDownUserView extends AbstractViewRight {
@ -63,13 +66,13 @@ export class AbstractSystemDropDownUserView extends AbstractViewRight {
settingsHelp() {
if (Settings.capa(Capa.Help)) {
showScreenPopup(require('View/Popup/KeyboardShortcutsHelp'));
showScreenPopup(KeyboardShortcutsHelpPopupView);
}
}
addAccountClick() {
if (this.capaAdditionalAccounts()) {
showScreenPopup(require('View/Popup/Account'));
showScreenPopup(AccountPopupView);
}
}
@ -89,7 +92,7 @@ export class AbstractSystemDropDownUserView extends AbstractViewRight {
// shortcuts help
shortcuts.add('?,f1,help', '', [KeyState.MessageList, KeyState.MessageView, KeyState.Settings], () => {
if (this.viewModelVisible) {
showScreenPopup(require('View/Popup/KeyboardShortcutsHelp'));
showScreenPopup(KeyboardShortcutsHelpPopupView);
return false;
}
return true;

View file

@ -7,9 +7,7 @@ import {
import { ClientSideKeyName } from 'Common/EnumsUser';
import { convertLangName } from 'Common/Utils';
import { getNotification, getNotificationFromResponse, reload as translatorReload } from 'Common/Translator';
import { getNotification, getNotificationFromResponse, reload as translatorReload, convertLangName } from 'Common/Translator';
import AppStore from 'Stores/User/App';
import LanguageStore from 'Stores/Language';
@ -23,6 +21,8 @@ import { AbstractViewCenter } from 'Knoin/AbstractViews';
import { rootAdmin } from 'Common/Links';
import { LanguagesPopupView } from 'View/Popup/Languages';
const Settings = rl.settings,
LoginSignMeType = {
@ -269,7 +269,7 @@ class LoginUserView extends AbstractViewCenter {
}
selectLanguage() {
showScreenPopup(require('View/Popup/Languages'), [this.language, this.languages(), LanguageStore.userLanguage()]);
showScreenPopup(LanguagesPopupView, [this.language, this.languages(), LanguageStore.userLanguage()]);
}
}

View file

@ -14,6 +14,10 @@ import MessageStore from 'Stores/User/Message';
import { showScreenPopup } from 'Knoin/Knoin';
import { AbstractViewLeft } from 'Knoin/AbstractViews';
import { ComposePopupView } from 'View/Popup/Compose';
import { FolderCreatePopupView } from 'View/Popup/FolderCreate';
import { ContactsPopupView } from 'View/Popup/Contacts';
const Settings = rl.settings;
class FolderListMailBoxUserView extends AbstractViewLeft {
@ -200,12 +204,12 @@ class FolderListMailBoxUserView extends AbstractViewLeft {
composeClick() {
if (Settings.capa(Capa.Composer)) {
showScreenPopup(require('View/Popup/Compose'));
showScreenPopup(ComposePopupView);
}
}
createFolder() {
showScreenPopup(require('View/Popup/FolderCreate'));
showScreenPopup(FolderCreatePopupView);
}
configureFolders() {
@ -214,7 +218,7 @@ class FolderListMailBoxUserView extends AbstractViewLeft {
contactsClick() {
if (this.allowContacts) {
showScreenPopup(require('View/Popup/Contacts'));
showScreenPopup(ContactsPopupView);
}
}
}

View file

@ -44,6 +44,10 @@ import Remote from 'Remote/User/Fetch';
import { command, showScreenPopup, popupVisibility } from 'Knoin/Knoin';
import { AbstractViewRight } from 'Knoin/AbstractViews';
import { FolderClearPopupView } from 'View/Popup/FolderClear';
import { ComposePopupView } from 'View/Popup/Compose';
import { AdvancedSearchPopupView } from 'View/Popup/AdvancedSearch';
const
Settings = rl.settings,
canBeMovedHelper = (self) => self.canBeMoved(),
@ -244,7 +248,7 @@ class MessageListMailBoxUserView extends AbstractViewRight {
@command()
clearCommand() {
if (Settings.capa(Capa.DangerousActions)) {
showScreenPopup(require('View/Popup/FolderClear'), [FolderStore.currentFolder()]);
showScreenPopup(FolderClearPopupView, [FolderStore.currentFolder()]);
}
}
@ -258,7 +262,7 @@ class MessageListMailBoxUserView extends AbstractViewRight {
@command(canBeMovedHelper)
multyForwardCommand() {
if (Settings.capa(Capa.Composer)) {
showScreenPopup(require('View/Popup/Compose'), [
showScreenPopup(ComposePopupView, [
ComposeType.ForwardAsAttachment,
MessageStore.messageListCheckedOrSelected()
]);
@ -350,7 +354,7 @@ class MessageListMailBoxUserView extends AbstractViewRight {
composeClick() {
if (Settings.capa(Capa.Composer)) {
showScreenPopup(require('View/Popup/Compose'));
showScreenPopup(ComposePopupView);
}
}
@ -761,7 +765,7 @@ class MessageListMailBoxUserView extends AbstractViewRight {
if (Settings.capa(Capa.Composer)) {
// write/compose (open compose popup)
shortcuts.add('w,c,new', '', [KeyState.MessageList, KeyState.MessageView], () => {
showScreenPopup(require('View/Popup/Compose'));
showScreenPopup(ComposePopupView);
return false;
});
}
@ -887,7 +891,7 @@ class MessageListMailBoxUserView extends AbstractViewRight {
advancedSearchClick() {
Settings.capa(Capa.SearchAdv)
&& showScreenPopup(require('View/Popup/AdvancedSearch'), [this.mainMessageListSearch()]);
&& showScreenPopup(AdvancedSearchPopupView, [this.mainMessageListSearch()]);
}
quotaTooltip() {

View file

@ -41,6 +41,8 @@ import Remote from 'Remote/User/Fetch';
import { command, showScreenPopup, createCommand } from 'Knoin/Knoin';
import { AbstractViewRight } from 'Knoin/AbstractViews';
import { ComposePopupView } from 'View/Popup/Compose';
const Settings = rl.settings;
function isTransparent(color) {
@ -342,7 +344,7 @@ class MessageViewMailBoxUserView extends AbstractViewRight {
* @returns {void}
*/
replyOrforward(sType) {
Settings.capa(Capa.Composer) && showScreenPopup(require('View/Popup/Compose'), [sType, MessageStore.message()]);
Settings.capa(Capa.Composer) && showScreenPopup(ComposePopupView, [sType, MessageStore.message()]);
}
checkHeaderHeight() {
@ -426,7 +428,7 @@ class MessageViewMailBoxUserView extends AbstractViewRight {
3 !== event.which &&
mailToHelper(
el.href,
Settings.capa(Capa.Composer) ? require('View/Popup/Compose') : null
Settings.capa(Capa.Composer) ? ComposePopupView : null
)
);
}
@ -684,13 +686,13 @@ class MessageViewMailBoxUserView extends AbstractViewRight {
composeClick() {
if (Settings.capa(Capa.Composer)) {
showScreenPopup(require('View/Popup/Compose'));
showScreenPopup(ComposePopupView);
}
}
editMessage() {
if (Settings.capa(Capa.Composer) && MessageStore.message()) {
showScreenPopup(require('View/Popup/Compose'), [ComposeType.Draft, MessageStore.message()]);
showScreenPopup(ComposePopupView, [ComposeType.Draft, MessageStore.message()]);
}
}

View file

@ -50,7 +50,6 @@
"@babel/runtime-corejs3": "7.8.7",
"babel-eslint": "10.1.0",
"babel-loader": "8.0.6",
"copy-webpack-plugin": "5.1.1",
"css-loader": "3.4.2",
"eslint": "6.8.0",
"eslint-config-prettier": "6.10.0",
@ -74,18 +73,18 @@
"gulp-size": "3.0.0",
"gulp-stripbom": "1.0.5",
"gulp-terser": "^1.4.0",
"gulp-through": "0.4.0",
"gulp-util": "3.0.8",
"gulp-zip": "5.0.1",
"node-fs": "0.1.7",
"openpgp": "2.6.2",
"prettier": "1.19.1",
"raw-loader": "4.0.0",
"rimraf": "3.0.2",
"style-loader": "1.1.3"
"rimraf": "3.0.2"
},
"dependencies": {
"webpack": "4.42.0",
"webpack-cli": "3.3.11"
"rollup": "^2.38.0",
"rollup-plugin-babel": "^4.4.0",
"rollup-plugin-external-globals": "^0.6.1",
"rollup-plugin-html": "^0.2.1",
"rollup-plugin-includepaths": "^0.2.4",
"rollup-plugin-terser": "^7.0.2"
}
}

View file

@ -7,6 +7,11 @@ if (!$gulp) {
exit('gulp not installed, run as root: npm install --global gulp-cli');
}
$rollup = trim(`which rollup`);
if (!$rollup) {
exit('rollup not installed, run as root: npm install --global rollup');
}
$options = getopt('', ['docker']);
$options['docker'] = isset($options['docker']);
@ -25,6 +30,11 @@ if ($return_var) {
exit("gulp failed with error code {$return_var}\n");
}
passthru("{$rollup} -c", $return_var);
if ($return_var) {
exit("rollup failed with error code {$return_var}\n");
}
$cmddir = escapeshellcmd(__DIR__) . '/snappymail/v/0.0.0/static';
if ($gzip = trim(`which gzip`)) {

95
rollup.config.js Normal file
View file

@ -0,0 +1,95 @@
/*
npm install rollup rollup-plugin-includepaths rollup-plugin-babel rollup-plugin-external-globals rollup-plugin-html rollup-plugin-terser
rollup -c
*/
import babel from 'rollup-plugin-babel';
import includePaths from 'rollup-plugin-includepaths';
import externalGlobals from "rollup-plugin-external-globals";
import html from 'rollup-plugin-html';
import { terser } from "rollup-plugin-terser";
let includePathOptions = {
include: {},
paths: ['dev'],
external: [],
extensions: ['.js', '.html']
};
let babelConfig = {
exclude: 'node_modules/**',
babelrc: false,
presets: [
[
'@babel/preset-env',
{
targets: {"chrome": "60"},
loose: true,
modules: false
}
]
],
plugins: [
[
'@babel/plugin-proposal-decorators',
{
legacy: true
}
],
'@babel/plugin-proposal-class-properties'
]
};
let terserConfig = {
output: {
comments: false
},
keep_classnames: true, // Required for AbstractModel and AbstractCollectionModel
compress:{
ecma: 6,
drop_console: true
/*
,hoist_props: false
,keep_fargs: false
,toplevel: true
,unsafe_arrows: true // Issue with knockoutjs
,unsafe_methods: true
,unsafe_proto: true
*/
}
// ,mangle: {reserved:['SendMessage']}
};
export default [{
external: ['ko'],
input: 'dev/admin.js',
// dest: 'snappymail/v/0.0.0/static/js/admin.rollup.js',
output: [
{file: 'snappymail/v/0.0.0/static/js/admin.js', format: 'iife'}, // format: 'es'
{file: 'snappymail/v/0.0.0/static/js/min/admin.min.js', format: 'iife', plugins: [terser(terserConfig)], }
],
plugins: [
babel(babelConfig),
includePaths(includePathOptions),
externalGlobals({
ko: 'ko'
})
],
}, {
external: ['ko'],
input: 'dev/app.js',
output: [
{file: 'snappymail/v/0.0.0/static/js/app.js', format: 'iife'},
{file: 'snappymail/v/0.0.0/static/js/min/app.min.js', format: 'iife', plugins: [terser(terserConfig)], }
],
plugins: [
babel(babelConfig),
includePaths(includePathOptions),
externalGlobals({
ko: 'ko'
}),
html({
include: '**/*.html'
})
],
}];

View file

@ -16,7 +16,7 @@ const concat = require('gulp-concat-util'),
const { config } = require('./config');
const { del, getHead } = require('./common');
const { webpack } = require('./webpack');
//const { rollupJS } = require('./rollup');
const jsClean = () => del(config.paths.staticJS + '/**/*.{js,map}');
@ -24,14 +24,28 @@ const jsClean = () => del(config.paths.staticJS + '/**/*.{js,map}');
const jsBoot = () => {
return gulp
.src('dev/boot.js')
.pipe(gulp.dest('snappymail/v/' + config.devVersion + '/static/js'));
.pipe(gulp.dest(config.paths.staticJS));
};
// ServiceWorker
const jsServiceWorker = () => {
return gulp
.src('dev/serviceworker.js')
.pipe(gulp.dest('snappymail/v/' + config.devVersion + '/static/js'));
.pipe(gulp.dest(config.paths.staticJS));
};
// OpenPGP
const jsOpenPGP = () => {
return gulp
.src('node_modules/openpgp/dist/openpgp.min.js')
.pipe(gulp.dest(config.paths.staticMinJS));
};
// OpenPGP Worker
const jsOpenPGPWorker = () => {
return gulp
.src('node_modules/openpgp/dist/openpgp.worker.min.js')
.pipe(gulp.dest(config.paths.staticMinJS));
};
// libs
@ -114,8 +128,8 @@ const jsLint = () =>
.pipe(eslint.failAfterError());
const jsState1 = gulp.series(jsLint);
const jsState3 = gulp.parallel(jsBoot, jsServiceWorker, jsLibs, jsApp, jsAdmin);
const jsState2 = gulp.series(jsClean, webpack, jsState3, jsMin);
const jsState3 = gulp.parallel(jsBoot, jsServiceWorker, jsOpenPGP, jsOpenPGPWorker, jsLibs/*, jsApp, jsAdmin*/);
const jsState2 = gulp.series(jsClean/*, rollupJS('app.js'), rollupJS('admin.js')*/, jsState3, jsMin);
exports.jsLint = jsLint;
exports.js = gulp.parallel(jsState1, jsState2);

View file

@ -1,30 +0,0 @@
/* RainLoop Webmail (c) RainLoop Team | Licensed under AGPL 3 */
const webpack = require('webpack');
const gutil = require('gulp-util');
const { config } = require('./config');
const webpackCfgBuilder = require('../webpack.config.builder.js');
const webpackError = (err) => {
if (err) {
gutil.log('[webpack]', '---');
gutil.log('[webpack]', err.error ? err.error.toString() : '');
gutil.log('[webpack]', err.message || '');
gutil.log('[webpack]', '---');
}
};
const webpackCallback = (done) => (err, stats) => {
if (err) {
throw new gutil.PluginError('webpack', err);
} else if (stats && stats.compilation && stats.compilation.errors && stats.compilation.errors[0]) {
throw new gutil.PluginError('webpack', stats.compilation.errors[0]);
}
done();
};
exports.webpack = (done) => {
webpack(webpackCfgBuilder(config.paths.staticJS, 'production'), webpackCallback(done));
};

View file

@ -1,134 +0,0 @@
const path = require('path');
const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const devPath = path.resolve(__dirname, 'dev');
const devPathJoin = path.join(__dirname, 'dev');
const externalPathJoin = path.join(__dirname, 'dev', 'External');
const loose = true;
//npm install closure-webpack-plugin google-closure-compiler
//const ClosurePlugin = require('closure-webpack-plugin');
const babelLoaderOptions = function() {
return {
ignore: [/\/core-js/],
cacheDirectory: true,
overrides: [
{
test: './node_modules/',
sourceType: 'unambiguous'
}
],
presets: [
[
'@babel/preset-env',
{
targets: {"chrome": "60"},
// useBuiltIns: 'usage',
// corejs: { version: 3, proposals: true },
loose: loose,
modules: false
}
]
],
plugins: [
[
'@babel/plugin-proposal-decorators',
{
legacy: true
}
],
'@babel/plugin-proposal-class-properties'
]
};
};
process.noDeprecation = true;
module.exports = function(publicPath, mode) {
return {
// mode: 'production',
mode: mode || 'development',
devtool: 'inline-source-map',
entry: {
'js/app': path.join(devPathJoin, 'app.js'),
'js/admin': path.join(devPathJoin, 'admin.js')
},
output: {
pathinfo: true,
path: path.join(__dirname, 'snappymail', 'v', '0.0.0', 'static'),
filename: '[name].js',
publicPath: publicPath || 'snappymail/v/0.0.0/static/'
},
performance: {
hints: false
},
optimization: {
concatenateModules: false,
minimize: false
/*
,minimizer: [
new ClosurePlugin({mode: 'STANDARD'}, {
language_in:'ECMASCRIPT6',
language_out:'ECMASCRIPT6',
compilation_level: 'ADVANCED_OPTIMIZATIONS'
// compiler flags here
//
// for debugging help, try these:
//
// formatting: 'PRETTY_PRINT'
// debug: true,
// renaming: false
})
]
*/
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
new webpack.DefinePlugin({}),
new CopyWebpackPlugin([
{ from: 'node_modules/openpgp/dist/openpgp.min.js', to: 'js/min/openpgp.min.js' },
{ from: 'node_modules/openpgp/dist/openpgp.worker.min.js', to: 'js/min/openpgp.worker.min.js' }
])
],
resolve: {
modules: [devPath, 'node_modules'],
extensions: ['.js'],
alias: {
'ko$': path.join(externalPathJoin, 'ko.js')
}
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
include: [devPath],
options: babelLoaderOptions()
},
{
test: /\.html$/,
loader: 'raw-loader',
include: [devPath]
},
{
test: /\.css/,
loaders: ['style-loader', 'css-loader'],
include: [devPath]
},
{
test: /\.json$/,
loader: 'json-loader',
include: [devPath]
}
]
},
externals: {
}
};
};

View file

@ -1 +0,0 @@
module.exports = require('./webpack.config.builder')();