@messenger-box/platform-mobile 10.0.3-alpha.14 → 10.0.3-alpha.143

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 (95) hide show
  1. package/CHANGELOG.md +216 -0
  2. package/lib/compute.js +2 -3
  3. package/lib/compute.js.map +1 -1
  4. package/lib/index.js.map +1 -1
  5. package/lib/module.js.map +1 -1
  6. package/lib/queries/inboxQueries.js +65 -0
  7. package/lib/queries/inboxQueries.js.map +1 -0
  8. package/lib/routes.json +2 -3
  9. package/lib/screens/inbox/DialogMessages.js +5 -3
  10. package/lib/screens/inbox/DialogMessages.js.map +1 -1
  11. package/lib/screens/inbox/DialogThreadMessages.js +3 -7
  12. package/lib/screens/inbox/DialogThreadMessages.js.map +1 -1
  13. package/lib/screens/inbox/DialogThreads.js +57 -18
  14. package/lib/screens/inbox/DialogThreads.js.map +1 -1
  15. package/lib/screens/inbox/Inbox.js.map +1 -1
  16. package/lib/screens/inbox/components/CachedImage/consts.js +1 -1
  17. package/lib/screens/inbox/components/CachedImage/consts.js.map +1 -1
  18. package/lib/screens/inbox/components/CachedImage/index.js +168 -46
  19. package/lib/screens/inbox/components/CachedImage/index.js.map +1 -1
  20. package/lib/screens/inbox/components/DialogItem.js +169 -0
  21. package/lib/screens/inbox/components/DialogItem.js.map +1 -0
  22. package/lib/screens/inbox/components/GiftedChatInboxComponent.js +313 -0
  23. package/lib/screens/inbox/components/GiftedChatInboxComponent.js.map +1 -0
  24. package/lib/screens/inbox/components/SlackMessageContainer/ImageViewerModal.js.map +1 -1
  25. package/lib/screens/inbox/components/SlackMessageContainer/PaymentMessage.js +197 -0
  26. package/lib/screens/inbox/components/SlackMessageContainer/PaymentMessage.js.map +1 -0
  27. package/lib/screens/inbox/components/SlackMessageContainer/SlackBubble.js +141 -31
  28. package/lib/screens/inbox/components/SlackMessageContainer/SlackBubble.js.map +1 -1
  29. package/lib/screens/inbox/components/SlackMessageContainer/SlackMessage.js.map +1 -1
  30. package/lib/screens/inbox/components/SubscriptionHandler.js +24 -0
  31. package/lib/screens/inbox/components/SubscriptionHandler.js.map +1 -0
  32. package/lib/screens/inbox/components/ThreadsViewItem.js +66 -44
  33. package/lib/screens/inbox/components/ThreadsViewItem.js.map +1 -1
  34. package/lib/screens/inbox/config/config.js +2 -2
  35. package/lib/screens/inbox/config/config.js.map +1 -1
  36. package/lib/screens/inbox/containers/ConversationView.js +1205 -432
  37. package/lib/screens/inbox/containers/ConversationView.js.map +1 -1
  38. package/lib/screens/inbox/containers/Dialogs.js +193 -80
  39. package/lib/screens/inbox/containers/Dialogs.js.map +1 -1
  40. package/lib/screens/inbox/containers/ThreadConversationView.js +724 -207
  41. package/lib/screens/inbox/containers/ThreadConversationView.js.map +1 -1
  42. package/lib/screens/inbox/containers/ThreadsView.js +83 -50
  43. package/lib/screens/inbox/containers/ThreadsView.js.map +1 -1
  44. package/lib/screens/inbox/hooks/useInboxMessages.js +31 -0
  45. package/lib/screens/inbox/hooks/useInboxMessages.js.map +1 -0
  46. package/lib/screens/inbox/hooks/useSafeDialogThreadsMachine.js +108 -0
  47. package/lib/screens/inbox/hooks/useSafeDialogThreadsMachine.js.map +1 -0
  48. package/lib/screens/inbox/workflow/dialog-threads-xstate.js +151 -0
  49. package/lib/screens/inbox/workflow/dialog-threads-xstate.js.map +1 -0
  50. package/package.json +4 -4
  51. package/src/index.ts +2 -0
  52. package/src/queries/inboxQueries.ts +299 -0
  53. package/src/queries/index.d.ts +2 -0
  54. package/src/queries/index.ts +1 -0
  55. package/src/screens/inbox/DialogMessages.tsx +2 -1
  56. package/src/screens/inbox/DialogThreadMessages.tsx +3 -11
  57. package/src/screens/inbox/DialogThreads.tsx +53 -60
  58. package/src/screens/inbox/components/Actionsheet.tsx +30 -0
  59. package/src/screens/inbox/components/CachedImage/consts.ts +4 -3
  60. package/src/screens/inbox/components/CachedImage/index.tsx +232 -61
  61. package/src/screens/inbox/components/DialogItem.tsx +306 -0
  62. package/src/screens/inbox/components/DialogsListItem.tsx +257 -176
  63. package/src/screens/inbox/components/ExpandableInput.tsx +460 -0
  64. package/src/screens/inbox/components/ExpandableInputActionSheet.tsx +518 -0
  65. package/src/screens/inbox/components/GiftedChatInboxComponent.tsx +411 -0
  66. package/src/screens/inbox/components/ServiceDialogsListItem.tsx +334 -172
  67. package/src/screens/inbox/components/SlackInput.tsx +23 -0
  68. package/src/screens/inbox/components/SlackMessageContainer/PaymentMessage.tsx +199 -0
  69. package/src/screens/inbox/components/SlackMessageContainer/SlackBubble.tsx +233 -23
  70. package/src/screens/inbox/components/SmartLoader.tsx +61 -0
  71. package/src/screens/inbox/components/SubscriptionHandler.tsx +41 -0
  72. package/src/screens/inbox/components/SupportServiceDialogsListItem.tsx +41 -24
  73. package/src/screens/inbox/components/ThreadsViewItem.tsx +177 -265
  74. package/src/screens/inbox/components/workflow/dialogs-list-item-xstate.ts +145 -0
  75. package/src/screens/inbox/components/workflow/service-dialogs-list-item-xstate.ts +159 -0
  76. package/src/screens/inbox/config/config.ts +2 -2
  77. package/src/screens/inbox/containers/ConversationView.tsx +2004 -698
  78. package/src/screens/inbox/containers/ConversationView.tsx.bk +1467 -0
  79. package/src/screens/inbox/containers/Dialogs.tsx +402 -196
  80. package/src/screens/inbox/containers/SupportServiceDialogs.tsx +2 -2
  81. package/src/screens/inbox/containers/ThreadConversationView.tsx +1337 -301
  82. package/src/screens/inbox/containers/ThreadsView.tsx +105 -186
  83. package/src/screens/inbox/containers/workflow/apollo/handleResult.ts +20 -0
  84. package/src/screens/inbox/containers/workflow/conversation-xstate.ts +313 -0
  85. package/src/screens/inbox/containers/workflow/dialogs-xstate.ts +196 -0
  86. package/src/screens/inbox/containers/workflow/thread-conversation-xstate.ts +401 -0
  87. package/src/screens/inbox/hooks/useInboxMessages.ts +34 -0
  88. package/src/screens/inbox/hooks/useSafeDialogThreadsMachine.ts +136 -0
  89. package/src/screens/inbox/index.ts +37 -0
  90. package/src/screens/inbox/machines/threadsMachine.ts +147 -0
  91. package/src/screens/inbox/workflow/dialog-threads-xstate.ts +163 -0
  92. package/lib/screens/inbox/components/DialogsListItem.js +0 -159
  93. package/lib/screens/inbox/components/DialogsListItem.js.map +0 -1
  94. package/lib/screens/inbox/components/ServiceDialogsListItem.js +0 -159
  95. package/lib/screens/inbox/components/ServiceDialogsListItem.js.map +0 -1
