finished with retractable upload sidebar

This commit is contained in:
Martina 2020-09-24 20:16:10 -07:00
parent e936391d66
commit ac3d970c47
6 changed files with 176 additions and 90 deletions

View File

@ -4,22 +4,53 @@ import * as Constants from "~/common/constants";
import { error } from "~/common/messages";
import { css } from "@emotion/react";
import { LoaderSpinner } from "~/components/system/components/Loaders";
const STYLES_ALERT = css`
const STYLES_ALERT = `
box-sizing: border-box;
z-index: ${Constants.zindex.alert};
position: fixed;
top: 56px;
width: calc(100% - ${Constants.sizes.navigation}px);
min-height: 48px;
background-color: ${Constants.system.red};
color: ${Constants.system.white};
min-height: 48px;
padding: 12px 48px;
display: flex;
flex-wrap: wrap;
align-items: center;
`;
const STYLES_WARNING = css`
${STYLES_ALERT}
background-color: ${Constants.system.red};
@supports (
(-webkit-backdrop-filter: blur(25px)) or (backdrop-filter: blur(25px))
) {
-webkit-backdrop-filter: blur(25px);
backdrop-filter: blur(25px);
background-color: rgba(255, 212, 201, 0.75);
color: ${Constants.system.red};
}
`;
const STYLES_INFO = css`
${STYLES_ALERT}
@supports (
(-webkit-backdrop-filter: blur(25px)) or (backdrop-filter: blur(25px))
) {
-webkit-backdrop-filter: blur(25px);
backdrop-filter: blur(25px);
background-color: rgba(212, 233, 250, 0.75);
color: ${Constants.system.brand};
}
`;
const STYLES_MESSAGE_BOX = css`
display: flex;
align-items: center;
`;
export class Alert extends React.Component {
state = {
message: null,
@ -61,16 +92,36 @@ export class Alert extends React.Component {
render() {
if (!this.state.message) {
return null;
if (
!this.props.fileLoading ||
!Object.keys(this.props.fileLoading).length
) {
return null;
}
return (
<div
css={STYLES_INFO}
style={{ cursor: "pointer" }}
onClick={() =>
this.props.onAction({
type: "SIDEBAR",
value: "SIDEBAR_ADD_FILE_TO_BUCKET",
})
}
>
<div css={STYLES_MESSAGE_BOX}>
<LoaderSpinner style={{ height: 16, width: 16, marginRight: 16 }} />
{Object.keys(this.props.fileLoading).length} file
{Object.keys(this.props.fileLoading).length === 1 ? "" : "s"}{" "}
uploading
</div>
</div>
);
}
return (
<div
css={STYLES_ALERT}
style={{
backgroundColor:
this.state.status === "INFO" ? Constants.system.brand : "auto",
...this.props.style,
}}
css={this.state.status === "INFO" ? STYLES_INFO : STYLES_WARNING}
style={this.props.style}
>
{this.state.message}
</div>

View File

@ -55,7 +55,7 @@ import Cookies from "universal-cookie";
import { GlobalViewerCID } from "~/components/core/viewers/GlobalViewerCID";
import { dispatchCustomEvent } from "~/common/custom-events";
import { Alert } from "~/components/core/Alert";
import { Alert, UploadingAlert } from "~/components/core/Alert";
const cookies = new Cookies();
@ -147,6 +147,7 @@ export default class ApplicationPage extends React.Component {
};
_handleDrop = async (e) => {
this._handleDismissSidebar();
// NOTE(jim): If this is true, then drag and drop came from a slate object.
const data = e.dataTransfer.getData("slate-object-drag-data");
if (data) {
@ -155,8 +156,6 @@ export default class ApplicationPage extends React.Component {
e.preventDefault();
this.setState({ fileLoading: true });
// TODO(jim): Refactor later
const navigation = NavigationData.generate(this.state.viewer);
const next = this.state.history[this.state.currentIndex];
@ -169,20 +168,20 @@ export default class ApplicationPage extends React.Component {
const files = [];
let fileLoading = {};
let sidebarOpen = false;
// let sidebarOpen = false;
if (e.dataTransfer.items && e.dataTransfer.items.length) {
for (var i = 0; i < e.dataTransfer.items.length; i++) {
if (e.dataTransfer.items[i].kind === "file") {
var file = e.dataTransfer.items[i].getAsFile();
if (!sidebarOpen) {
this._handleAction({
type: "SIDEBAR",
value: "SIDEBAR_ADD_FILE_TO_BUCKET",
data: slate,
});
sidebarOpen = true;
}
// if (!sidebarOpen) {
// this._handleAction({
// type: "SIDEBAR",
// value: "SIDEBAR_ADD_FILE_TO_BUCKET",
// data: slate,
// });
// sidebarOpen = true;
// }
files.push(file);
fileLoading[`${file.lastModified}-${file.name}`] = {
@ -209,7 +208,7 @@ export default class ApplicationPage extends React.Component {
// NOTE(jim): Stages each file.
this._handleRegisterFileLoading({ fileLoading });
this._handleUpload({ files, slate });
this._handleUpload({ files, slate, keys: Object.keys(fileLoading) });
};
_handleUploadFiles = async ({ files, slate, bucketName }) => {
@ -258,10 +257,15 @@ export default class ApplicationPage extends React.Component {
return;
}
this._handleUpload({ files: toUpload, slate, bucketName });
this._handleUpload({
files: toUpload,
slate,
bucketName,
keys: Object.keys(fileLoading),
});
};
_handleUpload = async ({ files, slate, bucketName }) => {
_handleUpload = async ({ files, slate, bucketName, keys }) => {
if (!files || !files.length) {
return null;
}
@ -284,67 +288,70 @@ export default class ApplicationPage extends React.Component {
return null;
}
Promise.allSettled(resolvedFiles)
.then((responses) => {
let succeeded = responses
.filter((res) => {
return res.status === "fulfilled" && res.value && !res.value.error;
})
.map((res) => res.value);
if (slate && slate.id && succeeded && succeeded.length) {
FileUtilities.uploadToSlate({ responses: succeeded, slate });
}
let responses = await Promise.allSettled(resolvedFiles);
let succeeded = responses
.filter((res) => {
return res.status === "fulfilled" && res.value && !res.value.error;
})
.then(async () => {
return await Actions.processPendingFiles();
})
.then(async (response) => {
if (!response) {
dispatchCustomEvent({
name: "create-alert",
detail: {
alert: {
message:
"We encountered issues updating your uploaded files. Please try again",
},
},
});
return;
}
if (response.error) {
dispatchCustomEvent({
name: "create-alert",
detail: {
alert: {
decorator: response.decorator,
},
},
});
return;
}
await this.rehydrate({ resetFiles: true });
dispatchCustomEvent({
name: "remote-update-slate-screen",
detail: {},
});
const { added, skipped } = response.data;
if (!added && !skipped) return;
let message = `${added || 0} file${added !== 1 ? "s" : ""} uploaded. `;
if (skipped) {
message += `${skipped || 0} duplicate / existing file${
added !== 1 ? "s were" : " was"
} skipped.`;
}
dispatchCustomEvent({
name: "create-alert",
detail: {
alert: { message, status: !added ? null : "INFO" },
.map((res) => res.value);
if (slate && slate.id && succeeded && succeeded.length) {
await FileUtilities.uploadToSlate({ responses: succeeded, slate });
}
let processResponse = await Actions.processPendingFiles();
if (!processResponse) {
dispatchCustomEvent({
name: "create-alert",
detail: {
alert: {
message:
"We encountered issues updating your uploaded files. Please try again",
},
});
},
});
return;
}
if (processResponse.error) {
dispatchCustomEvent({
name: "create-alert",
detail: {
alert: {
decorator: processResponse.decorator,
},
},
});
return;
}
await this.rehydrate({ resetFiles: true });
//NOTE(martina): to update the carousel to include the new file if you're on the data view page
dispatchCustomEvent({
name: "remote-update-carousel",
detail: {},
});
//NOTE(martina): to update the slate to include the new file if you're on a slate page
dispatchCustomEvent({
name: "remote-update-slate-screen",
detail: {},
});
if (!slate) {
const { added, skipped } = processResponse.data;
if (!added && !skipped) return;
let message = `${added || 0} file${added !== 1 ? "s" : ""} uploaded. `;
if (skipped) {
message += `${skipped || 0} duplicate / existing file${
added !== 1 ? "s were" : " was"
} skipped.`;
}
dispatchCustomEvent({
name: "create-alert",
detail: {
alert: { message, status: !added ? null : "INFO" },
},
});
}
this._handleRegisterLoadingFinished({ keys });
};
_handleRegisterFileLoading = ({ fileLoading }) => {
@ -358,6 +365,14 @@ export default class ApplicationPage extends React.Component {
});
};
_handleRegisterLoadingFinished = ({ keys }) => {
let fileLoading = this.state.fileLoading;
for (let key of keys) {
delete fileLoading[key];
}
this.setState({ fileLoading });
};
_handleDragEnter = (e) => {
e.preventDefault();
if (this.state.sidebar) {
@ -411,7 +426,6 @@ export default class ApplicationPage extends React.Component {
};
if (options && options.resetFiles) {
updates.fileLoading = null;
updates.sidebar = null;
}
@ -767,10 +781,12 @@ export default class ApplicationPage extends React.Component {
url={url}
>
<ApplicationLayout
onAction={this._handleAction}
header={headerElement}
navigation={navigationElement}
sidebar={sidebarElement}
onDismissSidebar={this._handleDismissSidebar}
fileLoading={this.state.fileLoading}
>
{scene}
</ApplicationLayout>

View File

@ -35,7 +35,15 @@ const STYLES_APPLICATION_HEADER = css`
height: 56px;
padding: 12px 48px 0 36px;
pointer-events: none;
background: ${Constants.system.white};
background-color: ${Constants.system.white};
@supports (
(-webkit-backdrop-filter: blur(25px)) or (backdrop-filter: blur(25px))
) {
-webkit-backdrop-filter: blur(25px);
backdrop-filter: blur(25px);
background-color: rgba(255, 255, 255, 0.75);
}
@media (max-width: ${Constants.sizes.mobile}px) {
padding: 12px 24px 0 12px;

View File

@ -205,6 +205,8 @@ export default class ApplicationLayout extends React.Component {
id="slate-client-body"
>
<Alert
fileLoading={this.props.fileLoading}
onAction={this.props.onAction}
style={{
paddingRight: this.props.sidebar
? `calc(${Constants.sizes.sidebar}px + 48px`
@ -215,6 +217,8 @@ export default class ApplicationLayout extends React.Component {
</div>
<div css={STYLES_BODY_MOBILE}>
<Alert
fileLoading={this.props.fileLoading}
onAction={this.props.onAction}
style={{
top: 0,
left: 0,

View File

@ -68,14 +68,15 @@ const STYLES_ICONS = css`
`;
export default class SidebarAddFileToBucket extends React.Component {
_handleUpload = async (e) => {
await this.props.onUpload({
_handleUpload = (e) => {
this.props.onUpload({
files: e.target.files,
slate:
this.props.current && this.props.current.slateId
? { id: this.props.current.slateId }
: null,
});
this.props.onCancel();
};
render() {
@ -142,7 +143,8 @@ export default class SidebarAddFileToBucket extends React.Component {
</System.ButtonPrimary>
<br />
{this.props.fileLoading ? (
{this.props.fileLoading &&
Object.keys(this.props.fileLoading).length ? (
<div css={STYLES_BAR_CONTAINER}>
<strong css={STYLES_PERFORMANCE}>
{Strings.bytesToSize(loaded)} / {Strings.bytesToSize(total)}

View File

@ -51,6 +51,12 @@ export default class SidebarAddFileToSlate extends React.Component {
selected: {},
};
componentDidMount = () => {
if (!this.props.sidebarData || !this.props.sidebarData.files) {
this.props.onCancel();
}
};
_handleCreateSlate = async () => {
if (
Object.values(this.state.selected).some((value) => {
@ -140,8 +146,7 @@ export default class SidebarAddFileToSlate extends React.Component {
marginBottom: "64px",
}}
>
Add {this.props.sidebarData.files.length || 0} file
{this.props.sidebarData.files.length === 1 ? "" : "s"} to slate
Add files to slate
</System.P>
<System.P css={STYLES_HEADER}>Slates</System.P>