kasunk99-livestream-core 0.3.15 → 0.3.17

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;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
+ {"version":3,"file":"LiveStreamViewerItem.d.ts","sourceRoot":"","sources":["../../src/components/LiveStreamViewerItem.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkE,MAAM,OAAO,CAAC;AAkBvF,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;AAwBF,eAAO,MAAM,oBAAoB,uDAme/B,CAAC"}
@@ -1,14 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { memo, useEffect, useMemo, useRef, useState } from 'react';
3
- import { ActivityIndicator, Animated, Dimensions, Keyboard, KeyboardAvoidingView, Modal, NativeModules, Platform, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View, } from 'react-native';
2
+ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { ActivityIndicator, Animated, Dimensions, Keyboard, 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;
12
10
  let RTCViewComponent = null;
13
11
  try {
14
12
  const webrtc = require('react-native-webrtc');
@@ -36,27 +34,99 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
36
34
  ? displayStreamObj.toURL()
37
35
  : undefined;
38
36
  const showVideo = RTCViewComponent && remoteStream && !!streamURL && hasVideoTrack;
37
+ // chatInput persists when keyboard closes without sending — cleared only on send
39
38
  const [chatInput, setChatInput] = useState('');
40
39
  const [chatMessages, setChatMessages] = useState([]);
40
+ // isTyping: user has opened the input panel
41
41
  const [isTyping, setIsTyping] = useState(false);
42
42
  const seededRoomRef = useRef(null);
43
43
  const chatListRef = useRef(null);
44
44
  const textInputRef = useRef(null);
45
- // Animates chat column up/down when keyboard opens/closes
45
+ // Cell layout tracking used to compensate for adjustResize shrinking the cell
46
+ const baseCellH = useRef(0); // largest height seen = keyboard-free baseline
47
+ const currCellH = useRef(0); // current cell height (may shrink with adjustResize)
48
+ const onCellLayout = useCallback((e) => {
49
+ const h = Math.round(e.nativeEvent.layout.height);
50
+ currCellH.current = h;
51
+ if (h > baseCellH.current)
52
+ baseCellH.current = h;
53
+ }, []);
54
+ // Animated values
55
+ const inputBottom = useRef(new Animated.Value(0)).current;
46
56
  const chatAnim = useRef(new Animated.Value(CHAT_BOTTOM_DEFAULT)).current;
47
- // Slide chat column based on typing state
57
+ // Remember last offset so re-open positions instantly instead of jumping
58
+ const lastOffsetRef = useRef(0);
59
+ // Compute the correct bottom offset for the floating input.
60
+ // If adjustResize has shrunk the cell (shrink > 0), subtract that from keyboard height
61
+ // so we don't double-offset. If cell hasn't shrunk, use full keyboard height.
62
+ const computeOffset = useCallback((kbH) => {
63
+ const base = baseCellH.current || SCREEN_HEIGHT;
64
+ const cur = currCellH.current || base;
65
+ const shrink = Math.max(0, base - cur);
66
+ return Math.max(BOTTOM_SAFE, kbH - shrink);
67
+ }, []);
68
+ // Pre-position the input immediately when typing starts (uses last known offset)
48
69
  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
70
+ if (isTyping && lastOffsetRef.current > 0) {
71
+ inputBottom.setValue(lastOffsetRef.current);
72
+ chatAnim.setValue(lastOffsetRef.current + 60);
73
+ }
74
+ else if (!isTyping) {
75
+ // Only reset if keyboard is not currently shown (let keyboard events handle active state)
76
+ inputBottom.setValue(0);
77
+ chatAnim.setValue(CHAT_BOTTOM_DEFAULT);
78
+ }
79
+ }, [isTyping, inputBottom, chatAnim]);
80
+ // Keyboard event listeners — platform-specific
56
81
  useEffect(() => {
57
- const sub = Keyboard.addListener('keyboardDidHide', () => setIsTyping(false));
58
- return () => sub.remove();
59
- }, []);
82
+ if (Platform.OS === 'android') {
83
+ // keyboardDidShow fires AFTER keyboard animation + AFTER adjustResize layout.
84
+ // currCellH.current has the post-resize value at this point.
85
+ const s1 = Keyboard.addListener('keyboardDidShow', (e) => {
86
+ const offset = computeOffset(e.endCoordinates.height);
87
+ lastOffsetRef.current = offset;
88
+ // Short animation so input slides into position rather than hard-jumping
89
+ Animated.parallel([
90
+ Animated.timing(inputBottom, { toValue: offset, duration: 120, useNativeDriver: false }),
91
+ Animated.timing(chatAnim, { toValue: offset + 60, duration: 120, useNativeDriver: false }),
92
+ ]).start();
93
+ });
94
+ const s2 = Keyboard.addListener('keyboardDidHide', () => {
95
+ // Only close typing if it wasn't already closed by sendChat
96
+ setIsTyping((prev) => {
97
+ if (prev) {
98
+ inputBottom.setValue(0);
99
+ chatAnim.setValue(CHAT_BOTTOM_DEFAULT);
100
+ }
101
+ return false;
102
+ });
103
+ });
104
+ return () => { s1.remove(); s2.remove(); };
105
+ }
106
+ // iOS — keyboardWillShow fires before keyboard so we can animate in sync
107
+ const s1 = Keyboard.addListener('keyboardWillShow', (e) => {
108
+ const offset = computeOffset(e.endCoordinates.height);
109
+ lastOffsetRef.current = offset;
110
+ Animated.parallel([
111
+ Animated.timing(inputBottom, { toValue: offset, duration: e.duration || 250, useNativeDriver: false }),
112
+ Animated.timing(chatAnim, { toValue: offset + 60, duration: e.duration || 250, useNativeDriver: false }),
113
+ ]).start();
114
+ });
115
+ const s2 = Keyboard.addListener('keyboardWillHide', (e) => {
116
+ Animated.parallel([
117
+ Animated.timing(inputBottom, { toValue: 0, duration: e.duration || 250, useNativeDriver: false }),
118
+ Animated.timing(chatAnim, { toValue: CHAT_BOTTOM_DEFAULT, duration: e.duration || 250, useNativeDriver: false }),
119
+ ]).start(() => setIsTyping(false));
120
+ });
121
+ return () => { s1.remove(); s2.remove(); };
122
+ }, [inputBottom, chatAnim, computeOffset]);
123
+ // Dismiss keyboard when stream becomes inactive
124
+ useEffect(() => {
125
+ if (!isActive) {
126
+ Keyboard.dismiss();
127
+ setIsTyping(false);
128
+ }
129
+ }, [isActive]);
60
130
  const seededFromRoomState = useMemo(() => {
61
131
  const chat = roomState && typeof roomState === 'object'
62
132
  ? roomState.chat
@@ -70,6 +140,7 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
70
140
  setChatInput('');
71
141
  setIsTyping(false);
72
142
  Keyboard.dismiss();
143
+ inputBottom.setValue(0);
73
144
  chatAnim.setValue(CHAT_BOTTOM_DEFAULT);
74
145
  return;
75
146
  }
@@ -78,7 +149,7 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
78
149
  setChatMessages([]);
79
150
  setChatInput('');
80
151
  }
