tangled
alpha
login
or
join now
stream.place
/
streamplace
74
fork
atom
Live video on the AT Protocol
74
fork
atom
overview
issues
1
pulls
pipelines
prelive: blue circles rather than green
Eli Mallon
3 weeks ago
5f07a1df
3d308438
+29
-13
4 changed files
expand all
collapse all
unified
split
js
app
components
live-dashboard
bento-grid.tsx
stream-monitor.tsx
components
src
components
dashboard
header.tsx
information-widget.tsx
+6
-2
js/app/components/live-dashboard/bento-grid.tsx
···
3
3
borders,
4
4
Button,
5
5
Dashboard,
6
6
+
useLivestream,
6
7
useLivestreamStore,
7
8
usePlayerStore,
8
9
useProfile,
···
81
82
const ingestConnectionState = usePlayerStore((x) => x.ingestConnectionState);
82
83
const ingestStarted = usePlayerStore((x) => x.ingestStarted);
83
84
const emojiData = useEmojiData();
85
85
+
const livestream = useLivestream();
84
86
85
87
// Calculate derived values
86
88
const isConnected = ingestConnectionState === "connected";
···
112
114
| "excellent"
113
115
| "good"
114
116
| "poor"
115
115
-
| "offline" => {
117
117
+
| "offline"
118
118
+
| "pre-live" => {
116
119
if (!isLive) return "offline";
120
120
+
if (!livestream) return "pre-live";
117
121
switch (segmentTiming.connectionQuality) {
118
122
case "good":
119
123
return "excellent";
···
124
128
default:
125
129
return "offline";
126
130
}
127
127
-
}, [isLive, segmentTiming.connectionQuality]);
131
131
+
}, [isLive, livestream, segmentTiming.connectionQuality]);
128
132
129
133
// Calculate messages per minute
130
134
const messagesPerMinute = useMemo((): number => {
+2
js/app/components/live-dashboard/stream-monitor.tsx
···
61
61
// Connection quality indicator
62
62
const getConnectionIcon = () => {
63
63
if (!isLive) return null;
64
64
+
if (!ls) return <Wifi size={16} color="#3b82f6" />;
64
65
65
66
switch (segmentTiming.connectionQuality) {
66
67
case "good":
···
76
77
77
78
const getConnectionColor = () => {
78
79
if (!isLive) return "red";
80
80
+
if (!ls) return "blue";
79
81
80
82
switch (segmentTiming.connectionQuality) {
81
83
case "good":
+6
-2
js/components/src/components/dashboard/header.tsx
···
36
36
}
37
37
38
38
interface StatusIndicatorProps {
39
39
-
status: "excellent" | "good" | "poor" | "offline";
39
39
+
status: "excellent" | "good" | "poor" | "offline" | "pre-live";
40
40
isLive: boolean;
41
41
}
42
42
···
44
44
const getStatusColor = () => {
45
45
if (!isLive) return bg.gray[500];
46
46
switch (status) {
47
47
+
case "pre-live":
48
48
+
return bg.blue[500];
47
49
case "excellent":
48
50
return bg.green[500];
49
51
case "good":
···
60
62
const getStatusText = () => {
61
63
if (!isLive) return "OFFLINE";
62
64
switch (status) {
65
65
+
case "pre-live":
66
66
+
return "NOT LIVE";
63
67
case "excellent":
64
68
return "EXCELLENT";
65
69
case "good":
···
101
105
uptime?: string;
102
106
bitrate?: string;
103
107
timeBetweenSegments?: number;
104
104
-
connectionStatus?: "excellent" | "good" | "poor" | "offline";
108
108
+
connectionStatus?: "excellent" | "good" | "poor" | "offline" | "pre-live";
105
109
problemsCount?: number;
106
110
onProblemsPress?: () => void;
107
111
}
+15
-9
js/components/src/components/dashboard/information-widget.tsx
···
13
13
import { LayoutChangeEvent, Text, TouchableOpacity, View } from "react-native";
14
14
import Svg, { Path, Line as SvgLine, Text as SvgText } from "react-native-svg";
15
15
import { useAQState } from "../../hooks";
16
16
-
import {
17
17
-
useLivestreamStore,
18
18
-
useSegment,
19
19
-
useViewers,
20
20
-
} from "../../livestream-store";
16
16
+
import { useLivestream, useSegment, useViewers } from "../../livestream-store";
21
17
import * as zero from "../../ui";
22
18
import { InfoBox, InfoRow } from "../ui";
23
19
···
50
46
const isCompactHeight = layoutMeasured && componentHeight < 350;
51
47
52
48
const seg = useSegment();
53
53
-
const livestream = useLivestreamStore((x) => x.livestream);
49
49
+
const livestream = useLivestream();
54
50
const viewers = useViewers();
55
51
56
52
const getBitrate = useCallback((): number => {
···
173
169
width: 8,
174
170
height: 8,
175
171
borderRadius: 4,
176
176
-
backgroundColor:
177
177
-
getConnectionStatus() === "good"
172
172
+
backgroundColor: !livestream
173
173
+
? "#3b82f6"
174
174
+
: getConnectionStatus() === "good"
178
175
? "#22c55e"
179
176
: getConnectionStatus() === "warning"
180
177
? "#f59e0b"
···
182
179
},
183
180
]}
184
181
/>
182
182
+
{!livestream && (
183
183
+
<Text style={[text.blue[400], { fontSize: 13, fontWeight: "600" }]}>
184
184
+
(not live)
185
185
+
</Text>
186
186
+
)}
185
187
</View>
186
188
<TouchableOpacity
187
189
onPress={() => setShowViewers(!showViewers)}
···
315
317
data={bitrateHistory}
316
318
width={componentWidth - 40}
317
319
height={120}
320
320
+
color={livestream ? "#22c55e" : "#3b82f6"}
318
321
/>
319
322
</View>
320
323
)}
···
395
398
data={bitrateHistory}
396
399
width={componentWidth - 40}
397
400
height={isCompactHeight ? 80 : 120}
401
401
+
color={livestream ? "#22c55e" : "#3b82f6"}
398
402
/>
399
403
</View>
400
404
)}
···
432
436
data,
433
437
width,
434
438
height,
439
439
+
color = "#22c55e",
435
440
}: {
436
441
data: number[];
437
442
width: number;
438
443
height: number;
444
444
+
color?: string;
439
445
}) {
440
446
const maxDataValue = Math.max(...data, 1);
441
447
const minDataValue = Math.min(...data);
···
515
521
</SvgText>
516
522
<Path
517
523
d={pathData}
518
518
-
stroke="#22c55e"
524
524
+
stroke={color}
519
525
strokeWidth="2"
520
526
fill="none"
521
527
strokeLinecap="round"