From 0bed9cab93c329766771060c25fdf0377e01d80f Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Tue, 18 May 2021 14:22:39 +0200 Subject: [PATCH 01/49] fix broken things --- packages/common-contexts/package.json | 2 +- .../Modules/FileBrowsers/BinFileBrowser.tsx | 22 +++++++++---------- .../Modules/FileBrowsers/CSFFileBrowser.tsx | 19 +++++++++------- .../FileBrowsers/SearchFileBrowser.tsx | 1 - .../FileBrowsers/views/FilesTable.view.tsx | 8 +++---- .../files-ui/src/Contexts/DriveContext.tsx | 20 +++++++++++++---- .../src/Contexts/FileBrowserContext.tsx | 4 ++-- yarn.lock | 8 +++---- 8 files changed, 48 insertions(+), 36 deletions(-) diff --git a/packages/common-contexts/package.json b/packages/common-contexts/package.json index e98b4bc62d..84be30f3c4 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.1", "axios": "^0.21.1", "uuid": "^8.3.1" }, diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx index f80c997999..3d518f4e55 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" -import { BucketType, FileSystemItem, useDrive } from "../../../Contexts/DriveContext" +import { FileSystemItem, useDrive } from "../../../Contexts/DriveContext" import { IBulkOperations, IFilesBrowserModuleProps } from "./types" import FilesTableView from "./views/FilesTable.view" import DragAndDrop from "../../../Contexts/DnDContext" @@ -13,14 +13,15 @@ import { ROUTE_LINKS } from "../../FilesRoutes" import { FileBrowserContext } from "../../../Contexts/FileBrowserContext" const BinFileBrowser: React.FC = ({ controls = false }: IFilesBrowserModuleProps) => { - const { removeCSFObjects, moveCSFObject, list } = useDrive() + const { removeCSFObjects, moveCSFObject, list, buckets } = useDrive() 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 bucket = useMemo(() => buckets.find(b => b.type === "trash"), [buckets]) + const refreshContents = useCallback( ( showLoading?: boolean @@ -30,7 +31,7 @@ const BinFileBrowser: React.FC = ({ controls = false } list({ path: currentPath, source: { - type: bucketType + type: bucket?.type } }).then((newContents) => { showLoading && setLoadingCurrentPath(false) @@ -54,7 +55,7 @@ const BinFileBrowser: React.FC = ({ controls = false } showLoading && setLoadingCurrentPath(false) } }, - [bucketType, list, currentPath] + [bucket, list, currentPath] ) useEffect(() => { @@ -77,7 +78,7 @@ const BinFileBrowser: React.FC = ({ controls = false } const deleteFile = useCallback(async (cid: string) => { const itemToDelete = pathContents.find((i) => i.cid === cid) - if (!itemToDelete) { + if (!itemToDelete || !bucket) { console.error("No item found to delete") return } @@ -86,7 +87,7 @@ const BinFileBrowser: React.FC = ({ controls = false } await removeCSFObjects({ paths: [`${currentPath}${itemToDelete.name}`], source: { - type: bucketType + type: bucket.type } }) refreshContents() @@ -108,7 +109,7 @@ const BinFileBrowser: React.FC = ({ controls = false } }) return Promise.reject() } - }, [addToastMessage, bucketType, currentPath, pathContents, refreshContents, removeCSFObjects]) + }, [addToastMessage, bucket, currentPath, pathContents, refreshContents, removeCSFObjects]) const deleteFiles = useCallback(async (cids: string[]) => { await Promise.all( @@ -126,9 +127,6 @@ const BinFileBrowser: React.FC = ({ controls = false } await moveCSFObject({ path: getPathWithFile("/", itemToRestore.name), new_path: getPathWithFile("/", itemToRestore.name), - source: { - type: "trash" - }, destination: { type: "csf" } @@ -175,6 +173,7 @@ const BinFileBrowser: React.FC = ({ controls = false } return ( = ({ controls = false } sourceFiles: pathContents, heading: t`Bin`, controls, - bucketType, itemOperations, bulkOperations }}> diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx index 06ddbc7f0c..5d9805a5c3 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx @@ -23,16 +23,19 @@ const CSFFileBrowser: React.FC = () => { uploadFiles, moveCSFObject, uploadsInProgress, - list + list, + buckets } = useDrive() 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 bucket = useMemo(() => { + return buckets.find(b => b.type === "csf") + }, [buckets]) const refreshContents = useCallback( ( path: string, @@ -43,7 +46,7 @@ const CSFFileBrowser: React.FC = () => { list({ path, source: { - type: bucketType + type: bucket?.type } }).then((newContents) => { showLoading && setLoadingCurrentPath(false) @@ -68,7 +71,7 @@ const CSFFileBrowser: React.FC = () => { showLoading && setLoadingCurrentPath(false) } }, - [bucketType, list] + [bucket, list] ) const { localStorageGet, localStorageSet } = useLocalStorage() const { profile } = useUser() @@ -168,10 +171,10 @@ const CSFFileBrowser: React.FC = () => { const handleDownload = useCallback(async (cid: string) => { const target = pathContents.find(item => item.cid === cid) - if (!target) return + if (!target || !bucket) return - await downloadFile(target, currentPath, bucketType) - }, [pathContents, downloadFile, currentPath, bucketType]) + await downloadFile(target, currentPath, bucket.type) + }, [pathContents, downloadFile, currentPath, bucket]) // Breadcrumbs/paths @@ -230,6 +233,7 @@ const CSFFileBrowser: React.FC = () => { return ( = () => { showUploadsInTable: true, sourceFiles: pathContents, heading: t`My Files`, - bucketType, controls: true, allowDropUpload: true, itemOperations, diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx index 0237f14510..e03069b2a1 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx @@ -129,7 +129,6 @@ const SearchFileBrowser: React.FC = ({ controls = fals controls, itemOperations, isSearch: true, - bucketType, getPath }}> diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx index 6a2854be9b..f792d515ee 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx @@ -299,9 +299,9 @@ const FilesTableView = () => { itemOperations, getPath, moduleRootPath, - bucketType, isSearch, - withSurvey + withSurvey, + bucket } = useFileBrowser() const classes = useStyles({ themeKey }) const [editing, setEditing] = useState() @@ -936,11 +936,11 @@ const FilesTableView = () => { ))} )} - {files && previewFileIndex !== undefined && ( + {files && previewFileIndex !== undefined && bucket && ( Promise createFolder: (body: FilesPathRequest) => Promise renameFile: (body: FilesMvRequest) => Promise @@ -72,7 +73,7 @@ type DriveContext = { downloadsInProgress: DownloadProgress[] spaceUsed: number getFolderTree: () => Promise - getFileInfo: (path: string) => Promise + getFileInfo: (path: string) => Promise secureAccountWithMasterPassword: (candidatePassword: string) => Promise } @@ -103,6 +104,16 @@ const DriveProvider = ({ children }: DriveContextProps) => { const [spaceUsed, setSpaceUsed] = useState(0) const [encryptionKey, setEncryptionKey] = useState() + const [buckets, setBuckets] = useState([]) + + useEffect(() => { + const fetchBuckets = async () => { + const result = await imployApiClient.listBuckets() + setBuckets(result) + } + fetchBuckets() + }, [imployApiClient]) + // Space used counter useEffect(() => { const getSpaceUsage = async () => { @@ -527,7 +538,8 @@ const DriveProvider = ({ children }: DriveContextProps) => { listBuckets, searchFiles, getFileInfo, - secureAccountWithMasterPassword + secureAccountWithMasterPassword, + buckets }} > {children} @@ -547,7 +559,7 @@ export { DriveProvider, useDrive } export type { FileSystemItem, DirectoryContentResponse, - CSFFilesFullinfoResponse as FileFullInfo, + CSFFilesFullInfoResponse as FileFullInfo, BucketType, SearchEntry } diff --git a/packages/files-ui/src/Contexts/FileBrowserContext.tsx b/packages/files-ui/src/Contexts/FileBrowserContext.tsx index f567003de5..c8b316b407 100644 --- a/packages/files-ui/src/Contexts/FileBrowserContext.tsx +++ b/packages/files-ui/src/Contexts/FileBrowserContext.tsx @@ -2,8 +2,9 @@ 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" - +import { Bucket } from "@chainsafe/files-api-client" interface FileBrowserContext extends IFilesBrowserModuleProps { + bucket?: Bucket itemOperations: {[contentType: string]: FileOperation[]} bulkOperations?: IBulkOperations @@ -24,7 +25,6 @@ interface FileBrowserContext extends IFilesBrowserModuleProps { refreshContents?: () => void currentPath: string - bucketType: BucketType loadingCurrentPath: boolean uploadsInProgress?: UploadProgress[] showUploadsInTable: boolean diff --git a/yarn.lock b/yarn.lock index 7cb9a92c00..972b9bfbca 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.1": + version "1.11.1" + resolved "https://npm.pkg.github.com/download/@chainsafe/files-api-client/1.11.1/e3f6bacbb76a09d44b755dc7a1d9b7e29aa8136a6aa648e784e89c31174abdb2#4879404c2f4d80c18448c11ab15cfff77bd0e7e5" + integrity sha512-6mvyFnqqlOUfMDdf1sSJHQzOA0/wBa4Rnt2TOfD+rV5z82tJYGADxEwCbmbQ3b8G4H01aNhIQxkoaDwFcnMp9w== "@chainsafe/web3-context@1.1.4": version "1.1.4" From 0a327c16cc9d84ed5e11026e002ac81a365bf2cc Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Thu, 20 May 2021 01:17:57 +0200 Subject: [PATCH 02/49] rewire for new apis --- packages/common-contexts/package.json | 2 +- .../src/BillingContext/BillingContext.tsx | 6 +- .../FilesApiContext.tsx} | 60 +-- .../src/FilesApiContext/index.ts | 7 + .../utils.ts | 0 .../src/ImployApiContext/index.ts | 7 - .../src/UserContext/UserContext.tsx | 13 +- packages/common-contexts/src/index.ts | 2 +- packages/files-ui/src/App.tsx | 8 +- .../src/Components/Elements/PasswordForm.tsx | 2 +- .../StrengthIndicator.tsx | 0 .../files-ui/src/Components/FilesRoutes.tsx | 4 +- .../src/Components/Layouts/AppHeader.tsx | 4 +- .../src/Components/Layouts/AppNav.tsx | 6 +- .../src/Components/Layouts/AppWrapper.tsx | 4 +- .../DownloadProgressModals/DownloadBox.tsx | 2 +- .../Modules/DownloadProgressModals/index.tsx | 2 +- .../Modules/FileBrowsers/BinFileBrowser.tsx | 129 +++---- .../Modules/FileBrowsers/CSFFileBrowser.tsx | 217 +++++------ .../CreateFolderModal.tsx} | 27 +- .../Modules/FileBrowsers/FileInfoModal.tsx | 12 +- .../Modules/FileBrowsers/MoveFileModal.tsx | 21 +- .../FileBrowsers/SearchFileBrowser.tsx | 46 +-- .../Components/Modules/FileBrowsers/types.ts | 6 +- .../FileSystemItem/FileSystemGridItem.tsx | 2 +- .../views/FileSystemItem/FileSystemItem.tsx | 2 +- .../FileSystemItem/FileSystemTableItem.tsx | 2 +- .../FileBrowsers/views/FilesTable.view.tsx | 18 +- .../Components/Modules/FilePreviewModal.tsx | 17 +- .../Modules/LoginModule/InitialScreen.tsx | 4 +- .../Modules/LoginModule/MigrateAccount.tsx | 6 +- .../SequenceSlides/SetMasterKey.slide.tsx | 233 ------------ .../src/Components/Modules/SearchModule.tsx | 39 +- .../src/Components/Modules/Settings/Plan.tsx | 2 +- .../Components/Modules/UploadFileModule.tsx | 9 +- .../UploadProgressModals/UploadBox.tsx | 2 +- .../Modules/UploadProgressModals/index.tsx | 2 +- .../src/Components/Pages/LoginPage.tsx | 4 +- .../files-ui/src/Contexts/DriveReducer.tsx | 2 +- .../src/Contexts/FileBrowserContext.tsx | 16 +- .../{DriveContext.tsx => FilesContext.tsx} | 342 ++++++++---------- .../src/Contexts/ThresholdKeyContext.tsx | 12 +- packages/files-ui/src/Utils/Helpers.tsx | 13 + packages/files-ui/src/Utils/pathUtils.ts | 2 +- yarn.lock | 8 +- 45 files changed, 499 insertions(+), 825 deletions(-) rename packages/common-contexts/src/{ImployApiContext/ImployApiContext.tsx => FilesApiContext/FilesApiContext.tsx} (86%) create mode 100644 packages/common-contexts/src/FilesApiContext/index.ts rename packages/common-contexts/src/{ImployApiContext => FilesApiContext}/utils.ts (100%) delete mode 100644 packages/common-contexts/src/ImployApiContext/index.ts rename packages/files-ui/src/Components/{Modules/MasterKeySequence/SequenceSlides => Elements}/StrengthIndicator.tsx (100%) rename packages/files-ui/src/Components/Modules/{CreateFolderModule.tsx => FileBrowsers/CreateFolderModal.tsx} (87%) delete mode 100644 packages/files-ui/src/Components/Modules/MasterKeySequence/SequenceSlides/SetMasterKey.slide.tsx rename packages/files-ui/src/Contexts/{DriveContext.tsx => FilesContext.tsx} (61%) diff --git a/packages/common-contexts/package.json b/packages/common-contexts/package.json index 84be30f3c4..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.11.1", + "@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/src/App.tsx b/packages/files-ui/src/App.tsx index 91e46eda4e..0c5a4b41e6 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 { DriveProvider } 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} > - @@ -130,7 +130,7 @@ 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..1a67a927b0 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, diff --git a/packages/files-ui/src/Components/Layouts/AppHeader.tsx b/packages/files-ui/src/Components/Layouts/AppHeader.tsx index 458c9077aa..3e3ccb65de 100644 --- a/packages/files-ui/src/Components/Layouts/AppHeader.tsx +++ b/packages/files-ui/src/Components/Layouts/AppHeader.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useState } from "react" -import { useImployApi, useUser } from "@chainsafe/common-contexts" +import { useFilesApi, useUser } from "@chainsafe/common-contexts" import { createStyles, makeStyles, useThemeSwitcher } from "@chainsafe/common-theme" import clsx from "clsx" import { @@ -154,7 +154,7 @@ interface IAppHeader { const AppHeader = ({ navOpen, setNavOpen }: IAppHeader) => { 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..e80c64812e 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 { useDrive } from "../../Contexts/FilesContext" import { createStyles, makeStyles, @@ -214,7 +214,7 @@ const AppNav: React.FC = ({ navOpen, setNavOpen }: IAppNav) => { const { spaceUsed } = useDrive() - 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..248af68f00 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 { useDrive } from "../../../Contexts/FilesContext" import DownloadBox from "./DownloadBox" const useStyles = makeStyles(({ constants, zIndex, breakpoints }: ITheme) => { diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx index 3d518f4e55..8936af6142 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" -import { FileSystemItem, useDrive } from "../../../Contexts/DriveContext" -import { IBulkOperations, IFilesBrowserModuleProps } from "./types" +import { FileSystemItem, useDrive } from "../../../Contexts/FilesContext" +import { IBulkOperations, IFileBrowserModuleProps } from "./types" import FilesTableView from "./views/FilesTable.view" import DragAndDrop from "../../../Contexts/DnDContext" import { t } from "@lingui/macro" @@ -11,9 +11,12 @@ import { 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, buckets } = useDrive() +const BinFileBrowser: React.FC = ({ controls = false }: IFileBrowserModuleProps) => { + const { buckets } = useDrive() + const { filesApiClient } = useFilesApi() const { addToastMessage } = useToaster() const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) const [pathContents, setPathContents] = useState([]) @@ -26,36 +29,24 @@ const BinFileBrowser: React.FC = ({ controls = false } ( showLoading?: boolean ) => { + if (!bucket) return try { showLoading && setLoadingCurrentPath(true) - list({ - path: currentPath, - source: { - type: bucket?.type - } - }).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) } }, - [bucket, list, currentPath] + [bucket, currentPath, filesApiClient] ) useEffect(() => { @@ -84,7 +75,7 @@ const BinFileBrowser: React.FC = ({ controls = false } } try { - await removeCSFObjects({ + await filesApiClient.removeFPSObjects(bucket.id, { paths: [`${currentPath}${itemToDelete.name}`], source: { type: bucket.type @@ -109,7 +100,7 @@ const BinFileBrowser: React.FC = ({ controls = false } }) return Promise.reject() } - }, [addToastMessage, bucket, currentPath, pathContents, refreshContents, removeCSFObjects]) + }, [addToastMessage, bucket, currentPath, pathContents, refreshContents, filesApiClient]) const deleteFiles = useCallback(async (cids: string[]) => { await Promise.all( @@ -119,47 +110,44 @@ const BinFileBrowser: React.FC = ({ controls = false } refreshContents() }, [deleteFile, refreshContents]) + const recoverItems = useCallback(async (cids: string[]) => { + if (!bucket) return Promise.reject() - 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), - 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.reject() - } - }, [addToastMessage, moveCSFObject, pathContents, refreshContents]) - - const recoverFiles = useCallback(async (cids: string[]) => { return Promise.all( - cids.map((cid: string) => - recoverFile(cid) - )) - }, [recoverFile]) + cids.map(async (cid: string) => { + const itemToRestore = pathContents.find((i) => i.cid === cid) + if (!itemToRestore) throw new Error("Not found") + try { + await filesApiClient.moveFPSObject(bucket.id, { + path: getPathWithFile("/", 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() + } + })) + }, [addToastMessage, pathContents, refreshContents, filesApiClient, bucket]) const bulkOperations: IBulkOperations = useMemo(() => ({ [CONTENT_TYPES.Directory]: [], @@ -175,9 +163,8 @@ const BinFileBrowser: React.FC = ({ controls = false } = () => { +const CSFFileBrowser: React.FC = () => { const { downloadFile, - renameFile, - moveFile, uploadFiles, - moveCSFObject, uploadsInProgress, - list, buckets } = useDrive() + const { filesApiClient } = useFilesApi() const { addToastMessage } = useToaster() const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) const [pathContents, setPathContents] = useState([]) @@ -33,48 +30,27 @@ const CSFFileBrowser: React.FC = () => { const { pathname } = useLocation() const [currentPath, setCurrentPath] = useState(extractDrivePath(pathname.split("/").slice(1).join("/"))) - const bucket = useMemo(() => { - return buckets.find(b => b.type === "csf") - }, [buckets]) - const refreshContents = useCallback( - ( - path: string, - showLoading?: boolean - ) => { - try { - showLoading && setLoadingCurrentPath(true) - list({ - path, - source: { - type: bucket?.type - } - }).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) - } - }, - [bucket, 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( @@ -92,9 +68,8 @@ const CSFFileBrowser: React.FC = () => { }, [localStorageGet, localStorageSet]) useEffect(() => { - refreshContents(currentPath, true) - // eslint-disable-next-line - }, []) + refreshContents(true) + }, [bucket]) useEffect(() => { let drivePath = extractDrivePath(pathname) @@ -103,92 +78,94 @@ const CSFFileBrowser: React.FC = () => { } if (drivePath !== currentPath) { setCurrentPath(decodeURI(drivePath)) - refreshContents(decodeURI(drivePath), true) + refreshContents(true) } }, [refreshContents, pathname, currentPath]) - // From drive - const moveFileToTrash = useCallback(async (cid: string) => { - const itemToDelete = pathContents.find((i) => i.cid === cid) - - if (!itemToDelete) { - console.error("No item found to move to the trash") - return - } - - try { - await moveCSFObject({ - path: getPathWithFile(currentPath, itemToDelete.name), - new_path: getPathWithFile("/", itemToDelete.name), - destination: { - type: "trash" + const moveFilesToBin = 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 renameItem = useCallback(async (cid: string, newName: string) => { + const itemToRename = pathContents.find(i => i.cid === cid) + if (!bucket || !itemToRename) return + + await filesApiClient.moveFPSObject(bucket.id, { + path: getPathWithFile(currentPath, itemToRename.name), + new_path: getPathWithFile(currentPath, newName) }) + await refreshContents() + }, [refreshContents, filesApiClient, bucket, currentPath, pathContents]) + + const moveItems = useCallback(async (cids: string[], newPath: string) => { + if (!bucket) return - 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]) + 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 || !bucket) return + const itemToDownload = pathContents.find(item => item.cid === cid) + if (!itemToDownload || !bucket) return - await downloadFile(target, currentPath, bucket.type) + await 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: () => redirect( - ROUTE_LINKS.Drive(encodeURI(encodeURI(getPathFromArray(arrayOfPaths.slice(0, index + 1))))) + ROUTE_LINKS.Drive(encodeURI(getPathFromArray(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) { @@ -201,13 +178,13 @@ const CSFFileBrowser: React.FC = () => { appearance: "error" }) } else { - await uploadFiles(files, path) + await uploadFiles(bucket.id, files, path) // refresh contents // using reducer because user may navigate to other paths // need to check currentPath and upload path is same - refreshContents(currentPath) + refreshContents() } - }, [addToastMessage, uploadFiles, currentPath, refreshContents]) + }, [addToastMessage, uploadFiles, bucket, refreshContents]) const viewFolder = useCallback((cid: string) => { const fileSystemItem = pathContents.find(f => f.cid === cid) @@ -238,11 +215,11 @@ const CSFFileBrowser: React.FC = () => { crumbs, moduleRootPath: ROUTE_LINKS.Drive(""), currentPath, - refreshContents:() => refreshContents(currentPath), - deleteFiles: moveFilesToTrash, - downloadFile:handleDownload, - handleMove, - handleRename, + refreshContents, + deleteItems: moveFilesToBin, + downloadFile: handleDownload, + moveItems, + renameItem: renameItem, viewFolder, handleUploadOnDrop, uploadsInProgress, 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..f193da58b4 100644 --- a/packages/files-ui/src/Components/Modules/CreateFolderModule.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CreateFolderModal.tsx @@ -4,7 +4,7 @@ import { Grid, Typography } from "@chainsafe/common-components" -import { useDrive } from "../../Contexts/DriveContext" +import { useDrive } from "../../../Contexts/FilesContext" import * as yup from "yup" import { createStyles, @@ -13,11 +13,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 +71,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,10 +121,11 @@ 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 }) + await filesApiClient.addFPSDirectory(bucket.id, { path: currentPath + values.name }) refreshContents && refreshContents() setCreatingFolder(false) helpers.resetForm() @@ -203,4 +204,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..3a02b8669f 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 { useDrive, 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,19 +162,19 @@ 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 { diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/MoveFileModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/MoveFileModal.tsx index 394a361908..9ab0b99879 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, bucket } = 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 e03069b2a1..d6a24b5c76 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" -import { BucketType, FileSystemItem, SearchEntry, useDrive } from "../../../Contexts/DriveContext" -import { IFilesBrowserModuleProps, IFilesTableBrowserProps } from "./types" +import { BucketType, FileSystemItem, SearchEntry, useDrive } from "../../../Contexts/FilesContext" +import { IFileBrowserModuleProps, IFilesTableBrowserProps } from "./types" import FilesTableView from "./views/FilesTable.view" import { CONTENT_TYPES } from "../../../Utils/Constants" import DragAndDrop from "../../../Contexts/DnDContext" @@ -8,41 +8,25 @@ import { useHistory, useLocation, useToaster } from "@chainsafe/common-component 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 } = useDrive() 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) 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/FileSystemItem/FileSystemGridItem.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemGridItem.tsx index de4a0b4402..7d069243c5 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" 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 07af776323..b3b7877b56 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 @@ -30,7 +30,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 } from "../../../../../Contexts/FilesContext" import { useFileBrowser } from "../../../../../Contexts/FileBrowserContext" const useStyles = makeStyles(({ breakpoints, constants }: CSFTheme) => { 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 6c4077bcc7..79d8617e65 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" diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx index f792d515ee..0cb08b2880 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.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 { FileSystemItem } from "../../../../Contexts/FilesContext" import FileSystemItemRow 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" @@ -286,9 +286,9 @@ const FilesTableView = () => { handleUploadOnDrop, bulkOperations, crumbs, - handleRename, - deleteFiles, - recoverFiles, + renameItem: handleRename, + deleteItems: deleteFiles, + recoverItems, viewFolder, currentPath, refreshContents, @@ -536,16 +536,16 @@ const FilesTableView = () => { }, [deleteFiles, selectedCids]) const handleRecoverFiles = useCallback(() => { - 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) => { @@ -973,7 +973,7 @@ const FilesTableView = () => { { refreshContents && ( <> - setCreateFolderModalOpen(false)} /> diff --git a/packages/files-ui/src/Components/Modules/FilePreviewModal.tsx b/packages/files-ui/src/Components/Modules/FilePreviewModal.tsx index bd2b45dc11..c0ca96a886 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 { BucketType, FileSystemItem, useDrive } 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 @@ -163,6 +164,7 @@ interface Props { const FilePreviewModal = ({ file, nextFile, previousFile, closePreview, path, bucketType }: Props) => { const classes = useStyles() const { getFileContent, downloadFile } = useDrive() + const { bucket } = useFileBrowser() const { desktop } = useThemeSwitcher() const [isLoading, setIsLoading] = useState(false) const [loadingProgress, setLoadingProgress] = useState(0) @@ -186,7 +188,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 +200,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) { @@ -264,7 +265,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 +273,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 +429,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..85424645dc 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" @@ -138,7 +138,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..96a48ea55f 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 { useDrive } from "../../../Contexts/FilesContext" +import { useFilesApi } from "@chainsafe/common-contexts" import { useThresholdKey } from "../../../Contexts/ThresholdKeyContext" import ConciseExplainer from "./ConciseExplainer" import { CSFTheme } from "../../../Themes/types" @@ -94,7 +94,7 @@ const MigrateAccount: React.FC = ({ className }: IMigrateAccount) => { const classes = useStyles() - const { validateMasterPassword } = useImployApi() + const { validateMasterPassword } = useFilesApi() const { secureAccountWithMasterPassword } = useDrive() const { addPasswordShare, logout } = useThresholdKey() const [migrateState, setMigrateState] = useState<"migrate"|"explainer"|"complete">("migrate") 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/SearchModule.tsx b/packages/files-ui/src/Components/Modules/SearchModule.tsx index 9c7e9b731f..04104ddda5 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 { useDrive, 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 } = useDrive() 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), []) 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..09adf0a259 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 { useDrive } from "../../../Contexts/FilesContext" import { Trans } from "@lingui/macro" import { ROUTE_LINKS } from "../../FilesRoutes" diff --git a/packages/files-ui/src/Components/Modules/UploadFileModule.tsx b/packages/files-ui/src/Components/Modules/UploadFileModule.tsx index 2d2de07e16..bddf7ed91e 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 { useDrive } from "../../Contexts/FilesContext" import { createStyles, makeStyles } from "@chainsafe/common-theme" import React, { useCallback, useState } from "react" import { Formik, Form } from "formik" @@ -78,7 +78,7 @@ const UploadFileModule = ({ modalOpen, close }: IUploadFileModuleProps) => { const classes = useStyles() const [isDoneDisabled, setIsDoneDisabled] = useState(true) const { uploadFiles } = useDrive() - const { currentPath, refreshContents } = useFileBrowser() + const { currentPath, refreshContents, bucket } = useFileBrowser() const UploadSchema = object().shape({ files: array().required(t`Please select a file to upload`) }) @@ -87,9 +87,10 @@ const UploadFileModule = ({ modalOpen, close }: IUploadFileModuleProps) => { }, []) const onSubmit = useCallback(async (values, helpers) => { + if (!bucket) return helpers.setSubmitting(true) try { - await uploadFiles(values.files, currentPath) + await uploadFiles(bucket.id, values.files, currentPath) helpers.resetForm() refreshContents && refreshContents() close() @@ -101,7 +102,7 @@ const UploadFileModule = ({ modalOpen, close }: IUploadFileModuleProps) => { } } helpers.setSubmitting(false) - }, [close, currentPath, uploadFiles, refreshContents]) + }, [close, currentPath, uploadFiles, refreshContents, bucket]) return ( { 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/DriveReducer.tsx b/packages/files-ui/src/Contexts/DriveReducer.tsx index dca5ecdda0..96f5661d30 100644 --- a/packages/files-ui/src/Contexts/DriveReducer.tsx +++ b/packages/files-ui/src/Contexts/DriveReducer.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/FileBrowserContext.tsx b/packages/files-ui/src/Contexts/FileBrowserContext.tsx index c8b316b407..f02f1f06d2 100644 --- a/packages/files-ui/src/Contexts/FileBrowserContext.tsx +++ b/packages/files-ui/src/Contexts/FileBrowserContext.tsx @@ -1,19 +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" +import { FileOperation, IBulkOperations, IFileBrowserModuleProps } from "../Components/Modules/FileBrowsers/types" +import { FileSystemItem, UploadProgress } from "./FilesContext" import { Bucket } from "@chainsafe/files-api-client" -interface FileBrowserContext extends IFilesBrowserModuleProps { +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 diff --git a/packages/files-ui/src/Contexts/DriveContext.tsx b/packages/files-ui/src/Contexts/FilesContext.tsx similarity index 61% rename from packages/files-ui/src/Contexts/DriveContext.tsx rename to packages/files-ui/src/Contexts/FilesContext.tsx index 213bb5cf21..c577dd1063 100644 --- a/packages/files-ui/src/Contexts/DriveContext.tsx +++ b/packages/files-ui/src/Contexts/FilesContext.tsx @@ -1,17 +1,14 @@ import { 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" @@ -21,7 +18,7 @@ import { readFileAsync } from "../Utils/Helpers" import { useBeforeunload } from "react-beforeunload" import { useThresholdKey } from "./ThresholdKeyContext" -type DriveContextProps = { +type FilesContextProps = { children: React.ReactNode | React.ReactNode[] } @@ -51,29 +48,19 @@ interface GetFileContentParams { onDownloadProgress?: (progressEvent: ProgressEvent) => void file: FileSystemItem path: string - bucketType: BucketType } +type Bucket = FilesBucket & { encryptionKey: string} -type DriveContext = { +type FilesContext = { buckets: Bucket[] - 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 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 } @@ -87,39 +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(() => { - const fetchBuckets = async () => { - const result = await imployApiClient.listBuckets() - setBuckets(result) - } - fetchBuckets() - }, [imployApiClient]) + 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.uploaded_size}, 0) + + setSpaceUsed(totalSize) } catch (error) { console.error(error) } @@ -127,12 +137,13 @@ const DriveProvider = ({ children }: DriveContextProps) => { if (isLoggedIn) { getSpaceUsage() } - }, [imployApiClient, isLoggedIn]) + }, [filesApiClient, isLoggedIn, buckets]) // Reset password on log out useEffect(() => { if (!isLoggedIn) { - setEncryptionKey(undefined) + setPersonalEncryptionKey(undefined) + setBuckets([]) } }, [isLoggedIn]) @@ -144,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) @@ -156,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") @@ -183,7 +194,7 @@ const DriveProvider = ({ children }: DriveContextProps) => { encryptForPublicKey, secureThresholdKeyAccount, decryptMessageWithThresholdKey, - encryptionKey, + personalEncryptionKey, isMasterPasswordSet ]) @@ -191,7 +202,7 @@ const DriveProvider = ({ children }: DriveContextProps) => { if (!publicKey || !validateMasterPassword(candidatePassword)) return const encryptedKey = await encryptForPublicKey(publicKey, candidatePassword) - setEncryptionKey(candidatePassword) + setPersonalEncryptionKey(candidatePassword) secureThresholdKeyAccount(encryptedKey) } @@ -223,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 } @@ -247,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 @@ -262,10 +274,12 @@ const DriveProvider = ({ children }: DriveContextProps) => { }) } // API call - await imployApiClient.addCSFFiles( + await filesApiClient.addFPSFiles( + bucketId, filesParam, path, - "", + undefined, + undefined, undefined, undefined, (progressEvent: { loaded: number; total: number }) => { @@ -305,99 +319,78 @@ 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" - }) - } - - 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 ({ cid, cancelToken, onDownloadProgress, file, path, bucketType }: GetFileContentParams) => { - if (!encryptionKey) { - throw new Error("No encryption key") + }, [addToastMessage, filesApiClient, buckets]) + + // const createFolder = async (body: FilesPathRequest) => { + // try { + // const result = await filesApiClient.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 renameFile = useCallback(async (body: FilesMvRequest) => { + // try { + // if (body.path !== body.new_path) { + // await filesApiClient.moveCSFObject(body) + // addToastMessage({ + // message: t`File renamed successfully`, + // appearance: "success" + // }) + // } + + // return Promise.resolve() + // } catch (error) { + // addToastMessage({ + // message: t`There was an error renaming this file`, + // appearance: "error" + // }) + // return Promise.reject() + // } + // }, [addToastMessage, filesApiClient]) + + // const moveFile = useCallback(async (body: FilesMvRequest) => { + // try { + // await filesApiClient.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, filesApiClient]) + + // 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) + + 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 @@ -410,11 +403,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, @@ -426,7 +419,7 @@ const DriveProvider = ({ children }: DriveContextProps) => { } else { const decrypted = await decryptFile( await result.data.arrayBuffer(), - encryptionKey + bucket.encryptionKey ) if (decrypted) { return new Blob([decrypted], { @@ -438,9 +431,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 = { @@ -451,9 +444,8 @@ 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, onDownloadProgress: (progressEvent) => { @@ -491,71 +483,35 @@ 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 context = React.useContext(FilesContext) if (context === undefined) { throw new Error("useDrive must be used within a DriveProvider") } return context } -export { DriveProvider, useDrive } +export { FilesProvider as DriveProvider, useDrive } export type { FileSystemItem, DirectoryContentResponse, 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 3554703e36..26ed565bc3 100644 --- a/packages/files-ui/src/Utils/pathUtils.ts +++ b/packages/files-ui/src/Utils/pathUtils.ts @@ -42,7 +42,7 @@ 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 } diff --git a/yarn.lock b/yarn.lock index 972b9bfbca..3b99513e16 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.11.1": - version "1.11.1" - resolved "https://npm.pkg.github.com/download/@chainsafe/files-api-client/1.11.1/e3f6bacbb76a09d44b755dc7a1d9b7e29aa8136a6aa648e784e89c31174abdb2#4879404c2f4d80c18448c11ab15cfff77bd0e7e5" - integrity sha512-6mvyFnqqlOUfMDdf1sSJHQzOA0/wBa4Rnt2TOfD+rV5z82tJYGADxEwCbmbQ3b8G4H01aNhIQxkoaDwFcnMp9w== +"@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" From cce919866ba9244e079b4e6a71af4efd502daf07 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 19 May 2021 23:22:44 +0000 Subject: [PATCH 03/49] lingui extract --- packages/files-ui/src/locales/en/messages.po | 24 -------------------- packages/files-ui/src/locales/fr/messages.po | 24 -------------------- 2 files changed, 48 deletions(-) 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" From 6bbb233360620bd89737c673f938979df5ebd02a Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Thu, 20 May 2021 09:28:45 +0200 Subject: [PATCH 04/49] clean up linting errors --- .../src/Components/Modules/FileBrowsers/BinFileBrowser.tsx | 1 - .../src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx | 2 +- .../Components/Modules/FileBrowsers/CreateFolderModal.tsx | 1 - .../src/Components/Modules/FileBrowsers/FileInfoModal.tsx | 2 +- .../src/Components/Modules/FileBrowsers/MoveFileModal.tsx | 2 +- .../Components/Modules/FileBrowsers/SearchFileBrowser.tsx | 2 +- .../Modules/FileBrowsers/views/FilesTable.view.tsx | 1 - .../files-ui/src/Components/Modules/FilePreviewModal.tsx | 7 +++---- 8 files changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx index 8936af6142..29fb8d9fe8 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx @@ -6,7 +6,6 @@ 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 { extractDrivePath, getPathWithFile } from "../../../Utils/pathUtils" import { ROUTE_LINKS } from "../../FilesRoutes" diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx index 7a2c56ea2b..b9acc53f00 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx @@ -69,7 +69,7 @@ const CSFFileBrowser: React.FC = () => { useEffect(() => { refreshContents(true) - }, [bucket]) + }, [bucket, refreshContents]) useEffect(() => { let drivePath = extractDrivePath(pathname) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CreateFolderModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CreateFolderModal.tsx index f193da58b4..f3c834231a 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/CreateFolderModal.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/FilesContext" import * as yup from "yup" import { createStyles, diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/FileInfoModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/FileInfoModal.tsx index 3a02b8669f..5904fb3329 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/FilesContext" +import { FileFullInfo } from "../../../Contexts/FilesContext" import { Button, formatBytes, diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/MoveFileModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/MoveFileModal.tsx index 9ab0b99879..db0f6432f1 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/MoveFileModal.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/MoveFileModal.tsx @@ -73,7 +73,7 @@ interface IMoveFileModuleProps { const MoveFileModule = ({ filesToMove, modalOpen, onClose, onCancel }: IMoveFileModuleProps) => { const classes = useStyles() const { filesApiClient } = useFilesApi() - const { moveItems, bucket } = useFileBrowser() + const { moveItems } = useFileBrowser() const [movingFile, setMovingFile] = useState(false) const [movePath, setMovePath] = useState(undefined) const [folderTree, setFolderTree] = useState([]) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx index d6a24b5c76..f99cc9c9e5 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" -import { BucketType, FileSystemItem, SearchEntry, useDrive } from "../../../Contexts/FilesContext" +import { FileSystemItem, SearchEntry, useDrive } from "../../../Contexts/FilesContext" import { IFileBrowserModuleProps, IFilesTableBrowserProps } from "./types" import FilesTableView from "./views/FilesTable.view" import { CONTENT_TYPES } from "../../../Utils/Constants" diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx index 0cb08b2880..fcc3368db9 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx @@ -940,7 +940,6 @@ const FilesTableView = () => { 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 { bucket } = useFileBrowser() @@ -230,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 && From 6bda3623c8077c91ea091240fc6d0db1b66f1d06 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Thu, 20 May 2021 14:03:59 +0200 Subject: [PATCH 05/49] Align paths and test --- .../Modules/FileBrowsers/CSFFileBrowser.tsx | 34 ++++++++++++------- .../FileBrowsers/CreateFolderModal.tsx | 4 +-- .../views/FileSystemItem/FileSystemItem.tsx | 18 +++------- .../FileBrowsers/views/FilesTable.view.tsx | 2 +- .../Components/Modules/UploadFileModule.tsx | 4 +-- .../files-ui/src/Contexts/FilesContext.tsx | 11 +++--- packages/files-ui/src/Utils/pathUtils.ts | 5 +-- 7 files changed, 40 insertions(+), 38 deletions(-) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx index b9acc53f00..2bf3f7b82b 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" import { Crumb, useToaster, useHistory, useLocation } from "@chainsafe/common-components" import { useDrive, FileSystemItem } from "../../../Contexts/FilesContext" -import { extractDrivePath, getArrayOfPaths, getPathFromArray, getPathWithFile } from "../../../Utils/pathUtils" +import { extractDrivePath, getArrayOfPaths, getURISafePathFromArray, getPathWithFile } from "../../../Utils/pathUtils" import { IBulkOperations, IFileBrowserModuleProps, IFilesTableBrowserProps } from "./types" import FilesTableView from "./views/FilesTable.view" import { CONTENT_TYPES } from "../../../Utils/Constants" @@ -77,12 +77,17 @@ const CSFFileBrowser: React.FC = () => { drivePath = "/" + drivePath } if (drivePath !== currentPath) { - setCurrentPath(decodeURI(drivePath)) + if (drivePath === "/") { + setCurrentPath(drivePath) + } else { + setCurrentPath(decodeURIComponent(drivePath.slice(0, -1))) + } + refreshContents(true) } }, [refreshContents, pathname, currentPath]) - const moveFilesToBin = useCallback(async (cids: string[]) => { + const moveItemsToBin = useCallback(async (cids: string[]) => { if (!bucket) return await Promise.all( cids.map(async (cid: string) => { @@ -134,7 +139,6 @@ const CSFFileBrowser: React.FC = () => { 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) @@ -156,11 +160,13 @@ const CSFFileBrowser: React.FC = () => { // 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(getPathFromArray(arrayOfPaths.slice(0, index + 1)))) + ROUTE_LINKS.Drive(getURISafePathFromArray(arrayOfPaths.slice(0, index + 1))) ) + } })), [arrayOfPaths, redirect]) @@ -179,9 +185,6 @@ const CSFFileBrowser: React.FC = () => { }) } else { await uploadFiles(bucket.id, files, path) - // refresh contents - // using reducer because user may navigate to other paths - // need to check currentPath and upload path is same refreshContents() } }, [addToastMessage, uploadFiles, bucket, refreshContents]) @@ -189,7 +192,14 @@ const CSFFileBrowser: React.FC = () => { 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 = "" + } + console.log(ROUTE_LINKS.Drive(`${urlSafePath}/${encodeURIComponent(`${fileSystemItem.name}`)}`)) + redirect(ROUTE_LINKS.Drive(`${urlSafePath}/${encodeURIComponent(`${fileSystemItem.name}`)}`)) } }, [currentPath, pathContents, redirect]) @@ -216,7 +226,7 @@ const CSFFileBrowser: React.FC = () => { moduleRootPath: ROUTE_LINKS.Drive(""), currentPath, refreshContents, - deleteItems: moveFilesToBin, + deleteItems: moveItemsToBin, downloadFile: handleDownload, moveItems, renameItem: renameItem, diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CreateFolderModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CreateFolderModal.tsx index f3c834231a..7843d76297 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/CreateFolderModal.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CreateFolderModal.tsx @@ -124,8 +124,8 @@ const CreateFolderModal: React.FC = ({ helpers.setSubmitting(true) try { setCreatingFolder(true) - await filesApiClient.addFPSDirectory(bucket.id, { path: currentPath + values.name }) - refreshContents && refreshContents() + await filesApiClient.addFPSDirectory(bucket.id, { path: `${currentPath}/${values.name}` }) + refreshContents && await refreshContents() setCreatingFolder(false) helpers.resetForm() close() 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 b3b7877b56..b9278acbbe 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 @@ -311,16 +311,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]) @@ -337,13 +327,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( @@ -351,7 +341,7 @@ const FileSystemItemRow = ({ if (desktop) { // on desktop if (isFolder) { - onFolderNavigation() + viewFolder && viewFolder(file.cid) } else { onFilePreview() } @@ -360,7 +350,7 @@ const FileSystemItemRow = ({ return } }, - [desktop, onFolderNavigation, onFilePreview, isFolder] + [desktop, viewFolder, file, onFilePreview, isFolder] ) const { click } = useDoubleClick(onSingleClick, onDoubleClick) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx index fcc3368db9..d36c39992c 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx @@ -347,7 +347,7 @@ const FilesTableView = () => { const files = useMemo(() => items.filter((i) => !i.isFolder), [items]) const selectedFiles = useMemo( - () => files.filter((file) => selectedCids.includes(file.cid)), + () => items.filter((file) => selectedCids.includes(file.cid)), [files, selectedCids] ) diff --git a/packages/files-ui/src/Components/Modules/UploadFileModule.tsx b/packages/files-ui/src/Components/Modules/UploadFileModule.tsx index bddf7ed91e..436d714c9c 100644 --- a/packages/files-ui/src/Components/Modules/UploadFileModule.tsx +++ b/packages/files-ui/src/Components/Modules/UploadFileModule.tsx @@ -90,10 +90,10 @@ const UploadFileModule = ({ modalOpen, close }: IUploadFileModuleProps) => { if (!bucket) return helpers.setSubmitting(true) try { + close() await uploadFiles(bucket.id, values.files, currentPath) - helpers.resetForm() refreshContents && refreshContents() - close() + helpers.resetForm() } catch (errors) { if (errors[0].message.includes("conflict with existing")) { helpers.setFieldError("files", "File/Folder exists") diff --git a/packages/files-ui/src/Contexts/FilesContext.tsx b/packages/files-ui/src/Contexts/FilesContext.tsx index c577dd1063..675eb9c984 100644 --- a/packages/files-ui/src/Contexts/FilesContext.tsx +++ b/packages/files-ui/src/Contexts/FilesContext.tsx @@ -273,15 +273,16 @@ const FilesProvider = ({ children }: FilesContextProps) => { appearance: "error" }) } - // API call - await filesApiClient.addFPSFiles( - bucketId, + + // TODO: Update this once API support for FPS is working + await filesApiClient.addCSFFiles( + // bucketId, filesParam, path, + "csf", undefined, undefined, - undefined, - undefined, + // undefined, (progressEvent: { loaded: number; total: number }) => { dispatchUploadsInProgress({ type: "progress", diff --git a/packages/files-ui/src/Utils/pathUtils.ts b/packages/files-ui/src/Utils/pathUtils.ts index 26ed565bc3..c4bc3055d4 100644 --- a/packages/files-ui/src/Utils/pathUtils.ts +++ b/packages/files-ui/src/Utils/pathUtils.ts @@ -22,10 +22,11 @@ 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("/") + console.log("/" + arrayOfPaths.map(encodeURIComponent).join("/")) + return "/" + arrayOfPaths.map(encodeURIComponent).join("/") } } From 5b2d4661074d512feea8e11da338f67e5380e6b2 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Thu, 20 May 2021 17:17:37 +0200 Subject: [PATCH 06/49] post merge fixes --- .../Modules/DownloadProgressModals/index.tsx | 23 +++++++++++-------- .../Modules/FileBrowsers/BinFileBrowser.tsx | 4 ++-- .../Modules/FileBrowsers/CSFFileBrowser.tsx | 4 ++-- .../FileBrowsers/SearchFileBrowser.tsx | 4 ++-- .../FileBrowsers/views/DragPreviewLayer.tsx | 2 +- .../views/FileSystemItem/FileSystemItem.tsx | 16 +++---------- .../{FilesTable.view.tsx => FilesList.tsx} | 6 ++--- .../Modules/UploadProgressModals/index.tsx | 8 ++++--- .../files-ui/src/Contexts/FilesContext.tsx | 2 +- .../{DriveReducer.tsx => FilesReducers.tsx} | 0 10 files changed, 32 insertions(+), 37 deletions(-) rename packages/files-ui/src/Components/Modules/FileBrowsers/views/{FilesTable.view.tsx => FilesList.tsx} (99%) rename packages/files-ui/src/Contexts/{DriveReducer.tsx => FilesReducers.tsx} (100%) diff --git a/packages/files-ui/src/Components/Modules/DownloadProgressModals/index.tsx b/packages/files-ui/src/Components/Modules/DownloadProgressModals/index.tsx index 248af68f00..2da87d2861 100644 --- a/packages/files-ui/src/Components/Modules/DownloadProgressModals/index.tsx +++ b/packages/files-ui/src/Components/Modules/DownloadProgressModals/index.tsx @@ -27,16 +27,19 @@ const DownloadProgressModals: React.FC = () => { const classes = useStyles() const { downloadsInProgress } = useDrive() - return ( -
- {downloadsInProgress.map((downloadInProgress) => ( - - ))} -
- ) + if (downloadsInProgress.length > 0) { + return ( +
+ {downloadsInProgress.map((downloadInProgress) => ( + + ))} +
+ )} else { + return null + } } export default DownloadProgressModals diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx index 29fb8d9fe8..db47dbeabf 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" import { FileSystemItem, useDrive } from "../../../Contexts/FilesContext" import { IBulkOperations, IFileBrowserModuleProps } from "./types" -import FilesTableView from "./views/FilesTable.view" +import FilesList from "./views/FilesList" import DragAndDrop from "../../../Contexts/DnDContext" import { t } from "@lingui/macro" import { CONTENT_TYPES } from "../../../Utils/Constants" @@ -176,7 +176,7 @@ const BinFileBrowser: React.FC = ({ controls = false }: bulkOperations }}> - +
) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx index 2bf3f7b82b..e631d9dba4 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx @@ -3,7 +3,7 @@ import { Crumb, useToaster, useHistory, useLocation } from "@chainsafe/common-co import { useDrive, FileSystemItem } from "../../../Contexts/FilesContext" import { extractDrivePath, getArrayOfPaths, getURISafePathFromArray, getPathWithFile } from "../../../Utils/pathUtils" import { IBulkOperations, IFileBrowserModuleProps, IFilesTableBrowserProps } from "./types" -import FilesTableView from "./views/FilesTable.view" +import FilesList from "./views/FilesList" import { CONTENT_TYPES } from "../../../Utils/Constants" import DragAndDrop from "../../../Contexts/DnDContext" import { t } from "@lingui/macro" @@ -243,7 +243,7 @@ const CSFFileBrowser: React.FC = () => { withSurvey: showSurvey && olderThanOneWeek }}> - +
) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx index f99cc9c9e5..84a848b9dc 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" import { FileSystemItem, SearchEntry, useDrive } from "../../../Contexts/FilesContext" import { IFileBrowserModuleProps, IFilesTableBrowserProps } from "./types" -import FilesTableView from "./views/FilesTable.view" +import FilesList from "./views/FilesList" import { CONTENT_TYPES } from "../../../Utils/Constants" import DragAndDrop from "../../../Contexts/DnDContext" import { useHistory, useLocation, useToaster } from "@chainsafe/common-components" @@ -116,7 +116,7 @@ const SearchFileBrowser: React.FC = ({ controls = false getPath }}> - +
) 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/FileSystemItem.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/FileSystemItem.tsx index 88c658c5a0..41e225f296 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" @@ -143,7 +141,7 @@ const FileSystemItemRow = ({ browserView, resetSelectedFiles }: IFileSystemItemRowProps) => { - const { downloadFile, currentPath, handleUploadOnDrop, moduleRootPath, handleMove } = useFileBrowser() + 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() 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 99% 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 ee871d9e7c..3689dc9c89 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesTable.view.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx @@ -277,7 +277,7 @@ const useStyles = makeStyles( const sortFoldersFirst = (a: FileSystemItem, b: FileSystemItem) => a.isFolder && a.content_type !== b.content_type ? -1 : 1 -const FilesTableView = () => { +const FilesList = () => { const { themeKey, desktop } = useThemeSwitcher() const { @@ -349,7 +349,7 @@ const FilesTableView = () => { const selectedFiles = useMemo( () => items.filter((file) => selectedCids.includes(file.cid)), - [files, selectedCids] + [selectedCids, items] ) const handleSortToggle = ( @@ -1011,4 +1011,4 @@ const FilesTableView = () => { ) } -export default FilesTableView +export default FilesList diff --git a/packages/files-ui/src/Components/Modules/UploadProgressModals/index.tsx b/packages/files-ui/src/Components/Modules/UploadProgressModals/index.tsx index 15c3a53f1c..4b31597fcb 100644 --- a/packages/files-ui/src/Components/Modules/UploadProgressModals/index.tsx +++ b/packages/files-ui/src/Components/Modules/UploadProgressModals/index.tsx @@ -33,8 +33,8 @@ const UploadProgressModals: React.FC = () => { const { uploadsInProgress } = useDrive() const { desktop } = useThemeSwitcher() - return ( -
+ if (uploadsInProgress.length > 0) { + return (
{uploadsInProgress.map( (uploadInProgress) => (desktop || uploadInProgress.complete || uploadInProgress.error) && ( @@ -45,7 +45,9 @@ const UploadProgressModals: React.FC = () => { ) )}
- ) + )} else { + return null + } } export default UploadProgressModals diff --git a/packages/files-ui/src/Contexts/FilesContext.tsx b/packages/files-ui/src/Contexts/FilesContext.tsx index 675eb9c984..587d07b432 100644 --- a/packages/files-ui/src/Contexts/FilesContext.tsx +++ b/packages/files-ui/src/Contexts/FilesContext.tsx @@ -11,7 +11,7 @@ import { useState } from "react" 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" diff --git a/packages/files-ui/src/Contexts/DriveReducer.tsx b/packages/files-ui/src/Contexts/FilesReducers.tsx similarity index 100% rename from packages/files-ui/src/Contexts/DriveReducer.tsx rename to packages/files-ui/src/Contexts/FilesReducers.tsx From 331deef8a7a80893d5d6e65f21eb4c606c8aab46 Mon Sep 17 00:00:00 2001 From: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Date: Fri, 21 May 2021 07:11:39 +1200 Subject: [PATCH 07/49] Apply suggestions from code review Co-authored-by: Tanmoy Basak Anjan --- .../src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx index e631d9dba4..1c272c2a30 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx @@ -198,7 +198,6 @@ const CSFFileBrowser: React.FC = () => { } else { urlSafePath = "" } - console.log(ROUTE_LINKS.Drive(`${urlSafePath}/${encodeURIComponent(`${fileSystemItem.name}`)}`)) redirect(ROUTE_LINKS.Drive(`${urlSafePath}/${encodeURIComponent(`${fileSystemItem.name}`)}`)) } }, [currentPath, pathContents, redirect]) From d96583b4a05250dce5c1c891fa07d646adcbab63 Mon Sep 17 00:00:00 2001 From: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Date: Fri, 21 May 2021 07:12:58 +1200 Subject: [PATCH 08/49] Update packages/files-ui/src/Utils/pathUtils.ts Co-authored-by: Tanmoy Basak Anjan --- packages/files-ui/src/Utils/pathUtils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/files-ui/src/Utils/pathUtils.ts b/packages/files-ui/src/Utils/pathUtils.ts index b39407e331..594153af6c 100644 --- a/packages/files-ui/src/Utils/pathUtils.ts +++ b/packages/files-ui/src/Utils/pathUtils.ts @@ -25,7 +25,6 @@ export function getArrayOfPaths(path: string): string[] { export function getURISafePathFromArray(arrayOfPaths: string[]): string { if (!arrayOfPaths.length) return "/" else { - console.log("/" + arrayOfPaths.map(encodeURIComponent).join("/")) return "/" + arrayOfPaths.map(encodeURIComponent).join("/") } } @@ -48,4 +47,4 @@ export function getParentPathFromFilePath(filePath: string) { export function extractDrivePath(pathname: string) { return pathname.split("/").slice(2).join("/").concat("/") -} \ No newline at end of file +} From 7cb0cb0f455cbe2d62f0c60b830ca4cea04cf05d Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 21 May 2021 14:29:28 +0200 Subject: [PATCH 09/49] update context and hook names --- packages/files-ui/src/App.tsx | 6 +- .../src/Components/Layouts/AppNav.tsx | 4 +- .../Modules/DownloadProgressModals/index.tsx | 4 +- .../Modules/FileBrowsers/BinFileBrowser.tsx | 4 +- .../Modules/FileBrowsers/CSFFileBrowser.tsx | 4 +- .../FileBrowsers/SearchFileBrowser.tsx | 4 +- .../Components/Modules/FilePreviewModal.tsx | 4 +- .../Modules/LoginModule/MigrateAccount.tsx | 4 +- .../src/Components/Modules/SearchModule.tsx | 5 +- .../src/Components/Modules/Settings/Plan.tsx | 4 +- .../Components/Modules/UploadFileModule.tsx | 4 +- .../Modules/UploadProgressModals/index.tsx | 4 +- .../files-ui/src/Contexts/FilesContext.tsx | 72 ++----------------- 13 files changed, 32 insertions(+), 91 deletions(-) diff --git a/packages/files-ui/src/App.tsx b/packages/files-ui/src/App.tsx index 0c5a4b41e6..6548568ab9 100644 --- a/packages/files-ui/src/App.tsx +++ b/packages/files-ui/src/App.tsx @@ -5,7 +5,7 @@ import { FilesApiProvider, UserProvider, BillingProvider } from "@chainsafe/comm 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/FilesContext" +import { FilesProvider } from "./Contexts/FilesContext" import FilesRoutes from "./Components/FilesRoutes" import AppWrapper from "./Components/Layouts/AppWrapper" import { useHotjar } from "react-use-hotjar" @@ -119,7 +119,7 @@ const App: React.FC<{}> = () => { network={directAuthNetwork} > - + @@ -127,7 +127,7 @@ const App: React.FC<{}> = () => { - + diff --git a/packages/files-ui/src/Components/Layouts/AppNav.tsx b/packages/files-ui/src/Components/Layouts/AppNav.tsx index e80c64812e..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 { useFilesApi, useUser } from "@chainsafe/common-contexts" -import { useDrive } from "../../Contexts/FilesContext" +import { useFiles } from "../../Contexts/FilesContext" import { createStyles, makeStyles, @@ -212,7 +212,7 @@ const AppNav: React.FC = ({ navOpen, setNavOpen }: IAppNav) => { const { desktop } = useThemeSwitcher() const classes = useStyles() - const { spaceUsed } = useDrive() + const { spaceUsed } = useFiles() const { isLoggedIn, secured } = useFilesApi() const { publicKey, isNewDevice, shouldInitializeAccount, logout } = useThresholdKey() diff --git a/packages/files-ui/src/Components/Modules/DownloadProgressModals/index.tsx b/packages/files-ui/src/Components/Modules/DownloadProgressModals/index.tsx index 2da87d2861..cb9fe1e2a4 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/FilesContext" +import { useFiles } from "../../../Contexts/FilesContext" import DownloadBox from "./DownloadBox" const useStyles = makeStyles(({ constants, zIndex, breakpoints }: ITheme) => { @@ -25,7 +25,7 @@ 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 ( diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx index db47dbeabf..98338ce1e7 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" -import { FileSystemItem, useDrive } from "../../../Contexts/FilesContext" +import { FileSystemItem, useFiles } from "../../../Contexts/FilesContext" import { IBulkOperations, IFileBrowserModuleProps } from "./types" import FilesList from "./views/FilesList" import DragAndDrop from "../../../Contexts/DnDContext" @@ -14,7 +14,7 @@ import { useFilesApi } from "@chainsafe/common-contexts" import { parseFileContentResponse } from "../../../Utils/Helpers" const BinFileBrowser: React.FC = ({ controls = false }: IFileBrowserModuleProps) => { - const { buckets } = useDrive() + const { buckets } = useFiles() const { filesApiClient } = useFilesApi() const { addToastMessage } = useToaster() const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx index 1c272c2a30..8e0bc05984 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" import { Crumb, useToaster, useHistory, useLocation } from "@chainsafe/common-components" -import { useDrive, FileSystemItem } from "../../../Contexts/FilesContext" +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" @@ -21,7 +21,7 @@ const CSFFileBrowser: React.FC = () => { uploadFiles, uploadsInProgress, buckets - } = useDrive() + } = useFiles() const { filesApiClient } = useFilesApi() const { addToastMessage } = useToaster() const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx index 84a848b9dc..6f4b4f1e62 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SearchFileBrowser.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" -import { FileSystemItem, SearchEntry, useDrive } from "../../../Contexts/FilesContext" +import { FileSystemItem, SearchEntry, useFiles } from "../../../Contexts/FilesContext" import { IFileBrowserModuleProps, IFilesTableBrowserProps } from "./types" import FilesList from "./views/FilesList" import { CONTENT_TYPES } from "../../../Utils/Constants" @@ -14,7 +14,7 @@ import { useFilesApi } from "@chainsafe/common-contexts" const SearchFileBrowser: React.FC = ({ controls = false }: IFileBrowserModuleProps) => { const { pathname } = useLocation() const { filesApiClient } = useFilesApi() - const { buckets } = useDrive() + const { buckets } = useFiles() const [searchTerm, setSearchTerm] = useState(pathname.split("/").slice(2)[0]) const { redirect } = useHistory() diff --git a/packages/files-ui/src/Components/Modules/FilePreviewModal.tsx b/packages/files-ui/src/Components/Modules/FilePreviewModal.tsx index b84870d76b..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 { FileSystemItem, useDrive } from "../../Contexts/FilesContext" +import { FileSystemItem, useFiles } from "../../Contexts/FilesContext" import MimeMatcher from "./../../Utils/MimeMatcher" import axios, { CancelTokenSource } from "axios" import { @@ -162,7 +162,7 @@ interface 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) diff --git a/packages/files-ui/src/Components/Modules/LoginModule/MigrateAccount.tsx b/packages/files-ui/src/Components/Modules/LoginModule/MigrateAccount.tsx index 96a48ea55f..1965afe09e 100644 --- a/packages/files-ui/src/Components/Modules/LoginModule/MigrateAccount.tsx +++ b/packages/files-ui/src/Components/Modules/LoginModule/MigrateAccount.tsx @@ -6,7 +6,7 @@ import { Typography } from "@chainsafe/common-components" import clsx from "clsx" -import { useDrive } from "../../../Contexts/FilesContext" +import { useFiles } from "../../../Contexts/FilesContext" import { useFilesApi } from "@chainsafe/common-contexts" import { useThresholdKey } from "../../../Contexts/ThresholdKeyContext" import ConciseExplainer from "./ConciseExplainer" @@ -95,7 +95,7 @@ const MigrateAccount: React.FC = ({ }: IMigrateAccount) => { const classes = useStyles() const { validateMasterPassword } = useFilesApi() - const { secureAccountWithMasterPassword } = useDrive() + 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/SearchModule.tsx b/packages/files-ui/src/Components/Modules/SearchModule.tsx index 04104ddda5..e9f1c34942 100644 --- a/packages/files-ui/src/Components/Modules/SearchModule.tsx +++ b/packages/files-ui/src/Components/Modules/SearchModule.tsx @@ -17,7 +17,7 @@ import { import { useState } from "react" import clsx from "clsx" import { ROUTE_LINKS } from "../FilesRoutes" -import { useDrive, BucketType, SearchEntry } from "../../Contexts/FilesContext" +import { useFiles, BucketType, SearchEntry } from "../../Contexts/FilesContext" import { CONTENT_TYPES } from "../../Utils/Constants" import { getParentPathFromFilePath } from "../../Utils/pathUtils" import { t, Trans } from "@lingui/macro" @@ -158,13 +158,14 @@ const SearchModule: React.FC = ({ const [searchQuery, setSearchQuery] = useState("") const [searchResults, setSearchResults] = useState<{results: SearchEntry[]; query: string} | undefined>(undefined) const ref = useRef(null) - const { buckets } = useDrive() + const { buckets } = useFiles() const { addToastMessage } = useToaster() const { filesApiClient } = useFilesApi() const bucket = useMemo(() => buckets.find(b => b.type === "csf"), [buckets]) const getSearchResults = useCallback(async (searchString: string) => { try { + debugger if (!searchString || !bucket) return [] const results = await filesApiClient.searchFiles({ bucket_id: bucket.id, query: searchString }) diff --git a/packages/files-ui/src/Components/Modules/Settings/Plan.tsx b/packages/files-ui/src/Components/Modules/Settings/Plan.tsx index 09adf0a259..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/FilesContext" +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 61ed640651..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/FilesContext" +import { useFiles } from "../../Contexts/FilesContext" import { createStyles, makeStyles } from "@chainsafe/common-theme" import React, { useCallback, useState } from "react" import { Formik, Form } from "formik" @@ -77,7 +77,7 @@ interface IUploadFileModuleProps { const UploadFileModule = ({ modalOpen, close }: IUploadFileModuleProps) => { const classes = useStyles() const [isDoneDisabled, setIsDoneDisabled] = useState(true) - const { uploadFiles } = useDrive() + const { uploadFiles } = useFiles() const { currentPath, refreshContents, bucket } = useFileBrowser() const UploadSchema = object().shape({ files: array().required(t`Please select a file to upload`) }) diff --git a/packages/files-ui/src/Components/Modules/UploadProgressModals/index.tsx b/packages/files-ui/src/Components/Modules/UploadProgressModals/index.tsx index 4b31597fcb..59e09dadd5 100644 --- a/packages/files-ui/src/Components/Modules/UploadProgressModals/index.tsx +++ b/packages/files-ui/src/Components/Modules/UploadProgressModals/index.tsx @@ -5,7 +5,7 @@ import { makeStyles, useThemeSwitcher } from "@chainsafe/common-theme" -import { useDrive } from "../../../Contexts/FilesContext" +import { useFiles } from "../../../Contexts/FilesContext" import UploadBox from "./UploadBox" const useStyles = makeStyles(({ constants, zIndex, breakpoints }: ITheme) => { @@ -30,7 +30,7 @@ const useStyles = makeStyles(({ constants, zIndex, breakpoints }: ITheme) => { const UploadProgressModals: React.FC = () => { const classes = useStyles() - const { uploadsInProgress } = useDrive() + const { uploadsInProgress } = useFiles() const { desktop } = useThemeSwitcher() if (uploadsInProgress.length > 0) { diff --git a/packages/files-ui/src/Contexts/FilesContext.tsx b/packages/files-ui/src/Contexts/FilesContext.tsx index 587d07b432..f1fd5ab5d0 100644 --- a/packages/files-ui/src/Contexts/FilesContext.tsx +++ b/packages/files-ui/src/Contexts/FilesContext.tsx @@ -127,7 +127,7 @@ const FilesProvider = ({ children }: FilesContextProps) => { try { // 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.uploaded_size}, 0) + .reduce((totalSize, bucket) => { return totalSize += (bucket as any).size}, 0) setSpaceUsed(totalSize) } catch (error) { @@ -139,7 +139,7 @@ const FilesProvider = ({ children }: FilesContextProps) => { } }, [filesApiClient, isLoggedIn, buckets]) - // Reset password on log out + // Reset encryption keys on log out useEffect(() => { if (!isLoggedIn) { setPersonalEncryptionKey(undefined) @@ -322,67 +322,7 @@ const FilesProvider = ({ children }: FilesContextProps) => { } }, [addToastMessage, filesApiClient, buckets]) - // const createFolder = async (body: FilesPathRequest) => { - // try { - // const result = await filesApiClient.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 renameFile = useCallback(async (body: FilesMvRequest) => { - // try { - // if (body.path !== body.new_path) { - // await filesApiClient.moveCSFObject(body) - // addToastMessage({ - // message: t`File renamed successfully`, - // appearance: "success" - // }) - // } - - // return Promise.resolve() - // } catch (error) { - // addToastMessage({ - // message: t`There was an error renaming this file`, - // appearance: "error" - // }) - // return Promise.reject() - // } - // }, [addToastMessage, filesApiClient]) - - // const moveFile = useCallback(async (body: FilesMvRequest) => { - // try { - // await filesApiClient.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, filesApiClient]) - - // const moveFiles = useCallback(async (filesToMove: FilesMvRequest[]) => { - // return Promise.all( - // filesToMove.map((fileToMove) => - // moveFile(fileToMove) - // ) - // ) - // }, [moveFile]) + const getFileContent = useCallback(async ( bucketId: string, @@ -504,15 +444,15 @@ const FilesProvider = ({ children }: FilesContextProps) => { ) } -const useDrive = () => { +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 { FilesProvider as DriveProvider, useDrive } +export { FilesProvider, useFiles } export type { FileSystemItem, DirectoryContentResponse, From fbb22c166340223f36e7eafc714c7ba5e26c0eca Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 21 May 2021 15:09:30 +0200 Subject: [PATCH 10/49] fix search --- packages/files-ui/src/Components/Modules/SearchModule.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/files-ui/src/Components/Modules/SearchModule.tsx b/packages/files-ui/src/Components/Modules/SearchModule.tsx index e9f1c34942..08d95fdef5 100644 --- a/packages/files-ui/src/Components/Modules/SearchModule.tsx +++ b/packages/files-ui/src/Components/Modules/SearchModule.tsx @@ -165,7 +165,6 @@ const SearchModule: React.FC = ({ const getSearchResults = useCallback(async (searchString: string) => { try { - debugger if (!searchString || !bucket) return [] const results = await filesApiClient.searchFiles({ bucket_id: bucket.id, query: searchString }) @@ -193,7 +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), []) + const debouncedSearch = useCallback(debounce(onSearch, 400), [getSearchResults]) const onSearchChange = (searchString: string) => { setSearchQuery(searchString) From 09bfff767bdb4e682a7afbebdcfdf808dc3e227d Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 21 May 2021 15:22:15 +0200 Subject: [PATCH 11/49] fix bin folder navigation --- .../files-ui/src/Components/FilesRoutes.tsx | 12 ++++---- .../Modules/FileBrowsers/BinFileBrowser.tsx | 30 +++++++++++++++---- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/packages/files-ui/src/Components/FilesRoutes.tsx b/packages/files-ui/src/Components/FilesRoutes.tsx index 1a67a927b0..32c1347f66 100644 --- a/packages/files-ui/src/Components/FilesRoutes.tsx +++ b/packages/files-ui/src/Components/FilesRoutes.tsx @@ -45,26 +45,26 @@ const FilesRoutes = () => { redirectPath={ROUTE_LINKS.Landing} /> + = ({ controls = false }: const [pathContents, setPathContents] = useState([]) 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]) @@ -47,11 +48,9 @@ const BinFileBrowser: React.FC = ({ controls = false }: }, [bucket, currentPath, filesApiClient] ) - useEffect(() => { refreshContents(true) - // eslint-disable-next-line - }, []) + }, [bucket, refreshContents]) useEffect(() => { let binPath = extractDrivePath(pathname) @@ -59,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]) @@ -148,6 +151,20 @@ const BinFileBrowser: React.FC = ({ controls = false }: })) }, [addToastMessage, pathContents, refreshContents, filesApiClient, bucket]) + 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 = "" + } + // console.log(ROUTE_LINKS.Bin(`${urlSafePath}/${encodeURIComponent(`${fileSystemItem.name}`)}`)) + redirect(ROUTE_LINKS.Bin(`${urlSafePath}/${encodeURIComponent(`${fileSystemItem.name}`)}`)) + } + }, [currentPath, pathContents, redirect]) + const bulkOperations: IBulkOperations = useMemo(() => ({ [CONTENT_TYPES.Directory]: [], [CONTENT_TYPES.File]: ["recover", "delete"] @@ -173,7 +190,8 @@ const BinFileBrowser: React.FC = ({ controls = false }: heading: t`Bin`, controls, itemOperations, - bulkOperations + bulkOperations, + viewFolder }}> From 6d6eebc6175d702fcb44ddff4f9c0e07d66c6453 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 21 May 2021 16:27:26 +0200 Subject: [PATCH 12/49] fix grid item navigation --- .../FileSystemItem/FileSystemGridItem.tsx | 15 ++++++------ .../views/FileSystemItem/FileSystemItem.tsx | 24 +++++++++---------- .../FileSystemItem/FileSystemTableItem.tsx | 10 ++++---- .../Modules/FileBrowsers/views/FilesList.tsx | 23 +++++++++--------- 4 files changed, 36 insertions(+), 36 deletions(-) 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 7d069243c5..b9ffcd38ea 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 @@ -12,6 +12,7 @@ import { CSFTheme } from "../../../../../Themes/types" import { FileSystemItem } from "../../../../../Contexts/FilesContext" import { ConnectDragPreview } from "react-dnd" import { Form, Formik } from "formik" +import { useFileBrowser } from "../../../../../Contexts/FileBrowserContext" const useStyles = makeStyles(({ breakpoints, constants, palette }: CSFTheme) => { return createStyles({ @@ -149,7 +150,6 @@ const FileSystemGridItem = React.forwardRef( renameSchema, setEditing, handleRename, - currentPath, menuItems, resetSelectedFiles, preview @@ -157,7 +157,8 @@ const FileSystemGridItem = React.forwardRef( const classes = useStyles() const { name, cid } = file const { desktop } = useThemeSwitcher() - + const { viewFolder } = useFileBrowser() + const handleClickOutside = useCallback( (e) => { if (forwardedRef.current && forwardedRef.current.contains(e.target)) { @@ -211,11 +212,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 41e225f296..24a977b650 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 @@ -28,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/FilesContext" +import { FileSystemItem as FileSystemItemType } from "../../../../../Contexts/FilesContext" import { useFileBrowser } from "../../../../../Contexts/FileBrowserContext" const useStyles = makeStyles(({ breakpoints, constants }: CSFTheme) => { @@ -98,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 @@ -121,7 +121,7 @@ interface IFileSystemItemRowProps { browserView: BrowserView } -const FileSystemItemRow = ({ +const FileSystemItem = ({ file, files, selected, @@ -140,7 +140,7 @@ const FileSystemItemRow = ({ itemOperations, browserView, resetSelectedFiles -}: IFileSystemItemRowProps) => { +}: IFileSystemItemProps) => { const { downloadFile, currentPath, handleUploadOnDrop, moveItems } = useFileBrowser() const { cid, name, isFolder, content_type } = file let Icon @@ -349,6 +349,7 @@ const FileSystemItemRow = ({ if (desktop) { // on desktop if (isFolder) { + debugger viewFolder && viewFolder(file.cid) } else { onFilePreview() @@ -415,10 +416,9 @@ const FileSystemItemRow = ({ onSubmit={(values) => { handleRename && handleRename( - `${currentPath}${name}`, - `${currentPath}${values.fileName}` + file.cid, + values.fileName ) - setEditing(undefined) }} > @@ -468,4 +468,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 a9f41fce8b..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 @@ -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/FilesList.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx index 5f7e3dff26..4ced5f54d1 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx @@ -33,8 +33,8 @@ 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/FilesContext" -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" @@ -274,7 +274,7 @@ 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 FilesList = () => { @@ -314,7 +314,7 @@ const FilesList = () => { const { selectedLocale } = useLanguageContext() const { redirect } = useHistory() - const items: FileSystemItem[] = useMemo(() => { + const items: FileSystemItemType[] = useMemo(() => { let temp = [] switch (column) { @@ -347,7 +347,7 @@ const FilesList = () => { const files = useMemo(() => items.filter((i) => !i.isFolder), [items]) - const selectedFiles = useMemo( + const selectedItems = useMemo( () => items.filter((file) => selectedCids.includes(file.cid)), [selectedCids, items] ) @@ -417,7 +417,7 @@ const FilesList = () => { if (selectedCids.length === items.length) { setSelectedCids([]) } else { - setSelectedCids([...items.map((file: FileSystemItem) => file.cid)]) + setSelectedCids([...items.map((file: FileSystemItemType) => file.cid)]) } }, [setSelectedCids, items, selectedCids]) @@ -885,7 +885,7 @@ const FilesList = () => { ))} {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={() => { @@ -926,13 +926,14 @@ const FilesList = () => { )} > {items.map((file, index) => ( - { close={() => setIsUploadModalOpen(false)} /> { setIsMoveFileModalOpen(false) From 702e554c0f63c24ad910e8850ea6c297c465919c Mon Sep 17 00:00:00 2001 From: Thibaut Sardan Date: Fri, 21 May 2021 17:46:46 +0200 Subject: [PATCH 13/49] init --- .../files-ui/src/Components/FilesRoutes.tsx | 10 +- .../FileBrowsers/SharedFoldersFileBrowser.tsx | 251 ++++++++++++++++++ .../FileBrowsers/SharedFoldersOverview.tsx | 246 +++++++++++++++++ 3 files changed, 506 insertions(+), 1 deletion(-) create mode 100644 packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersFileBrowser.tsx create mode 100644 packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx diff --git a/packages/files-ui/src/Components/FilesRoutes.tsx b/packages/files-ui/src/Components/FilesRoutes.tsx index 32c1347f66..f7ca4dd81d 100644 --- a/packages/files-ui/src/Components/FilesRoutes.tsx +++ b/packages/files-ui/src/Components/FilesRoutes.tsx @@ -8,6 +8,7 @@ import SearchPage from "./Pages/SearchPage" import BinPage from "./Pages/BinPage" import PurchasePlanPage from "./Pages/PurchasePlanPage" import { useThresholdKey } from "../Contexts/ThresholdKeyContext" +import SharedFoldersOverview from "./Modules/FileBrowsers/SharedFoldersOverview" export const SETTINGS_BASE = "/settings" export const ROUTE_LINKS = { @@ -23,7 +24,8 @@ export const ROUTE_LINKS = { SettingsDefault: `${SETTINGS_BASE}`, PurchasePlan: "/purchase", UserSurvey: "https://shrl.ink/8eeP", - GeneralFeedbackForm: "https://shrl.ink/gvVJ" + GeneralFeedbackForm: "https://shrl.ink/gvVJ", + SharedFolders: "/shared" } export const SETTINGS_PATHS = ["profile", "plan", "security"] as const @@ -50,6 +52,12 @@ const FilesRoutes = () => { component={BinPage} redirectPath={ROUTE_LINKS.Landing} /> + = () => { + const { + downloadFile, + uploadFiles, + uploadsInProgress, + buckets + } = useFiles() + const { filesApiClient } = useFilesApi() + const { addToastMessage } = useToaster() + const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) + const [pathContents, setPathContents] = useState([]) + const { redirect } = useHistory() + + const { pathname } = useLocation() + const [currentPath, setCurrentPath] = useState(extractDrivePath(pathname.split("/").slice(1).join("/"))) + + 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) + + 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( + () => profile?.createdAt + ? dayjs(Date.now()).diff(profile.createdAt, "day") > 7 + : false + , [profile] + ) + + useEffect(() => { + const dismissedFlag = localStorageGet(DISMISSED_SURVEY_KEY) + if (dismissedFlag === undefined || dismissedFlag === null) { + localStorageSet(DISMISSED_SURVEY_KEY, "false") + } + }, [localStorageGet, localStorageSet]) + + useEffect(() => { + refreshContents(true) + }, [bucket, refreshContents]) + + useEffect(() => { + let drivePath = extractDrivePath(pathname) + if (drivePath[0] !== "/") { + drivePath = "/" + drivePath + } + if (drivePath !== currentPath) { + if (drivePath === "/") { + setCurrentPath(drivePath) + } else { + setCurrentPath(decodeURIComponent(drivePath.slice(0, -1))) + } + + refreshContents(true) + } + }, [refreshContents, pathname, currentPath]) + + 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 + } + + 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 renameItem = useCallback(async (cid: string, newName: string) => { + const itemToRename = pathContents.find(i => i.cid === cid) + if (!bucket || !itemToRename) return + + await filesApiClient.moveFPSObject(bucket.id, { + path: getPathWithFile(currentPath, itemToRename.name), + new_path: getPathWithFile(currentPath, newName) }) + await refreshContents() + }, [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 itemToDownload = pathContents.find(item => item.cid === cid) + if (!itemToDownload || !bucket) return + + await 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: decodeURIComponent(path), + onClick: () => { + + redirect( + 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) { + hasFolder = true + } + } + if (hasFolder) { + addToastMessage({ + message: "Folder uploads are not supported currently", + appearance: "error" + }) + } else { + await uploadFiles(bucket.id, files, path) + 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) { + 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]) + + const bulkOperations: IBulkOperations = useMemo(() => ({ + [CONTENT_TYPES.Directory]: ["move"], + [CONTENT_TYPES.File]: ["delete", "move"] + }), []) + + const itemOperations: IFilesTableBrowserProps["itemOperations"] = useMemo(() => ({ + [CONTENT_TYPES.Audio]: ["preview"], + [CONTENT_TYPES.MP4]: ["preview"], + [CONTENT_TYPES.Image]: ["preview"], + [CONTENT_TYPES.Pdf]: ["preview"], + [CONTENT_TYPES.Text]: ["preview"], + [CONTENT_TYPES.File]: ["download", "info", "rename", "move", "delete"], + [CONTENT_TYPES.Directory]: ["rename", "move", "delete"] + }), []) + + return ( + + + + + + ) +} + +export default CSFFileBrowser diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx new file mode 100644 index 0000000000..0d956ff61a --- /dev/null +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx @@ -0,0 +1,246 @@ +import React, { useCallback, useEffect, useMemo, useState } from "react" +import { Crumb, useToaster, useHistory, useLocation } from "@chainsafe/common-components" +import { useFiles, FileSystemItem } from "../../../Contexts/FilesContext" +import { extractDrivePath, getArrayOfPaths, getURISafePathFromArray, getPathWithFile } from "../../../Utils/pathUtils" +import { IBulkOperations, 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 { ROUTE_LINKS } from "../../FilesRoutes" +import dayjs from "dayjs" +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 SharedFolderOverview = () => { + const { downloadFile, uploadFiles, uploadsInProgress, buckets } = useFiles() + const { filesApiClient } = useFilesApi() + const { addToastMessage } = useToaster() + const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) + const [pathContents, setPathContents] = useState([]) + const { redirect } = useHistory() + + const { pathname } = useLocation() + const [currentPath, setCurrentPath] = useState(extractDrivePath(pathname.split("/").slice(1).join("/"))) + + 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) + + 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( + () => profile?.createdAt + ? dayjs(Date.now()).diff(profile.createdAt, "day") > 7 + : false + , [profile] + ) + + useEffect(() => { + const dismissedFlag = localStorageGet(DISMISSED_SURVEY_KEY) + if (dismissedFlag === undefined || dismissedFlag === null) { + localStorageSet(DISMISSED_SURVEY_KEY, "false") + } + }, [localStorageGet, localStorageSet]) + + useEffect(() => { + refreshContents(true) + }, [bucket, refreshContents]) + + useEffect(() => { + let drivePath = extractDrivePath(pathname) + if (drivePath[0] !== "/") { + drivePath = "/" + drivePath + } + if (drivePath !== currentPath) { + if (drivePath === "/") { + setCurrentPath(drivePath) + } else { + setCurrentPath(decodeURIComponent(drivePath.slice(0, -1))) + } + + refreshContents(true) + } + }, [refreshContents, pathname, currentPath]) + + 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 + } + + 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 renameItem = useCallback(async (cid: string, newName: string) => { + const itemToRename = pathContents.find(i => i.cid === cid) + if (!bucket || !itemToRename) return + + await filesApiClient.moveFPSObject(bucket.id, { + path: getPathWithFile(currentPath, itemToRename.name), + new_path: getPathWithFile(currentPath, newName) }) + await refreshContents() + }, [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 itemToDownload = pathContents.find(item => item.cid === cid) + if (!itemToDownload || !bucket) return + + await 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: decodeURIComponent(path), + onClick: () => { + + redirect( + 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) { + hasFolder = true + } + } + if (hasFolder) { + addToastMessage({ + message: "Folder uploads are not supported currently", + appearance: "error" + }) + } else { + await uploadFiles(bucket.id, files, path) + 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) { + 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]) + + const bulkOperations: IBulkOperations = useMemo(() => ({ + [CONTENT_TYPES.Directory]: ["move"], + [CONTENT_TYPES.File]: ["delete", "move"] + }), []) + + const itemOperations: IFilesTableBrowserProps["itemOperations"] = useMemo(() => ({ + [CONTENT_TYPES.Audio]: ["preview"], + [CONTENT_TYPES.MP4]: ["preview"], + [CONTENT_TYPES.Image]: ["preview"], + [CONTENT_TYPES.Pdf]: ["preview"], + [CONTENT_TYPES.Text]: ["preview"], + [CONTENT_TYPES.File]: ["download", "info", "rename", "move", "delete"], + [CONTENT_TYPES.Directory]: ["rename", "move", "delete"] + }), []) + + return ( + + + + + + ) +} + +export default SharedFolderOverview From 2e0e6ad4822233898a3efe202dda069b68000ba5 Mon Sep 17 00:00:00 2001 From: Thibaut Sardan Date: Fri, 28 May 2021 15:57:28 +0200 Subject: [PATCH 14/49] add permission to buckets --- .../Modules/FileBrowsers/CSFFileBrowser.tsx | 8 +--- .../FileBrowsers/SharedFoldersOverview.tsx | 32 ++++++++-------- .../files-ui/src/Contexts/FilesContext.tsx | 37 ++++++++++++++----- 3 files changed, 44 insertions(+), 33 deletions(-) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx index 8e0bc05984..fdb41e31f0 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx @@ -16,18 +16,12 @@ import { FileBrowserContext } from "../../../Contexts/FileBrowserContext" import { parseFileContentResponse } from "../../../Utils/Helpers" const CSFFileBrowser: React.FC = () => { - const { - downloadFile, - uploadFiles, - uploadsInProgress, - buckets - } = useFiles() + const { downloadFile, uploadFiles, uploadsInProgress, buckets } = useFiles() const { filesApiClient } = useFilesApi() const { addToastMessage } = useToaster() const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) const [pathContents, setPathContents] = useState([]) const { redirect } = useHistory() - const { pathname } = useLocation() const [currentPath, setCurrentPath] = useState(extractDrivePath(pathname.split("/").slice(1).join("/"))) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx index 0d956ff61a..d60291a217 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx @@ -26,22 +26,22 @@ const SharedFolderOverview = () => { const { pathname } = useLocation() const [currentPath, setCurrentPath] = useState(extractDrivePath(pathname.split("/").slice(1).join("/"))) - 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) - - setPathContents( - newContents.map((fcr) => parseFileContentResponse(fcr)) - ) - }).catch(error => { - console.error(error) - }).finally(() => showLoading && setLoadingCurrentPath(false)) - }, [bucket, filesApiClient, currentPath]) + const bucketsToShow = useMemo(() => buckets.find(b => b.type === "share"), [buckets]) + + // const refreshContents = useCallback((showLoading?: boolean) => { + // if (!bucketsToShow) return + // showLoading && setLoadingCurrentPath(true) + // filesApiClient.getFPSChildList(bucket.id, { path: currentPath }) + // .then((newContents) => { + // showLoading && setLoadingCurrentPath(false) + + // 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() diff --git a/packages/files-ui/src/Contexts/FilesContext.tsx b/packages/files-ui/src/Contexts/FilesContext.tsx index f1fd5ab5d0..1a1c55cbc9 100644 --- a/packages/files-ui/src/Contexts/FilesContext.tsx +++ b/packages/files-ui/src/Contexts/FilesContext.tsx @@ -50,7 +50,12 @@ interface GetFileContentParams { path: string } -type Bucket = FilesBucket & { encryptionKey: string} +type BucketPermission = "writer" | "owner" | "reader" + +type Bucket = FilesBucket & { + encryptionKey: string + permission?: BucketPermission +} type FilesContext = { buckets: Bucket[] @@ -92,30 +97,42 @@ const FilesProvider = ({ children }: FilesContextProps) => { const [personalEncryptionKey, setPersonalEncryptionKey] = useState() const [buckets, setBuckets] = useState([]) const { profile } = useUser() + const { userId } = profile || {} 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) + const bucketUsers = [...bucket.readers, ...bucket.writers, ...bucket.owners] + const bucketUser = bucketUsers.find(bu => bu.uuid === 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]) + }, [decryptMessageWithThresholdKey, userId]) 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) - }))) + const bucketsWithKeys: Bucket[] = await Promise.all( + result.map(async (b) => { + + const permission = b.owners.find(owner => owner === profile?.userId) + ? "owner" as BucketPermission + : b.writers.find(writer => writer === profile?.userId) + ? "writer" as BucketPermission + : b.readers.find(reader => reader === profile?.userId) + ? "reader" as BucketPermission + : undefined + + return { + ...b, + encryptionKey: (b.type === "csf" || b.type === "trash") ? personalEncryptionKey : await getKeyForBucket(b), + permission + }})) setBuckets(bucketsWithKeys) return Promise.resolve() - }, [getKeyForBucket, filesApiClient, personalEncryptionKey]) + }, [personalEncryptionKey, filesApiClient, getKeyForBucket, profile?.userId]) useEffect(() => { refreshBuckets() From d6872292ec56c260de9c5af99cfe52cf7cdc1d3e Mon Sep 17 00:00:00 2001 From: Thibaut Sardan Date: Mon, 31 May 2021 23:27:46 +0200 Subject: [PATCH 15/49] Nav and filtering --- .../src/Components/Layouts/AppNav.tsx | 13 + .../FileBrowsers/SharedFoldersFileBrowser.tsx | 251 ----------- .../FileBrowsers/SharedFoldersOverview.tsx | 396 ++++++++---------- 3 files changed, 183 insertions(+), 477 deletions(-) delete mode 100644 packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersFileBrowser.tsx diff --git a/packages/files-ui/src/Components/Layouts/AppNav.tsx b/packages/files-ui/src/Components/Layouts/AppNav.tsx index 62efe83803..161a043ebf 100644 --- a/packages/files-ui/src/Components/Layouts/AppNav.tsx +++ b/packages/files-ui/src/Components/Layouts/AppNav.tsx @@ -292,6 +292,19 @@ const AppNav: React.FC = ({ navOpen, setNavOpen }: IAppNav) => { Home + + + + Shared + + = () => { - const { - downloadFile, - uploadFiles, - uploadsInProgress, - buckets - } = useFiles() - const { filesApiClient } = useFilesApi() - const { addToastMessage } = useToaster() - const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) - const [pathContents, setPathContents] = useState([]) - const { redirect } = useHistory() - - const { pathname } = useLocation() - const [currentPath, setCurrentPath] = useState(extractDrivePath(pathname.split("/").slice(1).join("/"))) - - 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) - - 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( - () => profile?.createdAt - ? dayjs(Date.now()).diff(profile.createdAt, "day") > 7 - : false - , [profile] - ) - - useEffect(() => { - const dismissedFlag = localStorageGet(DISMISSED_SURVEY_KEY) - if (dismissedFlag === undefined || dismissedFlag === null) { - localStorageSet(DISMISSED_SURVEY_KEY, "false") - } - }, [localStorageGet, localStorageSet]) - - useEffect(() => { - refreshContents(true) - }, [bucket, refreshContents]) - - useEffect(() => { - let drivePath = extractDrivePath(pathname) - if (drivePath[0] !== "/") { - drivePath = "/" + drivePath - } - if (drivePath !== currentPath) { - if (drivePath === "/") { - setCurrentPath(drivePath) - } else { - setCurrentPath(decodeURIComponent(drivePath.slice(0, -1))) - } - - refreshContents(true) - } - }, [refreshContents, pathname, currentPath]) - - 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 - } - - 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 renameItem = useCallback(async (cid: string, newName: string) => { - const itemToRename = pathContents.find(i => i.cid === cid) - if (!bucket || !itemToRename) return - - await filesApiClient.moveFPSObject(bucket.id, { - path: getPathWithFile(currentPath, itemToRename.name), - new_path: getPathWithFile(currentPath, newName) }) - await refreshContents() - }, [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 itemToDownload = pathContents.find(item => item.cid === cid) - if (!itemToDownload || !bucket) return - - await 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: decodeURIComponent(path), - onClick: () => { - - redirect( - 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) { - hasFolder = true - } - } - if (hasFolder) { - addToastMessage({ - message: "Folder uploads are not supported currently", - appearance: "error" - }) - } else { - await uploadFiles(bucket.id, files, path) - 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) { - 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]) - - const bulkOperations: IBulkOperations = useMemo(() => ({ - [CONTENT_TYPES.Directory]: ["move"], - [CONTENT_TYPES.File]: ["delete", "move"] - }), []) - - const itemOperations: IFilesTableBrowserProps["itemOperations"] = useMemo(() => ({ - [CONTENT_TYPES.Audio]: ["preview"], - [CONTENT_TYPES.MP4]: ["preview"], - [CONTENT_TYPES.Image]: ["preview"], - [CONTENT_TYPES.Pdf]: ["preview"], - [CONTENT_TYPES.Text]: ["preview"], - [CONTENT_TYPES.File]: ["download", "info", "rename", "move", "delete"], - [CONTENT_TYPES.Directory]: ["rename", "move", "delete"] - }), []) - - return ( - - - - - - ) -} - -export default CSFFileBrowser diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx index d60291a217..f0046f069e 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx @@ -1,245 +1,189 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" -import { Crumb, useToaster, useHistory, useLocation } from "@chainsafe/common-components" +import { useLocation, Typography, Table, TableHead, TableRow, TableHeadCell, TableBody, SortDirection } from "@chainsafe/common-components" import { useFiles, FileSystemItem } from "../../../Contexts/FilesContext" -import { extractDrivePath, getArrayOfPaths, getURISafePathFromArray, getPathWithFile } from "../../../Utils/pathUtils" -import { IBulkOperations, 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 { ROUTE_LINKS } from "../../FilesRoutes" +import { Trans } from "@lingui/macro" import dayjs from "dayjs" -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" +import { useFilesApi } from "@chainsafe/common-contexts" +// import { useLocalStorage } from "@chainsafe/browser-storage-hooks" +// import { DISMISSED_SURVEY_KEY } from "../../SurveyBanner" +import clsx from "clsx" +import { createStyles, makeStyles } from "@chainsafe/common-theme" +import { CSFTheme } from "../../../Themes/types" +// import { parseFileContentResponse } from "../../../Utils/Helpers" + +const useStyles = makeStyles( + ({ animation, breakpoints, constants, palette }: CSFTheme) => { + const desktopGridSettings = "50px 69px 3fr 190px 100px 45px !important" + const mobileGridSettings = "69px 3fr 45px !important" + + return createStyles({ + root: { + position: "relative", + [breakpoints.down("md")]: { + marginLeft: constants.generalUnit * 2, + marginRight: constants.generalUnit * 2 + }, + [breakpoints.up("md")]: { + border: "1px solid transparent", + padding: `0 ${constants.generalUnit}px`, + borderRadius: constants.generalUnit / 4, + minHeight: `calc(100vh - ${Number(constants.contentTopPadding)}px)`, + "&.droppable": { + borderColor: palette.additional["geekblue"][4] + } + } + }, + header: { + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + [breakpoints.down("md")]: { + marginTop: constants.generalUnit + } + }, + controls: { + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + "& > button": { + marginLeft: constants.generalUnit + } + }, + fadeOutLoading: { + opacity: 0.2, + transition: `opacity ${animation.transform * 3}ms` + }, + tableHead: { + marginTop: constants.generalUnit * 3 + }, + tableRow: { + border: "2px solid transparent", + transitionDuration: `${animation.transform}ms`, + [breakpoints.up("md")]: { + gridTemplateColumns: desktopGridSettings + }, + [breakpoints.down("md")]: { + gridTemplateColumns: mobileGridSettings + } + } + }) + } +) const SharedFolderOverview = () => { - const { downloadFile, uploadFiles, uploadsInProgress, buckets } = useFiles() + const classes = useStyles() + const { buckets } = useFiles() const { filesApiClient } = useFilesApi() - const { addToastMessage } = useToaster() const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) - const [pathContents, setPathContents] = useState([]) - const { redirect } = useHistory() - + const [direction, setDirection] = useState("ascend") + const [column, setColumn] = useState<"name" | "size" | "date_uploaded">("name") const { pathname } = useLocation() - const [currentPath, setCurrentPath] = useState(extractDrivePath(pathname.split("/").slice(1).join("/"))) - - const bucketsToShow = useMemo(() => buckets.find(b => b.type === "share"), [buckets]) - - // const refreshContents = useCallback((showLoading?: boolean) => { - // if (!bucketsToShow) return - // showLoading && setLoadingCurrentPath(true) - // filesApiClient.getFPSChildList(bucket.id, { path: currentPath }) - // .then((newContents) => { - // showLoading && setLoadingCurrentPath(false) - - // 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 bucketsToShow = useMemo(() => buckets.filter(b => b.type === "share"), [buckets]) - const olderThanOneWeek = useMemo( - () => profile?.createdAt - ? dayjs(Date.now()).diff(profile.createdAt, "day") > 7 - : false - , [profile] - ) - - useEffect(() => { - const dismissedFlag = localStorageGet(DISMISSED_SURVEY_KEY) - if (dismissedFlag === undefined || dismissedFlag === null) { - localStorageSet(DISMISSED_SURVEY_KEY, "false") - } - }, [localStorageGet, localStorageSet]) - - useEffect(() => { - refreshContents(true) - }, [bucket, refreshContents]) - - useEffect(() => { - let drivePath = extractDrivePath(pathname) - if (drivePath[0] !== "/") { - drivePath = "/" + drivePath - } - if (drivePath !== currentPath) { - if (drivePath === "/") { - setCurrentPath(drivePath) - } else { - setCurrentPath(decodeURIComponent(drivePath.slice(0, -1))) - } - - refreshContents(true) - } - }, [refreshContents, pathname, currentPath]) - - 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 - } - - 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 renameItem = useCallback(async (cid: string, newName: string) => { - const itemToRename = pathContents.find(i => i.cid === cid) - if (!bucket || !itemToRename) return - - await filesApiClient.moveFPSObject(bucket.id, { - path: getPathWithFile(currentPath, itemToRename.name), - new_path: getPathWithFile(currentPath, newName) }) - await refreshContents() - }, [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 itemToDownload = pathContents.find(item => item.cid === cid) - if (!itemToDownload || !bucket) return - - await 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: decodeURIComponent(path), - onClick: () => { - - redirect( - 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) { - hasFolder = true - } - } - if (hasFolder) { - addToastMessage({ - message: "Folder uploads are not supported currently", - appearance: "error" - }) + const handleSortToggle = ( + targetColumn: "name" | "size" | "date_uploaded" + ) => { + if (column !== targetColumn) { + setColumn(targetColumn) + setDirection("descend") } else { - await uploadFiles(bucket.id, files, path) - 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) { - let urlSafePath - if (currentPath !== "/") { - urlSafePath = `/${currentPath.slice(1).split("/").map(encodeURIComponent).join("/")}` + if (direction === "ascend") { + setDirection("descend") } else { - urlSafePath = "" + setDirection("ascend") } - redirect(ROUTE_LINKS.Drive(`${urlSafePath}/${encodeURIComponent(`${fileSystemItem.name}`)}`)) } - }, [currentPath, pathContents, redirect]) - - const bulkOperations: IBulkOperations = useMemo(() => ({ - [CONTENT_TYPES.Directory]: ["move"], - [CONTENT_TYPES.File]: ["delete", "move"] - }), []) - - const itemOperations: IFilesTableBrowserProps["itemOperations"] = useMemo(() => ({ - [CONTENT_TYPES.Audio]: ["preview"], - [CONTENT_TYPES.MP4]: ["preview"], - [CONTENT_TYPES.Image]: ["preview"], - [CONTENT_TYPES.Pdf]: ["preview"], - [CONTENT_TYPES.Text]: ["preview"], - [CONTENT_TYPES.File]: ["download", "info", "rename", "move", "delete"], - [CONTENT_TYPES.Directory]: ["rename", "move", "delete"] - }), []) + } return ( - - - - - +
+
+ + Shared folders + +
+
+
+ {/*
+ + + One sec, getting files ready... + +
*/} + + + + {/* + toggleAll()} + /> + */} + + {/* + Icon + */} + + handleSortToggle("name")} + sortDirection={column === "name" ? direction : undefined} + sortActive={column === "name"} + > + Name + + handleSortToggle("date_uploaded")} + sortDirection={ + column === "date_uploaded" ? direction : undefined + } + sortActive={column === "date_uploaded"} + > + Date uploaded + + handleSortToggle("size")} + sortDirection={column === "size" ? direction : undefined} + sortActive={column === "size"} + > + Size + + {/* Menu */} + + + + +
{bucketsToShow.length}
+
+
+
) } From 678613fbe40e2f11d276ebf0c2599100d7e6ed09 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 1 Jun 2021 12:52:10 +0000 Subject: [PATCH 16/49] lingui extract --- packages/files-ui/src/locales/en/messages.po | 6 ++++++ packages/files-ui/src/locales/fr/messages.po | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/packages/files-ui/src/locales/en/messages.po b/packages/files-ui/src/locales/en/messages.po index c3df1e8c5b..3dad304b11 100644 --- a/packages/files-ui/src/locales/en/messages.po +++ b/packages/files-ui/src/locales/en/messages.po @@ -496,6 +496,12 @@ msgstr "Setup incomplete" msgid "Share" msgstr "Share" +msgid "Shared" +msgstr "Shared" + +msgid "Shared folders" +msgstr "Shared folders" + msgid "Sign Out" msgstr "Sign Out" diff --git a/packages/files-ui/src/locales/fr/messages.po b/packages/files-ui/src/locales/fr/messages.po index f966adf790..f2b5f39433 100644 --- a/packages/files-ui/src/locales/fr/messages.po +++ b/packages/files-ui/src/locales/fr/messages.po @@ -497,6 +497,12 @@ msgstr "Configuration incomplète" msgid "Share" msgstr "Partager" +msgid "Shared" +msgstr "" + +msgid "Shared folders" +msgstr "" + msgid "Sign Out" msgstr "Déconnexion" From a62c580e9470aeec585b2983876ae2ca090b8f9e Mon Sep 17 00:00:00 2001 From: Thibaut Sardan Date: Tue, 1 Jun 2021 16:36:06 +0200 Subject: [PATCH 17/49] with some todos --- .../Modules/FileBrowsers/SharedFoldersOverview.tsx | 13 +++++++++++++ packages/files-ui/src/Contexts/FilesContext.tsx | 8 +++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx index f0046f069e..bf3c4e44b7 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx @@ -97,6 +97,7 @@ const SharedFolderOverview = () => { } } } + console.log(buckets) return (
{ One sec, getting files ready...
*/} +
{ + filesApiClient.listBuckets().then(console.log).catch(console.error) + // filesApiClient.createBucket({ + // name: "catBucket", + // encryption_key:"none", + // type: "share" + // }).then(console.log) + // .catch(console.error) + }}> + Click me +
{ const { userId } = profile || {} const getKeyForBucket = useCallback(async (bucket: FilesBucket) => { - const bucketUsers = [...bucket.readers, ...bucket.writers, ...bucket.owners] + // todo remove the "|| []" once this is set in the api. + const bucketUsers = [...bucket.readers || [], ...bucket.writers || [], ...bucket.owners || []] const bucketUser = bucketUsers.find(bu => bu.uuid === userId) if (!bucketUser || !bucketUser.encryption_key) { console.error(`Unable to retrieve encryption key for ${bucket.id}`) @@ -117,11 +118,12 @@ const FilesProvider = ({ children }: FilesContextProps) => { const bucketsWithKeys: Bucket[] = await Promise.all( result.map(async (b) => { + //todo remove the ? once the writers and readers are non null const permission = b.owners.find(owner => owner === profile?.userId) ? "owner" as BucketPermission - : b.writers.find(writer => writer === profile?.userId) + : b.writers?.find(writer => writer === profile?.userId) ? "writer" as BucketPermission - : b.readers.find(reader => reader === profile?.userId) + : b.readers?.find(reader => reader === profile?.userId) ? "reader" as BucketPermission : undefined From d5417d5199c7fbeee557ce40c8278f003e21b044 Mon Sep 17 00:00:00 2001 From: Thibaut Sardan Date: Thu, 10 Jun 2021 15:24:17 +0200 Subject: [PATCH 18/49] lint --- .../FileBrowsers/views/FileSystemItem/FileSystemGridItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 52cf53ec0e..77eecddbcb 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 @@ -158,7 +158,7 @@ const FileSystemGridItem = React.forwardRef( const { name, cid } = file const { desktop } = useThemeSwitcher() const { viewFolder } = useFileBrowser() - + const handleClickOutside = useCallback( (e) => { if (forwardedRef.current && forwardedRef.current.contains(e.target)) { From c97602290555cd980882b0330173ac0051f807bb Mon Sep 17 00:00:00 2001 From: Thibaut Sardan Date: Fri, 11 Jun 2021 16:15:44 +0200 Subject: [PATCH 19/49] basics --- .../FileBrowsers/SharedFolderRowWrapper.tsx | 149 ++++++++++++++++ .../FileBrowsers/SharedFoldersOverview.tsx | 90 ++++------ .../Components/Modules/FileBrowsers/types.ts | 4 + .../FileSystemItem/FileSystemGridItem.tsx | 2 - .../views/FileSystemItem/FileSystemItem.tsx | 1 - .../views/FileSystemItem/SharedFolderRow.tsx | 163 ++++++++++++++++++ .../Modules/FileBrowsers/views/FilesList.tsx | 2 - .../files-ui/src/Contexts/FilesContext.tsx | 97 +++++++---- 8 files changed, 406 insertions(+), 102 deletions(-) create mode 100644 packages/files-ui/src/Components/Modules/FileBrowsers/SharedFolderRowWrapper.tsx create mode 100644 packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/SharedFolderRow.tsx diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFolderRowWrapper.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFolderRowWrapper.tsx new file mode 100644 index 0000000000..de08cdd99e --- /dev/null +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFolderRowWrapper.tsx @@ -0,0 +1,149 @@ +import React, { useCallback } from "react" +import { DeleteSvg, EditSvg, IMenuItem } from "@chainsafe/common-components" +import { makeStyles, createStyles, useDoubleClick, useThemeSwitcher } from "@chainsafe/common-theme" +import { Trans } from "@lingui/macro" +import { CSFTheme } from "../../../Themes/types" +import { Bucket } from "@chainsafe/files-api-client" +import SharedFolderItem from "./views/FileSystemItem/SharedFolderRow" + +const useStyles = makeStyles(({ breakpoints, constants }: CSFTheme) => { + return createStyles({ + renameInput: { + width: "100%", + [breakpoints.up("md")]: { + margin: 0 + }, + [breakpoints.down("md")]: { + margin: `${constants.generalUnit * 4.2}px 0` + } + }, + modalRoot: { + [breakpoints.down("md")]: {} + }, + modalInner: { + [breakpoints.down("md")]: { + bottom: + Number(constants?.mobileButtonHeight) + constants.generalUnit, + borderTopLeftRadius: `${constants.generalUnit * 1.5}px`, + borderTopRightRadius: `${constants.generalUnit * 1.5}px`, + borderBottomLeftRadius: `${constants.generalUnit * 1.5}px`, + borderBottomRightRadius: `${constants.generalUnit * 1.5}px`, + maxWidth: `${breakpoints.width("md")}px !important` + } + }, + renameHeader: { + textAlign: "center" + }, + renameFooter: { + display: "flex", + flexDirection: "row", + alignItems: "center", + justifyContent: "flex-end" + }, + renameModal: { + padding: constants.generalUnit * 4 + }, + okButton: { + marginLeft: constants.generalUnit + }, + cancelButton: { + [breakpoints.down("md")]: { + position: "fixed", + bottom: 0, + left: 0, + width: "100%", + height: constants?.mobileButtonHeight + } + }, + menuIcon: { + display: "flex", + justifyContent: "center", + alignItems: "center", + width: 20, + marginRight: constants.generalUnit * 1.5, + "& svg": { + fill: constants.fileSystemItemRow.menuIcon + } + }, + dropdownIcon: { + "& svg": { + fill: constants.fileSystemItemRow.dropdownIcon + } + } + }) +}) + +interface Props { + bucket: Bucket +} + +const SharedFolderRowWrapper = ({ bucket }: Props) => { + const { desktop } = useThemeSwitcher() + const classes = useStyles() + + + const menuItems: IMenuItem[] = [{ + contents: ( + <> + + + Rename + + + ), + onClick: () => console.log("not implemented") + }, + { + contents: ( + <> + + + Delete + + + ), + onClick: () => console.log("not implemented") + }] + + const onSingleClick = useCallback( + (e) => { + if (desktop) { + // on desktop + } else { + // on mobile + } + }, + [desktop] + ) + + const onDoubleClick = useCallback( + () => { + if (desktop) { + // on desktop + } else { + // on mobile + return + } + }, + [desktop] + ) + + const { click } = useDoubleClick(onSingleClick, onDoubleClick) + + const onFolderClick = (e?: React.MouseEvent) => { + e?.persist() + click(e) + } + + return ( + <> + + + ) +} + +export default SharedFolderRowWrapper diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx index bf3c4e44b7..41f257dfbc 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx @@ -1,20 +1,17 @@ -import React, { useCallback, useEffect, useMemo, useState } from "react" -import { useLocation, Typography, Table, TableHead, TableRow, TableHeadCell, TableBody, SortDirection } from "@chainsafe/common-components" -import { useFiles, FileSystemItem } from "../../../Contexts/FilesContext" +import React, { useMemo, useState } from "react" +import { Typography, Table, TableHead, TableRow, TableHeadCell, TableBody, SortDirection } from "@chainsafe/common-components" +import { useFiles } from "../../../Contexts/FilesContext" import { Trans } from "@lingui/macro" -import dayjs from "dayjs" -import { useFilesApi } from "@chainsafe/common-contexts" -// import { useLocalStorage } from "@chainsafe/browser-storage-hooks" -// import { DISMISSED_SURVEY_KEY } from "../../SurveyBanner" -import clsx from "clsx" import { createStyles, makeStyles } from "@chainsafe/common-theme" import { CSFTheme } from "../../../Themes/types" -// import { parseFileContentResponse } from "../../../Utils/Helpers" +import { useFilesApi } from "../../../Contexts/FilesApiContext" +import SharedFolderRowWrapper from "./SharedFolderRowWrapper" + +export const desktopSharedGridSettings = "69px 3fr 190px 150px 69px !important" +export const mobileSharedGridSettings = "69px 3fr 45px !important" const useStyles = makeStyles( ({ animation, breakpoints, constants, palette }: CSFTheme) => { - const desktopGridSettings = "50px 69px 3fr 190px 100px 45px !important" - const mobileGridSettings = "69px 3fr 45px !important" return createStyles({ root: { @@ -62,10 +59,10 @@ const useStyles = makeStyles( border: "2px solid transparent", transitionDuration: `${animation.transform}ms`, [breakpoints.up("md")]: { - gridTemplateColumns: desktopGridSettings + gridTemplateColumns: desktopSharedGridSettings }, [breakpoints.down("md")]: { - gridTemplateColumns: mobileGridSettings + gridTemplateColumns: mobileSharedGridSettings } } }) @@ -76,10 +73,8 @@ const SharedFolderOverview = () => { const classes = useStyles() const { buckets } = useFiles() const { filesApiClient } = useFilesApi() - const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) const [direction, setDirection] = useState("ascend") const [column, setColumn] = useState<"name" | "size" | "date_uploaded">("name") - const { pathname } = useLocation() const bucketsToShow = useMemo(() => buckets.filter(b => b.type === "share"), [buckets]) @@ -97,7 +92,6 @@ const SharedFolderOverview = () => { } } } - console.log(buckets) return (
{
- {/*
- - - One sec, getting files ready... - -
*/} -
{ - filesApiClient.listBuckets().then(console.log).catch(console.error) - // filesApiClient.createBucket({ - // name: "catBucket", - // encryption_key:"none", - // type: "share" - // }).then(console.log) - // .catch(console.error) + filesApiClient.createBucket({ + name: "Cat Bucket", + // eslint-disable-next-line max-len + encryption_key:"dc19f9579902e949fd5d517951d6cf2203526b233dc25ca7323e5d56932237e5ed4369871f81015f921decc6d5b5e1b5cf60af9ffaeba4fde2d2969b5dfa131a195bb7b828c6dc2a8434e43f497ce6529d897f09f812f53d4e9f9f3c0f6bce31afd390943e6ae189c820783698521ce2b8f446f557e1c78185c8e8a0ae54370b88", + type: "share" + }).then(console.log) + .catch(console.error) }}> - Click me -
+ create shared bucket (hardcoded encryption key!) +
- {/* - toggleAll()} - /> - */} - {/* - Icon - */} + {/* Icon */} { > Name + + Shared with + { column === "date_uploaded" ? direction : undefined } sortActive={column === "date_uploaded"} - > - Date uploaded - - handleSortToggle("size")} - sortDirection={column === "size" ? direction : undefined} - sortActive={column === "size"} > Size {/* Menu */} - -
{bucketsToShow.length}
+ {bucketsToShow.map((bucket) => + + )}
diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/types.ts b/packages/files-ui/src/Components/Modules/FileBrowsers/types.ts index d2c3d175fa..128e55ca17 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/types.ts +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/types.ts @@ -12,6 +12,10 @@ export type FileOperation = | "preview" | "view_folder" +export type BucketOperation = + | "rename" + | "delete" + export type BrowserView = "grid" | "table" export type MoveModalMode = "move" | "recover" 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 77eecddbcb..ba60a40acd 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 @@ -12,7 +12,6 @@ import { CSFTheme } from "../../../../../Themes/types" import { FileSystemItem } from "../../../../../Contexts/FilesContext" import { ConnectDragPreview } from "react-dnd" import { Form, Formik } from "formik" -import { useFileBrowser } from "../../../../../Contexts/FileBrowserContext" const useStyles = makeStyles(({ breakpoints, constants, palette }: CSFTheme) => { return createStyles({ @@ -157,7 +156,6 @@ const FileSystemGridItem = React.forwardRef( const classes = useStyles() const { name, cid } = file const { desktop } = useThemeSwitcher() - const { viewFolder } = useFileBrowser() const handleClickOutside = useCallback( (e) => { 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 bb97978d0b..227ef29f7b 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 @@ -99,7 +99,6 @@ const useStyles = makeStyles(({ breakpoints, constants }: CSFTheme) => { }) interface IFileSystemItemProps { - index: number file: FileSystemItemType files: FileSystemItemType[] selected: string[] diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/SharedFolderRow.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/SharedFolderRow.tsx new file mode 100644 index 0000000000..488696af20 --- /dev/null +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/SharedFolderRow.tsx @@ -0,0 +1,163 @@ +import React from "react" +import { makeStyles, createStyles, useThemeSwitcher } from "@chainsafe/common-theme" +import { + FolderFilledIcon, + formatBytes, + IMenuItem, + MenuDropdown, + MoreIcon, + TableCell, + TableRow, + Typography +} from "@chainsafe/common-components" +import { CSFTheme } from "../../../../../Themes/types" +import { Bucket } from "@chainsafe/files-api-client" +import { desktopSharedGridSettings, mobileSharedGridSettings } from "../../SharedFoldersOverview" + +const useStyles = makeStyles(({ breakpoints, constants, palette }: CSFTheme) => { + + return createStyles({ + tableRow: { + border: "2px solid transparent", + [breakpoints.up("md")]: { + gridTemplateColumns: desktopSharedGridSettings + }, + [breakpoints.down("md")]: { + gridTemplateColumns: mobileSharedGridSettings + }, + "&.droppable": { + border: `2px solid ${palette.primary.main}` + } + }, + folderIcon: { + display: "flex", + flexDirection: "row", + alignItems: "center", + justifyContent: "center", + "& svg": { + width: constants.generalUnit * 2.5, + fill: palette.additional.gray[9] + } + }, + renameInput: { + width: "100%", + [breakpoints.up("md")]: { + margin: 0 + }, + [breakpoints.down("md")]: { + margin: `${constants.generalUnit * 4.2}px 0` + } + }, + menuIcon: { + display: "flex", + justifyContent: "center", + alignItems: "center", + width: 20, + marginRight: constants.generalUnit * 1.5, + "& svg": { + fill: constants.fileSystemItemRow.menuIcon + } + }, + desktopRename: { + display: "flex", + flexDirection: "row", + "& svg": { + width: 20, + height: 20 + } + }, + filename: { + whiteSpace: "nowrap", + textOverflow: "ellipsis", + overflow: "hidden", + "&.editing": { + overflow: "visible" + } + }, + dropdownIcon: { + "& svg": { + fill: constants.fileSystemItemRow.dropdownIcon + } + }, + dropdownOptions: { + backgroundColor: constants.fileSystemItemRow.optionsBackground, + color: constants.fileSystemItemRow.optionsColor, + border: `1px solid ${constants.fileSystemItemRow.optionsBorder}` + }, + dropdownItem: { + backgroundColor: constants.fileSystemItemRow.itemBackground, + color: constants.fileSystemItemRow.itemColor + } + }) +}) + +interface Props { + bucket: Bucket + onFolderClick: (e?: React.MouseEvent) => void + menuItems: IMenuItem[] +} + +const SharedFolderRow = ({ bucket, onFolderClick, menuItems }: Props) => { + const classes = useStyles() + const { name, size } = bucket + const { desktop } = useThemeSwitcher() + + return ( + + onFolderClick(e)} + > + + + onFolderClick(e)} + > + {name} + + onFolderClick(e)} + > + {bucket.readers[0]} + + {desktop && ( + <> + {/* + { + dayjs.unix(created_at).format("DD MMM YYYY h:mm a") + } + */} + + {formatBytes(size)} + + + )} + + + + + ) +} + +export default SharedFolderRow diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx index 6e0f73ca1e..a876a73928 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx @@ -877,7 +877,6 @@ const FilesList = () => { {items.map((file, index) => ( { {items.map((file, index) => ( { const { profile } = useUser() const { userId } = profile || {} - const getKeyForBucket = useCallback(async (bucket: FilesBucket) => { - // todo remove the "|| []" once this is set in the api. - const bucketUsers = [...bucket.readers || [], ...bucket.writers || [], ...bucket.owners || []] + const getKeyForSharedBucket = useCallback(async (bucket: FilesBucket) => { + const bucketUsers = [...bucket.readers, ...bucket.writers, ...bucket.owners] const bucketUser = bucketUsers.find(bu => bu.uuid === userId) - if (!bucketUser || !bucketUser.encryption_key) { + + if (!bucketUser?.encryption_key) { console.error(`Unable to retrieve encryption key for ${bucket.id}`) return "" } + const decrypted = await decryptMessageWithThresholdKey(bucketUser.encryption_key) + return decrypted || "" }, [decryptMessageWithThresholdKey, userId]) const refreshBuckets = useCallback(async () => { - if (!personalEncryptionKey) return + if (!personalEncryptionKey || !userId) return + const result = await filesApiClient.listBuckets() const bucketsWithKeys: Bucket[] = await Promise.all( result.map(async (b) => { - //todo remove the ? once the writers and readers are non null const permission = b.owners.find(owner => owner === profile?.userId) ? "owner" as BucketPermission - : b.writers?.find(writer => writer === profile?.userId) + : b.writers.find(writer => writer === profile?.userId) ? "writer" as BucketPermission - : b.readers?.find(reader => reader === profile?.userId) + : b.readers.find(reader => reader === profile?.userId) ? "reader" as BucketPermission : undefined + let encryptionKey = "" + + switch(b.type) { + case "csf": + case "trash": { + encryptionKey = personalEncryptionKey + break + } + case "share": { + encryptionKey = await getKeyForSharedBucket(b) + break + }} + return { ...b, - encryptionKey: (b.type === "csf" || b.type === "trash") ? personalEncryptionKey : await getKeyForBucket(b), + encryptionKey, permission - }})) + } + }) + ) setBuckets(bucketsWithKeys) return Promise.resolve() - }, [personalEncryptionKey, filesApiClient, getKeyForBucket, profile?.userId]) + }, [personalEncryptionKey, userId, filesApiClient, profile, getKeyForSharedBucket]) useEffect(() => { refreshBuckets() @@ -169,40 +186,44 @@ const FilesProvider = ({ children }: FilesContextProps) => { } }, [isLoggedIn]) - // Drive encryption handler - useEffect(() => { - const secureAccount = async () => { - if (!publicKey) return - const key = Buffer.from( - window.crypto.getRandomValues(new Uint8Array(32)) - ).toString("base64") - console.log("New key", key) - setPersonalEncryptionKey(key) - const encryptedKey = await encryptForPublicKey(publicKey, key) - console.log("Encrypted encryption key", encryptedKey) - secureThresholdKeyAccount(encryptedKey) - } + const secureAccount = useCallback(() => { + if (!publicKey) return + + const key = Buffer.from( + window.crypto.getRandomValues(new Uint8Array(32)) + ).toString("base64") + console.log("New key", key) + setPersonalEncryptionKey(key) + encryptForPublicKey(publicKey, key) + .then((encryptedKey) => { + console.log("Encrypted encryption key", encryptedKey) + secureThresholdKeyAccount(encryptedKey) + }) + .catch(console.error) + }, [encryptForPublicKey, publicKey, secureThresholdKeyAccount]) - const decryptKey = async (encryptedKey: string) => { - console.log("Decrypting retrieved key") - try { - const decryptedKey = await decryptMessageWithThresholdKey(encryptedKey) - if (decryptedKey) { - console.log("Decrypted key: ", decryptedKey) - setPersonalEncryptionKey(decryptedKey) - } - } catch (error) { - console.error("Error decrypting key: ", encryptedKey) - } - } + const decryptKey = useCallback((encryptedKey: string) => { + console.log("Decrypting retrieved key") + decryptMessageWithThresholdKey(encryptedKey) + .then((decryptedKey) => { + console.log("Decrypted key: ", decryptedKey) + setPersonalEncryptionKey(decryptedKey) + }) + .catch(console.error) + }, [decryptMessageWithThresholdKey]) + + // Drive encryption handler + useEffect(() => { if (isLoggedIn && publicKey && !personalEncryptionKey) { console.log("Checking whether account is secured ", secured) + if (!secured && !isMasterPasswordSet) { console.log("Generating key and securing account") secureAccount() } else { console.log("decrypting key") + console.log("encryptedEncryptionKey", encryptedEncryptionKey) if (encryptedEncryptionKey) { decryptKey(encryptedEncryptionKey) } @@ -217,7 +238,9 @@ const FilesProvider = ({ children }: FilesContextProps) => { secureThresholdKeyAccount, decryptMessageWithThresholdKey, personalEncryptionKey, - isMasterPasswordSet + isMasterPasswordSet, + secureAccount, + decryptKey ]) const secureAccountWithMasterPassword = async (candidatePassword: string) => { From c38c7c7c1da3e195c75c4326b035ed39b8743f7b Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 11 Jun 2021 14:17:31 +0000 Subject: [PATCH 20/49] lingui extract --- packages/files-ui/src/locales/en/messages.po | 3 +++ packages/files-ui/src/locales/fr/messages.po | 3 +++ 2 files changed, 6 insertions(+) diff --git a/packages/files-ui/src/locales/en/messages.po b/packages/files-ui/src/locales/en/messages.po index 9922a3d806..3a8640196c 100644 --- a/packages/files-ui/src/locales/en/messages.po +++ b/packages/files-ui/src/locales/en/messages.po @@ -523,6 +523,9 @@ msgstr "Shared" msgid "Shared folders" msgstr "Shared folders" +msgid "Shared with" +msgstr "Shared with" + msgid "Sign Out" msgstr "Sign Out" diff --git a/packages/files-ui/src/locales/fr/messages.po b/packages/files-ui/src/locales/fr/messages.po index 5a58a18ad2..a32032e922 100644 --- a/packages/files-ui/src/locales/fr/messages.po +++ b/packages/files-ui/src/locales/fr/messages.po @@ -524,6 +524,9 @@ msgstr "" msgid "Shared folders" msgstr "" +msgid "Shared with" +msgstr "" + msgid "Sign Out" msgstr "DĂ©connexion" From edd8d71acca7daf73bbda55aad18d2d820f1aedd Mon Sep 17 00:00:00 2001 From: Thibaut Sardan Date: Mon, 14 Jun 2021 13:15:57 +0200 Subject: [PATCH 21/49] User circles --- .../src/Components/Elements/SharedUser.tsx | 58 +++++++++++++++++++ .../views/FileSystemItem/SharedFolderRow.tsx | 13 ++++- 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 packages/files-ui/src/Components/Elements/SharedUser.tsx diff --git a/packages/files-ui/src/Components/Elements/SharedUser.tsx b/packages/files-ui/src/Components/Elements/SharedUser.tsx new file mode 100644 index 0000000000..687be55ef5 --- /dev/null +++ b/packages/files-ui/src/Components/Elements/SharedUser.tsx @@ -0,0 +1,58 @@ +import React from "react" +import { makeStyles, createStyles } from "@chainsafe/common-theme" +import { CSFTheme } from "../../Themes/types" +import { UserIcon } from "@chainsafe/common-components" + +const useStyles = makeStyles(({ breakpoints, constants, palette }: CSFTheme) => { + return createStyles({ + root: { + }, + bubble: { + borderRadius: "50%", + border: "solid black 1px", + backgroundColor: palette.additional["gray"][6], + color: palette.common.white.main + }, + text : { + textAlign: "center" + } + }) +}) + +interface Props { + sharedUsers: string[] +} + +const SharedUsers = ({ sharedUsers }: Props) => { + const classes = useStyles() + + if (!sharedUsers.length) { + return null + } + + const UserBubble = ({ text, hover }: {text?: string; hover: string}) =>
+ {text + ? {text} + : + } +
+ + return
+ {sharedUsers.length > 2 && ( + + )} + {sharedUsers.length == 2 && ( + + )} + +
+} + +export default SharedUsers \ No newline at end of file diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/SharedFolderRow.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/SharedFolderRow.tsx index 488696af20..297053096b 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/SharedFolderRow.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/SharedFolderRow.tsx @@ -11,8 +11,9 @@ import { Typography } from "@chainsafe/common-components" import { CSFTheme } from "../../../../../Themes/types" -import { Bucket } from "@chainsafe/files-api-client" +import { Bucket, BucketUser } from "@chainsafe/files-api-client" import { desktopSharedGridSettings, mobileSharedGridSettings } from "../../SharedFoldersOverview" +import SharedUsers from "../../../../Elements/SharedUser" const useStyles = makeStyles(({ breakpoints, constants, palette }: CSFTheme) => { @@ -102,6 +103,14 @@ const SharedFolderRow = ({ bucket, onFolderClick, menuItems }: Props) => { const { name, size } = bucket const { desktop } = useThemeSwitcher() + const getUserIds = (users: BucketUser[]): string[] => { + return users.reduce((acc: string[], user): string[] => { + return user.uuid ? [...acc, user.uuid] : acc + }, [] as string[]) + } + + const userIds = [...getUserIds(bucket.owners), ...getUserIds(bucket.readers), ...getUserIds(bucket.writers)] + return ( { className={classes.filename} onClick={(e) => onFolderClick(e)} > - {bucket.readers[0]} + {desktop && ( <> From 6095398a4d7de7921526c6aba1c54102d10b2fa8 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Tue, 15 Jun 2021 16:24:16 +0200 Subject: [PATCH 22/49] add storage bucket list and bucket details --- .../src/Components/Elements/BucketRow.tsx | 108 ++ .../FileSystemItem/FileSystemGridItem.tsx | 263 +++++ .../Modules/FileSystemItem/FileSystemItem.tsx | 470 ++++++++ .../FileSystemItem/FileSystemTableItem.tsx | 259 +++++ .../Modules/FilesList/DragConstants.ts | 3 + .../Modules/FilesList/FilesList.tsx | 1020 +++++++++++++++++ .../src/Components/Pages/BucketPage.tsx | 239 ++++ .../src/Components/Pages/Buckets.tsx | 28 - .../src/Components/Pages/BucketsPage.tsx | 158 +++ .../src/Components/StorageRoutes.tsx | 16 +- .../storage-ui/src/Contexts/DnDContext.tsx | 11 + .../src/Contexts/FileBrowserContext.tsx | 48 + ...educers.tsx => FileOperationsReducers.tsx} | 0 .../src/Contexts/StorageContext.tsx | 168 +-- packages/storage-ui/src/Contexts/types.ts | 58 + packages/storage-ui/src/Utils/pathUtils.ts | 18 +- 16 files changed, 2762 insertions(+), 105 deletions(-) create mode 100644 packages/storage-ui/src/Components/Elements/BucketRow.tsx create mode 100644 packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemGridItem.tsx create mode 100644 packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemItem.tsx create mode 100644 packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemTableItem.tsx create mode 100644 packages/storage-ui/src/Components/Modules/FilesList/DragConstants.ts create mode 100644 packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx create mode 100644 packages/storage-ui/src/Components/Pages/BucketPage.tsx delete mode 100644 packages/storage-ui/src/Components/Pages/Buckets.tsx create mode 100644 packages/storage-ui/src/Components/Pages/BucketsPage.tsx create mode 100644 packages/storage-ui/src/Contexts/DnDContext.tsx create mode 100644 packages/storage-ui/src/Contexts/FileBrowserContext.tsx rename packages/storage-ui/src/Contexts/{FilesReducers.tsx => FileOperationsReducers.tsx} (100%) create mode 100644 packages/storage-ui/src/Contexts/types.ts diff --git a/packages/storage-ui/src/Components/Elements/BucketRow.tsx b/packages/storage-ui/src/Components/Elements/BucketRow.tsx new file mode 100644 index 0000000000..8776d1f49b --- /dev/null +++ b/packages/storage-ui/src/Components/Elements/BucketRow.tsx @@ -0,0 +1,108 @@ +import React from "react" +import { makeStyles, createStyles } from "@chainsafe/common-theme" +import { DeleteSvg, formatBytes, MenuDropdown, MoreIcon, TableCell, TableRow, useHistory } from "@chainsafe/common-components" +import { Trans } from "@lingui/macro" +import dayjs from "dayjs" +import { Bucket, PinObject } from "@chainsafe/files-api-client" +import { CSFTheme } from "../../Themes/types" +import { useStorage } from "../../Contexts/StorageContext" +import { desktopGridSettings, mobileGridSettings } from "../Pages/CidsPage" +import { ROUTE_LINKS } from "../StorageRoutes" + +const useStyles = makeStyles(({ animation, constants, breakpoints }: CSFTheme) => + createStyles({ + dropdownIcon: { + "& svg": { + fill: constants.fileSystemItemRow.dropdownIcon + } + }, + dropdownOptions: { + backgroundColor: constants.fileSystemItemRow.optionsBackground, + color: constants.fileSystemItemRow.optionsColor, + border: `1px solid ${constants.fileSystemItemRow.optionsBorder}` + }, + dropdownItem: { + backgroundColor: constants.fileSystemItemRow.itemBackground, + color: constants.fileSystemItemRow.itemColor + }, + menuIcon: { + display: "flex", + justifyContent: "center", + alignItems: "center", + width: 20, + marginRight: constants.generalUnit * 1.5, + "& svg": { + fill: constants.fileSystemItemRow.menuIcon + } + }, + cid: { + whiteSpace: "nowrap", + textOverflow: "ellipsis", + overflow: "hidden", + "&.editing": { + overflow: "visible" + } + }, + tableRow: { + border: "2px solid transparent", + transitionDuration: `${animation.transform}ms`, + [breakpoints.up("md")]: { + gridTemplateColumns: desktopGridSettings + }, + [breakpoints.down("md")]: { + gridTemplateColumns: mobileGridSettings + } + } + }) +) +interface Props { + bucket: Bucket +} + +const IPFS_GATEWAY = "https://ipfs.infura.io:5001/api/v0/cat/" + +const BucketRow = ({ bucket }: Props) => { + const classes = useStyles() + const { removeBucket } = useStorage() + const { redirect } = useHistory() + return ( + + redirect(ROUTE_LINKS.Bucket(bucket.id, "/"))}> + {bucket.name} + + + {formatBytes(bucket.size)} + + + + + + Delete bucket + + + ), + onClick: () => removeBucket(bucket.id) + }]} + classNames={{ + icon: classes.dropdownIcon, + options: classes.dropdownOptions, + item: classes.dropdownItem + }} + indicator={MoreIcon} + /> + + + ) +} + +export default BucketRow \ No newline at end of file diff --git a/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemGridItem.tsx b/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemGridItem.tsx new file mode 100644 index 0000000000..422a8d4a5c --- /dev/null +++ b/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemGridItem.tsx @@ -0,0 +1,263 @@ +import React, { useCallback, useEffect } from "react" +import { makeStyles, createStyles, useThemeSwitcher } from "@chainsafe/common-theme" +import { t } from "@lingui/macro" +import clsx from "clsx" +import { + FormikTextInput, + IMenuItem, + MenuDropdown, + MoreIcon +} from "@chainsafe/common-components" +import { CSFTheme } from "../../../Themes/types" +import { FileSystemItem } from "../../../Contexts/StorageContext" +import { ConnectDragPreview } from "react-dnd" +import { Form, Formik } from "formik" + +const useStyles = makeStyles(({ breakpoints, constants, palette }: CSFTheme) => { + return createStyles({ + fileIcon: { + display: "flex", + flexDirection: "row", + alignItems: "center", + justifyContent: "center", + "& svg": { + width: constants.generalUnit * 2.5, + fill: constants.fileSystemItemRow.icon + } + }, + folderIcon: { + "& svg": { + fill: palette.additional.gray[9] + } + }, + gridIcon: { + display: "flex", + justifyContent: "center", + alignItems: "center", + height: constants.generalUnit * 16, + maxWidth: constants.generalUnit * 24, + border: `1px solid ${palette.additional["gray"][6]}`, + boxShadow: constants.filesTable.gridItemShadow, + "& svg": { + width: "30%" + }, + [breakpoints.down("lg")]: { + height: constants.generalUnit * 16 + }, + [breakpoints.down("sm")]: { + height: constants.generalUnit * 16 + }, + "&.highlighted": { + border: `1px solid ${palette.primary.main}` + } + }, + renameInput: { + width: "100%", + [breakpoints.up("md")]: { + margin: 0 + }, + [breakpoints.down("md")]: { + margin: `${constants.generalUnit * 4.2}px 0` + } + }, + menuIcon: { + display: "flex", + justifyContent: "center", + alignItems: "center", + width: 20, + marginRight: constants.generalUnit * 1.5, + "& svg": { + fill: constants.fileSystemItemRow.menuIcon + } + }, + desktopRename: { + display: "flex", + flexDirection: "row", + "& svg": { + width: 20, + height: 20 + } + }, + dropdownIcon: { + "& svg": { + fill: constants.fileSystemItemRow.dropdownIcon + } + }, + dropdownOptions: { + backgroundColor: constants.fileSystemItemRow.optionsBackground, + color: constants.fileSystemItemRow.optionsColor, + border: `1px solid ${constants.fileSystemItemRow.optionsBorder}` + }, + dropdownItem: { + backgroundColor: constants.fileSystemItemRow.itemBackground, + color: constants.fileSystemItemRow.itemColor + }, + gridViewContainer: { + display: "flex", + flex: 1, + maxWidth: constants.generalUnit * 24 + }, + gridFolderName: { + textAlign: "center", + wordBreak: "break-all", + overflowWrap: "break-word", + padding: constants.generalUnit + }, + gridViewIconNameBox: { + display: "flex", + flexDirection: "column", + width: "100%", + cursor: "pointer" + }, + menuTitleGrid: { + padding: `0 ${constants.generalUnit * 0.5}px`, + [breakpoints.down("md")]: { + padding: 0 + } + } + }) +}) + +interface IFileSystemTableItemProps { + isFolder: boolean + isOverMove: boolean + isOverUpload: boolean + selected: string[] + file: FileSystemItem + editing: string | undefined + onFolderOrFileClicks: (e?: React.MouseEvent) => void + icon: React.ReactNode + preview: ConnectDragPreview + renameSchema: any + setEditing: (editing: string | undefined) => void + handleRename?: (path: string, newPath: string) => Promise + currentPath: string | undefined + menuItems: IMenuItem[] + resetSelectedFiles: () => void +} + +const FileSystemGridItem = React.forwardRef( + ({ + isFolder, + isOverMove, + isOverUpload, + selected, + file, + editing, + onFolderOrFileClicks, + icon, + renameSchema, + setEditing, + handleRename, + menuItems, + resetSelectedFiles, + preview + }: IFileSystemTableItemProps, forwardedRef: any) => { + const classes = useStyles() + const { name, cid } = file + const { desktop } = useThemeSwitcher() + + const handleClickOutside = useCallback( + (e) => { + if (forwardedRef.current && forwardedRef.current.contains(e.target)) { + // inside click + return + } + if (e.defaultPrevented || e.isPropagationStopped) { + return + } + // outside click + resetSelectedFiles() + }, + [resetSelectedFiles, forwardedRef] + ) + + useEffect(() => { + document.addEventListener("click", handleClickOutside) + return () => { + document.removeEventListener("click", handleClickOutside) + } + }, [handleClickOutside]) + + return ( +
{ + e.preventDefault() + e.stopPropagation() + }} + > +
onFolderOrFileClicks(e)} + > +
+ {icon} +
+ {editing === cid && desktop ? ( + { + handleRename && + handleRename( + file.cid, + values.fileName + ) + }} + > + + { + if (event.key === "Escape") { + setEditing(undefined) + } + }} + placeholder = {isFolder + ? t`Please enter a folder name` + : t`Please enter a file name` + } + autoFocus={editing === cid} + /> + + + ) : ( +
{name}
+ )} +
+
+ +
+
+ ) + } +) + +FileSystemGridItem.displayName = "FileSystemGridItem" + +export default FileSystemGridItem diff --git a/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemItem.tsx b/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemItem.tsx new file mode 100644 index 0000000000..aba9cc7034 --- /dev/null +++ b/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemItem.tsx @@ -0,0 +1,470 @@ +import React, { useCallback, useEffect, useRef } from "react" +import { + FormikTextInput, + Typography, + Button, + FileImageSvg, + FilePdfSvg, + FileTextSvg, + FolderFilledSvg, + DownloadSvg, + DeleteSvg, + EditSvg, + IMenuItem, + RecoverSvg, + EyeSvg, + ExportSvg, + ShareAltSvg, + ExclamationCircleInverseSvg, + ZoomInSvg } from "@chainsafe/common-components" +import { makeStyles, createStyles, useDoubleClick, useThemeSwitcher } from "@chainsafe/common-theme" +import { Formik, Form } from "formik" +import CustomModal from "../../Elements/CustomModal" +import { Trans } from "@lingui/macro" +import { useDrag, useDrop } from "react-dnd" +import { getEmptyImage, NativeTypes } from "react-dnd-html5-backend" +import { CSFTheme } from "../../../Themes/types" +import FileItemTableItem from "./FileSystemTableItem" +import FileItemGridItem from "./FileSystemGridItem" +import { FileSystemItem as FileSystemItemType } from "../../../Contexts/StorageContext" +import { useFileBrowser } from "../../../Contexts/FileBrowserContext" +import { BrowserView, FileOperation } from "../../../Contexts/types" +import { DragTypes } from "../FilesList/DragConstants" + +const useStyles = makeStyles(({ breakpoints, constants }: CSFTheme) => { + return createStyles({ + renameInput: { + width: "100%", + [breakpoints.up("md")]: { + margin: 0 + }, + [breakpoints.down("md")]: { + margin: `${constants.generalUnit * 4.2}px 0` + } + }, + modalRoot: { + [breakpoints.down("md")]: {} + }, + modalInner: { + [breakpoints.down("md")]: { + bottom: + Number(constants?.mobileButtonHeight) + constants.generalUnit, + borderTopLeftRadius: `${constants.generalUnit * 1.5}px`, + borderTopRightRadius: `${constants.generalUnit * 1.5}px`, + borderBottomLeftRadius: `${constants.generalUnit * 1.5}px`, + borderBottomRightRadius: `${constants.generalUnit * 1.5}px`, + maxWidth: `${breakpoints.width("md")}px !important` + } + }, + renameHeader: { + textAlign: "center" + }, + renameFooter: { + display: "flex", + flexDirection: "row", + alignItems: "center", + justifyContent: "flex-end" + }, + renameModal: { + padding: constants.generalUnit * 4 + }, + okButton: { + marginLeft: constants.generalUnit + }, + cancelButton: { + [breakpoints.down("md")]: { + position: "fixed", + bottom: 0, + left: 0, + width: "100%", + height: constants?.mobileButtonHeight + } + }, + menuIcon: { + display: "flex", + justifyContent: "center", + alignItems: "center", + width: 20, + marginRight: constants.generalUnit * 1.5, + "& svg": { + fill: constants.fileSystemItemRow.menuIcon + } + }, + dropdownIcon: { + "& svg": { + fill: constants.fileSystemItemRow.dropdownIcon + } + } + }) +}) + +interface IFileSystemItemProps { + index: number + 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?: (cid: string, newName: string) => Promise + handleMove?: (cid: string, newPath: string) => Promise + deleteFile?: () => void + recoverFile?: (cid: string) => void + viewFolder?: (cid: string) => void + setPreviewFileIndex: (fileIndex: number | undefined) => void + moveFile?: () => void + setFileInfoPath: (path: string) => void + itemOperations: FileOperation[] + resetSelectedFiles: () => void + browserView: BrowserView +} + +const FileSystemItem = ({ + file, + files, + selected, + editing, + setEditing, + renameSchema, + handleRename, + deleteFile, + recoverFile, + viewFolder, + setPreviewFileIndex, + moveFile, + setFileInfoPath, + handleSelectCid, + handleAddToSelectedCids, + itemOperations, + browserView, + resetSelectedFiles +}: IFileSystemItemProps) => { + const { downloadFile, currentPath, handleUploadOnDrop, moveItems } = useFileBrowser() + const { cid, name, isFolder, content_type } = file + let Icon + if (isFolder) { + Icon = FolderFilledSvg + } else if (content_type.includes("image")) { + Icon = FileImageSvg + } else if (content_type.includes("pdf")) { + Icon = FilePdfSvg + } else { + Icon = FileTextSvg + } + + const { desktop } = useThemeSwitcher() + const classes = useStyles() + + + const allMenuItems: Record = { + rename: { + contents: ( + <> + + + Rename + + + ), + onClick: () => setEditing(cid) + }, + delete: { + contents: ( + <> + + + Delete + + + ), + onClick: () => deleteFile && deleteFile() + }, + download: { + contents: ( + <> + + + Download + + + ), + onClick: () => downloadFile && downloadFile(cid) + }, + move: { + contents: ( + <> + + + Move + + + ), + onClick: () => moveFile && moveFile() + }, + share: { + contents: ( + <> + + + Share + + + ), + onClick: () => console.log + }, + info: { + contents: ( + <> + + + Info + + + ), + onClick: () => setFileInfoPath(`${currentPath}${name}`) + }, + recover: { + contents: ( + <> + + + Recover + + + ), + onClick: () => recoverFile && recoverFile(cid) + }, + preview: { + contents: ( + <> + + + Preview + + + ), + onClick: () => setPreviewFileIndex(files?.indexOf(file)) + }, + view_folder: { + contents: ( + <> + + + View folder + + + ), + onClick: () => viewFolder && viewFolder(cid) + } + } + + const menuItems: IMenuItem[] = itemOperations.map( + (itemOperation) => allMenuItems[itemOperation] + ) + + const [, dragMoveRef, preview] = useDrag(() => + ({ type: DragTypes.MOVABLE_FILE, + item: () => { + if (selected.includes(file.cid)) { + return { ids: selected } + } else { + return { ids: [...selected, file.cid] } + } + } + }), [selected]) + + useEffect(() => { + // This gets called after every render, by default + + // Use empty image as a drag preview so browsers don't draw it + // and we can draw whatever we want on the custom drag layer instead. + preview(getEmptyImage(), { + // IE fallback: specify that we'd rather screenshot the node + // when it already knows it's being dragged so we can hide it with CSS. + captureDraggingState: true + }) + }) + + const [{ isOverMove }, dropMoveRef] = useDrop({ + accept: DragTypes.MOVABLE_FILE, + canDrop: () => isFolder, + drop: (item: {ids: string[]}) => { + moveItems && moveItems(item.ids, `${currentPath}${name}/`) + }, + collect: (monitor) => ({ + isOverMove: monitor.isOver() + }) + }) + + const [{ isOverUpload }, dropUploadRef] = useDrop({ + accept: [NativeTypes.FILE], + drop: (item: any) => { + handleUploadOnDrop && + handleUploadOnDrop(item.files, item.items, `${currentPath}${name}`) + }, + collect: (monitor) => ({ + isOverUpload: monitor.isOver() + }) + }) + + const fileOrFolderRef = useRef() + + if (!editing && isFolder) { + dropMoveRef(fileOrFolderRef) + dropUploadRef(fileOrFolderRef) + } + if (!editing && !isFolder) { + dragMoveRef(fileOrFolderRef) + } + + const onFilePreview = useCallback(() => { + setPreviewFileIndex(files?.indexOf(file)) + }, [file, files, setPreviewFileIndex]) + + const onSingleClick = useCallback( + (e) => { + if (desktop) { + // on desktop + if (e && (e.ctrlKey || e.metaKey)) { + handleAddToSelectedCids(cid) + } else { + handleSelectCid(cid) + } + } else { + // on mobile + if (isFolder) { + viewFolder && viewFolder(file.cid) + } else { + onFilePreview() + } + } + }, + [cid, handleSelectCid, handleAddToSelectedCids, desktop, isFolder, viewFolder, file, onFilePreview] + ) + + const onDoubleClick = useCallback( + () => { + if (desktop) { + // on desktop + if (isFolder) { + viewFolder && viewFolder(file.cid) + } else { + onFilePreview() + } + } else { + // on mobile + return + } + }, + [desktop, viewFolder, file, onFilePreview, isFolder] + ) + + const { click } = useDoubleClick(onSingleClick, onDoubleClick) + + const onFolderOrFileClicks = (e?: React.MouseEvent) => { + e?.persist() + click(e) + } + + const itemProps = { + ref: fileOrFolderRef, + currentPath, + editing, + file, + handleAddToSelectedCids, + handleRename, + icon: , + isFolder, + isOverMove, + isOverUpload, + menuItems, + onFolderOrFileClicks, + preview, + renameSchema, + selected, + setEditing, + resetSelectedFiles + } + + return ( + <> + { + browserView === "table" + ? + : + } + { + editing === cid && !desktop && ( + <> + setEditing("")} + > + { + handleRename && + handleRename( + file.cid, + values.fileName + ) + }} + > +
+ + Rename File/Folder + + +
+ + +
+ +
+
+ {name} + + ) + } + + ) +} + +export default FileSystemItem diff --git a/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemTableItem.tsx b/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemTableItem.tsx new file mode 100644 index 0000000000..d12792836a --- /dev/null +++ b/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemTableItem.tsx @@ -0,0 +1,259 @@ +import React from "react" +import { makeStyles, createStyles, useThemeSwitcher } from "@chainsafe/common-theme" +import { t } from "@lingui/macro" +import clsx from "clsx" +import { + Button, + CheckboxInput, + CheckSvg, + formatBytes, + FormikTextInput, + IMenuItem, + MenuDropdown, + MoreIcon, + TableCell, + TableRow, + Typography +} from "@chainsafe/common-components" +import dayjs from "dayjs" +import { ConnectDragPreview } from "react-dnd" +import { Form, Formik } from "formik" +import { CSFTheme } from "../../../Themes/types" +import { FileSystemItem } from "../../../Contexts/StorageContext" + +const useStyles = makeStyles(({ breakpoints, constants, palette }: CSFTheme) => { + const desktopGridSettings = "50px 69px 3fr 190px 100px 45px !important" + const mobileGridSettings = "69px 3fr 45px !important" + + return createStyles({ + tableRow: { + border: "2px solid transparent", + [breakpoints.up("md")]: { + gridTemplateColumns: desktopGridSettings + }, + [breakpoints.down("md")]: { + gridTemplateColumns: mobileGridSettings + }, + "&.droppable": { + border: `2px solid ${palette.primary.main}` + } + }, + fileIcon: { + display: "flex", + flexDirection: "row", + alignItems: "center", + justifyContent: "center", + "& svg": { + width: constants.generalUnit * 2.5, + fill: constants.fileSystemItemRow.icon + } + }, + folderIcon: { + "& svg": { + fill: palette.additional.gray[9] + } + }, + renameInput: { + width: "100%", + [breakpoints.up("md")]: { + margin: 0 + }, + [breakpoints.down("md")]: { + margin: `${constants.generalUnit * 4.2}px 0` + } + }, + menuIcon: { + display: "flex", + justifyContent: "center", + alignItems: "center", + width: 20, + marginRight: constants.generalUnit * 1.5, + "& svg": { + fill: constants.fileSystemItemRow.menuIcon + } + }, + desktopRename: { + display: "flex", + flexDirection: "row", + "& svg": { + width: 20, + height: 20 + } + }, + filename: { + whiteSpace: "nowrap", + textOverflow: "ellipsis", + overflow: "hidden", + "&.editing": { + overflow: "visible" + } + }, + dropdownIcon: { + "& svg": { + fill: constants.fileSystemItemRow.dropdownIcon + } + }, + dropdownOptions: { + backgroundColor: constants.fileSystemItemRow.optionsBackground, + color: constants.fileSystemItemRow.optionsColor, + border: `1px solid ${constants.fileSystemItemRow.optionsBorder}` + }, + dropdownItem: { + backgroundColor: constants.fileSystemItemRow.itemBackground, + color: constants.fileSystemItemRow.itemColor + } + }) +}) + +interface IFileSystemTableItemProps { + isFolder: boolean + isOverMove: boolean + isOverUpload: boolean + selected: string[] + file: FileSystemItem + editing: string | undefined + handleAddToSelectedCids: (selected: string) => void + onFolderOrFileClicks: (e?: React.MouseEvent) => void + icon: React.ReactNode + preview: ConnectDragPreview + renameSchema: any + setEditing: (editing: string | undefined) => void + handleRename?: (path: string, newPath: string) => Promise + currentPath: string | undefined + menuItems: IMenuItem[] +} + +const FileSystemTableItem = React.forwardRef( + ({ + isFolder, + isOverMove, + isOverUpload, + selected, + file, + editing, + handleAddToSelectedCids, + onFolderOrFileClicks, + icon, + preview, + renameSchema, + setEditing, + handleRename, + menuItems + }: IFileSystemTableItemProps, forwardedRef: any) => { + const classes = useStyles() + const { name, cid, created_at, size } = file + const { desktop } = useThemeSwitcher() + + return ( + + {desktop && ( + + handleAddToSelectedCids(cid)} + /> + + )} + onFolderOrFileClicks(e)} + > + {icon} + + !editing && onFolderOrFileClicks(e)} + > + {editing === cid && desktop ? ( + { + handleRename && + handleRename( + file.cid, + values.fileName + ) + }} + > +
+ { + if (event.key === "Escape") { + setEditing(undefined) + } + }} + placeholder = {isFolder + ? t`Please enter a folder name` + : t`Please enter a file name` + } + autoFocus={editing === cid} + /> + + +
+ ) : ( + {name} + )} +
+ {desktop && ( + <> + + { + !isFolder && !!created_at && dayjs.unix(created_at).format("DD MMM YYYY h:mm a") + } + + + {!isFolder && formatBytes(size)} + + + )} + + + +
+ ) + } +) + +FileSystemTableItem.displayName = "FileSystemTableItem" + +export default FileSystemTableItem diff --git a/packages/storage-ui/src/Components/Modules/FilesList/DragConstants.ts b/packages/storage-ui/src/Components/Modules/FilesList/DragConstants.ts new file mode 100644 index 0000000000..c5a5dd85ac --- /dev/null +++ b/packages/storage-ui/src/Components/Modules/FilesList/DragConstants.ts @@ -0,0 +1,3 @@ +export const DragTypes = { + MOVABLE_FILE: "movable_file" +} diff --git a/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx b/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx new file mode 100644 index 0000000000..1a8f4c8e90 --- /dev/null +++ b/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx @@ -0,0 +1,1020 @@ +import { createStyles, makeStyles, useThemeSwitcher } from "@chainsafe/common-theme" +import React, { useCallback, useEffect } from "react" +import { + Divider, + MenuDropdown, + PlusIcon, + SortDirection, + Table, + TableBody, + TableCell, + TableHead, + TableHeadCell, + TableRow, + Typography, + Breadcrumb, + CircularProgressBar, + Button, + PlusCircleIcon, + UploadIcon, + Dialog, + Loading, + CheckboxInput, + useHistory, + GridIcon, + TableIcon +} from "@chainsafe/common-components" +import { useState } from "react" +import { useMemo } from "react" +import { object, string } from "yup" +import EmptySvg from "../../../Media/Empty.svg" +import clsx from "clsx" +import { plural, t, Trans } from "@lingui/macro" +import { NativeTypes } from "react-dnd-html5-backend" +import { useDrop } from "react-dnd" +import { BrowserView, FileOperation, MoveModalMode } from "../../../Contexts/types" +import { FileSystemItem as FileSystemItemType } from "../../../Contexts/StorageContext" +import FileSystemItem from "../FileSystemItem/FileSystemItem" +// import FilePreviewModal from "../../FilePreviewModal" +// import UploadProgressModals from "../../UploadProgressModals" +// import DownloadProgressModals from "../../DownloadProgressModals" +// import CreateFolderModal from "../CreateFolderModal" +// import UploadFileModule from "../../UploadFileModule" +// import MoveFileModule from "../MoveFileModal" +// import FileInfoModal from "../FileInfoModal" +import { CONTENT_TYPES } from "../../../Utils/Constants" +import { CSFTheme } from "../../../Themes/types" +import MimeMatcher from "../../../Utils/MimeMatcher" +import { useLanguageContext } from "../../../Contexts/LanguageContext" +import { getPathWithFile } from "../../../Utils/pathUtils" +import { useFileBrowser } from "../../../Contexts/FileBrowserContext" + +interface IStyleProps { + themeKey: string +} + +const useStyles = makeStyles( + ({ animation, breakpoints, constants, palette, zIndex }: CSFTheme) => { + const desktopGridSettings = "50px 69px 3fr 190px 100px 45px !important" + const mobileGridSettings = "69px 3fr 45px !important" + return createStyles({ + root: { + position: "relative", + [breakpoints.down("md")]: { + marginLeft: constants.generalUnit * 2, + marginRight: constants.generalUnit * 2 + }, + [breakpoints.up("md")]: { + border: "1px solid transparent", + padding: `0 ${constants.generalUnit}px`, + borderRadius: constants.generalUnit / 4, + minHeight: `calc(100vh - ${Number(constants.contentTopPadding)}px)`, + "&.droppable": { + borderColor: palette.primary.main + } + } + // transitionDuration: `${animation.transform}ms`, + }, + header: { + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + [breakpoints.down("md")]: { + marginTop: constants.generalUnit + } + }, + controls: { + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + "& > button": { + marginLeft: constants.generalUnit + } + }, + breadCrumbContainer: { + margin: `${constants.generalUnit * 2}px 0`, + height: 22, + [breakpoints.down("md")]: { + marginTop: constants.generalUnit * 3, + marginBottom: 0 + } + }, + divider: { + "&:before, &:after": { + backgroundColor: palette.additional["gray"][4] + }, + [breakpoints.up("md")]: { + margin: `${constants.generalUnit * 3}px 0` + }, + [breakpoints.down("md")]: { + margin: `${constants.generalUnit * 3}px 0 0` + } + }, + noFiles: ({ themeKey }: IStyleProps) => ({ + display: "flex", + flexDirection: "column", + alignItems: "center", + marginTop: "25vh", + color: constants.filesTable.color, + "& svg": { + maxWidth: 180, + marginBottom: constants.generalUnit * 3, + "& path": { + "&:first-child": { + fill: themeKey === "dark" ? palette.additional.gray[2] : "" + }, + "&:nth-child(2)": { + stroke: themeKey === "dark" ? palette.additional.gray[2] : "", + fill: themeKey === "dark" ? "transparent" : "" + }, + "&:last-child": { + fill: themeKey === "dark" ? palette.additional.gray[4] : "", + stroke: themeKey === "dark" ? palette.additional.gray[2] : "" + } + } + } + }), + tableRow: { + border: "2px solid transparent", + transitionDuration: `${animation.transform}ms`, + [breakpoints.up("md")]: { + gridTemplateColumns: desktopGridSettings + }, + [breakpoints.down("md")]: { + gridTemplateColumns: mobileGridSettings + } + }, + fileIcon: { + display: "flex", + flexDirection: "row", + alignItems: "center", + justifyContent: "center", + "& svg": { + width: constants.generalUnit * 2.5 + } + }, + progressIcon: { + display: "flex", + flexDirection: "row", + alignItems: "center", + justifyContent: "center" + }, + dropdownIcon: { + "& svg": { + height: 20, + width: 20 + } + }, + dropdownOptions: { + "& > *": { + padding: 0 + } + }, + mobileButton: { + padding: `${constants.generalUnit * 2}px !important`, + borderRadius: 0, + justifyContent: "flex-start", + "& > *:last-child": { + display: "block", + width: "calc(100% - 24px)", + textAlign: "center" + } + }, + dropNotification: { + display: "block", + position: "fixed", + zIndex: zIndex?.layer4, + bottom: 0, + transform: "translateX(-50%)", + backgroundColor: palette.common.black.main, + color: constants.filesTable.uploadText, + opacity: 0, + visibility: "hidden", + transitionDuration: `${animation.transform}ms`, + textAlign: "center", + width: 260, + padding: `${constants.generalUnit}px 0`, + [breakpoints.up("md")]: { + left: `calc(50% + ${Number(constants.navWidth) / 2}px)` + }, + "&.active": { + opacity: 1, + visibility: "visible" + } + }, + loadingContainer: { + position: "absolute", + width: "100%", + paddingTop: constants.generalUnit * 6, + display: "flex", + flexDirection: "column", + alignItems: "center", + opacity: 0, + visibility: "hidden", + transition: `opacity ${animation.transform * 3}ms`, + "& svg": { + marginBottom: constants.generalUnit * 2 + } + }, + showLoadingContainer: { + visibility: "visible", + opacity: 1 + }, + fadeOutLoading: { + opacity: 0.2, + transition: `opacity ${animation.transform * 3}ms` + }, + tableHead: { + marginTop: constants.generalUnit * 3 + }, + bulkOperations: { + display: "flex", + flexDirection: "row", + marginTop: constants.generalUnit * 3, + minHeight: constants.generalUnit * 4.2, // reserve space for buttons for the interface not to jump when they get visible + "& > *": { + marginRight: constants.generalUnit + } + }, + confirmDeletionDialog: { + top: "50%" + }, + gridRoot: { + display: "grid", + gridTemplateColumns: "1fr 1fr 1fr 1fr 1fr 1fr", + gridColumnGap: constants.generalUnit * 2, + gridRowGap: constants.generalUnit * 2, + marginBottom: constants.generalUnit * 4, + marginTop: constants.generalUnit * 4, + [breakpoints.down("lg")]: { + gridTemplateColumns: "1fr 1fr 1fr 1fr" + }, + [breakpoints.down("md")]: { + margin: `${constants.generalUnit * 4}px 0` + }, + [breakpoints.down("sm")]: { + gridTemplateColumns: "1fr 1fr", + margin: `${constants.generalUnit * 2}px 0` + } + }, + viewToggleButton: { + border: "none", + "& svg": { + marginTop: "2px", + width: "20px", + height: "20px" + } + } + }) + } +) + +// Sorting +const sortFoldersFirst = (a: FileSystemItemType, b: FileSystemItemType) => + a.isFolder && a.content_type !== b.content_type ? -1 : 1 + +const FilesList = () => { + const { themeKey, desktop } = useThemeSwitcher() + + const { + heading, + controls = true, + sourceFiles, + handleUploadOnDrop, + bulkOperations, + crumbs, + renameItem: handleRename, + deleteItems: deleteFiles, + viewFolder, + currentPath, + refreshContents, + loadingCurrentPath, + uploadsInProgress, + showUploadsInTable, + allowDropUpload, + itemOperations, + moduleRootPath + } = useFileBrowser() + const classes = useStyles({ themeKey }) + const [editing, setEditing] = useState() + const [direction, setDirection] = useState("ascend") + const [column, setColumn] = useState<"name" | "size" | "date_uploaded">("name") + const [selectedCids, setSelectedCids] = useState([]) + const [previewFileIndex, setPreviewFileIndex] = useState() + const { selectedLocale } = useLanguageContext() + const { redirect } = useHistory() + + const items: FileSystemItemType[] = useMemo(() => { + let temp = [] + + switch (column) { + default: { + // case "name": { + temp = sourceFiles.sort((a, b) => { + return a.name.localeCompare(b.name, selectedLocale, { + sensitivity: "base" + }) + }) + break + } + case "size": { + temp = sourceFiles + .sort((a, b) => (a.size < b.size ? -1 : 1)) + .sort(sortFoldersFirst) + break + } + case "date_uploaded": { + temp = sourceFiles + .sort((a, b) => (a.created_at < b.created_at ? -1 : 1)) + .sort(sortFoldersFirst) + break + } + } + return direction === "descend" + ? temp.reverse().sort(sortFoldersFirst) + : temp.sort(sortFoldersFirst) + }, [sourceFiles, direction, column, selectedLocale]) + + const files = useMemo(() => items.filter((i) => !i.isFolder), [items]) + + const selectedItems = useMemo( + () => items.filter((file) => selectedCids.includes(file.cid)), + [selectedCids, items] + ) + + const handleSortToggle = ( + targetColumn: "name" | "size" | "date_uploaded" + ) => { + if (column !== targetColumn) { + setColumn(targetColumn) + setDirection("descend") + } else { + if (direction === "ascend") { + setDirection("descend") + } else { + setDirection("ascend") + } + } + } + + // Previews + const setNextPreview = () => { + if ( + files && + previewFileIndex !== undefined && + previewFileIndex < files.length - 1 + ) { + setPreviewFileIndex(previewFileIndex + 1) + } + } + + const setPreviousPreview = () => { + if (files && previewFileIndex !== undefined && previewFileIndex > 0) { + setPreviewFileIndex(previewFileIndex - 1) + } + } + + const clearPreview = () => { + setPreviewFileIndex(undefined) + } + + // Selection logic + const handleSelectCid = useCallback( + (cid: string) => { + if (selectedCids.includes(cid)) { + setSelectedCids([]) + } else { + setSelectedCids([cid]) + } + }, + [selectedCids] + ) + + const handleAddToSelectedCids = useCallback( + (cid: string) => { + if (selectedCids.includes(cid)) { + setSelectedCids( + selectedCids.filter((selectedCid: string) => selectedCid !== cid) + ) + } else { + setSelectedCids([...selectedCids, cid]) + } + }, + [selectedCids] + ) + + const toggleAll = useCallback(() => { + if (selectedCids.length === items.length) { + setSelectedCids([]) + } else { + setSelectedCids([...items.map((file: FileSystemItemType) => file.cid)]) + } + }, [setSelectedCids, items, selectedCids]) + + const invalidFilenameRegex = new RegExp("/") + const renameSchema = object().shape({ + fileName: string() + .min(1, t`Please enter a name`) + .max(65, t`Name too long`) + .test( + t`Invalid name`, + t`Name cannot contain '/' character`, + (val: string | null | undefined) => + !invalidFilenameRegex.test(val || "") + ) + .required(t`A name is required`) + }) + + const [{ isOverUploadable, isOverBrowser }, dropBrowserRef] = useDrop({ + accept: [NativeTypes.FILE], + drop: (item: any, monitor) => { + if (monitor.isOver({ shallow: true })) { + handleUploadOnDrop && + currentPath && + handleUploadOnDrop(item.files, item.items, currentPath) + refreshContents && refreshContents() + } + }, + collect: (monitor) => ({ + isOverBrowser: monitor.isOver(), + isOverUploadable: monitor.isOver({ shallow: true }), + canDrop: monitor.canDrop() + }) + }) + + // Modals + const [createFolderModalOpen, setCreateFolderModalOpen] = useState(false) + const [isUploadModalOpen, setIsUploadModalOpen] = useState(false) + const [isMoveFileModalOpen, setIsMoveFileModalOpen] = useState(false) + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false) + const [isDeletingFiles, setIsDeletingFiles] = useState(false) + const [fileInfoPath, setFileInfoPath] = useState( + undefined + ) + const [moveModalMode, setMoveModalMode] = useState() + + const [browserView, setBrowserView] = useState("table") + + // Bulk operations + const [validBulkOps, setValidBulkOps] = useState([]) + + useEffect(() => { + if (bulkOperations) { + let filteredList: FileOperation[] = [ + "delete", + "download", + "info", + "move", + "preview", + "rename", + "share", + "recover" + ] + for (let i = 0; i < selectedCids.length; i++) { + const contentType = items.find((item) => item.cid === selectedCids[i]) + ?.content_type + + if (contentType) { + if (contentType === CONTENT_TYPES.Directory) { + const validList = filteredList.filter( + (op: FileOperation) => + bulkOperations[contentType].indexOf(op) >= 0 + ) + if (validList.length > 0) { + filteredList = filteredList.filter( + (existingOp: FileOperation) => + validList.indexOf(existingOp) >= 0 + ) + } + } else { + const validList = filteredList.filter( + (op: FileOperation) => + bulkOperations[CONTENT_TYPES.File].indexOf(op) >= 0 + ) + if (validList.length > 0) { + filteredList = filteredList.filter( + (existingOp: FileOperation) => + validList.indexOf(existingOp) >= 0 + ) + } + } + } else { + const validList = filteredList.filter( + (op: FileOperation) => + bulkOperations[CONTENT_TYPES.File].indexOf(op) >= 0 + ) + if (validList.length > 0) { + filteredList = filteredList.filter( + (existingOp: FileOperation) => validList.indexOf(existingOp) >= 0 + ) + } + } + } + setValidBulkOps(filteredList) + } + }, [selectedCids, items, bulkOperations]) + + const handleDeleteFiles = useCallback(() => { + if (!deleteFiles) return + + setIsDeletingFiles(true) + deleteFiles(selectedCids) + .catch(console.error) + .finally(() => { + setIsDeletingFiles(false) + setSelectedCids([]) + setIsDeleteModalOpen(false) + }) + }, [deleteFiles, selectedCids]) + + const getItemOperations = useCallback( + (contentType: string) => { + const result = Object.keys(itemOperations).reduce( + (acc: FileOperation[], item: string) => { + const matcher = new MimeMatcher(item) + // Prevent Files options from being added to Directory options + if ( + !( + contentType === CONTENT_TYPES.Directory && + item === CONTENT_TYPES.File + ) && + matcher.match(contentType) + ) { + acc.push(...itemOperations[item]) + } + return acc + }, + [] + ) + return [...new Set(result)] + }, + [itemOperations] + ) + + const resetSelectedCids = useCallback(() => { + setSelectedCids([]) + }, []) + + useEffect(() => { + setSelectedCids([]) + }, [currentPath]) + + const handleViewFolder = useCallback((cid: string) => { + viewFolder && viewFolder(cid) + }, [viewFolder]) + + const handleOpenMoveFileDialog = useCallback((e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() + setIsMoveFileModalOpen(true) + }, []) + + const handleOpenDeleteDialog = useCallback((e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() + setIsDeleteModalOpen(true) + }, []) + + return ( +
+
+ + Drop to upload files + +
+ {/* */} +
+ {crumbs && moduleRootPath ? ( + redirect(moduleRootPath)} + showDropDown={!desktop} + /> + ) : null} +
+
+ + {heading} + +
+ {controls && desktop ? ( + <> + + + + + ) : ( + controls && !desktop && ( + <> + + setCreateFolderModalOpen(true)} + variant="primary" + size="large" + className={classes.mobileButton} + fullsize + > + + + Create folder + + + ) + }, + { + contents: ( + + ) + } + ]} + /> + + ) + )} +
+
+ + +
+ {selectedCids.length > 0 && ( + <> + {validBulkOps.indexOf("move") >= 0 && ( + + )} + {validBulkOps.indexOf("recover") >= 0 && ( + + )} + {validBulkOps.indexOf("delete") >= 0 && ( + + )} + + )} +
+
+ + + One sec, getting files ready... + +
+ {(desktop && items.length === 0) || + (!desktop && items.length === 0 /*&& uploadsInProgress?.length === 0 */) ? ( +
+ + + No files to show + +
+ ) : browserView === "table" ? ( + + {desktop && ( + + + + toggleAll()} + /> + + + {/* + Icon + */} + + handleSortToggle("name")} + sortDirection={column === "name" ? direction : undefined} + sortActive={column === "name"} + > + Name + + handleSortToggle("date_uploaded")} + sortDirection={ + column === "date_uploaded" ? direction : undefined + } + sortActive={column === "date_uploaded"} + > + Date uploaded + + handleSortToggle("size")} + sortDirection={column === "size" ? direction : undefined} + sortActive={column === "size"} + > + Size + + {/* Menu */} + + + )} + + {!desktop && + showUploadsInTable && + uploadsInProgress + ?.filter( + (uploadInProgress) => + uploadInProgress.path === currentPath && + !uploadInProgress.complete && + !uploadInProgress.error + ) + .map((uploadInProgress) => ( + + + + + + {uploadInProgress.noOfFiles > 1 + ? t`Uploading ${uploadInProgress.noOfFiles} files` + : uploadInProgress.fileName} + + + + ))} + {items.map((file, index) => ( + { + handleRename && (await handleRename(cid, newName)) + setEditing(undefined) + }} + deleteFile={() => { + setSelectedCids([file.cid]) + setIsDeleteModalOpen(true) + }} + viewFolder={handleViewFolder} + setPreviewFileIndex={setPreviewFileIndex} + moveFile={() => { + setSelectedCids([file.cid]) + setIsMoveFileModalOpen(true) + setMoveModalMode("move") + }} + setFileInfoPath={setFileInfoPath} + itemOperations={getItemOperations(file.content_type)} + resetSelectedFiles={resetSelectedCids} + browserView="table" + recoverFile={() => { + setSelectedCids([file.cid]) + setIsMoveFileModalOpen(true) + setMoveModalMode("recover") + }} + /> + ))} + +
+ ) : ( +
+ {items.map((file, index) => ( + { + handleRename && (await handleRename(path, newPath)) + setEditing(undefined) + }} + deleteFile={() => { + setSelectedCids([file.cid]) + setIsDeleteModalOpen(true) + }} + setPreviewFileIndex={setPreviewFileIndex} + moveFile={() => { + setSelectedCids([file.cid]) + setIsMoveFileModalOpen(true) + setMoveModalMode("move") + }} + setFileInfoPath={setFileInfoPath} + itemOperations={getItemOperations(file.content_type)} + resetSelectedFiles={resetSelectedCids} + recoverFile={() => { + setSelectedCids([file.cid]) + setIsMoveFileModalOpen(true) + setMoveModalMode("recover") + }} + browserView="grid" + /> + ))} +
+ )} + {/* {files && previewFileIndex !== undefined && bucket && ( + 0 ? setPreviousPreview : undefined} + path={isSearch && getPath ? getPath(files[previewFileIndex].cid) : getPathWithFile(currentPath, files[previewFileIndex].name)} + /> + )} */} + setIsDeleteModalOpen(false)} + accept={handleDeleteFiles} + requestMessage={ + plural(selectedCids.length, { + one: `You are about to delete ${selectedCids.length} file.`, + other: `You are about to delete ${selectedCids.length} files.` + }) + } + rejectText = {t`Cancel`} + acceptText = {t`Confirm`} + acceptButtonProps={{ loading: isDeletingFiles, disabled: isDeletingFiles }} + rejectButtonProps={{ disabled: isDeletingFiles }} + injectedClass={{ inner: classes.confirmDeletionDialog }} + onModalBodyClick={(e) => { + e.preventDefault() + e.stopPropagation() + }} + /> + {/* + */} + {/* { + refreshContents && ( + <> + setCreateFolderModalOpen(false)} + /> + setIsUploadModalOpen(false)} + /> + { + setIsMoveFileModalOpen(false) + setSelectedCids([]) + setMoveModalMode(undefined) + }} + onCancel={() => { + setIsMoveFileModalOpen(false) + setMoveModalMode(undefined) + }} + mode={moveModalMode} + /> + + ) + } */} + {/* + setFileInfoPath(undefined)} + /> */} +
+ ) +} + +export default FilesList diff --git a/packages/storage-ui/src/Components/Pages/BucketPage.tsx b/packages/storage-ui/src/Components/Pages/BucketPage.tsx new file mode 100644 index 0000000000..f275df93c1 --- /dev/null +++ b/packages/storage-ui/src/Components/Pages/BucketPage.tsx @@ -0,0 +1,239 @@ +import React, { useCallback, useEffect, useMemo, useState } from "react" +import { Crumb, useToaster, useHistory, useLocation, Typography } from "@chainsafe/common-components" +import { useStorage, FileSystemItem } from "../../Contexts/StorageContext" +import { getArrayOfPaths, getURISafePathFromArray, getPathWithFile, extractFileBrowserPathFromURL } from "../../Utils/pathUtils" +import { IBulkOperations, IFileBrowserModuleProps, IFilesTableBrowserProps } from "../../Contexts/types" +import FilesList from "../Modules/FilesList/FilesList" +import { CONTENT_TYPES } from "../../Utils/Constants" +import DragAndDrop from "../../Contexts/DnDContext" +import { t } from "@lingui/macro" +import { ROUTE_LINKS } from "../../Components/StorageRoutes" +import { useStorageApi } from "../../Contexts/StorageApiContext" +import { FileBrowserContext } from "../../Contexts/FileBrowserContext" +import { parseFileContentResponse } from "../../Utils/Helpers" +import { makeStyles, createStyles } from "@chainsafe/common-theme" +import { CSFTheme } from "../../Themes/types" + +const useStyles = makeStyles(({ breakpoints, animation, constants }: CSFTheme) => + createStyles({ + root: { + position: "relative", + minHeight: "100vh", + overflow: "hidden" + } + }) +) + +const BucketPage: React.FC = () => { + const classes = useStyles() + const { storageBuckets, uploadFiles, uploadsInProgress } = useStorage() + const { storageApiClient } = useStorageApi() + const { addToastMessage } = useToaster() + const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) + const [pathContents, setPathContents] = useState([]) + const { redirect } = useHistory() + + const { pathname } = useLocation() + const bucketId = useMemo(() => + pathname.split("/")[2] + , [pathname]) + + const currentPath = useMemo(() => { + return extractFileBrowserPathFromURL(pathname, ROUTE_LINKS.Bucket(bucketId, "/"))}, + [pathname, bucketId] + ) + const bucket = useMemo(() => storageBuckets.find(b => b.id === bucketId), [storageBuckets, bucketId]) + + const refreshContents = useCallback((showLoading?: boolean) => { + if (!bucket) return + showLoading && setLoadingCurrentPath(true) + storageApiClient.getBucketObjectChildrenList(bucket.id, { path: currentPath }) + .then((newContents) => { + showLoading && setLoadingCurrentPath(false) + + setPathContents( + newContents.map((fcr) => parseFileContentResponse(fcr)) + ) + }).catch(error => { + console.error(error) + }).finally(() => showLoading && setLoadingCurrentPath(false)) + }, [bucket, storageApiClient, currentPath]) + + useEffect(() => { + refreshContents(true) + }, [bucket, refreshContents]) + + const moveItemsToBin = useCallback(async (cids: string[]) => { + throw new Error("Not implemented") + // 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 + // } + + // try { + // await filesApiClient.moveBucketObjects(bucket.id, { + // path: getPathWithFile(currentPath, itemToDelete.name), + // new_path: getPathWithFile("/", itemToDelete.name), + // destination: buckets.find(b => b.type === "trash")?.id + // }) + // 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, storageApiClient, bucket]) + + const renameItem = useCallback(async (cid: string, newName: string) => { + const itemToRename = pathContents.find(i => i.cid === cid) + if (!bucket || !itemToRename) return + + storageApiClient.moveBucketObjects(bucket.id, { + path: getPathWithFile(currentPath, itemToRename.name), + new_path: getPathWithFile(currentPath, newName) }) + .then(() => refreshContents()) + .catch(console.error) + }, [refreshContents, storageApiClient, 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 (!itemToMove) return + try { + await storageApiClient.moveBucketObjects(bucket.id, { + path: getPathWithFile(currentPath, itemToMove.name), + new_path: getPathWithFile(newPath, itemToMove.name) + }) + const message = `${ + itemToMove.isFolder ? t`Folder` : t`File` + } ${t`moved successfully`}` + + addToastMessage({ + message: message, + appearance: "success" + }) + } catch (error) { + const message = `${t`There was an error moving this`} ${ + itemToMove.isFolder ? t`folder` : t`file` + }` + addToastMessage({ + message: message, + appearance: "error" + }) + } + })).finally(refreshContents) + }, [addToastMessage, pathContents, refreshContents, storageApiClient, bucket, currentPath]) + + const handleDownload = useCallback(async (cid: string) => { + throw new Error("Not implemented") + // const itemToDownload = pathContents.find(item => item.cid === cid) + // if (!itemToDownload || !bucket) return + + // downloadFile(bucket.id, itemToDownload, currentPath) + }, [pathContents, currentPath, bucket]) + + // Breadcrumbs/paths + const arrayOfPaths = useMemo(() => getArrayOfPaths(currentPath), [currentPath]) + const crumbs: Crumb[] = useMemo(() => arrayOfPaths.map((path, index) => ({ + text: decodeURIComponent(path), + onClick: () => { + redirect( + ROUTE_LINKS.Bucket(bucketId, getURISafePathFromArray(arrayOfPaths.slice(0, index + 1))) + ) + } + })), [arrayOfPaths, bucketId, 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) { + hasFolder = true + } + } + if (hasFolder) { + addToastMessage({ + message: "Folder uploads are not supported currently", + appearance: "error" + }) + } else { + uploadFiles(bucket.id, files, path).then(() => refreshContents()).catch(console.error) + } + }, [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) { + let urlSafePath = getURISafePathFromArray(getArrayOfPaths(currentPath)) + if (urlSafePath === "/") { + urlSafePath = "" + } + redirect(ROUTE_LINKS.Bucket(bucketId, `${urlSafePath}/${encodeURIComponent(`${fileSystemItem.name}`)}`)) + } + }, [currentPath, pathContents, redirect, bucketId]) + + const bulkOperations: IBulkOperations = useMemo(() => ({ + // [CONTENT_TYPES.Directory]: ["move"], + // [CONTENT_TYPES.File]: ["delete", "move"] + }), []) + + const itemOperations: IFilesTableBrowserProps["itemOperations"] = useMemo(() => ({ + [CONTENT_TYPES.Audio]: ["preview"], + [CONTENT_TYPES.MP4]: ["preview"], + [CONTENT_TYPES.Image]: ["preview"], + [CONTENT_TYPES.Pdf]: ["preview"], + [CONTENT_TYPES.Text]: ["preview"], + [CONTENT_TYPES.File]: ["download", "info", "rename", "move", "delete"], + [CONTENT_TYPES.Directory]: ["rename", "move", "delete"] + }), []) + + return ( + + + + + + ) +} + +export default BucketPage diff --git a/packages/storage-ui/src/Components/Pages/Buckets.tsx b/packages/storage-ui/src/Components/Pages/Buckets.tsx deleted file mode 100644 index e68275fc0d..0000000000 --- a/packages/storage-ui/src/Components/Pages/Buckets.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from "react" -import { makeStyles, createStyles } from "@chainsafe/common-theme" -import { Typography } from "@chainsafe/common-components" - -const useStyles = makeStyles(() => - createStyles({ - root: { - position: "relative", - minHeight: "100vh", - overflow: "hidden" - }, - tableHead: { - marginTop: 24 - } - }) -) - -const BucketsPage = () => { - const classes = useStyles() - - return ( -
- Buckets -
- ) -} - -export default BucketsPage diff --git a/packages/storage-ui/src/Components/Pages/BucketsPage.tsx b/packages/storage-ui/src/Components/Pages/BucketsPage.tsx new file mode 100644 index 0000000000..8914d4d7b2 --- /dev/null +++ b/packages/storage-ui/src/Components/Pages/BucketsPage.tsx @@ -0,0 +1,158 @@ +import React, { useState } from "react" +import { makeStyles, createStyles } from "@chainsafe/common-theme" +import { Button, Table, TableBody, TableHead, TableHeadCell, TableRow, TextInput, Typography } from "@chainsafe/common-components" +import { CSFTheme } from "../../Themes/types" +import { useStorage } from "../../Contexts/StorageContext" +import { Trans } from "@lingui/macro" +import BucketRow from "../Elements/BucketRow" +import CustomModal from "../Elements/CustomModal" + +export const desktopGridSettings = "3fr 190px 190px 190px 70px !important" +export const mobileGridSettings = "3fr 190px 190px 190px 70px !important" + +const useStyles = makeStyles(({ breakpoints, animation, constants }: CSFTheme) => + createStyles({ + root: { + position: "relative", + minHeight: "100vh", + overflow: "hidden" + }, + tableHead: { + marginTop: 24 + }, + tableRow: { + border: "2px solid transparent", + transitionDuration: `${animation.transform}ms`, + [breakpoints.up("md")]: { + gridTemplateColumns: desktopGridSettings + }, + [breakpoints.down("md")]: { + gridTemplateColumns: mobileGridSettings + } + }, + modalFooter: { + display: "flex", + flexDirection: "row", + alignItems: "center", + justifyContent: "flex-end" + }, + createBucketModal: { + padding: constants.generalUnit * 4 + }, + modalInner: { + [breakpoints.down("md")]: { + bottom: + Number(constants?.mobileButtonHeight) + constants.generalUnit, + borderTopLeftRadius: `${constants.generalUnit * 1.5}px`, + borderTopRightRadius: `${constants.generalUnit * 1.5}px`, + borderBottomLeftRadius: `${constants.generalUnit * 1.5}px`, + borderBottomRightRadius: `${constants.generalUnit * 1.5}px`, + maxWidth: `${breakpoints.width("md")}px !important` + } + }, + okButton: { + marginLeft: constants.generalUnit + }, + cancelButton: { + [breakpoints.down("md")]: { + position: "fixed", + bottom: 0, + left: 0, + width: "100%", + height: constants?.mobileButtonHeight + } + } + }) +) + +const BucketsPage = () => { + const classes = useStyles() + const { storageBuckets, createBucket } = useStorage() + const [createBucketModalOpen, setCreateBucketOpen] = useState(false) + const [newBucketName, setNewBucketName] = useState("") + + return ( +
+ Buckets + + + + + + Name + + + Created + + + Size + + {/* Menu */} + + + + {storageBuckets.map((bucket) => + + )} + +
+ + Bucket Name + setNewBucketName(String(val))} /> +
+ + +
+
+
+ ) +} + +export default BucketsPage diff --git a/packages/storage-ui/src/Components/StorageRoutes.tsx b/packages/storage-ui/src/Components/StorageRoutes.tsx index 0970100c3e..96313d51bc 100644 --- a/packages/storage-ui/src/Components/StorageRoutes.tsx +++ b/packages/storage-ui/src/Components/StorageRoutes.tsx @@ -3,7 +3,8 @@ import { Switch, ConditionalRoute } from "@chainsafe/common-components" import LoginPage from "./Pages/LoginPage" import { useStorageApi } from "../Contexts/StorageApiContext" import CidsPage from "./Pages/CidsPage" -import BucketsPage from "./Pages/Buckets" +import BucketsPage from "./Pages/BucketsPage" +import BucketPage from "./Pages/BucketPage" export const ROUTE_LINKS = { Landing: "/", @@ -11,7 +12,9 @@ export const ROUTE_LINKS = { Buckets: "/buckets", PrivacyPolicy: "https://files.chainsafe.io/privacy-policy", Terms: "https://files.chainsafe.io/terms-of-service", - ChainSafe: "https://chainsafe.io/" + ChainSafe: "https://chainsafe.io/", + BucketRoot: "/bucket", + Bucket: (id: string, bucketPath: string) => `/bucket/${id}${bucketPath}` } export const SETTINGS_PATHS = ["profile", "plan", "security"] as const @@ -34,9 +37,16 @@ const StorageRoutes = () => { path={ROUTE_LINKS.Buckets} isAuthorized={isLoggedIn} component={BucketsPage} - redirectPath={ROUTE_LINKS.Buckets} + redirectPath={ROUTE_LINKS.Landing} + /> + ( + + {children} + +) + +export default DragAndDrop diff --git a/packages/storage-ui/src/Contexts/FileBrowserContext.tsx b/packages/storage-ui/src/Contexts/FileBrowserContext.tsx new file mode 100644 index 0000000000..4bbbcb13f5 --- /dev/null +++ b/packages/storage-ui/src/Contexts/FileBrowserContext.tsx @@ -0,0 +1,48 @@ +import { Crumb } from "@chainsafe/common-components" +import React, { useContext } from "react" +import { FileOperation, IBulkOperations, IFileBrowserModuleProps } from "./types" +import { Bucket } from "@chainsafe/files-api-client" +import { FileSystemItem, UploadProgress } from "./StorageContext" + +interface FileBrowserContext extends IFileBrowserModuleProps { + bucket?: Bucket + itemOperations: {[contentType: string]: FileOperation[]} + bulkOperations?: IBulkOperations + renameItem?: (cid: string, newName: string) => Promise + moveItems?: (cids: string[], newPath: string) => Promise + downloadFile?: (cid: string) => Promise + deleteItems?: (cid: string[]) => Promise + recoverItems?: (cid: string[], newPath: string) => Promise + viewFolder?: (cid: string) => void + allowDropUpload?: boolean + + handleUploadOnDrop?: ( + files: File[], + fileItems: DataTransferItemList, + path: string, + ) => void + + refreshContents?: () => void + currentPath: string + loadingCurrentPath: boolean + uploadsInProgress?: UploadProgress[] + showUploadsInTable: boolean + sourceFiles: FileSystemItem[] + crumbs: Crumb[] | undefined + moduleRootPath: string | undefined + getPath?: (cid: string) => string + isSearch?: boolean + withSurvey?: boolean +} + +const FileBrowserContext = React.createContext(undefined) + +const useFileBrowser = () => { + const context = useContext(FileBrowserContext) + if (context === undefined) { + throw new Error("useFileBrowserContext must be called within a FileBrowserProvider") + } + return context +} + +export { FileBrowserContext, useFileBrowser } diff --git a/packages/storage-ui/src/Contexts/FilesReducers.tsx b/packages/storage-ui/src/Contexts/FileOperationsReducers.tsx similarity index 100% rename from packages/storage-ui/src/Contexts/FilesReducers.tsx rename to packages/storage-ui/src/Contexts/FileOperationsReducers.tsx diff --git a/packages/storage-ui/src/Contexts/StorageContext.tsx b/packages/storage-ui/src/Contexts/StorageContext.tsx index 11b77cfac0..11260ca842 100644 --- a/packages/storage-ui/src/Contexts/StorageContext.tsx +++ b/packages/storage-ui/src/Contexts/StorageContext.tsx @@ -3,15 +3,18 @@ import { DirectoryContentResponse, BucketType, SearchEntry, - PinObject + PinObject, + Bucket } from "@chainsafe/files-api-client" import React, { useCallback, useEffect, useReducer } from "react" import { useState } from "react" // import { v4 as uuidv4 } from "uuid" -import { downloadsInProgressReducer, uploadsInProgressReducer } from "./FilesReducers" +import { downloadsInProgressReducer, uploadsInProgressReducer } from "./FileOperationsReducers" // import { t } from "@lingui/macro" import { useBeforeunload } from "react-beforeunload" import { useStorageApi } from "./StorageApiContext" +import { v4 as uuidv4 } from "uuid" +import { t } from "@lingui/macro" type StorageContextProps = { children: React.ReactNode | React.ReactNode[] @@ -43,11 +46,14 @@ type StorageContext = { downloadsInProgress: DownloadProgress[] spaceUsed: number addPin: (cid: string) => void - // createPin: (bucketId: string, files: File[], path: string) => Promise + uploadFiles: (bucketId: string, files: File[], path: string) => Promise // downloadPin: (bucketId: string, itemToDownload: FileSystemItem, path: string) => void // getPinContent: (bucketId: string, params: GetFileContentParams) => Promise refreshPins: () => void unpin: (requestId: string) => void + storageBuckets: Bucket[] + createBucket: (name: string) => void + removeBucket: (id: string) => void } // This represents a File or Folder on the @@ -65,6 +71,7 @@ const StorageProvider = ({ children }: StorageContextProps) => { const { storageApiClient, isLoggedIn } = useStorageApi() const [spaceUsed, setSpaceUsed] = useState(0) const [pins, setPins] = useState([]) + const [storageBuckets, setStorageBuckets] = useState([]) const refreshPins = useCallback(() => { storageApiClient.listPins() @@ -72,9 +79,16 @@ const StorageProvider = ({ children }: StorageContextProps) => { .catch(console.error) }, [storageApiClient]) + const refreshBuckets = useCallback(() => { + storageApiClient.listBuckets() + .then((buckets) => setStorageBuckets(buckets.filter(b => b.type === "fps"))) + .catch(console.error) + }, [storageApiClient]) + useEffect(() => { isLoggedIn && refreshPins() - }, [isLoggedIn, refreshPins]) + isLoggedIn && refreshBuckets() + }, [isLoggedIn, refreshPins, refreshBuckets]) const unpin = useCallback((requestId: string) => { storageApiClient.deletePin(requestId) @@ -82,23 +96,22 @@ const StorageProvider = ({ children }: StorageContextProps) => { .catch(console.error) }, [storageApiClient, refreshPins]) - // // Space used counter - // useEffect(() => { - // const getSpaceUsage = async () => { - // try { - // // TODO: Update this to include Share buckets where the current user is the owner - // const totalSize = pins.filter(p => p.pin === "pinning") - // .reduce((totalSize, bucket) => { return totalSize += (bucket as any).size}, 0) - - // setSpaceUsed(totalSize) - // } catch (error) { - // console.error(error) - // } - // } - // if (isLoggedIn) { - // getSpaceUsage() - // } - // }, [storageApiClient, isLoggedIn, pins]) + // Space used counter + useEffect(() => { + const getSpaceUsage = async () => { + try { + // TODO: Update this to include Share buckets where the current user is the owner + const totalSize = storageBuckets.reduce((totalSize, bucket) => { return totalSize += (bucket as any).size}, 0) + + setSpaceUsed(totalSize) + } catch (error) { + console.error(error) + } + } + if (isLoggedIn) { + getSpaceUsage() + } + }, [isLoggedIn, storageBuckets]) // Reset encryption keys on log out useEffect(() => { @@ -112,7 +125,7 @@ const StorageProvider = ({ children }: StorageContextProps) => { [] ) - const [downloadsInProgress] = useReducer( + const [downloadsInProgress, dispatchDownloadsInProgess] = useReducer( downloadsInProgressReducer, [] ) @@ -141,53 +154,70 @@ const StorageProvider = ({ children }: StorageContextProps) => { .catch(console.error) }, [storageApiClient]) - // const createPin = useCallback(async (bucketId: string, files: File[], path: string) => { - // const bucket = pins.find(b => b.id === bucketId) - - // if (!bucket) { - // console.error("No encryption key for this bucket is available.") - // return - // } - - // const id = uuidv4() - // const uploadProgress: UploadProgress = { - // id, - // fileName: files[0].name, // TODO: Do we need this? - // complete: false, - // error: false, - // noOfFiles: files.length, - // progress: 0, - // path - // } - // dispatchUploadsInProgress({ type: "add", payload: uploadProgress }) - // try { - // // TODO: Make API Request to upload here + const createBucket = useCallback((name: string) => { + storageApiClient.createBucket({ + name, + type: "fps", + public: "read", + encryption_key:"" + }).then(() => + refreshBuckets() + ).catch(console.error) + }, [storageApiClient, refreshBuckets]) + + const removeBucket = useCallback((id: string) => { + storageApiClient.removeBucket(id) + .then(() => Promise.resolve()) + .catch(console.error) + }, [storageApiClient]) - // // setting complete - // dispatchUploadsInProgress({ type: "complete", payload: { id } }) - // setTimeout(() => { - // dispatchUploadsInProgress({ type: "remove", payload: { id } }) - // }, REMOVE_UPLOAD_PROGRESS_DELAY) + const uploadFiles = useCallback(async (bucketId: string, files: File[], path: string) => { + const bucket = storageBuckets.find(b => b.id === bucketId) - // return Promise.resolve() - // } catch (error) { - // console.error(error) - // // setting error - // let errorMessage = t`Something went wrong. We couldn't upload your file` + if (!bucket) { + console.error("No encryption key for this bucket is available.") + return + } - // // we will need a method to parse server errors - // if (Array.isArray(error) && error[0].message.includes("conflict")) { - // errorMessage = t`A file with the same name already exists` - // } - // dispatchUploadsInProgress({ - // type: "error", - // payload: { id, errorMessage } - // }) - // setTimeout(() => { - // dispatchUploadsInProgress({ type: "remove", payload: { id } }) - // }, REMOVE_UPLOAD_PROGRESS_DELAY) - // } - // }, [pins]) + const id = uuidv4() + const uploadProgress: UploadProgress = { + id, + fileName: files[0].name, // TODO: Do we need this? + complete: false, + error: false, + noOfFiles: files.length, + progress: 0, + path + } + dispatchUploadsInProgress({ type: "add", payload: uploadProgress }) + try { + // TODO: Make API Request to upload here + + // setting complete + dispatchUploadsInProgress({ type: "complete", payload: { id } }) + setTimeout(() => { + dispatchUploadsInProgress({ type: "remove", payload: { id } }) + }, REMOVE_UPLOAD_PROGRESS_DELAY) + + return Promise.resolve() + } catch (error) { + console.error(error) + // setting error + let errorMessage = t`Something went wrong. We couldn't upload your file` + + // we will need a method to parse server errors + if (Array.isArray(error) && error[0].message.includes("conflict")) { + errorMessage = t`A file with the same name already exists` + } + dispatchUploadsInProgress({ + type: "error", + payload: { id, errorMessage } + }) + setTimeout(() => { + dispatchUploadsInProgress({ type: "remove", payload: { id } }) + }, REMOVE_UPLOAD_PROGRESS_DELAY) + } + }, [storageBuckets]) // const getPinContent = useCallback(async ( // bucketId: string, @@ -285,7 +315,11 @@ const StorageProvider = ({ children }: StorageContextProps) => { downloadsInProgress, pins, refreshPins, - unpin + unpin, + storageBuckets, + createBucket, + removeBucket, + uploadFiles }} > {children} diff --git a/packages/storage-ui/src/Contexts/types.ts b/packages/storage-ui/src/Contexts/types.ts new file mode 100644 index 0000000000..311bfa7286 --- /dev/null +++ b/packages/storage-ui/src/Contexts/types.ts @@ -0,0 +1,58 @@ +import { Crumb } from "@chainsafe/common-components" +import { BucketType, FileSystemItem } from "./StorageContext" + +export type FileOperation = + | "rename" + | "delete" + | "download" + | "share" + | "move" + | "info" + | "recover" + | "preview" + | "view_folder" + +export type BrowserView = "grid" | "table" +export type MoveModalMode = "move" | "recover" + +export interface IFileBrowserModuleProps { + heading?: string + controls?: boolean +} + +export interface IBulkOperations { + [index: string]: FileOperation[] +} + +export interface IFilesTableBrowserProps + extends Omit { + itemOperations: {[contentType: string]: FileOperation[]} + + bulkOperations?: IBulkOperations + handleRename?: (path: string, new_path: string) => Promise + handleMove?: (path: string, new_path: string) => Promise + downloadFile?: (cid: string) => Promise + deleteFiles?: (cid: string[]) => Promise + recoverFile?: (cid: string) => Promise + recoverFiles?: (cid: string[], newPath: string) => Promise + viewFolder?: (cid: string) => void + allowDropUpload?: boolean + + handleUploadOnDrop?: ( + files: File[], + fileItems: DataTransferItemList, + path: string, + ) => void + + refreshContents?: () => void + currentPath: string + bucketType: BucketType + loadingCurrentPath: boolean + showUploadsInTable: boolean + sourceFiles: FileSystemItem[] + crumbs: Crumb[] | undefined + moduleRootPath: string | undefined + getPath?: (cid: string) => string + isSearch?: boolean + withSurvey?: boolean +} diff --git a/packages/storage-ui/src/Utils/pathUtils.ts b/packages/storage-ui/src/Utils/pathUtils.ts index 594153af6c..dea2c9c6a5 100644 --- a/packages/storage-ui/src/Utils/pathUtils.ts +++ b/packages/storage-ui/src/Utils/pathUtils.ts @@ -1,5 +1,5 @@ // trims a string at both ends for a character -function trimChar(str: string, char: string) { +export function trimChar(str: string, char: string) { char = char.charAt(0) if (str.charAt(0) === char) { str = str.substr(1, str.length - 1) @@ -25,11 +25,13 @@ export function getArrayOfPaths(path: string): string[] { export function getURISafePathFromArray(arrayOfPaths: string[]): string { if (!arrayOfPaths.length) return "/" else { - return "/" + arrayOfPaths.map(encodeURIComponent).join("/") + return `/${arrayOfPaths.map(encodeURIComponent).join("/")}` } } -// get path and file +// Safe append path to file name +// /path/to/somewhere + 1.txt -> /path/to/somewhere/1.txt +// /path/to/somewhere/ + 1.txt -> /path/to/somewhere/1.txt export function getPathWithFile(path: string, fileName: string) { return path === "/" ? `/${fileName}` @@ -38,13 +40,15 @@ export function getPathWithFile(path: string, fileName: string) { : `${path}/${fileName}` } -// get path and file +// /path/to/somewhere/1.txt -> /path/to/somewhere export function getParentPathFromFilePath(filePath: string) { const parentPath = filePath.substring(0, filePath.lastIndexOf("/")) if (!parentPath) return "/" return parentPath } -export function extractDrivePath(pathname: string) { - return pathname.split("/").slice(2).join("/").concat("/") -} +// /drive/path/to/somewhere -> /path/to/somewhere +export function extractFileBrowserPathFromURL(browserUrl: string, modulePath: string) { + const result = browserUrl.replace(modulePath, "").split("/").map(decodeURIComponent).join("/") + return result === "" ? "/" : result +} \ No newline at end of file From adae31a75d57156a2b9edadc392b87df304c2bc0 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Tue, 15 Jun 2021 17:18:50 +0200 Subject: [PATCH 23/49] clean up file browser modals --- .../CreateFolderModal/CreateFolderModal.tsx | 207 +++++++++++++++++ .../DownloadProgressModals/DownloadBox.tsx | 108 +++++++++ .../Modules/DownloadProgressModals/index.tsx | 44 ++++ .../Modules/FilesList/FilesList.tsx | 19 +- .../Modules/MoveFileModal/MoveFileModal.tsx | 218 ++++++++++++++++++ .../UploadFileModal/UploadFileModal.tsx | 169 ++++++++++++++ .../UploadProgressModals/UploadBox.tsx | 113 +++++++++ .../Modules/UploadProgressModals/index.tsx | 51 ++++ 8 files changed, 919 insertions(+), 10 deletions(-) create mode 100644 packages/storage-ui/src/Components/Modules/CreateFolderModal/CreateFolderModal.tsx create mode 100644 packages/storage-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx create mode 100644 packages/storage-ui/src/Components/Modules/DownloadProgressModals/index.tsx create mode 100644 packages/storage-ui/src/Components/Modules/MoveFileModal/MoveFileModal.tsx create mode 100644 packages/storage-ui/src/Components/Modules/UploadFileModal/UploadFileModal.tsx create mode 100644 packages/storage-ui/src/Components/Modules/UploadProgressModals/UploadBox.tsx create mode 100644 packages/storage-ui/src/Components/Modules/UploadProgressModals/index.tsx diff --git a/packages/storage-ui/src/Components/Modules/CreateFolderModal/CreateFolderModal.tsx b/packages/storage-ui/src/Components/Modules/CreateFolderModal/CreateFolderModal.tsx new file mode 100644 index 0000000000..91283d2750 --- /dev/null +++ b/packages/storage-ui/src/Components/Modules/CreateFolderModal/CreateFolderModal.tsx @@ -0,0 +1,207 @@ +import { + Button, + FormikTextInput, + Grid, + Typography +} from "@chainsafe/common-components" +import * as yup from "yup" +import { + createStyles, + makeStyles, + useMediaQuery +} 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 { Trans } from "@lingui/macro" +import { CSFTheme } from "../../../Themes/types" +import { useFileBrowser } from "../../../Contexts/FileBrowserContext" +import { useStorageApi } from "../../../Contexts/StorageApiContext" + + +const useStyles = makeStyles( + ({ breakpoints, constants, typography, zIndex }: CSFTheme) => { + return createStyles({ + root: { + padding: constants.generalUnit * 4, + flexDirection: "column" + }, + modalRoot: { + zIndex: zIndex?.blocker, + [breakpoints.down("md")]: {} + }, + modalInner: { + backgroundColor: constants.createFolder.backgroundColor, + color: constants.createFolder.color, + [breakpoints.down("md")]: { + bottom: + Number(constants?.mobileButtonHeight) + constants.generalUnit, + borderTopLeftRadius: `${constants.generalUnit * 1.5}px`, + borderTopRightRadius: `${constants.generalUnit * 1.5}px`, + maxWidth: `${breakpoints.width("md")}px !important` + } + }, + input: { + marginBottom: constants.generalUnit * 2 + }, + okButton: { + marginLeft: constants.generalUnit + }, + cancelButton: { + [breakpoints.down("md")]: { + position: "fixed", + bottom: 0, + left: 0, + width: "100%", + height: constants?.mobileButtonHeight + } + }, + label: { + fontSize: 14, + lineHeight: "22px" + }, + heading: { + color: constants.createFolder.color, + fontWeight: typography.fontWeight.semibold, + textAlign: "center", + marginBottom: constants.generalUnit * 4 + } + }) + } +) + +interface ICreateFolderModalProps { + modalOpen: boolean + close: () => void +} + +const CreateFolderModal: React.FC = ({ + modalOpen, + close +}: ICreateFolderModalProps) => { + const classes = useStyles() + const { storageApiClient } = useStorageApi() + const { currentPath, refreshContents, bucket } = useFileBrowser() + const [creatingFolder, setCreatingFolder] = useState(false) + const desktop = useMediaQuery("md") + const inputRef = useRef(null) + + useEffect(() => { + if (modalOpen) { + setTimeout(() => inputRef.current?.focus(), 100) + } + }, [modalOpen]) + + const folderNameValidator = yup.object().shape({ + name: yup + .string() + .required("Folder name is required") + .test( + "Invalid name", + "Folder name cannot contain '/' character", + (val: string | null | undefined) => !!val && !val.includes("/") + ) + }) + + return ( + + { + if (!bucket) return + helpers.setSubmitting(true) + try { + setCreatingFolder(true) + await storageApiClient.addBucketDirectory(bucket.id, { path: `${currentPath}/${values.name}` }) + refreshContents && await refreshContents() + setCreatingFolder(false) + helpers.resetForm() + close() + } catch (errors) { + setCreatingFolder(false) + if (errors[0].message.includes("Entry with such name can")) { + helpers.setFieldError("name", "Folder name is already in use") + } else { + helpers.setFieldError("name", errors[0].message) + } + } + helpers.setSubmitting(false) + }} + > +
+
+ {!desktop && ( + + + Create Folder + + + )} + + + + + close()} + size="medium" + className={classes.cancelButton} + variant={desktop ? "outline" : "gray"} + type="button" + > + Cancel + + + +
+
+
+
+ ) +} + +export default CreateFolderModal diff --git a/packages/storage-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx b/packages/storage-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx new file mode 100644 index 0000000000..9800cbdcd0 --- /dev/null +++ b/packages/storage-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx @@ -0,0 +1,108 @@ +import React from "react" +import { createStyles, ITheme, makeStyles } from "@chainsafe/common-theme" +import { DownloadProgress } from "../../../Contexts/StorageContext" +import { + ProgressBar, + Typography, + CheckCircleIcon, + CloseCircleIcon +} from "@chainsafe/common-components" +import clsx from "clsx" +import { Trans } from "@lingui/macro" + +const useStyles = makeStyles( + ({ constants, palette, animation, breakpoints }: ITheme) => { + return createStyles({ + boxContainer: { + backgroundColor: palette.additional["gray"][3], + margin: `${constants.generalUnit}px 0`, + border: `1px solid ${palette.additional["gray"][6]}`, + padding: constants.generalUnit * 2, + borderRadius: 4 + }, + appearBox: { + animation: `$slideLeft ${animation.translate}ms`, + [breakpoints.down("md")]: { + animation: `$slideUp ${animation.translate}ms` + } + }, + "@keyframes slideLeft": { + from: { transform: "translate(100%)" }, + to: { transform: "translate(0)" } + }, + "@keyframes slideUp": { + from: { transform: "translate(0, 100%)" }, + to: { transform: "translate(0, 0)" } + }, + contentContainer: { + display: "flex", + alignItems: "center" + }, + marginBottom: { + marginBottom: constants.generalUnit + }, + marginRight: { + marginRight: constants.generalUnit * 2 + } + }) + } +) + +interface IDownloadBox { + downloadInProgress: DownloadProgress +} + +const DownloadBox: React.FC = ({ downloadInProgress }) => { + const { + fileName, + complete, + error, + progress, + errorMessage + } = downloadInProgress + const classes = useStyles() + + return ( + <> +
+ {complete ? ( +
+ + + Download complete + +
+ ) : error ? ( +
+ + + {errorMessage} + +
+ ) : ( +
+ + Downloading {fileName}... + + +
+ )} +
+ + ) +} + +export default DownloadBox diff --git a/packages/storage-ui/src/Components/Modules/DownloadProgressModals/index.tsx b/packages/storage-ui/src/Components/Modules/DownloadProgressModals/index.tsx new file mode 100644 index 0000000000..1169ccc040 --- /dev/null +++ b/packages/storage-ui/src/Components/Modules/DownloadProgressModals/index.tsx @@ -0,0 +1,44 @@ +import React from "react" +import { createStyles, ITheme, makeStyles } from "@chainsafe/common-theme" +import { useStorage } from "../../../Contexts/StorageContext" +import DownloadBox from "./DownloadBox" + +const useStyles = makeStyles(({ constants, zIndex, breakpoints }: ITheme) => { + const WIDTH = 400 + return createStyles({ + root: { + margin: constants.generalUnit * 3, + position: "fixed", + right: 0, + bottom: 0, + borderRadius: 4, + padding: constants.generalUnit, + width: WIDTH, + zIndex: zIndex?.layer1, + [breakpoints.down("md")]: { + margin: constants.generalUnit, + width: `calc(100% - ${constants.generalUnit * 2}px)` + } + } + }) +}) + +const DownloadProgressModals: React.FC = () => { + const classes = useStyles() + const { downloadsInProgress } = useStorage() + + if (downloadsInProgress.length === 0) { return null } + + return ( +
+ {downloadsInProgress.map((downloadInProgress) => ( + + ))} +
+ ) +} + +export default DownloadProgressModals diff --git a/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx b/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx index 1a8f4c8e90..6eecf868d5 100644 --- a/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx +++ b/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx @@ -36,17 +36,16 @@ import { BrowserView, FileOperation, MoveModalMode } from "../../../Contexts/typ import { FileSystemItem as FileSystemItemType } from "../../../Contexts/StorageContext" import FileSystemItem from "../FileSystemItem/FileSystemItem" // import FilePreviewModal from "../../FilePreviewModal" -// import UploadProgressModals from "../../UploadProgressModals" -// import DownloadProgressModals from "../../DownloadProgressModals" -// import CreateFolderModal from "../CreateFolderModal" -// import UploadFileModule from "../../UploadFileModule" -// import MoveFileModule from "../MoveFileModal" +import UploadProgressModals from "../UploadProgressModals" +import DownloadProgressModals from "../DownloadProgressModals" +import CreateFolderModal from "../CreateFolderModal/CreateFolderModal" +import UploadFileModule from "../UploadFileModal/UploadFileModal" +import MoveFileModule from "../MoveFileModal/MoveFileModal" // import FileInfoModal from "../FileInfoModal" import { CONTENT_TYPES } from "../../../Utils/Constants" import { CSFTheme } from "../../../Themes/types" import MimeMatcher from "../../../Utils/MimeMatcher" import { useLanguageContext } from "../../../Contexts/LanguageContext" -import { getPathWithFile } from "../../../Utils/pathUtils" import { useFileBrowser } from "../../../Contexts/FileBrowserContext" interface IStyleProps { @@ -978,9 +977,9 @@ const FilesList = () => { e.stopPropagation() }} /> - {/* - */} - {/* { + + + { refreshContents && ( <> { /> ) - } */} + } {/* { + return createStyles({ + modalRoot: { + zIndex: zIndex?.blocker, + [breakpoints.down("md")]: {} + }, + modalInner: { + backgroundColor: constants.moveFileModal.background, + color: constants.moveFileModal.color, + [breakpoints.down("md")]: { + bottom: + Number(constants?.mobileButtonHeight) + constants.generalUnit, + borderTopLeftRadius: `${constants.generalUnit * 1.5}px`, + borderTopRightRadius: `${constants.generalUnit * 1.5}px`, + maxWidth: `${breakpoints.width("md")}px !important` + } + }, + okButton: { + marginLeft: constants.generalUnit + }, + cancelButton: { + [breakpoints.down("md")]: { + position: "fixed", + bottom: 0, + left: 0, + width: "100%", + height: constants?.mobileButtonHeight + } + }, + heading: { + fontWeight: typography.fontWeight.semibold, + textAlign: "left", + [breakpoints.down("md")]: { + textAlign: "center" + } + }, + treeContainer: { + padding: `${constants.generalUnit * 4}px 0`, + borderTop: `1px solid ${palette.additional["gray"][5]}`, + borderBottom: `1px solid ${palette.additional["gray"][5]}` + }, + treeScrollView: { + paddingLeft: constants.generalUnit * 4 + }, + paddedContainer: { + padding: `${constants.generalUnit * 2}px ${ + constants.generalUnit * 3 + }px` + } + }) + } +) + +interface IMoveFileModuleProps { + filesToMove: FileSystemItem[] + modalOpen: boolean + onClose: () => void + onCancel: () => void + mode?: MoveModalMode +} + +const MoveFileModule = ({ filesToMove, modalOpen, onClose, onCancel, mode }: IMoveFileModuleProps) => { + const classes = useStyles() + const { storageApiClient } = useStorageApi() + const { moveItems, recoverItems, bucket } = useFileBrowser() + const [movingFile, setMovingFile] = useState(false) + const [movePath, setMovePath] = useState(undefined) + const [folderTree, setFolderTree] = useState([]) + + const mapFolderTree = useCallback( + (folderTreeEntries: DirectoryContentResponse[]): ITreeNodeProps[] => { + return folderTreeEntries.map((entry) => ({ + id: entry.path, + title: entry.name, + expandable: true, + tree: entry.entries ? mapFolderTree(entry.entries) : [] + })) + }, + [] + ) + + const getFolderTreeData = useCallback(async () => { + if (!bucket) return + storageApiClient.getBucketDirectoriesTree(bucket.id).then((newFolderTree) => { + if (newFolderTree.entries) { + const folderTreeNodes = [ + { + id: "/", + title: "Home", + isExpanded: true, + expandable: true, + tree: mapFolderTree(newFolderTree.entries) + } + ] + setFolderTree(folderTreeNodes) + } else { + setFolderTree([]) + } + }).catch(console.error) + }, [storageApiClient, mapFolderTree, bucket]) + + useEffect(() => { + if (modalOpen) { + getFolderTreeData() + } else { + setMovePath(undefined) + } + }, [modalOpen, getFolderTreeData]) + + const onMoveFile = () => { + const moveFn = mode === "move" ? moveItems : recoverItems + if (!movePath || !moveFn) return + + setMovingFile(true) + moveFn(filesToMove.map(f => f.cid), movePath) + .then(onClose) + .catch(console.error) + .finally(() => setMovingFile(false)) + + } + + const desktop = useMediaQuery("md") + + return ( + { + e.preventDefault() + e.stopPropagation() + }} + > + + + Move to... + + + + +
+ {folderTree.length + ? } + selectedId={movePath} + onSelectNode={(path: string) => setMovePath(path)} + /> + : No folders + } +
+
+
+ + + Cancel + + + +
+ ) +} + +export default MoveFileModule diff --git a/packages/storage-ui/src/Components/Modules/UploadFileModal/UploadFileModal.tsx b/packages/storage-ui/src/Components/Modules/UploadFileModal/UploadFileModal.tsx new file mode 100644 index 0000000000..12fe8bd865 --- /dev/null +++ b/packages/storage-ui/src/Components/Modules/UploadFileModal/UploadFileModal.tsx @@ -0,0 +1,169 @@ +import { Button, FileInput } from "@chainsafe/common-components" +import { useStorage } from "../../../Contexts/StorageContext" +import { createStyles, makeStyles } from "@chainsafe/common-theme" +import React, { useCallback, useState } from "react" +import { Formik, Form } from "formik" +import { array, object } from "yup" +import CustomModal from "../../Elements/CustomModal" +import { Trans, t } from "@lingui/macro" +import clsx from "clsx" +import { CSFTheme } from "../../../Themes/types" +import { useFileBrowser } from "../../../Contexts/FileBrowserContext" + +const useStyles = makeStyles(({ constants, breakpoints }: CSFTheme) => + createStyles({ + root: { + }, + modalInner: { + backgroundColor: constants.uploadModal.background, + color: constants.uploadModal.color, + [breakpoints.down("md")]: { + maxWidth: `${breakpoints.width("md")}px !important` + } + }, + input: { + marginBottom: constants.generalUnit * 2 + }, + fileList: { + color: constants.uploadModal.color + }, + item: { + color: constants.uploadModal.color + }, + cta: {}, + okButton: { + marginLeft: constants.generalUnit, + "&.wide": { + paddingLeft: constants.generalUnit * 4, + paddingRight: constants.generalUnit * 4 + } + }, + cancelButton: {}, + label: { + fontSize: 14, + lineHeight: "22px" + }, + addFiles: { + backgroundColor: constants.uploadModal.addMoreBackground, + color: constants.uploadModal.addMore, + "& svg": { + fill: constants.uploadModal.addMore + } + }, + closeIcon: { + "& svg": { + fill: constants.uploadModal.icon + }, + "&:hover svg": { + fill: constants.uploadModal.iconHover + } + }, + footer: { + display: "flex", + flexDirection: "row", + justifyContent: "flex-end", + alignItems: "center", + padding: constants.generalUnit * 2, + backgroundColor: constants.uploadModal.footerBackground + } + }) +) + +interface IUploadFileModuleProps { + modalOpen: boolean + close: () => void +} + +const UploadFileModule = ({ modalOpen, close }: IUploadFileModuleProps) => { + const classes = useStyles() + const [isDoneDisabled, setIsDoneDisabled] = useState(true) + const { uploadFiles } = useStorage() + const { currentPath, refreshContents, bucket } = useFileBrowser() + + const UploadSchema = object().shape({ files: array().required(t`Please select a file to upload`) }) + + const onFileNumberChange = useCallback((filesNumber: number) => { + setIsDoneDisabled(filesNumber === 0) + }, []) + + const onSubmit = useCallback(async (values, helpers) => { + if (!bucket) return + helpers.setSubmitting(true) + try { + 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") + } else { + helpers.setFieldError("files", errors[0].message) + } + } + helpers.setSubmitting(false) + }, [close, currentPath, uploadFiles, refreshContents, bucket]) + + return ( + + +
+ +
+ + +
+ +
+
+ ) +} + +export default UploadFileModule diff --git a/packages/storage-ui/src/Components/Modules/UploadProgressModals/UploadBox.tsx b/packages/storage-ui/src/Components/Modules/UploadProgressModals/UploadBox.tsx new file mode 100644 index 0000000000..1ace157614 --- /dev/null +++ b/packages/storage-ui/src/Components/Modules/UploadProgressModals/UploadBox.tsx @@ -0,0 +1,113 @@ +import React from "react" +import { createStyles, makeStyles } from "@chainsafe/common-theme" +import { UploadProgress } from "../../../Contexts/StorageContext" +import { ProgressBar, Typography, CheckCircleIcon, CloseCircleIcon } from "@chainsafe/common-components" +import clsx from "clsx" +import { plural, Trans } from "@lingui/macro" +import { CSFTheme } from "../../../Themes/types" + +const useStyles = makeStyles( + ({ constants, palette, animation, breakpoints }: CSFTheme) => { + return createStyles({ + boxContainer: { + backgroundColor: palette.additional["gray"][3], + margin: `${constants.generalUnit}px 0`, + border: `1px solid ${palette.additional["gray"][6]}`, + padding: constants.generalUnit * 2, + borderRadius: 4, + [breakpoints.down("md")]: { + margin: 0 + } + }, + appearBox: { + animation: `$slideLeft ${animation.translate}ms`, + [breakpoints.down("md")]: { + animation: `$slideUp ${animation.translate}ms` + } + }, + "@keyframes slideLeft": { + from: { transform: "translate(100%)" }, + to: { transform: "translate(0)" } + }, + "@keyframes slideUp": { + from: { transform: "translate(0, 100%)" }, + to: { transform: "translate(0, 0)" } + }, + contentContainer: { + display: "flex", + alignItems: "center", + "& svg": { + fill: constants.uploadAlert.icon + } + }, + marginBottom: { + marginBottom: constants.generalUnit + }, + marginRight: { + marginRight: constants.generalUnit * 2 + } + }) + } +) + +interface IUploadBox { + uploadInProgress: UploadProgress +} + +const UploadBox: React.FC = (props) => { + const { uploadInProgress } = props + const { + complete, + error, + noOfFiles, + progress, + errorMessage + } = uploadInProgress + const classes = useStyles() + + return ( + <> +
+ {complete ? ( +
+ + + Upload complete + +
+ ) : error ? ( +
+ + + {errorMessage} + +
+ ) : ( +
+ {plural(noOfFiles, { + one: `Uploading and encrypting ${noOfFiles} file`, + other: `Uploading and encrypting ${noOfFiles} files` + })} + +
+ )} +
+ + ) +} + +export default UploadBox diff --git a/packages/storage-ui/src/Components/Modules/UploadProgressModals/index.tsx b/packages/storage-ui/src/Components/Modules/UploadProgressModals/index.tsx new file mode 100644 index 0000000000..a38bf63fe2 --- /dev/null +++ b/packages/storage-ui/src/Components/Modules/UploadProgressModals/index.tsx @@ -0,0 +1,51 @@ +import React from "react" +import { + createStyles, + ITheme, + makeStyles, + useThemeSwitcher +} from "@chainsafe/common-theme" +import { useStorage } from "../../../Contexts/StorageContext" +import UploadBox from "./UploadBox" + +const useStyles = makeStyles(({ constants, zIndex, breakpoints }: ITheme) => { + const WIDTH = 400 + return createStyles({ + root: { + margin: constants.generalUnit * 3, + position: "fixed", + right: 0, + bottom: 0, + borderRadius: 4, + padding: constants.generalUnit, + width: WIDTH, + zIndex: zIndex?.layer1, + [breakpoints.down("md")]: { + margin: constants.generalUnit, + width: `calc(100% - ${constants.generalUnit * 2}px)` + } + } + }) +}) + +const UploadProgressModals: React.FC = () => { + const classes = useStyles() + const { uploadsInProgress } = useStorage() + const { desktop } = useThemeSwitcher() + + if (uploadsInProgress.length === 0) { return null } + return (
+ {uploadsInProgress.map( + (uploadInProgress) => + (desktop || uploadInProgress.complete || uploadInProgress.error) && ( + + ) + )} +
+ ) +} + +export default UploadProgressModals From 3eb13fb09b8cb1c25db05453a4f05f9f948fb02c Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Tue, 15 Jun 2021 17:58:38 +0200 Subject: [PATCH 24/49] fix linting --- .../DownloadProgressModals/DownloadBox.tsx | 216 ++++++++--------- .../Modules/DownloadProgressModals/index.tsx | 88 +++---- .../UploadProgressModals/UploadBox.tsx | 226 +++++++++--------- .../Modules/UploadProgressModals/index.tsx | 102 ++++---- .../src/Contexts/StorageContext.tsx | 5 +- 5 files changed, 319 insertions(+), 318 deletions(-) diff --git a/packages/storage-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx b/packages/storage-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx index 9800cbdcd0..eb2d4364aa 100644 --- a/packages/storage-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx +++ b/packages/storage-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx @@ -1,108 +1,108 @@ -import React from "react" -import { createStyles, ITheme, makeStyles } from "@chainsafe/common-theme" -import { DownloadProgress } from "../../../Contexts/StorageContext" -import { - ProgressBar, - Typography, - CheckCircleIcon, - CloseCircleIcon -} from "@chainsafe/common-components" -import clsx from "clsx" -import { Trans } from "@lingui/macro" - -const useStyles = makeStyles( - ({ constants, palette, animation, breakpoints }: ITheme) => { - return createStyles({ - boxContainer: { - backgroundColor: palette.additional["gray"][3], - margin: `${constants.generalUnit}px 0`, - border: `1px solid ${palette.additional["gray"][6]}`, - padding: constants.generalUnit * 2, - borderRadius: 4 - }, - appearBox: { - animation: `$slideLeft ${animation.translate}ms`, - [breakpoints.down("md")]: { - animation: `$slideUp ${animation.translate}ms` - } - }, - "@keyframes slideLeft": { - from: { transform: "translate(100%)" }, - to: { transform: "translate(0)" } - }, - "@keyframes slideUp": { - from: { transform: "translate(0, 100%)" }, - to: { transform: "translate(0, 0)" } - }, - contentContainer: { - display: "flex", - alignItems: "center" - }, - marginBottom: { - marginBottom: constants.generalUnit - }, - marginRight: { - marginRight: constants.generalUnit * 2 - } - }) - } -) - -interface IDownloadBox { - downloadInProgress: DownloadProgress -} - -const DownloadBox: React.FC = ({ downloadInProgress }) => { - const { - fileName, - complete, - error, - progress, - errorMessage - } = downloadInProgress - const classes = useStyles() - - return ( - <> -
- {complete ? ( -
- - - Download complete - -
- ) : error ? ( -
- - - {errorMessage} - -
- ) : ( -
- - Downloading {fileName}... - - -
- )} -
- - ) -} - -export default DownloadBox +import React from "react" +import { createStyles, ITheme, makeStyles } from "@chainsafe/common-theme" +import { DownloadProgress } from "../../../Contexts/StorageContext" +import { + ProgressBar, + Typography, + CheckCircleIcon, + CloseCircleIcon +} from "@chainsafe/common-components" +import clsx from "clsx" +import { Trans } from "@lingui/macro" + +const useStyles = makeStyles( + ({ constants, palette, animation, breakpoints }: ITheme) => { + return createStyles({ + boxContainer: { + backgroundColor: palette.additional["gray"][3], + margin: `${constants.generalUnit}px 0`, + border: `1px solid ${palette.additional["gray"][6]}`, + padding: constants.generalUnit * 2, + borderRadius: 4 + }, + appearBox: { + animation: `$slideLeft ${animation.translate}ms`, + [breakpoints.down("md")]: { + animation: `$slideUp ${animation.translate}ms` + } + }, + "@keyframes slideLeft": { + from: { transform: "translate(100%)" }, + to: { transform: "translate(0)" } + }, + "@keyframes slideUp": { + from: { transform: "translate(0, 100%)" }, + to: { transform: "translate(0, 0)" } + }, + contentContainer: { + display: "flex", + alignItems: "center" + }, + marginBottom: { + marginBottom: constants.generalUnit + }, + marginRight: { + marginRight: constants.generalUnit * 2 + } + }) + } +) + +interface IDownloadBox { + downloadInProgress: DownloadProgress +} + +const DownloadBox: React.FC = ({ downloadInProgress }) => { + const { + fileName, + complete, + error, + progress, + errorMessage + } = downloadInProgress + const classes = useStyles() + + return ( + <> +
+ {complete ? ( +
+ + + Download complete + +
+ ) : error ? ( +
+ + + {errorMessage} + +
+ ) : ( +
+ + Downloading {fileName}... + + +
+ )} +
+ + ) +} + +export default DownloadBox diff --git a/packages/storage-ui/src/Components/Modules/DownloadProgressModals/index.tsx b/packages/storage-ui/src/Components/Modules/DownloadProgressModals/index.tsx index 1169ccc040..e11740e41b 100644 --- a/packages/storage-ui/src/Components/Modules/DownloadProgressModals/index.tsx +++ b/packages/storage-ui/src/Components/Modules/DownloadProgressModals/index.tsx @@ -1,44 +1,44 @@ -import React from "react" -import { createStyles, ITheme, makeStyles } from "@chainsafe/common-theme" -import { useStorage } from "../../../Contexts/StorageContext" -import DownloadBox from "./DownloadBox" - -const useStyles = makeStyles(({ constants, zIndex, breakpoints }: ITheme) => { - const WIDTH = 400 - return createStyles({ - root: { - margin: constants.generalUnit * 3, - position: "fixed", - right: 0, - bottom: 0, - borderRadius: 4, - padding: constants.generalUnit, - width: WIDTH, - zIndex: zIndex?.layer1, - [breakpoints.down("md")]: { - margin: constants.generalUnit, - width: `calc(100% - ${constants.generalUnit * 2}px)` - } - } - }) -}) - -const DownloadProgressModals: React.FC = () => { - const classes = useStyles() - const { downloadsInProgress } = useStorage() - - if (downloadsInProgress.length === 0) { return null } - - return ( -
- {downloadsInProgress.map((downloadInProgress) => ( - - ))} -
- ) -} - -export default DownloadProgressModals +import React from "react" +import { createStyles, ITheme, makeStyles } from "@chainsafe/common-theme" +import { useStorage } from "../../../Contexts/StorageContext" +import DownloadBox from "./DownloadBox" + +const useStyles = makeStyles(({ constants, zIndex, breakpoints }: ITheme) => { + const WIDTH = 400 + return createStyles({ + root: { + margin: constants.generalUnit * 3, + position: "fixed", + right: 0, + bottom: 0, + borderRadius: 4, + padding: constants.generalUnit, + width: WIDTH, + zIndex: zIndex?.layer1, + [breakpoints.down("md")]: { + margin: constants.generalUnit, + width: `calc(100% - ${constants.generalUnit * 2}px)` + } + } + }) +}) + +const DownloadProgressModals: React.FC = () => { + const classes = useStyles() + const { downloadsInProgress } = useStorage() + + if (downloadsInProgress.length === 0) { return null } + + return ( +
+ {downloadsInProgress.map((downloadInProgress) => ( + + ))} +
+ ) +} + +export default DownloadProgressModals diff --git a/packages/storage-ui/src/Components/Modules/UploadProgressModals/UploadBox.tsx b/packages/storage-ui/src/Components/Modules/UploadProgressModals/UploadBox.tsx index 1ace157614..676e157830 100644 --- a/packages/storage-ui/src/Components/Modules/UploadProgressModals/UploadBox.tsx +++ b/packages/storage-ui/src/Components/Modules/UploadProgressModals/UploadBox.tsx @@ -1,113 +1,113 @@ -import React from "react" -import { createStyles, makeStyles } from "@chainsafe/common-theme" -import { UploadProgress } from "../../../Contexts/StorageContext" -import { ProgressBar, Typography, CheckCircleIcon, CloseCircleIcon } from "@chainsafe/common-components" -import clsx from "clsx" -import { plural, Trans } from "@lingui/macro" -import { CSFTheme } from "../../../Themes/types" - -const useStyles = makeStyles( - ({ constants, palette, animation, breakpoints }: CSFTheme) => { - return createStyles({ - boxContainer: { - backgroundColor: palette.additional["gray"][3], - margin: `${constants.generalUnit}px 0`, - border: `1px solid ${palette.additional["gray"][6]}`, - padding: constants.generalUnit * 2, - borderRadius: 4, - [breakpoints.down("md")]: { - margin: 0 - } - }, - appearBox: { - animation: `$slideLeft ${animation.translate}ms`, - [breakpoints.down("md")]: { - animation: `$slideUp ${animation.translate}ms` - } - }, - "@keyframes slideLeft": { - from: { transform: "translate(100%)" }, - to: { transform: "translate(0)" } - }, - "@keyframes slideUp": { - from: { transform: "translate(0, 100%)" }, - to: { transform: "translate(0, 0)" } - }, - contentContainer: { - display: "flex", - alignItems: "center", - "& svg": { - fill: constants.uploadAlert.icon - } - }, - marginBottom: { - marginBottom: constants.generalUnit - }, - marginRight: { - marginRight: constants.generalUnit * 2 - } - }) - } -) - -interface IUploadBox { - uploadInProgress: UploadProgress -} - -const UploadBox: React.FC = (props) => { - const { uploadInProgress } = props - const { - complete, - error, - noOfFiles, - progress, - errorMessage - } = uploadInProgress - const classes = useStyles() - - return ( - <> -
- {complete ? ( -
- - - Upload complete - -
- ) : error ? ( -
- - - {errorMessage} - -
- ) : ( -
- {plural(noOfFiles, { - one: `Uploading and encrypting ${noOfFiles} file`, - other: `Uploading and encrypting ${noOfFiles} files` - })} - -
- )} -
- - ) -} - -export default UploadBox +import React from "react" +import { createStyles, makeStyles } from "@chainsafe/common-theme" +import { UploadProgress } from "../../../Contexts/StorageContext" +import { ProgressBar, Typography, CheckCircleIcon, CloseCircleIcon } from "@chainsafe/common-components" +import clsx from "clsx" +import { plural, Trans } from "@lingui/macro" +import { CSFTheme } from "../../../Themes/types" + +const useStyles = makeStyles( + ({ constants, palette, animation, breakpoints }: CSFTheme) => { + return createStyles({ + boxContainer: { + backgroundColor: palette.additional["gray"][3], + margin: `${constants.generalUnit}px 0`, + border: `1px solid ${palette.additional["gray"][6]}`, + padding: constants.generalUnit * 2, + borderRadius: 4, + [breakpoints.down("md")]: { + margin: 0 + } + }, + appearBox: { + animation: `$slideLeft ${animation.translate}ms`, + [breakpoints.down("md")]: { + animation: `$slideUp ${animation.translate}ms` + } + }, + "@keyframes slideLeft": { + from: { transform: "translate(100%)" }, + to: { transform: "translate(0)" } + }, + "@keyframes slideUp": { + from: { transform: "translate(0, 100%)" }, + to: { transform: "translate(0, 0)" } + }, + contentContainer: { + display: "flex", + alignItems: "center", + "& svg": { + fill: constants.uploadAlert.icon + } + }, + marginBottom: { + marginBottom: constants.generalUnit + }, + marginRight: { + marginRight: constants.generalUnit * 2 + } + }) + } +) + +interface IUploadBox { + uploadInProgress: UploadProgress +} + +const UploadBox: React.FC = (props) => { + const { uploadInProgress } = props + const { + complete, + error, + noOfFiles, + progress, + errorMessage + } = uploadInProgress + const classes = useStyles() + + return ( + <> +
+ {complete ? ( +
+ + + Upload complete + +
+ ) : error ? ( +
+ + + {errorMessage} + +
+ ) : ( +
+ {plural(noOfFiles, { + one: `Uploading and encrypting ${noOfFiles} file`, + other: `Uploading and encrypting ${noOfFiles} files` + })} + +
+ )} +
+ + ) +} + +export default UploadBox diff --git a/packages/storage-ui/src/Components/Modules/UploadProgressModals/index.tsx b/packages/storage-ui/src/Components/Modules/UploadProgressModals/index.tsx index a38bf63fe2..ac7f68bd18 100644 --- a/packages/storage-ui/src/Components/Modules/UploadProgressModals/index.tsx +++ b/packages/storage-ui/src/Components/Modules/UploadProgressModals/index.tsx @@ -1,51 +1,51 @@ -import React from "react" -import { - createStyles, - ITheme, - makeStyles, - useThemeSwitcher -} from "@chainsafe/common-theme" -import { useStorage } from "../../../Contexts/StorageContext" -import UploadBox from "./UploadBox" - -const useStyles = makeStyles(({ constants, zIndex, breakpoints }: ITheme) => { - const WIDTH = 400 - return createStyles({ - root: { - margin: constants.generalUnit * 3, - position: "fixed", - right: 0, - bottom: 0, - borderRadius: 4, - padding: constants.generalUnit, - width: WIDTH, - zIndex: zIndex?.layer1, - [breakpoints.down("md")]: { - margin: constants.generalUnit, - width: `calc(100% - ${constants.generalUnit * 2}px)` - } - } - }) -}) - -const UploadProgressModals: React.FC = () => { - const classes = useStyles() - const { uploadsInProgress } = useStorage() - const { desktop } = useThemeSwitcher() - - if (uploadsInProgress.length === 0) { return null } - return (
- {uploadsInProgress.map( - (uploadInProgress) => - (desktop || uploadInProgress.complete || uploadInProgress.error) && ( - - ) - )} -
- ) -} - -export default UploadProgressModals +import React from "react" +import { + createStyles, + ITheme, + makeStyles, + useThemeSwitcher +} from "@chainsafe/common-theme" +import { useStorage } from "../../../Contexts/StorageContext" +import UploadBox from "./UploadBox" + +const useStyles = makeStyles(({ constants, zIndex, breakpoints }: ITheme) => { + const WIDTH = 400 + return createStyles({ + root: { + margin: constants.generalUnit * 3, + position: "fixed", + right: 0, + bottom: 0, + borderRadius: 4, + padding: constants.generalUnit, + width: WIDTH, + zIndex: zIndex?.layer1, + [breakpoints.down("md")]: { + margin: constants.generalUnit, + width: `calc(100% - ${constants.generalUnit * 2}px)` + } + } + }) +}) + +const UploadProgressModals: React.FC = () => { + const classes = useStyles() + const { uploadsInProgress } = useStorage() + const { desktop } = useThemeSwitcher() + + if (uploadsInProgress.length === 0) { return null } + return (
+ {uploadsInProgress.map( + (uploadInProgress) => + (desktop || uploadInProgress.complete || uploadInProgress.error) && ( + + ) + )} +
+ ) +} + +export default UploadProgressModals diff --git a/packages/storage-ui/src/Contexts/StorageContext.tsx b/packages/storage-ui/src/Contexts/StorageContext.tsx index 2ba4bb2315..36cd0d613f 100644 --- a/packages/storage-ui/src/Contexts/StorageContext.tsx +++ b/packages/storage-ui/src/Contexts/StorageContext.tsx @@ -62,12 +62,13 @@ interface IFileSystemItem extends FileContentResponse { } type FileSystemItem = IFileSystemItem +const REMOVE_UPLOAD_PROGRESS_DELAY = 5000 const StorageContext = React.createContext(undefined) const StorageProvider = ({ children }: StorageContextProps) => { const { storageApiClient, isLoggedIn } = useStorageApi() - const [spaceUsed] = useState(0) + const [spaceUsed, setSpaceUsed] = useState(0) const [pins, setPins] = useState([]) const [storageBuckets, setStorageBuckets] = useState([]) @@ -118,7 +119,7 @@ const StorageProvider = ({ children }: StorageContextProps) => { } }, [isLoggedIn]) - const [uploadsInProgress] = useReducer( + const [uploadsInProgress, dispatchUploadsInProgress] = useReducer( uploadsInProgressReducer, [] ) From df484b20526efa75b2d1da2f2f6b44c4ce8113fd Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Tue, 15 Jun 2021 18:25:17 +0200 Subject: [PATCH 25/49] fix CRLF --- .../Modules/LoginModule/InitialScreen.tsx | 2 +- .../src/Components/Elements/BucketRow.tsx | 7 +-- .../Modules/FilesList/FilesList.tsx | 42 +++++++-------- .../Modules/MoveFileModal/MoveFileModal.tsx | 2 +- .../src/Components/Pages/BucketPage.tsx | 42 ++++++++------- .../src/Contexts/StorageContext.tsx | 53 ++++++++++++++++--- 6 files changed, 95 insertions(+), 53 deletions(-) diff --git a/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx b/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx index 43f1972f38..9dab43238b 100644 --- a/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx +++ b/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react" -import { Button, FacebookLogoIcon, GithubLogoIcon, MailIcon, GoogleLogoIcon, Loading, Typography } from "@chainsafe/common-components" +import { Button, GithubLogoIcon, MailIcon, GoogleLogoIcon, Loading, Typography } from "@chainsafe/common-components" import { createStyles, makeStyles, useThemeSwitcher } from "@chainsafe/common-theme" import { CSFTheme } from "../../../Themes/types" import { t, Trans } from "@lingui/macro" diff --git a/packages/storage-ui/src/Components/Elements/BucketRow.tsx b/packages/storage-ui/src/Components/Elements/BucketRow.tsx index 8776d1f49b..198716cc0c 100644 --- a/packages/storage-ui/src/Components/Elements/BucketRow.tsx +++ b/packages/storage-ui/src/Components/Elements/BucketRow.tsx @@ -2,8 +2,7 @@ import React from "react" import { makeStyles, createStyles } from "@chainsafe/common-theme" import { DeleteSvg, formatBytes, MenuDropdown, MoreIcon, TableCell, TableRow, useHistory } from "@chainsafe/common-components" import { Trans } from "@lingui/macro" -import dayjs from "dayjs" -import { Bucket, PinObject } from "@chainsafe/files-api-client" +import { Bucket } from "@chainsafe/files-api-client" import { CSFTheme } from "../../Themes/types" import { useStorage } from "../../Contexts/StorageContext" import { desktopGridSettings, mobileGridSettings } from "../Pages/CidsPage" @@ -56,11 +55,9 @@ const useStyles = makeStyles(({ animation, constants, breakpoints }: CSFTheme) = }) ) interface Props { - bucket: Bucket + bucket: Bucket } -const IPFS_GATEWAY = "https://ipfs.infura.io:5001/api/v0/cat/" - const BucketRow = ({ bucket }: Props) => { const classes = useStyles() const { removeBucket } = useStorage() diff --git a/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx b/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx index 6eecf868d5..cd5ab17306 100644 --- a/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx +++ b/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx @@ -301,7 +301,7 @@ const FilesList = () => { const [direction, setDirection] = useState("ascend") const [column, setColumn] = useState<"name" | "size" | "date_uploaded">("name") const [selectedCids, setSelectedCids] = useState([]) - const [previewFileIndex, setPreviewFileIndex] = useState() + const [, setPreviewFileIndex] = useState() const { selectedLocale } = useLanguageContext() const { redirect } = useHistory() @@ -359,25 +359,25 @@ const FilesList = () => { } // Previews - const setNextPreview = () => { - if ( - files && - previewFileIndex !== undefined && - previewFileIndex < files.length - 1 - ) { - setPreviewFileIndex(previewFileIndex + 1) - } - } - - const setPreviousPreview = () => { - if (files && previewFileIndex !== undefined && previewFileIndex > 0) { - setPreviewFileIndex(previewFileIndex - 1) - } - } - - const clearPreview = () => { - setPreviewFileIndex(undefined) - } + // const setNextPreview = () => { + // if ( + // files && + // previewFileIndex !== undefined && + // previewFileIndex < files.length - 1 + // ) { + // setPreviewFileIndex(previewFileIndex + 1) + // } + // } + + // const setPreviousPreview = () => { + // if (files && previewFileIndex !== undefined && previewFileIndex > 0) { + // setPreviewFileIndex(previewFileIndex - 1) + // } + // } + + // const clearPreview = () => { + // setPreviewFileIndex(undefined) + // } // Selection logic const handleSelectCid = useCallback( @@ -449,7 +449,7 @@ const FilesList = () => { const [isMoveFileModalOpen, setIsMoveFileModalOpen] = useState(false) const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false) const [isDeletingFiles, setIsDeletingFiles] = useState(false) - const [fileInfoPath, setFileInfoPath] = useState( + const [, setFileInfoPath] = useState( undefined ) const [moveModalMode, setMoveModalMode] = useState() diff --git a/packages/storage-ui/src/Components/Modules/MoveFileModal/MoveFileModal.tsx b/packages/storage-ui/src/Components/Modules/MoveFileModal/MoveFileModal.tsx index 03cdaadc4e..1684174711 100644 --- a/packages/storage-ui/src/Components/Modules/MoveFileModal/MoveFileModal.tsx +++ b/packages/storage-ui/src/Components/Modules/MoveFileModal/MoveFileModal.tsx @@ -3,7 +3,7 @@ import React, { useState, useEffect, useCallback } from "react" import CustomModal from "../../Elements/CustomModal" import CustomButton from "../../Elements/CustomButton" import { t, Trans } from "@lingui/macro" -import { DirectoryContentResponse, FileSystemItem, useStorage } from "../../../Contexts/StorageContext" +import { DirectoryContentResponse, FileSystemItem } from "../../../Contexts/StorageContext" import { Button, FolderIcon, Grid, ITreeNodeProps, ScrollbarWrapper, TreeView, Typography } from "@chainsafe/common-components" import { CSFTheme } from "../../../Themes/types" import { useFileBrowser } from "../../../Contexts/FileBrowserContext" diff --git a/packages/storage-ui/src/Components/Pages/BucketPage.tsx b/packages/storage-ui/src/Components/Pages/BucketPage.tsx index f275df93c1..a4aa5ec661 100644 --- a/packages/storage-ui/src/Components/Pages/BucketPage.tsx +++ b/packages/storage-ui/src/Components/Pages/BucketPage.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" -import { Crumb, useToaster, useHistory, useLocation, Typography } from "@chainsafe/common-components" +import { Crumb, useToaster, useHistory, useLocation } from "@chainsafe/common-components" import { useStorage, FileSystemItem } from "../../Contexts/StorageContext" import { getArrayOfPaths, getURISafePathFromArray, getPathWithFile, extractFileBrowserPathFromURL } from "../../Utils/pathUtils" import { IBulkOperations, IFileBrowserModuleProps, IFilesTableBrowserProps } from "../../Contexts/types" @@ -11,21 +11,19 @@ import { ROUTE_LINKS } from "../../Components/StorageRoutes" import { useStorageApi } from "../../Contexts/StorageApiContext" import { FileBrowserContext } from "../../Contexts/FileBrowserContext" import { parseFileContentResponse } from "../../Utils/Helpers" -import { makeStyles, createStyles } from "@chainsafe/common-theme" -import { CSFTheme } from "../../Themes/types" - -const useStyles = makeStyles(({ breakpoints, animation, constants }: CSFTheme) => - createStyles({ - root: { - position: "relative", - minHeight: "100vh", - overflow: "hidden" - } - }) -) +// import { makeStyles, createStyles } from "@chainsafe/common-theme" + +// const useStyles = makeStyles(() => +// createStyles({ +// root: { +// position: "relative", +// minHeight: "100vh", +// overflow: "hidden" +// } +// }) +// ) const BucketPage: React.FC = () => { - const classes = useStyles() const { storageBuckets, uploadFiles, uploadsInProgress } = useStorage() const { storageApiClient } = useStorageApi() const { addToastMessage } = useToaster() @@ -63,7 +61,9 @@ const BucketPage: React.FC = () => { refreshContents(true) }, [bucket, refreshContents]) - const moveItemsToBin = useCallback(async (cids: string[]) => { + const moveItemsToBin = useCallback(async ( + //cids: string[] + ) => { throw new Error("Not implemented") // if (!bucket) return // await Promise.all( @@ -99,7 +99,9 @@ const BucketPage: React.FC = () => { // return Promise.reject() // }} // )).finally(refreshContents) - }, [addToastMessage, currentPath, pathContents, refreshContents, storageApiClient, bucket]) + }, [ + //addToastMessage, currentPath, pathContents, refreshContents, storageApiClient, bucket + ]) const renameItem = useCallback(async (cid: string, newName: string) => { const itemToRename = pathContents.find(i => i.cid === cid) @@ -143,13 +145,17 @@ const BucketPage: React.FC = () => { })).finally(refreshContents) }, [addToastMessage, pathContents, refreshContents, storageApiClient, bucket, currentPath]) - const handleDownload = useCallback(async (cid: string) => { + const handleDownload = useCallback(async ( + //cid: string + ) => { throw new Error("Not implemented") // const itemToDownload = pathContents.find(item => item.cid === cid) // if (!itemToDownload || !bucket) return // downloadFile(bucket.id, itemToDownload, currentPath) - }, [pathContents, currentPath, bucket]) + }, [ + //pathContents, currentPath, bucket + ]) // Breadcrumbs/paths const arrayOfPaths = useMemo(() => getArrayOfPaths(currentPath), [currentPath]) diff --git a/packages/storage-ui/src/Contexts/StorageContext.tsx b/packages/storage-ui/src/Contexts/StorageContext.tsx index 36cd0d613f..4684797979 100644 --- a/packages/storage-ui/src/Contexts/StorageContext.tsx +++ b/packages/storage-ui/src/Contexts/StorageContext.tsx @@ -15,6 +15,8 @@ import { useBeforeunload } from "react-beforeunload" import { useStorageApi } from "./StorageApiContext" import { v4 as uuidv4 } from "uuid" import { t } from "@lingui/macro" +import { readFileAsync } from "../Utils/Helpers" +import { useToaster } from "../../../common-components/dist" type StorageContextProps = { children: React.ReactNode | React.ReactNode[] @@ -63,6 +65,7 @@ interface IFileSystemItem extends FileContentResponse { type FileSystemItem = IFileSystemItem const REMOVE_UPLOAD_PROGRESS_DELAY = 5000 +const MAX_FILE_SIZE = 2 * 1024 ** 3 const StorageContext = React.createContext(undefined) @@ -71,7 +74,7 @@ const StorageProvider = ({ children }: StorageContextProps) => { const [spaceUsed, setSpaceUsed] = useState(0) const [pins, setPins] = useState([]) const [storageBuckets, setStorageBuckets] = useState([]) - + const { addToastMessage } = useToaster() const refreshPins = useCallback(() => { storageApiClient.listPins() .then((pins) => setPins(pins.results || [])) @@ -124,7 +127,7 @@ const StorageProvider = ({ children }: StorageContextProps) => { [] ) - const [downloadsInProgress, dispatchDownloadsInProgess] = useReducer( + const [downloadsInProgress] = useReducer( downloadsInProgressReducer, [] ) @@ -174,14 +177,14 @@ const StorageProvider = ({ children }: StorageContextProps) => { const bucket = storageBuckets.find(b => b.id === bucketId) if (!bucket) { - console.error("No encryption key for this bucket is available.") + console.error("Destination bucket does not exist.") return } const id = uuidv4() const uploadProgress: UploadProgress = { id, - fileName: files[0].name, // TODO: Do we need this? + fileName: files[0].name, complete: false, error: false, noOfFiles: files.length, @@ -189,9 +192,45 @@ const StorageProvider = ({ children }: StorageContextProps) => { path } dispatchUploadsInProgress({ type: "add", payload: uploadProgress }) - try { - // TODO: Make API Request to upload here + try { + const filesParam = await Promise.all( + files + .filter((f) => f.size <= MAX_FILE_SIZE) + .map(async (f) => { + const fileData = await readFileAsync(f) + return { + data: new Blob([fileData], { type: f.type }), + fileName: f.name + } + }) + ) + if (filesParam.length !== files.length) { + addToastMessage({ + message: + "We can't encrypt files larger than 2GB. Some items will not be uploaded", + appearance: "error" + }) + } + await storageApiClient.uploadBucketObjects( + bucketId, + filesParam, + path, + undefined, + 1, + undefined, + (progressEvent: { loaded: number; total: number }) => { + dispatchUploadsInProgress({ + type: "progress", + payload: { + id, + progress: Math.ceil( + (progressEvent.loaded / progressEvent.total) * 100 + ) + } + }) + } + ) // setting complete dispatchUploadsInProgress({ type: "complete", payload: { id } }) setTimeout(() => { @@ -216,7 +255,7 @@ const StorageProvider = ({ children }: StorageContextProps) => { dispatchUploadsInProgress({ type: "remove", payload: { id } }) }, REMOVE_UPLOAD_PROGRESS_DELAY) } - }, [storageBuckets]) + }, [storageBuckets, addToastMessage, storageApiClient]) // const getPinContent = useCallback(async ( // bucketId: string, From 0105a5b0f3e7de9104d727ef00eec76f3b631401 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Tue, 15 Jun 2021 19:02:56 +0200 Subject: [PATCH 26/49] wire up file upload --- packages/storage-ui/src/Components/Elements/BucketRow.tsx | 4 ++-- .../Modules/CreateFolderModal/CreateFolderModal.tsx | 4 ++-- .../Modules/FileSystemItem/FileSystemGridItem.tsx | 4 ++-- .../Components/Modules/FileSystemItem/FileSystemItem.tsx | 4 ++-- .../Modules/FileSystemItem/FileSystemTableItem.tsx | 4 ++-- .../src/Components/Modules/FilesList/FilesList.tsx | 4 ++-- .../src/Components/Modules/MoveFileModal/MoveFileModal.tsx | 4 ++-- .../Components/Modules/UploadFileModal/UploadFileModal.tsx | 4 ++-- .../Components/Modules/UploadProgressModals/UploadBox.tsx | 4 ++-- packages/storage-ui/src/Components/Pages/BucketsPage.tsx | 4 ++-- packages/storage-ui/src/Contexts/StorageContext.tsx | 7 ++----- 11 files changed, 22 insertions(+), 25 deletions(-) diff --git a/packages/storage-ui/src/Components/Elements/BucketRow.tsx b/packages/storage-ui/src/Components/Elements/BucketRow.tsx index 198716cc0c..10400f9aa8 100644 --- a/packages/storage-ui/src/Components/Elements/BucketRow.tsx +++ b/packages/storage-ui/src/Components/Elements/BucketRow.tsx @@ -3,12 +3,12 @@ import { makeStyles, createStyles } from "@chainsafe/common-theme" import { DeleteSvg, formatBytes, MenuDropdown, MoreIcon, TableCell, TableRow, useHistory } from "@chainsafe/common-components" import { Trans } from "@lingui/macro" import { Bucket } from "@chainsafe/files-api-client" -import { CSFTheme } from "../../Themes/types" +import { CSSTheme } from "../../Themes/types" import { useStorage } from "../../Contexts/StorageContext" import { desktopGridSettings, mobileGridSettings } from "../Pages/CidsPage" import { ROUTE_LINKS } from "../StorageRoutes" -const useStyles = makeStyles(({ animation, constants, breakpoints }: CSFTheme) => +const useStyles = makeStyles(({ animation, constants, breakpoints }: CSSTheme) => createStyles({ dropdownIcon: { "& svg": { diff --git a/packages/storage-ui/src/Components/Modules/CreateFolderModal/CreateFolderModal.tsx b/packages/storage-ui/src/Components/Modules/CreateFolderModal/CreateFolderModal.tsx index 91283d2750..ecbcc82c8b 100644 --- a/packages/storage-ui/src/Components/Modules/CreateFolderModal/CreateFolderModal.tsx +++ b/packages/storage-ui/src/Components/Modules/CreateFolderModal/CreateFolderModal.tsx @@ -15,13 +15,13 @@ import { Formik, Form } from "formik" import CustomModal from "../../Elements/CustomModal" import CustomButton from "../../Elements/CustomButton" import { Trans } from "@lingui/macro" -import { CSFTheme } from "../../../Themes/types" +import { CSSTheme } from "../../../Themes/types" import { useFileBrowser } from "../../../Contexts/FileBrowserContext" import { useStorageApi } from "../../../Contexts/StorageApiContext" const useStyles = makeStyles( - ({ breakpoints, constants, typography, zIndex }: CSFTheme) => { + ({ breakpoints, constants, typography, zIndex }: CSSTheme) => { return createStyles({ root: { padding: constants.generalUnit * 4, diff --git a/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemGridItem.tsx b/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemGridItem.tsx index 422a8d4a5c..8790084dcf 100644 --- a/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemGridItem.tsx +++ b/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemGridItem.tsx @@ -8,12 +8,12 @@ import { MenuDropdown, MoreIcon } from "@chainsafe/common-components" -import { CSFTheme } from "../../../Themes/types" +import { CSSTheme } from "../../../Themes/types" import { FileSystemItem } from "../../../Contexts/StorageContext" import { ConnectDragPreview } from "react-dnd" import { Form, Formik } from "formik" -const useStyles = makeStyles(({ breakpoints, constants, palette }: CSFTheme) => { +const useStyles = makeStyles(({ breakpoints, constants, palette }: CSSTheme) => { return createStyles({ fileIcon: { display: "flex", diff --git a/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemItem.tsx b/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemItem.tsx index aba9cc7034..bc9d3b0303 100644 --- a/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemItem.tsx +++ b/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemItem.tsx @@ -23,7 +23,7 @@ import CustomModal from "../../Elements/CustomModal" import { Trans } from "@lingui/macro" import { useDrag, useDrop } from "react-dnd" import { getEmptyImage, NativeTypes } from "react-dnd-html5-backend" -import { CSFTheme } from "../../../Themes/types" +import { CSSTheme } from "../../../Themes/types" import FileItemTableItem from "./FileSystemTableItem" import FileItemGridItem from "./FileSystemGridItem" import { FileSystemItem as FileSystemItemType } from "../../../Contexts/StorageContext" @@ -31,7 +31,7 @@ import { useFileBrowser } from "../../../Contexts/FileBrowserContext" import { BrowserView, FileOperation } from "../../../Contexts/types" import { DragTypes } from "../FilesList/DragConstants" -const useStyles = makeStyles(({ breakpoints, constants }: CSFTheme) => { +const useStyles = makeStyles(({ breakpoints, constants }: CSSTheme) => { return createStyles({ renameInput: { width: "100%", diff --git a/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemTableItem.tsx b/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemTableItem.tsx index d12792836a..fff79b1d25 100644 --- a/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemTableItem.tsx +++ b/packages/storage-ui/src/Components/Modules/FileSystemItem/FileSystemTableItem.tsx @@ -18,10 +18,10 @@ import { import dayjs from "dayjs" import { ConnectDragPreview } from "react-dnd" import { Form, Formik } from "formik" -import { CSFTheme } from "../../../Themes/types" +import { CSSTheme } from "../../../Themes/types" import { FileSystemItem } from "../../../Contexts/StorageContext" -const useStyles = makeStyles(({ breakpoints, constants, palette }: CSFTheme) => { +const useStyles = makeStyles(({ breakpoints, constants, palette }: CSSTheme) => { const desktopGridSettings = "50px 69px 3fr 190px 100px 45px !important" const mobileGridSettings = "69px 3fr 45px !important" diff --git a/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx b/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx index cd5ab17306..78939775a0 100644 --- a/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx +++ b/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx @@ -43,7 +43,7 @@ import UploadFileModule from "../UploadFileModal/UploadFileModal" import MoveFileModule from "../MoveFileModal/MoveFileModal" // import FileInfoModal from "../FileInfoModal" import { CONTENT_TYPES } from "../../../Utils/Constants" -import { CSFTheme } from "../../../Themes/types" +import { CSSTheme } from "../../../Themes/types" import MimeMatcher from "../../../Utils/MimeMatcher" import { useLanguageContext } from "../../../Contexts/LanguageContext" import { useFileBrowser } from "../../../Contexts/FileBrowserContext" @@ -53,7 +53,7 @@ interface IStyleProps { } const useStyles = makeStyles( - ({ animation, breakpoints, constants, palette, zIndex }: CSFTheme) => { + ({ animation, breakpoints, constants, palette, zIndex }: CSSTheme) => { const desktopGridSettings = "50px 69px 3fr 190px 100px 45px !important" const mobileGridSettings = "69px 3fr 45px !important" return createStyles({ diff --git a/packages/storage-ui/src/Components/Modules/MoveFileModal/MoveFileModal.tsx b/packages/storage-ui/src/Components/Modules/MoveFileModal/MoveFileModal.tsx index 1684174711..7a40ea3a42 100644 --- a/packages/storage-ui/src/Components/Modules/MoveFileModal/MoveFileModal.tsx +++ b/packages/storage-ui/src/Components/Modules/MoveFileModal/MoveFileModal.tsx @@ -5,14 +5,14 @@ import CustomButton from "../../Elements/CustomButton" import { t, Trans } from "@lingui/macro" import { DirectoryContentResponse, FileSystemItem } from "../../../Contexts/StorageContext" import { Button, FolderIcon, Grid, ITreeNodeProps, ScrollbarWrapper, TreeView, Typography } from "@chainsafe/common-components" -import { CSFTheme } from "../../../Themes/types" +import { CSSTheme } from "../../../Themes/types" import { useFileBrowser } from "../../../Contexts/FileBrowserContext" import { useStorageApi } from "../../../Contexts/StorageApiContext" import { MoveModalMode } from "../../../Contexts/types" const useStyles = makeStyles( - ({ breakpoints, constants, palette, typography, zIndex }: CSFTheme) => { + ({ breakpoints, constants, palette, typography, zIndex }: CSSTheme) => { return createStyles({ modalRoot: { zIndex: zIndex?.blocker, diff --git a/packages/storage-ui/src/Components/Modules/UploadFileModal/UploadFileModal.tsx b/packages/storage-ui/src/Components/Modules/UploadFileModal/UploadFileModal.tsx index 12fe8bd865..21d833ca20 100644 --- a/packages/storage-ui/src/Components/Modules/UploadFileModal/UploadFileModal.tsx +++ b/packages/storage-ui/src/Components/Modules/UploadFileModal/UploadFileModal.tsx @@ -7,10 +7,10 @@ import { array, object } from "yup" import CustomModal from "../../Elements/CustomModal" import { Trans, t } from "@lingui/macro" import clsx from "clsx" -import { CSFTheme } from "../../../Themes/types" +import { CSSTheme } from "../../../Themes/types" import { useFileBrowser } from "../../../Contexts/FileBrowserContext" -const useStyles = makeStyles(({ constants, breakpoints }: CSFTheme) => +const useStyles = makeStyles(({ constants, breakpoints }: CSSTheme) => createStyles({ root: { }, diff --git a/packages/storage-ui/src/Components/Modules/UploadProgressModals/UploadBox.tsx b/packages/storage-ui/src/Components/Modules/UploadProgressModals/UploadBox.tsx index 676e157830..f7fdc60de5 100644 --- a/packages/storage-ui/src/Components/Modules/UploadProgressModals/UploadBox.tsx +++ b/packages/storage-ui/src/Components/Modules/UploadProgressModals/UploadBox.tsx @@ -4,10 +4,10 @@ import { UploadProgress } from "../../../Contexts/StorageContext" import { ProgressBar, Typography, CheckCircleIcon, CloseCircleIcon } from "@chainsafe/common-components" import clsx from "clsx" import { plural, Trans } from "@lingui/macro" -import { CSFTheme } from "../../../Themes/types" +import { CSSTheme } from "../../../Themes/types" const useStyles = makeStyles( - ({ constants, palette, animation, breakpoints }: CSFTheme) => { + ({ constants, palette, animation, breakpoints }: CSSTheme) => { return createStyles({ boxContainer: { backgroundColor: palette.additional["gray"][3], diff --git a/packages/storage-ui/src/Components/Pages/BucketsPage.tsx b/packages/storage-ui/src/Components/Pages/BucketsPage.tsx index 8914d4d7b2..322be6c0d7 100644 --- a/packages/storage-ui/src/Components/Pages/BucketsPage.tsx +++ b/packages/storage-ui/src/Components/Pages/BucketsPage.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react" import { makeStyles, createStyles } from "@chainsafe/common-theme" import { Button, Table, TableBody, TableHead, TableHeadCell, TableRow, TextInput, Typography } from "@chainsafe/common-components" -import { CSFTheme } from "../../Themes/types" +import { CSSTheme } from "../../Themes/types" import { useStorage } from "../../Contexts/StorageContext" import { Trans } from "@lingui/macro" import BucketRow from "../Elements/BucketRow" @@ -10,7 +10,7 @@ import CustomModal from "../Elements/CustomModal" export const desktopGridSettings = "3fr 190px 190px 190px 70px !important" export const mobileGridSettings = "3fr 190px 190px 190px 70px !important" -const useStyles = makeStyles(({ breakpoints, animation, constants }: CSFTheme) => +const useStyles = makeStyles(({ breakpoints, animation, constants }: CSSTheme) => createStyles({ root: { position: "relative", diff --git a/packages/storage-ui/src/Contexts/StorageContext.tsx b/packages/storage-ui/src/Contexts/StorageContext.tsx index a3877f2e50..1a20eeaf0d 100644 --- a/packages/storage-ui/src/Contexts/StorageContext.tsx +++ b/packages/storage-ui/src/Contexts/StorageContext.tsx @@ -3,8 +3,7 @@ import { DirectoryContentResponse, BucketType, SearchEntry, - PinObject, - Bucket + Bucket, PinStatus } from "@chainsafe/files-api-client" import React, { useCallback, useEffect, useReducer } from "react" @@ -17,7 +16,7 @@ import { useStorageApi } from "./StorageApiContext" import { v4 as uuidv4 } from "uuid" import { t } from "@lingui/macro" import { readFileAsync } from "../Utils/Helpers" -import { useToaster } from "../../../common-components/dist" +import { useToaster } from "@chainsafe/common-components" type StorageContextProps = { children: React.ReactNode | React.ReactNode[] @@ -74,10 +73,8 @@ const StorageContext = React.createContext(undefined const StorageProvider = ({ children }: StorageContextProps) => { const { storageApiClient, isLoggedIn } = useStorageApi() const [spaceUsed, setSpaceUsed] = useState(0) - const [pins, setPins] = useState([]) const [storageBuckets, setStorageBuckets] = useState([]) const { addToastMessage } = useToaster() - const [spaceUsed] = useState(0) const [pins, setPins] = useState([]) const refreshPins = useCallback(() => { From be5e3cf56ad193d418acee0302a4ba28533af5d8 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Wed, 16 Jun 2021 12:26:30 +0200 Subject: [PATCH 27/49] clean up styling --- .../src/Components/Elements/BucketRow.tsx | 7 ++-- .../src/Components/Pages/BucketsPage.tsx | 42 ++++++++++++++----- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/packages/storage-ui/src/Components/Elements/BucketRow.tsx b/packages/storage-ui/src/Components/Elements/BucketRow.tsx index 10400f9aa8..44246db87a 100644 --- a/packages/storage-ui/src/Components/Elements/BucketRow.tsx +++ b/packages/storage-ui/src/Components/Elements/BucketRow.tsx @@ -5,7 +5,7 @@ import { Trans } from "@lingui/macro" import { Bucket } from "@chainsafe/files-api-client" import { CSSTheme } from "../../Themes/types" import { useStorage } from "../../Contexts/StorageContext" -import { desktopGridSettings, mobileGridSettings } from "../Pages/CidsPage" +import { desktopGridSettings, mobileGridSettings } from "../Pages/BucketsPage" import { ROUTE_LINKS } from "../StorageRoutes" const useStyles = makeStyles(({ animation, constants, breakpoints }: CSSTheme) => @@ -34,7 +34,8 @@ const useStyles = makeStyles(({ animation, constants, breakpoints }: CSSTheme) = fill: constants.fileSystemItemRow.menuIcon } }, - cid: { + name: { + textAlign: "left", whiteSpace: "nowrap", textOverflow: "ellipsis", overflow: "hidden", @@ -67,7 +68,7 @@ const BucketRow = ({ bucket }: Props) => { type="grid" className={classes.tableRow} > - redirect(ROUTE_LINKS.Bucket(bucket.id, "/"))}> {bucket.name} diff --git a/packages/storage-ui/src/Components/Pages/BucketsPage.tsx b/packages/storage-ui/src/Components/Pages/BucketsPage.tsx index 322be6c0d7..4daab17bc4 100644 --- a/packages/storage-ui/src/Components/Pages/BucketsPage.tsx +++ b/packages/storage-ui/src/Components/Pages/BucketsPage.tsx @@ -7,8 +7,8 @@ import { Trans } from "@lingui/macro" import BucketRow from "../Elements/BucketRow" import CustomModal from "../Elements/CustomModal" -export const desktopGridSettings = "3fr 190px 190px 190px 70px !important" -export const mobileGridSettings = "3fr 190px 190px 190px 70px !important" +export const desktopGridSettings = "3fr 190px 70px !important" +export const mobileGridSettings = "3fr 190px 70px !important" const useStyles = makeStyles(({ breakpoints, animation, constants }: CSSTheme) => createStyles({ @@ -61,6 +61,24 @@ const useStyles = makeStyles(({ breakpoints, animation, constants }: CSSTheme) = width: "100%", height: constants?.mobileButtonHeight } + }, + controls: { + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + "& > button": { + marginLeft: constants.generalUnit + } + }, + header: { + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + [breakpoints.down("md")]: { + marginTop: constants.generalUnit + } } }) ) @@ -73,8 +91,16 @@ const BucketsPage = () => { return (
- Buckets - +
+ + + Buckets + + +
+ +
+
{ > Name - - Created - Date: Wed, 16 Jun 2021 12:37:26 +0200 Subject: [PATCH 28/49] refresh buckets on delete --- packages/storage-ui/src/Contexts/StorageContext.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/storage-ui/src/Contexts/StorageContext.tsx b/packages/storage-ui/src/Contexts/StorageContext.tsx index 1a20eeaf0d..1b346c64a8 100644 --- a/packages/storage-ui/src/Contexts/StorageContext.tsx +++ b/packages/storage-ui/src/Contexts/StorageContext.tsx @@ -170,9 +170,10 @@ const StorageProvider = ({ children }: StorageContextProps) => { const removeBucket = useCallback((id: string) => { storageApiClient.removeBucket(id) - .then(() => Promise.resolve()) + .then(refreshBuckets) + .then(Promise.resolve) .catch(console.error) - }, [storageApiClient]) + }, [storageApiClient, refreshBuckets]) const uploadFiles = useCallback(async (bucketId: string, files: File[], path: string) => { const bucket = storageBuckets.find(b => b.id === bucketId) From b57427459179cef0dc90d60a298eacc10365929a Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Wed, 16 Jun 2021 13:54:28 +0200 Subject: [PATCH 29/49] remove some styling --- .../src/Components/Pages/BucketPage.tsx | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/packages/storage-ui/src/Components/Pages/BucketPage.tsx b/packages/storage-ui/src/Components/Pages/BucketPage.tsx index a4aa5ec661..ca3475cff9 100644 --- a/packages/storage-ui/src/Components/Pages/BucketPage.tsx +++ b/packages/storage-ui/src/Components/Pages/BucketPage.tsx @@ -11,17 +11,6 @@ import { ROUTE_LINKS } from "../../Components/StorageRoutes" import { useStorageApi } from "../../Contexts/StorageApiContext" import { FileBrowserContext } from "../../Contexts/FileBrowserContext" import { parseFileContentResponse } from "../../Utils/Helpers" -// import { makeStyles, createStyles } from "@chainsafe/common-theme" - -// const useStyles = makeStyles(() => -// createStyles({ -// root: { -// position: "relative", -// minHeight: "100vh", -// overflow: "hidden" -// } -// }) -// ) const BucketPage: React.FC = () => { const { storageBuckets, uploadFiles, uploadsInProgress } = useStorage() @@ -198,18 +187,18 @@ const BucketPage: React.FC = () => { }, [currentPath, pathContents, redirect, bucketId]) const bulkOperations: IBulkOperations = useMemo(() => ({ - // [CONTENT_TYPES.Directory]: ["move"], - // [CONTENT_TYPES.File]: ["delete", "move"] + [CONTENT_TYPES.Directory]: [], + [CONTENT_TYPES.File]: [] }), []) const itemOperations: IFilesTableBrowserProps["itemOperations"] = useMemo(() => ({ - [CONTENT_TYPES.Audio]: ["preview"], - [CONTENT_TYPES.MP4]: ["preview"], - [CONTENT_TYPES.Image]: ["preview"], - [CONTENT_TYPES.Pdf]: ["preview"], - [CONTENT_TYPES.Text]: ["preview"], - [CONTENT_TYPES.File]: ["download", "info", "rename", "move", "delete"], - [CONTENT_TYPES.Directory]: ["rename", "move", "delete"] + [CONTENT_TYPES.Audio]: [], + [CONTENT_TYPES.MP4]: [], + [CONTENT_TYPES.Image]: [], + [CONTENT_TYPES.Pdf]: [], + [CONTENT_TYPES.Text]: [], + [CONTENT_TYPES.File]: [], + [CONTENT_TYPES.Directory]: [] }), []) return ( From e1841f3d73e01d8640171c73b0a5e48c9b1ee1c2 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Wed, 16 Jun 2021 14:11:38 +0200 Subject: [PATCH 30/49] remove bulk ops --- .../storage-ui/src/Components/Modules/FilesList/FilesList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx b/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx index 78939775a0..c12bf18f70 100644 --- a/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx +++ b/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx @@ -511,7 +511,7 @@ const FilesList = () => { } } } - setValidBulkOps(filteredList) + setValidBulkOps([]) } }, [selectedCids, items, bulkOperations]) From 13c5ccfea81bc512f097df81885c7986ebe06d82 Mon Sep 17 00:00:00 2001 From: Thibaut Sardan Date: Wed, 16 Jun 2021 16:25:59 +0200 Subject: [PATCH 31/49] user icons with tooltip --- .../src/Components/Elements/SharedUser.tsx | 123 ++++++++++++++---- .../FileBrowsers/SharedFoldersOverview.tsx | 2 +- .../Modules/LoginModule/InitialScreen.tsx | 2 +- 3 files changed, 100 insertions(+), 27 deletions(-) diff --git a/packages/files-ui/src/Components/Elements/SharedUser.tsx b/packages/files-ui/src/Components/Elements/SharedUser.tsx index 687be55ef5..d05b482c8a 100644 --- a/packages/files-ui/src/Components/Elements/SharedUser.tsx +++ b/packages/files-ui/src/Components/Elements/SharedUser.tsx @@ -1,20 +1,69 @@ -import React from "react" +import React, { useState } from "react" import { makeStyles, createStyles } from "@chainsafe/common-theme" import { CSFTheme } from "../../Themes/types" -import { UserIcon } from "@chainsafe/common-components" +import { Typography, UserIcon } from "@chainsafe/common-components" +import clsx from "clsx" -const useStyles = makeStyles(({ breakpoints, constants, palette }: CSFTheme) => { +const useStyles = makeStyles(({ zIndex, animation, constants, palette }: CSFTheme) => { return createStyles({ root: { + display: "flex" }, bubble: { + position: "relative", borderRadius: "50%", - border: "solid black 1px", backgroundColor: palette.additional["gray"][6], - color: palette.common.white.main + color: palette.common.white.main, + width: constants.generalUnit * 5, + height: constants.generalUnit * 5, + display: "flex", + alignItems: "center", + justifyContent: "center", + "& svg": { + fill: palette.common.white.main + }, + "&:first-child": { + marginRight: constants.generalUnit + } }, text : { textAlign: "center" + }, + tooltip: { + display: "flex", + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + left: "50%", + top: "-20px", + position: "absolute", + transform: "translate(-50%, -50%)", + zIndex: zIndex?.layer1, + transitionDuration: `${animation.transform}ms`, + opacity: 0, + visibility: "hidden", + backgroundColor: constants.loginModule.flagBg, + color: constants.loginModule.flagText, + padding: `${constants.generalUnit / 2}px ${constants.generalUnit}px`, + borderRadius: 2, + "&:after": { + transitionDuration: `${animation.transform}ms`, + content: "''", + position: "absolute", + top: "100%", + left: "50%", + transform: "translate(-50%,0)", + width: 0, + height: 0, + borderLeft: "5px solid transparent", + borderRight: "5px solid transparent", + borderTop: `5px solid ${constants.loginModule.flagBg}` + }, + "&.active": { + opacity: 1, + visibility: "visible" + } + } }) }) @@ -30,29 +79,53 @@ const SharedUsers = ({ sharedUsers }: Props) => { return null } - const UserBubble = ({ text, hover }: {text?: string; hover: string}) =>
- {text - ? {text} - : - } -
+ const UserBubble = ({ text, hover }: {text?: string; hover: string | string[]}) => { + const [isHover, setIsHover] = useState(false) - return
- {sharedUsers.length > 2 && ( - - )} - {sharedUsers.length == 2 && ( + return ( +
setIsHover(true)} + onMouseLeave={() => setIsHover(false)} + className={classes.bubble} + > +
+ { + Array.isArray(hover) + ? hover.map((user) =>
{user}
) + : hover + } +
+ {text + ? + {text} + + : + } +
+ ) + } + + return ( +
- )} - -
+ {sharedUsers.length > 2 && ( + + )} + {sharedUsers.length === 2 && ( + + )} +
+ ) } export default SharedUsers \ No newline at end of file diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx index 41f257dfbc..8ec327061a 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx @@ -113,7 +113,7 @@ const SharedFolderOverview = () => { filesApiClient.createBucket({ name: "Cat Bucket", // eslint-disable-next-line max-len - encryption_key:"dc19f9579902e949fd5d517951d6cf2203526b233dc25ca7323e5d56932237e5ed4369871f81015f921decc6d5b5e1b5cf60af9ffaeba4fde2d2969b5dfa131a195bb7b828c6dc2a8434e43f497ce6529d897f09f812f53d4e9f9f3c0f6bce31afd390943e6ae189c820783698521ce2b8f446f557e1c78185c8e8a0ae54370b88", + encryption_key:"51f27f1f1b7f342299e643eaf437ac80022b162728144ab79f3c0e966306b8d01da374c427d7614bcf8a67a2317e6748258dd8367499838316a0a63cdcbe0ff624e1c5284f70279b7d87d2d7a42d09e032f98adbbbe488abf74001f0dfb549fc2d4e8b11ccb082eab47fbef1d0012e81fdf12748e9355bbf1996a473b3ef1b2682", type: "share" }).then(console.log) .catch(console.error) diff --git a/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx b/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx index 43f1972f38..9dab43238b 100644 --- a/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx +++ b/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react" -import { Button, FacebookLogoIcon, GithubLogoIcon, MailIcon, GoogleLogoIcon, Loading, Typography } from "@chainsafe/common-components" +import { Button, GithubLogoIcon, MailIcon, GoogleLogoIcon, Loading, Typography } from "@chainsafe/common-components" import { createStyles, makeStyles, useThemeSwitcher } from "@chainsafe/common-theme" import { CSFTheme } from "../../../Themes/types" import { t, Trans } from "@lingui/macro" From 3b0c05886dd9f5be821bc796213214dfe7fbdade Mon Sep 17 00:00:00 2001 From: Thibaut Sardan Date: Thu, 17 Jun 2021 15:53:41 +0200 Subject: [PATCH 32/49] loader --- .../src/Icons/icons/UserShare.icon.tsx | 7 + packages/common-components/src/Icons/index.ts | 1 + .../src/Icons/svgs/user-share.svg | 3 + .../src/Components/Layouts/AppNav.tsx | 5 +- .../FileBrowsers/SharedFoldersOverview.tsx | 148 +++++++++++------- .../files-ui/src/Contexts/FilesContext.tsx | 12 +- 6 files changed, 115 insertions(+), 61 deletions(-) create mode 100644 packages/common-components/src/Icons/icons/UserShare.icon.tsx create mode 100644 packages/common-components/src/Icons/svgs/user-share.svg diff --git a/packages/common-components/src/Icons/icons/UserShare.icon.tsx b/packages/common-components/src/Icons/icons/UserShare.icon.tsx new file mode 100644 index 0000000000..e09787ad78 --- /dev/null +++ b/packages/common-components/src/Icons/icons/UserShare.icon.tsx @@ -0,0 +1,7 @@ +import * as React from "react" +import createSvgIcon from "../createSvgIcon" +import { ReactComponent as UserShareSvg } from "../svgs/user-share.svg" + +export { UserShareSvg } + +export default createSvgIcon() diff --git a/packages/common-components/src/Icons/index.ts b/packages/common-components/src/Icons/index.ts index 8d441d6688..7cf7e730c0 100644 --- a/packages/common-components/src/Icons/index.ts +++ b/packages/common-components/src/Icons/index.ts @@ -65,6 +65,7 @@ export { default as TableIcon, TableSvg } from "./icons/Table.icon" export { default as UploadIcon, UploadSvg } from "./icons/Upload.icon" export { default as UnionpayCardIcon, UnionpayCardSvg } from "./icons/UnionpayCard.icon" export { default as UserIcon, UserSvg } from "./icons/User.icon" +export { default as UserShareIcon, UserShareSvg } from "./icons/UserShare.icon" export { default as VisaCardIcon, VisaCardSvg } from "./icons/VisaCard.icon" export { default as WarningIcon, WarningSvg } from "./icons/Warning.icon" export { default as ZoomInIcon, ZoomInSvg } from "./icons/ZoomIn.icon" diff --git a/packages/common-components/src/Icons/svgs/user-share.svg b/packages/common-components/src/Icons/svgs/user-share.svg new file mode 100644 index 0000000000..39520327f2 --- /dev/null +++ b/packages/common-components/src/Icons/svgs/user-share.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/files-ui/src/Components/Layouts/AppNav.tsx b/packages/files-ui/src/Components/Layouts/AppNav.tsx index 11c2fb4004..9254bdad4b 100644 --- a/packages/files-ui/src/Components/Layouts/AppNav.tsx +++ b/packages/files-ui/src/Components/Layouts/AppNav.tsx @@ -16,7 +16,8 @@ import { ProgressBar, Button, formatBytes, - DeleteSvg } from "@chainsafe/common-components" + DeleteSvg, + UserShareSvg } from "@chainsafe/common-components" import { ROUTE_LINKS } from "../FilesRoutes" import { FREE_PLAN_LIMIT } from "../../Utils/Constants" import { Trans } from "@lingui/macro" @@ -298,7 +299,7 @@ const AppNav: React.FC = ({ navOpen, setNavOpen }: IAppNav) => { className={classes.navItem} to={ROUTE_LINKS.SharedFolders} > - + { const classes = useStyles() - const { buckets } = useFiles() - const { filesApiClient } = useFilesApi() + const { buckets, refreshBuckets, isLoadingBuckets } = useFiles() + const { filesApiClient, encryptedEncryptionKey } = useFilesApi() const [direction, setDirection] = useState("ascend") const [column, setColumn] = useState<"name" | "size" | "date_uploaded">("name") @@ -110,62 +132,78 @@ const SharedFolderOverview = () => { -
- - - - {/* Icon */} - - handleSortToggle("name")} - sortDirection={column === "name" ? direction : undefined} - sortActive={column === "name"} - > - Name - - - Shared with - - handleSortToggle("date_uploaded")} - sortDirection={ - column === "date_uploaded" ? direction : undefined - } - sortActive={column === "date_uploaded"} - > - Size - - {/* Menu */} - - - - {bucketsToShow.map((bucket) => - - )} - -
+ {isLoadingBuckets && ( +
+ + + Loading your shared folders... + +
+ )} + {!isLoadingBuckets && ( + + + + + {/* Icon */} + + handleSortToggle("name")} + sortDirection={column === "name" ? direction : undefined} + sortActive={column === "name"} + > + Name + + + Shared with + + handleSortToggle("date_uploaded")} + sortDirection={ + column === "date_uploaded" ? direction : undefined + } + sortActive={column === "date_uploaded"} + > + Size + + {/* Menu */} + + + + {bucketsToShow.map((bucket) => + + )} + +
+ )} ) } diff --git a/packages/files-ui/src/Contexts/FilesContext.tsx b/packages/files-ui/src/Contexts/FilesContext.tsx index f41f008fd9..4783dbc5e0 100644 --- a/packages/files-ui/src/Contexts/FilesContext.tsx +++ b/packages/files-ui/src/Contexts/FilesContext.tsx @@ -69,6 +69,7 @@ type FilesContext = { getFileContent: (bucketId: string, params: GetFileContentParams) => Promise refreshBuckets: () => Promise secureAccountWithMasterPassword: (candidatePassword: string) => Promise + isLoadingBuckets?: boolean } // This represents a File or Folder on the @@ -100,6 +101,7 @@ const FilesProvider = ({ children }: FilesContextProps) => { const [buckets, setBuckets] = useState([]) const { profile } = useUser() const { userId } = profile || {} + const [isLoadingBuckets, setIsLoadingBuckets] = useState(false) const getKeyForSharedBucket = useCallback(async (bucket: FilesBucket) => { const bucketUsers = [...bucket.readers, ...bucket.writers, ...bucket.owners] @@ -118,6 +120,7 @@ const FilesProvider = ({ children }: FilesContextProps) => { const refreshBuckets = useCallback(async () => { if (!personalEncryptionKey || !userId) return + setIsLoadingBuckets(true) const result = await filesApiClient.listBuckets() const bucketsWithKeys: Bucket[] = await Promise.all( @@ -152,6 +155,7 @@ const FilesProvider = ({ children }: FilesContextProps) => { }) ) setBuckets(bucketsWithKeys) + setIsLoadingBuckets(false) return Promise.resolve() }, [personalEncryptionKey, userId, filesApiClient, profile, getKeyForSharedBucket]) @@ -223,7 +227,6 @@ const FilesProvider = ({ children }: FilesContextProps) => { secureAccount() } else { console.log("decrypting key") - console.log("encryptedEncryptionKey", encryptedEncryptionKey) if (encryptedEncryptionKey) { decryptKey(encryptedEncryptionKey) } @@ -240,7 +243,8 @@ const FilesProvider = ({ children }: FilesContextProps) => { personalEncryptionKey, isMasterPasswordSet, secureAccount, - decryptKey + decryptKey, + isLoadingBuckets ]) const secureAccountWithMasterPassword = async (candidatePassword: string) => { @@ -472,8 +476,8 @@ const FilesProvider = ({ children }: FilesContextProps) => { downloadsInProgress, secureAccountWithMasterPassword, buckets, - refreshBuckets - + refreshBuckets, + isLoadingBuckets }} > {children} From e16b80be2a4823e8f2403f0d869553c47768eb51 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 17 Jun 2021 13:55:39 +0000 Subject: [PATCH 33/49] lingui extract --- packages/files-ui/src/locales/en/messages.po | 3 +++ packages/files-ui/src/locales/fr/messages.po | 3 +++ 2 files changed, 6 insertions(+) diff --git a/packages/files-ui/src/locales/en/messages.po b/packages/files-ui/src/locales/en/messages.po index 5c5a16600f..5b130ea09a 100644 --- a/packages/files-ui/src/locales/en/messages.po +++ b/packages/files-ui/src/locales/en/messages.po @@ -301,6 +301,9 @@ msgstr "Light Theme" msgid "Loading preview" msgstr "Loading preview" +msgid "Loading your shared folders..." +msgstr "Loading your shared folders..." + msgid "Looks like you’re signing in from a new browser. Please choose one of the following to continue:" msgstr "Looks like you’re signing in from a new browser. Please choose one of the following to continue:" diff --git a/packages/files-ui/src/locales/fr/messages.po b/packages/files-ui/src/locales/fr/messages.po index d093a1e1aa..52d11e08dc 100644 --- a/packages/files-ui/src/locales/fr/messages.po +++ b/packages/files-ui/src/locales/fr/messages.po @@ -302,6 +302,9 @@ msgstr "Thème clair" msgid "Loading preview" msgstr "Chargement de l’aperçu" +msgid "Loading your shared folders..." +msgstr "" + msgid "Looks like you’re signing in from a new browser. Please choose one of the following to continue:" msgstr "Connectez à partir d'un nouveau navigateur. Choisis une des options suivantes pour continuer:" From 9666ba465eb199b355147c909acd0865ae2a80f9 Mon Sep 17 00:00:00 2001 From: Thibaut Sardan Date: Thu, 17 Jun 2021 16:03:13 +0200 Subject: [PATCH 34/49] nav color --- packages/files-ui/src/Components/Layouts/AppNav.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/files-ui/src/Components/Layouts/AppNav.tsx b/packages/files-ui/src/Components/Layouts/AppNav.tsx index 9254bdad4b..3406643344 100644 --- a/packages/files-ui/src/Components/Layouts/AppNav.tsx +++ b/packages/files-ui/src/Components/Layouts/AppNav.tsx @@ -156,6 +156,9 @@ const useStyles = makeStyles( } }, "& svg": { + "& path" : { + fill: constants.nav.headingColor + }, transitionDuration: `${animation.transform}ms`, width: Number(constants.svgWidth), marginRight: constants.generalUnit * 2, From 4aa4b0ed8f4cd12c3fece4362d6f1b5d6f057cd5 Mon Sep 17 00:00:00 2001 From: Thibaut Sardan Date: Thu, 17 Jun 2021 16:13:53 +0200 Subject: [PATCH 35/49] Revert "nav color" This reverts commit 9666ba465eb199b355147c909acd0865ae2a80f9. --- packages/files-ui/src/Components/Layouts/AppNav.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/files-ui/src/Components/Layouts/AppNav.tsx b/packages/files-ui/src/Components/Layouts/AppNav.tsx index 3406643344..9254bdad4b 100644 --- a/packages/files-ui/src/Components/Layouts/AppNav.tsx +++ b/packages/files-ui/src/Components/Layouts/AppNav.tsx @@ -156,9 +156,6 @@ const useStyles = makeStyles( } }, "& svg": { - "& path" : { - fill: constants.nav.headingColor - }, transitionDuration: `${animation.transform}ms`, width: Number(constants.svgWidth), marginRight: constants.generalUnit * 2, From 9bf40f0d087179867ecb658ec87667c8b83af5ca Mon Sep 17 00:00:00 2001 From: Thibaut Sardan Date: Thu, 17 Jun 2021 16:14:13 +0200 Subject: [PATCH 36/49] Revert "loader" This reverts commit 3b0c05886dd9f5be821bc796213214dfe7fbdade. --- .../src/Icons/icons/UserShare.icon.tsx | 7 - packages/common-components/src/Icons/index.ts | 1 - .../src/Icons/svgs/user-share.svg | 3 - .../src/Components/Layouts/AppNav.tsx | 5 +- .../FileBrowsers/SharedFoldersOverview.tsx | 148 +++++++----------- .../files-ui/src/Contexts/FilesContext.tsx | 12 +- 6 files changed, 61 insertions(+), 115 deletions(-) delete mode 100644 packages/common-components/src/Icons/icons/UserShare.icon.tsx delete mode 100644 packages/common-components/src/Icons/svgs/user-share.svg diff --git a/packages/common-components/src/Icons/icons/UserShare.icon.tsx b/packages/common-components/src/Icons/icons/UserShare.icon.tsx deleted file mode 100644 index e09787ad78..0000000000 --- a/packages/common-components/src/Icons/icons/UserShare.icon.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import * as React from "react" -import createSvgIcon from "../createSvgIcon" -import { ReactComponent as UserShareSvg } from "../svgs/user-share.svg" - -export { UserShareSvg } - -export default createSvgIcon() diff --git a/packages/common-components/src/Icons/index.ts b/packages/common-components/src/Icons/index.ts index 7cf7e730c0..8d441d6688 100644 --- a/packages/common-components/src/Icons/index.ts +++ b/packages/common-components/src/Icons/index.ts @@ -65,7 +65,6 @@ export { default as TableIcon, TableSvg } from "./icons/Table.icon" export { default as UploadIcon, UploadSvg } from "./icons/Upload.icon" export { default as UnionpayCardIcon, UnionpayCardSvg } from "./icons/UnionpayCard.icon" export { default as UserIcon, UserSvg } from "./icons/User.icon" -export { default as UserShareIcon, UserShareSvg } from "./icons/UserShare.icon" export { default as VisaCardIcon, VisaCardSvg } from "./icons/VisaCard.icon" export { default as WarningIcon, WarningSvg } from "./icons/Warning.icon" export { default as ZoomInIcon, ZoomInSvg } from "./icons/ZoomIn.icon" diff --git a/packages/common-components/src/Icons/svgs/user-share.svg b/packages/common-components/src/Icons/svgs/user-share.svg deleted file mode 100644 index 39520327f2..0000000000 --- a/packages/common-components/src/Icons/svgs/user-share.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/files-ui/src/Components/Layouts/AppNav.tsx b/packages/files-ui/src/Components/Layouts/AppNav.tsx index 9254bdad4b..11c2fb4004 100644 --- a/packages/files-ui/src/Components/Layouts/AppNav.tsx +++ b/packages/files-ui/src/Components/Layouts/AppNav.tsx @@ -16,8 +16,7 @@ import { ProgressBar, Button, formatBytes, - DeleteSvg, - UserShareSvg } from "@chainsafe/common-components" + DeleteSvg } from "@chainsafe/common-components" import { ROUTE_LINKS } from "../FilesRoutes" import { FREE_PLAN_LIMIT } from "../../Utils/Constants" import { Trans } from "@lingui/macro" @@ -299,7 +298,7 @@ const AppNav: React.FC = ({ navOpen, setNavOpen }: IAppNav) => { className={classes.navItem} to={ROUTE_LINKS.SharedFolders} > - + { const classes = useStyles() - const { buckets, refreshBuckets, isLoadingBuckets } = useFiles() - const { filesApiClient, encryptedEncryptionKey } = useFilesApi() + const { buckets } = useFiles() + const { filesApiClient } = useFilesApi() const [direction, setDirection] = useState("ascend") const [column, setColumn] = useState<"name" | "size" | "date_uploaded">("name") @@ -132,78 +110,62 @@ const SharedFolderOverview = () => { - {isLoadingBuckets && ( -
- - - Loading your shared folders... - -
- )} - {!isLoadingBuckets && ( - - - - - {/* Icon */} - - handleSortToggle("name")} - sortDirection={column === "name" ? direction : undefined} - sortActive={column === "name"} - > - Name - - - Shared with - - handleSortToggle("date_uploaded")} - sortDirection={ - column === "date_uploaded" ? direction : undefined - } - sortActive={column === "date_uploaded"} - > - Size - - {/* Menu */} - - - - {bucketsToShow.map((bucket) => - - )} - -
- )} + + + + + {/* Icon */} + + handleSortToggle("name")} + sortDirection={column === "name" ? direction : undefined} + sortActive={column === "name"} + > + Name + + + Shared with + + handleSortToggle("date_uploaded")} + sortDirection={ + column === "date_uploaded" ? direction : undefined + } + sortActive={column === "date_uploaded"} + > + Size + + {/* Menu */} + + + + {bucketsToShow.map((bucket) => + + )} + +
) } diff --git a/packages/files-ui/src/Contexts/FilesContext.tsx b/packages/files-ui/src/Contexts/FilesContext.tsx index 4783dbc5e0..f41f008fd9 100644 --- a/packages/files-ui/src/Contexts/FilesContext.tsx +++ b/packages/files-ui/src/Contexts/FilesContext.tsx @@ -69,7 +69,6 @@ type FilesContext = { getFileContent: (bucketId: string, params: GetFileContentParams) => Promise refreshBuckets: () => Promise secureAccountWithMasterPassword: (candidatePassword: string) => Promise - isLoadingBuckets?: boolean } // This represents a File or Folder on the @@ -101,7 +100,6 @@ const FilesProvider = ({ children }: FilesContextProps) => { const [buckets, setBuckets] = useState([]) const { profile } = useUser() const { userId } = profile || {} - const [isLoadingBuckets, setIsLoadingBuckets] = useState(false) const getKeyForSharedBucket = useCallback(async (bucket: FilesBucket) => { const bucketUsers = [...bucket.readers, ...bucket.writers, ...bucket.owners] @@ -120,7 +118,6 @@ const FilesProvider = ({ children }: FilesContextProps) => { const refreshBuckets = useCallback(async () => { if (!personalEncryptionKey || !userId) return - setIsLoadingBuckets(true) const result = await filesApiClient.listBuckets() const bucketsWithKeys: Bucket[] = await Promise.all( @@ -155,7 +152,6 @@ const FilesProvider = ({ children }: FilesContextProps) => { }) ) setBuckets(bucketsWithKeys) - setIsLoadingBuckets(false) return Promise.resolve() }, [personalEncryptionKey, userId, filesApiClient, profile, getKeyForSharedBucket]) @@ -227,6 +223,7 @@ const FilesProvider = ({ children }: FilesContextProps) => { secureAccount() } else { console.log("decrypting key") + console.log("encryptedEncryptionKey", encryptedEncryptionKey) if (encryptedEncryptionKey) { decryptKey(encryptedEncryptionKey) } @@ -243,8 +240,7 @@ const FilesProvider = ({ children }: FilesContextProps) => { personalEncryptionKey, isMasterPasswordSet, secureAccount, - decryptKey, - isLoadingBuckets + decryptKey ]) const secureAccountWithMasterPassword = async (candidatePassword: string) => { @@ -476,8 +472,8 @@ const FilesProvider = ({ children }: FilesContextProps) => { downloadsInProgress, secureAccountWithMasterPassword, buckets, - refreshBuckets, - isLoadingBuckets + refreshBuckets + }} > {children} From 9816d384f846eebac7037e795d67197cd153798b Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 17 Jun 2021 14:19:20 +0000 Subject: [PATCH 37/49] lingui extract --- packages/files-ui/src/locales/en/messages.po | 3 --- packages/files-ui/src/locales/fr/messages.po | 3 --- 2 files changed, 6 deletions(-) diff --git a/packages/files-ui/src/locales/en/messages.po b/packages/files-ui/src/locales/en/messages.po index ae41a9e201..a6b75ece9d 100644 --- a/packages/files-ui/src/locales/en/messages.po +++ b/packages/files-ui/src/locales/en/messages.po @@ -304,9 +304,6 @@ msgstr "Light Theme" msgid "Loading preview" msgstr "Loading preview" -msgid "Loading your shared folders..." -msgstr "Loading your shared folders..." - msgid "Looks like you’re signing in from a new browser. Please choose one of the following to continue:" msgstr "Looks like you’re signing in from a new browser. Please choose one of the following to continue:" diff --git a/packages/files-ui/src/locales/fr/messages.po b/packages/files-ui/src/locales/fr/messages.po index 97f94ebe65..d7f3502316 100644 --- a/packages/files-ui/src/locales/fr/messages.po +++ b/packages/files-ui/src/locales/fr/messages.po @@ -305,9 +305,6 @@ msgstr "Thème clair" msgid "Loading preview" msgstr "Chargement de l’aperçu" -msgid "Loading your shared folders..." -msgstr "" - msgid "Looks like you’re signing in from a new browser. Please choose one of the following to continue:" msgstr "Connectez à partir d'un nouveau navigateur. Choisis une des options suivantes pour continuer:" From 5f9bd2d1220264ac863401b4758a2f3bda7ee11c Mon Sep 17 00:00:00 2001 From: Thibaut Sardan Date: Fri, 18 Jun 2021 14:26:33 +0200 Subject: [PATCH 38/49] Revert "Merge branch 'feat/tbaut-shared-overview-1041' of github.com:ChainSafe/files-ui into feat/storage-buckets-overview-1104" This reverts commit 6c8a65acc71d7cb1293f02561a9ad408d132cf2b, reversing changes made to bdf5fdcfeb7c38c83aba6dde8177480728be5a8b. --- .../src/Components/Elements/SharedUser.tsx | 131 ------------- .../files-ui/src/Components/FilesRoutes.tsx | 10 +- .../src/Components/Layouts/AppNav.tsx | 13 -- .../Modules/DownloadProgressModals/index.tsx | 23 +-- .../Modules/FileBrowsers/BinFileBrowser.tsx | 7 +- .../Modules/FileBrowsers/CSFFileBrowser.tsx | 10 +- .../FileBrowsers/SharedFolderRowWrapper.tsx | 149 --------------- .../FileBrowsers/SharedFoldersOverview.tsx | 173 ------------------ .../Components/Modules/FileBrowsers/types.ts | 4 - .../views/FileSystemItem/FileSystemItem.tsx | 1 + .../views/FileSystemItem/SharedFolderRow.tsx | 172 ----------------- .../Modules/FileBrowsers/views/FilesList.tsx | 2 + .../files-ui/src/Contexts/FilesContext.tsx | 119 ++++-------- packages/files-ui/src/Utils/pathUtils.ts | 2 +- packages/files-ui/src/locales/en/messages.po | 9 - packages/files-ui/src/locales/fr/messages.po | 9 - 16 files changed, 64 insertions(+), 770 deletions(-) delete mode 100644 packages/files-ui/src/Components/Elements/SharedUser.tsx delete mode 100644 packages/files-ui/src/Components/Modules/FileBrowsers/SharedFolderRowWrapper.tsx delete mode 100644 packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx delete mode 100644 packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/SharedFolderRow.tsx diff --git a/packages/files-ui/src/Components/Elements/SharedUser.tsx b/packages/files-ui/src/Components/Elements/SharedUser.tsx deleted file mode 100644 index d05b482c8a..0000000000 --- a/packages/files-ui/src/Components/Elements/SharedUser.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import React, { useState } from "react" -import { makeStyles, createStyles } from "@chainsafe/common-theme" -import { CSFTheme } from "../../Themes/types" -import { Typography, UserIcon } from "@chainsafe/common-components" -import clsx from "clsx" - -const useStyles = makeStyles(({ zIndex, animation, constants, palette }: CSFTheme) => { - return createStyles({ - root: { - display: "flex" - }, - bubble: { - position: "relative", - borderRadius: "50%", - backgroundColor: palette.additional["gray"][6], - color: palette.common.white.main, - width: constants.generalUnit * 5, - height: constants.generalUnit * 5, - display: "flex", - alignItems: "center", - justifyContent: "center", - "& svg": { - fill: palette.common.white.main - }, - "&:first-child": { - marginRight: constants.generalUnit - } - }, - text : { - textAlign: "center" - }, - tooltip: { - display: "flex", - flexDirection: "column", - alignItems: "center", - justifyContent: "center", - left: "50%", - top: "-20px", - position: "absolute", - transform: "translate(-50%, -50%)", - zIndex: zIndex?.layer1, - transitionDuration: `${animation.transform}ms`, - opacity: 0, - visibility: "hidden", - backgroundColor: constants.loginModule.flagBg, - color: constants.loginModule.flagText, - padding: `${constants.generalUnit / 2}px ${constants.generalUnit}px`, - borderRadius: 2, - "&:after": { - transitionDuration: `${animation.transform}ms`, - content: "''", - position: "absolute", - top: "100%", - left: "50%", - transform: "translate(-50%,0)", - width: 0, - height: 0, - borderLeft: "5px solid transparent", - borderRight: "5px solid transparent", - borderTop: `5px solid ${constants.loginModule.flagBg}` - }, - "&.active": { - opacity: 1, - visibility: "visible" - } - - } - }) -}) - -interface Props { - sharedUsers: string[] -} - -const SharedUsers = ({ sharedUsers }: Props) => { - const classes = useStyles() - - if (!sharedUsers.length) { - return null - } - - const UserBubble = ({ text, hover }: {text?: string; hover: string | string[]}) => { - const [isHover, setIsHover] = useState(false) - - return ( -
setIsHover(true)} - onMouseLeave={() => setIsHover(false)} - className={classes.bubble} - > -
- { - Array.isArray(hover) - ? hover.map((user) =>
{user}
) - : hover - } -
- {text - ? - {text} - - : - } -
- ) - } - - return ( -
- - {sharedUsers.length > 2 && ( - - )} - {sharedUsers.length === 2 && ( - - )} -
- ) -} - -export default SharedUsers \ No newline at end of file diff --git a/packages/files-ui/src/Components/FilesRoutes.tsx b/packages/files-ui/src/Components/FilesRoutes.tsx index 134d69cb2c..6428f807dd 100644 --- a/packages/files-ui/src/Components/FilesRoutes.tsx +++ b/packages/files-ui/src/Components/FilesRoutes.tsx @@ -8,7 +8,6 @@ import SearchPage from "./Pages/SearchPage" import BinPage from "./Pages/BinPage" import PurchasePlanPage from "./Pages/PurchasePlanPage" import { useThresholdKey } from "../Contexts/ThresholdKeyContext" -import SharedFoldersOverview from "./Modules/FileBrowsers/SharedFoldersOverview" export const SETTINGS_BASE = "/settings" export const ROUTE_LINKS = { @@ -24,8 +23,7 @@ export const ROUTE_LINKS = { SettingsDefault: `${SETTINGS_BASE}`, PurchasePlan: "/purchase", UserSurvey: "https://shrl.ink/kmAL", - GeneralFeedbackForm: "https://shrl.ink/gvVJ", - SharedFolders: "/shared" + GeneralFeedbackForm: "https://shrl.ink/gvVJ" } export const SETTINGS_PATHS = ["profile", "plan", "security"] as const @@ -52,12 +50,6 @@ const FilesRoutes = () => { component={BinPage} redirectPath={ROUTE_LINKS.Landing} /> - = ({ navOpen, setNavOpen }: IAppNav) => { Home
- - - - Shared - - { if (downloadsInProgress.length === 0) { return null } - if (downloadsInProgress.length > 0) { - return ( -
- {downloadsInProgress.map((downloadInProgress) => ( - - ))} -
- )} else { - return null - } + return ( +
+ {downloadsInProgress.map((downloadInProgress) => ( + + ))} +
+ ) } export default DownloadProgressModals diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx index 471a048fe6..4c95fecb4c 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx @@ -20,7 +20,10 @@ const BinFileBrowser: React.FC = ({ controls = false }: const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) const [pathContents, setPathContents] = useState([]) const { pathname } = useLocation() - const currentPath = useMemo(() => extractFileBrowserPathFromURL(pathname, ROUTE_LINKS.Bin("")), [pathname]) + const currentPath = useMemo(() => + extractFileBrowserPathFromURL(pathname, ROUTE_LINKS.Bin("")), + [pathname] + ) const { redirect } = useHistory() const bucket = useMemo(() => buckets.find(b => b.type === "trash"), [buckets]) @@ -156,7 +159,7 @@ const BinFileBrowser: React.FC = ({ controls = false }: = () => { const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) const [pathContents, setPathContents] = useState([]) const { redirect } = useHistory() + const { pathname } = useLocation() - const currentPath = useMemo(() => extractFileBrowserPathFromURL(pathname, ROUTE_LINKS.Drive("")), [pathname]) + const currentPath = useMemo(() => + extractFileBrowserPathFromURL(pathname, ROUTE_LINKS.Drive("")), + [pathname] + ) const bucket = useMemo(() => buckets.find(b => b.type === "csf"), [buckets]) const refreshContents = useCallback((showLoading?: boolean) => { @@ -182,9 +186,7 @@ const CSFFileBrowser: React.FC = () => { appearance: "error" }) } else { - uploadFiles(bucket.id, files, path) - .then(() => refreshContents()) - .catch(console.error) + uploadFiles(bucket.id, files, path).then(() => refreshContents()).catch(console.error) } }, [addToastMessage, uploadFiles, bucket, refreshContents]) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFolderRowWrapper.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFolderRowWrapper.tsx deleted file mode 100644 index de08cdd99e..0000000000 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFolderRowWrapper.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import React, { useCallback } from "react" -import { DeleteSvg, EditSvg, IMenuItem } from "@chainsafe/common-components" -import { makeStyles, createStyles, useDoubleClick, useThemeSwitcher } from "@chainsafe/common-theme" -import { Trans } from "@lingui/macro" -import { CSFTheme } from "../../../Themes/types" -import { Bucket } from "@chainsafe/files-api-client" -import SharedFolderItem from "./views/FileSystemItem/SharedFolderRow" - -const useStyles = makeStyles(({ breakpoints, constants }: CSFTheme) => { - return createStyles({ - renameInput: { - width: "100%", - [breakpoints.up("md")]: { - margin: 0 - }, - [breakpoints.down("md")]: { - margin: `${constants.generalUnit * 4.2}px 0` - } - }, - modalRoot: { - [breakpoints.down("md")]: {} - }, - modalInner: { - [breakpoints.down("md")]: { - bottom: - Number(constants?.mobileButtonHeight) + constants.generalUnit, - borderTopLeftRadius: `${constants.generalUnit * 1.5}px`, - borderTopRightRadius: `${constants.generalUnit * 1.5}px`, - borderBottomLeftRadius: `${constants.generalUnit * 1.5}px`, - borderBottomRightRadius: `${constants.generalUnit * 1.5}px`, - maxWidth: `${breakpoints.width("md")}px !important` - } - }, - renameHeader: { - textAlign: "center" - }, - renameFooter: { - display: "flex", - flexDirection: "row", - alignItems: "center", - justifyContent: "flex-end" - }, - renameModal: { - padding: constants.generalUnit * 4 - }, - okButton: { - marginLeft: constants.generalUnit - }, - cancelButton: { - [breakpoints.down("md")]: { - position: "fixed", - bottom: 0, - left: 0, - width: "100%", - height: constants?.mobileButtonHeight - } - }, - menuIcon: { - display: "flex", - justifyContent: "center", - alignItems: "center", - width: 20, - marginRight: constants.generalUnit * 1.5, - "& svg": { - fill: constants.fileSystemItemRow.menuIcon - } - }, - dropdownIcon: { - "& svg": { - fill: constants.fileSystemItemRow.dropdownIcon - } - } - }) -}) - -interface Props { - bucket: Bucket -} - -const SharedFolderRowWrapper = ({ bucket }: Props) => { - const { desktop } = useThemeSwitcher() - const classes = useStyles() - - - const menuItems: IMenuItem[] = [{ - contents: ( - <> - - - Rename - - - ), - onClick: () => console.log("not implemented") - }, - { - contents: ( - <> - - - Delete - - - ), - onClick: () => console.log("not implemented") - }] - - const onSingleClick = useCallback( - (e) => { - if (desktop) { - // on desktop - } else { - // on mobile - } - }, - [desktop] - ) - - const onDoubleClick = useCallback( - () => { - if (desktop) { - // on desktop - } else { - // on mobile - return - } - }, - [desktop] - ) - - const { click } = useDoubleClick(onSingleClick, onDoubleClick) - - const onFolderClick = (e?: React.MouseEvent) => { - e?.persist() - click(e) - } - - return ( - <> - - - ) -} - -export default SharedFolderRowWrapper diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx deleted file mode 100644 index 8ec327061a..0000000000 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import React, { useMemo, useState } from "react" -import { Typography, Table, TableHead, TableRow, TableHeadCell, TableBody, SortDirection } from "@chainsafe/common-components" -import { useFiles } from "../../../Contexts/FilesContext" -import { Trans } from "@lingui/macro" -import { createStyles, makeStyles } from "@chainsafe/common-theme" -import { CSFTheme } from "../../../Themes/types" -import { useFilesApi } from "../../../Contexts/FilesApiContext" -import SharedFolderRowWrapper from "./SharedFolderRowWrapper" - -export const desktopSharedGridSettings = "69px 3fr 190px 150px 69px !important" -export const mobileSharedGridSettings = "69px 3fr 45px !important" - -const useStyles = makeStyles( - ({ animation, breakpoints, constants, palette }: CSFTheme) => { - - return createStyles({ - root: { - position: "relative", - [breakpoints.down("md")]: { - marginLeft: constants.generalUnit * 2, - marginRight: constants.generalUnit * 2 - }, - [breakpoints.up("md")]: { - border: "1px solid transparent", - padding: `0 ${constants.generalUnit}px`, - borderRadius: constants.generalUnit / 4, - minHeight: `calc(100vh - ${Number(constants.contentTopPadding)}px)`, - "&.droppable": { - borderColor: palette.additional["geekblue"][4] - } - } - }, - header: { - display: "flex", - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - [breakpoints.down("md")]: { - marginTop: constants.generalUnit - } - }, - controls: { - display: "flex", - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - "& > button": { - marginLeft: constants.generalUnit - } - }, - fadeOutLoading: { - opacity: 0.2, - transition: `opacity ${animation.transform * 3}ms` - }, - tableHead: { - marginTop: constants.generalUnit * 3 - }, - tableRow: { - border: "2px solid transparent", - transitionDuration: `${animation.transform}ms`, - [breakpoints.up("md")]: { - gridTemplateColumns: desktopSharedGridSettings - }, - [breakpoints.down("md")]: { - gridTemplateColumns: mobileSharedGridSettings - } - } - }) - } -) - -const SharedFolderOverview = () => { - const classes = useStyles() - const { buckets } = useFiles() - const { filesApiClient } = useFilesApi() - const [direction, setDirection] = useState("ascend") - const [column, setColumn] = useState<"name" | "size" | "date_uploaded">("name") - - const bucketsToShow = useMemo(() => buckets.filter(b => b.type === "share"), [buckets]) - - const handleSortToggle = ( - targetColumn: "name" | "size" | "date_uploaded" - ) => { - if (column !== targetColumn) { - setColumn(targetColumn) - setDirection("descend") - } else { - if (direction === "ascend") { - setDirection("descend") - } else { - setDirection("ascend") - } - } - } - - return ( -
-
- - Shared folders - -
-
-
- - - - - - {/* Icon */} - - handleSortToggle("name")} - sortDirection={column === "name" ? direction : undefined} - sortActive={column === "name"} - > - Name - - - Shared with - - handleSortToggle("date_uploaded")} - sortDirection={ - column === "date_uploaded" ? direction : undefined - } - sortActive={column === "date_uploaded"} - > - Size - - {/* Menu */} - - - - {bucketsToShow.map((bucket) => - - )} - -
-
- ) -} - -export default SharedFolderOverview diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/types.ts b/packages/files-ui/src/Components/Modules/FileBrowsers/types.ts index 128e55ca17..d2c3d175fa 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/types.ts +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/types.ts @@ -12,10 +12,6 @@ export type FileOperation = | "preview" | "view_folder" -export type BucketOperation = - | "rename" - | "delete" - export type BrowserView = "grid" | "table" export type MoveModalMode = "move" | "recover" 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 83f0068abf..51ddf85f76 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 @@ -100,6 +100,7 @@ const useStyles = makeStyles(({ breakpoints, constants }: CSFTheme) => { }) interface IFileSystemItemProps { + index: number file: FileSystemItemType files: FileSystemItemType[] selected: string[] diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/SharedFolderRow.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/SharedFolderRow.tsx deleted file mode 100644 index 297053096b..0000000000 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FileSystemItem/SharedFolderRow.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import React from "react" -import { makeStyles, createStyles, useThemeSwitcher } from "@chainsafe/common-theme" -import { - FolderFilledIcon, - formatBytes, - IMenuItem, - MenuDropdown, - MoreIcon, - TableCell, - TableRow, - Typography -} from "@chainsafe/common-components" -import { CSFTheme } from "../../../../../Themes/types" -import { Bucket, BucketUser } from "@chainsafe/files-api-client" -import { desktopSharedGridSettings, mobileSharedGridSettings } from "../../SharedFoldersOverview" -import SharedUsers from "../../../../Elements/SharedUser" - -const useStyles = makeStyles(({ breakpoints, constants, palette }: CSFTheme) => { - - return createStyles({ - tableRow: { - border: "2px solid transparent", - [breakpoints.up("md")]: { - gridTemplateColumns: desktopSharedGridSettings - }, - [breakpoints.down("md")]: { - gridTemplateColumns: mobileSharedGridSettings - }, - "&.droppable": { - border: `2px solid ${palette.primary.main}` - } - }, - folderIcon: { - display: "flex", - flexDirection: "row", - alignItems: "center", - justifyContent: "center", - "& svg": { - width: constants.generalUnit * 2.5, - fill: palette.additional.gray[9] - } - }, - renameInput: { - width: "100%", - [breakpoints.up("md")]: { - margin: 0 - }, - [breakpoints.down("md")]: { - margin: `${constants.generalUnit * 4.2}px 0` - } - }, - menuIcon: { - display: "flex", - justifyContent: "center", - alignItems: "center", - width: 20, - marginRight: constants.generalUnit * 1.5, - "& svg": { - fill: constants.fileSystemItemRow.menuIcon - } - }, - desktopRename: { - display: "flex", - flexDirection: "row", - "& svg": { - width: 20, - height: 20 - } - }, - filename: { - whiteSpace: "nowrap", - textOverflow: "ellipsis", - overflow: "hidden", - "&.editing": { - overflow: "visible" - } - }, - dropdownIcon: { - "& svg": { - fill: constants.fileSystemItemRow.dropdownIcon - } - }, - dropdownOptions: { - backgroundColor: constants.fileSystemItemRow.optionsBackground, - color: constants.fileSystemItemRow.optionsColor, - border: `1px solid ${constants.fileSystemItemRow.optionsBorder}` - }, - dropdownItem: { - backgroundColor: constants.fileSystemItemRow.itemBackground, - color: constants.fileSystemItemRow.itemColor - } - }) -}) - -interface Props { - bucket: Bucket - onFolderClick: (e?: React.MouseEvent) => void - menuItems: IMenuItem[] -} - -const SharedFolderRow = ({ bucket, onFolderClick, menuItems }: Props) => { - const classes = useStyles() - const { name, size } = bucket - const { desktop } = useThemeSwitcher() - - const getUserIds = (users: BucketUser[]): string[] => { - return users.reduce((acc: string[], user): string[] => { - return user.uuid ? [...acc, user.uuid] : acc - }, [] as string[]) - } - - const userIds = [...getUserIds(bucket.owners), ...getUserIds(bucket.readers), ...getUserIds(bucket.writers)] - - return ( - - onFolderClick(e)} - > - - - onFolderClick(e)} - > - {name} - - onFolderClick(e)} - > - - - {desktop && ( - <> - {/* - { - dayjs.unix(created_at).format("DD MMM YYYY h:mm a") - } - */} - - {formatBytes(size)} - - - )} - - - - - ) -} - -export default SharedFolderRow diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx index fad09606bb..c26b895694 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx @@ -880,6 +880,7 @@ const FilesList = () => { {items.map((file, index) => ( { {items.map((file, index) => ( { const [personalEncryptionKey, setPersonalEncryptionKey] = useState() const [buckets, setBuckets] = useState([]) const { profile } = useUser() - const { userId } = profile || {} - const getKeyForSharedBucket = useCallback(async (bucket: FilesBucket) => { - const bucketUsers = [...bucket.readers, ...bucket.writers, ...bucket.owners] - const bucketUser = bucketUsers.find(bu => bu.uuid === userId) - - if (!bucketUser?.encryption_key) { + const getKeyForBucket = useCallback(async (bucket: FilesBucket) => { + const bucketUsers = [...bucket.owners, ...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, userId]) + }, [decryptMessageWithThresholdKey, profile]) const refreshBuckets = useCallback(async () => { - if (!personalEncryptionKey || !userId) return - + if (!personalEncryptionKey) return const result = await filesApiClient.listBuckets() - const bucketsWithKeys: Bucket[] = await Promise.all( - result.map(async (b) => { - - const permission = b.owners.find(owner => owner === profile?.userId) - ? "owner" as BucketPermission - : b.writers.find(writer => writer === profile?.userId) - ? "writer" as BucketPermission - : b.readers.find(reader => reader === profile?.userId) - ? "reader" as BucketPermission - : undefined - - let encryptionKey = "" - - switch(b.type) { - case "csf": - case "trash": { - encryptionKey = personalEncryptionKey - break - } - case "share": { - encryptionKey = await getKeyForSharedBucket(b) - break - }} - - return { - ...b, - encryptionKey, - permission - } - }) - ) + 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() - }, [personalEncryptionKey, userId, filesApiClient, profile, getKeyForSharedBucket]) + }, [getKeyForBucket, filesApiClient, personalEncryptionKey]) useEffect(() => { refreshBuckets() @@ -186,44 +149,40 @@ const FilesProvider = ({ children }: FilesContextProps) => { } }, [isLoggedIn]) - const secureAccount = useCallback(() => { - if (!publicKey) return - - const key = Buffer.from( - window.crypto.getRandomValues(new Uint8Array(32)) - ).toString("base64") - console.log("New key", key) - setPersonalEncryptionKey(key) - encryptForPublicKey(publicKey, key) - .then((encryptedKey) => { - console.log("Encrypted encryption key", encryptedKey) - secureThresholdKeyAccount(encryptedKey) - }) - .catch(console.error) - }, [encryptForPublicKey, publicKey, secureThresholdKeyAccount]) - - const decryptKey = useCallback((encryptedKey: string) => { - console.log("Decrypting retrieved key") - - decryptMessageWithThresholdKey(encryptedKey) - .then((decryptedKey) => { - console.log("Decrypted key: ", decryptedKey) - setPersonalEncryptionKey(decryptedKey) - }) - .catch(console.error) - }, [decryptMessageWithThresholdKey]) - // Drive encryption handler useEffect(() => { + const secureAccount = async () => { + if (!publicKey) return + const key = Buffer.from( + window.crypto.getRandomValues(new Uint8Array(32)) + ).toString("base64") + console.log("New key", key) + setPersonalEncryptionKey(key) + const encryptedKey = await encryptForPublicKey(publicKey, key) + console.log("Encrypted encryption key", encryptedKey) + secureThresholdKeyAccount(encryptedKey) + } + + const decryptKey = async (encryptedKey: string) => { + console.log("Decrypting retrieved key") + try { + const decryptedKey = await decryptMessageWithThresholdKey(encryptedKey) + if (decryptedKey) { + console.log("Decrypted key: ", decryptedKey) + setPersonalEncryptionKey(decryptedKey) + } + } catch (error) { + console.error("Error decrypting key: ", encryptedKey) + } + } + if (isLoggedIn && publicKey && !personalEncryptionKey) { console.log("Checking whether account is secured ", secured) - if (!secured && !isMasterPasswordSet) { console.log("Generating key and securing account") secureAccount() } else { console.log("decrypting key") - console.log("encryptedEncryptionKey", encryptedEncryptionKey) if (encryptedEncryptionKey) { decryptKey(encryptedEncryptionKey) } @@ -238,9 +197,7 @@ const FilesProvider = ({ children }: FilesContextProps) => { secureThresholdKeyAccount, decryptMessageWithThresholdKey, personalEncryptionKey, - isMasterPasswordSet, - secureAccount, - decryptKey + isMasterPasswordSet ]) const secureAccountWithMasterPassword = async (candidatePassword: string) => { diff --git a/packages/files-ui/src/Utils/pathUtils.ts b/packages/files-ui/src/Utils/pathUtils.ts index ba43712399..9a1b7940e7 100644 --- a/packages/files-ui/src/Utils/pathUtils.ts +++ b/packages/files-ui/src/Utils/pathUtils.ts @@ -50,4 +50,4 @@ export function getParentPathFromFilePath(filePath: string) { // /drive/path/to/somewhere -> /path/to/somewhere export function extractFileBrowserPathFromURL(browserUrl: string, modulePath: string) { return browserUrl.replace(modulePath, "").split("/").map(decodeURIComponent).join("/") -} +} \ 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 a6b75ece9d..f79c8aca53 100644 --- a/packages/files-ui/src/locales/en/messages.po +++ b/packages/files-ui/src/locales/en/messages.po @@ -529,15 +529,6 @@ msgstr "Setup incomplete" msgid "Share" msgstr "Share" -msgid "Shared" -msgstr "Shared" - -msgid "Shared folders" -msgstr "Shared folders" - -msgid "Shared with" -msgstr "Shared with" - msgid "Sign Out" msgstr "Sign Out" diff --git a/packages/files-ui/src/locales/fr/messages.po b/packages/files-ui/src/locales/fr/messages.po index d7f3502316..8ca5887cf3 100644 --- a/packages/files-ui/src/locales/fr/messages.po +++ b/packages/files-ui/src/locales/fr/messages.po @@ -530,15 +530,6 @@ msgstr "Configuration incomplète" msgid "Share" msgstr "Partager" -msgid "Shared" -msgstr "" - -msgid "Shared folders" -msgstr "" - -msgid "Shared with" -msgstr "" - msgid "Sign Out" msgstr "Déconnexion" From f98a3a5241d8eef765c19dd8b8a98742ee37c431 Mon Sep 17 00:00:00 2001 From: Michael Yankelev <12774278+FSM1@users.noreply.github.com> Date: Fri, 18 Jun 2021 17:15:38 +0200 Subject: [PATCH 39/49] Apply suggestions from code review Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Co-authored-by: Tanmoy Basak Anjan --- .../DownloadProgressModals/DownloadBox.tsx | 73 +++++++++---------- .../UploadFileModal/UploadFileModal.tsx | 2 +- .../src/Components/Pages/BucketPage.tsx | 5 +- .../src/Components/Pages/BucketsPage.tsx | 4 +- .../src/Contexts/StorageContext.tsx | 3 +- 5 files changed, 41 insertions(+), 46 deletions(-) diff --git a/packages/storage-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx b/packages/storage-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx index eb2d4364aa..30938c73f0 100644 --- a/packages/storage-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx +++ b/packages/storage-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx @@ -65,44 +65,41 @@ const DownloadBox: React.FC = ({ downloadInProgress }) => { return ( <>
- {complete ? ( -
- - - Download complete - -
- ) : error ? ( -
- - - {errorMessage} - -
- ) : ( -
- - Downloading {fileName}... - - -
- )} -
- - ) + { + complete + ?
+ + + Download complete + +
+ : error + ?
+ + + {errorMessage} + +
+ :
+ + Downloading {fileName}... + + +
+ } } export default DownloadBox diff --git a/packages/storage-ui/src/Components/Modules/UploadFileModal/UploadFileModal.tsx b/packages/storage-ui/src/Components/Modules/UploadFileModal/UploadFileModal.tsx index 21d833ca20..2538bdd729 100644 --- a/packages/storage-ui/src/Components/Modules/UploadFileModal/UploadFileModal.tsx +++ b/packages/storage-ui/src/Components/Modules/UploadFileModal/UploadFileModal.tsx @@ -96,7 +96,7 @@ const UploadFileModule = ({ modalOpen, close }: IUploadFileModuleProps) => { helpers.resetForm() } catch (errors) { if (errors[0].message.includes("conflict with existing")) { - helpers.setFieldError("files", "File/Folder exists") + helpers.setFieldError("files", t`File/Folder already exists`) } else { helpers.setFieldError("files", errors[0].message) } diff --git a/packages/storage-ui/src/Components/Pages/BucketPage.tsx b/packages/storage-ui/src/Components/Pages/BucketPage.tsx index ca3475cff9..0c0ef35107 100644 --- a/packages/storage-ui/src/Components/Pages/BucketPage.tsx +++ b/packages/storage-ui/src/Components/Pages/BucketPage.tsx @@ -26,9 +26,8 @@ const BucketPage: React.FC = () => { , [pathname]) const currentPath = useMemo(() => { - return extractFileBrowserPathFromURL(pathname, ROUTE_LINKS.Bucket(bucketId, "/"))}, - [pathname, bucketId] - ) + return extractFileBrowserPathFromURL(pathname, ROUTE_LINKS.Bucket(bucketId, "/")) + }, [pathname, bucketId]) const bucket = useMemo(() => storageBuckets.find(b => b.id === bucketId), [storageBuckets, bucketId]) const refreshContents = useCallback((showLoading?: boolean) => { diff --git a/packages/storage-ui/src/Components/Pages/BucketsPage.tsx b/packages/storage-ui/src/Components/Pages/BucketsPage.tsx index 4daab17bc4..463ec637ba 100644 --- a/packages/storage-ui/src/Components/Pages/BucketsPage.tsx +++ b/packages/storage-ui/src/Components/Pages/BucketsPage.tsx @@ -98,7 +98,7 @@ const BucketsPage = () => {
- +
{ }} closePosition="none" > - Bucket Name + Bucket Name setNewBucketName(String(val))} /> diff --git a/packages/storage-ui/src/Contexts/StorageContext.tsx b/packages/storage-ui/src/Contexts/StorageContext.tsx index 1b346c64a8..30d115a342 100644 --- a/packages/storage-ui/src/Contexts/StorageContext.tsx +++ b/packages/storage-ui/src/Contexts/StorageContext.tsx @@ -104,7 +104,6 @@ const StorageProvider = ({ children }: StorageContextProps) => { useEffect(() => { const getSpaceUsage = async () => { try { - // TODO: Update this to include Share buckets where the current user is the owner const totalSize = storageBuckets.reduce((totalSize, bucket) => { return totalSize += (bucket as any).size}, 0) setSpaceUsed(totalSize) @@ -210,7 +209,7 @@ const StorageProvider = ({ children }: StorageContextProps) => { if (filesParam.length !== files.length) { addToastMessage({ message: - "We can't encrypt files larger than 2GB. Some items will not be uploaded", + "We can't upload files larger than 2GB. Some items will not be uploaded", appearance: "error" }) } From 82ba2c5f14565914cfb277fd7124956a9addcc58 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 18 Jun 2021 17:55:46 +0200 Subject: [PATCH 40/49] implement feedback --- .../src/Components/Elements/BucketRow.tsx | 6 +- .../CreateFolderModal/CreateFolderModal.tsx | 12 +-- .../DownloadProgressModals/DownloadBox.tsx | 21 ++-- .../src/Components/Pages/BucketsPage.tsx | 99 ++++++++++++----- .../src/Contexts/StorageContext.tsx | 102 +----------------- 5 files changed, 97 insertions(+), 143 deletions(-) diff --git a/packages/storage-ui/src/Components/Elements/BucketRow.tsx b/packages/storage-ui/src/Components/Elements/BucketRow.tsx index 44246db87a..33a5bdb37e 100644 --- a/packages/storage-ui/src/Components/Elements/BucketRow.tsx +++ b/packages/storage-ui/src/Components/Elements/BucketRow.tsx @@ -5,7 +5,8 @@ import { Trans } from "@lingui/macro" import { Bucket } from "@chainsafe/files-api-client" import { CSSTheme } from "../../Themes/types" import { useStorage } from "../../Contexts/StorageContext" -import { desktopGridSettings, mobileGridSettings } from "../Pages/BucketsPage" +import desktopGridSettings from "../Pages/BucketsPage" +import mobileGridSettings from "../Pages/BucketsPage" import { ROUTE_LINKS } from "../StorageRoutes" const useStyles = makeStyles(({ animation, constants, breakpoints }: CSSTheme) => @@ -51,7 +52,8 @@ const useStyles = makeStyles(({ animation, constants, breakpoints }: CSSTheme) = }, [breakpoints.down("md")]: { gridTemplateColumns: mobileGridSettings - } + }, + cursor: "pointer" } }) ) diff --git a/packages/storage-ui/src/Components/Modules/CreateFolderModal/CreateFolderModal.tsx b/packages/storage-ui/src/Components/Modules/CreateFolderModal/CreateFolderModal.tsx index ecbcc82c8b..2ac0d9a021 100644 --- a/packages/storage-ui/src/Components/Modules/CreateFolderModal/CreateFolderModal.tsx +++ b/packages/storage-ui/src/Components/Modules/CreateFolderModal/CreateFolderModal.tsx @@ -14,7 +14,7 @@ import React, { useRef, useEffect, useState } from "react" import { Formik, Form } from "formik" import CustomModal from "../../Elements/CustomModal" import CustomButton from "../../Elements/CustomButton" -import { Trans } from "@lingui/macro" +import { t, Trans } from "@lingui/macro" import { CSSTheme } from "../../../Themes/types" import { useFileBrowser } from "../../../Contexts/FileBrowserContext" import { useStorageApi } from "../../../Contexts/StorageApiContext" @@ -96,10 +96,10 @@ const CreateFolderModal: React.FC = ({ const folderNameValidator = yup.object().shape({ name: yup .string() - .required("Folder name is required") + .required(t`Folder name is required`) .test( "Invalid name", - "Folder name cannot contain '/' character", + t`Folder name cannot contain '/' character`, (val: string | null | undefined) => !!val && !val.includes("/") ) }) @@ -133,7 +133,7 @@ const CreateFolderModal: React.FC = ({ } catch (errors) { setCreatingFolder(false) if (errors[0].message.includes("Entry with such name can")) { - helpers.setFieldError("name", "Folder name is already in use") + helpers.setFieldError("name", t`Folder name is already in use`) } else { helpers.setFieldError("name", errors[0].message) } @@ -167,9 +167,9 @@ const CreateFolderModal: React.FC = ({ diff --git a/packages/storage-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx b/packages/storage-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx index 30938c73f0..8681687415 100644 --- a/packages/storage-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx +++ b/packages/storage-ui/src/Components/Modules/DownloadProgressModals/DownloadBox.tsx @@ -65,9 +65,9 @@ const DownloadBox: React.FC = ({ downloadInProgress }) => { return ( <>
- { - complete - ?
+ {complete + ? ( +
= ({ downloadInProgress }) => { Download complete
- : error - ?
+ ) : error + ? ( +
= ({ downloadInProgress }) => { {errorMessage}
- :
+ ) : ( +
- Downloading {fileName}... + Downloading {fileName}...
- } + )} +
+ + ) } export default DownloadBox diff --git a/packages/storage-ui/src/Components/Pages/BucketsPage.tsx b/packages/storage-ui/src/Components/Pages/BucketsPage.tsx index 463ec637ba..de1cfd033e 100644 --- a/packages/storage-ui/src/Components/Pages/BucketsPage.tsx +++ b/packages/storage-ui/src/Components/Pages/BucketsPage.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react" -import { makeStyles, createStyles } from "@chainsafe/common-theme" -import { Button, Table, TableBody, TableHead, TableHeadCell, TableRow, TextInput, Typography } from "@chainsafe/common-components" +import { makeStyles, createStyles, useThemeSwitcher } from "@chainsafe/common-theme" +import { Button, Grid, PlusIcon, Table, TableBody, TableHead, TableHeadCell, TableRow, TextInput, Typography } from "@chainsafe/common-components" import { CSSTheme } from "../../Themes/types" import { useStorage } from "../../Contexts/StorageContext" import { Trans } from "@lingui/macro" @@ -10,7 +10,7 @@ import CustomModal from "../Elements/CustomModal" export const desktopGridSettings = "3fr 190px 70px !important" export const mobileGridSettings = "3fr 190px 70px !important" -const useStyles = makeStyles(({ breakpoints, animation, constants }: CSSTheme) => +const useStyles = makeStyles(({ breakpoints, animation, constants, typography }: CSSTheme) => createStyles({ root: { position: "relative", @@ -79,6 +79,19 @@ const useStyles = makeStyles(({ breakpoints, animation, constants }: CSSTheme) = [breakpoints.down("md")]: { marginTop: constants.generalUnit } + }, + modalRoot: { + padding: constants.generalUnit * 4, + flexDirection: "column" + }, + input: { + marginBottom: constants.generalUnit * 2 + }, + heading: { + color: constants.createFolder.color, + fontWeight: typography.fontWeight.semibold, + textAlign: "center", + marginBottom: constants.generalUnit * 4 } }) ) @@ -88,6 +101,7 @@ const BucketsPage = () => { const { storageBuckets, createBucket } = useStorage() const [createBucketModalOpen, setCreateBucketOpen] = useState(false) const [newBucketName, setNewBucketName] = useState("") + const { desktop } = useThemeSwitcher() return (
@@ -98,14 +112,19 @@ const BucketsPage = () => {
- +
{ }} closePosition="none" > - Bucket Name - setNewBucketName(String(val))} /> -
- - -
+ Bucket Name + setNewBucketName(String(val))} /> + +
+ + +
+ ) diff --git a/packages/storage-ui/src/Contexts/StorageContext.tsx b/packages/storage-ui/src/Contexts/StorageContext.tsx index 30d115a342..cf4dd90084 100644 --- a/packages/storage-ui/src/Contexts/StorageContext.tsx +++ b/packages/storage-ui/src/Contexts/StorageContext.tsx @@ -66,7 +66,6 @@ interface IFileSystemItem extends FileContentResponse { type FileSystemItem = IFileSystemItem const REMOVE_UPLOAD_PROGRESS_DELAY = 5000 -const MAX_FILE_SIZE = 2 * 1024 ** 3 const StorageContext = React.createContext(undefined) @@ -74,7 +73,6 @@ const StorageProvider = ({ children }: StorageContextProps) => { const { storageApiClient, isLoggedIn } = useStorageApi() const [spaceUsed, setSpaceUsed] = useState(0) const [storageBuckets, setStorageBuckets] = useState([]) - const { addToastMessage } = useToaster() const [pins, setPins] = useState([]) const refreshPins = useCallback(() => { @@ -197,7 +195,6 @@ const StorageProvider = ({ children }: StorageContextProps) => { try { const filesParam = await Promise.all( files - .filter((f) => f.size <= MAX_FILE_SIZE) .map(async (f) => { const fileData = await readFileAsync(f) return { @@ -206,13 +203,7 @@ const StorageProvider = ({ children }: StorageContextProps) => { } }) ) - if (filesParam.length !== files.length) { - addToastMessage({ - message: - "We can't upload files larger than 2GB. Some items will not be uploaded", - appearance: "error" - }) - } + await storageApiClient.uploadBucketObjects( bucketId, filesParam, @@ -256,99 +247,14 @@ const StorageProvider = ({ children }: StorageContextProps) => { dispatchUploadsInProgress({ type: "remove", payload: { id } }) }, REMOVE_UPLOAD_PROGRESS_DELAY) } - }, [storageBuckets, addToastMessage, storageApiClient]) - - // const getPinContent = useCallback(async ( - // bucketId: string, - // { cid, cancelToken, onDownloadProgress, file, path }: GetFileContentParams - // ) => { - // const bucket = pins.find(b => b.id === bucketId) - - // if (!bucket) { - // throw new Error("No encryption key for this bucket found") - // } - - // if (!file) { - // console.error("No file passed, and no file found for cid:", cid, "in pathContents:", path) - // throw new Error("No file found.") - // } - - // try { - // const result = await storageApiClient.getFileContent( - // { - // path: path, - // source: { - // id: bucket.id - // } - // }, - // cancelToken, - // onDownloadProgress - // ) - - // return result.data - // } catch (error) { - // console.error(error) - // return Promise.reject() - // } - // }, [pins, storageApiClient]) - - // const downloadPin = useCallback(async (bucketId: string, itemToDownload: FileSystemItem, path: string) => { - // const toastId = uuidv4() - // try { - // const downloadProgress: DownloadProgress = { - // id: toastId, - // fileName: itemToDownload.name, - // complete: false, - // error: false, - // progress: 0 - // } - // dispatchDownloadsInProgress({ type: "add", payload: downloadProgress }) - // const result = await getPinContent(bucketId, { - // cid: itemToDownload.cid, - // file: itemToDownload, - // path: `${path}/${itemToDownload.name}`, - // onDownloadProgress: (progressEvent) => { - // dispatchDownloadsInProgress({ - // type: "progress", - // payload: { - // id: toastId, - // progress: Math.ceil( - // (progressEvent.loaded / itemToDownload.size) * 100 - // ) - // } - // }) - // } - // }) - // if (!result) return - // const link = document.createElement("a") - // link.href = URL.createObjectURL(result) - // link.download = itemToDownload?.name || "file" - // link.click() - // dispatchDownloadsInProgress({ - // type: "complete", - // payload: { id: toastId } - // }) - // URL.revokeObjectURL(link.href) - // setTimeout(() => { - // dispatchDownloadsInProgress({ - // type: "remove", - // payload: { id: toastId } - // }) - // }, REMOVE_UPLOAD_PROGRESS_DELAY) - // return Promise.resolve() - // } catch (error) { - // dispatchDownloadsInProgress({ type: "error", payload: { id: toastId } }) - // return Promise.reject() - // } - // }, [getPinContent]) + }, [storageBuckets, storageApiClient]) + + return ( Date: Fri, 18 Jun 2021 17:59:39 +0200 Subject: [PATCH 41/49] more cleaning up --- .../Modules/FilesList/FilesList.tsx | 282 ++++++++---------- 1 file changed, 122 insertions(+), 160 deletions(-) diff --git a/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx b/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx index c12bf18f70..5616e2a2cb 100644 --- a/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx +++ b/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx @@ -72,7 +72,6 @@ const useStyles = makeStyles( borderColor: palette.primary.main } } - // transitionDuration: `${animation.transform}ms`, }, header: { display: "flex", @@ -358,27 +357,6 @@ const FilesList = () => { } } - // Previews - // const setNextPreview = () => { - // if ( - // files && - // previewFileIndex !== undefined && - // previewFileIndex < files.length - 1 - // ) { - // setPreviewFileIndex(previewFileIndex + 1) - // } - // } - - // const setPreviousPreview = () => { - // if (files && previewFileIndex !== undefined && previewFileIndex > 0) { - // setPreviewFileIndex(previewFileIndex - 1) - // } - // } - - // const clearPreview = () => { - // setPreviewFileIndex(undefined) - // } - // Selection logic const handleSelectCid = useCallback( (cid: string) => { @@ -418,7 +396,7 @@ const FilesList = () => { .min(1, t`Please enter a name`) .max(65, t`Name too long`) .test( - t`Invalid name`, + "Invalid name", t`Name cannot contain '/' character`, (val: string | null | undefined) => !invalidFilenameRegex.test(val || "") @@ -591,10 +569,6 @@ const FilesList = () => { Drop to upload files - {/* */}
{crumbs && moduleRootPath ? ( {
{controls && desktop ? ( <> -
-
{selectedCids.length > 0 && ( <> @@ -759,77 +722,76 @@ const FilesList = () => { One sec, getting files ready...
- {(desktop && items.length === 0) || - (!desktop && items.length === 0 /*&& uploadsInProgress?.length === 0 */) ? ( -
- - - No files to show - -
- ) : browserView === "table" ? ( -
- {desktop && ( - - - - toggleAll()} - /> - - - {/* + {(items.length === 0) ? ( +
+ + + No files to show + +
+ ) : browserView === "table" ? ( +
+ {desktop && ( + + + + toggleAll()} + /> + + + {/* Icon */} - - handleSortToggle("name")} - sortDirection={column === "name" ? direction : undefined} - sortActive={column === "name"} - > - Name - - handleSortToggle("date_uploaded")} - sortDirection={ - column === "date_uploaded" ? direction : undefined - } - sortActive={column === "date_uploaded"} - > - Date uploaded - - handleSortToggle("size")} - sortDirection={column === "size" ? direction : undefined} - sortActive={column === "size"} - > - Size - - {/* Menu */} - - - )} - - {!desktop && + + handleSortToggle("name")} + sortDirection={column === "name" ? direction : undefined} + sortActive={column === "name"} + > + Name + + handleSortToggle("date_uploaded")} + sortDirection={ + column === "date_uploaded" ? direction : undefined + } + sortActive={column === "date_uploaded"} + > + Date uploaded + + handleSortToggle("size")} + sortDirection={column === "size" ? direction : undefined} + sortActive={column === "size"} + > + Size + + {/* Menu */} + + + )} + + {!desktop && showUploadsInTable && uploadsInProgress ?.filter( @@ -859,53 +821,6 @@ const FilesList = () => { ))} - {items.map((file, index) => ( - { - handleRename && (await handleRename(cid, newName)) - setEditing(undefined) - }} - deleteFile={() => { - setSelectedCids([file.cid]) - setIsDeleteModalOpen(true) - }} - viewFolder={handleViewFolder} - setPreviewFileIndex={setPreviewFileIndex} - moveFile={() => { - setSelectedCids([file.cid]) - setIsMoveFileModalOpen(true) - setMoveModalMode("move") - }} - setFileInfoPath={setFileInfoPath} - itemOperations={getItemOperations(file.content_type)} - resetSelectedFiles={resetSelectedCids} - browserView="table" - recoverFile={() => { - setSelectedCids([file.cid]) - setIsMoveFileModalOpen(true) - setMoveModalMode("recover") - }} - /> - ))} - -
- ) : ( -
{items.map((file, index) => ( { files={files} selected={selectedCids} handleSelectCid={handleSelectCid} - viewFolder={handleViewFolder} handleAddToSelectedCids={handleAddToSelectedCids} 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={() => { setSelectedCids([file.cid]) setIsDeleteModalOpen(true) }} + viewFolder={handleViewFolder} setPreviewFileIndex={setPreviewFileIndex} moveFile={() => { setSelectedCids([file.cid]) @@ -936,16 +851,63 @@ const FilesList = () => { setFileInfoPath={setFileInfoPath} itemOperations={getItemOperations(file.content_type)} resetSelectedFiles={resetSelectedCids} + browserView="table" recoverFile={() => { setSelectedCids([file.cid]) setIsMoveFileModalOpen(true) setMoveModalMode("recover") }} - browserView="grid" /> ))} -
- )} + + + ) : ( +
+ {items.map((file, index) => ( + { + handleRename && (await handleRename(path, newPath)) + setEditing(undefined) + }} + deleteFile={() => { + setSelectedCids([file.cid]) + setIsDeleteModalOpen(true) + }} + setPreviewFileIndex={setPreviewFileIndex} + moveFile={() => { + setSelectedCids([file.cid]) + setIsMoveFileModalOpen(true) + setMoveModalMode("move") + }} + setFileInfoPath={setFileInfoPath} + itemOperations={getItemOperations(file.content_type)} + resetSelectedFiles={resetSelectedCids} + recoverFile={() => { + setSelectedCids([file.cid]) + setIsMoveFileModalOpen(true) + setMoveModalMode("recover") + }} + browserView="grid" + /> + ))} +
+ )} {/* {files && previewFileIndex !== undefined && bucket && ( Date: Fri, 18 Jun 2021 18:08:27 +0200 Subject: [PATCH 42/49] clean up unused imports --- .../src/Components/Pages/BucketsPage.tsx | 16 +++++++++++++--- .../storage-ui/src/Contexts/StorageContext.tsx | 4 ---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/storage-ui/src/Components/Pages/BucketsPage.tsx b/packages/storage-ui/src/Components/Pages/BucketsPage.tsx index de1cfd033e..1599bf4216 100644 --- a/packages/storage-ui/src/Components/Pages/BucketsPage.tsx +++ b/packages/storage-ui/src/Components/Pages/BucketsPage.tsx @@ -1,6 +1,17 @@ import React, { useState } from "react" -import { makeStyles, createStyles, useThemeSwitcher } from "@chainsafe/common-theme" -import { Button, Grid, PlusIcon, Table, TableBody, TableHead, TableHeadCell, TableRow, TextInput, Typography } from "@chainsafe/common-components" +import { makeStyles, createStyles } from "@chainsafe/common-theme" +import { + Button, + Grid, + PlusIcon, + Table, + TableBody, + TableHead, + TableHeadCell, + TableRow, + TextInput, + Typography +} from "@chainsafe/common-components" import { CSSTheme } from "../../Themes/types" import { useStorage } from "../../Contexts/StorageContext" import { Trans } from "@lingui/macro" @@ -101,7 +112,6 @@ const BucketsPage = () => { const { storageBuckets, createBucket } = useStorage() const [createBucketModalOpen, setCreateBucketOpen] = useState(false) const [newBucketName, setNewBucketName] = useState("") - const { desktop } = useThemeSwitcher() return (
diff --git a/packages/storage-ui/src/Contexts/StorageContext.tsx b/packages/storage-ui/src/Contexts/StorageContext.tsx index cf4dd90084..c60b9a2b15 100644 --- a/packages/storage-ui/src/Contexts/StorageContext.tsx +++ b/packages/storage-ui/src/Contexts/StorageContext.tsx @@ -16,7 +16,6 @@ import { useStorageApi } from "./StorageApiContext" import { v4 as uuidv4 } from "uuid" import { t } from "@lingui/macro" import { readFileAsync } from "../Utils/Helpers" -import { useToaster } from "@chainsafe/common-components" type StorageContextProps = { children: React.ReactNode | React.ReactNode[] @@ -49,9 +48,6 @@ type StorageContext = { spaceUsed: number uploadFiles: (bucketId: string, files: File[], path: string) => Promise addPin: (cid: string) => Promise - // createPin: (bucketId: string, files: File[], path: string) => Promise - // downloadPin: (bucketId: string, itemToDownload: FileSystemItem, path: string) => void - // getPinContent: (bucketId: string, params: GetFileContentParams) => Promise refreshPins: () => void unpin: (requestId: string) => void storageBuckets: Bucket[] From f60926c0a2fdff390d8b28ee0406b3b354966a21 Mon Sep 17 00:00:00 2001 From: Thibaut Sardan Date: Fri, 18 Jun 2021 18:19:31 +0200 Subject: [PATCH 43/49] show the failed pins --- packages/storage-ui/src/Contexts/StorageContext.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/storage-ui/src/Contexts/StorageContext.tsx b/packages/storage-ui/src/Contexts/StorageContext.tsx index 1b346c64a8..de595192b0 100644 --- a/packages/storage-ui/src/Contexts/StorageContext.tsx +++ b/packages/storage-ui/src/Contexts/StorageContext.tsx @@ -78,7 +78,7 @@ const StorageProvider = ({ children }: StorageContextProps) => { const [pins, setPins] = useState([]) const refreshPins = useCallback(() => { - storageApiClient.listPins(undefined, undefined, ["queued", "pinning", "pinned"]) + storageApiClient.listPins(undefined, undefined, ["queued", "pinning", "pinned", "failed"]) .then((pins) => setPins(pins.results || [])) .catch(console.error) }, [storageApiClient]) From 3ff88e2644121a3b7a96390dc11c53060dfd7a58 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 18 Jun 2021 18:19:39 +0200 Subject: [PATCH 44/49] add pinning bucket to size calc --- packages/storage-ui/src/Contexts/StorageContext.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/storage-ui/src/Contexts/StorageContext.tsx b/packages/storage-ui/src/Contexts/StorageContext.tsx index c60b9a2b15..6bacb54e15 100644 --- a/packages/storage-ui/src/Contexts/StorageContext.tsx +++ b/packages/storage-ui/src/Contexts/StorageContext.tsx @@ -79,7 +79,7 @@ const StorageProvider = ({ children }: StorageContextProps) => { const refreshBuckets = useCallback(() => { storageApiClient.listBuckets() - .then((buckets) => setStorageBuckets(buckets.filter(b => b.type === "fps"))) + .then((buckets) => setStorageBuckets(buckets.filter(b => b.type === "fps" || b.type === "pinning"))) .catch(console.error) }, [storageApiClient]) From 277f6dc385454822d57c991c5a1f8ce0e50c6403 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 18 Jun 2021 18:22:54 +0200 Subject: [PATCH 45/49] Hide facebook login --- .../Components/Modules/LoginModule/InitialScreen.tsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/storage-ui/src/Components/Modules/LoginModule/InitialScreen.tsx b/packages/storage-ui/src/Components/Modules/LoginModule/InitialScreen.tsx index 6e73bd3200..419c882f53 100644 --- a/packages/storage-ui/src/Components/Modules/LoginModule/InitialScreen.tsx +++ b/packages/storage-ui/src/Components/Modules/LoginModule/InitialScreen.tsx @@ -292,17 +292,6 @@ const InitialScreen = ({ className }: IInitialScreen) => { Continue with Google -
From aae17d1c68710541d5373bc1c1f0d42cdfff9aed Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 18 Jun 2021 20:31:57 +0200 Subject: [PATCH 49/49] rename to Storage --- packages/storage-ui/src/Components/Pages/LoginPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/storage-ui/src/Components/Pages/LoginPage.tsx b/packages/storage-ui/src/Components/Pages/LoginPage.tsx index 6a43cf859b..4ecd2fb861 100644 --- a/packages/storage-ui/src/Components/Pages/LoginPage.tsx +++ b/packages/storage-ui/src/Components/Pages/LoginPage.tsx @@ -128,7 +128,7 @@ const LoginPage = () => {
- ChainSafe Storage + Storage <>