kasunk99-livestream-core 0.3.12 → 0.3.14
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.
|
@@ -5,9 +5,6 @@ type LiveStreamViewerItemProps = {
|
|
|
5
5
|
isActive: boolean;
|
|
6
6
|
index: number;
|
|
7
7
|
};
|
|
8
|
-
/**
|
|
9
|
-
* Single full-screen viewer cell. When isActive, joins the stream room and consumes video/audio.
|
|
10
|
-
*/
|
|
11
8
|
export declare const LiveStreamViewerItem: React.NamedExoticComponent<LiveStreamViewerItemProps>;
|
|
12
9
|
export {};
|
|
13
10
|
//# sourceMappingURL=LiveStreamViewerItem.d.ts.map
|
|
@@ -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;AAiB1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAQ/C,KAAK,yBAAyB,GAAG;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAiCF,eAAO,MAAM,oBAAoB,uDA8Z/B,CAAC"}
|
|
@@ -4,28 +4,25 @@ import { ActivityIndicator, Animated, Dimensions, Keyboard, NativeModules, Platf
|
|
|
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
|
-
// iOS needs bottom safe-area padding; Android auto-adjusts via adjustResize
|
|
8
7
|
const BOTTOM_SAFE = Platform.OS === 'ios' ? 28 : 10;
|
|
8
|
+
const BOTTOM_BAR_H = 58;
|
|
9
|
+
const CHAT_BOTTOM_DEFAULT = BOTTOM_SAFE + BOTTOM_BAR_H + 8;
|
|
9
10
|
let RTCViewComponent = null;
|
|
10
11
|
try {
|
|
11
12
|
const webrtc = require('react-native-webrtc');
|
|
12
13
|
RTCViewComponent = webrtc.RTCView;
|
|
13
14
|
}
|
|
14
|
-
catch {
|
|
15
|
-
// react-native-webrtc not available in Expo Go
|
|
16
|
-
}
|
|
17
|
-
// expo-linear-gradient — lazy require so the package stays safe in non-Expo environments.
|
|
18
|
-
// The consuming app (social-casino-mobile) has expo-linear-gradient as a dependency.
|
|
15
|
+
catch { }
|
|
19
16
|
let LinearGradient = null;
|
|
20
17
|
try {
|
|
21
18
|
LinearGradient = require('expo-linear-gradient').LinearGradient;
|
|
22
19
|
}
|
|
23
|
-
catch {
|
|
24
|
-
|
|
20
|
+
catch { }
|
|
21
|
+
let RNKCEvents = null;
|
|
22
|
+
try {
|
|
23
|
+
RNKCEvents = require('react-native-keyboard-controller').KeyboardEvents;
|
|
25
24
|
}
|
|
26
|
-
|
|
27
|
-
* Single full-screen viewer cell. When isActive, joins the stream room and consumes video/audio.
|
|
28
|
-
*/
|
|
25
|
+
catch { }
|
|
29
26
|
export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream, isActive, index: _index, }) {
|
|
30
27
|
const roomId = isActive ? stream.roomId : null;
|
|
31
28
|
const { joined, joining, error, producerList, roomState, remoteStream, remoteVideoStream, webrtcUnavailable, consumeError, socket, viewerCount: liveViewerCount, streamEnded, } = useViewerSocket(roomId);
|
|
@@ -44,29 +41,31 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
|
|
|
44
41
|
const showVideo = RTCViewComponent && remoteStream && !!streamURL && hasVideoTrack;
|
|
45
42
|
const [chatInput, setChatInput] = useState('');
|
|
46
43
|
const [chatMessages, setChatMessages] = useState([]);
|
|
47
|
-
|
|
48
|
-
const kbOffset = useRef(new Animated.Value(0)).current;
|
|
49
|
-
useEffect(() => {
|
|
50
|
-
if (Platform.OS !== 'ios')
|
|
51
|
-
return;
|
|
52
|
-
const show = Keyboard.addListener('keyboardWillShow', (e) => {
|
|
53
|
-
Animated.timing(kbOffset, {
|
|
54
|
-
toValue: e.endCoordinates.height,
|
|
55
|
-
duration: e.duration ?? 260,
|
|
56
|
-
useNativeDriver: false,
|
|
57
|
-
}).start();
|
|
58
|
-
});
|
|
59
|
-
const hide = Keyboard.addListener('keyboardWillHide', (e) => {
|
|
60
|
-
Animated.timing(kbOffset, {
|
|
61
|
-
toValue: 0,
|
|
62
|
-
duration: e.duration ?? 220,
|
|
63
|
-
useNativeDriver: false,
|
|
64
|
-
}).start();
|
|
65
|
-
});
|
|
66
|
-
return () => { show.remove(); hide.remove(); };
|
|
67
|
-
}, [kbOffset]);
|
|
44
|
+
const [isTyping, setIsTyping] = useState(false);
|
|
68
45
|
const seededRoomRef = useRef(null);
|
|
69
46
|
const chatListRef = useRef(null);
|
|
47
|
+
const textInputRef = useRef(null);
|
|
48
|
+
// Keyboard-driven animated positions
|
|
49
|
+
const inputBottom = useRef(new Animated.Value(0)).current;
|
|
50
|
+
const chatAnim = useRef(new Animated.Value(CHAT_BOTTOM_DEFAULT)).current;
|
|
51
|
+
// Animate floating input and chat column with keyboard
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
const animate = (toInput, toChat, dur, onDone) => {
|
|
54
|
+
Animated.parallel([
|
|
55
|
+
Animated.timing(inputBottom, { toValue: toInput, duration: dur, useNativeDriver: false }),
|
|
56
|
+
Animated.timing(chatAnim, { toValue: toChat, duration: dur, useNativeDriver: false }),
|
|
57
|
+
]).start(() => onDone?.());
|
|
58
|
+
};
|
|
59
|
+
if (RNKCEvents) {
|
|
60
|
+
const s1 = RNKCEvents.addListener('keyboardWillShow', (e) => animate(e.height, e.height + 60, e.duration || 250));
|
|
61
|
+
const s2 = RNKCEvents.addListener('keyboardWillHide', (e) => animate(0, CHAT_BOTTOM_DEFAULT, e.duration || 250, () => setIsTyping(false)));
|
|
62
|
+
return () => { s1.remove(); s2.remove(); };
|
|
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]);
|
|
70
69
|
const seededFromRoomState = useMemo(() => {
|
|
71
70
|
const chat = roomState && typeof roomState === 'object'
|
|
72
71
|
? roomState.chat
|
|
@@ -78,6 +77,10 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
|
|
|
78
77
|
seededRoomRef.current = null;
|
|
79
78
|
setChatMessages([]);
|
|
80
79
|
setChatInput('');
|
|
80
|
+
setIsTyping(false);
|
|
81
|
+
Keyboard.dismiss();
|
|
82
|
+
inputBottom.setValue(0);
|
|
83
|
+
chatAnim.setValue(CHAT_BOTTOM_DEFAULT);
|
|
81
84
|
return;
|
|
82
85
|
}
|
|
83
86
|
if (seededRoomRef.current !== roomId) {
|
|
@@ -85,7 +88,7 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
|
|
|
85
88
|
setChatMessages([]);
|
|
86
89
|
setChatInput('');
|
|
87
90
|
}
|
|
88
|
-
}, [roomId]);
|
|
91
|
+
}, [roomId, inputBottom, chatAnim]);
|
|
89
92
|
useEffect(() => {
|
|
90
93
|
if (!roomId)
|
|
91
94
|
return;
|
|
@@ -157,7 +160,6 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
|
|
|
157
160
|
}
|
|
158
161
|
catch { /* ignore */ }
|
|
159
162
|
}, [chatData.length]);
|
|
160
|
-
// System audio playback (Android only)
|
|
161
163
|
useEffect(() => {
|
|
162
164
|
if (!socket || !joined || !isActive || Platform.OS !== 'android')
|
|
163
165
|
return;
|
|
@@ -209,12 +211,8 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
|
|
|
209
211
|
};
|
|
210
212
|
const hostLabel = stream.hostDisplayName || stream.title || 'Live';
|
|
211
213
|
const avatarLetter = hostLabel[0]?.toUpperCase() ?? 'L';
|
|
212
|
-
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 &&
|
|
213
|
-
|
|
214
|
-
'rgba(0,0,0,0.0)', // clears out ~30% down
|
|
215
|
-
'rgba(0,0,0,0.0)', // stays clear through mid-screen
|
|
216
|
-
'rgba(0,0,0,0.78)', // darkens toward bottom for chat / input
|
|
217
|
-
], 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() : '—' })] })] }), _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" })] })] }), _jsxs(Animated.View, { style: [styles.bottomPanel, { bottom: kbOffset }], pointerEvents: "box-none", children: [_jsx(View, { style: styles.chatColumn, pointerEvents: "box-none", children: _jsx(ScrollView, { ref: chatListRef, contentContainerStyle: styles.chatContent, showsVerticalScrollIndicator: false, keyboardShouldPersistTaps: "always", keyboardDismissMode: "none", 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))) }) }), _jsxs(View, { style: styles.inputBar, children: [_jsx(TouchableOpacity, { style: styles.inputIconBtn, activeOpacity: 0.7, children: _jsx(Text, { style: styles.inputIconGlyph, children: "\u263A" }) }), _jsx(TextInput, { style: styles.textInput, value: chatInput, onChangeText: setChatInput, placeholder: joined ? 'Say something...' : 'Connecting...', placeholderTextColor: "rgba(255,255,255,0.35)", editable: joined && !joining && !error, onSubmitEditing: sendChat, returnKeyType: "send", blurOnSubmit: false }), _jsx(TouchableOpacity, { style: styles.inputIconBtn, activeOpacity: 0.7, children: _jsx(Text, { style: styles.inputIconGlyph, children: "\uD83C\uDF39" }) }), _jsx(TouchableOpacity, { style: [styles.sendBtn, (!chatInput.trim() || !joined) && styles.sendBtnOff], onPress: sendChat, activeOpacity: 0.75, disabled: !joined || !chatInput.trim(), children: _jsx(Text, { style: styles.sendIcon, children: "\u25B6" }) })] })] }), 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" })] }) }))] }));
|
|
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", keyboardDismissMode: "on-drag", 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)
|
|
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" }) })] })), isTyping && (_jsxs(Animated.View, { style: [styles.floatingInputWrap, { bottom: inputBottom }], 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" }) })] })), 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" })] }) }))] }));
|
|
218
216
|
});
|
|
219
217
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
220
218
|
const styles = StyleSheet.create({
|
|
@@ -222,7 +220,6 @@ const styles = StyleSheet.create({
|
|
|
222
220
|
flex: 1,
|
|
223
221
|
backgroundColor: '#000',
|
|
224
222
|
},
|
|
225
|
-
// ── Video ─────────────────────────────────────────────────────────────────
|
|
226
223
|
rtcView: {
|
|
227
224
|
position: 'absolute', top: 0, left: 0, right: 0, bottom: 0,
|
|
228
225
|
backgroundColor: '#000',
|
|
@@ -246,7 +243,9 @@ const styles = StyleSheet.create({
|
|
|
246
243
|
textAlign: 'center',
|
|
247
244
|
paddingHorizontal: 28,
|
|
248
245
|
},
|
|
249
|
-
|
|
246
|
+
gradientOverlay: {
|
|
247
|
+
position: 'absolute', top: 0, left: 0, right: 0, bottom: 0,
|
|
248
|
+
},
|
|
250
249
|
topBar: {
|
|
251
250
|
position: 'absolute',
|
|
252
251
|
top: 10,
|
|
@@ -275,17 +274,8 @@ const styles = StyleSheet.create({
|
|
|
275
274
|
justifyContent: 'center',
|
|
276
275
|
flexShrink: 0,
|
|
277
276
|
},
|
|
278
|
-
avatarLetter: {
|
|
279
|
-
|
|
280
|
-
fontSize: 15,
|
|
281
|
-
fontWeight: '700',
|
|
282
|
-
},
|
|
283
|
-
hostName: {
|
|
284
|
-
color: '#fff',
|
|
285
|
-
fontSize: 14,
|
|
286
|
-
fontWeight: '600',
|
|
287
|
-
flex: 1,
|
|
288
|
-
},
|
|
277
|
+
avatarLetter: { color: '#fff', fontSize: 15, fontWeight: '700' },
|
|
278
|
+
hostName: { color: '#fff', fontSize: 14, fontWeight: '600', flex: 1 },
|
|
289
279
|
livePill: {
|
|
290
280
|
backgroundColor: '#ef4444',
|
|
291
281
|
borderRadius: 6,
|
|
@@ -293,12 +283,7 @@ const styles = StyleSheet.create({
|
|
|
293
283
|
paddingVertical: 3,
|
|
294
284
|
flexShrink: 0,
|
|
295
285
|
},
|
|
296
|
-
livePillText: {
|
|
297
|
-
color: '#fff',
|
|
298
|
-
fontSize: 11,
|
|
299
|
-
fontWeight: '700',
|
|
300
|
-
letterSpacing: 0.8,
|
|
301
|
-
},
|
|
286
|
+
livePillText: { color: '#fff', fontSize: 11, fontWeight: '700', letterSpacing: 0.8 },
|
|
302
287
|
viewerChip: {
|
|
303
288
|
flexDirection: 'row',
|
|
304
289
|
alignItems: 'center',
|
|
@@ -310,23 +295,15 @@ const styles = StyleSheet.create({
|
|
|
310
295
|
flexShrink: 0,
|
|
311
296
|
},
|
|
312
297
|
viewerEye: { fontSize: 11 },
|
|
313
|
-
viewerCount: {
|
|
314
|
-
color: 'rgba(255,255,255,0.88)',
|
|
315
|
-
fontSize: 12,
|
|
316
|
-
fontWeight: '500',
|
|
317
|
-
},
|
|
318
|
-
// ── Right action column ───────────────────────────────────────────────────
|
|
298
|
+
viewerCount: { color: 'rgba(255,255,255,0.88)', fontSize: 12, fontWeight: '500' },
|
|
319
299
|
rightColumn: {
|
|
320
300
|
position: 'absolute',
|
|
321
301
|
right: 12,
|
|
322
|
-
bottom:
|
|
302
|
+
bottom: BOTTOM_SAFE + BOTTOM_BAR_H + 8,
|
|
323
303
|
alignItems: 'center',
|
|
324
304
|
gap: 14,
|
|
325
305
|
},
|
|
326
|
-
actionBtn: {
|
|
327
|
-
alignItems: 'center',
|
|
328
|
-
gap: 5,
|
|
329
|
-
},
|
|
306
|
+
actionBtn: { alignItems: 'center', gap: 5 },
|
|
330
307
|
actionCircle: {
|
|
331
308
|
width: 48,
|
|
332
309
|
height: 48,
|
|
@@ -337,32 +314,16 @@ const styles = StyleSheet.create({
|
|
|
337
314
|
alignItems: 'center',
|
|
338
315
|
justifyContent: 'center',
|
|
339
316
|
},
|
|
340
|
-
actionIcon: {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
},
|
|
344
|
-
actionLabel: {
|
|
345
|
-
color: 'rgba(255,255,255,0.7)',
|
|
346
|
-
fontSize: 11,
|
|
347
|
-
fontWeight: '500',
|
|
348
|
-
},
|
|
349
|
-
// ── Bottom panel ──────────────────────────────────────────────────────────
|
|
350
|
-
bottomPanel: {
|
|
351
|
-
position: 'absolute',
|
|
352
|
-
left: 0,
|
|
353
|
-
right: 0,
|
|
354
|
-
paddingHorizontal: 12,
|
|
355
|
-
paddingBottom: BOTTOM_SAFE,
|
|
356
|
-
gap: 8,
|
|
357
|
-
},
|
|
358
|
-
// Chat messages
|
|
317
|
+
actionIcon: { fontSize: 22, color: '#fff' },
|
|
318
|
+
actionLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 11, fontWeight: '500' },
|
|
319
|
+
// Chat column — absolute, bottom driven by animation
|
|
359
320
|
chatColumn: {
|
|
321
|
+
position: 'absolute',
|
|
322
|
+
left: 12,
|
|
360
323
|
width: '66%',
|
|
361
324
|
maxHeight: Math.round(SCREEN_HEIGHT * 0.30),
|
|
362
325
|
},
|
|
363
|
-
chatContent: {
|
|
364
|
-
paddingBottom: 2,
|
|
365
|
-
},
|
|
326
|
+
chatContent: { paddingBottom: 2 },
|
|
366
327
|
chatBubble: {
|
|
367
328
|
alignSelf: 'flex-start',
|
|
368
329
|
marginBottom: 5,
|
|
@@ -381,53 +342,57 @@ const styles = StyleSheet.create({
|
|
|
381
342
|
paddingVertical: 4,
|
|
382
343
|
maxWidth: '100%',
|
|
383
344
|
},
|
|
384
|
-
chatLine: {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
},
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
},
|
|
395
|
-
joinText: {
|
|
396
|
-
color: 'rgba(255,255,255,0.48)',
|
|
397
|
-
fontSize: 12,
|
|
398
|
-
fontStyle: 'italic',
|
|
399
|
-
},
|
|
400
|
-
// Input bar
|
|
401
|
-
inputBar: {
|
|
345
|
+
chatLine: { fontSize: 13, lineHeight: 18 },
|
|
346
|
+
chatUsername: { fontWeight: '700' },
|
|
347
|
+
chatMsg: { color: '#e5e5e5', fontWeight: '400' },
|
|
348
|
+
joinText: { color: 'rgba(255,255,255,0.48)', fontSize: 12, fontStyle: 'italic' },
|
|
349
|
+
// Bottom action bar
|
|
350
|
+
bottomBar: {
|
|
351
|
+
position: 'absolute',
|
|
352
|
+
bottom: BOTTOM_SAFE,
|
|
353
|
+
left: 12,
|
|
354
|
+
right: 12,
|
|
402
355
|
flexDirection: 'row',
|
|
403
356
|
alignItems: 'center',
|
|
404
|
-
|
|
405
|
-
borderRadius: 999,
|
|
406
|
-
borderWidth: StyleSheet.hairlineWidth,
|
|
407
|
-
borderColor: 'rgba(255,255,255,0.12)',
|
|
408
|
-
paddingHorizontal: 4,
|
|
409
|
-
paddingVertical: 4,
|
|
410
|
-
gap: 2,
|
|
357
|
+
gap: 6,
|
|
411
358
|
},
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
alignItems: 'center',
|
|
416
|
-
justifyContent: 'center',
|
|
359
|
+
typeTouchable: {
|
|
360
|
+
flex: 1,
|
|
361
|
+
backgroundColor: 'rgba(255,255,255,0.12)',
|
|
417
362
|
borderRadius: 20,
|
|
363
|
+
paddingHorizontal: 14,
|
|
364
|
+
paddingVertical: 10,
|
|
365
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
366
|
+
borderColor: 'rgba(255,255,255,0.18)',
|
|
418
367
|
},
|
|
419
|
-
|
|
420
|
-
|
|
368
|
+
typePlaceholder: { color: 'rgba(255,255,255,0.50)', fontSize: 14 },
|
|
369
|
+
bottomIconBtn: { width: 40, height: 40, alignItems: 'center', justifyContent: 'center' },
|
|
370
|
+
bottomIconGlyph: { fontSize: 22 },
|
|
371
|
+
// Floating input bar — sits directly above keyboard, animated
|
|
372
|
+
floatingInputWrap: {
|
|
373
|
+
position: 'absolute',
|
|
374
|
+
left: 0,
|
|
375
|
+
right: 0,
|
|
376
|
+
flexDirection: 'row',
|
|
377
|
+
alignItems: 'center',
|
|
378
|
+
backgroundColor: 'rgba(20,20,20,0.96)',
|
|
379
|
+
paddingHorizontal: 12,
|
|
380
|
+
paddingVertical: 8,
|
|
381
|
+
gap: 8,
|
|
382
|
+
borderTopWidth: StyleSheet.hairlineWidth,
|
|
383
|
+
borderTopColor: 'rgba(255,255,255,0.10)',
|
|
421
384
|
},
|
|
422
|
-
|
|
385
|
+
floatingTextInput: {
|
|
423
386
|
flex: 1,
|
|
387
|
+
backgroundColor: 'rgba(255,255,255,0.10)',
|
|
388
|
+
borderRadius: 20,
|
|
389
|
+
paddingHorizontal: 14,
|
|
390
|
+
paddingVertical: Platform.OS === 'ios' ? 10 : 8,
|
|
424
391
|
color: '#fff',
|
|
425
392
|
fontSize: 14,
|
|
426
|
-
paddingHorizontal: 6,
|
|
427
|
-
paddingVertical: Platform.OS === 'ios' ? 10 : 6,
|
|
428
393
|
minHeight: 40,
|
|
429
394
|
},
|
|
430
|
-
|
|
395
|
+
floatingSendBtn: {
|
|
431
396
|
width: 40,
|
|
432
397
|
height: 40,
|
|
433
398
|
borderRadius: 20,
|
|
@@ -435,42 +400,15 @@ const styles = StyleSheet.create({
|
|
|
435
400
|
alignItems: 'center',
|
|
436
401
|
justifyContent: 'center',
|
|
437
402
|
},
|
|
438
|
-
sendBtnOff: {
|
|
439
|
-
|
|
440
|
-
},
|
|
441
|
-
sendIcon: {
|
|
442
|
-
color: '#fff',
|
|
443
|
-
fontSize: 15,
|
|
444
|
-
fontWeight: '700',
|
|
445
|
-
},
|
|
446
|
-
// ── Stream ended ──────────────────────────────────────────────────────────
|
|
403
|
+
sendBtnOff: { backgroundColor: 'rgba(255,255,255,0.12)' },
|
|
404
|
+
sendIcon: { color: '#fff', fontSize: 15, fontWeight: '700' },
|
|
447
405
|
endedOverlay: {
|
|
448
406
|
position: 'absolute', top: 0, left: 0, right: 0, bottom: 0,
|
|
449
407
|
backgroundColor: 'rgba(0,0,0,0.80)',
|
|
450
408
|
alignItems: 'center',
|
|
451
409
|
justifyContent: 'center',
|
|
452
410
|
},
|
|
453
|
-
endedCard: {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
paddingHorizontal: 36,
|
|
457
|
-
},
|
|
458
|
-
endedTitle: {
|
|
459
|
-
color: '#fff',
|
|
460
|
-
fontSize: 22,
|
|
461
|
-
fontWeight: '700',
|
|
462
|
-
},
|
|
463
|
-
endedSub: {
|
|
464
|
-
color: 'rgba(255,255,255,0.5)',
|
|
465
|
-
fontSize: 14,
|
|
466
|
-
textAlign: 'center',
|
|
467
|
-
lineHeight: 20,
|
|
468
|
-
},
|
|
469
|
-
gradientOverlay: {
|
|
470
|
-
position: 'absolute',
|
|
471
|
-
top: 0,
|
|
472
|
-
left: 0,
|
|
473
|
-
right: 0,
|
|
474
|
-
bottom: 0,
|
|
475
|
-
},
|
|
411
|
+
endedCard: { alignItems: 'center', gap: 10, paddingHorizontal: 36 },
|
|
412
|
+
endedTitle: { color: '#fff', fontSize: 22, fontWeight: '700' },
|
|
413
|
+
endedSub: { color: 'rgba(255,255,255,0.5)', fontSize: 14, textAlign: 'center', lineHeight: 20 },
|
|
476
414
|
});
|
package/package.json
CHANGED