// @flow import * as React from "react"; import { observable } from "mobx"; import { observer, inject } from "mobx-react"; import styled from "styled-components"; import Dropzone from "react-dropzone"; import LoadingIndicator from "components/LoadingIndicator"; import Flex from "components/Flex"; import Modal from "components/Modal"; import Button from "components/Button"; import AvatarEditor from "react-avatar-editor"; import { uploadFile, dataUrlToBlob } from "utils/uploadFile"; import UiStore from "stores/UiStore"; type Props = { children?: React.Node, onSuccess: string => void | Promise, onError: string => void, submitText: string, borderRadius: number, ui: UiStore, }; @observer class ImageUpload extends React.Component { @observable isUploading: boolean = false; @observable isCropping: boolean = false; @observable zoom: number = 1; file: File; avatarEditorRef: AvatarEditor; static defaultProps = { submitText: "Crop Picture", borderRadius: 150, }; onDropAccepted = async (files: File[]) => { this.isCropping = true; this.file = files[0]; }; handleCrop = () => { this.isUploading = true; // allow the UI to update before converting the canvas to a Blob // for large images this can cause the page rendering to hang. setImmediate(this.uploadImage); }; uploadImage = async () => { const canvas = this.avatarEditorRef.getImage(); const imageBlob = dataUrlToBlob(canvas.toDataURL()); try { const attachment = await uploadFile(imageBlob, { name: this.file.name, public: true, }); this.props.onSuccess(attachment.url); } catch (err) { this.props.onError(err.message); } finally { this.isUploading = false; this.isCropping = false; } }; handleClose = () => { this.isUploading = false; this.isCropping = false; }; handleZoom = (event: SyntheticDragEvent<*>) => { let target = event.target; if (target instanceof HTMLInputElement) { this.zoom = parseFloat(target.value); } }; renderCropping() { const { ui, submitText } = this.props; return ( {this.isUploading && } (this.avatarEditorRef = ref)} image={this.file} width={250} height={250} border={25} borderRadius={this.props.borderRadius} color={ ui.theme === "light" ? [255, 255, 255, 0.6] : [0, 0, 0, 0.6] } // RGBA scale={this.zoom} rotate={0} /> {this.isUploading ? "Uploading…" : submitText} ); } render() { if (this.isCropping) { return this.renderCropping(); } return ( {this.props.children} ); } } const AvatarEditorContainer = styled(Flex)` margin-bottom: 30px; `; const RangeInput = styled.input` display: block; width: 300px; margin-bottom: 30px; height: 4px; cursor: pointer; color: inherit; border-radius: 99999px; background-color: #dee1e3; appearance: none; &::-webkit-slider-thumb { -webkit-appearance: none; height: 16px; width: 16px; border-radius: 50%; background: ${props => props.theme.text}; cursor: pointer; } &:focus { outline: none; } `; const CropButton = styled(Button)` width: 300px; `; export default inject("ui")(ImageUpload);