@codewithvincent/react-native-love-chat 0.1.0

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE +21 -0
  3. package/README.md +152 -0
  4. package/dist/Chat.d.ts +3 -0
  5. package/dist/Chat.js +78 -0
  6. package/dist/components/Bubble.d.ts +21 -0
  7. package/dist/components/Bubble.js +55 -0
  8. package/dist/components/Composer.d.ts +14 -0
  9. package/dist/components/Composer.js +44 -0
  10. package/dist/components/FooterReplyPreview.d.ts +7 -0
  11. package/dist/components/FooterReplyPreview.js +130 -0
  12. package/dist/components/Icons.d.ts +21 -0
  13. package/dist/components/Icons.js +71 -0
  14. package/dist/components/InputToolbar.d.ts +17 -0
  15. package/dist/components/InputToolbar.js +110 -0
  16. package/dist/components/Message.d.ts +21 -0
  17. package/dist/components/Message.js +131 -0
  18. package/dist/components/MessageList.d.ts +6 -0
  19. package/dist/components/MessageList.js +111 -0
  20. package/dist/components/MessageStatus.d.ts +6 -0
  21. package/dist/components/MessageStatus.js +59 -0
  22. package/dist/components/ReactionBubble.d.ts +55 -0
  23. package/dist/components/ReactionBubble.js +262 -0
  24. package/dist/components/ReplyPreview.d.ts +7 -0
  25. package/dist/components/ReplyPreview.js +102 -0
  26. package/dist/components/UploadFooter.d.ts +4 -0
  27. package/dist/components/UploadFooter.js +33 -0
  28. package/dist/components/adapters/UniversalAudio.d.ts +8 -0
  29. package/dist/components/adapters/UniversalAudio.js +23 -0
  30. package/dist/components/adapters/UniversalBlurView.d.ts +3 -0
  31. package/dist/components/adapters/UniversalBlurView.js +26 -0
  32. package/dist/components/adapters/UniversalVideo.d.ts +8 -0
  33. package/dist/components/adapters/UniversalVideo.js +20 -0
  34. package/dist/components/media/AudioCard.d.ts +7 -0
  35. package/dist/components/media/AudioCard.js +187 -0
  36. package/dist/components/media/FileCard.d.ts +8 -0
  37. package/dist/components/media/FileCard.js +68 -0
  38. package/dist/components/media/ImageCard.d.ts +9 -0
  39. package/dist/components/media/ImageCard.js +76 -0
  40. package/dist/components/media/VideoCard.d.ts +10 -0
  41. package/dist/components/media/VideoCard.js +232 -0
  42. package/dist/index.d.ts +9 -0
  43. package/dist/index.js +36 -0
  44. package/dist/types.d.ts +71 -0
  45. package/dist/types.js +2 -0
  46. package/dist/utils/index.d.ts +14 -0
  47. package/dist/utils/index.js +44 -0
  48. package/dist/utils/theme.d.ts +77 -0
  49. package/dist/utils/theme.js +55 -0
  50. package/package.json +67 -0
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = AudioCard;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const react_native_1 = require("react-native");
7
+ const utils_1 = require("../../utils");
8
+ const theme_1 = require("../../utils/theme");
9
+ const Icons_1 = require("../Icons");
10
+ const UniversalAudio_1 = require("../adapters/UniversalAudio");
11
+ function AudioCard({ uri, maxWidth, isMine }) {
12
+ const soundRef = (0, react_1.useRef)(null);
13
+ const intervalRef = (0, react_1.useRef)(null);
14
+ const progressAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
15
+ const totalDuration = (0, react_1.useRef)(0);
16
+ const [isPlaying, setIsPlaying] = (0, react_1.useState)(false);
17
+ const [isLoading, setIsLoading] = (0, react_1.useState)(false);
18
+ const [currentTime, setCurrentTime] = (0, react_1.useState)('0:00');
19
+ const theme = (0, theme_1.useTheme)();
20
+ (0, react_1.useEffect)(() => {
21
+ return () => {
22
+ cleanup();
23
+ };
24
+ }, []);
25
+ const cleanup = async () => {
26
+ var _a, _b;
27
+ if (UniversalAudio_1.AudioEngine.type === 'expo-audio' && soundRef.current) {
28
+ await ((_b = (_a = soundRef.current) === null || _a === void 0 ? void 0 : _a.stop) === null || _b === void 0 ? void 0 : _b.call(_a));
29
+ }
30
+ if (UniversalAudio_1.AudioEngine.type === 'rnsound' && soundRef.current) {
31
+ soundRef.current.release();
32
+ }
33
+ if (intervalRef.current)
34
+ clearInterval(intervalRef.current);
35
+ };
36
+ const formatTime = (sec) => {
37
+ const m = Math.floor(sec / 60);
38
+ const s = Math.floor(sec % 60);
39
+ return `${m}:${s < 10 ? '0' : ''}${s}`;
40
+ };
41
+ const onFinish = () => {
42
+ setIsPlaying(false);
43
+ progressAnim.setValue(0);
44
+ setCurrentTime('0:00');
45
+ if (intervalRef.current)
46
+ clearInterval(intervalRef.current);
47
+ };
48
+ const play = async () => {
49
+ var _a;
50
+ if (!uri)
51
+ return;
52
+ // 🔵 EXPO-AUDIO MODE
53
+ if (UniversalAudio_1.AudioEngine.type === 'expo-audio') {
54
+ try {
55
+ setIsLoading(true);
56
+ const { createAudioPlayer } = UniversalAudio_1.AudioEngine.ExpoAudio;
57
+ const player = createAudioPlayer({ uri });
58
+ soundRef.current = player;
59
+ player.play();
60
+ setIsPlaying(true);
61
+ setIsLoading(false);
62
+ totalDuration.current = (_a = player.duration) !== null && _a !== void 0 ? _a : 0;
63
+ intervalRef.current = setInterval(() => {
64
+ var _a;
65
+ const pos = (_a = player.currentTime) !== null && _a !== void 0 ? _a : 0;
66
+ if (totalDuration.current > 0) {
67
+ progressAnim.setValue(pos / totalDuration.current);
68
+ }
69
+ setCurrentTime(formatTime(pos));
70
+ if (player.didJustFinish) {
71
+ onFinish();
72
+ }
73
+ }, 200);
74
+ }
75
+ catch (e) {
76
+ utils_1.Logger.error('expo-audio error', e);
77
+ setIsLoading(false);
78
+ }
79
+ return;
80
+ }
81
+ // 🟠 RN SOUND MODE
82
+ if (UniversalAudio_1.AudioEngine.type === 'rnsound') {
83
+ const Sound = UniversalAudio_1.AudioEngine.RNSound;
84
+ if (!soundRef.current) {
85
+ setIsLoading(true);
86
+ const sound = new Sound(uri, '', (err) => {
87
+ setIsLoading(false);
88
+ if (err) {
89
+ utils_1.Logger.error('RN Sound error', err);
90
+ return;
91
+ }
92
+ soundRef.current = sound;
93
+ totalDuration.current = sound.getDuration();
94
+ sound.play(onFinish);
95
+ setIsPlaying(true);
96
+ intervalRef.current = setInterval(() => {
97
+ sound.getCurrentTime((sec) => {
98
+ if (totalDuration.current > 0) {
99
+ progressAnim.setValue(sec / totalDuration.current);
100
+ }
101
+ setCurrentTime(formatTime(sec));
102
+ });
103
+ }, 200);
104
+ });
105
+ return;
106
+ }
107
+ soundRef.current.play(onFinish);
108
+ setIsPlaying(true);
109
+ return;
110
+ }
111
+ utils_1.Logger.error('No audio engine available.');
112
+ };
113
+ const pause = async () => {
114
+ var _a, _b;
115
+ if (!soundRef.current)
116
+ return;
117
+ if (UniversalAudio_1.AudioEngine.type === 'expo-audio') {
118
+ (_b = (_a = soundRef.current).pause) === null || _b === void 0 ? void 0 : _b.call(_a);
119
+ }
120
+ if (UniversalAudio_1.AudioEngine.type === 'rnsound') {
121
+ soundRef.current.pause();
122
+ }
123
+ setIsPlaying(false);
124
+ if (intervalRef.current)
125
+ clearInterval(intervalRef.current);
126
+ };
127
+ const progressWidth = progressAnim.interpolate({
128
+ inputRange: [0, 1],
129
+ outputRange: ['0%', '100%'],
130
+ });
131
+ const cardBackgroundColor = isMine ? theme.colors.ownFileBg : theme.colors.otherFileBg;
132
+ const textColor = isMine ? theme.colors.ownMessageText : theme.colors.otherMessageText;
133
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
134
+ styles.container,
135
+ {
136
+ maxWidth: maxWidth || utils_1.appSize.width(60),
137
+ backgroundColor: cardBackgroundColor
138
+ },
139
+ ], children: [(0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: [styles.playButton, { backgroundColor: theme.colors.darkRed }], onPress: isPlaying ? pause : play, disabled: isLoading, children: isLoading ? ((0, jsx_runtime_1.jsx)(react_native_1.ActivityIndicator, { size: "small", color: "#fff" })) : isPlaying ? ((0, jsx_runtime_1.jsx)(Icons_1.PauseIcon, { size: 18, color: "#fff" })) : ((0, jsx_runtime_1.jsx)(Icons_1.PlayIcon, { size: 18, color: "#fff" })) }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.middle, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.progressBackground }), (0, jsx_runtime_1.jsx)(react_native_1.Animated.View, { style: [
140
+ styles.progressForeground,
141
+ { width: progressWidth, backgroundColor: theme.colors.darkRed },
142
+ ] })] }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.timeText, { color: textColor }], children: currentTime })] }));
143
+ }
144
+ const styles = react_native_1.StyleSheet.create({
145
+ container: {
146
+ flexDirection: 'row',
147
+ minWidth: utils_1.appSize.width(60),
148
+ alignItems: 'center',
149
+ backgroundColor: theme_1.defaultTheme.colors.white,
150
+ paddingVertical: 8,
151
+ paddingHorizontal: 10,
152
+ borderRadius: utils_1.appSize.width(2),
153
+ flexShrink: 1,
154
+ },
155
+ playButton: {
156
+ width: 38,
157
+ height: 38,
158
+ borderRadius: 19,
159
+ backgroundColor: theme_1.defaultTheme.colors.darkRed,
160
+ justifyContent: 'center',
161
+ alignItems: 'center',
162
+ marginRight: 12,
163
+ },
164
+ timeText: {
165
+ marginLeft: 12,
166
+ width: 45,
167
+ fontSize: 12,
168
+ color: '#666',
169
+ textAlign: 'right',
170
+ },
171
+ middle: {
172
+ flex: 1,
173
+ justifyContent: 'center',
174
+ height: 4,
175
+ borderRadius: 2,
176
+ backgroundColor: '#e0e0e0',
177
+ overflow: 'hidden',
178
+ },
179
+ progressBackground: {
180
+ ...react_native_1.StyleSheet.absoluteFillObject,
181
+ backgroundColor: '#e0e0e0',
182
+ },
183
+ progressForeground: {
184
+ ...react_native_1.StyleSheet.absoluteFillObject,
185
+ backgroundColor: theme_1.defaultTheme.colors.darkRed,
186
+ },
187
+ });
@@ -0,0 +1,8 @@
1
+ interface FileCardProps {
2
+ fileName: string;
3
+ color?: string;
4
+ isMine?: boolean;
5
+ time?: string;
6
+ }
7
+ export default function FileCard({ fileName, isMine, time, }: FileCardProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = FileCard;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_native_1 = require("react-native");
6
+ const utils_1 = require("../../utils");
7
+ const theme_1 = require("../../utils/theme");
8
+ const Icons_1 = require("../Icons");
9
+ function FileCard({ fileName, isMine = false, time, }) {
10
+ const theme = (0, theme_1.useTheme)();
11
+ // WhatsApp-style colors
12
+ // Sent: Lighter green bubble, file card is slightly darker green/transparent
13
+ // Received: White bubble, file card is light gray
14
+ const cardBackgroundColor = isMine ? theme.colors.ownFileBg : theme.colors.otherFileBg;
15
+ const textColor = isMine ? theme.colors.ownMessageText : theme.colors.otherMessageText;
16
+ const subTextColor = isMine ? 'rgba(255, 255, 255, 0.7)' : 'rgba(0, 0, 0, 0.5)';
17
+ const iconColor = isMine ? theme.colors.white : theme.colors.darkRed;
18
+ const getExtension = (name) => {
19
+ var _a;
20
+ return ((_a = name.split('.').pop()) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'FILE';
21
+ };
22
+ const extension = getExtension(fileName);
23
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.container, children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [styles.card, { backgroundColor: cardBackgroundColor }], children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.iconContainer, children: [(0, jsx_runtime_1.jsx)(Icons_1.FileIcon, { size: 24, color: iconColor }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.extensionText, { color: iconColor }], children: extension })] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.infoContainer, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.fileName, { color: textColor }], numberOfLines: 1, ellipsizeMode: "middle", children: fileName }), (0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: [styles.fileSize, { color: subTextColor }], children: ["1.2 MB \u2022 ", extension] })] })] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.footer, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.timeText, { color: subTextColor }], children: time }) })] }));
24
+ }
25
+ const styles = react_native_1.StyleSheet.create({
26
+ container: {
27
+ minWidth: utils_1.appSize.width(60),
28
+ maxWidth: utils_1.appSize.width(75),
29
+ },
30
+ card: {
31
+ flexDirection: 'row',
32
+ alignItems: 'center',
33
+ borderRadius: 8,
34
+ padding: 10,
35
+ marginBottom: 4,
36
+ },
37
+ iconContainer: {
38
+ alignItems: 'center',
39
+ justifyContent: 'center',
40
+ marginRight: 10,
41
+ },
42
+ extensionText: {
43
+ fontSize: 8,
44
+ fontWeight: 'bold',
45
+ marginTop: 2,
46
+ },
47
+ infoContainer: {
48
+ flex: 1,
49
+ justifyContent: 'center',
50
+ },
51
+ fileName: {
52
+ fontSize: 14,
53
+ fontWeight: '500',
54
+ marginBottom: 2,
55
+ },
56
+ fileSize: {
57
+ fontSize: 11,
58
+ },
59
+ footer: {
60
+ flexDirection: 'row',
61
+ justifyContent: 'flex-end',
62
+ paddingRight: 4,
63
+ paddingBottom: 2,
64
+ },
65
+ timeText: {
66
+ fontSize: 10,
67
+ }
68
+ });
@@ -0,0 +1,9 @@
1
+ interface ImageCardProps {
2
+ uri: string;
3
+ maxWidth?: number;
4
+ time?: string;
5
+ isFullScreen?: boolean;
6
+ setFullScreen?: any;
7
+ }
8
+ export default function ImageCard({ uri, maxWidth, time, isFullScreen, setFullScreen, }: ImageCardProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = ImageCard;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const react_native_1 = require("react-native");
7
+ const utils_1 = require("../../utils");
8
+ const Icons_1 = require("../Icons");
9
+ const react_native_safe_area_context_1 = require("react-native-safe-area-context");
10
+ const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = react_native_1.Dimensions.get('window');
11
+ const ImageComponent = react_native_1.Image;
12
+ function ImageCard({ uri, maxWidth, time, isFullScreen, setFullScreen, }) {
13
+ const inset = (0, react_native_safe_area_context_1.useSafeAreaInsets)();
14
+ const bubbleWidth = maxWidth || utils_1.appSize.width(65);
15
+ const [bubbleHeight, setBubbleHeight] = (0, react_1.useState)(bubbleWidth * 0.75);
16
+ (0, react_1.useEffect)(() => {
17
+ react_native_1.Image.getSize(uri, (w, h) => {
18
+ const ratio = h / w;
19
+ const height = bubbleWidth * ratio;
20
+ const minH = 120;
21
+ const maxH = 400;
22
+ setBubbleHeight(Math.min(Math.max(height, minH), maxH));
23
+ }, () => {
24
+ setBubbleHeight(bubbleWidth * 0.75);
25
+ });
26
+ }, [uri]);
27
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
28
+ styles.container,
29
+ { width: bubbleWidth, height: bubbleHeight },
30
+ ], children: [(0, jsx_runtime_1.jsx)(ImageComponent, { source: { uri }, style: styles.image, resizeMode: "cover" }), time && ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.timeOverlay, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.timeText, children: time }) }))] }), (0, jsx_runtime_1.jsx)(react_native_1.Modal, { visible: isFullScreen, transparent: false, onRequestClose: () => setFullScreen(false), animationType: "fade", children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.modal, children: [(0, jsx_runtime_1.jsx)(ImageComponent, { source: { uri }, style: styles.fullScreenImage, resizeMode: "contain" }), (0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: [
31
+ styles.closeButton,
32
+ { top: inset.top + 10 },
33
+ ], onPress: () => setFullScreen(false), children: (0, jsx_runtime_1.jsx)(Icons_1.CloseIcon, { size: 24, color: "#fff" }) })] }) })] }));
34
+ }
35
+ const styles = react_native_1.StyleSheet.create({
36
+ container: {
37
+ borderRadius: utils_1.appSize.width(2),
38
+ overflow: 'hidden',
39
+ backgroundColor: '#000',
40
+ margin: -2,
41
+ },
42
+ image: {
43
+ width: '100%',
44
+ height: '100%',
45
+ },
46
+ modal: {
47
+ flex: 1,
48
+ justifyContent: 'center',
49
+ alignItems: 'center',
50
+ backgroundColor: '#000',
51
+ },
52
+ fullScreenImage: {
53
+ width: SCREEN_WIDTH,
54
+ height: SCREEN_HEIGHT,
55
+ },
56
+ closeButton: {
57
+ position: 'absolute',
58
+ right: 20,
59
+ zIndex: 10,
60
+ padding: 10,
61
+ },
62
+ timeOverlay: {
63
+ position: 'absolute',
64
+ bottom: 5,
65
+ right: 5,
66
+ backgroundColor: 'rgba(0,0,0,0.5)',
67
+ borderRadius: 10,
68
+ paddingHorizontal: 6,
69
+ paddingVertical: 2,
70
+ },
71
+ timeText: {
72
+ color: '#fff',
73
+ fontSize: 10,
74
+ fontWeight: '600',
75
+ },
76
+ });
@@ -0,0 +1,10 @@
1
+ interface VideoMessageProps {
2
+ file: {
3
+ uri: string;
4
+ };
5
+ maxWidth?: number;
6
+ time?: string;
7
+ onLongPress?: () => void;
8
+ }
9
+ export default function VideoCard({ file, maxWidth, time, onLongPress, }: VideoMessageProps): import("react/jsx-runtime").JSX.Element;
10
+ export {};
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = VideoCard;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const react_native_1 = require("react-native");
7
+ const utils_1 = require("../../utils");
8
+ const theme_1 = require("../../utils/theme");
9
+ const Icons_1 = require("../Icons");
10
+ const react_native_safe_area_context_1 = require("react-native-safe-area-context");
11
+ const UniversalVideo_1 = require("../adapters/UniversalVideo");
12
+ const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = react_native_1.Dimensions.get('window');
13
+ const DEFAULT_ASPECT_RATIO = 16 / 9;
14
+ function VideoCard({ file, maxWidth, time, onLongPress, // ✅ ADDED
15
+ }) {
16
+ const inset = (0, react_native_safe_area_context_1.useSafeAreaInsets)();
17
+ const videoRef = (0, react_1.useRef)(null);
18
+ const theme = (0, theme_1.useTheme)();
19
+ const [isPlaying, setIsPlaying] = (0, react_1.useState)(false);
20
+ const [duration, setDuration] = (0, react_1.useState)(0);
21
+ const [current, setCurrent] = (0, react_1.useState)(0);
22
+ const [isFullScreen, setFullScreen] = (0, react_1.useState)(false);
23
+ const progressAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
24
+ const bubbleWidth = maxWidth || utils_1.appSize.width(65);
25
+ const initialHeight = Math.min(Math.max(bubbleWidth / DEFAULT_ASPECT_RATIO, 120), 350);
26
+ const [videoHeight, setVideoHeight] = (0, react_1.useState)(initialHeight);
27
+ (0, react_1.useEffect)(() => {
28
+ progressAnim.setValue(current / (duration || 1));
29
+ }, [current, duration]);
30
+ const formatTime = (secs) => {
31
+ if (!secs)
32
+ return '0:00';
33
+ const m = Math.floor(secs / 60);
34
+ const s = Math.floor(secs % 60);
35
+ return `${m}:${s < 10 ? '0' + s : s}`;
36
+ };
37
+ const progressWidth = progressAnim.interpolate({
38
+ inputRange: [0, 1],
39
+ outputRange: ['0%', '100%'],
40
+ });
41
+ if (UniversalVideo_1.VideoEngine.type === 'none') {
42
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
43
+ styles.wrapper,
44
+ {
45
+ width: bubbleWidth,
46
+ height: 120,
47
+ justifyContent: 'center',
48
+ alignItems: 'center',
49
+ backgroundColor: theme.colors.black,
50
+ },
51
+ ], children: [(0, jsx_runtime_1.jsx)(Icons_1.VideoIcon, { size: 40, color: theme.colors.white }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: { color: 'white', marginTop: 10 }, children: "Video not supported" })] }));
52
+ }
53
+ const VideoComponent = UniversalVideo_1.VideoEngine.type === 'expo-video'
54
+ ? UniversalVideo_1.VideoEngine.ExpoVideo.Video
55
+ : UniversalVideo_1.VideoEngine.RNVideo;
56
+ const togglePlayPause = () => {
57
+ setIsPlaying(prev => !prev);
58
+ };
59
+ const renderVideoPlayer = (isFull) => ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: isFull
60
+ ? styles.fullScreenWrapper
61
+ : [styles.videoArea, { width: bubbleWidth, height: videoHeight }], children: [(0, jsx_runtime_1.jsx)(VideoComponent, { ref: videoRef, source: { uri: file.uri }, style: isFull ? styles.fullScreenVideo : styles.video, resizeMode: isFull ? 'contain' : 'cover', paused: !isPlaying, onLoad: (meta) => {
62
+ var _a;
63
+ const dur = (_a = meta === null || meta === void 0 ? void 0 : meta.duration) !== null && _a !== void 0 ? _a : ((meta === null || meta === void 0 ? void 0 : meta.durationMillis)
64
+ ? meta.durationMillis / 1000
65
+ : 0);
66
+ setDuration(dur);
67
+ if (!isFull && (meta === null || meta === void 0 ? void 0 : meta.naturalSize)) {
68
+ const { width: w, height: h } = meta.naturalSize;
69
+ if (w && h) {
70
+ const ratio = h / w;
71
+ let finalHeight = bubbleWidth * ratio;
72
+ finalHeight = Math.min(Math.max(finalHeight, 120), 350);
73
+ setVideoHeight(finalHeight);
74
+ }
75
+ }
76
+ }, onProgress: (data) => {
77
+ const time = (data === null || data === void 0 ? void 0 : data.currentTime) ||
78
+ (data === null || data === void 0 ? void 0 : data.positionMillis) / 1000 ||
79
+ 0;
80
+ if (time !== undefined && time !== null) {
81
+ setCurrent(time);
82
+ }
83
+ }, onEnd: () => {
84
+ setIsPlaying(false);
85
+ setCurrent(duration);
86
+ progressAnim.setValue(1);
87
+ } }), (0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: react_native_1.StyleSheet.absoluteFill, onPress: togglePlayPause, onLongPress: onLongPress, delayLongPress: 250, activeOpacity: 1, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.centerButton, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.controlCircle, children: isPlaying ? ((0, jsx_runtime_1.jsx)(Icons_1.PauseIcon, { size: 24 })) : ((0, jsx_runtime_1.jsx)(Icons_1.PlayIcon, { size: 24 })) }) }) }), !isFull && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.progressContainer, children: (0, jsx_runtime_1.jsx)(react_native_1.Animated.View, { style: [
88
+ styles.progress,
89
+ {
90
+ width: progressWidth,
91
+ backgroundColor: theme.colors.darkRed,
92
+ },
93
+ ] }) }), time && ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.timeOverlay, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.timeText, children: time }) }))] })), isFull && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
94
+ styles.modalCloseButton,
95
+ { top: inset.top + 10 },
96
+ ], children: (0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { onPress: () => setFullScreen(false), children: (0, jsx_runtime_1.jsx)(Icons_1.CloseIcon, { size: 24, color: "#fff" }) }) }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.modalProgressWrapper, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.modalTime, children: formatTime(current) }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.modalProgressBackground, children: (0, jsx_runtime_1.jsx)(react_native_1.Animated.View, { style: [
97
+ styles.modalProgressForeground,
98
+ {
99
+ width: progressWidth,
100
+ backgroundColor: theme.colors.darkRed,
101
+ },
102
+ ] }) }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.modalTime, children: formatTime(duration) })] })] }))] }));
103
+ if (isFullScreen) {
104
+ return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: react_native_1.StyleSheet.absoluteFill, children: renderVideoPlayer(true) }));
105
+ }
106
+ return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.wrapper, { width: bubbleWidth }], children: renderVideoPlayer(false) }));
107
+ }
108
+ const styles = react_native_1.StyleSheet.create({
109
+ wrapper: {
110
+ borderRadius: utils_1.appSize.width(2),
111
+ overflow: 'hidden',
112
+ backgroundColor: theme_1.defaultTheme.colors.black,
113
+ margin: -2,
114
+ },
115
+ videoArea: {
116
+ borderRadius: utils_1.appSize.width(2),
117
+ overflow: 'hidden',
118
+ backgroundColor: theme_1.defaultTheme.colors.black,
119
+ },
120
+ video: {
121
+ width: '100%',
122
+ height: '100%',
123
+ },
124
+ bottomGradient: {
125
+ position: 'absolute',
126
+ bottom: 0,
127
+ width: '100%',
128
+ height: utils_1.appSize.height(5),
129
+ backgroundColor: 'rgba(0,0,0,0.35)',
130
+ justifyContent: 'flex-end',
131
+ paddingBottom: 6,
132
+ paddingHorizontal: 8,
133
+ },
134
+ durationBadge: {
135
+ flexDirection: 'row',
136
+ alignItems: 'center',
137
+ alignSelf: 'flex-end',
138
+ backgroundColor: 'rgba(0,0,0,0.55)',
139
+ paddingHorizontal: 8,
140
+ paddingVertical: 3,
141
+ borderRadius: 10,
142
+ },
143
+ durationText: {
144
+ color: theme_1.defaultTheme.colors.white,
145
+ fontSize: 13,
146
+ marginLeft: 5,
147
+ },
148
+ centerButton: {
149
+ position: 'absolute',
150
+ top: '40%',
151
+ left: '40%',
152
+ },
153
+ controlCircle: {
154
+ width: utils_1.appSize.width(10),
155
+ height: utils_1.appSize.width(10),
156
+ borderRadius: 31,
157
+ backgroundColor: 'rgba(0,0,0,0.55)',
158
+ justifyContent: 'center',
159
+ alignItems: 'center',
160
+ },
161
+ progressContainer: {
162
+ height: 4,
163
+ backgroundColor: 'rgba(255,255,255,0.2)',
164
+ justifyContent: 'center',
165
+ },
166
+ progressBackground: {
167
+ ...react_native_1.StyleSheet.absoluteFillObject,
168
+ backgroundColor: theme_1.defaultTheme.colors.black,
169
+ },
170
+ progress: {
171
+ height: 4,
172
+ backgroundColor: theme_1.defaultTheme.colors.darkRed,
173
+ },
174
+ fullScreenWrapper: {
175
+ width: SCREEN_WIDTH,
176
+ height: SCREEN_HEIGHT,
177
+ backgroundColor: '#000',
178
+ justifyContent: 'center',
179
+ alignItems: 'center',
180
+ },
181
+ fullScreenVideo: {
182
+ width: SCREEN_WIDTH,
183
+ height: SCREEN_HEIGHT,
184
+ },
185
+ modalPlayButton: {
186
+ position: 'absolute',
187
+ alignSelf: 'center',
188
+ top: '45%',
189
+ },
190
+ modalCloseButton: {
191
+ position: 'absolute',
192
+ right: 20,
193
+ zIndex: 9999,
194
+ },
195
+ modalProgressWrapper: {
196
+ position: 'absolute',
197
+ bottom: 40,
198
+ width: SCREEN_WIDTH - 40,
199
+ flexDirection: 'row',
200
+ alignItems: 'center',
201
+ },
202
+ modalProgressBackground: {
203
+ flex: 1,
204
+ height: 6,
205
+ backgroundColor: '#e0e0e0',
206
+ borderRadius: 3,
207
+ marginHorizontal: 10,
208
+ overflow: 'hidden',
209
+ },
210
+ modalProgressForeground: {
211
+ height: '100%',
212
+ backgroundColor: theme_1.defaultTheme.colors.darkRed,
213
+ },
214
+ modalTime: {
215
+ color: '#fff',
216
+ fontSize: 14,
217
+ },
218
+ timeOverlay: {
219
+ position: 'absolute',
220
+ bottom: 25, // Above the progress bar
221
+ right: 5,
222
+ backgroundColor: 'rgba(0,0,0,0.5)',
223
+ borderRadius: 10,
224
+ paddingHorizontal: 6,
225
+ paddingVertical: 2,
226
+ },
227
+ timeText: {
228
+ color: '#fff',
229
+ fontSize: 10,
230
+ fontWeight: '600',
231
+ },
232
+ });
@@ -0,0 +1,9 @@
1
+ export * from './types';
2
+ export * from './utils/theme';
3
+ export { default as Chat } from './Chat';
4
+ export { default as Message } from './components/Message';
5
+ export { default as MessageList } from './components/MessageList';
6
+ export { default as InputToolbar } from './components/InputToolbar';
7
+ export { default as Composer } from './components/Composer';
8
+ export { default as Bubble } from './components/Bubble';
9
+ export { default as MessageStatus } from './components/MessageStatus';
package/dist/index.js ADDED
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.MessageStatus = exports.Bubble = exports.Composer = exports.InputToolbar = exports.MessageList = exports.Message = exports.Chat = void 0;
21
+ __exportStar(require("./types"), exports);
22
+ __exportStar(require("./utils/theme"), exports);
23
+ var Chat_1 = require("./Chat");
24
+ Object.defineProperty(exports, "Chat", { enumerable: true, get: function () { return __importDefault(Chat_1).default; } });
25
+ var Message_1 = require("./components/Message");
26
+ Object.defineProperty(exports, "Message", { enumerable: true, get: function () { return __importDefault(Message_1).default; } });
27
+ var MessageList_1 = require("./components/MessageList");
28
+ Object.defineProperty(exports, "MessageList", { enumerable: true, get: function () { return __importDefault(MessageList_1).default; } });
29
+ var InputToolbar_1 = require("./components/InputToolbar");
30
+ Object.defineProperty(exports, "InputToolbar", { enumerable: true, get: function () { return __importDefault(InputToolbar_1).default; } });
31
+ var Composer_1 = require("./components/Composer");
32
+ Object.defineProperty(exports, "Composer", { enumerable: true, get: function () { return __importDefault(Composer_1).default; } });
33
+ var Bubble_1 = require("./components/Bubble");
34
+ Object.defineProperty(exports, "Bubble", { enumerable: true, get: function () { return __importDefault(Bubble_1).default; } });
35
+ var MessageStatus_1 = require("./components/MessageStatus");
36
+ Object.defineProperty(exports, "MessageStatus", { enumerable: true, get: function () { return __importDefault(MessageStatus_1).default; } });