added restore logic and fixed delete and added multi delete

This commit is contained in:
subnub
2024-06-30 13:17:04 -04:00
parent e3970ad394
commit 9f1ab10f40
25 changed files with 633 additions and 278 deletions
+65 -2
View File
@@ -509,7 +509,7 @@ class FileController {
}
};
deleteFile = async (req: RequestType, res: Response) => {
restoreFile = async (req: RequestType, res: Response) => {
if (!req.user) {
return;
}
@@ -518,7 +518,26 @@ class FileController {
const userID = req.user._id;
const fileID = req.body.id;
console.log("id", fileID);
const file = await fileService.restoreFile(userID, fileID);
res.send(file);
} catch (e: unknown) {
if (e instanceof Error) {
console.log("\nRestore File Error File Route:", e.message);
}
res.status(500).send("Server error restoring file");
}
};
deleteFile = async (req: RequestType, res: Response) => {
if (!req.user) {
return;
}
try {
const userID = req.user._id;
const fileID = req.body.id;
await this.chunkService.deleteFile(userID, fileID);
@@ -532,6 +551,29 @@ class FileController {
}
};
deleteMulti = async (req: RequestType, res: Response) => {
if (!req.user) {
return;
}
try {
const userID = req.user._id;
const items = req.body.items;
console.log("items", req.body);
await this.chunkService.deleteMulti(userID, items);
res.send();
} catch (e: unknown) {
if (e instanceof Error) {
console.log("Delete Multi Error File Route:", e.message);
}
res.status(500).send("Server error deleting multi");
}
};
trashMulti = async (req: RequestType, res: Response) => {
if (!req.user) {
return;
@@ -552,6 +594,27 @@ class FileController {
res.status(500).send("Server error trashing multi");
}
};
restoreMulti = async (req: RequestType, res: Response) => {
if (!req.user) {
return;
}
try {
const userID = req.user._id;
const items = req.body.items;
await this.chunkService.restoreMulti(userID, items);
res.send();
} catch (e: unknown) {
if (e instanceof Error) {
console.log("\nRestore Multi Error File Route:", e.message);
}
res.status(500).send("Server error restoring multi");
}
};
}
export default FileController;
+21
View File
@@ -241,6 +241,27 @@ class FolderController {
}
};
restoreFolder = async (req: RequestType, res: Response) => {
if (!req.user) {
return;
}
try {
const userID = req.user._id;
const folderID = req.body.id;
await folderService.restoreFolder(userID, folderID);
res.send();
} catch (e: unknown) {
if (e instanceof Error) {
console.log("Restore Folder Error Folder Route:", e.message);
}
res.status(500).send("Server error restore folder");
}
};
renameFolder = async (req: RequestType, res: Response) => {
if (!req.user) {
return;
+27
View File
@@ -87,6 +87,18 @@ class DbUtil {
return result;
};
restoreFile = async (fileID: string, userID: string) => {
const result = await File.updateOne(
{ _id: new ObjectId(fileID), "metadata.owner": userID },
{
$set: {
"metadata.trashed": null,
},
}
);
return result;
};
getFileInfo = async (fileID: string, userID: string) => {
//TODO: Using mongoose like this causes the object to be returned in raw form
// const file = await File.findOne({
@@ -168,6 +180,21 @@ class DbUtil {
return result;
};
restoreFilesByParent = async (parentList: string, userID: string) => {
const result = await File.updateMany(
{
"metadata.owner": userID,
"metadata.parentList": { $regex: `.*${parentList}.*` }, // REGEX
},
{
$set: {
"metadata.trashed": null,
},
}
);
return result;
};
renameFile = async (fileID: string, userID: string, title: string) => {
const updateFileResponse = await File.findOneAndUpdate(
{ _id: new ObjectId(fileID), "metadata.owner": userID },
+15
View File
@@ -173,6 +173,21 @@ class DbUtil {
);
return result;
};
restoreFoldersByParent = async (parentList: string[], userID: string) => {
const result = await Folder.updateMany(
{
owner: userID,
parentList: { $all: parentList },
},
{
$set: {
trashed: null,
},
}
);
return result;
};
}
export default DbUtil;
+6
View File
@@ -103,6 +103,10 @@ router.patch("/file-service/trash", auth, fileController.trashFile);
router.patch("/file-service/trash-multi", auth, fileController.trashMulti);
router.patch("/file-service/restore", auth, fileController.restoreFile);
router.patch("/file-service/restore-multi", auth, fileController.restoreMulti);
router.delete("/file-service/remove-link/:id", auth, fileController.removeLink);
router.delete(
@@ -113,6 +117,8 @@ router.delete(
router.delete("/file-service/remove", auth, fileController.deleteFile);
router.delete("/file-service/remove-multi", auth, fileController.deleteMulti);
router.post(
"/file-service/send-share-email",
auth,
+2
View File
@@ -39,6 +39,8 @@ router.patch("/folder-service/move", auth, folderController.moveFolder);
router.patch("/folder-service/trash", auth, folderController.trashFolder);
router.patch("/folder-service/restore", auth, folderController.restoreFolder);
router.get(
"/folder-service/subfolder-list-full",
auth,
+1 -1
View File
@@ -39,7 +39,7 @@ export interface FolderInterface
parentList: string[];
_doc?: any;
personalFolder?: boolean;
trashed?: boolean;
trashed: boolean | null;
}
const Folder = mongoose.model<FolderInterface>("Folder", folderSchema);
@@ -504,6 +504,60 @@ class FileSystemService implements ChunkInterface {
}
};
restoreMulti = async (
userID: string,
items: {
type: "file" | "folder" | "quick-item";
id: string;
file?: FileInterface;
folder?: FolderInterface;
}[]
) => {
const fileList = items.filter(
(item) => item.type === "file" || item.type === "quick-item"
);
const folderList = items
.filter((item) => item.type === "folder")
.sort((a, b) => {
if (!a.folder || !b.folder) return 0;
return b.folder.parentList.length - a.folder.parentList.length;
});
for (const file of fileList) {
await fileService.restoreFile(userID, file.id);
}
for (const folder of folderList) {
await folderService.restoreFolder(userID, folder.id);
}
};
deleteMulti = async (
userID: string,
items: {
type: "file" | "folder" | "quick-item";
id: string;
file?: FileInterface;
folder?: FolderInterface;
}[]
) => {
const fileList = items.filter(
(item) => item.type === "file" || item.type === "quick-item"
);
const folderList = items
.filter((item) => item.type === "folder")
.sort((a, b) => {
if (!a.folder || !b.folder) return 0;
return b.folder.parentList.length - a.folder.parentList.length;
});
for (const file of fileList) {
await this.deleteFile(userID, file.id);
}
for (const folder of folderList) {
await this.deleteFolder(userID, folder.id);
}
};
deleteFile = async (userID: string, fileID: string) => {
const file = await dbUtilsFile.getFileInfo(fileID, userID);
@@ -614,6 +614,26 @@ class S3Service implements ChunkInterface {
folder?: FolderInterface;
}[]
) => {};
restoreMulti = async (
userID: string,
items: {
type: "file" | "folder" | "quick-item";
id: string;
file?: FileInterface;
folder?: FolderInterface;
}[]
) => {};
deleteMulti = async (
userID: string,
items: {
type: "file" | "folder" | "quick-item";
id: string;
file?: FileInterface;
folder?: FolderInterface;
}[]
) => {};
}
export default S3Service;
@@ -28,9 +28,9 @@ const tempCreateVideoThumbnailFS = (
const writeStream = fs.createWriteStream(
env.fsDirectory + thumbnailFilename
);
const tempWriteStream = fs.createWriteStream(
env.fsDirectory + "temp/" + thumbnailFilename
);
const tempDirectory = env.fsDirectory + "temp/" + thumbnailFilename;
const tempWriteStream = fs.createWriteStream(tempDirectory);
const decipher = crypto.createDecipheriv(
"aes256",
CIPHER_KEY,
@@ -49,7 +49,7 @@ const tempCreateVideoThumbnailFS = (
decryptedReadStream.pipe(tempWriteStream, { end: true });
ffmpeg(env.fsDirectory + "temp/" + thumbnailFilename, {
ffmpeg(tempDirectory, {
timeout: 60,
})
.seek(1)
@@ -97,7 +97,9 @@ const tempCreateVideoThumbnailFS = (
if (!updatedFile) return reject();
resolve(updatedFile?.toObject());
fs.unlink(tempDirectory, (err) => {
resolve(updatedFile?.toObject());
});
})
.on("error", (err, _, stderr) => {
console.log("error", err, stderr);
+6
View File
@@ -236,6 +236,12 @@ class MongoFileService {
return trashedFile;
};
restoreFile = async (userID: string, fileID: string) => {
const restoredFile = await dbUtilsFile.restoreFile(fileID, userID);
if (!restoredFile) throw new NotFoundError("Restore File Not Found Error");
return restoredFile;
};
renameFile = async (userID: string, fileID: string, title: string) => {
const file = await dbUtilsFile.renameFile(fileID, userID, title);
+15
View File
@@ -225,6 +225,21 @@ class FolderService {
await utilsFile.trashFilesByParent(parentList.toString(), userID);
};
restoreFolder = async (userID: string, folderID: string) => {
const folder = await utilsFolder.getFolderInfo(folderID, userID);
if (!folder) throw new NotFoundError("Restore Folder Not Found Error");
folder.trashed = null;
await folder.save();
const parentList = [...folder.parentList, folder._id!.toString()];
await utilsFolder.restoreFoldersByParent(parentList, userID);
await utilsFile.restoreFilesByParent(parentList.toString(), userID);
};
moveFolder = async (userID: string, folderID: string, parentID: string) => {
let parentList = ["/"];
+23
View File
@@ -113,6 +113,20 @@ export const trashMultiAPI = async (items: any) => {
return response.data;
};
export const restoreFileAPI = async (fileID: string) => {
const response = await axios.patch(`/file-service/restore`, {
id: fileID,
});
return response.data;
};
export const restoreMultiAPI = async (items: any) => {
const response = await axios.patch(`/file-service/restore-multi`, {
items,
});
return response.data;
};
export const renameFileAPI = async (fileID: string, name: string) => {
const response = await axios.patch(`/file-service/rename`, {
id: fileID,
@@ -130,3 +144,12 @@ export const deleteFileAPI = async (fileID: string) => {
});
return response.data;
};
export const deleteMultiAPI = async (items: any) => {
const response = await axios.delete(`/file-service/remove-multi`, {
data: {
items,
},
});
return response.data;
};
+9 -2
View File
@@ -56,8 +56,15 @@ export const renameFolder = async (folderID: string, name: string) => {
return response.data;
};
export const trashFolderAPI = (folderID: string) => {
const response = axios.patch("/folder-service/trash", {
export const trashFolderAPI = async (folderID: string) => {
const response = await axios.patch("/folder-service/trash", {
id: folderID,
});
return response.data;
};
export const restoreFolderAPI = async (folderID: string) => {
const response = await axios.patch("/folder-service/restore", {
id: folderID,
});
return response.data;
+17 -1
View File
@@ -6,6 +6,7 @@ import {
renameFileAPI,
downloadFileAPI,
trashFileAPI,
restoreFileAPI,
} from "../../api/filesAPI";
import { useFilesClient, useQuickFilesClient } from "../../hooks/files";
import { useFoldersClient } from "../../hooks/folders";
@@ -15,6 +16,7 @@ import { setShareSelected } from "../../actions/selectedItem";
import {
deleteFolder,
renameFolder,
restoreFolderAPI,
trashFolderAPI,
} from "../../api/foldersAPI";
import { useClickOutOfBounds, useUtils } from "../../hooks/utils";
@@ -27,6 +29,7 @@ import ShareIcon from "../../icons/ShareIcon";
import DownloadIcon from "../../icons/DownloadIcon";
import MoveIcon from "../../icons/MoveIcon";
import RestoreIcon from "../../icons/RestoreIcon";
import { restoreItemPopup } from "../../popups/file";
const ContextMenu = (props) => {
const { invalidateFilesCache } = useFilesClient();
@@ -148,6 +151,19 @@ const ContextMenu = (props) => {
}
};
const restoreItem = async () => {
props.closeContext();
const result = await restoreItemPopup();
if (!result) return;
if (!props.folderMode) {
await restoreFileAPI(props.file._id);
invalidateFilesCache();
} else {
await restoreFolderAPI(props.folder._id);
invalidateFoldersCache();
}
};
const openMoveItemModal = async () => {
props.closeContext();
if (!props.folderMode) {
@@ -268,7 +284,7 @@ const ContextMenu = (props) => {
</li>
)}
{isTrash && (
<li onClick={deleteItem} className={liClassname}>
<li onClick={restoreItem} className={liClassname}>
<a className="flex">
<span className={spanClassname}>
<RestoreIcon className="w-[19px] h-[20px]" />
+13 -5
View File
@@ -13,6 +13,7 @@ import { setPopupFile } from "../../actions/popupFile";
import bytes from "bytes";
import { useAppDispatch, useAppSelector } from "../../hooks/store";
import { setMainSelect, setMultiSelectMode } from "../../reducers/selected";
import PlayButtonIcon from "../../icons/PlayIcon";
const FileItem = React.memo((props) => {
const { file } = props;
@@ -215,11 +216,18 @@ const FileItem = React.memo((props) => {
)}
>
{hasThumbnail ? (
<img
className="w-full min-h-[88px] max-h-[88px] h-full flex object-cover"
src={image}
onError={imageOnError}
/>
<div className="w-full min-h-[88px] max-h-[88px] h-full flex">
<img
className=" object-cover"
src={image}
onError={imageOnError}
/>
{file.metadata.isVideo && (
<div className="w-full h-full absolute flex justify-center items-center text-white">
<PlayButtonIcon className="w-[50px] h-[50px]" />
</div>
)}
</div>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
+57 -52
View File
@@ -1,10 +1,19 @@
import { useMemo } from "react";
import React, { useMemo } from "react";
import { useAppDispatch, useAppSelector } from "../../hooks/store";
import { resetMultiSelect } from "../../reducers/selected";
import Swal from "sweetalert2";
import { trashMultiAPI } from "../../api/filesAPI";
import {
deleteMultiAPI,
restoreMultiAPI,
trashMultiAPI,
} from "../../api/filesAPI";
import { useFilesClient, useQuickFilesClient } from "../../hooks/files";
import { useFoldersClient } from "../../hooks/folders";
import TrashIcon from "../../icons/TrashIcon";
import Moveicon from "../../icons/MoveIcon";
import { deleteItemsPopup, restoreItemsPopup } from "../../popups/file";
import RestoreIcon from "../../icons/RestoreIcon";
import { useUtils } from "../../hooks/utils";
const MultiSelectBar = () => {
const dispatch = useAppDispatch();
@@ -21,11 +30,13 @@ const MultiSelectBar = () => {
const { invalidateFoldersCache } = useFoldersClient();
const { invalidateQuickFilesCache } = useQuickFilesClient();
const { isTrash } = useUtils();
const closeMultiSelect = () => {
dispatch(resetMultiSelect());
};
const deleteItems = async () => {
const trashItems = async () => {
const result = await Swal.fire({
title: "Move to trash?",
text: "Items in the trash will eventually be deleted.",
@@ -36,7 +47,7 @@ const MultiSelectBar = () => {
confirmButtonText: "Yes",
});
if (result.value) {
if (result) {
const itemsToTrash = Object.values(multiSelectMap);
await trashMultiAPI(itemsToTrash);
invalidateFilesCache();
@@ -46,6 +57,31 @@ const MultiSelectBar = () => {
}
};
const deleteItems = async () => {
const result = await deleteItemsPopup();
if (result) {
const itemsToTrash = Object.values(multiSelectMap);
await deleteMultiAPI(itemsToTrash);
invalidateFilesCache();
invalidateFoldersCache();
invalidateQuickFilesCache();
closeMultiSelect();
}
};
const restoreItems = async () => {
const result = await restoreItemsPopup();
if (result) {
const itemsToTrash = Object.values(multiSelectMap);
await restoreMultiAPI(itemsToTrash);
invalidateFilesCache();
invalidateFoldersCache();
invalidateQuickFilesCache();
closeMultiSelect();
}
};
if (!multiSelectMode) return <div></div>;
return (
@@ -62,56 +98,25 @@ const MultiSelectBar = () => {
</div>
<div className="flex flex-row items-center">
<svg
width="17"
height="18"
viewBox="0 0 17 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="ml-4 cursor-pointer"
onClick={deleteItems}
>
<g id="trash">
<path
id="Shape"
fill-rule="evenodd"
clip-rule="evenodd"
d="M16.0694 2.57072H11.5707V1.92803C11.5707 0.863209 10.7075 0 9.64265 0H7.07192C6.0071 0 5.14389 0.863209 5.14389 1.92803V2.57072H0.645132C0.290178 2.57072 0.00244141 2.85846 0.00244141 3.21341C0.00244141 3.56837 0.290215 3.85607 0.645132 3.85607H1.34371L2.57317 17.4108C2.60348 17.7427 2.88255 17.9963 3.21586 17.995H13.4987C13.832 17.9964 14.1111 17.7427 14.1414 17.4108L15.3708 3.85607H16.0694C16.4244 3.85607 16.7121 3.56833 16.7121 3.21338C16.7121 2.85842 16.4244 2.57072 16.0694 2.57072ZM6.42923 1.92803C6.42923 1.57308 6.71697 1.28534 7.07192 1.28534H9.64265C9.9976 1.28534 10.2853 1.57308 10.2853 1.92803V2.57072H6.42927V1.92803H6.42923ZM3.80263 16.7096H12.9119L14.0803 3.85607H5.78658H2.63745L3.80263 16.7096Z"
fill="currentColor"
{!isTrash && (
<TrashIcon className="ml-4 cursor-pointer" onClick={trashItems} />
)}
{isTrash && (
<React.Fragment>
<RestoreIcon
className="ml-4 cursor-pointer h-[20px] w-[20px]"
onClick={restoreItems}
/>
<path
id="Path"
d="M6.42938 14.7385C6.4293 14.7376 6.42926 14.7367 6.42919 14.7358L5.7865 5.73834C5.76131 5.38339 5.45312 5.1161 5.09821 5.14129C4.74325 5.16649 4.47596 5.47467 4.50116 5.82959L5.14385 14.8271C5.16783 15.1641 5.44868 15.425 5.78654 15.4241H5.83282C6.1869 15.3995 6.45401 15.0925 6.42938 14.7385Z"
fill="currentColor"
<TrashIcon
className="ml-4 cursor-pointer text-red-500"
onClick={deleteItems}
/>
<path
id="Path_2"
d="M8.35729 5.1416C8.00234 5.1416 7.7146 5.42934 7.7146 5.78429V14.7818C7.7146 15.1367 8.00234 15.4245 8.35729 15.4245C8.71224 15.4245 8.99998 15.1367 8.99998 14.7818V5.78429C8.99998 5.42934 8.71224 5.1416 8.35729 5.1416Z"
fill="currentColor"
/>
<path
id="Path_3"
d="M11.6164 5.14129C11.2615 5.1161 10.9533 5.38339 10.9281 5.73834L10.2854 14.7358C10.2594 15.0898 10.5253 15.3979 10.8793 15.4239C10.8804 15.424 10.8814 15.424 10.8825 15.4241H10.9281C11.266 15.425 11.5468 15.1641 11.5708 14.8271L12.2135 5.82959C12.2387 5.47467 11.9714 5.16652 11.6164 5.14129Z"
fill="currentColor"
/>
</g>
</svg>
<svg
width="19"
height="16"
viewBox="0 0 19 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="ml-4 cursor-pointer"
>
<path
id="Combined Shape"
fill-rule="evenodd"
clip-rule="evenodd"
d="M7.63107 0C7.83994 0 8.03661 0.0983374 8.16193 0.265429L9.95357 2.65429H17.9164C18.2829 2.65429 18.58 2.95138 18.58 3.31786V15.2621C18.58 15.6286 18.2829 15.9257 17.9164 15.9257H0.663571C0.297091 15.9257 0 15.6286 0 15.2621V0.663571C0 0.297091 0.297091 0 0.663571 0H7.63107ZM7.212 1.5H1.5V14.425H17.08V4.154L9.20357 4.15429L7.212 1.5ZM9.96369 9.51348C10.0439 9.43969 10.0892 9.33783 10.0892 9.23078C10.0892 9.12373 10.0439 9.02188 9.96369 8.9486L7.2712 6.4802C7.15381 6.37263 6.98149 6.34301 6.8334 6.40433C6.68532 6.46565 6.58893 6.60648 6.58893 6.76238V7.93162H4.30032C3.92929 7.93162 3.62719 8.22315 3.62719 8.5812V9.88036C3.62719 10.2384 3.92929 10.5299 4.30032 10.5299H6.58893V11.6992C6.58893 11.8551 6.68478 11.9964 6.8334 12.0577C6.98203 12.1191 7.15435 12.0889 7.2712 11.9819L9.96369 9.51348Z"
fill="currentColor"
/>
</svg>
</React.Fragment>
)}
{!isTrash && (
<Moveicon className="ml-4 cursor-pointer" onClick={() => {}} />
)}
</div>
</div>
</div>
+1 -1
View File
@@ -58,7 +58,7 @@ class PopupWindow extends React.Component {
</div>
) : (
<video
className="popup-window__video"
className="popup-window__video !max-h-[70vh]"
src={this.props.state.video}
ref={this.props.video}
type="video/mp4"
+202 -203
View File
@@ -1,244 +1,243 @@
import PopupWindow from "./PopupWindow";
import {hidePopup} from "../../actions/popupFile"
import { hidePopup } from "../../actions/popupFile";
import axios from "../../axiosInterceptor";
import env from "../../enviroment/envFrontEnd";
import {connect} from "react-redux";
import { connect } from "react-redux";
import React from "react";
import { setPhotoID } from "../../actions/photoViewer";
import axiosNonInterceptor from "axios";
class PopupWindowContainer extends React.Component {
constructor(props) {
super(props);
constructor(props) {
super(props);
this.wrapperRef = React.createRef();
this.video = React.createRef();
this.imageData = "";
this.tempToken = "";
this.wrapperRef = React.createRef();
this.video = React.createRef();
this.imageData = ""
this.tempToken = ""
this.state = {
image: "/images/cloud-svg.svg",
imageClassname: this.props.popupFile.metadata.hasThumbnail
? "popup-window__image popup-window popup-window--gone"
: "popup-window__image",
video: "",
spinnerClassname: "popup-window__spinner__wrapper",
};
}
this.state = {
image: "/images/cloud-svg.svg",
imageClassname: this.props.popupFile.metadata.hasThumbnail ? "popup-window__image popup-window popup-window--gone" : "popup-window__image",
video: "",
spinnerClassname: "popup-window__spinner__wrapper"
}
getFileExtension = (filename) => {
const filenameSplit = filename.split(".");
if (filenameSplit.length > 1) {
const extension = filenameSplit[filenameSplit.length - 1];
return extension.toUpperCase();
} else {
return "Unknown";
}
};
getFileExtension = (filename) => {
const filenameSplit = filename.split(".");
if (filenameSplit.length > 1) {
const extension = filenameSplit[filenameSplit.length - 1]
return extension.toUpperCase();
} else {
return "Unknown"
}
}
getThumbnail = () => {
if (this.getFileExtension(this.props.popupFile.filename).toLowerCase() === "svg") {
this.props.popupFile.metadata.hasThumbnail = false;
return this.setState(() => {
return {
...this.state,
image: "/images/cloud-svg.svg",
imageClassname: "popup-window__image"
}
})
}
const thumbnailID = this.props.popupFile.metadata.drive ? this.props.popupFile._id : this.props.popupFile.metadata.thumbnailID
const config = {
responseType: 'arraybuffer'
getThumbnail = () => {
if (
this.getFileExtension(this.props.popupFile.filename).toLowerCase() ===
"svg"
) {
this.props.popupFile.metadata.hasThumbnail = false;
return this.setState(() => {
return {
...this.state,
image: "/images/cloud-svg.svg",
imageClassname: "popup-window__image",
};
const isDrive = this.props.popupFile.metadata.drive;
const isPersonal = this.props.popupFile.metadata.personalFile;
const url = isDrive ? `/file-service-google/thumbnail/${thumbnailID}`
: !isPersonal ? `/file-service/thumbnail/${thumbnailID}` : `/file-service-personal/thumbnail/${thumbnailID}`;
axios.get(url, config).then((results) => {
const imgFile = new Blob([results.data]);
const imgUrl = URL.createObjectURL(imgFile);
this.setState(() => ({
...this.state,
image: imgUrl,
imageClassname: "popup-window__image popup-window__image--loaded",
spinnerClassname: "popup-window__spinner__wrapper popup-window--gone"
}))
}).catch((err) => {
console.log(err)
})
});
}
thumbnailOnError = () => {
const thumbnailID = this.props.popupFile.metadata.drive
? this.props.popupFile._id
: this.props.popupFile.metadata.thumbnailID;
const config = {
responseType: "arraybuffer",
};
const isDrive = this.props.popupFile.metadata.drive;
const isPersonal = this.props.popupFile.metadata.personalFile;
const url = isDrive
? `/file-service-google/thumbnail/${thumbnailID}`
: !isPersonal
? `/file-service/thumbnail/${thumbnailID}`
: `/file-service-personal/thumbnail/${thumbnailID}`;
axios
.get(url, config)
.then((results) => {
const imgFile = new Blob([results.data]);
const imgUrl = URL.createObjectURL(imgFile);
this.setState(() => ({
...this.state,
imageClassname: "popup-window__image",
spinnerClassname: "popup-window__spinner__wrapper popup-window--gone",
image: "/images/cloud-svg.svg"
}))
...this.state,
image: imgUrl,
imageClassname: "popup-window__image popup-window__image--loaded",
spinnerClassname: "popup-window__spinner__wrapper popup-window--gone",
}));
})
.catch((err) => {
console.log(err);
});
};
thumbnailOnError = () => {
this.setState(() => ({
...this.state,
imageClassname: "popup-window__image",
spinnerClassname: "popup-window__spinner__wrapper popup-window--gone",
image: "/images/cloud-svg.svg",
}));
};
handleClickOutside = (e) => {
if (this.wrapperRef && !this.wrapperRef.current.contains(event.target)) {
this.props.dispatch(hidePopup());
}
};
handleClickOutside = (e) => {
getVideo = () => {
// const config = {
// headers: {
// uuid: window.sessionStorage.getItem("uuid")
// }
if (this.wrapperRef && !this.wrapperRef.current.contains(event.target)) {
this.props.dispatch(hidePopup())
}
}
// };
getVideo = () => {
console.log("gettings stream video token");
// const config = {
// headers: {
// uuid: window.sessionStorage.getItem("uuid")
// }
// };
axios
.get("/file-service/download/access-token-stream-video")
.then(() => {
console.log("stream video got token");
console.log("gettings stream video token")
axios.get("/file-service/download/access-token-stream-video").then(() => {
// TODO: Fix this
const finalUrl = `http://localhost:5173/api/file-service/stream-video/${this.props.popupFile._id}`;
console.log("stream video got token")
this.setState(() => ({
...this.state,
video: finalUrl,
}));
})
.catch((e) => {
console.log("Stream Video Error", e.message);
});
const isDrive = this.props.popupFile.metadata.drive;
const isPersonal = this.props.popupFile.metadata.personalFile;
const finalUrl = isDrive ?
`/file-service-google/stream-video/${this.props.popupFile._id}`
: !isPersonal ? `/file-service/stream-video/${this.props.popupFile._id}`
: `/file-service-personal/stream-video/${this.props.popupFile._id}`
this.setState(() => ({
...this.state,
video: finalUrl
}))
// axios.get(currentURL +'/file-service/download/get-token-video',config)
// .then((response) => {
}).catch((e) => {
console.log("Stream Video Error", e.message);
// this.tempToken = response.data.tempToken;
// const uuidID = window.sessionStorage.getItem("uuid");
// const isDrive = this.props.popupFile.metadata.drive;
// const isPersonal = this.props.popupFile.metadata.personalFile;
// const finalUrl = isDrive ?
// currentURL + `/file-service-google/stream-video/${this.props.popupFile._id}/${this.tempToken}/${uuidID}`
// : !isPersonal ? currentURL + `/file-service/stream-video/${this.props.popupFile._id}/${this.tempToken}/${uuidID}`
// : currentURL + `/file-service-personal/stream-video/${this.props.popupFile._id}/${this.tempToken}/${uuidID}`
// this.setState(() => ({
// ...this.state,
// video: finalUrl
// }))
// }).catch((err) => {
// console.log(err)
// })
};
componentWillUnmount = () => {
document.removeEventListener("mousedown", this.handleClickOutside);
if (this.props.popupFile.metadata.isVideo) {
axios
.delete(`/file-service/remove-stream-video-token`)
.then(() => {
console.log("removed video access token");
})
.catch((err) => {
console.log(err);
});
// axios.get(currentURL +'/file-service/download/get-token-video',config)
// .then((response) => {
// this.tempToken = response.data.tempToken;
this.video.current.pause();
// const uuidID = window.sessionStorage.getItem("uuid");
// const isDrive = this.props.popupFile.metadata.drive;
// const isPersonal = this.props.popupFile.metadata.personalFile;
// const finalUrl = isDrive ?
// currentURL + `/file-service-google/stream-video/${this.props.popupFile._id}/${this.tempToken}/${uuidID}`
// : !isPersonal ? currentURL + `/file-service/stream-video/${this.props.popupFile._id}/${this.tempToken}/${uuidID}`
// : currentURL + `/file-service-personal/stream-video/${this.props.popupFile._id}/${this.tempToken}/${uuidID}`
// this.setState(() => ({
// ...this.state,
// video: finalUrl
// }))
// }).catch((err) => {
// console.log(err)
// })
this.setState(() => ({
...this.state,
video: "",
}));
}
};
componentWillUnmount = () => {
componentDidMount = () => {
document.addEventListener("mousedown", this.handleClickOutside);
document.removeEventListener('mousedown', this.handleClickOutside);
if (this.props.popupFile.metadata.isVideo) {
axios.delete(`/file-service/remove-stream-video-token`).then(() => {
console.log("removed video access token");
}).catch((err) => {
console.log(err);
})
this.video.current.pause();
this.setState(() => ({
...this.state,
video: ""
}))
}
if (
this.props.popupFile.metadata.hasThumbnail &&
!this.props.popupFile.metadata.isVideo &&
!this.props.popupFile.metadata.drive
) {
this.getThumbnail();
} else if (
this.props.popupFile.metadata.drive &&
this.props.popupFile.metadata.hasThumbnail &&
!this.props.popupFile.metadata.googleDoc &&
!this.props.popupFile.metadata.isVideo
) {
this.getThumbnail();
} else if (
this.props.popupFile.metadata.drive &&
this.props.popupFile.metadata.hasThumbnail &&
!this.props.popupFile.metadata.isVideo
) {
this.setState(() => ({
...this.state,
imageClassname: "popup-window__image",
spinnerClassname: "popup-window__spinner__wrapper popup-window--gone",
image: "/images/cloud-svg.svg",
}));
} else if (this.props.popupFile.metadata.isVideo) {
this.getVideo();
}
};
componentDidMount = () => {
hidePopupWindow = () => {
this.props.dispatch(hidePopup());
};
document.addEventListener('mousedown', this.handleClickOutside);
if (this.props.popupFile.metadata.hasThumbnail && !this.props.popupFile.metadata.isVideo && !this.props.popupFile.metadata.drive) {
this.getThumbnail()
} else if (this.props.popupFile.metadata.drive && this.props.popupFile.metadata.hasThumbnail && !this.props.popupFile.metadata.googleDoc && !this.props.popupFile.metadata.isVideo) {
this.getThumbnail();
} else if (this.props.popupFile.metadata.drive && this.props.popupFile.metadata.hasThumbnail && !this.props.popupFile.metadata.isVideo) {
this.setState(() => ({
...this.state,
imageClassname: "popup-window__image",
spinnerClassname: "popup-window__spinner__wrapper popup-window--gone",
image: "/images/cloud-svg.svg"
}))
}else if (this.props.popupFile.metadata.isVideo) {
this.getVideo();
}
}
hidePopupWindow = () => {
this.props.dispatch(hidePopup());
}
setPhotoViewerWindow = () => {
const isGoogle = this.props.popupFile.metadata.drive
const isPersonal = this.props.popupFile.metadata.personalFile;
this.props.dispatch(setPhotoID(this.props.popupFile._id, isGoogle, isPersonal))
}
render() {
return <PopupWindow
wrapperRef={this.wrapperRef}
video={this.video}
hidePopupWindow={this.hidePopupWindow}
state={this.state}
setPhotoViewerWindow={this.setPhotoViewerWindow}
thumbnailOnError={this.thumbnailOnError}
{...this.props}
/>
}
setPhotoViewerWindow = () => {
const isGoogle = this.props.popupFile.metadata.drive;
const isPersonal = this.props.popupFile.metadata.personalFile;
this.props.dispatch(
setPhotoID(this.props.popupFile._id, isGoogle, isPersonal)
);
};
render() {
return (
<PopupWindow
wrapperRef={this.wrapperRef}
video={this.video}
hidePopupWindow={this.hidePopupWindow}
state={this.state}
setPhotoViewerWindow={this.setPhotoViewerWindow}
thumbnailOnError={this.thumbnailOnError}
{...this.props}
/>
);
}
}
const connectPropToState = (state) => ({
popupFile: state.popupFile,
})
popupFile: state.popupFile,
});
export default connect(connectPropToState)(PopupWindowContainer)
export default connect(connectPropToState)(PopupWindowContainer);
+9 -5
View File
@@ -13,6 +13,7 @@ import { setPopupFile } from "../../actions/popupFile";
import { FileInterface } from "../../types/file";
import { useAppDispatch, useAppSelector } from "../../hooks/store";
import { setMainSelect, setMultiSelectMode } from "../../reducers/selected";
import PlayButtonIcon from "../../icons/PlayIcon";
interface QuickAccessItemProps {
file: FileInterface;
@@ -127,11 +128,14 @@ const QuickAccessItem = memo((props: QuickAccessItemProps) => {
)}
>
{hasThumbnail ? (
<img
className="w-full min-h-[88px] max-h-[88px] h-full flex object-cover"
src={image}
onError={imageOnError}
/>
<div className="w-full min-h-[88px] max-h-[88px] h-full flex">
<img className=" object-cover" src={image} onError={imageOnError} />
{file.metadata.isVideo && (
<div className="w-full h-full absolute flex justify-center items-center text-white">
<PlayButtonIcon className="w-[50px] h-[50px]" />
</div>
)}
</div>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
+2
View File
@@ -1,5 +1,6 @@
interface MoveiconProps {
className?: string;
onClick?: () => void;
}
const Moveicon = (props: MoveiconProps) => {
@@ -11,6 +12,7 @@ const Moveicon = (props: MoveiconProps) => {
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={props.className}
onClick={props.onClick}
>
<path
id="Combined Shape"
+18
View File
@@ -0,0 +1,18 @@
interface PlayIconProps {
className?: string;
}
const PlayIcon = (props: PlayIconProps) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
className={props.className}
>
<title>play</title>
<path d="M8,5.14V19.14L19,12.14L8,5.14Z" fill="currentColor" />
</svg>
);
};
export default PlayIcon;
+3 -1
View File
@@ -1,13 +1,15 @@
interface RestoreIconProps {
className?: string;
onClick?: () => void;
}
const RestoreIcon = (props: any) => {
const RestoreIcon = (props: RestoreIconProps) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
className={props.className}
onClick={props.onClick}
>
<title>restore</title>
<path
+2
View File
@@ -1,5 +1,6 @@
interface TrashIconProps {
className?: string;
onClick?: () => void;
}
const TrashIcon = (props: TrashIconProps) => {
@@ -11,6 +12,7 @@ const TrashIcon = (props: TrashIconProps) => {
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={props.className}
onClick={props.onClick}
>
<g id="trash">
<path
+38
View File
@@ -0,0 +1,38 @@
import Swal from "sweetalert2";
export const restoreItemPopup = async () => {
const result = await Swal.fire({
title: "Restore item?",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yes",
});
return result.value;
};
export const restoreItemsPopup = async () => {
const result = await Swal.fire({
title: "Restore items?",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yes",
});
return result.value;
};
export const deleteItemsPopup = async () => {
const result = await Swal.fire({
title: "Delete items?",
text: "You will not be able to recover these items.",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yes",
});
return result.value;
};