Live video on the AT Protocol

app: fix a couple places that were causing excessive rerenders

+22 -30
+1 -9
js/app/components/mobile/ui.tsx
··· 10 10 useKeyboardSlide, 11 11 useLivestreamInfo, 12 12 usePlayerDimensions, 13 - useSegmentTiming as useSegmentMetrics, 14 13 View, 15 14 } from "@streamplace/components"; 16 15 import { bottom, zIndex } from "@streamplace/components/src/lib/theme/atoms"; ··· 38 37 } = useLivestreamInfo(); 39 38 const { width, height, isPlayerRatioGreater } = usePlayerDimensions(); 40 39 const { slideKeyboard } = useKeyboardSlide(); 41 - const { connectionQuality, segmentDeltas, mean, range } = useSegmentMetrics(); 42 40 const { doSetIngestCamera } = useCameraToggle(); 43 41 const avatars = useAvatars(profile?.did ? [profile?.did] : []); 44 42 ··· 133 131 layout.flex.center, 134 132 ]} 135 133 > 136 - <PlayerUI.MetricsPanel 137 - connectionQuality={connectionQuality} 138 - showMetrics={isLive || isSelfAndNotLive} 139 - segmentDeltas={segmentDeltas} 140 - mean={mean || 999} 141 - range={range || 999} 142 - /> 134 + <PlayerUI.MetricsPanel showMetrics={isLive || isSelfAndNotLive} /> 143 135 </View> 144 136 )} 145 137 {isSelfAndNotLive ? (
+12 -6
js/app/features/streamplace/streamplaceProvider.tsx
··· 2 2 import { createContext, useEffect } from "react"; 3 3 import { useAppDispatch, useAppSelector } from "store/hooks"; 4 4 import { Text, View } from "tamagui"; 5 - import { DEFAULT_URL, initialize, selectStreamplace } from "./streamplaceSlice"; 5 + import { 6 + DEFAULT_URL, 7 + initialize, 8 + selectInitialized, 9 + selectUrl, 10 + } from "./streamplaceSlice"; 6 11 7 12 export const StreamplaceContext = createContext({ 8 13 url: DEFAULT_URL, ··· 13 18 }: { 14 19 children: React.ReactNode; 15 20 }): React.ReactElement { 16 - const streamplace = useAppSelector(selectStreamplace); 21 + const url = useAppSelector(selectUrl); 22 + const initialized = useAppSelector(selectInitialized); 17 23 const dispatch = useAppDispatch(); 18 24 useEffect(() => { 19 - if (!streamplace.initialized) { 25 + if (!initialized) { 20 26 dispatch(initialize()); 21 27 } 22 - }, [streamplace.initialized]); 23 - if (!streamplace.initialized) { 28 + }, [initialized]); 29 + if (!initialized) { 24 30 return ( 25 31 <View f={1}> 26 32 <Text>StreamplaceProvider loading...</Text> ··· 29 35 ); 30 36 } 31 37 return ( 32 - <StreamplaceContext.Provider value={{ url: streamplace.url }}> 38 + <StreamplaceContext.Provider value={{ url: url }}> 33 39 {children} 34 40 </StreamplaceContext.Provider> 35 41 );
+2
js/app/features/streamplace/streamplaceSlice.tsx
··· 222 222 selectors: { 223 223 selectStreamplace: (streamplace) => streamplace, 224 224 selectUrl: (streamplace) => streamplace.url, 225 + selectInitialized: (streamplace) => streamplace.initialized, 225 226 selectRecentSegments: (streamplace) => streamplace.recentSegments, 226 227 selectMySegments: (streamplace) => streamplace.mySegments, 227 228 selectUserMuted: (streamplace) => streamplace.userMuted, ··· 244 245 selectUserMuted, 245 246 selectChatWarned, 246 247 selectUrl, 248 + selectInitialized, 247 249 } = streamplaceSlice.selectors;
+2 -1
js/app/src/router.tsx
··· 554 554 options={{ 555 555 headerTitle: "Go Live", 556 556 drawerItemStyle: isNative ? undefined : { display: "none" }, 557 + drawerLabel: () => <Text>Go Live</Text>, 557 558 title: "Go live", 558 559 drawerIcon: () => <Video />, 559 560 headerShown: false, 560 561 }} 561 562 /> 562 563 </Drawer.Navigator> 563 - {livePopup && ( 564 + {isWeb && livePopup && ( 564 565 <Popup 565 566 onPress={() => { 566 567 navigation.navigate("LiveDashboard");
+1 -2
js/components/src/components/chat/chat-box.tsx
··· 81 81 createChatMessage({ 82 82 text: message, 83 83 reply: replyTo || undefined, 84 - }).then(() => { 85 - setSubmitting(false); 86 84 }); 85 + setSubmitting(false); 87 86 }; 88 87 useEffect(() => { 89 88 if (replyTo && textAreaRef.current) {
+4 -11
js/components/src/components/mobile-player/ui/metrics.tsx
··· 1 1 import { AlertCircle, CircleCheck, CircleX } from "lucide-react-native"; 2 + import { useSegmentTiming } from "../../../hooks/useSegmentTiming"; 2 3 import * as atoms from "../../../lib/theme/atoms"; 3 4 import { Text, View } from "../../ui"; 4 5 5 6 type MetricsPanelProps = { 6 - connectionQuality: "good" | "degraded" | "bad" | string; 7 7 showMetrics: boolean; 8 - segmentDeltas: number[]; 9 - mean: number; 10 - range: number; 11 8 }; 12 9 13 - export function MetricsPanel({ 14 - connectionQuality, 15 - showMetrics, 16 - segmentDeltas, 17 - mean, 18 - range, 19 - }: MetricsPanelProps) { 10 + export function MetricsPanel({ showMetrics }: MetricsPanelProps) { 11 + const { connectionQuality, segmentDeltas, mean, range } = useSegmentTiming(); 12 + 20 13 let icon = <CircleX color="#d44" />; 21 14 let color = "#d44"; 22 15 if (connectionQuality === "good") {
-1
pkg/bus/segchanman.go
··· 107 107 } 108 108 curBuf = append(curBuf, seg) 109 109 if len(curBuf) > bufSize { 110 - log.Warn(ctx, "segment buffer is too large, dropping oldest segment", "user", user, "rendition", rendition, "bufSize", bufSize, "len", len(curBuf)) 111 110 curBuf = curBuf[1:] 112 111 } 113 112 b.segBuf[key] = curBuf