@@ -1,36 +1,47 @@
1
- import React, { useMemo } from 'react';
1
+ import React, { useMemo, useState, useCallback, useRef } from 'react';
2
2
  import {
3
3
  Text,
4
- Image,
5
4
  Pressable,
6
5
  HStack,
7
6
  Box,
8
- AvatarGroup,
9
7
  Avatar,
10
8
  AvatarFallbackText,
11
9
  AvatarImage,
12
- AvatarBadge,
13
- View,
14
10
  } from '@admin-layout/gluestack-ui-mobile';
15
11
  import { format, isToday, isYesterday } from 'date-fns';
16
12
  import { useFocusEffect } from '@react-navigation/native';
17
13
  import { IChannel, IUserAccount } from 'common';
18
- import {
19
- useOnThreadCreatedUpdatedSubscription,
20
- useOnThreadChatMessageAddedSubscription,
21
- useThreadMessagesQuery,
22
- OnThreadCreatedUpdatedDocument as THREAD_CHAT_ADDED,
23
- OnThreadChatMessageAddedDocument as CHAT_MESSAGE_ADDED,
24
- } from 'common/graphql';
14
+ import { THREAD_CREATED_UPDATED, CHAT_MESSAGE_ADDED, useThreadMessagesQuery } from '../../../queries/inboxQueries';
25
15
  import { startCase } from 'lodash-es';
