Restructure app initiation
- Split initialization steps into their own files. - Use Choo routes instead of viewManager to properly set up 404 - Add styling for different colors of Picnic cards
This commit is contained in:
parent
49ab635660
commit
b093595f1d
|
@ -0,0 +1,27 @@
|
|||
export const appListeners = (app, state, emitter) => {
|
||||
emitter.on('DOMContentLoaded', () => {
|
||||
document.title = app.siteConfig.siteName;
|
||||
// Emitter listeners
|
||||
emitter.on('render', callback => {
|
||||
app.setSessionState();
|
||||
// This is a dirty hack to get the callback to call *after* re-rendering.
|
||||
if (callback && typeof callback === "function") {
|
||||
setTimeout(() => {
|
||||
callback();
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
|
||||
emitter.on('change-view', newView => {
|
||||
// Change the view and call render. Makes it easier to call within views.
|
||||
state.currentView = newView;
|
||||
emitter.emit('render', () => { });
|
||||
});
|
||||
|
||||
emitter.on('set-language', newLanguage => {
|
||||
app.setSettingsItem('lang', newLanguage);
|
||||
state.language = newLanguage;
|
||||
emitter.emit('render', () => { });
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import { I18n } from './i18n';
|
||||
import { globalView } from './views/global';
|
||||
import { homeView } from './views/home';
|
||||
import { loginView } from './views/login';
|
||||
import { searchView } from './views/search';
|
||||
import { errorView } from './views/404';
|
||||
|
||||
export const appRoutes = (app) => {
|
||||
const i18n = new I18n(app.state); // Global I18n class passed to all views
|
||||
|
||||
app.route('/', (state, emit) => globalView(state, emit, i18n, homeView));
|
||||
|
||||
app.route('/login', (state, emit) => globalView(state, emit, i18n, loginView));
|
||||
|
||||
app.route('/search', (state, emit) => globalView(state, emit, i18n, searchView));
|
||||
|
||||
app.route('/404', (state, emit) => globalView(state, emit, i18n, errorView));
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
export const appState = (app, state, emitter) => {
|
||||
const sessionState = app.getSessionState();
|
||||
if (sessionState) {
|
||||
Object.keys(sessionState).forEach(key => {
|
||||
if (typeof state[key] === 'undefined') {
|
||||
state[key] = sessionState[key];
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Default state variables
|
||||
state.currentView = 'home';
|
||||
state.language = app.getSettingsItem('lang') ? app.getSettingsItem('lang') : (navigator.language || navigator.userLanguage).split('-')[0];
|
||||
state.viewStates = {};
|
||||
state.isLoggedIn = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
export const appUtilities = (app) => {
|
||||
app.getSettingsItem = settingsKey => {
|
||||
let savedSettings = window.localStorage.getItem('settings');
|
||||
if (savedSettings) {
|
||||
savedSettings = JSON.parse(savedSettings);
|
||||
if (typeof savedSettings[settingsKey] !== 'undefined') {
|
||||
return savedSettings[settingsKey];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
app.setSettingsItem = (settingsKey, value) => {
|
||||
let savedSettings = window.localStorage.getItem('settings');
|
||||
if (savedSettings) {
|
||||
savedSettings = JSON.parse(savedSettings);
|
||||
} else {
|
||||
savedSettings = {};
|
||||
}
|
||||
savedSettings[settingsKey] = value;
|
||||
return window.localStorage.setItem('settings', JSON.stringify(savedSettings));
|
||||
}
|
||||
app.getSessionState = () => {
|
||||
let sessionState = window.sessionStorage.getItem('sessionState');
|
||||
if (sessionState) {
|
||||
return JSON.parse(sessionState);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
app.setSessionState = () => {
|
||||
return window.sessionStorage.setItem('sessionState', JSON.stringify(app.state));
|
||||
}
|
||||
}
|
|
@ -18,6 +18,10 @@
|
|||
"logged_out_recent_updates": "Recent Updates",
|
||||
"logged_out_join_now": "Join Now!"
|
||||
},
|
||||
"404": {
|
||||
"header": "Oops!",
|
||||
"subheader": "It looks like the page you requested doesn't exist. Please try a different one!"
|
||||
},
|
||||
"login": {
|
||||
"log_in": "Log In",
|
||||
"email": "Email",
|
||||
|
|
87
app/index.js
87
app/index.js
|
@ -3,7 +3,10 @@ import 'babel-polyfill';
|
|||
import choo from 'choo';
|
||||
|
||||
import config from './config.json';
|
||||
import { viewManager } from './views/manager';
|
||||
import { appRoutes } from './appRoutes';
|
||||
import { appListeners } from './appListeners';
|
||||
import { appState } from './appState.js';
|
||||
import { appUtilities } from './appUtilities.js';
|
||||
|
||||
const app = choo();
|
||||
|
||||
|
@ -13,88 +16,18 @@ if (process.env.NODE_ENV !== 'production') {
|
|||
}
|
||||
|
||||
app.use((state, emitter) => {
|
||||
app.getSettingsItem = settingsKey => {
|
||||
let savedSettings = window.localStorage.getItem('settings');
|
||||
if (savedSettings) {
|
||||
savedSettings = JSON.parse(savedSettings);
|
||||
if (typeof savedSettings[settingsKey] !== 'undefined') {
|
||||
return savedSettings[settingsKey];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
app.setSettingsItem = (settingsKey, value) => {
|
||||
let savedSettings = window.localStorage.getItem('settings');
|
||||
if (savedSettings) {
|
||||
savedSettings = JSON.parse(savedSettings);
|
||||
} else {
|
||||
savedSettings = {};
|
||||
}
|
||||
savedSettings[settingsKey] = value;
|
||||
return window.localStorage.setItem('settings', JSON.stringify(savedSettings));
|
||||
}
|
||||
app.getSessionState = () => {
|
||||
let sessionState = window.sessionStorage.getItem('sessionState');
|
||||
if (sessionState) {
|
||||
return JSON.parse(sessionState);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
app.setSessionState = () => {
|
||||
return window.sessionStorage.setItem('sessionState', JSON.stringify(app.state));
|
||||
}
|
||||
app.siteConfig = config;
|
||||
appUtilities(app);
|
||||
});
|
||||
|
||||
// App state and emitters
|
||||
app.use((state, emitter) => {
|
||||
const sessionState = app.getSessionState();
|
||||
if (sessionState) {
|
||||
Object.keys(sessionState).forEach(key => {
|
||||
if (typeof state[key] === 'undefined') {
|
||||
state[key] = sessionState[key];
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Default state variables
|
||||
state.currentView = 'home';
|
||||
state.language = app.getSettingsItem('lang') ? app.getSettingsItem('lang') : (navigator.language || navigator.userLanguage).split('-')[0];
|
||||
state.viewStates = {};
|
||||
state.isLoggedIn = false;
|
||||
}
|
||||
appState(app, state);
|
||||
|
||||
// Listeners
|
||||
emitter.on('DOMContentLoaded', () => {
|
||||
document.title = config.siteName;
|
||||
// Emitter listeners
|
||||
emitter.on('render', callback => {
|
||||
app.setSessionState();
|
||||
// This is a dirty hack to get the callback to call *after* re-rendering.
|
||||
if (callback && typeof callback === "function") {
|
||||
setTimeout(() => {
|
||||
callback();
|
||||
}, 50);
|
||||
}
|
||||
appListeners(app, state, emitter);
|
||||
});
|
||||
|
||||
emitter.on('change-view', newView => {
|
||||
// Change the view and call render. Makes it easier to call within views.
|
||||
state.currentView = newView;
|
||||
emitter.emit('render', () => {});
|
||||
});
|
||||
|
||||
emitter.on('set-language', newLanguage => {
|
||||
app.setSettingsItem('lang', newLanguage);
|
||||
state.language = newLanguage;
|
||||
emitter.emit('render', () => {});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// For the main screen, pass the viewManager function in viewManager.js,
|
||||
// which is given the app's state from above and the emitter.emit method that
|
||||
// triggers the app's emitter listeners.
|
||||
app.route('/', viewManager);
|
||||
app.route('/:page', viewManager);
|
||||
app.route('/404', viewManager);
|
||||
// Routes
|
||||
appRoutes(app);
|
||||
|
||||
app.mount('body'); // Overwrite the `<body>` tag with the content of the Choo app
|
||||
|
|
|
@ -107,7 +107,8 @@ footer nav {
|
|||
margin: 0 0 0 auto;
|
||||
}
|
||||
|
||||
.card.info {
|
||||
.card {
|
||||
&.info {
|
||||
background: $picnic-info;
|
||||
color: $picnic-white;
|
||||
|
||||
|
@ -115,6 +116,31 @@ footer nav {
|
|||
color: $picnic-white;
|
||||
}
|
||||
}
|
||||
&.success {
|
||||
background: $picnic-success;
|
||||
color: $picnic-white;
|
||||
|
||||
* {
|
||||
color: $picnic-white;
|
||||
}
|
||||
}
|
||||
&.warning {
|
||||
background: $picnic-warning;
|
||||
color: $picnic-white;
|
||||
|
||||
* {
|
||||
color: $picnic-white;
|
||||
}
|
||||
}
|
||||
&.error {
|
||||
background: $picnic-error;
|
||||
color: $picnic-white;
|
||||
|
||||
* {
|
||||
color: $picnic-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.background-dull {
|
||||
background: $picnic-dull;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import html from 'choo/html';
|
||||
|
||||
export const errorView = (state, emit, i18n) => {
|
||||
return html`<section class="error card">
|
||||
<header>
|
||||
<h1>${i18n.__('404.header')}</h1>
|
||||
</header>
|
||||
<footer>
|
||||
<h2>${i18n.__('404.subheader')}</h2>
|
||||
</footer>
|
||||
</section>`;
|
||||
}
|
|
@ -2,34 +2,9 @@ import html from 'choo/html';
|
|||
|
||||
import headerImage from '../../dev/images/header.png';
|
||||
|
||||
import { I18n } from '../i18n';
|
||||
import { homeView } from './home';
|
||||
import { loginView } from './login';
|
||||
import { searchView } from './search';
|
||||
|
||||
export const viewManager = (state, emit) => {
|
||||
const i18n = new I18n(state); // Global I18n class passed to all views
|
||||
// In viewManager all we are doing is checking the app's state
|
||||
// and passing the state and emit to the relevant view.
|
||||
let htmlContent = html`<div>loading</div>`;
|
||||
switch (state.params.page) {
|
||||
case 'home':
|
||||
default: {
|
||||
htmlContent = homeView(state, emit, i18n);
|
||||
break;
|
||||
}
|
||||
case 'login': {
|
||||
htmlContent = loginView(state, emit, i18n);
|
||||
break;
|
||||
}
|
||||
case 'search': {
|
||||
htmlContent = searchView(state, emit, i18n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export const globalView = (state, emit, i18n, view) => {
|
||||
// Create a wrapper for view content that includes global header/footer
|
||||
let view = html`<body>
|
||||
return html`<body>
|
||||
<header>
|
||||
<nav>
|
||||
<div class="brand">
|
||||
|
@ -58,7 +33,7 @@ export const viewManager = (state, emit) => {
|
|||
</header>
|
||||
|
||||
<main class="container">
|
||||
${htmlContent}
|
||||
${view(state, emit, i18n)}
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
|
@ -74,6 +49,4 @@ export const viewManager = (state, emit) => {
|
|||
</nav>
|
||||
</footer>
|
||||
</body>`;
|
||||
|
||||
return view;
|
||||
}
|
Loading…
Reference in New Issue