added upload changes
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user