diff --git a/packages/files-ui/cypress/support/index.ts b/packages/files-ui/cypress/support/index.ts index cb8233c617..f0febdb6c2 100644 --- a/packages/files-ui/cypress/support/index.ts +++ b/packages/files-ui/cypress/support/index.ts @@ -28,5 +28,19 @@ Cypress.on("uncaught:exception", (err) => { } }) +// Hide fetch/XHR requests +// interim solution until cypress adds configuration support +// source https://gist.github.com/simenbrekken/3d2248f9e50c1143bf9dbe02e67f5399 +const app = window.top + +if(app != null && !app.document.head.querySelector("[data-hide-command-log-request]")) { + const style = app.document.createElement("style") + style.innerHTML = + ".command-name-request, .command-name-xhr { display: none }" + style.setAttribute("data-hide-command-log-request", "") + + app.document.head.appendChild(style) +} + // Alternatively you can use CommonJS syntax: // require('./commands') diff --git a/packages/files-ui/cypress/support/page-objects/toasts/recoverSuccessToast.ts b/packages/files-ui/cypress/support/page-objects/toasts/recoverSuccessToast.ts new file mode 100644 index 0000000000..19a1815be4 --- /dev/null +++ b/packages/files-ui/cypress/support/page-objects/toasts/recoverSuccessToast.ts @@ -0,0 +1,4 @@ +export const recoverSuccessToast = { + body: () => cy.get("[data-testId=toast-recover-success]", { timeout: 10000 }), + closeButton: () => cy.get("[data-testid=button-close-toast-recover-success]") +} diff --git a/packages/files-ui/cypress/tests/file-management-spec.ts b/packages/files-ui/cypress/tests/file-management-spec.ts index 67025b40c9..24ebd14914 100644 --- a/packages/files-ui/cypress/tests/file-management-spec.ts +++ b/packages/files-ui/cypress/tests/file-management-spec.ts @@ -11,6 +11,7 @@ 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 { recoverSuccessToast } from "../support/page-objects/toasts/recoverSuccessToast" import { uploadCompleteToast } from "../support/page-objects/toasts/uploadCompleteToast" describe("File management", () => { @@ -89,12 +90,16 @@ describe("File management", () => { // ensure the home root now has the folder and file navigationMenu.homeNavButton().click() - homePage.fileItemRow().should("have.length", 2) + homePage.fileItemRow() + .should("be.visible") + .should("have.length", 2) homePage.fileItemName().should("contain.text", folderName) homePage.fileItemName().should("contain.text", $fileName) - // ensure folder already in the root cannot be moved to Home - homePage.fileItemName().contains(`${$fileName}`).click() + // ensure file already in the root cannot be moved to Home + homePage.fileItemName().contains(`${$fileName}`) + .should("be.visible") + .click() homePage.moveSelectedButton().click() moveItemModal.folderList().contains("Home").click() moveItemModal.errorLabel().should("be.visible") @@ -112,8 +117,10 @@ describe("File management", () => { // select a parent folder and initiate move action homePage.fileItemName().contains("Parent").click() homePage.moveSelectedButton().click() + moveItemModal.body().should("be.visible") // ensure folder already in the root cannot be moved to Home + moveItemModal.folderList().should("be.visible") moveItemModal.folderList().contains("Home").click() moveItemModal.body().should("be.visible") moveItemModal.errorLabel().should("be.visible") @@ -259,6 +266,8 @@ describe("File management", () => { recoverItemModal.folderList().contains("Home").click() recoverItemModal.recoverButton().safeClick() binPage.fileItemRow().should("not.exist") + recoverSuccessToast.body().should("be.visible") + recoverSuccessToast.closeButton().click() // ensure recovered file is correct navigationMenu.homeNavButton().click() diff --git a/packages/files-ui/cypress/tests/file-preview-spec.ts b/packages/files-ui/cypress/tests/file-preview-spec.ts index d20d5b53bf..8a7507789a 100644 --- a/packages/files-ui/cypress/tests/file-preview-spec.ts +++ b/packages/files-ui/cypress/tests/file-preview-spec.ts @@ -11,6 +11,7 @@ describe("File Preview", () => { // add files homePage.uploadFile("../fixtures/uploadedFiles/logo.png") homePage.uploadFile("../fixtures/uploadedFiles/text-file.txt") + homePage.fileItemRow().should("have.length", 2) // store their file names as cypress aliases for later comparison homePage.fileItemName().eq(0).invoke("text").as("fileNameA") diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx index 516c3a6e19..ffc630d104 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/BinFileBrowser.tsx @@ -93,7 +93,8 @@ const BinFileBrowser: React.FC = ({ controls = false }: ).then(() => { addToast({ title: t`Data restored successfully`, - type: "success" + type: "success", + testId: "recover-success" }) }).catch((error) => { console.error("Error recovering:", error) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx index 0627a1663e..37c0e02aa3 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/CSFFileBrowser.tsx @@ -34,6 +34,8 @@ const CSFFileBrowser: React.FC = () => { const { pathname } = useLocation() const currentPath = useMemo(() => extractFileBrowserPathFromURL(pathname, ROUTE_LINKS.Drive("")), [pathname]) const bucket = useMemo(() => buckets.find(b => b.type === "csf"), [buckets]) + const { profile, localStore } = useUser() + const [showSurvey, setShowSurvey] = useState(false) const refreshContents = useCallback((showLoading?: boolean) => { if (!bucket) return @@ -50,10 +52,6 @@ const CSFFileBrowser: React.FC = () => { }).finally(() => showLoading && setLoadingCurrentPath(false)) }, [bucket, filesApiClient, currentPath]) - const { profile, localStore, setLocalStore } = useUser() - - const showSurvey = localStore && localStore[DISMISSED_SURVEY_KEY] === "false" - const olderThanOneWeek = useMemo( () => profile?.createdAt ? dayjs(Date.now()).diff(profile.createdAt, "day") > 7 @@ -62,11 +60,14 @@ const CSFFileBrowser: React.FC = () => { ) useEffect(() => { - const dismissedFlag = localStore && localStore[DISMISSED_SURVEY_KEY] - if (dismissedFlag === undefined || dismissedFlag === null) { - setLocalStore({ [DISMISSED_SURVEY_KEY]: "false" }, "update") + if (!localStore) { + return + } + + if (localStore[DISMISSED_SURVEY_KEY] === "false"){ + setShowSurvey(true) } - }, [localStore, setLocalStore]) + }, [localStore]) useEffect(() => { refreshContents(true) diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx index 3e6f46d8b9..f3d3df1afd 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/SharedFoldersOverview.tsx @@ -267,7 +267,7 @@ const SharedFolderOverview = () => { )} { const { localStore, setLocalStore } = useUser() - const [hasSeenSharingExplainerModal, setHasSeenSharingExplainerModal] = useState(false) - const dismissedFlag = localStore ? localStore[DISMISSED_SHARING_EXPLAINER_KEY] : null - + const [hasSeenSharingExplainerModal, setHasSeenSharingExplainerModal] = useState(true) useEffect(() => { - if (dismissedFlag === "false"){ - setHasSeenSharingExplainerModal(true) - } else if (dismissedFlag === null) { - // the dismiss flag was never set - setLocalStore({ [DISMISSED_SHARING_EXPLAINER_KEY]: "false" }, "update") - setHasSeenSharingExplainerModal(true) + if (!localStore) { + return + } + + if (localStore[DISMISSED_SHARING_EXPLAINER_KEY] === "false"){ + setHasSeenSharingExplainerModal(false) } - }, [dismissedFlag, setLocalStore]) + }, [localStore, setLocalStore]) const hideModal = useCallback(() => { setLocalStore({ [DISMISSED_SHARING_EXPLAINER_KEY]: "true" }, "update") - setHasSeenSharingExplainerModal(false) + setHasSeenSharingExplainerModal(true) }, [setLocalStore]) return { hasSeenSharingExplainerModal, hideModal } diff --git a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx index eb5f982beb..78da7bc74e 100644 --- a/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx +++ b/packages/files-ui/src/Components/Modules/FileBrowsers/views/FilesList.tsx @@ -348,7 +348,7 @@ const FilesList = ({ isShared = false }: Props) => { const { hasSeenSharingExplainerModal, hideModal } = useSharingExplainerModalFlag() const [hasClickedShare, setClickedShare] = useState(false) const showExplainerBeforeShare = useMemo(() => - hasSeenSharingExplainerModal && hasClickedShare + !hasSeenSharingExplainerModal && hasClickedShare , [hasClickedShare, hasSeenSharingExplainerModal] ) const items: FileSystemItemType[] = useMemo(() => { @@ -616,9 +616,10 @@ const FilesList = ({ isShared = false }: Props) => { setIsDeleteModalOpen(true) }, []) - const handleOpenShareDialog = useCallback((e: React.MouseEvent) => { - e.preventDefault() - e.stopPropagation() + const handleOpenShareDialog = useCallback((e?: React.MouseEvent) => { + e?.preventDefault() + e?.stopPropagation() + setClickedShare(true) setIsShareModalOpen(true) }, []) @@ -649,13 +650,9 @@ const FilesList = ({ isShared = false }: Props) => { [classes.menuIcon]) const onShare = useCallback((fileSystemItem: FileSystemItemType) => { - if(hasSeenSharingExplainerModal) { - setClickedShare(true) - } - setSelectedItems([fileSystemItem]) - setIsShareModalOpen(true) - }, [hasSeenSharingExplainerModal]) + handleOpenShareDialog() + }, [handleOpenShareDialog]) return (
{ return } else { switch (next) { - case 3: + case STEP_NUMBER: setLocalStore({ [DISMISSED_SHARING_EXPLAINER_KEY]: "true" }, "update") - setStep(3) + setStep(STEP_NUMBER) break case STEP_NUMBER + 1: onHide() diff --git a/packages/files-ui/src/Components/SurveyBanner.tsx b/packages/files-ui/src/Components/SurveyBanner.tsx index 8a08f3a066..92778c79c9 100644 --- a/packages/files-ui/src/Components/SurveyBanner.tsx +++ b/packages/files-ui/src/Components/SurveyBanner.tsx @@ -62,7 +62,7 @@ const SurveyBanner = ({ onHide }: Props) => { setLocalStore({ [DISMISSED_SURVEY_KEY]: "true" }, "update") }, [setLocalStore, onHide]) - const onOpen = useCallback(() => { + const onOpenLink = useCallback(() => { onClose() window.open(ROUTE_LINKS.UserSurvey, "_blank") }, [onClose]) @@ -77,7 +77,7 @@ const SurveyBanner = ({ onHide }: Props) => { Schedule a 15 min call diff --git a/packages/files-ui/src/Contexts/UserContext.tsx b/packages/files-ui/src/Contexts/UserContext.tsx index 8a49a1ebb9..058edf38ce 100644 --- a/packages/files-ui/src/Contexts/UserContext.tsx +++ b/packages/files-ui/src/Contexts/UserContext.tsx @@ -3,6 +3,8 @@ import { useCallback, useEffect } from "react" import { useFilesApi } from "./FilesApiContext" import { useState } from "react" import { t } from "@lingui/macro" +import { DISMISSED_SHARING_EXPLAINER_KEY } from "../Components/Modules/FileBrowsers/hooks/useSharingExplainerModalFlag" +import { DISMISSED_SURVEY_KEY } from "../Components/SurveyBanner" import { Details } from "@chainsafe/files-api-client" type UserContextProps = { @@ -44,23 +46,20 @@ 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 toStore = method === "update" + ? { ...localStore, ...newData } + : newData + + filesApiClient.updateUserLocalStore(toStore) + .then(_setLocalStore) + .catch(console.error) + + }, [filesApiClient, localStore]) const refreshProfile = useCallback(async () => { try { @@ -78,40 +77,48 @@ const UserProvider = ({ children }: UserContextProps) => { setProfile(profileState) return Promise.resolve() } catch (error) { + console.error(error) return Promise.reject("There was an error getting profile.") } }, [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) - } + const initLocalStore = useCallback((apiStore: ILocalStore | undefined) => { + let initStore = apiStore || {} + + if (apiStore?.[DISMISSED_SHARING_EXPLAINER_KEY] === undefined) { + initStore = { ...initStore, [DISMISSED_SHARING_EXPLAINER_KEY]: "false" } + } + + if (apiStore?.[DISMISSED_SURVEY_KEY] === undefined) { + initStore = { ...initStore, [DISMISSED_SURVEY_KEY]: "false" } } - if (isLoggedIn) { - manageAsync() + + _setLocalStore(initStore) + }, []) + + useEffect(() => { + if (!isLoggedIn) { + return } - }, [isLoggedIn, localStore, filesApiClient]) + + filesApiClient.getUserLocalStore() + .then((apiStore) => { + initLocalStore(apiStore) + }) + .catch((e) => { + console.error(e) + initLocalStore({}) + }) + }, [isLoggedIn, filesApiClient, initLocalStore]) useEffect(() => { - if (isLoggedIn) { - refreshProfile() - .catch(console.error) + if (!isLoggedIn) { + return } + + refreshProfile() + .catch(console.error) + }, [isLoggedIn, refreshProfile]) const updateProfile = async (firstName?: string, lastName?: string) => {