diff --git a/dev/App/Abstract.js b/dev/App/Abstract.js index 8857004e7..24720b890 100644 --- a/dev/App/Abstract.js +++ b/dev/App/Abstract.js @@ -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(); diff --git a/dev/App/User.js b/dev/App/User.js index 6d05c092f..592e142ea 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -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(); diff --git a/dev/Common/HtmlEditor.js b/dev/Common/Html.js similarity index 93% rename from dev/Common/HtmlEditor.js rename to dev/Common/Html.js index b52cddb73..8e9b36018 100644 --- a/dev/Common/HtmlEditor.js +++ b/dev/Common/Html.js @@ -1,5 +1,23 @@ import { createCKEditor } from 'External/CKEditor.js'; +const + htmlre = /[&<>"']/g, + htmlmap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + +/** + * @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; diff --git a/dev/Common/Plugins.js b/dev/Common/Plugins.js index e651a7fef..1059d9c60 100644 --- a/dev/Common/Plugins.js +++ b/dev/Common/Plugins.js @@ -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]); }); } diff --git a/dev/Common/Translator.js b/dev/Common/Translator.js index 1bfad2a3f..c08a59606 100644 --- a/dev/Common/Translator.js +++ b/dev/Common/Translator.js @@ -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 + ); +} diff --git a/dev/Common/Utils.js b/dev/Common/Utils.js index e534915a1..7cff2a83e 100644 --- a/dev/Common/Utils.js +++ b/dev/Common/Utils.js @@ -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 diff --git a/dev/Common/UtilsUser.js b/dev/Common/UtilsUser.js index f4b180150..6f9dacc1e 100644 --- a/dev/Common/UtilsUser.js +++ b/dev/Common/UtilsUser.js @@ -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 = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' - }, - 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, '
') .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, diff --git a/dev/External/Admin/ko.js b/dev/External/Admin/ko.js index d49342146..b6c595a11 100644 --- a/dev/External/Admin/ko.js +++ b/dev/External/Admin/ko.js @@ -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; } diff --git a/dev/External/User/ko.js b/dev/External/User/ko.js index 6b7de9e31..7664daf77 100644 --- a/dev/External/User/ko.js +++ b/dev/External/User/ko.js @@ -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']; diff --git a/dev/External/ko.js b/dev/External/ko.js index 2e9b1aea6..773141ccc 100644 --- a/dev/External/ko.js +++ b/dev/External/ko.js @@ -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); } }; diff --git a/dev/Knoin/Knoin.js b/dev/Knoin/Knoin.js index edb6fd240..3be527b3a 100644 --- a/dev/Knoin/Knoin.js +++ b/dev/Knoin/Knoin.js @@ -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, diff --git a/dev/Model/Email.js b/dev/Model/Email.js index 22960beb3..71fb88369 100644 --- a/dev/Model/Email.js +++ b/dev/Model/Email.js @@ -1,4 +1,4 @@ -import { encodeHtml } from 'Common/UtilsUser'; +import { encodeHtml } from 'Common/Html'; import { AbstractModel } from 'Knoin/AbstractModel'; diff --git a/dev/Model/Folder.js b/dev/Model/Folder.js deleted file mode 100644 index 5ee3e9d79..000000000 --- a/dev/Model/Folder.js +++ /dev/null @@ -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(' / '); - } -} diff --git a/dev/Model/FolderCollection.js b/dev/Model/FolderCollection.js index 912e0069b..e1b293360 100644 --- a/dev/Model/FolderCollection.js +++ b/dev/Model/FolderCollection.js @@ -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(' / '); + } +} diff --git a/dev/Model/Message.js b/dev/Model/Message.js index c42e66a26..aff189b75 100644 --- a/dev/Model/Message.js +++ b/dev/Model/Message.js @@ -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))) diff --git a/dev/Screen/Admin/Settings.js b/dev/Screen/Admin/Settings.js index 4b5371ea8..bb6d75fd7 100644 --- a/dev/Screen/Admin/Settings.js +++ b/dev/Screen/Admin/Settings.js @@ -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', diff --git a/dev/Screen/User/MailBox.js b/dev/Screen/User/MailBox.js index 1762d940f..192e589d2 100644 --- a/dev/Screen/User/MailBox.js +++ b/dev/Screen/User/MailBox.js @@ -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); diff --git a/dev/Screen/User/Settings.js b/dev/Screen/User/Settings.js index c5c23c1c9..367814bcb 100644 --- a/dev/Screen/User/Settings.js +++ b/dev/Screen/User/Settings.js @@ -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); diff --git a/dev/Settings/Admin/Domains.js b/dev/Settings/Admin/Domains.js index e0df0ac41..e99c67b11 100644 --- a/dev/Settings/Admin/Domains.js +++ b/dev/Settings/Admin/Domains.js @@ -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]); } } diff --git a/dev/Settings/Admin/General.js b/dev/Settings/Admin/General.js index 9a308014f..30f10e4dc 100644 --- a/dev/Settings/Admin/General.js +++ b/dev/Settings/Admin/General.js @@ -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() diff --git a/dev/Settings/Admin/Plugins.js b/dev/Settings/Admin/Plugins.js index 23970c1d0..deb382290 100644 --- a/dev/Settings/Admin/Plugins.js +++ b/dev/Settings/Admin/Plugins.js @@ -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]); } } diff --git a/dev/Settings/User/Accounts.js b/dev/Settings/User/Accounts.js index 648cedbac..f8b12975e 100644 --- a/dev/Settings/User/Accounts.js +++ b/dev/Settings/User/Accounts.js @@ -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]); } /** diff --git a/dev/Settings/User/Filters.js b/dev/Settings/User/Filters.js index 31d94b53b..ff79e3d3b 100644 --- a/dev/Settings/User/Filters.js +++ b/dev/Settings/User/Filters.js @@ -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) { diff --git a/dev/Settings/User/Folders.js b/dev/Settings/User/Folders.js index 6a17781c2..b10c160ad 100644 --- a/dev/Settings/User/Folders.js +++ b/dev/Settings/User/Folders.js @@ -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) { diff --git a/dev/Settings/User/General.js b/dev/Settings/User/General.js index 6d4987b02..f828eb311 100644 --- a/dev/Settings/User/General.js +++ b/dev/Settings/User/General.js @@ -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()]); } } diff --git a/dev/Settings/User/OpenPgp.js b/dev/Settings/User/OpenPgp.js index 0c1a0f621..5ac68d0e5 100644 --- a/dev/Settings/User/OpenPgp.js +++ b/dev/Settings/User/OpenPgp.js @@ -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]); } } diff --git a/dev/Settings/User/Security.js b/dev/Settings/User/Security.js index 567b8356b..f64ca6604 100644 --- a/dev/Settings/User/Security.js +++ b/dev/Settings/User/Security.js @@ -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() { diff --git a/dev/Settings/User/Templates.js b/dev/Settings/User/Templates.js index 2b3ebde49..534ec3a56 100644 --- a/dev/Settings/User/Templates.js +++ b/dev/Settings/User/Templates.js @@ -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]); } } diff --git a/dev/Stores/User/Pgp.js b/dev/Stores/User/Pgp.js index 08d2f7fdf..be60a5546 100644 --- a/dev/Stores/User/Pgp.js +++ b/dev/Stores/User/Pgp.js @@ -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( diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index c4d9b1457..a85c4f925 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -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()) { diff --git a/dev/View/Popup/Contacts.js b/dev/View/Popup/Contacts.js index fa2167a36..3e3a6df3a 100644 --- a/dev/View/Popup/Contacts.js +++ b/dev/View/Popup/Contacts.js @@ -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(); } } } diff --git a/dev/View/Popup/FolderCreate.js b/dev/View/Popup/FolderCreate.js index 083369287..abc878828 100644 --- a/dev/View/Popup/FolderCreate.js +++ b/dev/View/Popup/FolderCreate.js @@ -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 }; diff --git a/dev/View/Popup/Languages.js b/dev/View/Popup/Languages.js index 08eb0fd63..2f42af61f 100644 --- a/dev/View/Popup/Languages.js +++ b/dev/View/Popup/Languages.js @@ -1,6 +1,6 @@ import ko from 'ko'; -import { convertLangName } from 'Common/Utils'; +import { convertLangName } from 'Common/Translator'; import { AbstractViewPopup } from 'Knoin/AbstractViews'; diff --git a/dev/View/Popup/Plugin.js b/dev/View/Popup/Plugin.js index 59e9e1fa7..f242c75fc 100644 --- a/dev/View/Popup/Plugin.js +++ b/dev/View/Popup/Plugin.js @@ -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() ]); diff --git a/dev/View/Popup/SieveScript.js b/dev/View/Popup/SieveScript.js index 257bf3b04..7bbf91a7d 100644 --- a/dev/View/Popup/SieveScript.js +++ b/dev/View/Popup/SieveScript.js @@ -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(), diff --git a/dev/View/Popup/Template.js b/dev/View/Popup/Template.js index b167537eb..d8693b2f8 100644 --- a/dev/View/Popup/Template.js +++ b/dev/View/Popup/Template.js @@ -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'; diff --git a/dev/View/Popup/TwoFactorConfiguration.js b/dev/View/Popup/TwoFactorConfiguration.js index cb020936d..1497f0fcc 100644 --- a/dev/View/Popup/TwoFactorConfiguration.js +++ b/dev/View/Popup/TwoFactorConfiguration.js @@ -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() { diff --git a/dev/View/User/AbstractSystemDropDown.js b/dev/View/User/AbstractSystemDropDown.js index 7fcfaefaf..1bdd49969 100644 --- a/dev/View/User/AbstractSystemDropDown.js +++ b/dev/View/User/AbstractSystemDropDown.js @@ -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; diff --git a/dev/View/User/Login.js b/dev/View/User/Login.js index 01d29ae52..ea1a290df 100644 --- a/dev/View/User/Login.js +++ b/dev/View/User/Login.js @@ -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()]); } } diff --git a/dev/View/User/MailBox/FolderList.js b/dev/View/User/MailBox/FolderList.js index 50be553c2..01a19f418 100644 --- a/dev/View/User/MailBox/FolderList.js +++ b/dev/View/User/MailBox/FolderList.js @@ -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); } } } diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index 98efe0abd..fddf5f425 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -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() { diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 8c3877e16..1c4e2a3e1 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -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()]); } } diff --git a/package.json b/package.json index 53ff996de..e66c9ace4 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/release.php b/release.php index 2921b1994..6027f0db6 100755 --- a/release.php +++ b/release.php @@ -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`)) { diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 000000000..3309a26a4 --- /dev/null +++ b/rollup.config.js @@ -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' + }) + ], +}]; diff --git a/tasks/js.js b/tasks/js.js index bcf516ff5..363b28707 100644 --- a/tasks/js.js +++ b/tasks/js.js @@ -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); diff --git a/tasks/webpack.js b/tasks/webpack.js deleted file mode 100644 index f2cb8e884..000000000 --- a/tasks/webpack.js +++ /dev/null @@ -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)); -}; diff --git a/webpack.config.builder.js b/webpack.config.builder.js deleted file mode 100644 index 3193abda3..000000000 --- a/webpack.config.builder.js +++ /dev/null @@ -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: { - } - }; -}; diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 81d4a386c..000000000 --- a/webpack.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./webpack.config.builder')();