import { useEffect } from 'react'; import { Text, View } from 'react-native'; import Animated, { Easing, useAnimatedStyle, useSharedValue, withRepeat, withTiming, } from 'react-native-reanimated'; import type { Human } from '@/api/types'; import { resolveHumanDisplay } from '@/lib/humans'; import type { ComposingUser } from '@/features/stream-view/stream-presence-context'; interface ComposingIndicatorProps { users: ComposingUser[]; networkHumans?: Human[]; } /** * Slim horizontal pill stack pinned just under the metadata header. Each * pill is the typing/recording indicator for a single user. Rendered above * the particle canvas with a translucent background so it reads on any * media. Mobile equivalent of desktop's vertical writing-mode indicator. */ export function ComposingIndicator({ users, networkHumans, }: ComposingIndicatorProps) { if (users.length === 0) return null; return ( {users.map((u) => { const { displayName } = resolveHumanDisplay(u.humanId, networkHumans); const label = u.mode === 'recording' ? `${displayName} is recording` : u.mode === 'screen' ? `${displayName} is sharing` : `${displayName} is typing`; return ( {label} ); })} ); } function BouncingDots() { return ( ); } function Dot({ delay }: { delay: number }) { const y = useSharedValue(0); useEffect(() => { const start = setTimeout(() => { y.value = withRepeat( withTiming(-3, { duration: 360, easing: Easing.inOut(Easing.quad), }), -1, true, ); }, delay); return () => clearTimeout(start); }, [delay, y]); const style = useAnimatedStyle(() => ({ transform: [{ translateY: y.value }], })); return ( ); }