Skip to content

Commit f5a3b9d

Browse files
authored
Merge pull request #40 from coreofscience/limit-by-file-size
Limit by file size
2 parents 87cb0f3 + 7400a91 commit f5a3b9d

File tree

11 files changed

+240
-33
lines changed

11 files changed

+240
-33
lines changed

src/components/providers/FilesProvider.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,33 @@ const FilesProvider: FC<Props> = ({ children }: Props) => {
4141
});
4242
}, []);
4343

44+
const swap = useCallback(
45+
(hash: string) => {
46+
setFiles((prev) => {
47+
const index = prev.findIndex((file) => file.hash === hash);
48+
if (index < 0) {
49+
return prev;
50+
}
51+
return [
52+
files[index],
53+
...prev.slice(0, index),
54+
...prev.slice(index + 1),
55+
];
56+
});
57+
},
58+
[files]
59+
);
60+
4461
const value = useMemo(
4562
() => ({
4663
add,
4764
files,
4865
remove,
4966
track,
5067
progress,
68+
swap,
5169
}),
52-
[add, files, remove, track, progress]
70+
[add, files, remove, track, progress, swap]
5371
);
5472

5573
return <FileContext.Provider value={value}>{children}</FileContext.Provider>;

src/components/upload/FileCard.css

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
justify-content: space-between;
66
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.25);
77
position: relative;
8-
padding: 35px 1em;
8+
padding: 45px 1em 50px;
99
text-align: center;
1010
margin-top: 1em;
1111
}
1212

