Live video on the AT Protocol
79
fork

Configure Feed

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

Merge pull request #619 from BarryCarlyon/barry/tunestreamkey

feat: make the Stream Key display less suck

authored by

Eli Mallon and committed by
GitHub
ac9e527b 2d0b4104

+155 -49
+155 -49
js/app/components/live-dashboard/stream-key.tsx
··· 1 - import { Body, Button, Code, Row, View } from "@streamplace/components"; 1 + import { 2 + Body, 3 + Button, 4 + Code, 5 + Row, 6 + View, 7 + useTheme, 8 + useToast, 9 + } from "@streamplace/components"; 2 10 import { Redirect } from "components/aqlink"; 3 11 import Loading from "components/loading/loading"; 4 12 import { ··· 7 15 selectIsReady, 8 16 selectUserProfile, 9 17 } from "features/bluesky/blueskySlice"; 18 + import { Clipboard, ClipboardCheck } from "lucide-react-native"; 10 19 import { useEffect, useState } from "react"; 20 + import { ScrollView, TextInput } from "react-native"; 11 21 import { useAppDispatch, useAppSelector } from "store/hooks"; 12 22 13 23 const FormRow = ({ children }: { children: React.ReactNode }) => { 14 24 return ( 15 - <Row fullWidth padding="lg" align="start"> 25 + <Row fullWidth align="start"> 16 26 {children} 17 27 </Row> 18 28 ); ··· 50 60 const url = useAppSelector((state) => state.streamplace.url); 51 61 52 62 return ( 53 - <View flex={1} centered fullWidth padding="lg"> 54 - <View fullWidth style={{ maxWidth: 600 }}> 55 - <FormRow> 56 - <Button 57 - variant={protocol !== "rtmp" ? "secondary" : "primary"} 58 - onPress={() => setProtocol("rtmp")} 59 - > 60 - RTMP 61 - </Button> 62 - <Button 63 - variant={protocol !== "whip" ? "secondary" : "primary"} 64 - onPress={() => setProtocol("whip")} 65 - > 66 - WHIP 67 - </Button> 68 - </FormRow> 69 - 70 - {protocol === "whip" && <WHIPDescription url={url} />} 71 - {protocol === "rtmp" && <RTMPDescription url={url} />} 72 - 73 - <FormRow> 74 - <Label>Output Settings</Label> 75 - <Content> 76 - <Body>Output mode: Advanced</Body> 77 - <Body> 78 - Keyframe Interval: <Code>1s</Code> 79 - </Body> 80 - <Body> 81 - x264 Options: <Code>bframes=0</Code> 82 - </Body> 83 - </Content> 84 - </FormRow> 63 + <ScrollView> 64 + <View flex={1} align="center" justify="start" padding="md" fullWidth> 65 + <View fullWidth style={{ maxWidth: 600 }}> 66 + <FormRow> 67 + <Button 68 + variant={protocol !== "rtmp" ? "secondary" : "primary"} 69 + onPress={() => setProtocol("rtmp")} 70 + style={{ 71 + borderTopRightRadius: "0px", 72 + borderBottomRightRadius: "0px", 73 + }} 74 + > 75 + RTMP 76 + </Button> 77 + <Button 78 + variant={protocol !== "whip" ? "secondary" : "primary"} 79 + onPress={() => setProtocol("whip")} 80 + style={{ 81 + borderTopLeftRadius: "0px", 82 + borderBottomLeftRadius: "0px", 83 + }} 84 + > 85 + WHIP 86 + </Button> 87 + </FormRow> 88 + {protocol === "whip" && <WHIPDescription url={url} />} 89 + {protocol === "rtmp" && <RTMPDescription url={url} />} 90 + <FormRow> 91 + <Label>Output Settings</Label> 92 + <Content> 93 + <Body> 94 + Output mode: Advanced 95 + <br /> 96 + Keyframe Interval: <Code>1s</Code> 97 + <br /> 98 + x264 Options: <Code>bframes=0</Code> 99 + </Body> 100 + </Content> 101 + </FormRow> 102 + </View> 85 103 </View> 86 - </View> 104 + </ScrollView> 87 105 ); 88 106 } 89 107 ··· 99 117 <FormRow> 100 118 <Label>Server</Label> 101 119 <Content> 102 - <Body>{url}</Body> 120 + <TextInput 121 + value={url} 122 + readOnly={true} 123 + style={[ 124 + { 125 + backgroundColor: "#1a1a1a", 126 + borderWidth: 1, 127 + borderColor: "#333", 128 + borderRadius: 8, 129 + padding: 12, 130 + color: "white", 131 + }, 132 + ]} 133 + /> 103 134 </Content> 104 135 </FormRow> 105 136 <FormRow> ··· 127 158 <FormRow> 128 159 <Label>Server</Label> 129 160 <Content> 130 - <Body>{rtmpUrl}</Body> 161 + <TextInput 162 + value={rtmpUrl} 163 + readOnly={true} 164 + style={[ 165 + { 166 + backgroundColor: "#1a1a1a", 167 + borderWidth: 1, 168 + borderColor: "#333", 169 + borderRadius: 8, 170 + padding: 12, 171 + color: "white", 172 + }, 173 + ]} 174 + /> 131 175 </Content> 132 176 </FormRow> 133 177 <FormRow> ··· 143 187 export default StreamKeyScreen; 144 188 145 189 export function StreamKey() { 190 + const theme = useTheme(); 191 + const toast = useToast(); 192 + 146 193 const dispatch = useAppDispatch(); 147 194 const [generating, setGenerating] = useState(false); 195 + const [hidekey, setHidekey] = useState(true); 196 + const [didcopy, setDidcopy] = useState(false); 148 197 const newKey = useAppSelector((state) => state.bluesky.newKey); 198 + 199 + let foregroundColor = theme.theme.colors.text || "#fff"; 149 200 150 201 useEffect(() => { 151 202 if (!newKey) { 152 203 return; 153 204 } 154 205 155 - (async () => { 156 - try { 157 - await navigator.clipboard.writeText(newKey.privateKey); 158 - // TODO: Replace with custom toast implementation 159 - console.log("Bearer token copied to clipboard"); 160 - } catch (e) { 161 - // not allowed. oh well. 162 - console.log("Could not copy to clipboard"); 163 - } 164 - })(); 165 - 166 206 return () => { 167 207 dispatch(clearStreamKeyRecord()); 168 208 }; 169 209 }, [newKey, dispatch]); 170 210 211 + const handleCopy = async () => { 212 + if (!newKey) { 213 + return; 214 + } 215 + 216 + try { 217 + await navigator.clipboard.writeText(newKey.privateKey); 218 + setDidcopy(true); 219 + 220 + toast.show("Stream Key", "Stream Key was copied to your clipboard", { 221 + duration: 4, 222 + }); 223 + } catch (e) { 224 + // not allowed. oh well. 225 + toast.show( 226 + "Stream Key", 227 + "Failed to copy the Stream Key to your clipboard", 228 + { duration: 4 }, 229 + ); 230 + } 231 + }; 232 + 171 233 if (generating) { 172 234 return <Loading />; 173 235 } 174 236 175 237 if (newKey) { 176 - return <Code>{newKey.privateKey}</Code>; 238 + return ( 239 + <Row fullWidth flex={1} align="start"> 240 + <TextInput 241 + value={newKey.privateKey} 242 + secureTextEntry={hidekey} 243 + readOnly={true} 244 + style={[ 245 + { 246 + backgroundColor: "#1a1a1a", 247 + borderWidth: 1, 248 + borderColor: "#333", 249 + borderRadius: 8, 250 + padding: 12, 251 + color: "white", 252 + flex: 1, 253 + borderTopRightRadius: "0px", 254 + borderBottomRightRadius: "0px", 255 + }, 256 + ]} 257 + onFocus={(e) => { 258 + setHidekey(false); 259 + }} 260 + onBlur={() => { 261 + setHidekey(true); 262 + }} 263 + selectTextOnFocus={true} 264 + /> 265 + <Button 266 + onPress={handleCopy} 267 + style={[ 268 + { 269 + borderTopLeftRadius: "0px", 270 + borderBottomLeftRadius: "0px", 271 + }, 272 + ]} 273 + > 274 + {didcopy ? ( 275 + <ClipboardCheck color={foregroundColor} size={24} /> 276 + ) : ( 277 + <Clipboard color={foregroundColor} size={24} /> 278 + )} 279 + </Button> 280 + </Row> 281 + ); 177 282 } 178 283 179 284 return ( ··· 181 286 onPress={async () => { 182 287 try { 183 288 setGenerating(true); 289 + setDidcopy(false); 184 290 await dispatch(createStreamKeyRecord({ store: false })); 185 291 } catch (e) { 186 292 console.error("failed to generate stream key", e);