mirror of
https://github.com/filecoin-project/slate.git
synced 2024-11-09 20:28:29 +03:00
5c0747ff71
- remove Store.js. We'll use a queue system, it'll be easier to cancel file uploading from the queue directly - remove uploadFiles functions. It'll be replaced by the queue system - remove the context parameter from the upload function. We'll use onProgress handler instead - add UploadAbort parameter to upload, which is an object that will give us the ability to abort an occurring upload outside of the function scope
239 lines
6.9 KiB
JavaScript
239 lines
6.9 KiB
JavaScript
import * as Actions from "~/common/actions";
|
|
import * as Credentials from "~/common/credentials";
|
|
import * as Strings from "~/common/strings";
|
|
import * as Validations from "~/common/validations";
|
|
import * as Events from "~/common/custom-events";
|
|
import * as Logging from "~/common/logging";
|
|
import * as Environment from "~/common/environment";
|
|
|
|
import { encode, isBlurhashValid } from "blurhash";
|
|
import { v4 as uuid } from "uuid";
|
|
|
|
const STAGING_DEAL_BUCKET = "stage-deal";
|
|
|
|
export const fileKey = ({ lastModified, name }) => `${lastModified}-${name}`;
|
|
|
|
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) => {
|
|
let ratio = Math.min(100 / image.height, 100 / image.width);
|
|
image.height = image.height * ratio;
|
|
image.width = image.width * ratio;
|
|
const canvas = document.createElement("canvas");
|
|
canvas.width = image.width;
|
|
canvas.height = image.height;
|
|
const context = canvas.getContext("2d");
|
|
context.scale(ratio, ratio);
|
|
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);
|
|
};
|
|
|
|
// NOTE(jim): We're speaking to a different server now.
|
|
const getCookie = (name) => {
|
|
var match = document.cookie.match(new RegExp("(^| )" + name + "=([^;]+)"));
|
|
if (match) return match[2];
|
|
};
|
|
|
|
export const uploadLink = async ({ url, slate }) => {
|
|
Events.dispatchMessage({ message: "Uploading link...", status: "INFO" });
|
|
let createResponse = await Actions.createLink({ url, slate });
|
|
if (Events.hasError(createResponse)) {
|
|
return;
|
|
}
|
|
|
|
const { added, skipped } = createResponse.data;
|
|
if (added) {
|
|
Events.dispatchMessage({ message: "Link added", status: "INFO" });
|
|
} else if (skipped) {
|
|
Events.dispatchMessage({
|
|
message: "You've already saved this link",
|
|
});
|
|
return;
|
|
}
|
|
};
|
|
|
|
export const upload = async ({ file, onProgress, bucketName, uploadAbort }) => {
|
|
let formData = new FormData();
|
|
const HEIC2ANY = require("heic2any");
|
|
|
|
// NOTE(jim): You must provide a file from an type="file" input field.
|
|
if (!file) {
|
|
return null;
|
|
}
|
|
|
|
const isZipFile =
|
|
file.type.startsWith("application/zip") || file.type.startsWith("application/x-zip-compressed");
|
|
const isUnityFile = await Validations.isUnityFile(file);
|
|
|
|
// 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,
|
|
}); //TODO(martina): figure out how to cancel an await if upload has been cancelled
|
|
|
|
formData.append("data", converted);
|
|
} else {
|
|
formData.append("data", file);
|
|
}
|
|
|
|
const _privateUploadMethod = (path, file) =>
|
|
new Promise((resolve) => {
|
|
const XHR = new XMLHttpRequest();
|
|
|
|
if (uploadAbort) uploadAbort.abort = XHR.abort.bind(XHR);
|
|
|
|
XHR.open("post", path, true);
|
|
XHR.setRequestHeader("authorization", getCookie(Credentials.session.key));
|
|
XHR.onerror = (event) => {
|
|
Logging.error(event);
|
|
XHR.abort();
|
|
};
|
|
|
|
// NOTE(jim): UPLOADS ONLY.
|
|
XHR.upload.addEventListener(
|
|
"progress",
|
|
(event) => {
|
|
if (event.lengthComputable) {
|
|
Logging.log("FILE UPLOAD PROGRESS", event);
|
|
if (onProgress) onProgress(event);
|
|
}
|
|
},
|
|
false
|
|
);
|
|
XHR.addEventListener("abort", () => {
|
|
resolve({ aborted: true });
|
|
});
|
|
|
|
XHR.onloadend = (event) => {
|
|
Logging.log("FILE UPLOAD END", event);
|
|
try {
|
|
return resolve(JSON.parse(event.target.response));
|
|
} catch (e) {
|
|
return resolve({
|
|
error: "SERVER_UPLOAD_ERROR",
|
|
});
|
|
}
|
|
};
|
|
XHR.send(formData);
|
|
});
|
|
|
|
const storageDealRoute = `${Environment.URI_SHOVEL}/api/deal/`;
|
|
const generalRoute = `${Environment.URI_SHOVEL}/api/data/`;
|
|
const zipUploadRoute = `${Environment.URI_SHOVEL}/api/data/zip/`;
|
|
|
|
if (!storageDealRoute || !generalRoute || !zipUploadRoute) {
|
|
Events.dispatchMessage({ message: "We could not find our upload server." });
|
|
|
|
return {
|
|
decorator: "NO_UPLOAD_RESOURCE_URI_ATTACHED",
|
|
error: true,
|
|
};
|
|
}
|
|
|
|
let res;
|
|
if (isZipFile && isUnityFile) {
|
|
res = await _privateUploadMethod(`${zipUploadRoute}${file.name}`, file);
|
|
} else if (bucketName && bucketName === STAGING_DEAL_BUCKET) {
|
|
res = await _privateUploadMethod(`${storageDealRoute}${file.name}`, file);
|
|
} else {
|
|
res = await _privateUploadMethod(`${generalRoute}${file.name}`, file);
|
|
}
|
|
|
|
if (!res || res.error || res.aborted) {
|
|
return res;
|
|
}
|
|
|
|
let item = res.data.data;
|
|
if (item.type.startsWith("image/")) {
|
|
let url = Strings.getURLfromCID(item.cid);
|
|
try {
|
|
let blurhash = await encodeImageToBlurhash(url);
|
|
if (isBlurhashValid(blurhash).result) {
|
|
item.blurhash = blurhash;
|
|
}
|
|
} catch (e) {
|
|
Logging.error(e);
|
|
}
|
|
}
|
|
|
|
return item;
|
|
};
|
|
|
|
export const formatPastedImages = ({ clipboardItems }) => {
|
|
let files = [];
|
|
for (let i = 0; i < clipboardItems.length; i++) {
|
|
// Note(Amine): skip content if it's not an image
|
|
if (clipboardItems[i].type.indexOf("image") === -1) continue;
|
|
const file = clipboardItems[i].getAsFile();
|
|
files.push(file);
|
|
}
|
|
return { files };
|
|
};
|
|
|
|
export const formatDroppedFiles = async ({ dataTransfer }) => {
|
|
// NOTE(jim): If this is true, then drag and drop came from a slate object.
|
|
const data = dataTransfer.getData("slate-object-drag-data");
|
|
if (data) {
|
|
return;
|
|
}
|
|
|
|
const files = [];
|
|
if (dataTransfer.items && dataTransfer.items.length) {
|
|
for (var i = 0; i < dataTransfer.items.length; i++) {
|
|
const data = dataTransfer.items[i];
|
|
|
|
let file = null;
|
|
if (data.kind === "file") {
|
|
file = data.getAsFile();
|
|
} else if (data.kind == "string" && data.type == "text/uri-list") {
|
|
try {
|
|
const dataAsString = new Promise((resolve) => data.getAsString((d) => resolve(d)));
|
|
const resp = await fetch(await dataAsString);
|
|
const blob = resp.blob();
|
|
|
|
file = new File(blob, `data-${uuid()}`);
|
|
file.name = `data-${uuid()}`;
|
|
} catch (e) {
|
|
Events.dispatchMessage({
|
|
message: "File type not supported. Please try a different file",
|
|
});
|
|
|
|
return { error: true };
|
|
}
|
|
}
|
|
|
|
files.push(file);
|
|
}
|
|
}
|
|
|
|
return { files };
|
|
};
|
|
|
|
export const formatUploadedFiles = ({ files }) => {
|
|
let toUpload = [];
|
|
for (let i = 0; i < files.length; i++) {
|
|
let file = files[i];
|
|
if (!file) {
|
|
continue;
|
|
}
|
|
toUpload.push(file);
|
|
}
|
|
|
|
return { files: toUpload };
|
|
};
|