diff --git a/src/app.tsx b/src/app.tsx index 4fbe4c0..c429428 100755 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,6 +1,6 @@ import ReactDOM from "react-dom/client"; import { Provider } from "react-redux"; -import configStore from "./store/configureStore"; +import store from "./store/configureStore"; import AppRouter from "./routers/AppRouter"; import "normalize.css/normalize.css"; import "./styles/styles.scss"; @@ -11,7 +11,6 @@ import { QueryClient, QueryClientProvider } from "react-query"; // import '../node_modules/@fortawesome/fontawesome-free/css/all.css'; // import '../node_modules/@fortawesome/fontawesome-free/js/all.js'; -const store = configStore(); const queryClient = new QueryClient(); const jsxWrapper = ( diff --git a/src/components/Dataform/index.jsx b/src/components/Dataform/index.jsx index a3971f3..199408f 100644 --- a/src/components/Dataform/index.jsx +++ b/src/components/Dataform/index.jsx @@ -22,10 +22,10 @@ const DataForm = memo(() => { } else if (!fileList) { return; } - if (reachedIntersect) { + if (reachedIntersect && !isFetchingNextPage) { filesFetchNextPage(); } - }, [reachedIntersect, initialLoad]); + }, [reachedIntersect, initialLoad, isFetchingNextPage]); return (
diff --git a/src/components/FileItem/index.jsx b/src/components/FileItem/index.jsx index 5bfbc2a..75c3191 100644 --- a/src/components/FileItem/index.jsx +++ b/src/components/FileItem/index.jsx @@ -11,18 +11,21 @@ import { getFileColor, getFileExtension } from "../../utils/files"; import { startSetSelectedItem } from "../../actions/selectedItem"; import { setPopupFile } from "../../actions/popupFile"; import bytes from "bytes"; +import { useAppDispatch, useAppSelector } from "../../hooks/store"; +import { setMainSelect } from "../../reducers/selected"; const FileItem = React.memo((props) => { const { file } = props; - const elementSelected = useSelector( - (state) => state.selectedItem.selected === file._id - ); + const elementSelected = useAppSelector((state) => { + if (state.selected.mainSection.type !== "file") return false; + return state.selected.mainSection.id === file._id; + }); const listView = useSelector((state) => state.filter.listView); const { image, hasThumbnail, imageOnError } = useThumbnail( file.metadata.hasThumbnail, file.metadata.thumbnailID ); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const lastSelected = useRef(0); const { onContextMenu, @@ -58,7 +61,9 @@ const FileItem = React.memo((props) => { const currentDate = Date.now(); if (!elementSelected) { - dispatch(startSetSelectedItem(file._id, true, false)); + dispatch( + setMainSelect({ file, id: file._id, type: "file", folder: null }) + ); lastSelected.current = Date.now(); return; } diff --git a/src/components/FolderItem/index.jsx b/src/components/FolderItem/index.jsx index 1ff902f..37d0915 100644 --- a/src/components/FolderItem/index.jsx +++ b/src/components/FolderItem/index.jsx @@ -7,15 +7,18 @@ import { useNavigate } from "react-router-dom"; import { startSetSelectedItem } from "../../actions/selectedItem"; import mobilecheck from "../../utils/mobileCheck"; import moment from "moment"; +import { useAppDispatch, useAppSelector } from "../../hooks/store"; +import { setMainSelect } from "../../reducers/selected"; const FolderItem = React.memo((props) => { const { folder } = props; - const elementSelected = useSelector( - (state) => state.selectedItem.selected === folder._id - ); + const elementSelected = useAppSelector((state) => { + if (state.selected.mainSection.type !== "folder") return false; + return state.selected.mainSection.id === folder._id; + }); const lastSelected = useRef(0); const navigate = useNavigate(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const { onContextMenu, closeContextMenu, @@ -30,7 +33,14 @@ const FolderItem = React.memo((props) => { const currentDate = Date.now(); if (!elementSelected) { - dispatch(startSetSelectedItem(folder._id, false, false)); + dispatch( + setMainSelect({ + file: null, + id: folder._id, + type: "folder", + folder: folder, + }) + ); lastSelected.current = Date.now(); return; } @@ -113,7 +123,7 @@ const FolderItem = React.memo((props) => { elementSelected ? "text-white" : "text-black" )} > - {props.folder.name} + {folder.name}

{ const { data: folders } = useFolders(); + const { isHome } = useUtils(); const sortBy = useSelector((state) => state.filter.sortBy); const dispatch = useDispatch(); @@ -67,7 +69,12 @@ const Folders = memo(() => { return (
-

+

{folders?.length === 0 ? "No Folders" : "Folders"}

diff --git a/src/components/LeftSection/LeftSection.jsx b/src/components/LeftSection/LeftSection.jsx index 77ffe48..d316b22 100644 --- a/src/components/LeftSection/LeftSection.jsx +++ b/src/components/LeftSection/LeftSection.jsx @@ -14,7 +14,7 @@ class LeftSection extends React.Component { render() { return (
- -
- -
{ className="text-[#637381] text-[18px] leading-[21px] font-medium m-0 no-underline animate cursor-pointer" onClick={goHome} > - myDrive + Home spacer diff --git a/src/components/PopupWindow/PopupWindow.jsx b/src/components/PopupWindow/PopupWindow.jsx index 3887af0..a133656 100644 --- a/src/components/PopupWindow/PopupWindow.jsx +++ b/src/components/PopupWindow/PopupWindow.jsx @@ -111,69 +111,6 @@ class PopupWindow extends React.Component { />
); - - return ( -
-

- {capitalize(this.props.popupFile.filename)} -

- - {!this.props.popupFile.metadata.isVideo ? ( -
- {!this.props.popupFile.metadata.hasThumbnail ? ( -

No Preview Available

- ) : undefined} - - {!this.props.popupFile.metadata.hasThumbnail ? ( - - ) : ( - - )} - -
- {!this.props.popupFile.metadata.hasThumbnail ? undefined : ( - - )} -
-
- ) : ( - - )} - - - -
- ); } } diff --git a/src/components/QuickAccessItem/index.tsx b/src/components/QuickAccessItem/index.tsx new file mode 100644 index 0000000..c4edd0b --- /dev/null +++ b/src/components/QuickAccessItem/index.tsx @@ -0,0 +1,165 @@ +import capitalize from "../../utils/capitalize"; +import moment from "moment"; +import React, { memo, useMemo, useRef } from "react"; +import ContextMenu from "../ContextMenu"; +import classNames from "classnames"; +import { getFileColor, getFileExtension } from "../../utils/files"; +import { useThumbnail } from "../../hooks/files"; +import { useContextMenu } from "../../hooks/contextMenu"; +import { useDispatch, useSelector } from "react-redux"; +import mobilecheck from "../../utils/mobileCheck"; +import { startSetSelectedItem } from "../../actions/selectedItem"; +import { setPopupFile } from "../../actions/popupFile"; +import { FileInterface } from "../../types/file"; +import { useAppDispatch, useAppSelector } from "../../hooks/store"; +import { setMainSelect } from "../../reducers/selected"; + +interface QuickAccessItemProps { + file: FileInterface; +} + +const QuickAccessItem = memo((props: QuickAccessItemProps) => { + const { file } = props; + const elementSelected = useAppSelector((state) => { + if (state.selected.mainSection.type !== "quick-item") return false; + return state.selected.mainSection.id === file._id; + }); + console.log("ele selected 2", elementSelected); + const { image, hasThumbnail, imageOnError } = useThumbnail( + file.metadata.hasThumbnail, + file.metadata.thumbnailID + ); + const dispatch = useAppDispatch(); + const lastSelected = useRef(0); + + const { + onContextMenu, + closeContextMenu, + onTouchStart, + onTouchMove, + onTouchEnd, + clickStopPropagation, + ...contextMenuState + } = useContextMenu(); + + const fileExtension = useMemo( + () => getFileExtension(file.filename), + [file.filename] + ); + + const imageColor = useMemo( + () => getFileColor(file.filename), + [file.filename] + ); + + // TODO: See if we can memoize this + const quickItemClick = () => { + const currentDate = Date.now(); + + if (!elementSelected) { + // dispatch(startSetSelectedItem(file._id, true, true)); + dispatch( + setMainSelect({ file, id: file._id, type: "quick-item", folder: null }) + ); + lastSelected.current = Date.now(); + return; + } + + const isMobile = mobilecheck(); + + if (isMobile || currentDate - lastSelected.current < 1500) { + dispatch(setPopupFile({ showPopup: true, ...file })); + } + + lastSelected.current = Date.now(); + }; + + return ( +
+ {contextMenuState.selected && ( +
+ +
+ )} +
+ {hasThumbnail ? ( + + ) : ( + + + + )} + {!hasThumbnail && ( +
+

{fileExtension}

+
+ )} +
+
+

+ {capitalize(file.filename)} +

+ +
+
+ ); +}); + +export default QuickAccessItem; diff --git a/src/components/QuickAccessItem/index.jsx b/src/components/QuickAccessItem/index2.jsx similarity index 98% rename from src/components/QuickAccessItem/index.jsx rename to src/components/QuickAccessItem/index2.jsx index 1e71624..fbe249d 100644 --- a/src/components/QuickAccessItem/index.jsx +++ b/src/components/QuickAccessItem/index2.jsx @@ -10,6 +10,7 @@ import { useDispatch, useSelector } from "react-redux"; import mobilecheck from "../../utils/mobileCheck"; import { startSetSelectedItem } from "../../actions/selectedItem"; import { setPopupFile } from "../../actions/popupFile"; +import { useAppSelector } from "../../hooks/store"; const QuickAccessItem = memo((props) => { const { file } = props; diff --git a/src/components/RightSection/index.jsx b/src/components/RightSection/index.tsx similarity index 74% rename from src/components/RightSection/index.jsx rename to src/components/RightSection/index.tsx index 636f8c3..d7fa46b 100644 --- a/src/components/RightSection/index.jsx +++ b/src/components/RightSection/index.tsx @@ -9,15 +9,17 @@ import { getFileExtension } from "../../utils/files"; import { useContextMenu } from "../../hooks/contextMenu"; import { setPopupFile } from "../../actions/popupFile"; import { useNavigate } from "react-router-dom"; +import { useAppSelector } from "../../hooks/store"; +import { resetSelected } from "../../reducers/selected"; const RightSection = memo(() => { - const selectedItem = useSelector((state) => state.selectedItem); + const selectedItem = useAppSelector((state) => state.selected.mainSection); const navigate = useNavigate(); const dispatch = useDispatch(); const formattedName = useMemo(() => { - if (!selectedItem) return ""; - const name = selectedItem.name; + if (!selectedItem.id) return ""; + const name = selectedItem.file?.filename || selectedItem.folder?.name || ""; const maxLength = 66; const ellipsis = "..."; if (name.length <= maxLength) { @@ -31,22 +33,27 @@ const RightSection = memo(() => { const end = name.slice(-endLength); return `${start}${ellipsis}${end}`; - }, [selectedItem?.name, selectedItem?.file]); + }, [ + selectedItem?.id, + selectedItem?.file?.filename, + selectedItem?.folder?.name, + ]); - const formattedDate = useMemo( - () => moment(selectedItem.date).format("L"), - [selectedItem?.date, moment] - ); + const formattedDate = useMemo(() => { + const date = + selectedItem.file?.uploadDate || selectedItem.folder?.createdAt; + return moment(date).format("L"); + }, [selectedItem?.file?.uploadDate, selectedItem.folder?.createdAt, moment]); const fileSize = useMemo(() => { - if (!selectedItem || !selectedItem.size) return 0; - return bytes(selectedItem.size); - }, [selectedItem?.size, bytes]); + if (!selectedItem.file?.length) return 0; + return bytes(selectedItem.file.length); + }, [selectedItem?.file?.length, bytes]); const fileExtension = useMemo(() => { - if (!selectedItem?.file) return null; - return getFileExtension(selectedItem.name); - }, [selectedItem?.file, selectedItem?.name, getFileExtension]); + if (!selectedItem?.file?.filename) return null; + return getFileExtension(selectedItem.file.filename); + }, [selectedItem?.file?.filename, getFileExtension]); const { onContextMenu, @@ -58,24 +65,24 @@ const RightSection = memo(() => { ...contextMenuState } = useContextMenu(); - const resetSelected = () => { - dispatch(resetSelectedItem()); + const reset = () => { + dispatch(resetSelected()); }; - const openItem = (e) => { + const openItem = () => { if (selectedItem.file) { - dispatch(setPopupFile({ showPopup: true, ...selectedItem.data })); + dispatch(setPopupFile({ showPopup: true, ...selectedItem.file })); } else { - navigate(`/folder/${selectedItem.data._id}`); + navigate(`/folder/${selectedItem.id}`); } }; return (
- {selectedItem.name === "" ? ( + {selectedItem.id === "" ? (
filedetailsicon @@ -97,7 +104,7 @@ const RightSection = memo(() => {
@@ -112,13 +119,13 @@ const RightSection = memo(() => { Type - {selectedItem.size ? fileExtension : "Folder"} + {selectedItem.file ? fileExtension : "Folder"}
@@ -136,29 +143,17 @@ const RightSection = memo(() => { {formattedDate}
-
- - Location - - - {selectedItem.drive - ? "Google Drive" - : selectedItem.personalFile - ? "Amazon S3" - : "myDrive"} - -
- Privacy + Access - {selectedItem.link ? "Public" : "Only you"} + {selectedItem.file?.metadata.link ? "Public" : "Private"}
@@ -172,6 +167,7 @@ const RightSection = memo(() => {
@@ -185,8 +181,8 @@ const RightSection = memo(() => { contextSelected={contextMenuState} closeContext={closeContextMenu} folderMode={!selectedItem.file} - file={selectedItem.data} - folder={selectedItem.data} + file={selectedItem.file} + folder={selectedItem.folder} />
)} diff --git a/src/hooks/files.ts b/src/hooks/files.ts index 8937d4f..52bdcda 100644 --- a/src/hooks/files.ts +++ b/src/hooks/files.ts @@ -85,17 +85,24 @@ export const useQuickFilesClient = () => { interface thumbnailState { hasThumbnail: boolean; - image: null | string; + image: undefined | string; } -export const useThumbnail = (hasThumbnail: boolean, thumbnailID: string) => { +export const useThumbnail = (hasThumbnail: boolean, thumbnailID?: string) => { const [state, setState] = useState({ hasThumbnail: false, - image: null, + image: undefined, }); + const imageOnError = useCallback(() => { + setState({ + hasThumbnail: false, + image: undefined, + }); + }, [setState]); const getThumbnail = useCallback(async () => { try { + if (!thumbnailID) return; const thumbnailData = await getFileThumbnail(thumbnailID); setState({ hasThumbnail: true, @@ -105,17 +112,10 @@ export const useThumbnail = (hasThumbnail: boolean, thumbnailID: string) => { console.log("error getting thumbnail data", e); imageOnError(); } - }, [thumbnailID]); - - const imageOnError = () => { - setState({ - hasThumbnail: false, - image: null, - }); - }; + }, [thumbnailID, getFileThumbnail, setState, imageOnError]); useEffect(() => { - if (!hasThumbnail) return; + if (!hasThumbnail || !thumbnailID) return; getThumbnail(); }, [hasThumbnail, getThumbnail]); diff --git a/src/hooks/store.ts b/src/hooks/store.ts new file mode 100644 index 0000000..9246942 --- /dev/null +++ b/src/hooks/store.ts @@ -0,0 +1,6 @@ +import { useDispatch, useSelector } from "react-redux"; +import { AppDispatch, RootState } from "../store/configureStore"; + +// Use throughout your app instead of plain `useDispatch` and `useSelector` +export const useAppDispatch = useDispatch.withTypes(); +export const useAppSelector = useSelector.withTypes(); diff --git a/src/reducers/selected.ts b/src/reducers/selected.ts new file mode 100644 index 0000000..4a8f73b --- /dev/null +++ b/src/reducers/selected.ts @@ -0,0 +1,40 @@ +import { FileInterface } from "../types/file"; +import { createSlice, configureStore, PayloadAction } from "@reduxjs/toolkit"; +import { FolderInterface } from "../types/folders"; + +interface MainSecionType { + type: "" | "quick-item" | "file" | "folder"; + id: string; + file: FileInterface | null; + folder: FolderInterface | null; +} + +export interface SelectedStateType { + mainSection: MainSecionType; + popupModal: FileInterface | null; +} + +const initialState: SelectedStateType = { + mainSection: { + type: "", + id: "", + file: null, + folder: null, + }, + popupModal: null, +}; + +const selectedSlice = createSlice({ + name: "selected", + initialState, + reducers: { + setMainSelect: (state, action: PayloadAction) => { + state.mainSection = action.payload; + }, + resetSelected: () => initialState, + }, +}); + +export const { setMainSelect, resetSelected } = selectedSlice.actions; + +export default selectedSlice.reducer; diff --git a/src/store/configureStore.js b/src/store/configureStore.ts similarity index 56% rename from src/store/configureStore.js rename to src/store/configureStore.ts index 7d02b80..30a67f5 100755 --- a/src/store/configureStore.js +++ b/src/store/configureStore.ts @@ -18,33 +18,36 @@ import moverReducer from "../reducers/mover"; import folderTreeReducer from "../reducers/folderTree"; import uploadStorageSwitcherReducer from "../reducers/uploadStorageSwitcher"; import mobileContextMenuReducer from "../reducers/mobileContextMenu"; +import selectedReducer from "../reducers/selected"; //const composeEnchancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; -export default () => { - const store = configureStore({ - reducer: { - auth: authReducer, - main: mainReducer, - files: fileReducer, - folders: folderReducer, - filter: filterReducer, - selectedItem: selectedItemReducer, - uploads: uploadsReducer, - storage: storageReducer, - quickFiles: quickFilesReducer, - popupFile: popupFilesReducer, - settings: settingsReducer, - parent: parentReducer, - addOptions: addOptionsReducer, - photoViewer: photoViewerReducer, - routes: routesReducer, - mover: moverReducer, - folderTree: folderTreeReducer, - storageSwitcher: uploadStorageSwitcherReducer, - mobileContextMenu: mobileContextMenuReducer, - }, - }); +const store = configureStore({ + reducer: { + auth: authReducer, + main: mainReducer, + files: fileReducer, + folders: folderReducer, + filter: filterReducer, + selected: selectedReducer, + selectedItem: selectedItemReducer, + uploads: uploadsReducer, + storage: storageReducer, + quickFiles: quickFilesReducer, + popupFile: popupFilesReducer, + settings: settingsReducer, + parent: parentReducer, + addOptions: addOptionsReducer, + photoViewer: photoViewerReducer, + routes: routesReducer, + mover: moverReducer, + folderTree: folderTreeReducer, + storageSwitcher: uploadStorageSwitcherReducer, + mobileContextMenu: mobileContextMenuReducer, + }, +}); - return store; -}; +export type RootState = ReturnType; +export type AppDispatch = typeof store.dispatch; + +export default store; diff --git a/src/types/file.ts b/src/types/file.ts new file mode 100644 index 0000000..5fe65b0 --- /dev/null +++ b/src/types/file.ts @@ -0,0 +1,43 @@ +export interface FileInterface { + _id: string; + length: number; + chunkSize: number; + uploadDate: string; + filename: string; + lastErrorObject: { updatedExisting: any }; + metadata: { + owner: string; + parent: string; + parentList: string; + hasThumbnail: boolean; + isVideo: boolean; + thumbnailID?: string; + size: number; + IV: Buffer; + linkType?: "one" | "public"; + link?: string; + filePath?: string; + s3ID?: string; + personalFile?: boolean; + }; +} +interface example { + selectedRightSectionItem: { + folder?: { + id: string; + name: string; + }; + file?: { + id: string; + filename: string; + }; + }; + popupFile: { + id: string; + filename: string; + }; + selectedItem: { + type: string; + id: string; + }; +} diff --git a/src/types/folders.ts b/src/types/folders.ts new file mode 100644 index 0000000..6046b2b --- /dev/null +++ b/src/types/folders.ts @@ -0,0 +1,9 @@ +export interface FolderInterface { + name: string; + parent: string; + owner: string; + createdAt: Date; + updatedAt: Date; + parentList: string[]; + personalFolder?: boolean; +}