@messenger-box/platform-mobile 0.0.1-alpha.362 → 0.0.1-alpha.365

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 (29) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/lib/index.js +2281 -553
  3. package/lib/index.js.map +1 -1
  4. package/lib/screens/inbox/DialogThreadMessages.d.ts +7 -0
  5. package/lib/screens/inbox/DialogThreads.d.ts +6 -0
  6. package/lib/screens/inbox/components/SupportServiceDialogsListItem.d.ts +21 -0
  7. package/lib/screens/inbox/components/ThreadsViewItem.d.ts +18 -0
  8. package/lib/screens/inbox/config/config.d.ts +2 -0
  9. package/lib/screens/inbox/containers/Dialogs.d.ts +1 -0
  10. package/lib/screens/inbox/containers/SupportServiceDialogs.d.ts +6 -0
  11. package/lib/screens/inbox/containers/ThreadConversationView.d.ts +11 -0
  12. package/lib/screens/inbox/containers/ThreadsView.d.ts +8 -0
  13. package/lib/screens/index.d.ts +2 -0
  14. package/package.json +4 -4
  15. package/src/navigation/InboxNavigation.tsx +63 -0
  16. package/src/screens/inbox/DialogThreadMessages.tsx +90 -0
  17. package/src/screens/inbox/DialogThreads.tsx +107 -0
  18. package/src/screens/inbox/Inbox.tsx +5 -3
  19. package/src/screens/inbox/components/DialogsListItem.tsx +7 -3
  20. package/src/screens/inbox/components/SlackMessageContainer/SlackBubble.tsx +5 -5
  21. package/src/screens/inbox/components/SupportServiceDialogsListItem.tsx +295 -0
  22. package/src/screens/inbox/components/ThreadsViewItem.tsx +236 -0
  23. package/src/screens/inbox/config/config.ts +4 -0
  24. package/src/screens/inbox/containers/ConversationView.tsx +440 -156
  25. package/src/screens/inbox/containers/Dialogs.tsx +2 -1
  26. package/src/screens/inbox/containers/SupportServiceDialogs.tsx +119 -0
  27. package/src/screens/inbox/containers/ThreadConversationView.tsx +764 -0
  28. package/src/screens/inbox/containers/ThreadsView.tsx +205 -0
  29. package/src/screens/index.ts +3 -1
