From 2408533891bf6a9a8f4bb90d4ed57eec810f1869 Mon Sep 17 00:00:00 2001 From: Basile Spaenlehauer Date: Thu, 28 Nov 2024 12:54:41 +0100 Subject: [PATCH] fix: code smells (#497) * fix: code smells * fix: revert change to use isValid from react-hook-form --- src/components/layout/BorderedSection.tsx | 2 +- src/components/layout/CenteredContainer.tsx | 39 +-- src/config/selectors.ts | 1 - .../requestPasswordReset/ResetPassword.tsx | 245 ------------------ src/modules/player/Chatbox.tsx | 2 +- src/modules/player/ItemNavigation.tsx | 2 +- src/modules/player/config/selectors.ts | 29 --- .../player/errors/FallbackComponent.tsx | 6 +- .../player/errors/NetworkErrorAlert.tsx | 5 +- .../player/item/FromShortcutButton.tsx | 6 +- src/modules/player/item/Item.tsx | 60 +---- src/modules/player/item/SectionHeader.tsx | 5 +- src/modules/player/item/useCollapseAction.tsx | 17 ++ .../navigationIsland/NavigationIsland.tsx | 2 +- .../navigationIsland/PinnedItemsButton.tsx | 2 +- .../navigationIsland/PreviousNextButtons.tsx | 75 +++--- src/modules/player/rightPanel/SideContent.tsx | 7 +- src/modules/player/tree/LoadingTree.tsx | 21 +- src/modules/player/tree/Node.tsx | 2 +- src/modules/player/tree/TreeView.tsx | 4 +- src/modules/player/ui/CardThumbnail.tsx | 2 +- src/modules/player/utils/shuffle.ts | 2 +- src/routes/account/storage.tsx | 9 +- src/routes/auth/reset-password.tsx | 4 +- src/routes/email.change.tsx | 4 +- src/routes/player/$rootId/$itemId.tsx | 3 +- 26 files changed, 123 insertions(+), 433 deletions(-) delete mode 100644 src/modules/auth/components/requestPasswordReset/ResetPassword.tsx delete mode 100644 src/modules/player/config/selectors.ts create mode 100644 src/modules/player/item/useCollapseAction.tsx diff --git a/src/components/layout/BorderedSection.tsx b/src/components/layout/BorderedSection.tsx index 357447564..8e6ef8968 100644 --- a/src/components/layout/BorderedSection.tsx +++ b/src/components/layout/BorderedSection.tsx @@ -15,7 +15,7 @@ export function BorderedSection({ title, topActions, children, -}: BorderedSectionProps): JSX.Element { +}: Readonly): JSX.Element { return ( diff --git a/src/components/layout/CenteredContainer.tsx b/src/components/layout/CenteredContainer.tsx index c2ea78730..7c3363402 100644 --- a/src/components/layout/CenteredContainer.tsx +++ b/src/components/layout/CenteredContainer.tsx @@ -4,24 +4,25 @@ import { Container, Stack } from '@mui/material'; import { DEFAULT_BACKGROUND_COLOR } from '@graasp/ui'; -const CenteredContainer = ({ +export function CenteredContainer({ children, -}: { +}: Readonly<{ children: ReactNode; -}): JSX.Element => ( - - - - {children} - - - -); -export default CenteredContainer; +}>): JSX.Element { + return ( + + + + {children} + + + + ); +} diff --git a/src/config/selectors.ts b/src/config/selectors.ts index ac6bf5d93..65e250ee7 100644 --- a/src/config/selectors.ts +++ b/src/config/selectors.ts @@ -132,7 +132,6 @@ export const USER_SWITCH_ID = 'userSwitch'; export const SUCCESS_CONTENT_ID = 'successContent'; export const BACK_BUTTON_ID = 'backButtonId'; export const RESEND_EMAIL_BUTTON_ID = 'resendEmailButton'; -export const buildMemberMenuItemId = (id: string) => `memberMenuItemId-${id}`; export const PASSWORD_SUCCESS_ALERT = 'passwordSuccessAlert'; export const PLATFORM_ADVERTISEMENT_CONTAINER_ID = diff --git a/src/modules/auth/components/requestPasswordReset/ResetPassword.tsx b/src/modules/auth/components/requestPasswordReset/ResetPassword.tsx deleted file mode 100644 index 51e663caa..000000000 --- a/src/modules/auth/components/requestPasswordReset/ResetPassword.tsx +++ /dev/null @@ -1,245 +0,0 @@ -import { useState } from 'react'; -import { useForm } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; - -import { LoadingButton } from '@mui/lab'; -import { - Alert, - Checkbox, - FormControlLabel, - Stack, - TextField, -} from '@mui/material'; -import Typography from '@mui/material/Typography'; - -import { isPasswordStrong } from '@graasp/sdk'; - -import { useSearch } from '@tanstack/react-router'; - -import { ButtonLink } from '@/components/ui/ButtonLink'; -import { TypographyLink } from '@/components/ui/TypographyLink'; -import { NS } from '@/config/constants'; -import { mutations } from '@/config/queryClient'; -import { - RESET_PASSWORD_BACK_TO_LOGIN_BUTTON_ID, - RESET_PASSWORD_ERROR_MESSAGE_ID, - RESET_PASSWORD_NEW_PASSWORD_CONFIRMATION_FIELD_ERROR_TEXT_ID, - RESET_PASSWORD_NEW_PASSWORD_CONFIRMATION_FIELD_ID, - RESET_PASSWORD_NEW_PASSWORD_FIELD_ERROR_TEXT_ID, - RESET_PASSWORD_NEW_PASSWORD_FIELD_ID, - RESET_PASSWORD_SUBMIT_BUTTON_ID, - RESET_PASSWORD_SUCCESS_MESSAGE_ID, -} from '@/config/selectors'; - -import { HELP_EMAIL } from '~auth/constants'; -import { useValidateJWTToken } from '~auth/hooks/useValidateJWTToken'; -import { AUTH } from '~auth/langs'; - -import { PasswordAdornment } from '../common/adornments'; -import { CenteredContent } from '../layout/CenteredContent'; -import { DialogHeader } from '../layout/DialogHeader'; -import { InvalidTokenScreen } from './InvalidTokenScreen'; - -const { useResolvePasswordResetRequest } = mutations; - -type Inputs = { - password: string; - confirmPassword: string; -}; -export function ResetPassword() { - const { t } = useTranslation(NS.Auth); - - const search = useSearch({ from: '/auth/reset-password' }); - const { isValid, token } = useValidateJWTToken(search.t); - - const [showPasswords, setShowPasswords] = useState(false); - - const { - register, - handleSubmit, - formState: { errors }, - } = useForm(); - - const { - mutate: resolveRequestPasswordReset, - isPending: isLoading, - isError, - isSuccess, - } = useResolvePasswordResetRequest(); - - if (!isValid) { - return ; - } - - const resetPassword = ({ password }: Inputs) => { - resolveRequestPasswordReset({ password, token }); - }; - - const passwordErrorMessage = errors.password?.message; - const confirmPasswordErrorMessage = errors.confirmPassword?.message; - const hasErrors = Boolean( - passwordErrorMessage || confirmPasswordErrorMessage, - ); - - return ( - - {t(AUTH.RESET_PASSWORD_DESCRIPTION)} - - {t(AUTH.RESET_PASSWORD_REQUIREMENTS_TITLE)} -
    -
  • - {t(AUTH.RESET_PASSWORD_REQUIREMENTS_LENGTH, { length: 8 })} -
  • -
  • - {t(AUTH.RESET_PASSWORD_REQUIREMENTS_LOWERCASE, { - count: 1, - })} -
  • -
  • - {t(AUTH.RESET_PASSWORD_REQUIREMENTS_UPPERCASE, { - count: 1, - })} -
  • -
  • - {t(AUTH.RESET_PASSWORD_REQUIREMENTS_NUMBER, { - count: 1, - })} -
  • -
-
-
- } - /> - } - > - - - isPasswordStrong(value) || t(AUTH.PASSWORD_WEAK_ERROR), - })} - label={t(AUTH.RESET_PASSWORD_NEW_PASSWORD_FIELD_LABEL)} - variant="outlined" - error={Boolean(passwordErrorMessage)} - helperText={passwordErrorMessage} - type={showPasswords ? '' : 'password'} - fullWidth - disabled={isSuccess || isError} - /> - - isPasswordStrong(value) || t(AUTH.PASSWORD_WEAK_ERROR), - match: (confirmPassword, formState) => - confirmPassword === formState.password || - t(AUTH.PASSWORD_DO_NOT_MATCH_ERROR), - }, - })} - label={t(AUTH.RESET_PASSWORD_NEW_PASSWORD_CONFIRMATION_FIELD_LABEL)} - variant="outlined" - error={Boolean(confirmPasswordErrorMessage)} - helperText={confirmPasswordErrorMessage} - type={showPasswords ? '' : 'password'} - fullWidth - disabled={isSuccess || isError} - /> - setShowPasswords((v) => !v)} - /> - } - label={t(AUTH.SHOW_PASSWORD)} - /> - {isError && ( - <> - - {t(AUTH.RESET_PASSWORD_ERROR_MESSAGE, { email: HELP_EMAIL })} - - - {t(AUTH.REQUEST_PASSWORD_RESET_TITLE)} - - - )} - {isSuccess ? ( - <> - - {t(AUTH.RESET_PASSWORD_SUCCESS_MESSAGE)} - - - {t(AUTH.BACK_TO_SIGN_IN_BUTTON)} - - - ) : ( - - {t(AUTH.RESET_PASSWORD_BUTTON)} - - )} - - { - // only show this when `isSuccess` is false - !isSuccess && ( - - {t(AUTH.BACK_TO_SIGN_IN_BUTTON)} - - ) - } - - ); -} diff --git a/src/modules/player/Chatbox.tsx b/src/modules/player/Chatbox.tsx index 081f277d5..eaec7de0c 100644 --- a/src/modules/player/Chatbox.tsx +++ b/src/modules/player/Chatbox.tsx @@ -17,7 +17,7 @@ const { type Props = { item: DiscriminatedItem; }; -// todo: add chatbox in the project + const Chatbox = ({ item }: Props): JSX.Element => { const { data: messages, isLoading: isChatLoading } = useItemChat(item.id); const { data: itemPermissions, isLoading: isLoadingItemPermissions } = diff --git a/src/modules/player/ItemNavigation.tsx b/src/modules/player/ItemNavigation.tsx index 8ae93dab4..247bdafb4 100644 --- a/src/modules/player/ItemNavigation.tsx +++ b/src/modules/player/ItemNavigation.tsx @@ -12,7 +12,7 @@ import { NS } from '@/config/constants.ts'; import { axios, hooks } from '@/config/queryClient'; import { MAIN_MENU_ID, TREE_VIEW_ID } from '@/config/selectors'; -import LoadingTree from './tree/LoadingTree.tsx'; +import { LoadingTree } from './tree/LoadingTree.tsx'; import { TreeView } from './tree/TreeView.tsx'; import { combineUuids, shuffleAllButLastItemInArray } from './utils/shuffle.ts'; diff --git a/src/modules/player/config/selectors.ts b/src/modules/player/config/selectors.ts deleted file mode 100644 index be744817b..000000000 --- a/src/modules/player/config/selectors.ts +++ /dev/null @@ -1,29 +0,0 @@ -export const SHOW_MORE_ITEMS_ID = 'showMoreItems'; -export const HOME_NAVIGATION_STACK_ID = 'homeNavigation'; -export const MY_ITEMS_ID = 'myItems'; - -export const HIDDEN_WRAPPER_ID_CY = 'hiddenWrapper'; -export const buildHiddenWrapperId = (id: string, isHidden: boolean): string => - `${HIDDEN_WRAPPER_ID_CY}-${id}-${isHidden ? 'grayed' : 'visible'}`; - -export const BUILDER_EDIT_BUTTON_ID = 'builderEditButton'; - -export const buildTreeShortcutItemClass = (id: string): string => - `buildTreeShortcutItem-${id}`; - -export const buildDataCyWrapper = (dataCy: string): string => - `[data-cy="${dataCy}"]`; - -export const HEADER_MEMBER_MENU_BUTTON_ID = 'headerMemberMenuButton'; -export const HEADER_MEMBER_MENU_SEE_PROFILE_BUTTON_ID = - 'headerMemberMenuSeeProfileButton'; -export const HEADER_MEMBER_MENU_SIGN_IN_BUTTON_ID = - 'headerMemberMenuSignInButton'; - -export const HEADER_MEMBER_MENU_SIGN_OUT_BUTTON_ID = - 'headerMemberMenuSignOutButton'; -export const buildMemberMenuItemId = (id: string): string => - `memberMenuItem-${id}`; -export const OWN_ITEMS_GRID_ID = 'ownItemsGrid'; -export const buildMemberAvatarId = (id?: string): string => - `memberAvatar-${id}`; diff --git a/src/modules/player/errors/FallbackComponent.tsx b/src/modules/player/errors/FallbackComponent.tsx index dcebc09e8..5892d21a1 100644 --- a/src/modules/player/errors/FallbackComponent.tsx +++ b/src/modules/player/errors/FallbackComponent.tsx @@ -6,7 +6,7 @@ import { Box, Stack, Typography } from '@mui/material'; import { ButtonLink } from '@/components/ui/ButtonLink'; import { NS } from '@/config/constants'; -const FallbackComponent = (): JSX.Element => { +export function FallbackComponent(): JSX.Element { const { t: translateBuilder } = useTranslation(NS.Player); return ( @@ -41,6 +41,4 @@ const FallbackComponent = (): JSX.Element => { /> ); -}; - -export default FallbackComponent; +} diff --git a/src/modules/player/errors/NetworkErrorAlert.tsx b/src/modules/player/errors/NetworkErrorAlert.tsx index c4fc56a98..4789f5b34 100644 --- a/src/modules/player/errors/NetworkErrorAlert.tsx +++ b/src/modules/player/errors/NetworkErrorAlert.tsx @@ -1,6 +1,5 @@ import { useTranslation } from 'react-i18next'; -import Refresh from '@mui/icons-material/Refresh'; import { Alert, AlertTitle, @@ -10,6 +9,8 @@ import { Typography, } from '@mui/material'; +import { RefreshCwIcon } from 'lucide-react'; + import { NS } from '@/config/constants'; export function NetworkErrorAlert(): JSX.Element { @@ -29,7 +30,7 @@ export function NetworkErrorAlert(): JSX.Element { window.location.reload()}> - + diff --git a/src/modules/player/item/FromShortcutButton.tsx b/src/modules/player/item/FromShortcutButton.tsx index 57d118f0e..40a84e242 100644 --- a/src/modules/player/item/FromShortcutButton.tsx +++ b/src/modules/player/item/FromShortcutButton.tsx @@ -11,7 +11,7 @@ import { BACK_TO_SHORTCUT_ID } from '@/config/selectors'; export const ID_FORMAT = '(?=.*[0-9])(?=.*[a-zA-Z])([a-z0-9-]+)'; -const FromShortcutButton = (): JSX.Element | null => { +export function FromShortcutButton(): JSX.Element | null { const search = useSearch({ from: '/player/$rootId/$itemId/' }); const { t } = useTranslation(NS.Player); const { from: fromUrl, fromName } = search; @@ -44,6 +44,4 @@ const FromShortcutButton = (): JSX.Element | null => { } return null; -}; - -export default FromShortcutButton; +} diff --git a/src/modules/player/item/Item.tsx b/src/modules/player/item/Item.tsx index ee1666dca..a025b9b1b 100644 --- a/src/modules/player/item/Item.tsx +++ b/src/modules/player/item/Item.tsx @@ -47,7 +47,7 @@ import { getRouteApi } from '@tanstack/react-router'; import { useAuth } from '@/AuthContext'; import { NS } from '@/config/constants'; import { API_HOST, GRAASP_ASSETS_URL, H5P_INTEGRATION_URL } from '@/config/env'; -import { axios, hooks, mutations } from '@/config/queryClient'; +import { axios, hooks } from '@/config/queryClient'; import { buildAppId, buildCollapsibleId, @@ -60,8 +60,9 @@ import { import NavigationIsland from '~player/navigationIsland/NavigationIsland'; import { FolderCard } from '~player/ui/FolderCard'; -import FromShortcutButton from './FromShortcutButton'; -import SectionHeader from './SectionHeader'; +import { FromShortcutButton } from './FromShortcutButton'; +import { SectionHeader } from './SectionHeader'; +import { useCollapseAction } from './useCollapseAction'; import usePageTitle from './usePageTitle'; const paginationContentFilter = (items: PackedItem[]): PackedItem[] => @@ -134,16 +135,7 @@ const FileContent = ({ item }: FileContentProps) => { isLoading: isFileContentLoading, isError: isFileError, } = useFileContentUrl(item.id); - const { mutate: triggerAction } = mutations.usePostItemAction(); - - const onCollapse = (c: boolean) => { - triggerAction({ - itemId: item.id, - payload: { - type: c ? ActionTriggers.CollapseItem : ActionTriggers.UnCollapseItem, - }, - }); - }; + const { triggerAction, onCollapse } = useCollapseAction(item.id); const onDownloadClick = useCallback(() => { triggerAction({ @@ -183,8 +175,8 @@ const FileContent = ({ item }: FileContentProps) => { const LinkContent = ({ item }: { item: LinkItemType }): JSX.Element => { const { user } = useAuth(); + const { triggerAction, onCollapse } = useCollapseAction(item.id); - const { mutate: triggerAction } = mutations.usePostItemAction(); const handleLinkClick = () => { // trigger player Action for link click triggerAction({ @@ -193,15 +185,6 @@ const LinkContent = ({ item }: { item: LinkItemType }): JSX.Element => { }); }; - const onCollapse = (c: boolean) => { - triggerAction({ - itemId: item.id, - payload: { - type: c ? ActionTriggers.CollapseItem : ActionTriggers.UnCollapseItem, - }, - }); - }; - return ( { }; const DocumentContent = ({ item }: { item: DocumentItemType }): JSX.Element => { - const { mutate: triggerAction } = mutations.usePostItemAction(); + const { onCollapse } = useCollapseAction(item.id); - const onCollapse = (c: boolean) => { - triggerAction({ - itemId: item.id, - payload: { - type: c ? ActionTriggers.CollapseItem : ActionTriggers.UnCollapseItem, - }, - }); - }; return ( { const AppContent = ({ item }: { item: AppItemType }): JSX.Element => { const { user } = useAuth(); - const { mutate: triggerAction } = mutations.usePostItemAction(); - - const onCollapse = (c: boolean) => { - triggerAction({ - itemId: item.id, - payload: { - type: c ? ActionTriggers.CollapseItem : ActionTriggers.UnCollapseItem, - }, - }); - }; + const { onCollapse } = useCollapseAction(item.id); return ( <> @@ -290,20 +256,12 @@ const AppContent = ({ item }: { item: AppItemType }): JSX.Element => { const H5PContent = ({ item }: { item: H5PItemType }): JSX.Element => { const { t } = useTranslation(NS.Common); - const { mutate: triggerAction } = mutations.usePostItemAction(); + const { onCollapse } = useCollapseAction(item.id); const contentId = item?.extra?.h5p?.contentId; if (!contentId) { return {t('ERRORS.UNEXPECTED')}; } - const onCollapse = (c: boolean) => { - triggerAction({ - itemId: item.id, - payload: { - type: c ? ActionTriggers.CollapseItem : ActionTriggers.UnCollapseItem, - }, - }); - }; return ( { +export function SectionHeader({ item }: SectionHeaderProps): JSX.Element { const { t, i18n } = useTranslation(NS.Player); const thumbnailSrc = item.thumbnails?.medium; @@ -42,5 +42,4 @@ const SectionHeader = ({ item }: SectionHeaderProps): JSX.Element => { ); -}; -export default SectionHeader; +} diff --git a/src/modules/player/item/useCollapseAction.tsx b/src/modules/player/item/useCollapseAction.tsx new file mode 100644 index 000000000..27f69a1b6 --- /dev/null +++ b/src/modules/player/item/useCollapseAction.tsx @@ -0,0 +1,17 @@ +import { ActionTriggers } from '@graasp/sdk'; + +import { mutations } from '@/config/queryClient'; + +export function useCollapseAction(itemId: string) { + const { mutate: triggerAction } = mutations.usePostItemAction(); + + const onCollapse = (c: boolean) => { + triggerAction({ + itemId, + payload: { + type: c ? ActionTriggers.CollapseItem : ActionTriggers.UnCollapseItem, + }, + }); + }; + return { triggerAction, onCollapse }; +} diff --git a/src/modules/player/navigationIsland/NavigationIsland.tsx b/src/modules/player/navigationIsland/NavigationIsland.tsx index c0fbb1b5e..68db9da68 100644 --- a/src/modules/player/navigationIsland/NavigationIsland.tsx +++ b/src/modules/player/navigationIsland/NavigationIsland.tsx @@ -5,7 +5,7 @@ import { NAVIGATION_ISLAND_CY } from '@/config/selectors'; import useChatButton from './ChatButton'; import useGeolocationButton from './GeolocationButton'; import usePinnedItemsButton from './PinnedItemsButton'; -import usePreviousNextButtons from './PreviousNextButtons'; +import { usePreviousNextButtons } from './PreviousNextButtons'; const NavigationIslandBox = (): JSX.Element | null => { const { previousButton, nextButton } = usePreviousNextButtons(); diff --git a/src/modules/player/navigationIsland/PinnedItemsButton.tsx b/src/modules/player/navigationIsland/PinnedItemsButton.tsx index adb509817..0cc9440f5 100644 --- a/src/modules/player/navigationIsland/PinnedItemsButton.tsx +++ b/src/modules/player/navigationIsland/PinnedItemsButton.tsx @@ -26,7 +26,7 @@ const usePinnedItemsButton = (): { pinnedButton: JSX.Element | null } => { const childrenPinnedCount = children?.filter(({ settings: s, hidden }) => s.isPinned && !hidden) - ?.length || 0; + ?.length ?? 0; // don't show the button if there are no items pinned in all descendants if (childrenPinnedCount <= 0) { diff --git a/src/modules/player/navigationIsland/PreviousNextButtons.tsx b/src/modules/player/navigationIsland/PreviousNextButtons.tsx index 6c671d9a1..27e42d849 100644 --- a/src/modules/player/navigationIsland/PreviousNextButtons.tsx +++ b/src/modules/player/navigationIsland/PreviousNextButtons.tsx @@ -13,10 +13,33 @@ import { import { LoadingButton, NavigationButton } from './customButtons'; -const usePreviousNextButtons = (): { +function getPrevious( + itemId: string, + // includes the rootItem + folderHierarchy: DiscriminatedItem[], +): DiscriminatedItem | null { + const idx = folderHierarchy.findIndex(({ id }) => id === itemId); + if (idx < 0) { + return null; + } + return folderHierarchy[idx - 1]; +} + +function getNext( + itemId: string, + folderHierarchy: DiscriminatedItem[], +): DiscriminatedItem | null { + const idx = folderHierarchy.findIndex(({ id }) => id === itemId); + if (idx < 0 || idx + 1 < folderHierarchy.length) { + return null; + } + return folderHierarchy[idx + 1]; +} + +export function usePreviousNextButtons(): { previousButton: JSX.Element | null; nextButton: JSX.Element | null; -} => { +} { const { rootId, itemId } = useParams({ from: '/player/$rootId/$itemId/' }); const search = useSearch({ from: '/player/$rootId/$itemId/' }); const { user } = useAuth(); @@ -46,48 +69,19 @@ const usePreviousNextButtons = (): { }; } - const prevRoot: DiscriminatedItem | null = rootItem || null; - let prev: DiscriminatedItem | null = null; - let next: DiscriminatedItem | null = null; - // if there are no descendants then there is no need to navigate - if (!Array.isArray(descendants)) { + if (!Array.isArray(descendants) || !rootItem) { return { previousButton: null, nextButton: null }; } - let folderHierarchy = descendants; - - if (shuffle) { - // seed for shuffling is consistent for member + root (base) item combination - const baseId = rootId || ''; - const memberId = user?.id || ''; - const combinedUuids = combineUuids(baseId, memberId); - folderHierarchy = shuffleAllButLastItemInArray( - folderHierarchy, - combinedUuids, - ); - } + const folderHierarchy = shuffle + ? // seed for shuffling is consistent for member + root (base) item combination + shuffleAllButLastItemInArray(descendants, combineUuids(rootId, user?.id)) + : descendants; + const folderHierarchyIncludingRoot = [rootItem, ...folderHierarchy]; - // when focusing on the root item - if (itemId === rootId && folderHierarchy.length) { - // there is no previous and the nex in the first item in the hierarchy - [next] = folderHierarchy; - // when focusing on the descendants - } else { - const idx = folderHierarchy.findIndex(({ id }) => id === itemId); - - // if index is not found, then do not show navigation - if (idx < 0) { - return { previousButton: null, nextButton: null }; - } - - // if index is 0, previous is root - prev = idx === 0 ? prevRoot : folderHierarchy[idx - 1]; - // check if the next element is inside the bounds of folderHierarchy, of not, next will simply stay null - if (idx + 1 < folderHierarchy.length) { - next = folderHierarchy[idx + 1]; - } - } + const prev = getPrevious(itemId, folderHierarchyIncludingRoot); + const next = getNext(itemId, folderHierarchyIncludingRoot); // should we display both buttons if they are disabled ? if (!prev && !next) { @@ -119,5 +113,4 @@ const usePreviousNextButtons = (): { ), }; -}; -export default usePreviousNextButtons; +} diff --git a/src/modules/player/rightPanel/SideContent.tsx b/src/modules/player/rightPanel/SideContent.tsx index 2d462e1a1..35e97e33d 100644 --- a/src/modules/player/rightPanel/SideContent.tsx +++ b/src/modules/player/rightPanel/SideContent.tsx @@ -92,15 +92,14 @@ const SideContent = ({ content, item }: Props): JSX.Element | null => { const pinnedItems = children?.filter( ({ settings: s, hidden }) => s.isPinned && !hidden, ); - const pinnedCount = pinnedItems?.length || 0; + const pinnedCount = pinnedItems?.length ?? 0; const toggleFullscreen = () => { setIsFullscreen(!isFullscreen); }; const displayFullscreenButton = () => { - // todo: add this to settings (?) - const fullscreen = search.fullscreen; + const { fullscreen } = search; if (isMobile || !fullscreen) { return null; } @@ -157,7 +156,7 @@ const SideContent = ({ content, item }: Props): JSX.Element | null => { {/* show children pinned items */} {pinnedItems.map((pinnedItem) => ( - + ))} diff --git a/src/modules/player/tree/LoadingTree.tsx b/src/modules/player/tree/LoadingTree.tsx index 09d21b5a2..bd60a539f 100644 --- a/src/modules/player/tree/LoadingTree.tsx +++ b/src/modules/player/tree/LoadingTree.tsx @@ -1,12 +1,13 @@ import { Skeleton, Stack, Typography } from '@mui/material'; -const LoadingTree = (): JSX.Element => ( - - {['elem 1', 'elem 2', 'elem 3', 'elem 4', 'elem 5'].map((el) => ( - - - - ))} - -); -export default LoadingTree; +export function LoadingTree(): JSX.Element { + return ( + + {['elem 1', 'elem 2', 'elem 3', 'elem 4', 'elem 5'].map((el) => ( + + + + ))} + + ); +} diff --git a/src/modules/player/tree/Node.tsx b/src/modules/player/tree/Node.tsx index 6abac994f..fa36f434f 100644 --- a/src/modules/player/tree/Node.tsx +++ b/src/modules/player/tree/Node.tsx @@ -31,7 +31,7 @@ export function TreeNode({ level, isSelected, firstLevelStyle = {}, -}: NodeProps): JSX.Element { +}: Readonly): JSX.Element { return ( ): JSX.Element { const itemsToShow = items?.filter((item) => onlyShowContainerItems ? GRAASP_MENU_ITEMS.includes(item.type) : true, ); diff --git a/src/modules/player/ui/CardThumbnail.tsx b/src/modules/player/ui/CardThumbnail.tsx index 52689e3ae..dc7117912 100644 --- a/src/modules/player/ui/CardThumbnail.tsx +++ b/src/modules/player/ui/CardThumbnail.tsx @@ -16,7 +16,7 @@ export function CardThumbnail({ width, minHeight, type = ItemType.FOLDER, -}: CardThumbnailProps): JSX.Element { +}: Readonly): JSX.Element { const theme = useTheme(); if (thumbnail) { diff --git a/src/modules/player/utils/shuffle.ts b/src/modules/player/utils/shuffle.ts index b462cccad..fb9050835 100644 --- a/src/modules/player/utils/shuffle.ts +++ b/src/modules/player/utils/shuffle.ts @@ -43,7 +43,7 @@ export function factorial(n: number): number { return n ? n * factorial(n - 1) : 1; } -export function combineUuids(uuid1: string, uuid2: string): string { +export function combineUuids(uuid1: string, uuid2?: string): string { const combinedUUID = uuid1 + uuid2; // use v5 UUID with URL namespace return uuidv5(combinedUUID, uuidv5.URL); diff --git a/src/routes/account/storage.tsx b/src/routes/account/storage.tsx index 74647929e..deccc361d 100644 --- a/src/routes/account/storage.tsx +++ b/src/routes/account/storage.tsx @@ -27,10 +27,11 @@ function StorageRoute(): JSX.Element { values={{ email: ADMIN_CONTACT, }} - components={ - // eslint-disable-next-line jsx-a11y/anchor-has-content - [] - } + components={[ + + _ + , + ]} /> {t('STORAGE_INFO')} diff --git a/src/routes/auth/reset-password.tsx b/src/routes/auth/reset-password.tsx index 551badc70..81c990cd5 100644 --- a/src/routes/auth/reset-password.tsx +++ b/src/routes/auth/reset-password.tsx @@ -60,7 +60,7 @@ type Inputs = { export function ResetPassword() { const { t } = useTranslation(NS.Auth); const search = Route.useSearch(); - const { isValid, token } = useValidateJWTToken(search.t); + const { isValid: isTokenValid, token } = useValidateJWTToken(search.t); const [showPasswords, setShowPasswords] = useState(false); @@ -77,7 +77,7 @@ export function ResetPassword() { isSuccess, } = useResolvePasswordResetRequest(); - if (!isValid) { + if (!isTokenValid) { return ; } diff --git a/src/routes/email.change.tsx b/src/routes/email.change.tsx index 59554526f..cb69f1b20 100644 --- a/src/routes/email.change.tsx +++ b/src/routes/email.change.tsx @@ -14,7 +14,7 @@ import { zodValidator } from '@tanstack/zod-adapter'; import { HttpStatusCode, isAxiosError } from 'axios'; import { z } from 'zod'; -import CenteredContainer from '@/components/layout/CenteredContainer'; +import { CenteredContainer } from '@/components/layout/CenteredContainer'; import { ButtonLink } from '@/components/ui/ButtonLink'; import { NS } from '@/config/constants'; import { ACCOUNT_SETTINGS_PATH } from '@/config/paths'; @@ -53,7 +53,7 @@ type EmailChangeContentProps = { function EmailChangeContent({ newEmail, jwtToken, -}: EmailChangeContentProps): JSX.Element { +}: Readonly): JSX.Element { const { t } = useTranslation(NS.Account); const { mutate: validateEmail, diff --git a/src/routes/player/$rootId/$itemId.tsx b/src/routes/player/$rootId/$itemId.tsx index f695bee95..8a91cc83c 100644 --- a/src/routes/player/$rootId/$itemId.tsx +++ b/src/routes/player/$rootId/$itemId.tsx @@ -6,8 +6,7 @@ import { Box, Typography, useTheme } from '@mui/material'; import { Context } from '@graasp/sdk'; import { Main, Platform, PlatformSwitch, useMobileView } from '@graasp/ui'; -import { createFileRoute } from '@tanstack/react-router'; -import { Outlet } from '@tanstack/react-router'; +import { Outlet, createFileRoute } from '@tanstack/react-router'; import { fallback, zodValidator } from '@tanstack/zod-adapter'; import { z } from 'zod';