Verified Commit 5c049ace authored by Sandro Lutz's avatar Sandro Lutz
Browse files

Add dynamic imports (performance boost)

parent f9033f63
...@@ -7,11 +7,17 @@ module.exports = { ...@@ -7,11 +7,17 @@ module.exports = {
browser: true, browser: true,
node: true, node: true,
}, },
parser: 'babel-eslint',
parserOptions: {
ecmaVersion: 6,
sourceType: 'module',
},
rules: { rules: {
'no-multi-str': 0, 'no-multi-str': 0,
'no-underscore-dangle': 0, 'no-underscore-dangle': 0,
'no-console': 0, 'no-console': 0,
'import/prefer-default-export': 0, 'import/prefer-default-export': 0,
'import/no-extraneous-dependencies': ['warning', {'peerDependencies': true}],
'camelcase': 0, 'camelcase': 0,
'prettier/prettier': 'error', 'prettier/prettier': 'error',
}, },
......
This diff is collapsed.
import m from 'mithril'; import m from 'mithril';
import { Button } from 'polythene-mithril'; import { Button } from 'polythene-mithril-button';
import { ButtonCSS } from 'polythene-css'; import { ButtonCSS } from 'polythene-css';
ButtonCSS.addStyle('.blue-button', { ButtonCSS.addStyle('.blue-button', {
......
import m from 'mithril'; import m from 'mithril';
import { Checkbox } from 'polythene-mithril'; import { Checkbox } from 'polythene-mithril-checkbox';
/** /**
* Generic Checkbox component * Generic Checkbox component
......
import m from 'mithril'; import m from 'mithril';
import { apiUrl } from 'config'; import { apiUrl } from 'config';
import { Card } from 'polythene-mithril'; import { Card } from 'polythene-mithril-card';
import { Spinner } from 'amiv-web-ui-components'; import Spinner from 'amiv-web-ui-components/src/spinner';
export default class EventCard { export default class EventCard {
/** /**
......
import m from 'mithril'; import m from 'mithril';
import { Search, Shadow, IconButton } from 'polythene-mithril'; import { Search } from 'polythene-mithril-search';
import { RadioGroup } from 'amiv-web-ui-components'; import { Shadow } from 'polythene-mithril-shadow';
import { IconButton } from 'polythene-mithril-icon-button';
import RadioGroup from 'amiv-web-ui-components/src/radioGroup';
import debounce from 'amiv-web-ui-components/src/debounce'; import debounce from 'amiv-web-ui-components/src/debounce';
import icons from 'amiv-web-ui-components/src/icons'; import icons from 'amiv-web-ui-components/src/icons';
import Button from './Button'; import Button from './Button';
......
import m from 'mithril'; import m from 'mithril';
import { Tabs } from 'polythene-mithril'; import { Tabs } from 'polythene-mithril-tabs';
/** /**
* Generic Tab component * Generic Tab component
......
import m from 'mithril'; import m from 'mithril';
import { TextField } from 'polythene-mithril'; import { TextField } from 'polythene-mithril-textfield';
/** /**
* Generic TextField component * Generic TextField component
......
// src/index.js // src/index.js
import m from 'mithril'; import m from 'mithril';
import Raven from 'raven-js'; import Raven from 'raven-js';
import { Button } from 'polythene-mithril-button';
import { sentryUrl, sentryEnvironment } from 'config'; import { sentryUrl, sentryEnvironment } from 'config';
import { loadLanguage, currentLanguage, isLanguageValid } from './models/language'; import Spinner from 'amiv-web-ui-components/src/spinner';
import { loadLanguage, currentLanguage, isLanguageValid, i18n } from './models/language';
import { Error404, Error401 } from './views/errors'; import { Error404, Error401 } from './views/errors';
import { isLoggedIn, checkLogin } from './models/auth'; import { isLoggedIn, checkLogin } from './models/auth';
import studydocList from './views/studydocs/studydocList';
import studydocNew from './views/studydocs/studydocNew';
import eventList from './views/events/eventList';
import profile from './views/profile';
import layout from './views/layout'; import layout from './views/layout';
import frontpage from './views/frontpage'; import frontpage from './views/frontpage';
import logout from './views/logout';
import about from './views/amiv/about'; import about from './views/amiv/about';
import board from './views/amiv/board'; import logout from './views/logout';
import teams from './views/amiv/teams';
import jobOfferList from './views/jobs/jobofferList';
import legalNotice from './views/legalNotice'; import legalNotice from './views/legalNotice';
import './styles/base.less'; import './styles/base.less';
...@@ -44,21 +39,21 @@ Raven.context(() => { ...@@ -44,21 +39,21 @@ Raven.context(() => {
{ {
url: '/:language/studydocuments', url: '/:language/studydocuments',
reason: 'studydocs.accessDenied', reason: 'studydocs.accessDenied',
view: () => m(studydocList), viewAsync: './views/studydocs/studydocList',
}, },
{ {
url: '/:language/studydocuments/new', url: '/:language/studydocuments/new',
reason: 'studydocs.accessDenied', reason: 'studydocs.accessDenied',
view: () => m(studydocNew), viewAsync: './views/studydocs/studydocNew',
}, },
{ {
url: '/:language/studydocuments/:documentId', url: '/:language/studydocuments/:documentId',
reason: 'studydocs.accessDenied', reason: 'studydocs.accessDenied',
view: vnode => m(studydocList, vnode.attrs), viewAsync: './views/studydocs/studydocList',
}, },
{ {
url: '/:language/profile', url: '/:language/profile',
view: vnode => m(profile, vnode.attrs), viewAsync: './views/profile',
}, },
]; ];
...@@ -70,11 +65,11 @@ Raven.context(() => { ...@@ -70,11 +65,11 @@ Raven.context(() => {
}, },
{ {
url: '/:language/board', url: '/:language/board',
view: () => m(board), viewAsync: './views/amiv/board',
}, },
{ {
url: '/:language/teams', url: '/:language/teams',
view: () => m(teams), viewAsync: './views/amiv/teams',
}, },
{ {
url: '/:language/about', url: '/:language/about',
...@@ -86,19 +81,19 @@ Raven.context(() => { ...@@ -86,19 +81,19 @@ Raven.context(() => {
}, },
{ {
url: '/:language/events', url: '/:language/events',
view: () => m(eventList), viewAsync: './views/events/eventList',
}, },
{ {
url: '/:language/events/:eventId', url: '/:language/events/:eventId',
view: vnode => m(eventList, vnode.attrs), viewAsync: './views/events/eventList',
}, },
{ {
url: '/:language/jobs', url: '/:language/jobs',
view: () => m(jobOfferList), viewAsync: './views/jobs/jobofferList',
}, },
{ {
url: '/:language/jobs/:offerId', url: '/:language/jobs/:offerId',
view: vnode => m(jobOfferList, vnode.attrs), viewAsync: './views/jobs/jobofferList',
}, },
{ {
url: '/:language/legal-notice', url: '/:language/legal-notice',
...@@ -108,7 +103,45 @@ Raven.context(() => { ...@@ -108,7 +103,45 @@ Raven.context(() => {
function onmatch(args, route) { function onmatch(args, route) {
if (isLanguageValid(args.language)) { if (isLanguageValid(args.language)) {
return { view: vnode => m(layout, route.view(vnode)) }; if (route.view) {
return { view: vnode => m(layout, route.view(vnode)) };
}
return {
oninit() {
this.error = false;
import(/* webpackInclude: /\.js$/ */ `${route.viewAsync}`)
.then(loadedModule => {
this.loadedModule = loadedModule;
m.redraw();
})
.catch(() => {
this.error = true;
});
},
view(vnode) {
if (this.loadedModule) {
return m(layout, m(this.loadedModule.default, vnode.attrs));
}
if (this.error) {
return m(
layout,
m('.error', [
m('p', i18n('error.loadingPage')),
m(Button, {
className: 'blue-button',
name: 'retry',
label: i18n('retry'),
events: {
onclick: () => window.location.reload(),
},
}),
])
);
}
return m(layout, m('.loading', m(Spinner, { show: true, size: '96px' })));
},
};
} }
return { return {
view() { view() {
......
...@@ -251,5 +251,6 @@ export default { ...@@ -251,5 +251,6 @@ export default {
notFound: 'Die gewünschte Seite konnte nicht gefunden werden.', notFound: 'Die gewünschte Seite konnte nicht gefunden werden.',
translationUnavailable: 'Übersetzung nicht verfügbar.', translationUnavailable: 'Übersetzung nicht verfügbar.',
shownLanguage: 'Zeige Text in {{shown_language}}.', shownLanguage: 'Zeige Text in {{shown_language}}.',
loadingPage: 'Die gewünschte Seite konnte nicht geladen werden.',
}, },
}; };
...@@ -251,5 +251,6 @@ export default { ...@@ -251,5 +251,6 @@ export default {
notFound: 'The page you are looking for could not be found.', notFound: 'The page you are looking for could not be found.',
translationUnavailable: 'Translation not available.', translationUnavailable: 'Translation not available.',
shownLanguage: 'Showing text in {{shown_language}}.', shownLanguage: 'Showing text in {{shown_language}}.',
loadingPage: 'Could not load the requested page.',
}, },
}; };
...@@ -67,6 +67,7 @@ section { ...@@ -67,6 +67,7 @@ section {
} }
main { main {
position: relative;
width: 100%; width: 100%;
> * { > * {
...@@ -74,6 +75,23 @@ main { ...@@ -74,6 +75,23 @@ main {
padding: 0 1em; padding: 0 1em;
margin: 0 auto; margin: 0 auto;
} }
> .loading {
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
}
> .error {
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
text-align: center;
font-size: 2em;
color: rgba(0, 0, 0, .54);
}
} }
h1 { h1 {
......
...@@ -64,3 +64,16 @@ ...@@ -64,3 +64,16 @@
margin-right: 1em; margin-right: 1em;
} }
} }
.event-loading {
position: relative;
width: 100%;
height: 96px;
> * {
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
}
}
import m from 'mithril'; import m from 'mithril';
import { currentLanguage } from '../../models/language'; import { Button } from 'polythene-mithril-button';
import Spinner from 'amiv-web-ui-components/src/spinner';
import { currentLanguage, i18n } from '../../models/language';
import contentUrlEnglish from '../../content/amiv/markdown/about.en.md';
import contentUrlGerman from '../../content/amiv/markdown/about.de.md';
// ensure that all markdown files are compiled export default class Amiv {
require.context('../../content/amiv/markdown');
export default class AMIV {
oninit() { oninit() {
this.content = ''; this.content = null;
this._load(); this.loaded = false;
this.constructor
._load()
.then(response => {
this.content = response;
})
.catch(() => {
this.error = true;
});
} }
view() { view() {
return m('div#about', m.trust(this.content)); if (this.content) {
} return m('div#about', m.trust(this.content));
}
async _load() { if (this.error) {
if (this.content) return; return m('.error', [
m('p', i18n('error.loadingPage')),
m(Button, {
className: 'blue-button',
name: 'retry',
label: i18n('retry'),
events: {
onclick: () => window.location.reload(),
},
}),
]);
}
return m('.loading', m(Spinner, { show: true, size: '96px' }));
}
this.content = await m.request({ static _load() {
url: `/amiv/about.${currentLanguage()}.html`, return m.request({
url: `/${currentLanguage === 'de' ? contentUrlGerman : contentUrlEnglish}`,
method: 'GET', method: 'GET',
deserialize: response => response, deserialize: response => response,
}); });
......
import m from 'mithril'; import m from 'mithril';
import marked from 'marked'; import marked from 'marked';
import escape from 'html-escape'; import escape from 'html-escape';
import { Tabs } from '../../components'; import Tabs from '../../components/Tabs';
import { boardPortraits, boardImage } from '../../content/amiv/data/board_portraits'; import { boardPortraits, boardImage } from '../../content/amiv/data/board_portraits';
import { boardTaskDescriptions } from '../../content/amiv/data/board_roles'; import { boardTaskDescriptions } from '../../content/amiv/data/board_roles';
import { i18n, currentLanguage } from '../../models/language'; import { i18n, currentLanguage } from '../../models/language';
...@@ -49,7 +49,7 @@ class ImageGroup { ...@@ -49,7 +49,7 @@ class ImageGroup {
activeSelected: true, activeSelected: true,
autofit: true, autofit: true,
element: 'tab', element: 'tab',
selectedTab: this._selectedTabIndex, selectedTabIndex: this._selectedTabIndex,
onChange: data => { onChange: data => {
this._selectedTabIndex = data.index; this._selectedTabIndex = data.index;
}, },
......
import m from 'mithril'; import m from 'mithril';
import marked from 'marked'; import marked from 'marked';
import escape from 'html-escape'; import escape from 'html-escape';
import { Card, IconButton, Icon } from 'polythene-mithril'; import { Card } from 'polythene-mithril-card';
import { IconButton } from 'polythene-mithril-icon-button';
import { Icon } from 'polythene-mithril-icon';
import logos from '../../images/logos'; import logos from '../../images/logos';
import { data as data_ressorts } from '../../content/amiv/data/ressorts'; import { data as data_ressorts } from '../../content/amiv/data/ressorts';
import { data as data_commissions } from '../../content/amiv/data/commissions'; import { data as data_commissions } from '../../content/amiv/data/commissions';
......
...@@ -2,7 +2,7 @@ import m from 'mithril'; ...@@ -2,7 +2,7 @@ import m from 'mithril';
import { Icon } from 'polythene-mithril'; import { Icon } from 'polythene-mithril';
import { i18n } from '../models/language'; import { i18n } from '../models/language';
import { login } from '../models/auth'; import { login } from '../models/auth';
import { Button } from '../components'; import Button from '../components/Button';
import icons from '../images/icons'; import icons from '../images/icons';
/** /**
......
...@@ -2,11 +2,13 @@ import m from 'mithril'; ...@@ -2,11 +2,13 @@ import m from 'mithril';
import { Icon } from 'polythene-mithril'; import { Icon } from 'polythene-mithril';
import marked from 'marked'; import marked from 'marked';
import escape from 'html-escape'; import escape from 'html-escape';
import { Form, TextInput, Spinner } from 'amiv-web-ui-components'; import Form from 'amiv-web-ui-components/src/form';
import { TextInput } from 'amiv-web-ui-components/src/inputFields';
import Spinner from 'amiv-web-ui-components/src/spinner';
import { Infobox } from '../errors'; import { Infobox } from '../errors';
import { log } from '../../models/log'; import { log } from '../../models/log';
import { isLoggedIn, login } from '../../models/auth'; import { isLoggedIn, login } from '../../models/auth';
import { Button } from '../../components'; import Button from '../../components/Button';
import { i18n, currentLocale } from '../../models/language'; import { i18n, currentLocale } from '../../models/language';
import icons from '../../images/icons'; import icons from '../../images/icons';
......
import m from 'mithril'; import m from 'mithril';
import { Icon } from 'polythene-mithril';
import { apiUrl } from 'config'; import { apiUrl } from 'config';
import { ExpansionPanel } from 'amiv-web-ui-components'; import ExpansionPanel from 'amiv-web-ui-components/src/expansionPanel';
import Spinner from 'amiv-web-ui-components/src/spinner';
import logos from '../../images/logos'; import logos from '../../images/logos';
import { i18n, currentLocale } from '../../models/language'; import { i18n, currentLocale } from '../../models/language';
import { EventController } from '../../models/events'; import { EventController } from '../../models/events';
import { FilteredListPage, FilteredListDataStore } from '../filteredListPage'; import { FilteredListPage, FilteredListDataStore } from '../filteredListPage';
import EventDetails from './eventDetails';
const controller = new EventController({}, true); const controller = new EventController({}, true);
const dataStore = new FilteredListDataStore(); const dataStore = new FilteredListDataStore();
let eventDetailsModule;
/** /**
* EventList class * EventList class
* *
...@@ -204,7 +205,17 @@ export default class EventList extends FilteredListPage { ...@@ -204,7 +205,17 @@ export default class EventList extends FilteredListPage {
]), ]),
content: ({ expanded }) => { content: ({ expanded }) => {
if (expanded) { if (expanded) {
return m(EventDetails, { event }); if (eventDetailsModule) {
return m(eventDetailsModule.default, { event });
}
import(/* webpackInclude: /\.js$/ */ /* webpackChunkName: "event" */ './eventDetails').then(
loadedModule => {
eventDetailsModule = loadedModule;
m.redraw();
}
);
return m('.event-loading', m(Spinner, { show: true, size: '48px' }));
} }
return m(''); return m('');
}, },
......