aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/FileUpload/FileUpload.tsx8
-rw-r--r--src/containers/PollCreation/ImageInput.tsx10
-rw-r--r--src/containers/PollCreation/PollCreation.tsx2
-rw-r--r--src/containers/PollCreation/useS3Preupload.ts40
-rw-r--r--src/containers/Profile/ProfileInfo.tsx23
-rw-r--r--src/hooks/useS3Preupload.tsx73
-rw-r--r--src/utils/files.ts29
-rw-r--r--src/utils/getLocalFileUrl.ts15
-rw-r--r--src/utils/uploadFileToS3.ts57
9 files changed, 135 insertions, 122 deletions
diff --git a/src/components/FileUpload/FileUpload.tsx b/src/components/FileUpload/FileUpload.tsx
index d455f62..961fa9a 100644
--- a/src/components/FileUpload/FileUpload.tsx
+++ b/src/components/FileUpload/FileUpload.tsx
@@ -1,10 +1,9 @@
import React, { useRef } from 'react';
import Button from '@material-ui/core/Button';
import CloudUpload from '@material-ui/icons/CloudUpload';
-import { getLocalFileUrl } from '../../utils/files';
interface PropTypes {
- callback: (fileUrl: string, file: File) => void;
+ callback: (file: File) => void;
}
@@ -13,10 +12,7 @@ const FileUpload: React.FC<PropTypes> = ({ callback, children }) => {
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const files = event.target?.files;
- if (files?.length) {
- const file = files[0];
- getLocalFileUrl(file).then(url => callback(url, file));
- };
+ if (files?.length) callback(files[0]);
};
const handleClick = () => {
diff --git a/src/containers/PollCreation/ImageInput.tsx b/src/containers/PollCreation/ImageInput.tsx
index 475d527..e807865 100644
--- a/src/containers/PollCreation/ImageInput.tsx
+++ b/src/containers/PollCreation/ImageInput.tsx
@@ -10,6 +10,7 @@ import { Check, CancelOutlined } from '@material-ui/icons';
import AttachLink from '../../components/AttachLink/AttachLink';
import FileUpload from '../../components/FileUpload/FileUpload';
import BackgroundImage from '../../components/Image/BackgroundImage';
+import getLocalFileUrl from '../../utils/getLocalFileUrl';
interface PropTypes {
callback: (file?: File | string) => void;
@@ -59,9 +60,12 @@ const ImageInput: React.FC<PropTypes> = ({ callback, progress }) => {
callback(undefined);
};
- const childrenCallback = (fileUrl: string, file?: File) => {
- setUrl(fileUrl);
- callback(file || fileUrl);
+ const childrenCallback = (value: File | string) => {
+ if (value instanceof File) {
+ getLocalFileUrl(value).then(localUrl => setUrl(localUrl));
+ } else setUrl(value);
+
+ callback(value);
};
const Upload = (
diff --git a/src/containers/PollCreation/PollCreation.tsx b/src/containers/PollCreation/PollCreation.tsx
index 03ab905..87bdcf7 100644
--- a/src/containers/PollCreation/PollCreation.tsx
+++ b/src/containers/PollCreation/PollCreation.tsx
@@ -12,12 +12,12 @@ import {
} from '@material-ui/core';
import { useSnackbar } from 'notistack';
+import useS3Preupload from './useS3Preupload';
import ImageInput from './ImageInput';
import UserStrip from '../../components/UserStrip/UserStrip';
import { post } from '../../requests';
import { useAuth } from '../../hooks/useAuth';
import { useFeed } from '../../hooks/APIClient';
-import useS3Preupload from '../../hooks/useS3Preupload';
const useStyles = makeStyles(theme => ({
diff --git a/src/containers/PollCreation/useS3Preupload.ts b/src/containers/PollCreation/useS3Preupload.ts
new file mode 100644
index 0000000..ef3a408
--- /dev/null
+++ b/src/containers/PollCreation/useS3Preupload.ts
@@ -0,0 +1,40 @@
+import { useState, useCallback, useMemo } from 'react';
+import uploadFileToS3 from '../../utils/uploadFileToS3';
+
+
+interface Hook {
+ setValue: (value: File | string | undefined) => void;
+ isReady: boolean;
+ resolve: () => Promise<string>;
+ progress: number;
+}
+
+export default (): Hook => {
+ const [url, setUrl] = useState<string>();
+ const [file, setFile] = useState<File>();
+ const [progress, setProgress] = useState<number>(0);
+
+ const isReady = useMemo(() => Boolean(file || url), [file, url]);
+
+ const setValue: Hook['setValue'] = useCallback(value => {
+ if (value instanceof File) {
+ setFile(value);
+ setUrl(undefined);
+ } else {
+ setUrl(value);
+ setFile(undefined);
+ }
+ }, [setUrl, setFile]);
+
+ const resolve = useCallback(async (quality?: number): Promise<string> => {
+ if (file) return uploadFileToS3(file, quality, setProgress);
+ return url || '';
+ }, [file, url]);
+
+ return {
+ setValue,
+ isReady,
+ resolve,
+ progress
+ };
+};
diff --git a/src/containers/Profile/ProfileInfo.tsx b/src/containers/Profile/ProfileInfo.tsx
index c9831f3..fbd6272 100644
--- a/src/containers/Profile/ProfileInfo.tsx
+++ b/src/containers/Profile/ProfileInfo.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useCallback } from 'react';
import { Badge, Typography } from '@material-ui/core/';
import { makeStyles } from '@material-ui/core/styles';
import { User } from 'which-types';
@@ -6,10 +6,11 @@ import CameraAltIcon from '@material-ui/icons/CameraAlt';
import VerifiedIcon from '@material-ui/icons/CheckCircleOutline';
import Skeleton from '@material-ui/lab/Skeleton';
import Highlight from './Highlight';
-import AttachLink from '../../components/AttachLink/AttachLink';
+import FileUpload from '../../components/FileUpload/FileUpload';
import Avatar from '../../components/Avatar/Avatar';
import { patch } from '../../requests';
import { useAuth } from '../../hooks/useAuth';
+import uploadFileToS3 from '../../utils/uploadFileToS3';
interface PropTypes {
@@ -88,14 +89,16 @@ const ProfileInfo: React.FC<PropTypes> = ({
}) => {
const classes = useStyles();
const { user } = useAuth();
+
const dateSince = new Date(userInfo?.createdAt || '').toLocaleDateString();
- const patchAvatar = (url: string) => {
- const id = user?._id;
- patch(`/users/${id}`, { avatarUrl: url }).then(res => {
- setUserInfo(res.data);
- });
- };
+ const handleUpdateAvatar = useCallback(async (file: File) => {
+ if (user) {
+ uploadFileToS3(file, 1)
+ .then(avatarUrl => patch(`/users/${user._id}`, { avatarUrl }))
+ .then(response => setUserInfo(response.data));
+ }
+ }, [user, setUserInfo]);
return (
<div className={classes.root}>
@@ -104,7 +107,7 @@ const ProfileInfo: React.FC<PropTypes> = ({
? <Skeleton animation="wave" variant="circle" width={150} height={150} className={classes.avatar} />
: userInfo?._id === user?._id
? (
- <AttachLink callback={patchAvatar}>
+ <FileUpload callback={handleUpdateAvatar}>
<div className={classes.avatarContainer}>
<Badge
overlap="circle"
@@ -121,7 +124,7 @@ const ProfileInfo: React.FC<PropTypes> = ({
<Avatar className={classes.avatar} user={userInfo} />
</Badge>
</div>
- </AttachLink>
+ </FileUpload>
)
: <Avatar className={classes.avatar} user={userInfo} />
}
diff --git a/src/hooks/useS3Preupload.tsx b/src/hooks/useS3Preupload.tsx
deleted file mode 100644
index 9516459..0000000
--- a/src/hooks/useS3Preupload.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { useState, useCallback, useMemo } from 'react';
-import axios from 'axios';
-import Bluebird from 'bluebird';
-import { get } from '../requests';
-import { compressFile } from '../utils/files';
-
-interface ProgressEvent {
- loaded: number;
- total: number;
-}
-
-interface Hook {
- setValue: (value: File | string | undefined) => void;
- isReady: boolean;
- resolve: () => Promise<string>;
- progress: number;
-}
-
-export default (): Hook => {
- const [url, setUrl] = useState<string>();
- const [file, setFile] = useState<File>();
- const [progress, setProgress] = useState<number>(0);
-
- const isReady = useMemo(() => Boolean(file || url), [file, url]);
-
- const setValue: Hook['setValue'] = useCallback(value => {
- if (value instanceof File) {
- setFile(value);
- setUrl(undefined);
- } else {
- setUrl(value);
- setFile(undefined);
- }
- }, [setUrl, setFile]);
-
- const handleUploadProgress = useCallback((progressEvent: ProgressEvent): void => {
- // Only allow upload progress reach 95%, and set 100% when request is resolved
- setProgress(Math.round((progressEvent.loaded * 95) / progressEvent.total));
- }, [setProgress]);
-
- const resolve = useCallback(async (quality?: number): Promise<string> => {
- if (file) {
- const config = {
- headers: { 'Content-Type': 'image/png' },
- onUploadProgress: handleUploadProgress
- };
-
- setProgress(0.01);
-
- // Add querystring to avoid caching request in some browsers, see
- // https://stackoverflow.com/questions/59339561/safari-skipping-xmlhttprequests
- return Bluebird.all([get(`/files?key=${file.name}`), compressFile(file, quality)])
- .then(([response, compressedFile]) => {
- const uploadUrl = response.data;
- return axios.put(uploadUrl, compressedFile, config);
- })
- .then(response => {
- setProgress(100);
- const uri = response.config.url;
- return uri ? uri.slice(0, uri.indexOf('?')) : '';
- });
- }
- setProgress(100);
- return url || '';
- }, [file, handleUploadProgress, url]);
-
- return {
- setValue,
- isReady,
- resolve,
- progress
- };
-};
diff --git a/src/utils/files.ts b/src/utils/files.ts
deleted file mode 100644
index 5d16fdb..0000000
--- a/src/utils/files.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import Compressor from 'compressorjs';
-
-
-export const getLocalFileUrl = (file: File): Promise<string> => {
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
-
- reader.addEventListener('load', () => {
- if (typeof reader.result === 'string') resolve(reader.result);
- }, false);
-
- reader.addEventListener('error', () => {
- reject(new Error('Error reading the file'))
- }, false );
-
- if (file) reader.readAsDataURL(file);
- });
-};
-
-export const compressFile = (file: File, quality = 0.6): Promise<Blob> => {
- return new Promise((resolve, reject) => {
- return new Compressor(file, {
- success: result => resolve(result),
- error: err => reject(err),
- quality
- });
- });
-};
-
diff --git a/src/utils/getLocalFileUrl.ts b/src/utils/getLocalFileUrl.ts
new file mode 100644
index 0000000..1469804
--- /dev/null
+++ b/src/utils/getLocalFileUrl.ts
@@ -0,0 +1,15 @@
+export default (file: File): Promise<string> => {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+
+ reader.addEventListener('load', () => {
+ if (typeof reader.result === 'string') resolve(reader.result);
+ }, false);
+
+ reader.addEventListener('error', () => {
+ reject(new Error('Error reading the file'));
+ }, false);
+
+ if (file) reader.readAsDataURL(file);
+ });
+};
diff --git a/src/utils/uploadFileToS3.ts b/src/utils/uploadFileToS3.ts
new file mode 100644
index 0000000..de1b1aa
--- /dev/null
+++ b/src/utils/uploadFileToS3.ts
@@ -0,0 +1,57 @@
+import Compressor from 'compressorjs';
+import axios from 'axios';
+import Bluebird from 'bluebird';
+import { get } from '../requests';
+
+interface ProgressEvent {
+ loaded: number;
+ total: number;
+}
+
+const compressFile = (file: File, quality = 0.6): Promise<File | Blob> => {
+ return new Promise((resolve, reject) => {
+ if (quality === 1) resolve(file);
+ else return new Compressor(file, {
+ success: result => resolve(result),
+ error: err => reject(err),
+ quality
+ });
+ });
+};
+
+export default (
+ file: File,
+ quality?: number,
+ setProgress?: (progress: number) => void
+): Promise<string> => {
+
+ const onUploadProgress = (progressEvent: ProgressEvent): void => {
+ if (setProgress) {
+ // Only allow upload progress reach 95%, and set 100% when request is resolved
+ const progress = Math.round((progressEvent.loaded * 95) / progressEvent.total);
+ setProgress(progress);
+ }
+ };
+
+ const config = {
+ headers: { 'Content-Type': 'image/png' },
+ onUploadProgress
+ };
+
+ // Indicate start
+ if (setProgress) setProgress(0.01);
+
+ // Add querystring to avoid caching request in some browsers, see
+ // https://stackoverflow.com/questions/59339561/safari-skipping-xmlhttprequests
+ return Bluebird.all([get(`/files?key=${file.name}`), compressFile(file, quality)])
+ .then(([response, compressedFile]) => {
+ const uploadUrl = response.data;
+ return axios.put(uploadUrl, compressedFile, config);
+ })
+ .then(response => {
+ if (setProgress) setProgress(100);
+ const uri = response.config.url;
+ return uri ? uri.slice(0, uri.indexOf('?')) : '';
+ });
+};
+