diff --git a/cypress/e2e/item/share/shareItem.cy.ts b/cypress/e2e/item/share/shareItem.cy.ts index ba816beac..40f3ea607 100644 --- a/cypress/e2e/item/share/shareItem.cy.ts +++ b/cypress/e2e/item/share/shareItem.cy.ts @@ -1,5 +1,7 @@ import { Context, ItemLoginSchemaType, ItemTagType } from '@graasp/sdk'; +import shortUUID from 'short-uuid'; + import { buildItemPath } from '@/config/paths'; import { SETTINGS } from '../../../../src/config/constants'; @@ -37,16 +39,17 @@ describe('Share Item', () => { cy.visit(buildItemPath(item.id)); openShareItemTab(item.id); + const { fromUUID } = shortUUID(); // sharing link cy.get(`#${SHARE_ITEM_DIALOG_LINK_ID}`).should( 'contain', - `${buildGraaspPlayerView(item.id)}`, + `${buildGraaspPlayerView(fromUUID(item.id))}`, ); cy.get(`#${SHARE_ITEM_DIALOG_LINK_SELECT_ID}`).click(); cy.get(`li[data-value="${Context.Builder}"]`).click(); cy.get(`#${SHARE_ITEM_DIALOG_LINK_ID}`).should( 'have.text', - `${buildGraaspBuilderView(item.id)}`, + `${buildGraaspBuilderView(fromUUID(item.id))}`, ); const visiblitySelect = cy.get( diff --git a/package.json b/package.json index 8fdbad9bb..54d2a3271 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "react-router": "6.15.0", "react-router-dom": "6.15.0", "react-toastify": "9.1.3", + "short-uuid": "4.2.2", "stylis": "4.3.0", "stylis-plugin-rtl": "2.1.1", "uuid": "9.0.0", diff --git a/src/components/item/header/ItemHeader.tsx b/src/components/item/header/ItemHeader.tsx index 6185407c5..a21085ae3 100644 --- a/src/components/item/header/ItemHeader.tsx +++ b/src/components/item/header/ItemHeader.tsx @@ -1,10 +1,9 @@ -import { useMatch } from 'react-router'; - import Stack from '@mui/material/Stack'; -import { Loader } from '@graasp/ui'; +import { Loader, useShortenURLParams } from '@graasp/ui'; + +import { ITEM_ID_PARAMS } from '@/config/paths'; -import { buildItemPath } from '../../../config/paths'; import { hooks } from '../../../config/queryClient'; import { ITEM_HEADER_ID } from '../../../config/selectors'; import ErrorAlert from '../../common/ErrorAlert'; @@ -18,8 +17,7 @@ type Props = { }; const ItemHeader = ({ showNavigation = true }: Props): JSX.Element => { - const match = useMatch(buildItemPath()); - const itemId = match?.params?.itemId; + const itemId = useShortenURLParams(ITEM_ID_PARAMS); const { data: item, isLoading: isItemLoading, isError } = useItem(itemId); if (isItemLoading) { diff --git a/src/components/item/sharing/SharingLink.tsx b/src/components/item/sharing/SharingLink.tsx index 82e303b1a..1a168d60e 100644 --- a/src/components/item/sharing/SharingLink.tsx +++ b/src/components/item/sharing/SharingLink.tsx @@ -11,6 +11,8 @@ import Tooltip from '@mui/material/Tooltip'; import { Context } from '@graasp/sdk'; import { FAILURE_MESSAGES, SUCCESS_MESSAGES } from '@graasp/translations'; +import shortUUID from 'short-uuid'; + import { SHARE_LINK_COLOR, SHARE_LINK_CONTAINER_BORDER_STYLE, @@ -57,6 +59,7 @@ type Props = { itemId?: string; }; +const { fromUUID } = shortUUID(); const SharingLink = ({ itemId }: Props): JSX.Element => { const { t: translateMessages } = useMessagesTranslation(); const { t: translateBuilder } = useBuilderTranslation(); @@ -69,11 +72,11 @@ const SharingLink = ({ itemId }: Props): JSX.Element => { if (itemId) { switch (linkType) { case Context.Builder: { - setLink(buildGraaspBuilderView(itemId)); + setLink(buildGraaspBuilderView(fromUUID(itemId || ''))); break; } case Context.Player: { - setLink(buildGraaspPlayerView(itemId)); + setLink(buildGraaspPlayerView(fromUUID(itemId || ''))); break; } default: diff --git a/src/components/layout/Navigation.tsx b/src/components/layout/Navigation.tsx index 1bbcc1f6e..6b30d1914 100644 --- a/src/components/layout/Navigation.tsx +++ b/src/components/layout/Navigation.tsx @@ -1,11 +1,17 @@ -import { useLocation, useMatch } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; -import { HomeMenu, ItemMenu, Navigation } from '@graasp/ui'; +import { + HomeMenu, + ItemMenu, + Navigation, + useShortenURLParams, +} from '@graasp/ui'; import { useBuilderTranslation } from '../../config/i18n'; import { FAVORITE_ITEMS_PATH, HOME_PATH, + ITEM_ID_PARAMS, SHARED_ITEMS_PATH, buildItemPath, } from '../../config/paths'; @@ -27,9 +33,8 @@ const { const Navigator = (): JSX.Element | null => { const { t: translateBuilder } = useBuilderTranslation(); - const match = useMatch(buildItemPath()); + const itemId = useShortenURLParams(ITEM_ID_PARAMS); const { pathname } = useLocation(); - const itemId = match?.params?.itemId; const { data: currentMember } = useCurrentMember(); const { data: item, isLoading: isItemLoading } = useItem(itemId); const itemPath = item?.path; diff --git a/src/components/main/ImportH5P.jsx b/src/components/main/ImportH5P.jsx index f9f055fbf..3b8688397 100644 --- a/src/components/main/ImportH5P.jsx +++ b/src/components/main/ImportH5P.jsx @@ -1,25 +1,25 @@ import { useEffect, useState } from 'react'; -import { useMatch } from 'react-router'; import Typography from '@mui/material/Typography'; import { routines } from '@graasp/query-client'; import { MAX_ZIP_FILE_SIZE } from '@graasp/sdk'; +import { useShortenURLParams } from '@graasp/ui'; import '@uppy/dashboard/dist/style.css'; import { Dashboard } from '@uppy/react'; import { useBuilderTranslation } from '../../config/i18n'; import notifier from '../../config/notifier'; -import { buildItemPath } from '../../config/paths'; +import { ITEM_ID_PARAMS } from '../../config/paths'; import { H5P_DASHBOARD_UPLOADER_ID } from '../../config/selectors'; import { BUILDER } from '../../langs/constants'; import { configureH5PImportUppy, humanFileSize } from '../../utils/uppy'; const ImportH5P = () => { const [uppy, setUppy] = useState(null); - const match = useMatch(buildItemPath()); - const itemId = match?.params?.itemId; + const itemId = useShortenURLParams(ITEM_ID_PARAMS); + const { t: translateBuilder } = useBuilderTranslation(); const onComplete = (result) => { diff --git a/src/components/main/ImportZip.jsx b/src/components/main/ImportZip.jsx index a22bcaf21..24169f8a6 100644 --- a/src/components/main/ImportZip.jsx +++ b/src/components/main/ImportZip.jsx @@ -1,25 +1,26 @@ import { useEffect, useState } from 'react'; -import { useMatch } from 'react-router'; import Typography from '@mui/material/Typography'; import { routines } from '@graasp/query-client'; import { MAX_ZIP_FILE_SIZE } from '@graasp/sdk'; +import { useShortenURLParams } from '@graasp/ui'; import '@uppy/dashboard/dist/style.css'; import { Dashboard } from '@uppy/react'; import { useBuilderTranslation } from '../../config/i18n'; import notifier from '../../config/notifier'; -import { buildItemPath } from '../../config/paths'; +import { ITEM_ID_PARAMS } from '../../config/paths'; import { ZIP_DASHBOARD_UPLOADER_ID } from '../../config/selectors'; import { BUILDER } from '../../langs/constants'; import { configureZipImportUppy, humanFileSize } from '../../utils/uppy'; const ImportZip = () => { const [uppy, setUppy] = useState(null); - const match = useMatch(buildItemPath()); - const itemId = match?.params?.itemId; + + const itemId = useShortenURLParams(ITEM_ID_PARAMS); + const { t: translateBuilder } = useBuilderTranslation(); const onComplete = (result) => { diff --git a/src/components/main/ItemScreen.tsx b/src/components/main/ItemScreen.tsx index a4957d306..7d4a80594 100644 --- a/src/components/main/ItemScreen.tsx +++ b/src/components/main/ItemScreen.tsx @@ -1,8 +1,13 @@ import { useEffect } from 'react'; -import { useParams } from 'react-router'; import { ItemType } from '@graasp/sdk'; -import { ItemLoginAuthorization, Loader } from '@graasp/ui'; +import { + ItemLoginAuthorization, + Loader, + useShortenURLParams, +} from '@graasp/ui'; + +import { ITEM_ID_PARAMS } from '@/config/paths'; import { PERMISSIONS_EDITION_ALLOWED } from '../../config/constants'; import { hooks, mutations } from '../../config/queryClient'; @@ -36,9 +41,9 @@ const { } = hooks; const ItemScreen = (): JSX.Element => { - const { itemId } = useParams(); - const { data: item, isError, isLoading } = useItem(itemId); + const itemId = useShortenURLParams(ITEM_ID_PARAMS); + const { data: item, isError, isLoading } = useItem(itemId); const { setEditingItemId, openedActionTabId, setOpenedActionTabId } = useLayoutContext(); const { data: currentMember } = useCurrentUserContext(); @@ -101,7 +106,7 @@ const ItemScreen = (): JSX.Element => { const WrappedItemScreen = (): JSX.Element => { const { mutate: signOut } = mutations.useSignOut(); const { mutate: itemLoginSignIn } = mutations.usePostItemLogin(); - const { itemId } = useParams(); + const itemId = useShortenURLParams(ITEM_ID_PARAMS); const ForbiddenContent = ; diff --git a/src/components/main/ItemsTable.tsx b/src/components/main/ItemsTable.tsx index 6b386798d..672a6e1a0 100644 --- a/src/components/main/ItemsTable.tsx +++ b/src/components/main/ItemsTable.tsx @@ -1,5 +1,5 @@ import { useCallback, useMemo } from 'react'; -import { useNavigate, useParams } from 'react-router'; +import { useNavigate } from 'react-router'; import { DiscriminatedItem, @@ -11,6 +11,7 @@ import { } from '@graasp/sdk'; import { ItemRecord, ResultOfRecord } from '@graasp/sdk/frontend'; import { COMMON } from '@graasp/translations'; +import { useShortenURLParams } from '@graasp/ui'; import { Table as GraaspTable } from '@graasp/ui/dist/table'; import { CellClickedEvent, ColDef, IRowDragItem } from 'ag-grid-community'; @@ -77,7 +78,9 @@ const ItemsTable = ({ const { t: translateCommon } = useCommonTranslation(); const { t: translateEnums } = useEnumsTranslation(); const navigate = useNavigate(); - const { itemId } = useParams(); + + const itemId = useShortenURLParams('itemId'); + const { data: parentItem } = useItem(itemId); const { data: member } = useCurrentUserContext(); diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index 046fb210f..9bc9e4d73 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -1,4 +1,4 @@ -import { Link, useParams } from 'react-router-dom'; +import { Link } from 'react-router-dom'; import { Grid, Typography, styled } from '@mui/material'; import Box from '@mui/material/Box'; @@ -11,12 +11,13 @@ import { PlatformSwitch, defaultHostsMapper, usePlatformNavigation, + useShortenURLParams, } from '@graasp/ui'; import { HOST_MAP } from '@/config/externalPaths'; import { APP_NAME, GRAASP_LOGO_HEADER_HEIGHT } from '../../config/constants'; -import { HOME_PATH } from '../../config/paths'; +import { HOME_PATH, ITEM_ID_PARAMS } from '../../config/paths'; import { APP_NAVIGATION_PLATFORM_SWITCH_BUTTON_IDS, APP_NAVIGATION_PLATFORM_SWITCH_ID, @@ -46,7 +47,8 @@ export const platformsHostsMap = defaultHostsMapper({ const Main = ({ children }: Props): JSX.Element => { const { isMainMenuOpen, setIsMainMenuOpen } = useLayoutContext(); - const { itemId } = useParams(); + const itemId = useShortenURLParams(ITEM_ID_PARAMS); + const getNavigationEvents = usePlatformNavigation(platformsHostsMap, itemId); const platformProps = { [Platform.Builder]: { diff --git a/src/components/main/NewItemModal.tsx b/src/components/main/NewItemModal.tsx index 605ce7c64..b24f57670 100644 --- a/src/components/main/NewItemModal.tsx +++ b/src/components/main/NewItemModal.tsx @@ -1,5 +1,4 @@ import { useState } from 'react'; -import { useMatch } from 'react-router'; import { Box, styled } from '@mui/material'; import Dialog from '@mui/material/Dialog'; @@ -16,11 +15,11 @@ import { ItemType, } from '@graasp/sdk'; import { COMMON } from '@graasp/translations'; -import { Button } from '@graasp/ui'; +import { Button, useShortenURLParams } from '@graasp/ui'; import { DOUBLE_CLICK_DELAY_MS } from '../../config/constants'; import { useBuilderTranslation, useCommonTranslation } from '../../config/i18n'; -import { buildItemPath } from '../../config/paths'; +import { ITEM_ID_PARAMS } from '../../config/paths'; import { mutations } from '../../config/queryClient'; import { CREATE_ITEM_CLOSE_BUTTON_ID, @@ -83,9 +82,7 @@ const NewItemModal = ({ open, handleClose }: Props): JSX.Element => { const { mutate: postItem } = mutations.usePostItem(); const { mutate: postEtherpad } = mutations.usePostEtherpad(); - - const match = useMatch(buildItemPath()); - const parentId = match?.params?.itemId; + const parentId = useShortenURLParams(ITEM_ID_PARAMS); const submitAndDisableConfirmButtonFor = ( submitFn: () => void | boolean, diff --git a/src/config/paths.ts b/src/config/paths.ts index 15fdf9f60..01b034b86 100644 --- a/src/config/paths.ts +++ b/src/config/paths.ts @@ -8,6 +8,6 @@ export const buildItemPath = (id = ':itemId'): string => `${ITEMS_PATH}/${id}`; export const REDIRECT_PATH = '/redirect'; export const MEMBER_PROFILE_PATH = '/profile'; export const RECYCLE_BIN_PATH = '/recycle-bin'; - export const buildItemSettingsPath = (id = ':itemId'): string => `${ITEMS_PATH}/${id}/settings`; +export const ITEM_ID_PARAMS = 'itemId'; diff --git a/yarn.lock b/yarn.lock index 1a037b16f..eb42ca3dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3480,9 +3480,9 @@ __metadata: linkType: hard "@types/node@npm:^18.17.5": - version: 18.18.0 - resolution: "@types/node@npm:18.18.0" - checksum: 61bcffa28eb713e7a4c66fd369df603369c3f834a783faeced95fe3e78903faa25f1a704d49e054f41d71b7915eeb066d10a37cc699421fcf5dd267f96ad5808 + version: 18.18.1 + resolution: "@types/node@npm:18.18.1" + checksum: 079085afc8615b91727900628410a2077f7fff51c5c3e20c4ab2d9caae4010b035aac13dae1221155e9d6ca46084aebf68121642140527b4009eee17b716d339 languageName: node linkType: hard @@ -7507,6 +7507,7 @@ __metadata: react-router-dom: 6.15.0 react-toastify: 9.1.3 rollup-plugin-visualizer: 5.9.2 + short-uuid: 4.2.2 stylis: 4.3.0 stylis-plugin-rtl: 2.1.1 typescript: 5.2.2