From a01f0256a1c230a9bd011df9653df6a6120f6af0 Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Thu, 4 Jun 2026 12:54:43 +0200 Subject: [PATCH] =?UTF-8?q?=E2=AC=86=EF=B8=8F(frontend)=20upgrade=20eslint?= =?UTF-8?q?-plugin-react-hooks=20by=20two=20major=20versions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upgrade introduced around 50 new linting errors. Fix the low-hanging fruit and address straightforward violations. Several of the new rules appear to target patterns intended for newer React compiler capabilities. Since the project currently runs on React 18 rather than React 19, disable these rules for now instead of applying potentially inappropriate changes. Follow-up work can address the new rules in a dedicated PR, potentially alongside a future React version upgrade. --- src/frontend/eslint.config.js | 7 ++ src/frontend/package-lock.json | 67 ++++++++++++++----- src/frontend/package.json | 6 +- .../notifications/components/StyledToast.tsx | 11 +++ .../components/StyledToastContainer.tsx | 16 +++++ .../notifications/components/Toast.tsx | 28 +------- .../components/ToastAnyRecording.tsx | 3 +- .../notifications/components/ToastJoined.tsx | 3 +- .../components/ToastLowerHand.tsx | 3 +- .../components/ToastMessageReceived.tsx | 3 +- .../notifications/components/ToastMuted.tsx | 3 +- .../components/ToastPermissionsRemoved.tsx | 3 +- .../notifications/components/ToastRaised.tsx | 3 +- .../components/ToastRecordingRequest.tsx | 3 +- .../components/ToastRecordingSaving.tsx | 3 +- .../WaitingParticipantNotification.tsx | 2 +- .../features/pip/components/PipControlBar.tsx | 1 + .../pip/components/PipFloatingReactions.tsx | 6 +- .../reactions/components/ReactionPortals.tsx | 11 ++- .../components/IsIdleDisconnectModal.tsx | 1 - .../livekit/hooks/useCopyRoomToClipboard.ts | 7 +- src/frontend/src/primitives/Checkbox.tsx | 2 +- src/frontend/src/primitives/Menu.tsx | 2 +- src/frontend/src/primitives/Popover.tsx | 29 +------- src/frontend/src/primitives/Radio.tsx | 2 +- src/frontend/src/primitives/Select.tsx | 2 +- src/frontend/src/primitives/StyledPopover.tsx | 29 ++++++++ src/frontend/src/primitives/Switch.tsx | 2 +- 28 files changed, 158 insertions(+), 100 deletions(-) create mode 100644 src/frontend/src/features/notifications/components/StyledToast.tsx create mode 100644 src/frontend/src/features/notifications/components/StyledToastContainer.tsx create mode 100644 src/frontend/src/primitives/StyledPopover.tsx diff --git a/src/frontend/eslint.config.js b/src/frontend/eslint.config.js index a0d6e64b..1a164f64 100644 --- a/src/frontend/eslint.config.js +++ b/src/frontend/eslint.config.js @@ -54,6 +54,13 @@ export default tseslint.config( { allowConstantExport: true }, ], + // React Compiler rules (v7+) — disable until migrated to React 19 + 'react-hooks/react-compiler': 'off', + 'react-hooks/set-state-in-effect': 'off', + 'react-hooks/preserve-manual-memoization': 'off', + 'react-hooks/immutability': 'off', + 'react-hooks/refs': 'off', + // jsx-a11y recommended rules ...jsxA11y.configs.recommended.rules, diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 5b84ae48..9d5b38ee 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -48,11 +48,11 @@ "@types/react-dom": "18.3.1", "@vitejs/plugin-react": "6.0.2", "eslint": "9.39.4", - "eslint-config-prettier": "10.1.5", + "eslint-config-prettier": "10.1.8", "eslint-plugin-jsx-a11y": "6.10.2", "eslint-plugin-react": "7.37.5", - "eslint-plugin-react-hooks": "5.2.0", - "eslint-plugin-react-refresh": "0.4.20", + "eslint-plugin-react-hooks": "7.1.1", + "eslint-plugin-react-refresh": "0.5.2", "globals": "17.6.0", "postcss": "8.5.15", "prettier": "3.8.3", @@ -6932,9 +6932,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -7035,26 +7035,33 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", - "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, "engines": { - "node": ">=10" + "node": ">=18" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.20", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", - "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", + "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==", "dev": true, "license": "MIT", "peerDependencies": { - "eslint": ">=8.40" + "eslint": "^9 || ^10" } }, "node_modules/eslint-plugin-react/node_modules/brace-expansion": { @@ -8079,6 +8086,23 @@ "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.2.1.tgz", "integrity": "sha512-Rf4YVNYpKjZ6ASAmibcwTNciQ5Co5Ztq6iZPEykHpkoflnD/K5ryE/rHehFsTm4NJj8nKDhbi3eKBWGogmNnkg==" }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, "node_modules/hono": { "version": "4.12.18", "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.18.tgz", @@ -13873,6 +13897,19 @@ "peerDependencies": { "zod": "^3.25.28 || ^4" } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } } } } diff --git a/src/frontend/package.json b/src/frontend/package.json index 4fda522c..2d899e9c 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -55,11 +55,11 @@ "@types/react-dom": "18.3.1", "@vitejs/plugin-react": "6.0.2", "eslint": "9.39.4", - "eslint-config-prettier": "10.1.5", + "eslint-config-prettier": "10.1.8", "eslint-plugin-jsx-a11y": "6.10.2", "eslint-plugin-react": "7.37.5", - "eslint-plugin-react-hooks": "5.2.0", - "eslint-plugin-react-refresh": "0.4.20", + "eslint-plugin-react-hooks": "7.1.1", + "eslint-plugin-react-refresh": "0.5.2", "globals": "17.6.0", "postcss": "8.5.15", "prettier": "3.8.3", diff --git a/src/frontend/src/features/notifications/components/StyledToast.tsx b/src/frontend/src/features/notifications/components/StyledToast.tsx new file mode 100644 index 00000000..82f75576 --- /dev/null +++ b/src/frontend/src/features/notifications/components/StyledToast.tsx @@ -0,0 +1,11 @@ +import { styled } from '@/styled-system/jsx' + +export const StyledToast = styled('div', { + base: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + gap: '1rem', + padding: '10px', + }, +}) diff --git a/src/frontend/src/features/notifications/components/StyledToastContainer.tsx b/src/frontend/src/features/notifications/components/StyledToastContainer.tsx new file mode 100644 index 00000000..ac97f97f --- /dev/null +++ b/src/frontend/src/features/notifications/components/StyledToastContainer.tsx @@ -0,0 +1,16 @@ +import { styled } from '@/styled-system/jsx' + +export const StyledToastContainer = styled('div', { + base: { + margin: 0.5, + boxShadow: + 'rgba(0, 0, 0, 0.5) 0px 4px 8px 0px, rgba(0, 0, 0, 0.3) 0px 6px 20px 4px', + backgroundColor: 'greyscale.700', + color: 'white', + borderRadius: '8px', + '&[data-entering]': { animation: 'fade 200ms' }, + '&[data-exiting]': { animation: 'fade 150ms reverse ease-in' }, + width: 'fit-content', + marginLeft: 'auto', + }, +}) diff --git a/src/frontend/src/features/notifications/components/Toast.tsx b/src/frontend/src/features/notifications/components/Toast.tsx index bee02c45..bc5ebc61 100644 --- a/src/frontend/src/features/notifications/components/Toast.tsx +++ b/src/frontend/src/features/notifications/components/Toast.tsx @@ -1,36 +1,12 @@ import { useToast } from '@react-aria/toast' import { Button } from '@/primitives' import { RiCloseLine } from '@remixicon/react' -import { styled } from '@/styled-system/jsx' import { useRef } from 'react' import type { ToastState } from '@react-stately/toast' import type { ToastData } from './ToastProvider' import type { QueuedToast } from '@react-stately/toast' - -export const StyledToastContainer = styled('div', { - base: { - margin: 0.5, - boxShadow: - 'rgba(0, 0, 0, 0.5) 0px 4px 8px 0px, rgba(0, 0, 0, 0.3) 0px 6px 20px 4px', - backgroundColor: 'greyscale.700', - color: 'white', - borderRadius: '8px', - '&[data-entering]': { animation: 'fade 200ms' }, - '&[data-exiting]': { animation: 'fade 150ms reverse ease-in' }, - width: 'fit-content', - marginLeft: 'auto', - }, -}) - -export const StyledToast = styled('div', { - base: { - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - gap: '1rem', - padding: '10px', - }, -}) +import { StyledToastContainer } from './StyledToastContainer' +import { StyledToast } from './StyledToast' export interface ToastProps { key: string diff --git a/src/frontend/src/features/notifications/components/ToastAnyRecording.tsx b/src/frontend/src/features/notifications/components/ToastAnyRecording.tsx index 75c2f44c..113606de 100644 --- a/src/frontend/src/features/notifications/components/ToastAnyRecording.tsx +++ b/src/frontend/src/features/notifications/components/ToastAnyRecording.tsx @@ -1,7 +1,8 @@ import { useToast } from '@react-aria/toast' import { useMemo, useRef } from 'react' -import { StyledToastContainer, type ToastProps } from './Toast' +import type { ToastProps } from './Toast' +import { StyledToastContainer } from './StyledToastContainer' import { HStack } from '@/styled-system/jsx' import { useTranslation } from 'react-i18next' import { NotificationType } from '../NotificationType' diff --git a/src/frontend/src/features/notifications/components/ToastJoined.tsx b/src/frontend/src/features/notifications/components/ToastJoined.tsx index f6f8b527..64e09a7e 100644 --- a/src/frontend/src/features/notifications/components/ToastJoined.tsx +++ b/src/frontend/src/features/notifications/components/ToastJoined.tsx @@ -6,10 +6,11 @@ import Source = Track.Source import { useMaybeLayoutContext } from '@livekit/components-react' import { ParticipantTile } from '@/features/rooms/livekit/components/ParticipantTile' -import { StyledToastContainer, type ToastProps } from './Toast' +import { type ToastProps } from './Toast' import { HStack, styled } from '@/styled-system/jsx' import { Div } from '@/primitives' import { useTranslation } from 'react-i18next' +import { StyledToastContainer } from './StyledToastContainer' const ClickableToast = styled(RACButton, { base: { diff --git a/src/frontend/src/features/notifications/components/ToastLowerHand.tsx b/src/frontend/src/features/notifications/components/ToastLowerHand.tsx index 97a21920..85cf9309 100644 --- a/src/frontend/src/features/notifications/components/ToastLowerHand.tsx +++ b/src/frontend/src/features/notifications/components/ToastLowerHand.tsx @@ -1,11 +1,12 @@ import { useToast } from '@react-aria/toast' import { useRef } from 'react' -import { StyledToastContainer, type ToastProps } from './Toast' +import { type ToastProps } from './Toast' import { HStack } from '@/styled-system/jsx' import { useTranslation } from 'react-i18next' import { Button } from '@/primitives' import { css } from '@/styled-system/css' +import { StyledToastContainer } from './StyledToastContainer' export function ToastLowerHand({ state, ...props }: Readonly) { const { t } = useTranslation('notifications', { keyPrefix: 'lowerHand' }) diff --git a/src/frontend/src/features/notifications/components/ToastMessageReceived.tsx b/src/frontend/src/features/notifications/components/ToastMessageReceived.tsx index ea954a6d..125c85b1 100644 --- a/src/frontend/src/features/notifications/components/ToastMessageReceived.tsx +++ b/src/frontend/src/features/notifications/components/ToastMessageReceived.tsx @@ -1,13 +1,14 @@ import { useToast } from '@react-aria/toast' import { useEffect, useRef } from 'react' -import { StyledToastContainer, type ToastProps } from './Toast' +import { type ToastProps } from './Toast' import { Text } from '@/primitives' import { RiMessage2Line } from '@remixicon/react' import { useSidePanel } from '@/features/rooms/livekit/hooks/useSidePanel' import { Button as RACButton } from 'react-aria-components' import { css } from '@/styled-system/css' import { useTranslation } from 'react-i18next' +import { StyledToastContainer } from './StyledToastContainer' export function ToastMessageReceived({ state, diff --git a/src/frontend/src/features/notifications/components/ToastMuted.tsx b/src/frontend/src/features/notifications/components/ToastMuted.tsx index 76bc8eeb..4706c3fc 100644 --- a/src/frontend/src/features/notifications/components/ToastMuted.tsx +++ b/src/frontend/src/features/notifications/components/ToastMuted.tsx @@ -1,9 +1,10 @@ import { useToast } from '@react-aria/toast' import { useRef } from 'react' -import { StyledToastContainer, type ToastProps } from './Toast' +import { type ToastProps } from './Toast' import { HStack } from '@/styled-system/jsx' import { useTranslation } from 'react-i18next' +import { StyledToastContainer } from './StyledToastContainer' export function ToastMuted({ state, ...props }: Readonly) { const { t } = useTranslation('notifications') diff --git a/src/frontend/src/features/notifications/components/ToastPermissionsRemoved.tsx b/src/frontend/src/features/notifications/components/ToastPermissionsRemoved.tsx index 090e4768..38220c93 100644 --- a/src/frontend/src/features/notifications/components/ToastPermissionsRemoved.tsx +++ b/src/frontend/src/features/notifications/components/ToastPermissionsRemoved.tsx @@ -1,9 +1,10 @@ import { useToast } from '@react-aria/toast' import { useMemo, useRef } from 'react' -import { StyledToastContainer, type ToastProps } from './Toast' +import { type ToastProps } from './Toast' import { HStack } from '@/styled-system/jsx' import { useTranslation } from 'react-i18next' +import { StyledToastContainer } from './StyledToastContainer' export function ToastPermissionsRemoved({ state, diff --git a/src/frontend/src/features/notifications/components/ToastRaised.tsx b/src/frontend/src/features/notifications/components/ToastRaised.tsx index 380ddf97..20c9ca41 100644 --- a/src/frontend/src/features/notifications/components/ToastRaised.tsx +++ b/src/frontend/src/features/notifications/components/ToastRaised.tsx @@ -1,13 +1,14 @@ import { useToast } from '@react-aria/toast' import { useRef } from 'react' -import { StyledToastContainer, type ToastProps } from './Toast' +import { type ToastProps } from './Toast' import { HStack } from '@/styled-system/jsx' import { Button, Div } from '@/primitives' import { useTranslation } from 'react-i18next' import { RiCloseLine, RiHand } from '@remixicon/react' import { useSidePanel } from '@/features/rooms/livekit/hooks/useSidePanel' import { css } from '@/styled-system/css' +import { StyledToastContainer } from './StyledToastContainer' export function ToastRaised({ state, ...props }: Readonly) { const { t } = useTranslation('notifications') diff --git a/src/frontend/src/features/notifications/components/ToastRecordingRequest.tsx b/src/frontend/src/features/notifications/components/ToastRecordingRequest.tsx index 5ade87ad..85e7fe9d 100644 --- a/src/frontend/src/features/notifications/components/ToastRecordingRequest.tsx +++ b/src/frontend/src/features/notifications/components/ToastRecordingRequest.tsx @@ -1,13 +1,14 @@ import { useToast } from '@react-aria/toast' import { useMemo, useRef } from 'react' -import { StyledToastContainer, type ToastProps } from './Toast' +import { type ToastProps } from './Toast' import { HStack } from '@/styled-system/jsx' import { useTranslation } from 'react-i18next' import { NotificationType } from '../NotificationType' import { Button } from '@/primitives' import { css } from '@/styled-system/css' import { useSidePanel } from '@/features/rooms/livekit/hooks/useSidePanel' +import { StyledToastContainer } from './StyledToastContainer' export function ToastRecordingRequest({ state, diff --git a/src/frontend/src/features/notifications/components/ToastRecordingSaving.tsx b/src/frontend/src/features/notifications/components/ToastRecordingSaving.tsx index d4814f0e..4370a6fe 100644 --- a/src/frontend/src/features/notifications/components/ToastRecordingSaving.tsx +++ b/src/frontend/src/features/notifications/components/ToastRecordingSaving.tsx @@ -2,12 +2,13 @@ import { useToast } from '@react-aria/toast' import { useMemo, useRef } from 'react' import { Text } from '@/primitives' -import { StyledToastContainer, type ToastProps } from './Toast' +import { type ToastProps } from './Toast' import { HStack } from '@/styled-system/jsx' import { useTranslation } from 'react-i18next' import { useUser } from '@/features/auth/api/useUser' import { css } from '@/styled-system/css' import { RecordingMode } from '@/features/recording' +import { StyledToastContainer } from './StyledToastContainer' export function ToastRecordingSaving({ state, diff --git a/src/frontend/src/features/notifications/components/WaitingParticipantNotification.tsx b/src/frontend/src/features/notifications/components/WaitingParticipantNotification.tsx index a4994c26..80ad8fb2 100644 --- a/src/frontend/src/features/notifications/components/WaitingParticipantNotification.tsx +++ b/src/frontend/src/features/notifications/components/WaitingParticipantNotification.tsx @@ -1,4 +1,4 @@ -import { StyledToastContainer } from './Toast' +import { StyledToastContainer } from './StyledToastContainer' import { HStack, VStack } from '@/styled-system/jsx' import { Avatar } from '@/components/Avatar' import { Button, Text } from '@/primitives' diff --git a/src/frontend/src/features/pip/components/PipControlBar.tsx b/src/frontend/src/features/pip/components/PipControlBar.tsx index 6f849db5..1425923f 100644 --- a/src/frontend/src/features/pip/components/PipControlBar.tsx +++ b/src/frontend/src/features/pip/components/PipControlBar.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react-refresh/only-export-components */ import { styled } from '@/styled-system/jsx' import { useTranslation } from 'react-i18next' import { AudioDevicesControl } from '@/features/rooms/livekit/components/controls/Device/AudioDevicesControl' diff --git a/src/frontend/src/features/pip/components/PipFloatingReactions.tsx b/src/frontend/src/features/pip/components/PipFloatingReactions.tsx index e612d6b0..1bb616f3 100644 --- a/src/frontend/src/features/pip/components/PipFloatingReactions.tsx +++ b/src/frontend/src/features/pip/components/PipFloatingReactions.tsx @@ -1,6 +1,6 @@ import { useSnapshot } from 'valtio' import { reactionsStore } from '@/stores/reactions' -import { useMemo, useRef } from 'react' +import { useRef, useState } from 'react' import { FloatingReaction } from '@/features/reactions/components/ReactionPortals' import { Reaction } from '@/features/reactions/types' import { css } from '@/styled-system/css' @@ -19,8 +19,8 @@ export const PipFloatingReactions = () => { const PipFloatingReaction = ({ reaction }: { reaction: Reaction }) => { const containerRef = useRef(null) - const speed = useMemo(() => Math.random() * 1.5 + 0.5, []) - const scale = useMemo(() => Math.max(Math.random() + 0.5, 1), []) + const [speed] = useState(() => Math.random() * 1.5 + 0.5) + const [scale] = useState(() => Math.max(Math.random() + 0.5, 1)) return (
Math.random() * window.innerWidth * REACTION_SPAWN_WIDTH_RATIO, - [] + const [left] = useState( + () => Math.random() * window.innerWidth * REACTION_SPAWN_WIDTH_RATIO ) useEffect(() => { @@ -115,8 +114,8 @@ export function FloatingReaction({ } const ReactionPortal = ({ reaction }: { reaction: Reaction }) => { - const speed = useMemo(() => Math.random() * 1.5 + 0.5, []) - const scale = useMemo(() => Math.max(Math.random() + 0.5, 1), []) + const [speed] = useState(() => Math.random() * 1.5 + 0.5) + const [scale] = useState(() => Math.max(Math.random() + 0.5, 1)) return createPortal(
{ useSrCountdownAnnouncement(remainingSeconds) useEffect(() => { - setTimeRemaining(IDLE_DISCONNECT_TIMEOUT_MS) const interval = setInterval(() => { setTimeRemaining((prev) => { if (prev <= 1000) { diff --git a/src/frontend/src/features/rooms/livekit/hooks/useCopyRoomToClipboard.ts b/src/frontend/src/features/rooms/livekit/hooks/useCopyRoomToClipboard.ts index 7df70d4d..6f07d80f 100644 --- a/src/frontend/src/features/rooms/livekit/hooks/useCopyRoomToClipboard.ts +++ b/src/frontend/src/features/rooms/livekit/hooks/useCopyRoomToClipboard.ts @@ -31,13 +31,14 @@ export const useCopyRoomToClipboard = (room: ApiRoom | undefined) => { } }, [isRoomUrlCopied]) + const roomSlug = room?.slug const roomUrl = useMemo(() => { - return room?.slug ? getRouteUrl('room', room.slug) : '' - }, [room?.slug]) + return roomSlug ? getRouteUrl('room', roomSlug) : '' + }, [roomSlug]) const hasTelephonyInfo = useMemo(() => { return telephony.enabled && room?.pin_code - }, [telephony.enabled, room?.pin_code]) + }, [telephony.enabled, room]) const content = useMemo(() => { if (!roomUrl || !room) return '' diff --git a/src/frontend/src/primitives/Checkbox.tsx b/src/frontend/src/primitives/Checkbox.tsx index 97fdbcfd..06445e94 100644 --- a/src/frontend/src/primitives/Checkbox.tsx +++ b/src/frontend/src/primitives/Checkbox.tsx @@ -10,7 +10,7 @@ import { FieldErrors } from './FieldErrors' import { FieldDescription } from './FieldDescription' // styled taken from example at https://react-spectrum.adobe.com/react-aria/Checkbox.html -export const StyledCheckbox = styled(RACCheckbox, { +const StyledCheckbox = styled(RACCheckbox, { base: { display: 'flex', alignItems: 'center', diff --git a/src/frontend/src/primitives/Menu.tsx b/src/frontend/src/primitives/Menu.tsx index f6008716..86eba76d 100644 --- a/src/frontend/src/primitives/Menu.tsx +++ b/src/frontend/src/primitives/Menu.tsx @@ -1,6 +1,6 @@ import { ReactNode } from 'react' import { MenuTrigger } from 'react-aria-components' -import { StyledPopover } from './Popover' +import { StyledPopover } from './StyledPopover' import { Box } from './Box' /** diff --git a/src/frontend/src/primitives/Popover.tsx b/src/frontend/src/primitives/Popover.tsx index 5ccd995f..d7ec918e 100644 --- a/src/frontend/src/primitives/Popover.tsx +++ b/src/frontend/src/primitives/Popover.tsx @@ -2,39 +2,12 @@ import { ReactNode } from 'react' import { DialogProps, DialogTrigger, - Popover as RACPopover, Dialog, OverlayArrow, } from 'react-aria-components' import { styled } from '@/styled-system/jsx' import { Box } from './Box' - -export const StyledPopover = styled(RACPopover, { - base: { - minWidth: 'var(--trigger-width)', - '&[data-entering]': { - animation: 'slide 200ms', - }, - '&[data-exiting]': { - animation: 'slide 200ms reverse ease-in', - }, - '&[data-placement="bottom"]': { - marginTop: 0.25, - }, - '&[data-placement=top]': { - '--origin': 'translateY(8px)', - }, - '&[data-placement=bottom]': { - '--origin': 'translateY(-8px)', - }, - '&[data-placement=right]': { - '--origin': 'translateX(-8px)', - }, - '&[data-placement=left]': { - '--origin': 'translateX(8px)', - }, - }, -}) +import { StyledPopover } from './StyledPopover' const StyledOverlayArrow = styled(OverlayArrow, { base: { diff --git a/src/frontend/src/primitives/Radio.tsx b/src/frontend/src/primitives/Radio.tsx index 46822aec..a7cbdd36 100644 --- a/src/frontend/src/primitives/Radio.tsx +++ b/src/frontend/src/primitives/Radio.tsx @@ -6,7 +6,7 @@ import { styled } from '@/styled-system/jsx' import { type StyledVariantProps } from '@/styled-system/types' // styled taken from example at https://react-spectrum.adobe.com/react-aria/Checkbox.html and changed for round radios -export const StyledRadio = styled(RACRadio, { +const StyledRadio = styled(RACRadio, { base: { display: 'flex', alignItems: 'center', diff --git a/src/frontend/src/primitives/Select.tsx b/src/frontend/src/primitives/Select.tsx index ae8a78be..db8b2a5e 100644 --- a/src/frontend/src/primitives/Select.tsx +++ b/src/frontend/src/primitives/Select.tsx @@ -11,7 +11,7 @@ import { } from 'react-aria-components' import { useTranslation } from 'react-i18next' import { Box } from './Box' -import { StyledPopover } from './Popover' +import { StyledPopover } from './StyledPopover' import { menuRecipe } from '@/primitives/menuRecipe.ts' import { css } from '@/styled-system/css' import type { Placement } from '@react-types/overlays' diff --git a/src/frontend/src/primitives/StyledPopover.tsx b/src/frontend/src/primitives/StyledPopover.tsx new file mode 100644 index 00000000..c6c11c29 --- /dev/null +++ b/src/frontend/src/primitives/StyledPopover.tsx @@ -0,0 +1,29 @@ +import { Popover as RACPopover } from 'react-aria-components' +import { styled } from '@/styled-system/jsx' + +export const StyledPopover = styled(RACPopover, { + base: { + minWidth: 'var(--trigger-width)', + '&[data-entering]': { + animation: 'slide 200ms', + }, + '&[data-exiting]': { + animation: 'slide 200ms reverse ease-in', + }, + '&[data-placement="bottom"]': { + marginTop: 0.25, + }, + '&[data-placement=top]': { + '--origin': 'translateY(8px)', + }, + '&[data-placement=bottom]': { + '--origin': 'translateY(-8px)', + }, + '&[data-placement=right]': { + '--origin': 'translateX(-8px)', + }, + '&[data-placement=left]': { + '--origin': 'translateX(8px)', + }, + }, +}) diff --git a/src/frontend/src/primitives/Switch.tsx b/src/frontend/src/primitives/Switch.tsx index fa9dc3c4..c9578b2c 100644 --- a/src/frontend/src/primitives/Switch.tsx +++ b/src/frontend/src/primitives/Switch.tsx @@ -6,7 +6,7 @@ import { styled } from '@/styled-system/jsx' import { type StyledVariantProps } from '@/styled-system/types' import { RiCheckLine, RiCloseFill } from '@remixicon/react' -export const StyledSwitch = styled(RACSwitch, { +const StyledSwitch = styled(RACSwitch, { base: { display: 'flex', alignItems: 'center',