diff --git a/backend/controllers/file.ts b/backend/controllers/file.ts
index d880942..6a42d5b 100644
--- a/backend/controllers/file.ts
+++ b/backend/controllers/file.ts
@@ -495,6 +495,8 @@ class FileController {
const userID = req.user._id;
const fileID = req.body.id;
+ console.log("id", fileID);
+
await this.chunkService.deleteFile(userID, fileID);
res.send();
diff --git a/backend/services/ChunkService/FileSystemService.ts b/backend/services/ChunkService/FileSystemService.ts
index 01c5a36..91990e8 100644
--- a/backend/services/ChunkService/FileSystemService.ts
+++ b/backend/services/ChunkService/FileSystemService.ts
@@ -172,7 +172,8 @@ class FileSystemService implements ChunkInterface {
const filePath = currentFile.metadata.filePath!;
- const IV = currentFile.metadata.IV.buffer as Buffer;
+ const IV = currentFile.metadata.IV;
+ console.log("iv", IV);
const readStream = fs.createReadStream(filePath);
@@ -260,7 +261,7 @@ class FileSystemService implements ChunkInterface {
if (!file) throw new NotFoundError("File Thumbnail Not Found");
const password = user.getEncryptionKey();
- const IV = file.metadata.IV.buffer as Buffer;
+ const IV = file.metadata.IV;
if (!password) throw new ForbiddenError("Invalid Encryption Key");
diff --git a/src/api/filesAPI.ts b/src/api/filesAPI.ts
index e753cce..5b229dc 100644
--- a/src/api/filesAPI.ts
+++ b/src/api/filesAPI.ts
@@ -11,6 +11,8 @@ interface QueryKeyParams {
startAt?: boolean;
}
+// GET
+
export const getFilesList = async ({
queryKey,
pageParam,
@@ -47,3 +49,36 @@ export const getQuickFilesList = async () => {
});
return response.data;
};
+
+export const getFileThumbnail = async (thumbnailID: string) => {
+ const url = `http://localhost:5173/api/file-service/thumbnail/${thumbnailID}`;
+ const config = {
+ responseType: "arraybuffer",
+ };
+
+ const response = await axios.get(url, config);
+
+ const imgFile = new Blob([response.data]);
+ const imgUrl = URL.createObjectURL(imgFile);
+
+ return imgUrl;
+};
+
+// PATCH
+export const renameFile = async (fileID: string, name: string) => {
+ const response = await axios.patch(`/file-service/rename`, {
+ id: fileID,
+ title: name,
+ });
+ return response.data;
+};
+
+// DELETE
+export const deleteFile = async (fileID: string) => {
+ const response = await axios.delete(`/file-service/remove`, {
+ data: {
+ id: fileID,
+ },
+ });
+ return response.data;
+};
diff --git a/src/components/Dataform/DataForm.jsx b/src/components/Dataform/DataForm.jsx
index 18e23f4..913c7f0 100644
--- a/src/components/Dataform/DataForm.jsx
+++ b/src/components/Dataform/DataForm.jsx
@@ -13,8 +13,11 @@ import { useFiles } from "../../hooks/files";
import { useFolders } from "../../hooks/folders";
const DataForm = (props) => {
- const params = useParams();
- const { data: files, fetchNextPage: filesFetchNextPage } = useFiles();
+ const {
+ data: files,
+ fetchNextPage: filesFetchNextPage,
+ invalidateFilesCache,
+ } = useFiles();
const { data: folders } = useFolders();
return (
@@ -39,6 +42,12 @@ const DataForm = (props) => {
>
Next page
+
{
+ const { invalidateFilesCache } = useFiles();
+ const { invalidateQuickFilesCache } = useQuickFiles();
+ const liClassname =
+ "flex w-full px-[20px] py-[12px] items-center font-normal justify-start no-underline transition-all duration-400 ease-in-out hover:bg-[#f6f5fd] hover:text-[#3c85ee] hover:font-medium";
+ const spanClassname = "inline-flex mr-[18px]";
- render() {
- return (
- // settings__folder__margin
-
-
- -
-
-
-
-
- Rename
-
-
- {!this.props.folderMode ? (
- -
-
- {
+ if (!value) {
+ return "Please Enter a Name";
+ }
+ },
+ });
+ await renameFile(props.file._id, filename);
+ invalidateFilesCache();
+ invalidateQuickFilesCache();
+ }
+ };
+
+ const deleteItem = async () => {
+ if (!props.folderMode) {
+ const result = await Swal.fire({
+ title: "Confirm Deletion",
+ text: "You cannot undo this action",
+ icon: "warning",
+ showCancelButton: true,
+ confirmButtonColor: "#3085d6",
+ cancelButtonColor: "#d33",
+ confirmButtonText: "Yes, delete",
+ });
+
+ if (result.value) {
+ await deleteFile(props.file._id);
+ invalidateFilesCache();
+ invalidateQuickFilesCache();
+ }
+ }
+ };
+
+ return (
+
- );
- }
-}
+ ) : undefined}
+ {!props.folderMode ? (
+
-
+
+
+
+
+ Download
+
+
+ ) : undefined}
+ -
+
+
+
+ {" "}
+ Move
+
+
+ -
+
+
+
+
+ Delete
+
+
+
+
+ );
+};
export default NewContextMenu;
diff --git a/src/components/NewContextMenu/index.jsx b/src/components/NewContextMenu/index.jsx
index 8508844..b442ade 100644
--- a/src/components/NewContextMenu/index.jsx
+++ b/src/components/NewContextMenu/index.jsx
@@ -1,73 +1,73 @@
import React from "react";
-import {connect} from "react-redux";
+import { connect } from "react-redux";
import { setShareSelected } from "../../actions/selectedItem";
import NewContextMenu from "./NewContextMenu";
class NewContextMenuContainer extends React.Component {
+ constructor(props) {
+ super(props);
- constructor(props) {
- super(props);
+ this.wrapperRef = React.createRef();
+ }
- this.wrapperRef = React.createRef();
+ componentDidMount = () => {
+ document.addEventListener("mousedown", this.handleClickOutside);
+ };
+
+ componentWillUnmount = () => {
+ document.removeEventListener("mousedown", this.handleClickOutside);
+ };
+
+ componentDidUpdate = () => {};
+
+ handleClickOutside = () => {
+ if (
+ this.props.contextSelected.selected &&
+ this.wrapperRef &&
+ !this.wrapperRef.current.contains(event.target)
+ ) {
+ this.props.closeContext();
}
+ };
- componentDidMount = () => {
+ startFileDownload = () => {
+ console.log("start download file", this.props.file._id);
+ this.props.downloadFile(this.props.file._id, this.props.file);
+ };
- document.addEventListener("mousedown", this.handleClickOutside)
- }
+ startRenameFile = async (e) => {
+ console.log("rename file click");
+ this.props.changeEditNameMode();
+ };
- componentWillUnmount = () => {
+ startDeleteFile = async () => {
+ this.props.changeDeleteMode();
+ };
- document.removeEventListener("mousedown", this.handleClickOutside)
- }
+ startShareFile = () => {
+ this.props.dispatch(setShareSelected({ ...this.props.file }));
+ };
- componentDidUpdate = () => {
- }
-
- handleClickOutside = () => {
- if (this.props.contextSelected && this.wrapperRef && !this.wrapperRef.current.contains(event.target)) {
- this.props.closeContext();
- }
- }
-
- startFileDownload = () => {
- console.log("start download file", this.props.file._id);
- this.props.downloadFile(this.props.file._id, this.props.file)
- }
-
- startRenameFile = async(e) => {
- console.log("rename file click")
- this.props.changeEditNameMode();
- }
-
- startDeleteFile = async() => {
- this.props.changeDeleteMode()
- }
-
- startShareFile = () => {
- this.props.dispatch(setShareSelected({...this.props.file}))
- }
-
-
- stopPropagation = (e) => {
- e.stopPropagation()
- if (this.props.quickItemMode || this.props.gridMode) this.props.closeContext();
- }
-
- render() {
-
- return
- }
+ stopPropagation = (e) => {
+ e.stopPropagation();
+ if (this.props.quickItemMode || this.props.gridMode)
+ this.props.closeContext();
+ };
+ render() {
+ return (
+
+ );
+ }
}
-export default connect()(NewContextMenuContainer);
\ No newline at end of file
+export default connect()(NewContextMenuContainer);
diff --git a/src/components/QuickAccessItem/QuickAccessItem.jsx b/src/components/QuickAccessItem/QuickAccessItem.jsx
index 3cab419..92718f5 100644
--- a/src/components/QuickAccessItem/QuickAccessItem.jsx
+++ b/src/components/QuickAccessItem/QuickAccessItem.jsx
@@ -1,117 +1,79 @@
import capitalize from "../../utils/capitalize";
import moment from "moment";
-import React, { useMemo } from "react";
+import React, { useMemo, useState } from "react";
import NewContextMenu from "../NewContextMenu";
import classNames from "classnames";
+import { getFileColor, getFileExtension } from "../../utils/files";
+import { useThumbnail } from "../../hooks/files";
+import { useContextMenu } from "../../hooks/contextMenu";
const QuickAccessItem = (props) => {
- const fileExtension = useMemo(() => {
- const filenameSplit = props.filename.split(".");
+ const { image, hasThumbnail, imageOnError } = useThumbnail(
+ props.metadata.hasThumbnail,
+ props.metadata.thumbnailID
+ );
- if (filenameSplit.length > 1) {
- let extension = filenameSplit[filenameSplit.length - 1];
+ const { onContextMenu, closeContextMenu, ...contextMenuState } =
+ useContextMenu();
- if (extension.length > 4)
- extension =
- extension.substring(0, 3) +
- extension.substring(extension.length - 1, extension.length);
+ const fileExtension = useMemo(
+ () => getFileExtension(props.filename),
+ [props.filename]
+ );
- return extension.toUpperCase();
- } else {
- return "UNK";
- }
- }, [props.filename]);
-
- const imageColor = useMemo(() => {
- const letter = fileExtension.substring(0, 1).toUpperCase();
-
- const colorObj = {
- A: "#e53935",
- B: "#d81b60",
- C: "#8e24aa",
- D: "#5e35b1",
- E: "#3949ab",
- F: "#1e88e5",
- G: "#039be5",
- H: "#00acc1",
- I: "#00897b",
- J: "#43a047",
- K: "#fdd835",
- L: "#ffb300",
- M: "#fb8c00",
- N: "#f4511e",
- O: "#d32f2f",
- P: "#c2185b",
- Q: "#7b1fa2",
- R: "#512da8",
- S: "#303f9f",
- T: "#1976d2",
- U: "#0288d1",
- V: "#0097a7",
- W: "#0097a7",
- X: "#00796b",
- Y: "#388e3c",
- Z: "#fbc02d",
- };
-
- if (colorObj[letter]) {
- return colorObj[letter];
- } else {
- return "#03a9f4";
- }
- }, [props.filename]);
+ const imageColor = useMemo(
+ () => getFileColor(props.filename),
+ [props.filename]
+ );
const elementSelected = useMemo(
() => `quick-${props._id}` === props.selected,
[props._id, props.selected]
);
- console.log("thumbnail", props.state);
-
return (
{
props.fileClick(props._id, props, true);
}}
- onContextMenu={props.selectContext}
+ onContextMenu={onContextMenu}
onTouchStart={props.onTouchStart}
onTouchEnd={props.onTouchEnd}
onTouchMove={props.onTouchMove}
>
-
-
-
+ {contextMenuState.selected && (
+
+
+
+ )}
- {props.state.hasThumbnail ? (
+ {hasThumbnail ? (

) : (
)}
- {!props.state.hasThumbnail && (
+ {!hasThumbnail && (
diff --git a/src/components/QuickAccessItem/index.jsx b/src/components/QuickAccessItem/index.jsx
index 54b4360..ff3072c 100644
--- a/src/components/QuickAccessItem/index.jsx
+++ b/src/components/QuickAccessItem/index.jsx
@@ -25,7 +25,6 @@ class QuickAccessItemContainer extends React.Component {
this.lastTouch = 0;
this.state = {
- contextMenuPos: {},
image: "/images/file-svg.svg",
imageClassname: "noSelect file__item-no-thumbnail",
contextSelected: false,
@@ -33,99 +32,6 @@ class QuickAccessItemContainer extends React.Component {
};
}
- componentDidMount = () => {
- const hasThumbnail = this.props.metadata.hasThumbnail;
- console.log("has", hasThumbnail);
-
- if (hasThumbnail && !this.failedToLoad) {
- this.getThumbnail();
- }
- };
-
- closeContext = () => {
- this.setState(() => {
- return {
- ...this.state,
- contextSelected: false,
- };
- });
- };
-
- selectContext = (e) => {
- if (e) e.stopPropagation();
- if (e) e.preventDefault();
-
- if (mobilecheck()) {
- this.props.dispatch(setMobileContextMenu(true, this.props));
- return;
- }
-
- this.setState(() => {
- return {
- ...this.state,
- contextSelected: {
- width: e.clientX,
- height: e.clientY,
- },
- };
- });
- };
-
- getThumbnail = async () => {
- const thumbnailID = this.props.metadata.thumbnailID;
- const imageClassname = "noSelect";
-
- // GOOGLE DRIVE IMAGE
- if (this.props.metadata.drive) {
- return await this.setState(() => ({
- ...this.state,
- image: this.props.metadata.thumbnailID,
- imageClassname: imageClassname,
- }));
- }
-
- const config = {
- responseType: "arraybuffer",
- };
-
- await this.setState(() => ({
- ...this.state,
- image: "/images/file-svg.svg",
- imageClassname: "noSelect file__item-no-thumbnail",
- }));
-
- const url = `http://localhost:5173/api/file-service/thumbnail/${thumbnailID}`;
-
- axios
- .get(url, config)
- .then((results) => {
- const imgFile = new Blob([results.data]);
- const imgUrl = URL.createObjectURL(imgFile);
- console.log("got image");
-
- this.setState(() => ({
- ...this.state,
- image: imgUrl,
- imageClassname: imageClassname,
- hasThumbnail: true,
- }));
- })
- .catch((err) => {
- console.log(err);
- });
- };
-
- thumbnailOnError = (e) => {
- console.log("thumbnail on error", e);
-
- this.setState(() => ({
- ...this.state,
- image: "/images/file-svg.svg",
- imageClassname: "noSelect file__item-no-thumbnail",
- hasThumbnail: false,
- }));
- };
-
onTouchStart = () => {
const date = new Date();
this.lastTouch = date.getTime();
@@ -146,43 +52,10 @@ class QuickAccessItemContainer extends React.Component {
this.lastTouch = 0;
if (difference > 500) {
- this.selectContext();
+ // this.selectContext();
}
};
- getContextMenu = (e) => {
- if (e) e.preventDefault();
-
- const isMobile = mobileCheck();
-
- const windowX = window.innerWidth;
- const windowY = window.innerHeight;
-
- let styleObj = { right: 0, left: 0, top: "-3px", bottom: 0 };
-
- if (isMobile) {
- styleObj = { bottom: 0, left: "2px" };
- } else {
- const clientY = e.nativeEvent.clientY;
- const clientX = e.nativeEvent.clientX;
-
- if (clientX > windowX / 2) {
- styleObj = { ...styleObj, left: "unset", right: 0 };
- } else {
- styleObj = { ...styleObj, left: 0, right: "unset" };
- }
- }
-
- this.setState(() => ({
- ...this.state,
- contextMenuPos: styleObj,
- }));
-
- this.props.dispatch(setSelected("quick-" + this.props._id));
- this.props.dispatch(setRightSelected("quick-" + this.props._id));
- this.props.dispatch(setLastSelected(0));
- };
-
changeEditNameMode = async () => {
let inputValue = this.props.filename;
@@ -257,17 +130,13 @@ class QuickAccessItemContainer extends React.Component {
render() {
return (
diff --git a/src/hooks/contextMenu.ts b/src/hooks/contextMenu.ts
new file mode 100644
index 0000000..8197633
--- /dev/null
+++ b/src/hooks/contextMenu.ts
@@ -0,0 +1,33 @@
+import { useState } from "react";
+
+export const useContextMenu = () => {
+ const [contextData, setContextData] = useState({
+ selected: false,
+ X: 0,
+ Y: 0,
+ lastTouched: 0,
+ });
+
+ const onContextMenu = (e: React.MouseEvent
) => {
+ if (e) e.stopPropagation();
+ if (e) e.preventDefault();
+ setContextData({
+ ...contextData,
+ selected: true,
+ X: e.clientX,
+ Y: e.clientY,
+ });
+ };
+
+ const closeContextMenu = () => {
+ setContextData({
+ ...contextData,
+ selected: false,
+ X: 0,
+ Y: 0,
+ lastTouched: 0,
+ });
+ };
+
+ return { ...contextData, onContextMenu, closeContextMenu };
+};
diff --git a/src/hooks/files.ts b/src/hooks/files.ts
index 27ff580..b9c42d0 100644
--- a/src/hooks/files.ts
+++ b/src/hooks/files.ts
@@ -1,6 +1,11 @@
-import { useInfiniteQuery, useQuery } from "react-query";
+import { useInfiniteQuery, useQuery, useQueryClient } from "react-query";
import { useParams } from "react-router-dom";
-import { getFilesList, getQuickFilesList } from "../api/filesAPI";
+import {
+ getFileThumbnail,
+ getFilesList,
+ getQuickFilesList,
+} from "../api/filesAPI";
+import { useCallback, useEffect, useState } from "react";
export const useFiles = () => {
const params = useParams();
@@ -17,6 +22,7 @@ export const useFiles = () => {
getFilesList,
{
getNextPageParam: (lastPage, pages) => {
+ console.log("pages", pages);
const lastElement = lastPage[lastPage.length - 1];
if (!lastElement) return undefined;
return {
@@ -27,15 +33,79 @@ export const useFiles = () => {
}
);
+ const filesReactClientQuery = useQueryClient();
+
+ const invalidateFilesCache = () => {
+ filesReactClientQuery.invalidateQueries({
+ queryKey: [
+ "files",
+ {
+ parent: params.id || "/",
+ search: "",
+ sortBy: undefined,
+ limit: undefined,
+ },
+ ],
+ });
+ };
+
const testFunction = () => {
console.log("this is a test function");
};
- return { ...filesReactQuery, testFunction };
+ return { ...filesReactQuery, testFunction, invalidateFilesCache };
};
export const useQuickFiles = () => {
const quickFilesQuery = useQuery("quickFiles", getQuickFilesList);
- return quickFilesQuery;
+ const quickFilesReactClientQuery = useQueryClient();
+
+ const invalidateQuickFilesCache = () => {
+ quickFilesReactClientQuery.invalidateQueries({
+ queryKey: "quickFiles",
+ });
+ };
+
+ return { ...quickFilesQuery, invalidateQuickFilesCache };
+};
+
+interface thumbnailState {
+ hasThumbnail: boolean;
+ image: null | string;
+}
+
+export const useThumbnail = (hasThumbnail: boolean, thumbnailID: string) => {
+ const [state, setState] = useState({
+ hasThumbnail: false,
+ image: null,
+ });
+
+ const getThumbnail = useCallback(async () => {
+ try {
+ const thumbnailData = await getFileThumbnail(thumbnailID);
+ setState({
+ hasThumbnail: true,
+ image: thumbnailData,
+ });
+ } catch (e) {
+ console.log("error getting thumbnail data", e);
+ imageOnError();
+ }
+ }, [thumbnailID]);
+
+ const imageOnError = () => {
+ setState({
+ hasThumbnail: false,
+ image: null,
+ });
+ };
+
+ useEffect(() => {
+ if (!hasThumbnail) return;
+
+ getThumbnail();
+ }, [hasThumbnail, getThumbnail]);
+
+ return { ...state, imageOnError };
};
diff --git a/src/utils/files.ts b/src/utils/files.ts
new file mode 100644
index 0000000..46043e4
--- /dev/null
+++ b/src/utils/files.ts
@@ -0,0 +1,59 @@
+export const getFileExtension = (filename: string) => {
+ const filenameSplit = filename.split(".");
+
+ if (filenameSplit.length > 1) {
+ let extension = filenameSplit[filenameSplit.length - 1];
+
+ if (extension.length > 4)
+ extension =
+ extension.substring(0, 3) +
+ extension.substring(extension.length - 1, extension.length);
+
+ return extension.toUpperCase();
+ } else {
+ return "UNK";
+ }
+};
+
+type ColorMap = {
+ [key: string]: string;
+};
+
+export const getFileColor = (filename: string) => {
+ const letter = getFileExtension(filename).substring(0, 1).toUpperCase();
+
+ const colorMap: ColorMap = {
+ A: "#e53935",
+ B: "#d81b60",
+ C: "#8e24aa",
+ D: "#5e35b1",
+ E: "#3949ab",
+ F: "#1e88e5",
+ G: "#039be5",
+ H: "#00acc1",
+ I: "#00897b",
+ J: "#43a047",
+ K: "#fdd835",
+ L: "#ffb300",
+ M: "#fb8c00",
+ N: "#f4511e",
+ O: "#d32f2f",
+ P: "#c2185b",
+ Q: "#7b1fa2",
+ R: "#512da8",
+ S: "#303f9f",
+ T: "#1976d2",
+ U: "#0288d1",
+ V: "#0097a7",
+ W: "#0097a7",
+ X: "#00796b",
+ Y: "#388e3c",
+ Z: "#fbc02d",
+ };
+
+ if (colorMap[letter]) {
+ return colorMap[letter];
+ } else {
+ return "#03a9f4";
+ }
+};