kasunk99-livestream-core 0.3.7 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveStreamViewerItem.d.ts","sourceRoot":"","sources":["../../src/components/LiveStreamViewerItem.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqD,MAAM,OAAO,CAAC;AAe1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAK/C,KAAK,yBAAyB,GAAG;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAgBF;;GAEG;AACH,eAAO,MAAM,oBAAoB,
|
|
1
|
+
{"version":3,"file":"LiveStreamViewerItem.d.ts","sourceRoot":"","sources":["../../src/components/LiveStreamViewerItem.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqD,MAAM,OAAO,CAAC;AAe1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAK/C,KAAK,yBAAyB,GAAG;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAgBF;;GAEG;AACH,eAAO,MAAM,oBAAoB,uDAgX/B,CAAC"}
|
|
@@ -17,7 +17,7 @@ catch {
|
|
|
17
17
|
*/
|
|
18
18
|
export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream, isActive, index, }) {
|
|
19
19
|
const roomId = isActive ? stream.roomId : null;
|
|
20
|
-
const { joined, joining, error, producerList, roomState, remoteStream, remoteVideoStream, webrtcUnavailable, consumeError, socket, viewerCount: liveViewerCount, } = useViewerSocket(roomId);
|
|
20
|
+
const { joined, joining, error, producerList, roomState, remoteStream, remoteVideoStream, webrtcUnavailable, consumeError, socket, viewerCount: liveViewerCount, streamEnded, } = useViewerSocket(roomId);
|
|
21
21
|
// Prefer real-time socket count (updated on every join/leave event);
|
|
22
22
|
// fall back to the HTTP-polled value from the feed for the initial render.
|
|
23
23
|
const viewerCount = liveViewerCount > 0 ? liveViewerCount : stream.viewerCount;
|
|
@@ -220,13 +220,24 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
|
|
|
220
220
|
return (_jsxs(View, { style: styles.container, children: [showVideo && RTCViewComponent && streamURL ? (_jsx(RTCViewComponent, { streamURL: streamURL, stream: displayStream, style: styles.rtcView, objectFit: "cover", mirror: false }, `rtc-${trackCount}-${streamURL}`)) : (_jsxs(View, { style: styles.videoPlaceholder, children: [joining && _jsx(Text, { style: styles.placeholderText, children: "Joining..." }), error && _jsx(Text, { style: [styles.placeholderText, styles.errorText], children: error }), joined && !joining && !error && (_jsxs(_Fragment, { children: [_jsx(Text, { style: styles.liveBadge, children: "LIVE" }), _jsx(Text, { style: styles.placeholderText, children: stream.roomId }), hasVideo && webrtcUnavailable && (_jsxs(Text, { style: [styles.hintText, styles.warnText], children: ["Video needs a development build.", '\n', "Run: npx expo run:android"] })), hasVideo && consumeError && (_jsx(Text, { style: [styles.hintText, styles.errorText], children: consumeError })), hasVideo && !remoteStream && !webrtcUnavailable && !consumeError && (_jsx(Text, { style: styles.hintText, children: "Loading video..." })), !hasVideo && joined && producerList.length === 0 && (_jsxs(Text, { style: [styles.hintText, styles.warnText], children: ["Host has not started camera yet.", '\n', "Use a web client to stream, or wait for host to produce."] })), hasVideo && remoteStream && !RTCViewComponent && (_jsx(Text, { style: styles.hintText, children: "Video received (use dev build to see it)" })), hasVideo && remoteStream && RTCViewComponent && !hasVideoTrack && (_jsx(Text, { style: [styles.hintText, styles.warnText], children: "Audio only (no video track yet)" })), hasVideo && remoteStream && hasVideoTrack && !streamURL && (_jsx(Text, { style: [styles.hintText, styles.warnText], children: "Video track received but cannot display (need dev build with react-native-webrtc)" }))] }))] })), _jsxs(View, { style: styles.overlay, children: [_jsxs(View, { style: styles.topMeta, children: [_jsx(View, { style: styles.row, children: _jsxs(Text, { style: styles.roomId, numberOfLines: 1, children: ["LIVE \u00B7 ", stream.hostDisplayName || stream.title || stream.roomId] }) }), _jsx(View, { style: styles.stats, children: _jsxs(Text, { style: styles.statsText, children: ["\uD83D\uDC41 ", viewerCount] }) })] }), _jsx(Animated.View, { style: [styles.chatPanel, { bottom: chatBottomAnim }], pointerEvents: "box-none", children: _jsxs(ScrollView, { scrollEnabled: false, keyboardShouldPersistTaps: "always", contentContainerStyle: styles.chatStack, children: [_jsx(View, { style: styles.chatListWrapper, children: _jsx(ScrollView, { ref: chatListRef, contentContainerStyle: styles.chatListContent, showsVerticalScrollIndicator: false, keyboardDismissMode: "on-drag", keyboardShouldPersistTaps: "always", nestedScrollEnabled: true, children: chatData.map((item) => (_jsxs(Text, { style: styles.chatLine, numberOfLines: 2, children: [_jsx(Text, { style: [styles.chatName, { color: getNameColor(item.displayName) }], children: item.displayName }), _jsxs(Text, { style: styles.chatText, children: [" ", item.text] })] }, item.id))) }) }), _jsxs(View, { style: styles.chatInputRow, children: [_jsx(TextInput, { style: styles.chatInput, value: chatInput, onChangeText: setChatInput, placeholder: joined ? 'Say something...' : 'Connecting...', placeholderTextColor: "#9ca3af", editable: joined && !joining && !error, onSubmitEditing: sendChat, returnKeyType: "send" }), _jsx(TouchableOpacity, { onPress: sendChat, disabled: !joined || joining || !!error || !chatInput.trim(), style: [
|
|
221
221
|
styles.chatSendButton,
|
|
222
222
|
(!joined || joining || !!error || !chatInput.trim()) && styles.chatSendButtonDisabled,
|
|
223
|
-
], children: _jsx(Text, { style: styles.chatSendText, children: "\u27A4" }) })] })] }) })] })] }));
|
|
223
|
+
], children: _jsx(Text, { style: styles.chatSendText, children: "\u27A4" }) })] })] }) })] }), streamEnded && (_jsx(View, { style: styles.streamEndedOverlay, children: _jsx(Text, { style: styles.streamEndedText, children: "Live stream ended" }) }))] }));
|
|
224
224
|
});
|
|
225
225
|
const styles = StyleSheet.create({
|
|
226
226
|
container: {
|
|
227
227
|
flex: 1,
|
|
228
228
|
backgroundColor: '#0a0a0a',
|
|
229
229
|
},
|
|
230
|
+
streamEndedOverlay: {
|
|
231
|
+
...StyleSheet.absoluteFillObject,
|
|
232
|
+
backgroundColor: 'rgba(0,0,0,0.82)',
|
|
233
|
+
alignItems: 'center',
|
|
234
|
+
justifyContent: 'center',
|
|
235
|
+
},
|
|
236
|
+
streamEndedText: {
|
|
237
|
+
color: '#ffffff',
|
|
238
|
+
fontSize: 20,
|
|
239
|
+
fontWeight: '600',
|
|
240
|
+
},
|
|
230
241
|
rtcView: {
|
|
231
242
|
...StyleSheet.absoluteFillObject,
|
|
232
243
|
backgroundColor: '#0a0a0a',
|
|
@@ -15,6 +15,8 @@ type ViewerSocketState = {
|
|
|
15
15
|
consumeRetryKey: number;
|
|
16
16
|
/** Real-time viewer count, updated via viewer-count-updated socket event. */
|
|
17
17
|
viewerCount: number;
|
|
18
|
+
/** True once the host disconnects — triggers the "stream ended" overlay. */
|
|
19
|
+
streamEnded: boolean;
|
|
18
20
|
};
|
|
19
21
|
/**
|
|
20
22
|
* Connects to the livestream signaling server and joins a room as viewer.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useViewerSocket.d.ts","sourceRoot":"","sources":["../../src/hooks/useViewerSocket.ts"],"names":[],"mappings":"AACA,OAAO,EAAM,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7D,KAAK,iBAAiB,GAAG;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,6EAA6E;IAC7E,eAAe,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"useViewerSocket.d.ts","sourceRoot":"","sources":["../../src/hooks/useViewerSocket.ts"],"names":[],"mappings":"AACA,OAAO,EAAM,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7D,KAAK,iBAAiB,GAAG;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,6EAA6E;IAC7E,eAAe,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB,GAAG;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CA2gBpG"}
|
|
@@ -21,6 +21,7 @@ export function useViewerSocket(roomId) {
|
|
|
21
21
|
consumeError: null,
|
|
22
22
|
consumeRetryKey: 0,
|
|
23
23
|
viewerCount: 0,
|
|
24
|
+
streamEnded: false,
|
|
24
25
|
});
|
|
25
26
|
const socketRef = useRef(null);
|
|
26
27
|
const roomIdRef = useRef(roomId);
|
|
@@ -62,6 +63,7 @@ export function useViewerSocket(roomId) {
|
|
|
62
63
|
consumeError: null,
|
|
63
64
|
consumeRetryKey: 0,
|
|
64
65
|
viewerCount: 0,
|
|
66
|
+
streamEnded: false,
|
|
65
67
|
});
|
|
66
68
|
consumeRetryCountRef.current = 0;
|
|
67
69
|
}, []);
|
|
@@ -87,7 +89,7 @@ export function useViewerSocket(roomId) {
|
|
|
87
89
|
setState((prev) => ({ ...prev, error: 'Livestream server not configured', joining: false }));
|
|
88
90
|
return;
|
|
89
91
|
}
|
|
90
|
-
setState((prev) => ({ ...prev, joining: true, error: null }));
|
|
92
|
+
setState((prev) => ({ ...prev, joining: true, error: null, streamEnded: false }));
|
|
91
93
|
const JOIN_TIMEOUT_MS = 12000;
|
|
92
94
|
const joinTimeout = setTimeout(() => {
|
|
93
95
|
if (roomIdRef.current !== roomId)
|
|
@@ -201,6 +203,11 @@ export function useViewerSocket(roomId) {
|
|
|
201
203
|
setState((prev) => ({ ...prev, viewerCount: payload.viewerCount }));
|
|
202
204
|
}
|
|
203
205
|
});
|
|
206
|
+
socket.on('peer-left', (payload) => {
|
|
207
|
+
if (payload?.isHost) {
|
|
208
|
+
setState((prev) => ({ ...prev, streamEnded: true, producerList: [] }));
|
|
209
|
+
}
|
|
210
|
+
});
|
|
204
211
|
socket.on('system-audio-chunk', (payload) => {
|
|
205
212
|
const b64 = typeof payload === 'string' ? payload
|
|
206
213
|
: typeof payload?.data === 'string'
|
package/package.json
CHANGED