diff --git a/client/src/views/common/UploadImage.js b/client/src/views/common/UploadImage.js index 94a9c9643..9aff6ce47 100644 --- a/client/src/views/common/UploadImage.js +++ b/client/src/views/common/UploadImage.js @@ -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 (
- {avatar === null && this.state.src === null && ( - -

Drag-and-drop team avater here or click to select files.
Only *.jpeg and *.png images

-
- )} - {(avatar !== null || this.state.src !== null) && ( - - {(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) + + {(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 ( +
+ +
+ ) + } else { + return ( +
- + {(this.state.change || (avatar === null && banner === null && this.state.src === null)) && ( + + this.onDrop(e, this.state.edit)} + className={classes.dropzone} + multiple={false} + > +

Drag-and-drop team {this.state.edit} here or click to select files.
Only *.jpeg and *.png images

+
+ +
+ )} + {!this.state.change && ( + + Banner Crop Preview + + + )} + {!this.state.change && ( + + AvatarCrop Preview + + + )} + {(this.state.avatarImage !== null || this.state.bannerImage !== null) && ( + Click Update Team to save image changes! + )}
- ) - } else { - return ( - -
- Crop Preview -
- -
- -
+ +
+ +
+ {this.state.src !== null && ( { this.cropper = ref }} onImgLoad={this.onImageLoaded} + ratio={this.state.edit === 'avatar' ? 1 : 4} /> -
-
- - - - -
-
- - ) - } - }} - - )} + )} +
+
+ + + + +
+
+
+ ) + } + }} +
) } - 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) diff --git a/client/src/views/dashboard/components/ManageTeamDetails.js b/client/src/views/dashboard/components/ManageTeamDetails.js index b4a736166..33eac05ed 100644 --- a/client/src/views/dashboard/components/ManageTeamDetails.js +++ b/client/src/views/dashboard/components/ManageTeamDetails.js @@ -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 (
- + })} + + + + + +
diff --git a/client/src/views/dashboard/components/ManageTeamEdit.js b/client/src/views/dashboard/components/ManageTeamEdit.js index 5a257cb4a..6cb6596b8 100644 --- a/client/src/views/dashboard/components/ManageTeamEdit.js +++ b/client/src/views/dashboard/components/ManageTeamEdit.js @@ -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 - +