kasunk99-livestream-core 0.3.13 → 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.
@@ -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;AAwB1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAO/C,KAAK,yBAAyB,GAAG;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AA8BF;;GAEG;AACH,eAAO,MAAM,oBAAoB,uDAiX/B,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,40 +1,25 @@
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, Dimensions, KeyboardAvoidingView, NativeModules, Platform, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View, } from 'react-native';
4
- // react-native-keyboard-controller handles Android/iOS keyboard edge cases better
5
- // than the built-in KeyboardAvoidingView. Fall back to the built-in if unavailable.
6
- let KAV = KeyboardAvoidingView;
7
- try {
8
- KAV = require('react-native-keyboard-controller').KeyboardAvoidingView;
9
- }
10
- catch {
11
- // use built-in
12
- }
3
+ import { ActivityIndicator, Animated, Dimensions, Keyboard, KeyboardAvoidingView, Modal, NativeModules, Platform, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View, } from 'react-native';
13
4
  import { useViewerSocket } from '../hooks/useViewerSocket';
14
5
  const SCREEN_HEIGHT = Dimensions.get('window').height;
15
6
  const CHAT_NAME_COLORS = ['#f97316', '#22c55e', '#3b82f6', '#eab308', '#ec4899', '#a855f7'];
16
- // iOS needs bottom safe-area padding; Android auto-adjusts via adjustResize
17
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;
10
+ // Chat shifts up when keyboard is open — fixed offset works with both adjustResize + adjustNothing
11
+ const CHAT_BOTTOM_TYPING = BOTTOM_SAFE + 8;
18
12
  let RTCViewComponent = null;
19
13
  try {
20
14
  const webrtc = require('react-native-webrtc');
21
15
  RTCViewComponent = webrtc.RTCView;
22
16
  }
23
- catch {
24
- // react-native-webrtc not available in Expo Go
25
- }
26
- // expo-linear-gradient — lazy require so the package stays safe in non-Expo environments.
27
- // The consuming app (social-casino-mobile) has expo-linear-gradient as a dependency.
17
+ catch { }
28
18
  let LinearGradient = null;
29
19
  try {
30
20
  LinearGradient = require('expo-linear-gradient').LinearGradient;
31
21
  }
32
- catch {
33
- // expo-linear-gradient not available
34
- }
35
- /**
36
- * Single full-screen viewer cell. When isActive, joins the stream room and consumes video/audio.
37
- */
22
+ catch { }
38
23
  export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream, isActive, index: _index, }) {
39
24
  const roomId = isActive ? stream.roomId : null;
40
25
  const { joined, joining, error, producerList, roomState, remoteStream, remoteVideoStream, webrtcUnavailable, consumeError, socket, viewerCount: liveViewerCount, streamEnded, } = useViewerSocket(roomId);
@@ -53,8 +38,25 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
53
38
  const showVideo = RTCViewComponent && remoteStream && !!streamURL && hasVideoTrack;
54
39
  const [chatInput, setChatInput] = useState('');
55
40
  const [chatMessages, setChatMessages] = useState([]);
41
+ const [isTyping, setIsTyping] = useState(false);
56
42
  const seededRoomRef = useRef(null);
57
43
  const chatListRef = useRef(null);
44
+ const textInputRef = useRef(null);
45
+ // Animates chat column up/down when keyboard opens/closes
46
+ const chatAnim = useRef(new Animated.Value(CHAT_BOTTOM_DEFAULT)).current;
47
+ // Slide chat column based on typing state
48
+ useEffect(() => {
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
+ }, []);
58
60
  const seededFromRoomState = useMemo(() => {
59
61
  const chat = roomState && typeof roomState === 'object'
60
62
  ? roomState.chat
@@ -66,6 +68,9 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
66
68
  seededRoomRef.current = null;
67
69
  setChatMessages([]);
68
70
  setChatInput('');
71
+ setIsTyping(false);
72
+ Keyboard.dismiss();
73
+ chatAnim.setValue(CHAT_BOTTOM_DEFAULT);
69
74
  return;
70
75
  }
71
76
  if (seededRoomRef.current !== roomId) {
@@ -73,7 +78,7 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
73
78
  setChatMessages([]);
74
79
  setChatInput('');
75
80
  }
76
- }, [roomId]);
81
+ }, [roomId, chatAnim]);
77
82
  useEffect(() => {
78
83
  if (!roomId)
79
84
  return;
@@ -145,7 +150,6 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
145
150
  }
146
151
  catch { /* ignore */ }
147
152
  }, [chatData.length]);
