Client: upload a zip archive containing gpx files
This commit is contained in:
		| @@ -2,7 +2,7 @@ import mpwoGenericApi from '../mwpoApi' | ||||
| import mpwoApi from '../mwpoApi/activities' | ||||
| import { history } from '../index' | ||||
| import { formatChartData } from '../utils' | ||||
| import { setError } from './index' | ||||
| import { setError, setLoading } from './index' | ||||
|  | ||||
| export const pushActivities = activities => ({ | ||||
|   type: 'PUSH_ACTIVITIES', | ||||
| @@ -23,10 +23,17 @@ export const addActivity = form => dispatch => mpwoApi | ||||
|   .addActivity(form) | ||||
|   .then(ret => { | ||||
|     if (ret.status === 'created') { | ||||
|       history.push(`/activities/${ret.data.activities[0].id}`) | ||||
|       if (ret.data.activities.length === 0) { | ||||
|           dispatch(setError('activities: no correct file')) | ||||
|       } else if (ret.data.activities.length === 1) { | ||||
|           history.push(`/activities/${ret.data.activities[0].id}`) | ||||
|       } else { // ret.data.activities.length > 1 | ||||
|           history.push('/') | ||||
|       } | ||||
|     } else { | ||||
|       dispatch(setError(`activities: ${ret.message}`)) | ||||
|     } | ||||
|     dispatch(setLoading()) | ||||
|   }) | ||||
|   .catch(error => dispatch(setError(`activities: ${error}`))) | ||||
|  | ||||
| @@ -97,6 +104,7 @@ export const editActivity = form => dispatch => mpwoGenericApi | ||||
|     } else { | ||||
|       dispatch(setError(`activities: ${ret.message}`)) | ||||
|     } | ||||
|     dispatch(setLoading()) | ||||
|   }) | ||||
|   .catch(error => dispatch(setError(`activities: ${error}`))) | ||||
|  | ||||
|   | ||||
| @@ -13,6 +13,10 @@ export const setError = message => ({ | ||||
|   message, | ||||
| }) | ||||
|  | ||||
| export const setLoading = () => ({ | ||||
|   type: 'SET_LOADING', | ||||
| }) | ||||
|  | ||||
| export const getData = (target, id = null, data = null) => dispatch => { | ||||
|   if (id !== null && isNaN(id)) { | ||||
|     return dispatch(setError(target, `${target}: Incorrect id`)) | ||||
|   | ||||
| @@ -1,10 +1,11 @@ | ||||
| import React from 'react' | ||||
| import { Helmet } from 'react-helmet' | ||||
| import { connect } from 'react-redux' | ||||
|  | ||||
| import FormWithGpx from './ActivityForms/FormWithGpx' | ||||
| import FormWithoutGpx from './ActivityForms/FormWithoutGpx' | ||||
|  | ||||
| export default class ActivityAddEdit extends React.Component { | ||||
| class ActivityAddEdit extends React.Component { | ||||
|   constructor(props, context) { | ||||
|     super(props, context) | ||||
|     this.state = { | ||||
| @@ -21,7 +22,7 @@ export default class ActivityAddEdit extends React.Component { | ||||
|   } | ||||
|  | ||||
|   render() { | ||||
|     const { activity, message, sports } = this.props | ||||
|     const { activity, loading, message, sports } = this.props | ||||
|     const { withGpx } = this.state | ||||
|     return ( | ||||
|       <div> | ||||
| @@ -59,6 +60,7 @@ export default class ActivityAddEdit extends React.Component { | ||||
|                             <input | ||||
|                               type="radio" | ||||
|                               name="withGpx" | ||||
|                               disabled={loading} | ||||
|                               checked={withGpx} | ||||
|                               onChange={event => this.handleRadioChange(event)} | ||||
|                             /> | ||||
| @@ -70,6 +72,7 @@ export default class ActivityAddEdit extends React.Component { | ||||
|                             <input | ||||
|                               type="radio" | ||||
|                               name="withoutGpx" | ||||
|                               disabled={loading} | ||||
|                               checked={!withGpx} | ||||
|                               onChange={event => this.handleRadioChange(event)} | ||||
|                             /> | ||||
| @@ -96,3 +99,8 @@ export default class ActivityAddEdit extends React.Component { | ||||
|   } | ||||
| } | ||||
|  | ||||
| export default connect( | ||||
|   state => ({ | ||||
|     loading: state.loading | ||||
|   }), | ||||
| )(ActivityAddEdit) | ||||
|   | ||||
| @@ -1,12 +1,15 @@ | ||||
| import React from 'react' | ||||
| import { connect } from 'react-redux' | ||||
|  | ||||
| import { setLoading } from '../../../actions/index' | ||||
| import { addActivity, editActivity } from '../../../actions/activities' | ||||
| import { history } from '../../../index' | ||||
|  | ||||
|  | ||||
| function FormWithGpx (props) { | ||||
|   const { activity, onAddActivity, onEditActivity, sports } = props | ||||
|   const { | ||||
|     activity, loading, onAddActivity, onEditActivity, sports | ||||
|   } = props | ||||
|   const sportId = activity ? activity.sport_id : '' | ||||
|   return ( | ||||
|     <form | ||||
| @@ -20,6 +23,7 @@ function FormWithGpx (props) { | ||||
|           <select | ||||
|             className="form-control input-lg" | ||||
|             defaultValue={sportId} | ||||
|             disabled={loading} | ||||
|             name="sport" | ||||
|             required | ||||
|           > | ||||
| @@ -39,6 +43,7 @@ function FormWithGpx (props) { | ||||
|             <input | ||||
|               name="title" | ||||
|               defaultValue={activity ? activity.title : ''} | ||||
|               disabled={loading} | ||||
|               className="form-control input-lg" | ||||
|             /> | ||||
|           </label> | ||||
| @@ -46,10 +51,12 @@ function FormWithGpx (props) { | ||||
|       ) : ( | ||||
|         <div className="form-group"> | ||||
|           <label> | ||||
|             GPX file: | ||||
|             <strong>gpx</strong> file or <strong>zip</strong>{' '} | ||||
|             file containing <strong>gpx</strong> (no folder inside): | ||||
|             <input | ||||
|               accept=".gpx" | ||||
|               accept=".gpx, .zip" | ||||
|               className="form-control input-lg" | ||||
|               disabled={loading} | ||||
|               name="gpxFile" | ||||
|               required | ||||
|               type="file" | ||||
| @@ -57,30 +64,39 @@ function FormWithGpx (props) { | ||||
|           </label> | ||||
|         </div> | ||||
|       )} | ||||
|       <input | ||||
|         type="submit" | ||||
|         className="btn btn-primary btn-lg btn-block" | ||||
|         onClick={ | ||||
|           event => activity | ||||
|           ? onEditActivity(event, activity) | ||||
|           : onAddActivity(event) | ||||
|         } | ||||
|         value="Submit" | ||||
|       /> | ||||
|       <input | ||||
|         type="submit" | ||||
|         className="btn btn-secondary btn-lg btn-block" | ||||
|         onClick={() => history.go(-1)} | ||||
|         value="Cancel" | ||||
|       /> | ||||
|       {loading ? ( | ||||
|         <div className="loader" /> | ||||
|       ) : ( | ||||
|         <div> | ||||
|           <input | ||||
|             type="submit" | ||||
|             className="btn btn-primary btn-lg btn-block" | ||||
|             onClick={ | ||||
|               event => activity | ||||
|                 ? onEditActivity(event, activity) | ||||
|                 : onAddActivity(event) | ||||
|             } | ||||
|             value="Submit" | ||||
|           /> | ||||
|           <input | ||||
|             type="submit" | ||||
|             className="btn btn-secondary btn-lg btn-block" | ||||
|             onClick={() => history.go(-1)} | ||||
|             value="Cancel" | ||||
|           /> | ||||
|         </div> | ||||
|       )} | ||||
|     </form> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| export default connect( | ||||
|   () => ({ }), | ||||
|   state => ({ | ||||
|     loading: state.loading | ||||
|   }), | ||||
|   dispatch => ({ | ||||
|     onAddActivity: e => { | ||||
|       dispatch(setLoading()) | ||||
|       const form = new FormData() | ||||
|       form.append('file', e.target.form.gpxFile.files[0]) | ||||
|       form.append( | ||||
| @@ -89,6 +105,7 @@ export default connect( | ||||
|       dispatch(addActivity(form)) | ||||
|     }, | ||||
|     onEditActivity: (e, activity) => { | ||||
|       dispatch(setLoading()) | ||||
|       dispatch(editActivity({ | ||||
|         id: activity.id, | ||||
|         sport_id: +e.target.form.sport.value, | ||||
|   | ||||
| @@ -173,3 +173,18 @@ input, textarea { | ||||
| .unlink { | ||||
|   color: black; | ||||
| } | ||||
|  | ||||
| .loader { | ||||
|   animation: spin 2s linear infinite; | ||||
|   border: 16px solid #f3f3f3; | ||||
|   border-top: 16px solid #3498db; | ||||
|   border-radius: 50%; | ||||
|   height: 120px; | ||||
|   margin-left: 41%; | ||||
|   width: 120px; | ||||
| } | ||||
|  | ||||
| @keyframes spin { | ||||
|   0% { transform: rotate(0deg); } | ||||
|   100% { transform: rotate(360deg); } | ||||
| } | ||||
|   | ||||
| @@ -93,6 +93,15 @@ const gpx = (state = initial.gpx, action) => { | ||||
|   } | ||||
| } | ||||
|  | ||||
| const loading = (state = initial.loading, action) => { | ||||
|   switch (action.type) { | ||||
|     case 'SET_LOADING': | ||||
|       return !state | ||||
|     default: | ||||
|       return state | ||||
|   } | ||||
| } | ||||
|  | ||||
| const message = (state = initial.message, action) => { | ||||
|   switch (action.type) { | ||||
|     case 'AUTH_ERROR': | ||||
| @@ -176,6 +185,7 @@ const reducers = combineReducers({ | ||||
|   formData, | ||||
|   formProfile, | ||||
|   gpx, | ||||
|   loading, | ||||
|   message, | ||||
|   messages, | ||||
|   records, | ||||
|   | ||||
| @@ -44,6 +44,7 @@ export default { | ||||
|   chartData: [], | ||||
|   // check if storing gpx content is OK | ||||
|   gpx: null, | ||||
|   loading: false, | ||||
|   records: { | ||||
|     ...emptyData, | ||||
|   }, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user