kasunk99-livestream-core 0.3.14 → 0.3.15
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;
|
|
1
|
+
{"version":3,"file":"LiveStreamViewerItem.d.ts","sourceRoot":"","sources":["../../src/components/LiveStreamViewerItem.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqD,MAAM,OAAO,CAAC;AAmB1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAU/C,KAAK,yBAAyB,GAAG;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAwBF,eAAO,MAAM,oBAAoB,uDAka/B,CAAC"}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { memo, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
-
import { ActivityIndicator, Animated, Dimensions, Keyboard, NativeModules, Platform, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View, } from 'react-native';
|
|
3
|
+
import { ActivityIndicator, Animated, Dimensions, Keyboard, KeyboardAvoidingView, Modal, NativeModules, Platform, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View, } from 'react-native';
|
|
4
4
|
import { useViewerSocket } from '../hooks/useViewerSocket';
|
|
5
5
|
const SCREEN_HEIGHT = Dimensions.get('window').height;
|
|
6
6
|
const CHAT_NAME_COLORS = ['#f97316', '#22c55e', '#3b82f6', '#eab308', '#ec4899', '#a855f7'];
|
|
7
7
|
const BOTTOM_SAFE = Platform.OS === 'ios' ? 28 : 10;
|
|
8
8
|
const BOTTOM_BAR_H = 58;
|
|
9
9
|
const CHAT_BOTTOM_DEFAULT = BOTTOM_SAFE + BOTTOM_BAR_H + 8;
|
|
10
|
+
// Chat shifts up when keyboard is open — fixed offset works with both adjustResize + adjustNothing
|
|
11
|
+
const CHAT_BOTTOM_TYPING = BOTTOM_SAFE + 8;
|
|
10
12
|
let RTCViewComponent = null;
|
|
11
13
|
try {
|
|
12
14
|
const webrtc = require('react-native-webrtc');
|
|
@@ -18,11 +20,6 @@ try {
|
|
|
18
20
|
LinearGradient = require('expo-linear-gradient').LinearGradient;
|
|
19
21
|
}
|
|
20
22
|
catch { }
|
|
21
|
-
let RNKCEvents = null;
|
|
22
|
-
try {
|
|
23
|
-
RNKCEvents = require('react-native-keyboard-controller').KeyboardEvents;
|
|
24
|
-
}
|
|
25
|
-
catch { }
|
|
26
23
|
export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream, isActive, index: _index, }) {
|
|
27
24
|
const roomId = isActive ? stream.roomId : null;
|
|
28
25
|
const { joined, joining, error, producerList, roomState, remoteStream, remoteVideoStream, webrtcUnavailable, consumeError, socket, viewerCount: liveViewerCount, streamEnded, } = useViewerSocket(roomId);
|
|
@@ -45,27 +42,21 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
|
|
|
45
42
|
const seededRoomRef = useRef(null);
|
|
46
43
|
const chatListRef = useRef(null);
|
|
47
44
|
const textInputRef = useRef(null);
|
|
48
|
-
//
|
|
49
|
-
const inputBottom = useRef(new Animated.Value(0)).current;
|
|
45
|
+
// Animates chat column up/down when keyboard opens/closes
|
|
50
46
|
const chatAnim = useRef(new Animated.Value(CHAT_BOTTOM_DEFAULT)).current;
|
|
51
|
-
//
|
|
47
|
+
// Slide chat column based on typing state
|
|
52
48
|
useEffect(() => {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
// Standard Keyboard API fallback
|
|
65
|
-
const sub1 = Keyboard.addListener((Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow'), (e) => animate(e.endCoordinates.height, e.endCoordinates.height + 60, e.duration || 250));
|
|
66
|
-
const sub2 = Keyboard.addListener((Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide'), (e) => animate(0, CHAT_BOTTOM_DEFAULT, e.duration || 200, () => setIsTyping(false)));
|
|
67
|
-
return () => { sub1.remove(); sub2.remove(); };
|
|
68
|
-
}, [inputBottom, chatAnim]);
|
|
49
|
+
Animated.timing(chatAnim, {
|
|
50
|
+
toValue: isTyping ? CHAT_BOTTOM_TYPING : CHAT_BOTTOM_DEFAULT,
|
|
51
|
+
duration: 220,
|
|
52
|
+
useNativeDriver: false,
|
|
53
|
+
}).start();
|
|
54
|
+
}, [isTyping, chatAnim]);
|
|
55
|
+
// When keyboard fully hides, close the floating input modal
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const sub = Keyboard.addListener('keyboardDidHide', () => setIsTyping(false));
|
|
58
|
+
return () => sub.remove();
|
|
59
|
+
}, []);
|
|
69
60
|
const seededFromRoomState = useMemo(() => {
|
|
70
61
|
const chat = roomState && typeof roomState === 'object'
|
|
71
62
|
? roomState.chat
|
|
@@ -79,7 +70,6 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
|
|
|
79
70
|
setChatInput('');
|
|
80
71
|
setIsTyping(false);
|
|
81
72
|
Keyboard.dismiss();
|
|
82
|
-
inputBottom.setValue(0);
|
|
83
73
|
chatAnim.setValue(CHAT_BOTTOM_DEFAULT);
|
|
84
74
|
return;
|
|
85
75
|
}
|
|
@@ -88,7 +78,7 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
|
|
|
88
78
|
setChatMessages([]);
|
|
89
79
|
setChatInput('');
|
|
90
80
|
}
|
|
91
|
-
}, [roomId,
|
|
81
|
+
}, [roomId, chatAnim]);
|
|
92
82
|
useEffect(() => {
|
|
93
83
|
if (!roomId)
|
|
94
84
|
return;
|
|
@@ -211,8 +201,8 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
|
|
|
211
201
|
};
|
|
212
202
|
const hostLabel = stream.hostDisplayName || stream.title || 'Live';
|
|
213
203
|
const avatarLetter = hostLabel[0]?.toUpperCase() ?? 'L';
|
|
214
|
-
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(ActivityIndicator, { size: "large", color: "rgba(255,255,255,0.6)" }), !joining && error && _jsx(Text, { style: styles.placeholderError, children: error }), joined && !joining && !error && hasVideo && !remoteStream && (_jsx(ActivityIndicator, { size: "large", color: "rgba(255,255,255,0.45)" })), joined && !joining && !error && hasVideo && webrtcUnavailable && (_jsx(Text, { style: styles.placeholderHint, children: "Video needs a development build" })), joined && !joining && !error && hasVideo && consumeError && (_jsx(Text, { style: styles.placeholderError, children: consumeError }))] })), LinearGradient ? (_jsx(LinearGradient, { colors: ['rgba(0,0,0,0.70)', 'rgba(0,0,0,0.0)', 'rgba(0,0,0,0.0)', 'rgba(0,0,0,0.78)'], locations: [0, 0.28, 0.48, 1], style: styles.gradientOverlay, pointerEvents: "none" })) : null, _jsxs(View, { style: styles.topBar, pointerEvents: "box-none", children: [_jsxs(View, { style: styles.hostRow, pointerEvents: "none", children: [_jsx(View, { style: styles.avatar, children: _jsx(Text, { style: styles.avatarLetter, children: avatarLetter }) }), _jsx(Text, { style: styles.hostName, numberOfLines: 1, children: hostLabel }), _jsx(View, { style: styles.livePill, children: _jsx(Text, { style: styles.livePillText, children: "LIVE" }) })] }), _jsxs(View, { style: styles.viewerChip, pointerEvents: "none", children: [_jsx(Text, { style: styles.viewerEye, children: "\uD83D\uDC41" }), _jsx(Text, { style: styles.viewerCount, children: viewerCount > 0 ? viewerCount.toLocaleString() : '—' })] })] }), !isTyping && (_jsxs(View, { style: styles.rightColumn, pointerEvents: "box-none", children: [_jsxs(TouchableOpacity, { style: styles.actionBtn, activeOpacity: 0.75, children: [_jsx(View, { style: styles.actionCircle, children: _jsx(Text, { style: styles.actionIcon, children: "\u2665" }) }), _jsx(Text, { style: styles.actionLabel, children: "Like" })] }), _jsxs(TouchableOpacity, { style: styles.actionBtn, activeOpacity: 0.75, children: [_jsx(View, { style: styles.actionCircle, children: _jsx(Text, { style: styles.actionIcon, children: "\uD83C\uDF81" }) }), _jsx(Text, { style: styles.actionLabel, children: "Gift" })] }), _jsxs(TouchableOpacity, { style: styles.actionBtn, activeOpacity: 0.75, children: [_jsx(View, { style: styles.actionCircle, children: _jsx(Text, { style: styles.actionIcon, children: "\u2197" }) }), _jsx(Text, { style: styles.actionLabel, children: "Share" })] })] })), _jsx(Animated.View, { style: [styles.chatColumn, { bottom: chatAnim }], pointerEvents: "box-none", children: _jsx(ScrollView, { ref: chatListRef, contentContainerStyle: styles.chatContent, showsVerticalScrollIndicator: false, keyboardShouldPersistTaps: "always",
|
|
215
|
-
setIsTyping(true); }, activeOpacity: 0.7, children: _jsx(Text, { style: styles.typePlaceholder, children: joined && !joining ? 'Say something...' : 'Connecting...' }) }), _jsx(TouchableOpacity, { style: styles.bottomIconBtn, activeOpacity: 0.7, children: _jsx(Text, { style: styles.bottomIconGlyph, children: "\u263A" }) }), _jsx(TouchableOpacity, { style: styles.bottomIconBtn, activeOpacity: 0.7, children: _jsx(Text, { style: styles.bottomIconGlyph, children: "\uD83C\uDF39" }) }), _jsx(TouchableOpacity, { style: styles.bottomIconBtn, activeOpacity: 0.7, children: _jsx(Text, { style: styles.bottomIconGlyph, children: "\uD83C\uDF81" }) }), _jsx(TouchableOpacity, { style: styles.bottomIconBtn, activeOpacity: 0.7, children: _jsx(Text, { style: styles.bottomIconGlyph, children: "\u2197" }) })] })),
|
|
204
|
+
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(ActivityIndicator, { size: "large", color: "rgba(255,255,255,0.6)" }), !joining && error && _jsx(Text, { style: styles.placeholderError, children: error }), joined && !joining && !error && hasVideo && !remoteStream && (_jsx(ActivityIndicator, { size: "large", color: "rgba(255,255,255,0.45)" })), joined && !joining && !error && hasVideo && webrtcUnavailable && (_jsx(Text, { style: styles.placeholderHint, children: "Video needs a development build" })), joined && !joining && !error && hasVideo && consumeError && (_jsx(Text, { style: styles.placeholderError, children: consumeError }))] })), LinearGradient ? (_jsx(LinearGradient, { colors: ['rgba(0,0,0,0.70)', 'rgba(0,0,0,0.0)', 'rgba(0,0,0,0.0)', 'rgba(0,0,0,0.78)'], locations: [0, 0.28, 0.48, 1], style: styles.gradientOverlay, pointerEvents: "none" })) : null, _jsxs(View, { style: styles.topBar, pointerEvents: "box-none", children: [_jsxs(View, { style: styles.hostRow, pointerEvents: "none", children: [_jsx(View, { style: styles.avatar, children: _jsx(Text, { style: styles.avatarLetter, children: avatarLetter }) }), _jsx(Text, { style: styles.hostName, numberOfLines: 1, children: hostLabel }), _jsx(View, { style: styles.livePill, children: _jsx(Text, { style: styles.livePillText, children: "LIVE" }) })] }), _jsxs(View, { style: styles.viewerChip, pointerEvents: "none", children: [_jsx(Text, { style: styles.viewerEye, children: "\uD83D\uDC41" }), _jsx(Text, { style: styles.viewerCount, children: viewerCount > 0 ? viewerCount.toLocaleString() : '—' })] })] }), !isTyping && (_jsxs(View, { style: styles.rightColumn, pointerEvents: "box-none", children: [_jsxs(TouchableOpacity, { style: styles.actionBtn, activeOpacity: 0.75, children: [_jsx(View, { style: styles.actionCircle, children: _jsx(Text, { style: styles.actionIcon, children: "\u2665" }) }), _jsx(Text, { style: styles.actionLabel, children: "Like" })] }), _jsxs(TouchableOpacity, { style: styles.actionBtn, activeOpacity: 0.75, children: [_jsx(View, { style: styles.actionCircle, children: _jsx(Text, { style: styles.actionIcon, children: "\uD83C\uDF81" }) }), _jsx(Text, { style: styles.actionLabel, children: "Gift" })] }), _jsxs(TouchableOpacity, { style: styles.actionBtn, activeOpacity: 0.75, children: [_jsx(View, { style: styles.actionCircle, children: _jsx(Text, { style: styles.actionIcon, children: "\u2197" }) }), _jsx(Text, { style: styles.actionLabel, children: "Share" })] })] })), _jsx(Animated.View, { style: [styles.chatColumn, { bottom: chatAnim }], pointerEvents: "box-none", children: _jsx(ScrollView, { ref: chatListRef, contentContainerStyle: styles.chatContent, showsVerticalScrollIndicator: false, keyboardShouldPersistTaps: "always", children: chatData.map((item) => item.displayName ? (_jsx(View, { style: styles.chatBubble, children: _jsxs(Text, { style: styles.chatLine, numberOfLines: 3, children: [_jsxs(Text, { style: [styles.chatUsername, { color: getNameColor(item.displayName) }], children: [item.displayName, ' '] }), _jsx(Text, { style: styles.chatMsg, children: item.text })] }) }, item.id)) : (_jsx(View, { style: styles.joinBubble, children: _jsx(Text, { style: styles.joinText, children: item.text }) }, item.id))) }) }), !isTyping && (_jsxs(View, { style: styles.bottomBar, children: [_jsx(TouchableOpacity, { style: styles.typeTouchable, onPress: () => { if (joined && !joining && !error)
|
|
205
|
+
setIsTyping(true); }, activeOpacity: 0.7, children: _jsx(Text, { style: styles.typePlaceholder, children: joined && !joining ? 'Say something...' : 'Connecting...' }) }), _jsx(TouchableOpacity, { style: styles.bottomIconBtn, activeOpacity: 0.7, children: _jsx(Text, { style: styles.bottomIconGlyph, children: "\u263A" }) }), _jsx(TouchableOpacity, { style: styles.bottomIconBtn, activeOpacity: 0.7, children: _jsx(Text, { style: styles.bottomIconGlyph, children: "\uD83C\uDF39" }) }), _jsx(TouchableOpacity, { style: styles.bottomIconBtn, activeOpacity: 0.7, children: _jsx(Text, { style: styles.bottomIconGlyph, children: "\uD83C\uDF81" }) }), _jsx(TouchableOpacity, { style: styles.bottomIconBtn, activeOpacity: 0.7, children: _jsx(Text, { style: styles.bottomIconGlyph, children: "\u2197" }) })] })), streamEnded && (_jsx(View, { style: styles.endedOverlay, children: _jsxs(View, { style: styles.endedCard, children: [_jsx(Text, { style: styles.endedTitle, children: "Stream ended" }), _jsx(Text, { style: styles.endedSub, children: "The host has ended this live stream" })] }) })), _jsx(Modal, { visible: isTyping, transparent: true, animationType: "none", statusBarTranslucent: true, onRequestClose: () => Keyboard.dismiss(), children: _jsxs(KeyboardAvoidingView, { style: styles.inputModalKAV, behavior: Platform.OS === 'ios' ? 'padding' : 'height', children: [_jsx(TouchableOpacity, { style: styles.inputModalBackdrop, activeOpacity: 1, onPress: () => Keyboard.dismiss() }), _jsxs(View, { style: styles.floatingInputWrap, children: [_jsx(TextInput, { ref: textInputRef, style: styles.floatingTextInput, value: chatInput, onChangeText: setChatInput, placeholder: "Say something...", placeholderTextColor: "rgba(255,255,255,0.40)", editable: joined && !joining && !error, onSubmitEditing: sendChat, returnKeyType: "send", submitBehavior: "submit", autoFocus: true }), _jsx(TouchableOpacity, { style: [styles.floatingSendBtn, (!chatInput.trim() || !joined) && styles.sendBtnOff], onPress: sendChat, disabled: !joined || !chatInput.trim(), activeOpacity: 0.75, children: _jsx(Text, { style: styles.sendIcon, children: "\u25B6" }) })] })] }) })] }));
|
|
216
206
|
});
|
|
217
207
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
218
208
|
const styles = StyleSheet.create({
|
|
@@ -316,7 +306,7 @@ const styles = StyleSheet.create({
|
|
|
316
306
|
},
|
|
317
307
|
actionIcon: { fontSize: 22, color: '#fff' },
|
|
318
308
|
actionLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 11, fontWeight: '500' },
|
|
319
|
-
// Chat column — absolute, bottom
|
|
309
|
+
// Chat column — absolute, bottom animated between default and typing positions
|
|
320
310
|
chatColumn: {
|
|
321
311
|
position: 'absolute',
|
|
322
312
|
left: 12,
|
|
@@ -368,16 +358,20 @@ const styles = StyleSheet.create({
|
|
|
368
358
|
typePlaceholder: { color: 'rgba(255,255,255,0.50)', fontSize: 14 },
|
|
369
359
|
bottomIconBtn: { width: 40, height: 40, alignItems: 'center', justifyContent: 'center' },
|
|
370
360
|
bottomIconGlyph: { fontSize: 22 },
|
|
371
|
-
//
|
|
361
|
+
// Modal input overlay
|
|
362
|
+
inputModalKAV: {
|
|
363
|
+
flex: 1,
|
|
364
|
+
},
|
|
365
|
+
inputModalBackdrop: {
|
|
366
|
+
flex: 1,
|
|
367
|
+
},
|
|
372
368
|
floatingInputWrap: {
|
|
373
|
-
position: 'absolute',
|
|
374
|
-
left: 0,
|
|
375
|
-
right: 0,
|
|
376
369
|
flexDirection: 'row',
|
|
377
370
|
alignItems: 'center',
|
|
378
|
-
backgroundColor: 'rgba(20,20,20,0.
|
|
371
|
+
backgroundColor: 'rgba(20,20,20,0.97)',
|
|
379
372
|
paddingHorizontal: 12,
|
|
380
373
|
paddingVertical: 8,
|
|
374
|
+
paddingBottom: BOTTOM_SAFE,
|
|
381
375
|
gap: 8,
|
|
382
376
|
borderTopWidth: StyleSheet.hairlineWidth,
|
|
383
377
|
borderTopColor: 'rgba(255,255,255,0.10)',
|
package/package.json
CHANGED