diff --git a/.eslintrc.json b/.eslintrc.json index 210af778d3..d046b33919 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -25,7 +25,7 @@ "rules": { "comma-spacing": ["error", { "before": false, "after": true }], "cypress/no-pause": "error", - "indent": ["error", 2], + "@typescript-eslint/indent": ["error", 2], "linebreak-style": ["error", "unix"], "quotes": ["error", "double"], "semi": ["error", "never"], diff --git a/packages/common-components/rollup.config.js b/packages/common-components/rollup.config.js index cf32c5eaa8..b9323226bb 100644 --- a/packages/common-components/rollup.config.js +++ b/packages/common-components/rollup.config.js @@ -20,7 +20,7 @@ export default { plugins: [ peerDepsExternal(), image(), - resolve(), + resolve({ browser: true }), commonjs(), typescript(), postcss({ diff --git a/packages/common-components/src/Button/Button.tsx b/packages/common-components/src/Button/Button.tsx index 3717ec36d4..0900773aaf 100644 --- a/packages/common-components/src/Button/Button.tsx +++ b/packages/common-components/src/Button/Button.tsx @@ -7,7 +7,6 @@ import { Typography } from "../Typography" const useStyles = makeStyles( ({ constants, typography, animation, palette, overrides }: ITheme) => createStyles({ - // JSS in CSS goes here root: { ...typography.button, borderRadius: `${constants.generalUnit / 4}px`, @@ -110,6 +109,29 @@ const useStyles = makeStyles( }, ...overrides?.Button?.variants?.secondary?.root }, + tertiary: { + backgroundColor: palette.additional["gray"][3], + color: palette.common.black.main, + "& svg": { + fill: palette.common.white.main + }, + "&:hover": { + backgroundColor: palette.primary.main, + color: palette.common.white.main, + ...overrides?.Button?.variants?.tertiary?.hover + }, + "&:focus": { + backgroundColor: palette.primary.main, + color: palette.common.white.main, + ...overrides?.Button?.variants?.tertiary?.focus + }, + "&:active": { + backgroundColor: palette.primary.main, + color: palette.common.white.main, + ...overrides?.Button?.variants?.tertiary?.active + }, + ...overrides?.Button?.variants?.tertiary?.root + }, outline: { color: palette.additional["gray"][8], backgroundColor: palette.common?.white.main, @@ -271,7 +293,7 @@ interface IButtonProps extends Omit { className?: string children?: ReactNode | ReactNode[] fullsize?: boolean - variant?: "link" | "primary" | "secondary" | "outline" | "dashed" | "danger" + variant?: "link" | "primary" | "secondary" |"tertiary" | "outline" | "dashed" | "danger" iconButton?: boolean size?: "large" | "medium" | "small" type?: "button" | "submit" | "reset" diff --git a/packages/common-components/src/Icons/types.d.ts b/packages/common-components/src/Icons/types.d.ts index af2915b8ae..6a6c6adddb 100644 --- a/packages/common-components/src/Icons/types.d.ts +++ b/packages/common-components/src/Icons/types.d.ts @@ -4,7 +4,7 @@ type fontSizeProp = "inherit" | "small" | "medium" | "large" declare module "*.svg" { import React = require("react") export const ReactComponent: React.FunctionComponent> const src: string export default src diff --git a/packages/common-components/src/MenuDropdown/MenuDropdown.tsx b/packages/common-components/src/MenuDropdown/MenuDropdown.tsx index eaffb61646..f42b97f24a 100644 --- a/packages/common-components/src/MenuDropdown/MenuDropdown.tsx +++ b/packages/common-components/src/MenuDropdown/MenuDropdown.tsx @@ -162,12 +162,12 @@ interface IMenuDropdownProps { indicator?: typeof SvgIcon animation?: "rotate" | "flip" | "none" anchor?: - | "top-left" - | "top-center" - | "top-right" - | "bottom-left" - | "bottom-center" - | "bottom-right" + | "top-left" + | "top-center" + | "top-right" + | "bottom-left" + | "bottom-center" + | "bottom-right" menuItems: IMenuItem[] title?: string classNames?: { diff --git a/packages/common-components/src/TextInput/TextInput.tsx b/packages/common-components/src/TextInput/TextInput.tsx index ae4454bf60..e78705b14b 100644 --- a/packages/common-components/src/TextInput/TextInput.tsx +++ b/packages/common-components/src/TextInput/TextInput.tsx @@ -72,6 +72,7 @@ const useStyles = makeStyles( "&.disabled": { "& input": { color: palette.additional["gray"][6], + "-webkit-text-fill-color": palette.additional["gray"][6], backgroundColor: palette.additional["gray"][3], ...overrides?.TextInput?.input?.disabled }, diff --git a/packages/common-components/src/Toasts/ToastContent.tsx b/packages/common-components/src/Toasts/ToastContent.tsx index 74fc98ccc5..5f74826548 100644 --- a/packages/common-components/src/Toasts/ToastContent.tsx +++ b/packages/common-components/src/Toasts/ToastContent.tsx @@ -7,7 +7,7 @@ import { CheckCircleIcon, CloseCircleIcon, CloseCirceSvg, CrossSvg } from "../Ic import { ProgressBar } from "../ProgressBar" import { Loading } from "../Spinner" -const useStyles = makeStyles(({ constants, palette, animation, overrides }: ITheme) => { +const useStyles = makeStyles(({ constants, palette, overrides }: ITheme) => { return createStyles({ root: { backgroundColor: palette.additional["gray"][3], @@ -16,10 +16,6 @@ const useStyles = makeStyles(({ constants, palette, animation, overrides }: IThe padding: constants.generalUnit * 2, borderRadius: 4, position: "relative", - "&:hover $closeIcon": { - visibility: "visible", - opacity: 1 - }, ...overrides?.Toasts?.root }, progressBox: { @@ -66,9 +62,6 @@ const useStyles = makeStyles(({ constants, palette, animation, overrides }: IThe backgroundColor: palette.additional["gray"][1], border: "1px solid", borderColor: palette.additional["gray"][9], - opacity: 0, - visibility: "hidden", - transition: `opacity ${animation.transform}ms`, display: "flex", alignItems: "center", justifyContent: "center", @@ -147,6 +140,7 @@ const ToastContent = ({ toast, onClose }: ToastContentProps) => { } {isClosable &&
diff --git a/packages/common-components/src/Typography/Typography.tsx b/packages/common-components/src/Typography/Typography.tsx index cae0cd156e..31e1b025cc 100644 --- a/packages/common-components/src/Typography/Typography.tsx +++ b/packages/common-components/src/Typography/Typography.tsx @@ -78,18 +78,18 @@ interface OwnProps extends React.HTMLProps { className?: string children?: ReactNode | ReactNode[] variant?: - | "h1" - | "h2" - | "h3" - | "h4" - | "h5" - | "h6" - | "subtitle1" - | "subtitle2" - | "body1" - | "body2" - | "caption" - | "button" + | "h1" + | "h2" + | "h3" + | "h4" + | "h5" + | "h6" + | "subtitle1" + | "subtitle2" + | "body1" + | "body2" + | "caption" + | "button" component?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "span" | "p" } diff --git a/packages/common-components/src/index.ts b/packages/common-components/src/index.ts index 8684f17bf5..a6d8a17044 100644 --- a/packages/common-components/src/index.ts +++ b/packages/common-components/src/index.ts @@ -20,6 +20,7 @@ export * from "./Icons" export * from "./Modal" export * from "./NumberInput" export * from "./MenuDropdown" +export * from "./Paper" export * from "./ProgressBar" export * from "./RadioInput" export * from "./Router" diff --git a/packages/common-components/src/stories/Button.stories.tsx b/packages/common-components/src/stories/Button.stories.tsx index d45cbee53f..cffaf6a3b2 100644 --- a/packages/common-components/src/stories/Button.stories.tsx +++ b/packages/common-components/src/stories/Button.stories.tsx @@ -15,7 +15,7 @@ export const actionsData = { onClick: action("onClickButton") } -type VariantOption = "primary" | "outline" | "dashed" | "danger" | undefined; +type VariantOption = "primary" | "secondary" | "tertiary" | "outline" | "dashed" | "danger" | undefined; const variantOptions: VariantOption[] = [ "primary", "outline", diff --git a/packages/common-components/src/stories/Typography.stories.tsx b/packages/common-components/src/stories/Typography.stories.tsx index 4ad36e0e58..51c271d7e0 100644 --- a/packages/common-components/src/stories/Typography.stories.tsx +++ b/packages/common-components/src/stories/Typography.stories.tsx @@ -15,17 +15,17 @@ export const actionsData = { } type VariantOption = "h1" - |"h2" - |"h3" - |"h4" - |"h5" - |"h6" - |"subtitle1" - |"subtitle2" - |"body1" - |"body2" - |"caption" - |"button" +|"h2" +|"h3" +|"h4" +|"h5" +|"h6" +|"subtitle1" +|"subtitle2" +|"body1" +|"body2" +|"caption" +|"button" const variantOptions: VariantOption[] = [ "h1", diff --git a/packages/common-theme/src/Create/CreateBreakpoints.ts b/packages/common-theme/src/Create/CreateBreakpoints.ts index 3cb7901e33..42ce306827 100644 --- a/packages/common-theme/src/Create/CreateBreakpoints.ts +++ b/packages/common-theme/src/Create/CreateBreakpoints.ts @@ -6,10 +6,10 @@ export type Overwrite = Omit & U type GenerateStringUnion = Extract< - { - [Key in keyof T]: true extends T[Key] ? Key : never - }[keyof T], - string +{ + [Key in keyof T]: true extends T[Key] ? Key : never +}[keyof T], +string > /** @@ -22,18 +22,18 @@ type GenerateStringUnion = Extract< * @internal */ export type OverridableStringUnion> = GenerateStringUnion< - Overwrite +Overwrite > export type BreakpointDefaults = Record< - "xs" | "sm" | "md" | "lg" | "xl" | string, - true +"xs" | "sm" | "md" | "lg" | "xl" | string, +true > export interface BreakpointOverrides {[key: string]: unknown} export type Breakpoint = OverridableStringUnion< - BreakpointDefaults, - BreakpointOverrides +BreakpointDefaults, +BreakpointOverrides > export type BreakpointValues = { [key in Breakpoint]: number } export const keys: Breakpoint[] = [] @@ -49,10 +49,10 @@ export interface IBreakpoints { } export type BreakpointsOptions = Partial< - { - unit: string - step: number - } & IBreakpoints +{ + unit: string + step: number +} & IBreakpoints > // Keep in mind that @media is inclusive by the CSS specification. diff --git a/packages/common-theme/src/Defaults/ThemeConfig.ts b/packages/common-theme/src/Defaults/ThemeConfig.ts index ec5fdc1e23..86acc99a74 100644 --- a/packages/common-theme/src/Defaults/ThemeConfig.ts +++ b/packages/common-theme/src/Defaults/ThemeConfig.ts @@ -169,7 +169,7 @@ const DefaultThemeConfig: IThemeConfig = { }, button: { ...defaultFontStyles, - fontWeight: defaultFontWeights.regular, + fontWeight: defaultFontWeights.semibold, fontSize: 14, lineHeight: "22px" }, diff --git a/packages/common-theme/src/Hooks/useDoubleClick.ts b/packages/common-theme/src/Hooks/useDoubleClick.ts index 32ae768923..38ae8c1eb5 100644 --- a/packages/common-theme/src/Hooks/useDoubleClick.ts +++ b/packages/common-theme/src/Hooks/useDoubleClick.ts @@ -9,12 +9,12 @@ export function useDoubleClick( const onClick = useCallback((e?: React.MouseEvent) => { if (e) { switch (e.detail) { - case 1: - actionSingleClick && actionSingleClick(e) - break - case 2: - actionDoubleClick && actionDoubleClick(e) - break + case 1: + actionSingleClick && actionSingleClick(e) + break + case 2: + actionDoubleClick && actionDoubleClick(e) + break } } }, [actionSingleClick, actionDoubleClick]) diff --git a/packages/common-theme/src/Overrides/Button.ts b/packages/common-theme/src/Overrides/Button.ts index 1c7d5237b8..106c92e11c 100644 --- a/packages/common-theme/src/Overrides/Button.ts +++ b/packages/common-theme/src/Overrides/Button.ts @@ -24,6 +24,12 @@ export interface IButtonOverride { focus?: Record active?: Record } + tertiary?: { + root?: Record + hover?: Record + focus?: Record + active?: Record + } outline?: { root?: Record hover?: Record diff --git a/packages/files-ui/cypress/support/page-objects/binPage.ts b/packages/files-ui/cypress/support/page-objects/binPage.ts index 7b8f48429b..afb126f1c3 100644 --- a/packages/files-ui/cypress/support/page-objects/binPage.ts +++ b/packages/files-ui/cypress/support/page-objects/binPage.ts @@ -8,7 +8,6 @@ export const binPage = { // bin page specific file browser elements recoverSelectedButton: () => cy.get("[data-testId=button-recover-selected-file]"), deleteSelectedButton: () => cy.get("[data-testId=button-delete-selected-file]"), - permanentDeleteSuccessToast: () => cy.get("[data-testId=toast-deletion-success]", { timeout: 10000 }), selectAllCheckbox: () => cy.get("[data-testId=checkbox-select-all]"), // kebab menu elements diff --git a/packages/files-ui/cypress/support/page-objects/homePage.ts b/packages/files-ui/cypress/support/page-objects/homePage.ts index 87a161e0a3..cbfd7d260c 100644 --- a/packages/files-ui/cypress/support/page-objects/homePage.ts +++ b/packages/files-ui/cypress/support/page-objects/homePage.ts @@ -1,6 +1,7 @@ import { basePage } from "./basePage" import { fileBrowser } from "./fileBrowser" import { fileUploadModal } from "./modals/fileUploadModal" +import { uploadCompleteToast } from "./toasts/uploadCompleteToast" export const homePage = { ...basePage, @@ -12,8 +13,6 @@ export const homePage = { moveSelectedButton: () => cy.get("[data-testId=button-move-selected-file]"), deleteSelectedButton: () => cy.get("[data-testId=button-delete-selected-file]"), selectAllCheckbox: () => cy.get("[data-testId=checkbox-select-all]"), - uploadStatusToast: () => cy.get("[data-testId=toast-upload-status]", { timeout: 10000 }), - deleteSuccessToast: () => cy.get("[data-testId=toast-deletion-success]", { timeout: 10000 }), fileRenameInput: () => cy.get("[data-cy=rename-form] input"), fileRenameSubmitButton: () => cy.get("[data-cy=rename-submit-button]"), fileRenameErrorLabel: () => cy.get("[data-cy=rename-form] span.minimal.error"), @@ -36,7 +35,8 @@ export const homePage = { // ensure upload is complete before proceeding fileUploadModal.body().should("not.exist") - this.uploadStatusToast().should("not.exist") + uploadCompleteToast.body().should("be.visible") + uploadCompleteToast.closeButton().click() } } diff --git a/packages/files-ui/cypress/support/page-objects/toasts/deleteSuccessToast.ts b/packages/files-ui/cypress/support/page-objects/toasts/deleteSuccessToast.ts new file mode 100644 index 0000000000..223017ff80 --- /dev/null +++ b/packages/files-ui/cypress/support/page-objects/toasts/deleteSuccessToast.ts @@ -0,0 +1,4 @@ +export const deleteSuccessToast = { + body: () => cy.get("[data-testId=toast-deletion-success]", { timeout: 10000 }), + closeButton: () => cy.get("[data-testid=button-close-toast-deletion-success]") +} diff --git a/packages/files-ui/cypress/support/page-objects/toasts/moveSuccessToast.ts b/packages/files-ui/cypress/support/page-objects/toasts/moveSuccessToast.ts new file mode 100644 index 0000000000..97e78d1707 --- /dev/null +++ b/packages/files-ui/cypress/support/page-objects/toasts/moveSuccessToast.ts @@ -0,0 +1,4 @@ +export const moveSuccessToast = { + body: () => cy.get("[data-testId=toast-move-success]", { timeout: 10000 }), + closeButton: () => cy.get("[data-testid=button-close-toast-move-success]") +} diff --git a/packages/files-ui/cypress/support/page-objects/toasts/uploadCompleteToast.ts b/packages/files-ui/cypress/support/page-objects/toasts/uploadCompleteToast.ts new file mode 100644 index 0000000000..632b2acbef --- /dev/null +++ b/packages/files-ui/cypress/support/page-objects/toasts/uploadCompleteToast.ts @@ -0,0 +1,4 @@ +export const uploadCompleteToast = { + body: () => cy.get("[data-testId=toast-upload-complete]", { timeout: 10000 }), + closeButton: () => cy.get("[data-testid=button-close-toast-upload-complete]") +} diff --git a/packages/files-ui/cypress/tests/file-management-spec.ts b/packages/files-ui/cypress/tests/file-management-spec.ts index fdabafa8ad..67025b40c9 100644 --- a/packages/files-ui/cypress/tests/file-management-spec.ts +++ b/packages/files-ui/cypress/tests/file-management-spec.ts @@ -9,6 +9,9 @@ import { deleteFileModal } from "../support/page-objects/modals/deleteFileModal" import { fileUploadModal } from "../support/page-objects/modals/fileUploadModal" import { moveItemModal } from "../support/page-objects/modals/moveItemModal" import { recoverItemModal } from "../support/page-objects/modals/recoverItemModal" +import { deleteSuccessToast } from "../support/page-objects/toasts/deleteSuccessToast" +import { moveSuccessToast } from "../support/page-objects/toasts/moveSuccessToast" +import { uploadCompleteToast } from "../support/page-objects/toasts/uploadCompleteToast" describe("File management", () => { @@ -81,6 +84,8 @@ describe("File management", () => { homePage.moveSelectedButton().click() moveItemModal.folderList().contains("Home").click() moveItemModal.moveButton().safeClick() + moveSuccessToast.body().should("be.visible") + moveSuccessToast.closeButton().click() // ensure the home root now has the folder and file navigationMenu.homeNavButton().click() @@ -174,6 +179,8 @@ describe("File management", () => { fileUploadModal.removeFileButton().should("have.length", 2) fileUploadModal.uploadButton().safeClick() fileUploadModal.body().should("not.exist") + uploadCompleteToast.body().should("be.visible") + uploadCompleteToast.closeButton().click() homePage.fileItemRow().should("have.length", 2) }) @@ -229,6 +236,8 @@ describe("File management", () => { deleteFileModal.body().should("be.visible") deleteFileModal.confirmButton().safeClick() deleteFileModal.body().should("not.exist") + deleteSuccessToast.body().should("be.visible") + deleteSuccessToast.closeButton().click() homePage.fileItemRow().should("not.exist") // confirm the deleted file is moved to the bin @@ -267,10 +276,14 @@ describe("File management", () => { homePage.fileItemKebabButton().first().click() homePage.deleteMenuOption().click() deleteFileModal.confirmButton().safeClick() + deleteSuccessToast.body().should("be.visible") + deleteSuccessToast.closeButton().click() navigationMenu.binNavButton().click() binPage.fileItemKebabButton().first().click() binPage.deleteMenuOption().click() deleteFileModal.confirmButton().safeClick() + deleteSuccessToast.body().should("be.visible") + deleteSuccessToast.closeButton().click() binPage.fileItemRow().should("not.exist") navigationMenu.homeNavButton().click() homePage.fileItemRow().should("not.exist") @@ -288,6 +301,8 @@ describe("File management", () => { deleteFileModal.body().should("be.visible") deleteFileModal.confirmButton().safeClick() deleteFileModal.body().should("not.exist") + deleteSuccessToast.body().should("be.visible") + deleteSuccessToast.closeButton().click() homePage.fileItemRow().should("not.exist") // confirm the deleted folder is moved to the bin @@ -312,10 +327,14 @@ describe("File management", () => { homePage.fileItemKebabButton().first().click() homePage.deleteMenuOption().click() deleteFileModal.confirmButton().safeClick() + deleteSuccessToast.body().should("be.visible") + deleteSuccessToast.closeButton().click() navigationMenu.binNavButton().click() binPage.fileItemKebabButton().first().click() binPage.deleteMenuOption().click() deleteFileModal.confirmButton().safeClick() + deleteSuccessToast.body().should("be.visible") + deleteSuccessToast.closeButton().click() binPage.fileItemRow().should("not.exist") navigationMenu.homeNavButton().click() homePage.fileItemRow().should("not.exist") @@ -366,12 +385,15 @@ describe("File management", () => { homePage.fileItemKebabButton().click() homePage.deleteMenuOption().click() deleteFileModal.confirmButton().safeClick() - homePage.deleteSuccessToast().should("not.exist") + deleteSuccessToast.body().should("be.visible") + deleteSuccessToast.closeButton().click() + navigationMenu.binNavButton().click() binPage.fileItemKebabButton().click() binPage.deleteMenuOption().click() deleteFileModal.confirmButton().safeClick() - binPage.permanentDeleteSuccessToast().should("not.exist") + deleteSuccessToast.body().should("be.visible") + deleteSuccessToast.closeButton().click() navigationMenu.spaceUsedLabel().should("contain.text", "0 Bytes") }) @@ -391,7 +413,8 @@ describe("File management", () => { homePage.selectAllCheckbox().click() homePage.deleteSelectedButton().click() deleteFileModal.confirmButton().safeClick() - homePage.deleteSuccessToast().should("not.exist") + deleteSuccessToast.body().should("be.visible") + deleteSuccessToast.closeButton().click() homePage.fileItemRow().should("have.length", 0) // recover both of the files in the same action diff --git a/packages/files-ui/cypress/tests/file-preview-spec.ts b/packages/files-ui/cypress/tests/file-preview-spec.ts index 0003413416..d20850efd3 100644 --- a/packages/files-ui/cypress/tests/file-preview-spec.ts +++ b/packages/files-ui/cypress/tests/file-preview-spec.ts @@ -72,7 +72,8 @@ describe("File Preview", () => { homePage.fileItemName().dblclick() previewModal.unsupportedFileLabel().should("exist") previewModal.downloadUnsupportedFileButton().should("be.visible") - + cy.get("@downloadRequest").should('be.undefined') + previewModal.downloadUnsupportedFileButton().click() // ensure the download request contains the correct file cy.get("@downloadRequest").its("request.body").should("contain", { path: "/file.zip" diff --git a/packages/files-ui/package.json b/packages/files-ui/package.json index a7bbe46066..9689c793e4 100644 --- a/packages/files-ui/package.json +++ b/packages/files-ui/package.json @@ -72,7 +72,7 @@ "@types/yup": "^0.29.9", "@types/zxcvbn": "^4.4.0", "babel-plugin-macros": "^2.8.0", - "cypress": "^8.4", + "cypress": "^8.5", "cypress-file-upload": "^5.0.8", "cypress-pipe": "^2.0.0" }, diff --git a/packages/files-ui/src/Components/Elements/MnemonicForm.tsx b/packages/files-ui/src/Components/Elements/MnemonicForm.tsx index ae064dfca4..31e95cf972 100644 --- a/packages/files-ui/src/Components/Elements/MnemonicForm.tsx +++ b/packages/files-ui/src/Components/Elements/MnemonicForm.tsx @@ -92,8 +92,8 @@ const useStyles = makeStyles(({ animation, constants, palette, zIndex }: CSFThem ) interface Props { - buttonLabel?: string - onComplete: () => void + buttonLabel?: string + onComplete: () => void } const MnemonicForm = ({ buttonLabel, onComplete }: Props) => { diff --git a/packages/files-ui/src/Components/Elements/ShareTransferRequestModal.tsx b/packages/files-ui/src/Components/Elements/ShareTransferRequestModal.tsx index dab63e66ad..61c2a6094b 100644 --- a/packages/files-ui/src/Components/Elements/ShareTransferRequestModal.tsx +++ b/packages/files-ui/src/Components/Elements/ShareTransferRequestModal.tsx @@ -10,7 +10,7 @@ import clsx from "clsx" import { CSFTheme } from "../../Themes/types" interface Props { - requests: ShareTransferRequest[] + requests: ShareTransferRequest[] } const useStyles = makeStyles(({ constants }: CSFTheme) => diff --git a/packages/files-ui/src/Components/Elements/SharedUsers.tsx b/packages/files-ui/src/Components/Elements/SharedUsers.tsx index 30d44bfbda..6ea07668bb 100644 --- a/packages/files-ui/src/Components/Elements/SharedUsers.tsx +++ b/packages/files-ui/src/Components/Elements/SharedUsers.tsx @@ -12,7 +12,7 @@ const useStyles = makeStyles(() => { }) }) interface Props { - bucket: BucketKeyPermission + bucket: BucketKeyPermission } const SharedUsers = ({ bucket }: Props) => { diff --git a/packages/files-ui/src/Components/Elements/TeamModal.tsx b/packages/files-ui/src/Components/Elements/TeamModal.tsx new file mode 100644 index 0000000000..df5acb3abf --- /dev/null +++ b/packages/files-ui/src/Components/Elements/TeamModal.tsx @@ -0,0 +1,77 @@ +import { Button, Typography } from "@chainsafe/common-components" +import { createStyles, makeStyles } from "@chainsafe/common-theme" +import { Trans } from "@lingui/macro" +import React, { useCallback } from "react" +import { CSFTheme } from "../../Themes/types" +import { ROUTE_LINKS } from "../FilesRoutes" +import CustomModal from "./CustomModal" + +const useStyles = makeStyles( + ({ constants }: CSFTheme) => { + return createStyles({ + root: { + padding: `${constants.generalUnit * 6}px ${constants.generalUnit * 4}px`, + flexDirection: "column", + display: "flex", + alignItems: "center" + }, + title: { + marginBottom: constants.generalUnit * 3 + }, + modalInner: { + maxWidth: "600px !important" + }, + buttonContainer: { + width: "100%" + }, + nextButton: { + margin: "auto", + marginTop: constants.generalUnit * 3 + } + }) + }) + +interface Props { + onHide: () => void +} + +const TeamModal = ({ onHide }: Props) => { + const classes = useStyles() + + const onSignupTeamClick = useCallback(() => { + window.open(ROUTE_LINKS.TeamSignup, "_blank") + onHide() + }, [onHide]) + + return ( + +
+ + Teams + + + A better sharing experience is coming soon. + +
+ +
+
+
+ )} + +export default TeamModal diff --git a/packages/files-ui/src/Components/FilesRoutes.tsx b/packages/files-ui/src/Components/FilesRoutes.tsx index 4db2c0a4e2..b90c6cd350 100644 --- a/packages/files-ui/src/Components/FilesRoutes.tsx +++ b/packages/files-ui/src/Components/FilesRoutes.tsx @@ -34,7 +34,8 @@ export const ROUTE_LINKS = { const adjustedRawCurrentPath = !rawCurrentPath ? "/" : rawCurrentPath return `/shared/${bucketId}${adjustedRawCurrentPath}` }, - DiscordInvite: "https://discord.gg/zAEY37fNb2" + DiscordInvite: "https://discord.gg/zAEY37fNb2", + TeamSignup: "https://shrl.ink/cgQy" } export const SETTINGS_PATHS = ["profile", "security", "plan"] as const diff --git a/packages/files-ui/src/Components/Layouts/AppHeader.tsx b/packages/files-ui/src/Components/Layouts/AppHeader.tsx index 8bc1919f4c..0a58868e50 100644 --- a/packages/files-ui/src/Components/Layouts/AppHeader.tsx +++ b/packages/files-ui/src/Components/Layouts/AppHeader.tsx @@ -8,7 +8,8 @@ import { HamburgerMenu, MenuDropdown, PowerDownSvg, - useHistory + useHistory, + Button } from "@chainsafe/common-components" import { ROUTE_LINKS } from "../FilesRoutes" import SearchModule from "../Modules/SearchModule" @@ -17,6 +18,7 @@ import { useThresholdKey } from "../../Contexts/ThresholdKeyContext" import { CSFTheme } from "../../Themes/types" import { useUser } from "../../Contexts/UserContext" import { useFilesApi } from "../../Contexts/FilesApiContext" +import TeamModal from "../Elements/TeamModal" const useStyles = makeStyles( ({ palette, animation, breakpoints, constants, zIndex }: CSFTheme) => { @@ -99,9 +101,7 @@ const useStyles = makeStyles( justifyContent: "flex-end", alignItems: "center", flexDirection: "row", - [breakpoints.up("md")]: { - marginLeft: constants.accountControlsPadding - }, + "& > *:first-child": { marginRight: constants.generalUnit * 2 } @@ -142,6 +142,19 @@ const useStyles = makeStyles( }, title : { marginLeft: constants.generalUnit + }, + buttonsSection: { + display: "flex", + alignItems: "center", + margin: `0 ${constants.generalUnit * 2}px`, + + "& button" : { + height: constants.generalUnit * 4, + + "&:not(:first-child)": { + marginLeft: constants.generalUnit * 2 + } + } } }) } @@ -159,6 +172,7 @@ const AppHeader = ({ navOpen, setNavOpen }: IAppHeader) => { const { publicKey, isNewDevice, shouldInitializeAccount, logout } = useThresholdKey() const { getProfileTitle, removeUser } = useUser() const [searchActive, setSearchActive] = useState(false) + const [isTeamModalOpen, setIsTeamModalOpen] = useState(false) const { history } = useHistory() const signOut = useCallback(async () => { @@ -171,6 +185,14 @@ const AppHeader = ({ navOpen, setNavOpen }: IAppHeader) => { }, [logout, removeUser, history]) + const onReportBugClick = useCallback(() => { + window.open(ROUTE_LINKS.DiscordInvite, "_blank") + }, []) + + const onStartATeamClick = useCallback(() => { + setIsTeamModalOpen(true) + }, []) + return (
{ setSearchActive={setSearchActive} /> +
+ + +
{ )} )} + {isTeamModalOpen && setIsTeamModalOpen(false)}/>}
) } diff --git a/packages/files-ui/src/Components/Layouts/AppNav.tsx b/packages/files-ui/src/Components/Layouts/AppNav.tsx index df8f78e2e6..beee0b06fa 100644 --- a/packages/files-ui/src/Components/Layouts/AppNav.tsx +++ b/packages/files-ui/src/Components/Layouts/AppNav.tsx @@ -1,9 +1,5 @@ import { useFiles } from "../../Contexts/FilesContext" -import { - createStyles, - makeStyles, - useThemeSwitcher -} from "@chainsafe/common-theme" +import { createStyles, makeStyles, useThemeSwitcher } from "@chainsafe/common-theme" import React, { useCallback } from "react" import clsx from "clsx" import { @@ -14,10 +10,10 @@ import { SettingSvg, PowerDownSvg, ProgressBar, - Button, formatBytes, DeleteSvg, - UserShareSvg } from "@chainsafe/common-components" + UserShareSvg +} from "@chainsafe/common-components" import { ROUTE_LINKS } from "../FilesRoutes" import { Trans } from "@lingui/macro" import { useThresholdKey } from "../../Contexts/ThresholdKeyContext" @@ -214,15 +210,12 @@ interface IAppNav { setNavOpen: (state: boolean) => void } -const AppNav: React.FC = ({ navOpen, setNavOpen }: IAppNav) => { +const AppNav = ({ navOpen, setNavOpen }: IAppNav) => { const { desktop } = useThemeSwitcher() const classes = useStyles() - const { storageSummary } = useFiles() - const { isLoggedIn, secured } = useFilesApi() const { publicKey, isNewDevice, shouldInitializeAccount, logout } = useThresholdKey() - const { removeUser } = useUser() const signOut = useCallback(() => { @@ -236,19 +229,15 @@ const AppNav: React.FC = ({ navOpen, setNavOpen }: IAppNav) => { } }, [desktop, navOpen, setNavOpen]) - const collectFeedback = () => { - window.open(ROUTE_LINKS.DiscordInvite, "_blank") - } - return (
@@ -349,37 +338,25 @@ const AppNav: React.FC = ({ navOpen, setNavOpen }: IAppNav) => {
{desktop && ( -
- { - storageSummary && ( - <> - {`${formatBytes(storageSummary.used_storage, 2)} of ${formatBytes( - storageSummary.total_storage, 2 - )} used`} - - - ) +
+ {storageSummary && ( + <> + {`${formatBytes(storageSummary.used_storage, 2)} of ${formatBytes( + storageSummary.total_storage, 2 + )} used`} + + + ) } -
)} {!desktop && ( diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx index 1cade321ee..516c3a6e19 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx @@ -7,7 +7,12 @@ import { t } from "@lingui/macro" import { CONTENT_TYPES } from "../../../Utils/Constants" import { IFilesTableBrowserProps } from "../../Modules/FileBrowsers/types" import { useHistory, useLocation, useToasts } from "@chainsafe/common-components" -import { extractFileBrowserPathFromURL, getPathWithFile, getUrlSafePathWithFile } from "../../../Utils/pathUtils" +import { + extractFileBrowserPathFromURL, + getAbsolutePathsFromCids, + getUrlSafePathWithFile, + pathEndingWithSlash +} from "../../../Utils/pathUtils" import { ROUTE_LINKS } from "../../FilesRoutes" import { FileBrowserContext } from "../../../Contexts/FileBrowserContext" import { useFilesApi } from "../../../Contexts/FilesApiContext" @@ -25,108 +30,78 @@ const BinFileBrowser: React.FC = ({ controls = false }: const bucket = useMemo(() => buckets.find(b => b.type === "trash"), [buckets]) - const refreshContents = useCallback( - ( - showLoading?: boolean - ) => { - if (!bucket) return - try { - showLoading && setLoadingCurrentPath(true) - filesApiClient.getBucketObjectChildrenList(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, currentPath, filesApiClient] + const refreshContents = useCallback((showLoading?: boolean) => { + if (!bucket) return + try { + showLoading && setLoadingCurrentPath(true) + filesApiClient.getBucketObjectChildrenList(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, currentPath, filesApiClient] ) useEffect(() => { refreshContents(true) }, [bucket, refreshContents]) - const deleteFile = useCallback(async (cid: string) => { - const itemToDelete = pathContents.find((i) => i.cid === cid) + const deleteItems = useCallback(async (cids: string[]) => { + if (!bucket) return - if (!itemToDelete || !bucket) { - console.error("Bucket not set or no item found to delete") - return - } + const pathsToDelete = getAbsolutePathsFromCids(cids, currentPath, pathContents) + + filesApiClient.removeBucketObject(bucket.id, { paths: pathsToDelete }) + .then(() => { + addToast({ + title: t`Data deleted successfully`, + type: "success", + testId: "deletion-success" + }) + }).catch((error) => { + console.error("Error deleting:", error) + addToast({ + title: t`There was an error deleting your data`, + type: "error" + }) + }).finally(() => { + refreshContents() + refreshBuckets() + }) + }, [addToast, bucket, currentPath, filesApiClient, pathContents, refreshBuckets, refreshContents]) - try { - await filesApiClient.removeBucketObject( - bucket.id, - { paths: [getPathWithFile(currentPath, itemToDelete.name)] } - ) + const recoverItems = useCallback(async (cids: string[], newPath: string) => { + if (!bucket) return - refreshContents() - refreshBuckets() - const message = `${ - itemToDelete.isFolder ? t`Folder` : t`File` - } ${t`deleted successfully`}` + const pathsToRecover = getAbsolutePathsFromCids(cids, currentPath, pathContents) + + filesApiClient.moveBucketObjects( + bucket.id, + { + paths: pathsToRecover, + new_path: pathEndingWithSlash(newPath), + destination: buckets.find(b => b.type === "csf")?.id + } + ).then(() => { addToast({ - title: message, - type: "success", - testId: "permanent-deletion-success" + title: t`Data restored successfully`, + type: "success" }) - return Promise.resolve() - } catch (error) { - const message = `${t`There was an error deleting this`} ${ - itemToDelete.isFolder ? t`folder` : t`file` - }` + }).catch((error) => { + console.error("Error recovering:", error) addToast({ - title: message, + title: t`There was an error restoring your data`, type: "error" }) - return Promise.reject() - } - }, [addToast, bucket, currentPath, pathContents, refreshContents, refreshBuckets, filesApiClient]) - - const deleteItems = useCallback(async (cids: string[]) => { - await Promise.all( - cids.map((cid: string) => - deleteFile(cid) - )) - refreshContents() - }, [deleteFile, refreshContents]) - - const recoverItems = useCallback(async (cids: string[], newPath: string) => { - if (!bucket) return - await Promise.all( - cids.map(async (cid: string) => { - const itemToRestore = pathContents.find((i) => i.cid === cid) - if (!itemToRestore) return - try { - await filesApiClient.moveBucketObjects( - bucket.id, - { - paths: [getPathWithFile(currentPath, itemToRestore.name)], - new_path: getPathWithFile(newPath, itemToRestore.name), - destination: buckets.find(b => b.type === "csf")?.id - } - ) - - const message = `${itemToRestore.isFolder ? t`Folder` : t`File`} ${t`recovered successfully`}` - - addToast({ - title: message, - type: "success" - }) - } catch (error) { - const message = `${t`There was an error recovering this`} ${itemToRestore.isFolder ? t`folder` : t`file`}` - addToast({ - title: message, - type: "error" - }) - } - })).finally(refreshContents) + }).finally(refreshContents) }, [addToast, pathContents, refreshContents, filesApiClient, bucket, buckets, currentPath]) const viewFolder = useCallback((cid: string) => { diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx index 56f6d88d0f..86d5fa225b 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx @@ -6,7 +6,9 @@ import { getURISafePathFromArray, getPathWithFile, extractFileBrowserPathFromURL, - getUrlSafePathWithFile + getUrlSafePathWithFile, + getAbsolutePathsFromCids, + pathEndingWithSlash } from "../../../Utils/pathUtils" import { IBulkOperations, IFileBrowserModuleProps, IFilesTableBrowserProps } from "./types" import FilesList from "./views/FilesList" @@ -17,7 +19,6 @@ import { ROUTE_LINKS } from "../../FilesRoutes" import dayjs from "dayjs" import { useFilesApi } from "../../../Contexts/FilesApiContext" import { useUser } from "../../../Contexts/UserContext" -import { useLocalStorage } from "@chainsafe/browser-storage-hooks" import { DISMISSED_SURVEY_KEY } from "../../SurveyBanner" import { FileBrowserContext } from "../../../Contexts/FileBrowserContext" import { parseFileContentResponse } from "../../../Utils/Helpers" @@ -50,10 +51,9 @@ const CSFFileBrowser: React.FC = () => { }).finally(() => showLoading && setLoadingCurrentPath(false)) }, [bucket, filesApiClient, currentPath]) - const { localStorageGet, localStorageSet } = useLocalStorage() - const { profile } = useUser() + const { profile, localStore, setLocalStore } = useUser() - const showSurvey = localStorageGet(DISMISSED_SURVEY_KEY) === "false" + const showSurvey = localStore && localStore[DISMISSED_SURVEY_KEY] === "false" const olderThanOneWeek = useMemo( () => profile?.createdAt @@ -63,11 +63,11 @@ const CSFFileBrowser: React.FC = () => { ) useEffect(() => { - const dismissedFlag = localStorageGet(DISMISSED_SURVEY_KEY) + const dismissedFlag = localStore && localStore[DISMISSED_SURVEY_KEY] if (dismissedFlag === undefined || dismissedFlag === null) { - localStorageSet(DISMISSED_SURVEY_KEY, "false") + setLocalStore({ [DISMISSED_SURVEY_KEY]: "false" }, "update") } - }, [localStorageGet, localStorageSet]) + }, [localStore, setLocalStore]) useEffect(() => { refreshContents(true) @@ -75,42 +75,28 @@ const CSFFileBrowser: React.FC = () => { const moveItemsToBin = useCallback(async (cids: string[], hideToast?: boolean) => { 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, { - paths: [getPathWithFile(currentPath, itemToDelete.name)], - new_path: getPathWithFile("/", itemToDelete.name), - destination: buckets.find(b => b.type === "trash")?.id - }) - if (!hideToast) { - const message = `${ - itemToDelete.isFolder ? t`Folder` : t`File` - } ${t`deleted successfully`}` - addToast({ - title: message, - type: "success", - testId: "deletion-success" - }) - } - return Promise.resolve() - } catch (error) { - const message = `${t`There was an error deleting this`} ${ - itemToDelete.isFolder ? t`folder` : t`file` - }` - addToast({ - title: message, - type: "error" - }) - return Promise.reject() - }} - )).finally(refreshContents) + + const pathsToDelete = getAbsolutePathsFromCids(cids, currentPath, pathContents) + + filesApiClient.moveBucketObjects(bucket.id, { + paths: pathsToDelete, + new_path: "/", + destination: buckets.find(b => b.type === "trash")?.id + }).then(() => { + if (!hideToast) { + addToast({ + title: t`Data moved to bin successfully`, + type: "success", + testId: "deletion-success" + }) + } + }).catch((error) => { + console.error("Error deleting:", error) + addToast({ + title: t`There was an error deleting your data`, + type: "error" + }) + }).finally(refreshContents) }, [addToast, currentPath, pathContents, refreshContents, filesApiClient, bucket, buckets]) // Rename @@ -127,33 +113,26 @@ 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) - if (!itemToMove) return - try { - await filesApiClient.moveBucketObjects(bucket.id, { - paths: [getPathWithFile(currentPath, itemToMove.name)], - new_path: getPathWithFile(newPath, itemToMove.name) - }) - const message = `${ - itemToMove.isFolder ? t`Folder` : t`File` - } ${t`moved successfully`}` - - addToast({ - title: message, - type: "success" - }) - } catch (error) { - const message = `${t`There was an error moving this`} ${ - itemToMove.isFolder ? t`folder` : t`file` - }` - addToast({ - title: message, - type: "error" - }) - } - })).finally(refreshContents) + + + const pathsToMove = getAbsolutePathsFromCids(cids, currentPath, pathContents) + + filesApiClient.moveBucketObjects(bucket.id, { + paths: pathsToMove, + new_path: pathEndingWithSlash(newPath) + }).then(() => { + addToast({ + title: t`Data moved successfully`, + type: "success", + testId: "move-success" + }) + }).catch((error) => { + console.error("Error moving:", error) + addToast({ + title: t`There was an error moving your data`, + type: "error" + }) + }).finally(refreshContents) }, [addToast, pathContents, refreshContents, filesApiClient, bucket, currentPath]) const handleDownload = useCallback(async (cid: string) => { @@ -199,8 +178,8 @@ const CSFFileBrowser: React.FC = () => { }, [currentPath, pathContents, redirect]) const bulkOperations: IBulkOperations = useMemo(() => ({ - [CONTENT_TYPES.Directory]: ["download", "move", "delete"], - [CONTENT_TYPES.File]: ["download", "delete", "move"] + [CONTENT_TYPES.Directory]: ["download", "move", "delete", "share"], + [CONTENT_TYPES.File]: ["download", "delete", "move", "share"] }), []) const itemOperations: IFilesTableBrowserProps["itemOperations"] = useMemo(() => ({ @@ -210,7 +189,7 @@ const CSFFileBrowser: React.FC = () => { [CONTENT_TYPES.Pdf]: ["preview"], [CONTENT_TYPES.Text]: ["preview"], [CONTENT_TYPES.File]: ["download", "info", "rename", "move", "delete", "share"], - [CONTENT_TYPES.Directory]: ["download", "rename", "move", "delete"] + [CONTENT_TYPES.Directory]: ["download", "rename", "move", "delete", "share"] }), []) return ( diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx index 090032121f..d48f5646ab 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/ShareModal.tsx @@ -181,12 +181,11 @@ const useStyles = makeStyles( ) interface IShareFileProps { - file: FileSystemItem + fileSystemItems: FileSystemItem[] close: () => void - filePath: string } -const ShareModal = ({ close, file, filePath }: IShareFileProps) => { +const ShareModal = ({ close, fileSystemItems }: IShareFileProps) => { const classes = useStyles() const { handleCreateSharedFolder } = useCreateOrEditSharedFolder() const { accountRestricted } = useFilesApi() @@ -196,7 +195,7 @@ const ShareModal = ({ close, file, filePath }: IShareFileProps) => { const [keepOriginalFile, setKeepOriginalFile] = useState(true) const [destinationBucket, setDestinationBucket] = useState() const { buckets, transferFileBetweenBuckets } = useFiles() - const { bucket } = useFileBrowser() + const { bucket, currentPath } = useFileBrowser() const { profile } = useUser() const [nameError, setNameError] = useState("") const inSharedBucket = useMemo(() => bucket?.type === "share", [bucket]) @@ -215,7 +214,7 @@ 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 owner or writer + // all buckets where the user is reader 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)))) @@ -288,13 +287,11 @@ const ShareModal = ({ close, file, filePath }: IShareFileProps) => { return } - transferFileBetweenBuckets(bucket.id, file, filePath, bucketToUpload, keepOriginalFile) + transferFileBetweenBuckets(bucket, fileSystemItems, currentPath, bucketToUpload, keepOriginalFile) close() }, [ bucket, destinationBucket, - file, - filePath, handleCreateSharedFolder, isUsingCurrentBucket, sharedFolderName, @@ -302,7 +299,9 @@ const ShareModal = ({ close, file, filePath }: IShareFileProps) => { sharedFolderWriters, keepOriginalFile, close, - transferFileBetweenBuckets + transferFileBetweenBuckets, + currentPath, + fileSystemItems ]) return ( @@ -432,7 +431,7 @@ const ShareModal = ({ close, file, filePath }: IShareFileProps) => { setKeepOriginalFile(!keepOriginalFile)} - label={t`Keep original file`} + label={t`Keep original files`} />
)} diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFileBrowser.tsx index 54523d8fc6..bc45edc992 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFileBrowser.tsx @@ -5,7 +5,9 @@ import { getURISafePathFromArray, getPathWithFile, extractSharedFileBrowserPathFromURL, - getUrlSafePathWithFile + getUrlSafePathWithFile, + getAbsolutePathsFromCids, + pathEndingWithSlash } from "../../../Utils/pathUtils" import { IBulkOperations, IFilesTableBrowserProps } from "./types" import { CONTENT_TYPES } from "../../../Utils/Constants" @@ -21,7 +23,7 @@ import FilesList from "./views/FilesList" import getFilesFromDataTransferItems from "../../../Utils/getFilesFromDataTransferItems" const SharedFileBrowser = () => { - const { downloadFile, uploadFiles, buckets, refreshBuckets, getStorageSummary } = useFiles() + const { downloadFile, uploadFiles, buckets, getStorageSummary, refreshBuckets } = useFiles() const { filesApiClient, accountRestricted } = useFilesApi() const { addToast } = useToasts() const [loadingCurrentPath, setLoadingCurrentPath] = useState(false) @@ -95,48 +97,29 @@ const SharedFileBrowser = () => { refreshContents(true) }, [bucket, refreshContents]) - const deleteFile = useCallback(async (cid: string) => { - const itemToDelete = pathContents.find((i) => i.cid === cid) - - if (!itemToDelete || !bucket) { - console.error("Bucket not set or no item found to delete") - return - } + const deleteItems = useCallback(async (cids: string[]) => { + if (!bucket) return - try { - await filesApiClient.removeBucketObject( - bucket.id, - { paths: [getPathWithFile(currentPath, itemToDelete.name)] } - ) + const pathsToDelete = getAbsolutePathsFromCids(cids, currentPath, pathContents) - refreshContents(false) - refreshBuckets(false) - const message = `${ - itemToDelete.isFolder ? t`Folder` : t`File` - } ${t`deleted successfully`}` - addToast({ - title: message, - type: "success" - }) - return Promise.resolve() - } catch (error) { - const message = `${t`There was an error deleting this`} ${ - itemToDelete.isFolder ? t`folder` : t`file` - }` - addToast({ - title: message, - type: "error" + filesApiClient.removeBucketObject(bucket.id, { paths: pathsToDelete }) + .then(() => { + addToast({ + title: t`Data deleted successfully`, + type: "success", + testId: "deletion-success" + }) + }).catch((error) => { + console.error("Error deleting:", error) + addToast({ + title: t`There was an error deleting your data`, + type: "error" + }) + }).finally(() => { + refreshContents() + refreshBuckets() }) - return Promise.reject() - } - }, [addToast, bucket, currentPath, pathContents, refreshContents, refreshBuckets, filesApiClient]) - - const deleteItems = useCallback(async (cids: string[]) => { - await Promise.all( - cids.map((cid: string) => - deleteFile(cid) - )) - }, [deleteFile]) + }, [addToast, bucket, currentPath, filesApiClient, pathContents, refreshBuckets, refreshContents]) // Rename const renameItem = useCallback(async (cid: string, newName: string) => { @@ -151,16 +134,25 @@ const SharedFileBrowser = () => { 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.moveBucketObjects(bucket.id, { - paths: [getPathWithFile(currentPath, itemToMove.name)], - new_path: getPathWithFile(newPath, itemToMove.name) - }) - })).finally(refreshContents) - }, [refreshContents, filesApiClient, bucket, currentPath, pathContents]) + + const pathsToMove = getAbsolutePathsFromCids(cids, currentPath, pathContents) + + filesApiClient.moveBucketObjects(bucket.id, { + paths: pathsToMove, + new_path: pathEndingWithSlash(newPath) + }).then(() => { + addToast({ + title: t`Data moved successfully`, + type: "success" + }) + }).catch((error) => { + console.error("Error recovering:", error) + addToast({ + title: t`There was an error restoring your data`, + type: "error" + }) + }).finally(refreshContents) + }, [refreshContents, filesApiClient, bucket, currentPath, pathContents, addToast]) const handleDownload = useCallback(async (cid: string) => { const itemToDownload = pathContents.find(item => item.cid === cid) @@ -188,6 +180,18 @@ const SharedFileBrowser = () => { }) return } + let hasFolder = false + for (let i = 0; i < files.length; i++) { + if (fileItems[i].webkitGetAsEntry()?.isDirectory) { + hasFolder = true + } + } + if (hasFolder) { + addToast({ + title: t`Folder uploads are not supported currently`, + type: "error" + }) + } const flattenedFiles = await getFilesFromDataTransferItems(fileItems) const paths = [...new Set(flattenedFiles.map(f => f.filepath))] paths.forEach(p => { @@ -196,43 +200,43 @@ const SharedFileBrowser = () => { }, [uploadFiles, bucket, accountRestricted, addToast]) const bulkOperations: IBulkOperations = useMemo(() => ({ - [CONTENT_TYPES.Directory]: ["download", "move", "delete"], + [CONTENT_TYPES.Directory]: ["download", "move", "delete", "share"], [CONTENT_TYPES.File]: ["download", "delete", "move", "share"] }), []) const itemOperations: IFilesTableBrowserProps["itemOperations"] = useMemo(() => { switch (access) { - case "owner": - return { - [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", "share"], - [CONTENT_TYPES.Directory]: ["rename", "move", "delete"] - } - case "writer": - return { - [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", "report", "share"], - [CONTENT_TYPES.Directory]: ["rename", "move", "delete"] - } - // case "reader": - default: - return { - [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", "report", "share"], - [CONTENT_TYPES.Directory]: [] - } + case "owner": + return { + [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", "share"], + [CONTENT_TYPES.Directory]: ["rename", "move", "delete", "share"] + } + case "writer": + return { + [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", "report", "share"], + [CONTENT_TYPES.Directory]: ["rename", "move", "delete", "share"] + } + // case "reader": + default: + return { + [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", "report", "share"], + [CONTENT_TYPES.Directory]: [] + } } }, [access]) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx index 033606c183..51f508bf0b 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx @@ -168,7 +168,6 @@ const SharedFolderOverview = () => { const openSharedFolder = useCallback((bucketId: string) => { redirect(ROUTE_LINKS.SharedFolderExplorer(bucketId, "/")) }, [redirect]) - return ( <>
{ - const { localStorageGet, localStorageSet } = useLocalStorage() + const { localStore, setLocalStore } = useUser() const [hasSeenSharingExplainerModal, setHasSeenSharingExplainerModal] = useState(false) - const dismissedFlag = localStorageGet(DISMISSED_SHARING_EXPLAINER_KEY) + const dismissedFlag = localStore ? localStore[DISMISSED_SHARING_EXPLAINER_KEY] : null useEffect(() => { if (dismissedFlag === "false"){ setHasSeenSharingExplainerModal(true) } else if (dismissedFlag === null) { // the dismiss flag was never set - localStorageSet(DISMISSED_SHARING_EXPLAINER_KEY, "false") + setLocalStore({ [DISMISSED_SHARING_EXPLAINER_KEY]: "false" }, "update") setHasSeenSharingExplainerModal(true) } - }, [dismissedFlag, localStorageSet]) + }, [dismissedFlag, setLocalStore]) const hideModal = useCallback(() => { - localStorageSet(DISMISSED_SHARING_EXPLAINER_KEY, "true") + setLocalStore({ [DISMISSED_SHARING_EXPLAINER_KEY]: "true" }, "update") setHasSeenSharingExplainerModal(false) - }, [localStorageSet]) + }, [setLocalStore]) return { hasSeenSharingExplainerModal, hideModal } } \ No newline at end of file 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 79c6937151..0dabb8a43a 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 @@ -122,7 +122,7 @@ interface IFileSystemItemProps { browserView: BrowserView reportFile?: (path: string) => void showFileInfo?: (path: string) => void - share?: (path: string, fileIndex: number) => void + handleShare?: (file: FileSystemItemType) => void showPreview?: (fileIndex: number) => void } @@ -145,7 +145,7 @@ const FileSystemItem = ({ resetSelectedFiles, reportFile, showFileInfo, - share, + handleShare, showPreview }: IFileSystemItemProps) => { const { bucket, downloadFile, currentPath, handleUploadOnDrop, moveItems } = useFileBrowser() @@ -247,7 +247,7 @@ const FileSystemItem = ({ ), - onClick: () => share && share(filePath, files?.indexOf(file)) + onClick: () => handleShare && handleShare(file) }, info: { contents: ( @@ -316,9 +316,8 @@ const FileSystemItem = ({ currentPath, downloadFile, moveFile, - share, + handleShare, filePath, - files, showFileInfo, recoverFile, onFilePreview, 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 bb407017c7..6a078ed736 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 @@ -24,6 +24,7 @@ import { BucketKeyPermission } from "../../../../../Contexts/FilesContext" import UserBubble from "../../../../Elements/UserBubble" import { nameValidator } from "../../../../../Utils/validationSchema" import Menu from "../../../../../UI-components/Menu" +import { getUserDisplayName } from "../../../../../Utils/getUserDisplayName" const useStyles = makeStyles(({ breakpoints, constants, palette }: CSFTheme) => { @@ -275,7 +276,7 @@ const SharedFolderRow = ({ bucket, handleRename, openSharedFolder, handleDeleteS {isOwner ? t`me` - : } + : } } { switch (column) { // defaults to name sorting - default: { - temp = sourceFiles.sort((a, b) => { - return a.name.localeCompare(b.name, selectedLocale, { - sensitivity: "base" + default: { + 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 - } + 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) @@ -511,15 +511,15 @@ const FilesList = ({ isShared = false }: Props) => { if (!!permission && isShared) { switch(permission) { - case "owner": - fileOperations = ownerOperations - break - case "writer": - fileOperations = writerOperations - break - case "reader": - fileOperations = readerOperations - break + case "owner": + fileOperations = ownerOperations + break + case "writer": + fileOperations = writerOperations + break + case "reader": + fileOperations = readerOperations + break } } @@ -626,6 +626,12 @@ const FilesList = ({ isShared = false }: Props) => { setIsDeleteModalOpen(true) }, []) + const handleOpenShareDialog = useCallback((e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() + setIsShareModalOpen(true) + }, []) + const mobileMenuItems = useMemo(() => [ { contents: ( @@ -654,16 +660,16 @@ const FilesList = ({ isShared = false }: Props) => { ], [classes.menuIcon, accountRestricted]) - const onShare = useCallback((fileInfoPath: string, fileIndex: number) => { + const onShare = useCallback((fileSystemItem: FileSystemItemType) => { if(hasSeenSharingExplainerModal) { setClickedShare(true) } - setFilePath(fileInfoPath) - setFileIndex(fileIndex) + setSelectedItems([fileSystemItem]) setIsShareModalOpen(true) }, [hasSeenSharingExplainerModal]) + return ( <>
{ Delete selected )} + {validBulkOps.includes("share") && ( + + )} )}
@@ -936,18 +951,18 @@ const FilesList = ({ isShared = false }: Props) => { 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) @@ -955,25 +970,25 @@ 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='table' + reportFile={(fileInfoPath: string) => { + setFilePath(fileInfoPath) setIsReportFileModalOpen(true)} } - showFileInfo={(filePath: string) => { - setFilePath(filePath) + showFileInfo={(fileInfoPath: string) => { + setFilePath(fileInfoPath) setIsFileInfoModalOpen(true) }} showPreview={(fileIndex: number) => { setFileIndex(fileIndex) setIsPreviewOpen(true) }} - share={onShare} + handleShare={onShare} /> ))} @@ -1026,7 +1041,7 @@ const FilesList = ({ isShared = false }: Props) => { setFilePath(fileInfoPath) setIsFileInfoModalOpen(true) }} - share={onShare} + handleShare={onShare} showPreview={(fileIndex: number) => { setFileIndex(fileIndex) setIsPreviewOpen(true) @@ -1034,7 +1049,8 @@ const FilesList = ({ isShared = false }: Props) => { /> ))} - )} + ) + } setIsDeleteModalOpen(false)} @@ -1112,24 +1128,27 @@ const FilesList = ({ isShared = false }: Props) => { }} /> } - { !showExplainerBeforeShare && isShareModalOpen && filePath && fileIndex !== undefined && + { !showExplainerBeforeShare && isShareModalOpen && selectedItems.length && { setIsShareModalOpen(false) setFilePath(undefined) }} - filePath={currentPath} + fileSystemItems={selectedItems} /> } - - {accountRestricted && + {accountRestricted && - } + } + + ) } diff --git a/packages/files-ui/src/Components/Modules/LoginModule/InitializeAccount.tsx b/packages/files-ui/src/Components/Modules/LoginModule/InitializeAccount.tsx index f9aa2c18f4..cca6d89394 100644 --- a/packages/files-ui/src/Components/Modules/LoginModule/InitializeAccount.tsx +++ b/packages/files-ui/src/Components/Modules/LoginModule/InitializeAccount.tsx @@ -22,19 +22,19 @@ const InitializeAccount = ({ className }: IInitializeAccount) => { , [addPasswordShare, resetShouldInitialize]) switch (initializeState) { - case "explainer": - return setInitializeState("setUpPassword")} - /> - case "setUpPassword": - return setInitializeState("explainer")} - setPassword={onSetPassword} - /> - default: - return null + case "explainer": + return setInitializeState("setUpPassword")} + /> + case "setUpPassword": + return setInitializeState("explainer")} + setPassword={onSetPassword} + /> + default: + return null } } diff --git a/packages/files-ui/src/Components/Modules/Settings/ProfileTab/index.tsx b/packages/files-ui/src/Components/Modules/Settings/ProfileTab/index.tsx index 692c3e5768..13ba3547a5 100644 --- a/packages/files-ui/src/Components/Modules/Settings/ProfileTab/index.tsx +++ b/packages/files-ui/src/Components/Modules/Settings/ProfileTab/index.tsx @@ -8,7 +8,8 @@ import { useToasts, RadioInput, TextInput, - CheckIcon + CheckIcon, + Divider } from "@chainsafe/common-components" import { makeStyles, @@ -42,11 +43,6 @@ const useStyles = makeStyles(({ constants, breakpoints, palette, typography }: C borderBottom: "none" } }, - header: { - fontSize: 28, - lineHeight: "32px", - marginBottom: constants.generalUnit * 5 - }, boxContainer: { marginBottom: constants.generalUnit * 4 }, @@ -131,7 +127,7 @@ const useStyles = makeStyles(({ constants, breakpoints, palette, typography }: C borderRadius: 4, paddingLeft: 20, paddingTop: 14, - margin: 6, + margin: "6px 0", [breakpoints.down("sm")]: { width: "100%" }, @@ -330,10 +326,10 @@ const ProfileView = () => { Profile settings + {profile?.publicAddress &&
createStyles({ root: { - paddingTop: constants.generalUnit * 2, paddingBottom: constants.generalUnit * 3, maxWidth: breakpoints.values["md"], [breakpoints.down("md")]: { @@ -160,11 +159,12 @@ const Security = ({ className }: SecurityProps) => { data-cy="settings-security-header" > Sign-in methods + {showWarning && ( createStyles({ container: { - margin: `${constants.generalUnit * 3}px ${constants.generalUnit * 4}px` + padding: constants.generalUnit, + margin: `${constants.generalUnit * 1.5}px 0` }, noCard: { margin: `${constants.generalUnit * 2}px 0` diff --git a/packages/files-ui/src/Components/Modules/Settings/SubscriptionTab/CurrentProduct.tsx b/packages/files-ui/src/Components/Modules/Settings/SubscriptionTab/CurrentProduct.tsx index 0474338b32..ead2f3e880 100644 --- a/packages/files-ui/src/Components/Modules/Settings/SubscriptionTab/CurrentProduct.tsx +++ b/packages/files-ui/src/Components/Modules/Settings/SubscriptionTab/CurrentProduct.tsx @@ -1,140 +1,119 @@ import React from "react" import { - Grid, Button, - Typography, - ProgressBar, formatBytes, - Link + Link, + Loading, + ProgressBar, + Typography } from "@chainsafe/common-components" import { makeStyles, ITheme, createStyles } from "@chainsafe/common-theme" -import clsx from "clsx" +import { ROUTE_LINKS } from "../../../FilesRoutes" import { useFiles } from "../../../../Contexts/FilesContext" import { t, Trans } from "@lingui/macro" -import { ROUTE_LINKS } from "../../../FilesRoutes" +import clsx from "clsx" +import { useBilling } from "../../../../Contexts/BillingContext" -const useStyles = makeStyles(({ constants, palette, breakpoints }: ITheme) => +const useStyles = makeStyles(({ breakpoints, constants }: ITheme) => createStyles({ - container: { - margin: constants.generalUnit * 4 - }, - storageBox: { - maxWidth: 400 - }, - margins: { - marginBottom: constants.generalUnit * 2 - }, - storagePlan: { - marginTop: constants.generalUnit * 4 - }, - earlyAdopter: { - fontWeight: "bold" - }, - essentialContainer: { - width: 300 - }, - subtitle: { - color: palette.additional["gray"][8], - [breakpoints.down("md")]: { - fontSize: 16, - lineHeight: "22px" + root: { + padding: constants.generalUnit, + maxWidth: 240, + "& h2, & h5": { + marginBottom: constants.generalUnit, + fontWeight: 400 + }, + "& h5": { } }, spaceUsedBox: { - marginTop: constants.generalUnit * 3, [breakpoints.down("md")]: { marginBottom: constants.generalUnit, width: "inherit" } }, - spaceUsedMargin: { - marginBottom: constants.generalUnit + usageBar: { + maxWidth: "70%", + marginBottom: constants.generalUnit, + marginTop: constants.generalUnit, + overflow: "hidden" }, - changePlanButton: { - marginTop: constants.generalUnit * 2, - width: "inherit" + buttons: { + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + "& > *:first-child": { + marginRight: constants.generalUnit + } }, link: { + display: "block", + width: "100%", textDecoration: "none" } }) ) -const CurrentProduct: React.FC = () => { +interface ICurrentProduct { + className?: string +} + +const CurrentProduct = ({ className }: ICurrentProduct) => { const classes = useStyles() const { storageSummary } = useFiles() + const { currentSubscription } = useBilling() - return ( - - - {storageSummary && -
-
- - Storage Plan - - - {t`Early Adopter: Free up to ${formatBytes( - storageSummary.total_storage, 2 - )}`} - - - {t`Your first ${formatBytes( - storageSummary.total_storage, 2 - )} are free, and you’ll get a discount on our monthly plan once you need more than that`}. - -
-
-
- {t`${formatBytes(storageSummary.used_storage, 2)} of ${formatBytes( - storageSummary.total_storage, 2 - )} used (${Math.ceil(storageSummary.used_storage / storageSummary.total_storage) * 100}%)`} - - -
- + + Your plan + + { + currentSubscription + ? + {currentSubscription?.product.name} + + : + } + {storageSummary && + <> +
+ + {t`${formatBytes(storageSummary.used_storage, 2)} of ${formatBytes( + storageSummary.total_storage, 2 + )} used`} ({((storageSummary.used_storage / storageSummary.total_storage) * 100).toFixed(1)}%) + + +
+
+ + - -
+ Change Plan + +
- } + + } - - - ) + ) } -export default CurrentProduct +export default CurrentProduct \ No newline at end of file diff --git a/packages/files-ui/src/Components/Modules/Settings/SubscriptionTab/index.tsx b/packages/files-ui/src/Components/Modules/Settings/SubscriptionTab/index.tsx index 4a818a1bec..159d510dae 100644 --- a/packages/files-ui/src/Components/Modules/Settings/SubscriptionTab/index.tsx +++ b/packages/files-ui/src/Components/Modules/Settings/SubscriptionTab/index.tsx @@ -1,16 +1,19 @@ import React from "react" -import CurrentProduct from "./CurrentProduct" import CurrentCard from "./CurrentCard" import { Divider, Typography } from "@chainsafe/common-components" import { makeStyles, createStyles, ITheme } from "@chainsafe/common-theme" import { Trans } from "@lingui/macro" import { Elements } from "@stripe/react-stripe-js" import { loadStripe } from "@stripe/stripe-js" +import CurrentProduct from "./CurrentProduct" -const useStyles = makeStyles(({ constants }: ITheme) => +const useStyles = makeStyles(({ breakpoints, constants }: ITheme) => createStyles({ - heading: { - marginBottom: constants.generalUnit * 2 + root: { + [breakpoints.down("sm")]: { + paddingLeft: constants.generalUnit, + paddingRight: constants.generalUnit + } } }) ) @@ -22,11 +25,10 @@ const PlanView: React.FC = () => { return ( -
+
Payment and Subscriptions diff --git a/packages/files-ui/src/Components/Modules/Settings/index.tsx b/packages/files-ui/src/Components/Modules/Settings/index.tsx index 709fc17fbf..0ad4e352f5 100644 --- a/packages/files-ui/src/Components/Modules/Settings/index.tsx +++ b/packages/files-ui/src/Components/Modules/Settings/index.tsx @@ -1,32 +1,32 @@ import React, { useCallback, useMemo } from "react" import ProfileTab from "./ProfileTab" -import { Tabs as TabsOrigin, +import { + Tabs, TabPane as TabPaneOrigin, - Typography, Divider, + Typography, + Divider, Breadcrumb, Crumb, useParams, useHistory, ITabPaneProps, CaretRightIcon, - SubscriptionPlanIcon, - LockIcon, - ITabsProps + LockIcon } from "@chainsafe/common-components" import { makeStyles, ITheme, createStyles, useThemeSwitcher } from "@chainsafe/common-theme" import { ROUTE_LINKS, SettingsPath } from "../../FilesRoutes" import { t, Trans } from "@lingui/macro" import SubscriptionTab from "./SubscriptionTab" -import { ProfileIcon } from "@chainsafe/common-components" +import { ProfileIcon, SubscriptionPlanIcon } from "@chainsafe/common-components" import clsx from "clsx" import SecurityTab from "./SecurityTab" -const Tabs = (props: ITabsProps) => TabsOrigin(props) const TabPane = (props: ITabPaneProps) => TabPaneOrigin(props) const useStyles = makeStyles(({ constants, breakpoints, palette }: ITheme) => createStyles({ title: { marginTop: constants.generalUnit, + cursor: "pointer", [breakpoints.down("md")]: { fontSize: 20, lineHeight: "28px", @@ -59,7 +59,7 @@ const useStyles = makeStyles(({ constants, breakpoints, palette }: ITheme) => tabsContainer: { borderRadius: 10, backgroundColor: palette.additional["gray"][3], - marginTop: constants.generalUnit * 4, + marginTop: constants.generalUnit * 2, [breakpoints.down("md")]: { borderRadius: 0, marginTop: 0 @@ -67,13 +67,7 @@ const useStyles = makeStyles(({ constants, breakpoints, palette }: ITheme) => }, tabPane: { flex: 1, - padding: `${constants.generalUnit * 4}px ${constants.generalUnit * 4}px`, - "&.fullWidthTab": { - [breakpoints.down("lg")]: { - paddingLeft: constants.generalUnit, - paddingRight: constants.generalUnit - } - }, + padding: `${constants.generalUnit * 2}px ${constants.generalUnit * 2}px`, [breakpoints.down("md")]: { padding: `${constants.generalUnit * 2}px 0` } @@ -138,8 +132,7 @@ const Settings: React.FC = () => { const { desktop } = useThemeSwitcher() const { path = desktop ? "profile" : undefined } = useParams<{path: SettingsPath}>() const classes = useStyles() - const { redirect } = useHistory() - + const { redirect, history } = useHistory() const onSelectTab = useCallback( (key: SettingsPath) => redirect(ROUTE_LINKS.SettingsPath(key)) @@ -162,6 +155,7 @@ const Settings: React.FC = () => { variant="h1" component="p" className={classes.title} + onClick={() => history.push(ROUTE_LINKS.SettingsDefault)} > Settings @@ -195,7 +189,7 @@ const Settings: React.FC = () => { } iconRight={} title={t`Security`} @@ -205,7 +199,7 @@ const Settings: React.FC = () => { } diff --git a/packages/files-ui/src/Components/SharingExplainerModal.tsx b/packages/files-ui/src/Components/SharingExplainerModal.tsx index b343186713..4061e965dd 100644 --- a/packages/files-ui/src/Components/SharingExplainerModal.tsx +++ b/packages/files-ui/src/Components/SharingExplainerModal.tsx @@ -1,4 +1,3 @@ -import { useLocalStorage } from "@chainsafe/browser-storage-hooks" import { Button, Link, Typography } from "@chainsafe/common-components" import { createStyles, makeStyles } from "@chainsafe/common-theme" import { t, Trans } from "@lingui/macro" @@ -10,6 +9,7 @@ import step1Image from "../Media/sharingExplainer/step1.png" import step2Image from "../Media/sharingExplainer/step2.png" import step3Image from "../Media/sharingExplainer/step3.png" import { DISMISSED_SHARING_EXPLAINER_KEY } from "./Modules/FileBrowsers/hooks/useSharingExplainerModalFlag" +import { useUser } from "../Contexts/UserContext" const useStyles = makeStyles( ({ palette, constants }: CSFTheme) => { @@ -68,61 +68,61 @@ const useStyles = makeStyles( }) interface Props { - showModal: boolean - onHide: () => void + showModal: boolean + onHide: () => void } const STEP_NUMBER = 3 const SharingExplainerModal = ({ showModal, onHide }: Props) => { const classes = useStyles() - const { localStorageSet } = useLocalStorage() + const { setLocalStore } = useUser() const [step, setStep] = useState(1) const Slides = useCallback(() => { switch (step) { - default: - return <> -
- You can now create shared folders to share a file.
-
- {"share -
- + default: + return <> +
+ You can now create shared folders to share a file.
+
+ {"share +
+ - case 2: - return <> -
Add viewers and editors by username, sharing id or Ethereum address.
-
- {"share -
- + case 2: + return <> +
Add viewers and editors by username, sharing id or Ethereum address.
+
+ {"share +
+ - case 3: - return <> -
- Create your public username in Settings! - -
-
- {"share -
- + case 3: + return <> +
+ Create your public username in Settings! + +
+
+ {"share +
+ } }, [classes.buttonLink, classes.image, classes.imageContainer, classes.title, step]) @@ -132,18 +132,18 @@ const SharingExplainerModal = ({ showModal, onHide }: Props) => { return } else { switch (next) { - case 3: - localStorageSet(DISMISSED_SHARING_EXPLAINER_KEY, "true") - setStep(3) - break - case STEP_NUMBER + 1: - onHide() - break - default: - break + case 3: + setLocalStore({ [DISMISSED_SHARING_EXPLAINER_KEY]: "true" }, "update") + setStep(3) + break + case STEP_NUMBER + 1: + onHide() + break + default: + break } } - }, [localStorageSet, onHide]) + }, [setLocalStore, onHide]) return ( void + onHide: () => void } const SurveyBanner = ({ onHide }: Props) => { const classes = useStyles() - const { localStorageSet } = useLocalStorage() + const { setLocalStore } = useUser() const onClose = useCallback(() => { onHide() - localStorageSet(DISMISSED_SURVEY_KEY, "true") - }, [localStorageSet, onHide]) + setLocalStore({ [DISMISSED_SURVEY_KEY]: "true" }, "update") + }, [setLocalStore, onHide]) const onOpen = useCallback(() => { onClose() diff --git a/packages/files-ui/src/Contexts/BillingContext.tsx b/packages/files-ui/src/Contexts/BillingContext.tsx index 8059e1357a..8e683680ae 100644 --- a/packages/files-ui/src/Contexts/BillingContext.tsx +++ b/packages/files-ui/src/Contexts/BillingContext.tsx @@ -1,16 +1,37 @@ import * as React from "react" import { useFilesApi } from "./FilesApiContext" -import { useEffect, useState } from "react" -import { Card } from "@chainsafe/files-api-client" +import { ReactNode, useEffect, useState } from "react" +import { Card, CurrentSubscription } from "@chainsafe/files-api-client" import { useCallback } from "react" +import { t } from "@lingui/macro" type BillingContextProps = { - children: React.ReactNode | React.ReactNode[] + children: ReactNode | ReactNode[] } interface IBillingContext { defaultCard: Card | undefined refreshDefaultCard: () => void + currentSubscription: CurrentSubscription | undefined + fetchCurrentSubscription: () => void +} + +const ProductMapping: {[key: string]: { + name: string + description: string +}} = { + prod_JwRu6Ph25b1f2O: { + name: t`Free plan`, + description: t`This is the free product.` + }, + prod_JwS49Qfnr6vD3K: { + name: t`Standard plan`, + description: t`Standard plan` + }, + prod_JwSGHB8qFx7rRM: { + name: t`Premium plan`, + description: t`Premium plan` + } } const BillingContext = React.createContext( @@ -19,6 +40,7 @@ const BillingContext = React.createContext( const BillingProvider = ({ children }: BillingContextProps) => { const { filesApiClient, isLoggedIn } = useFilesApi() + const [currentSubscription, setCurrentSubscription] = useState() const [defaultCard, setDefaultCard] = useState(undefined) const refreshDefaultCard = useCallback(() => { @@ -36,11 +58,33 @@ const BillingProvider = ({ children }: BillingContextProps) => { } }, [refreshDefaultCard, isLoggedIn, filesApiClient]) + const fetchCurrentSubscription = useCallback(() => { + filesApiClient.getCurrentSubscription() + .then((subscription) => { + subscription.product.name = ProductMapping[subscription.product.id].name + subscription.product.description = ProductMapping[subscription.product.id].description + setCurrentSubscription(subscription) + }) + .catch((error: any) => { + console.error(error) + }) + }, [filesApiClient]) + + useEffect(() => { + if (isLoggedIn && !currentSubscription) { + fetchCurrentSubscription() + } else if (!isLoggedIn) { + setCurrentSubscription(undefined) + } + }, [isLoggedIn, fetchCurrentSubscription, currentSubscription]) + return ( {children} diff --git a/packages/files-ui/src/Contexts/FilesApiContext.tsx b/packages/files-ui/src/Contexts/FilesApiContext.tsx index c8f69c6456..02bf865579 100644 --- a/packages/files-ui/src/Contexts/FilesApiContext.tsx +++ b/packages/files-ui/src/Contexts/FilesApiContext.tsx @@ -66,7 +66,7 @@ const FilesApiProvider = ({ apiUrl, withLocalStorage = true, children }: FilesAp const [accountRestricted, setAccountRestricted] = useState(false) const [refreshToken, setRefreshToken] = useState(undefined) const [decodedRefreshToken, setDecodedRefreshToken] = useState< - { exp: number; enckey?: string; mps?: string; uuid: string } | undefined + { exp: number; enckey?: string; mps?: string; uuid: string } | undefined >(undefined) // returning user diff --git a/packages/files-ui/src/Contexts/FilesContext.tsx b/packages/files-ui/src/Contexts/FilesContext.tsx index 732899178a..194a5a3ffa 100644 --- a/packages/files-ui/src/Contexts/FilesContext.tsx +++ b/packages/files-ui/src/Contexts/FilesContext.tsx @@ -80,9 +80,9 @@ type FilesContext = { readers?: UpdateSharedFolderUser[] ) => Promise transferFileBetweenBuckets: ( - sourceBucketId: string, - sourceFile: FileSystemItem, - path: string, + sourceBucket: BucketKeyPermission, + sourceItems: FileSystemItem[], + currentPath: string, destinationBucket: BucketKeyPermission, deleteFromSource?: boolean ) => Promise @@ -160,15 +160,15 @@ const FilesProvider = ({ children }: FilesContextProps) => { let encryptionKey = "" switch(bucket.type) { - case "csf": - case "trash": { - encryptionKey = personalEncryptionKey - break - } - case "share": { - encryptionKey = await getKeyForSharedBucket(bucket) - break - }} + case "csf": + case "trash": { + encryptionKey = personalEncryptionKey + break + } + case "share": { + encryptionKey = await getKeyForSharedBucket(bucket) + break + }} return encryptionKey }, [getKeyForSharedBucket, personalEncryptionKey, userId]) @@ -380,8 +380,7 @@ const FilesProvider = ({ children }: FilesContextProps) => { type: "success", progress: 0, onProgressCancel: cancelSource.cancel, - isClosable: false, - testId: "upload-status" + isClosable: false } const toastId = addToast(toastParams) @@ -411,7 +410,8 @@ const FilesProvider = ({ children }: FilesContextProps) => { title: "Upload complete", progress: undefined, onProgressCancel: undefined, - isClosable: true + isClosable: true, + testId: "upload-complete" }, true) return Promise.resolve() } catch (error: any) { @@ -748,98 +748,128 @@ const FilesProvider = ({ children }: FilesContextProps) => { }, [publicKey, getUsersWithEncryptionKey, filesApiClient, refreshBuckets]) const transferFileBetweenBuckets = useCallback(async ( - sourceBucketId: string, - sourceFile: FileSystemItem, - path: string, + sourceBucket: BucketKeyPermission, + sourceItems: FileSystemItem[], + currentPath: string, destinationBucket: BucketKeyPermission, keepOriginal = true ) => { - const UPLOAD_PATH = "/" + getFileList(sourceItems, currentPath, sourceBucket.id).then(async (allItems) => { + setTransfersInProgress(true) + const inSharedBucket = sourceBucket.type === "share" + const cancelSource = axios.CancelToken.source() + const cancelToken = cancelSource.token + + const totalFileSize = allItems.reduce((sum, item) => sum + item.size, 0) + const totalFileNumber = allItems.length + + if (!totalFileNumber) { + addToast({ + title: inSharedBucket + ? t`No files to copy` + : t`No files to share`, + type: "error" + }) + return + } - const cancelSource = axios.CancelToken.source() - const cancelToken = cancelSource.token + const toastParams: ToastParams = { + title: inSharedBucket + ? t`Copying files` + : t`Sharing files`, + type: "success", + progress: 0, + onProgressCancel: cancelSource.cancel, + isClosable: false + } + const toastId = addToast(toastParams) + let successCount = 0 + + try { + await allItems.reduce(async (totalProgress: Promise, item, i) => { + const previousProgress = await totalProgress + const fileProgress = `${i + 1}/${totalFileNumber}` + try { + const file = await getFileContent(sourceBucket.id, { + cid: item.cid, + file: item, + path: getPathWithFile(item.path, item.name), + cancelToken, + onDownloadProgress: async (progressEvent) => { + const currentProgress = Math.ceil((((2 * previousProgress) + progressEvent.loaded) * 50) / totalFileSize) + updateToast(toastId, { + ...toastParams, + title: t`${inSharedBucket ? "Copying" : "Sharing"} ${fileProgress} - ${item.name}`, + progress: currentProgress + }) + } + }) - const toastParams: ToastParams = { - title: t`Sharing your file (Downloading)`, - type: "success", - progress: 0, - onProgressCancel: cancelSource.cancel, - isClosable: false - } - const toastId = addToast(toastParams) - setTransfersInProgress(true) + if(file) { + await encryptAndUploadFiles( + destinationBucket, + [new File([file], item.name, { type: item.content_type })], + getRelativePath(item.path, currentPath), + async (progressEvent) => { + const currentProgress = Math.ceil((((2 * previousProgress) + progressEvent.loaded + item.size) * 50) / totalFileSize) + updateToast(toastId, { + title: t`${inSharedBucket ? "Copying" : "Sharing"} ${fileProgress} - ${item.name}`, + type: "success", + progress: currentProgress + }) + }, + cancelToken + ) + } + + if (!keepOriginal) { + await filesApiClient.removeBucketObject(sourceBucket.id, { paths: [getPathWithFile(item.path, item.name)] }) + } + successCount++ + return previousProgress + item.size + } catch (error) { + console.error(error) + return previousProgress + item.size + } + }, Promise.resolve(0)) - getFileContent(sourceBucketId, { - cid: sourceFile.cid, - file: sourceFile, - path: getPathWithFile(path, sourceFile.name), - cancelToken, - onDownloadProgress: (progressEvent) => { - updateToast(toastId, { - ...toastParams, - progress: Math.ceil( - (progressEvent.loaded / sourceFile.size) * 50 - ) - }) - } - }).then(async (fileContent) => { - if (!fileContent) { updateToast(toastId, { - title: t`An error occurred while downloading the file`, - type: "error", - progress: undefined, - onProgressCancel: undefined, + title: successCount === totalFileNumber + ? t`${inSharedBucket ? "Copying" : "Sharing"} complete` + : successCount + ? t`${successCount} files transferred successfully, ${totalFileNumber - successCount} failed` + : t`${inSharedBucket ? "Copying" : "Sharing"} failed`, + type: "success", + progress: undefined, isClosable: true }, true) setTransfersInProgress(false) - return - } - - await encryptAndUploadFiles( - destinationBucket, - [new File([fileContent], sourceFile.name, { type: sourceFile.content_type })], - UPLOAD_PATH, - (progressEvent) => { - updateToast(toastId, { - title: t`Encrypting & uploading`, - type: "success", - progress: Math.ceil( - 50 + (progressEvent.loaded / sourceFile.size) * 50 - ) - }) - }, - cancelToken - ) + refreshBuckets() - if (!keepOriginal) { - await filesApiClient.removeBucketObject(sourceBucketId, { paths: [getPathWithFile(path, sourceFile.name)] }) + } catch (error) { + console.error(error) + setTransfersInProgress(false) + let errorMessage = successCount + ? t`${successCount} files transferred successfully, ${totalFileNumber - successCount} failed` + : t`${inSharedBucket ? "Copying" : "Sharing"} failed` + if (axios.isCancel(error)) { + errorMessage = successCount + ? t`${ + inSharedBucket ? "Copying" : "Sharing" + } cancelled - ${successCount} files ${inSharedBucket ? "copied" : "shared"} successfully` + : t`${inSharedBucket ? "Copying" : "Sharing"} cancelled` + } + updateToast(toastId, { + title: errorMessage, + type: "error", + progress: undefined, + isClosable: true + }, true) } - updateToast(toastId, { - title: t`Transfer complete`, - type: "success", - progress: undefined, - isClosable: true - }, true) - setTransfersInProgress(false) - return Promise.resolve() }).catch((error) => { console.error(error) - let errorMessage = `${t`An error occurred: `} ${typeof(error) === "string" ? error : error.length ? error[0].message : ""}` - if (axios.isCancel(error)) { - errorMessage = t`Sharing cancelled` - } - updateToast(toastId, { - title: errorMessage, - type: "error", - progress: undefined, - onProgressCancel: undefined, - isClosable: true - }, true) - setTransfersInProgress(false) - }).finally(() => { - refreshBuckets() }) - }, [getFileContent, encryptAndUploadFiles, filesApiClient, refreshBuckets, addToast, updateToast]) + }, [getFileContent, encryptAndUploadFiles, filesApiClient, refreshBuckets, addToast, updateToast, getFileList]) return ( { const [showBanner, setShowBanner] = useState(false) const [hasTouchedCookieBanner, setHasTouchedCookieBanner ] = useState(false) const { localStorageGet, localStorageSet } = useLocalStorage() + const { profile } = useUser() const classes = useStyles() const posthogInitialized = useMemo(() => @@ -114,6 +116,14 @@ const PosthogProvider = ({ children }: PosthogProviderProps) => { } }, [posthogInitialized, touchCookieBanner]) + useEffect(() => { + if (profile) { + posthogInitialized && posthog.identify(profile.userId) + } else { + posthogInitialized && posthog.reset() + } + }, [profile, posthogInitialized]) + return ( { +{typeOfLogin: LOGIN_TYPE; clientId: string; verifier: string; jwtParams?: any} => { switch (loginType) { - case "google": { - return { - typeOfLogin: loginType, - clientId: process.env.REACT_APP_GOOGLE_CLIENT_ID || "", - verifier: "chainsafe-uuid-testnet" + case "google": { + return { + typeOfLogin: loginType, + clientId: process.env.REACT_APP_GOOGLE_CLIENT_ID || "", + verifier: "chainsafe-uuid-testnet" + } } - } - case "github":{ - return { - typeOfLogin: loginType, - clientId: process.env.REACT_APP_AUTH0_CLIENT_ID || "", - verifier: "chainsafe-uuid-testnet", - jwtParams: { - domain: process.env.REACT_APP_AUTH0_DOMAIN || "" + case "github":{ + return { + typeOfLogin: loginType, + clientId: process.env.REACT_APP_AUTH0_CLIENT_ID || "", + verifier: "chainsafe-uuid-testnet", + jwtParams: { + domain: process.env.REACT_APP_AUTH0_DOMAIN || "" + } } } - } - default:{ - throw new Error(`${loginType} is unsupported`) - } + default:{ + throw new Error(`${loginType} is unsupported`) + } } } @@ -246,7 +246,7 @@ const ThresholdKeyProvider = ({ children, network = "mainnet", enableLogging = f } else { setPrivateKey(privKeyString) } - } catch (error) { + } catch (error: any) { // Under certain circumstances (approval of login on another device) the metadata // cached may be stale, resulting in a failure to reconstruct the key. This is // identified through the nonce. Manually refreshing the metadata cache solves this problem @@ -382,16 +382,16 @@ const ThresholdKeyProvider = ({ children, network = "mainnet", enableLogging = f if (userInfo && loginType) { switch (loginType) { - case "jwt": - setLoggedinAs(t`Web3: ${centerEllipsis(String(address), 4)}`) - break - case "google": - case "github": - setLoggedinAs(`${capitalize(loginType)}: ${centerEllipsis(userInfo.userInfo.email, 4)}`) - break - default: - setLoggedinAs(`${centerEllipsis(userInfo.publicAddress, 4)}`) - break + case "jwt": + setLoggedinAs(t`Web3: ${centerEllipsis(String(address), 4)}`) + break + case "google": + case "github": + setLoggedinAs(`${capitalize(loginType)}: ${centerEllipsis(userInfo.userInfo.email, 4)}`) + break + default: + setLoggedinAs(`${centerEllipsis(userInfo.publicAddress, 4)}`) + break } } }, [userInfo, address]) @@ -781,7 +781,7 @@ const ThresholdKeyProvider = ({ children, network = "mainnet", enableLogging = f const newKeyDetails = await TKeySdk.getKeyDetails() setKeyDetails(newKeyDetails) return - } catch (error) { + } catch (error: any) { if (error.message.includes("nonce")) { await TKeySdk.updateMetadata() await TKeySdk.syncShareMetadata() diff --git a/packages/files-ui/src/Contexts/UserContext.tsx b/packages/files-ui/src/Contexts/UserContext.tsx index 92abc2cd56..3250078193 100644 --- a/packages/files-ui/src/Contexts/UserContext.tsx +++ b/packages/files-ui/src/Contexts/UserContext.tsx @@ -18,8 +18,14 @@ export type Profile = { username?: string } +interface ILocalStore { + [key: string]: any +} + interface IUserContext { profile: Profile | undefined + localStore: ILocalStore | undefined + setLocalStore: (newData: ILocalStore, method?: "update" | "overwrite") => void refreshProfile(): Promise updateProfile: ( firstName: string, @@ -34,10 +40,26 @@ interface IUserContext { const UserContext = React.createContext(undefined) + const UserProvider = ({ children }: UserContextProps) => { const { filesApiClient, isLoggedIn } = useFilesApi() const [profile, setProfile] = useState(undefined) + const [localStore, _setLocalStore] = useState() + + const setLocalStore = useCallback((newData: ILocalStore, method: "update" | "overwrite" = "update") => { + switch (method) { + case "update": + _setLocalStore({ + ...localStore, + ...newData + }) + break + case "overwrite": + _setLocalStore(newData) + break + } + }, [localStore]) const refreshProfile = useCallback(async () => { try { @@ -59,6 +81,31 @@ const UserProvider = ({ children }: UserContextProps) => { } }, [filesApiClient]) + useEffect(() => { + const manageAsync = async () => { + if (!localStore) { + // Fetch + try { + const fetched = await filesApiClient.getUserLocalStore() + if (!fetched) { + _setLocalStore({}) + } else { + _setLocalStore(fetched) + } + } catch(error) { + console.error(error) + _setLocalStore({}) + } + } else { + // Store + await filesApiClient.updateUserLocalStore(localStore) + } + } + if (isLoggedIn) { + manageAsync() + } + }, [isLoggedIn, localStore, filesApiClient]) + useEffect(() => { if (isLoggedIn) { refreshProfile() @@ -132,7 +179,10 @@ const UserProvider = ({ children }: UserContextProps) => { } } - const removeUser = useCallback(() => setProfile(undefined), []) + const removeUser = useCallback(() => { + setProfile(undefined) + _setLocalStore(undefined) + }, []) const getProfileTitle = () => { if (profile?.publicAddress) { @@ -149,6 +199,8 @@ const UserProvider = ({ children }: UserContextProps) => { return ( ({ } } }, + tertiary: { + root: { + backgroundColor: "var(--gray5)", + color: "var(--gray9)", + "& svg": { + fill: "var(--gray9)" + } + }, + active: { + backgroundColor: "var(--gray7)", + color: "var(--gray9)", + "& svg": { + fill: "var(--gray9)" + } + }, + hover: { + backgroundColor: "var(--gray7)", + color: "var(--gray9)", + "& svg": { + fill: "var(--gray9)" + } + }, + focus: { + color: "none", + backgroundColor: "none", + "& svg": { + fill: "none" + } + } + }, outline: { root: { backgroundColor: "var(--gray3) !important", diff --git a/packages/files-ui/src/UI-components/Menu.tsx b/packages/files-ui/src/UI-components/Menu.tsx index de15dbe51a..c5f99c988f 100644 --- a/packages/files-ui/src/UI-components/Menu.tsx +++ b/packages/files-ui/src/UI-components/Menu.tsx @@ -6,23 +6,23 @@ import { useCallback } from "react" import { CSFTheme } from "../Themes/types" interface Option { - contents: ReactNode - onClick?: () => void - disabled?: boolean + contents: ReactNode + onClick?: () => void + disabled?: boolean } interface CustomClasses { - iconContainer?: string - menuWrapper?: string - focusVisible?: string - root?: string + iconContainer?: string + menuWrapper?: string + focusVisible?: string + root?: string } interface Props { - icon?: ReactNode - options: Option[] - style?: CustomClasses - testId?: string + icon?: ReactNode + options: Option[] + style?: CustomClasses + testId?: string } const useStyles = makeStyles(({ constants }: CSFTheme) => { diff --git a/packages/files-ui/src/Utils/MimeMatcher.ts b/packages/files-ui/src/Utils/MimeMatcher.ts index d0466c7d73..c2c1186aec 100644 --- a/packages/files-ui/src/Utils/MimeMatcher.ts +++ b/packages/files-ui/src/Utils/MimeMatcher.ts @@ -46,48 +46,48 @@ function matcher(expected: string[]) { } class MimeMatcher { - expected = [] as { typeMatcher: (actual: string) => boolean; subTypeMatcher: (actual: string) => boolean }[] + expected = [] as { typeMatcher: (actual: string) => boolean; subTypeMatcher: (actual: string) => boolean }[] - constructor(expected: string | string[]) { - if (Array.isArray(expected)) { - this.expected = expected.map(mimeType => { - const { valid, type, subType } = parse(mimeType) - if (valid && type && subType) { - return ({ - typeMatcher: createMatcher(type), - subTypeMatcher: createMatcher(subType) - }) - } else { - const msg = `Value "${mimeType}" is not valid mime type.It should have format "type/subtype".` - throw new TypeError(msg) - } - }) - } - else { - const { valid, type, subType } = parse(expected) + constructor(expected: string | string[]) { + if (Array.isArray(expected)) { + this.expected = expected.map(mimeType => { + const { valid, type, subType } = parse(mimeType) if (valid && type && subType) { - this.expected = [{ + return ({ typeMatcher: createMatcher(type), subTypeMatcher: createMatcher(subType) - }] + }) } else { - const msg = `Value "${expected}" is not valid mime type.It should have format "type/subtype".` + const msg = `Value "${mimeType}" is not valid mime type.It should have format "type/subtype".` throw new TypeError(msg) } - } - + }) } - - match(actual: string) { - const { valid, type, subType } = parse(actual) + else { + const { valid, type, subType } = parse(expected) if (valid && type && subType) { - return this.expected.some(({ typeMatcher, subTypeMatcher }) => { - return typeMatcher(type) && subTypeMatcher(subType) - }) + this.expected = [{ + typeMatcher: createMatcher(type), + subTypeMatcher: createMatcher(subType) + }] } else { - return false + const msg = `Value "${expected}" is not valid mime type.It should have format "type/subtype".` + throw new TypeError(msg) } } + + } + + match(actual: string) { + const { valid, type, subType } = parse(actual) + if (valid && type && subType) { + return this.expected.some(({ typeMatcher, subTypeMatcher }) => { + return typeMatcher(type) && subTypeMatcher(subType) + }) + } else { + return false + } + } } export { isValid, parse, matcher } diff --git a/packages/files-ui/src/Utils/contentTypeGuesser.ts b/packages/files-ui/src/Utils/contentTypeGuesser.ts index 6f7af8db43..ae7b19cdd3 100644 --- a/packages/files-ui/src/Utils/contentTypeGuesser.ts +++ b/packages/files-ui/src/Utils/contentTypeGuesser.ts @@ -2,25 +2,25 @@ const guessContentType = (fileName: string) => { const { length, [length - 1]: ext } = fileName.split(".") switch (ext) { - case "pdf": - return "application/pdf" - case "jpg": - case "jpeg": - case "png": - case "gif": - case "bmp": - return `image/${ext}` - case "mp3": - case "m4a": - return `audio/${ext}` - case "mp4": - return `video/${ext}` - case "txt": - return "text/plain" - case "md": - return "text/markdown" - default: - return "application/octet-stream" + case "pdf": + return "application/pdf" + case "jpg": + case "jpeg": + case "png": + case "gif": + case "bmp": + return `image/${ext}` + case "mp3": + case "m4a": + return `audio/${ext}` + case "mp4": + return `video/${ext}` + case "txt": + return "text/plain" + case "md": + return "text/markdown" + default: + return "application/octet-stream" } } diff --git a/packages/files-ui/src/Utils/pathUtils.ts b/packages/files-ui/src/Utils/pathUtils.ts index 06419a65b1..ee3cf3a5f6 100644 --- a/packages/files-ui/src/Utils/pathUtils.ts +++ b/packages/files-ui/src/Utils/pathUtils.ts @@ -1,3 +1,5 @@ +import { FileSystemItem } from "../Contexts/FilesContext" + // trims a string at both ends for a character export function trimChar(str: string, char: string) { char = char.charAt(0) @@ -109,3 +111,20 @@ export const getUrlSafePathWithFile = (path: string, fileName: string) => { return `${urlSafePath}/${encodeURIComponent(fileName)}` } + +export const getAbsolutePathsFromCids = (cids: string[], currentPath: string, pathContents: FileSystemItem[]) => { + return cids.map((cid: string) => { + const item = pathContents.find((i) => i.cid === cid) + if (!item) { + return undefined + } + return getPathWithFile(currentPath, item.name) + }).filter((item): item is string => !!item) +} + +export const pathEndingWithSlash = (path: string) => { + const lastChar = path.substr(-1) + return lastChar === "/" + ? path + : `${path}/` +} \ No newline at end of file diff --git a/packages/files-ui/src/locales/de/messages.po b/packages/files-ui/src/locales/de/messages.po index 762d2c5117..c643a7cf1d 100644 --- a/packages/files-ui/src/locales/de/messages.po +++ b/packages/files-ui/src/locales/de/messages.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-04-20 00:28+0200\n" -"PO-Revision-Date: 2021-09-22 21:38+0000\n" +"PO-Revision-Date: 2021-09-29 19:36+0000\n" "Last-Translator: J. Lavoie \n" "Language-Team: German \n" "Language: de\n" @@ -16,6 +16,9 @@ msgstr "" msgid "A backup secret phrase will be generated and used for your account.<0/>We do not store it and <1>it can only be displayed once. Save it somewhere safe!" msgstr "Es wird ein Sicherungsgeheimsatz generiert und für Ihr Konto verwendet.<0/>Wir speichern ihn nicht und <1>er kann nur einmal angezeigt werden. Speichern Sie ihn an einem sicheren Ort!" +msgid "A better sharing experience is coming soon." +msgstr "Ein besseres Teilen-Erlebnis ist in Vorbereitung." + msgid "A file with the same name already exists" msgstr "Es existiert bereits eine Datei mit demselben Namen" @@ -38,7 +41,7 @@ msgid "Add at least one more authentication method to protect your account. You msgstr "Fügen Sie mindestens eine weitere Authentifizierungsmethode hinzu, um Ihr Konto zu schützen. Sie bräuchten nur zwei, um sich von jedem Gerät aus bei Files anzumelden." msgid "Add by sharing address, username or wallet address" -msgstr "" +msgstr "Durch Freigabe der Adresse, des Benutzernamens oder der Brieftaschenadresse hinzufügen" msgid "Add card" msgstr "" @@ -67,6 +70,9 @@ msgstr "Sicherungsgeheimsatz" msgid "Backup secret phrase does not match user account, please double-check and try again." msgstr "Der Sicherungsgeheimsatz stimmt nicht mit dem Benutzerkonto überein, bitte überprüfen Sie dies und versuchen Sie es erneut." +#~ msgid "Basic - Free" +#~ msgstr "" + msgid "Billed Monthly" msgstr "" @@ -97,12 +103,6 @@ msgstr "IID (Inhaltsidentifikator)" msgid "Cancel" msgstr "Abbrechen" -#~ msgid "Card added successfully" -#~ msgstr "" - -#~ msgid "Card details could not be validated" -#~ msgstr "" - msgid "Card inputs invalid" msgstr "" @@ -196,6 +196,18 @@ msgstr "" msgid "Dark Theme" msgstr "Dunkles Farbschema" +msgid "Data deleted successfully" +msgstr "" + +msgid "Data moved successfully" +msgstr "" + +msgid "Data moved to bin successfully" +msgstr "" + +msgid "Data restored successfully" +msgstr "" + msgid "Date uploaded" msgstr "Hochgeladen am" @@ -268,6 +280,18 @@ msgstr "Geben Sie das Passwort ein:" msgid "Enter the verification code:" msgstr "Geben Sie den Verifizierungscode ein:" +#~ msgid "Error while creating new shared folder" +#~ msgstr "" + +#~ msgid "Error while downloading {0}" +#~ msgstr "" + +#~ msgid "Error while uploading {0}" +#~ msgstr "Error while uploading {0}" + +#~ msgid "Essentials - Free" +#~ msgstr "Essentials - Kostenlos" + msgid "Failed to add payment method" msgstr "" @@ -283,9 +307,6 @@ msgid "" "sure you have activated your wallet." msgstr "" -msgid "File" -msgstr "Datei" - msgid "File Info" msgstr "Dateiinfos" @@ -307,14 +328,11 @@ msgstr "Dateifreigabeschlüssel" msgid "First name" msgstr "Vorname" -msgid "Folder" -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" @@ -325,6 +343,9 @@ msgstr "Aus Sicherheitsgründen werden wir Sie bei jeder Anmeldung nach einer de msgid "Forget this browser" msgstr "Diesen Browser vergessen" +msgid "Free plan" +msgstr "" + msgid "General" msgstr "Allgemein" @@ -352,9 +373,6 @@ msgstr "Zurück" msgid "Go to Payments" msgstr "" -msgid "Got an issue?" -msgstr "" - msgid "Got it" msgstr "" @@ -418,6 +436,9 @@ msgstr "" msgid "Looks like you’re signing in from a new browser. Please choose one of the following to continue:" msgstr "Sieht aus, als würden Sie sich über einen neuen Browser anmelden. Bitte wählen Sie eine der folgenden Möglichkeiten, um fortzufahren:" +msgid "Lorem ipsum aenean et rutrum magna. Morbi nec placerat erat. Nunc elementum sed libero sit amet convallis. Quisque non arcu vitae ex fringilla molestie." +msgstr "Lorem ipsum aenean et rutrum magna. Morbi nec placerat erat. Nunc elementum sed libero sit amet convallis. Quisque non arcu vitae ex fringilla molestie." + msgid "Manage Access" msgstr "" @@ -439,12 +460,6 @@ msgstr "Meine Dateien" msgid "Name" msgstr "Name" -#~ msgid "Name on card" -#~ msgstr "" - -#~ msgid "Name on card is required" -#~ msgstr "" - msgid "Name too long" msgstr "Name zu lang" @@ -463,8 +478,8 @@ msgstr "" msgid "No file to download." msgstr "" -msgid "No files to show" -msgstr "Keine Dateien zum Anzeigen" +#~ msgid "No files to show" +#~ msgstr "Keine Dateien zum Anzeigen" msgid "No folders" msgstr "Keine Ordner" @@ -490,6 +505,9 @@ msgstr "Eine Sekunde, die Dateien werden vorbereitet …" msgid "Only you can see this." msgstr "Nur Sie können dies sehen." +msgid "Oops! You need to pay for this month to upload more content." +msgstr "" + msgid "Operating system:" msgstr "Betriebssystem:" @@ -523,6 +541,9 @@ msgstr "Passwörter müssen übereinstimmen" msgid "Payment and Subscriptions" msgstr "" +msgid "Plan" +msgstr "" + msgid "Please enter a file name" msgstr "Bitte geben Sie einen Dateinamen ein" @@ -538,6 +559,9 @@ msgstr "Bitte geben Sie ein Passwort an" msgid "Please select a file to upload" msgstr "Bitte wählen Sie eine Datei zum Hochladen" +msgid "Premium plan" +msgstr "" + msgid "Preview" msgstr "Vorschau" @@ -581,16 +605,19 @@ msgid "Rename" msgstr "Umbenennen" msgid "Rename file" -msgstr "" +msgstr "Datei umbenennen" msgid "Rename folder" -msgstr "" +msgstr "Ordner umbenennen" msgid "Report" -msgstr "" +msgstr "Melden" msgid "Report a File" -msgstr "" +msgstr "Datei melden" + +msgid "Report a bug" +msgstr "Fehler melden" msgid "Requested from" msgstr "Angefordert von" @@ -688,6 +715,9 @@ msgstr "Anmelden" msgid "Sign in with a different account" msgstr "Mit einem anderen Konto anmelden" +msgid "Sign me up!" +msgstr "" + msgid "Sign-in methods" msgstr "Anmeldemethoden" @@ -706,15 +736,18 @@ msgstr "" msgid "Something went wrong!" msgstr "Etwas ist schief gelaufen!" -#~ msgid "Something went wrong, please try again" -#~ msgstr "" - msgid "Something went wrong. We couldn't upload your file" msgstr "Es ist etwas schief gelaufen. Wir konnten Ihre Datei nicht hochladen" +msgid "Standard plan" +msgstr "" + msgid "Start Upload" msgstr "Hochladen starten" +msgid "Start a team" +msgstr "" + msgid "Storage Plan" msgstr "Speicherplan" @@ -724,6 +757,9 @@ msgstr "" msgid "Subscription Plan" msgstr "" +msgid "Teams" +msgstr "" + msgid "Technical" msgstr "Technisch" @@ -751,8 +787,8 @@ msgstr "Es gab einen Fehler bei der Authentifizierung" msgid "There was an error connecting your wallet" msgstr "" -msgid "There was an error deleting this" -msgstr "Es gab einen Fehler beim Löschen dieses" +msgid "There was an error deleting your data" +msgstr "" msgid "There was an error getting search results" msgstr "Es gab einen Fehler beim Abrufen der Suchergebnisse" @@ -760,15 +796,18 @@ msgstr "Es gab einen Fehler beim Abrufen der Suchergebnisse" msgid "There was an error getting the preview." msgstr "Es gab einen Fehler beim Abrufen der Vorschau." -msgid "There was an error moving this" -msgstr "Es gab einen Fehler beim Verschieben dieses" +msgid "There was an error moving your data" +msgstr "" -msgid "There was an error recovering this" -msgstr "Es gab einen Fehler bei der Wiederherstellung dieses" +msgid "There was an error restoring your data" +msgstr "" msgid "There was an error when setting username." msgstr "" +msgid "This is the free product." +msgstr "" + msgid "This username is already taken" msgstr "" @@ -793,6 +832,9 @@ msgstr "Eine andere Methode versuchen" msgid "Try for 7 days" msgstr "" +msgid "Unable to upload" +msgstr "" + msgid "Update" msgstr "Aktualisieren" @@ -811,6 +853,9 @@ msgstr "Hochladen" msgid "Uploads cancelled" msgstr "" +msgid "Uploads disabled" +msgstr "" + msgid "Use a different login method" msgstr "Eine andere Anmeldemethode verwenden" @@ -895,30 +940,18 @@ msgstr "" msgid "Your first {0} are free, and you’ll get a discount on our monthly plan once you need more than that" msgstr "" +msgid "Your plan" +msgstr "" + msgid "Your recovery key can be used to restore your account in place of your backup secret phrase." msgstr "Ihr Wiederherstellungsschlüssel kann zur Wiederherstellung Ihres Kontos anstelle Ihres Sicherungsgeheimsatz verwendet werden." -msgid "deleted successfully" -msgstr "erfolgreich gelöscht" - -msgid "file" -msgstr "Datei" - -msgid "folder" -msgstr "Ordner" - msgid "me" msgstr "ich" -msgid "moved successfully" -msgstr "erfolgreich verschoben" - msgid "on" msgstr "am" -msgid "recovered successfully" -msgstr "erfolgreich wiederhergestellt" - msgid "unknown" msgstr "unbekannt" diff --git a/packages/files-ui/src/locales/en/messages.po b/packages/files-ui/src/locales/en/messages.po index 359d9b2d6a..9c19316004 100644 --- a/packages/files-ui/src/locales/en/messages.po +++ b/packages/files-ui/src/locales/en/messages.po @@ -16,6 +16,9 @@ msgstr "" msgid "A backup secret phrase will be generated and used for your account.<0/>We do not store it and <1>it can only be displayed once. Save it somewhere safe!" msgstr "A backup secret phrase will be generated and used for your account.<0/>We do not store it and <1>it can only be displayed once. Save it somewhere safe!" +msgid "A better sharing experience is coming soon." +msgstr "A better sharing experience is coming soon." + msgid "A file with the same name already exists" msgstr "A file with the same name already exists" @@ -67,6 +70,9 @@ msgstr "Backup secret phrase" msgid "Backup secret phrase does not match user account, please double-check and try again." msgstr "Backup secret phrase does not match user account, please double-check and try again." +#~ msgid "Basic - Free" +#~ msgstr "Basic - Free" + msgid "Billed Monthly" msgstr "Billed Monthly" @@ -97,12 +103,6 @@ msgstr "CID (Content Identifier)" msgid "Cancel" msgstr "Cancel" -#~ msgid "Card added successfully" -#~ msgstr "Card added successfully" - -#~ msgid "Card details could not be validated" -#~ msgstr "Card details could not be validated" - msgid "Card inputs invalid" msgstr "Card inputs invalid" @@ -196,6 +196,18 @@ msgstr "Credit card on file" msgid "Dark Theme" msgstr "Dark Theme" +msgid "Data deleted successfully" +msgstr "Data deleted successfully" + +msgid "Data moved successfully" +msgstr "Data moved successfully" + +msgid "Data moved to bin successfully" +msgstr "Data moved to bin successfully" + +msgid "Data restored successfully" +msgstr "Data restored successfully" + msgid "Date uploaded" msgstr "Date uploaded" @@ -268,6 +280,18 @@ msgstr "Enter password:" msgid "Enter the verification code:" msgstr "Enter the verification code:" +#~ msgid "Error while creating new shared folder" +#~ msgstr "Error while creating new shared folder" + +#~ msgid "Error while downloading {0}" +#~ msgstr "Error while downloading {0}" + +#~ msgid "Error while uploading {0}" +#~ msgstr "Error while uploading {0}" + +#~ msgid "Essentials - Free" +#~ msgstr "Essentials - Free" + msgid "Failed to add payment method" msgstr "Failed to add payment method" @@ -286,9 +310,6 @@ msgstr "" "If you are using a contract wallet, please make \n" "sure you have activated your wallet." -msgid "File" -msgstr "File" - msgid "File Info" msgstr "File Info" @@ -310,14 +331,11 @@ msgstr "Files sharing key" msgid "First name" msgstr "First name" -msgid "Folder" -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" @@ -328,6 +346,9 @@ msgstr "For security reasons, each time you sign in we’ll ask you for one of t msgid "Forget this browser" msgstr "Forget this browser" +msgid "Free plan" +msgstr "Free plan" + msgid "General" msgstr "General" @@ -355,9 +376,6 @@ msgstr "Go back" msgid "Go to Payments" msgstr "Go to Payments" -msgid "Got an issue?" -msgstr "Got an issue?" - msgid "Got it" msgstr "Got it" @@ -421,6 +439,9 @@ msgstr "Loading..." 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:" +msgid "Lorem ipsum aenean et rutrum magna. Morbi nec placerat erat. Nunc elementum sed libero sit amet convallis. Quisque non arcu vitae ex fringilla molestie." +msgstr "Lorem ipsum aenean et rutrum magna. Morbi nec placerat erat. Nunc elementum sed libero sit amet convallis. Quisque non arcu vitae ex fringilla molestie." + msgid "Manage Access" msgstr "Manage Access" @@ -442,12 +463,6 @@ msgstr "My Files" msgid "Name" msgstr "Name" -#~ msgid "Name on card" -#~ msgstr "Name on card" - -#~ msgid "Name on card is required" -#~ msgstr "Name on card is required" - msgid "Name too long" msgstr "Name too long" @@ -466,8 +481,8 @@ msgstr "No Card" msgid "No file to download." msgstr "No file to download." -msgid "No files to show" -msgstr "No files to show" +#~ msgid "No files to show" +#~ msgstr "No files to show" msgid "No folders" msgstr "No folders" @@ -493,6 +508,9 @@ msgstr "One sec, getting files ready…" msgid "Only you can see this." msgstr "Only you can see this." +msgid "Oops! You need to pay for this month to upload more content." +msgstr "Oops! You need to pay for this month to upload more content." + msgid "Operating system:" msgstr "Operating system:" @@ -526,6 +544,9 @@ msgstr "Passwords must match" msgid "Payment and Subscriptions" msgstr "Payment and Subscriptions" +msgid "Plan" +msgstr "Plan" + msgid "Please enter a file name" msgstr "Please enter a file name" @@ -541,6 +562,9 @@ msgstr "Please provide a password" msgid "Please select a file to upload" msgstr "Please select a file to upload" +msgid "Premium plan" +msgstr "Premium plan" + msgid "Preview" msgstr "Preview" @@ -595,6 +619,9 @@ msgstr "Report" msgid "Report a File" msgstr "Report a File" +msgid "Report a bug" +msgstr "Report a bug" + msgid "Requested from" msgstr "Requested from" @@ -691,6 +718,9 @@ msgstr "Sign in" msgid "Sign in with a different account" msgstr "Sign in with a different account" +msgid "Sign me up!" +msgstr "Sign me up!" + msgid "Sign-in methods" msgstr "Sign-in methods" @@ -709,15 +739,18 @@ msgstr "Something went wrong with email login! Please try again." msgid "Something went wrong!" msgstr "Something went wrong!" -#~ msgid "Something went wrong, please try again" -#~ msgstr "Something went wrong, please try again" - msgid "Something went wrong. We couldn't upload your file" msgstr "Something went wrong. We couldn't upload your file" +msgid "Standard plan" +msgstr "Standard plan" + msgid "Start Upload" msgstr "Start Upload" +msgid "Start a team" +msgstr "Start a team" + msgid "Storage Plan" msgstr "Storage Plan" @@ -727,6 +760,9 @@ msgstr "Stored by miner" msgid "Subscription Plan" msgstr "Subscription Plan" +msgid "Teams" +msgstr "Teams" + msgid "Technical" msgstr "Technical" @@ -754,8 +790,8 @@ 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 deleting this" -msgstr "There was an error deleting this" +msgid "There was an error deleting your data" +msgstr "There was an error deleting your data" msgid "There was an error getting search results" msgstr "There was an error getting search results" @@ -763,15 +799,18 @@ 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" -msgstr "There was an error moving this" +msgid "There was an error moving your data" +msgstr "There was an error moving your data" -msgid "There was an error recovering this" -msgstr "There was an error recovering this" +msgid "There was an error restoring your data" +msgstr "There was an error restoring your data" msgid "There was an error when setting username." msgstr "There was an error when setting username." +msgid "This is the free product." +msgstr "This is the free product." + msgid "This username is already taken" msgstr "This username is already taken" @@ -796,6 +835,9 @@ msgstr "Try another method" msgid "Try for 7 days" msgstr "Try for 7 days" +msgid "Unable to upload" +msgstr "Unable to upload" + msgid "Update" msgstr "Update" @@ -814,6 +856,9 @@ msgstr "Upload" msgid "Uploads cancelled" msgstr "Uploads cancelled" +msgid "Uploads disabled" +msgstr "Uploads disabled" + msgid "Use a different login method" msgstr "Use a different login method" @@ -898,30 +943,18 @@ msgstr "You've got a payment due. Until you've settled up, we've placed your acc 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" +msgid "Your plan" +msgstr "Your plan" + msgid "Your recovery key can be used to restore your account in place of your backup secret phrase." msgstr "Your recovery key can be used to restore your account in place of your backup secret phrase." -msgid "deleted successfully" -msgstr "deleted successfully" - -msgid "file" -msgstr "file" - -msgid "folder" -msgstr "folder" - msgid "me" msgstr "me" -msgid "moved successfully" -msgstr "moved successfully" - msgid "on" msgstr "on" -msgid "recovered successfully" -msgstr "recovered successfully" - msgid "unknown" msgstr "unknown" diff --git a/packages/files-ui/src/locales/es/messages.po b/packages/files-ui/src/locales/es/messages.po index e2d5164c01..e0d407f7f1 100644 --- a/packages/files-ui/src/locales/es/messages.po +++ b/packages/files-ui/src/locales/es/messages.po @@ -1,22 +1,25 @@ msgid "" msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-05-06 20:51+0200\n" -"Mime-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 2.4.3\n" +"PO-Revision-Date: 2021-09-30 12:40+0000\n" +"Last-Translator: Anonymous \n" +"Language-Team: Spanish \n" "Language: es\n" -"Project-Id-Version: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" "MIME-Version: 1.0\n" -"Report-Msgid-Bugs-To: \n" -"Plural-Forms: \n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.9-dev\n" +"Mime-Version: 1.0\n" msgid "A backup secret phrase will be generated and used for your account.<0/>We do not store it and <1>it can only be displayed once. Save it somewhere safe!" msgstr "" +msgid "A better sharing experience is coming soon." +msgstr "" + msgid "A file with the same name already exists" msgstr "Ya existe un archivo con el mismo nombre" @@ -68,6 +71,9 @@ msgstr "" msgid "Backup secret phrase does not match user account, please double-check and try again." msgstr "" +#~ msgid "Basic - Free" +#~ msgstr "" + msgid "Billed Monthly" msgstr "" @@ -98,12 +104,6 @@ msgstr "CID (Identificador de contenido)" msgid "Cancel" msgstr "Cancelar" -#~ msgid "Card added successfully" -#~ msgstr "" - -#~ msgid "Card details could not be validated" -#~ msgstr "" - msgid "Card inputs invalid" msgstr "" @@ -197,6 +197,18 @@ msgstr "" msgid "Dark Theme" msgstr "Tema oscuro" +msgid "Data deleted successfully" +msgstr "" + +msgid "Data moved successfully" +msgstr "" + +msgid "Data moved to bin successfully" +msgstr "" + +msgid "Data restored successfully" +msgstr "" + msgid "Date uploaded" msgstr "Fecha de subida" @@ -269,6 +281,18 @@ msgstr "Introducir la contraseña:" msgid "Enter the verification code:" msgstr "" +#~ msgid "Error while creating new shared folder" +#~ msgstr "" + +#~ msgid "Error while downloading {0}" +#~ msgstr "" + +#~ msgid "Error while uploading {0}" +#~ msgstr "" + +#~ msgid "Essentials - Free" +#~ msgstr "Esenciales - Gratis" + msgid "Failed to add payment method" msgstr "" @@ -287,9 +311,6 @@ msgstr "" "Si está utilizando una billetera de contrato, haga\n" "seguro que has activado tu billetera." -msgid "File" -msgstr "Archivo" - msgid "File Info" msgstr "Información del archivo" @@ -311,14 +332,11 @@ msgstr "" msgid "First name" msgstr "Primer Nombre" -msgid "Folder" -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" @@ -329,6 +347,9 @@ msgstr "Por motivos de seguridad, cada vez que inicie sesión le pediremos uno d msgid "Forget this browser" msgstr "Olvida este navegador" +msgid "Free plan" +msgstr "" + msgid "General" msgstr "General" @@ -356,9 +377,6 @@ msgstr "Regresar" msgid "Go to Payments" msgstr "" -msgid "Got an issue?" -msgstr "" - msgid "Got it" msgstr "" @@ -422,6 +440,9 @@ msgstr "" msgid "Looks like you’re signing in from a new browser. Please choose one of the following to continue:" msgstr "Parece que está iniciando sesión desde un nuevo navegador. Elija una de las siguientes opciones para continuar:" +msgid "Lorem ipsum aenean et rutrum magna. Morbi nec placerat erat. Nunc elementum sed libero sit amet convallis. Quisque non arcu vitae ex fringilla molestie." +msgstr "Lorem ipsum Aenean, y un gran maquillaje. Usamos más y colocamos en ratas. Ahora elemento libre, pero mucho del valle. , Quien no se doblegue, de la vida de una fringilla molestie." + msgid "Manage Access" msgstr "" @@ -443,12 +464,6 @@ msgstr "Mis Archivos" msgid "Name" msgstr "Nombre" -#~ msgid "Name on card" -#~ msgstr "" - -#~ msgid "Name on card is required" -#~ msgstr "" - msgid "Name too long" msgstr "" @@ -467,8 +482,8 @@ msgstr "" msgid "No file to download." msgstr "" -msgid "No files to show" -msgstr "No hay archivos para mostrar" +#~ msgid "No files to show" +#~ msgstr "No hay archivos para mostrar" msgid "No folders" msgstr "Sin carpetas" @@ -494,6 +509,9 @@ msgstr "" msgid "Only you can see this." msgstr "" +msgid "Oops! You need to pay for this month to upload more content." +msgstr "" + msgid "Operating system:" msgstr "Sistema operativo:" @@ -527,6 +545,9 @@ msgstr "Las contraseñas deben coincidir" msgid "Payment and Subscriptions" msgstr "" +msgid "Plan" +msgstr "" + msgid "Please enter a file name" msgstr "Ingrese un nombre de archivo" @@ -542,6 +563,9 @@ msgstr "Por favor ingrese una contraseña" msgid "Please select a file to upload" msgstr "" +msgid "Premium plan" +msgstr "" + msgid "Preview" msgstr "Avance" @@ -596,6 +620,9 @@ msgstr "" msgid "Report a File" msgstr "" +msgid "Report a bug" +msgstr "" + msgid "Requested from" msgstr "Solicitado a" @@ -692,6 +719,9 @@ msgstr "Registrarse" msgid "Sign in with a different account" msgstr "Inicie sesión con una cuenta diferente" +msgid "Sign me up!" +msgstr "" + msgid "Sign-in methods" msgstr "Métodos de inicio de sesión" @@ -710,15 +740,18 @@ msgstr "" msgid "Something went wrong!" msgstr "" -#~ msgid "Something went wrong, please try again" -#~ msgstr "" - msgid "Something went wrong. We couldn't upload your file" msgstr "Algo salió mal. No pudimos subir tu archivo" +msgid "Standard plan" +msgstr "" + msgid "Start Upload" msgstr "Iniciar la subida" +msgid "Start a team" +msgstr "" + msgid "Storage Plan" msgstr "Plan de almacenamiento" @@ -728,6 +761,9 @@ msgstr "Almacenado por el minero" msgid "Subscription Plan" msgstr "" +msgid "Teams" +msgstr "" + msgid "Technical" msgstr "Técnico" @@ -755,8 +791,8 @@ msgstr "Hubo un error de autenticación." msgid "There was an error connecting your wallet" msgstr "Hubo un error al conectar su billetera" -msgid "There was an error deleting this" -msgstr "Hubo un error al eliminar esto." +msgid "There was an error deleting your data" +msgstr "" msgid "There was an error getting search results" msgstr "Se produjo un error al obtener los resultados de la búsqueda." @@ -764,15 +800,18 @@ msgstr "Se produjo un error al obtener los resultados de la búsqueda." msgid "There was an error getting the preview." msgstr "Hubo un error al obtener la vista previa." -msgid "There was an error moving this" +msgid "There was an error moving your data" msgstr "" -msgid "There was an error recovering this" -msgstr "Hubo un error al recuperar esto." +msgid "There was an error restoring your data" +msgstr "" msgid "There was an error when setting username." msgstr "" +msgid "This is the free product." +msgstr "" + msgid "This username is already taken" msgstr "" @@ -797,6 +836,9 @@ msgstr "Prueba con otro método" msgid "Try for 7 days" msgstr "" +msgid "Unable to upload" +msgstr "" + msgid "Update" msgstr "Actualizar" @@ -815,6 +857,9 @@ msgstr "Subir" msgid "Uploads cancelled" msgstr "" +msgid "Uploads disabled" +msgstr "" + msgid "Use a different login method" msgstr "Utilice un método de inicio de sesión diferente" @@ -899,30 +944,18 @@ msgstr "" msgid "Your first {0} are free, and you’ll get a discount on our monthly plan once you need more than that" msgstr "" -msgid "Your recovery key can be used to restore your account in place of your backup secret phrase." +msgid "Your plan" msgstr "" -msgid "deleted successfully" -msgstr "borrado exitosamente" - -msgid "file" -msgstr "archivo" - -msgid "folder" -msgstr "carpeta" - -msgid "me" +msgid "Your recovery key can be used to restore your account in place of your backup secret phrase." msgstr "" -msgid "moved successfully" +msgid "me" msgstr "" msgid "on" msgstr "en" -msgid "recovered successfully" -msgstr "recuperado con éxito" - msgid "unknown" msgstr "" diff --git a/packages/files-ui/src/locales/fr/messages.mo b/packages/files-ui/src/locales/fr/messages.mo new file mode 100644 index 0000000000..3304428284 Binary files /dev/null and b/packages/files-ui/src/locales/fr/messages.mo differ diff --git a/packages/files-ui/src/locales/fr/messages.po b/packages/files-ui/src/locales/fr/messages.po index 09d59bfaf1..d9f12da1a1 100644 --- a/packages/files-ui/src/locales/fr/messages.po +++ b/packages/files-ui/src/locales/fr/messages.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-04-23 11:05+0200\n" -"PO-Revision-Date: 2021-09-22 21:38+0000\n" +"PO-Revision-Date: 2021-09-29 19:36+0000\n" "Last-Translator: J. Lavoie \n" "Language-Team: French \n" "Language: fr\n" @@ -17,6 +17,9 @@ msgstr "" msgid "A backup secret phrase will be generated and used for your account.<0/>We do not store it and <1>it can only be displayed once. Save it somewhere safe!" msgstr "Une phrase secrète de sauvegarde sera générée et utilisé pour ce compte.<0/>Nous ne la sauvergardons pas <1>elle ne peut être affichée qu’une seule fois. Gardez-la dans un endroit sûr !" +msgid "A better sharing experience is coming soon." +msgstr "Une meilleure expérience de partage sera bientôt disponible." + msgid "A file with the same name already exists" msgstr "Un fichier avec ce nom existe déjà" @@ -68,6 +71,9 @@ msgstr "Phrase secrète de sauvegarde" msgid "Backup secret phrase does not match user account, please double-check and try again." msgstr "La phrase secrète de sauvegarde est incorrecte, merci de vérifier et réessayer." +#~ msgid "Basic - Free" +#~ msgstr "" + msgid "Billed Monthly" msgstr "" @@ -98,12 +104,6 @@ msgstr "CID (Identifiant de contenu)" msgid "Cancel" msgstr "Annuler" -#~ msgid "Card added successfully" -#~ msgstr "" - -#~ msgid "Card details could not be validated" -#~ msgstr "" - msgid "Card inputs invalid" msgstr "" @@ -197,6 +197,18 @@ msgstr "" msgid "Dark Theme" msgstr "Thème sombre" +msgid "Data deleted successfully" +msgstr "" + +msgid "Data moved successfully" +msgstr "" + +msgid "Data moved to bin successfully" +msgstr "" + +msgid "Data restored successfully" +msgstr "" + msgid "Date uploaded" msgstr "Téléversé le" @@ -269,6 +281,18 @@ msgstr "Entrez le mot de passe :" msgid "Enter the verification code:" msgstr "Entrez le code de vérification :" +#~ msgid "Error while creating new shared folder" +#~ msgstr "Erreur lors de la création d’un nouveau dossier partagé" + +#~ msgid "Error while downloading {0}" +#~ msgstr "Erreur lors du téléchargement de {0}" + +#~ msgid "Error while uploading {0}" +#~ msgstr "Erreur lors du téléversement de {0} + +#~ msgid "Essentials - Free" +#~ msgstr "Essentials - Gratuit" + msgid "Failed to add payment method" msgstr "" @@ -287,9 +311,6 @@ msgstr "" "Si vous utilisez un contract wallet, veuillez\n" "vérifier que vous avez activé votre wallet." -msgid "File" -msgstr "Fichier" - msgid "File Info" msgstr "Infos du fichier" @@ -311,14 +332,11 @@ msgstr "Clé de partage des fichiers" msgid "First name" msgstr "Prénom" -msgid "Folder" -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" @@ -329,6 +347,9 @@ msgstr "Pour des raisons de sécurité, chaque fois que vous vous connectez, nou msgid "Forget this browser" msgstr "Oublier ce navigateur" +msgid "Free plan" +msgstr "" + msgid "General" msgstr "Général" @@ -356,9 +377,6 @@ msgstr "Retour" msgid "Go to Payments" msgstr "" -msgid "Got an issue?" -msgstr "Vous avez un problème ?" - msgid "Got it" msgstr "Compris" @@ -422,6 +440,9 @@ msgstr "Chargement…" msgid "Looks like you’re signing in from a new browser. Please choose one of the following to continue:" msgstr "Il semble que vous vous connectiez à partir d’un nouveau navigateur. Veuillez choisir une des options suivantes pour continuer :" +msgid "Lorem ipsum aenean et rutrum magna. Morbi nec placerat erat. Nunc elementum sed libero sit amet convallis. Quisque non arcu vitae ex fringilla molestie." +msgstr "Lorem ipsum aenean et rutrum magna. Morbi nec placerat erat. Nunc elementum sed libero sit amet convallis. Quisque non arcu vitae ex fringilla molestie." + msgid "Manage Access" msgstr "Gérer l’accès" @@ -443,12 +464,6 @@ msgstr "Mes Fichiers" msgid "Name" msgstr "Nom" -#~ msgid "Name on card" -#~ msgstr "" - -#~ msgid "Name on card is required" -#~ msgstr "" - msgid "Name too long" msgstr "Le nom est trop long" @@ -467,8 +482,8 @@ msgstr "" msgid "No file to download." msgstr "Aucun fichier à télécharger." -msgid "No files to show" -msgstr "Aucun fichier à afficher" +#~ msgid "No files to show" +#~ msgstr "Aucun fichier à afficher" msgid "No folders" msgstr "Aucun dossier" @@ -494,6 +509,9 @@ msgstr "Un instant, nous préparons les fichiers…" msgid "Only you can see this." msgstr "Vous seul(e) pouvez voir ceci." +msgid "Oops! You need to pay for this month to upload more content." +msgstr "" + msgid "Operating system:" msgstr "Système d’exploitation :" @@ -527,6 +545,9 @@ msgstr "Les mots de passes de correspondent pas" msgid "Payment and Subscriptions" msgstr "" +msgid "Plan" +msgstr "" + msgid "Please enter a file name" msgstr "Veuillez entrer un nom de fichier" @@ -542,6 +563,9 @@ msgstr "Merci de donner un mot de passe" msgid "Please select a file to upload" msgstr "Merci de sélectionner un fichier à téléverser" +msgid "Premium plan" +msgstr "" + msgid "Preview" msgstr "Aperçu" @@ -596,6 +620,9 @@ msgstr "Signaler" msgid "Report a File" msgstr "Signaler un fichier" +msgid "Report a bug" +msgstr "Signaler une erreur" + msgid "Requested from" msgstr "Envoyé par" @@ -692,6 +719,9 @@ msgstr "Se connecter" msgid "Sign in with a different account" msgstr "Se connecter avec un autre compte" +msgid "Sign me up!" +msgstr "S'inscrire !" + msgid "Sign-in methods" msgstr "Méthodes de connexion" @@ -710,15 +740,18 @@ msgstr "Un problème est survenu lors de la connexion avec courriel ! Veuillez msgid "Something went wrong!" msgstr "Quelque chose a mal tourné !" -#~ msgid "Something went wrong, please try again" -#~ msgstr "" - msgid "Something went wrong. We couldn't upload your file" msgstr "Un problème est survenu. Nous n’avons pas pu téléverser votre fichier" +msgid "Standard plan" +msgstr "" + msgid "Start Upload" msgstr "Démarrer le téléversement" +msgid "Start a team" +msgstr "Créer une équipe" + msgid "Storage Plan" msgstr "Plan de stockage" @@ -728,6 +761,9 @@ msgstr "Sauvegardé par le mineur" msgid "Subscription Plan" msgstr "" +msgid "Teams" +msgstr "Équipes" + msgid "Technical" msgstr "Technique" @@ -755,8 +791,8 @@ 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 deleting this" -msgstr "Une erreur s’est produite lors de la suppression" +msgid "There was an error deleting your data" +msgstr "" msgid "There was an error getting search results" msgstr "Une erreur s’est produite lors de l’obtention des résultats de recherche" @@ -764,14 +800,17 @@ msgstr "Une erreur s’est produite lors de l’obtention des résultats de rech 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" -msgstr "Une erreur s’est produite lors du déplacement" +msgid "There was an error moving your data" +msgstr "" -msgid "There was an error recovering this" -msgstr "Une erreur s’est produite lors de la récupération" +msgid "There was an error restoring your data" +msgstr "" msgid "There was an error when setting username." -msgstr "Il y a eu une erreur dans le paramétrage du nom d’utilisateur." +msgstr "" + +msgid "This is the free product." +msgstr "" msgid "This username is already taken" msgstr "Ce nom d’utilisateur est déjà pris" @@ -797,6 +836,9 @@ msgstr "Essayer une autre méthode" msgid "Try for 7 days" msgstr "" +msgid "Unable to upload" +msgstr "" + msgid "Update" msgstr "Mettre à jour" @@ -815,6 +857,9 @@ msgstr "Téléverser" msgid "Uploads cancelled" msgstr "Téléversements annulés" +msgid "Uploads disabled" +msgstr "" + msgid "Use a different login method" msgstr "Utilisez une méthode de connexion différente" @@ -899,30 +944,18 @@ msgstr "" msgid "Your first {0} are free, and you’ll get a discount on our monthly plan once you need more than that" msgstr "" +msgid "Your plan" +msgstr "" + msgid "Your recovery key can be used to restore your account in place of your backup secret phrase." msgstr "Votre clé de récupération peut être utilisée pour restaurer votre compte à la place de votre phrase de sauvegarde secrète." -msgid "deleted successfully" -msgstr "supprimé avec succès" - -msgid "file" -msgstr "fichier" - -msgid "folder" -msgstr "dossier" - msgid "me" msgstr "moi" -msgid "moved successfully" -msgstr "déplacé avec succès" - msgid "on" msgstr "le" -msgid "recovered successfully" -msgstr "récupéré avec succès" - msgid "unknown" msgstr "inconnu" diff --git a/packages/files-ui/src/locales/no/messages.po b/packages/files-ui/src/locales/no/messages.po index 18dddd9af5..7d177b2fc3 100644 --- a/packages/files-ui/src/locales/no/messages.po +++ b/packages/files-ui/src/locales/no/messages.po @@ -3,19 +3,22 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-04-20 00:28+0200\n" -"PO-Revision-Date: 2021-07-29 09:32+0000\n" +"PO-Revision-Date: 2021-09-30 12:40+0000\n" "Last-Translator: Allan Nordhøy \n" "Language-Team: Norwegian Bokmål \n" "Language: no\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.9-dev\n" "Mime-Version: 1.0\n" msgid "A backup secret phrase will be generated and used for your account.<0/>We do not store it and <1>it can only be displayed once. Save it somewhere safe!" msgstr "" +msgid "A better sharing experience is coming soon." +msgstr "" + msgid "A file with the same name already exists" msgstr "" @@ -67,6 +70,9 @@ msgstr "" msgid "Backup secret phrase does not match user account, please double-check and try again." msgstr "" +#~ msgid "Basic - Free" +#~ msgstr "" + msgid "Billed Monthly" msgstr "" @@ -97,12 +103,6 @@ msgstr "" msgid "Cancel" msgstr "Avbryt" -#~ msgid "Card added successfully" -#~ msgstr "" - -#~ msgid "Card details could not be validated" -#~ msgstr "" - msgid "Card inputs invalid" msgstr "" @@ -196,6 +196,18 @@ msgstr "" msgid "Dark Theme" msgstr "Mørk drakt" +msgid "Data deleted successfully" +msgstr "" + +msgid "Data moved successfully" +msgstr "" + +msgid "Data moved to bin successfully" +msgstr "" + +msgid "Data restored successfully" +msgstr "" + msgid "Date uploaded" msgstr "Dato opplastet" @@ -268,6 +280,18 @@ msgstr "Skriv inn passord:" msgid "Enter the verification code:" msgstr "Skriv inn bekreftelseskoden:" +#~ msgid "Error while creating new shared folder" +#~ msgstr "" + +#~ msgid "Error while downloading {0}" +#~ msgstr "" + +#~ msgid "Error while uploading {0} +#~ msgstr "" + +#~ msgid "Essentials - Free" +#~ msgstr "" + msgid "Failed to add payment method" msgstr "" @@ -283,9 +307,6 @@ msgid "" "sure you have activated your wallet." msgstr "" -msgid "File" -msgstr "Fil" - msgid "File Info" msgstr "Filinfo" @@ -307,14 +328,11 @@ msgstr "" msgid "First name" msgstr "Fornavn" -msgid "Folder" -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" @@ -325,6 +343,9 @@ msgstr "" msgid "Forget this browser" msgstr "" +msgid "Free plan" +msgstr "" + msgid "General" msgstr "" @@ -352,9 +373,6 @@ msgstr "Tilbake" msgid "Go to Payments" msgstr "" -msgid "Got an issue?" -msgstr "" - msgid "Got it" msgstr "" @@ -418,6 +436,9 @@ msgstr "" msgid "Looks like you’re signing in from a new browser. Please choose one of the following to continue:" msgstr "" +msgid "Lorem ipsum aenean et rutrum magna. Morbi nec placerat erat. Nunc elementum sed libero sit amet convallis. Quisque non arcu vitae ex fringilla molestie." +msgstr "" + msgid "Manage Access" msgstr "" @@ -439,12 +460,6 @@ msgstr "Mine filer" msgid "Name" msgstr "Navn" -#~ msgid "Name on card" -#~ msgstr "" - -#~ msgid "Name on card is required" -#~ msgstr "" - msgid "Name too long" msgstr "Navnet er for langt" @@ -463,8 +478,8 @@ msgstr "" msgid "No file to download." msgstr "" -msgid "No files to show" -msgstr "Ingen filer å vise" +#~ msgid "No files to show" +#~ msgstr "Ingen filer å vise" msgid "No folders" msgstr "Ingen mapper" @@ -490,6 +505,9 @@ msgstr "" msgid "Only you can see this." msgstr "Kun du kan se dette." +msgid "Oops! You need to pay for this month to upload more content." +msgstr "" + msgid "Operating system:" msgstr "Operativsystem:" @@ -523,6 +541,9 @@ msgstr "Passordene må samsvare" msgid "Payment and Subscriptions" msgstr "" +msgid "Plan" +msgstr "" + msgid "Please enter a file name" msgstr "Skriv inn et filnavn" @@ -538,6 +559,9 @@ msgstr "Angi et passord" msgid "Please select a file to upload" msgstr "Velg en fil å laste opp" +msgid "Premium plan" +msgstr "" + msgid "Preview" msgstr "Forhåndsvis" @@ -592,6 +616,9 @@ msgstr "" msgid "Report a File" msgstr "" +msgid "Report a bug" +msgstr "" + msgid "Requested from" msgstr "Forespurt fra" @@ -688,6 +715,9 @@ msgstr "Logg inn" msgid "Sign in with a different account" msgstr "Logg inn med en annen konto" +msgid "Sign me up!" +msgstr "" + msgid "Sign-in methods" msgstr "Innloggingsmetoder" @@ -706,15 +736,18 @@ msgstr "" msgid "Something went wrong!" msgstr "Noe gikk galt." -#~ msgid "Something went wrong, please try again" -#~ msgstr "" - msgid "Something went wrong. We couldn't upload your file" msgstr "" +msgid "Standard plan" +msgstr "" + msgid "Start Upload" msgstr "" +msgid "Start a team" +msgstr "" + msgid "Storage Plan" msgstr "" @@ -724,6 +757,9 @@ msgstr "" msgid "Subscription Plan" msgstr "" +msgid "Teams" +msgstr "" + msgid "Technical" msgstr "Teknisk" @@ -751,7 +787,7 @@ msgstr "" msgid "There was an error connecting your wallet" msgstr "" -msgid "There was an error deleting this" +msgid "There was an error deleting your data" msgstr "" msgid "There was an error getting search results" @@ -760,15 +796,18 @@ msgstr "" msgid "There was an error getting the preview." msgstr "" -msgid "There was an error moving this" +msgid "There was an error moving your data" msgstr "" -msgid "There was an error recovering this" +msgid "There was an error restoring your data" msgstr "" msgid "There was an error when setting username." msgstr "" +msgid "This is the free product." +msgstr "" + msgid "This username is already taken" msgstr "" @@ -793,6 +832,9 @@ msgstr "Prøv annen metode" msgid "Try for 7 days" msgstr "" +msgid "Unable to upload" +msgstr "" + msgid "Update" msgstr "Oppdater" @@ -811,6 +853,9 @@ msgstr "Last opp" msgid "Uploads cancelled" msgstr "" +msgid "Uploads disabled" +msgstr "" + msgid "Use a different login method" msgstr "Bruk en annen innloggingsmetode" @@ -895,30 +940,18 @@ msgstr "" msgid "Your first {0} are free, and you’ll get a discount on our monthly plan once you need more than that" msgstr "" -msgid "Your recovery key can be used to restore your account in place of your backup secret phrase." +msgid "Your plan" msgstr "" -msgid "deleted successfully" -msgstr "slettet" - -msgid "file" -msgstr "fil" - -msgid "folder" -msgstr "mappe" +msgid "Your recovery key can be used to restore your account in place of your backup secret phrase." +msgstr "" msgid "me" msgstr "" -msgid "moved successfully" -msgstr "flyttet" - msgid "on" msgstr "" -msgid "recovered successfully" -msgstr "gjenopprettet" - msgid "unknown" msgstr "" diff --git a/packages/gaming-ui/package.json b/packages/gaming-ui/package.json index 22d3bdd943..3ed9f2799d 100644 --- a/packages/gaming-ui/package.json +++ b/packages/gaming-ui/package.json @@ -56,7 +56,7 @@ "@types/yup": "^0.29.9", "@types/zxcvbn": "^4.4.0", "babel-plugin-macros": "^2.8.0", - "cypress": "^8.4", + "cypress": "^8.5", "cypress-file-upload": "^5.0.8", "cypress-pipe": "^2.0.0" }, diff --git a/packages/gaming-ui/src/Components/Elements/ApiKeyCard.tsx b/packages/gaming-ui/src/Components/Elements/ApiKeyCard.tsx new file mode 100644 index 0000000000..db489b7d8b --- /dev/null +++ b/packages/gaming-ui/src/Components/Elements/ApiKeyCard.tsx @@ -0,0 +1,86 @@ +import React from "react" +import { AccessKey } from "@chainsafe/files-api-client" +import { makeStyles, createStyles } from "@chainsafe/common-theme" +import { CSGTheme } from "../../Themes/types" +import { Button, Typography, Paper } from "@chainsafe/common-components" +import { Trans } from "@lingui/macro" +import dayjs from "dayjs" + + +const useStyles = makeStyles(({ constants }: CSGTheme) => + createStyles({ + root: { + position: "relative", + margin: constants.generalUnit, + borderRadius: constants.generalUnit / 2, + maxWidth: 250, + padding: `${constants.generalUnit * 2}px ${constants.generalUnit}px` + }, + button: { + marginTop: constants.generalUnit * 2 + } + }) +) + +interface IApiKeyCard { + apiKey: AccessKey + deleteKey: () => void +} + +const ApiKeyCard = ({ apiKey, deleteKey }: IApiKeyCard) => { + const classes = useStyles() + + return ( + + + + Id: + + + + { apiKey.id } + + + + Status: + + + + { apiKey.status } + + + + Created on: + + + + { dayjs(apiKey.created_at).format("DD MMM YYYY h:mm a") } + + + + ) +} + +export default ApiKeyCard \ No newline at end of file diff --git a/packages/gaming-ui/src/Components/Elements/SecretField.tsx b/packages/gaming-ui/src/Components/Elements/SecretField.tsx index f8f501bc8d..ba059aa666 100644 --- a/packages/gaming-ui/src/Components/Elements/SecretField.tsx +++ b/packages/gaming-ui/src/Components/Elements/SecretField.tsx @@ -3,7 +3,7 @@ import { EyeOpenIcon, EyeClosedIcon, Typography } from "@chainsafe/common-compon import { makeStyles, createStyles } from "@chainsafe/common-theme" interface Props { - value: string + value: string } const useStyles = makeStyles(() => diff --git a/packages/gaming-ui/src/Components/GamingRoutes.tsx b/packages/gaming-ui/src/Components/GamingRoutes.tsx index a13dc10f19..22d434014a 100644 --- a/packages/gaming-ui/src/Components/GamingRoutes.tsx +++ b/packages/gaming-ui/src/Components/GamingRoutes.tsx @@ -34,6 +34,14 @@ const GamingRoutes = () => { + diff --git a/packages/gaming-ui/src/Components/Layouts/AppNav.tsx b/packages/gaming-ui/src/Components/Layouts/AppNav.tsx index 33eac97cff..7746860bf1 100644 --- a/packages/gaming-ui/src/Components/Layouts/AppNav.tsx +++ b/packages/gaming-ui/src/Components/Layouts/AppNav.tsx @@ -9,11 +9,7 @@ import { Link, Typography, PowerDownSvg, - // ProgressBar, - // formatBytes, - ChainsafeLogo, - SettingSvg, - SubscriptionSvg + ChainsafeLogo } from "@chainsafe/common-components" import { ROUTE_LINKS } from "../GamingRoutes" import { Trans } from "@lingui/macro" @@ -241,53 +237,9 @@ const AppNav: React.FC = ({ navOpen, setNavOpen }: IAppNav) => { )}
- {desktop && ( -
- {/* {`${formatBytes(spaceUsed, 2)} of ${formatBytes( - FREE_PLAN_LIMIT, 2 - )} used`} - */} -
- )} {!desktop && (
+const useStyles = makeStyles(({ breakpoints, constants, palette, zIndex }: CSGTheme) => createStyles({ root: { - position: "relative" + position: "relative", + margin: constants.generalUnit }, header: { display: "flex", @@ -48,41 +31,30 @@ const useStyles = makeStyles(({ constants, breakpoints, animation, zIndex, palet marginLeft: constants.generalUnit } }, - tableHead: { - marginTop: 24 - }, - tableRow: { - border: "2px solid transparent", - transitionDuration: `${animation.transform}ms`, - [breakpoints.up("md")]: { - gridTemplateColumns: desktopGridSettings - }, - [breakpoints.down("md")]: { - gridTemplateColumns: mobileGridSettings - } - }, - 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: { + dataArea: { + marginTop: constants.generalUnit * 2, display: "flex", - justifyContent: "center", - alignItems: "center", - width: 20, - marginRight: constants.generalUnit * 1.5, - "& svg": { - fill: constants.fileSystemItemRow.menuIcon + flexDirection: "row", + justifyContent: "space-between", + flexWrap: "wrap", + "& > *": { + margin: constants.generalUnit, + width:"100%", + [breakpoints.up("xs")]: { + maxWidth: `calc(100% - ${constants.generalUnit * 2}px)` + }, + [breakpoints.up("sm")]: { + maxWidth: `calc(50% - ${constants.generalUnit * 2}px)` + }, + [breakpoints.up("md")]: { + maxWidth: `calc(33% - ${constants.generalUnit * 2}px)` + }, + [breakpoints.up("lg")]: { + maxWidth: `calc(25% - ${constants.generalUnit * 2}px)` + }, + [breakpoints.up("xl")]: { + maxWidth: `calc(20% - ${constants.generalUnit * 2}px)` + } } }, modalRoot: { @@ -137,7 +109,7 @@ const useStyles = makeStyles(({ constants, breakpoints, animation, zIndex, palet }) ) -const ApiKeys = () => { +const DashboardModule = () => { const classes = useStyles() const { gamingApiClient } = useGamingApi() const [keys, setKeys] = useState([]) @@ -161,15 +133,17 @@ const ApiKeys = () => { const fetchAccessKeys = useCallback(() => { gamingApiClient.listAccessKeys() - .then(setKeys) + .then(keys => setKeys(keys.filter(key => key.type === "gaming"))) .catch(console.error) }, [gamingApiClient]) - const createStorageAccessKey = useCallback(() => { + const createGamingAccessKey = useCallback(() => { gamingApiClient.createAccessKey({ type: "gaming" }) - .then(setNewKey) - .then(fetchAccessKeys) - .then(() => setIsNewKeyModalOpen(true)) + .then((key) => { + setNewKey(key) + fetchAccessKeys() + setIsNewKeyModalOpen(true) + }) .catch(console.error) }, [fetchAccessKeys, gamingApiClient]) @@ -193,16 +167,16 @@ const ApiKeys = () => { data-cy="api-keys-header" > - API Keys + Dashboard
- - - - - Id - - - Type - - - Status - - - Created At - - {/* Menu */} - - - - {keys.map(k => - - - - {k.id} - - - - - {k.type} - - - - - {k.status} - - - - - {dayjs(k.created_at).format("DD MMM YYYY h:mm a")} - - - - - - - Delete Key - - - ), - onClick: () => deleteAccessKey(k.id) - }]} - classNames={{ - icon: classes.dropdownIcon, - options: classes.dropdownOptions, - item: classes.dropdownItem - }} - indicator={MoreIcon} - /> - - )} - -
+
+ { + keys.map((key: AccessKey, index: number) => ( + deleteAccessKey(key.id)} + apiKey={key} />)) + } +
{
- + Make sure to save the secret, as it can only be displayed once. {copiedSecret && ( @@ -351,13 +247,12 @@ const ApiKeys = () => {
@@ -365,4 +260,4 @@ const ApiKeys = () => { ) } -export default ApiKeys +export default DashboardModule \ No newline at end of file diff --git a/packages/gaming-ui/src/Components/Pages/DashboardPage.tsx b/packages/gaming-ui/src/Components/Pages/DashboardPage.tsx index 8a3df055c5..2cd132244a 100644 --- a/packages/gaming-ui/src/Components/Pages/DashboardPage.tsx +++ b/packages/gaming-ui/src/Components/Pages/DashboardPage.tsx @@ -1,27 +1,10 @@ import React from "react" -import { makeStyles, createStyles } from "@chainsafe/common-theme" -import { Typography } from "@chainsafe/common-components" - -const useStyles = makeStyles(() => - createStyles({ - root: { - position: "relative" - } - }) -) +import DashboardModule from "../Modules/DashboardModule" const DashboardPage = () => { - const classes = useStyles() return ( -
- - Dashboard - -
+ ) } diff --git a/packages/gaming-ui/src/Components/Pages/SettingsPage.tsx b/packages/gaming-ui/src/Components/Pages/SettingsPage.tsx index d7aa60c146..e1ab8bb179 100644 --- a/packages/gaming-ui/src/Components/Pages/SettingsPage.tsx +++ b/packages/gaming-ui/src/Components/Pages/SettingsPage.tsx @@ -1,21 +1,17 @@ import React, { useCallback } from "react" import { Tabs, - TabPane as TabPaneOrigin, + // TabPane as TabPaneOrigin, Typography, Divider, useParams, - useHistory, - ITabPaneProps, - CaretRightIcon, - LockIcon + useHistory } from "@chainsafe/common-components" import { makeStyles, createStyles, useThemeSwitcher } from "@chainsafe/common-theme" import { ROUTE_LINKS, SettingsPath } from "../GamingRoutes" -import { t, Trans } from "@lingui/macro" +import { Trans } from "@lingui/macro" import clsx from "clsx" -import ApiKeys from "../Modules/ApiKeys" import { CSGTheme } from "../../Themes/types" -const TabPane = (props: ITabPaneProps) => TabPaneOrigin(props) +// const TabPane = (props: ITabPaneProps) => TabPaneOrigin(props) const useStyles = makeStyles(({ constants, breakpoints, palette }: CSGTheme) => createStyles({ title: { @@ -165,16 +161,6 @@ const SettingsPage: React.FC = () => { classes.injectedTabList) }} > - } - iconRight={} - title={t`Api Keys`} - tabKey="apiKeys" - testId="apiKeys-tab" - > - -
} diff --git a/packages/gaming-ui/src/Contexts/GamingApiContext.tsx b/packages/gaming-ui/src/Contexts/GamingApiContext.tsx index d05ecde538..33880d7350 100644 --- a/packages/gaming-ui/src/Contexts/GamingApiContext.tsx +++ b/packages/gaming-ui/src/Contexts/GamingApiContext.tsx @@ -15,28 +15,28 @@ const isReturningUserStorageKey = "csg.isReturningUser" const TORUS_USERINFO_KEY = "csg.userInfo" const getProviderSpecificParams = (loginType: LOGIN_TYPE): - {typeOfLogin: LOGIN_TYPE; clientId: string; verifier: string; jwtParams?: any} => { +{typeOfLogin: LOGIN_TYPE; clientId: string; verifier: string; jwtParams?: any} => { switch (loginType) { - case "google": { - return { - typeOfLogin: loginType, - clientId: process.env.REACT_APP_GOOGLE_CLIENT_ID || "", - verifier: "chainsafe-uuid-testnet" + case "google": { + return { + typeOfLogin: loginType, + clientId: process.env.REACT_APP_GOOGLE_CLIENT_ID || "", + verifier: "chainsafe-uuid-testnet" + } } - } - case "github":{ - return { - typeOfLogin: loginType, - clientId: process.env.REACT_APP_AUTH0_CLIENT_ID || "", - verifier: "chainsafe-uuid-testnet", - jwtParams: { - domain: process.env.REACT_APP_AUTH0_DOMAIN || "" + case "github":{ + return { + typeOfLogin: loginType, + clientId: process.env.REACT_APP_AUTH0_CLIENT_ID || "", + verifier: "chainsafe-uuid-testnet", + jwtParams: { + domain: process.env.REACT_APP_AUTH0_DOMAIN || "" + } } } - } - default:{ - throw new Error(`${loginType} is unsupported`) - } + default:{ + throw new Error(`${loginType} is unsupported`) + } } } @@ -102,7 +102,7 @@ const GamingApiProvider = ({ apiUrl, withLocalStorage = true, children }: Gaming const [accessToken, setAccessToken] = useState(undefined) const [refreshToken, setRefreshToken] = useState(undefined) const [decodedRefreshToken, setDecodedRefreshToken] = useState< - { exp: number; enckey?: string; mps?: string; uuid: string } | undefined + { exp: number; enckey?: string; mps?: string; uuid: string } | undefined >(undefined) // returning user @@ -134,18 +134,18 @@ const GamingApiProvider = ({ apiUrl, withLocalStorage = true, children }: Gaming if (userInfo && loginType) { switch (loginType) { - case "jwt": - setLoggedinAs(t`Web3: ${centerEllipsis(String(address), 4)}`) - break - case "google": - setLoggedinAs(`${capitalize(loginType)}: ${centerEllipsis(`${userInfo.email}`, 4)}`) - break - case "github": - setLoggedinAs(`${capitalize(loginType)}: ${centerEllipsis(`${userInfo.email}`, 4)}`) - break - default: - setLoggedinAs(`${centerEllipsis(`${userInfo.publicAddress}`, 4)}`) - break + case "jwt": + setLoggedinAs(t`Web3: ${centerEllipsis(String(address), 4)}`) + break + case "google": + setLoggedinAs(`${capitalize(loginType)}: ${centerEllipsis(`${userInfo.email}`, 4)}`) + break + case "github": + setLoggedinAs(`${capitalize(loginType)}: ${centerEllipsis(`${userInfo.email}`, 4)}`) + break + default: + setLoggedinAs(`${centerEllipsis(`${userInfo.publicAddress}`, 4)}`) + break } } }, [userInfo, address]) diff --git a/packages/gaming-ui/src/Themes/Constants.ts b/packages/gaming-ui/src/Themes/Constants.ts index 28fabee2d5..b0c6c8ac13 100644 --- a/packages/gaming-ui/src/Themes/Constants.ts +++ b/packages/gaming-ui/src/Themes/Constants.ts @@ -156,6 +156,6 @@ export interface CsGColors extends IConstants { } } surveyBanner: { - color: string + color: string } } \ No newline at end of file diff --git a/packages/gaming-ui/src/locales/en/messages.po b/packages/gaming-ui/src/locales/en/messages.po index e46ba11826..4c44a4d96d 100644 --- a/packages/gaming-ui/src/locales/en/messages.po +++ b/packages/gaming-ui/src/locales/en/messages.po @@ -13,33 +13,18 @@ msgstr "" "Language-Team: \n" "Plural-Forms: \n" -msgid "API Keys" -msgstr "API Keys" - msgid "Add API Key" msgstr "Add API Key" -msgid "Api Keys" -msgstr "Api Keys" - -msgid "Back to plan settings" -msgstr "Back to plan settings" - -msgid "Billed Monthly" -msgstr "Billed Monthly" - -msgid "Billed Yearly" -msgstr "Billed Yearly" - -msgid "Billing" -msgstr "Billing" - msgid "By connecting your wallet, you agree to our <0>Terms of Service and <1>Privacy Policy" msgstr "By connecting your wallet, you agree to our <0>Terms of Service and <1>Privacy Policy" msgid "Check your inbox! We've sent another email." msgstr "Check your inbox! We've sent another email." +msgid "Close" +msgstr "Close" + msgid "Connect Wallet to Gaming" msgstr "Connect Wallet to Gaming" @@ -67,11 +52,14 @@ msgstr "Continue with Web3 Wallet" msgid "Copied!" msgstr "Copied!" -msgid "Created At" -msgstr "Created At" +msgid "Created on:" +msgstr "Created on:" + +msgid "Dashboard" +msgstr "Dashboard" -msgid "Delete Key" -msgstr "Delete Key" +msgid "Delete key" +msgstr "Delete key" msgid "Didn't receive the email ?" msgstr "Didn't receive the email ?" @@ -97,9 +85,6 @@ msgstr "" "If you are using a contract wallet, please make \n" "sure you have activated your wallet." -msgid "Gaming Plan" -msgstr "Gaming Plan" - msgid "Get Started" msgstr "Get Started" @@ -109,8 +94,8 @@ msgstr "Go back" msgid "Hold on, we are logging you in…" msgstr "Hold on, we are logging you in…" -msgid "Id" -msgstr "Id" +msgid "Id:" +msgstr "Id:" msgid "Key ID" msgstr "Key ID" @@ -130,9 +115,6 @@ msgstr "Please enter a valid email" msgid "Privacy Policy" msgstr "Privacy Policy" -msgid "Purchase with card" -msgstr "Purchase with card" - msgid "Secret" msgstr "Secret" @@ -160,11 +142,8 @@ msgstr "Something went wrong!" msgid "Something went wrong! Please try again." msgstr "Something went wrong! Please try again." -msgid "Status" -msgstr "Status" - -msgid "Subscription Plan" -msgstr "Subscription Plan" +msgid "Status:" +msgstr "Status:" msgid "Terms and Conditions" msgstr "Terms and Conditions" @@ -184,9 +163,6 @@ msgstr "There was an error connecting your wallet" msgid "Try again" msgstr "Try again" -msgid "Type" -msgstr "Type" - msgid "Use a different login method" msgstr "Use a different login method" diff --git a/packages/storage-ui/package.json b/packages/storage-ui/package.json index 58da774b9e..9c5e863db4 100644 --- a/packages/storage-ui/package.json +++ b/packages/storage-ui/package.json @@ -64,7 +64,7 @@ "@types/yup": "^0.29.9", "@types/zxcvbn": "^4.4.0", "babel-plugin-macros": "^2.8.0", - "cypress": "^8.4", + "cypress": "^8.5", "cypress-file-upload": "^5.0.8", "cypress-pipe": "^2.0.0" }, diff --git a/packages/storage-ui/src/Components/Elements/CidRow.tsx b/packages/storage-ui/src/Components/Elements/CidRow.tsx index 74927f785d..e1bcc29086 100644 --- a/packages/storage-ui/src/Components/Elements/CidRow.tsx +++ b/packages/storage-ui/src/Components/Elements/CidRow.tsx @@ -56,7 +56,7 @@ const useStyles = makeStyles(({ animation, constants, breakpoints }: CSSTheme) = }) ) interface Props { - pinStatus: PinStatus + pinStatus: PinStatus } const IPFS_GATEWAY = process.env.REACT_APP_IPFS_GATEWAY || "" diff --git a/packages/storage-ui/src/Components/Elements/SecretField.tsx b/packages/storage-ui/src/Components/Elements/SecretField.tsx index 55ee36164e..7639455276 100644 --- a/packages/storage-ui/src/Components/Elements/SecretField.tsx +++ b/packages/storage-ui/src/Components/Elements/SecretField.tsx @@ -4,8 +4,8 @@ import { makeStyles, createStyles } from "@chainsafe/common-theme" import clsx from "clsx" interface Props { - value: string - className?: string + value: string + className?: string } const useStyles = makeStyles(() => diff --git a/packages/storage-ui/src/Components/Modules/ApiKeys.tsx b/packages/storage-ui/src/Components/Modules/ApiKeys.tsx index 7a5a9abb2f..2835108dc0 100644 --- a/packages/storage-ui/src/Components/Modules/ApiKeys.tsx +++ b/packages/storage-ui/src/Components/Modules/ApiKeys.tsx @@ -347,7 +347,7 @@ const ApiKeys = () => {
- + Make sure to save the secret, as it can only be displayed once. {copiedSecret && ( diff --git a/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx b/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx index eb3c4b4e04..c998d85f81 100644 --- a/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx +++ b/packages/storage-ui/src/Components/Modules/FilesList/FilesList.tsx @@ -323,27 +323,27 @@ const FilesList = () => { let temp = [] switch (column) { - default: { + default: { // case "name": { - temp = sourceFiles.sort((a, b) => { - return a.name.localeCompare(b.name, selectedLocale, { - sensitivity: "base" + 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 - } + 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) diff --git a/packages/storage-ui/src/Components/Modules/SurveyBanner.tsx b/packages/storage-ui/src/Components/Modules/SurveyBanner.tsx index bb4530d34e..d0fa268290 100644 --- a/packages/storage-ui/src/Components/Modules/SurveyBanner.tsx +++ b/packages/storage-ui/src/Components/Modules/SurveyBanner.tsx @@ -54,7 +54,7 @@ const useStyles = makeStyles( }) interface Props { - onHide: () => void + onHide: () => void } const SurveyBanner = ({ onHide }: Props) => { diff --git a/packages/storage-ui/src/Components/Pages/CidsPage.tsx b/packages/storage-ui/src/Components/Pages/CidsPage.tsx index 5b03cb1363..11cb057135 100644 --- a/packages/storage-ui/src/Components/Pages/CidsPage.tsx +++ b/packages/storage-ui/src/Components/Pages/CidsPage.tsx @@ -79,14 +79,14 @@ const CidsPage = () => { let temp = [] switch (sortColumn) { - case "size": { - temp = pins.sort((a, b) => (a.info?.size < b.info?.size ? -1 : 1)) - break - } - default: { - temp = pins.sort((a, b) => (a.created < b.created ? -1 : 1)) - break - } + case "size": { + temp = pins.sort((a, b) => (a.info?.size < b.info?.size ? -1 : 1)) + break + } + default: { + temp = pins.sort((a, b) => (a.created < b.created ? -1 : 1)) + break + } } return sortDirection === "descend" ? temp.reverse() : temp }, [pins, sortDirection, sortColumn]) diff --git a/packages/storage-ui/src/Contexts/FileOperationsReducers.tsx b/packages/storage-ui/src/Contexts/FileOperationsReducers.tsx index fc5e8984b1..486b1fe8df 100644 --- a/packages/storage-ui/src/Contexts/FileOperationsReducers.tsx +++ b/packages/storage-ui/src/Contexts/FileOperationsReducers.tsx @@ -3,117 +3,117 @@ import { DownloadProgress, UploadProgress } from "./StorageContext" export function uploadsInProgressReducer( uploadsInProgress: UploadProgress[], action: - | { type: "add"; payload: UploadProgress } - | { type: "progress"; payload: { id: string; progress: number } } - | { type: "complete"; payload: { id: string } } - | { type: "error"; payload: { id: string; errorMessage?: string } } - | { type: "remove"; payload: { id: string } } + | { type: "add"; payload: UploadProgress } + | { type: "progress"; payload: { id: string; progress: number } } + | { type: "complete"; payload: { id: string } } + | { type: "error"; payload: { id: string; errorMessage?: string } } + | { type: "remove"; payload: { id: string } } ): UploadProgress[] { const getProgressIndex = () => uploadsInProgress.findIndex((progress) => progress.id === action.payload.id) switch (action.type) { - case "add": { - return [...uploadsInProgress, action.payload] - } - case "progress": { - const progressIndex = getProgressIndex() - if (progressIndex > -1) { - uploadsInProgress[progressIndex].progress = action.payload.progress - return [...uploadsInProgress] - } else { - return uploadsInProgress + case "add": { + return [...uploadsInProgress, action.payload] } - } - case "complete": { - const progressIndex = getProgressIndex() - if (progressIndex > -1) { - uploadsInProgress[progressIndex].complete = true - return [...uploadsInProgress] - } else { - return uploadsInProgress + case "progress": { + const progressIndex = getProgressIndex() + if (progressIndex > -1) { + uploadsInProgress[progressIndex].progress = action.payload.progress + return [...uploadsInProgress] + } else { + return uploadsInProgress + } } - } - case "error": { - const progressIndex = getProgressIndex() - if (progressIndex > -1) { - uploadsInProgress[progressIndex].error = true - uploadsInProgress[progressIndex].errorMessage = + case "complete": { + const progressIndex = getProgressIndex() + if (progressIndex > -1) { + uploadsInProgress[progressIndex].complete = true + return [...uploadsInProgress] + } else { + return uploadsInProgress + } + } + case "error": { + const progressIndex = getProgressIndex() + if (progressIndex > -1) { + uploadsInProgress[progressIndex].error = true + uploadsInProgress[progressIndex].errorMessage = action.payload.errorMessage - return [...uploadsInProgress] - } else { - return uploadsInProgress + return [...uploadsInProgress] + } else { + return uploadsInProgress + } } - } - case "remove": { - const progressIndex = getProgressIndex() - if (progressIndex > -1) { - uploadsInProgress.splice(progressIndex, 1) - return [...uploadsInProgress] - } else { - return uploadsInProgress + case "remove": { + const progressIndex = getProgressIndex() + if (progressIndex > -1) { + uploadsInProgress.splice(progressIndex, 1) + return [...uploadsInProgress] + } else { + return uploadsInProgress + } } - } - default: - return uploadsInProgress + default: + return uploadsInProgress } } export function downloadsInProgressReducer( downloadsInProgress: DownloadProgress[], action: - | { type: "add"; payload: DownloadProgress } - | { type: "progress"; payload: { id: string; progress: number } } - | { type: "complete"; payload: { id: string } } - | { type: "error"; payload: { id: string; errorMessage?: string } } - | { type: "remove"; payload: { id: string } } + | { type: "add"; payload: DownloadProgress } + | { type: "progress"; payload: { id: string; progress: number } } + | { type: "complete"; payload: { id: string } } + | { type: "error"; payload: { id: string; errorMessage?: string } } + | { type: "remove"; payload: { id: string } } ): DownloadProgress[] { const getProgressIndex = () => downloadsInProgress.findIndex( (download) => download.id === action.payload.id ) switch (action.type) { - case "add": { - return [...downloadsInProgress, action.payload] - } - case "progress": { - const progressIndex = getProgressIndex() - if (progressIndex > -1) { - downloadsInProgress[progressIndex].progress = action.payload.progress - return [...downloadsInProgress] - } else { - return downloadsInProgress + case "add": { + return [...downloadsInProgress, action.payload] } - } - case "complete": { - const progressIndex = getProgressIndex() - if (progressIndex > -1) { - downloadsInProgress[progressIndex].complete = true - return [...downloadsInProgress] - } else { - return downloadsInProgress + case "progress": { + const progressIndex = getProgressIndex() + if (progressIndex > -1) { + downloadsInProgress[progressIndex].progress = action.payload.progress + return [...downloadsInProgress] + } else { + return downloadsInProgress + } } - } - case "error": { - const progressIndex = getProgressIndex() - if (progressIndex > -1) { - downloadsInProgress[progressIndex].error = true - downloadsInProgress[progressIndex].errorMessage = + case "complete": { + const progressIndex = getProgressIndex() + if (progressIndex > -1) { + downloadsInProgress[progressIndex].complete = true + return [...downloadsInProgress] + } else { + return downloadsInProgress + } + } + case "error": { + const progressIndex = getProgressIndex() + if (progressIndex > -1) { + downloadsInProgress[progressIndex].error = true + downloadsInProgress[progressIndex].errorMessage = action.payload.errorMessage - return [...downloadsInProgress] - } else { - return downloadsInProgress + return [...downloadsInProgress] + } else { + return downloadsInProgress + } } - } - case "remove": { - const progressIndex = getProgressIndex() - if (progressIndex > -1) { - downloadsInProgress.splice(progressIndex, 1) - return [...downloadsInProgress] - } else { - return downloadsInProgress + case "remove": { + const progressIndex = getProgressIndex() + if (progressIndex > -1) { + downloadsInProgress.splice(progressIndex, 1) + return [...downloadsInProgress] + } else { + return downloadsInProgress + } } - } - default: - return downloadsInProgress + default: + return downloadsInProgress } } diff --git a/packages/storage-ui/src/Contexts/StorageApiContext.tsx b/packages/storage-ui/src/Contexts/StorageApiContext.tsx index eaa2e38631..deee4c4cc6 100644 --- a/packages/storage-ui/src/Contexts/StorageApiContext.tsx +++ b/packages/storage-ui/src/Contexts/StorageApiContext.tsx @@ -15,28 +15,28 @@ const isReturningUserStorageKey = "css.isReturningUser" const TORUS_USERINFO_KEY = "css.userInfo" const getProviderSpecificParams = (loginType: LOGIN_TYPE): - {typeOfLogin: LOGIN_TYPE; clientId: string; verifier: string; jwtParams?: any} => { +{typeOfLogin: LOGIN_TYPE; clientId: string; verifier: string; jwtParams?: any} => { switch (loginType) { - case "google": { - return { - typeOfLogin: loginType, - clientId: process.env.REACT_APP_GOOGLE_CLIENT_ID || "", - verifier: "chainsafe-uuid-testnet" + case "google": { + return { + typeOfLogin: loginType, + clientId: process.env.REACT_APP_GOOGLE_CLIENT_ID || "", + verifier: "chainsafe-uuid-testnet" + } } - } - case "github":{ - return { - typeOfLogin: loginType, - clientId: process.env.REACT_APP_AUTH0_CLIENT_ID || "", - verifier: "chainsafe-uuid-testnet", - jwtParams: { - domain: process.env.REACT_APP_AUTH0_DOMAIN || "" + case "github":{ + return { + typeOfLogin: loginType, + clientId: process.env.REACT_APP_AUTH0_CLIENT_ID || "", + verifier: "chainsafe-uuid-testnet", + jwtParams: { + domain: process.env.REACT_APP_AUTH0_DOMAIN || "" + } } } - } - default:{ - throw new Error(`${loginType} is unsupported`) - } + default:{ + throw new Error(`${loginType} is unsupported`) + } } } @@ -101,7 +101,7 @@ const StorageApiProvider = ({ apiUrl, withLocalStorage = true, children }: Stora const [accessToken, setAccessToken] = useState(undefined) const [refreshToken, setRefreshToken] = useState(undefined) const [decodedRefreshToken, setDecodedRefreshToken] = useState< - { exp: number; enckey?: string; mps?: string; uuid: string } | undefined + { exp: number; enckey?: string; mps?: string; uuid: string } | undefined >(undefined) // returning user @@ -133,18 +133,18 @@ const StorageApiProvider = ({ apiUrl, withLocalStorage = true, children }: Stora if (userInfo && loginType) { switch (loginType) { - case "jwt": - setLoggedinAs(t`Web3: ${centerEllipsis(String(address), 4)}`) - break - case "google": - setLoggedinAs(`${capitalize(loginType)}: ${centerEllipsis(`${userInfo.email}`, 4)}`) - break - case "github": - setLoggedinAs(`${capitalize(loginType)}: ${centerEllipsis(`${userInfo.email}`, 4)}`) - break - default: - setLoggedinAs(`${centerEllipsis(`${userInfo.publicAddress}`, 4)}`) - break + case "jwt": + setLoggedinAs(t`Web3: ${centerEllipsis(String(address), 4)}`) + break + case "google": + setLoggedinAs(`${capitalize(loginType)}: ${centerEllipsis(`${userInfo.email}`, 4)}`) + break + case "github": + setLoggedinAs(`${capitalize(loginType)}: ${centerEllipsis(`${userInfo.email}`, 4)}`) + break + default: + setLoggedinAs(`${centerEllipsis(`${userInfo.publicAddress}`, 4)}`) + break } } }, [userInfo, address]) diff --git a/packages/storage-ui/src/Themes/Constants.ts b/packages/storage-ui/src/Themes/Constants.ts index 13d485b3db..844ac3663b 100644 --- a/packages/storage-ui/src/Themes/Constants.ts +++ b/packages/storage-ui/src/Themes/Constants.ts @@ -156,6 +156,6 @@ export interface CsSColors extends IConstants { } } surveyBanner: { - color: string + color: string } } \ No newline at end of file diff --git a/packages/storage-ui/src/Utils/MimeMatcher.ts b/packages/storage-ui/src/Utils/MimeMatcher.ts index d0466c7d73..c2c1186aec 100644 --- a/packages/storage-ui/src/Utils/MimeMatcher.ts +++ b/packages/storage-ui/src/Utils/MimeMatcher.ts @@ -46,48 +46,48 @@ function matcher(expected: string[]) { } class MimeMatcher { - expected = [] as { typeMatcher: (actual: string) => boolean; subTypeMatcher: (actual: string) => boolean }[] + expected = [] as { typeMatcher: (actual: string) => boolean; subTypeMatcher: (actual: string) => boolean }[] - constructor(expected: string | string[]) { - if (Array.isArray(expected)) { - this.expected = expected.map(mimeType => { - const { valid, type, subType } = parse(mimeType) - if (valid && type && subType) { - return ({ - typeMatcher: createMatcher(type), - subTypeMatcher: createMatcher(subType) - }) - } else { - const msg = `Value "${mimeType}" is not valid mime type.It should have format "type/subtype".` - throw new TypeError(msg) - } - }) - } - else { - const { valid, type, subType } = parse(expected) + constructor(expected: string | string[]) { + if (Array.isArray(expected)) { + this.expected = expected.map(mimeType => { + const { valid, type, subType } = parse(mimeType) if (valid && type && subType) { - this.expected = [{ + return ({ typeMatcher: createMatcher(type), subTypeMatcher: createMatcher(subType) - }] + }) } else { - const msg = `Value "${expected}" is not valid mime type.It should have format "type/subtype".` + const msg = `Value "${mimeType}" is not valid mime type.It should have format "type/subtype".` throw new TypeError(msg) } - } - + }) } - - match(actual: string) { - const { valid, type, subType } = parse(actual) + else { + const { valid, type, subType } = parse(expected) if (valid && type && subType) { - return this.expected.some(({ typeMatcher, subTypeMatcher }) => { - return typeMatcher(type) && subTypeMatcher(subType) - }) + this.expected = [{ + typeMatcher: createMatcher(type), + subTypeMatcher: createMatcher(subType) + }] } else { - return false + const msg = `Value "${expected}" is not valid mime type.It should have format "type/subtype".` + throw new TypeError(msg) } } + + } + + match(actual: string) { + const { valid, type, subType } = parse(actual) + if (valid && type && subType) { + return this.expected.some(({ typeMatcher, subTypeMatcher }) => { + return typeMatcher(type) && subTypeMatcher(subType) + }) + } else { + return false + } + } } export { isValid, parse, matcher } diff --git a/packages/storage-ui/src/Utils/contentTypeGuesser.ts b/packages/storage-ui/src/Utils/contentTypeGuesser.ts index d65fb94191..e6ec42156b 100644 --- a/packages/storage-ui/src/Utils/contentTypeGuesser.ts +++ b/packages/storage-ui/src/Utils/contentTypeGuesser.ts @@ -2,24 +2,24 @@ const guessContentType = (fileName: string) => { const { length, [length - 1]: ext } = fileName.split(".") switch (ext) { - case "pdf": - return "application/pdf" - case "jpg": - case "png": - case "gif": - case "bmp": - return `image/${ext}` - case "mp3": - case "m4a": - return `audio/${ext}` - case "mp4": - return `video/${ext}` - case "txt": - return "text/plain" - case "md": - return "text/markdown" - default: - return "application/octet-stream" + case "pdf": + return "application/pdf" + case "jpg": + case "png": + case "gif": + case "bmp": + return `image/${ext}` + case "mp3": + case "m4a": + return `audio/${ext}` + case "mp4": + return `video/${ext}` + case "txt": + return "text/plain" + case "md": + return "text/markdown" + default: + return "application/octet-stream" } } diff --git a/yarn.lock b/yarn.lock index c9843ffe93..872cb302d2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10736,10 +10736,10 @@ cypress-pipe@^2.0.0: resolved "https://registry.yarnpkg.com/cypress-pipe/-/cypress-pipe-2.0.0.tgz#577df7a70a8603d89a96dfe4092a605962181af8" integrity sha512-KW9s+bz4tFLucH3rBGfjW+Q12n7S4QpUSSyxiGrgPOfoHlbYWzAGB3H26MO0VTojqf9NVvfd5Kt0MH5XMgbfyg== -cypress@^8.4: - version "8.4.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-8.4.0.tgz#09ec06a73f1cb10121c103cba15076e659e24876" - integrity sha512-RtVgGFR06ikyMaq/VqapeqOjGaIA42PpK7F0qe1MCiFArfUuJECsLmeYaOA+1TlmNUgJNMSF5fWKkZIJr5Uc7w== +cypress@^8.5: + version "8.5.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-8.5.0.tgz#5712ca170913f8344bf167301205c4217c1eb9bd" + integrity sha512-MMkXIS+Ro2KETn4gAlG3tIc/7FiljuuCZP0zpd9QsRG6MZSyZW/l1J3D4iQM6WHsVxuX4rFChn5jPFlC2tNSvQ== dependencies: "@cypress/request" "^2.88.6" "@cypress/xvfb" "^1.2.4" @@ -10775,6 +10775,7 @@ cypress@^8.4: minimist "^1.2.5" ospath "^1.2.2" pretty-bytes "^5.6.0" + proxy-from-env "1.0.0" ramda "~0.27.1" request-progress "^3.0.0" supports-color "^8.1.1" @@ -20105,6 +20106,11 @@ proxy-addr@~2.0.5: forwarded "~0.1.2" ipaddr.js "1.9.1" +proxy-from-env@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4= + proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"