Compare commits
6 Commits
929d1ca928
...
5aeee0bc01
Author | SHA1 | Date |
---|---|---|
Robbie Antenesse | 5aeee0bc01 | |
Robbie Antenesse | 4498ed002a | |
Robbie Antenesse | cd0baa7605 | |
Robbie Antenesse | d8f0de9ec4 | |
Robbie Antenesse | 43a8c006a1 | |
Robbie Antenesse | bcde0c6dc7 |
|
@ -14,9 +14,11 @@ export const appListeners = (app, state, emitter) => {
|
||||||
emitter.on('set-language', newLanguage => {
|
emitter.on('set-language', newLanguage => {
|
||||||
app.setSettingsItem('lang', newLanguage);
|
app.setSettingsItem('lang', newLanguage);
|
||||||
state.language = newLanguage;
|
state.language = newLanguage;
|
||||||
emitter.emit('render', () => { });
|
emitter.emit('render');
|
||||||
});
|
});
|
||||||
|
|
||||||
emitter.emit('render'); // This should hopefully only run once after the DOM is loaded. It prevents routing issues where 'render' hasn't been defined yet
|
app.checkIfLoggedIn(state).then(isLoggedIn => {
|
||||||
|
emitter.emit('render'); // This should hopefully only run once after the DOM is loaded. It prevents routing issues where 'render' hasn't been defined yet
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -9,6 +9,8 @@ export const appRoutes = (app) => {
|
||||||
|
|
||||||
app.route('/login', (state, emit) => globalView(state, emit, loginView));
|
app.route('/login', (state, emit) => globalView(state, emit, loginView));
|
||||||
|
|
||||||
|
app.route('/logout', () => window.location.reload()); // If Choo navigates here, refresh the page instead so the server can handle it and log out
|
||||||
|
|
||||||
app.route('/search', (state, emit) => globalView(state, emit, searchView));
|
app.route('/search', (state, emit) => globalView(state, emit, searchView));
|
||||||
|
|
||||||
app.route('/404', (state, emit) => globalView(state, emit, errorView));
|
app.route('/404', (state, emit) => globalView(state, emit, errorView));
|
||||||
|
|
|
@ -19,4 +19,19 @@ export const appUtilities = (app) => {
|
||||||
savedSettings[settingsKey] = value;
|
savedSettings[settingsKey] = value;
|
||||||
return window.localStorage.setItem('settings', JSON.stringify(savedSettings));
|
return window.localStorage.setItem('settings', JSON.stringify(savedSettings));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.checkIfLoggedIn = (appState) => {
|
||||||
|
return fetch('/api/account/validate', { method: 'post' })
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(response => {
|
||||||
|
if (response.error !== false) {
|
||||||
|
console.warn(response);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.info(response.message);
|
||||||
|
appState.isLoggedIn = true;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -7,7 +7,11 @@ export class I18n {
|
||||||
default: en,
|
default: en,
|
||||||
en,
|
en,
|
||||||
};
|
};
|
||||||
this.language = appState.language;
|
this.appState = appState;
|
||||||
|
}
|
||||||
|
|
||||||
|
get language () {
|
||||||
|
return this.appState.language;
|
||||||
}
|
}
|
||||||
|
|
||||||
translate (section, phrase) {
|
translate (section, phrase) {
|
||||||
|
|
|
@ -3,10 +3,13 @@
|
||||||
"locale": "en",
|
"locale": "en",
|
||||||
"global": {
|
"global": {
|
||||||
"menu_search": "Search for Books",
|
"menu_search": "Search for Books",
|
||||||
"menu_login": "Log In",
|
"menu_about": "About",
|
||||||
|
"menu_login": "Log In / Create Account",
|
||||||
|
"menu_account": "My Profile",
|
||||||
"menu_logout": "Log Out",
|
"menu_logout": "Log Out",
|
||||||
"footer_repo": "Repo",
|
"footer_repo": "Repo",
|
||||||
"footer_chat": "Chat"
|
"footer_chat": "Chat",
|
||||||
|
"change_language": "Change Language"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"logged_out_subtitle": "All the Book Buzz in Once Place",
|
"logged_out_subtitle": "All the Book Buzz in Once Place",
|
||||||
|
@ -16,7 +19,10 @@
|
||||||
"logged_out_community_header": "A Look Inside the Hive",
|
"logged_out_community_header": "A Look Inside the Hive",
|
||||||
"logged_out_recent_reviews": "Recent Reviews",
|
"logged_out_recent_reviews": "Recent Reviews",
|
||||||
"logged_out_recent_updates": "Recent Updates",
|
"logged_out_recent_updates": "Recent Updates",
|
||||||
"logged_out_join_now": "Join Now!"
|
"logged_out_join_now": "Join Now!",
|
||||||
|
"logged_in_subtitle": "Welcome!",
|
||||||
|
"logged_in_updates": "Updates",
|
||||||
|
"logged_in_interactions": "Interactions"
|
||||||
},
|
},
|
||||||
"404": {
|
"404": {
|
||||||
"header": "Oops!",
|
"header": "Oops!",
|
||||||
|
|
|
@ -19,9 +19,18 @@ export const globalView = (state, emit, view) => {
|
||||||
<label for="navMenu" class="burger pseudo button">${'\u2261'}</label>
|
<label for="navMenu" class="burger pseudo button">${'\u2261'}</label>
|
||||||
|
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<a href="/search" class="pseudo button"><i class="icon-search" aria-label=${i18n.__('global.menu_search')}></i></a>
|
<a href="/search" class="pseudo button">
|
||||||
<a href="/login" class="pseudo button">${i18n.__('global.menu_login')}</a>
|
<i class="icon-search" aria-labeledBy="searchLabel"></i> <span id="searchLabel">${i18n.__('global.menu_search')}</span>
|
||||||
<a href="/logout" class="pseudo button">${i18n.__('global.menu_logout')}</a>
|
</a>
|
||||||
|
<a href="/about" class="pseudo button">${i18n.__('global.menu_about')}</a>
|
||||||
|
${
|
||||||
|
state.isLoggedIn === true
|
||||||
|
? [
|
||||||
|
html`<a href="/account" class="pseudo button">${i18n.__('global.menu_account')}</a>`,
|
||||||
|
html`<a href="/logout" class="pseudo button">${i18n.__('global.menu_logout')}</a>`,
|
||||||
|
]
|
||||||
|
: html`<a href="/login" class="pseudo button">${i18n.__('global.menu_login')}</a>`
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
@ -31,14 +40,30 @@ export const globalView = (state, emit, view) => {
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<nav>
|
<nav class="flex one">
|
||||||
<div class="links">
|
<div class="two-third-600">
|
||||||
<a href="https://gitlab.com/Alamantus/Readlebee" class="pseudo button">
|
<div class="links">
|
||||||
${i18n.__('global.footer_repo')}
|
<a href="https://gitlab.com/Alamantus/Readlebee" class="pseudo button">
|
||||||
</a>
|
${i18n.__('global.footer_repo')}
|
||||||
<a href="https://gitter.im/Readlebee/community" class="pseudo button">
|
</a>
|
||||||
${i18n.__('global.footer_chat')}
|
<a href="https://gitter.im/Readlebee/community" class="pseudo button">
|
||||||
</a>
|
${i18n.__('global.footer_chat')}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="third-600">
|
||||||
|
<label class="flex">
|
||||||
|
<span class="third">${i18n.__('global.change_language')}:</span>
|
||||||
|
<select class="two-third" onchange=${e => emit('set-language', e.target.value)}>
|
||||||
|
${Object.keys(i18n.availableLanguages).map(languageKey => {
|
||||||
|
if (languageKey === 'default') return null;
|
||||||
|
const language = i18n.availableLanguages[languageKey];
|
||||||
|
return html`<option value=${language.locale} ${state.language === language.locale ? 'selected' : null}>
|
||||||
|
${language.name}
|
||||||
|
</option>`;
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
@ -5,19 +5,18 @@ export class HomeController extends ViewController {
|
||||||
// Super passes state, view name, and default state to ViewController,
|
// Super passes state, view name, and default state to ViewController,
|
||||||
// which stores state in this.appState and the view controller's state to this.state
|
// which stores state in this.appState and the view controller's state to this.state
|
||||||
super(state, i18n, 'home', {
|
super(state, i18n, 'home', {
|
||||||
recentReviews: [],
|
loggedOut: {
|
||||||
recentUpdates: [],
|
recentReviews: [],
|
||||||
|
recentUpdates: [],
|
||||||
|
},
|
||||||
|
loggedIn: {
|
||||||
|
updates: [], // statuses, ratings, and reviews from people you follow.
|
||||||
|
interactions: [], // likes, comments, recommendations, etc.
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// If using controller methods in an input's onchange or onclick instance,
|
// If using controller methods in an input's onchange or onclick instance,
|
||||||
// either bind the class's 'this' instance to the method first...
|
// either bind the class's 'this' instance to the method first...
|
||||||
// or use `onclick=${() => controller.submit()}` to maintain the 'this' of the class instead.
|
// or use `onclick=${() => controller.submit()}` to maintain the 'this' of the class instead.
|
||||||
}
|
}
|
||||||
|
|
||||||
get recentReviews() {
|
|
||||||
return [...this.state.recentReviews];
|
|
||||||
}
|
|
||||||
get recentUpdates() {
|
|
||||||
return [...this.state.recentUpdates];
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@ import html from 'choo/html';
|
||||||
|
|
||||||
import { HomeController } from './controller'; // The controller for this view, where processing should happen.
|
import { HomeController } from './controller'; // The controller for this view, where processing should happen.
|
||||||
import { loggedOutView } from './loggedOut';
|
import { loggedOutView } from './loggedOut';
|
||||||
|
import { loggedInView } from './loggedIn';
|
||||||
|
|
||||||
// This is the view function that is exported and used in the view manager.
|
// This is the view function that is exported and used in the view manager.
|
||||||
export const homeView = (state, emit, i18n) => {
|
export const homeView = (state, emit, i18n) => {
|
||||||
|
@ -12,7 +13,7 @@ export const homeView = (state, emit, i18n) => {
|
||||||
return [
|
return [
|
||||||
(!controller.isLoggedIn
|
(!controller.isLoggedIn
|
||||||
? loggedOutView(controller, emit)
|
? loggedOutView(controller, emit)
|
||||||
: html`<p>lol wut how are u logged in</p>`
|
: loggedInView(controller, emit)
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
import html from 'choo/html';
|
||||||
|
|
||||||
|
export const loggedInView = (homeController, emit) => {
|
||||||
|
const { __ } = homeController.i18n;
|
||||||
|
|
||||||
|
return [
|
||||||
|
html`<section>
|
||||||
|
<h2>${__('home.logged_in_subtitle')}</h2>
|
||||||
|
<div class="flex one two-700">
|
||||||
|
<div>
|
||||||
|
<div class="card">
|
||||||
|
<header>
|
||||||
|
<h3>${__('home.logged_in_updates')}</h3>
|
||||||
|
<button class="small pseudo pull-right tooltip-left" data-tooltip=${__('interaction.reload')}>
|
||||||
|
<i class="icon-reload"></i>
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
<footer>
|
||||||
|
${homeController.state.loggedIn.updates.map(update => reviewCard(homeController, update))}
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="card">
|
||||||
|
<header>
|
||||||
|
<h3>${__('home.logged_in_interactions')}</h3>
|
||||||
|
<button class="small pseudo pull-right tooltip-left" data-tooltip=${__('interaction.reload')}>
|
||||||
|
<i class="icon-reload"></i>
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
<footer>
|
||||||
|
${homeController.state.loggedIn.interactions.map(interaction => reviewCard(homeController, interaction))}
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>`,
|
||||||
|
];
|
||||||
|
}
|
|
@ -57,7 +57,7 @@ export const loggedOutView = (homeController, emit) => {
|
||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
<footer>
|
<footer>
|
||||||
${homeController.recentReviews.map(review => reviewCard(homeController, review))}
|
${homeController.state.loggedOut.recentReviews.map(review => reviewCard(homeController, review))}
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -70,7 +70,7 @@ export const loggedOutView = (homeController, emit) => {
|
||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
<footer>
|
<footer>
|
||||||
${homeController.recentUpdates.map(review => reviewCard(homeController, review))}
|
${homeController.state.loggedOut.recentUpdates.map(update => reviewCard(homeController, update))}
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,6 +16,7 @@ export class LoginController extends ViewController {
|
||||||
},
|
},
|
||||||
loginError: '',
|
loginError: '',
|
||||||
createError: '',
|
createError: '',
|
||||||
|
loginMessage: '',
|
||||||
createMessage: '',
|
createMessage: '',
|
||||||
pageMessage: '',
|
pageMessage: '',
|
||||||
isChecking: false,
|
isChecking: false,
|
||||||
|
@ -47,7 +48,7 @@ export class LoginController extends ViewController {
|
||||||
|
|
||||||
validateLogin () {
|
validateLogin () {
|
||||||
const { __ } = this.i18n;
|
const { __ } = this.i18n;
|
||||||
this.state.createError = '';
|
this.state.loginError = '';
|
||||||
this.state.isChecking = true;
|
this.state.isChecking = true;
|
||||||
|
|
||||||
this.emit('render', () => {
|
this.emit('render', () => {
|
||||||
|
@ -60,7 +61,7 @@ export class LoginController extends ViewController {
|
||||||
loginEmail,
|
loginEmail,
|
||||||
loginPassword,
|
loginPassword,
|
||||||
].includes('')) {
|
].includes('')) {
|
||||||
this.state.createError = __('login.create_required_field_blank');
|
this.state.loginError = __('login.login_required_field_blank');
|
||||||
this.state.isChecking = false;
|
this.state.isChecking = false;
|
||||||
this.emit('render');
|
this.emit('render');
|
||||||
return;
|
return;
|
||||||
|
@ -132,6 +133,7 @@ export class LoginController extends ViewController {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.appState.isLoggedIn = true;
|
||||||
this.state.loginMessage = __(response.message);
|
this.state.loginMessage = __(response.message);
|
||||||
this.state.isChecking = false;
|
this.state.isChecking = false;
|
||||||
this.clearLoginForm();
|
this.clearLoginForm();
|
||||||
|
|
|
@ -6,6 +6,28 @@ export const loginView = (state, emit, i18n) => {
|
||||||
const controller = new LoginController(state, emit, i18n);
|
const controller = new LoginController(state, emit, i18n);
|
||||||
const { __ } = controller.i18n;
|
const { __ } = controller.i18n;
|
||||||
|
|
||||||
|
if (controller.appState.isLoggedIn === true) {
|
||||||
|
setTimeout(() => {
|
||||||
|
controller.state.loginMessage = '';
|
||||||
|
emit('pushState', '/')
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
return html`<div class="modal">
|
||||||
|
<input type="checkbox" checked>
|
||||||
|
<label class="overlay"></label>
|
||||||
|
|
||||||
|
<article class="success card">
|
||||||
|
<header>
|
||||||
|
${
|
||||||
|
controller.state.loginMessage === ''
|
||||||
|
? __('login.already_logged_in')
|
||||||
|
: controller.state.loginMessage
|
||||||
|
}
|
||||||
|
</header>
|
||||||
|
</article>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
return html`<section>
|
return html`<section>
|
||||||
|
|
||||||
${
|
${
|
||||||
|
|
|
@ -147,16 +147,16 @@ async function routes(fastify, options) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
fastify.get('/api/account/login', async (request, reply) => {
|
fastify.post('/api/account/login', async (request, reply) => {
|
||||||
const formDataIsValid = Account.loginDataIsValid(request.body);
|
const formDataIsValid = Account.loginDataIsValid(request.body);
|
||||||
if (formDataIsValid !== true) {
|
if (formDataIsValid !== true) {
|
||||||
return reply.code(400).send(formDataIsValid);
|
return reply.code(400).send(formDataIsValid);
|
||||||
}
|
}
|
||||||
|
|
||||||
const account = new Account(fastify.models.User);
|
const account = new Account(fastify.models.User);
|
||||||
const user = account.validateLogin(request.body.email, request.body.password);
|
const user = await account.validateLogin(request.body.email, request.body.password);
|
||||||
|
|
||||||
if (user.error !== true) {
|
if (user.error === true) {
|
||||||
return reply.code(400).send(user);
|
return reply.code(400).send(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ async function routes(fastify, options) {
|
||||||
})
|
})
|
||||||
.send({
|
.send({
|
||||||
error: false,
|
error: false,
|
||||||
message: 'api.account_create_success',
|
message: 'api.account_login_success',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -209,11 +209,8 @@ async function routes(fastify, options) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
fastify.get('/api/logout', async (request, reply) => {
|
fastify.get('/logout', async (request, reply) => {
|
||||||
return reply.clearCookie('token', { path: '/' }).send({
|
return reply.clearCookie('token', { path: '/' }).redirect('/');
|
||||||
error: false,
|
|
||||||
message: 'api._account_logout_success',
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue