From 0308f98ef9a32e8912487c6b0a02d2d37f981609 Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Mon, 30 Jun 2025 20:50:39 +0200 Subject: [PATCH] =?UTF-8?q?=E2=8F=AA=EF=B8=8F(frontend)=20rollback=20vendo?= =?UTF-8?q?red=20hooks=20and=20install=20react-hook=20dependency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove copied livekit hooks and install react-hook deps directly for better browser support. Livekit's modern-only version causes ResizeObserver exceptions in production. Original dependency includes necessary polyfills. --- src/frontend/package-lock.json | 52 +++++++ src/frontend/package.json | 1 + .../components/FullScreenShareWarning.tsx | 4 +- .../components/ParticipantPlaceholder.tsx | 4 +- .../rooms/livekit/hooks/useResizeObserver.ts | 127 ------------------ .../prefabs/ControlBar/MoreOptions.tsx | 4 +- 6 files changed, 59 insertions(+), 133 deletions(-) delete mode 100644 src/frontend/src/features/rooms/livekit/hooks/useResizeObserver.ts diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 9b679ad2..11c0fca2 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -13,6 +13,7 @@ "@livekit/track-processors": "0.5.7", "@pandacss/preset-panda": "0.54.0", "@react-aria/toast": "3.0.5", + "@react-hook/size": "2.1.2", "@remixicon/react": "4.6.0", "@tanstack/react-query": "5.81.5", "@timephy/rnnoise-wasm": "1.0.0", @@ -1210,6 +1211,12 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@juggle/resize-observer": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", + "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", + "license": "Apache-2.0" + }, "node_modules/@livekit/components-core": { "version": "0.12.8", "resolved": "https://registry.npmjs.org/@livekit/components-core/-/components-core-0.12.8.tgz", @@ -2701,6 +2708,51 @@ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, + "node_modules/@react-hook/latest": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@react-hook/latest/-/latest-1.0.3.tgz", + "integrity": "sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/@react-hook/passive-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz", + "integrity": "sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/@react-hook/resize-observer": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@react-hook/resize-observer/-/resize-observer-1.2.6.tgz", + "integrity": "sha512-DlBXtLSW0DqYYTW3Ft1/GQFZlTdKY5VAFIC4+km6IK5NiPPDFchGbEJm1j6pSgMqPRHbUQgHJX7RaR76ic1LWA==", + "license": "MIT", + "dependencies": { + "@juggle/resize-observer": "^3.3.1", + "@react-hook/latest": "^1.0.2", + "@react-hook/passive-layout-effect": "^1.2.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/@react-hook/size": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@react-hook/size/-/size-2.1.2.tgz", + "integrity": "sha512-BmE5asyRDxSuQ9p14FUKJ0iBRgV9cROjqNG9jT/EjCM+xHha1HVqbPoT+14FQg1K7xIydabClCibUY4+1tw/iw==", + "license": "MIT", + "dependencies": { + "@react-hook/passive-layout-effect": "^1.2.0", + "@react-hook/resize-observer": "^1.2.1" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, "node_modules/@react-stately/autocomplete": { "version": "3.0.0-beta.2", "resolved": "https://registry.npmjs.org/@react-stately/autocomplete/-/autocomplete-3.0.0-beta.2.tgz", diff --git a/src/frontend/package.json b/src/frontend/package.json index 92cf0ab2..76e1c21d 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -18,6 +18,7 @@ "@livekit/track-processors": "0.5.7", "@pandacss/preset-panda": "0.54.0", "@react-aria/toast": "3.0.5", + "@react-hook/size": "2.1.2", "@remixicon/react": "4.6.0", "@tanstack/react-query": "5.81.5", "@timephy/rnnoise-wasm": "1.0.0", diff --git a/src/frontend/src/features/rooms/livekit/components/FullScreenShareWarning.tsx b/src/frontend/src/features/rooms/livekit/components/FullScreenShareWarning.tsx index decdb784..cb40792e 100644 --- a/src/frontend/src/features/rooms/livekit/components/FullScreenShareWarning.tsx +++ b/src/frontend/src/features/rooms/livekit/components/FullScreenShareWarning.tsx @@ -4,9 +4,9 @@ import { useMemo, useRef } from 'react' import { ScreenSharePreferenceStore } from '@/stores/ScreenSharePreferences' import { useSnapshot } from 'valtio' import { useLocalParticipant } from '@livekit/components-react' -import { useSize } from '../hooks/useResizeObserver' import { TrackReferenceOrPlaceholder } from '@livekit/components-core' import { useTranslation } from 'react-i18next' +import useSize from '@react-hook/size' export const FullScreenShareWarning = ({ trackReference, @@ -16,7 +16,7 @@ export const FullScreenShareWarning = ({ const { t } = useTranslation('rooms', { keyPrefix: 'fullScreenWarning' }) const warningContainerRef = useRef(null) - const { width: containerWidth } = useSize(warningContainerRef) + const containerWidth = useSize(warningContainerRef)[0] const { localParticipant } = useLocalParticipant() const screenSharePreferences = useSnapshot(ScreenSharePreferenceStore) diff --git a/src/frontend/src/features/rooms/livekit/components/ParticipantPlaceholder.tsx b/src/frontend/src/features/rooms/livekit/components/ParticipantPlaceholder.tsx index 44fb4fc7..ae88e682 100644 --- a/src/frontend/src/features/rooms/livekit/components/ParticipantPlaceholder.tsx +++ b/src/frontend/src/features/rooms/livekit/components/ParticipantPlaceholder.tsx @@ -3,8 +3,8 @@ import { styled } from '@/styled-system/jsx' import { Avatar } from '@/components/Avatar' import { useIsSpeaking } from '@livekit/components-react' import { getParticipantColor } from '@/features/rooms/utils/getParticipantColor' -import { useSize } from '@/features/rooms/livekit/hooks/useResizeObserver' import { useMemo, useRef } from 'react' +import useSize from '@react-hook/size' const StyledParticipantPlaceHolder = styled('div', { base: { @@ -28,7 +28,7 @@ export const ParticipantPlaceholder = ({ const participantColor = getParticipantColor(participant) const placeholderEl = useRef(null) - const { width, height } = useSize(placeholderEl) + const [width, height] = useSize(placeholderEl) const minDimension = Math.min(width, height) const avatarSize = useMemo( diff --git a/src/frontend/src/features/rooms/livekit/hooks/useResizeObserver.ts b/src/frontend/src/features/rooms/livekit/hooks/useResizeObserver.ts deleted file mode 100644 index 1e9de1c4..00000000 --- a/src/frontend/src/features/rooms/livekit/hooks/useResizeObserver.ts +++ /dev/null @@ -1,127 +0,0 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -import * as React from 'react' - -const useLatest = (current: T) => { - const storedValue = React.useRef(current) - React.useEffect(() => { - storedValue.current = current - }) - return storedValue -} - -/** - * A React hook that fires a callback whenever ResizeObserver detects a change to its size - * code extracted from https://github.com/jaredLunde/react-hook/blob/master/packages/resize-observer/src/index.tsx in order to not include the polyfill for resize-observer - * - * @internal - */ -export function useResizeObserver( - target: React.RefObject, - callback: UseResizeObserverCallback -) { - const resizeObserver = getResizeObserver() - const storedCallback = useLatest(callback) - - React.useLayoutEffect(() => { - let didUnsubscribe = false - - const targetEl = target.current - if (!targetEl) return - - function cb(entry: ResizeObserverEntry, observer: ResizeObserver) { - if (didUnsubscribe) return - storedCallback.current(entry, observer) - } - - resizeObserver?.subscribe(targetEl as HTMLElement, cb) - - return () => { - didUnsubscribe = true - resizeObserver?.unsubscribe(targetEl as HTMLElement, cb) - } - }, [target.current, resizeObserver, storedCallback]) - - return resizeObserver?.observer -} - -function createResizeObserver() { - let ticking = false - let allEntries: ResizeObserverEntry[] = [] - - const callbacks: Map> = new Map() - - if (typeof window === 'undefined') { - return - } - - const observer = new ResizeObserver( - (entries: ResizeObserverEntry[], obs: ResizeObserver) => { - allEntries = allEntries.concat(entries) - if (!ticking) { - window.requestAnimationFrame(() => { - const triggered = new Set() - for (let i = 0; i < allEntries.length; i++) { - if (triggered.has(allEntries[i].target)) continue - triggered.add(allEntries[i].target) - const cbs = callbacks.get(allEntries[i].target) - cbs?.forEach((cb) => cb(allEntries[i], obs)) - } - allEntries = [] - ticking = false - }) - } - ticking = true - } - ) - - return { - observer, - subscribe(target: HTMLElement, callback: UseResizeObserverCallback) { - observer.observe(target) - const cbs = callbacks.get(target) ?? [] - cbs.push(callback) - callbacks.set(target, cbs) - }, - unsubscribe(target: HTMLElement, callback: UseResizeObserverCallback) { - const cbs = callbacks.get(target) ?? [] - if (cbs.length === 1) { - observer.unobserve(target) - callbacks.delete(target) - return - } - const cbIndex = cbs.indexOf(callback) - if (cbIndex !== -1) cbs.splice(cbIndex, 1) - callbacks.set(target, cbs) - }, - } -} - -let _resizeObserver: ReturnType - -const getResizeObserver = () => - !_resizeObserver - ? (_resizeObserver = createResizeObserver()) - : _resizeObserver - -export type UseResizeObserverCallback = ( - entry: ResizeObserverEntry, - observer: ResizeObserver -) => unknown - -export const useSize = (target: React.RefObject) => { - const [size, setSize] = React.useState({ width: 0, height: 0 }) - React.useLayoutEffect(() => { - if (target.current) { - const { width, height } = target.current.getBoundingClientRect() - setSize({ width, height }) - } - }, [target.current]) - - const resizeCallback = React.useCallback( - (entry: ResizeObserverEntry) => setSize(entry.contentRect), - [] - ) - // Where the magic happens - useResizeObserver(target, resizeCallback) - return size -} diff --git a/src/frontend/src/features/rooms/livekit/prefabs/ControlBar/MoreOptions.tsx b/src/frontend/src/features/rooms/livekit/prefabs/ControlBar/MoreOptions.tsx index 811b3a80..b85305a5 100644 --- a/src/frontend/src/features/rooms/livekit/prefabs/ControlBar/MoreOptions.tsx +++ b/src/frontend/src/features/rooms/livekit/prefabs/ControlBar/MoreOptions.tsx @@ -4,13 +4,13 @@ import { ParticipantsToggle } from '../../components/controls/Participants/Parti import { ToolsToggle } from '../../components/controls/ToolsToggle' import { InfoToggle } from '../../components/controls/InfoToggle' import { AdminToggle } from '../../components/AdminToggle' -import { useSize } from '../../hooks/useResizeObserver' import { useState, RefObject } from 'react' import { Dialog, DialogTrigger, Popover } from 'react-aria-components' import { Button } from '@/primitives' import { ToggleButtonProps } from '@/primitives/ToggleButton' import { RiArrowDownSLine, RiArrowUpSLine } from '@remixicon/react' import { useTranslation } from 'react-i18next' +import useSize from '@react-hook/size' const CONTROL_BAR_BREAKPOINT = 1100 @@ -70,7 +70,7 @@ export const MoreOptions = ({ }: { parentElement: RefObject }) => { - const { width: parentWidth } = useSize(parentElement) + const parentWidth = useSize(parentElement)[0] return (