26
16
  import colors from 'tailwindcss/colors';
17
+ import { SubscriptionHandler } from './SubscriptionHandler';
18
+
19
+ // Helper function to safely create a Date object
20
+ const safeDate = (dateValue) => {
21
+ if (!dateValue) return null;
22
+
23
+ try {
24
+ const date = new Date(dateValue);
25
+ // Check if the date is valid by seeing if it returns a valid time value
26
+ if (isNaN(date.getTime())) {
27
+ console.warn('Invalid date value detected:', dateValue);
28
+ return null;
29
+ }
30
+ return date;
31
+ } catch (error) {
32
+ console.warn('Error creating date from value:', dateValue, error);
33
+ return null;
34
+ }
35
+ };
27
36
 
28
37
  const createdAtText = (value: string) => {
29
38
  if (!value) return '';
30
- let date = new Date(value);
39
+ const date = safeDate(value);
40
+ if (!date) return '';
41
+
31
42
  if (isToday(date)) return 'Today';
32
43
  if (isYesterday(date)) return 'Yesterday';
33
- return format(new Date(value), 'MMM dd, yyyy');
44
+ return format(date, 'MMM dd, yyyy');
34
45
  };
35
46
 
36
47
  export interface IDialogListChannel extends IChannel {
@@ -45,8 +56,120 @@ export interface IDialogListItemProps {
45
56
  onOpen: (id: any, title: any, postParentId?: any) => void;
46
57
  refreshing: boolean;
47
58
  role: any;
59
+ visible?: boolean;
48
60
  }
49
61
 
62
+ // Helper function to compute the last message from a list of messages
63
+ const computeLastMessage = (threadMessages) => {
64
+ if (!threadMessages || !threadMessages.length) return null;
65
+
66
+ // Extract the post and replies from thread messages
67
+ let posts = [];
68
+ let replies = [];
69
+
70
+ // Helper function to check if a message is an image or contains only image content
71
+ const isTextMessage = (msg) => {
72
+ if (!msg) return false;
73
+
74
+ // If message is empty or only contains image-specific markers, ignore it
75
+ if (!msg.message || msg.message === '') return false;
76
+
77
+ // Check if message looks like an image URL or reference
78
+ const isImageMessage =
79
+ msg.message.includes('<img') ||
80
+ msg.message.includes('[Image]') ||
81
+ msg.message.includes('![') ||
82
+ (/\.(jpeg|jpg|gif|png|bmp|webp)/i.test(msg.message) &&
83
+ (msg.message.includes('http') || msg.message.includes('/images/')));
84
+
85
+ // Return true only for text messages (not image messages)
86
+ return !isImageMessage;
87
+ };
88
+
89
+ threadMessages.forEach((thread) => {
90
+ if (thread?.post && isTextMessage(thread.post)) {
91
+ posts.push(thread.post);
92
+ }
93
+ if (thread?.replies?.length) {
94
+ replies = [...replies, ...thread.replies.filter((r) => isTextMessage(r))];
95
+ }
96
+ });
97
+
98
+ // Combine and sort all messages
99
+ const allMessages = [...posts, ...replies];
100
+ if (!allMessages.length) return null;
101
+
102
+ // Return the most recent message - use safe date comparison
103
+ return allMessages.reduce((a, b) => {
104
+ const dateA = safeDate(a?.createdAt);
105
+ const dateB = safeDate(b?.createdAt);
106
+
107
+ // If either date is invalid, prefer the message with valid date
108
+ if (!dateA) return b;
109
+ if (!dateB) return a;
110
+
111
+ return dateA > dateB ? a : b;
112
+ }, allMessages[0]);
113
+ };
114
+
115
+ const ServiceChannelWithLastMessage = React.memo(({ channel, lastMessage }: { channel: any; lastMessage: any }) => {
116
+ // Debug output for component rendering
117
+ React.useEffect(() => {
118
+ console.log(`ServiceChannelWithLastMessage rendered for channel ${channel?.id}:`, {
119
+ hasLastMessage: !!lastMessage,
120
+ messageId: lastMessage?.id,
121
+ messageText: lastMessage?.message?.substring(0, 20) + (lastMessage?.message?.length > 20 ? '...' : ''),
122
+ date: lastMessage?.createdAt ? safeDate(lastMessage.createdAt)?.toISOString() : 'none',
123
+ });
124
+ }, [lastMessage, channel?.id]);
125
+
126
+ const count = 30;
127
+ const title = channel?.title.slice(0, count) + (channel?.title.length > count ? '...' : '');
128
+
129
+ // Define message display text
130
+ let displayMessage = 'No messages yet';
131
+
132
+ if (lastMessage) {
133
+ // Check for file attachments
134
+ const hasFileAttachments = lastMessage.files?.data?.length > 0;
135
+
136
+ // Check if the message appears to be an image
137
+ const isImageMessage =
138
+ lastMessage.message?.includes('<img') ||
139
+ lastMessage.message?.includes('[Image]') ||
140
+ lastMessage.message?.includes('![') ||
141
+ (/\.(jpeg|jpg|gif|png|bmp|webp)/i.test(lastMessage.message || '') &&
142
+ ((lastMessage.message || '').includes('http') || (lastMessage.message || '').includes('/images/')));
143
+
144
+ if (hasFileAttachments) {
145
+ displayMessage = '📎 File attachment';
146
+ } else if (isImageMessage) {
147
+ displayMessage = '[Image]';
148
+ } else if (lastMessage.message && lastMessage.message.trim() !== '') {
149
+ displayMessage = lastMessage.message;
150
+ } else {
151
+ displayMessage = '(Empty message)';
152
+ }
153
+ }
154
+
155
+ return (
156
+ <HStack space={'sm'} className="flex-1 justify-between">
157
+ <Box className="flex-[0.8]">
158
+ <Text color={colors.gray[600]} className="text-base text-wrap flex-wrap font-semibold">
159
+ {title}
160
+ </Text>
161
+ <Text color={colors.gray[600]} numberOfLines={1}>
162
+ {displayMessage}
163
+ </Text>
164
+ </Box>
165
+
166
+ <Box className="flex-[0.2]">
167
+ <Text color={colors.gray[500]}>{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}</Text>
168
+ </Box>
169
+ </HStack>
170
+ );
171
+ });
172
+
50
173
  /**
51
174
  * TODO:
52
175
  * - Get Reservation info: reservation date, status
@@ -60,10 +183,17 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
60
183
  onOpen,
61
184
  refreshing,
62
185
  role,
186
+ visible = true,
63
187
  }) {
64
- const [servicePostParentId, setServicePostParentId] = React.useState(null);
188
+ const isMountedRef = useRef(true);
189
+ const [subscriptionsTimestamp, setSubscriptionsTimestamp] = useState(Date.now());
190
+
191
+ // Only run queries/subscriptions if visible
192
+ const shouldQuery = !!channel?.id && visible;
193
+
194
+ // Query for thread messages (minimal fields if possible)
65
195
  const {
66
- data: threadMessages,
196
+ data: threadMessagesData,
67
197
  loading: threadMessageLoading,
68
198
  error,
69
199
  refetch: refetchThreadMessages,
@@ -74,56 +204,41 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
74
204
  role,
75
205
  limit: 2,
76
206
  },
207
+ skip: !shouldQuery, // Only run if visible
77
208
  fetchPolicy: 'cache-and-network',
209
+ onCompleted: (data) => {
210
+ if (!shouldQuery) return;
211
+ },
212
+ onError: (error) => {
213
+ if (!shouldQuery) return;
214
+ console.error(`Error fetching thread messages for channel ${channel?.id}:`, error);
215
+ },
78
216
  });
79
217
 
80
- useFocusEffect(
81
- React.useCallback(() => {
82
- // Do something when the screen is focused
83
- // refetchMessages({ channelId: channel?.id?.toString(), limit: 25 });
84
- refetchThreadMessages({ channelId: channel?.id?.toString(), role, limit: 2 });
85
- return () => {
86
- // Do something when the screen is unfocused
87
- // Useful for cleanup functions
88
- };
89
- }, [refreshing]),
90
- );
218
+ // Extract threadMessages data for easier access
219
+ const threadMessages = useMemo(() => threadMessagesData?.threadMessages?.data || [], [threadMessagesData]);
91
220
 
221
+ // Calculate lastMessage directly from threadMessages
92
222
  const lastMessage = useMemo(() => {
93
- if (!threadMessages?.threadMessages?.data?.length) {
94
- return null;
95
- }
96
- const { data }: any = threadMessages.threadMessages;
97
- const replies =
98
- data
99
- ?.map((t: any) => t?.replies)
100
- ?.flat(1)
101
- ?.filter((p: any) => p?.message !== '') ?? [];
102
- if (replies?.length) {
103
- return replies[0];
104
- // return replies[replies.length - 1];
105
- } else {
106
- const post =
107
- data?.find((t: any) => {
108
- return t?.post?.message !== '';
109
- }) ??
110
- data?.find((t: any) => {
111
- return t?.post;
112
- }) ??
113
- null;
114
- return post ? post?.post : null;
115
- }
116
- }, [threadMessages]);
223
+ if (!shouldQuery) return null;
224
+ return computeLastMessage(threadMessages);
225
+ }, [shouldQuery, threadMessages]);
226
+
227
+ // Calculate servicePostParentId based on lastMessage
228
+ const servicePostParentId = useMemo(() => {
229
+ if (!shouldQuery || !lastMessage) return null;
230
+ return lastMessage?.parentId ? lastMessage?.parentId : lastMessage?.id;
231
+ }, [shouldQuery, lastMessage]);
117
232
 
233
+ // Handle component unmount
118
234
  React.useEffect(() => {
119
- if (lastMessage) {
120
- const sParentId = lastMessage?.parentId ? lastMessage?.parentId : lastMessage?.id;
121
- setServicePostParentId(sParentId);
122
- }
123
- }, [lastMessage]);
235
+ return () => {
236
+ isMountedRef.current = false;
237
+ };
238
+ }, []);
124
239
 
125
- const creatorAndMembersId = React.useMemo(() => {
126
- if (!channel?.members) return null;
240
+ const creatorAndMembersId = useMemo(() => {
241
+ if (!shouldQuery || !channel?.members) return null;
127
242
  const membersIds: any =
128
243
  channel?.members
129
244
  ?.filter((m: any) => m !== null && m?.user?.id !== currentUser?.id)
@@ -131,42 +246,59 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
131
246
  const creatorId: any = channel?.creator?.id;
132
247
  const mergedIds: any = [].concat(membersIds, creatorId) ?? [];
133
248
  return mergedIds?.filter((m: any, pos: any) => mergedIds?.indexOf(m) === pos) ?? [];
134
- }, [channel]);
135
-
136
- const postParentId = React.useMemo(() => {
137
- if (!creatorAndMembersId?.length) return null;
249
+ }, [shouldQuery, channel, currentUser]);
138
250
 
251
+ const postParentId = useMemo(() => {
252
+ if (!shouldQuery || !creatorAndMembersId?.length) return null;
139
253
  return creatorAndMembersId?.length && creatorAndMembersId?.includes(currentUser?.id)
140
254
  ? null
141
255
  : lastMessage?.parentId
142
256
  ? lastMessage?.parentId
143
257
  : lastMessage?.id ?? 0;
144
- }, [creatorAndMembersId, lastMessage]);
145
-
146
- // const { data: threadCreatedUpdated } = useOnThreadCreatedUpdatedSubscription({
147
- // variables: {
148
- // channelId: channel?.id?.toString(),
149
- // postParentId:
150
- // postParentId == null
151
- // ? null
152
- // : lastMessage?.parentId
153
- // ? lastMessage?.parentId ?? null
154
- // : lastMessage?.id ?? null,
155
- // },
156
- // });
157
-
158
- // React.useEffect(() => {
159
- // if (threadCreatedUpdated?.threadCreatedUpdated?.data) {
160
- // refetchThreadMessages({ channelId: channel?.id?.toString(), role, limit: 2 });
161
- // }
162
- // }, [threadCreatedUpdated]);
258
+ }, [shouldQuery, creatorAndMembersId, lastMessage, currentUser]);
259
+
260
+ // Function to force refresh the component state
261
+ const refreshThreadState = useCallback(() => {
262
+ if (shouldQuery && channel?.id && isMountedRef.current) {
263
+ refetchThreadMessages({
264
+ channelId: channel?.id?.toString(),
265
+ role,
266
+ limit: 2,
267
+ }).catch((err) => {
268
+ console.error('Error refreshing thread state:', err);
269
+ });
270
+ }
271
+ }, [shouldQuery, channel?.id, refetchThreadMessages, role]);
272
+
273
+ // Reset subscriptions when the component gains focus
274
+ useFocusEffect(
275
+ React.useCallback(() => {
276
+ if (!shouldQuery || !channel?.id) return;
277
+ setSubscriptionsTimestamp(Date.now());
278
+ const fetchFreshData = async () => {
279
+ try {
280
+ await refetchThreadMessages({
281
+ channelId: channel?.id?.toString(),
282
+ role,
283
+ limit: 2,
284
+ });
285
+ } catch (error) {
286
+ console.error('Error refetching thread messages on focus:', error);
287
+ }
288
+ };
289
+ fetchFreshData();
290
+ return () => {};
291
+ }, [shouldQuery, refreshing, channel?.id, refetchThreadMessages, role]),
292
+ );
163
293
 
164
294
  return (
165
295
  <Pressable
166
- onPress={() => channel?.id !== selectedChannelId && onOpen(channel?.id, channel?.title, postParentId)}
167
- className="flex-1 border rounded-md border-gray-200 dark:border-gray-600 dark:bg-gray-700"
296
+ // onPress={() => channel?.id !== selectedChannelId && onOpen(channel?.id, channel?.title, postParentId)}
297
+ onPress={() => onOpen(channel?.id, channel?.title, postParentId)}
298
+ className="flex-1 rounded-md border-gray-200 dark:border-gray-600 dark:bg-gray-700"
299
+ style={{ borderBottomWidth: 1, borderColor: '#e5e7eb', marginVertical: 0 }}
168
300
  >
169
- <HStack space={'sm'} className="flex-1 w-[100%] py-3 justify-between items-center">
301
+ <HStack space={'md'} className="flex-1 w-[100%] px-2 py-3 items-center">
170
302
  <Box className="flex-[0.1] items-start pl-3">
171
303
  <Avatar
172
304
  key={'service-channels-key-' + channel?.id}
@@ -183,102 +315,132 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
183
315
  />
184
316
  </Avatar>
185
317
  </Box>
186
- <Box className="flex-[0.9]">
318
+ <Box className="flex-1">
187
319
  <ServiceChannelWithLastMessage
320
+ key={`channel-${channel?.id}-${subscriptionsTimestamp}`}
188
321
  channel={channel}
189
322
  lastMessage={lastMessage}
190
- subscribeToNewMessages={() =>
191
- subscribeToMore({
192
- document: THREAD_CHAT_ADDED,
193
- variables: {
194
- channelId: channel?.id?.toString(),
195
- postParentId: postParentId ? servicePostParentId : null,
196
- },
197
- updateQuery: (prev, { subscriptionData }: any) => {
198
- if (!subscriptionData.data) return prev;
199
-
200
- const newPostThreadData: any = subscriptionData?.data?.threadCreatedUpdated?.data;
201
- const newMessage: any = subscriptionData?.data?.threadCreatedUpdated?.lastMessage;
202
- const data =
203
- prev?.threadMessages?.data?.map((t: any) =>
204
- t.id === newPostThreadData?.id
205
- ? {
206
- ...t,
207
- replies: [...t?.replies, newMessage],
208
- replyCount: newPostThreadData?.replyCount,
209
- lastReplyAt: newPostThreadData?.lastReplyAt,
210
- updatedAt: newPostThreadData?.updatedAt,
211
- }
212
- : t,
213
- ) ?? [];
214
-
215
- return Object.assign({}, prev, {
216
- threadMessages: {
217
- ...prev?.threadMessages,
218
- totalCount: newPostThreadData?.totalCount ?? 0,
219
- data: data?.length > 0 ? data : [newPostThreadData],
220
- // totalCount: prev?.threadMessages?.totalCount + 1,
221
- //data: data,
222
- },
223
- });
224
- },
225
- })
226
- }
227
323
  />
228
- {/* <Text
229
- flex={1}
230
- color="gray.600"
231
- p={0}
232
- m={0}
233
- w={'100%'}
234
- justifyContent={''}
235
- fontSize="lg"
236
- fontWeight="semibold"
237
- >
238
- {title}
239
- </Text> */}
240
- {/* <Text flex={0.1} color="gray.600">
241
- {lastMessage?.message ?? ''}
242
- </Text> */}
324
+
325
+ {/* Subscription handlers for real-time updates */}
326
+ {shouldQuery && (
327
+ <>
328
+ <SubscriptionHandler
329
+ subscribeToMore={subscribeToMore}
330
+ document={THREAD_CREATED_UPDATED}
331
+ variables={{
332
+ channelId: channel?.id?.toString(),
333
+ postParentId: postParentId || servicePostParentId || null,
334
+ }}
335
+ updateQuery={(prev, { subscriptionData }: any) => {
336
+ if (!subscriptionData?.data) {
337
+ return prev;
338
+ }
339
+ try {
340
+ const newPostThreadData: any =
341
+ subscriptionData?.data?.threadCreatedUpdated?.data;
342
+ const newMessage: any =
343
+ subscriptionData?.data?.threadCreatedUpdated?.lastMessage;
344
+ if (!newPostThreadData || !newMessage) {
345
+ return prev;
346
+ }
347
+ const prevThreads = prev?.threadMessages?.data || [];
348
+ const threadExists = prevThreads.some(
349
+ (t: any) => t.id === newPostThreadData?.id,
350
+ );
351
+ let updatedThreads;
352
+ if (threadExists) {
353
+ updatedThreads = prevThreads.map((t: any) =>
354
+ t.id === newPostThreadData?.id
355
+ ? {
356
+ ...t,
357
+ replies: [...(t?.replies || []), newMessage],
358
+ replyCount: newPostThreadData?.replyCount,
359
+ lastReplyAt: newPostThreadData?.lastReplyAt,
360
+ updatedAt: newPostThreadData?.updatedAt,
361
+ }
362
+ : t,
363
+ );
364
+ } else {
365
+ updatedThreads = [...prevThreads, newPostThreadData];
366
+ }
367
+ return {
368
+ ...prev,
369
+ threadMessages: {
370
+ ...prev?.threadMessages,
371
+ totalCount:
372
+ newPostThreadData?.totalCount ??
373
+ prev?.threadMessages?.totalCount ??
374
+ 0,
375
+ data: updatedThreads,
376
+ },
377
+ };
378
+ } catch (error) {
379
+ console.error(`Thread subscription error for channel ${channel?.id}:`, error);
380
+ return prev;
381
+ }
382
+ }}
383
+ />
384
+ <SubscriptionHandler
385
+ subscribeToMore={subscribeToMore}
386
+ document={CHAT_MESSAGE_ADDED}
387
+ variables={{
388
+ channelId: channel?.id?.toString(),
389
+ postParentId: postParentId || servicePostParentId || null,
390
+ }}
391
+ updateQuery={(prev, { subscriptionData }: any) => {
392
+ if (!subscriptionData?.data) {
393
+ return prev;
394
+ }
395
+ try {
396
+ const newMessage = subscriptionData?.data?.chatMessageAdded;
397
+ if (!newMessage) {
398
+ return prev;
399
+ }
400
+ const prevThreads = prev?.threadMessages?.data || [];
401
+ const threadIndex = prevThreads.findIndex((t: any) => {
402
+ if (newMessage.parentId && t.post?.id === newMessage.parentId) {
403
+ return true;
404
+ }
405
+ if (t.replies && t.replies.some((r: any) => r.id === newMessage.parentId)) {
406
+ return true;
407
+ }
408
+ return false;
409
+ });
410
+ if (threadIndex === -1) {
411
+ return prev;
412
+ }
413
+ const updatedThreads = [...prevThreads];
414
+ const thread = { ...updatedThreads[threadIndex] };
415
+ thread.replies = [...(thread.replies || []), newMessage];
416
+ const currentReplyCount =
417
+ typeof thread.replyCount === 'number' ? thread.replyCount : 0;
418
+ thread.replyCount = currentReplyCount + 1;
419
+ thread.lastReplyAt = newMessage.createdAt;
420
+ thread.updatedAt = newMessage.createdAt;
421
+ updatedThreads[threadIndex] = thread;
422
+ return {
423
+ ...prev,
424
+ threadMessages: {
425
+ ...prev?.threadMessages,
426
+ data: updatedThreads,
427
+ },
428
+ };
429
+ } catch (error) {
430
+ console.error(
431
+ `Chat message subscription error for channel ${channel?.id}:`,
432
+ error,
433
+ );
434
+ return prev;
435
+ }
436
+ }}
437
+ />
438
+ </>
439
+ )}
243
440
  </Box>
