diff --git a/packages/common-contexts/package.json b/packages/common-contexts/package.json index e98b4bc62d..1ced8ff24f 100644 --- a/packages/common-contexts/package.json +++ b/packages/common-contexts/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@chainsafe/browser-storage-hooks": "^1.0.1", - "@chainsafe/files-api-client": "1.9.0", + "@chainsafe/files-api-client": "1.11.2", "axios": "^0.21.1", "uuid": "^8.3.1" }, diff --git a/packages/common-contexts/src/BillingContext/BillingContext.tsx b/packages/common-contexts/src/BillingContext/BillingContext.tsx index 3c21e12635..662ea9ef5d 100644 --- a/packages/common-contexts/src/BillingContext/BillingContext.tsx +++ b/packages/common-contexts/src/BillingContext/BillingContext.tsx @@ -1,5 +1,5 @@ import * as React from "react" -import { useImployApi } from "../ImployApiContext" +import { useFilesApi } from "../FilesApiContext" import axios, { AxiosResponse } from "axios" type BillingContextProps = { @@ -31,11 +31,11 @@ interface IStripeResponse { } const BillingProvider = ({ children }: BillingContextProps) => { - const { imployApiClient } = useImployApi() + const { filesApiClient } = useFilesApi() const addCard = async (cardToken: string) => { try { - await imployApiClient.addCard({ token: cardToken }) + await filesApiClient.addCard({ token: cardToken }) return Promise.resolve() } catch (error) { return Promise.reject("There was an error adding card.") diff --git a/packages/common-contexts/src/ImployApiContext/ImployApiContext.tsx b/packages/common-contexts/src/FilesApiContext/FilesApiContext.tsx similarity index 86% rename from packages/common-contexts/src/ImployApiContext/ImployApiContext.tsx rename to packages/common-contexts/src/FilesApiContext/FilesApiContext.tsx index 2d5e29a4ec..7587aa0103 100644 --- a/packages/common-contexts/src/ImployApiContext/ImployApiContext.tsx +++ b/packages/common-contexts/src/FilesApiContext/FilesApiContext.tsx @@ -1,7 +1,7 @@ import { useWeb3 } from "@chainsafe/web3-context" import * as React from "react" import { useState, useEffect, useMemo, useCallback } from "react" -import { IImployApiClient, ImployApiClient, Token, IdentityProvider, OAuthIdentityToken } from "@chainsafe/files-api-client" +import { IFilesApiClient, FilesApiClient, Token, IdentityProvider, OAuthIdentityToken } from "@chainsafe/files-api-client" import jwtDecode from "jwt-decode" import axios from "axios" import { decryptFile } from "../helpers" @@ -11,14 +11,14 @@ export { IdentityProvider as OAuthProvider } const tokenStorageKey = "csf.refreshToken" const isReturningUserStorageKey = "csf.isReturningUser" -type ImployApiContextProps = { +type FilesApiContextProps = { apiUrl?: string withLocalStorage?: boolean children: React.ReactNode | React.ReactNode[] } -type ImployApiContext = { - imployApiClient: IImployApiClient +type FilesApiContext = { + filesApiClient: IFilesApiClient isLoggedIn: boolean | undefined secured: boolean | undefined isReturningUser: boolean @@ -47,9 +47,9 @@ type ImployApiContext = { isMasterPasswordSet: boolean } -const ImployApiContext = React.createContext(undefined) +const FilesApiContext = React.createContext(undefined) -const ImployApiProvider = ({ apiUrl, withLocalStorage = true, children }: ImployApiContextProps) => { +const FilesApiProvider = ({ apiUrl, withLocalStorage = true, children }: FilesApiContextProps) => { const maintenanceMode = process.env.REACT_APP_MAINTENANCE_MODE === "true" const { wallet, onboard, checkIsReady, isReady } = useWeb3() @@ -63,11 +63,11 @@ const ImployApiProvider = ({ apiUrl, withLocalStorage = true, children }: Imploy }), []) const initialApiClient = useMemo(() => { - return new ImployApiClient({}, apiUrl, initialAxiosInstance) + return new FilesApiClient({}, apiUrl, initialAxiosInstance) }, [apiUrl, initialAxiosInstance] ) - const [imployApiClient, setImployApiClient] = useState(initialApiClient) + const [filesApiClient, setFilesApiClient] = useState(initialApiClient) const [isLoadingUser, setIsLoadingUser] = useState(true) // access tokens @@ -87,8 +87,8 @@ const ImployApiProvider = ({ apiUrl, withLocalStorage = true, children }: Imploy setRefreshToken(refreshToken) refreshToken.token && withLocalStorage && localStorageSet(tokenStorageKey, refreshToken.token) !withLocalStorage && sessionStorageSet(tokenStorageKey, refreshToken.token) - accessToken.token && imployApiClient.setToken(accessToken.token) - }, [imployApiClient, localStorageSet, sessionStorageSet, withLocalStorage]) + accessToken.token && filesApiClient.setToken(accessToken.token) + }, [filesApiClient, localStorageSet, sessionStorageSet, withLocalStorage]) const setReturningUser = () => { // set returning user @@ -115,7 +115,7 @@ const ImployApiProvider = ({ apiUrl, withLocalStorage = true, children }: Imploy ? localStorageGet(tokenStorageKey) : sessionStorageGet(tokenStorageKey) if (refreshTokenLocal) { - const refreshTokenApiClient = new ImployApiClient( + const refreshTokenApiClient = new FilesApiClient( {}, apiUrl, axiosInstance @@ -148,9 +148,9 @@ const ImployApiProvider = ({ apiUrl, withLocalStorage = true, children }: Imploy } ) - const apiClient = new ImployApiClient({}, apiUrl, axiosInstance) + const apiClient = new FilesApiClient({}, apiUrl, axiosInstance) const savedRefreshToken = localStorageGet(tokenStorageKey) - setImployApiClient(apiClient) + setFilesApiClient(apiClient) if (!maintenanceMode && savedRefreshToken) { try { const { @@ -200,7 +200,7 @@ const ImployApiProvider = ({ apiUrl, withLocalStorage = true, children }: Imploy const { access_token, refresh_token - } = await imployApiClient.verifyServiceIdentityToken({ + } = await filesApiClient.verifyServiceIdentityToken({ signature: signature, public_key: publicKey, service_identity_token: identityToken @@ -228,8 +228,8 @@ const ImployApiProvider = ({ apiUrl, withLocalStorage = true, children }: Imploy }, [refreshToken]) useEffect(() => { - if (accessToken && accessToken.token && imployApiClient) { - imployApiClient?.setToken(accessToken.token) + if (accessToken && accessToken.token && filesApiClient) { + filesApiClient?.setToken(accessToken.token) const decodedAccessToken = jwtDecode<{ perm: { secured?: string } }>( accessToken.token ) @@ -239,7 +239,7 @@ const ImployApiProvider = ({ apiUrl, withLocalStorage = true, children }: Imploy setSecured(false) } } - }, [accessToken, imployApiClient]) + }, [accessToken, filesApiClient]) const isLoggedIn = () => { if (isLoadingUser) { @@ -259,7 +259,7 @@ const ImployApiProvider = ({ apiUrl, withLocalStorage = true, children }: Imploy const getProviderUrl = async (provider: OAuthIdentityToken) => { try { - const { url } = await imployApiClient.getOauth2Provider(provider) + const { url } = await filesApiClient.getOauth2Provider(provider) return Promise.resolve(url) } catch { return Promise.reject("There was an error logging in") @@ -271,7 +271,7 @@ const ImployApiProvider = ({ apiUrl, withLocalStorage = true, children }: Imploy const { access_token, refresh_token - } = await imployApiClient.postOauth2CodeGithub(code, state) + } = await filesApiClient.postOauth2CodeGithub(code, state) setTokensAndSave(access_token, refresh_token) setReturningUser() return Promise.resolve() @@ -292,7 +292,7 @@ const ImployApiProvider = ({ apiUrl, withLocalStorage = true, children }: Imploy const { access_token, refresh_token - } = await imployApiClient.postOauth2CodeGoogle( + } = await filesApiClient.postOauth2CodeGoogle( code, state, scope, @@ -314,7 +314,7 @@ const ImployApiProvider = ({ apiUrl, withLocalStorage = true, children }: Imploy const { access_token, refresh_token - } = await imployApiClient.postOauth2CodeFacebook(code, state) + } = await filesApiClient.postOauth2CodeFacebook(code, state) setTokensAndSave(access_token, refresh_token) setReturningUser() @@ -328,7 +328,7 @@ const ImployApiProvider = ({ apiUrl, withLocalStorage = true, children }: Imploy setAccessToken(undefined) setRefreshToken(undefined) setDecodedRefreshToken(undefined) - imployApiClient.setToken("") + filesApiClient.setToken("") localStorageRemove(tokenStorageKey) !withLocalStorage && sessionStorageRemove(tokenStorageKey) } @@ -336,14 +336,14 @@ const ImployApiProvider = ({ apiUrl, withLocalStorage = true, children }: Imploy const secureThresholdKeyAccount = async (encryptedKey: string) => { try { if (decodedRefreshToken && refreshToken) { - await imployApiClient.secure({ + await filesApiClient.secure({ encryption_key: encryptedKey }) const { access_token, refresh_token - } = await imployApiClient.getRefreshToken({ + } = await filesApiClient.getRefreshToken({ refresh: refreshToken.token }) @@ -376,9 +376,9 @@ const ImployApiProvider = ({ apiUrl, withLocalStorage = true, children }: Imploy } return ( - {children} - + ) } -const useImployApi = () => { - const context = React.useContext(ImployApiContext) +const useFilesApi = () => { + const context = React.useContext(FilesApiContext) if (context === undefined) { throw new Error("useAuth must be used within a AuthProvider") } return context } -export { ImployApiProvider, useImployApi } +export { FilesApiProvider, useFilesApi } diff --git a/packages/common-contexts/src/FilesApiContext/index.ts b/packages/common-contexts/src/FilesApiContext/index.ts new file mode 100644 index 0000000000..985ca76041 --- /dev/null +++ b/packages/common-contexts/src/FilesApiContext/index.ts @@ -0,0 +1,7 @@ +export { + FilesApiProvider, + useFilesApi, + OAuthProvider +} from "./FilesApiContext" + +export { signMessage } from "./utils" diff --git a/packages/common-contexts/src/ImployApiContext/utils.ts b/packages/common-contexts/src/FilesApiContext/utils.ts similarity index 100% rename from packages/common-contexts/src/ImployApiContext/utils.ts rename to packages/common-contexts/src/FilesApiContext/utils.ts diff --git a/packages/common-contexts/src/ImployApiContext/index.ts b/packages/common-contexts/src/ImployApiContext/index.ts deleted file mode 100644 index c2db455673..0000000000 --- a/packages/common-contexts/src/ImployApiContext/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export { - ImployApiProvider, - useImployApi, - OAuthProvider -} from "./ImployApiContext" - -export { signMessage } from "./utils" diff --git a/packages/common-contexts/src/UserContext/UserContext.tsx b/packages/common-contexts/src/UserContext/UserContext.tsx index a045bd9949..20a0ac3c6c 100644 --- a/packages/common-contexts/src/UserContext/UserContext.tsx +++ b/packages/common-contexts/src/UserContext/UserContext.tsx @@ -1,6 +1,6 @@ import * as React from "react" import { useCallback, useEffect } from "react" -import { useImployApi } from "../ImployApiContext" +import { useFilesApi } from "../FilesApiContext" import { useState } from "react" type UserContextProps = { @@ -8,6 +8,7 @@ type UserContextProps = { } export type Profile = { + userId: string firstName?: string lastName?: string publicAddress?: string @@ -30,15 +31,16 @@ interface IUserContext { const UserContext = React.createContext(undefined) const UserProvider = ({ children }: UserContextProps) => { - const { imployApiClient, isLoggedIn } = useImployApi() + const { filesApiClient, isLoggedIn } = useFilesApi() const [profile, setProfile] = useState(undefined) const refreshProfile = useCallback(async () => { try { - const profileApiData = await imployApiClient.getUser() + const profileApiData = await filesApiClient.getUser() const profileState = { + userId: profileApiData.uuid, firstName: profileApiData.first_name, lastName: profileApiData.last_name, email: profileApiData.email, @@ -50,7 +52,7 @@ const UserProvider = ({ children }: UserContextProps) => { } catch (error) { return Promise.reject("There was an error getting profile.") } - }, [imployApiClient]) + }, [filesApiClient]) useEffect(() => { if (isLoggedIn) { @@ -63,13 +65,14 @@ const UserProvider = ({ children }: UserContextProps) => { if (!profile) return Promise.reject("Profile not initialized") try { - const profileData = await imployApiClient.updateUser({ + const profileData = await filesApiClient.updateUser({ first_name: firstName || "", last_name: lastName || "", email: profile.email || "" }) setProfile({ + ...profile, firstName: profileData.first_name, lastName: profileData.last_name, email: profileData.email, diff --git a/packages/common-contexts/src/index.ts b/packages/common-contexts/src/index.ts index e79060c992..00a833aece 100644 --- a/packages/common-contexts/src/index.ts +++ b/packages/common-contexts/src/index.ts @@ -1,4 +1,4 @@ -export * from "./ImployApiContext" +export * from "./FilesApiContext" export * from "./UserContext" export * from "./BillingContext" export * from "./helpers" diff --git a/packages/files-ui/package.json b/packages/files-ui/package.json index 8b583d9b3f..18eebef60c 100644 --- a/packages/files-ui/package.json +++ b/packages/files-ui/package.json @@ -33,7 +33,7 @@ "react-h5-audio-player": "^3.5.0", "react-hotkeys-hook": "^2.4.0", "react-markdown": "^5.0.3", - "react-pdf": "^5.0.0", + "react-pdf": "5.3.0", "react-scripts": "3.4.4", "react-swipeable": "^6.0.1", "react-toast-notifications": "^2.4.0", diff --git a/packages/files-ui/src/App.tsx b/packages/files-ui/src/App.tsx index 91e46eda4e..6548568ab9 100644 --- a/packages/files-ui/src/App.tsx +++ b/packages/files-ui/src/App.tsx @@ -1,11 +1,11 @@ import React, { useCallback, useEffect } from "react" import { init as initSentry, ErrorBoundary, showReportDialog } from "@sentry/react" import { Web3Provider } from "@chainsafe/web3-context" -import { ImployApiProvider, UserProvider, BillingProvider } from "@chainsafe/common-contexts" +import { FilesApiProvider, UserProvider, BillingProvider } from "@chainsafe/common-contexts" import { ThemeSwitcher } from "@chainsafe/common-theme" import "@chainsafe/common-theme/dist/font-faces.css" import { Button, CssBaseline, Modal, Router, ToasterProvider, Typography } from "@chainsafe/common-components" -import { DriveProvider } from "./Contexts/DriveContext" +import { FilesProvider } from "./Contexts/FilesContext" import FilesRoutes from "./Components/FilesRoutes" import AppWrapper from "./Components/Layouts/AppWrapper" import { useHotjar } from "react-use-hotjar" @@ -110,7 +110,7 @@ const App: React.FC<{}> = () => { checkNetwork={false} cacheWalletSelection={canUseLocalStorage} > - @@ -119,7 +119,7 @@ const App: React.FC<{}> = () => { network={directAuthNetwork} > - + @@ -127,10 +127,10 @@ const App: React.FC<{}> = () => { - + - + diff --git a/packages/files-ui/src/Components/Elements/PasswordForm.tsx b/packages/files-ui/src/Components/Elements/PasswordForm.tsx index f5b56527a4..70d68950c0 100644 --- a/packages/files-ui/src/Components/Elements/PasswordForm.tsx +++ b/packages/files-ui/src/Components/Elements/PasswordForm.tsx @@ -7,7 +7,7 @@ import { createStyles, makeStyles } from "@chainsafe/common-theme" import { CSFTheme } from "../../Themes/types" import zxcvbn from "zxcvbn" import { t } from "@lingui/macro" -import StrengthIndicator from "../Modules/MasterKeySequence/SequenceSlides/StrengthIndicator" +import StrengthIndicator from "./StrengthIndicator" import clsx from "clsx" const useStyles = makeStyles(({ breakpoints, constants }: CSFTheme) => diff --git a/packages/files-ui/src/Components/Modules/MasterKeySequence/SequenceSlides/StrengthIndicator.tsx b/packages/files-ui/src/Components/Elements/StrengthIndicator.tsx similarity index 100% rename from packages/files-ui/src/Components/Modules/MasterKeySequence/SequenceSlides/StrengthIndicator.tsx rename to packages/files-ui/src/Components/Elements/StrengthIndicator.tsx diff --git a/packages/files-ui/src/Components/FilesRoutes.tsx b/packages/files-ui/src/Components/FilesRoutes.tsx index a58f86d8bf..32c1347f66 100644 --- a/packages/files-ui/src/Components/FilesRoutes.tsx +++ b/packages/files-ui/src/Components/FilesRoutes.tsx @@ -2,7 +2,7 @@ import React, { useMemo } from "react" import { Switch, ConditionalRoute } from "@chainsafe/common-components" import LoginPage from "./Pages/LoginPage" import SettingsPage from "./Pages/SettingsPage" -import { useImployApi } from "@chainsafe/common-contexts" +import { useFilesApi } from "@chainsafe/common-contexts" import DrivePage from "./Pages/DrivePage" import SearchPage from "./Pages/SearchPage" import BinPage from "./Pages/BinPage" @@ -30,7 +30,7 @@ export const SETTINGS_PATHS = ["profile", "plan", "security"] as const export type SettingsPath = typeof SETTINGS_PATHS[number] const FilesRoutes = () => { - const { isLoggedIn, secured } = useImployApi() + const { isLoggedIn, secured } = useFilesApi() const { isNewDevice, publicKey, shouldInitializeAccount } = useThresholdKey() const isAuthorized = useMemo(() => isLoggedIn && secured && !!publicKey && !isNewDevice && !shouldInitializeAccount, @@ -45,26 +45,26 @@ const FilesRoutes = () => { redirectPath={ROUTE_LINKS.Landing} /> + { const { desktop } = useThemeSwitcher() const classes = useStyles() - const { isLoggedIn, secured } = useImployApi() + const { isLoggedIn, secured } = useFilesApi() const { publicKey, isNewDevice, shouldInitializeAccount, logout } = useThresholdKey() const { getProfileTitle, removeUser } = useUser() const [searchActive, setSearchActive] = useState(false) diff --git a/packages/files-ui/src/Components/Layouts/AppNav.tsx b/packages/files-ui/src/Components/Layouts/AppNav.tsx index 0dd65365aa..62efe83803 100644 --- a/packages/files-ui/src/Components/Layouts/AppNav.tsx +++ b/packages/files-ui/src/Components/Layouts/AppNav.tsx @@ -1,5 +1,5 @@ -import { useImployApi, useUser } from "@chainsafe/common-contexts" -import { useDrive } from "../../Contexts/DriveContext" +import { useFilesApi, useUser } from "@chainsafe/common-contexts" +import { useFiles } from "../../Contexts/FilesContext" import { createStyles, makeStyles, @@ -212,9 +212,9 @@ const AppNav: React.FC = ({ navOpen, setNavOpen }: IAppNav) => { const { desktop } = useThemeSwitcher() const classes = useStyles() - const { spaceUsed } = useDrive() + const { spaceUsed } = useFiles() - const { isLoggedIn, secured } = useImployApi() + const { isLoggedIn, secured } = useFilesApi() const { publicKey, isNewDevice, shouldInitializeAccount, logout } = useThresholdKey() const { removeUser } = useUser() diff --git a/packages/files-ui/src/Components/Layouts/AppWrapper.tsx b/packages/files-ui/src/Components/Layouts/AppWrapper.tsx index a927e64133..c6a116046c 100644 --- a/packages/files-ui/src/Components/Layouts/AppWrapper.tsx +++ b/packages/files-ui/src/Components/Layouts/AppWrapper.tsx @@ -1,4 +1,4 @@ -import { useImployApi } from "@chainsafe/common-contexts" +import { useFilesApi } from "@chainsafe/common-contexts" import { createStyles, ITheme, makeStyles } from "@chainsafe/common-theme" import React, { useState } from "react" import { ReactNode } from "react" @@ -59,7 +59,7 @@ const useStyles = makeStyles( const AppWrapper: React.FC = ({ children }: IAppWrapper) => { const classes = useStyles() const [navOpen, setNavOpen] = useState(false) - const { isLoggedIn, secured } = useImployApi() + const { isLoggedIn, secured } = useFilesApi() const { publicKey, isNewDevice, shouldInitializeAccount } = useThresholdKey() return ( diff --git a/packages/files-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx b/packages/files-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx index 255eb341dc..113e10314c 100644 --- a/packages/files-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx +++ b/packages/files-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx @@ -1,6 +1,6 @@ import React from "react" import { createStyles, ITheme, makeStyles } from "@chainsafe/common-theme" -import { DownloadProgress } from "../../../Contexts/DriveContext" +import { DownloadProgress } from "../../../Contexts/FilesContext" import { ProgressBar, Typography, diff --git a/packages/files-ui/src/Components/Modules/DownloadProgressModals/index.tsx b/packages/files-ui/src/Components/Modules/DownloadProgressModals/index.tsx index 6327572184..43a0a009e8 100644 --- a/packages/files-ui/src/Components/Modules/DownloadProgressModals/index.tsx +++ b/packages/files-ui/src/Components/Modules/DownloadProgressModals/index.tsx @@ -1,6 +1,6 @@ import React from "react" import { createStyles, ITheme, makeStyles } from "@chainsafe/common-theme" -import { useDrive } from "../../../Contexts/DriveContext" +import { useFiles } from "../../../Contexts/FilesContext" import DownloadBox from "./DownloadBox" const useStyles = makeStyles(({ constants, zIndex, breakpoints }: ITheme) => { @@ -25,7 +25,9 @@ const useStyles = makeStyles(({ constants, zIndex, breakpoints }: ITheme) => { const DownloadProgressModals: React.FC = () => { const classes = useStyles() - const { downloadsInProgress } = useDrive() + const { downloadsInProgress } = useFiles() + + if (downloadsInProgress.length === 0) { return null } return (
diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx index f80c997999..74cce9dd58 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx @@ -1,66 +1,56 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" -import { BucketType, FileSystemItem, useDrive } from "../../../Contexts/DriveContext" -import { IBulkOperations, IFilesBrowserModuleProps } from "./types" -import FilesTableView from "./views/FilesTable.view" +import { FileSystemItem, useFiles } from "../../../Contexts/FilesContext" +import { IBulkOperations, IFileBrowserModuleProps } from "./types" +import FilesList from "./views/FilesList" import DragAndDrop from "../../../Contexts/DnDContext" import { t } from "@lingui/macro" import { CONTENT_TYPES } from "../../../Utils/Constants" import { IFilesTableBrowserProps } from "../../Modules/FileBrowsers/types" -import { guessContentType } from "../../../Utils/contentTypeGuesser" -import { useLocation, useToaster } from "@chainsafe/common-components" +import { useHistory, useLocation, useToaster } from "@chainsafe/common-components" import { extractDrivePath, getPathWithFile } from "../../../Utils/pathUtils" import { ROUTE_LINKS } from "../../FilesRoutes" import { FileBrowserContext } from "../../../Contexts/FileBrowserContext" +import { useFilesApi } from "@chainsafe/common-contexts" +import { parseFileContentResponse } from "../../../Utils/Helpers" -const BinFileBrowser: React.FC = ({ controls = false }: IFilesBrowserModuleProps) => { - const { removeCSFObjects, moveCSFObject, list } = useDrive() +const BinFileBrowser: React.FC = ({ controls = false }: IFileBrowserModuleProps) => { + const { buckets } = useFiles() + const { filesApiClient } = useFilesApi() const { addToastMessage } = useToaster() const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) const [pathContents, setPathContents] = useState([]) - const bucketType: BucketType = "trash" const { pathname } = useLocation() const [currentPath, setCurrentPath] = useState(extractDrivePath(pathname.split("/").slice(1).join("/"))) + const { redirect } = useHistory() + + const bucket = useMemo(() => buckets.find(b => b.type === "trash"), [buckets]) const refreshContents = useCallback( ( showLoading?: boolean ) => { + if (!bucket) return try { showLoading && setLoadingCurrentPath(true) - list({ - path: currentPath, - source: { - type: bucketType - } - }).then((newContents) => { - showLoading && setLoadingCurrentPath(false) - - setPathContents( - newContents.map((fcr) => ({ - ...fcr, - content_type: - fcr.content_type !== "application/octet-stream" - ? fcr.content_type - : guessContentType(fcr.name), - isFolder: - fcr.content_type === "application/chainsafe-files-directory" - })) - ) - }).catch((error) => { - throw error - }) + filesApiClient.getFPSChildList(bucket.id, { path: currentPath }) + .then((newContents) => { + showLoading && setLoadingCurrentPath(false) + setPathContents( + newContents.map((fcr) => parseFileContentResponse(fcr)) + ) + }).catch((error) => { + throw error + }) } catch (error) { console.error(error) showLoading && setLoadingCurrentPath(false) } }, - [bucketType, list, currentPath] + [bucket, currentPath, filesApiClient] ) - useEffect(() => { refreshContents(true) - // eslint-disable-next-line - }, []) + }, [bucket, refreshContents]) useEffect(() => { let binPath = extractDrivePath(pathname) @@ -68,7 +58,11 @@ const BinFileBrowser: React.FC = ({ controls = false } binPath = "/" + binPath } if (binPath !== currentPath) { - setCurrentPath(decodeURI(binPath)) + if (binPath === "/") { + setCurrentPath(binPath) + } else { + setCurrentPath(decodeURIComponent(binPath.slice(0, -1))) + } refreshContents(true) } }, [refreshContents, pathname, currentPath]) @@ -77,16 +71,16 @@ const BinFileBrowser: React.FC = ({ controls = false } const deleteFile = useCallback(async (cid: string) => { const itemToDelete = pathContents.find((i) => i.cid === cid) - if (!itemToDelete) { - console.error("No item found to delete") + if (!itemToDelete || !bucket) { + console.error("Bucket not set or no item found to delete") return } try { - await removeCSFObjects({ + await filesApiClient.removeFPSObjects(bucket.id, { paths: [`${currentPath}${itemToDelete.name}`], source: { - type: bucketType + type: bucket.type } }) refreshContents() @@ -108,9 +102,9 @@ const BinFileBrowser: React.FC = ({ controls = false } }) return Promise.reject() } - }, [addToastMessage, bucketType, currentPath, pathContents, refreshContents, removeCSFObjects]) + }, [addToastMessage, bucket, currentPath, pathContents, refreshContents, filesApiClient]) - const deleteFiles = useCallback(async (cids: string[]) => { + const deleteItems = useCallback(async (cids: string[]) => { await Promise.all( cids.map((cid: string) => deleteFile(cid) @@ -118,50 +112,56 @@ const BinFileBrowser: React.FC = ({ controls = false } refreshContents() }, [deleteFile, refreshContents]) - - const recoverFile = useCallback(async (cid: string) => { - const itemToRestore = pathContents.find((i) => i.cid === cid) - if (!itemToRestore) throw new Error("Not found") - try { - await moveCSFObject({ - path: getPathWithFile("/", itemToRestore.name), - new_path: getPathWithFile("/", itemToRestore.name), - source: { - type: "trash" - }, - destination: { - type: "csf" + const recoverItems = useCallback(async (cids: string[]) => { + if (!bucket) return Promise.reject() + return Promise.all( + cids.map(async (cid: string) => { + const itemToRestore = pathContents.find((i) => i.cid === cid) + if (!itemToRestore) throw new Error("Item to restore not found") + try { + await filesApiClient.moveFPSObject(bucket.id, { + path: getPathWithFile(currentPath, itemToRestore.name), + new_path: getPathWithFile("/", itemToRestore.name), + destination: { + type: "csf" + } + }) + refreshContents() + + const message = `${ + itemToRestore.isFolder ? t`Folder` : t`File` + } ${t`recovered successfully`}` + + addToastMessage({ + message: message, + appearance: "success" + }) + return Promise.resolve() + } catch (error) { + const message = `${t`There was an error recovering this`} ${ + itemToRestore.isFolder ? t`folder` : t`file` + }` + addToastMessage({ + message: message, + appearance: "error" + }) + return Promise.resolve() } - }) - refreshContents() - - const message = `${ - itemToRestore.isFolder ? t`Folder` : t`File` - } ${t`recovered successfully`}` - - addToastMessage({ - message: message, - appearance: "success" - }) - return Promise.resolve() - } catch (error) { - const message = `${t`There was an error recovering this`} ${ - itemToRestore.isFolder ? t`folder` : t`file` - }` - addToastMessage({ - message: message, - appearance: "error" - }) - return Promise.reject() + })) + }, [addToastMessage, pathContents, refreshContents, filesApiClient, bucket, currentPath]) + + const viewFolder = useCallback((cid: string) => { + const fileSystemItem = pathContents.find(f => f.cid === cid) + if (fileSystemItem && fileSystemItem.content_type === CONTENT_TYPES.Directory) { + let urlSafePath + if (currentPath !== "/") { + urlSafePath = `/${currentPath.slice(1).split("/").map(encodeURIComponent).join("/")}` + } else { + urlSafePath = "" + } + redirect(ROUTE_LINKS.Bin(`${urlSafePath}/${encodeURIComponent(`${fileSystemItem.name}`)}`)) } - }, [addToastMessage, moveCSFObject, pathContents, refreshContents]) - - const recoverFiles = useCallback(async (cids: string[]) => { - return Promise.all( - cids.map((cid: string) => - recoverFile(cid) - )) - }, [recoverFile]) + }, [currentPath, pathContents, redirect]) const bulkOperations: IBulkOperations = useMemo(() => ({ [CONTENT_TYPES.Directory]: [], @@ -175,10 +175,10 @@ const BinFileBrowser: React.FC = ({ controls = false } return ( = ({ controls = false } sourceFiles: pathContents, heading: t`Bin`, controls, - bucketType, itemOperations, - bulkOperations + bulkOperations, + viewFolder }}> - + ) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx index 06ddbc7f0c..1fcdfba736 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx @@ -1,77 +1,56 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" import { Crumb, useToaster, useHistory, useLocation } from "@chainsafe/common-components" -import { useDrive, FileSystemItem, BucketType } from "../../../Contexts/DriveContext" -import { extractDrivePath, getArrayOfPaths, getPathFromArray, getPathWithFile } from "../../../Utils/pathUtils" -import { IBulkOperations, IFilesBrowserModuleProps, IFilesTableBrowserProps } from "./types" -import FilesTableView from "./views/FilesTable.view" +import { useFiles, FileSystemItem } from "../../../Contexts/FilesContext" +import { extractDrivePath, getArrayOfPaths, getURISafePathFromArray, getPathWithFile } from "../../../Utils/pathUtils" +import { IBulkOperations, IFileBrowserModuleProps, IFilesTableBrowserProps } from "./types" +import FilesList from "./views/FilesList" import { CONTENT_TYPES } from "../../../Utils/Constants" import DragAndDrop from "../../../Contexts/DnDContext" import { t } from "@lingui/macro" -import { guessContentType } from "../../../Utils/contentTypeGuesser" import { ROUTE_LINKS } from "../../FilesRoutes" import dayjs from "dayjs" -import { useUser } from "@chainsafe/common-contexts" +import { useUser, useFilesApi } from "@chainsafe/common-contexts" import { useLocalStorage } from "@chainsafe/browser-storage-hooks" import { DISMISSED_SURVEY_KEY } from "../../SurveyBanner" import { FileBrowserContext } from "../../../Contexts/FileBrowserContext" +import { parseFileContentResponse } from "../../../Utils/Helpers" -const CSFFileBrowser: React.FC = () => { +const CSFFileBrowser: React.FC = () => { const { downloadFile, - renameFile, - moveFile, uploadFiles, - moveCSFObject, uploadsInProgress, - list - } = useDrive() + buckets + } = useFiles() + const { filesApiClient } = useFilesApi() const { addToastMessage } = useToaster() const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) const [pathContents, setPathContents] = useState([]) - const bucketType: BucketType = "csf" const { redirect } = useHistory() const { pathname } = useLocation() const [currentPath, setCurrentPath] = useState(extractDrivePath(pathname.split("/").slice(1).join("/"))) - const refreshContents = useCallback( - ( - path: string, - showLoading?: boolean - ) => { - try { - showLoading && setLoadingCurrentPath(true) - list({ - path, - source: { - type: bucketType - } - }).then((newContents) => { - showLoading && setLoadingCurrentPath(false) - // Remove this when the API returns dates - setPathContents( - newContents.map((fcr) => ({ - ...fcr, - content_type: - fcr.content_type !== "application/octet-stream" - ? fcr.content_type - : guessContentType(fcr.name), - isFolder: - fcr.content_type === "application/chainsafe-files-directory" - })) - ) - }).catch(error => { - throw error - }) - } catch (error) { - console.error(error) + const bucket = useMemo(() => buckets.find(b => b.type === "csf"), [buckets]) + + const refreshContents = useCallback((showLoading?: boolean) => { + if (!bucket) return + showLoading && setLoadingCurrentPath(true) + filesApiClient.getFPSChildList(bucket.id, { path: currentPath }) + .then((newContents) => { showLoading && setLoadingCurrentPath(false) - } - }, - [bucketType, list] - ) + + setPathContents( + newContents.map((fcr) => parseFileContentResponse(fcr)) + ) + }).catch(error => { + console.error(error) + }).finally(() => showLoading && setLoadingCurrentPath(false)) + }, [bucket, filesApiClient, currentPath]) + const { localStorageGet, localStorageSet } = useLocalStorage() const { profile } = useUser() + const showSurvey = localStorageGet(DISMISSED_SURVEY_KEY) === "false" const olderThanOneWeek = useMemo( @@ -89,9 +68,8 @@ const CSFFileBrowser: React.FC = () => { }, [localStorageGet, localStorageSet]) useEffect(() => { - refreshContents(currentPath, true) - // eslint-disable-next-line - }, []) + refreshContents(true) + }, [bucket, refreshContents]) useEffect(() => { let drivePath = extractDrivePath(pathname) @@ -99,93 +77,101 @@ const CSFFileBrowser: React.FC = () => { drivePath = "/" + drivePath } if (drivePath !== currentPath) { - setCurrentPath(decodeURI(drivePath)) - refreshContents(decodeURI(drivePath), true) - } - }, [refreshContents, pathname, currentPath]) - - // From drive - const moveFileToTrash = useCallback(async (cid: string) => { - const itemToDelete = pathContents.find((i) => i.cid === cid) + if (drivePath === "/") { + setCurrentPath(drivePath) + } else { + setCurrentPath(decodeURIComponent(drivePath.slice(0, -1))) + } - if (!itemToDelete) { - console.error("No item found to move to the trash") - return + refreshContents(true) } + }, [refreshContents, pathname, currentPath]) - try { - await moveCSFObject({ - path: getPathWithFile(currentPath, itemToDelete.name), - new_path: getPathWithFile("/", itemToDelete.name), - destination: { - type: "trash" + const moveItemsToBin = useCallback(async (cids: string[]) => { + if (!bucket) return + await Promise.all( + cids.map(async (cid: string) => { + const itemToDelete = pathContents.find((i) => i.cid === cid) + if (!itemToDelete) { + console.error("No item found to move to the trash") + return } - }) - refreshContents(currentPath) - const message = `${ - itemToDelete.isFolder ? t`Folder` : t`File` - } ${t`deleted successfully`}` - addToastMessage({ - message: message, - appearance: "success" - }) - return Promise.resolve() - } catch (error) { - const message = `${t`There was an error deleting this`} ${ - itemToDelete.isFolder ? t`folder` : t`file` - }` - addToastMessage({ - message: message, - appearance: "error" - }) - return Promise.reject() - } - }, [addToastMessage, currentPath, pathContents, refreshContents, moveCSFObject]) - const moveFilesToTrash = useCallback(async (cids: string[]) => { - await Promise.all( - cids.map((cid: string) => - moveFileToTrash(cid) - )) - await refreshContents(currentPath) - }, [moveFileToTrash, refreshContents, currentPath]) + try { + await filesApiClient.moveFPSObject(bucket.id, { + path: getPathWithFile(currentPath, itemToDelete.name), + new_path: getPathWithFile("/", itemToDelete.name), + destination: { + type: "trash" + } + }) + const message = `${ + itemToDelete.isFolder ? t`Folder` : t`File` + } ${t`deleted successfully`}` + addToastMessage({ + message: message, + appearance: "success" + }) + return Promise.resolve() + } catch (error) { + const message = `${t`There was an error deleting this`} ${ + itemToDelete.isFolder ? t`folder` : t`file` + }` + addToastMessage({ + message: message, + appearance: "error" + }) + return Promise.reject() + }} + )).finally(refreshContents) + }, [addToastMessage, currentPath, pathContents, refreshContents, filesApiClient, bucket]) // Rename - const handleRename = useCallback(async (path: string, newPath: string) => { - // TODO set loading - await renameFile({ path: path, new_path: newPath }) - await refreshContents(currentPath) - }, [renameFile, currentPath, refreshContents]) - - const handleMove = useCallback(async (path: string, new_path: string) => { - // TODO set loading - await moveFile({ - path: path, - new_path: new_path - }) - await refreshContents(currentPath) - }, [moveFile, refreshContents, currentPath]) + const renameItem = useCallback(async (cid: string, newName: string) => { + const itemToRename = pathContents.find(i => i.cid === cid) + if (!bucket || !itemToRename) return + + filesApiClient.moveFPSObject(bucket.id, { + path: getPathWithFile(currentPath, itemToRename.name), + new_path: getPathWithFile(currentPath, newName) }).then(() => refreshContents()) + .catch(console.error) + }, [refreshContents, filesApiClient, bucket, currentPath, pathContents]) + + const moveItems = useCallback(async (cids: string[], newPath: string) => { + if (!bucket) return + await Promise.all( + cids.map(async (cid: string) => { + const itemToMove = pathContents.find(i => i.cid === cid) + if (!bucket || !itemToMove) return + await filesApiClient.moveFPSObject(bucket.id, { + path: getPathWithFile(currentPath, itemToMove.name), + new_path: getPathWithFile(newPath, itemToMove.name) + }) + })).finally(refreshContents) + }, [refreshContents, filesApiClient, bucket, currentPath, pathContents]) const handleDownload = useCallback(async (cid: string) => { - const target = pathContents.find(item => item.cid === cid) - if (!target) return - - await downloadFile(target, currentPath, bucketType) - }, [pathContents, downloadFile, currentPath, bucketType]) + const itemToDownload = pathContents.find(item => item.cid === cid) + if (!itemToDownload || !bucket) return + downloadFile(bucket.id, itemToDownload, currentPath) + }, [pathContents, downloadFile, currentPath, bucket]) // Breadcrumbs/paths const arrayOfPaths = useMemo(() => getArrayOfPaths(currentPath), [currentPath]) const crumbs: Crumb[] = useMemo(() => arrayOfPaths.map((path, index) => ({ - text: path, - onClick: () => + text: decodeURIComponent(path), + onClick: () => { + redirect( - ROUTE_LINKS.Drive(encodeURI(encodeURI(getPathFromArray(arrayOfPaths.slice(0, index + 1))))) + ROUTE_LINKS.Drive(getURISafePathFromArray(arrayOfPaths.slice(0, index + 1))) ) + } })), [arrayOfPaths, redirect]) const handleUploadOnDrop = useCallback(async (files: File[], fileItems: DataTransferItemList, path: string) => { + if (!bucket) return let hasFolder = false for (let i = 0; i < files.length; i++) { if (fileItems[i].webkitGetAsEntry().isDirectory) { @@ -198,18 +184,20 @@ const CSFFileBrowser: React.FC = () => { appearance: "error" }) } else { - await uploadFiles(files, path) - // refresh contents - // using reducer because user may navigate to other paths - // need to check currentPath and upload path is same - refreshContents(currentPath) + uploadFiles(bucket.id, files, path).then(() => refreshContents()).catch(console.error) } - }, [addToastMessage, uploadFiles, currentPath, refreshContents]) + }, [addToastMessage, uploadFiles, bucket, refreshContents]) const viewFolder = useCallback((cid: string) => { const fileSystemItem = pathContents.find(f => f.cid === cid) if (fileSystemItem && fileSystemItem.content_type === CONTENT_TYPES.Directory) { - redirect(ROUTE_LINKS.Drive(`${currentPath}${fileSystemItem.name}`)) + let urlSafePath + if (currentPath !== "/") { + urlSafePath = `/${currentPath.slice(1).split("/").map(encodeURIComponent).join("/")}` + } else { + urlSafePath = "" + } + redirect(ROUTE_LINKS.Drive(`${urlSafePath}/${encodeURIComponent(`${fileSystemItem.name}`)}`)) } }, [currentPath, pathContents, redirect]) @@ -230,15 +218,16 @@ const CSFFileBrowser: React.FC = () => { return ( refreshContents(currentPath), - deleteFiles: moveFilesToTrash, - downloadFile:handleDownload, - handleMove, - handleRename, + refreshContents, + deleteItems: moveItemsToBin, + downloadFile: handleDownload, + moveItems, + renameItem: renameItem, viewFolder, handleUploadOnDrop, uploadsInProgress, @@ -246,14 +235,13 @@ const CSFFileBrowser: React.FC = () => { showUploadsInTable: true, sourceFiles: pathContents, heading: t`My Files`, - bucketType, controls: true, allowDropUpload: true, itemOperations, withSurvey: showSurvey && olderThanOneWeek }}> - + ) diff --git a/packages/files-ui/src/Components/Modules/CreateFolderModule.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CreateFolderModal.tsx similarity index 87% rename from packages/files-ui/src/Components/Modules/CreateFolderModule.tsx rename to packages/files-ui/src/Components/Modules/FileBrowsers/CreateFolderModal.tsx index a3d16d4e2f..7843d76297 100644 --- a/packages/files-ui/src/Components/Modules/CreateFolderModule.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CreateFolderModal.tsx @@ -4,7 +4,6 @@ import { Grid, Typography } from "@chainsafe/common-components" -import { useDrive } from "../../Contexts/DriveContext" import * as yup from "yup" import { createStyles, @@ -13,11 +12,12 @@ import { } from "@chainsafe/common-theme" import React, { useRef, useEffect, useState } from "react" import { Formik, Form } from "formik" -import CustomModal from "../Elements/CustomModal" -import CustomButton from "../Elements/CustomButton" +import CustomModal from "../../Elements/CustomModal" +import CustomButton from "../../Elements/CustomButton" import { Trans } from "@lingui/macro" -import { CSFTheme } from "../../Themes/types" -import { useFileBrowser } from "../../Contexts/FileBrowserContext" +import { CSFTheme } from "../../../Themes/types" +import { useFileBrowser } from "../../../Contexts/FileBrowserContext" +import { useFilesApi } from "@chainsafe/common-contexts" const useStyles = makeStyles( ({ breakpoints, constants, typography, zIndex }: CSFTheme) => { @@ -70,20 +70,19 @@ const useStyles = makeStyles( } ) -interface ICreateFolderModuleProps { +interface ICreateFolderModalProps { modalOpen: boolean close: () => void } -const CreateFolderModule: React.FC = ({ +const CreateFolderModal: React.FC = ({ modalOpen, close -}: ICreateFolderModuleProps) => { +}: ICreateFolderModalProps) => { const classes = useStyles() - const { currentPath, refreshContents } = useFileBrowser() - const { createFolder } = useDrive() + const { filesApiClient } = useFilesApi() + const { currentPath, refreshContents, bucket } = useFileBrowser() const [creatingFolder, setCreatingFolder] = useState(false) - const desktop = useMediaQuery("md") const inputRef = useRef(null) @@ -121,11 +120,12 @@ const CreateFolderModule: React.FC = ({ validationSchema={folderNameValidator} validateOnChange={false} onSubmit={async (values, helpers) => { + if (!bucket) return helpers.setSubmitting(true) try { setCreatingFolder(true) - await createFolder({ path: currentPath + values.name }) - refreshContents && refreshContents() + await filesApiClient.addFPSDirectory(bucket.id, { path: `${currentPath}/${values.name}` }) + refreshContents && await refreshContents() setCreatingFolder(false) helpers.resetForm() close() @@ -203,4 +203,4 @@ const CreateFolderModule: React.FC = ({ ) } -export default CreateFolderModule +export default CreateFolderModal diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/FileInfoModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/FileInfoModal.tsx index 08a319ed75..4f70115ad7 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/FileInfoModal.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/FileInfoModal.tsx @@ -7,7 +7,7 @@ import React, { useState, useEffect } from "react" import CustomModal from "../../Elements/CustomModal" import CustomButton from "../../Elements/CustomButton" import { Trans } from "@lingui/macro" -import { useDrive, FileFullInfo } from "../../../Contexts/DriveContext" +import { FileFullInfo } from "../../../Contexts/FilesContext" import { Button, formatBytes, @@ -18,6 +18,8 @@ import { import clsx from "clsx" import { CSFTheme } from "../../../Themes/types" import dayjs from "dayjs" +import { useFilesApi } from "@chainsafe/common-contexts" +import { useFileBrowser } from "../../../Contexts/FileBrowserContext" const useStyles = makeStyles( ({ breakpoints, constants, palette, typography, zIndex, animation }: CSFTheme) => { @@ -160,22 +162,23 @@ const FileInfoModal: React.FC = ({ close }: IFileInfoModuleProps) => { const classes = useStyles() - - const { getFileInfo } = useDrive() + const { filesApiClient } = useFilesApi() const [loadingFileInfo, setLoadingInfo] = useState(false) const [fullFileInfo, setFullFullInfo] = useState( undefined ) + const { bucket } = useFileBrowser() useEffect(() => { const getFullFileInfo = async () => { - if (fileInfoPath) { + if (fileInfoPath && bucket) { try { setLoadingInfo(true) - const fullFileResponse = await getFileInfo(fileInfoPath) + const fullFileResponse = await filesApiClient.getFPSFileInfo(bucket.id, { path: fileInfoPath }) setFullFullInfo(fullFileResponse) setLoadingInfo(false) - } catch { + } catch (e){ + console.error(e) setLoadingInfo(false) } } diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/MoveFileModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/MoveFileModal.tsx index 394a361908..db0f6432f1 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/MoveFileModal.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/MoveFileModal.tsx @@ -3,11 +3,11 @@ import React, { useState, useEffect, useCallback } from "react" import CustomModal from "../../Elements/CustomModal" import CustomButton from "../../Elements/CustomButton" import { Trans } from "@lingui/macro" -import { useDrive, DirectoryContentResponse, FileSystemItem } from "../../../Contexts/DriveContext" +import { DirectoryContentResponse, FileSystemItem } from "../../../Contexts/FilesContext" import { Button, FolderIcon, Grid, ITreeNodeProps, ScrollbarWrapper, TreeView, Typography } from "@chainsafe/common-components" -import { getPathWithFile } from "../../../Utils/pathUtils" import { CSFTheme } from "../../../Themes/types" import { useFileBrowser } from "../../../Contexts/FileBrowserContext" +import { useFilesApi } from "@chainsafe/common-contexts" const useStyles = makeStyles( ({ breakpoints, constants, palette, typography, zIndex }: CSFTheme) => { @@ -72,11 +72,12 @@ interface IMoveFileModuleProps { const MoveFileModule = ({ filesToMove, modalOpen, onClose, onCancel }: IMoveFileModuleProps) => { const classes = useStyles() - const { getFolderTree, moveFiles } = useDrive() + const { filesApiClient } = useFilesApi() + const { moveItems } = useFileBrowser() const [movingFile, setMovingFile] = useState(false) const [movePath, setMovePath] = useState(undefined) const [folderTree, setFolderTree] = useState([]) - const { currentPath, refreshContents } = useFileBrowser() + const { refreshContents } = useFileBrowser() const mapFolderTree = useCallback( (folderTreeEntries: DirectoryContentResponse[]): ITreeNodeProps[] => { @@ -91,7 +92,8 @@ const MoveFileModule = ({ filesToMove, modalOpen, onClose, onCancel }: IMoveFile ) const getFolderTreeData = useCallback(async () => { - getFolderTree().then((newFolderTree) => { + // TODO: Update this when the getBucketTree method is available on the API + filesApiClient.getCSFTree().then((newFolderTree) => { if (newFolderTree.entries) { const folderTreeNodes = [ { @@ -107,7 +109,7 @@ const MoveFileModule = ({ filesToMove, modalOpen, onClose, onCancel }: IMoveFile setFolderTree([]) } }).catch(console.error) - }, [getFolderTree, mapFolderTree]) + }, [filesApiClient, mapFolderTree]) useEffect(() => { if (modalOpen) { @@ -121,12 +123,7 @@ const MoveFileModule = ({ filesToMove, modalOpen, onClose, onCancel }: IMoveFile if (movePath) { setMovingFile(true) - moveFiles( - filesToMove.map((file) => ({ - path: `${currentPath}${file.name}`, - new_path: getPathWithFile(movePath, file.name) - })) - ) + moveItems && moveItems(filesToMove.map(f => f.cid), movePath) .then(() => { refreshContents && refreshContents() }) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx index 0237f14510..6f4b4f1e62 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx @@ -1,48 +1,32 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" -import { BucketType, FileSystemItem, SearchEntry, useDrive } from "../../../Contexts/DriveContext" -import { IFilesBrowserModuleProps, IFilesTableBrowserProps } from "./types" -import FilesTableView from "./views/FilesTable.view" +import { FileSystemItem, SearchEntry, useFiles } from "../../../Contexts/FilesContext" +import { IFileBrowserModuleProps, IFilesTableBrowserProps } from "./types" +import FilesList from "./views/FilesList" import { CONTENT_TYPES } from "../../../Utils/Constants" import DragAndDrop from "../../../Contexts/DnDContext" import { useHistory, useLocation, useToaster } from "@chainsafe/common-components" import { getParentPathFromFilePath } from "../../../Utils/pathUtils" import { ROUTE_LINKS } from "../../FilesRoutes" import { t } from "@lingui/macro" -import { SearchParams } from "../SearchModule" import { FileBrowserContext } from "../../../Contexts/FileBrowserContext" +import { useFilesApi } from "@chainsafe/common-contexts" -const SearchFileBrowser: React.FC = ({ controls = false }: IFilesBrowserModuleProps) => { +const SearchFileBrowser: React.FC = ({ controls = false }: IFileBrowserModuleProps) => { const { pathname } = useLocation() + const { filesApiClient } = useFilesApi() + const { buckets } = useFiles() const [searchTerm, setSearchTerm] = useState(pathname.split("/").slice(2)[0]) const { redirect } = useHistory() - const { listBuckets, searchFiles } = useDrive() - - const [bucketType] = useState("csf") - const [currentSearchBucket, setCurrentSearchBucket] = useState() const { addToastMessage } = useToaster() + const bucket = useMemo(() => buckets.find(b => b.type === "csf"), [buckets]) + const getSearchResults = useCallback(async (searchString: string) => { try { - if (!searchString) return [] - let bucketId - if ( - currentSearchBucket && - currentSearchBucket.bucketType === bucketType - ) { - // we have the bucket id - bucketId = currentSearchBucket.bucketId - } else { - // fetch bucket id - const results = await listBuckets(bucketType) - const bucket1 = results[0] - setCurrentSearchBucket({ - bucketType, - bucketId: bucket1.id - }) - bucketId = bucket1.id - } - const results = await searchFiles(bucketId || "", searchString) + if (!searchString || !bucket) return [] + + const results = await filesApiClient.searchFiles({ bucket_id: bucket.id, query: searchString }) return results } catch (err) { addToastMessage({ @@ -51,13 +35,13 @@ const SearchFileBrowser: React.FC = ({ controls = fals }) return Promise.reject(err) } - }, [addToastMessage, bucketType, currentSearchBucket, listBuckets, searchFiles]) + }, [addToastMessage, bucket, filesApiClient]) useEffect(() => { - const drivePath = pathname.split("/").slice(2)[0] + const searchPath = pathname.split("/").slice(2)[0] - setSearchTerm(decodeURI(drivePath)) - getSearchResults(decodeURI(drivePath)) + setSearchTerm(decodeURI(searchPath)) + getSearchResults(decodeURI(searchPath)) }, [getSearchResults, pathname]) const [loadingSearchResults, setLoadingSearchResults] = useState(true) @@ -129,11 +113,10 @@ const SearchFileBrowser: React.FC = ({ controls = fals controls, itemOperations, isSearch: true, - bucketType, getPath }}> - + ) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/types.ts b/packages/files-ui/src/Components/Modules/FileBrowsers/types.ts index 1bbe867a16..48bb82c8a1 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/types.ts +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/types.ts @@ -1,5 +1,5 @@ import { Crumb } from "@chainsafe/common-components" -import { BucketType, FileSystemItem, UploadProgress } from "../../../Contexts/DriveContext" +import { BucketType, FileSystemItem, UploadProgress } from "../../../Contexts/FilesContext" export type FileOperation = | "rename" @@ -14,7 +14,7 @@ export type FileOperation = export type BrowserView = "grid" | "table" -export interface IFilesBrowserModuleProps { +export interface IFileBrowserModuleProps { heading?: string // TODO: once pagination & unique content requests are present, this might change to a passed in function controls?: boolean @@ -25,7 +25,7 @@ export interface IBulkOperations { } export interface IFilesTableBrowserProps - extends Omit { + extends Omit { itemOperations: {[contentType: string]: FileOperation[]} bulkOperations?: IBulkOperations diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/DragPreviewLayer.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/DragPreviewLayer.tsx index 95722994b4..367769c0ec 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/DragPreviewLayer.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/DragPreviewLayer.tsx @@ -3,7 +3,7 @@ import { makeStyles } from "@chainsafe/common-theme" import clsx from "clsx" import React from "react" import { useDragLayer, XYCoord } from "react-dnd" -import { FileSystemItem } from "../../../../Contexts/DriveContext" +import { FileSystemItem } from "../../../../Contexts/FilesContext" import { CSFTheme } from "../../../../Themes/types" import { DragTypes } from "../DragConstants" import { BrowserView } from "../types" diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemGridItem.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemGridItem.tsx index de4a0b4402..1921947b75 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemGridItem.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemGridItem.tsx @@ -9,7 +9,7 @@ import { MoreIcon } from "@chainsafe/common-components" import { CSFTheme } from "../../../../../Themes/types" -import { FileSystemItem } from "../../../../../Contexts/DriveContext" +import { FileSystemItem } from "../../../../../Contexts/FilesContext" import { ConnectDragPreview } from "react-dnd" import { Form, Formik } from "formik" @@ -149,7 +149,6 @@ const FileSystemGridItem = React.forwardRef( renameSchema, setEditing, handleRename, - currentPath, menuItems, resetSelectedFiles, preview @@ -211,11 +210,11 @@ const FileSystemGridItem = React.forwardRef( }} validationSchema={renameSchema} onSubmit={(values) => { - handleRename && handleRename( - `${currentPath}${name}`, - `${currentPath}${values.fileName}` - ) - setEditing(undefined) + handleRename && + handleRename( + file.cid, + values.fileName + ) }} >
diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemItem.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemItem.tsx index b40892d13b..5149a3fe4a 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemItem.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemItem.tsx @@ -16,9 +16,7 @@ import { ExportSvg, ShareAltSvg, ExclamationCircleInverseSvg, - ZoomInSvg, - useHistory -} from "@chainsafe/common-components" + ZoomInSvg } from "@chainsafe/common-components" import { makeStyles, createStyles, useDoubleClick, useThemeSwitcher } from "@chainsafe/common-theme" import { Formik, Form } from "formik" import CustomModal from "../../../../Elements/CustomModal" @@ -30,7 +28,7 @@ import { BrowserView, FileOperation } from "../../types" import { CSFTheme } from "../../../../../Themes/types" import FileItemTableItem from "./FileSystemTableItem" import FileItemGridItem from "./FileSystemGridItem" -import { FileSystemItem } from "../../../../../Contexts/DriveContext" +import { FileSystemItem as FileSystemItemType } from "../../../../../Contexts/FilesContext" import { useFileBrowser } from "../../../../../Contexts/FileBrowserContext" const useStyles = makeStyles(({ breakpoints, constants }: CSFTheme) => { @@ -100,18 +98,18 @@ const useStyles = makeStyles(({ breakpoints, constants }: CSFTheme) => { }) }) -interface IFileSystemItemRowProps { +interface IFileSystemItemProps { index: number - file: FileSystemItem - files: FileSystemItem[] + file: FileSystemItemType + files: FileSystemItemType[] selected: string[] handleSelectCid(selectedCid: string): void handleAddToSelectedCids(selectedCid: string): void editing: string | undefined setEditing(editing: string | undefined): void renameSchema: any - handleRename?: (path: string, newPath: string) => Promise - handleMove?: (path: string, newPath: string) => Promise + handleRename?: (cid: string, newName: string) => Promise + handleMove?: (cid: string, newPath: string) => Promise deleteFile?: () => void recoverFile?: (cid: string) => void viewFolder?: (cid: string) => void @@ -123,7 +121,7 @@ interface IFileSystemItemRowProps { browserView: BrowserView } -const FileSystemItemRow = ({ +const FileSystemItem = ({ file, files, selected, @@ -142,8 +140,8 @@ const FileSystemItemRow = ({ itemOperations, browserView, resetSelectedFiles -}: IFileSystemItemRowProps) => { - const { downloadFile, currentPath, handleUploadOnDrop, moduleRootPath, handleMove } = useFileBrowser() +}: IFileSystemItemProps) => { + const { downloadFile, currentPath, handleUploadOnDrop, moveItems } = useFileBrowser() const { cid, name, isFolder, content_type } = file let Icon if (isFolder) { @@ -159,7 +157,6 @@ const FileSystemItemRow = ({ const { desktop } = useThemeSwitcher() const classes = useStyles() - const { redirect } = useHistory() const allMenuItems: Record = { rename: { @@ -294,14 +291,7 @@ const FileSystemItemRow = ({ accept: DragTypes.MOVABLE_FILE, canDrop: () => isFolder, drop: (item: {ids: string[]}) => { - item.ids.forEach((cid) => { - const fileToMove = files.find(f => f.cid === cid) - handleMove && fileToMove && - handleMove( - `${currentPath}${fileToMove.name}`, - `${currentPath}${name}/${fileToMove.name}` - ) - }) + moveItems && moveItems(item.ids, `${currentPath}${name}/`) }, collect: (monitor) => ({ isOverMove: monitor.isOver() @@ -329,16 +319,6 @@ const FileSystemItemRow = ({ dragMoveRef(fileOrFolderRef) } - const onFolderNavigation = useCallback(() => { - resetSelectedFiles() - if (!moduleRootPath) { - console.debug("Module root path not set") - return - } - const newPath = `${moduleRootPath}${currentPath}${encodeURI(name)}` - redirect(newPath) - }, [currentPath, name, redirect, moduleRootPath, resetSelectedFiles]) - const onFilePreview = useCallback(() => { setPreviewFileIndex(files?.indexOf(file)) }, [file, files, setPreviewFileIndex]) @@ -355,13 +335,13 @@ const FileSystemItemRow = ({ } else { // on mobile if (isFolder) { - onFolderNavigation() + viewFolder && viewFolder(file.cid) } else { onFilePreview() } } }, - [cid, handleSelectCid, handleAddToSelectedCids, desktop, isFolder, onFolderNavigation, onFilePreview] + [cid, handleSelectCid, handleAddToSelectedCids, desktop, isFolder, viewFolder, file, onFilePreview] ) const onDoubleClick = useCallback( @@ -369,7 +349,7 @@ const FileSystemItemRow = ({ if (desktop) { // on desktop if (isFolder) { - onFolderNavigation() + viewFolder && viewFolder(file.cid) } else { onFilePreview() } @@ -378,7 +358,7 @@ const FileSystemItemRow = ({ return } }, - [desktop, onFolderNavigation, onFilePreview, isFolder] + [desktop, viewFolder, file, onFilePreview, isFolder] ) const { click } = useDoubleClick(onSingleClick, onDoubleClick) @@ -435,10 +415,9 @@ const FileSystemItemRow = ({ onSubmit={(values) => { handleRename && handleRename( - `${currentPath}${name}`, - `${currentPath}${values.fileName}` + file.cid, + values.fileName ) - setEditing(undefined) }} > @@ -488,4 +467,4 @@ const FileSystemItemRow = ({ ) } -export default FileSystemItemRow +export default FileSystemItem diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemTableItem.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemTableItem.tsx index 51889308e5..601d5e8f2b 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemTableItem.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemTableItem.tsx @@ -17,7 +17,7 @@ import { } from "@chainsafe/common-components" import { CSFTheme } from "../../../../../Themes/types" import dayjs from "dayjs" -import { FileSystemItem } from "../../../../../Contexts/DriveContext" +import { FileSystemItem } from "../../../../../Contexts/FilesContext" import { ConnectDragPreview } from "react-dnd" import { Form, Formik } from "formik" @@ -138,7 +138,6 @@ const FileSystemTableItem = React.forwardRef( renameSchema, setEditing, handleRename, - currentPath, menuItems }: IFileSystemTableItemProps, forwardedRef: any) => { const classes = useStyles() @@ -184,11 +183,10 @@ const FileSystemTableItem = React.forwardRef( validationSchema={renameSchema} onSubmit={(values) => { handleRename && - handleRename( - `${currentPath}${name}`, - `${currentPath}${values.fileName}` - ) - setEditing(undefined) + handleRename( + file.cid, + values.fileName + ) }} > diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx similarity index 96% rename from packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx rename to packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx index 9a92188e0d..7e3cb87c9b 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx @@ -33,12 +33,12 @@ import { plural, t, Trans } from "@lingui/macro" import { NativeTypes } from "react-dnd-html5-backend" import { useDrop } from "react-dnd" import { BrowserView, FileOperation } from "../types" -import { FileSystemItem } from "../../../../Contexts/DriveContext" -import FileSystemItemRow from "./FileSystemItem/FileSystemItem" +import { FileSystemItem as FileSystemItemType } from "../../../../Contexts/FilesContext" +import FileSystemItem from "./FileSystemItem/FileSystemItem" import FilePreviewModal from "../../FilePreviewModal" import UploadProgressModals from "../../UploadProgressModals" import DownloadProgressModals from "../../DownloadProgressModals" -import CreateFolderModule from "../../CreateFolderModule" +import CreateFolderModal from "../CreateFolderModal" import UploadFileModule from "../../UploadFileModule" import MoveFileModule from "../MoveFileModal" import FileInfoModal from "../FileInfoModal" @@ -274,10 +274,10 @@ const useStyles = makeStyles( ) // Sorting -const sortFoldersFirst = (a: FileSystemItem, b: FileSystemItem) => +const sortFoldersFirst = (a: FileSystemItemType, b: FileSystemItemType) => a.isFolder && a.content_type !== b.content_type ? -1 : 1 -const FilesTableView = () => { +const FilesList = () => { const { themeKey, desktop } = useThemeSwitcher() const { @@ -287,9 +287,9 @@ const FilesTableView = () => { handleUploadOnDrop, bulkOperations, crumbs, - handleRename, - deleteFiles, - recoverFiles, + renameItem: handleRename, + deleteItems: deleteFiles, + recoverItems, viewFolder, currentPath, refreshContents, @@ -300,9 +300,9 @@ const FilesTableView = () => { itemOperations, getPath, moduleRootPath, - bucketType, isSearch, - withSurvey + withSurvey, + bucket } = useFileBrowser() const classes = useStyles({ themeKey }) const [editing, setEditing] = useState() @@ -314,7 +314,7 @@ const FilesTableView = () => { const { selectedLocale } = useLanguageContext() const { redirect } = useHistory() - const items: FileSystemItem[] = useMemo(() => { + const items: FileSystemItemType[] = useMemo(() => { let temp = [] switch (column) { @@ -347,9 +347,9 @@ const FilesTableView = () => { const files = useMemo(() => items.filter((i) => !i.isFolder), [items]) - const selectedFiles = useMemo( - () => files.filter((file) => selectedCids.includes(file.cid)), - [files, selectedCids] + const selectedItems = useMemo( + () => items.filter((file) => selectedCids.includes(file.cid)), + [selectedCids, items] ) const handleSortToggle = ( @@ -417,7 +417,7 @@ const FilesTableView = () => { if (selectedCids.length === items.length) { setSelectedCids([]) } else { - setSelectedCids([...items.map((file: FileSystemItem) => file.cid)]) + setSelectedCids([...items.map((file: FileSystemItemType) => file.cid)]) } }, [setSelectedCids, items, selectedCids]) @@ -540,16 +540,16 @@ const FilesTableView = () => { const handleRecoverFiles = useCallback((e: React.MouseEvent) => { e.preventDefault() e.stopPropagation() - if (!recoverFiles) return + if (!recoverItems) return setIsRecoveringFiles(true) - recoverFiles(selectedCids) + recoverItems(selectedCids) .catch(console.error) .finally(() => { setIsRecoveringFiles(false) setSelectedCids([]) }) - }, [recoverFiles, selectedCids]) + }, [recoverItems, selectedCids]) const getItemOperations = useCallback( (contentType: string) => { @@ -885,7 +885,7 @@ const FilesTableView = () => { ))} {items.map((file, index) => ( - { editing={editing} setEditing={setEditing} renameSchema={renameSchema} - handleRename={async (path: string, newPath: string) => { - handleRename && (await handleRename(path, newPath)) + handleRename={async (cid: string, newName: string) => { + handleRename && (await handleRename(cid, newName)) setEditing(undefined) }} deleteFile={() => { @@ -914,6 +914,7 @@ const FilesTableView = () => { itemOperations={getItemOperations(file.content_type)} resetSelectedFiles={resetSelectedCids} browserView="table" + recoverFile={() => recoverItems && recoverItems([file.cid])} /> ))} @@ -926,13 +927,14 @@ const FilesTableView = () => { )} > {items.map((file, index) => ( - { ))} )} - {files && previewFileIndex !== undefined && ( + {files && previewFileIndex !== undefined && bucket && ( { { refreshContents && ( <> - setCreateFolderModalOpen(false)} /> @@ -1004,7 +1005,7 @@ const FilesTableView = () => { close={() => setIsUploadModalOpen(false)} /> { setIsMoveFileModalOpen(false) @@ -1024,4 +1025,4 @@ const FilesTableView = () => { ) } -export default FilesTableView +export default FilesList diff --git a/packages/files-ui/src/Components/Modules/FilePreviewModal.tsx b/packages/files-ui/src/Components/Modules/FilePreviewModal.tsx index bd2b45dc11..b0412d36ce 100644 --- a/packages/files-ui/src/Components/Modules/FilePreviewModal.tsx +++ b/packages/files-ui/src/Components/Modules/FilePreviewModal.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useRef } from "react" import { useState } from "react" import { createStyles, makeStyles, useThemeSwitcher } from "@chainsafe/common-theme" -import { BucketType, FileSystemItem, useDrive } from "../../Contexts/DriveContext" +import { FileSystemItem, useFiles } from "../../Contexts/FilesContext" import MimeMatcher from "./../../Utils/MimeMatcher" import axios, { CancelTokenSource } from "axios" import { @@ -30,6 +30,7 @@ import MarkdownPreview from "./PreviewRenderers/MarkdownPreview" import { useHotkeys } from "react-hotkeys-hook" import { t, Trans } from "@lingui/macro" import { CSFTheme } from "../../Themes/types" +import { useFileBrowser } from "../../Contexts/FileBrowserContext" export interface IPreviewRendererProps { contents: Blob @@ -157,12 +158,12 @@ interface Props { previousFile?: () => void closePreview: () => void path: string - bucketType: BucketType } -const FilePreviewModal = ({ file, nextFile, previousFile, closePreview, path, bucketType }: Props) => { +const FilePreviewModal = ({ file, nextFile, previousFile, closePreview, path }: Props) => { const classes = useStyles() - const { getFileContent, downloadFile } = useDrive() + const { getFileContent, downloadFile } = useFiles() + const { bucket } = useFileBrowser() const { desktop } = useThemeSwitcher() const [isLoading, setIsLoading] = useState(false) const [loadingProgress, setLoadingProgress] = useState(0) @@ -186,7 +187,7 @@ const FilePreviewModal = ({ file, nextFile, previousFile, closePreview, path, bu useEffect(() => { const getContents = async () => { - if (!cid || !size) return + if (!cid || !size || !bucket) return if (source.current) { source.current.cancel("Cancelling previous request") @@ -198,15 +199,14 @@ const FilePreviewModal = ({ file, nextFile, previousFile, closePreview, path, bu setError(undefined) try { - const content = await getFileContent({ + const content = await getFileContent(bucket.id, { cid, cancelToken: token, onDownloadProgress: (evt) => { setLoadingProgress((evt.loaded / size) * 100) }, file, - path, - bucketType + path }) if (content) { @@ -229,7 +229,7 @@ const FilePreviewModal = ({ file, nextFile, previousFile, closePreview, path, bu if (content_type && compatibleFilesMatcher.match(content_type)) { getContents() } - }, [cid, size, content_type, getFileContent, file, path, bucketType]) + }, [cid, size, content_type, getFileContent, file, path, bucket]) const validRendererMimeType = content_type && @@ -264,7 +264,7 @@ const FilePreviewModal = ({ file, nextFile, previousFile, closePreview, path, bu }) const handleDownload = () => { - if (!name || !cid) return + if (!name || !cid || !bucket) return if (fileContent) { const link = document.createElement("a") link.href = URL.createObjectURL(fileContent) @@ -272,7 +272,7 @@ const FilePreviewModal = ({ file, nextFile, previousFile, closePreview, path, bu link.click() URL.revokeObjectURL(link.href) } else { - downloadFile(file, path, bucketType) + downloadFile(bucket.id, file, path) } } @@ -428,7 +428,7 @@ const FilePreviewModal = ({ file, nextFile, previousFile, closePreview, path, bu diff --git a/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx b/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx index 5d43c7caab..ab454167e2 100644 --- a/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx +++ b/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx @@ -3,7 +3,7 @@ import { Button, FacebookLogoIcon, GithubLogoIcon, GoogleLogoIcon, Loading, Typo import { createStyles, makeStyles, useThemeSwitcher } from "@chainsafe/common-theme" import { CSFTheme } from "../../../Themes/types" import { t, Trans } from "@lingui/macro" -import { useImployApi } from "@chainsafe/common-contexts" +import { useFilesApi } from "@chainsafe/common-contexts" import { useWeb3 } from "@chainsafe/web3-context" import { useThresholdKey } from "../../../Contexts/ThresholdKeyContext" import { LOGIN_TYPE } from "@toruslabs/torus-direct-web-sdk" @@ -98,7 +98,6 @@ const useStyles = makeStyles( marginRight: constants.generalUnit * 3.5 }, [breakpoints.down("md")]: { - // TODO: confirm how to move this around display: "none" } }, @@ -112,7 +111,6 @@ const useStyles = makeStyles( fontWeight: 400 }, [breakpoints.down("md")]: { - // TODO: confirm how to move this around display: "none" } }, @@ -138,7 +136,7 @@ interface IInitialScreen { } const InitialScreen = ({ className }: IInitialScreen) => { - const { selectWallet, resetAndSelectWallet } = useImployApi() + const { selectWallet, resetAndSelectWallet } = useFilesApi() const { desktop } = useThemeSwitcher() const { wallet } = useWeb3() const { login, status, resetStatus } = useThresholdKey() diff --git a/packages/files-ui/src/Components/Modules/LoginModule/MigrateAccount.tsx b/packages/files-ui/src/Components/Modules/LoginModule/MigrateAccount.tsx index 64bc3b51e1..1965afe09e 100644 --- a/packages/files-ui/src/Components/Modules/LoginModule/MigrateAccount.tsx +++ b/packages/files-ui/src/Components/Modules/LoginModule/MigrateAccount.tsx @@ -6,8 +6,8 @@ import { Typography } from "@chainsafe/common-components" import clsx from "clsx" -import { useDrive } from "../../../Contexts/DriveContext" -import { useImployApi } from "@chainsafe/common-contexts" +import { useFiles } from "../../../Contexts/FilesContext" +import { useFilesApi } from "@chainsafe/common-contexts" import { useThresholdKey } from "../../../Contexts/ThresholdKeyContext" import ConciseExplainer from "./ConciseExplainer" import { CSFTheme } from "../../../Themes/types" @@ -94,8 +94,8 @@ const MigrateAccount: React.FC = ({ className }: IMigrateAccount) => { const classes = useStyles() - const { validateMasterPassword } = useImployApi() - const { secureAccountWithMasterPassword } = useDrive() + const { validateMasterPassword } = useFilesApi() + const { secureAccountWithMasterPassword } = useFiles() const { addPasswordShare, logout } = useThresholdKey() const [migrateState, setMigrateState] = useState<"migrate"|"explainer"|"complete">("migrate") const [masterPassword, setMasterPassword] = useState("") diff --git a/packages/files-ui/src/Components/Modules/MasterKeySequence/SequenceSlides/SetMasterKey.slide.tsx b/packages/files-ui/src/Components/Modules/MasterKeySequence/SequenceSlides/SetMasterKey.slide.tsx deleted file mode 100644 index 050a23d4a6..0000000000 --- a/packages/files-ui/src/Components/Modules/MasterKeySequence/SequenceSlides/SetMasterKey.slide.tsx +++ /dev/null @@ -1,233 +0,0 @@ -import { createStyles, makeStyles } from "@chainsafe/common-theme" -import React from "react" -import { Button, FormikCheckboxInput, FormikTextInput, Typography } from "@chainsafe/common-components" -import clsx from "clsx" -import { Form, Formik } from "formik" -import * as yup from "yup" -import { ROUTE_LINKS } from "../../../FilesRoutes" -import zxcvbn from "zxcvbn" -import StrengthIndicator from "./StrengthIndicator" -import { CSFTheme } from "../../../../Themes/types" - -const useStyles = makeStyles(({ breakpoints, constants }: CSFTheme) => - createStyles({ - root: { - maxWidth: 320, - [breakpoints.down("md")]: {}, - "& p": { - fontWeight: 400, - marginBottom: constants.generalUnit * 2, - [breakpoints.up("md")]: { - color: constants.masterKey.desktop.color - }, - [breakpoints.down("md")]: { - color: constants.masterKey.mobile.color - } - }, - "& h2": { - textAlign: "center", - marginBottom: constants.generalUnit * 4.125, - [breakpoints.up("md")]: { - color: constants.masterKey.desktop.color - }, - [breakpoints.down("md")]: { - color: constants.masterKey.mobile.color - } - } - }, - input: { - margin: 0, - width: "100%", - marginBottom: constants.generalUnit * 1.5, - "& span": { - [breakpoints.up("md")]: { - color: constants.masterKey.desktop.color - }, - [breakpoints.down("md")]: { - color: constants.masterKey.mobile.color - } - } - }, - highlight: { - fontWeight: 700, - textDecoration: "underline" - }, - checkbox: { - marginBottom: constants.generalUnit, - [breakpoints.up("md")]: { - color: constants.masterKey.desktop.color, - "& svg": { - fill: `${constants.masterKey.desktop.checkbox} !important` - } - }, - [breakpoints.down("md")]: { - color: constants.masterKey.mobile.color, - "& svg": { - fill: `${constants.masterKey.mobile.checkbox} !important` - } - } - }, - button: { - marginTop: constants.generalUnit * 3 - }, - inputLabel: { - fontSize: "16px", - lineHeight: "24px", - [breakpoints.up("md")]: { - color: constants.masterKey.desktop.color - }, - [breakpoints.down("md")]: { - color: constants.masterKey.mobile.color - }, - marginBottom: constants.generalUnit - }, - link: { - [breakpoints.up("md")]: { - color: constants.masterKey.desktop.link - }, - [breakpoints.down("md")]: { - color: constants.masterKey.mobile.link - } - } - }) -) - -interface ISetMasterKeySlide { - className?: string -} - -const SetMasterKeySlide: React.FC = ({ - className -}: ISetMasterKeySlide) => { - const classes = useStyles() - // const { secureDrive } = useDrive() - - const masterKeyValidation = yup.object().shape({ - masterKey: yup - .string() - .test( - "Complexity", - "Encryption password needs to be more complex", - async (val: string | null | undefined | object) => { - if (val === undefined) { - return false - } - - const complexity = zxcvbn(`${val}`) - if (complexity.score >= 2) { - return true - } - return false - } - ) - .required("Please provide an encryption password"), - confirmMasterKey: yup - .string() - .oneOf( - [yup.ref("masterKey"), undefined], - "Encryption password must match" - ) - .required("Encryption password confirmation is required'"), - privacyPolicy: yup - .boolean() - .oneOf([true], "Please accept the privacy policy"), - terms: yup.boolean().oneOf([true], "Please accept the terms & conditions.") - }) - - return ( -
- - Set an Encryption Password - - { - helpers.setSubmitting(true) - // secureDrive(values.masterKey) - helpers.setSubmitting(false) - }} - > - - } - /> - - - Please record your encryption password somewhere safe.
- Forgetting this password means{" "} - - you are permanently locked out of your account. - -
- - I have read the{" "} - - Privacy Policy - - - } - /> - - I have read the{" "} - - Terms of Service - - - } - /> - - -
-
- ) -} - -export default SetMasterKeySlide diff --git a/packages/files-ui/src/Components/Modules/PreviewRenderers/PDFPreview.tsx b/packages/files-ui/src/Components/Modules/PreviewRenderers/PDFPreview.tsx index 6743f4a39f..406a0d03cd 100644 --- a/packages/files-ui/src/Components/Modules/PreviewRenderers/PDFPreview.tsx +++ b/packages/files-ui/src/Components/Modules/PreviewRenderers/PDFPreview.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react" import { IPreviewRendererProps } from "../FilePreviewModal" import { makeStyles, ITheme, createStyles } from "@chainsafe/common-theme" -import { Document, Page } from "react-pdf" +import { Document, Page } from "react-pdf/dist/esm/entry.webpack" import { Button, Typography } from "@chainsafe/common-components" import { Trans } from "@lingui/macro" diff --git a/packages/files-ui/src/Components/Modules/SearchModule.tsx b/packages/files-ui/src/Components/Modules/SearchModule.tsx index 9c7e9b731f..08d95fdef5 100644 --- a/packages/files-ui/src/Components/Modules/SearchModule.tsx +++ b/packages/files-ui/src/Components/Modules/SearchModule.tsx @@ -5,7 +5,7 @@ import { useOnClickOutside, useThemeSwitcher } from "@chainsafe/common-theme" -import React, { ChangeEvent, useCallback, useRef } from "react" +import React, { ChangeEvent, useCallback, useMemo, useRef } from "react" import { ArrowLeftIcon, Button, @@ -17,11 +17,12 @@ import { import { useState } from "react" import clsx from "clsx" import { ROUTE_LINKS } from "../FilesRoutes" -import { useDrive, BucketType, SearchEntry } from "../../Contexts/DriveContext" +import { useFiles, BucketType, SearchEntry } from "../../Contexts/FilesContext" import { CONTENT_TYPES } from "../../Utils/Constants" import { getParentPathFromFilePath } from "../../Utils/pathUtils" import { t, Trans } from "@lingui/macro" import { CSFTheme } from "../../Themes/types" +import { useFilesApi } from "@chainsafe/common-contexts" export interface SearchParams { bucketType: BucketType @@ -157,28 +158,16 @@ const SearchModule: React.FC = ({ const [searchQuery, setSearchQuery] = useState("") const [searchResults, setSearchResults] = useState<{results: SearchEntry[]; query: string} | undefined>(undefined) const ref = useRef(null) - const { listBuckets, searchFiles } = useDrive() + const { buckets } = useFiles() const { addToastMessage } = useToaster() - const [bucketType] = useState("csf") - const [currentSearchBucket, setCurrentSearchBucket] = useState() - const getSearchResults = async (searchString: string) => { + const { filesApiClient } = useFilesApi() + const bucket = useMemo(() => buckets.find(b => b.type === "csf"), [buckets]) + + const getSearchResults = useCallback(async (searchString: string) => { try { - if (!searchString) return [] - let bucketId - if (currentSearchBucket?.bucketType === bucketType) { - // we have the bucket id - bucketId = currentSearchBucket.bucketId - } else { - // fetch bucket id - const results = await listBuckets(bucketType) - const bucket1 = results[0] - setCurrentSearchBucket({ - bucketType, - bucketId: bucket1.id - }) - bucketId = bucket1.id - } - const results = await searchFiles(bucketId || "", searchString) + if (!searchString || !bucket) return [] + + const results = await filesApiClient.searchFiles({ bucket_id: bucket.id, query: searchString }) return results } catch (err) { addToastMessage({ @@ -187,7 +176,7 @@ const SearchModule: React.FC = ({ }) return Promise.reject(err) } - } + }, [addToastMessage, bucket, filesApiClient]) const { redirect } = useHistory() @@ -203,9 +192,7 @@ const SearchModule: React.FC = ({ // TODO useCallback is maybe not needed here // eslint-disable-next-line react-hooks/exhaustive-deps - const debouncedSearch = useCallback(debounce(onSearch, 400), [ - currentSearchBucket?.bucketId - ]) + const debouncedSearch = useCallback(debounce(onSearch, 400), [getSearchResults]) const onSearchChange = (searchString: string) => { setSearchQuery(searchString) diff --git a/packages/files-ui/src/Components/Modules/Settings/Plan.tsx b/packages/files-ui/src/Components/Modules/Settings/Plan.tsx index f863d05244..91600b04d1 100644 --- a/packages/files-ui/src/Components/Modules/Settings/Plan.tsx +++ b/packages/files-ui/src/Components/Modules/Settings/Plan.tsx @@ -10,7 +10,7 @@ import { import { makeStyles, ITheme, createStyles } from "@chainsafe/common-theme" import clsx from "clsx" import { FREE_PLAN_LIMIT } from "../../../Utils/Constants" -import { useDrive } from "../../../Contexts/DriveContext" +import { useFiles } from "../../../Contexts/FilesContext" import { Trans } from "@lingui/macro" import { ROUTE_LINKS } from "../../FilesRoutes" @@ -74,7 +74,7 @@ const useStyles = makeStyles((theme: ITheme) => const PlanView: React.FC = () => { const classes = useStyles() - const { spaceUsed } = useDrive() + const { spaceUsed } = useFiles() return ( diff --git a/packages/files-ui/src/Components/Modules/UploadFileModule.tsx b/packages/files-ui/src/Components/Modules/UploadFileModule.tsx index 76fd082a85..cf0d6f11c0 100644 --- a/packages/files-ui/src/Components/Modules/UploadFileModule.tsx +++ b/packages/files-ui/src/Components/Modules/UploadFileModule.tsx @@ -1,5 +1,5 @@ import { Button, FileInput } from "@chainsafe/common-components" -import { useDrive } from "../../Contexts/DriveContext" +import { useFiles } from "../../Contexts/FilesContext" import { createStyles, makeStyles } from "@chainsafe/common-theme" import React, { useCallback, useState } from "react" import { Formik, Form } from "formik" @@ -77,8 +77,8 @@ interface IUploadFileModuleProps { const UploadFileModule = ({ modalOpen, close }: IUploadFileModuleProps) => { const classes = useStyles() const [isDoneDisabled, setIsDoneDisabled] = useState(true) - const { uploadFiles } = useDrive() - const { currentPath, refreshContents } = useFileBrowser() + const { uploadFiles } = useFiles() + const { currentPath, refreshContents, bucket } = useFileBrowser() const UploadSchema = object().shape({ files: array().required(t`Please select a file to upload`) }) @@ -87,12 +87,13 @@ const UploadFileModule = ({ modalOpen, close }: IUploadFileModuleProps) => { }, []) const onSubmit = useCallback(async (values, helpers) => { + if (!bucket) return helpers.setSubmitting(true) try { - await uploadFiles(values.files, currentPath) - helpers.resetForm() - refreshContents && refreshContents() close() + await uploadFiles(bucket.id, values.files, currentPath) + refreshContents && refreshContents() + helpers.resetForm() } catch (errors) { if (errors[0].message.includes("conflict with existing")) { helpers.setFieldError("files", "File/Folder exists") @@ -101,7 +102,7 @@ const UploadFileModule = ({ modalOpen, close }: IUploadFileModuleProps) => { } } helpers.setSubmitting(false) - }, [close, currentPath, uploadFiles, refreshContents]) + }, [close, currentPath, uploadFiles, refreshContents, bucket]) return ( { @@ -30,21 +30,21 @@ const useStyles = makeStyles(({ constants, zIndex, breakpoints }: ITheme) => { const UploadProgressModals: React.FC = () => { const classes = useStyles() - const { uploadsInProgress } = useDrive() + const { uploadsInProgress } = useFiles() const { desktop } = useThemeSwitcher() - return ( -
- {uploadsInProgress.map( - (uploadInProgress) => - (desktop || uploadInProgress.complete || uploadInProgress.error) && ( - - ) - )} -
+ if (uploadsInProgress.length === 0) { return null } + return (
+ {uploadsInProgress.map( + (uploadInProgress) => + (desktop || uploadInProgress.complete || uploadInProgress.error) && ( + + ) + )} +
) } diff --git a/packages/files-ui/src/Components/Pages/LoginPage.tsx b/packages/files-ui/src/Components/Pages/LoginPage.tsx index 60d068561e..bb5c74d3f6 100644 --- a/packages/files-ui/src/Components/Pages/LoginPage.tsx +++ b/packages/files-ui/src/Components/Pages/LoginPage.tsx @@ -13,7 +13,7 @@ import TopDarkSVG from "../../Media/landing/layers/dark/Top.dark.svg" import BottomLightSVG from "../../Media/landing/layers/light/Bottom.light.svg" import TopLightSVG from "../../Media/landing/layers/light/Top.light.svg" // import { ForegroundSVG } from "../../Media/landing/layers/ForegroundSVG" -import { useImployApi } from "@chainsafe/common-contexts" +import { useFilesApi } from "@chainsafe/common-contexts" import MigrateAccount from "../Modules/LoginModule/MigrateAccount" import InitializeAccount from "../Modules/LoginModule/InitializeAccount" @@ -127,7 +127,7 @@ const useStyles = makeStyles( ) const Content = ({ className }: { className: string }) => { - const { isMasterPasswordSet } = useImployApi() + const { isMasterPasswordSet } = useFilesApi() const { keyDetails, isNewDevice, shouldInitializeAccount } = useThresholdKey() const shouldSaveNewDevice = !!keyDetails && isNewDevice && keyDetails.requiredShares <= 0 const areSharesMissing = !!keyDetails && keyDetails.requiredShares > 0 diff --git a/packages/files-ui/src/Contexts/FileBrowserContext.tsx b/packages/files-ui/src/Contexts/FileBrowserContext.tsx index f567003de5..f02f1f06d2 100644 --- a/packages/files-ui/src/Contexts/FileBrowserContext.tsx +++ b/packages/files-ui/src/Contexts/FileBrowserContext.tsx @@ -1,18 +1,17 @@ import { Crumb } from "@chainsafe/common-components" import React, { useContext } from "react" -import { FileOperation, IBulkOperations, IFilesBrowserModuleProps } from "../Components/Modules/FileBrowsers/types" -import { BucketType, FileSystemItem, UploadProgress } from "./DriveContext" - -interface FileBrowserContext extends IFilesBrowserModuleProps { +import { FileOperation, IBulkOperations, IFileBrowserModuleProps } from "../Components/Modules/FileBrowsers/types" +import { FileSystemItem, UploadProgress } from "./FilesContext" +import { Bucket } from "@chainsafe/files-api-client" +interface FileBrowserContext extends IFileBrowserModuleProps { + bucket?: Bucket itemOperations: {[contentType: string]: FileOperation[]} - bulkOperations?: IBulkOperations - handleRename?: (path: string, new_path: string) => Promise - handleMove?: (path: string, new_path: string) => Promise + renameItem?: (cid: string, newName: string) => Promise + moveItems?: (cids: string[], newPath: string) => Promise downloadFile?: (cid: string) => Promise - deleteFiles?: (cid: string[]) => Promise - recoverFile?: (cid: string) => Promise - recoverFiles?: (cid: string[]) => Promise + deleteItems?: (cid: string[]) => Promise + recoverItems?: (cid: string[]) => Promise viewFolder?: (cid: string) => void allowDropUpload?: boolean @@ -24,7 +23,6 @@ interface FileBrowserContext extends IFilesBrowserModuleProps { refreshContents?: () => void currentPath: string - bucketType: BucketType loadingCurrentPath: boolean uploadsInProgress?: UploadProgress[] showUploadsInTable: boolean diff --git a/packages/files-ui/src/Contexts/DriveContext.tsx b/packages/files-ui/src/Contexts/FilesContext.tsx similarity index 59% rename from packages/files-ui/src/Contexts/DriveContext.tsx rename to packages/files-ui/src/Contexts/FilesContext.tsx index b5bcaa5908..3cf2feb37a 100644 --- a/packages/files-ui/src/Contexts/DriveContext.tsx +++ b/packages/files-ui/src/Contexts/FilesContext.tsx @@ -1,27 +1,24 @@ import { - CSFFilesFullinfoResponse, + CSFFilesFullInfoResponse, FileContentResponse, - FilesMvRequest, - FilesPathRequest, DirectoryContentResponse, BucketType, - Bucket, - SearchEntry, - FilesRmRequest + Bucket as FilesBucket, + SearchEntry } from "@chainsafe/files-api-client" import React, { useCallback, useEffect, useReducer } from "react" import { useState } from "react" -import { decryptFile, encryptFile, useImployApi } from "@chainsafe/common-contexts" +import { decryptFile, encryptFile, useFilesApi, useUser } from "@chainsafe/common-contexts" import { v4 as uuidv4 } from "uuid" import { useToaster } from "@chainsafe/common-components" -import { downloadsInProgressReducer, uploadsInProgressReducer } from "./DriveReducer" +import { downloadsInProgressReducer, uploadsInProgressReducer } from "./FilesReducers" import { CancelToken } from "axios" import { t } from "@lingui/macro" import { readFileAsync } from "../Utils/Helpers" import { useBeforeunload } from "react-beforeunload" import { useThresholdKey } from "./ThresholdKeyContext" -type DriveContextProps = { +type FilesContextProps = { children: React.ReactNode | React.ReactNode[] } @@ -51,28 +48,19 @@ interface GetFileContentParams { onDownloadProgress?: (progressEvent: ProgressEvent) => void file: FileSystemItem path: string - bucketType: BucketType } +type Bucket = FilesBucket & { encryptionKey: string} -type DriveContext = { - uploadFiles: (files: File[], path: string) => Promise - createFolder: (body: FilesPathRequest) => Promise - renameFile: (body: FilesMvRequest) => Promise - moveFile: (body: FilesMvRequest) => Promise - moveFiles: (filesToMove: FilesMvRequest[]) => Promise - moveCSFObject: (body: FilesMvRequest) => Promise - removeCSFObjects: (body: FilesRmRequest) => Promise - downloadFile: (itemToDownload: FileSystemItem, path: string, bucketType: BucketType) => void - getFileContent: ({ cid, cancelToken, onDownloadProgress, file }: GetFileContentParams) => Promise - list: (body: FilesPathRequest) => Promise - listBuckets: (bucketType: BucketType) => Promise - searchFiles: (bucketId: string, searchString: string) => Promise +type FilesContext = { + buckets: Bucket[] uploadsInProgress: UploadProgress[] downloadsInProgress: DownloadProgress[] spaceUsed: number - getFolderTree: () => Promise - getFileInfo: (path: string) => Promise + uploadFiles: (bucketId: string, files: File[], path: string) => Promise + downloadFile: (bucketId: string, itemToDownload: FileSystemItem, path: string) => void + getFileContent: (bucketId: string, params: GetFileContentParams) => Promise + refreshBuckets: () => Promise secureAccountWithMasterPassword: (candidatePassword: string) => Promise } @@ -86,29 +74,62 @@ type FileSystemItem = IFileSystemItem const REMOVE_UPLOAD_PROGRESS_DELAY = 5000 const MAX_FILE_SIZE = 2 * 1024 ** 3 -const DriveContext = React.createContext(undefined) +const FilesContext = React.createContext(undefined) -const DriveProvider = ({ children }: DriveContextProps) => { +const FilesProvider = ({ children }: FilesContextProps) => { const { - imployApiClient, + filesApiClient, isLoggedIn, secured, secureThresholdKeyAccount, encryptedEncryptionKey, isMasterPasswordSet, validateMasterPassword - } = useImployApi() + } = useFilesApi() const { publicKey, encryptForPublicKey, decryptMessageWithThresholdKey } = useThresholdKey() const { addToastMessage } = useToaster() const [spaceUsed, setSpaceUsed] = useState(0) - const [encryptionKey, setEncryptionKey] = useState() + const [personalEncryptionKey, setPersonalEncryptionKey] = useState() + const [buckets, setBuckets] = useState([]) + const { profile } = useUser() + + const getKeyForBucket = useCallback(async (bucket: FilesBucket) => { + // TODO: Add bucket.owners here when the API returns this + const bucketUsers = [...bucket.readers, ...bucket.writers] + const bucketUser = bucketUsers.find(bu => bu.uuid === profile?.userId) + if (!bucketUser || !bucketUser.encryption_key) { + console.error(`Unable to retrieve encryption key for ${bucket.id}`) + return "" + } + const decrypted = await decryptMessageWithThresholdKey(bucketUser.encryption_key) + return decrypted || "" + }, [decryptMessageWithThresholdKey, profile]) + + const refreshBuckets = useCallback(async () => { + if (!personalEncryptionKey) return + const result = await filesApiClient.listBuckets() + + const bucketsWithKeys: Bucket[] = await Promise.all(result.map(async (b) => ({ + ...b, + encryptionKey: (b.type === "csf" || b.type === "trash") ? personalEncryptionKey : await getKeyForBucket(b) + }))) + setBuckets(bucketsWithKeys) + return Promise.resolve() + }, [getKeyForBucket, filesApiClient, personalEncryptionKey]) + + useEffect(() => { + refreshBuckets() + }, [refreshBuckets]) // Space used counter useEffect(() => { const getSpaceUsage = async () => { try { - const { csf_size } = await imployApiClient.getCSFFilesStoreInfo() - setSpaceUsed(csf_size) + // TODO: Update this to include Share buckets where the current user is the owner + const totalSize = buckets.filter(b => b.type === "csf" || b.type === "trash") + .reduce((totalSize, bucket) => { return totalSize += (bucket as any).size}, 0) + + setSpaceUsed(totalSize) } catch (error) { console.error(error) } @@ -116,12 +137,13 @@ const DriveProvider = ({ children }: DriveContextProps) => { if (isLoggedIn) { getSpaceUsage() } - }, [imployApiClient, isLoggedIn]) + }, [filesApiClient, isLoggedIn, buckets]) - // Reset password on log out + // Reset encryption keys on log out useEffect(() => { if (!isLoggedIn) { - setEncryptionKey(undefined) + setPersonalEncryptionKey(undefined) + setBuckets([]) } }, [isLoggedIn]) @@ -133,7 +155,7 @@ const DriveProvider = ({ children }: DriveContextProps) => { window.crypto.getRandomValues(new Uint8Array(32)) ).toString("base64") console.log("New key", key) - setEncryptionKey(key) + setPersonalEncryptionKey(key) const encryptedKey = await encryptForPublicKey(publicKey, key) console.log("Encrypted encryption key", encryptedKey) secureThresholdKeyAccount(encryptedKey) @@ -145,14 +167,14 @@ const DriveProvider = ({ children }: DriveContextProps) => { const decryptedKey = await decryptMessageWithThresholdKey(encryptedKey) if (decryptedKey) { console.log("Decrypted key: ", decryptedKey) - setEncryptionKey(decryptedKey) + setPersonalEncryptionKey(decryptedKey) } } catch (error) { console.error("Error decrypting key: ", encryptedKey) } } - if (isLoggedIn && publicKey && !encryptionKey) { + if (isLoggedIn && publicKey && !personalEncryptionKey) { console.log("Checking whether account is secured ", secured) if (!secured && !isMasterPasswordSet) { console.log("Generating key and securing account") @@ -172,7 +194,7 @@ const DriveProvider = ({ children }: DriveContextProps) => { encryptForPublicKey, secureThresholdKeyAccount, decryptMessageWithThresholdKey, - encryptionKey, + personalEncryptionKey, isMasterPasswordSet ]) @@ -180,7 +202,7 @@ const DriveProvider = ({ children }: DriveContextProps) => { if (!publicKey || !validateMasterPassword(candidatePassword)) return const encryptedKey = await encryptForPublicKey(publicKey, candidatePassword) - setEncryptionKey(candidatePassword) + setPersonalEncryptionKey(candidatePassword) secureThresholdKeyAccount(encryptedKey) } @@ -212,10 +234,11 @@ const DriveProvider = ({ children }: DriveContextProps) => { } }) - const uploadFiles = useCallback(async (files: File[], path: string) => { + const uploadFiles = useCallback(async (bucketId: string, files: File[], path: string) => { + const bucket = buckets.find(b => b.id === bucketId) - if (!encryptionKey) { - console.error("No encryption key") + if (!bucket || !bucket.encryptionKey) { + console.error("No encryption key for this bucket is available.") return } @@ -236,7 +259,7 @@ const DriveProvider = ({ children }: DriveContextProps) => { .filter((f) => f.size <= MAX_FILE_SIZE) .map(async (f) => { const fileData = await readFileAsync(f) - const encryptedData = await encryptFile(fileData, encryptionKey) + const encryptedData = await encryptFile(fileData, bucket.encryptionKey) return { data: new Blob([encryptedData], { type: f.type }), fileName: f.name @@ -250,13 +273,16 @@ const DriveProvider = ({ children }: DriveContextProps) => { appearance: "error" }) } - // API call - await imployApiClient.addCSFFiles( + + // TODO: Update this once API support for FPS is working + await filesApiClient.addCSFFiles( + // bucketId, filesParam, path, - "", + "csf", undefined, undefined, + // undefined, (progressEvent: { loaded: number; total: number }) => { dispatchUploadsInProgress({ type: "progress", @@ -294,99 +320,18 @@ const DriveProvider = ({ children }: DriveContextProps) => { dispatchUploadsInProgress({ type: "remove", payload: { id } }) }, REMOVE_UPLOAD_PROGRESS_DELAY) } - }, [addToastMessage, encryptionKey, imployApiClient]) - - const createFolder = async (body: FilesPathRequest) => { - try { - const result = await imployApiClient.addCSFDirectory(body) - addToastMessage({ - message: t`Folder created successfully`, - appearance: "success" - }) - return result - } catch (error) { - addToastMessage({ - message: t`There was an error creating this folder`, - appearance: "error" - }) - return Promise.reject() - } - } - - const getFolderTree = async () => { - try { - const result = await imployApiClient.getCSFTree() - return result - } catch (error) { - addToastMessage({ - message: t`There was an error getting folder info`, - appearance: "error" - }) - return Promise.reject() - } - } - - const getFileInfo = async (path: string) => { - try { - const result = await imployApiClient.getCSFFileInfo({ path }) - return result - } catch (error) { - addToastMessage({ - message: t`There was an error getting file info`, - appearance: "error" - }) - return Promise.reject() - } - } - - const renameFile = useCallback(async (body: FilesMvRequest) => { - try { - if (body.path !== body.new_path) { - await imployApiClient.moveCSFObject(body) - addToastMessage({ - message: t`File renamed successfully`, - appearance: "success" - }) - } + }, [addToastMessage, filesApiClient, buckets]) - return Promise.resolve() - } catch (error) { - addToastMessage({ - message: t`There was an error renaming this file`, - appearance: "error" - }) - return Promise.reject() - } - }, [addToastMessage, imployApiClient]) - const moveFile = useCallback(async (body: FilesMvRequest) => { - try { - await imployApiClient.moveCSFObject(body) - addToastMessage({ - message: t`File moved successfully`, - appearance: "success" - }) - return Promise.resolve() - } catch (error) { - addToastMessage({ - message: t`There was an error moving this file`, - appearance: "error" - }) - return Promise.reject() - } - }, [addToastMessage, imployApiClient]) - const moveFiles = useCallback(async (filesToMove: FilesMvRequest[]) => { - return Promise.all( - filesToMove.map((fileToMove) => - moveFile(fileToMove) - ) - ) - }, [moveFile]) + const getFileContent = useCallback(async ( + bucketId: string, + { cid, cancelToken, onDownloadProgress, file, path }: GetFileContentParams + ) => { + const bucket = buckets.find(b => b.id === bucketId) - const getFileContent = useCallback(async ({ cid, cancelToken, onDownloadProgress, file, path, bucketType }: GetFileContentParams) => { - if (!encryptionKey) { - throw new Error("No encryption key") + if (!bucket || !bucket.encryptionKey) { + throw new Error("No encryption key for this bucket found") } // when a file is accessed from the search page, a file and a path are passed in @@ -399,11 +344,11 @@ const DriveProvider = ({ children }: DriveContextProps) => { } try { - const result = await imployApiClient.getFileContent( + const result = await filesApiClient.getFileContent( { path: path, source: { - type: bucketType + id: bucket.id } }, cancelToken, @@ -415,7 +360,7 @@ const DriveProvider = ({ children }: DriveContextProps) => { } else { const decrypted = await decryptFile( await result.data.arrayBuffer(), - encryptionKey + bucket.encryptionKey ) if (decrypted) { return new Blob([decrypted], { @@ -427,9 +372,9 @@ const DriveProvider = ({ children }: DriveContextProps) => { console.error(error) return Promise.reject() } - }, [encryptionKey, imployApiClient]) + }, [buckets, filesApiClient]) - const downloadFile = useCallback(async (itemToDownload: FileSystemItem, path: string, bucketType: BucketType) => { + const downloadFile = useCallback(async (bucketId: string, itemToDownload: FileSystemItem, path: string) => { const toastId = uuidv4() try { const downloadProgress: DownloadProgress = { @@ -440,11 +385,10 @@ const DriveProvider = ({ children }: DriveContextProps) => { progress: 0 } dispatchDownloadsInProgress({ type: "add", payload: downloadProgress }) - const result = await getFileContent({ + const result = await getFileContent(bucketId, { cid: itemToDownload.cid, - bucketType: bucketType, file: itemToDownload, - path: path, + path: `${path}/${itemToDownload.name}`, onDownloadProgress: (progressEvent) => { dispatchDownloadsInProgress({ type: "progress", @@ -480,74 +424,39 @@ const DriveProvider = ({ children }: DriveContextProps) => { } }, [getFileContent]) - const list = async (body: FilesPathRequest) => { - try { - return imployApiClient.getCSFChildList(body) - } catch (error) { - return Promise.reject() - } - } - - const listBuckets = async (bucketType: BucketType) => { - return await imployApiClient.listBuckets(bucketType) - } - - const removeCSFObjects = async (body: FilesRmRequest) => { - return await imployApiClient.removeCSFObjects(body) - } - - const moveCSFObject = async (body: FilesMvRequest) => { - return await imployApiClient.moveCSFObject(body) - } - - const searchFiles = useCallback(async (bucketId: string, searchString: string) => { - return await imployApiClient.searchFiles({ - bucket_id: bucketId || "", - query: searchString - }) - }, [imployApiClient]) - return ( - {children} - + ) } -const useDrive = () => { - const context = React.useContext(DriveContext) +const useFiles = () => { + const context = React.useContext(FilesContext) if (context === undefined) { - throw new Error("useDrive must be used within a DriveProvider") + throw new Error("useFiles must be used within a FilesProvider") } return context } -export { DriveProvider, useDrive } +export { FilesProvider, useFiles } export type { FileSystemItem, DirectoryContentResponse, - CSFFilesFullinfoResponse as FileFullInfo, + CSFFilesFullInfoResponse as FileFullInfo, BucketType, SearchEntry } diff --git a/packages/files-ui/src/Contexts/DriveReducer.tsx b/packages/files-ui/src/Contexts/FilesReducers.tsx similarity index 98% rename from packages/files-ui/src/Contexts/DriveReducer.tsx rename to packages/files-ui/src/Contexts/FilesReducers.tsx index dca5ecdda0..96f5661d30 100644 --- a/packages/files-ui/src/Contexts/DriveReducer.tsx +++ b/packages/files-ui/src/Contexts/FilesReducers.tsx @@ -1,4 +1,4 @@ -import { DownloadProgress, UploadProgress } from "./DriveContext" +import { DownloadProgress, UploadProgress } from "./FilesContext" export function uploadsInProgressReducer( uploadsInProgress: UploadProgress[], diff --git a/packages/files-ui/src/Contexts/ThresholdKeyContext.tsx b/packages/files-ui/src/Contexts/ThresholdKeyContext.tsx index b8cd063e19..e0a3509546 100644 --- a/packages/files-ui/src/Contexts/ThresholdKeyContext.tsx +++ b/packages/files-ui/src/Contexts/ThresholdKeyContext.tsx @@ -9,7 +9,7 @@ import ShareSerializationModule, { SHARE_SERIALIZATION_MODULE_NAME } from "@tkey import { ServiceProviderBase } from "@tkey/service-provider-base" import { TorusStorageLayer } from "@tkey/storage-layer-torus" import bowser from "bowser" -import { useImployApi } from "@chainsafe/common-contexts" +import { useFilesApi } from "@chainsafe/common-contexts" import { utils, Wallet } from "ethers" import EthCrypto from "eth-crypto" import { useWeb3 } from "@chainsafe/web3-context" @@ -123,7 +123,7 @@ const getProviderSpecificParams = (loginType: LOGIN_TYPE): } const ThresholdKeyProvider = ({ children, network = "mainnet", enableLogging = false, apiKey }: ThresholdKeyProviderProps) => { - const { imployApiClient, thresholdKeyLogin, logout } = useImployApi() + const { filesApiClient, thresholdKeyLogin, logout } = useFilesApi() const { provider, isReady, checkIsReady, address } = useWeb3() const [userInfo, setUserInfo] = useState() const [TKeySdk, setTKeySdk] = useState() @@ -506,19 +506,19 @@ const ThresholdKeyProvider = ({ children, network = "mainnet", enableLogging = f addressToUse = await signer.getAddress() } - const { token } = await imployApiClient.getIdentityWeb3Token(addressToUse) + const { token } = await filesApiClient.getIdentityWeb3Token(addressToUse) if (!token) throw new Error("Token undefined") setStatus("awaiting confirmation") const signature = await signer.signMessage(token) setStatus("logging in") - const web3IdentityToken = await imployApiClient.postIdentityWeb3Token({ + const web3IdentityToken = await filesApiClient.postIdentityWeb3Token({ signature: signature, token: token, public_address: addressToUse }) - const uuidToken = await imployApiClient.generateServiceIdentityToken({ + const uuidToken = await filesApiClient.generateServiceIdentityToken({ identity_provider: loginType, identity_token: web3IdentityToken.token || "" }) @@ -541,7 +541,7 @@ const ThresholdKeyProvider = ({ children, network = "mainnet", enableLogging = f const oauthIdToken = await loginHandler.handleLoginWindow({}) setStatus("logging in") const userInfo = await loginHandler.getUserInfo(oauthIdToken) - const uuidToken = await imployApiClient.generateServiceIdentityToken({ + const uuidToken = await filesApiClient.generateServiceIdentityToken({ identity_provider: loginType, identity_token: oauthIdToken.idToken || oauthIdToken.accessToken }) diff --git a/packages/files-ui/src/Utils/Helpers.tsx b/packages/files-ui/src/Utils/Helpers.tsx index 20253ee222..943e0a9905 100644 --- a/packages/files-ui/src/Utils/Helpers.tsx +++ b/packages/files-ui/src/Utils/Helpers.tsx @@ -1,4 +1,7 @@ import { useLocation } from "@chainsafe/common-components" +import { guessContentType } from "./contentTypeGuesser" +import { FileContentResponse } from "@chainsafe/files-api-client" +import { FileSystemItem } from "../Contexts/FilesContext" export const centerEllipsis = (address: string, remaining = 6) => { if (address.length <= remaining * 2) { @@ -31,3 +34,13 @@ export function useQuery() { export const capitalize = (value: string) => { return value.charAt(0).toUpperCase() + value.slice(1) } + +export const parseFileContentResponse = (fcr: FileContentResponse): FileSystemItem => ({ + ...fcr, + content_type: + fcr.content_type !== "application/octet-stream" + ? fcr.content_type + : guessContentType(fcr.name), + isFolder: + fcr.content_type === "application/chainsafe-files-directory" +}) diff --git a/packages/files-ui/src/Utils/pathUtils.ts b/packages/files-ui/src/Utils/pathUtils.ts index ef4174ac5a..594153af6c 100644 --- a/packages/files-ui/src/Utils/pathUtils.ts +++ b/packages/files-ui/src/Utils/pathUtils.ts @@ -22,10 +22,10 @@ export function getArrayOfPaths(path: string): string[] { // [] -> "/" // ["path", "to", "this"] -> "/path/to/this" -export function getPathFromArray(arrayOfPaths: string[]): string { +export function getURISafePathFromArray(arrayOfPaths: string[]): string { if (!arrayOfPaths.length) return "/" else { - return "/" + arrayOfPaths.join("/") + return "/" + arrayOfPaths.map(encodeURIComponent).join("/") } } @@ -42,9 +42,9 @@ export function getPathWithFile(path: string, fileName: string) { export function getParentPathFromFilePath(filePath: string) { const parentPath = filePath.substring(0, filePath.lastIndexOf("/")) if (!parentPath) return "/" - else return parentPath + return parentPath } export function extractDrivePath(pathname: string) { return pathname.split("/").slice(2).join("/").concat("/") -} \ No newline at end of file +} diff --git a/packages/files-ui/src/locales/en/messages.po b/packages/files-ui/src/locales/en/messages.po index 6da639cac8..aaa2718800 100644 --- a/packages/files-ui/src/locales/en/messages.po +++ b/packages/files-ui/src/locales/en/messages.po @@ -190,12 +190,6 @@ msgstr "File Info" msgid "File format not supported." msgstr "File format not supported." -msgid "File moved successfully" -msgstr "File moved successfully" - -msgid "File renamed successfully" -msgstr "File renamed successfully" - msgid "File size" msgstr "File size" @@ -211,9 +205,6 @@ msgstr "First name" msgid "Folder" msgstr "Folder" -msgid "Folder created successfully" -msgstr "Folder created successfully" - msgid "Folders" msgstr "Folders" @@ -553,33 +544,18 @@ msgstr "There was an error authenticating" msgid "There was an error connecting your wallet" msgstr "There was an error connecting your wallet" -msgid "There was an error creating this folder" -msgstr "There was an error creating this folder" - msgid "There was an error deleting this" msgstr "There was an error deleting this" -msgid "There was an error getting file info" -msgstr "There was an error getting file info" - -msgid "There was an error getting folder info" -msgstr "There was an error getting folder info" - msgid "There was an error getting search results" msgstr "There was an error getting search results" msgid "There was an error getting the preview." msgstr "There was an error getting the preview." -msgid "There was an error moving this file" -msgstr "There was an error moving this file" - msgid "There was an error recovering this" msgstr "There was an error recovering this" -msgid "There was an error renaming this file" -msgstr "There was an error renaming this file" - msgid "Try again" msgstr "Try again" diff --git a/packages/files-ui/src/locales/fr/messages.po b/packages/files-ui/src/locales/fr/messages.po index af228a0680..d70fa2bf47 100644 --- a/packages/files-ui/src/locales/fr/messages.po +++ b/packages/files-ui/src/locales/fr/messages.po @@ -191,12 +191,6 @@ msgstr "Info du fichier" msgid "File format not supported." msgstr "Format de fichier non supporté." -msgid "File moved successfully" -msgstr "Fichier déplacé avec succès" - -msgid "File renamed successfully" -msgstr "Fichier renommé avec succès" - msgid "File size" msgstr "Taille" @@ -212,9 +206,6 @@ msgstr "Prénom" msgid "Folder" msgstr "Dossier" -msgid "Folder created successfully" -msgstr "Dossier créé avec succès" - msgid "Folders" msgstr "Dossiers" @@ -554,33 +545,18 @@ msgstr "Une erreur s'est produite lors de l'authentification" msgid "There was an error connecting your wallet" msgstr "Une erreur s'est produite lors de la connexion de votre wallet" -msgid "There was an error creating this folder" -msgstr "Une erreur s'est produite lors de la création de ce dossier" - msgid "There was an error deleting this" msgstr "Une erreur s'est produite lors de la suppression" -msgid "There was an error getting file info" -msgstr "Une erreur s'est produite lors de l'obtention des informations sur le fichier" - -msgid "There was an error getting folder info" -msgstr "Une erreur s'est produite lors de l'obtention des informations sur le dossier" - msgid "There was an error getting search results" msgstr "Une erreur s'est produite lors de l'obtention des résultats de recherche" msgid "There was an error getting the preview." msgstr "Une erreur s'est produite lors de la génération de l’aperçu." -msgid "There was an error moving this file" -msgstr "Une erreur s'est produite lors du déplacement de ce fichier" - msgid "There was an error recovering this" msgstr "Une erreur s'est produite lors de la récupération" -msgid "There was an error renaming this file" -msgstr "Une erreur s'est produite lors de renommage de ce fichier" - msgid "Try again" msgstr "Essayer de nouveau" diff --git a/yarn.lock b/yarn.lock index 1db1e8dad2..bc4ad6cd48 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1664,10 +1664,10 @@ resolved "https://registry.yarnpkg.com/@chainsafe/browser-storage-hooks/-/browser-storage-hooks-1.0.1.tgz#26d32cde1999914db755a631e2643823c54959f7" integrity sha512-Q4b5gQAZnsRXKeADspd5isqfwwhhXjDk70y++YadufA6EZ3tf340oW0OVszp74KaGEw+CAYFGQR4X7bzpZ3x9Q== -"@chainsafe/files-api-client@1.9.0": - version "1.9.0" - resolved "https://npm.pkg.github.com/download/@chainsafe/files-api-client/1.9.0/4670ba7db48668a6ac1f5ef204bdae517594fcfb5855e4324fe228dfc9da4f29#08dd2c11392619d5cd9f3828876347d07177cd72" - integrity sha512-m5zJkB4zVKZE+8RFThsky1kJmh09niMyAB6xLykyCkUJeF2b4t1Wd5OItoSPTeo09tzUNjQmh+HbOP/jUDA9+Q== +"@chainsafe/files-api-client@1.11.2": + version "1.11.2" + resolved "https://npm.pkg.github.com/download/@chainsafe/files-api-client/1.11.2/67d8597bea590f72e00eb384d1b140b342012ea38e49f0ef2801425da816a2b9#c77351af8289f228ed6733ed2e59983e2c7b0d75" + integrity sha512-723zDIbWFX6HUUcXm7nXiziQyzIM619VsXkGH3wOXCmS+UiLs1gUnS8j1k41AkjZoYAu6t/Vkbssxm6/1yjK1Q== "@chainsafe/web3-context@1.1.4": version "1.1.4" @@ -11448,6 +11448,14 @@ file-loader@4.3.0, file-loader@^4.2.0: loader-utils "^1.2.3" schema-utils "^2.5.0" +file-loader@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" + integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + file-selector@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.2.2.tgz#76186ac94ea01a18262a1e9ee36a8815911bc0b4" @@ -15130,6 +15138,11 @@ merge-descriptors@1.0.1: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= +merge-refs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/merge-refs/-/merge-refs-1.0.0.tgz#388348bce22e623782c6df9d3c4fc55888276120" + integrity sha512-WZ4S5wqD9FCR9hxkLgvcHJCBxzXzy3VVE6p8W2OzxRzB+hLRlcadGE2bW9xp2KSzk10rvp4y+pwwKO6JQVguMg== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -16472,10 +16485,10 @@ pbkdf2@^3.0.17, pbkdf2@^3.0.3, pbkdf2@^3.0.9: safe-buffer "^5.0.1" sha.js "^2.4.8" -pdfjs-dist@2.4.456: - version "2.4.456" - resolved "https://registry.yarnpkg.com/pdfjs-dist/-/pdfjs-dist-2.4.456.tgz#0eaad2906cda866bbb393e79a0e5b4e68bd75520" - integrity sha512-yckJEHq3F48hcp6wStEpbN9McOj328Ib09UrBlGAKxvN2k+qYPN5iq6TH6jD1C0pso7zTep+g/CKsYgdrQd5QA== +pdfjs-dist@2.6.347: + version "2.6.347" + resolved "https://registry.yarnpkg.com/pdfjs-dist/-/pdfjs-dist-2.6.347.tgz#f257ed66e83be900cd0fd28524a2187fb9e25cd5" + integrity sha512-QC+h7hG2su9v/nU1wEI3SnpPIrqJODL7GTDFvR74ANKGq1AFJW16PH8VWnhpiTi9YcLSFV9xLeWSgq+ckHLdVQ== pend@~1.2.0: version "1.2.0" @@ -18105,18 +18118,19 @@ react-markdown@^5.0.3: unist-util-visit "^2.0.0" xtend "^4.0.1" -react-pdf@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/react-pdf/-/react-pdf-5.0.0.tgz#baddeecd5c5ef92ae57aed1ee141203ad14d4b5d" - integrity sha512-VpqZjpZGEevmotLYl6acU6GYQeJ0dxn9+5sth5QjWLFhKu0xy3zSZgt3U3m97zW6UWzQ/scvw5drfPyun5l4eA== +react-pdf@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/react-pdf/-/react-pdf-5.3.0.tgz#29f128c70fdb2397ce9d65f3402e762847259f9d" + integrity sha512-9/8gx5uy79PTkN7+NZoFxiOYfMWoBFlUEdAPyf9ZVvOtPdumJD9z6j7O40pMSlw2vENfdNoPIo8E+iolafg4Hg== dependencies: "@babel/runtime" "^7.0.0" + file-loader "^6.0.0" make-cancellable-promise "^1.0.0" make-event-props "^1.1.0" merge-class-names "^1.1.1" - pdfjs-dist "2.4.456" + merge-refs "^1.0.0" + pdfjs-dist "2.6.347" prop-types "^15.6.2" - worker-loader "^3.0.0" react-popper-tooltip@^2.8.3: version "2.11.1" @@ -22322,14 +22336,6 @@ worker-farm@^1.7.0: dependencies: errno "~0.1.7" -worker-loader@^3.0.0: - version "3.0.5" - resolved "https://registry.yarnpkg.com/worker-loader/-/worker-loader-3.0.5.tgz#6e13a583c4120ba419eece8e4f2e098b014311bf" - integrity sha512-cOh4UqTtvT8eHpyuuTK2C66Fg/G5Pb7g11bwtKm7uyD0vj2hCGY1APlSzVD75V9ciYZt44VPbFPiSFTSLxkQ+w== - dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" - worker-rpc@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/worker-rpc/-/worker-rpc-0.1.1.tgz#cb565bd6d7071a8f16660686051e969ad32f54d5"