@samirify/messenger 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.
@@ -0,0 +1,644 @@
1
+ import { View, TouchableOpacity, Text, ScrollView, ActivityIndicator, RefreshControl, Image, TextInput, Platform, KeyboardAvoidingView, FlatList } from 'react-native';
2
+ import { createContext, useState, useRef, useContext } from 'react';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+ import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
5
+
6
+ // src/native/InboxScreen.tsx
7
+ var MessengerContext = createContext(null);
8
+ function useMessengerConfig() {
9
+ const ctx = useContext(MessengerContext);
10
+ if (!ctx) {
11
+ throw new Error(
12
+ "@gymlium/messenger: useMessengerConfig must be used within MessengerProvider"
13
+ );
14
+ }
15
+ return ctx;
16
+ }
17
+
18
+ // src/api.ts
19
+ async function listThreads(http) {
20
+ const res = await http.getJson(
21
+ "/api/messages/threads"
22
+ );
23
+ return { ...res, data: res.data?.threads ?? [] };
24
+ }
25
+ async function openThread(http, clientUserId) {
26
+ return http.postJson("/api/messages/threads", {
27
+ ...clientUserId ? { client_user_id: clientUserId } : {}
28
+ });
29
+ }
30
+ async function getThread(http, threadId, limit = 50, beforeId) {
31
+ const params = new URLSearchParams({ limit: String(limit) });
32
+ return http.getJson(`/api/messages/threads/${threadId}?${params.toString()}`);
33
+ }
34
+ async function sendMessage(http, threadId, body, image) {
35
+ const path = `/api/messages/threads/${threadId}/messages`;
36
+ if (image?.uri || image?.file) {
37
+ const form = new FormData();
38
+ if (body.trim()) form.append("body", body.trim());
39
+ const filename = image.name ?? image.uri?.split("/").pop() ?? "photo.jpg";
40
+ const match = /\.(\w+)$/.exec(filename);
41
+ const type = image.type ?? (match ? `image/${match[1]}` : "image/jpeg");
42
+ if (image.file) {
43
+ form.append("image", image.file, filename);
44
+ } else if (image.uri.startsWith("blob:") || image.uri.startsWith("data:")) {
45
+ const blob = await fetch(image.uri).then((r) => r.blob());
46
+ form.append("image", blob, filename);
47
+ } else {
48
+ form.append("image", {
49
+ uri: image.uri,
50
+ name: filename,
51
+ type
52
+ });
53
+ }
54
+ return http.postForm(path, form);
55
+ }
56
+ return http.postJson(path, { body });
57
+ }
58
+
59
+ // src/queryKeys.ts
60
+ var messengerKeys = {
61
+ all: ["messenger"],
62
+ threads: () => [...messengerKeys.all, "threads"],
63
+ thread: (id) => [...messengerKeys.all, "thread", id]
64
+ };
65
+
66
+ // src/hooks/useThreads.ts
67
+ function useThreads(options) {
68
+ const { http, enabled: featureOn } = useMessengerConfig();
69
+ const enabled = featureOn;
70
+ return useQuery({
71
+ queryKey: messengerKeys.threads(),
72
+ queryFn: async () => {
73
+ const res = await listThreads(http);
74
+ if (!res.success) {
75
+ throw new Error(res.message ?? "Failed to load threads");
76
+ }
77
+ return res.data;
78
+ },
79
+ enabled
80
+ });
81
+ }
82
+ function useOpenThread() {
83
+ const { http, navigation } = useMessengerConfig();
84
+ const qc = useQueryClient();
85
+ return useMutation({
86
+ mutationFn: async (clientUserId) => {
87
+ const res = await openThread(http, clientUserId);
88
+ if (!res.success || !res.data?.thread?.id) {
89
+ throw new Error(res.message ?? "Could not open thread");
90
+ }
91
+ return res.data.thread;
92
+ },
93
+ onSuccess: (thread) => {
94
+ void qc.invalidateQueries({ queryKey: messengerKeys.threads() });
95
+ navigation.openThread(thread.id);
96
+ }
97
+ });
98
+ }
99
+
100
+ // src/i18nKeys.ts
101
+ var MessengerI18n = {
102
+ off: {
103
+ key: "mobile.messenger.off",
104
+ fallback: "Messaging is not enabled for this account."
105
+ },
106
+ inbox: { key: "mobile.messenger.inbox", fallback: "Chat" },
107
+ chat: { key: "mobile.messenger.chat", fallback: "Chat" },
108
+ messageCoach: {
109
+ key: "mobile.messenger.messageCoach",
110
+ fallback: "Message your coach"
111
+ },
112
+ empty: {
113
+ key: "mobile.messenger.empty",
114
+ fallback: "No conversations yet"
115
+ },
116
+ yourCoach: {
117
+ key: "mobile.messenger.yourCoach",
118
+ fallback: "Your coach"
119
+ },
120
+ photo: { key: "mobile.messenger.photo", fallback: "Photo" },
121
+ noMessages: {
122
+ key: "mobile.messenger.noMessages",
123
+ fallback: "No messages yet"
124
+ },
125
+ startConversation: {
126
+ key: "mobile.messenger.startConversation",
127
+ fallback: "Say hello \u2014 send the first message."
128
+ },
129
+ typeMessage: {
130
+ key: "mobile.messenger.typeMessage",
131
+ fallback: "Type a message\u2026"
132
+ },
133
+ client: { key: "mobile.trainer.client", fallback: "Client" },
134
+ back: { key: "common.back", fallback: "Back" }
135
+ };
136
+ function FeatureOff() {
137
+ const { t, theme, navigation, onFeatureOff } = useMessengerConfig();
138
+ if (onFeatureOff) return /* @__PURE__ */ jsx(Fragment, { children: onFeatureOff() });
139
+ const { colors, accentColor } = theme;
140
+ return /* @__PURE__ */ jsxs(
141
+ View,
142
+ {
143
+ style: {
144
+ flex: 1,
145
+ backgroundColor: colors.background,
146
+ alignItems: "center",
147
+ justifyContent: "center",
148
+ padding: 24
149
+ },
150
+ children: [
151
+ /* @__PURE__ */ jsx(Text, { style: { color: colors.textMuted, textAlign: "center" }, children: t(MessengerI18n.off.key, MessengerI18n.off.fallback) }),
152
+ /* @__PURE__ */ jsx(TouchableOpacity, { onPress: navigation.goBack, style: { marginTop: 16 }, children: /* @__PURE__ */ jsx(Text, { style: { color: accentColor }, children: t(MessengerI18n.back.key, MessengerI18n.back.fallback) }) })
153
+ ]
154
+ }
155
+ );
156
+ }
157
+ function ThreadRow({ thread }) {
158
+ const { user, t, isRtl, theme, navigation } = useMessengerConfig();
159
+ const { colors, accentColor, fonts } = theme;
160
+ const unread = Number(thread.unread_count ?? 0);
161
+ return /* @__PURE__ */ jsx(
162
+ TouchableOpacity,
163
+ {
164
+ onPress: () => navigation.openThread(thread.id),
165
+ activeOpacity: 0.8,
166
+ children: /* @__PURE__ */ jsx(
167
+ View,
168
+ {
169
+ style: {
170
+ marginBottom: 10,
171
+ padding: 14,
172
+ borderRadius: 14,
173
+ backgroundColor: colors.surface,
174
+ borderWidth: 1,
175
+ borderColor: colors.border
176
+ },
177
+ children: /* @__PURE__ */ jsxs(
178
+ View,
179
+ {
180
+ style: {
181
+ flexDirection: isRtl ? "row-reverse" : "row",
182
+ alignItems: "center",
183
+ gap: 12
184
+ },
185
+ children: [
186
+ /* @__PURE__ */ jsxs(View, { style: { flex: 1 }, children: [
187
+ /* @__PURE__ */ jsx(
188
+ Text,
189
+ {
190
+ style: {
191
+ color: colors.text,
192
+ fontFamily: fonts.displaySemiBold,
193
+ fontWeight: "600",
194
+ fontSize: 16
195
+ },
196
+ children: user?.role === "admin" ? thread.client_name ?? t(MessengerI18n.client.key, MessengerI18n.client.fallback) : t(
197
+ MessengerI18n.yourCoach.key,
198
+ MessengerI18n.yourCoach.fallback
199
+ )
200
+ }
201
+ ),
202
+ /* @__PURE__ */ jsx(
203
+ Text,
204
+ {
205
+ style: {
206
+ color: colors.textSecondary,
207
+ fontFamily: fonts.body,
208
+ fontSize: 13,
209
+ marginTop: 4
210
+ },
211
+ numberOfLines: 1,
212
+ children: thread.last_body || (thread.last_image_url ? t(MessengerI18n.photo.key, MessengerI18n.photo.fallback) : t(
213
+ MessengerI18n.noMessages.key,
214
+ MessengerI18n.noMessages.fallback
215
+ ))
216
+ }
217
+ )
218
+ ] }),
219
+ unread > 0 && /* @__PURE__ */ jsx(
220
+ View,
221
+ {
222
+ style: {
223
+ minWidth: 22,
224
+ height: 22,
225
+ borderRadius: 11,
226
+ backgroundColor: accentColor,
227
+ alignItems: "center",
228
+ justifyContent: "center",
229
+ paddingHorizontal: 6
230
+ },
231
+ children: /* @__PURE__ */ jsx(
232
+ Text,
233
+ {
234
+ style: {
235
+ color: "#FFF",
236
+ fontSize: 11,
237
+ fontFamily: fonts.bodySemiBold,
238
+ fontWeight: "600"
239
+ },
240
+ children: unread
241
+ }
242
+ )
243
+ }
244
+ )
245
+ ]
246
+ }
247
+ )
248
+ }
249
+ )
250
+ }
251
+ );
252
+ }
253
+ function InboxScreen() {
254
+ const { enabled, user, t, isRtl, theme, navigation } = useMessengerConfig();
255
+ const { colors, accentColor, fonts } = theme;
256
+ const threadsQuery = useThreads();
257
+ const openThread2 = useOpenThread();
258
+ if (!enabled) return /* @__PURE__ */ jsx(FeatureOff, {});
259
+ const threads = threadsQuery.data ?? [];
260
+ const loading = threadsQuery.isLoading && threads.length === 0;
261
+ return /* @__PURE__ */ jsxs(View, { style: { flex: 1, backgroundColor: colors.background }, children: [
262
+ /* @__PURE__ */ jsxs(
263
+ View,
264
+ {
265
+ style: {
266
+ paddingTop: 56,
267
+ paddingHorizontal: 16,
268
+ paddingBottom: 12,
269
+ flexDirection: isRtl ? "row-reverse" : "row",
270
+ alignItems: "center",
271
+ gap: 12
272
+ },
273
+ children: [
274
+ /* @__PURE__ */ jsx(TouchableOpacity, { onPress: navigation.goBack, hitSlop: 12, children: /* @__PURE__ */ jsx(Text, { style: { fontSize: 22, color: colors.text }, children: isRtl ? "\u2192" : "\u2190" }) }),
275
+ /* @__PURE__ */ jsx(
276
+ Text,
277
+ {
278
+ style: {
279
+ color: colors.text,
280
+ fontSize: 22,
281
+ fontFamily: fonts.displayBold,
282
+ fontWeight: "700",
283
+ flex: 1
284
+ },
285
+ children: t(MessengerI18n.inbox.key, MessengerI18n.inbox.fallback)
286
+ }
287
+ )
288
+ ]
289
+ }
290
+ ),
291
+ /* @__PURE__ */ jsxs(
292
+ ScrollView,
293
+ {
294
+ contentContainerStyle: { paddingHorizontal: 16, paddingBottom: 32 },
295
+ refreshControl: /* @__PURE__ */ jsx(
296
+ RefreshControl,
297
+ {
298
+ refreshing: threadsQuery.isRefetching,
299
+ onRefresh: () => void threadsQuery.refetch(),
300
+ tintColor: accentColor
301
+ }
302
+ ),
303
+ children: [
304
+ user?.role === "client" && threads.length === 0 && /* @__PURE__ */ jsx(
305
+ TouchableOpacity,
306
+ {
307
+ onPress: () => openThread2.mutate(void 0),
308
+ disabled: openThread2.isPending,
309
+ style: {
310
+ marginBottom: 14,
311
+ paddingVertical: 14,
312
+ borderRadius: 12,
313
+ backgroundColor: accentColor,
314
+ alignItems: "center"
315
+ },
316
+ children: openThread2.isPending ? /* @__PURE__ */ jsx(ActivityIndicator, { color: "#FFF" }) : /* @__PURE__ */ jsx(
317
+ Text,
318
+ {
319
+ style: {
320
+ color: "#FFF",
321
+ fontFamily: fonts.bodySemiBold,
322
+ fontWeight: "600"
323
+ },
324
+ children: t(
325
+ MessengerI18n.messageCoach.key,
326
+ MessengerI18n.messageCoach.fallback
327
+ )
328
+ }
329
+ )
330
+ }
331
+ ),
332
+ loading ? /* @__PURE__ */ jsx(ActivityIndicator, { color: accentColor, style: { marginTop: 40 } }) : threads.length === 0 ? /* @__PURE__ */ jsxs(
333
+ View,
334
+ {
335
+ style: {
336
+ alignItems: "center",
337
+ paddingVertical: 40,
338
+ borderRadius: 14,
339
+ backgroundColor: colors.surface,
340
+ borderWidth: 1,
341
+ borderColor: colors.border
342
+ },
343
+ children: [
344
+ /* @__PURE__ */ jsx(Text, { style: { fontSize: 28, color: colors.textMuted }, children: "\xB7\xB7\xB7" }),
345
+ /* @__PURE__ */ jsx(
346
+ Text,
347
+ {
348
+ style: {
349
+ color: colors.textMuted,
350
+ marginTop: 12,
351
+ fontFamily: fonts.body,
352
+ textAlign: "center"
353
+ },
354
+ children: t(MessengerI18n.empty.key, MessengerI18n.empty.fallback)
355
+ }
356
+ )
357
+ ]
358
+ }
359
+ ) : threads.map((th) => /* @__PURE__ */ jsx(ThreadRow, { thread: th }, th.id))
360
+ ]
361
+ }
362
+ )
363
+ ] });
364
+ }
365
+ var POLL_MS = 18e3;
366
+ function useThread(threadId, options) {
367
+ const { http, enabled: featureOn } = useMessengerConfig();
368
+ const enabled = featureOn && Number.isFinite(threadId) && threadId > 0;
369
+ const poll = true;
370
+ return useQuery({
371
+ queryKey: messengerKeys.thread(threadId),
372
+ queryFn: async () => {
373
+ const res = await getThread(http, threadId);
374
+ if (!res.success) {
375
+ throw new Error(res.message ?? "Failed to load thread");
376
+ }
377
+ return res.data;
378
+ },
379
+ enabled,
380
+ refetchInterval: enabled && poll ? POLL_MS : false
381
+ });
382
+ }
383
+ function useSendMessage(threadId) {
384
+ const { http } = useMessengerConfig();
385
+ const qc = useQueryClient();
386
+ return useMutation({
387
+ mutationFn: async (input) => {
388
+ const res = await sendMessage(http, threadId, input.body, input.image);
389
+ if (!res.success || !res.data?.message) {
390
+ throw new Error(res.message ?? "Send failed");
391
+ }
392
+ return res.data.message;
393
+ },
394
+ onSuccess: (message) => {
395
+ qc.setQueryData(
396
+ messengerKeys.thread(threadId),
397
+ (prev) => {
398
+ if (!prev) return prev;
399
+ if (prev.messages.some((m) => m.id === message.id)) return prev;
400
+ return { ...prev, messages: [...prev.messages, message] };
401
+ }
402
+ );
403
+ void qc.invalidateQueries({ queryKey: messengerKeys.threads() });
404
+ }
405
+ });
406
+ }
407
+ function MessageBubble({ message }) {
408
+ const { user, isRtl, theme } = useMessengerConfig();
409
+ const { colors, accentColor, fonts } = theme;
410
+ const mine = message.sender_user_id === user?.id;
411
+ return /* @__PURE__ */ jsxs(
412
+ View,
413
+ {
414
+ style: {
415
+ alignSelf: mine ? isRtl ? "flex-start" : "flex-end" : isRtl ? "flex-end" : "flex-start",
416
+ maxWidth: "80%",
417
+ marginBottom: 10,
418
+ backgroundColor: mine ? accentColor : colors.surfaceSecondary,
419
+ borderRadius: 16,
420
+ paddingHorizontal: 12,
421
+ paddingVertical: 10
422
+ },
423
+ children: [
424
+ message.image_url ? /* @__PURE__ */ jsx(
425
+ Image,
426
+ {
427
+ source: { uri: message.image_url },
428
+ style: {
429
+ width: 180,
430
+ height: 180,
431
+ borderRadius: 10,
432
+ marginBottom: message.body ? 8 : 0
433
+ }
434
+ }
435
+ ) : null,
436
+ message.body ? /* @__PURE__ */ jsx(
437
+ Text,
438
+ {
439
+ style: {
440
+ color: mine ? "#FFF" : colors.text,
441
+ fontFamily: fonts.body,
442
+ fontSize: 15
443
+ },
444
+ children: message.body
445
+ }
446
+ ) : null
447
+ ]
448
+ }
449
+ );
450
+ }
451
+ function Composer({
452
+ sending,
453
+ onSend
454
+ }) {
455
+ const { t, isRtl, theme, pickImage } = useMessengerConfig();
456
+ const { colors, accentColor, fonts } = theme;
457
+ const [body, setBody] = useState("");
458
+ async function onPick() {
459
+ const image = await pickImage();
460
+ if (image) {
461
+ await onSend(body, image);
462
+ setBody("");
463
+ }
464
+ }
465
+ async function onPressSend() {
466
+ if (!body.trim() || sending) return;
467
+ const text = body;
468
+ await onSend(text, null);
469
+ setBody("");
470
+ }
471
+ return /* @__PURE__ */ jsxs(
472
+ View,
473
+ {
474
+ style: {
475
+ flexDirection: isRtl ? "row-reverse" : "row",
476
+ alignItems: "flex-end",
477
+ gap: 8,
478
+ paddingHorizontal: 12,
479
+ paddingVertical: 10,
480
+ borderTopWidth: 1,
481
+ borderTopColor: colors.border,
482
+ paddingBottom: Platform.OS === "ios" ? 28 : 12
483
+ },
484
+ children: [
485
+ /* @__PURE__ */ jsx(TouchableOpacity, { onPress: onPick, hitSlop: 8, disabled: sending, children: /* @__PURE__ */ jsx(Text, { style: { fontSize: 22, color: accentColor }, children: "\u25A3" }) }),
486
+ /* @__PURE__ */ jsx(
487
+ TextInput,
488
+ {
489
+ value: body,
490
+ onChangeText: setBody,
491
+ placeholder: t(
492
+ MessengerI18n.typeMessage.key,
493
+ MessengerI18n.typeMessage.fallback
494
+ ),
495
+ placeholderTextColor: colors.textMuted,
496
+ multiline: true,
497
+ style: {
498
+ flex: 1,
499
+ maxHeight: 100,
500
+ borderWidth: 1,
501
+ borderColor: colors.border,
502
+ borderRadius: 16,
503
+ paddingHorizontal: 14,
504
+ paddingVertical: 10,
505
+ color: colors.text,
506
+ fontFamily: fonts.body,
507
+ textAlign: isRtl ? "right" : "left",
508
+ backgroundColor: colors.surface
509
+ }
510
+ }
511
+ ),
512
+ /* @__PURE__ */ jsx(
513
+ TouchableOpacity,
514
+ {
515
+ onPress: onPressSend,
516
+ disabled: sending || !body.trim(),
517
+ style: {
518
+ width: 40,
519
+ height: 40,
520
+ borderRadius: 20,
521
+ backgroundColor: body.trim() ? accentColor : colors.surfaceSecondary,
522
+ alignItems: "center",
523
+ justifyContent: "center"
524
+ },
525
+ children: sending ? /* @__PURE__ */ jsx(ActivityIndicator, { color: "#FFF", size: "small" }) : /* @__PURE__ */ jsx(
526
+ Text,
527
+ {
528
+ style: {
529
+ color: body.trim() ? "#FFF" : colors.textMuted,
530
+ fontSize: 16,
531
+ fontWeight: "700"
532
+ },
533
+ children: "\u27A4"
534
+ }
535
+ )
536
+ }
537
+ )
538
+ ]
539
+ }
540
+ );
541
+ }
542
+ function FeatureOff2() {
543
+ const { t, theme, onFeatureOff } = useMessengerConfig();
544
+ if (onFeatureOff) return /* @__PURE__ */ jsx(Fragment, { children: onFeatureOff() });
545
+ return /* @__PURE__ */ jsx(
546
+ View,
547
+ {
548
+ style: {
549
+ flex: 1,
550
+ backgroundColor: theme.colors.background,
551
+ alignItems: "center",
552
+ justifyContent: "center"
553
+ },
554
+ children: /* @__PURE__ */ jsx(Text, { style: { color: theme.colors.textMuted }, children: t(MessengerI18n.off.key, MessengerI18n.off.fallback) })
555
+ }
556
+ );
557
+ }
558
+ function ThreadScreen({ threadId }) {
559
+ const { enabled, t, isRtl, theme, navigation } = useMessengerConfig();
560
+ const { colors, accentColor, fonts } = theme;
561
+ const listRef = useRef(null);
562
+ const threadQuery = useThread(threadId);
563
+ const sendMutation = useSendMessage(threadId);
564
+ if (!enabled) return /* @__PURE__ */ jsx(FeatureOff2, {});
565
+ const messages = threadQuery.data?.messages ?? [];
566
+ const loading = threadQuery.isLoading && messages.length === 0;
567
+ async function handleSend(body, image) {
568
+ if (!body.trim() && !image || sendMutation.isPending) return;
569
+ await sendMutation.mutateAsync({ body, image });
570
+ setTimeout(() => listRef.current?.scrollToEnd({ animated: true }), 100);
571
+ }
572
+ return /* @__PURE__ */ jsxs(
573
+ KeyboardAvoidingView,
574
+ {
575
+ style: { flex: 1, backgroundColor: colors.background },
576
+ behavior: Platform.OS === "ios" ? "padding" : void 0,
577
+ keyboardVerticalOffset: 0,
578
+ children: [
579
+ /* @__PURE__ */ jsxs(
580
+ View,
581
+ {
582
+ style: {
583
+ paddingTop: 56,
584
+ paddingHorizontal: 16,
585
+ paddingBottom: 12,
586
+ flexDirection: isRtl ? "row-reverse" : "row",
587
+ alignItems: "center",
588
+ gap: 12,
589
+ borderBottomWidth: 1,
590
+ borderBottomColor: colors.border
591
+ },
592
+ children: [
593
+ /* @__PURE__ */ jsx(TouchableOpacity, { onPress: navigation.goBack, hitSlop: 12, children: /* @__PURE__ */ jsx(Text, { style: { fontSize: 22, color: colors.text }, children: isRtl ? "\u2192" : "\u2190" }) }),
594
+ /* @__PURE__ */ jsx(
595
+ Text,
596
+ {
597
+ style: {
598
+ color: colors.text,
599
+ fontSize: 18,
600
+ fontFamily: fonts.displayBold,
601
+ fontWeight: "700",
602
+ flex: 1
603
+ },
604
+ children: t(MessengerI18n.chat.key, MessengerI18n.chat.fallback)
605
+ }
606
+ )
607
+ ]
608
+ }
609
+ ),
610
+ loading ? /* @__PURE__ */ jsx(ActivityIndicator, { color: accentColor, style: { marginTop: 40 } }) : /* @__PURE__ */ jsx(
611
+ FlatList,
612
+ {
613
+ ref: listRef,
614
+ data: messages,
615
+ keyExtractor: (m) => String(m.id),
616
+ contentContainerStyle: { padding: 16, paddingBottom: 8 },
617
+ onContentSizeChange: () => listRef.current?.scrollToEnd({ animated: false }),
618
+ ListEmptyComponent: /* @__PURE__ */ jsx(
619
+ Text,
620
+ {
621
+ style: {
622
+ color: colors.textMuted,
623
+ textAlign: "center",
624
+ marginTop: 40,
625
+ fontFamily: fonts.body
626
+ },
627
+ children: t(
628
+ MessengerI18n.startConversation.key,
629
+ MessengerI18n.startConversation.fallback
630
+ )
631
+ }
632
+ ),
633
+ renderItem: ({ item }) => /* @__PURE__ */ jsx(MessageBubble, { message: item })
634
+ }
635
+ ),
636
+ /* @__PURE__ */ jsx(Composer, { sending: sendMutation.isPending, onSend: handleSend })
637
+ ]
638
+ }
639
+ );
640
+ }
641
+
642
+ export { Composer, InboxScreen, MessageBubble, ThreadScreen };
643
+ //# sourceMappingURL=index.js.map
644
+ //# sourceMappingURL=index.js.map