file-utilities: adds reusable upload method

This commit is contained in:
@wwwjim 2020-08-19 22:20:18 -07:00
parent ba470a6fde
commit b836735d82
3 changed files with 124 additions and 107 deletions

93
common/file-utilities.js Normal file
View File

@ -0,0 +1,93 @@
export const upload = async ({ file, slate, setState }) => {
let formData = new FormData();
const HEIC2ANY = require("heic2any");
// 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,
});
formData.append("data", converted);
} else {
formData.append("data", file);
}
const upload = (path) =>
new Promise((resolve, reject) => {
const XHR = new XMLHttpRequest();
XHR.open("post", path, true);
XHR.onerror = (event) => {
console.log(event);
};
// NOTE(jim): UPLOADS ONLY.
XHR.upload.addEventListener(
"progress",
(event) => {
if (event.lengthComputable) {
console.log("FILE UPLOAD PROGRESS", event);
setState({
fileLoading: {
...this.state.fileLoading,
[`${file.lastModified}-${file.name}`]: {
name: file.name,
loaded: event.loaded,
total: event.total,
},
},
});
}
},
false
);
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",
});
}
};
XHR.send(formData);
});
const json = await upload(`/api/data/${file.name}`);
console.log(json);
if (!json || json.error || !json.data) {
setState({
fileLoading: {
...this.state.fileLoading,
[`${file.lastModified}-${file.name}`]: {
name: file.name,
failed: true,
},
},
});
return !json ? { error: "NO_RESPONSE" } : json;
}
if (slate) {
const addResponse = await fetch(`/api/slates/add-url`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({ slate, data: { ...json.data } }),
});
if (!addResponse || addResponse.error) {
console.log(addResponse.error);
alert("TODO: Adding an image to Slate went wrong.");
}
}
return json;
};

View File

@ -4,6 +4,7 @@ import * as Actions from "~/common/actions";
import * as State from "~/common/state";
import * as Credentials from "~/common/credentials";
import * as Validations from "~/common/validations";
import * as FileUtilities from "~/common/file-utilities";
import * as System from "~/components/system";
// NOTE(jim):
@ -102,106 +103,16 @@ export default class ApplicationPage extends React.Component {
this.setState({ online: navigator.onLine });
};
_handleRegisterFile = ({ fileLoading }) => {
_handleUploadFile = async ({ file, slate }) => {
return await FileUtilities.upload({ file, slate, setState: this.setState });
};
_handleRegisterFileLoading = ({ fileLoading }) => {
return this.setState({
fileLoading,
});
};
_handleUploadFile = async ({ file, slate }) => {
let formData = new FormData();
const HEIC2ANY = require("heic2any");
// 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,
});
formData.append("data", converted);
} else {
formData.append("data", file);
}
const upload = (path) =>
new Promise((resolve, reject) => {
const XHR = new XMLHttpRequest();
XHR.open("post", path, true);
XHR.onerror = (event) => {
console.log(event);
};
// NOTE(jim): UPLOADS ONLY.
XHR.upload.addEventListener(
"progress",
(event) => {
if (event.lengthComputable) {
console.log("FILE UPLOAD PROGRESS", event);
this.setState({
fileLoading: {
...this.state.fileLoading,
[`${file.lastModified}-${file.name}`]: {
name: file.name,
loaded: event.loaded,
total: event.total,
},
},
});
}
},
false
);
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",
});
}
};
XHR.send(formData);
});
const json = await upload(`/api/data/${file.name}`);
console.log(json);
if (!json || json.error || !json.data) {
this.setState({
fileLoading: {
...this.state.fileLoading,
[`${file.lastModified}-${file.name}`]: {
name: file.name,
failed: true,
},
},
});
return !json ? { error: "NO_RESPONSE" } : json;
}
if (slate) {
const addResponse = await fetch(`/api/slates/add-url`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({ slate, data: { ...json.data } }),
});
if (!addResponse || addResponse.error) {
console.log(addResponse.error);
alert("TODO: Adding an image to Slate went wrong.");
}
}
return json;
};
_handleDragEnter = (e) => {
if (this.state.sidebar) {
return;
@ -272,7 +183,7 @@ export default class ApplicationPage extends React.Component {
}
// NOTE(jim): Stages each file.
this._handleRegisterFile({ fileLoading });
this._handleRegisterFileLoading({ fileLoading });
// NOTE(jim): Uploads each file.
for (let i = 0; i < files.length; i++) {
@ -517,9 +428,6 @@ export default class ApplicationPage extends React.Component {
};
render() {
// TODO(colin): Populate this.
console.log({ analytics: this.props.analytics });
// NOTE(jim): Not authenticated.
if (!this.state.viewer) {
return (
@ -594,7 +502,7 @@ export default class ApplicationPage extends React.Component {
onSelectedChange: this._handleSelectedChange,
onSubmit: this._handleSubmit,
onCancel: this._handleCancel,
onRegisterFile: this._handleRegisterFile,
onRegisterFileLoading: this._handleRegisterFileLoading,
onUploadFile: this._handleUploadFile,
onSidebarLoading: this._handleSidebarLoading,
onAction: this._handleAction,

View File

@ -83,11 +83,14 @@ export default class SidebarAddFileToBucket extends React.Component {
return;
}
this.props.onRegisterFile({ fileLoading });
this.props.onRegisterFileLoading({ fileLoading });
for (let i = 0; i < files.length; i++) {
const file = files[i];
const slate = this.props.data && this.props.data.slateId ? { id: this.props.data.slateId } : null;
const slate =
this.props.data && this.props.data.slateId
? { id: this.props.data.slateId }
: null;
const response = await this.props.onUploadFile({
file,
@ -111,12 +114,20 @@ export default class SidebarAddFileToBucket extends React.Component {
render() {
return (
<React.Fragment>
<System.P style={{ fontFamily: Constants.font.semiBold }}>Upload data</System.P>
<input css={STYLES_FILE_HIDDEN} type="file" id="file" onChange={this._handleUpload} />
<System.P style={{ fontFamily: Constants.font.semiBold }}>
Upload data
</System.P>
<input
css={STYLES_FILE_HIDDEN}
type="file"
id="file"
onChange={this._handleUpload}
/>
{this.props.data && this.props.data.decorator === "SLATE" ? (
<System.P style={{ marginTop: 24 }}>
This will add data to your Slate named <strong>{this.props.data.slatename}</strong>.
This will add data to your Slate named{" "}
<strong>{this.props.data.slatename}</strong>.
</System.P>
) : null}
@ -125,12 +136,17 @@ export default class SidebarAddFileToBucket extends React.Component {
type="label"
htmlFor="file"
style={{ marginTop: 24 }}
loading={!!this.props.fileLoading}>
loading={!!this.props.fileLoading}
>
Add file
</System.ButtonPrimary>
{!this.props.fileLoading ? (
<System.ButtonSecondary full style={{ marginTop: 16 }} onClick={this.props.onCancel}>
<System.ButtonSecondary
full
style={{ marginTop: 16 }}
onClick={this.props.onCancel}
>
Cancel
</System.ButtonSecondary>
) : null}