Live video on the AT Protocol
79
fork

Configure Feed

Select the types of activity you want to include in your feed.

at iroh-stream 154 lines 4.5 kB view raw
1import { 2 Button, 3 ContentRights, 4 ContentWarnings, 5 formatHandle, 6 formatHandleWithAt, 7 layout, 8 PlayerUI, 9 ShareSheet, 10 Text, 11 useAvatars, 12 useDID, 13 useLivestreamInfo, 14 useLivestreamStore, 15 zero, 16} from "@streamplace/components"; 17import FollowButton from "components/follow-button"; 18import { ChevronLeft, ChevronRight } from "lucide-react-native"; 19import { Image, Linking, Pressable, View } from "react-native"; 20const { gap, px, py, colors } = zero; 21 22export function BottomMetadata({ 23 setShowChat, 24 showChat, 25}: { 26 setShowChat: (show: boolean) => void; 27 showChat: boolean; 28}) { 29 const { profile } = useLivestreamInfo(); 30 const avatars = useAvatars(profile?.did ? [profile?.did] : []); 31 const ls = useLivestreamStore((x) => x.livestream); 32 const segment = useLivestreamStore((x) => x.segment); 33 34 const did = useDID(); 35 36 // Get content warnings and rights directly from the latest segment 37 const contentWarnings = 38 (segment?.contentWarnings?.warnings as string[]) || []; 39 const contentRights = segment?.contentRights; 40 41 return ( 42 <View 43 style={[ 44 layout.position.relative, 45 { 46 backgroundColor: "rgba(0, 0, 0, 0.9)", 47 borderTopWidth: 1, 48 borderTopColor: "rgba(255, 255, 255, 0.1)", 49 }, 50 px[5], 51 py[3], 52 ]} 53 > 54 <View 55 style={[ 56 layout.flex.row, 57 layout.flex.spaceBetween, 58 { height: "100%", flex: "auto" as any }, 59 ]} 60 > 61 {/* Left side - Profile info */} 62 <View 63 style={[ 64 layout.flex.row, 65 layout.flex.center, 66 gap.all[3], 67 { flex: 1, minWidth: 0 }, 68 ]} 69 > 70 {profile?.did && avatars[profile?.did]?.avatar && ( 71 <Image 72 key="avatar" 73 source={{ 74 uri: avatars[profile?.did]?.avatar, 75 }} 76 style={{ width: 42, height: 42, borderRadius: 999 }} 77 resizeMode="cover" 78 /> 79 )} 80 {!(profile?.did && avatars[profile?.did]?.avatar) && ( 81 <Image 82 key="avatar" 83 source={require("./../../assets/images/goose.png")} 84 style={{ width: 42, height: 42, borderRadius: 999 }} 85 resizeMode="cover" 86 /> 87 )} 88 <View style={{ flex: 1, minWidth: 0 }}> 89 <View 90 style={[layout.flex.row, layout.flex.alignCenter, gap.all[2]]} 91 > 92 <Pressable 93 onPress={() => { 94 if (profile?.handle) { 95 const url = `https://bsky.app/profile/${formatHandle(profile)}`; 96 Linking.openURL(url); 97 } 98 }} 99 > 100 <Text style={{ color: "white", fontWeight: "600" }}> 101 {profile ? formatHandleWithAt(profile) : "@user"} 102 </Text> 103 </Pressable> 104 {did && profile && ( 105 <FollowButton streamerDID={profile?.did} currentUserDID={did} /> 106 )} 107 </View> 108 <Text 109 style={{ color: colors.gray[400] }} 110 numberOfLines={3} 111 ellipsizeMode="tail" 112 > 113 {ls?.record.title || "Stream Title"} 114 </Text> 115 </View> 116 </View> 117 118 {/* Right side - Viewer count and collapse chat */} 119 <View style={[layout.flex.row, layout.flex.align.center, gap.all[4]]}> 120 <PlayerUI.Viewers /> 121 <ShareSheet /> 122 <View> 123 <Button 124 variant="outline" 125 size="sm" 126 width="min" 127 style={{ aspectRatio: 1 }} 128 onPress={() => { 129 setShowChat(!showChat); 130 }} 131 > 132 {showChat ? ( 133 <ChevronRight color="white" size={16} /> 134 ) : ( 135 <ChevronLeft color="white" size={16} /> 136 )} 137 </Button> 138 </View> 139 </View> 140 </View> 141 142 {/* Content Metadata - Below the main profile/controls bar */} 143 {(contentWarnings.length > 0 || 144 (contentRights && Object.keys(contentRights).length > 0)) && ( 145 <View style={[py[2]]}> 146 <ContentWarnings warnings={contentWarnings} compact={true} /> 147 {contentRights && ( 148 <ContentRights contentRights={contentRights} compact={true} /> 149 )} 150 </View> 151 )} 152 </View> 153 ); 154}