Client: Upload and crop image
This commit is contained in:
1 parent
8920be0e5a
commit
9ab6a337d3
8 files changed
+902
-193
No files matched your search
@@ -9,3 +9,5 @@ AUTH0_AUDIENCE=<auth0-audience>
|
||||
AUTH0_ISSUER=<auth0-issuer>
|
||||
AUTH0_CLIENTID=<auth0-clientid>
|
||||
AUTH0_CALLBACKURL=<auth0-callbackurl>
|
||||
|
||||
AZURE_STORAGE_CONNECTION_STRING=<azure-connections-string>
|
||||
Generated
+637
-188
File diff suppressed because it is too large.
Load diff
@@ -78,6 +78,7 @@
|
||||
"apollo-link-ws": "^1.0.8",
|
||||
"apollo-utilities": "^1.0.20",
|
||||
"auth0-lock": "^11.9.0",
|
||||
"azure-storage": "^2.10.1",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"classnames": "^2.2.6",
|
||||
"cross-fetch": "^2.2.2",
|
||||
@@ -88,8 +89,12 @@
|
||||
"react": "^16.5.0",
|
||||
"react-apollo": "^2.1.11",
|
||||
"react-dom": "^16.5.0",
|
||||
"react-dropzone": "^5.1.0",
|
||||
"react-image-crop": "^5.0.0",
|
||||
"react-image-cropper": "^1.3.0",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"recompose": "^0.30.0",
|
||||
"sharp": "^0.20.8",
|
||||
"subscriptions-transport-ws": "^0.9.14"
|
||||
},
|
||||
"standard": {
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Dropzone from 'react-dropzone'
|
||||
// import ReactCrop from 'react-image-crop'
|
||||
import { Cropper } from 'react-image-cropper'
|
||||
// import azure from 'azure-storage'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
|
||||
import Dialog from '@material-ui/core/Dialog'
|
||||
import DialogActions from '@material-ui/core/DialogActions'
|
||||
import DialogContent from '@material-ui/core/DialogContent'
|
||||
import Button from '@material-ui/core/Button'
|
||||
|
||||
const styles = theme => ({
|
||||
dropzoneContainer: {
|
||||
display: 'block',
|
||||
margin: '1em',
|
||||
minWidth: 200,
|
||||
height: 200
|
||||
},
|
||||
dropzone: {
|
||||
display: 'block',
|
||||
padding: '3em',
|
||||
minWidth: 200,
|
||||
height: 200,
|
||||
borderWidth: 2,
|
||||
borderColor: '#999',
|
||||
backgroundColor: '#eee'
|
||||
},
|
||||
dropzoneText: {
|
||||
textAlign: 'center'
|
||||
},
|
||||
cropPreview: {
|
||||
width: 200,
|
||||
height: 200
|
||||
},
|
||||
cropBox: {
|
||||
width: 350,
|
||||
height: 350
|
||||
},
|
||||
reactCrop: {
|
||||
}
|
||||
})
|
||||
|
||||
class UploadImage extends Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
// this.saveToStorage = this.saveToStorage.bind(this)
|
||||
this.onDrop = this.onDrop.bind(this)
|
||||
this.handleCrop = this.handleCrop.bind(this)
|
||||
this.onCropComplete = this.onCropComplete.bind(this)
|
||||
this.onCropChange = this.onCropChange.bind(this)
|
||||
this.getCroppedImg = this.getCroppedImg.bind(this)
|
||||
this.handleClickOpen = this.handleClickOpen.bind(this)
|
||||
this.handleClose = this.handleClose.bind(this)
|
||||
|
||||
this.state = {
|
||||
open: false,
|
||||
file: null,
|
||||
src: null,
|
||||
crop: {
|
||||
x: 10,
|
||||
y: 10,
|
||||
width: 200,
|
||||
height: 200
|
||||
},
|
||||
cropped: null,
|
||||
rejected: null
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { classes, team, authId } = this.props
|
||||
console.log('UploadImage', team, authId, this.state)
|
||||
return (
|
||||
<div className={classes.dropzoneContainer}>
|
||||
{this.state.src === null && (
|
||||
<Dropzone
|
||||
accept='image/jpeg, image/png'
|
||||
onDrop={this.onDrop}
|
||||
className={classes.dropzone}
|
||||
multiple={false}
|
||||
>
|
||||
<p>Drag-and-drop team avater here or click to select files. <br /> Only *.jpeg and *.png images</p>
|
||||
</Dropzone>
|
||||
)}
|
||||
{this.state.cropped !== null && (
|
||||
<img src={this.state.cropped} className={classes.cropPreview} alt='Crop Preview' />
|
||||
)}
|
||||
{this.state.src !== null && (
|
||||
<Dialog
|
||||
open={this.state.open}
|
||||
onClose={this.handleClose}
|
||||
aria-labelledby='form-dialog-title'
|
||||
>
|
||||
<div classes={classes.dialogContainer}>
|
||||
<DialogContent>
|
||||
<div className={classes.cropBox}>
|
||||
<Cropper
|
||||
src={this.state.src}
|
||||
originX={100}
|
||||
originY={100}
|
||||
ref={ref => { this.cropper = ref }}
|
||||
onImgLoad={this.onImageLoaded}
|
||||
/>
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={this.handleClose} color='primary'>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={this.handleCrop}
|
||||
color='primary'
|
||||
>
|
||||
Crop Image
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</div>
|
||||
</Dialog>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
onDrop (file) {
|
||||
console.log('onDrop', file)
|
||||
this.onSelectFile(file)
|
||||
this.setState({
|
||||
file
|
||||
})
|
||||
}
|
||||
|
||||
onSelectFile (file) {
|
||||
if (file && file.length > 0) {
|
||||
const reader = new window.FileReader()
|
||||
reader.addEventListener(
|
||||
'load',
|
||||
() =>
|
||||
this.setState({
|
||||
src: reader.result,
|
||||
open: true
|
||||
}),
|
||||
false
|
||||
)
|
||||
reader.readAsDataURL(file[0])
|
||||
}
|
||||
}
|
||||
|
||||
handleCrop () {
|
||||
console.log('handleCrop', this.cropper)
|
||||
this.setState({ cropped: this.cropper.crop(), open: false })
|
||||
}
|
||||
|
||||
onCropComplete (crop, pixelCrop) {
|
||||
console.log('onCropComplete', crop, pixelCrop, this.state)
|
||||
const cropped = this.getCroppedImg(crop, pixelCrop, this.state.src)
|
||||
this.setState({ cropped })
|
||||
}
|
||||
|
||||
onCropChange (crop) {
|
||||
this.setState({ crop })
|
||||
}
|
||||
|
||||
getCroppedImg (image, pixelCrop, fileName) {
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = pixelCrop.width
|
||||
canvas.height = pixelCrop.height
|
||||
const ctx = canvas.getContext('2d')
|
||||
|
||||
ctx.drawImage(
|
||||
image,
|
||||
pixelCrop.x,
|
||||
pixelCrop.y,
|
||||
pixelCrop.width,
|
||||
pixelCrop.height,
|
||||
0,
|
||||
0,
|
||||
pixelCrop.width,
|
||||
pixelCrop.height
|
||||
)
|
||||
|
||||
// As Base64 string
|
||||
return canvas.toDataURL('image/jpeg')
|
||||
|
||||
// As a blob
|
||||
/*
|
||||
return new Promise((resolve, reject) => {
|
||||
canvas.toBlob(file => {
|
||||
file.name = fileName
|
||||
resolve(file)
|
||||
}, 'image/jpeg')
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
handleClickOpen () {
|
||||
this.setState({ open: true })
|
||||
}
|
||||
|
||||
handleClose () {
|
||||
this.setState({ open: false, src: null })
|
||||
}
|
||||
/*
|
||||
saveToStorage (xhr) {
|
||||
const blobService = azure.createBlobService()
|
||||
const container = ''
|
||||
const Stream = require('stream')
|
||||
const readable = new Stream.Readable()
|
||||
readable.push(xhr.responseText)
|
||||
readable.push(null)
|
||||
|
||||
blobService.createBlockBlobFromStream(
|
||||
container,
|
||||
this.state.accepted[0].name,
|
||||
readable,
|
||||
xhr.responseText.length,
|
||||
(error, result, response) => {
|
||||
if (error) return (<p>{error}</p>)
|
||||
return 'Success'
|
||||
}
|
||||
)
|
||||
xhr.send()
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
UploadImage.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
team: PropTypes.object,
|
||||
authId: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
export default withStyles(styles)(UploadImage)
|
||||
|
||||
/*
|
||||
<ReactCrop
|
||||
src={this.state.src}
|
||||
crop={this.state.crop}
|
||||
onImageLoaded={this.onImageLoaded}
|
||||
onComplete={this.onCropComplete}
|
||||
onChange={this.onCropChange}
|
||||
className={classes.reactCrop}
|
||||
imageStyle={{ display: 'block', maxWidth: '100%', maxHeight: 'stretch' }}
|
||||
/>
|
||||
*/
|
||||
@@ -1,3 +1,4 @@
|
||||
import MessageCard from './MessageCard'
|
||||
import UploadImage from './UploadImage'
|
||||
|
||||
export { MessageCard }
|
||||
export { MessageCard, UploadImage }
|
||||
@@ -20,6 +20,8 @@ import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery'
|
||||
|
||||
import { checkGraphQLError } from '../../../utils/graphql-errors'
|
||||
|
||||
import { UploadImage } from '../../common'
|
||||
|
||||
const styles = theme => ({
|
||||
dialogContainer: {
|
||||
display: 'block',
|
||||
@@ -101,6 +103,7 @@ export class ManageTeamEdit extends Component {
|
||||
Edit Team Details
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<UploadImage team={team} authId={authId} />
|
||||
<TextField
|
||||
autoFocus
|
||||
margin='dense'
|
||||
|
||||
@@ -40,7 +40,8 @@ module.exports = merge(commonConfig, {
|
||||
loader: 'style-loader!css-loader!postcss-loader!sass-loader'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
target: 'web'
|
||||
})
|
||||
|
||||
module.exports.serve = {
|
||||
|
||||
@@ -61,11 +61,12 @@ module.exports = merge(commonConfig, {
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
{loader: 'css-loader', options: { importLoaders: 2 }},
|
||||
{ loader: 'css-loader', options: { importLoaders: 2 } },
|
||||
'postcss-loader',
|
||||
{loader: 'sass-loader'}
|
||||
{ loader: 'sass-loader' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
target: 'web'
|
||||
})
|
||||
Reference in new issue
Block a user