From 2c444ddacf2315246c4a8ef8246a70b2347b6d75 Mon Sep 17 00:00:00 2001 From: Cataldo Mazzilli Date: Mon, 16 Sep 2024 17:42:42 +0200 Subject: [PATCH 1/5] feat: handle open with docs over size failures Refs: FILES-841 --- .../constants/index.ts | 3 +- .../hooks/useHeaderActions.ts | 60 ++++++++++ .../hooks/useOpenWithDocs.test.ts | 109 ++++++++++++++++++ .../hooks/useOpenWithDocs.ts | 77 +++++++++++++ src/carbonio-files-ui-common/utils/utils.ts | 20 ---- .../views/components/DisplayerActions.tsx | 8 +- .../views/components/FilesQuota.test.tsx | 6 +- .../views/components/List.test.tsx | 11 +- .../views/components/List.tsx | 57 ++------- .../views/components/NodeListItem.test.tsx | 5 +- .../views/components/NodeListItem.tsx | 7 +- .../components/versioning/VersionRow.tsx | 6 +- .../components/versioning/Versioning.test.tsx | 5 +- 13 files changed, 291 insertions(+), 83 deletions(-) create mode 100644 src/carbonio-files-ui-common/hooks/useHeaderActions.ts create mode 100644 src/carbonio-files-ui-common/hooks/useOpenWithDocs.test.ts create mode 100644 src/carbonio-files-ui-common/hooks/useOpenWithDocs.ts diff --git a/src/carbonio-files-ui-common/constants/index.ts b/src/carbonio-files-ui-common/constants/index.ts index e3e68a05..6d721335 100644 --- a/src/carbonio-files-ui-common/constants/index.ts +++ b/src/carbonio-files-ui-common/constants/index.ts @@ -133,7 +133,8 @@ export const HTTP_STATUS_CODE = { maxVersionReached: 405, /** aborted or blocked request */ aborted: 0, - overQuota: 422 + overQuota: 422, + docsFileSizeExceeded: 413 } as const; export const ERROR_CODE = { diff --git a/src/carbonio-files-ui-common/hooks/useHeaderActions.ts b/src/carbonio-files-ui-common/hooks/useHeaderActions.ts new file mode 100644 index 00000000..775a3570 --- /dev/null +++ b/src/carbonio-files-ui-common/hooks/useHeaderActions.ts @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 2024 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { useCallback } from 'react'; + +import { HeaderAction } from '@zextras/carbonio-ui-preview/lib/preview/Header'; +import { useTranslation } from 'react-i18next'; + +import { useHealthInfo } from './useHealthInfo'; +import { useOpenWithDocs } from './useOpenWithDocs'; +import { useActiveNode } from '../../hooks/useActiveNode'; +import { DISPLAYER_TABS } from '../constants'; +import { File } from '../types/graphql/types'; +import { canEdit, canOpenWithDocs } from '../utils/ActionsFactory'; +import { downloadNode } from '../utils/utils'; + +export function useHeaderActions(): (node: File) => Array { + const [t] = useTranslation(); + const openNodeWithDocs = useOpenWithDocs(); + const { setActiveNode } = useActiveNode(); + const { canUseDocs } = useHealthInfo(); + + return useCallback( + (node) => { + const actions: Array = [ + { + icon: 'ShareOutline', + id: 'ShareOutline', + tooltipLabel: t('preview.actions.tooltip.manageShares', 'Manage shares'), + onClick: (): void => setActiveNode(node.id, DISPLAYER_TABS.sharing) + }, + { + icon: 'DownloadOutline', + tooltipLabel: t('preview.actions.tooltip.download', 'Download'), + id: 'DownloadOutline', + onClick: (): void => downloadNode(node.id) + } + ]; + if (canEdit({ nodes: [node], canUseDocs })) { + actions.unshift({ + icon: 'Edit2Outline', + id: 'Edit', + onClick: (): Promise => openNodeWithDocs(node.id), + tooltipLabel: t('preview.actions.tooltip.edit', 'Edit') + }); + } else if (canOpenWithDocs({ nodes: [node], canUseDocs })) { + actions.unshift({ + id: 'OpenWithDocs', + icon: 'BookOpenOutline', + tooltipLabel: t('actions.openWithDocs', 'Open document'), + onClick: (): Promise => openNodeWithDocs(node.id) + }); + } + return actions; + }, + [canUseDocs, openNodeWithDocs, setActiveNode, t] + ); +} diff --git a/src/carbonio-files-ui-common/hooks/useOpenWithDocs.test.ts b/src/carbonio-files-ui-common/hooks/useOpenWithDocs.test.ts new file mode 100644 index 00000000..bd8eaa81 --- /dev/null +++ b/src/carbonio-files-ui-common/hooks/useOpenWithDocs.test.ts @@ -0,0 +1,109 @@ +/* + * SPDX-FileCopyrightText: 2024 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { faker } from '@faker-js/faker'; +import { act } from '@testing-library/react-hooks'; +import { CreateSnackbarFn, CreateSnackbarFnArgs } from '@zextras/carbonio-design-system'; +import { http, HttpResponse } from 'msw'; + +import { OpenWithDocsResponse, useOpenWithDocs } from './useOpenWithDocs'; +import server from '../../mocks/server'; +import { DOCS_ENDPOINT, HTTP_STATUS_CODE, OPEN_FILE_PATH } from '../constants'; +import { setupHook } from '../tests/utils'; + +let mockCreateSnackbar: jest.MockedFn; + +jest.mock('@zextras/carbonio-design-system', () => ({ + ...jest.requireActual('@zextras/carbonio-design-system'), + useSnackbar: (): CreateSnackbarFn => mockCreateSnackbar +})); + +beforeEach(() => { + mockCreateSnackbar = jest.fn(); +}); + +describe('useOpenWithDocs hook', () => { + it('should open the returned url if the document can be opened', async () => { + const url = faker.internet.url(); + server.use( + http.get, never, OpenWithDocsResponse>( + `${DOCS_ENDPOINT}${OPEN_FILE_PATH}/:id`, + () => HttpResponse.json({ url }) + ) + ); + + const spyWindowOpen = jest.spyOn(window, 'open').mockImplementation(); + const { result } = setupHook(() => useOpenWithDocs()); + await result.current('id'); + expect(spyWindowOpen).toHaveBeenCalledWith(url, url); + }); + + it('should show specific snackbar if document cannot be opened due to its size', async () => { + server.use( + http.get, never, OpenWithDocsResponse>( + `${DOCS_ENDPOINT}${OPEN_FILE_PATH}/:id`, + () => HttpResponse.json(null, { status: HTTP_STATUS_CODE.docsFileSizeExceeded }) + ) + ); + + const spyWindowOpen = jest.spyOn(window, 'open').mockImplementation(); + const { result } = setupHook(() => useOpenWithDocs()); + + await act(async () => { + await result.current('id'); + }); + + expect(spyWindowOpen).not.toHaveBeenCalled(); + const label = + 'The item exceeds the size limit allowed and cannot be opened. To view the item, please download it on your device'; + expect(mockCreateSnackbar).toHaveBeenCalledWith( + expect.objectContaining({ + label, + actionLabel: 'Ok', + disableAutoHide: true, + severity: 'warning' + }) + ); + }); + + it('should show generic error snackbar if status code is unhandled', async () => { + server.use( + http.get, never, OpenWithDocsResponse>( + `${DOCS_ENDPOINT}${OPEN_FILE_PATH}/:id`, + () => HttpResponse.json(null, { status: 500 }) + ) + ); + + const spyWindowOpen = jest.spyOn(window, 'open').mockImplementation(); + const { result } = setupHook(() => useOpenWithDocs()); + + await act(async () => { + await result.current('id'); + }); + + expect(spyWindowOpen).not.toHaveBeenCalled(); + const label = 'Something went wrong'; + expect(mockCreateSnackbar).toHaveBeenCalledWith( + expect.objectContaining({ label }) + ); + }); + + it('should show generic error snackbar if there is a network error', async () => { + server.use(http.get(`${DOCS_ENDPOINT}${OPEN_FILE_PATH}/:id`, () => HttpResponse.error())); + + const spyWindowOpen = jest.spyOn(window, 'open').mockImplementation(); + const { result } = setupHook(() => useOpenWithDocs()); + + await act(async () => { + await result.current('id'); + }); + + expect(spyWindowOpen).not.toHaveBeenCalled(); + const label = 'Something went wrong'; + expect(mockCreateSnackbar).toHaveBeenCalledWith( + expect.objectContaining({ label }) + ); + }); +}); diff --git a/src/carbonio-files-ui-common/hooks/useOpenWithDocs.ts b/src/carbonio-files-ui-common/hooks/useOpenWithDocs.ts new file mode 100644 index 00000000..5057dc03 --- /dev/null +++ b/src/carbonio-files-ui-common/hooks/useOpenWithDocs.ts @@ -0,0 +1,77 @@ +/* + * SPDX-FileCopyrightText: 2024 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { useCallback } from 'react'; + +import { useSnackbar } from '@zextras/carbonio-design-system'; +import { useTranslation } from 'react-i18next'; + +import { DOCS_ENDPOINT, HTTP_STATUS_CODE, OPEN_FILE_PATH } from '../constants'; + +export type OpenWithDocsResponse = { url: string }; + +const docsTabMap: { [url: string]: Window } = {}; + +const openNodeWithDocs = (url: string): void => { + if (docsTabMap[url] == null || docsTabMap[url]?.closed) { + docsTabMap[url] = window.open(url, url) as Window; + } else { + docsTabMap[url].focus(); + } +}; + +type OpenWithDocsFn = (id: string, version?: number) => Promise; + +export const useOpenWithDocs = (): OpenWithDocsFn => { + const createSnackbar = useSnackbar(); + const [t] = useTranslation(); + return useCallback( + async (id, version) => { + try { + const response = await fetch( + `${DOCS_ENDPOINT}${OPEN_FILE_PATH}/${encodeURIComponent(id)}${ + version ? `?version=${version}` : '' + }`, + { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + } + ); + if (!response.ok) { + if (response.status === HTTP_STATUS_CODE.docsFileSizeExceeded) { + createSnackbar({ + key: new Date().toLocaleString(), + severity: 'warning', + label: t( + 'snackbar.openWithDocs.error.exceedSizeLimit', + 'The item exceeds the size limit allowed and cannot be opened. To view the item, please download it on your device' + ), + replace: true, + actionLabel: t('snackbar.openWithDocs.error.exceedSizeLimit.actionLabel', 'Ok'), + disableAutoHide: true + }); + } else { + throw new Error('Error code not handled'); + } + } else { + const { url } = (await response.json()) as OpenWithDocsResponse; + openNodeWithDocs(url); + } + } catch (error) { + createSnackbar({ + key: new Date().toLocaleString(), + severity: 'warning', + label: t('errorCode.code', 'Something went wrong', { context: 'Generic' }), + replace: true, + hideButton: true + }); + } + }, + [createSnackbar, t] + ); +}; diff --git a/src/carbonio-files-ui-common/utils/utils.ts b/src/carbonio-files-ui-common/utils/utils.ts index c555194e..12593966 100644 --- a/src/carbonio-files-ui-common/utils/utils.ts +++ b/src/carbonio-files-ui-common/utils/utils.ts @@ -16,11 +16,9 @@ import { DefaultTheme } from 'styled-components'; import { DATE_FORMAT, - DOCS_ENDPOINT, DOCS_EXTENSIONS, DOWNLOAD_PATH, INTERNAL_PATH, - OPEN_FILE_PATH, REST_ENDPOINT, ROOTS, TIMERS, @@ -334,24 +332,6 @@ export const downloadNode = (id: string, version?: number): void => { } }; -const docsTabMap: { [url: string]: Window } = {}; - -/** - * Open with docs - */ -export const openNodeWithDocs = (id: string, version?: number): void => { - if (id) { - const url = `${DOCS_ENDPOINT}${OPEN_FILE_PATH}/${encodeURIComponent(id)}${ - version ? `?version=${version}` : '' - }`; - if (docsTabMap[url] == null || docsTabMap[url]?.closed) { - docsTabMap[url] = window.open(url, url) as Window; - } else { - docsTabMap[url].focus(); - } - } -}; - export const inputElement = ((): HTMLInputElement => { const input = document.createElement('input'); if (input) { diff --git a/src/carbonio-files-ui-common/views/components/DisplayerActions.tsx b/src/carbonio-files-ui-common/views/components/DisplayerActions.tsx index 9a0d2601..c68fd9b9 100644 --- a/src/carbonio-files-ui-common/views/components/DisplayerActions.tsx +++ b/src/carbonio-files-ui-common/views/components/DisplayerActions.tsx @@ -24,6 +24,7 @@ import { useDeletePermanentlyModal } from '../../hooks/modals/useDeletePermanent import { useMoveModal } from '../../hooks/modals/useMoveModal'; import { useRenameModal } from '../../hooks/modals/useRenameModal'; import { useHealthInfo } from '../../hooks/useHealthInfo'; +import { useOpenWithDocs } from '../../hooks/useOpenWithDocs'; import { Action, GetNodeParentType } from '../../types/common'; import { File, MakeOptional, Node } from '../../types/graphql/types'; import { @@ -32,7 +33,7 @@ import { getAllPermittedActions } from '../../utils/ActionsFactory'; import { isSupportedByPreview } from '../../utils/previewUtils'; -import { downloadNode, isFile, openNodeWithDocs } from '../../utils/utils'; +import { downloadNode, isFile } from '../../utils/utils'; interface DisplayerActionsParams { node: ActionsFactoryNodeType & @@ -94,6 +95,8 @@ export const DisplayerActions: React.VFC = ({ node }) => const { sendViaMail } = useSendViaMail(); + const openNodeWithDocs = useOpenWithDocs(); + const sendViaMailCallback = useCallback(() => { sendViaMail(node.id); }, [node, sendViaMail]); @@ -117,7 +120,7 @@ export const DisplayerActions: React.VFC = ({ node }) => // if preview is not supported and document can be opened with docs, open editor openNodeWithDocs(node.id); } - }, [$isSupportedByPreview, permittedDisplayerActions, openPreview, node.id]); + }, [$isSupportedByPreview, permittedDisplayerActions, openPreview, node.id, openNodeWithDocs]); const itemsMap = useMemo>>( () => ({ @@ -230,6 +233,7 @@ export const DisplayerActions: React.VFC = ({ node }) => openCopyNodesModal, openDeletePermanentlyModal, openMoveNodesModal, + openNodeWithDocs, openRenameModal, preview, restoreNodeCallback, diff --git a/src/carbonio-files-ui-common/views/components/FilesQuota.test.tsx b/src/carbonio-files-ui-common/views/components/FilesQuota.test.tsx index d92bb446..a70a096d 100644 --- a/src/carbonio-files-ui-common/views/components/FilesQuota.test.tsx +++ b/src/carbonio-files-ui-common/views/components/FilesQuota.test.tsx @@ -17,13 +17,17 @@ import * as useFilesQuotaInfo from '../../hooks/useFilesQuotaInfo'; import { screen, setup } from '../../tests/utils'; import { humanFileSize } from '../../utils/utils'; -const mockQuota = jest.fn().mockReturnValue(
mock Quota
); +let mockQuota: (props: QuotaProps) => React.JSX.Element; jest.mock('@zextras/carbonio-design-system', () => ({ ...jest.requireActual('@zextras/carbonio-design-system'), Quota: (props: QuotaProps): unknown => mockQuota(props) })); +beforeEach(() => { + mockQuota = jest.fn().mockReturnValue(
mock Quota
); +}); + describe('Files Quota', () => { describe('Unlimited available space (limit = 0)', () => { it('should show the string "[used] of unlimited spaceā€', () => { diff --git a/src/carbonio-files-ui-common/views/components/List.test.tsx b/src/carbonio-files-ui-common/views/components/List.test.tsx index dfada4df..d41d480d 100644 --- a/src/carbonio-files-ui-common/views/components/List.test.tsx +++ b/src/carbonio-files-ui-common/views/components/List.test.tsx @@ -8,11 +8,11 @@ import React from 'react'; import { List } from './List'; import { PREVIEW_PATH, PREVIEW_TYPE, REST_ENDPOINT } from '../../constants'; import { ICON_REGEXP, SELECTORS } from '../../constants/test'; +import * as useOpenWithDocs from '../../hooks/useOpenWithDocs'; import { populateFile, populateNodes } from '../../mocks/mockUtils'; import { selectNodes, setup, screen } from '../../tests/utils'; import { NodeType } from '../../types/graphql/types'; import * as previewUtils from '../../utils/previewUtils'; -import * as utils from '../../utils/utils'; describe('List', () => { describe('Badge', () => { @@ -87,7 +87,8 @@ describe('List', () => { }); test('Double click on node that is supported by both preview and docs and has write permissions open document with docs', async () => { - const openWithDocsFn = jest.spyOn(utils, 'openNodeWithDocs'); + const openWithDocsFn = jest.fn(); + jest.spyOn(useOpenWithDocs, 'useOpenWithDocs').mockReturnValue(openWithDocsFn); const node = populateFile(); node.permissions.can_write_file = true; node.mime_type = 'application/vnd.oasis.opendocument.text'; @@ -101,7 +102,8 @@ describe('List', () => { }); test('Double click on node that is supported by both preview and docs but does not have write permissions open document with preview', async () => { - const openWithDocsFn = jest.spyOn(utils, 'openNodeWithDocs'); + const openWithDocsFn = jest.fn(); + jest.spyOn(useOpenWithDocs, 'useOpenWithDocs').mockReturnValue(openWithDocsFn); const node = populateFile(); node.permissions.can_write_file = false; node.mime_type = 'application/vnd.oasis.opendocument.text'; @@ -126,7 +128,8 @@ describe('List', () => { }); test('Double click on node that is not supported by preview nor docs does nothing', async () => { - const openWithDocsFn = jest.spyOn(utils, 'openNodeWithDocs'); + const openWithDocsFn = jest.fn(); + jest.spyOn(useOpenWithDocs, 'useOpenWithDocs').mockReturnValue(openWithDocsFn); const getDocumentPreviewSrcFn = jest.spyOn(previewUtils, 'getDocumentPreviewSrc'); const getPdfPreviewSrcFn = jest.spyOn(previewUtils, 'getPdfPreviewSrc'); const getImgPreviewSrcFn = jest.spyOn(previewUtils, 'getImgPreviewSrc'); diff --git a/src/carbonio-files-ui-common/views/components/List.tsx b/src/carbonio-files-ui-common/views/components/List.tsx index 16c1ea68..e22365f7 100644 --- a/src/carbonio-files-ui-common/views/components/List.tsx +++ b/src/carbonio-files-ui-common/views/components/List.tsx @@ -9,9 +9,7 @@ import React, { useCallback, useContext, useEffect, useMemo, useState } from 're import { useQuery, useReactiveVar } from '@apollo/client'; import { Action as DSAction, Container, useSnackbar } from '@zextras/carbonio-design-system'; import { PreviewsManagerContext } from '@zextras/carbonio-ui-preview'; -import { HeaderAction } from '@zextras/carbonio-ui-preview/lib/preview/Header'; import { PreviewManagerContextType } from '@zextras/carbonio-ui-preview/lib/preview/PreviewManager'; -import { TFunction } from 'i18next'; import { isEmpty, find, filter, includes, reduce, size, some } from 'lodash'; import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; @@ -48,7 +46,9 @@ import { OpenCopyModal, useCopyModal } from '../../hooks/modals/useCopyModal'; import { useDeletePermanentlyModal } from '../../hooks/modals/useDeletePermanentlyModal'; import { OpenMoveModal, useMoveModal } from '../../hooks/modals/useMoveModal'; import { OpenRenameModal, useRenameModal } from '../../hooks/modals/useRenameModal'; +import { useHeaderActions } from '../../hooks/useHeaderActions'; import { useHealthInfo } from '../../hooks/useHealthInfo'; +import { useOpenWithDocs } from '../../hooks/useOpenWithDocs'; import useSelection from '../../hooks/useSelection'; import { useUpload } from '../../hooks/useUpload'; import { Action, Crumb, NodeListItemType } from '../../types/common'; @@ -58,56 +58,16 @@ import { ActionsFactoryCheckerMap, buildActionItems, canBeMoveDestination, - canEdit, - canOpenWithDocs, getAllPermittedActions } from '../../utils/ActionsFactory'; import { getPreviewSrc, isSupportedByPreview } from '../../utils/previewUtils'; import { getUploadAddType } from '../../utils/uploadUtils'; -import { downloadNode, humanFileSize, isFile, isFolder, openNodeWithDocs } from '../../utils/utils'; +import { downloadNode, humanFileSize, isFile, isFolder } from '../../utils/utils'; const MainContainer = styled(Container)` border-left: 0.0625rem solid ${(props): string => props.theme.palette.gray6.regular}; `; -function getHeaderActions( - t: TFunction, - setActiveNode: ReturnType['setActiveNode'], - node: File, - canUseDocs: boolean -): Array { - const actions: Array = [ - { - icon: 'ShareOutline', - id: 'ShareOutline', - tooltipLabel: t('preview.actions.tooltip.manageShares', 'Manage shares'), - onClick: (): void => setActiveNode(node.id, DISPLAYER_TABS.sharing) - }, - { - icon: 'DownloadOutline', - tooltipLabel: t('preview.actions.tooltip.download', 'Download'), - id: 'DownloadOutline', - onClick: (): void => downloadNode(node.id) - } - ]; - if (canEdit({ nodes: [node], canUseDocs })) { - actions.unshift({ - icon: 'Edit2Outline', - id: 'Edit', - onClick: (): void => openNodeWithDocs(node.id), - tooltipLabel: t('preview.actions.tooltip.edit', 'Edit') - }); - } else if (canOpenWithDocs({ nodes: [node], canUseDocs })) { - actions.unshift({ - id: 'OpenWithDocs', - icon: 'BookOpenOutline', - tooltipLabel: t('actions.openWithDocs', 'Open document'), - onClick: (): void => openNodeWithDocs(node.id) - }); - } - return actions; -} - interface ListProps { nodes: NodeListItemType[]; loading?: boolean; @@ -203,6 +163,7 @@ export const List: React.VFC = ({ }, [folderId] ); + const openNodeWithDocs = useOpenWithDocs(); const { canUsePreview, canUseDocs } = useHealthInfo(); @@ -348,10 +309,12 @@ export const List: React.VFC = ({ openNodeWithDocs(nodeToOpen.id); exitSelectionMode(); } - }, [nodes, selectedIDs, exitSelectionMode]); + }, [nodes, selectedIDs, openNodeWithDocs, exitSelectionMode]); const { initPreview, emptyPreview, openPreview } = useContext(PreviewsManagerContext); + const getHeaderActions = useHeaderActions(); + const nodesForPreview = useMemo( () => reduce( @@ -371,7 +334,7 @@ export const List: React.VFC = ({ filename: node.name, extension: node.extension ?? undefined, size: (node.size !== undefined && humanFileSize(node.size, t)) || undefined, - actions: getHeaderActions(t, setActiveNode, node, canUseDocs), + actions: getHeaderActions(node), closeAction: { id: 'close-action', icon: 'ArrowBackOutline', @@ -398,7 +361,7 @@ export const List: React.VFC = ({ }, [] ), - [canUseDocs, nodes, setActiveNode, t] + [getHeaderActions, nodes, t] ); useEffect(() => { @@ -416,7 +379,7 @@ export const List: React.VFC = ({ // if preview is not supported and document can be opened with docs, open editor openNodeWithDocs(id); } - }, [nodes, permittedSelectionModeActions, selectedIDs, openPreview]); + }, [nodes, permittedSelectionModeActions, selectedIDs, openPreview, openNodeWithDocs]); const itemsMap = useMemo>>( () => ({ diff --git a/src/carbonio-files-ui-common/views/components/NodeListItem.test.tsx b/src/carbonio-files-ui-common/views/components/NodeListItem.test.tsx index 0bdbe361..d711f5ff 100644 --- a/src/carbonio-files-ui-common/views/components/NodeListItem.test.tsx +++ b/src/carbonio-files-ui-common/views/components/NodeListItem.test.tsx @@ -23,6 +23,7 @@ import { import { ICON_REGEXP, SELECTORS } from '../../constants/test'; import { ListContext } from '../../contexts'; import * as useHealthInfo from '../../hooks/useHealthInfo'; +import * as useOpenWithDocs from '../../hooks/useOpenWithDocs'; import * as usePreview from '../../hooks/usePreview'; import { populateFile, @@ -40,7 +41,6 @@ import { } from '../../utils/previewUtils'; import { formatDate, humanFileSize } from '../../utils/utils'; import 'jest-styled-components'; -import * as utils from '../../utils/utils'; export function getMissingProps(): Pick< NodeListItemProps, @@ -99,7 +99,8 @@ describe('Node List Item', () => { ])( `should %s when canUsePreview is %s, canUseDocs is %s, canWriteFile is %s, mime_type is %s `, async (action, canUsePreview, canUseDocs, canWriteFile, mimeType) => { - const openWithDocsFn = jest.spyOn(utils, 'openNodeWithDocs'); + const openWithDocsFn = jest.fn(); + jest.spyOn(useOpenWithDocs, 'useOpenWithDocs').mockReturnValue(openWithDocsFn); const openPreview = jest.fn(); jest.spyOn(usePreview, 'usePreview').mockReturnValue({ diff --git a/src/carbonio-files-ui-common/views/components/NodeListItem.tsx b/src/carbonio-files-ui-common/views/components/NodeListItem.tsx index 32315b7c..ba118e9e 100644 --- a/src/carbonio-files-ui-common/views/components/NodeListItem.tsx +++ b/src/carbonio-files-ui-common/views/components/NodeListItem.tsx @@ -43,6 +43,7 @@ import { useDeletePermanentlyModal } from '../../hooks/modals/useDeletePermanent import { useMoveModal } from '../../hooks/modals/useMoveModal'; import { useRenameModal } from '../../hooks/modals/useRenameModal'; import { useHealthInfo } from '../../hooks/useHealthInfo'; +import { useOpenWithDocs } from '../../hooks/useOpenWithDocs'; import { usePreview } from '../../hooks/usePreview'; import { useUpload } from '../../hooks/useUpload'; import { Action, NodeListItemType, URLParams } from '../../types/common'; @@ -63,7 +64,6 @@ import { import { getUploadAddType } from '../../utils/uploadUtils'; import { downloadNode, - openNodeWithDocs, isFile, isSearchView, formatDate, @@ -170,6 +170,7 @@ export const NodeListItem = ({ [node.id, node.type] ); const { canUsePreview, canUseDocs } = useHealthInfo(); + const openNodeWithDocs = useOpenWithDocs(); const $isSupportedByPreview = useMemo( () => @@ -237,6 +238,7 @@ export const NodeListItem = ({ $isSupportedByPreview, navigateToFolder, node.id, + openNodeWithDocs, openPreview ]); @@ -364,10 +366,11 @@ export const NodeListItem = ({ [ t, sendViaMailCallback, + openNodeWithDocs, + node, openPreview, createSnackbar, setActiveNode, - node, toggleFlag, openCopyNodesModal, openMoveNodesModal, diff --git a/src/carbonio-files-ui-common/views/components/versioning/VersionRow.tsx b/src/carbonio-files-ui-common/views/components/versioning/VersionRow.tsx index 161098f8..2160ec52 100644 --- a/src/carbonio-files-ui-common/views/components/versioning/VersionRow.tsx +++ b/src/carbonio-files-ui-common/views/components/versioning/VersionRow.tsx @@ -26,7 +26,8 @@ import { DropdownListItemContent } from '../../../design_system_fork/DropdownLis import { CloneVersionType } from '../../../hooks/graphql/mutations/useCloneVersionMutation'; import { DeleteVersionsType } from '../../../hooks/graphql/mutations/useDeleteVersionsMutation'; import { KeepVersionsType } from '../../../hooks/graphql/mutations/useKeepVersionsMutation'; -import { downloadNode, formatDate, humanFileSize, openNodeWithDocs } from '../../../utils/utils'; +import { useOpenWithDocs } from '../../../hooks/useOpenWithDocs'; +import { downloadNode, formatDate, humanFileSize } from '../../../utils/utils'; import { GridItem } from '../StyledComponents'; const CustomText = styled(Text).attrs({ weight: 'light', size: 'small' })` @@ -106,6 +107,7 @@ export const VersionRow: React.VFC<{ const [t] = useTranslation(); const createSnackbar = useSnackbar(); const { locale } = useUserInfo(); + const openNodeWithDocs = useOpenWithDocs(); const deleteVersionCallback = useCallback(() => { deleteVersions(nodeId, [version]); @@ -155,7 +157,7 @@ export const VersionRow: React.VFC<{ const openVersionWithDocsCallback = useCallback(() => { openNodeWithDocs(nodeId, version); - }, [nodeId, version]); + }, [nodeId, openNodeWithDocs, version]); const items = useMemo(() => { const actions: DropdownItem[] = [ diff --git a/src/carbonio-files-ui-common/views/components/versioning/Versioning.test.tsx b/src/carbonio-files-ui-common/views/components/versioning/Versioning.test.tsx index 571263f6..969c85b9 100644 --- a/src/carbonio-files-ui-common/views/components/versioning/Versioning.test.tsx +++ b/src/carbonio-files-ui-common/views/components/versioning/Versioning.test.tsx @@ -13,6 +13,7 @@ import { Versioning } from './Versioning'; import server from '../../../../mocks/server'; import { CONFIGS, ERROR_CODE, REST_ENDPOINT, UPLOAD_VERSION_PATH } from '../../../constants'; import { ACTION_REGEXP, COLORS, ICON_REGEXP, SELECTORS } from '../../../constants/test'; +import * as useOpenWithDocs from '../../../hooks/useOpenWithDocs'; import { UploadRequestBody, UploadVersionRequestParams, @@ -377,7 +378,7 @@ describe('Versioning', () => { test('open with docs version', async () => { const openNodeWithDocsSpy = jest.fn(); - jest.spyOn(moduleUtils, 'openNodeWithDocs').mockImplementation(openNodeWithDocsSpy); + jest.spyOn(useOpenWithDocs, 'useOpenWithDocs').mockReturnValue(openNodeWithDocsSpy); const fileVersion1 = populateFile(); fileVersion1.permissions.can_write_file = true; @@ -937,7 +938,7 @@ describe('Versioning', () => { const versions = [version]; const openNodeWithDocsSpy = jest.fn(); - jest.spyOn(moduleUtils, 'openNodeWithDocs').mockImplementation(openNodeWithDocsSpy); + jest.spyOn(useOpenWithDocs, 'useOpenWithDocs').mockReturnValue(openNodeWithDocsSpy); const mocks = { Query: { From 19d7bc592a69feb5bbf1c3e1b4df8d956314a973 Mon Sep 17 00:00:00 2001 From: Cataldo Mazzilli Date: Thu, 19 Sep 2024 17:04:44 +0200 Subject: [PATCH 2/5] fix: update snackbar and status code --- .../constants/index.ts | 2 +- ...hDocs.test.ts => useOpenWithDocs.test.tsx} | 22 +++++++++++---- ...useOpenWithDocs.ts => useOpenWithDocs.tsx} | 28 +++++++++++++------ 3 files changed, 37 insertions(+), 15 deletions(-) rename src/carbonio-files-ui-common/hooks/{useOpenWithDocs.test.ts => useOpenWithDocs.test.tsx} (84%) rename src/carbonio-files-ui-common/hooks/{useOpenWithDocs.ts => useOpenWithDocs.tsx} (68%) diff --git a/src/carbonio-files-ui-common/constants/index.ts b/src/carbonio-files-ui-common/constants/index.ts index 6d721335..bf1ab295 100644 --- a/src/carbonio-files-ui-common/constants/index.ts +++ b/src/carbonio-files-ui-common/constants/index.ts @@ -134,7 +134,7 @@ export const HTTP_STATUS_CODE = { /** aborted or blocked request */ aborted: 0, overQuota: 422, - docsFileSizeExceeded: 413 + docsFileSizeExceeded: 403 } as const; export const ERROR_CODE = { diff --git a/src/carbonio-files-ui-common/hooks/useOpenWithDocs.test.ts b/src/carbonio-files-ui-common/hooks/useOpenWithDocs.test.tsx similarity index 84% rename from src/carbonio-files-ui-common/hooks/useOpenWithDocs.test.ts rename to src/carbonio-files-ui-common/hooks/useOpenWithDocs.test.tsx index bd8eaa81..5c5cf9dd 100644 --- a/src/carbonio-files-ui-common/hooks/useOpenWithDocs.test.ts +++ b/src/carbonio-files-ui-common/hooks/useOpenWithDocs.test.tsx @@ -3,9 +3,11 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ +import React from 'react'; + import { faker } from '@faker-js/faker'; import { act } from '@testing-library/react-hooks'; -import { CreateSnackbarFn, CreateSnackbarFnArgs } from '@zextras/carbonio-design-system'; +import { CreateSnackbarFn, CreateSnackbarFnArgs, Text } from '@zextras/carbonio-design-system'; import { http, HttpResponse } from 'msw'; import { OpenWithDocsResponse, useOpenWithDocs } from './useOpenWithDocs'; @@ -26,18 +28,18 @@ beforeEach(() => { describe('useOpenWithDocs hook', () => { it('should open the returned url if the document can be opened', async () => { - const url = faker.internet.url(); + const fileOpenUrl = faker.internet.url(); server.use( http.get, never, OpenWithDocsResponse>( `${DOCS_ENDPOINT}${OPEN_FILE_PATH}/:id`, - () => HttpResponse.json({ url }) + () => HttpResponse.json({ fileOpenUrl }) ) ); const spyWindowOpen = jest.spyOn(window, 'open').mockImplementation(); const { result } = setupHook(() => useOpenWithDocs()); await result.current('id'); - expect(spyWindowOpen).toHaveBeenCalledWith(url, url); + expect(spyWindowOpen).toHaveBeenCalledWith(fileOpenUrl, fileOpenUrl); }); it('should show specific snackbar if document cannot be opened due to its size', async () => { @@ -56,8 +58,16 @@ describe('useOpenWithDocs hook', () => { }); expect(spyWindowOpen).not.toHaveBeenCalled(); - const label = - 'The item exceeds the size limit allowed and cannot be opened. To view the item, please download it on your device'; + const label = ( + <> + + {'The item exceeds the size limit allowed and cannot be opened.'} + + + {'To view the item, please download it on your device'} + + + ); expect(mockCreateSnackbar).toHaveBeenCalledWith( expect.objectContaining({ label, diff --git a/src/carbonio-files-ui-common/hooks/useOpenWithDocs.ts b/src/carbonio-files-ui-common/hooks/useOpenWithDocs.tsx similarity index 68% rename from src/carbonio-files-ui-common/hooks/useOpenWithDocs.ts rename to src/carbonio-files-ui-common/hooks/useOpenWithDocs.tsx index 5057dc03..27c4e16a 100644 --- a/src/carbonio-files-ui-common/hooks/useOpenWithDocs.ts +++ b/src/carbonio-files-ui-common/hooks/useOpenWithDocs.tsx @@ -4,14 +4,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { useCallback } from 'react'; +import React, { useCallback } from 'react'; -import { useSnackbar } from '@zextras/carbonio-design-system'; +import { useSnackbar, Text } from '@zextras/carbonio-design-system'; import { useTranslation } from 'react-i18next'; import { DOCS_ENDPOINT, HTTP_STATUS_CODE, OPEN_FILE_PATH } from '../constants'; -export type OpenWithDocsResponse = { url: string }; +export type OpenWithDocsResponse = { fileOpenUrl: string }; const docsTabMap: { [url: string]: Window } = {}; @@ -47,9 +47,21 @@ export const useOpenWithDocs = (): OpenWithDocsFn => { createSnackbar({ key: new Date().toLocaleString(), severity: 'warning', - label: t( - 'snackbar.openWithDocs.error.exceedSizeLimit', - 'The item exceeds the size limit allowed and cannot be opened. To view the item, please download it on your device' + label: ( + <> + + {t( + 'snackbar.openWithDocs.error.exceedSizeLimit', + 'The item exceeds the size limit allowed and cannot be opened.' + )} + + + {t( + 'snackbar.openWithDocs.error.pleaseDownload', + 'To view the item, please download it on your device' + )} + + ), replace: true, actionLabel: t('snackbar.openWithDocs.error.exceedSizeLimit.actionLabel', 'Ok'), @@ -59,8 +71,8 @@ export const useOpenWithDocs = (): OpenWithDocsFn => { throw new Error('Error code not handled'); } } else { - const { url } = (await response.json()) as OpenWithDocsResponse; - openNodeWithDocs(url); + const { fileOpenUrl } = (await response.json()) as OpenWithDocsResponse; + openNodeWithDocs(fileOpenUrl); } } catch (error) { createSnackbar({ From 9ff4704c1f08059f51de0cb20b16dcf0329435b6 Mon Sep 17 00:00:00 2001 From: Cataldo Mazzilli Date: Mon, 30 Sep 2024 12:18:40 +0200 Subject: [PATCH 3/5] feat: track openWithDocs events --- __mocks__/@zextras/carbonio-shell-ui.tsx | 6 +++ package-lock.json | 51 +++++++++---------- package.json | 2 +- .../constants/index.ts | 4 ++ .../hooks/useOpenWithDocs.tsx | 13 ++++- src/hooks/useTracker.ts | 8 +++ 6 files changed, 55 insertions(+), 29 deletions(-) create mode 100644 src/hooks/useTracker.ts diff --git a/__mocks__/@zextras/carbonio-shell-ui.tsx b/__mocks__/@zextras/carbonio-shell-ui.tsx index c8056dce..7ba3d9ed 100644 --- a/__mocks__/@zextras/carbonio-shell-ui.tsx +++ b/__mocks__/@zextras/carbonio-shell-ui.tsx @@ -80,3 +80,9 @@ export const EMAIL_VALIDATION_REGEX = export const registerActions: typeof shell.registerActions = () => undefined; export const removeActions: typeof shell.removeActions = () => undefined; export const Spinner: typeof shell.Spinner = () => <>Spinner component stub; +const noop = (): void => undefined; +export const useTracker: typeof shell.useTracker = () => ({ + capture: noop, + enableTracker: noop, + reset: noop +}); diff --git a/package-lock.json b/package-lock.json index 9b2d609d..1904ddce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@apollo/client": "3.9.10", "@zextras/carbonio-design-system": "^7.1.0", - "@zextras/carbonio-shell-ui": ">=7.0.1-devel.1724145588904 <7.0.1", + "@zextras/carbonio-shell-ui": "8.0.3-devel.1727173773674", "@zextras/carbonio-ui-preview": "^2.0.0", "core-js": "^3.36.1", "graphql": "^16.8.1", @@ -7963,9 +7963,9 @@ "dev": true }, "node_modules/@zextras/carbonio-design-system": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@zextras/carbonio-design-system/-/carbonio-design-system-7.1.0.tgz", - "integrity": "sha512-opMbJXF9TgUnHPzwyJNuLSlMGK76OqfoXPcBh6uY2hQHsWM1l8XqpaN8Bb9PM1dY0VD1aQr/KINgGhauWmhKrQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@zextras/carbonio-design-system/-/carbonio-design-system-7.1.1.tgz", + "integrity": "sha512-VpQ2a/62J6iE3NLOx9KjZ5nSRQjYQd8nfElu0ZIlunLXBMwBXFQt4UWIRrd7v5P/fKcyi2O47h5u15yvXqglHA==", "hasInstallScript": true, "dependencies": { "@floating-ui/dom": "^1.5.3", @@ -7981,22 +7981,22 @@ } }, "node_modules/@zextras/carbonio-shell-ui": { - "version": "7.0.1-devel.1724145588904", - "resolved": "https://registry.npmjs.org/@zextras/carbonio-shell-ui/-/carbonio-shell-ui-7.0.1-devel.1724145588904.tgz", - "integrity": "sha512-tfGBC6+tvBZquUWdt+TKrrwYty/iBQSnzbq7gIz6HEWJKrjg8zX5RjghKti23jWDWTJQ8Cr/QCHcshv+An+VAg==", + "version": "8.0.3-devel.1727173773674", + "resolved": "https://registry.npmjs.org/@zextras/carbonio-shell-ui/-/carbonio-shell-ui-8.0.3-devel.1727173773674.tgz", + "integrity": "sha512-gTYbmnoWxjuDHWyZzb4q1ol/glDbm7E2DPsQ9+/jj3MiGAhcJX69GPyRKHJ3ym2EgUmf8HbZisxjaT8/I9kNog==", "dependencies": { "@fontsource/roboto": "^5.0.8", "@sentry/browser": "^7.103.0", "@tinymce/tinymce-react": "^4.3.2", - "@zextras/carbonio-design-system": "^7.1.0", - "@zextras/carbonio-ui-preview": "^2.0.0", + "@zextras/carbonio-design-system": "^7.1.1", + "@zextras/carbonio-ui-preview": "^2.2.0", "darkreader": "^4.9.79", "history": "^5.3.0", "i18next": "^22.5.1", "i18next-http-backend": "^2.5.0", "immer": "^10.0.3", "lodash": "^4.17.21", - "posthog-js": "^1.145.1", + "posthog-js": "^1.163.1", "react": "^17.0.2", "react-dom": "^17.0.2", "react-i18next": "^12.3.1", @@ -8011,8 +8011,8 @@ "npm": "v10" }, "peerDependencies": { - "@zextras/carbonio-design-system": "^7.1.0", - "@zextras/carbonio-ui-preview": "^2.0.0", + "@zextras/carbonio-design-system": "^7.1.1", + "@zextras/carbonio-ui-preview": "^2.2.0", "core-js": "^3.31.1", "lodash": "^4.17.21", "react": "^17.0.2", @@ -8076,16 +8076,15 @@ } }, "node_modules/@zextras/carbonio-ui-preview": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@zextras/carbonio-ui-preview/-/carbonio-ui-preview-2.0.0.tgz", - "integrity": "sha512-gUz7UckZ5c1w6ZD1hERQzliOGJxWsj/HcVj1C1ZDNNVTzeGFHjRsh3zRBT8ucQcx7XJ9WDJabk6WgP3m5o9xRA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@zextras/carbonio-ui-preview/-/carbonio-ui-preview-2.2.0.tgz", + "integrity": "sha512-UetOICJ8Tm3UP4mQkQZoiRAtS4z+iQb1adJl2TSH3ShdTrYgTafMxljcfAQKXk26J25wrd3rzd0PpBPJ2S6SiQ==", "dependencies": { "core-js": "^3.37.1", - "react-pdf": "^9.0.0" + "react-pdf": "^9.1.0" }, "peerDependencies": { "@zextras/carbonio-design-system": ">=1.0.0", - "lodash": "^4.17.21", "react": "^17.0.2", "react-dom": "^17.0.2" } @@ -21988,9 +21987,9 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/posthog-js": { - "version": "1.154.2", - "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.154.2.tgz", - "integrity": "sha512-EM8xn5V86fTg/pUhCyyMCRotI07tXDLQryc5TDQinT9kCRz8799G1n+Bq7drOEYdD/xbvcKfcfTWmlMrTZCZ6A==", + "version": "1.164.2", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.164.2.tgz", + "integrity": "sha512-WyoVJS0rZHGafyIXuXBihVtxR6ZhrIuN378SuziJWzYOXFheSUi5cjG4Mxu8XRc8/NBXpSAMY/9MZvLF+MzDuA==", "dependencies": { "fflate": "^0.4.8", "preact": "^10.19.3", @@ -21998,9 +21997,9 @@ } }, "node_modules/preact": { - "version": "10.23.1", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.23.1.tgz", - "integrity": "sha512-O5UdRsNh4vdZaTieWe3XOgSpdMAmkIYBCT3VhQDlKrzyCm8lUYsk0fmVEvoQQifoOjFRTaHZO69ylrzTW2BH+A==", + "version": "10.24.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.0.tgz", + "integrity": "sha512-aK8Cf+jkfyuZ0ZZRG9FbYqwmEiGQ4y/PUO4SuTWoyWL244nZZh7bd5h2APd4rSNDYTBNghg1L+5iJN3Skxtbsw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -25933,9 +25932,9 @@ } }, "node_modules/web-vitals": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.2.tgz", - "integrity": "sha512-nYfoOqb4EmElljyXU2qdeE76KsvoHdftQKY4DzA9Aw8DervCg2bG634pHLrJ/d6+B4mE3nWTSJv8Mo7B2mbZkw==" + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.3.tgz", + "integrity": "sha512-/CFAm1mNxSmOj6i0Co+iGFJ58OS4NRGVP+AWS/l509uIK5a1bSoIVaHz/ZumpHTfHSZBpgrJ+wjfpAOrTHok5Q==" }, "node_modules/webcrypto-core": { "version": "1.7.9", diff --git a/package.json b/package.json index 71b0b3c1..15e60484 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "dependencies": { "@apollo/client": "3.9.10", "@zextras/carbonio-design-system": "^7.1.0", - "@zextras/carbonio-shell-ui": ">=7.0.1-devel.1724145588904 <7.0.1", + "@zextras/carbonio-shell-ui": "8.0.3-devel.1727173773674", "@zextras/carbonio-ui-preview": "^2.0.0", "core-js": "^3.36.1", "graphql": "^16.8.1", diff --git a/src/carbonio-files-ui-common/constants/index.ts b/src/carbonio-files-ui-common/constants/index.ts index bf1ab295..c3907476 100644 --- a/src/carbonio-files-ui-common/constants/index.ts +++ b/src/carbonio-files-ui-common/constants/index.ts @@ -183,3 +183,7 @@ export const FILTER_TYPE = { export const FILES_ROUTE = 'files'; export const FILES_APP_ID = 'carbonio-files-ui'; + +export const TRACKER_EVENT = { + openDocumentWithDocs: 'Open document with Docs' +} as const; diff --git a/src/carbonio-files-ui-common/hooks/useOpenWithDocs.tsx b/src/carbonio-files-ui-common/hooks/useOpenWithDocs.tsx index 27c4e16a..5375ca63 100644 --- a/src/carbonio-files-ui-common/hooks/useOpenWithDocs.tsx +++ b/src/carbonio-files-ui-common/hooks/useOpenWithDocs.tsx @@ -9,7 +9,14 @@ import React, { useCallback } from 'react'; import { useSnackbar, Text } from '@zextras/carbonio-design-system'; import { useTranslation } from 'react-i18next'; -import { DOCS_ENDPOINT, HTTP_STATUS_CODE, OPEN_FILE_PATH } from '../constants'; +import { useTracker } from '../../hooks/useTracker'; +import { + DOCS_ENDPOINT, + FILES_APP_ID, + HTTP_STATUS_CODE, + OPEN_FILE_PATH, + TRACKER_EVENT +} from '../constants'; export type OpenWithDocsResponse = { fileOpenUrl: string }; @@ -28,6 +35,7 @@ type OpenWithDocsFn = (id: string, version?: number) => Promise; export const useOpenWithDocs = (): OpenWithDocsFn => { const createSnackbar = useSnackbar(); const [t] = useTranslation(); + const { capture } = useTracker(); return useCallback( async (id, version) => { try { @@ -42,6 +50,7 @@ export const useOpenWithDocs = (): OpenWithDocsFn => { } } ); + capture(TRACKER_EVENT.openDocumentWithDocs, { app: FILES_APP_ID, success: response.ok }); if (!response.ok) { if (response.status === HTTP_STATUS_CODE.docsFileSizeExceeded) { createSnackbar({ @@ -84,6 +93,6 @@ export const useOpenWithDocs = (): OpenWithDocsFn => { }); } }, - [createSnackbar, t] + [capture, createSnackbar, t] ); }; diff --git a/src/hooks/useTracker.ts b/src/hooks/useTracker.ts new file mode 100644 index 00000000..1595fef3 --- /dev/null +++ b/src/hooks/useTracker.ts @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2024 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { useTracker as useShellTracker } from '@zextras/carbonio-shell-ui'; + +export const useTracker: typeof useShellTracker = () => useShellTracker(); From 28642757e977101654f02a9d73f459305d73da47 Mon Sep 17 00:00:00 2001 From: Cataldo Mazzilli Date: Wed, 2 Oct 2024 10:32:09 +0200 Subject: [PATCH 4/5] Apply suggestions from code review Co-authored-by: Beatrice Guerra --- src/carbonio-files-ui-common/hooks/useHeaderActions.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/carbonio-files-ui-common/hooks/useHeaderActions.ts b/src/carbonio-files-ui-common/hooks/useHeaderActions.ts index 775a3570..680951f4 100644 --- a/src/carbonio-files-ui-common/hooks/useHeaderActions.ts +++ b/src/carbonio-files-ui-common/hooks/useHeaderActions.ts @@ -42,7 +42,9 @@ export function useHeaderActions(): (node: File) => Array { actions.unshift({ icon: 'Edit2Outline', id: 'Edit', - onClick: (): Promise => openNodeWithDocs(node.id), + onClick: (): void => { + openNodeWithDocs(node.id); + }, tooltipLabel: t('preview.actions.tooltip.edit', 'Edit') }); } else if (canOpenWithDocs({ nodes: [node], canUseDocs })) { @@ -50,7 +52,9 @@ export function useHeaderActions(): (node: File) => Array { id: 'OpenWithDocs', icon: 'BookOpenOutline', tooltipLabel: t('actions.openWithDocs', 'Open document'), - onClick: (): Promise => openNodeWithDocs(node.id) + onClick: (): void => { + openNodeWithDocs(node.id); + }, }); } return actions; From 2615ebbc6d5bbfba5490e6be69d85d37b9277a9a Mon Sep 17 00:00:00 2001 From: Cataldo Mazzilli Date: Tue, 12 Nov 2024 11:11:06 +0100 Subject: [PATCH 5/5] fix: fix lint errors --- src/carbonio-files-ui-common/hooks/useUpload.ts | 4 ++-- .../components/sharing/publicLink/PublicLinkComponent.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/carbonio-files-ui-common/hooks/useUpload.ts b/src/carbonio-files-ui-common/hooks/useUpload.ts index 6b454e5f..9b748149 100644 --- a/src/carbonio-files-ui-common/hooks/useUpload.ts +++ b/src/carbonio-files-ui-common/hooks/useUpload.ts @@ -9,12 +9,12 @@ import { useCallback, useMemo } from 'react'; import { forEach, map, filter, includes, reduce, noop, partition } from 'lodash'; import { v4 as uuidv4 } from 'uuid'; -import buildClient from '../apollo'; +import { useCreateFolderMutation } from './graphql/mutations/useCreateFolderMutation'; import { useUpdateFolderContent } from './graphql/useUpdateFolderContent'; +import buildClient from '../apollo'; import { nodeSortVar } from '../apollo/nodeSortVar'; import { UploadFunctions, uploadFunctionsVar, uploadVar } from '../apollo/uploadVar'; import { UploadFolderItem } from '../types/common'; -import { useCreateFolderMutation } from './graphql/mutations/useCreateFolderMutation'; import { UploadItem, UploadStatus } from '../types/graphql/client-types'; import { File as FilesFile } from '../types/graphql/types'; import { DeepPick } from '../types/utils'; diff --git a/src/carbonio-files-ui-common/views/components/sharing/publicLink/PublicLinkComponent.tsx b/src/carbonio-files-ui-common/views/components/sharing/publicLink/PublicLinkComponent.tsx index 7a885d79..b4856927 100644 --- a/src/carbonio-files-ui-common/views/components/sharing/publicLink/PublicLinkComponent.tsx +++ b/src/carbonio-files-ui-common/views/components/sharing/publicLink/PublicLinkComponent.tsx @@ -118,7 +118,7 @@ export const PublicLinkComponent = ({ onEditConfirm( id, linkDescriptionValue, - updatedTimestamp !== expiresAt ? (updatedTimestamp ?? 0) : undefined + updatedTimestamp !== expiresAt ? updatedTimestamp ?? 0 : undefined ) ]), [expiresAt, id, linkDescriptionValue, onEditConfirm, updatedTimestamp]