Skip to content

Commit 6a9a92b

Browse files
wuweiweiwuCompuIves
authored andcommitted
feat: UPLOADING FILES (codesandbox#685)
* YAY UPLOADING FILES WORK * Force CircleCI rebuild * Upload progress modal * Remove only image restriction * Fix linting issues * More specific errors for uploading failure. * prelim modal * Finished table * update cerebral sequences and model * state updates * updating cerebral state tree for files * pretty file management modal + loading animation * Fix second argument of error * Only show upload for signed in users * Sort name of uploads and add to table * Always open file in new tab
1 parent a473014 commit 6a9a92b

File tree

23 files changed

+596
-4
lines changed

23 files changed

+596
-4
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import styled, { keyframes } from 'styled-components';
2+
3+
const rotateAnimation = keyframes`
4+
0% {
5+
transform: rotateX(-37.5deg) rotateY(45deg);
6+
}
7+
100% {
8+
transform: rotateX(-37.5deg) rotateY(405deg);
9+
}
10+
`;
11+
12+
export const Cube = styled.div`
13+
position: absolute;
14+
width: ${props => props.size + 1}px;
15+
height: ${props => props.size + 1}px;
16+
17+
* {
18+
position: absolute;
19+
width: ${props => props.size + 1}px;
20+
height: ${props => props.size + 1}px;
21+
}
22+
`;
23+
24+
export const Sides = styled.div`
25+
${({ noAnimation }) =>
26+
!noAnimation && `animation: ${rotateAnimation} 1s linear infinite;`};
27+
transform-style: preserve-3d;
28+
transform: rotateX(-37.5deg) rotateY(45deg);
29+
30+
* {
31+
box-sizing: border-box;
32+
background-color: rgba(242, 119, 119, 0.5);
33+
border: ${({ size }) => size / 10}px solid white;
34+
}
35+
`;
36+
37+
export const Side = styled.div`
38+
transform-origin: 50% 50%;
39+
transform: ${({ rotate }) => rotate} translateZ(${({ size }) => size / 2}px);
40+
`;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react';
2+
3+
import { Cube, Sides, Side } from './elements';
4+
5+
function CubeComponent({ size = 150, noAnimation, className }) {
6+
return (
7+
<Cube className={className} size={size}>
8+
<Sides noAnimation={noAnimation} size={size}>
9+
<Side rotate="rotateX(90deg)" size={size} />
10+
<Side rotate="rotateX(-90deg)" size={size} />
11+
<Side rotate="rotateY(0deg)" size={size} />
12+
<Side rotate="rotateY(-180deg)" size={size} />
13+
<Side rotate="rotateY(-90deg)" size={size} />
14+
<Side rotate="rotateY(90deg)" size={size} />
15+
</Sides>
16+
</Cube>
17+
);
18+
}
19+
20+
export default CubeComponent;
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import styled, { keyframes } from 'styled-components';
2+
3+
import delayInEffect from 'common/utils/animation/delay-effect';
4+
import OpaqueLogo from 'app/components/OpaqueLogo';
5+
import LocalLogo from 'react-icons/lib/md/laptop';
6+
import Cube from './Cube';
7+
8+
export const Container = styled.div`
9+
position: relative;
10+
background-color: ${props => props.theme.background};
11+
12+
text-align: center;
13+
14+
font-size: 1.125rem;
15+
padding: 1rem 2rem;
16+
font-weight: 500;
17+
color: rgba(255, 255, 255, 0.8);
18+
`;
19+
20+
export const DeployAnimationContainer = styled.div`
21+
display: flex;
22+
justify-content: center;
23+
align-items: center;
24+
padding: 3rem;
25+
26+
${({ deploying }) => deploying && delayInEffect(0, false)};
27+
`;
28+
29+
export const StyledLocalLogo = styled(LocalLogo)`
30+
position: absolute;
31+
font-size: 4rem;
32+
transform: translateY(15px) translateX(-100px);
33+
background-color: ${props => props.theme.background};
34+
color: white;
35+
z-index: 20;
36+
`;
37+
38+
const cubeAnimation = keyframes`
39+
0% {
40+
transform: translateY(20px) translateX(-100px) scale(0, 0);
41+
}
42+
43+
20% {
44+
transform: translateY(20px) translateX(-100px) scale(1, 1);
45+
}
46+
47+
80% {
48+
transform: translateY(20px) translateX(80px) scale(1, 1);
49+
}
50+
51+
100% {
52+
transform: translateY(20px) translateX(80px) scale(1, 1);
53+
}
54+
`;
55+
56+
export const StyledCube = styled(Cube)`
57+
position: absolute;
58+
animation: ${cubeAnimation} 2s ease-in infinite;
59+
animation-delay: ${({ i }) => i * 0.5}s;
60+
transform: translateY(20px) translateX(-100px) scale(0, 0);
61+
`;
62+
63+
export const StyledLogo = styled(OpaqueLogo)`
64+
position: absolute;
65+
z-index: 10;
66+
transform: translateY(10px) translateX(80px);
67+
`;
68+
69+
export const DeployText = styled.div`
70+
${delayInEffect()};
71+
margin-top: 2rem;
72+
margin-bottom: 2rem;
73+
font-size: 1.125rem;
74+
`;
75+
76+
export const Result = styled.div`
77+
${delayInEffect(0.25)};
78+
font-size: 1.125rem;
79+
margin-bottom: 1rem;
80+
`;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from 'react';
2+
3+
import {
4+
Container,
5+
DeployAnimationContainer,
6+
StyledLocalLogo,
7+
StyledCube,
8+
StyledLogo,
9+
DeployText,
10+
Result,
11+
} from './elements';
12+
13+
function Progress({ message, result }) {
14+
return (
15+
<Container>
16+
{result ? (
17+
<Result>{result}</Result>
18+
) : (
19+
<div>
20+
<DeployAnimationContainer deploying>
21+
<StyledLocalLogo />
22+
{[0, 1, 2, 3].map(i => <StyledCube key={i} i={i} size={20} />)}
23+
<StyledLogo width={70} height={70} />
24+
</DeployAnimationContainer>
25+
<DeployText>{message}</DeployText>
26+
</div>
27+
)}
28+
</Container>
29+
);
30+
}
31+
32+
export default Progress;

packages/app/src/app/pages/Sandbox/Editor/Workspace/Files/DirectoryEntry/Entry/EditIcons/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import CrossIcon from 'react-icons/lib/md/clear';
44
import EditIcon from 'react-icons/lib/go/pencil';
55
import AddFileIcon from 'react-icons/lib/md/insert-drive-file';
66
import AddDirectoryIcon from 'react-icons/lib/md/create-new-folder';
7+
import UploadFileIcon from 'react-icons/lib/md/file-upload';
78

89
import Tooltip from 'common/components/Tooltip';
910

@@ -24,6 +25,7 @@ function EditIcons({
2425
onCreateFile,
2526
onCreateDirectory,
2627
active,
28+
onUploadFile,
2729
forceShow,
2830
}) {
2931
// Phones need double click if we show elements on click, that's why we only want
@@ -43,6 +45,13 @@ function EditIcons({
4345
</Icon>
4446
</Tooltip>
4547
)}
48+
{onUploadFile && (
49+
<Tooltip title="Upload File">
50+
<Icon onClick={handleClick(onUploadFile)}>
51+
<UploadFileIcon />
52+
</Icon>
53+
</Tooltip>
54+
)}
4655
{onCreateFile && (
4756
<Tooltip title="New File">
4857
<Icon onClick={handleClick(onCreateFile)}>

packages/app/src/app/pages/Sandbox/Editor/Workspace/Files/DirectoryEntry/Entry/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import FileIcon from 'react-icons/lib/fa/file';
66
import FolderIcon from 'react-icons/lib/fa/folder';
77
import EditIcon from 'react-icons/lib/go/pencil';
88
import DeleteIcon from 'react-icons/lib/go/trashcan';
9+
import UploadFileIcon from 'react-icons/lib/md/file-upload';
910

1011
import theme from 'common/theme';
1112

@@ -79,6 +80,7 @@ class Entry extends React.PureComponent {
7980
connectDragSource, // eslint-disable-line
8081
onCreateModuleClick,
8182
onCreateDirectoryClick,
83+
onUploadFileClick,
8284
deleteEntry,
8385
onClick,
8486
markTabsNotDirty,
@@ -102,6 +104,11 @@ class Entry extends React.PureComponent {
102104
action: onCreateDirectoryClick,
103105
icon: FolderIcon,
104106
},
107+
onUploadFileClick && {
108+
title: 'Upload File',
109+
action: onUploadFileClick,
110+
icon: UploadFileIcon,
111+
},
105112
rename && {
106113
title: 'Rename',
107114
action: this.rename,
@@ -159,6 +166,7 @@ class Entry extends React.PureComponent {
159166
hovering={hovering}
160167
onCreateFile={onCreateModuleClick}
161168
onCreateDirectory={onCreateDirectoryClick}
169+
onUploadFile={onUploadFileClick}
162170
onDelete={deleteEntry && this.delete}
163171
onEdit={rename && this.rename}
164172
active={active}

packages/app/src/app/pages/Sandbox/Editor/Workspace/Files/DirectoryEntry/index.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,34 @@ class DirectoryEntry extends React.Component {
112112
this.resetState();
113113
};
114114

115+
onUploadFileClick = () => {
116+
const fileSelector = document.createElement('input');
117+
fileSelector.setAttribute('type', 'file');
118+
fileSelector.onchange = event => {
119+
const file = event.target.files[0];
120+
if (!file) {
121+
return;
122+
}
123+
124+
this.readImageFile(file, base64Image => {
125+
this.props.signals.files.fileUploaded({
126+
content: base64Image,
127+
name: file.name,
128+
});
129+
});
130+
};
131+
132+
fileSelector.click();
133+
};
134+
135+
readImageFile = (imageFile, callback) => {
136+
const reader = new FileReader();
137+
reader.onload = e => {
138+
callback(e.target.result);
139+
};
140+
reader.readAsDataURL(imageFile);
141+
};
142+
115143
renameDirectory = (directoryShortid, title) => {
116144
this.props.signals.files.directoryRenamed({ title, directoryShortid });
117145
};
@@ -202,6 +230,7 @@ class DirectoryEntry extends React.Component {
202230
rename={!root && this.renameDirectory}
203231
onCreateModuleClick={this.onCreateModuleClick}
204232
onCreateDirectoryClick={this.onCreateDirectoryClick}
233+
onUploadFileClick={this.onUploadFileClick}
205234
deleteEntry={!root && this.deleteDirectory}
206235
hasChildren={this.getChildren().length > 0}
207236
closeTree={this.closeTree}

packages/app/src/app/pages/Sandbox/Editor/Workspace/Files/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ class Files extends React.Component {
1717
this.directory.onCreateDirectoryClick();
1818
};
1919

20+
uploadFile = () => {
21+
// INCREDIBLY BAD PRACTICE! TODO: FIX THIS
22+
this.directory.onUploadFileClick();
23+
};
24+
2025
render() {
2126
const store = this.props.store;
2227
const sandbox = store.editor.currentSandbox;
@@ -42,6 +47,7 @@ class Files extends React.Component {
4247
forceShow={window.__isTouch}
4348
onCreateFile={this.createModule}
4449
onCreateDirectory={this.createDirectory}
50+
onUploadFile={store.isLoggedIn ? this.uploadFile : undefined}
4551
/>
4652
}
4753
>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
import DeleteIcon from 'react-icons/lib/md/delete';
4+
import Tooltip from 'common/components/Tooltip';
5+
6+
export const DeleteFileButton = styled(props => (
7+
<Tooltip title="Delete File">
8+
<button {...props}>
9+
<DeleteIcon />
10+
</button>
11+
</Tooltip>
12+
))`
13+
font-size: 1.2em;
14+
background-color: inherit;
15+
border: none;
16+
padding: 5px 6px 9px 6px;
17+
color: rgba(255, 255, 255, 0.5);
18+
cursor: pointer;
19+
&:hover {
20+
color: rgba(255, 255, 255, 1);
21+
}
22+
&[disabled] {
23+
opacity: 0.5;
24+
cursor: default;
25+
}
26+
`;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as React from 'react';
2+
import { DeleteFileButton } from './elements';
3+
4+
export default class DeleteFileButtonContainer extends React.PureComponent {
5+
deleteFile = () => {
6+
this.props.onDelete({ id: this.props.id });
7+
};
8+
9+
render() {
10+
return <DeleteFileButton onClick={this.deleteFile} />;
11+
}
12+
}

0 commit comments

Comments
 (0)