KazuyaNagase 6 лет назад
Родитель
Сommit
2b78c909c3
2 измененных файлов с 87 добавлено и 75 удалено
  1. 86 59
      src/client/js/components/ProfileImageUploader.jsx
  2. 1 16
      src/server/views/me/index.html

+ 86 - 59
src/client/js/components/ProfileImageUploader.jsx

@@ -1,7 +1,8 @@
 import React from 'react';
 import PropTypes from 'prop-types';
+import Modal from 'react-bootstrap/es/Modal';
+import Button from 'react-bootstrap/es/Button';
 import { withTranslation } from 'react-i18next';
-
 import ReactCrop from 'react-image-crop';
 import AppContainer from '../services/AppContainer';
 import { createSubscribedElement } from './UnstatedUtils';
@@ -12,56 +13,41 @@ class ProfileImageUploader extends React.Component {
   // demo: https://codesandbox.io/s/72py4jlll6
   constructor(props) {
     super();
-
     this.state = {
+      show: false,
       src: null,
-      crop: {
-        unit: '%',
-        width: 30,
-        aspect: 1,
-      },
+      crop: null,
+      croppedImageUrl: null,
     };
-
     this.onSelectFile = this.onSelectFile.bind(this);
     this.onImageLoaded = this.onImageLoaded.bind(this);
-    this.onCropComplete = this.onCropComplete.bind(this);
     this.onCropChange = this.onCropChange.bind(this);
-    this.makeClientCrop = this.makeClientCrop.bind(this);
     this.getCroppedImg = this.getCroppedImg.bind(this);
-    this.hanndleSubmit = this.handleSubmit.bind(this);
+    this.crop = this.crop.bind(this);
+    this.cancel = this.cancel.bind(this);
+    this.reset = this.reset.bind(this);
   }
 
   onSelectFile(e) {
+    console.log(e);
     if (e.target.files && e.target.files.length > 0) {
       const reader = new FileReader();
       reader.addEventListener('load', () => this.setState({ src: reader.result }));
       reader.readAsDataURL(e.target.files[0]);
+      this.show();
     }
   }
 
-  // If you setState the crop in here you should return false.
   onImageLoaded(image) {
     this.imageRef = image;
+    this.reset();
+    return false; // Return false when setting crop state in here.
   }
 
-  onCropComplete(crop) {
-    this.makeClientCrop(crop);
-  }
-
-  onCropChange(crop, percentCrop) {
-    // You could also use percentCrop:
-    // this.setState({ crop: percentCrop });
+  onCropChange(crop) {
     this.setState({ crop });
   }
 
-  async makeClientCrop(crop) {
-    // GW-201 で crop済みの画像を state におくときにコメントを外す(未使用変数の lint エラーが生じるためコメントアウト)
-    // if (this.imageRef && crop.width && crop.height) {
-    //   const croppedImageUrl = await this.getCroppedImg(this.imageRef, crop, 'newFile.jpeg');
-    //   this.setState({ croppedImageUrl });
-    // }
-  }
-
   async getCroppedImg(image, crop, fileName) {
     const canvas = document.createElement('canvas');
     const scaleX = image.naturalWidth / image.width;
@@ -69,10 +55,7 @@ class ProfileImageUploader extends React.Component {
     canvas.width = crop.width;
     canvas.height = crop.height;
     const ctx = canvas.getContext('2d');
-
-    ctx.drawImage(image, crop.x * scaleX, crop.y * scaleY, crop.width * scaleX, crop.height * scaleY,
-      0, 0, crop.width, crop.height);
-
+    ctx.drawImage(image, crop.x * scaleX, crop.y * scaleY, crop.width * scaleX, crop.height * scaleY, 0, 0, crop.width, crop.height);
     return new Promise((resolve, reject) => {
       canvas.toBlob((blob) => {
         if (!blob) {
@@ -87,37 +70,82 @@ class ProfileImageUploader extends React.Component {
     });
   }
 
-  handleSubmit() {
-    // GW-201 にて、crop された画像をサーバー側に送る処理を記述する
-    // me/index.html の 199~240行目の
-    // `$("#pictureUploadForm input[name=profileImage]").on('change', function(){...}`
-    // の処理を node で記述
+  async crop() {
+    // crop immages
+    if (this.imageRef && this.state.crop.width && this.state.crop.height) {
+      const croppedImageUrl = await this.getCroppedImg(this.imageRef, this.state.crop, '/images/icons/user');
+      this.setState({ croppedImageUrl });
+    }
+    this.hide();
   }
 
-  render() {
-    const { crop, src } = this.state;
+  show() {
+    this.setState({ show: true });
+  }
 
+  hide() {
+    this.setState({
+      show: false,
+    });
+  }
+
+  cancel() {
+    this.hide();
+  }
+
+  reset() {
+    const size = Math.min(this.imageRef.width, this.imageRef.height);
+    this.setState({
+      crop: {
+        unit: 'px',
+        x: this.imageRef.width / 2 - size / 2,
+        y: this.imageRef.height / 2 - size / 2,
+        width: size,
+        height: size,
+        aspect: 1,
+      },
+    });
+  }
+
+  render() {
+    const { t } = this.props;
+    const { crop, src, croppedImageUrl } = this.state;
     return (
-      <div className="App">
-        <input type="file" onChange={this.onSelectFile} name="profileImage" accept="image/*" />
-        {src
-        && (
-        <div>
-          <ReactCrop
-            src={src}
-            crop={crop}
-            onImageLoaded={this.onImageLoaded}
-            onComplete={this.onCropComplete}
-            onChange={this.onCropChange}
-          />
-          <button
-            type="button"
-            onClick={this.handleSubmit}
-          >
-          完了
-          </button>
+      <div className="ProfileImageUploader">
+        <div className="form-group">
+          <label htmlFfor="" className="col-sm-4 control-label">
+            {t('Upload new image')}
+          </label>
+        </div>
+        <div className="col-sm-8">
+          {croppedImageUrl && (
+            <img src={croppedImageUrl} className="picture picture-lg img-circle" id="settingUserPicture" />
+          )}
+          <input type="file" onChange={this.onSelectFile} name="profileImage" accept="image/*" />
+          <Modal show={this.state.show} onHide={this.cancel}>
+            <Modal.Header closeButton>
+              <Modal.Title>Image Crop</Modal.Title>
+            </Modal.Header>
+            <Modal.Body className="my-4">
+              <ReactCrop src={src} crop={crop} circularCrop onImageLoaded={this.onImageLoaded} onChange={this.onCropChange} />
+            </Modal.Body>
+            <Modal.Footer>
+              <div className="d-flex justify-content-between">
+                <Button bsStyle="danger" onClick={this.reset}>
+                  Reset
+                </Button>
+                <div className="d-flex">
+                  <Button bsStyle="default" onClick={this.cancel}>
+                    Cancel
+                  </Button>
+                  <Button bsStyle="primary" onClick={this.crop}>
+                    Crop
+                  </Button>
+                </div>
+              </div>
+            </Modal.Footer>
+          </Modal>
         </div>
-        )}
       </div>
     );
   }
@@ -130,9 +158,8 @@ class ProfileImageUploader extends React.Component {
 const ProfileImageFormWrapper = (props) => {
   return createSubscribedElement(ProfileImageUploader, props, [AppContainer]);
 };
-
 ProfileImageUploader.propTypes = {
+  t: PropTypes.func.isRequired, // i18next
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
 };
-
 export default withTranslation()(ProfileImageFormWrapper);

+ 1 - 16
src/server/views/me/index.html

@@ -166,22 +166,7 @@
         </div><!-- /.form-group -->
 
         <div class="form-group">
-          <label for="" class="col-sm-4 control-label">
-            {{ t('Upload new image') }}
-          </label>
-          <div class="col-sm-8">
-            {% if fileUploadService.getIsUploadable() %}
-            <form action="/_api/attachments.uploadProfileImage" id="pictureUploadForm" method="post" class="form-horizontal" role="form">
-              <input type="hidden" name="_csrf" value="{{ csrf() }}">
-              <div id="profile-image-uploader"></div>
-              <div id="pictureUploadFormProgress" class="d-flex align-items-center">
-              </div>
-            </form>
-            {% else %}
-            * {{ t('page_me.form_help.profile_image1') }}<br>
-            * {{ t('page_me.form_help.profile_image2') }}<br>
-            {% endif %}
-          </div>
+          <div id="profile-image-uploader"></div>
         </div><!-- /.form-group -->
 
       </div><!-- /.col-sm- -->