81
- }, [roomId, chatAnim]);
152
+ }, [roomId, inputBottom, chatAnim]);
82
153
  useEffect(() => {
83
154
  if (!roomId)
84
155
  return;
@@ -193,7 +264,11 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
193
264
  const text = chatInput.trim();
194
265
  if (!text || !socket || !joined)
195
266
  return;
267
+ // Clear input, close panel, dismiss keyboard — all before the emit
268
+ // so the button press completes synchronously before any state teardown.
196
269
  setChatInput('');
270
+ setIsTyping(false);
271
+ Keyboard.dismiss();
197
272
  socket.emit('chat-message', { text }, (res) => {
198
273
  if (res?.error)
199
274
  console.log('[viewer] chat send error', res.error);
@@ -201,8 +276,9 @@ export const LiveStreamViewerItem = memo(function LiveStreamViewerItem({ stream,
201
276
  };
202
277
  const hostLabel = stream.hostDisplayName || stream.title || 'Live';
203
278
  const avatarLetter = hostLabel[0]?.toUpperCase() ?? 'L';
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" }) })] })] }) })] }));
279
+ const hasUnsent = chatInput.trim().length > 0;
280
+ return (_jsxs(View, { style: styles.container, onLayout: onCellLayout, 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, hasUnsent && styles.typeTouchableFilled], onPress: () => { if (joined && !joining && !error)
281
+ setIsTyping(true); }, activeOpacity: 0.7, children: _jsx(Text, { style: [styles.typePlaceholder, hasUnsent && styles.typePlaceholderFilled], numberOfLines: 1, children: hasUnsent ? chatInput : (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, (!hasUnsent || !joined) && styles.sendBtnOff], onPress: sendChat, disabled: !joined || !hasUnsent, 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" })] }) }))] }));
206
282
  });
207
283
  // ─────────────────────────────────────────────────────────────────────────────
208
284
  const styles = StyleSheet.create({
@@ -306,7 +382,6 @@ const styles = StyleSheet.create({
306
382
  },
307
383
  actionIcon: { fontSize: 22, color: '#fff' },
308
384
  actionLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 11, fontWeight: '500' },
309
- // Chat column — absolute, bottom animated between default and typing positions
310
385
  chatColumn: {
311
386
  position: 'absolute',
312
387
  left: 12,
@@ -355,23 +430,29 @@ const styles = StyleSheet.create({
355
430
  borderWidth: StyleSheet.hairlineWidth,
356
431
  borderColor: 'rgba(255,255,255,0.18)',
357
432
  },
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,
433
+ typeTouchableFilled: {
434
+ backgroundColor: 'rgba(255,255,255,0.18)',
435
+ borderColor: 'rgba(255,255,255,0.30)',
364
436
  },
365
- inputModalBackdrop: {
366
- flex: 1,
437
+ typePlaceholder: {
438
+ color: 'rgba(255,255,255,0.50)',
439
+ fontSize: 14,
367
440
  },
441
+ typePlaceholderFilled: {
442
+ color: '#fff',
443
+ },
444
+ bottomIconBtn: { width: 40, height: 40, alignItems: 'center', justifyContent: 'center' },
445
+ bottomIconGlyph: { fontSize: 22 },
446
+ // Floating input — absolute, bottom driven by Animated.Value
368
447
  floatingInputWrap: {
448
+ position: 'absolute',
449
+ left: 0,
450
+ right: 0,
369
451
  flexDirection: 'row',
370
452
  alignItems: 'center',
371
453
  backgroundColor: 'rgba(20,20,20,0.97)',
372
454
  paddingHorizontal: 12,
373
455
  paddingVertical: 8,
374
- paddingBottom: BOTTOM_SAFE,
375
456
  gap: 8,
376
457
  borderTopWidth: StyleSheet.hairlineWidth,
377
458
  borderTopColor: 'rgba(255,255,255,0.10)',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kasunk99-livestream-core",
3
- "version": "0.3.15",
3
+ "version": "0.3.17",
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",