From 5ce61d06b251c84c8d65f78df6e664a5a7241c28 Mon Sep 17 00:00:00 2001 From: talksik Date: Thu, 12 Mar 2026 10:07:56 -0700 Subject: [PATCH] refactor: prevent unnecessary prop drilling Components throughout the tree are anyways utilizing the zustand store, so there is no reason to send props for the same store methods. --- .../features/playback/media-particle-view.tsx | 7 +++-- .../features/playback/particle-renderer.tsx | 26 +++++++++++++------ js/src/pages/stream-player-page.tsx | 6 +---- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/js/src/features/playback/media-particle-view.tsx b/js/src/features/playback/media-particle-view.tsx index 4153333..26efe40 100644 --- a/js/src/features/playback/media-particle-view.tsx +++ b/js/src/features/playback/media-particle-view.tsx @@ -8,7 +8,6 @@ import { useAudioSource } from "@/components/audio/use-audio-source"; interface MediaParticleViewProps { particle: StreamParticle; - onEnded: () => void; } function formatTime(ms: number): string { @@ -36,12 +35,12 @@ function DurationPill({ export function MediaParticleView({ particle, - onEnded, }: MediaParticleViewProps) { const cachedUrl = usePlaybackStore( (s) => s.downloadUrlCache[particle.id], ); const cacheDownloadUrl = usePlaybackStore((s) => s.cacheDownloadUrl); + const next = usePlaybackStore((s) => s.next); const paused = usePlaybackStore((s) => s.paused); const [error, setError] = useState(null); @@ -113,7 +112,7 @@ export function MediaParticleView({ crossOrigin="anonymous" src={cachedUrl} autoPlay - onEnded={onEnded} + onEnded={next} onTimeUpdate={(e) => { setCurrentTimeMs(e.currentTarget.currentTime * 1000); }} @@ -134,7 +133,7 @@ export function MediaParticleView({ src={cachedUrl} autoPlay playsInline - onEnded={onEnded} + onEnded={next} onTimeUpdate={(e) => { setCurrentTimeMs(e.currentTarget.currentTime * 1000); }} diff --git a/js/src/features/playback/particle-renderer.tsx b/js/src/features/playback/particle-renderer.tsx index d7b9568..93cb6f9 100644 --- a/js/src/features/playback/particle-renderer.tsx +++ b/js/src/features/playback/particle-renderer.tsx @@ -10,15 +10,14 @@ import { AckButton } from "./ack-button"; interface ParticleRendererProps { particle: StreamParticle; - onNext: () => void; - onPrev: () => void; } export function ParticleRenderer({ particle, - onNext, - onPrev, }: ParticleRendererProps) { + const next = usePlaybackStore((s) => s.next); + const prev = usePlaybackStore((s) => s.prev); + const markParticlesSeen = useAppStore((s) => s.markParticlesSeen); const markedRef = useRef(null); @@ -33,15 +32,15 @@ export function ParticleRenderer({ const handleClick = (e: React.MouseEvent) => { const rect = e.currentTarget.getBoundingClientRect(); const x = (e.clientX - rect.left) / rect.width; - if (x < 0.3) onPrev(); - else if (x > 0.7) onNext(); + if (x < 0.3) prev(); + else if (x > 0.7) next(); }; const renderContent = () => { switch (particle.type) { case "media": - {/* NOTE: it's more robust to re-mount the MediaParticleView when the particle changes, to ensure playback state is well-behaved */} - return ; + {/* NOTE: it's more robust to re-mount the MediaParticleView when the particle changes, to ensure playback state is well-behaved */ } + return ; case "text": return ; default: @@ -61,3 +60,14 @@ export function ParticleRenderer({ ); } + +function RenderParticle({ particle }: { particle: StreamParticle }) { + switch (particle.type) { + case "media": + return ; + case "text": + return ; + default: + return ; + } +} diff --git a/js/src/pages/stream-player-page.tsx b/js/src/pages/stream-player-page.tsx index 43833a5..e55b516 100644 --- a/js/src/pages/stream-player-page.tsx +++ b/js/src/pages/stream-player-page.tsx @@ -211,11 +211,7 @@ export function StreamPlayerPage() { {/* Particle content — fills entire viewport */}
{currentParticle && ( - + )}