@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,262 @@
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.computePositions = void 0;
40
+ exports.default = ReactionBubble;
41
+ const jsx_runtime_1 = require("react/jsx-runtime");
42
+ const react_1 = require("react");
43
+ const react_native_1 = require("react-native");
44
+ const react_native_reanimated_1 = __importStar(require("react-native-reanimated"));
45
+ const react_native_root_siblings_1 = __importDefault(require("react-native-root-siblings"));
46
+ const UniversalBlurView_1 = require("./../components/adapters/UniversalBlurView");
47
+ const utils_1 = require("../utils");
48
+ const theme_1 = require("../utils/theme");
49
+ const Icons_1 = require("./Icons");
50
+ const SCREEN = react_native_1.Dimensions.get('window');
51
+ const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
52
+ // Helper exported for tests (non-breaking)
53
+ const computePositions = ({ pos, reactionsCount, isMine, screenW, screenH, popupItemWidth = 42, popupHeight = 60, menuWidth = 200, menuItemCount = 3, menuItemHeight = 44, verticalGap = 12, horizontalMargin = 8, reactionCoreWidth = 28, reactionHorizontalPadding = 8, popupHorizontalPadding = 12, }) => {
54
+ // More accurate width estimation: per-reaction width + container paddings
55
+ const perReactionWidth = reactionCoreWidth + reactionHorizontalPadding * 2;
56
+ const emojiPanelWidth = reactionsCount * perReactionWidth + popupHorizontalPadding * 2;
57
+ let left = pos.x + pos.width / 2 - emojiPanelWidth / 2;
58
+ if (isMine) {
59
+ left = pos.x + pos.width - emojiPanelWidth - utils_1.appSize.width(6.5);
60
+ }
61
+ left = clamp(left, horizontalMargin, screenW - emojiPanelWidth - horizontalMargin);
62
+ let top = pos.y - popupHeight - verticalGap;
63
+ let emojiBelow = false;
64
+ if (top < horizontalMargin) {
65
+ top = pos.y + pos.height + verticalGap;
66
+ emojiBelow = true;
67
+ }
68
+ // Compute menu height based on visible items
69
+ const menuHeight = menuItemCount * menuItemHeight + verticalGap; // include gap/padding
70
+ // Default: menu below the message
71
+ let menuTop = emojiBelow ? top + popupHeight + verticalGap : pos.y + pos.height + verticalGap;
72
+ let menuLeft = isMine ? pos.x + pos.width - menuWidth : pos.x;
73
+ menuLeft = clamp(menuLeft, horizontalMargin, screenW - menuWidth - horizontalMargin);
74
+ // Check bottom space and reposition menu above if insufficient
75
+ const bottomSpace = screenH - menuTop;
76
+ const neededSpace = menuHeight + 16;
77
+ const placeAbove = bottomSpace < neededSpace;
78
+ if (placeAbove) {
79
+ menuTop = emojiBelow ? pos.y - menuHeight - verticalGap : top - menuHeight - verticalGap;
80
+ if (menuTop < horizontalMargin) {
81
+ menuTop = horizontalMargin;
82
+ }
83
+ }
84
+ return {
85
+ emojiPanel: { left, top, width: emojiPanelWidth, height: popupHeight },
86
+ menu: { left: menuLeft, top: menuTop, width: menuWidth, height: menuHeight, above: placeAbove },
87
+ };
88
+ };
89
+ exports.computePositions = computePositions;
90
+ function ReactionBubble({ reactions, selectedReaction, onReactionPress, style, bubbleStyle, reactionStyle, highlightColor, children, isMine, onPress, onLongPress, onReply, onDelete, onDownload, isFile, ...rest }) {
91
+ const triggerRef = (0, react_1.useRef)(null);
92
+ const siblingRef = (0, react_1.useRef)(null);
93
+ const theme = (0, theme_1.useTheme)();
94
+ const scale = (0, react_native_reanimated_1.useSharedValue)(0);
95
+ const animStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => ({
96
+ transform: [
97
+ {
98
+ scale: (0, react_native_reanimated_1.withSpring)(scale.value, {
99
+ damping: 12,
100
+ stiffness: 220,
101
+ mass: 0.25,
102
+ }),
103
+ },
104
+ ],
105
+ }));
106
+ const showPopup = (pos) => {
107
+ hidePopup();
108
+ scale.value = 0;
109
+ // Feature-detection gate: only use robust positioning when Dimensions exists and measureInWindow is available
110
+ const canMeasure = typeof react_native_1.UIManager.measureInWindow === 'function' && SCREEN && typeof SCREEN.width === 'number';
111
+ let left;
112
+ let top;
113
+ let menuLeft;
114
+ let menuTop;
115
+ const menuWidth = 200;
116
+ if (canMeasure) {
117
+ const positions = (0, exports.computePositions)({
118
+ pos,
119
+ reactionsCount: reactions.length,
120
+ isMine,
121
+ screenW: SCREEN.width,
122
+ screenH: SCREEN.height,
123
+ popupItemWidth: 42,
124
+ popupHeight: 60,
125
+ menuWidth,
126
+ menuItemCount: (onReply ? 1 : 0) + (isFile && onDownload ? 1 : 0) + (onDelete ? 1 : 0),
127
+ menuItemHeight: 44,
128
+ verticalGap: 12,
129
+ horizontalMargin: 8,
130
+ reactionCoreWidth: 28,
131
+ reactionHorizontalPadding: 8,
132
+ popupHorizontalPadding: 12,
133
+ });
134
+ left = positions.emojiPanel.left;
135
+ top = positions.emojiPanel.top;
136
+ menuLeft = positions.menu.left;
137
+ menuTop = positions.menu.top;
138
+ }
139
+ else {
140
+ // Fallback to previous behavior (unchanged)
141
+ const popupWidth = reactions.length * 42;
142
+ const popupHeight = 60;
143
+ const screenW = SCREEN.width;
144
+ top = pos.y - popupHeight - 12;
145
+ left = pos.x + pos.width / 2 - popupWidth / 2;
146
+ if (isMine) {
147
+ left = pos.x + pos.width - popupWidth - utils_1.appSize.width(6.5);
148
+ }
149
+ left = clamp(left, 10, screenW - popupWidth - 10);
150
+ menuTop = pos.y + pos.height + 10;
151
+ let _menuLeft = isMine ? (pos.x + pos.width - menuWidth) : pos.x;
152
+ menuLeft = clamp(_menuLeft, 10, screenW - menuWidth - 10);
153
+ }
154
+ siblingRef.current = new react_native_root_siblings_1.default(((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.overlayContainer, children: [(0, jsx_runtime_1.jsx)(react_native_1.Pressable, { style: react_native_1.StyleSheet.absoluteFill, onPress: hidePopup, children: (0, jsx_runtime_1.jsx)(UniversalBlurView_1.UniversalBlurView, { style: react_native_1.StyleSheet.absoluteFill, blurType: 'dark', blurAmount: 12, tint: "dark", intensity: 60, reducedTransparencyFallbackColor: "#000000CC" }) }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
155
+ styles.messageDuplicateWrapper,
156
+ {
157
+ top: pos.y,
158
+ left: pos.x,
159
+ width: pos.width,
160
+ },
161
+ ], children: children }), (0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { entering: react_native_reanimated_1.ZoomIn.duration(100), exiting: react_native_reanimated_1.ZoomOut.duration(100), style: {
162
+ position: 'absolute',
163
+ top,
164
+ left,
165
+ }, children: (0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { style: [styles.popup, bubbleStyle, animStyle], children: reactions.map((r, i) => ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: () => handleReactionPress(r), children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.reaction, reactionStyle], children: r }) }, i))) }) }), (0, jsx_runtime_1.jsxs)(react_native_reanimated_1.default.View, { entering: react_native_reanimated_1.ZoomIn.duration(150).delay(50), exiting: react_native_reanimated_1.ZoomOut.duration(150), style: [
166
+ styles.contextMenu,
167
+ {
168
+ top: menuTop,
169
+ left: menuLeft,
170
+ width: menuWidth,
171
+ }
172
+ ], children: [onReply && ((0, jsx_runtime_1.jsxs)(react_native_1.Pressable, { style: styles.menuItem, onPress: () => { hidePopup(); onReply(); }, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.menuText, children: "Reply" }), (0, jsx_runtime_1.jsx)(Icons_1.ReplyIcon, { size: 18, color: "#fff" })] })), isFile && onDownload && ((0, jsx_runtime_1.jsxs)(react_native_1.Pressable, { style: styles.menuItem, onPress: () => { hidePopup(); onDownload(); }, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.menuText, children: "Download" }), (0, jsx_runtime_1.jsx)(Icons_1.DownloadIcon, { size: 18, color: "#fff" })] })), onDelete && ((0, jsx_runtime_1.jsxs)(react_native_1.Pressable, { style: [styles.menuItem, { borderBottomWidth: 0 }], onPress: () => { hidePopup(); onDelete(); }, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.menuText, { color: theme_1.defaultTheme.colors.darkRed }], children: "Delete" }), (0, jsx_runtime_1.jsx)(Icons_1.TrashIcon, { size: 18 })] }))] })] })));
173
+ requestAnimationFrame(() => {
174
+ scale.value = 1;
175
+ });
176
+ };
177
+ const hidePopup = () => {
178
+ if (siblingRef.current) {
179
+ siblingRef.current.destroy();
180
+ siblingRef.current = null;
181
+ }
182
+ };
183
+ const handleLongPress = () => {
184
+ const node = (0, react_native_1.findNodeHandle)(triggerRef.current);
185
+ if (!node)
186
+ return;
187
+ react_native_1.UIManager.measureInWindow(node, (x, y, width, height) => {
188
+ showPopup({ x, y, width, height });
189
+ });
190
+ if (onLongPress) {
191
+ onLongPress();
192
+ }
193
+ };
194
+ const handleReactionPress = (reaction) => {
195
+ hidePopup();
196
+ onReactionPress(reaction);
197
+ };
198
+ (0, react_1.useEffect)(() => {
199
+ return hidePopup;
200
+ }, []);
201
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { children: [(0, jsx_runtime_1.jsx)(react_native_1.Pressable, { ref: triggerRef, onPress: onPress, onLongPress: handleLongPress, style: ({ pressed }) => [
202
+ style,
203
+ {
204
+ backgroundColor: pressed ? highlightColor : style === null || style === void 0 ? void 0 : style.backgroundColor,
205
+ },
206
+ ], ...rest, children: children }), selectedReaction && ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: () => onReactionPress(undefined), children: (0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { style: [styles.miniReaction, { backgroundColor: theme.colors.primary }], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { children: selectedReaction }) }) }))] }));
207
+ }
208
+ const styles = react_native_1.StyleSheet.create({
209
+ overlayContainer: {
210
+ ...react_native_1.StyleSheet.absoluteFillObject,
211
+ zIndex: 99999,
212
+ },
213
+ messageDuplicateWrapper: {
214
+ position: 'absolute',
215
+ zIndex: 50,
216
+ },
217
+ popup: {
218
+ flexDirection: 'row',
219
+ paddingVertical: 8,
220
+ paddingHorizontal: 12,
221
+ borderRadius: 40,
222
+ backgroundColor: '#202A30',
223
+ zIndex: 100,
224
+ },
225
+ reaction: {
226
+ fontSize: 28,
227
+ paddingHorizontal: 8,
228
+ },
229
+ miniReaction: {
230
+ marginStart: 8,
231
+ padding: 4,
232
+ borderRadius: 100,
233
+ width: utils_1.appSize.width(8),
234
+ height: utils_1.appSize.width(8),
235
+ backgroundColor: theme_1.defaultTheme.colors.primary,
236
+ top: -4,
237
+ alignItems: 'center',
238
+ justifyContent: 'center',
239
+ },
240
+ contextMenu: {
241
+ position: 'absolute',
242
+ backgroundColor: 'rgba(30, 30, 30, 0.95)',
243
+ borderRadius: 12,
244
+ paddingVertical: 4,
245
+ width: 200,
246
+ zIndex: 100,
247
+ },
248
+ menuItem: {
249
+ flexDirection: 'row',
250
+ alignItems: 'center',
251
+ justifyContent: 'space-between',
252
+ paddingVertical: 12,
253
+ paddingHorizontal: 16,
254
+ borderBottomWidth: react_native_1.StyleSheet.hairlineWidth,
255
+ borderBottomColor: 'rgba(255, 255, 255, 0.1)',
256
+ },
257
+ menuText: {
258
+ color: '#fff',
259
+ fontSize: 16,
260
+ fontWeight: '400',
261
+ },
262
+ });
@@ -0,0 +1,7 @@
1
+ import { IMessage } from '../types';
2
+ interface ReplyPreviewProps {
3
+ replyTo: IMessage;
4
+ maxMediaWidth?: number;
5
+ }
6
+ declare const ReplyPreview: ({ replyTo }: ReplyPreviewProps) => import("react/jsx-runtime").JSX.Element;
7
+ export default ReplyPreview;
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const jsx_runtime_1 = require("react/jsx-runtime");
4
+ const react_native_1 = require("react-native");
5
+ const utils_1 = require("../utils");
6
+ const theme_1 = require("../utils/theme");
7
+ const Icons_1 = require("./Icons");
8
+ const ReplyPreview = ({ replyTo }) => {
9
+ var _a, _b;
10
+ const theme = (0, theme_1.useTheme)();
11
+ if (!replyTo)
12
+ return null;
13
+ const senderName = ((_a = replyTo.user) === null || _a === void 0 ? void 0 : _a.name) || 'Unknown';
14
+ const fileType = (_b = replyTo.fileType) === null || _b === void 0 ? void 0 : _b.toLowerCase();
15
+ const caption = replyTo.caption || '';
16
+ const text = replyTo.text || '';
17
+ const getFileNameFromUrl = (url) => {
18
+ console.log('url', url);
19
+ if (!url)
20
+ return undefined;
21
+ try {
22
+ const clean = url.split('?')[0];
23
+ const parts = clean.split('/');
24
+ const last = parts[parts.length - 1];
25
+ return last || undefined;
26
+ }
27
+ catch {
28
+ return undefined;
29
+ }
30
+ };
31
+ const getExtension = (name) => {
32
+ if (!name)
33
+ return undefined;
34
+ const ext = name.split('.').pop();
35
+ return ext ? ext.toLowerCase() : undefined;
36
+ };
37
+ const computedFileName = replyTo.fileName || getFileNameFromUrl(replyTo.fileUrl);
38
+ const extension = getExtension(computedFileName);
39
+ const isMedia = fileType === 'image' || fileType === 'video' || !!replyTo.image || !!replyTo.video;
40
+ const isFile = !!computedFileName || fileType === 'audio' || fileType === 'file' || !!replyTo.audio;
41
+ const renderMedia = () => ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.mediaRow, children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.textWrapper, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { numberOfLines: 1, style: [styles.senderName, { color: theme.colors.darkPrimary }], children: senderName }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: !caption && fileType ? { minWidth: '100%' } : null, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { numberOfLines: 1, style: [styles.captionText, { color: theme.colors.gray }], children: caption || (fileType === 'image' || replyTo.image ? 'Photo' : 'Video') }) })] }), fileType === 'image' || replyTo.image ? ((0, jsx_runtime_1.jsx)(react_native_1.Image, { source: { uri: replyTo.image || replyTo.fileUrl }, style: styles.mediaThumb, resizeMode: "cover" })) : ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.mediaThumb, children: (0, jsx_runtime_1.jsx)(Icons_1.VideoIcon, { size: 30, color: theme.colors.white }) }))] }));
42
+ const renderFileRow = () => ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.fileRow, children: [fileType === 'audio' ||
43
+ replyTo.audio ||
44
+ (extension && ['mp3', 'wav', 'm4a', 'aac', 'ogg'].includes(extension)) ? ((0, jsx_runtime_1.jsx)(Icons_1.AudioIcon, { size: 18, color: theme.colors.gray })) : ((0, jsx_runtime_1.jsx)(Icons_1.FileIcon, { size: 18, color: theme.colors.gray })), (0, jsx_runtime_1.jsx)(react_native_1.Text, { numberOfLines: 1, style: [styles.fileName, { color: theme.colors.gray }], children: computedFileName || (extension ? extension.toUpperCase() : fileType || 'File') })] }));
45
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [styles.container, { backgroundColor: theme.colors.softRed }], children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.borderIndicator, { backgroundColor: theme.colors.primary }] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.textWrapper, children: isMedia ? (renderMedia()) : isFile ? (renderFileRow()) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { numberOfLines: 1, style: [styles.senderName, { color: theme.colors.darkPrimary }], children: senderName }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { numberOfLines: 1, style: [styles.textSnippet, { color: theme.colors.darkBrown }], children: text })] })) })] }));
46
+ };
47
+ const styles = react_native_1.StyleSheet.create({
48
+ container: {
49
+ flexDirection: 'row',
50
+ backgroundColor: theme_1.defaultTheme.colors.softRed,
51
+ padding: utils_1.appSize.width(1.5),
52
+ borderRadius: utils_1.appSize.width(2),
53
+ width: 'auto',
54
+ maxWidth: '100%',
55
+ marginVertical: utils_1.appSize.height(0.5),
56
+ },
57
+ borderIndicator: {
58
+ width: 3,
59
+ backgroundColor: theme_1.defaultTheme.colors.primary,
60
+ borderRadius: 3,
61
+ marginRight: 8,
62
+ },
63
+ textWrapper: {
64
+ flexShrink: 1,
65
+ },
66
+ senderName: {
67
+ fontSize: utils_1.appSize.font(11),
68
+ fontWeight: '600',
69
+ color: theme_1.defaultTheme.colors.darkPrimary,
70
+ },
71
+ textSnippet: {
72
+ fontSize: utils_1.appSize.font(13),
73
+ color: theme_1.defaultTheme.colors.darkBrown,
74
+ },
75
+ mediaRow: {
76
+ flexDirection: 'row',
77
+ alignItems: 'center',
78
+ },
79
+ captionText: {
80
+ fontSize: utils_1.appSize.font(12),
81
+ color: theme_1.defaultTheme.colors.gray,
82
+ },
83
+ mediaThumb: {
84
+ width: utils_1.appSize.width(10),
85
+ height: utils_1.appSize.width(10),
86
+ borderRadius: 6,
87
+ marginLeft: 10,
88
+ overflow: 'hidden',
89
+ backgroundColor: '#ccc',
90
+ justifyContent: 'center',
91
+ alignItems: 'center',
92
+ },
93
+ fileRow: {
94
+ flexDirection: 'row',
95
+ alignItems: 'center',
96
+ },
97
+ fileName: {
98
+ marginLeft: 6,
99
+ color: theme_1.defaultTheme.colors.gray,
100
+ },
101
+ });
102
+ exports.default = ReplyPreview;
@@ -0,0 +1,4 @@
1
+ declare const UploadFooter: ({ onActionPress, }: {
2
+ onActionPress: (type: string) => void;
3
+ }) => import("react/jsx-runtime").JSX.Element;
4
+ export default UploadFooter;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const jsx_runtime_1 = require("react/jsx-runtime");
4
+ const react_native_1 = require("react-native");
5
+ const react_native_safe_area_context_1 = require("react-native-safe-area-context");
6
+ const utils_1 = require("../utils");
7
+ const theme_1 = require("../utils/theme");
8
+ const Icons_1 = require("./Icons");
9
+ const uploadOptions = [
10
+ { id: '1', label: 'Image', icon: Icons_1.ImageIcon },
11
+ { id: '2', label: 'Video', icon: Icons_1.VideoIcon },
12
+ { id: '3', label: 'Document', icon: Icons_1.FileIcon },
13
+ ];
14
+ const UploadFooter = ({ onActionPress, }) => {
15
+ const inset = (0, react_native_safe_area_context_1.useSafeAreaInsets)();
16
+ const theme = (0, theme_1.useTheme)();
17
+ return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.container, { backgroundColor: theme.colors.verySoftGray }, { paddingBottom: inset.bottom + 20 }], children: uploadOptions.map(item => {
18
+ const Icon = item.icon;
19
+ return ((0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: styles.iconBox, onPress: () => onActionPress(item.label), children: (0, jsx_runtime_1.jsx)(Icon, { size: 22, color: theme.colors.darkRed }) }, item.id));
20
+ }) }));
21
+ };
22
+ const styles = react_native_1.StyleSheet.create({
23
+ container: {
24
+ flexDirection: 'row',
25
+ padding: utils_1.appSize.width(3),
26
+ backgroundColor: theme_1.defaultTheme.colors.verySoftGray,
27
+ justifyContent: 'space-between',
28
+ },
29
+ iconBox: {
30
+ padding: 10,
31
+ },
32
+ });
33
+ exports.default = UploadFooter;
@@ -0,0 +1,8 @@
1
+ type EngineType = 'expo-audio' | 'rnsound' | 'none';
2
+ export interface AudioEngineType {
3
+ type: EngineType;
4
+ ExpoAudio: any;
5
+ RNSound: any;
6
+ }
7
+ export declare const AudioEngine: AudioEngineType;
8
+ export {};
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ // src/adapters/UniversalAudio.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.AudioEngine = void 0;
5
+ let ExpoAudio = null;
6
+ let RNSound = null;
7
+ // Try expo-audio first
8
+ try {
9
+ ExpoAudio = require('expo-audio');
10
+ }
11
+ catch { }
12
+ // Fallback to react-native-sound
13
+ if (!ExpoAudio) {
14
+ try {
15
+ RNSound = require('react-native-sound');
16
+ }
17
+ catch { }
18
+ }
19
+ exports.AudioEngine = {
20
+ type: ExpoAudio ? 'expo-audio' : RNSound ? 'rnsound' : 'none',
21
+ ExpoAudio,
22
+ RNSound,
23
+ };
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import { ViewProps } from 'react-native';
3
+ export declare const UniversalBlurView: React.FC<ViewProps & any>;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UniversalBlurView = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_native_1 = require("react-native");
6
+ let NativeBlurView = null;
7
+ // Try Expo Blur first (Expo Go / Expo Dev Client)
8
+ try {
9
+ NativeBlurView = require('expo-blur').BlurView;
10
+ }
11
+ catch { }
12
+ // Fallback to community blur (Bare RN)
13
+ if (!NativeBlurView) {
14
+ try {
15
+ NativeBlurView = require('@react-native-community/blur').BlurView;
16
+ }
17
+ catch { }
18
+ }
19
+ const UniversalBlurView = (props) => {
20
+ if (NativeBlurView) {
21
+ return (0, jsx_runtime_1.jsx)(NativeBlurView, { ...props });
22
+ }
23
+ // Final fallback (no blur installed)
24
+ return (0, jsx_runtime_1.jsx)(react_native_1.View, { ...props });
25
+ };
26
+ exports.UniversalBlurView = UniversalBlurView;
@@ -0,0 +1,8 @@
1
+ type EngineType = 'expo-video' | 'rn-video' | 'none';
2
+ export interface VideoEngineType {
3
+ type: EngineType;
4
+ ExpoVideo: any;
5
+ RNVideo: any;
6
+ }
7
+ export declare const VideoEngine: VideoEngineType;
8
+ export {};
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VideoEngine = void 0;
4
+ let ExpoVideo = null;
5
+ let RNVideo = null;
6
+ try {
7
+ ExpoVideo = require('expo-video');
8
+ }
9
+ catch { }
10
+ if (!ExpoVideo) {
11
+ try {
12
+ RNVideo = require('react-native-video').default;
13
+ }
14
+ catch { }
15
+ }
16
+ exports.VideoEngine = {
17
+ type: ExpoVideo ? 'expo-video' : RNVideo ? 'rn-video' : 'none',
18
+ ExpoVideo,
19
+ RNVideo,
20
+ };
@@ -0,0 +1,7 @@
1
+ interface AudioMessageProps {
2
+ uri: string;
3
+ maxWidth?: number;
4
+ isMine?: boolean;
5
+ }
6
+ export default function AudioCard({ uri, maxWidth, isMine }: AudioMessageProps): import("react/jsx-runtime").JSX.Element;
7
+ export {};