added upload changes

This commit is contained in:
subnub
2024-07-22 00:43:04 -04:00
parent 73caa872c9
commit db925a69d2
17 changed files with 425 additions and 255 deletions
+10 -6
View File
@@ -5,7 +5,10 @@ import { useClickOutOfBounds } from "../../hooks/utils";
import { showCreateFolderPopup } from "../../popups/folder";
import { useAppDispatch } from "../../hooks/store";
import { startAddFile } from "../../actions/files";
import { useRef } from "react";
import { RefObject, useRef } from "react";
import axiosNonInterceptor from "axios";
import uuid from "uuid";
import { useUploader } from "../../hooks/files";
interface AddNewDropdownProps {
closeDropdown: () => void;
@@ -15,7 +18,8 @@ const AddNewDropdown: React.FC<AddNewDropdownProps> = (props) => {
const params = useParams();
const { invalidateFoldersCache } = useFoldersClient();
const { wrapperRef } = useClickOutOfBounds(props.closeDropdown);
const uploadRef = useRef("");
const uploadRef: RefObject<HTMLInputElement> = useRef(null);
const { uploadFiles } = useUploader();
const dispatch = useAppDispatch();
const createFolder = async () => {
@@ -35,10 +39,10 @@ const AddNewDropdown: React.FC<AddNewDropdownProps> = (props) => {
props.closeDropdown();
console.log("handle upload");
dispatch(startAddFile(uploadRef.current, params.id));
if (uploadRef && uploadRef.current) {
uploadRef.current = "";
}
const files = uploadRef.current?.files;
if (!files) return;
uploadFiles(files);
};
return (
@@ -1,33 +0,0 @@
import Spinner from "../SpinnerLogin";
import React from "react";
const DownloadPage = ({ download, state }) => {
if (state.size === "" && !state.error) {
return (
<div className="downloadpage__box">
<Spinner />
</div>
);
}
return (
<div className="downloadpage">
{!state.error ? (
<div className="downloadpage__box">
<p className="downloadpage__box__title">{state.title}</p>
<p className="downloadpage__box__subtitle">Type: {state.type}</p>
<p className="downloadpage__box__subtitle">Size: {state.size}</p>
<button className="button popup-window__button" onClick={download}>
Download
</button>
</div>
) : (
<div className="downloadpage__box">
<p>Unauthorized/Not Found Download</p>
</div>
)}
</div>
);
};
export default DownloadPage;
-85
View File
@@ -1,85 +0,0 @@
import DownloadPage from "./DownloadPage";
import capitalize from "../../utils/capitalize";
import axios from "../../axiosInterceptor";
import bytes from "bytes";
import React from "react";
class DownloadPageContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
error: false,
title: "",
type: "",
size: "",
}
this.isPersonalFile = false;
}
getFileExtension = (filename) => {
const filenameSplit = filename.split(".");
if (filenameSplit.length > 1) {
const extension = filenameSplit[filenameSplit.length - 1]
return extension.toUpperCase();
} else {
return "Unknown"
}
}
componentDidMount = () => {
const _id = this.props.match.params.id;
const tempToken = this.props.match.params.tempToken
axios.get(`/file-service/public/info/${_id}/${tempToken}`).then((results) => {
const data = results.data;
const title = capitalize(data.filename);
const size = bytes(data.length);
const type = this.getFileExtension(title);
this.isPersonalFile = results.data.metadata.personalFile;
this.setState(() => ({
...this.state,
title,
type,
size
}))
}).catch((err) => {
console.log(err)
this.setState(() => ({...this.state, error: true}))
})
}
download = () => {
const _id = this.props.match.params.id;
const tempToken = this.props.match.params.tempToken
const finalUrl = !this.isPersonalFile ? `/file-service/public/download/${_id}/${tempToken}` : `/file-service-personal/public/download/${_id}/${tempToken}`;
const link = document.createElement('a');
document.body.appendChild(link);
link.href = finalUrl;
link.setAttribute('type', 'hidden');
link.click();
}
render() {
return <DownloadPage state={this.state} download={this.download} {...this.props}/>
}
}
export default DownloadPageContainer;
+129
View File
@@ -0,0 +1,129 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { useParams } from "react-router-dom";
import axios from "../../axiosInterceptor";
import {
downloadPublicFileAPI,
getPublicFileInfoAPI,
} from "../../api/filesAPI";
import { toast, ToastContainer } from "react-toastify";
import SpinnerPage from "../SpinnerPage";
import moment from "moment";
import { getFileColor, getFileExtension } from "../../utils/files";
import { FileInterface } from "../../types/file";
import bytes from "bytes";
const PublicDownloadPage = () => {
const [file, setFile] = useState<FileInterface | null>(null);
const params = useParams();
const getFile = useCallback(async () => {
try {
const id = params.id!;
const tempToken = params.tempToken!;
const fileResponse = await getPublicFileInfoAPI(id, tempToken);
setFile(fileResponse);
} catch (e) {
console.log("Error getting publicfile info", e);
toast.error("Error getting public file");
}
}, [params.id, params.tempToken, getPublicFileInfoAPI]);
const downloadItem = useCallback(() => {
const id = params.id!;
const tempToken = params.tempToken!;
downloadPublicFileAPI(id, tempToken);
}, [params.id, params.tempToken, downloadPublicFileAPI]);
useEffect(() => {
getFile();
}, [getFile]);
if (!file) {
return (
<div className="w-screen h-screen flex justify-center items-center">
<SpinnerPage />
<ToastContainer position="bottom-right" />
</div>
);
}
const fileExtension = getFileExtension(file.filename, 3);
const imageColor = getFileColor(file.filename);
const formattedDate = moment(file.uploadDate).format("L");
const fileSize = bytes(file.metadata.size);
return (
<div>
<div className="flex justify-center items-center w-screen h-screen bg-black bg-opacity-90">
<div
className="absolute top-[20px] flex justify-between w-full"
id="actions-wrapper"
>
<div className="ml-4 flex items-center">
<span className="inline-flex items-center mr-[15px] max-w-[27px] min-w-[27px] min-h-[27px] max-h-[27px]">
<div
className="h-[27px] w-[27px] bg-red-500 rounded-[3px] flex flex-row justify-center items-center"
style={{ background: imageColor }}
>
<span className="font-semibold text-[9.5px] text-white">
{fileExtension}
</span>
</div>
</span>
<p className="text-md text-white text-ellipsis overflow-hidden max-w-[200px] md:max-w-[600px] whitespace-nowrap">
{file.filename}
</p>
</div>
</div>
<div className="w-[300px] p-4 bg-white rounded-md border shadow-lg">
<div className="mt-2 flex justify-between">
<span className="text-[#637381] text-[13px] font-normal leading-[20px] min-w-[50px]">
Type
</span>
<span className="text-[#212b36] text-[13px] font-normal leading-[20px]">
{fileExtension}
</span>
</div>
<div className="mt-2 flex justify-between">
<span className="text-[#637381] text-[13px] font-normal leading-[20px] min-w-[50px]">
Size
</span>
<span className="text-[#212b36] text-[13px] font-normal leading-[20px]">
{fileSize}
</span>
</div>
<div className="mt-2 flex justify-between">
<span className="text-[#637381] text-[13px] font-normal leading-[20px] min-w-[50px]">
Created
</span>
<span className="text-[#212b36] text-[13px] font-normal leading-[20px]">
{formattedDate}
</span>
</div>
<div className="mt-2 flex justify-between">
<span className="text-[#637381] text-[13px] font-normal leading-[20px] min-w-[50px]">
Access
</span>
<span className="text-[#212b36] text-[13px] font-normal leading-[20px]">
{file.metadata.link ? "Public" : "Private"}
</span>
</div>
<div className="mt-[15px] flex justify-center">
<a
onClick={downloadItem}
className="w-[80px] h-[40px] inline-flex items-center justify-center border border-[#3c85ee] rounded-[4px] text-[#3c85ee] text-[15px] font-medium no-underline animate mr-4 cursor-pointer hover:bg-[#f6f5fd]"
>
Download
</a>
</div>
</div>
</div>
<ToastContainer position="bottom-right" />
</div>
);
};
export default PublicDownloadPage;
+5 -1
View File
@@ -9,6 +9,10 @@ import { useAppSelector } from "../../hooks/store";
import { ToastContainer } from "react-toastify";
const Homepage = () => {
const showUploader = useAppSelector(
(state) => state.uploader.uploads.length !== 0
);
return (
<div>
<HomepageSpinner />
@@ -17,7 +21,7 @@ const Homepage = () => {
<Header />
<div className="flex space-between">
<MainSection />
<Uploader />
{showUploader && <Uploader />}
</div>
</div>
+4 -1
View File
@@ -298,7 +298,10 @@ const PhotoViewerPopup = memo(() => {
className="max-w-[80vw] max-h-[80vh] flex justify-center items-center"
>
{!file.metadata.isVideo && (
<img src={image} className="max-w-full max-h-full object-contain" />
<img
src={image}
className="max-w-full max-h-full object-contain select-none"
/>
)}
{file.metadata.isVideo && (
<video
+1 -1
View File
@@ -151,7 +151,7 @@ const SharePopup = memo(() => {
useEffect(() => {
if (!file.metadata.link) return;
const url = `${window.location.origin}/download-page/${file._id}/${shareLink}`;
const url = `${window.location.origin}/public-download/${file._id}/${file.metadata.link}`;
console.log("url", url);
setShareLink(url);
}, [file.metadata.link]);
+44 -92
View File
@@ -1,18 +1,18 @@
import { connect } from "react-redux";
import React, { useEffect, useRef } from "react";
import bytes from "bytes";
import { useFilesClient, useQuickFilesClient } from "../../hooks/files";
import { getCancelToken } from "../../utils/cancelTokenManager";
import CloseIcon from "../../icons/CloseIcon";
import CheckCircleIcon from "../../icons/CheckCircleIcon";
import AlertIcon from "../../icons/AlertIcon";
const UploadItem = (props) => {
const filesRefreshed = useRef(false);
const { invalidateFilesCache } = useFilesClient();
const { invalidateQuickFilesCache } = useQuickFilesClient();
const completed = props.completed;
const uploadImage = props.getUploadImage();
const canceled = props.canceled;
const { completed, canceled, progress, name, id } = props;
const cancelToken = getCancelToken(id);
// TODO: Add ability to cancel indivdual uploads
useEffect(() => {
if (completed && !filesRefreshed.current) {
invalidateFilesCache();
@@ -21,97 +21,49 @@ const UploadItem = (props) => {
}
}, [completed]);
if (!completed && !canceled) {
return (
<div className="elem__upload uploading__now">
<div className="upload__elem--status">
<span>
<img src="/assets/upload_now.svg" alt="upload" />
</span>
</div>
<div className="upload__info">
<div className="top__upload">
<div className="upload__text">
<p>{props.name}</p>
</div>
<div className="upload__size">
<div className="stop__download">
<span>
<img
onClick={props.cancelUploadEvent}
src="/assets/cancel.svg"
alt="cancel"
/>
</span>
</div>
<span>{bytes(props.size)}</span>
</div>
const cancelUpload = () => {
cancelToken.cancel();
};
const ProgressIcon = () => {
if (completed) {
return <CheckCircleIcon className="w-[20px] h-[20px] text-green-600" />;
} else if (canceled) {
return <AlertIcon className="w-[20px] h-[20px] text-red-600" />;
} else {
return (
<CloseIcon
className="w-[20px] h-[20px] cursor-pointer"
onClick={cancelUpload}
/>
);
}
};
return (
<div className="relative p-[20px] flex justify-between items-start hover:bg-[#f6f5fd]">
<div className="w-full">
<div className="flex justify-between items-center mb-[15px]">
<div className="mr-[30px]">
<p className="text-[15px] leading-[18px] font-medium max-w-[160px] overflow-hidden whitespace-nowrap text-ellipsis">
{name}
</p>
</div>
<div className="bottom__upload">
<div className="progress__upload">
<div
className="active__progress"
style={{ width: `${props.progress}%` }}
></div>
</div>
<div>
<ProgressIcon />
</div>
</div>
<div>
<div className="w-full bg-[#e0dcf3] rounded-[1.5px] h-[3px] relative">
<div
className="h-[3px] bg-[#3c85ee] rounded-[1.5px]"
style={{ width: `${progress}%` }}
></div>
</div>
</div>
</div>
);
} else if (completed) {
return (
<div className="elem__upload uploaded__already">
<div className="upload__elem--status">
<span>
<img src="/assets/uploaded__success.svg" alt="upload" />
</span>
</div>
<div className="upload__info">
<div className="top__upload">
<div className="upload__text">
<p>{props.name}</p>
</div>
<div className="upload__size">
<span>{bytes(props.size)}</span>
</div>
</div>
<div className="bottom__upload">
<div className="progress__upload">
<div
className="active__progress"
style={{ width: `${props.progress}%` }}
></div>
</div>
</div>
</div>
</div>
);
} else {
return (
<div className="elem__upload uploaded__cancelled">
<div className="upload__elem--status">
<span>
<img src="/assets/uploaded__failed.svg" alt="upload" />
</span>
</div>
<div className="upload__info">
<div className="top__upload">
<div className="upload__text">
<p>{props.name}</p>
</div>
<div className="retry__download">
<a href="#">Retry</a>
</div>
</div>
<div className="bottom__upload">
<div className="failed__info">
<span>Upload failed</span>
</div>
</div>
</div>
</div>
);
}
</div>
);
};
export default connect()(UploadItem);
+31 -32
View File
@@ -1,39 +1,38 @@
import { useAppSelector } from "../../hooks/store";
import UploadItem from "../UploadItem";
import React from "react";
const Uploader = React.forwardRef((props, ref) => (
<div
className="upload__status"
style={
props.uploads.length !== 0 ? { display: "block" } : { display: "none" }
}
>
<div className="head__upload">
<p>
Uploading {props.uploads.length}{" "}
{props.uploads.length === 1 ? "file" : "files"}
</p>
<div className="hide__upload">
<a onClick={() => props.minimizeUploader()}>
<img src="/assets/upload_hide.svg" alt="upload__hide" />
</a>
<a onClick={() => props.cancelAllUploadsEvent()}>
<img
src="/assets/close-white.svg"
style={{ height: "24px" }}
alt="upload__hide"
/>
</a>
const Uploader = React.forwardRef((props, ref) => {
const uploads = useAppSelector((state) => state.uploader.uploads);
return (
<div className="upload__status">
<div className="head__upload">
<p>
Uploading {uploads.length} {uploads.length === 1 ? "file" : "files"}
</p>
<div className="hide__upload">
<a onClick={() => props.minimizeUploader()}>
<img src="/assets/upload_hide.svg" alt="upload__hide" />
</a>
<a onClick={() => props.cancelAllUploadsEvent()}>
<img
src="/assets/close-white.svg"
style={{ height: "24px" }}
alt="upload__hide"
/>
</a>
</div>
</div>
<div className="content__upload">
{props.uploaderShow
? uploads.map((upload) => {
return <UploadItem key={upload.id} {...upload} />;
})
: undefined}
</div>
</div>
<div className="content__upload">
{props.uploaderShow
? props.uploads.map((upload) => {
return <UploadItem key={upload.id} {...upload} />;
})
: undefined}
</div>
</div>
));
);
});
export default Uploader;