2020-09-27 00:15:37 +03:00
|
|
|
import * as Actions from "~/common/actions";
|
2020-10-02 07:24:10 +03:00
|
|
|
import * as Store from "~/common/store";
|
2020-10-05 00:30:28 +03:00
|
|
|
import * as Constants from "~/common/constants";
|
2020-10-02 07:24:10 +03:00
|
|
|
|
|
|
|
import { dispatchCustomEvent } from "~/common/custom-events";
|
2020-10-05 00:30:28 +03:00
|
|
|
import { encode } from "blurhash";
|
2020-09-12 01:25:33 +03:00
|
|
|
|
2020-09-28 20:48:44 +03:00
|
|
|
const STAGING_DEAL_BUCKET = "stage-deal";
|
|
|
|
|
2020-10-05 00:30:28 +03:00
|
|
|
const loadImage = async (src) =>
|
|
|
|
new Promise((resolve, reject) => {
|
|
|
|
const img = new Image();
|
|
|
|
img.crossOrigin = "Anonymous";
|
|
|
|
img.onload = () => resolve(img);
|
|
|
|
img.onerror = (...args) => reject(args);
|
|
|
|
img.src = src;
|
|
|
|
});
|
|
|
|
|
|
|
|
const getImageData = (image) => {
|
|
|
|
const canvas = document.createElement("canvas");
|
|
|
|
canvas.width = image.width;
|
|
|
|
canvas.height = image.height;
|
|
|
|
const context = canvas.getContext("2d");
|
|
|
|
context.drawImage(image, 0, 0);
|
|
|
|
return context.getImageData(0, 0, image.width, image.height);
|
|
|
|
};
|
|
|
|
|
|
|
|
const encodeImageToBlurhash = async (imageUrl) => {
|
|
|
|
const image = await loadImage(imageUrl);
|
|
|
|
const imageData = getImageData(image);
|
|
|
|
return encode(imageData.data, imageData.width, imageData.height, 4, 4);
|
|
|
|
};
|
|
|
|
|
2020-09-23 14:17:56 +03:00
|
|
|
export const upload = async ({ file, context, bucketName }) => {
|
2020-08-20 08:20:18 +03:00
|
|
|
let formData = new FormData();
|
|
|
|
const HEIC2ANY = require("heic2any");
|
|
|
|
|
2020-08-20 08:29:33 +03:00
|
|
|
// NOTE(jim): You must provide a file from an type="file" input field.
|
|
|
|
if (!file) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-08-20 08:20:18 +03:00
|
|
|
// TODO(jim): Put this somewhere else to handle conversion cases.
|
|
|
|
if (file.type.startsWith("image/heic")) {
|
|
|
|
const converted = await HEIC2ANY({
|
|
|
|
blob: file,
|
|
|
|
toType: "image/png",
|
|
|
|
quality: 1,
|
2020-10-02 07:24:10 +03:00
|
|
|
}); //TODO(martina): figure out how to cancel an await if upload has been cancelled
|
2020-08-20 08:20:18 +03:00
|
|
|
|
|
|
|
formData.append("data", converted);
|
|
|
|
} else {
|
|
|
|
formData.append("data", file);
|
|
|
|
}
|
|
|
|
|
2020-10-02 07:24:10 +03:00
|
|
|
if (Store.checkCancelled(`${file.lastModified}-${file.name}`)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const _privateUploadMethod = (path, file) =>
|
2020-08-20 08:20:18 +03:00
|
|
|
new Promise((resolve, reject) => {
|
|
|
|
const XHR = new XMLHttpRequest();
|
2020-10-09 13:43:56 +03:00
|
|
|
|
2020-10-02 07:24:10 +03:00
|
|
|
window.addEventListener(
|
|
|
|
`cancel-${file.lastModified}-${file.name}`,
|
|
|
|
() => {
|
|
|
|
XHR.abort();
|
|
|
|
}
|
|
|
|
);
|
2020-10-09 13:43:56 +03:00
|
|
|
|
2020-08-20 08:20:18 +03:00
|
|
|
XHR.open("post", path, true);
|
|
|
|
XHR.onerror = (event) => {
|
|
|
|
console.log(event);
|
2020-10-09 05:32:30 +03:00
|
|
|
XHR.abort();
|
2020-08-20 08:20:18 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
// NOTE(jim): UPLOADS ONLY.
|
|
|
|
XHR.upload.addEventListener(
|
|
|
|
"progress",
|
|
|
|
(event) => {
|
2020-08-21 10:07:39 +03:00
|
|
|
if (!context) {
|
2020-08-20 08:29:33 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-20 08:20:18 +03:00
|
|
|
if (event.lengthComputable) {
|
|
|
|
console.log("FILE UPLOAD PROGRESS", event);
|
2020-08-21 10:07:39 +03:00
|
|
|
context.setState({
|
2020-08-20 08:20:18 +03:00
|
|
|
fileLoading: {
|
2020-08-21 10:07:39 +03:00
|
|
|
...context.state.fileLoading,
|
2020-08-20 08:20:18 +03:00
|
|
|
[`${file.lastModified}-${file.name}`]: {
|
|
|
|
name: file.name,
|
|
|
|
loaded: event.loaded,
|
|
|
|
total: event.total,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
false
|
|
|
|
);
|
|
|
|
|
2020-10-02 07:24:10 +03:00
|
|
|
window.removeEventListener(
|
|
|
|
`cancel-${file.lastModified}-${file.name}`,
|
|
|
|
() => XHR.abort()
|
|
|
|
);
|
|
|
|
|
2020-08-20 08:20:18 +03:00
|
|
|
XHR.onloadend = (event) => {
|
|
|
|
console.log("FILE UPLOAD END", event);
|
|
|
|
try {
|
|
|
|
return resolve(JSON.parse(event.target.response));
|
|
|
|
} catch (e) {
|
|
|
|
return resolve({
|
|
|
|
error: "SERVER_UPLOAD_ERROR",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
2020-09-19 22:36:58 +03:00
|
|
|
XHR.send(formData);
|
2020-08-20 08:20:18 +03:00
|
|
|
});
|
|
|
|
|
2020-10-05 00:30:28 +03:00
|
|
|
let res;
|
2020-09-25 20:40:54 +03:00
|
|
|
// TODO(jim): Make this smarter.
|
2020-09-28 20:48:44 +03:00
|
|
|
if (bucketName && bucketName === STAGING_DEAL_BUCKET) {
|
2020-10-05 00:30:28 +03:00
|
|
|
res = await _privateUploadMethod(`/api/data/deal/${file.name}`, file);
|
2020-09-23 14:17:56 +03:00
|
|
|
} else {
|
2020-10-05 00:30:28 +03:00
|
|
|
res = await _privateUploadMethod(`/api/data/${file.name}`, file);
|
2020-09-23 14:17:56 +03:00
|
|
|
}
|
|
|
|
|
2020-10-05 00:30:28 +03:00
|
|
|
if (!res || res.error || !res.data) {
|
2020-08-21 10:07:39 +03:00
|
|
|
if (context) {
|
|
|
|
context.setState({
|
2020-08-20 08:29:33 +03:00
|
|
|
fileLoading: {
|
2020-08-21 10:07:39 +03:00
|
|
|
...context.state.fileLoading,
|
2020-08-20 08:29:33 +03:00
|
|
|
[`${file.lastModified}-${file.name}`]: {
|
|
|
|
name: file.name,
|
|
|
|
failed: true,
|
|
|
|
},
|
2020-08-20 08:20:18 +03:00
|
|
|
},
|
2020-08-20 08:29:33 +03:00
|
|
|
});
|
|
|
|
}
|
2020-09-20 23:38:27 +03:00
|
|
|
dispatchCustomEvent({
|
|
|
|
name: "create-alert",
|
|
|
|
detail: {
|
|
|
|
alert: { message: "Some of your files could not be uploaded" },
|
|
|
|
},
|
|
|
|
});
|
2020-08-20 08:29:33 +03:00
|
|
|
|
2020-10-05 00:30:28 +03:00
|
|
|
return !res ? { error: "NO_RESPONSE" } : res;
|
2020-08-20 08:20:18 +03:00
|
|
|
}
|
|
|
|
|
2020-10-05 00:30:28 +03:00
|
|
|
if (res.data.data.type.startsWith("image/")) {
|
|
|
|
let url = `${Constants.gateways.ipfs}/${res.data.data.cid}`;
|
|
|
|
let blurhash = await encodeImageToBlurhash(url);
|
|
|
|
res.data.data.blurhash = blurhash;
|
|
|
|
}
|
|
|
|
|
|
|
|
await Actions.createPendingFiles({ data: res.data });
|
|
|
|
|
|
|
|
res.data = res.data.data;
|
|
|
|
|
|
|
|
return { file, json: res };
|
2020-09-20 23:38:27 +03:00
|
|
|
};
|
2020-08-20 08:20:18 +03:00
|
|
|
|
2020-09-20 23:38:27 +03:00
|
|
|
export const uploadToSlate = async ({ responses, slate }) => {
|
2020-09-27 00:15:37 +03:00
|
|
|
let added;
|
|
|
|
let skipped;
|
|
|
|
if (responses && responses.length) {
|
|
|
|
const addResponse = await Actions.addFileToSlate({
|
2020-09-20 23:38:27 +03:00
|
|
|
slate,
|
|
|
|
data: responses.map((res) => {
|
|
|
|
return { title: res.file.name, ...res.json.data };
|
|
|
|
}),
|
2020-09-27 00:15:37 +03:00
|
|
|
});
|
2020-09-20 23:38:27 +03:00
|
|
|
|
2020-09-27 00:15:37 +03:00
|
|
|
if (!addResponse) {
|
|
|
|
dispatchCustomEvent({
|
|
|
|
name: "create-alert",
|
|
|
|
detail: {
|
|
|
|
alert: {
|
|
|
|
message:
|
|
|
|
"We're having trouble connecting right now. Please try again later",
|
|
|
|
},
|
2020-09-12 01:25:33 +03:00
|
|
|
},
|
2020-09-27 00:15:37 +03:00
|
|
|
});
|
|
|
|
return;
|
|
|
|
} else if (addResponse.error) {
|
|
|
|
dispatchCustomEvent({
|
|
|
|
name: "create-alert",
|
|
|
|
detail: { alert: { decorator: addResponse.decorator } },
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
added = addResponse.added;
|
|
|
|
skipped = addResponse.skipped;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let message = `${added || 0} file${
|
|
|
|
added !== 1 ? "s" : ""
|
|
|
|
} uploaded to slate. `;
|
|
|
|
if (skipped) {
|
|
|
|
message += `${skipped || 0} duplicate / existing file${
|
|
|
|
added !== 1 ? "s were" : " was"
|
|
|
|
} skipped.`;
|
2020-08-20 08:20:18 +03:00
|
|
|
}
|
2020-09-27 00:15:37 +03:00
|
|
|
dispatchCustomEvent({
|
|
|
|
name: "create-alert",
|
|
|
|
detail: {
|
|
|
|
alert: { message, status: !added ? null : "INFO" },
|
|
|
|
},
|
|
|
|
});
|
2020-08-20 08:20:18 +03:00
|
|
|
};
|