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

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