148
- // System audio playback (Android only)
149
153
  useEffect(() => {
150
154
  if (!socket || !joined || !isActive || Platform.OS !== 'android')
151
155
  return;
@@ -197,12 +201,8 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
197
201
  };
198
202
  const hostLabel = stream.hostDisplayName || stream.title || 'Live';
199
203
  const avatarLetter = hostLabel[0]?.toUpperCase() ?? 'L';
200
- return (_jsxs(KAV, { style: styles.container, behavior: Platform.OS === 'ios' ? 'padding' : 'height', 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: [
201
- 'rgba(0,0,0,0.70)', // top edge opaque for host info readability
202
- 'rgba(0,0,0,0.0)', // clears out ~30% down
203
- 'rgba(0,0,0,0.0)', // stays clear through mid-screen
204
- 'rgba(0,0,0,0.78)', // darkens toward bottom for chat / input
205
- ], 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(View, { style: styles.bottomSection, 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", submitBehavior: "submit" }), _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" })] }) }))] }));
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" }) })] })] }) })] }));
206
206
  });
207
207
  // ─────────────────────────────────────────────────────────────────────────────
208
208
  const styles = StyleSheet.create({
@@ -210,7 +210,6 @@ const styles = StyleSheet.create({
210
210
  flex: 1,
211
211
  backgroundColor: '#000',
212
212
  },
213
- // ── Video ─────────────────────────────────────────────────────────────────
214
213
  rtcView: {
215
214
  position: 'absolute', top: 0, left: 0, right: 0, bottom: 0,
216
215
  backgroundColor: '#000',
@@ -234,7 +233,9 @@ const styles = StyleSheet.create({
234
233
  textAlign: 'center',
235
234
  paddingHorizontal: 28,
236
235
  },
237
- // ── Top bar ───────────────────────────────────────────────────────────────
236
+ gradientOverlay: {
237
+ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0,
238
+ },
238
239
  topBar: {
239
240
  position: 'absolute',
240
241
  top: 10,
@@ -263,17 +264,8 @@ const styles = StyleSheet.create({
263
264
  justifyContent: 'center',
264
265
  flexShrink: 0,
265
266
  },
266
- avatarLetter: {
267
- color: '#fff',
268
- fontSize: 15,
269
- fontWeight: '700',
270
- },
271
- hostName: {
272
- color: '#fff',
273
- fontSize: 14,
274
- fontWeight: '600',
275
- flex: 1,
276
- },
267
+ avatarLetter: { color: '#fff', fontSize: 15, fontWeight: '700' },
268
+ hostName: { color: '#fff', fontSize: 14, fontWeight: '600', flex: 1 },
277
269
  livePill: {
278
270
  backgroundColor: '#ef4444',
279
271
  borderRadius: 6,
@@ -281,12 +273,7 @@ const styles = StyleSheet.create({
281
273
  paddingVertical: 3,
282
274
  flexShrink: 0,
283
275
  },
284
- livePillText: {
285
- color: '#fff',
286
- fontSize: 11,
287
- fontWeight: '700',
288
- letterSpacing: 0.8,
289
- },
276
+ livePillText: { color: '#fff', fontSize: 11, fontWeight: '700', letterSpacing: 0.8 },
290
277
  viewerChip: {
291
278
  flexDirection: 'row',
292
279
  alignItems: 'center',
@@ -298,23 +285,15 @@ const styles = StyleSheet.create({
298
285
  flexShrink: 0,
299
286
  },
300
287
  viewerEye: { fontSize: 11 },
301
- viewerCount: {
302
- color: 'rgba(255,255,255,0.88)',
303
- fontSize: 12,
304
- fontWeight: '500',
305
- },
306
- // ── Right action column ───────────────────────────────────────────────────
288
+ viewerCount: { color: 'rgba(255,255,255,0.88)', fontSize: 12, fontWeight: '500' },
307
289
  rightColumn: {
308
290
  position: 'absolute',
309
291
  right: 12,
310
- bottom: 92,
292
+ bottom: BOTTOM_SAFE + BOTTOM_BAR_H + 8,
311
293
  alignItems: 'center',
312
294
  gap: 14,
313
295
  },
314
- actionBtn: {
315
- alignItems: 'center',
316
- gap: 5,
317
- },
296
+ actionBtn: { alignItems: 'center', gap: 5 },
318
297
  actionCircle: {
319
298
  width: 48,
320
299
  height: 48,
@@ -325,31 +304,16 @@ const styles = StyleSheet.create({
325
304
  alignItems: 'center',
326
305
  justifyContent: 'center',
327
306
  },
328
- actionIcon: {
329
- fontSize: 22,
330
- color: '#fff',
331
- },
332
- actionLabel: {
333
- color: 'rgba(255,255,255,0.7)',
334
- fontSize: 11,
335
- fontWeight: '500',
336
- },
337
- // ── Bottom section (in-flow — KAV lifts this above the keyboard) ─────────
338
- bottomSection: {
339
- flex: 1,
340
- justifyContent: 'flex-end',
341
- paddingHorizontal: 12,
342
- paddingBottom: BOTTOM_SAFE,
343
- gap: 8,
344
- },
345
- // Chat messages
307
+ actionIcon: { fontSize: 22, color: '#fff' },
308
+ actionLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 11, fontWeight: '500' },
309
+ // Chat column — absolute, bottom animated between default and typing positions
346
310
  chatColumn: {
311
+ position: 'absolute',
312
+ left: 12,
347
313
  width: '66%',
348
314
  maxHeight: Math.round(SCREEN_HEIGHT * 0.30),
349
315
  },
350
- chatContent: {
351
- paddingBottom: 2,
352
- },
316
+ chatContent: { paddingBottom: 2 },
353
317
  chatBubble: {
354
318
  alignSelf: 'flex-start',
355
319
  marginBottom: 5,
@@ -368,53 +332,61 @@ const styles = StyleSheet.create({
368
332
  paddingVertical: 4,
369
333
  maxWidth: '100%',
370
334
  },
371
- chatLine: {
372
- fontSize: 13,
373
- lineHeight: 18,
335
+ chatLine: { fontSize: 13, lineHeight: 18 },
336
+ chatUsername: { fontWeight: '700' },
337
+ chatMsg: { color: '#e5e5e5', fontWeight: '400' },
338
+ joinText: { color: 'rgba(255,255,255,0.48)', fontSize: 12, fontStyle: 'italic' },
339
+ // Bottom action bar
340
+ bottomBar: {
341
+ position: 'absolute',
342
+ bottom: BOTTOM_SAFE,
343
+ left: 12,
344
+ right: 12,
345
+ flexDirection: 'row',
346
+ alignItems: 'center',
347
+ gap: 6,
374
348
  },
375
- chatUsername: {
376
- fontWeight: '700',
349
+ typeTouchable: {
350
+ flex: 1,
351
+ backgroundColor: 'rgba(255,255,255,0.12)',
352
+ borderRadius: 20,
353
+ paddingHorizontal: 14,
354
+ paddingVertical: 10,
355
+ borderWidth: StyleSheet.hairlineWidth,
356
+ borderColor: 'rgba(255,255,255,0.18)',
377
357
  },
378
- chatMsg: {
379
- color: '#e5e5e5',
380
- fontWeight: '400',
358
+ typePlaceholder: { color: 'rgba(255,255,255,0.50)', fontSize: 14 },
359
+ bottomIconBtn: { width: 40, height: 40, alignItems: 'center', justifyContent: 'center' },
360
+ bottomIconGlyph: { fontSize: 22 },
361
+ // Modal input overlay
362
+ inputModalKAV: {
363
+ flex: 1,
381
364
  },
382
- joinText: {
383
- color: 'rgba(255,255,255,0.48)',
384
- fontSize: 12,
385
- fontStyle: 'italic',
365
+ inputModalBackdrop: {
366
+ flex: 1,
386
367
  },
387
- // Input bar
388
- inputBar: {
368
+ floatingInputWrap: {
389
369
  flexDirection: 'row',
390
370
  alignItems: 'center',
391
- backgroundColor: 'rgba(18,18,18,0.75)',
392
- borderRadius: 999,
393
- borderWidth: StyleSheet.hairlineWidth,
394
- borderColor: 'rgba(255,255,255,0.12)',
395
- paddingHorizontal: 4,
396
- paddingVertical: 4,
397
- gap: 2,
398
- },
399
- inputIconBtn: {
400
- width: 40,
401
- height: 40,
402
- alignItems: 'center',
403
- justifyContent: 'center',
404
- borderRadius: 20,
405
- },
406
- inputIconGlyph: {
407
- fontSize: 21,
371
+ backgroundColor: 'rgba(20,20,20,0.97)',
372
+ paddingHorizontal: 12,
373
+ paddingVertical: 8,
374
+ paddingBottom: BOTTOM_SAFE,
375
+ gap: 8,
376
+ borderTopWidth: StyleSheet.hairlineWidth,
377
+ borderTopColor: 'rgba(255,255,255,0.10)',
408
378
  },
409
- textInput: {
379
+ floatingTextInput: {
410
380
  flex: 1,
381
+ backgroundColor: 'rgba(255,255,255,0.10)',
382
+ borderRadius: 20,
383
+ paddingHorizontal: 14,
384
+ paddingVertical: Platform.OS === 'ios' ? 10 : 8,
411
385
  color: '#fff',
412
386
  fontSize: 14,
413
- paddingHorizontal: 6,
414
- paddingVertical: Platform.OS === 'ios' ? 10 : 6,
415
387
  minHeight: 40,
416
388
  },
417
- sendBtn: {
389
+ floatingSendBtn: {
418
390
  width: 40,
419
391
  height: 40,
420
392
  borderRadius: 20,
@@ -422,42 +394,15 @@ const styles = StyleSheet.create({
422
394
  alignItems: 'center',
423
395
  justifyContent: 'center',
424
396
  },
425
- sendBtnOff: {
426
- backgroundColor: 'rgba(255,255,255,0.1)',
427
- },
428
- sendIcon: {
429
- color: '#fff',
430
- fontSize: 15,
431
- fontWeight: '700',
432
- },
433
- // ── Stream ended ──────────────────────────────────────────────────────────
397
+ sendBtnOff: { backgroundColor: 'rgba(255,255,255,0.12)' },
398
+ sendIcon: { color: '#fff', fontSize: 15, fontWeight: '700' },
434
399
  endedOverlay: {
435
400
  position: 'absolute', top: 0, left: 0, right: 0, bottom: 0,
436
401
  backgroundColor: 'rgba(0,0,0,0.80)',
437
402
  alignItems: 'center',
438
403
  justifyContent: 'center',
439
404
  },
440
- endedCard: {
441
- alignItems: 'center',
442
- gap: 10,
443
- paddingHorizontal: 36,
444
- },
445
- endedTitle: {
446
- color: '#fff',
447
- fontSize: 22,
448
- fontWeight: '700',
449
- },
450
- endedSub: {
451
- color: 'rgba(255,255,255,0.5)',
452
- fontSize: 14,
453
- textAlign: 'center',
454
- lineHeight: 20,
455
- },
456
- gradientOverlay: {
457
- position: 'absolute',
458
- top: 0,
459
- left: 0,
460
- right: 0,
461
- bottom: 0,
462
- },
405
+ endedCard: { alignItems: 'center', gap: 10, paddingHorizontal: 36 },
406
+ endedTitle: { color: '#fff', fontSize: 22, fontWeight: '700' },
407
+ endedSub: { color: 'rgba(255,255,255,0.5)', fontSize: 14, textAlign: 'center', lineHeight: 20 },
463
408
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kasunk99-livestream-core",
3
- "version": "0.3.13",
3
+ "version": "0.3.15",
4
4
  "description": "Reusable livestream viewer/host module for React Native (Expo) — mediasoup + Socket.IO",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",