Add banner image insert and update

This commit is contained in:
Barry Low committed 2018-09-21 00:45:45 -07:00
1 parent ecc538648e
commit 177f804c42
3 files changed
+298 -142

No files matched your search

+187 -96
View File
@@ -12,6 +12,7 @@ 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'
import Typography from '@material-ui/core/Typography'
import MessageCard from './MessageCard'
@@ -36,20 +37,28 @@ const styles = theme => ({
dropzoneText: {
textAlign: 'center'
},
cropPreview: {
width: 200,
height: 200
cropAvatarPreview: {
width: 100,
height: 100
},
cropBox: {
cropBannerPreview: {
width: 400,
height: 100
},
cropAvatarBox: {
width: 350,
height: 350
},
cropBannerBox: {
width: 650,
height: 350
},
reactCrop: {
}
})
const imageConfig = {
quality: 0.5,
const imageAvatarConfig = {
quality: 0.6,
maxWidth: 200,
maxHeight: 200,
autoRotate: true,
@@ -57,6 +66,15 @@ const imageConfig = {
mimeType: 'image/jpeg'
}
const imageBannerConfig = {
quality: 0.6,
maxWidth: 800,
maxHeight: 200,
autoRotate: true,
debug: true,
mimeType: 'image/jpeg'
}
class UploadImage extends Component {
constructor (props) {
super(props)
@@ -66,10 +84,14 @@ class UploadImage extends Component {
this.handleClickOpen = this.handleClickOpen.bind(this)
this.handleClose = this.handleClose.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.saveAvatar = this.saveAvatar.bind(this)
this.handleChangeImage = this.handleChangeImage.bind(this)
this.cancelChangeImage = this.cancelChangeImage.bind(this)
this.saveImage = this.saveImage.bind(this)
this.state = {
open: false,
edit: 'avatar',
change: false,
file: null,
src: null,
crop: {
@@ -82,103 +104,147 @@ class UploadImage extends Component {
cropped: null,
rejected: null,
uploaded: false,
image: ''
avatarImage: null,
bannerImage: null,
sasqr: null
}
}
render () {
const { classes, team, authId, handleAvatar } = this.props
const { classes, team, authId, handleAvatar, handleBanner } = this.props
let avatar = null
if (team.profile !== null && team.profile.avatar !== undefined && team.profile.avatar !== null && team.profile.avatar !== 'a') {
avatar = team.profile.avatar
let banner = null
if ((team.profile !== null && team.profile.avatar !== undefined && team.profile.avatar !== null && team.profile.avatar !== 'a') || this.state.avatarImage !== null) {
avatar = this.state.avatarImage !== null ? this.state.avatarImage : `${team.profile.avatar}?${Math.random()}`
} else {
if (this.state.image !== '') avatar = this.state.image
avatar = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EAvatar%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E'
}
if ((team.profile !== null && team.profile.banner !== undefined && team.profile.banner !== null && team.profile.banner !== '') || this.state.bannerImage !== null) {
banner = this.state.bannerImage !== null ? this.state.bannerImage : `${team.profile.banner}?${Math.random()}`
} else {
banner = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22200%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Asvg%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23banner_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%3E%3Ctitle%3EBanner%20Placeholder%3C%2Ftitle%3E%3Crect%20stroke%3D%22null%22%20id%3D%22svg_1%22%20fill%3D%22%2355595c%22%20height%3D%22200%22%20width%3D%22800%22%2F%3E%3Cg%20stroke%3D%22null%22%20id%3D%22banner_164edaf95ee%22%3E%3Ctext%20stroke%3D%22null%22%20id%3D%22svg_3%22%20y%3D%22106.5%22%20x%3D%22367.73438%22%3EBanner%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E'
}
console.log('UploadImage', team, avatar, authId, this.state)
let handleImage
this.state.edit === 'avatar' ? handleImage = handleAvatar : handleImage = handleBanner
console.log('UploadImage', team, avatar, banner, authId, this.state)
return (
<div className={classes.dropzoneContainer}>
{avatar === null && 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>
)}
{(avatar !== null || this.state.src !== null) && (
<Mutation mutation={GET_AZURE_SAS} >
{(getAzureSAS, { loading, error, data }) => {
console.log('getAzureSAS: ', error, data)
if (!this.state.uploaded) {
if (data !== undefined && data.getAzureSAS !== null) {
this.saveAvatar(data.getAzureSAS, team.slug, handleAvatar)
}
// this.handleSubmit(getAzureSAS, team.slug)
<Mutation mutation={GET_AZURE_SAS} >
{(getAzureSAS, { loading, error, data }) => {
console.log('getAzureSAS: ', error, data)
if (!this.state.uploaded) {
if (data !== undefined && data.getAzureSAS !== null) {
this.saveImage(data.getAzureSAS, team.slug, handleImage)
}
if (this.state.cropping) {
return (
// this.handleSubmit(getAzureSAS, team.slug)
}
if (this.state.cropping) {
return (
<div>
<MessageCard message='Cropping...' />
</div>
)
} else {
return (
<React.Fragment>
<div>
<MessageCard message='Cropping...' />
{(this.state.change || (avatar === null && banner === null && this.state.src === null)) && (
<React.Fragment>
<Dropzone
accept='image/jpeg, image/png'
onDrop={(e) => this.onDrop(e, this.state.edit)}
className={classes.dropzone}
multiple={false}
>
<p>Drag-and-drop team {this.state.edit} here or click to select files. <br /> Only *.jpeg and *.png images</p>
</Dropzone>
<Button
onClick={(e) => this.cancelChangeImage()}
color='primary'
>
Cancel Image Change
</Button>
</React.Fragment>
)}
{!this.state.change && (
<React.Fragment>
<img src={banner} className={classes.cropBannerPreview} alt='Banner Crop Preview' />
<Button
onClick={(e) => this.handleChangeImage(e, 'banner')}
color='primary'
>
Change Banner
</Button>
</React.Fragment>
)}
{!this.state.change && (
<React.Fragment>
<img src={avatar} className={classes.cropAvatarPreview} alt='AvatarCrop Preview' />
<Button
onClick={(e) => this.handleChangeImage(e, 'avatar')}
color='primary'
>
Change Avatar
</Button>
</React.Fragment>
)}
{(this.state.avatarImage !== null || this.state.bannerImage !== null) && (
<Typography variant='title' color='primary' >Click Update Team to save image changes!</Typography>
)}
</div>
)
} else {
return (
<React.Fragment>
<div>
<img src={avatar} className={classes.cropPreview} alt='Crop Preview' />
</div>
<Dialog
open={this.state.open}
onClose={this.handleClose}
aria-labelledby='form-dialog-title'
>
<div classes={classes.dialogContainer}>
<DialogContent>
<div className={classes.cropBox}>
<Dialog
open={this.state.open}
onClose={this.handleClose}
aria-labelledby='form-dialog-title'
>
<div classes={classes.dialogContainer}>
<DialogContent>
<div className={this.state.edit === 'avatar' ? classes.cropAvatarBox : classes.cropBannerBox}>
{this.state.src !== null && (
<Cropper
src={this.state.src}
originX={100}
originY={100}
ref={ref => { this.cropper = ref }}
onImgLoad={this.onImageLoaded}
ratio={this.state.edit === 'avatar' ? 1 : 4}
/>
</div>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClose} color='primary'>
Cancel
</Button>
<Button
onClick={(e) => this.handleCrop(e, getAzureSAS, team.slug, handleAvatar)}
color='primary'
>
Crop Image
</Button>
</DialogActions>
</div>
</Dialog>
</React.Fragment>
)
}
}}
</Mutation>
)}
)}
</div>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClose} color='primary'>
Cancel
</Button>
<Button
onClick={(e) => this.handleCrop(e, getAzureSAS, team.slug, handleImage)}
color='primary'
>
Crop Image
</Button>
</DialogActions>
</div>
</Dialog>
</React.Fragment>
)
}
}}
</Mutation>
</div>
)
}
onDrop (file) {
onDrop (file, editImageType) {
console.log('onDrop', file)
this.onSelectFile(file)
this.onSelectFile(file, editImageType)
this.setState({
file
})
}
onSelectFile (file) {
onSelectFile (file, editImageType) {
if (file && file.length > 0) {
const reader = new window.FileReader()
reader.addEventListener(
@@ -194,53 +260,77 @@ class UploadImage extends Component {
}
}
async handleCrop (e, getAzureSAS, teamSlug, handleAvatar) {
async handleCrop (e, getAzureSAS, teamSlug, handleImage) {
e.preventDefault()
const data = await getAzureSAS({ variables: { teamSlug } })
console.log('handleCrop', this.cropper, data)
const cropped = this.cropper.crop()
const croppedBlob = await imgSrcToBlob(cropped, 'image/png', 'Anonymous')
let imageConfig = this.state.edit === 'avatar' ? imageAvatarConfig : imageBannerConfig
let resizedBlob = await readAndCompressImage(croppedBlob, imageConfig)
return this.setState({ cropped: resizedBlob, cropping: true, open: false }, () => {
this.saveAvatar(data.getAzureSAS, teamSlug, handleAvatar)
this.saveImage(data.getAzureSAS, teamSlug, handleImage)
})
}
async handleSubmit (e, getAzureSAS, teamSlug) {
e.preventDefault()
await getAzureSAS({ variables: { teamSlug } })
let SAS = await getAzureSAS({ variables: { teamSlug } })
console.log('handleSubmit: ', await SAS)
return this.setState({ sasqr: SAS })
}
async saveAvatar (SASQueryParameters, teamSlug, handleAvatar) {
const fileName = `${teamSlug}-avatar.jpg`
console.log('saveAvatar SASurl', process.env.AZURE_SAS_URL)
const SASurl2 = `${process.env.AZURE_STORAGE_URL}?${SASQueryParameters}`
const SASurl = process.env.AZURE_SAS_URL
console.log('getAzureSAS2 SASurl: ', SASurl2, SASurl)
async saveImage (SASQueryParameters, teamSlug, handleImage) {
const fileName = this.state.edit === 'avatar' ? `${teamSlug}-avatar.jpg` : `${teamSlug}-banner.jpg`
let SASurl
if (this.state.sasqr === null) {
SASurl = process.env.AZURE_SAS_URL
} else {
SASurl = `${process.env.AZURE_STORAGE_URL}?${this.state.sasqr}`
}
console.log('getAzureSAS2 SASurl: ', SASurl, this.state.sasqr)
const pipeline = Azure.StorageURL.newPipeline(new Azure.AnonymousCredential())
const serviceURL = new Azure.ServiceURL(SASurl, pipeline)
console.log('getAzureSAS2: ', serviceURL)
const containerName = teamSlug
const containerURL = Azure.ContainerURL.fromServiceURL(serviceURL, containerName)
console.log('getAzureSAS3: ', containerURL)
const blockBlobURL = Azure.BlockBlobURL.fromContainerURL(containerURL, fileName)
console.log('getAzureSAS34: ', blockBlobURL)
const avatar = await Azure.uploadBrowserDataToBlockBlob(Azure.Aborter.None, this.state.cropped, blockBlobURL)
console.log('saveAvatar: ', avatar)
const image = `${process.env.AZURE_STORAGE_URL}${teamSlug}/${fileName}`
handleAvatar(image)
this.setState({ cropping: false, uploaded: true, image: image })
return avatar
await Azure.uploadBrowserDataToBlockBlob(Azure.Aborter.None, this.state.cropped, blockBlobURL)
const image = `${process.env.AZURE_STORAGE_URL}${teamSlug}/${fileName}?${Math.random()}`
handleImage(image)
let avatarImage = this.state.edit === 'avatar' ? image : null
let bannerImage = this.state.edit === 'banner' ? image : null
this.setState({ cropping: false, uploaded: true, change: false, avatarImage: avatarImage, bannerImage: bannerImage })
return image
}
handleClickOpen () {
this.setState({ open: true })
handleChangeImage (e, editImageType) {
e.preventDefault()
let crop = editImageType === 'avatar' ? { x: 10, y: 10, width: 200, height: 200 } : { x: 10, y: 10, width: 800, height: 200 }
this.setState({
edit: editImageType,
change: true,
crop: crop
})
}
cancelChangeImage () {
this.setState({ change: false })
}
handleClickOpen (e, editImageType) {
e.preventDefault()
this.setState({
open: true
})
}
handleClose () {
this.setState({ open: false, src: null })
this.setState({ open: false, src: null, change: false })
}
}
@@ -248,7 +338,8 @@ UploadImage.propTypes = {
classes: PropTypes.object.isRequired,
team: PropTypes.object,
authId: PropTypes.string.isRequired,
handleAvatar: PropTypes.func.isRequired
handleAvatar: PropTypes.func.isRequired,
handleBanner: PropTypes.func.isRequired
}
export default withStyles(styles)(UploadImage)
@@ -5,10 +5,11 @@ import { withStyles } from '@material-ui/core/styles'
import Button from '@material-ui/core/Button'
import DetailsIcon from '@material-ui/icons/Details'
import Dialog from '@material-ui/core/Dialog'
import DialogActions from '@material-ui/core/DialogActions'
import DialogContent from '@material-ui/core/DialogContent'
import DialogTitle from '@material-ui/core/DialogTitle'
import Typography from '@material-ui/core/Typography'
import Card from '@material-ui/core/Card'
import CardContent from '@material-ui/core/CardContent'
import CardActions from '@material-ui/core/CardActions'
import CardMedia from '@material-ui/core/CardMedia'
const styles = theme => ({
dialogContainer: {
@@ -18,6 +19,33 @@ const styles = theme => ({
},
buttonRight: {
justifyContent: 'flex-end'
},
cardGrid: {
padding: `${theme.spacing.unit * 8}px 0`
},
card: {
display: 'flex',
width: '100%'
},
cardDetails: {
display: 'flex',
flex: 1
},
cardContent: {
flex: 1
},
cardAvatarMedia: {
flex: 1,
width: 100,
height: 100,
maxWidth: 100,
justifyContent: 'flex-start'
},
cardBannerMedia: {
flex: 1,
width: 600,
height: 150,
justifyContent: 'flex-start'
}
})
@@ -35,7 +63,20 @@ export class ManageTeamDetails extends Component {
render () {
console.log(this.props.team)
const { classes } = this.props
const { name, members, profile, createdAt } = this.props.team
let avatar
let banner
if (profile.avatar !== undefined && profile.avatar !== 'a') {
avatar = profile.avatar
} else {
avatar = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EAvatar%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E'
}
if (profile.banner !== undefined && profile.banner !== 'a') {
banner = profile.banner
} else {
banner = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22200%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Asvg%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23banner_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%3E%3Ctitle%3EBanner%20Placeholder%3C%2Ftitle%3E%3Crect%20stroke%3D%22null%22%20id%3D%22svg_1%22%20fill%3D%22%2355595c%22%20height%3D%22200%22%20width%3D%22800%22%2F%3E%3Cg%20stroke%3D%22null%22%20id%3D%22banner_164edaf95ee%22%3E%3Ctext%20stroke%3D%22null%22%20id%3D%22svg_3%22%20y%3D%22106.5%22%20x%3D%22367.73438%22%3EBanner%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E'
}
return (
<div>
<Button
@@ -51,51 +92,62 @@ export class ManageTeamDetails extends Component {
onClose={this.handleClose}
aria-labelledby='form-dialog-title'
>
<div classes={this.props.classes.dialogContainer}>
<DialogTitle id='form-dialog-title'>
{name} Team Details
</DialogTitle>
<DialogContent>
<Typography
variant='display3'
align='center'
color='textPrimary'
gutterBottom
>
{name}
</Typography>
<Typography variant='subheading' color='textSecondary'>
{createdAt}
</Typography>
<Typography variant='subheading' color='textSecondary'>
Motto: {profile.motto}
</Typography>
<Typography variant='subheading' color='textSecondary'>
Description: {profile.description}
</Typography>
<Typography variant='subheading' paragraph gutterBottom>
Members: {members.length}
</Typography>
<Typography variant='subheading' color='primary'>
Team Admin:&nbsp;
{members.map(member => {
if (member.role === 'OWNER' || member.role === 'ADMIN') {
if (
member.member !== null &&
<div classes={classes.dialogContainer}>
<Card className={classes.card}>
<CardContent>
<Typography variant='title' id='form-dialog-title'>
{name} Team Details
</Typography>
<CardMedia
className={classes.cardBannerMedia}
image={banner}
title={`${name} ${banner}`}
/>
<CardMedia
className={classes.cardAvatarMedia}
image={avatar}
title={`${name} ${avatar}`}
/>
<Typography
variant='display3'
align='center'
color='textPrimary'
gutterBottom
>
{name}
</Typography>
<Typography variant='subheading' color='textSecondary'>
{createdAt}
</Typography>
<Typography variant='subheading' color='textSecondary'>
Motto: {profile.motto}
</Typography>
<Typography variant='subheading' color='textSecondary'>
Description: {profile.description}
</Typography>
<Typography variant='subheading' paragraph gutterBottom>
Members: {members.length}
</Typography>
<Typography variant='subheading' color='primary'>
Team Admin:&nbsp;
{members.map(member => {
if (member.role === 'OWNER' || member.role === 'ADMIN') {
if (member.member !== null &&
member.member.alias !== undefined &&
member.member.alias !== null
) {
return member.member.alias
) {
return member.member.alias
}
}
}
})}
</Typography>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClose} color='primary'>
Close
</Button>
</DialogActions>
})}
</Typography>
<CardActions>
<Button onClick={this.handleClose} color='primary'>
Close
</Button>
</CardActions>
</CardContent>
</Card>
</div>
</Dialog>
</div>
@@ -42,11 +42,14 @@ export class ManageTeamEdit extends Component {
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.handleAvatar = this.handleAvatar.bind(this)
this.handleBanner = this.handleBanner.bind(this)
console.log('ManageTeamEdit', props.team)
const motto = props.team.profile.motto || ''
const description = props.team.profile.description || ''
const avatar = props.team.profile.avatar || ''
const banner = props.team.profile.banner || ''
this.state = {
open: false,
motto: motto,
@@ -108,7 +111,12 @@ export class ManageTeamEdit extends Component {
Edit Team Details
</DialogTitle>
<DialogContent>
<UploadImage team={team} authId={authId} handleAvatar={this.handleAvatar} />
<UploadImage
team={team}
authId={authId}
handleAvatar={this.handleAvatar}
handleBanner={this.handleBanner}
/>
<TextField
autoFocus
margin='dense'
@@ -191,6 +199,11 @@ export class ManageTeamEdit extends Component {
this.setState({ avatar: avatarUrl })
}
handleBanner (bannerUrl) {
console.log('handleBanner: ', bannerUrl)
this.setState({ banner: bannerUrl })
}
async handleSubmit (e, updateTeamProfile, slug) {
console.log('handleSubmit: ', this.state)
e.preventDefault()