mirror of
https://github.com/ascribe/onion.git
synced 2024-12-22 17:33:14 +01:00
Remove ReactS3FineUploader's dependency on react-router's location
ReactS3FineUploader used to check the current url’s query params to determine which method it should use to upload, but this decision means the component is tightly coupled with react-router and history.js. A major pain point is having to propagate the location prop all the way down to this component even when it’s not necessary. Now, ReactS3FineUploader’s parent elements can either parse the current query params themselves or, if they have a location from react-router, simply use the location. Added a few utils to help parse url params.
This commit is contained in:
parent
e145e50228
commit
d23331d9b9
@ -20,8 +20,7 @@ let FurtherDetailsFileuploader = React.createClass({
|
||||
submitFile: React.PropTypes.func,
|
||||
isReadyForFormSubmission: React.PropTypes.func,
|
||||
editable: React.PropTypes.bool,
|
||||
multiple: React.PropTypes.bool,
|
||||
location: React.PropTypes.object
|
||||
multiple: React.PropTypes.bool
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
@ -89,11 +88,10 @@ let FurtherDetailsFileuploader = React.createClass({
|
||||
}}
|
||||
areAssetsDownloadable={true}
|
||||
areAssetsEditable={this.props.editable}
|
||||
multiple={this.props.multiple}
|
||||
location={this.props.location}/>
|
||||
multiple={this.props.multiple} />
|
||||
</Property>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default FurtherDetailsFileuploader;
|
||||
export default FurtherDetailsFileuploader;
|
||||
|
@ -28,8 +28,7 @@ let CreateContractForm = React.createClass({
|
||||
fileClassToUpload: React.PropTypes.shape({
|
||||
singular: React.PropTypes.string,
|
||||
plural: React.PropTypes.string
|
||||
}),
|
||||
location: React.PropTypes.object
|
||||
})
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
@ -87,8 +86,7 @@ let CreateContractForm = React.createClass({
|
||||
areAssetsEditable={true}
|
||||
setIsUploadReady={this.setIsUploadReady}
|
||||
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
|
||||
fileClassToUpload={this.props.fileClassToUpload}
|
||||
location={this.props.location}/>
|
||||
fileClassToUpload={this.props.fileClassToUpload} />
|
||||
</Property>
|
||||
<Property
|
||||
name='name'
|
||||
@ -110,4 +108,4 @@ let CreateContractForm = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
export default CreateContractForm;
|
||||
export default CreateContractForm;
|
||||
|
@ -26,12 +26,15 @@ let RegisterPieceForm = React.createClass({
|
||||
isFineUploaderActive: React.PropTypes.bool,
|
||||
isFineUploaderEditable: React.PropTypes.bool,
|
||||
enableLocalHashing: React.PropTypes.bool,
|
||||
children: React.PropTypes.element,
|
||||
onLoggedOut: React.PropTypes.func,
|
||||
|
||||
// For this form to work with SlideContainer, we sometimes have to disable it
|
||||
disabled: React.PropTypes.bool,
|
||||
location: React.PropTypes.object
|
||||
location: React.PropTypes.object,
|
||||
children: React.PropTypes.oneOfType([
|
||||
React.PropTypes.arrayOf(React.PropTypes.element),
|
||||
React.PropTypes.element
|
||||
])
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
@ -116,7 +119,7 @@ let RegisterPieceForm = React.createClass({
|
||||
onLoggedOut={this.props.onLoggedOut}
|
||||
disabled={!this.props.isFineUploaderEditable}
|
||||
enableLocalHashing={enableLocalHashing}
|
||||
location={this.props.location}/>
|
||||
uploadMethod={this.props.location.query.method} />
|
||||
</Property>
|
||||
<Property
|
||||
name='artist_name'
|
||||
|
@ -37,6 +37,7 @@ let InputFineUploader = React.createClass({
|
||||
onLoggedOut: React.PropTypes.func,
|
||||
|
||||
enableLocalHashing: React.PropTypes.bool,
|
||||
uploadMethod: React.PropTypes.string,
|
||||
|
||||
// provided by Property
|
||||
disabled: React.PropTypes.bool,
|
||||
@ -46,8 +47,7 @@ let InputFineUploader = React.createClass({
|
||||
fileClassToUpload: React.PropTypes.shape({
|
||||
singular: React.PropTypes.string,
|
||||
plural: React.PropTypes.string
|
||||
}),
|
||||
location: React.PropTypes.object
|
||||
})
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
@ -107,10 +107,10 @@ let InputFineUploader = React.createClass({
|
||||
}}
|
||||
onInactive={this.props.onLoggedOut}
|
||||
enableLocalHashing={this.props.enableLocalHashing}
|
||||
fileClassToUpload={this.props.fileClassToUpload}
|
||||
location={this.props.location}/>
|
||||
uploadMethod={this.props.uploadMethod}
|
||||
fileClassToUpload={this.props.fileClassToUpload} />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default InputFineUploader;
|
||||
export default InputFineUploader;
|
||||
|
@ -20,8 +20,7 @@ import { getLangText } from '../../utils/lang_utils';
|
||||
|
||||
let ContractSettingsUpdateButton = React.createClass({
|
||||
propTypes: {
|
||||
contract: React.PropTypes.object,
|
||||
location: React.PropTypes.object
|
||||
contract: React.PropTypes.object
|
||||
},
|
||||
|
||||
submitFile(file) {
|
||||
@ -90,10 +89,9 @@ let ContractSettingsUpdateButton = React.createClass({
|
||||
plural: getLangText('UPDATE')
|
||||
}}
|
||||
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
|
||||
submitFile={this.submitFile}
|
||||
location={this.props.location}/>
|
||||
submitFile={this.submitFile} />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default ContractSettingsUpdateButton;
|
||||
export default ContractSettingsUpdateButton;
|
||||
|
@ -27,6 +27,7 @@ let FileDragAndDrop = React.createClass({
|
||||
areAssetsEditable: React.PropTypes.bool,
|
||||
|
||||
enableLocalHashing: React.PropTypes.bool,
|
||||
uploadMethod: React.PropTypes.string,
|
||||
|
||||
// triggers a FileDragAndDrop-global spinner
|
||||
hashingProgress: React.PropTypes.number,
|
||||
@ -41,8 +42,7 @@ let FileDragAndDrop = React.createClass({
|
||||
plural: React.PropTypes.string
|
||||
}),
|
||||
|
||||
allowedExtensions: React.PropTypes.string,
|
||||
location: React.PropTypes.object
|
||||
allowedExtensions: React.PropTypes.string
|
||||
},
|
||||
|
||||
handleDragOver(event) {
|
||||
@ -137,19 +137,19 @@ let FileDragAndDrop = React.createClass({
|
||||
},
|
||||
|
||||
render: function () {
|
||||
let { filesToUpload,
|
||||
dropzoneInactive,
|
||||
className,
|
||||
hashingProgress,
|
||||
handleCancelHashing,
|
||||
multiple,
|
||||
enableLocalHashing,
|
||||
fileClassToUpload,
|
||||
areAssetsDownloadable,
|
||||
areAssetsEditable,
|
||||
allowedExtensions,
|
||||
location
|
||||
} = this.props;
|
||||
const {
|
||||
filesToUpload,
|
||||
dropzoneInactive,
|
||||
className,
|
||||
hashingProgress,
|
||||
handleCancelHashing,
|
||||
multiple,
|
||||
enableLocalHashing,
|
||||
uploadMethod,
|
||||
fileClassToUpload,
|
||||
areAssetsDownloadable,
|
||||
areAssetsEditable,
|
||||
allowedExtensions } = this.props;
|
||||
|
||||
// has files only is true if there are files that do not have the status deleted or canceled
|
||||
let hasFiles = filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1).length > 0;
|
||||
@ -185,8 +185,8 @@ let FileDragAndDrop = React.createClass({
|
||||
hasFiles={hasFiles}
|
||||
onClick={this.handleOnClick}
|
||||
enableLocalHashing={enableLocalHashing}
|
||||
fileClassToUpload={fileClassToUpload}
|
||||
location={location}/>
|
||||
uploadMethod={uploadMethod}
|
||||
fileClassToUpload={fileClassToUpload} />
|
||||
<FileDragAndDropPreviewIterator
|
||||
files={filesToUpload}
|
||||
handleDeleteFile={this.handleDeleteFile}
|
||||
|
@ -3,30 +3,28 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import { getLangText } from '../../../utils/lang_utils';
|
||||
import { dragAndDropAvailable } from '../../../utils/feature_detection_utils';
|
||||
|
||||
|
||||
import { getLangText } from '../../../utils/lang_utils';
|
||||
import { getCurrentQueryParams } from '../../../utils/url_utils';
|
||||
|
||||
let FileDragAndDropDialog = React.createClass({
|
||||
propTypes: {
|
||||
hasFiles: React.PropTypes.bool,
|
||||
multipleFiles: React.PropTypes.bool,
|
||||
onClick: React.PropTypes.func,
|
||||
enableLocalHashing: React.PropTypes.bool,
|
||||
uploadMethod: React.PropTypes.string,
|
||||
onClick: React.PropTypes.func,
|
||||
|
||||
// A class of a file the user has to upload
|
||||
// Needs to be defined both in singular as well as in plural
|
||||
fileClassToUpload: React.PropTypes.shape({
|
||||
singular: React.PropTypes.string,
|
||||
plural: React.PropTypes.string
|
||||
}),
|
||||
|
||||
location: React.PropTypes.object
|
||||
})
|
||||
},
|
||||
|
||||
getDragDialog(fileClass) {
|
||||
if(dragAndDropAvailable) {
|
||||
if (dragAndDropAvailable) {
|
||||
return [
|
||||
<p>{getLangText('Drag %s here', fileClass)}</p>,
|
||||
<p>{getLangText('or')}</p>
|
||||
@ -37,26 +35,31 @@ let FileDragAndDropDialog = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
const queryParams = this.props.location.query;
|
||||
const {
|
||||
hasFiles,
|
||||
multipleFiles,
|
||||
enableLocalHashing,
|
||||
uploadMethod,
|
||||
fileClassToUpload,
|
||||
onClick } = this.props;
|
||||
|
||||
if(this.props.hasFiles) {
|
||||
if (hasFiles) {
|
||||
return null;
|
||||
} else {
|
||||
if(this.props.enableLocalHashing && !queryParams.method) {
|
||||
if (enableLocalHashing && !uploadMethod) {
|
||||
const currentQueryParams = getCurrentQueryParams();
|
||||
|
||||
let queryParamsHash = Object.assign({}, queryParams);
|
||||
const queryParamsHash = Object.assign({}, currentQueryParams);
|
||||
queryParamsHash.method = 'hash';
|
||||
|
||||
let queryParamsUpload = Object.assign({}, queryParams);
|
||||
const queryParamsUpload = Object.assign({}, currentQueryParams);
|
||||
queryParamsUpload.method = 'upload';
|
||||
|
||||
let { location } = this.props;
|
||||
|
||||
return (
|
||||
<div className="file-drag-and-drop-dialog present-options">
|
||||
<p>{getLangText('Would you rather')}</p>
|
||||
<Link
|
||||
to={location.pathname}
|
||||
to={window.location.pathname}
|
||||
query={queryParamsHash}>
|
||||
<span className="btn btn-default btn-sm">
|
||||
{getLangText('Hash your work')}
|
||||
@ -64,9 +67,9 @@ let FileDragAndDropDialog = React.createClass({
|
||||
</Link>
|
||||
|
||||
<span> or </span>
|
||||
|
||||
|
||||
<Link
|
||||
to={location.pathname}
|
||||
to={window.location.pathname}
|
||||
query={queryParamsUpload}>
|
||||
<span className="btn btn-default btn-sm">
|
||||
{getLangText('Upload and hash your work')}
|
||||
@ -75,26 +78,27 @@ let FileDragAndDropDialog = React.createClass({
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
if(this.props.multipleFiles) {
|
||||
if (multipleFiles) {
|
||||
return (
|
||||
<span className="file-drag-and-drop-dialog">
|
||||
{this.getDragDialog(this.props.fileClassToUpload.plural)}
|
||||
{this.getDragDialog(fileClassToUpload.plural)}
|
||||
<span
|
||||
className="btn btn-default"
|
||||
onClick={this.props.onClick}>
|
||||
{getLangText('choose %s to upload', this.props.fileClassToUpload.plural)}
|
||||
onClick={onClick}>
|
||||
{getLangText('choose %s to upload', fileClassToUpload.plural)}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
let dialog = queryParams.method === 'hash' ? getLangText('choose a %s to hash', this.props.fileClassToUpload.singular) : getLangText('choose a %s to upload', this.props.fileClassToUpload.singular);
|
||||
const dialog = uploadMethod === 'hash' ? getLangText('choose a %s to hash', fileClassToUpload.singular)
|
||||
: getLangText('choose a %s to upload', fileClassToUpload.singular);
|
||||
|
||||
return (
|
||||
<span className="file-drag-and-drop-dialog">
|
||||
{this.getDragDialog(this.props.fileClassToUpload.singular)}
|
||||
{this.getDragDialog(fileClassToUpload.singular)}
|
||||
<span
|
||||
className="btn btn-default"
|
||||
onClick={this.props.onClick}>
|
||||
onClick={onClick}>
|
||||
{dialog}
|
||||
</span>
|
||||
</span>
|
||||
@ -105,4 +109,4 @@ let FileDragAndDropDialog = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
export default FileDragAndDropDialog;
|
||||
export default FileDragAndDropDialog;
|
||||
|
@ -18,7 +18,6 @@ import { displayValidFilesFilter, transformAllowedExtensionsToInputAcceptProp }
|
||||
import { getCookie } from '../../utils/fetch_api_utils';
|
||||
import { getLangText } from '../../utils/lang_utils';
|
||||
|
||||
|
||||
let ReactS3FineUploader = React.createClass({
|
||||
propTypes: {
|
||||
keyRoutine: React.PropTypes.shape({
|
||||
@ -107,11 +106,14 @@ let ReactS3FineUploader = React.createClass({
|
||||
// One solution we found in the process of tackling this problem was to hash
|
||||
// the file in the browser using md5 and then uploading the resulting text document instead
|
||||
// of the actual file.
|
||||
// This boolean essentially enables that behavior
|
||||
//
|
||||
// This boolean and string essentially enable that behavior.
|
||||
// Right now, we determine which upload method to use by appending a query parameter,
|
||||
// which should be passed into 'uploadMethod':
|
||||
// 'hash': upload using the hash
|
||||
// 'upload': upload full file (default if not specified)
|
||||
enableLocalHashing: React.PropTypes.bool,
|
||||
|
||||
// automatically injected by React-Router
|
||||
query: React.PropTypes.object,
|
||||
uploadMethod: React.PropTypes.string,
|
||||
|
||||
// A class of a file the user has to upload
|
||||
// Needs to be defined both in singular as well as in plural
|
||||
@ -126,9 +128,7 @@ let ReactS3FineUploader = React.createClass({
|
||||
fileInputElement: React.PropTypes.oneOfType([
|
||||
React.PropTypes.func,
|
||||
React.PropTypes.element
|
||||
]),
|
||||
|
||||
location: React.PropTypes.object
|
||||
])
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
@ -192,11 +192,11 @@ let ReactS3FineUploader = React.createClass({
|
||||
filesToUpload: [],
|
||||
uploader: new fineUploader.s3.FineUploaderBasic(this.propsToConfig()),
|
||||
csrfToken: getCookie(AppConstants.csrftoken),
|
||||
|
||||
|
||||
// -1: aborted
|
||||
// -2: uninitialized
|
||||
hashingProgress: -2,
|
||||
|
||||
|
||||
// this is for logging
|
||||
chunks: {}
|
||||
};
|
||||
@ -354,7 +354,6 @@ let ReactS3FineUploader = React.createClass({
|
||||
/* FineUploader specific callback function handlers */
|
||||
|
||||
onUploadChunk(id, name, chunkData) {
|
||||
|
||||
let chunks = this.state.chunks;
|
||||
|
||||
chunks[id + '-' + chunkData.startByte + '-' + chunkData.endByte] = {
|
||||
@ -370,10 +369,9 @@ let ReactS3FineUploader = React.createClass({
|
||||
},
|
||||
|
||||
onUploadChunkSuccess(id, chunkData, responseJson, xhr) {
|
||||
|
||||
let chunks = this.state.chunks;
|
||||
let chunkKey = id + '-' + chunkData.startByte + '-' + chunkData.endByte;
|
||||
|
||||
|
||||
if(chunks[chunkKey]) {
|
||||
chunks[chunkKey].completed = true;
|
||||
chunks[chunkKey].responseJson = responseJson;
|
||||
@ -414,7 +412,7 @@ let ReactS3FineUploader = React.createClass({
|
||||
} else {
|
||||
console.warn('You didn\'t define submitFile in as a prop in react-s3-fine-uploader');
|
||||
}
|
||||
|
||||
|
||||
// for explanation, check comment of if statement above
|
||||
if(this.props.isReadyForFormSubmission && this.props.setIsUploadReady) {
|
||||
// also, lets check if after the completion of this upload,
|
||||
@ -597,7 +595,6 @@ let ReactS3FineUploader = React.createClass({
|
||||
} else {
|
||||
throw new Error(getLangText('File upload could not be paused.'));
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
handleResumeFile(fileId) {
|
||||
@ -647,16 +644,14 @@ let ReactS3FineUploader = React.createClass({
|
||||
// md5 hash of a file locally and just upload a txt file containing that hash.
|
||||
//
|
||||
// In the view this only happens when the user is allowed to do local hashing as well
|
||||
// as when the correct query parameter is present in the url ('hash' and not 'upload')
|
||||
let queryParams = this.props.location.query;
|
||||
if(this.props.enableLocalHashing && queryParams && queryParams.method === 'hash') {
|
||||
|
||||
let convertedFilePromises = [];
|
||||
// as when the correct method prop is present ('hash' and not 'upload')
|
||||
if (this.props.enableLocalHashing && this.props.uploadMethod === 'hash') {
|
||||
const convertedFilePromises = [];
|
||||
let overallFileSize = 0;
|
||||
|
||||
// "files" is not a classical Javascript array but a Javascript FileList, therefore
|
||||
// we can not use map to convert values
|
||||
for(let i = 0; i < files.length; i++) {
|
||||
|
||||
// for calculating the overall progress of all submitted files
|
||||
// we'll need to calculate the overall sum of all files' sizes
|
||||
overallFileSize += files[i].size;
|
||||
@ -668,7 +663,6 @@ let ReactS3FineUploader = React.createClass({
|
||||
// we're using promises to handle that
|
||||
let hashedFilePromise = computeHashOfFile(files[i]);
|
||||
convertedFilePromises.push(hashedFilePromise);
|
||||
|
||||
}
|
||||
|
||||
// To react after the computation of all files, we define the resolvement
|
||||
@ -676,7 +670,6 @@ let ReactS3FineUploader = React.createClass({
|
||||
// with their txt representative
|
||||
Q.all(convertedFilePromises)
|
||||
.progress(({index, value: {progress, reject}}) => {
|
||||
|
||||
// hashing progress has been aborted from outside
|
||||
// To get out of the executing, we need to call reject from the
|
||||
// inside of the promise's execution.
|
||||
@ -696,18 +689,14 @@ let ReactS3FineUploader = React.createClass({
|
||||
// currently hashing files
|
||||
let overallHashingProgress = 0;
|
||||
for(let i = 0; i < files.length; i++) {
|
||||
|
||||
let filesSliceOfOverall = files[i].size / overallFileSize;
|
||||
overallHashingProgress += filesSliceOfOverall * files[i].progress;
|
||||
|
||||
}
|
||||
|
||||
// Multiply by 100, since react-progressbar expects decimal numbers
|
||||
this.setState({ hashingProgress: overallHashingProgress * 100});
|
||||
|
||||
})
|
||||
.then((convertedFiles) => {
|
||||
|
||||
// clear hashing progress, since its done
|
||||
this.setState({ hashingProgress: -2});
|
||||
|
||||
@ -823,20 +812,18 @@ let ReactS3FineUploader = React.createClass({
|
||||
changeSet.status = { $set: status };
|
||||
|
||||
let filesToUpload = React.addons.update(this.state.filesToUpload, { [fileId]: changeSet });
|
||||
|
||||
|
||||
this.setState({ filesToUpload });
|
||||
},
|
||||
|
||||
isDropzoneInactive() {
|
||||
let filesToDisplay = this.state.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1);
|
||||
let queryParams = this.props.location.query;
|
||||
const filesToDisplay = this.state.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1);
|
||||
|
||||
if((this.props.enableLocalHashing && !queryParams.method) || !this.props.areAssetsEditable || !this.props.multiple && filesToDisplay.length > 0) {
|
||||
if ((this.props.enableLocalHashing && !this.props.uploadMethod) || !this.props.areAssetsEditable || !this.props.multiple && filesToDisplay.length > 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
getAllowedExtensions() {
|
||||
@ -850,17 +837,16 @@ let ReactS3FineUploader = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
let {
|
||||
multiple,
|
||||
areAssetsDownloadable,
|
||||
areAssetsEditable,
|
||||
onInactive,
|
||||
enableLocalHashing,
|
||||
fileClassToUpload,
|
||||
validation,
|
||||
fileInputElement,
|
||||
location
|
||||
} = this.props;
|
||||
const {
|
||||
multiple,
|
||||
areAssetsDownloadable,
|
||||
areAssetsEditable,
|
||||
onInactive,
|
||||
enableLocalHashing,
|
||||
uploadMethod,
|
||||
fileClassToUpload,
|
||||
validation,
|
||||
fileInputElement } = this.props;
|
||||
|
||||
// Here we initialize the template that has been either provided from the outside
|
||||
// or the default input that is FileDragAndDrop.
|
||||
@ -870,8 +856,8 @@ let ReactS3FineUploader = React.createClass({
|
||||
areAssetsEditable,
|
||||
onInactive,
|
||||
enableLocalHashing,
|
||||
uploadMethod,
|
||||
fileClassToUpload,
|
||||
location,
|
||||
onDrop: this.handleUploadFile,
|
||||
filesToUpload: this.state.filesToUpload,
|
||||
handleDeleteFile: this.handleDeleteFile,
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
import requests from '../utils/requests';
|
||||
|
||||
import { generateOrderingQueryParams } from '../utils/fetch_api_utils';
|
||||
import { mergeOptions } from '../utils/general_utils';
|
||||
import { generateOrderingQueryParams } from '../utils/url_utils';
|
||||
|
||||
let EditionListFetcher = {
|
||||
/**
|
||||
|
@ -3,7 +3,7 @@
|
||||
import requests from '../utils/requests';
|
||||
|
||||
import { mergeOptions } from '../utils/general_utils';
|
||||
import { generateOrderingQueryParams } from '../utils/fetch_api_utils';
|
||||
import { generateOrderingQueryParams } from '../utils/url_utils';
|
||||
|
||||
let PieceListFetcher = {
|
||||
/**
|
||||
|
@ -2,63 +2,10 @@
|
||||
|
||||
import Q from 'q';
|
||||
|
||||
import { sanitize } from './general_utils';
|
||||
import AppConstants from '../constants/application_constants';
|
||||
|
||||
// TODO: Create Unittests that test all functions
|
||||
|
||||
/**
|
||||
* Takes a key-value object of this form:
|
||||
*
|
||||
* {
|
||||
* 'page': 1,
|
||||
* 'pageSize': 10
|
||||
* }
|
||||
*
|
||||
* and converts it to a query-parameter, which you can append to your URL.
|
||||
* The return looks like this:
|
||||
*
|
||||
* ?page=1&page_size=10
|
||||
*
|
||||
* CamelCase gets converted to snake_case!
|
||||
*
|
||||
*/
|
||||
export function argsToQueryParams(obj) {
|
||||
|
||||
obj = sanitize(obj);
|
||||
|
||||
return Object
|
||||
.keys(obj)
|
||||
.map((key, i) => {
|
||||
let s = '';
|
||||
|
||||
if(i === 0) {
|
||||
s += '?';
|
||||
} else {
|
||||
s += '&';
|
||||
}
|
||||
|
||||
let snakeCaseKey = key.replace(/[A-Z]/, (match) => '_' + match.toLowerCase());
|
||||
|
||||
return s + snakeCaseKey + '=' + encodeURIComponent(obj[key]);
|
||||
})
|
||||
.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a string and a boolean and generates a string query parameter for
|
||||
* an API call.
|
||||
*/
|
||||
export function generateOrderingQueryParams(orderBy, orderAsc) {
|
||||
let interpolation = '';
|
||||
|
||||
if(!orderAsc) {
|
||||
interpolation += '-';
|
||||
}
|
||||
|
||||
return interpolation + orderBy;
|
||||
}
|
||||
|
||||
export function status(response) {
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return response;
|
||||
@ -68,7 +15,7 @@ export function status(response) {
|
||||
|
||||
export function getCookie(name) {
|
||||
let parts = document.cookie.split(';');
|
||||
|
||||
|
||||
for(let i = 0; i < parts.length; i++) {
|
||||
if(parts[i].indexOf(AppConstants.csrftoken + '=') > -1) {
|
||||
return parts[i].split('=').pop();
|
||||
@ -111,4 +58,4 @@ export function fetchImageAsBlob(url) {
|
||||
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
import Q from 'q';
|
||||
|
||||
import { argsToQueryParams, getCookie } from '../utils/fetch_api_utils';
|
||||
|
||||
import AppConstants from '../constants/application_constants';
|
||||
|
||||
import {excludePropFromObject} from '../utils/general_utils';
|
||||
import { getCookie } from '../utils/fetch_api_utils';
|
||||
import { excludePropFromObject } from '../utils/general_utils';
|
||||
import { argsToQueryParams } from '../utils/url_utils';
|
||||
|
||||
class Requests {
|
||||
_merge(defaults, options) {
|
||||
|
83
js/utils/url_utils.js
Normal file
83
js/utils/url_utils.js
Normal file
@ -0,0 +1,83 @@
|
||||
'use strict'
|
||||
|
||||
import camelCase from 'camelcase';
|
||||
import snakeCase from 'snake-case';
|
||||
import qs from 'qs';
|
||||
|
||||
import { sanitize } from './general_utils';
|
||||
|
||||
// TODO: Create Unittests that test all functions
|
||||
|
||||
/**
|
||||
* Takes a key-value dictionary of this form:
|
||||
*
|
||||
* {
|
||||
* 'page': 1,
|
||||
* 'pageSize': 10
|
||||
* }
|
||||
*
|
||||
* and converts it to a query-parameter, which you can append to your URL.
|
||||
* The return looks like this:
|
||||
*
|
||||
* ?page=1&page_size=10
|
||||
*
|
||||
* CamelCase gets converted to snake_case!
|
||||
*
|
||||
* @param {object} obj Query params dictionary
|
||||
* @return {string} Query params string
|
||||
*/
|
||||
export function argsToQueryParams(obj) {
|
||||
const sanitizedObj = sanitize(obj);
|
||||
const queryParamObj = {};
|
||||
|
||||
Object
|
||||
.keys(sanitizedObj)
|
||||
.forEach((key) => {
|
||||
queryParamObj[snakeCase(key)] = sanitizedObj[key];
|
||||
});
|
||||
|
||||
// Use bracket arrayFormat as history.js and react-router use it
|
||||
return '?' + qs.stringify(queryParamObj, { arrayFormat: 'brackets' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current url's query params as an key-val dictionary.
|
||||
* snake_case gets converted to CamelCase!
|
||||
* @return {object} Query params dictionary
|
||||
*/
|
||||
export function getCurrentQueryParams() {
|
||||
return queryParamsToArgs(window.location.search.substring(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given query param string into a key-val dictionary.
|
||||
* snake_case gets converted to CamelCase!
|
||||
* @param {string} queryParamString Query params string
|
||||
* @return {object} Query params dictionary
|
||||
*/
|
||||
export function queryParamsToArgs(queryParamString) {
|
||||
const qsQueryParamObj = qs.parse(queryParamString);
|
||||
const camelCaseParamObj = {};
|
||||
|
||||
Object
|
||||
.keys(qsQueryParamObj)
|
||||
.forEach((key) => {
|
||||
camelCaseParamObj[camelCase(key)] = qsQueryParamObj[key];
|
||||
});
|
||||
|
||||
return camelCaseParamObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a string and a boolean and generates a string query parameter for
|
||||
* an API call.
|
||||
*/
|
||||
export function generateOrderingQueryParams(orderBy, orderAsc) {
|
||||
let interpolation = '';
|
||||
|
||||
if(!orderAsc) {
|
||||
interpolation += '-';
|
||||
}
|
||||
|
||||
return interpolation + orderBy;
|
||||
}
|
@ -46,6 +46,7 @@
|
||||
"browser-sync": "^2.7.5",
|
||||
"browserify": "^9.0.8",
|
||||
"browserify-shim": "^3.8.10",
|
||||
"camelcase": "^1.2.1",
|
||||
"classnames": "^1.2.2",
|
||||
"compression": "^1.4.4",
|
||||
"envify": "^3.4.0",
|
||||
@ -73,6 +74,7 @@
|
||||
"object-assign": "^2.0.0",
|
||||
"opn": "^3.0.2",
|
||||
"q": "^1.4.1",
|
||||
"qs": "^5.2.0",
|
||||
"raven-js": "^1.1.19",
|
||||
"react": "0.13.2",
|
||||
"react-bootstrap": "0.25.1",
|
||||
@ -83,6 +85,7 @@
|
||||
"react-textarea-autosize": "^2.5.2",
|
||||
"reactify": "^1.1.0",
|
||||
"shmui": "^0.1.0",
|
||||
"snake-case": "^1.1.1",
|
||||
"spark-md5": "~1.0.0",
|
||||
"uglifyjs": "^2.4.10",
|
||||
"vinyl-buffer": "^1.0.0",
|
||||
|
Loading…
Reference in New Issue
Block a user