about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--app/javascript/flavours/glitch/actions/compose.js19
-rw-r--r--app/javascript/flavours/glitch/util/resize_image.js116
2 files changed, 124 insertions, 11 deletions
diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js
index 39381faa8..199a79e7f 100644
--- a/app/javascript/flavours/glitch/actions/compose.js
+++ b/app/javascript/flavours/glitch/actions/compose.js
@@ -3,6 +3,7 @@ import { CancelToken } from 'axios';
 import { throttle } from 'lodash';
 import { search as emojiSearch } from 'flavours/glitch/util/emoji/emoji_mart_search_light';
 import { useEmoji } from './emojis';
+import resizeImage from 'flavours/glitch/util/resize_image';
 
 import {
   updateTimeline,
@@ -207,18 +208,14 @@ export function uploadCompose(files) {
 
     dispatch(uploadComposeRequest());
 
-    let data = new FormData();
-    data.append('file', files[0]);
+    resizeImage(files[0]).then(file => {
+      const data = new FormData();
+      data.append('file', file);
 
-    api(getState).post('/api/v1/media', data, {
-      onUploadProgress: function (e) {
-        dispatch(uploadComposeProgress(e.loaded, e.total));
-      },
-    }).then(function (response) {
-      dispatch(uploadComposeSuccess(response.data));
-    }).catch(function (error) {
-      dispatch(uploadComposeFail(error));
-    });
+      return api(getState).post('/api/v1/media', data, {
+        onUploadProgress: ({ loaded, total }) => dispatch(uploadComposeProgress(loaded, total)),
+      }).then(({ data }) => dispatch(uploadComposeSuccess(data)));
+    }).catch(error => dispatch(uploadComposeFail(error)));
   };
 };
 
diff --git a/app/javascript/flavours/glitch/util/resize_image.js b/app/javascript/flavours/glitch/util/resize_image.js
new file mode 100644
index 000000000..279a858ca
--- /dev/null
+++ b/app/javascript/flavours/glitch/util/resize_image.js
@@ -0,0 +1,116 @@
+import EXIF from 'exif-js';
+
+const MAX_IMAGE_DIMENSION = 1280;
+
+const getImageUrl = inputFile => new Promise((resolve, reject) => {
+  if (window.URL && URL.createObjectURL) {
+    try {
+      resolve(URL.createObjectURL(inputFile));
+    } catch (error) {
+      reject(error);
+    }
+    return;
+  }
+
+  const reader = new FileReader();
+  reader.onerror = (...args) => reject(...args);
+  reader.onload  = ({ target }) => resolve(target.result);
+
+  reader.readAsDataURL(inputFile);
+});
+
+const loadImage = inputFile => new Promise((resolve, reject) => {
+  getImageUrl(inputFile).then(url => {
+    const img = new Image();
+
+    img.onerror = (...args) => reject(...args);
+    img.onload  = () => resolve(img);
+
+    img.src = url;
+  }).catch(reject);
+});
+
+const getOrientation = (img, type = 'image/png') => new Promise(resolve => {
+  if (type !== 'image/jpeg') {
+    resolve(1);
+    return;
+  }
+
+  EXIF.getData(img, () => {
+    const orientation = EXIF.getTag(img, 'Orientation');
+    resolve(orientation);
+  });
+});
+
+const processImage = (img, { width, height, orientation, type = 'image/png' }) => new Promise(resolve => {
+  const canvas  = document.createElement('canvas');
+
+  if (4 < orientation && orientation < 9) {
+    canvas.width  = height;
+    canvas.height = width;
+  } else {
+    canvas.width  = width;
+    canvas.height = height;
+  }
+
+  const context = canvas.getContext('2d');
+
+  switch (orientation) {
+  case 2: context.transform(-1, 0, 0, 1, width, 0); break;
+  case 3: context.transform(-1, 0, 0, -1, width, height); break;
+  case 4: context.transform(1, 0, 0, -1, 0, height); break;
+  case 5: context.transform(0, 1, 1, 0, 0, 0); break;
+  case 6: context.transform(0, 1, -1, 0, height, 0); break;
+  case 7: context.transform(0, -1, -1, 0, height, width); break;
+  case 8: context.transform(0, -1, 1, 0, 0, width); break;
+  }
+
+  context.drawImage(img, 0, 0, width, height);
+
+  canvas.toBlob(resolve, type);
+});
+
+const resizeImage = (img, type = 'image/png') => new Promise((resolve, reject) => {
+  const { width, height } = img;
+
+  let newWidth, newHeight;
+
+  if (width > height) {
+    newHeight = height * MAX_IMAGE_DIMENSION / width;
+    newWidth  = MAX_IMAGE_DIMENSION;
+  } else if (height > width) {
+    newWidth  = width * MAX_IMAGE_DIMENSION / height;
+    newHeight = MAX_IMAGE_DIMENSION;
+  } else {
+    newWidth  = MAX_IMAGE_DIMENSION;
+    newHeight = MAX_IMAGE_DIMENSION;
+  }
+
+  getOrientation(img, type)
+    .then(orientation => processImage(img, {
+      width: newWidth,
+      height: newHeight,
+      orientation,
+      type,
+    }))
+    .then(resolve)
+    .catch(reject);
+});
+
+export default inputFile => new Promise((resolve, reject) => {
+  if (!inputFile.type.match(/image.*/) || inputFile.type === 'image/gif') {
+    resolve(inputFile);
+    return;
+  }
+
+  loadImage(inputFile).then(img => {
+    if (img.width < MAX_IMAGE_DIMENSION && img.height < MAX_IMAGE_DIMENSION) {
+      resolve(inputFile);
+      return;
+    }
+
+    resizeImage(img, inputFile.type)
+      .then(resolve)
+      .catch(() => resolve(inputFile));
+  }).catch(reject);
+});