diff options
| author | Noah Loomans <noahloomans@gmail.com> | 2018-01-17 16:26:04 +0100 | 
|---|---|---|
| committer | Noah Loomans <noahloomans@gmail.com> | 2018-01-17 16:26:04 +0100 | 
| commit | 1b3f4ea79f947558573fbce5a2e2d0c2c5dd6a8d (patch) | |
| tree | 52a41cbd31a69de31edf83578e3055d076fa1c3e /src/client | |
| parent | c0aa588bc8f85b13b5a55ccd6cdf11bf99048a1c (diff) | |
Add view code
Diffstat (limited to 'src/client')
| -rw-r--r-- | src/client/react/actions/view.js | 28 | ||||
| -rw-r--r-- | src/client/react/components/container/Search.js | 21 | ||||
| -rw-r--r-- | src/client/react/components/container/View.js | 70 | ||||
| -rw-r--r-- | src/client/react/components/page/User.js | 2 | ||||
| -rw-r--r-- | src/client/react/index.js | 2 | ||||
| -rw-r--r-- | src/client/react/reducers.js | 2 | ||||
| -rw-r--r-- | src/client/react/reducers/view.js | 38 | 
7 files changed, 159 insertions, 4 deletions
diff --git a/src/client/react/actions/view.js b/src/client/react/actions/view.js new file mode 100644 index 0000000..f9f0be2 --- /dev/null +++ b/src/client/react/actions/view.js @@ -0,0 +1,28 @@ +// eslint-disable-next-line import/prefer-default-export +export const fetchSchedule = user => (dispatch) => { +  dispatch({ +    type: 'VIEW/FETCH_SCHEDULE_REQUEST', +    user, +  }); + +  fetch(`/get/${user}`).then( +    // success +    (r) => { +      r.text().then((htmlStr) => { +        dispatch({ +          type: 'VIEW/FETCH_SCHEDULE_SUCCESS', +          user, +          htmlStr, +        }); +      }); +    }, + +    // error +    () => { +      dispatch({ +        type: 'VIEW/FETCH_SCHEDULE_FAILURE', +        user, +      }); +    }, +  ); +}; diff --git a/src/client/react/components/container/Search.js b/src/client/react/components/container/Search.js index 27b0563..9a99833 100644 --- a/src/client/react/components/container/Search.js +++ b/src/client/react/components/container/Search.js @@ -29,6 +29,12 @@ class Search extends React.Component {      this.props.dispatch(setUser(this.props.urlUser));    } +  componentWillReceiveProps(nextProps) { +    if (nextProps.urlUser !== this.props.urlUser) { +      this.props.dispatch(setUser(nextProps.urlUser)); +    } +  } +    onFocus() {      this.setState({        hasFocus: true, @@ -51,10 +57,18 @@ class Search extends React.Component {          case 'ArrowDown':            this.props.dispatch(changeSelectedResult(+1));            break; -        case 'Enter': -          if (this.props.selectedResult) { -            this.props.history.push(`/${this.props.selectedResult}`); +        case 'Enter': { +          const result = this.props.selectedResult || this.props.results[0]; + +          if (result === this.props.urlUser) { +            // EDGE CASE: The user is set if the user changes, but it doesn't +            // change if the result is already the one we are viewing. +            // Therefor, we need to dispatch the SET_USER command manually. +            this.props.dispatch(setUser(this.props.urlUser)); +          } else if (result) { +            this.props.history.push(`/${result}`);            } +        }            break;          default:            throw new Error('This should never happen... pls?'); @@ -100,6 +114,7 @@ class Search extends React.Component {  }  Search.propTypes = { +  results: PropTypes.arrayOf(PropTypes.string).isRequired,    selectedResult: PropTypes.string,    urlUser: PropTypes.string,    isExactMatch: PropTypes.bool.isRequired, diff --git a/src/client/react/components/container/View.js b/src/client/react/components/container/View.js new file mode 100644 index 0000000..9bac66f --- /dev/null +++ b/src/client/react/components/container/View.js @@ -0,0 +1,70 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import createDOMPurify from 'dompurify'; +import { connect } from 'react-redux'; +import { withRouter } from 'react-router-dom'; + +import { fetchSchedule } from '../../actions/view'; + +function cleanMeetingpointHTML(htmlStr) { +  const DOMPurify = createDOMPurify(window); + +  return DOMPurify.sanitize(htmlStr, { +    ADD_ATTR: ['rules'], +  }); +} + +class View extends React.Component { +  componentDidMount() { +    if (!this.loadingFinished(this.props.user)) { +      this.props.dispatch(fetchSchedule(this.props.user)); +    } +  } + +  componentWillReceiveProps(nextProps) { +    if (nextProps.user !== this.props.user && !this.loadingFinished(nextProps.user)) { +      this.props.dispatch(fetchSchedule(nextProps.user)); +    } +  } + +  loadingFinished(user) { +    return this.props.schedules.hasOwnProperty(user) && +      this.props.schedules[user].state === 'finished'; +  } + +  render() { +    if (!this.loadingFinished(this.props.user)) { +      return ( +        <div> +          Loading... +        </div> +      ); +    } + +    const cleanHTML = cleanMeetingpointHTML(this.props.schedules[this.props.user].htmlStr); + +    return ( +      // eslint-disable-next-line react/no-danger +      <div dangerouslySetInnerHTML={{ __html: cleanHTML }} /> +    ); +  } +} + +View.propTypes = { +  user: PropTypes.string, +  dispatch: PropTypes.func.isRequired, +  schedules: PropTypes.objectOf(PropTypes.shape({ +    state: PropTypes.string.isRequired, +    htmlStr: PropTypes.string, +  })).isRequired, +}; + +View.defaultProps = { +  user: null, +}; + +const mapStateToProps = state => ({ +  schedules: state.view.schedules, +}); + +export default withRouter(connect(mapStateToProps)(View)); diff --git a/src/client/react/components/page/User.js b/src/client/react/components/page/User.js index 2ad65a6..ea8cd10 100644 --- a/src/client/react/components/page/User.js +++ b/src/client/react/components/page/User.js @@ -2,6 +2,7 @@ import React from 'react';  import PropTypes from 'prop-types';  import { Redirect } from 'react-router-dom';  import Search from '../container/Search'; +import View from '../container/View';  import users from '../../users';  const App = ({ match }) => { @@ -15,6 +16,7 @@ const App = ({ match }) => {    return (      <div>        <Search urlUser={user} /> +      <View user={user} />      </div>    );  }; diff --git a/src/client/react/index.js b/src/client/react/index.js index ffa5403..a7006d4 100644 --- a/src/client/react/index.js +++ b/src/client/react/index.js @@ -13,7 +13,7 @@ import User from './components/page/User';  const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;  const store = createStore(    reducer, -  composeEnhancers(applyMiddleware(logger, thunk)), +  composeEnhancers(applyMiddleware(thunk, logger)),  );  ReactDOM.render( diff --git a/src/client/react/reducers.js b/src/client/react/reducers.js index 9fdf2c4..fb97228 100644 --- a/src/client/react/reducers.js +++ b/src/client/react/reducers.js @@ -1,8 +1,10 @@  import { combineReducers } from 'redux';  import search from './reducers/search'; +import view from './reducers/view';  const rootReducer = combineReducers({    search, +  view,  });  export default rootReducer; diff --git a/src/client/react/reducers/view.js b/src/client/react/reducers/view.js new file mode 100644 index 0000000..276d8ae --- /dev/null +++ b/src/client/react/reducers/view.js @@ -0,0 +1,38 @@ +const DEFAULT_STATE = { +  schedules: {}, +}; + +const view = (state = DEFAULT_STATE, action) => { +  switch (action.type) { +    case 'VIEW/FETCH_SCHEDULE_REQUEST': +      return { +        ...state, +        schedules: { +          ...state.schedules, +          [action.user]: { +            state: 'fetching', +          }, +        }, +      }; +    case 'VIEW/FETCH_SCHEDULE_SUCCESS': +      return { +        ...state, +        schedules: { +          ...state.schedules, +          [action.user]: { +            ...state.schedules[action.user], +            state: 'finished', +            htmlStr: action.htmlStr, +          }, +        }, +      }; +    default: +      return state; +  } +}; + +export default view; + +export const _test = { +  DEFAULT_STATE, +};  | 