244
- {/* <Text flex={0.2} color="gray.500">
245
- {lastMessage ? createdAtText(lastMessage?.createdAt) : ''}
246
- </Text> */}
247
441
  </HStack>
248
442
  </Pressable>
249
443
  );
250
444
  };
251
445
 
252
- const ServiceChannelWithLastMessage = React.memo(({ channel, lastMessage, subscribeToNewMessages }: any) => {
253
- React.useEffect(() => subscribeToNewMessages(), []);
254
- return (
255
- <HStack space={'sm'} className="flex-1 justify-center items-center">
256
- <Box className="flex-[0.8]">
257
- <Text color={colors.gray[600]} className="text-lg flex-wrap font-semibold">
258
- {channel?.title}
259
- </Text>
260
- <Text color={colors.gray[600]} numberOfLines={1}>
261
- {/* {creatorAndMembersId?.length && creatorAndMembersId?.includes(currentUser?.id)
262
- ? lastMessageCreatorAndMembers?.message ?? ''
263
- : lastMessage?.message ?? ''} */}
264
- {lastMessage?.message ?? ''}
265
- </Text>
266
- </Box>
267
-
268
- <Box className="flex-[0.2]">
269
- {/* {creatorAndMembersId?.length && creatorAndMembersId?.includes(currentUser?.id) ? (
270
- <Text color="gray.500">
271
- {lastMessageCreatorAndMembers
272
- ? createdAtText(lastMessageCreatorAndMembers?.createdAt)
273
- : ''}
274
- </Text>
275
- ) : (
276
- <Text color="gray.500">{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}</Text>
277
- )} */}
278
- <Text color={colors.gray[500]}>{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}</Text>
279
- </Box>
280
- </HStack>
281
- );
282
- });
283
-
284
446
  export const ServiceDialogsListItem = React.memo(ServiceDialogsListItemComponent);
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import { TextInput, TextInputProps } from 'react-native';
3
+
4
+ export const SlackInput = React.forwardRef<TextInput, TextInputProps>((props, ref) => {
5
+ return (
6
+ <TextInput
7
+ ref={ref}
8
+ {...props}
9
+ style={[
10
+ {
11
+ flex: 1,
12
+ fontSize: 16,
13
+ padding: 10,
14
+ borderRadius: 8,
15
+ backgroundColor: '#f8f8f8',
16
+ borderColor: '#e0e0e0',
17
+ borderWidth: 1,
18
+ },
19
+ props.style,
20
+ ]}
21
+ />
22
+ );
23
+ });