diff --git a/client/js/api.js b/client/js/api.js index 497525e..24d4a7a 100644 --- a/client/js/api.js +++ b/client/js/api.js @@ -2,14 +2,13 @@ const request = require('superagent'); const config = require('./config.js'); -const EventListener = require('./event_listener.js'); +const events = require('./events.js'); class Api { constructor() { this.user = null; this.userName = null; this.userPassword = null; - this.authenticated = new EventListener(); } get(url) { @@ -70,11 +69,11 @@ class Api { .then(response => { this.user = response.user; resolve(); - this.authenticated.fire(); + events.notify(events.Authentication); }).catch(response => { reject(response.description); this.logout(); - this.authenticated.fire(); + events.notify(events.Authentication); }); }); } @@ -83,7 +82,7 @@ class Api { this.user = null; this.userName = null; this.userPassword = null; - this.authenticated.fire(); + events.notify(events.Authentication); } isLoggedIn() { diff --git a/client/js/controllers/auth_controller.js b/client/js/controllers/auth_controller.js index 04cfec5..d68cdb8 100644 --- a/client/js/controllers/auth_controller.js +++ b/client/js/controllers/auth_controller.js @@ -3,6 +3,7 @@ const cookies = require('js-cookie'); const page = require('page'); const api = require('../api.js'); +const events = require('../events.js'); const topNavController = require('../controllers/top_nav_controller.js'); const LoginView = require('../views/login_view.js'); const PasswordResetView = require('../views/password_reset_view.js'); @@ -11,16 +12,21 @@ class AuthController { constructor() { this.loginView = new LoginView(); this.passwordResetView = new PasswordResetView(); + } - const auth = cookies.getJSON('auth'); - if (auth && auth.user && auth.password) { - api.login(auth.user, auth.password).catch(errorMessage => { - page('/'); - this.loginView.notifyError( - 'An error happened while trying to log you in: ' + - errorMessage); - }); - } + login() { + return new Promise((resolve, reject) => { + const auth = cookies.getJSON('auth'); + if (auth && auth.user && auth.password) { + api.login(auth.user, auth.password) + .then(resolve) + .catch(errorMessage => { + reject(errorMessage); + }); + } else { + resolve(); + } + }); } registerRoutes() { @@ -51,7 +57,7 @@ class AuthController { options); resolve(); page('/'); - this.loginView.notifySuccess('Logged in'); + events.notify(events.Success, 'Logged in'); }).catch(errorMessage => { reject(errorMessage); }); }); }}); @@ -61,7 +67,7 @@ class AuthController { api.logout(); cookies.remove('auth'); page('/'); - this.loginView.notifySuccess('Logged out'); + events.notify(events.Success, 'Logged out'); } passwordResetRoute() { @@ -89,15 +95,15 @@ class AuthController { cookies.set( 'auth', {'user': name, 'password': password}, {}); page('/'); - this.passwordResetView.notifySuccess( + events.notify(events.Success, 'New password: ' + password); }).catch(errorMessage => { page('/'); - this.passwordResetView.notifyError(errorMessage); + events.notify(events.Error, errorMessage); }); }).catch(response => { page('/'); - this.passwordResetView.notifyError(response.description); + events.notify(events.Error, response.description); }); } } diff --git a/client/js/controllers/top_nav_controller.js b/client/js/controllers/top_nav_controller.js index a6da22f..9ee7f07 100644 --- a/client/js/controllers/top_nav_controller.js +++ b/client/js/controllers/top_nav_controller.js @@ -1,6 +1,7 @@ 'use strict'; const api = require('../api.js'); +const events = require('../events.js'); const TopNavView = require('../views/top_nav_view.js'); class NavigationItem { @@ -31,11 +32,13 @@ class TopNavController { 'help': new NavigationItem('E', 'Help', '/help'), }; - api.authenticated.listen(() => { - this.updateVisibility(); - this.topNavView.render(this.items, this.activeItem); - this.topNavView.activate(this.activeItem); - }); + events.listen( + events.Authentication, + () => { + this.updateVisibility(); + this.topNavView.render(this.items, this.activeItem); + this.topNavView.activate(this.activeItem); + }); this.updateVisibility(); this.topNavView.render(this.items, this.activeItem); diff --git a/client/js/controllers/users_controller.js b/client/js/controllers/users_controller.js index b7d3e2b..ce23f70 100644 --- a/client/js/controllers/users_controller.js +++ b/client/js/controllers/users_controller.js @@ -3,6 +3,7 @@ const cookies = require('js-cookie'); const page = require('page'); const api = require('../api.js'); +const events = require('../events.js'); const topNavController = require('../controllers/top_nav_controller.js'); const RegistrationView = require('../views/registration_view.js'); const UserView = require('../views/user_view.js'); @@ -50,7 +51,7 @@ class UsersController { cookies.set('auth', {'user': name, 'password': password}); resolve(); page('/'); - this.registrationView.notifySuccess('Welcome aboard!'); + events.notify(events.Success, 'Welcome aboard!'); }).catch(response => { reject(response.description); }); @@ -74,7 +75,7 @@ class UsersController { next(); }).catch(response => { this.userView.empty(); - this.userView.notifyError(response.description); + events.notify(events.Error, response.description); }); } } diff --git a/client/js/event_listener.js b/client/js/event_listener.js deleted file mode 100644 index e7eebff..0000000 --- a/client/js/event_listener.js +++ /dev/null @@ -1,24 +0,0 @@ -class EventListener { - constructor() { - this.listeners = []; - } - - listen(callback) { - this.listeners.push(callback); - } - - unlisten(callback) { - const index = this.listeners.indexOf(callback); - if (index !== -1) { - this.listeners.splice(index, 1); - } - } - - fire(data) { - for (let listener of this.listeners) { - listener(data); - } - } -} - -module.exports = EventListener; diff --git a/client/js/events.js b/client/js/events.js new file mode 100644 index 0000000..8fb42d4 --- /dev/null +++ b/client/js/events.js @@ -0,0 +1,28 @@ +'use strict'; + +let listeners = []; + +function listen(messageClass, handler) { + if (!(messageClass in listeners)) { + listeners[messageClass] = []; + } + listeners[messageClass].push(handler); +} + +function notify(messageClass, message) { + if (!(messageClass in listeners)) { + return; + } + for (let handler of listeners[messageClass]) { + handler(message); + } +} + +module.exports = { + Success: 1, + Error: 2, + Authentication: 3, + + notify: notify, + listen: listen, +}; diff --git a/client/js/main.js b/client/js/main.js index b3706db..03b73cc 100644 --- a/client/js/main.js +++ b/client/js/main.js @@ -3,18 +3,29 @@ require('./util/handlebars-helpers.js'); let controllers = []; +const authController = require('./controllers/auth_controller.js'); +controllers.push(authController); controllers.push(require('./controllers/posts_controller.js')); controllers.push(require('./controllers/users_controller.js')); controllers.push(require('./controllers/help_controller.js')); -controllers.push(require('./controllers/auth_controller.js')); controllers.push(require('./controllers/comments_controller.js')); controllers.push(require('./controllers/history_controller.js')); controllers.push(require('./controllers/tags_controller.js')); controllers.push(require('./controllers/home_controller.js')); +const events = require('./events.js'); const page = require('page'); for (let controller of controllers) { controller.registerRoutes(); } -page(); + +authController.login().then(() => { + page(); +}).catch(errorMessage => { + page(); + page('/'); + events.notify( + events.Error, + 'An error happened while trying to log you in: ' + errorMessage); +}); diff --git a/client/js/views/base_view.js b/client/js/views/base_view.js index c805d8a..c5ed465 100644 --- a/client/js/views/base_view.js +++ b/client/js/views/base_view.js @@ -1,13 +1,32 @@ 'use strict'; const handlebars = require('handlebars'); +const events = require('../events.js'); +const contentHolder = document.getElementById('content-holder'); + +function messageHandler(message, className) { + const messagesHolder = contentHolder.querySelector('.messages'); + if (!messagesHolder) { + alert(message); + return; + } + /* TODO: animate this */ + const node = document.createElement('div'); + node.innerHTML = message.replace(/\n/g, '
'); + node.classList.add('message'); + node.classList.add(className); + messagesHolder.appendChild(node); +} + +events.listen(events.Success, msg => { messageHandler(msg, 'success'); }); +events.listen(events.Error, msg => { messageHandler(msg, 'error'); }); // fix iterating over NodeList in Chrome and Opera NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; class BaseView { constructor() { - this.contentHolder = document.getElementById('content-holder'); + this.contentHolder = contentHolder; } getTemplate(templatePath) { @@ -20,24 +39,6 @@ class BaseView { return handlebars.compile(templateText); } - notifyError(message) { - this.notify(message, 'error'); - } - - notifySuccess(message) { - this.notify(message, 'success'); - } - - notify(message, className) { - const messagesHolder = this.contentHolder.querySelector('.messages'); - /* TODO: animate this */ - const node = document.createElement('div'); - node.innerHTML = message.replace(/\n/g, '
'); - node.classList.add('message'); - node.classList.add(className); - messagesHolder.appendChild(node); - } - clearMessages() { const messagesHolder = this.contentHolder.querySelector('.messages'); /* TODO: animate that */ diff --git a/client/js/views/login_view.js b/client/js/views/login_view.js index 3708e65..1d963e5 100644 --- a/client/js/views/login_view.js +++ b/client/js/views/login_view.js @@ -1,6 +1,7 @@ 'use strict'; const config = require('../config.js'); +const events = require('../events.js'); const BaseView = require('./base_view.js'); class LoginView extends BaseView { @@ -34,7 +35,7 @@ class LoginView extends BaseView { }) .catch(errorMessage => { this.enableForm(form); - this.notifyError(errorMessage); + events.notify(events.Error, errorMessage); }); }); } diff --git a/client/js/views/password_reset_view.js b/client/js/views/password_reset_view.js index 23b17ee..0bba9c8 100644 --- a/client/js/views/password_reset_view.js +++ b/client/js/views/password_reset_view.js @@ -1,5 +1,6 @@ 'use strict'; +const events = require('../events.js'); const BaseView = require('./base_view.js'); class PasswordResetView extends BaseView { @@ -22,13 +23,14 @@ class PasswordResetView extends BaseView { options .proceed(userNameOrEmailField.value) .then(() => { - this.notifySuccess( + events.notify( + events.Success, 'E-mail has been sent. To finish the procedure, ' + 'please click the link it contains.'); }) .catch(errorMessage => { this.enableForm(form); - this.notifyError(errorMessage); + events.notify(events.Error, errorMessage); }); }); } diff --git a/client/js/views/registration_view.js b/client/js/views/registration_view.js index 1a1583e..597ade9 100644 --- a/client/js/views/registration_view.js +++ b/client/js/views/registration_view.js @@ -1,6 +1,7 @@ 'use strict'; const config = require('../config.js'); +const events = require('../events.js'); const BaseView = require('./base_view.js'); class RegistrationView extends BaseView { @@ -11,12 +12,13 @@ class RegistrationView extends BaseView { render(options) { this.showView(this.template()); - const form = document.querySelector('#content-holder form'); - this.decorateValidator(form); - const userNameField = document.getElementById('user-name'); - const passwordField = document.getElementById('user-password'); - const emailField = document.getElementById('user-email'); + const form = this.contentHolder.querySelector('form'); + const userNameField = this.contentHolder.querySelector('#user-name'); + const passwordField = this.contentHolder.querySelector('#user-password'); + const emailField = this.contentHolder.querySelector('#user-email'); + + this.decorateValidator(form); userNameField.setAttribute('pattern', config.userNameRegex); passwordField.setAttribute('pattern', config.passwordRegex); @@ -34,7 +36,7 @@ class RegistrationView extends BaseView { }) .catch(errorMessage => { this.enableForm(form); - this.notifyError(errorMessage); + events.notify(events.Error, errorMessage); }); }); }