1313
.fileCard .fileCard__closeButton {
14-
background-color: #f0972f;
15-
color: #fcfcfc;
14+
background-color: var(--color-tree-root);
15+
color: var(--color-bg);
1616
height: 30px;
1717
width: 30px;
1818
border: none;
@@ -46,27 +46,45 @@
4646
.fileCard .fileCard__keywords {
4747
margin-top: 1em;
4848
font-size: 16px;
49-
color: #5b5b5b;
49+
color: var(--color-label);
5050
max-width: 90%;
5151
}
5252

5353
.fileCard .fileCard__statGroup {
5454
margin-top: 1em;
5555
}
56+
5657
.fileCard .fileCard__stats {
5758
margin-top: 0.5em;
5859
display: flex;
5960
flex-direction: column;
6061
align-items: center;
6162
}
63+
6264
.fileCard .fileCard__stats__count {
6365
font-size: 24px;
6466
font-weight: 600;
6567
}
68+
6669
.fileCard .fileCard__stats__unit {
6770
font-size: 14px;
68-
color: #5b5b5b;
71+
color: var(--color-label);
72+
}
73+
74+
.fileCard .fileCard__weight {
75+
position: absolute;
76+
bottom: 0.5rem;
77+
right: 0.5rem;
78+
}
79+
80+
.fileCard .fileCard__weight small {
81+
font-size: 60%;
6982
}
83+
84+
.fileCard.capped .fileCard__weight small {
85+
color: var(--color-red-accent);
86+
}
87+
7088
.fileCard_progressBar {
7189
position: absolute;
7290
bottom: 0;
@@ -77,9 +95,42 @@
7795
background: #f0f0f0;
7896
}
7997
.fileCard_progressBar .fileCard_progressAdjustment {
80-
background: #009d35;
98+
background: var(--color-tree-leaf);
8199
width: 100%;
82100
transform-origin: 0%;
83101
height: 3px;
84102
margin: 0;
85103
}
104+
105+
.fileCard.capped {
106+
background: var(--color-bg-accent);
107+
}
108+
109+
.fileCard .fileCard__moveButton {
110+
background-color: var(--color-tree-root);
111+
color: var(--color-bg);
112+
height: 30px;
113+
width: 30px;
114+
border: none;
115+
display: flex;
116+
justify-content: center;
117+
align-items: center;
118+
position: absolute;
119+
top: 0;
120+
left: 0;
121+
transition: background-color 200ms cubic-bezier(0.42, 0, 0.56, 1.7);
122+
}
123+
124+
.fileCard .fileCard__moveButton:hover,
125+
.fileCard .fileCard__moveButton:active {
126+
background-color: var(--color-tree-leaf);
127+
}
128+
129+
.fileCard .fileCard__moveButton img {
130+
width: 14px;
131+
height: 14px;
132+
}
133+
134+
.fileCard .fileCard__moveButton svg.move-icon path {
135+
fill: var(--color-bg);
136+
}

src/components/upload/FileCard.tsx

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import React, { FC } from "react";
22

33
import CancelFile from "../vectors/CancelFile";
4+
import MoveFirstIcon from "../vectors/MoveFirstIcon";
5+
6+
import { round } from "../../utils/mathUtils";
7+
import { MAX_SIZE } from "../../utils/computeQuantities";
8+
49
import "./FileCard.css";
510

611
interface Props {
@@ -9,7 +14,11 @@ interface Props {
914
articles?: number;
1015
citations?: number;
1116
remove?: () => void;
17+
move?: () => void;
1218
progress: number;
19+
capped?: boolean;
20+
size: number;
21+
cumSize: number;
1322
}
1423

1524
const FileCard: FC<Props> = ({
@@ -19,9 +28,23 @@ const FileCard: FC<Props> = ({
1928
citations = 0,
2029
progress = 0,
2130
remove = () => {},
31+
move = () => {},
32+
capped = true,
33+
size = 0,
34+
cumSize = 0,
2235
}: Props) => {
36+
const countFormat = new Intl.NumberFormat(undefined);
37+
const weightFormat = new Intl.NumberFormat(undefined, {
38+
minimumFractionDigits: 2,
39+
});
40+
2341
return (
24-
<div className="fileCard">
42+
<div className={capped ? "fileCard capped" : "fileCard"}>
43+
{capped && (
44+
<button onClick={move} className="fileCard__moveButton">
45+
<MoveFirstIcon />
46+
</button>
47+
)}
2548
<button onClick={remove} className="fileCard__closeButton">
2649
<CancelFile />
2750
</button>
@@ -31,18 +54,28 @@ const FileCard: FC<Props> = ({
3154
<small className="fileCard__keywords">{keywords.join(", ")}</small>
3255
<div className="fileCard__statGroup">
3356
<span className="fileCard__stats">
34-
<strong className="fileCard__stats__count">{articles}</strong>
57+
<strong className="fileCard__stats__count">
58+
{countFormat.format(articles)}
59+
</strong>
3560
<small className="fileCard__stats__unit">
3661
{articles === 1 ? "article" : "articles"}
3762
</small>
3863
</span>
3964
<span className="fileCard__stats">
40-
<strong className="fileCard__stats__count">{citations}</strong>
65+
<strong className="fileCard__stats__count">
66+
{countFormat.format(citations)}
67+
</strong>
4168
<small className="fileCard__stats__unit">
4269
{citations === 1 ? "citation" : "citations"}
4370
</small>
4471
</span>
4572
</div>
73+
<div className="fileCard__weight">
74+
<small title="To keep our costs down, we need to limit the ammount of data we process for each tree.">
75+
{weightFormat.format(round(cumSize, 2))} /{" "}
76+
{weightFormat.format(round(MAX_SIZE, 2))} [MB]
77+
</small>
78+
</div>
4679
<div className="fileCard_progressBar">
4780
<div
4881
className="fileCard_progressAdjustment"

src/components/upload/Home.tsx

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,19 @@ import FileDropper from "./FileDropper";
77
import UploadIndicator from "./UploadIndicator";
88
import FileErrors from "./FileErrors";
99

10-
import { FileMetadata } from "../../utils/customTypes";
1110
import useFiles from "../../hooks/useFiles";
1211
import FirebaseContext from "../../context/FirebaseContext";
1312

13+
import computeQuantities, { MAX_SIZE } from "../../utils/computeQuantities";
14+
import { round } from "../../utils/mathUtils";
15+
1416
import "./Home.css";
1517

16-
const numberFormat = new Intl.NumberFormat();
18+
const countFormat = new Intl.NumberFormat();
19+
const weightFormat = new Intl.NumberFormat(undefined, {
20+
minimumFractionDigits: 2,
21+
maximumFractionDigits: 2,
22+
});
1723

1824
const createTree = async ({
1925
app,
@@ -54,21 +60,13 @@ const Home: FC<{}> = () => {
5460
const firebase = useContext(FirebaseContext);
5561
const history = useHistory();
5662

57-
const totalArticles = files.reduce((acc, el) => acc + (el.articles || 0), 0);
58-
const totalCitations = files.reduce(
59-
(acc, el) => acc + (el.citations || 0),
60-
0
61-
);
62-
const articleCap = Math.min(totalArticles, 500);
63-
const citationCap = files
64-
.reduce(
65-
(acc: FileMetadata[], el) =>
66-
acc.reduce((acc, el) => acc + (el.articles || 0), 0) >= articleCap
67-
? acc
68-
: [...acc, el],
69-
[]
70-
)
71-
.reduce((acc, el) => acc + (el.citations || 0), 0);
63+
const {
64+
totalArticles,
65+
totalCitations,
66+
articleCap,
67+
citationCap,
68+
sizeCap,
69+
} = computeQuantities(files);
7270

7371
const [create, { isLoading, isError }] = useMutation(createTree, {
7472
onSuccess: (treeId: string) => history.push(`/tree/${treeId}`),
@@ -87,18 +85,24 @@ const Home: FC<{}> = () => {
8785
<div className="information-cant-article">
8886
<div className="frame-article">
8987
<span className="total-articles">
90-
{numberFormat.format(articleCap)}/
91-
{numberFormat.format(totalArticles)}
88+
{countFormat.format(articleCap)}/{countFormat.format(totalArticles)}
9289
</span>
9390
<span className="articles">articles</span>
9491
</div>
9592
<div className="frame-article">
9693
<span className="total-articles">
97-
{numberFormat.format(citationCap)}/
98-
{numberFormat.format(totalCitations)}
94+
{countFormat.format(citationCap)}/
95+
{countFormat.format(totalCitations)}
9996
</span>
10097
<span className="articles">citations</span>
10198
</div>
99+
<div className="frame-article">
100+
<span className="total-articles">
101+
{weightFormat.format(round(sizeCap, 2))}/
102+
{weightFormat.format(round(MAX_SIZE, 2))}
103+
</span>
104+
<span className="articles">size [MB]</span>
105+
</div>
102106
</div>
103107
<br></br>
104108
<div>Time to create your Tree of Science.</div>

src/components/upload/UploadIndicator.tsx

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,41 @@
1-
import React, { FC, useContext } from "react";
1+
import React, { FC, useContext, useEffect, useState } from "react";
22
import "./UploadIndicator.css";
33

44
import FileCard from "./FileCard";
55
import FileContext from "../../context/FileContext";
66
import useFiles from "../../hooks/useFiles";
7+
import { MAX_SIZE } from "../../utils/computeQuantities";
78

89
interface Props {}
910

1011
const UploadIndicator: FC<Props> = () => {
11-
const { remove } = useContext(FileContext);
12+
const { remove, swap } = useContext(FileContext);
1213
const { progress } = useContext(FileContext);
1314
const files = useFiles();
15+
16+
const [cappedFiles, setCappedFiles] = useState<{ [hash: string]: boolean }>(
17+
{}
18+
);
19+
20+
let cumSize = 0;
21+
22+
useEffect(() => {
23+
let size = 0;
24+
for (const file of files) {
25+
size += file.blob.size / 2 ** 20;
26+
27+
if (size <= MAX_SIZE) {
28+
setCappedFiles((prev) => ({ ...prev, [file.hash]: false }));
29+
} else {
30+
setCappedFiles((prev) => ({ ...prev, [file.hash]: true }));
31+
}
32+
}
33+
}, [files]);
34+
1435
return (
1536
<div className="uploadIndicator">
1637
{files.map((file) => {
38+
cumSize += file.blob.size / 2 ** 20;
1739
return (
1840
<FileCard
1941
name={file.name}
@@ -22,6 +44,10 @@ const UploadIndicator: FC<Props> = () => {
2244
citations={file.citations}
2345
keywords={file.keywords}
2446
remove={() => remove(file.hash)}
47+
move={() => swap(file.hash)}
48+
capped={cappedFiles[file.hash]}
49+
size={file.blob.size / 2 ** 20}
50+
cumSize={cumSize}
2551
key={file.hash}
2652
/>
2753
);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from "react";
2+
3+
const MoveFirstIcon = () => (
4+
<svg
5+
className="move-icon"
6+
viewBox="0 0 18 20"
7+
fill="none"
8+
xmlns="http://www.w3.org/2000/svg"
9+
>
10+
<path
11+
d="M16 10L8 2M10 10l-8 8M16 18l-8-8M10 2l-8 8"
12+
stroke="#FCFCFC"
13+
stroke-width="3"
14+
/>
15+
</svg>
16+
);
17+
18+
export default MoveFirstIcon;

src/context/FileContext.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const FileContext = createContext<FileContexType>({
55
add: () => {},
66
remove: () => {},
77
track: () => {},
8+
swap: () => {},
89
progress: {},
910
files: [],
1011
});

src/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ template {
370370
--color-tree-trunk: #904c14;
371371
--color-bg: #fcfcfc;
372372
--color-bg-accent: #f0f0f0;
373+
--color-red-accent: rgb(226, 132, 148);
373374
--color-link: #366fc6;
374375
--color-link-hover: #3a6099;
375376
--color-label: #5b5b5b;

0 commit comments

Comments
 (0)