@@ -0,0 +1,764 @@
1
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import {
3
+ Box,
4
+ Button,
5
+ HStack,
6
+ VStack,
7
+ Icon,
8
+ Image,
9
+ Spinner,
10
+ Text,
11
+ useColorModeValue,
12
+ ScrollView,
13
+ Center,
14
+ Avatar,
15
+ } from 'native-base';
16
+ import { Platform, TouchableOpacity } from 'react-native';
17
+ import { useNavigation, useFocusEffect, useRoute } from '@react-navigation/native';
18
+ import { useSelector } from 'react-redux';
19
+ import { orderBy, uniqBy, startCase } from 'lodash';
20
+ import * as ImagePicker from 'expo-image-picker';
21
+ import { encode as atob } from 'base-64';
22
+ import { Ionicons, MaterialCommunityIcons } from '@expo/vector-icons';
23
+ import { Actions, GiftedChat, IMessage, MessageText, Send } from 'react-native-gifted-chat';
24
+ import {
25
+ useCheckForNewMessagesQuery,
26
+ useGetAllUsersQuery,
27
+ useThreadMessagesLazyQuery,
28
+ useSendThreadMessageMutation,
29
+ useMessagesQuery,
30
+ useSendMessagesMutation,
31
+ useViewChannelDetailQuery,
32
+ useUploadFilesNative,
33
+ useGetNewMongooseObjectIdQuery,
34
+ useOnThreadChatMessageAddedSubscription,
35
+ } from '@messenger-box/platform-client';
36
+ import { IFileInfo } from '@messenger-box/core';
37
+ import { config } from '../config';
38
+ import { userSelector } from '@adminide-stack/user-auth0-client';
39
+ import { SlackMessage, ImageViewerModal } from '../components/SlackMessageContainer';
40
+ import CachedImage from '../components/CachedImage';
41
+ import { format, isToday, isYesterday } from 'date-fns';
42
+ const {
43
+ MESSAGES_PER_PAGE,
44
+ CALL_TO_ACTION_BOX_BGCOLOR,
45
+ CALL_TO_ACTION_PATH,
46
+ CALL_TO_ACTION_BUTTON_BORDERCOLOR,
47
+ CALL_TO_ACTION_TEXT_COLOR,
48
+ } = config;
49
+
50
+ const createdAtText = (value: string) => {
51
+ if (!value) return '';
52
+ let date = new Date(value);
53
+ if (isToday(date)) return 'Today';
54
+ if (isYesterday(date)) return 'Yesterday';
55
+ return format(new Date(value), 'MMM dd, yyyy');
56
+ };
57
+
58
+ interface IMessageProps extends IMessage {
59
+ type: string;
60
+ propsConfiguration?: any;
61
+ }
62
+
63
+ export interface AlertMessageAttachmentsInterface {
64
+ title: string;
65
+ isTitleHtml: boolean;
66
+ icon: string;
67
+ callToAction: {
68
+ title: string;
69
+ link: string;
70
+ };
71
+ }
72
+
73
+ const ThreadConversationViewComponent = ({ channelId, postParentId, isPostParentIdThread, role }: any) => {
74
+ const { params } = useRoute<any>();
75
+ const [channelToTop, setChannelToTop] = useState(0);
76
+ const [channelMessages, setChannelMessages] = useState<any>([]);
77
+ const auth: any = useSelector(userSelector);
78
+ const [totalCount, setTotalCount] = useState<any>(0);
79
+ const [selectedImage, setImage] = useState<string>('');
80
+ const [loadingOldMessages, setLoadingOldMessages] = useState<boolean>(false);
81
+ const [loadEarlierMsg, setLoadEarlierMsg] = useState(false);
82
+ const color = useColorModeValue('white', 'black');
83
+ // const color = useColorModeValue('black', 'white');
84
+ const navigation = useNavigation<any>();
85
+ const [files, setFiles] = useState<File[]>([]);
86
+ const [images, setImages] = useState<ImagePicker.ImageInfo[]>([]);
87
+ const [msg, setMsg] = useState<string>('');
88
+ const [loading, setLoading] = useState(false);
89
+ const [imageLoading, setImageLoading] = useState(false);
90
+ const [expoTokens, setExpoTokens] = useState<any[]>([]);
91
+ const [isShowImageViewer, setImageViewer] = useState<boolean>(false);
92
+ const [imageObject, setImageObject] = useState<any>({});
93
+ const [parentId, setParentId] = useState<any>(postParentId);
94
+ const { startUpload } = useUploadFilesNative();
95
+ const [threadPost, setThreadPost] = useState<any[]>([]);
96
+ const { data: mongooseObjectId } = useGetNewMongooseObjectIdQuery({
97
+ fetchPolicy: 'network-only',
98
+ pollInterval: 5000,
99
+ });
100
+
101
+ const {
102
+ data: newThreadMsg,
103
+ loading: newThreadMsgLoading,
104
+ error: newThreadMsgError,
105
+ } = useOnThreadChatMessageAddedSubscription({
106
+ variables: {
107
+ channelId: channelId?.toString(),
108
+ postParentId: !parentId || parentId == 0 ? null : parentId?.toString(),
109
+ },
110
+ });
111
+
112
+ const [sendThreadMessage] = useSendThreadMessageMutation();
113
+
114
+ const [getThreadMessages, { data: threadMessagesData, loading: threadLoading, refetch: refetchThreadMessages }] =
115
+ useThreadMessagesLazyQuery({
116
+ variables: {
117
+ channelId: !parentId || parentId == 0 ? null : channelId?.toString(),
118
+ role: role?.toString(),
119
+ postParentId: !parentId || parentId == 0 ? null : parentId?.toString(),
120
+ },
121
+ fetchPolicy: 'cache-and-network',
122
+ });
123
+
124
+ const {
125
+ data,
126
+ loading: messageLoading,
127
+ refetch,
128
+ }: any = useMessagesQuery({
129
+ variables: {
130
+ channelId: !parentId || parentId == 0 ? null : channelId?.toString(),
131
+ parentId: !parentId || parentId == 0 ? null : parentId?.toString(),
132
+ limit: MESSAGES_PER_PAGE,
133
+ },
134
+ skip: !channelId,
135
+ fetchPolicy: 'cache-and-network',
136
+ });
137
+
138
+ // const { data: checkForMessages }: any = useCheckForNewMessagesQuery({
139
+ // variables: {
140
+ // channelId: !parentId || parentId == 0 ? null : channelId?.toString(),
141
+ // parentId: !parentId || parentId == 0 ? null : parentId?.toString(),
142
+ // },
143
+ // skip: !channelId,
144
+ // fetchPolicy: 'network-only',
145
+ // pollInterval: 5000,
146
+ // });
147
+
148
+ useFocusEffect(
149
+ React.useCallback(() => {
150
+ navigation?.setOptions({ title: params?.title ?? 'Thread' });
151
+ if (parentId || parentId == 0) {
152
+ refetchThreadMessages({
153
+ channelId: !parentId || parentId == 0 ? null : channelId?.toString(),
154
+ role: role?.toString(),
155
+ postParentId: !parentId || parentId == 0 ? null : parentId?.toString(),
156
+ });
157
+ }
158
+ refetch({
159
+ channelId: !parentId || parentId == 0 ? null : channelId?.toString(),
160
+ parentId: !parentId || parentId == 0 ? null : parentId?.toString(),
161
+ limit: MESSAGES_PER_PAGE,
162
+ }).then(({ data }) => {
163
+ if (!data?.messages) {
164
+ return;
165
+ }
166
+ const { data: messages, totalCount }: any = data.messages;
167
+ setTotalCount(totalCount);
168
+ setChannelMessages((oldMessages: any) => uniqBy([...oldMessages, ...messages], ({ id }) => id));
169
+ });
170
+ return () => {
171
+ setTotalCount(0);
172
+ setChannelMessages([]);
173
+ };
174
+ }, []),
175
+ );
176
+
177
+ useEffect(() => {
178
+ setParentId(postParentId);
179
+ }, [postParentId]);
180
+
181
+ useEffect(() => {
182
+ if (parentId && parentId == 0) {
183
+ getThreadMessages({
184
+ variables: {
185
+ channelId: !parentId || parentId == 0 ? null : channelId?.toString(),
186
+ role: role?.toString(),
187
+ postParentId: !parentId || parentId == 0 ? null : parentId?.toString(),
188
+ },
189
+ });
190
+ }
191
+ }, [parentId]);
192
+
193
+ useEffect(() => {
194
+ if (threadMessagesData?.threadMessages) {
195
+ const { data: threads, totalCount: threadTotalCount }: any = threadMessagesData?.threadMessages;
196
+ const threadMessage = threads?.map((t: any) => t?.post);
197
+ setThreadPost(threadMessage);
198
+ if (!isPostParentIdThread) {
199
+ // setTotalCount((pc: any) => pc + threadTotalCount);
200
+ setChannelMessages((oldMessages: any) => uniqBy([...threadMessage, ...oldMessages], ({ id }) => id));
201
+ }
202
+ }
203
+ }, [threadMessagesData, isPostParentIdThread]);
204
+
205
+ React.useEffect(() => {
206
+ if (newThreadMsg) {
207
+ console.log('newThreadMsg');
208
+ const msg = newThreadMsg?.threadChatMessageAdded;
209
+ setTotalCount((preCount: any) => preCount + 1);
210
+ setChannelMessages((oldMessages: any) => uniqBy([...oldMessages, msg], ({ id }) => id));
211
+ }
212
+ }, [newThreadMsg]);
213
+
214
+ // useEffect(() => {
215
+ // if (data?.messages?.data && (loadingOldMessages || channelMessages.length === 0)) {
216
+ // const { data: messages, totalCount: messeageTotalCount } = data.messages;
217
+ // if (messages && messages.length > 0) {
218
+ // setChannelMessages((oldMessages: any) => uniqBy([...messages, ...oldMessages], ({ id }) => id));
219
+ // setLoadingOldMessages(false);
220
+ // }
221
+ // if (totalCount !== messeageTotalCount) setTotalCount(messeageTotalCount);
222
+ // }
223
+ // }, [data, loadingOldMessages, channelMessages, totalCount]);
224
+
225
+ useEffect(() => {
226
+ if ((data?.messages?.data && channelMessages.length === 0) || (data?.messages?.data && loadingOldMessages)) {
227
+ const { data: messages, totalCount: messeageTotalCount } = data.messages;
228
+ if (messages && messages.length > 0) {
229
+ setChannelMessages((oldMessages: any) => uniqBy([...messages, ...oldMessages], ({ id }) => id));
230
+ setTotalCount(messeageTotalCount);
231
+ }
232
+ setLoadEarlierMsg(false);
233
+ if (loadingOldMessages) setLoadingOldMessages(false);
234
+ }
235
+ }, [data, loadingOldMessages, channelMessages]);
236
+
237
+ // useEffect(() => {
238
+ // if (
239
+ // !messageLoading &&
240
+ // checkForMessages?.messages?.totalCount &&
241
+ // checkForMessages?.messages?.totalCount > totalCount
242
+ // ) {
243
+ // const numberOfNewMessages = checkForMessages?.messages?.totalCount - totalCount;
244
+ // console.log('new msg check');
245
+ // refetch({
246
+ // channelId: !parentId || parentId == 0 ? null : channelId?.toString(),
247
+ // parentId: !parentId || parentId == 0 ? null : parentId?.toString(),
248
+ // limit: numberOfNewMessages,
249
+ // }).then(({ data }) => {
250
+ // if (!data?.messages) {
251
+ // return;
252
+ // }
253
+ // const { data: messages, totalCount }: any = data.messages;
254
+ // setTotalCount(totalCount);
255
+ // setChannelMessages((oldMessages: any) => uniqBy([...oldMessages, ...messages], ({ id }) => id));
256
+ // });
257
+ // }
258
+ // }, [checkForMessages, totalCount, parentId]);
259
+
260
+ React.useEffect(() => {
261
+ if (selectedImage) setImageLoading(false);
262
+ }, [selectedImage]);
263
+
264
+ // const onFetchOld = useCallback(() => {
265
+ // if (data?.messages?.totalCount > channelMessages.length) {
266
+ // setLoadEarlierMsg(true);
267
+ // refetch({
268
+ // channelId: !parentId || parentId == 0 ? null : channelId?.toString(),
269
+ // parentId: !parentId || parentId == 0 ? null : parentId?.toString(),
270
+ // skip: channelMessages.length,
271
+ // })?.then((res: any) => setLoadingOldMessages(true));
272
+ // }
273
+ // }, [data, channelMessages]);
274
+
275
+ const onFetchOld = useCallback((data: any, channelMessages: any) => {
276
+ if (data?.messages?.totalCount > channelMessages.length) {
277
+ setLoadEarlierMsg(true);
278
+ refetch({
279
+ channelId: !parentId || parentId == 0 ? null : channelId?.toString(),
280
+ parentId: !parentId || parentId == 0 ? null : parentId?.toString(),
281
+ skip: channelMessages.length,
282
+ })?.then((res: any) => setLoadingOldMessages(true));
283
+ }
284
+ }, []);
285
+
286
+ // const isCloseToTop = ({ layoutMeasurement, contentOffset, contentSize }) => {
287
+ // return contentOffset.y <= 100; // 100px from top
288
+ // };
289
+
290
+ const isCloseToTop = ({ layoutMeasurement, contentOffset, contentSize }) => {
291
+ const paddingToTop = 60;
292
+ return contentSize.height - layoutMeasurement.height - paddingToTop <= contentOffset.y;
293
+ };
294
+
295
+ const dataURLtoFile = (dataurl: any, filename: any) => {
296
+ var arr = dataurl.split(','),
297
+ mime = arr[0].match(/:(.*?);/)[1],
298
+ bstr = atob(arr[1]),
299
+ n = bstr.length,
300
+ u8arr = new Uint8Array(n);
301
+ while (n--) {
302
+ u8arr[n] = bstr.charCodeAt(n);
303
+ }
304
+ return new File([u8arr], filename, { type: mime });
305
+ };
306
+
307
+ const onSelectImages = async () => {
308
+ setImageLoading(true);
309
+ let imageSource: any = await ImagePicker.launchImageLibraryAsync({
310
+ mediaTypes: ImagePicker.MediaTypeOptions.Images,
311
+ allowsEditing: true,
312
+ aspect: [4, 3],
313
+ quality: 1,
314
+ base64: true,
315
+ });
316
+ if (!imageSource.cancelled) {
317
+ const image = 'data:image/jpeg;base64,' + imageSource?.base64;
318
+ setImage(image);
319
+ const file = dataURLtoFile(image, 'inputImage.jpg');
320
+ setFiles((files) => files.concat(file));
321
+ setImages((images) => images.concat(imageSource as ImagePicker.ImageInfo));
322
+ }
323
+ if (imageSource.cancelled) setLoading(false);
324
+ };
325
+
326
+ const sendPushNotification = async (title: String, body: String, data: any, to: any) => {
327
+ try {
328
+ const response = await fetch('https://exp.host/--/api/v2/push/send/', {
329
+ method: 'POST',
330
+ headers: {
331
+ Accept: 'application/json',
332
+ 'Accept-Encoding': 'gzip, deflate',
333
+ 'Content-Type': 'application/json',
334
+ },
335
+ body: JSON.stringify({
336
+ to: to,
337
+ data: data,
338
+ title: title,
339
+ body: body,
340
+ sound: Platform.OS === 'android' ? undefined || null : 'default',
341
+ }),
342
+ });
343
+ const result: any = await response.json();
344
+ console.log('expo api response', result);
345
+ } catch (error) {
346
+ console.error('Error:', error);
347
+ }
348
+ };
349
+
350
+ // const ObjectId = (m = Math, d = Date, h = 16, s = (s:any) => m.floor(s).toString(h)) =>
351
+ // s(d.now() / 1000) + ' '.repeat(h).replace(/./g, () => s(m.random() * h))
352
+
353
+ const handleSend = useCallback(
354
+ async (message: string) => {
355
+ if (!channelId) return;
356
+ if (!message && message != ' ' && images.length == 0) return;
357
+
358
+ if (images && images.length > 0) {
359
+ const postId: any = mongooseObjectId?.getNewMongooseObjectId;
360
+ setLoading(true);
361
+ const uploadResponse = await startUpload({
362
+ file: images,
363
+ saveUploadedFile: {
364
+ variables: {
365
+ postId,
366
+ },
367
+ },
368
+ createUploadLink: {
369
+ variables: {
370
+ postId,
371
+ },
372
+ },
373
+ });
374
+ if (uploadResponse?.error) setLoading(false);
375
+ const uploadedFiles = uploadResponse.data as unknown as IFileInfo[];
376
+ if (uploadResponse.data) {
377
+ setImage('');
378
+ setFiles([]);
379
+ setImages([]);
380
+ //setLoading(false);
381
+ const files = uploadedFiles?.map((f: any) => f.id) ?? null;
382
+ await sendThreadMessage({
383
+ variables: {
384
+ postId,
385
+ channelId,
386
+ postParentId: !parentId || parentId == 0 ? null : parentId,
387
+ threadMessageInput: {
388
+ content: message,
389
+ files,
390
+ role,
391
+ },
392
+ },
393
+ update: (cache, { data, errors }: any) => {
394
+ if (!data || errors) {
395
+ setLoading(false);
396
+ return;
397
+ }
398
+ const responseMessage = data?.sendThreadMessage?.lastMessage;
399
+ if (!parentId || parentId == 0) {
400
+ setChannelMessages((messages: any) => [
401
+ ...messages,
402
+ {
403
+ ...responseMessage,
404
+ files: {
405
+ totalCount: uploadedFiles.length,
406
+ data: uploadedFiles,
407
+ },
408
+ },
409
+ ]);
410
+ }
411
+ setTotalCount((t: any) => t + 1);
412
+ setChannelToTop(channelToTop + 1);
413
+ setLoading(false);
414
+ setMsg('');
415
+
416
+ if (!parentId || parentId == 0) {
417
+ setParentId(responseMessage?.id);
418
+ }
419
+ const msg = message == '' ? 'Send a file' : message;
420
+ fetchTokenAndSendPushNotification(msg, channelId, parentId);
421
+ },
422
+ });
423
+ }
424
+ } else {
425
+ setLoading(true);
426
+ await sendThreadMessage({
427
+ variables: {
428
+ channelId,
429
+ postParentId: !parentId || parentId == 0 ? null : parentId,
430
+ threadMessageInput: {
431
+ content: message,
432
+ role,
433
+ },
434
+ },
435
+ update: (cache, { data, errors }: any) => {
436
+ if (!data || errors) {
437
+ setLoading(false);
438
+ return;
439
+ }
440
+ const responseMessage = data?.sendThreadMessage?.lastMessage;
441
+ if (!parentId || parentId == 0) {
442
+ setChannelMessages((messages: any) => [...messages, responseMessage]);
443
+ setTotalCount((t: any) => t + 1);
444
+ }
445
+ setChannelToTop(channelToTop + 1);
446
+ setLoading(false);
447
+ setMsg('');
448
+ if (!parentId || parentId == 0) {
449
+ setParentId(responseMessage?.id);
450
+ }
451
+ fetchTokenAndSendPushNotification(message, channelId, parentId);
452
+ },
453
+ });
454
+ }
455
+ },
456
+ [mongooseObjectId, setChannelMessages, channelId, images, parentId, expoTokens],
457
+ );
458
+
459
+ const fetchTokenAndSendPushNotification = (message: any, channelId: any, parentId: any) => {
460
+ const givenName = auth?.profile?.given_name ?? '';
461
+ const familyName = auth?.profile?.family_name ?? '';
462
+ const fullName = givenName ? givenName + ' ' + familyName : '';
463
+ const title: String = fullName ? fullName : 'Message';
464
+ const body: String = message;
465
+ const notificationData: any = {
466
+ url: config.THREAD_MESSEGE_PATH,
467
+ params: { channelId, title: params?.title ?? 'Thread', postParentId: parentId, hideTabBar: true },
468
+ screen: 'DialogThreadMessages',
469
+ };
470
+ if (parentId || parentId == 0) {
471
+ refetchThreadMessages({
472
+ channelId: !parentId || parentId == 0 ? null : channelId?.toString(),
473
+ role: role?.toString(),
474
+ postParentId: !parentId || parentId == 0 ? null : parentId?.toString(),
475
+ })?.then((res: any) => {
476
+ if (res?.data?.threadMessages?.data?.length > 0) {
477
+ const participants =
478
+ res?.data?.threadMessages?.data?.map((t: any) => t?.participants)?.flat(1) ?? [];
479
+ const participantsTokens =
480
+ participants?.length > 0
481
+ ? participants
482
+ ?.filter((u: any) => u?.id != auth?.id)
483
+ ?.map((p: any) => p.tokens)
484
+ ?.flat(1)
485
+ : [];
486
+ const expoTokens =
487
+ participantsTokens?.length > 0
488
+ ? participantsTokens
489
+ ?.filter((t: any) => t?.type == 'EXPO_NOTIFICATION_TOKEN')
490
+ ?.map((et: any) => et?.token)
491
+ : [];
492
+ if (expoTokens?.length > 0) {
493
+ const to: any = expoTokens;
494
+ sendPushNotification(title, body, notificationData, to);
495
+ }
496
+ }
497
+ });
498
+ }
499
+ };
500
+
501
+ const messageList = useMemo(() => {
502
+ let currentDate = '';
503
+ let res: any = [];
504
+ if (channelMessages?.length) {
505
+ orderBy(channelMessages, ['createdAt'], ['desc']).map((msg) => {
506
+ let message: IMessageProps = {
507
+ _id: '',
508
+ text: '',
509
+ createdAt: 0,
510
+ user: {
511
+ _id: '',
512
+ name: '',
513
+ avatar: '',
514
+ },
515
+ type: '',
516
+ };
517
+ const date = new Date(msg.createdAt);
518
+ message._id = msg.id;
519
+ message.text = msg.message;
520
+ message.createdAt = date;
521
+ (message.user = {
522
+ _id: msg?.author?.id ?? auth?.profile?.id,
523
+ name:
524
+ msg?.author?.givenName ??
525
+ auth?.profile?.given_name + ' ' + msg?.author?.familyName ??
526
+ auth?.profile?.family_name,
527
+ avatar: msg?.author?.picture ?? auth?.profile?.picture,
528
+ }),
529
+ (message.image = msg.files?.data[0]?.url),
530
+ (message.sent = msg?.isDelivered),
531
+ (message.received = msg?.isRead);
532
+ message.type = msg?.type;
533
+ message.propsConfiguration = msg?.propsConfiguration;
534
+ res.push(message);
535
+ });
536
+ }
537
+ return res;
538
+ }, [channelMessages]);
539
+
540
+ const renderSend = (props) => {
541
+ return (
542
+ <Send {...props}>
543
+ <Box>
544
+ <MaterialCommunityIcons
545
+ name="send-circle"
546
+ style={{ marginBottom: 5, marginRight: 5 }}
547
+ size={32}
548
+ color="#2e64e5"
549
+ />
550
+ </Box>
551
+ </Send>
552
+ );
553
+ };
554
+
555
+ const renderMessageText = (props: any) => {
556
+ const { currentMessage } = props;
557
+ if (currentMessage.type === 'ALERT') {
558
+ const attachment = currentMessage?.propsConfiguration?.contents?.attachment;
559
+ let action: string = '';
560
+ let actionId: any = '';
561
+ if (attachment?.callToAction?.link?.includes('my-reservation-details')) {
562
+ action = CALL_TO_ACTION_PATH;
563
+ actionId = attachment?.callToAction?.link.split('/').pop();
564
+ }
565
+
566
+ return (
567
+ <Box bg={CALL_TO_ACTION_BOX_BGCOLOR} borderRadius={15} pb={2}>
568
+ {attachment?.callToAction ? (
569
+ <Button
570
+ variant={'outline'}
571
+ size={'sm'}
572
+ borderColor={CALL_TO_ACTION_BUTTON_BORDERCOLOR}
573
+ onPress={() => navigation.navigate(action, { reservationId: actionId })}
574
+ >
575
+ <Text color={CALL_TO_ACTION_TEXT_COLOR}>{attachment.callToAction.title}</Text>
576
+ </Button>
577
+ ) : null}
578
+ <MessageText
579
+ {...props}
580
+ textStyle={{ left: { marginLeft: 5, color: CALL_TO_ACTION_TEXT_COLOR, paddingHorizontal: 2 } }}
581
+ />
582
+ </Box>
583
+ );
584
+ } else {
585
+ return <MessageText {...props} textStyle={{ left: { marginLeft: 5 } }} />;
586
+ }
587
+ };
588
+
589
+ const renderActions = (props) => {
590
+ return (
591
+ <Actions
592
+ {...props}
593
+ icon={() => <Icon as={Ionicons} name={'image'} size={'lg'} color={'black'} onPress={onSelectImages} />}
594
+ />
595
+ );
596
+ };
597
+
598
+ const renderAccessory = (props) => {
599
+ return (
600
+ <Box>
601
+ {selectedImage !== '' ? (
602
+ <HStack alignItems={'center'}>
603
+ <Image ml={3} key={selectedImage} alt={'image'} source={{ uri: selectedImage }} size={'xs'} />
604
+ <Button
605
+ variant={'ghost'}
606
+ colorScheme={'secondary'}
607
+ onPress={() => {
608
+ setFiles([]);
609
+ setImage('');
610
+ setImages([]);
611
+ }}
612
+ >
613
+ Cancel
614
+ </Button>
615
+ </HStack>
616
+ ) : null}
617
+ </Box>
618
+ );
619
+ };
620
+
621
+ const setImageViewerObject = (obj: any, v: boolean) => {
622
+ setImageObject(obj);
623
+ setImageViewer(v);
624
+ };
625
+
626
+ const modalContent = React.useMemo(() => {
627
+ if (!imageObject) return <></>;
628
+ const { image, _id } = imageObject;
629
+ return (
630
+ <CachedImage
631
+ style={{ width: '100%', height: '100%' }}
632
+ resizeMode={'cover'}
633
+ // cacheKey={`${_id}-conversation-modal-image-key`}
634
+ cacheKey={`${_id}-slack-bubble-imageKey`}
635
+ source={{
636
+ uri: image,
637
+ //headers: `Authorization: Bearer ${token}`,
638
+ expiresIn: 86400,
639
+ }}
640
+ alt={'image'}
641
+ />
642
+ );
643
+ }, [imageObject]);
644
+
645
+ const renderMessage = (props: any) => {
646
+ return <SlackMessage {...props} isShowImageViewer={isShowImageViewer} setImageViewer={setImageViewerObject} />;
647
+ };
648
+
649
+ return (
650
+ <>
651
+ {/* {loadingOldMessages && <Spinner />} */}
652
+ {loadEarlierMsg && <Spinner />}
653
+ {isPostParentIdThread && (
654
+ <>
655
+ {threadPost?.length > 0 && (
656
+ <>
657
+ <VStack px={2} pt={2} pb={0} space={2}>
658
+ <HStack space={2} alignItems={'center'}>
659
+ <Avatar
660
+ bg={'transparent'}
661
+ size={10}
662
+ _image={{ borderRadius: 6, borderWidth: 2, borderColor: '#fff' }}
663
+ source={{
664
+ uri: threadPost[0]?.author?.picture,
665
+ }}
666
+ >
667
+ {startCase(threadPost[0]?.author?.username?.charAt(0))}
668
+ </Avatar>
669
+ <Box>
670
+ <Text color={'black'} fontWeight={'bold'}>
671
+ {threadPost[0]?.author?.givenName ?? ''}{' '}
672
+ {threadPost[0]?.author?.familyName ?? ''}
673
+ </Text>
674
+ <Text color={'gray.500'} pl={0}>
675
+ {createdAtText(threadPost[0]?.createdAt)} at{' '}
676
+ {format(new Date(threadPost[0]?.createdAt), 'hh:ss:a')}
677
+ </Text>
678
+ </Box>
679
+ </HStack>
680
+ <HStack px={2} space={2} alignItems={'center'}>
681
+ <Text>{threadPost[0]?.message ?? ''}</Text>
682
+ </HStack>
683
+ </VStack>
684
+
685
+ <Box py={4}>
686
+ <Box px={4} borderTopWidth={1} borderBottomWidth={1} py={2} borderColor={'gray.200'}>
687
+ <Text color={'gray.600'} fontWeight={'bold'}>
688
+ {threadPost[0]?.replies?.totalCount}{' '}
689
+ {threadPost[0]?.replies?.totalCount > 0 ? 'replies' : 'reply'}
690
+ </Text>
691
+ </Box>
692
+ </Box>
693
+ </>
694
+ )}
695
+ </>
696
+ )}
697
+ <GiftedChat
698
+ wrapInSafeArea={false}
699
+ renderLoading={() => <Spinner />}
700
+ messages={messageList}
701
+ onSend={(messages) => handleSend(messages[0]?.text ?? ' ')}
702
+ text={msg ? msg : ' '}
703
+ onInputTextChanged={(text) => setMsg(text)}
704
+ renderFooter={() => (loading ? <Spinner /> : imageLoading ? <Spinner /> : '')}
705
+ scrollToBottom
706
+ user={{
707
+ _id: auth?.id || '',
708
+ }}
709
+ isTyping={true}
710
+ alwaysShowSend={loading ? false : true}
711
+ // onLoadEarlier={onFetchOld}
712
+ infiniteScroll={true}
713
+ renderSend={renderSend}
714
+ loadEarlier={data?.messages?.totalCount > channelMessages.length}
715
+ isLoadingEarlier={loadEarlierMsg}
716
+ renderLoadEarlier={() =>
717
+ !loadEarlierMsg && (
718
+ <Center py={2}>
719
+ <Button
720
+ onPress={() => onFetchOld(data, channelMessages)}
721
+ variant={'outline'}
722
+ _text={{ color: 'black', fontSize: 15, fontWeight: 'bold' }}
723
+ >
724
+ Load earlier messages
725
+ </Button>
726
+ </Center>
727
+ )
728
+ }
729
+ renderMessageText={renderMessageText}
730
+ minInputToolbarHeight={50}
731
+ renderActions={renderActions}
732
+ renderAccessory={renderAccessory}
733
+ renderMessage={renderMessage}
734
+ renderChatFooter={() => (
735
+ <ImageViewerModal
736
+ isVisible={isShowImageViewer}
737
+ setVisible={setImageViewer}
738
+ modalContent={modalContent}
739
+ />
740
+ )}
741
+ messagesContainerStyle={messageList?.length == 0 && { transform: [{ scaleY: -1 }] }}
742
+ renderChatEmpty={() => (
743
+ <>
744
+ {!threadLoading && !messageLoading && messageList && messageList?.length == 0 && (
745
+ <Box p={5}>
746
+ <Center mt={6}>
747
+ <Icon as={Ionicons} name="chatbubbles" size={'xl'} />
748
+ <Text>You don't have any message yet!</Text>
749
+ </Center>
750
+ </Box>
751
+ )}
752
+ </>
753
+ )}
754
+ lightboxProps={{
755
+ underlayColor: 'transparent',
756
+ springConfig: { tension: 90000, friction: 90000 },
757
+ disabled: true,
758
+ }}
759
+ />
760
+ </>
761
+ );
762
+ };
763
+
764
+ export const ThreadConversationView = React.memo(ThreadConversationViewComponent);