diff options
Diffstat (limited to 'animism-align/frontend')
| -rw-r--r-- | animism-align/frontend/app/actions.js | 1 | ||||
| -rw-r--r-- | animism-align/frontend/app/api/crud.fetch.js | 6 | ||||
| -rw-r--r-- | animism-align/frontend/app/common/fonts.css | 2 | ||||
| -rw-r--r-- | animism-align/frontend/app/common/form.component.js | 2 | ||||
| -rw-r--r-- | animism-align/frontend/app/common/form.css | 3 | ||||
| -rw-r--r-- | animism-align/frontend/app/session.js | 6 | ||||
| -rw-r--r-- | animism-align/frontend/app/store.js | 4 | ||||
| -rw-r--r-- | animism-align/frontend/app/types.js | 7 | ||||
| -rw-r--r-- | animism-align/frontend/app/views/audio/audio.reducer.js | 2 | ||||
| -rw-r--r-- | animism-align/frontend/app/views/auth/auth.actions.js | 26 | ||||
| -rw-r--r-- | animism-align/frontend/app/views/auth/auth.css | 4 | ||||
| -rw-r--r-- | animism-align/frontend/app/views/auth/auth.gate.js | 40 | ||||
| -rw-r--r-- | animism-align/frontend/app/views/auth/auth.login.js | 16 | ||||
| -rw-r--r-- | animism-align/frontend/app/views/auth/auth.reducer.js | 40 |
14 files changed, 123 insertions, 36 deletions
diff --git a/animism-align/frontend/app/actions.js b/animism-align/frontend/app/actions.js index 199661d..67cbf85 100644 --- a/animism-align/frontend/app/actions.js +++ b/animism-align/frontend/app/actions.js @@ -17,6 +17,7 @@ const crudActions = [ 'media', 'episode', 'venue', + 'user', ].reduce((a,b) => (a[b] = crud_actions(b)) && a, {}) export default diff --git a/animism-align/frontend/app/api/crud.fetch.js b/animism-align/frontend/app/api/crud.fetch.js index c88225e..a8d4a0d 100644 --- a/animism-align/frontend/app/api/crud.fetch.js +++ b/animism-align/frontend/app/api/crud.fetch.js @@ -1,4 +1,5 @@ import fetch from 'node-fetch' +import { session } from 'app/session' export function crud_fetch(type, tag) { const uri = '/api/v1/' + type + '/' + (tag || '') @@ -54,6 +55,7 @@ export function _get_headers() { credentials: 'same-origin', headers: { 'Accept': 'application/json', + 'Authorization': "JWT " + session.get("access_token"), }, } } @@ -64,6 +66,7 @@ export function post(data) { credentials: 'same-origin', headers: { 'Accept': 'application/json', + 'Authorization': "JWT " + session.get("access_token"), 'Content-Type': 'application/json' }, } @@ -75,6 +78,7 @@ export function postBody(data) { credentials: 'same-origin', headers: { 'Accept': 'application/json', + 'Authorization': "JWT " + session.get("access_token"), }, } } @@ -85,6 +89,7 @@ export function put(data) { credentials: 'same-origin', headers: { 'Accept': 'application/json', + 'Authorization': "JWT " + session.get("access_token"), 'Content-Type': 'application/json' }, } @@ -96,6 +101,7 @@ export function destroy(data) { credentials: 'same-origin', headers: { 'Accept': 'application/json', + 'Authorization': "JWT " + session.get("access_token"), 'Content-Type': 'application/json' }, } diff --git a/animism-align/frontend/app/common/fonts.css b/animism-align/frontend/app/common/fonts.css index c782885..7d231d8 100644 --- a/animism-align/frontend/app/common/fonts.css +++ b/animism-align/frontend/app/common/fonts.css @@ -36,11 +36,13 @@ font-family: 'Roboto'; src: url('/static/fonts/Roboto-Regular.ttf') format('truetype'); } +/* @font-face { font-family: 'Roboto'; src: url('/static/fonts/Roboto-Italic.ttf') format('truetype'); font-style: italic; } +*/ /* @font-face { font-family: 'Roboto'; diff --git a/animism-align/frontend/app/common/form.component.js b/animism-align/frontend/app/common/form.component.js index 37ef9e0..12c56ee 100644 --- a/animism-align/frontend/app/common/form.component.js +++ b/animism-align/frontend/app/common/form.component.js @@ -238,6 +238,6 @@ export const SubmitButton = (props) => ( className={props.className ? "submit " + props.className : "submit"} onClick={props.onClick} >{props.title}</button> - {props.after && <span>{props.after}</span>} + {props.after && <span className="after">{props.after}</span>} </label> ) diff --git a/animism-align/frontend/app/common/form.css b/animism-align/frontend/app/common/form.css index 1ba81c8..c52b86f 100644 --- a/animism-align/frontend/app/common/form.css +++ b/animism-align/frontend/app/common/form.css @@ -37,6 +37,9 @@ form label.textarea { form input[type="checkbox"] { margin: -0.0625rem 0.625rem 0 8rem; } +form label span.after { + margin-left: 1rem; +} /* form errors */ diff --git a/animism-align/frontend/app/session.js b/animism-align/frontend/app/session.js index be93952..0050d69 100644 --- a/animism-align/frontend/app/session.js +++ b/animism-align/frontend/app/session.js @@ -12,8 +12,6 @@ export const getDefaultInt = (key, def) => { export const getDefaultFloat = (key, def) => { return parseFloat(getDefault(key, def), 10) } - -const username = session.get('username') -if (!username) { - session.set('username', 'anonymous') +export const getDefaultBoolean = (key, def) => { + return session.get(key) === String(def) } diff --git a/animism-align/frontend/app/store.js b/animism-align/frontend/app/store.js index d1843f7..df8b835 100644 --- a/animism-align/frontend/app/store.js +++ b/animism-align/frontend/app/store.js @@ -3,7 +3,6 @@ import { connectRouter, routerMiddleware } from 'connected-react-router' import { createBrowserHistory } from 'history' // import createDebounce from 'redux-debounced' import thunk from 'redux-thunk' -// import { login } from 'app/utils' // cms import uploadReducer from 'app/views/upload/upload.reducer' @@ -12,6 +11,7 @@ import siteReducer from 'app/views/site/site.reducer' import mediaReducer from 'app/views/media/media.reducer' import episodeReducer from 'app/views/episode/episode.reducer' import venueReducer from 'app/views/venue/venue.reducer' +import authReducer from 'app/views/auth/auth.reducer' // editor import alignReducer from 'app/views/align/align.reducer' @@ -23,7 +23,6 @@ import viewerReducer from 'app/views/viewer/viewer.reducer' const createRootReducer = history => ( combineReducers({ - auth: (state = {}) => state, router: connectRouter(history), site: siteReducer, @@ -32,6 +31,7 @@ const createRootReducer = history => ( media: mediaReducer, episode: episodeReducer, venue: venueReducer, + auth: authReducer, align: alignReducer, paragraph: paragraphReducer, diff --git a/animism-align/frontend/app/types.js b/animism-align/frontend/app/types.js index 943527c..d4d701a 100644 --- a/animism-align/frontend/app/types.js +++ b/animism-align/frontend/app/types.js @@ -1,8 +1,6 @@ import { with_type, crud_type } from 'app/api/crud.types' export const api = crud_type('api', []) -export const auth = crud_type('auth', []) - export const upload = crud_type('upload', []) export const media = crud_type('media', []) export const peaks = crud_type('peaks', []) @@ -10,6 +8,7 @@ export const text = crud_type('text', []) export const annotation = crud_type('annotation', []) export const episode = crud_type('episode', []) export const venue = crud_type('venue', []) +export const user = crud_type('user', []) export const paragraph = crud_type('paragraph', [ 'update_transcript', ]) @@ -37,6 +36,10 @@ export const viewer = with_type('viewer', [ 'open_footnote', ]) +export const auth = with_type('auth', [ + 'logged_in', 'logged_out', +]) + export const site = with_type('site', [ ]) diff --git a/animism-align/frontend/app/views/audio/audio.reducer.js b/animism-align/frontend/app/views/audio/audio.reducer.js index bd6bbae..269ad44 100644 --- a/animism-align/frontend/app/views/audio/audio.reducer.js +++ b/animism-align/frontend/app/views/audio/audio.reducer.js @@ -10,7 +10,7 @@ const initialState = { cc: true, } -export default function alignReducer(state = initialState, action) { +export default function authReducer(state = initialState, action) { // console.log(action.type, action) switch (action.type) { case types.audio.play: diff --git a/animism-align/frontend/app/views/auth/auth.actions.js b/animism-align/frontend/app/views/auth/auth.actions.js index d7663b7..936e062 100644 --- a/animism-align/frontend/app/views/auth/auth.actions.js +++ b/animism-align/frontend/app/views/auth/auth.actions.js @@ -1,12 +1,14 @@ import fetch from 'node-fetch' +import jsonwebtoken from 'jsonwebtoken' +import * as types from 'app/types' import { session } from 'app/session' const urls = { login: "/api/v1/auth/login", } -export const login = (data) => dispatch => ( +export const login = data => dispatch => ( fetch(urls.login, { method: 'POST', body: JSON.stringify(data), @@ -18,16 +20,26 @@ export const login = (data) => dispatch => ( }) .then(req => req.json()) .then(res => { - if (res.access_token) { - session.set('access_token', res.access_token) + if (!res.access_token) { + throw new Error(res.description) } - return res - }) - .catch(error => { - console.error(error) + session.set('access_token', res.access_token) + load_access_token()(dispatch) }) ) +export const load_access_token = () => dispatch => { + const access_token = session.get('access_token') || null + if (access_token) { + const creds = jsonwebtoken.decode(access_token) + const user_id = creds.identity + return dispatch({ type: types.auth.logged_in, user_id }) + } else { + return dispatch({ type: types.auth.logged_out }) + } +} + export const logout = () => dispatch => { session.set('access_token', '') + return dispatch({ type: types.auth.logged_out }) } diff --git a/animism-align/frontend/app/views/auth/auth.css b/animism-align/frontend/app/views/auth/auth.css index e9ceb85..2bf5053 100644 --- a/animism-align/frontend/app/views/auth/auth.css +++ b/animism-align/frontend/app/views/auth/auth.css @@ -2,13 +2,13 @@ position: fixed; top: 0; left: 0; width: 100%; height: 100%; - background: linear-gradient(#190051, #1c0406); + background: linear-gradient(#190051, #020800); display: flex; justify-content: center; align-items: center; } .login { - background: #333; + background: #282828; padding: 1rem; box-shadow: 0 5px 10px #000; } diff --git a/animism-align/frontend/app/views/auth/auth.gate.js b/animism-align/frontend/app/views/auth/auth.gate.js index 498d32a..ba69256 100644 --- a/animism-align/frontend/app/views/auth/auth.gate.js +++ b/animism-align/frontend/app/views/auth/auth.gate.js @@ -1,4 +1,5 @@ import React, { Component } from 'react' +import { connect } from 'react-redux' import './auth.css' @@ -6,24 +7,49 @@ import actions from 'app/actions' import AuthLogin from './auth.login' -export default class AuthGate extends Component { +class AuthGate extends Component { constructor(props) { super(props) + actions.auth.load_access_token() } - componentDidMount() { - // actions.site.loadProject() + componentDidUpdate(prevProps) { + if (this.props.user_id !== prevProps.user_id) { + this.load() + } } - componentDidUpdate() { - + load() { + if (!this.props.user_id) return + actions.user.show(this.props.user_id) + .then(() => { + actions.site.loadProject() + }).catch(error => { + if (error.status_code === 401) { + actions.auth.logout() + } else { + console.error(error) + } + }) } render() { + if (this.props.user) { + return this.props.children + } return ( <div className='auth'> - <AuthLogin /> + {this.props.logged_in + ? <div className="login">Logging in...</div> + : <AuthLogin onAuthenticate={this.load} /> + } </div> ) } -}
\ No newline at end of file +} + +const mapStateToProps = state => ({ + ...state.auth, +}) + +export default connect(mapStateToProps)(AuthGate) diff --git a/animism-align/frontend/app/views/auth/auth.login.js b/animism-align/frontend/app/views/auth/auth.login.js index 9ba4c0b..6697901 100644 --- a/animism-align/frontend/app/views/auth/auth.login.js +++ b/animism-align/frontend/app/views/auth/auth.login.js @@ -22,7 +22,6 @@ export default class AuthLogin extends Component { handleChange(e) { e && e.preventDefault() - console.log(e.target.name, e.target.value) this.setState({ data: { ...this.state.data, @@ -34,14 +33,11 @@ export default class AuthLogin extends Component { handleSubmit(e) { e && e.preventDefault() this.setState({ error: null }) - actions.auth.login(this.state) - .then(res => { - console.log(res) - if (res.error) { - this.props.onAuthenticate() - } else { - this.setState({ error }) - } + actions.auth.login(this.state.data) + .then(this.props.onAuthenticate) + .catch(error => { + console.error(error) + this.setState({ error: error.description || error.message }) }) } @@ -49,7 +45,7 @@ export default class AuthLogin extends Component { return ( <form className='login' onSubmit={this.handleSubmit}> <h6> - Welcome to the Animism Editor + {'Welcome to Animism '}{'🐍'} </h6> <TextInput title="Username" diff --git a/animism-align/frontend/app/views/auth/auth.reducer.js b/animism-align/frontend/app/views/auth/auth.reducer.js new file mode 100644 index 0000000..05741f3 --- /dev/null +++ b/animism-align/frontend/app/views/auth/auth.reducer.js @@ -0,0 +1,40 @@ +import * as types from 'app/types' +import { getDefault } from 'app/session' + +const initialState = { + logged_in: !!getDefault('access_token', false), + user_id: null, + user: null, +} + +export default function authReducer(state = initialState, action) { + // console.log(action.type, action) + switch (action.type) { + case types.auth.logged_in: + return { + ...state, + logged_in: true, + user_id: action.user_id, + user: null, + } + case types.auth.logged_out: + return { + ...state, + logged_in: false, + user_id: null, + user: null, + } + case types.user.show: + if (action.data.res.id !== state.user_id) { + return state + } + return { + ...state, + user: { + ...action.data.res, + } + } + default: + return state + } +} |
