fixed server-side diff loading

This commit is contained in:
Adam Brown 2016-12-17 10:21:00 -05:00
parent d66292047f
commit 660c6e6b19
9 changed files with 2122 additions and 2407 deletions

4434
dist/browser-bundle.js vendored

File diff suppressed because one or more lines are too long

View File

@ -18,9 +18,6 @@ const stateName = "dubdiff_state"
//from the given object //from the given object
const copyKeys = (obj, keys) => keys.reduce((acc, p)=>{acc[p]=obj[p]; return acc}, {}) const copyKeys = (obj, keys) => keys.reduce((acc, p)=>{acc[p]=obj[p]; return acc}, {})
console.log(copyKeys({a:1, b:2}, ['a']))
console.log(copyKeys({}, ['a']))
//utility method for retrieving json data from the local store //utility method for retrieving json data from the local store
function getLocalState (keys) { function getLocalState (keys) {
if (localStorage.getItem(stateName)) { if (localStorage.getItem(stateName)) {
@ -55,6 +52,7 @@ class LocalStorage extends React.Component {
//load the state from the local storage //load the state from the local storage
componentDidMount() { componentDidMount() {
//only if the status is EMPTY //only if the status is EMPTY
/*
if (this.props.input.original=='' && this.props.input.final == '') { if (this.props.input.original=='' && this.props.input.final == '') {
const localState = getLocalState(['input']) const localState = getLocalState(['input'])
if (localState.input && localState.input.original) if (localState.input && localState.input.original)
@ -62,6 +60,7 @@ class LocalStorage extends React.Component {
if (localState.input && localState.input.final) if (localState.input && localState.input.final)
this.props.onChangeFinal(localState.input.final) this.props.onChangeFinal(localState.input.final)
} }
*/
} }
//save the state to local storage //save the state to local storage
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {

View File

@ -21,6 +21,8 @@ import LocalStorage from './LocalStorage'
//initial state is rehydrated from the server //initial state is rehydrated from the server
const initialState = window.__INITIAL_STATE__ const initialState = window.__INITIAL_STATE__
console.log('initialState', initialState)
//create the redux store //create the redux store
//initial state is retrieved from localStore //initial state is retrieved from localStore
const store = Redux.createStore( const store = Redux.createStore(

View File

@ -98,9 +98,18 @@ export const save = () =>
//dispatch post request //dispatch post request
fetch(endpointUri, fetchOptions) fetch(endpointUri, fetchOptions)
.then(response => { .then(response => {
dispatch({type: 'STATUS_SET', data: Status.CLEAN}) if (response.ok)
dispatch({type: 'STATUS_SET', data: Status.CLEAN})
else {
response.text().then( (responseText) => {
const error = {message:`${response.status}: ${responseText}`}
dispatch({type: 'STATUS_SET', data: Status.DIRTY})
dispatch({type: 'STATUS_SET_ERROR', data: StatusError.SAVE_ERROR, error})
})
}
}) })
.catch(error => { .catch(error => {
//!!! could use a better error message here
dispatch({type: 'STATUS_SET', data: Status.DIRTY}) dispatch({type: 'STATUS_SET', data: Status.DIRTY})
dispatch({type: 'STATUS_SET_ERROR', data: StatusError.SAVE_ERROR, error}) dispatch({type: 'STATUS_SET_ERROR', data: StatusError.SAVE_ERROR, error})
}) })

View File

@ -13,11 +13,11 @@ const mapStateToProps = (state) => ({
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
onSave: () => dispatch(Actions.save()) onSave: () => dispatch(Actions.save()),
onReset: () => dispatch(Actions.reset())
}) })
const SaveStatus = (props) => { const SaveStatus = (props) => {
console.log(props.status)
if (props.status.type == Status.SAVING) return ( if (props.status.type == Status.SAVING) return (
<Message size='tiny' floating compact icon> <Message size='tiny' floating compact icon>
<Icon name='circle notched' loading /> <Icon name='circle notched' loading />
@ -40,6 +40,7 @@ const SaveStatus = (props) => {
<Message.Content> <Message.Content>
<Message.Header>Error saving diff</Message.Header> <Message.Header>Error saving diff</Message.Header>
{props.status.error.message} {props.status.error.message}
<br/>
<Button onClick={props.onSave}>Retry</Button> <Button onClick={props.onSave}>Retry</Button>
</Message.Content> </Message.Content>
</Message> </Message>
@ -49,7 +50,9 @@ const SaveStatus = (props) => {
<Icon name='exclamation' /> <Icon name='exclamation' />
<Message.Content> <Message.Content>
<Message.Header>Error loading diff</Message.Header> <Message.Header>Error loading diff</Message.Header>
Server returned {props.status.error} {props.status.error.message}
<br/>
<Button onClick={props.onReset}>New Diff</Button>
</Message.Content> </Message.Content>
</Message> </Message>
) )

View File

@ -19,6 +19,6 @@ export const Status = {
} }
export const StatusError = { export const StatusError = {
LOADING_ERROR: 'LOAD_ERROR', LOAD_ERROR: 'LOAD_ERROR',
SAVING_ERROR: 'SAVE_ERROR' SAVE_ERROR: 'SAVE_ERROR'
} }

View File

@ -71,5 +71,5 @@ export function status (state, action) {
else if (action.type == 'STATUS_SET_ERROR' && isValidError(action.data)) else if (action.type == 'STATUS_SET_ERROR' && isValidError(action.data))
return Object.assign({}, state, {error: action.error, hasError: true, errorType:action.data}) return Object.assign({}, state, {error: action.error, hasError: true, errorType:action.data})
else else
return {type:Status.EMPTY, hasError: false, error:null} return state || {type:Status.EMPTY, hasError: false, error:null}
} }

View File

@ -9,7 +9,8 @@ import comparisonRouter from './comparison'
import * as reducers from '../common/reducers' import * as reducers from '../common/reducers'
import * as actions from '../common/actions'
import {Status, StatusError} from '../common/constants'
import render from './render' import render from './render'
@ -27,27 +28,33 @@ app.use('/api/compare', comparisonRouter);
//the following routes are for server-side rendering of the app //the following routes are for server-side rendering of the app
//we should render the comparison directly from the server //we should render the comparison directly from the server
//this loading logic could be moved into ../common/actions because it is isomorphic
//this is garbage, we should use a robust method for loading comparisons, parallel to how saving works
//comparisons should be loaded isomorphically
app.route('/:comparisonId') app.route('/:comparisonId')
.get((req, res) => { .get((req, res) => {
const store = createSessionStore() const store = createSessionStore()
const endpointUri = `http://localhost:${PORT}/api/compare/${req.params.comparisonId}`
//fetch the comparison //fetch the comparison
fetchComparison(req.params.comparisonId) fetch(endpointUri)
.then( ({a,b}) => { .then(response => {
store.dispatch({type: 'UPDATE_ORIGINAL_INPUT', data: a}) if (response.ok)
store.dispatch({type: 'UPDATE_FINAL_INPUT', data: b}) return response.json()
render(store, req, res) else {
}) response.text().then( () => {
.catch( error => { const error = {message:`${response.status}: ${response.statusText}`}
//... what to do here? initAndRenderError(error, store, req, res)
console.log(`Error fetching comparison with id ${req.params.comparisonId}`, error) })
}) }
})
.then( ({a,b}) => {
initAndRenderComparison({a,b}, store, req, res)
})
.catch( error => {
initAndRenderError(error, store, req, res)
})
}) })
app.route('/') app.route('/')
@ -69,17 +76,15 @@ function createSessionStore() {
) )
} }
function initAndRenderComparison({a,b}, store, req, res) {
function fetchComparison(id) { store.dispatch({type: 'UPDATE_ORIGINAL_INPUT', data: a})
const endpointUri = `http://localhost:${PORT}/api/compare/${id}` store.dispatch({type: 'UPDATE_FINAL_INPUT', data: b})
const fetchOptions = { store.dispatch({type: 'STATUS_SET', data: Status.CLEAN})
method: 'GET' render(store, req, res)
}
//dispatch post request
return fetch(endpointUri, fetchOptions)
.then(response => response.json())
} }
function initAndRenderError(error, store, req, res) {
//router.get('/', controller.index); store.dispatch({type: 'STATUS_SET', data: Status.EMPTY})
store.dispatch({type: 'STATUS_SET_ERROR', data: StatusError.LOAD_ERROR, error})
render(store, req, res)
}

View File

@ -10,7 +10,6 @@ export default function render(store, req, res) {
// Send the rendered page back to the client // Send the rendered page back to the client
match({ routes, location: req.url }, (error, redirectLocation, renderProps) => { match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
if (error) { if (error) {
console.log(error)
res.status(500).send(renderError('Routing Error:', error.message)) res.status(500).send(renderError('Routing Error:', error.message))
} else if (redirectLocation) { } else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search) res.redirect(302, redirectLocation.pathname + redirectLocation.search)