diff --git a/packages/files-ui/src/Components/Elements/RestrictedModeBanner.tsx b/packages/files-ui/src/Components/Elements/RestrictedModeBanner.tsx new file mode 100644 index 0000000000..c1b6bbc39f --- /dev/null +++ b/packages/files-ui/src/Components/Elements/RestrictedModeBanner.tsx @@ -0,0 +1,49 @@ +import { Typography, Button, useHistory } from "@chainsafe/common-components" +import { createStyles, makeStyles, useThemeSwitcher } from "@chainsafe/common-theme" +import { Trans } from "@lingui/macro" +import React from "react" +import { CSFTheme } from "../../Themes/types" +import { ROUTE_LINKS } from "../FilesRoutes" + +const useStyles = makeStyles( + ({ breakpoints, constants, palette }: CSFTheme) => { + return createStyles({ + accountRestrictedNotification: { + position: "fixed", + bottom: 0, + backgroundColor: palette.additional["gray"][10], + color: palette.additional["gray"][1], + padding: `${constants.generalUnit * 2}px ${constants.generalUnit * 3}px`, + left: 0, + width: "100vw", + [breakpoints.up("md")]: { + left: `${constants.navWidth}px`, + width:`calc(100vw - ${constants.navWidth}px)`, + display: "flex", + justifyContent: "space-between", + alignItems: "center" + } + } + }) + } +) + +const RestrictedModeBanner = () => { + const classes = useStyles() + const { desktop } = useThemeSwitcher() + const { redirect } = useHistory() + + return ( +
+ + You've got a payment due. Until you've settled up, we've placed your account in restricted mode + + +
) +} + +export default RestrictedModeBanner \ No newline at end of file diff --git a/packages/files-ui/src/Components/Layouts/AppWrapper.tsx b/packages/files-ui/src/Components/Layouts/AppWrapper.tsx index e9da33b95c..90d6e5891a 100644 --- a/packages/files-ui/src/Components/Layouts/AppWrapper.tsx +++ b/packages/files-ui/src/Components/Layouts/AppWrapper.tsx @@ -24,7 +24,6 @@ const useStyles = makeStyles( padding: "0", "&.active": { // This moves the content areas based on the size of the nav bar - padding: `${0}px ${constants.contentPadding}px ${0}px ${ Number(constants.navWidth) + Number(constants.contentPadding) @@ -34,20 +33,18 @@ const useStyles = makeStyles( [breakpoints.down("md")]: {} }, content: { + height: "initial", [breakpoints.up("md")]: { height: "100%", minHeight: "100vh", transitionDuration: `${animation.translate}ms`, - padding: 0, "&.active": { - height: "initial", padding: `${constants.contentTopPadding}px 0 0` } }, [breakpoints.down("md")]: { minHeight: "100vh", "&.active": { - height: "initial", padding: `${constants.mobileHeaderHeight}px 0 0` } } @@ -84,14 +81,16 @@ const AppWrapper: React.FC = ({ children }: IAppWrapper) => { setNavOpen={setNavOpen} />
{children}
diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx index 1727cbb873..56f6d88d0f 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx @@ -25,6 +25,7 @@ import getFilesFromDataTransferItems from "../../../Utils/getFilesFromDataTransf const CSFFileBrowser: React.FC = () => { const { downloadFile, uploadFiles, buckets } = useFiles() + const { accountRestricted } = useFilesApi() const { filesApiClient } = useFilesApi() const { addToast } = useToasts() const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) @@ -92,12 +93,11 @@ const CSFFileBrowser: React.FC = () => { const message = `${ itemToDelete.isFolder ? t`Folder` : t`File` } ${t`deleted successfully`}` - const id = addToast({ + addToast({ title: message, type: "success", testId: "deletion-success" }) - console.log(id) } return Promise.resolve() } catch (error) { @@ -176,12 +176,20 @@ const CSFFileBrowser: React.FC = () => { const handleUploadOnDrop = useCallback(async (files: File[], fileItems: DataTransferItemList, path: string) => { if (!bucket) return + if (accountRestricted) { + addToast({ + type:"error", + title: t`Uploads disabled`, + subtitle: t`Oops! You need to pay for this month to upload more content.` + }) + return + } const flattenedFiles = await getFilesFromDataTransferItems(fileItems) const paths = [...new Set(flattenedFiles.map(f => f.filepath))] paths.forEach(p => { uploadFiles(bucket, flattenedFiles.filter(f => f.filepath === p), getPathWithFile(path, p)) }) - }, [uploadFiles, bucket]) + }, [uploadFiles, bucket, accountRestricted, addToast]) const viewFolder = useCallback((cid: string) => { const fileSystemItem = pathContents.find(f => f.cid === cid) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx index 7b7bc01bc5..090032121f 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx @@ -14,6 +14,7 @@ import { useFileBrowser } from "../../../Contexts/FileBrowserContext" import clsx from "clsx" import { useEffect } from "react" import { nameValidator } from "../../../Utils/validationSchema" +import { useFilesApi } from "../../../Contexts/FilesApiContext" const useStyles = makeStyles( ({ breakpoints, constants, palette, typography, zIndex }: CSFTheme) => { @@ -188,6 +189,7 @@ interface IShareFileProps { const ShareModal = ({ close, file, filePath }: IShareFileProps) => { const classes = useStyles() const { handleCreateSharedFolder } = useCreateOrEditSharedFolder() + const { accountRestricted } = useFilesApi() const [sharedFolderName, setSharedFolderName] = useState("") const { sharedFolderReaders, sharedFolderWriters, handleLookupUser, onNewUsers, usersError, resetUsers } = useLookupSharedFolderUser() const [isUsingCurrentBucket, setIsUsingCurrentBucket] = useState(true) @@ -213,14 +215,16 @@ const ShareModal = ({ close, file, filePath }: IShareFileProps) => { .filter(buck => buck.type === "share" || buck.type === "csf") // filter out the current bucket .filter(buck => buck.id !== bucket?.id) - // all buckets where the user is reader or writer + // all buckets where the user is owner or writer .filter(buck => !!buck.writers.find((w) => w.uuid === profile.userId) || !!buck.owners.find((o) => o.uuid === profile.userId)) + // filter out CSF and share buckets where user is an owner if their account is restricted + .filter(buck => !(!!accountRestricted && (buck.type === "csf" || !!buck.owners.find(o => o.uuid === profile.userId)))) .map(buck => ({ label: buck.name || t`Home`, value: buck.id })) } - , [bucket, buckets, profile]) + , [bucket, buckets, profile, accountRestricted]) const hasNoSharedBucket = useMemo(() => bucketsOptions.length === 0, [bucketsOptions.length]) @@ -232,10 +236,10 @@ const ShareModal = ({ close, file, filePath }: IShareFileProps) => { // if the user has no shared bucket, we default to new folder creation useEffect(() => { - if (hasNoSharedBucket) { + if (hasNoSharedBucket && !accountRestricted) { setIsUsingCurrentBucket(false) } - }, [hasNoSharedBucket]) + }, [hasNoSharedBucket, accountRestricted]) const onNameChange = useCallback((value?: string | number) => { if (value === undefined) return diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFileBrowser.tsx index 2697dd1b98..54523d8fc6 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFileBrowser.tsx @@ -18,10 +18,11 @@ import { useFilesApi } from "../../../Contexts/FilesApiContext" import { useUser } from "../../../Contexts/UserContext" import DragAndDrop from "../../../Contexts/DnDContext" import FilesList from "./views/FilesList" +import getFilesFromDataTransferItems from "../../../Utils/getFilesFromDataTransferItems" const SharedFileBrowser = () => { const { downloadFile, uploadFiles, buckets, refreshBuckets, getStorageSummary } = useFiles() - const { filesApiClient } = useFilesApi() + const { filesApiClient, accountRestricted } = useFilesApi() const { addToast } = useToasts() const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) const [pathContents, setPathContents] = useState([]) @@ -179,23 +180,20 @@ const SharedFileBrowser = () => { 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) { + if (accountRestricted) { addToast({ - title: t`Folder uploads are not supported currently`, - type: "error" + type:"error", + title: t`Unable to upload`, + subtitle: t`Oops! You need to pay for this month to upload more content.` }) - } else { - uploadFiles(bucket, files, path) - .then(() => refreshContents()) - .catch(console.error) + return } - }, [addToast, uploadFiles, bucket, refreshContents]) + const flattenedFiles = await getFilesFromDataTransferItems(fileItems) + const paths = [...new Set(flattenedFiles.map(f => f.filepath))] + paths.forEach(p => { + uploadFiles(bucket, flattenedFiles.filter(f => f.filepath === p), getPathWithFile(path, p)) + }) + }, [uploadFiles, bucket, accountRestricted, addToast]) const bulkOperations: IBulkOperations = useMemo(() => ({ [CONTENT_TYPES.Directory]: ["download", "move", "delete"], diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx index 3e6f46d8b9..033606c183 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx @@ -26,6 +26,7 @@ import { SharedFolderModalMode } from "./types" import SharingExplainerModal from "../../SharingExplainerModal" import { useSharingExplainerModalFlag } from "./hooks/useSharingExplainerModalFlag" import { usePageTrack } from "../../../Contexts/PosthogContext" +import RestrictedModeBanner from "../../Elements/RestrictedModeBanner" export const desktopSharedGridSettings = "69px 3fr 120px 190px 150px 45px !important" export const mobileSharedGridSettings = "3fr 80px 45px !important" @@ -38,7 +39,10 @@ const useStyles = makeStyles( position: "relative", [breakpoints.down("md")]: { marginLeft: constants.generalUnit * 2, - marginRight: constants.generalUnit * 2 + marginRight: constants.generalUnit * 2, + "&.bottomBanner": { + paddingBottom: constants.bottomBannerMobileHeight + } }, [breakpoints.up("md")]: { border: "1px solid transparent", @@ -47,6 +51,10 @@ const useStyles = makeStyles( minHeight: `calc(100vh - ${Number(constants.contentTopPadding)}px)`, "&.droppable": { borderColor: palette.additional["geekblue"][4] + }, + "&.bottomBanner": { + minHeight: `calc(100vh - ${Number(constants.contentTopPadding) + Number(constants.bottomBannerHeight)}px)`, + paddingBottom: constants.bottomBannerHeight } } }, @@ -108,7 +116,7 @@ type SortingType = "name" | "size" | "date_uploaded" const SharedFolderOverview = () => { const classes = useStyles() - const { filesApiClient } = useFilesApi() + const { filesApiClient, accountRestricted } = useFilesApi() const { buckets, isLoadingBuckets, refreshBuckets } = useFiles() const [createOrEditSharedFolderMode, setCreateOrEditSharedFolderMode] = useState(undefined) const [bucketToEdit, setBucketToEdit] = useState(undefined) @@ -160,10 +168,13 @@ const SharedFolderOverview = () => { const openSharedFolder = useCallback((bucketId: string) => { redirect(ROUTE_LINKS.SharedFolderExplorer(bucketId, "/")) }, [redirect]) + return ( <>
{ setBucketToEdit(undefined) setCreateOrEditSharedFolderMode("create") }} + disabled={accountRestricted} > Create a Shared Folder @@ -294,6 +306,9 @@ const SharedFolderOverview = () => { injectedClass={{ inner: classes.confirmDeletionDialog }} testId="shared-folder-deletion" /> + {accountRestricted && + + } ) } 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 e46746314f..b2e7da0af3 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx @@ -53,6 +53,8 @@ import SharedUsers from "../../../Elements/SharedUsers" import Menu from "../../../../UI-components/Menu" import SharingExplainerModal from "../../../SharingExplainerModal" import { useSharingExplainerModalFlag } from "../hooks/useSharingExplainerModalFlag" +import { useFilesApi } from "../../../../Contexts/FilesApiContext" +import RestrictedModeBanner from "../../../Elements/RestrictedModeBanner" const baseOperations: FileOperation[] = ["download", "info", "preview", "share"] const readerOperations: FileOperation[] = [...baseOperations, "report"] @@ -73,7 +75,10 @@ const useStyles = makeStyles( position: "relative", [breakpoints.down("md")]: { marginLeft: constants.generalUnit * 2, - marginRight: constants.generalUnit * 2 + marginRight: constants.generalUnit * 2, + "&.bottomBanner": { + paddingBottom: constants.bottomBannerMobileHeight + } }, [breakpoints.up("md")]: { border: "1px solid transparent", @@ -82,6 +87,10 @@ const useStyles = makeStyles( minHeight: `calc(100vh - ${Number(constants.contentTopPadding)}px)`, "&.droppable": { borderColor: palette.primary.main + }, + "&.bottomBanner": { + minHeight: `calc(100vh - ${Number(constants.contentTopPadding) + Number(constants.bottomBannerHeight)}px)`, + paddingBottom: constants.bottomBannerHeight } } }, @@ -312,6 +321,7 @@ const FilesList = ({ isShared = false }: Props) => { const [isReportFileModalOpen, setIsReportFileModalOpen] = useState(false) const [isFileInfoModalOpen, setIsFileInfoModalOpen] = useState(false) const [isShareModalOpen, setIsShareModalOpen] = useState(false) + const { accountRestricted } = useFilesApi() const { heading, @@ -626,7 +636,8 @@ const FilesList = ({ isShared = false }: Props) => { ), - onClick: () => setCreateFolderModalOpen(true) + onClick: () => setCreateFolderModalOpen(true), + disabled: accountRestricted }, { contents: ( @@ -637,10 +648,11 @@ const FilesList = ({ isShared = false }: Props) => { ), - onClick: () => setIsUploadModalOpen(true) + onClick: () => setIsUploadModalOpen(true), + disabled: accountRestricted } ], - [classes.menuIcon]) + [classes.menuIcon, accountRestricted]) const onShare = useCallback((fileInfoPath: string, fileIndex: number) => { if(hasSeenSharingExplainerModal) { @@ -653,95 +665,58 @@ const FilesList = ({ isShared = false }: Props) => { }, [hasSeenSharingExplainerModal]) return ( -
-
+
- - Drop to upload files - -
- -
- {crumbs && moduleRootPath && ( - redirect(moduleRootPath)} - showDropDown={!desktop} - /> - )} -
-
- + Drop to upload files + + + +
- {heading} - - {isShared && bucket && ( -
- -
- )} -
- {controls && desktop ? ( - <> - - { - permission !== "reader" && ( - <> - - - - ) - } - - ) : ( - controls && !desktop && ( + {crumbs && moduleRootPath && ( + redirect(moduleRootPath)} + showDropDown={!desktop} + /> + )} +
+
+ + {heading} + + {isShared && bucket && ( +
+ +
+ )} +
+ {controls && desktop ? ( <> - } - options={mobileMenuItems} - style={{ focusVisible: classes.focusVisible }} - /> + { + permission !== "reader" && ( + <> + + + + ) + } - ) - )} -
-
- { withSurvey && !isShared && isSurveyBannerVisible - ? - : - } - -
- {selectedItems.length > 0 && ( - <> - {validBulkOps.includes("download") && (selectedItems.length > 1 || selectionContainsAFolder) && ( - - )} - {validBulkOps.includes("move") && ( - - )} - {validBulkOps.includes("recover") && ( - - )} - {validBulkOps.includes("delete") && ( - + ) : ( + controls && !desktop && ( + <> + + } + options={mobileMenuItems} + style={{ focusVisible: classes.focusVisible }} + /> + + ) )} - - )} -
-
- - +
+ { withSurvey && !isShared && isSurveyBannerVisible + ? + : + } + +
+ {selectedItems.length > 0 && ( + <> + {validBulkOps.includes("download") && (selectedItems.length > 1 || selectionContainsAFolder) && ( + + )} + {validBulkOps.includes("move") && ( + + )} + {validBulkOps.includes("recover") && ( + + )} + {validBulkOps.includes("delete") && ( + + )} + + )} +
+
- One sec, getting files ready… - -
- {!items.length - ? ( -
+ - - - No files to show - -
- ) - : browserView === "table" + One sec, getting files ready… + + + {!items.length ? ( - - {desktop && ( - - - - toggleAll()} - testId="select-all" - /> - - - {/* Icon */} - - handleSortToggle("name")} - sortDirection={column === "name" ? direction : undefined} - sortActive={column === "name"} - > - Name - - handleSortToggle("date_uploaded")} - sortDirection={ - column === "date_uploaded" ? direction : undefined + + + No files to show + + + ) + : browserView === "table" + ? ( +
+ {desktop && ( + + + + toggleAll()} + testId="select-all" + /> + + + {/* 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 */} + + + )} + + {items.map((file, index) => ( + { + handleRename && (await handleRename(cid, newName)) + setEditing(undefined) + }} + deleteFile={() => { + setSelectedItems([file]) + setIsDeleteModalOpen(true) + }} + viewFolder={handleViewFolder} + moveFile={() => { + setSelectedItems([file]) + setIsMoveFileModalOpen(true) + setMoveModalMode("move") + }} + itemOperations={getItemOperations(file.content_type)} + resetSelectedFiles={resetSelectedItems} + browserView="table" + recoverFile={() => { + setSelectedItems([file]) + setIsMoveFileModalOpen(true) + setMoveModalMode("recover") + }} + reportFile={(filePath: string) => { + setFilePath(filePath) + setIsReportFileModalOpen(true)} } - sortActive={column === "date_uploaded"} - > - Date uploaded - - handleSortToggle("size")} - sortDirection={column === "size" ? direction : undefined} - sortActive={column === "size"} - > - Size - - {/* Menu */} - - - )} - + showFileInfo={(filePath: string) => { + setFilePath(filePath) + setIsFileInfoModalOpen(true) + }} + showPreview={(fileIndex: number) => { + setFileIndex(fileIndex) + setIsPreviewOpen(true) + }} + share={onShare} + /> + ))} + +
+ ) + : ( +
{items.map((file, index) => ( { files={files} selectedCids={selectedCids} handleSelectItem={handleSelectItem} + viewFolder={handleViewFolder} handleAddToSelectedItems={handleAddToSelectedItems} editing={editing} setEditing={setEditing} - handleRename={async (cid: string, newName: string) => { - handleRename && (await handleRename(cid, newName)) + handleRename={async (path: string, newPath: string) => { + handleRename && (await handleRename(path, newPath)) setEditing(undefined) }} deleteFile={() => { setSelectedItems([file]) setIsDeleteModalOpen(true) }} - viewFolder={handleViewFolder} moveFile={() => { setSelectedItems([file]) setIsMoveFileModalOpen(true) @@ -936,146 +1012,89 @@ const FilesList = ({ isShared = false }: Props) => { }} itemOperations={getItemOperations(file.content_type)} resetSelectedFiles={resetSelectedItems} - browserView="table" recoverFile={() => { setSelectedItems([file]) setIsMoveFileModalOpen(true) setMoveModalMode("recover") }} - reportFile={(filePath: string) => { - setFilePath(filePath) + browserView="grid" + reportFile={(fileInfoPath: string) => { + setFilePath(fileInfoPath) setIsReportFileModalOpen(true)} } - showFileInfo={(filePath: string) => { - setFilePath(filePath) + showFileInfo={(fileInfoPath: string) => { + setFilePath(fileInfoPath) setIsFileInfoModalOpen(true) }} + share={onShare} showPreview={(fileIndex: number) => { setFileIndex(fileIndex) setIsPreviewOpen(true) }} - share={onShare} /> ))} - - - ) - : ( -
- {items.map((file, index) => ( - { - handleRename && (await handleRename(path, newPath)) - setEditing(undefined) - }} - deleteFile={() => { - setSelectedItems([file]) - setIsDeleteModalOpen(true) - }} - moveFile={() => { - setSelectedItems([file]) - setIsMoveFileModalOpen(true) - setMoveModalMode("move") - }} - itemOperations={getItemOperations(file.content_type)} - resetSelectedFiles={resetSelectedItems} - recoverFile={() => { - setSelectedItems([file]) - setIsMoveFileModalOpen(true) - setMoveModalMode("recover") - }} - browserView="grid" - reportFile={(fileInfoPath: string) => { - setFilePath(fileInfoPath) - setIsReportFileModalOpen(true)} - } - showFileInfo={(fileInfoPath: string) => { - setFilePath(fileInfoPath) - setIsFileInfoModalOpen(true) +
+ )} + setIsDeleteModalOpen(false)} + accept={handleDeleteFiles} + requestMessage={ + plural(selectedCids.length, { + one: `You are about to delete ${selectedCids.length} item.`, + other: `You are about to delete ${selectedCids.length} items.` + }) + } + rejectText = {t`Cancel`} + acceptText = {t`Confirm`} + acceptButtonProps={{ loading: isDeletingFiles, disabled: isDeletingFiles, testId: "confirm-deletion" }} + rejectButtonProps={{ disabled: isDeletingFiles, testId: "cancel-deletion" }} + injectedClass={{ inner: classes.confirmDeletionDialog }} + onModalBodyClick={(e) => { + e.preventDefault() + e.stopPropagation() + }} + testId="file-deletion" + /> + { + refreshContents && ( + <> + setCreateFolderModalOpen(false)} + /> + setIsUploadModalOpen(false)} + /> + {isMoveFileModalOpen && ( + { + setIsMoveFileModalOpen(false) + setSelectedItems([]) + setMoveModalMode(undefined) }} - share={onShare} - showPreview={(fileIndex: number) => { - setFileIndex(fileIndex) - setIsPreviewOpen(true) + onCancel={() => { + setIsMoveFileModalOpen(false) + setMoveModalMode(undefined) }} + mode={moveModalMode} /> - ))} -
- )} - setIsDeleteModalOpen(false)} - accept={handleDeleteFiles} - requestMessage={ - plural(selectedCids.length, { - one: `You are about to delete ${selectedCids.length} item.`, - other: `You are about to delete ${selectedCids.length} items.` - }) + )} + + ) } - rejectText = {t`Cancel`} - acceptText = {t`Confirm`} - acceptButtonProps={{ loading: isDeletingFiles, disabled: isDeletingFiles, testId: "confirm-deletion" }} - rejectButtonProps={{ disabled: isDeletingFiles, testId: "cancel-deletion" }} - injectedClass={{ inner: classes.confirmDeletionDialog }} - onModalBodyClick={(e) => { - e.preventDefault() - e.stopPropagation() - }} - testId="file-deletion" - /> - { - refreshContents && ( - <> - setCreateFolderModalOpen(false)} - /> - setIsUploadModalOpen(false)} - /> - {isMoveFileModalOpen && ( - { - setIsMoveFileModalOpen(false) - setSelectedItems([]) - setMoveModalMode(undefined) - }} - onCancel={() => { - setIsMoveFileModalOpen(false) - setMoveModalMode(undefined) - }} - mode={moveModalMode} - /> - )} - - ) - } - {isPreviewOpen && files.length && fileIndex !== undefined && ( - 0 ? setPreviousPreview : undefined} - filePath={isSearch && getPath ? getPath(files[fileIndex].cid) : getPathWithFile(currentPath, files[fileIndex].name)} - /> - )} - { filePath && isReportFileModalOpen && + {isPreviewOpen && files.length && fileIndex !== undefined && ( + 0 ? setPreviousPreview : undefined} + filePath={isSearch && getPath ? getPath(files[fileIndex].cid) : getPathWithFile(currentPath, files[fileIndex].name)} + /> + )} + { filePath && isReportFileModalOpen && { @@ -1083,8 +1102,8 @@ const FilesList = ({ isShared = false }: Props) => { setFilePath(undefined) }} /> - } - { filePath && isFileInfoModalOpen && + } + { filePath && isFileInfoModalOpen && { @@ -1092,8 +1111,8 @@ const FilesList = ({ isShared = false }: Props) => { setFilePath(undefined) }} /> - } - { !showExplainerBeforeShare && isShareModalOpen && filePath && fileIndex !== undefined && + } + { !showExplainerBeforeShare && isShareModalOpen && filePath && fileIndex !== undefined && { @@ -1102,12 +1121,16 @@ const FilesList = ({ isShared = false }: Props) => { }} filePath={currentPath} /> + } + +
+ {accountRestricted && + } - -
+ ) } diff --git a/packages/files-ui/src/Contexts/FilesApiContext.tsx b/packages/files-ui/src/Contexts/FilesApiContext.tsx index c438838790..c8f69c6456 100644 --- a/packages/files-ui/src/Contexts/FilesApiContext.tsx +++ b/packages/files-ui/src/Contexts/FilesApiContext.tsx @@ -34,6 +34,7 @@ type FilesApiContext = { validateMasterPassword: (candidatePassword: string) => Promise encryptedEncryptionKey?: string isMasterPasswordSet: boolean + accountRestricted?: boolean } const FilesApiContext = React.createContext(undefined) @@ -62,6 +63,7 @@ const FilesApiProvider = ({ apiUrl, withLocalStorage = true, children }: FilesAp // access tokens const [accessToken, setAccessToken] = useState(undefined) const [secured, setSecured] = useState(undefined) + const [accountRestricted, setAccountRestricted] = useState(false) const [refreshToken, setRefreshToken] = useState(undefined) const [decodedRefreshToken, setDecodedRefreshToken] = useState< { exp: number; enckey?: string; mps?: string; uuid: string } | undefined @@ -216,7 +218,7 @@ const FilesApiProvider = ({ apiUrl, withLocalStorage = true, children }: FilesAp useEffect(() => { if (accessToken && accessToken.token && filesApiClient) { filesApiClient?.setToken(accessToken.token) - const decodedAccessToken = jwtDecode<{ perm: { secured?: string } }>( + const decodedAccessToken = jwtDecode<{ perm: { secured?: string; files?: string } }>( accessToken.token ) if (decodedAccessToken.perm.secured === "true") { @@ -224,6 +226,11 @@ const FilesApiProvider = ({ apiUrl, withLocalStorage = true, children }: FilesAp } else { setSecured(false) } + if (decodedAccessToken.perm.files === "restricted") { + setAccountRestricted(true) + } else { + setAccountRestricted(false) + } } }, [accessToken, filesApiClient]) @@ -308,7 +315,8 @@ const FilesApiProvider = ({ apiUrl, withLocalStorage = true, children }: FilesAp thresholdKeyLogin, secureThresholdKeyAccount, encryptedEncryptionKey: decodedRefreshToken?.enckey, - isMasterPasswordSet: !!decodedRefreshToken?.mps + isMasterPasswordSet: !!decodedRefreshToken?.mps, + accountRestricted }} > {children} diff --git a/packages/files-ui/src/Themes/Constants.ts b/packages/files-ui/src/Themes/Constants.ts index 11ddd93e5e..2868d1b13a 100644 --- a/packages/files-ui/src/Themes/Constants.ts +++ b/packages/files-ui/src/Themes/Constants.ts @@ -11,7 +11,9 @@ export const UI_CONSTANTS = { topPadding: 8 * 3, mobileNavWidth: 8 * 30, headerTopPadding: 8 * 3, - accountControlsPadding: 8 * 7 + accountControlsPadding: 8 * 7, + bottomBannerHeight: 80, + bottomBannerMobileHeight: 130 } export interface CsfColors extends IConstants { diff --git a/packages/files-ui/src/UI-components/Menu.tsx b/packages/files-ui/src/UI-components/Menu.tsx index eca9deb4e6..de15dbe51a 100644 --- a/packages/files-ui/src/UI-components/Menu.tsx +++ b/packages/files-ui/src/UI-components/Menu.tsx @@ -8,6 +8,7 @@ import { CSFTheme } from "../Themes/types" interface Option { contents: ReactNode onClick?: () => void + disabled?: boolean } interface CustomClasses { @@ -78,6 +79,7 @@ export default function Menu({ icon, options, style, testId }: Props) { }} focusVisibleClassName={clsx(style?.focusVisible)} className={classes.options} + disabled={option.disabled} > {option.contents} diff --git a/packages/files-ui/src/locales/de/messages.po b/packages/files-ui/src/locales/de/messages.po index 2553156418..8aa4259967 100644 --- a/packages/files-ui/src/locales/de/messages.po +++ b/packages/files-ui/src/locales/de/messages.po @@ -58,6 +58,9 @@ msgstr "Es ist ein Fehler aufgetreten:" msgid "Approve" msgstr "Genehmigen" +msgid "Back to plan settings" +msgstr "" + msgid "Backup secret phrase" msgstr "Sicherungsgeheimsatz" @@ -184,6 +187,9 @@ msgstr "Ordner erstellen" msgid "Create your public username in <0>Settings!" msgstr "" +msgid "Credit card on file" +msgstr "" + msgid "Dark Theme" msgstr "Dunkles Farbschema" @@ -301,8 +307,8 @@ msgstr "Ordner" msgid "Folder name is already in use" msgstr "" -msgid "Folder uploads are not supported currently" -msgstr "" +#~ msgid "Folder uploads are not supported currently" +#~ msgstr "" msgid "Folders" msgstr "Ordner" @@ -337,6 +343,9 @@ msgstr "" msgid "Go back" msgstr "Zurück" +msgid "Go to Payments" +msgstr "" + msgid "Got an issue?" msgstr "" @@ -874,6 +883,9 @@ msgstr "Sie haben noch keinen Benutzernamen festgelegt." msgid "You will need to sign a message in your wallet to complete sign in." msgstr "" +msgid "You've got a payment due. Until you've settled up, we've placed your account in restricted mode" +msgstr "" + msgid "Your first {0} are free, and you’ll get a discount on our monthly plan once you need more than that" msgstr "" @@ -912,3 +924,6 @@ msgstr "{0, plural, one {{1} Datei wird verschlüsselt und hochgeladen} other {{ msgid "{0, plural, one {You are about to delete {1} item.} other {You are about to delete {2} items.}}" msgstr "{0, plural, one {Sie sind dabei, {1} Artikel zu löschen.} other {Sie sind dabei, {2} Artikel zu löschen.}}" + +msgid "{0} of {1} used ({2}%)" +msgstr "" diff --git a/packages/files-ui/src/locales/en/messages.po b/packages/files-ui/src/locales/en/messages.po index 982b845b94..7d92f8fe89 100644 --- a/packages/files-ui/src/locales/en/messages.po +++ b/packages/files-ui/src/locales/en/messages.po @@ -58,6 +58,9 @@ msgstr "An error occurred:" msgid "Approve" msgstr "Approve" +msgid "Back to plan settings" +msgstr "Back to plan settings" + msgid "Backup secret phrase" msgstr "Backup secret phrase" @@ -184,6 +187,9 @@ msgstr "Create folder" msgid "Create your public username in <0>Settings!" msgstr "Create your public username in <0>Settings!" +msgid "Credit card on file" +msgstr "Credit card on file" + msgid "Dark Theme" msgstr "Dark Theme" @@ -304,8 +310,8 @@ msgstr "Folder" msgid "Folder name is already in use" msgstr "Folder name is already in use" -msgid "Folder uploads are not supported currently" -msgstr "Folder uploads are not supported currently" +#~ msgid "Folder uploads are not supported currently" +#~ msgstr "Folder uploads are not supported currently" msgid "Folders" msgstr "Folders" @@ -340,6 +346,9 @@ msgstr "Give view-only permission to:" msgid "Go back" msgstr "Go back" +msgid "Go to Payments" +msgstr "Go to Payments" + msgid "Got an issue?" msgstr "Got an issue?" @@ -877,6 +886,9 @@ msgstr "You haven't set a username yet." msgid "You will need to sign a message in your wallet to complete sign in." msgstr "You will need to sign a message in your wallet to complete sign in." +msgid "You've got a payment due. Until you've settled up, we've placed your account in restricted mode" +msgstr "You've got a payment due. Until you've settled up, we've placed your account in restricted mode" + msgid "Your first {0} are free, and you’ll get a discount on our monthly plan once you need more than that" msgstr "Your first {0} are free, and you’ll get a discount on our monthly plan once you need more than that" @@ -915,3 +927,6 @@ msgstr "{0, plural, one {Encrypting and uploading {1} file} other {Encrypting an msgid "{0, plural, one {You are about to delete {1} item.} other {You are about to delete {2} items.}}" msgstr "{0, plural, one {You are about to delete {1} item.} other {You are about to delete {2} items.}}" + +msgid "{0} of {1} used ({2}%)" +msgstr "{0} of {1} used ({2}%)" diff --git a/packages/files-ui/src/locales/es/messages.po b/packages/files-ui/src/locales/es/messages.po index e0741762b6..8f55976b51 100644 --- a/packages/files-ui/src/locales/es/messages.po +++ b/packages/files-ui/src/locales/es/messages.po @@ -59,6 +59,9 @@ msgstr "" msgid "Approve" msgstr "Aprobar" +msgid "Back to plan settings" +msgstr "" + msgid "Backup secret phrase" msgstr "" @@ -185,6 +188,9 @@ msgstr "Crear Carpeta" msgid "Create your public username in <0>Settings!" msgstr "" +msgid "Credit card on file" +msgstr "" + msgid "Dark Theme" msgstr "Tema oscuro" @@ -305,8 +311,8 @@ msgstr "Archivo" msgid "Folder name is already in use" msgstr "" -msgid "Folder uploads are not supported currently" -msgstr "" +#~ msgid "Folder uploads are not supported currently" +#~ msgstr "" msgid "Folders" msgstr "Archivos" @@ -341,6 +347,9 @@ msgstr "" msgid "Go back" msgstr "Regresar" +msgid "Go to Payments" +msgstr "" + msgid "Got an issue?" msgstr "" @@ -878,6 +887,9 @@ msgstr "" msgid "You will need to sign a message in your wallet to complete sign in." msgstr "Deberá firmar un mensaje en su billetera para completar el inicio de sesión." +msgid "You've got a payment due. Until you've settled up, we've placed your account in restricted mode" +msgstr "" + msgid "Your first {0} are free, and you’ll get a discount on our monthly plan once you need more than that" msgstr "" @@ -916,3 +928,6 @@ msgstr "" msgid "{0, plural, one {You are about to delete {1} item.} other {You are about to delete {2} items.}}" msgstr "" + +msgid "{0} of {1} used ({2}%)" +msgstr "" diff --git a/packages/files-ui/src/locales/fr/messages.po b/packages/files-ui/src/locales/fr/messages.po index f65049a258..3ada4e31b7 100644 --- a/packages/files-ui/src/locales/fr/messages.po +++ b/packages/files-ui/src/locales/fr/messages.po @@ -59,6 +59,9 @@ msgstr "Une erreur s'est produite :" msgid "Approve" msgstr "Accepter" +msgid "Back to plan settings" +msgstr "" + msgid "Backup secret phrase" msgstr "Phrase secrète de sauvegarde" @@ -185,6 +188,9 @@ msgstr "Créer un dossier" msgid "Create your public username in <0>Settings!" msgstr "Créez votre nom d'utilisateur public dans <0>Paramètres !" +msgid "Credit card on file" +msgstr "" + msgid "Dark Theme" msgstr "Thème sombre" @@ -305,8 +311,8 @@ msgstr "Dossier" msgid "Folder name is already in use" msgstr "Le nom du dossier est déjà utilisé" -msgid "Folder uploads are not supported currently" -msgstr "Le téléversement de dossiers n'est pas actuellement pris en charge" +#~ msgid "Folder uploads are not supported currently" +#~ msgstr "Le téléversement de dossiers n'est pas actuellement pris en charge" msgid "Folders" msgstr "Dossiers" @@ -341,6 +347,9 @@ msgstr "Donner l’accès en lecture seule à :" msgid "Go back" msgstr "Retour" +msgid "Go to Payments" +msgstr "" + msgid "Got an issue?" msgstr "Vous avez un problème ?" @@ -446,8 +455,8 @@ msgstr "Suivant" msgid "Nice to see you again!" msgstr "Ravi de te revoir !" -msgid "No file to download." -msgstr "Aucun fichier à télécharger." +msgid "No Card" +msgstr "" msgid "No file to download." msgstr "Aucun fichier à télécharger." @@ -878,6 +887,9 @@ msgstr "Vous n’avez pas encore défini de nom d’utilisateur." msgid "You will need to sign a message in your wallet to complete sign in." msgstr "Vous devrez signer un message avec votre wallet pour terminer la procédure connexion." +msgid "You've got a payment due. Until you've settled up, we've placed your account in restricted mode" +msgstr "" + msgid "Your first {0} are free, and you’ll get a discount on our monthly plan once you need more than that" msgstr "" @@ -916,3 +928,6 @@ msgstr "{0, plural, one {Chiffrement et téléversement de {1} fichier} other {C msgid "{0, plural, one {You are about to delete {1} item.} other {You are about to delete {2} items.}}" msgstr "{0, plural, one {Vous êtes sur le point de supprimer {1} élément.} other {Vous êtes sur le point de supprimer {2} éléments.}}" + +msgid "{0} of {1} used ({2}%)" +msgstr "" diff --git a/packages/files-ui/src/locales/no/messages.po b/packages/files-ui/src/locales/no/messages.po index 5274a443d6..a8f9690da8 100644 --- a/packages/files-ui/src/locales/no/messages.po +++ b/packages/files-ui/src/locales/no/messages.po @@ -58,6 +58,9 @@ msgstr "" msgid "Approve" msgstr "Godkjenn" +msgid "Back to plan settings" +msgstr "" + msgid "Backup secret phrase" msgstr "" @@ -184,6 +187,9 @@ msgstr "Opprett mappe" msgid "Create your public username in <0>Settings!" msgstr "" +msgid "Credit card on file" +msgstr "" + msgid "Dark Theme" msgstr "Mørk drakt" @@ -301,8 +307,8 @@ msgstr "Mappe" msgid "Folder name is already in use" msgstr "" -msgid "Folder uploads are not supported currently" -msgstr "" +#~ msgid "Folder uploads are not supported currently" +#~ msgstr "" msgid "Folders" msgstr "Mapper" @@ -337,6 +343,9 @@ msgstr "" msgid "Go back" msgstr "Tilbake" +msgid "Go to Payments" +msgstr "" + msgid "Got an issue?" msgstr "" @@ -874,6 +883,9 @@ msgstr "Du har ikke satt noe brukernavn enda." msgid "You will need to sign a message in your wallet to complete sign in." msgstr "" +msgid "You've got a payment due. Until you've settled up, we've placed your account in restricted mode" +msgstr "" + msgid "Your first {0} are free, and you’ll get a discount on our monthly plan once you need more than that" msgstr "" @@ -912,3 +924,6 @@ msgstr "" msgid "{0, plural, one {You are about to delete {1} item.} other {You are about to delete {2} items.}}" msgstr "" + +msgid "{0} of {1} used ({2}%)" +msgstr ""