From e04010257a275d575f2efa736ebe6069b5135798 Mon Sep 17 00:00:00 2001 From: Arjun Patel Date: Mon, 1 Jun 2026 17:13:41 -0700 Subject: [PATCH] idiomatic react --- js/desktop/src/hooks/use-stream-autoplay.ts | 86 +++++++++++---------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/js/desktop/src/hooks/use-stream-autoplay.ts b/js/desktop/src/hooks/use-stream-autoplay.ts index 1e3b5d0..8e24eef 100644 --- a/js/desktop/src/hooks/use-stream-autoplay.ts +++ b/js/desktop/src/hooks/use-stream-autoplay.ts @@ -1,4 +1,4 @@ -import { useEffect, useRef } from 'react'; +import { useEffect, useEffectEvent, useRef } from 'react'; import beepSound from '../../assets/sounds/beep.wav'; import type { Network, Particle, StreamProperties } from '@/api/types'; import { apiClient } from '@/api/client'; @@ -22,6 +22,49 @@ export function useStreamAutoplay( const userId = useAuthStore((s) => s.user?.id) ?? ''; const settledIdRef = useRef(undefined); + // The autoplay action reads the latest userId/network/stream/networkId at the + // moment a new child settles, without making any of them a reactive trigger — + // the only thing that should fire this is a change in the latest child's id. + const onNewLatestChild = useEffectEvent((child: Particle) => { + if (child.created_by_human_id === userId) return; + + if (useAutoplayStore.getState().muted) return; + + if (child.type === 'text') { + // Browser autoplay policy can block this before user interaction; that's + // fine — the beep is a nice-to-have, not a critical signal. + new Audio(beepSound) + .play() + .catch((err) => logError(err, { scope: 'autoplay.beep' })); + return; + } + + if (child.type !== 'media') return; + + const { displayName, initials } = resolveHumanDisplay( + child.created_by_human_id, + network?.humans, + ); + + apiClient + .getParticleDownloadUrl(child.properties.object_id) + .then((downloadUrl) => { + platform.autoplay.play({ + particleId: child.id, + streamId: streamParticle.id, + networkId, + downloadUrl, + mimeType: child.properties.mime_type, + durationMs: child.properties.duration_ms, + senderName: displayName, + senderInitials: initials, + }); + }) + .catch((err) => + logError(err, { scope: 'autoplay.fetchUrl', particleId: child.id }), + ); + }); + useEffect(() => { if (!latestChild) return; @@ -34,43 +77,6 @@ export function useStreamAutoplay( if (latestChild.id === settledIdRef.current) return; settledIdRef.current = latestChild.id; - if (latestChild.created_by_human_id === userId) return; - - if (useAutoplayStore.getState().muted) return; - - if (latestChild.type === 'text') { - // Browser autoplay policy can block this before user interaction; that's - // fine — the beep is a nice-to-have, not a critical signal. - new Audio(beepSound) - .play() - .catch((err) => logError(err, { scope: 'autoplay.beep' })); - return; - } - - if (latestChild.type !== 'media') return; - - const particle = latestChild; - const { displayName, initials } = resolveHumanDisplay( - particle.created_by_human_id, - network?.humans, - ); - - apiClient - .getParticleDownloadUrl(particle.properties.object_id) - .then((downloadUrl) => { - platform.autoplay.play({ - particleId: particle.id, - streamId: streamParticle.id, - networkId, - downloadUrl, - mimeType: particle.properties.mime_type, - durationMs: particle.properties.duration_ms, - senderName: displayName, - senderInitials: initials, - }); - }) - .catch((err) => - logError(err, { scope: 'autoplay.fetchUrl', particleId: particle.id }), - ); - }, [latestChild?.id]); // eslint-disable-line react-hooks/exhaustive-deps + onNewLatestChild(latestChild); + }, [latestChild]); }