diff --git a/src/internal/web/render.go b/src/internal/web/render.go new file mode 100644 index 0000000..7750799 --- /dev/null +++ b/src/internal/web/render.go @@ -0,0 +1,48 @@ +package web + +import ( + "html/template" + "net/http" + + "gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/data" + + "github.com/gorilla/mux" +) + +// TmplFuncs handles global template functions +var tmplFuncs = []template.FuncMap{ + map[string]interface{}{ + "safeURL": func(s string) template.URL { + return template.URL(s) + }, + }, +} + +// AddTmplFunc adds a template function +func AddTmplFunc(name string, f interface{}) error { + tmplFuncs = append(tmplFuncs, map[string]interface{}{ + name: f, + }) + return nil +} + +// TemplateData represents object passed to template renderer +type TemplateData struct { + Route string + Data map[string]interface{} +} + +// Rends a view +func (e *Env) Rends(w http.ResponseWriter, r *http.Request, template string) error { + if r.Header.Get("Accept") == "application/json" { + return e.Render.JSON(w, http.StatusOK, TemplateData{ + Route: mux.CurrentRoute(r).GetName(), + Data: data.GetAllData(r), + }) + } + + return e.Render.HTML(w, http.StatusOK, template, TemplateData{ + Route: mux.CurrentRoute(r).GetName(), + Data: data.GetAllData(r), + }) +} diff --git a/src/public/js/actions/actionCreators.js b/src/public/js/actions/actionCreators.js index 6712a3a..06fa96c 100644 --- a/src/public/js/actions/actionCreators.js +++ b/src/public/js/actions/actionCreators.js @@ -1,119 +1,56 @@ -import axios from 'axios' +import { configureAxios, request } from '../requests' // Select Movie -export const SELECT_MOVIE = 'SELECT_MOVIE' export function selectMovie(index) { return { - type: SELECT_MOVIE, + type: 'SELECT_MOVIE', index } } -export const IS_USER_LOGGED_IN = 'IS_USER_LOGGED_IN' export function isUserLoggedIn() { return { - type: IS_USER_LOGGED_IN, + type: 'IS_USER_LOGGED_IN', } } -export const ADD_ERROR = 'ADD_ERROR' export function addError(message) { return { - type: ADD_ERROR, + type: 'ADD_ERROR', payload: { message, } } } -export const DISMISS_ERROR = 'DISMISS_ERROR' export function dismissError() { return { - type: DISMISS_ERROR, + type: 'DISMISS_ERROR', } } -export const USER_LOGOUT = 'USER_LOGOUT' export function userLogout() { return { - type: USER_LOGOUT, + type: 'USER_LOGOUT', } } -export const USER_LOGIN_FULFILLED = 'USER_LOGIN_FULFILLED', - USER_LOGIN_PENDING = 'USER_LOGIN_PENDING'; export function loginUser(username, password) { - return function(dispatch) { - dispatch({ - type: USER_LOGIN_PENDING, - }) - axios.post( + return request( + 'USER_LOGIN', + configureAxios().post( '/users/login', { username: username, password: password, }, - ).then(response => { - if (response.data.status === 'error') - { - dispatch({ - type: ADD_ERROR, - payload: { - message: response.data.message, - } - }) - } - dispatch({ - type: USER_LOGIN_FULFILLED, - payload: response.data, - }) - }).catch(error => { - console.log(error) - }) - } + ) + ) } -export const MOVIE_LIST_FETCH_FULFILLED = 'MOVIE_LIST_FETCH_FULFILLED', - MOVIE_LIST_FETCH_PENDING = 'MOVIE_LIST_FETCH_PENDING'; export function fetchMovies() { - const token = localStorage.getItem('token'); - const header = 'Bearer ' + token; - return function(dispatch) { - dispatch({ - type: MOVIE_LIST_FETCH_PENDING, - }) - axios.get('/movies/explore/popular', { - headers: { 'Authorization': header }, - }) - .then(response => { - if (response.data.status === 'error') - { - dispatch({ - type: ADD_ERROR, - payload: { - message: response.data.message, - } - }) - } else { - dispatch({ - type: MOVIE_LIST_FETCH_FULFILLED, - payload: response.data, - }) - } - }) - .catch(error => { - // Unauthorized - if (error.response.status == 401) { - dispatch({ - type: USER_LOGOUT, - }) - } - dispatch({ - type: ADD_ERROR, - payload: { - message: error.response.data, - } - }) - }) - } + return request( + 'MOVIE_LIST_FETCH', + configureAxios().get('/movies/explore/popular') + ) } diff --git a/src/public/js/reducers/errors.js b/src/public/js/reducers/errors.js index 7853485..fcc5b8a 100644 --- a/src/public/js/reducers/errors.js +++ b/src/public/js/reducers/errors.js @@ -1,12 +1,10 @@ -import { ADD_ERROR, DISMISS_ERROR } from '../actions/actionCreators' - export default function error(state = {}, action) { switch (action.type) { - case ADD_ERROR: + case 'ADD_ERROR': return Object.assign({}, state, { message: action.payload.message, }) - case DISMISS_ERROR: + case 'DISMISS_ERROR': return {}; default: return state; diff --git a/src/public/js/reducers/movie-store.js b/src/public/js/reducers/movie-store.js index d217d0d..716a71c 100644 --- a/src/public/js/reducers/movie-store.js +++ b/src/public/js/reducers/movie-store.js @@ -1,5 +1,3 @@ -import { SELECT_MOVIE, MOVIE_LIST_FETCH_FULFILLED, MOVIE_LIST_FETCH_PENDING } from '../actions/actionCreators' - const defaultState = { movies: [], selectedMovieIndex: 0, @@ -7,13 +5,13 @@ const defaultState = { export default function movieStore(state = defaultState, action) { switch (action.type) { - case MOVIE_LIST_FETCH_FULFILLED: + case 'MOVIE_LIST_FETCH_FULFILLED': return Object.assign({}, state, { movies: action.payload.data, }) - case MOVIE_LIST_FETCH_PENDING: + case 'MOVIE_LIST_FETCH_PENDING': return state - case SELECT_MOVIE: + case 'SELECT_MOVIE': return Object.assign({}, state, { selectedMovieIndex: action.index, }) diff --git a/src/public/js/reducers/users.js b/src/public/js/reducers/users.js index c2c767e..471ffdc 100644 --- a/src/public/js/reducers/users.js +++ b/src/public/js/reducers/users.js @@ -1,4 +1,3 @@ -import { IS_USER_LOGGED_IN, USER_LOGIN_PENDING, USER_LOGIN_FULFILLED, USER_LOGOUT } from '../actions/actionCreators' import jwtDecode from 'jwt-decode' const defaultState = { @@ -10,22 +9,22 @@ const defaultState = { export default function userStore(state = defaultState, action) { switch (action.type) { - case USER_LOGIN_PENDING: + case 'USER_LOGIN_PENDING': return Object.assign({}, state, { userLoading: true, }) - case USER_LOGIN_FULFILLED: + case 'USER_LOGIN_FULFILLED': if (action.payload.status === "error") { return logoutUser(state) } return updateFromToken(state, action.payload.data.token) - case IS_USER_LOGGED_IN: + case 'IS_USER_LOGGED_IN': let localToken = localStorage.getItem('token'); if (!localToken || localToken === "") { return state; } return updateFromToken(state, localToken) - case USER_LOGOUT: + case 'USER_LOGOUT': return logoutUser(state) default: return state; diff --git a/src/public/js/requests.js b/src/public/js/requests.js new file mode 100644 index 0000000..87c8bda --- /dev/null +++ b/src/public/js/requests.js @@ -0,0 +1,59 @@ +import axios from 'axios' + +// This functions returns an axios instance, the token is added to the +// configuration if found in the localStorage +export function configureAxios(headers = {}) { + // Get the token from the localStorate + const token = localStorage.getItem('token'); + if (token) { + headers = { 'Authorization': `Bearer ${token}` }; + } + + return axios.create({ + headers + }) +} + +// This function takes en event prefix to dispatch evens during the life of the +// request, it also take a promise (axios request) +export function request(eventPrefix, promise) { + // Events + const pending = `${eventPrefix}_PENDING`; + const fulfilled = `${eventPrefix}_FULFILLED`; + + return function(dispatch) { + dispatch({ + type: pending, + }) + promise + .then(response => { + if (response.data.status === 'error') + { + dispatch({ + type: 'ADD_ERROR', + payload: { + message: response.data.message, + } + }) + } + dispatch({ + type: fulfilled, + payload: response.data, + }) + }) + .catch(error => { + // Unauthorized + if (error.response.status == 401) { + dispatch({ + type: 'USER_LOGOUT', + }) + } + dispatch({ + type: 'ADD_ERROR', + payload: { + message: error.response.data, + } + }) + }) + } +}