@messenger-box/platform-mobile 10.0.3-alpha.16 → 10.0.3-alpha.162

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 +248 -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 +13 -1
  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 +5 -5
  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 +230 -170
  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 +330 -171
  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 -175
  93. package/lib/screens/inbox/components/DialogsListItem.js.map +0 -1
  94. package/lib/screens/inbox/components/ServiceDialogsListItem.js +0 -165
  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,39 +246,55 @@ 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)}
296
+ // onPress={() => channel?.id !== selectedChannelId && onOpen(channel?.id, channel?.title, postParentId)}
297
+ onPress={() => onOpen(channel?.id, channel?.title, postParentId)}
167
298
  className="flex-1 rounded-md border-gray-200 dark:border-gray-600 dark:bg-gray-700"
168
299
  style={{ borderBottomWidth: 1, borderColor: '#e5e7eb', marginVertical: 0 }}
169
300
  >
@@ -186,102 +317,130 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
186
317
  </Box>
187
318
  <Box className="flex-1">
188
319
  <ServiceChannelWithLastMessage
320
+ key={`channel-${channel?.id}-${subscriptionsTimestamp}`}
189
321
  channel={channel}
190
322
  lastMessage={lastMessage}
191
- subscribeToNewMessages={() =>
192
- subscribeToMore({
193
- document: THREAD_CHAT_ADDED,
194
- variables: {
195
- channelId: channel?.id?.toString(),
196
- postParentId: postParentId ? servicePostParentId : null,
197
- },
198
- updateQuery: (prev, { subscriptionData }: any) => {
199
- if (!subscriptionData.data) return prev;
200
-
201
- const newPostThreadData: any = subscriptionData?.data?.threadCreatedUpdated?.data;
202
- const newMessage: any = subscriptionData?.data?.threadCreatedUpdated?.lastMessage;
203
- const data =
204
- prev?.threadMessages?.data?.map((t: any) =>
205
- t.id === newPostThreadData?.id
206
- ? {
207
- ...t,
208
- replies: [...t?.replies, newMessage],
209
- replyCount: newPostThreadData?.replyCount,
210
- lastReplyAt: newPostThreadData?.lastReplyAt,
211
- updatedAt: newPostThreadData?.updatedAt,
212
- }
213
- : t,
214
- ) ?? [];
215
-
216
- return Object.assign({}, prev, {
217
- threadMessages: {
218
- ...prev?.threadMessages,
219
- totalCount: newPostThreadData?.totalCount ?? 0,
220
- data: data?.length > 0 ? data : [newPostThreadData],
221
- // totalCount: prev?.threadMessages?.totalCount + 1,
222
- //data: data,
223
- },
224
- });
225
- },
226
- })
227
- }
228
323
  />
229
- {/* <Text
230
- flex={1}
231
- color="gray.600"
232
- p={0}
233
- m={0}
234
- w={'100%'}
235
- justifyContent={''}
236
- fontSize="lg"
237
- fontWeight="semibold"
238
- >
239
- {title}
240
- </Text> */}
241
- {/* <Text flex={0.1} color="gray.600">
242
- {lastMessage?.message ?? ''}
243
- </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
+ )}
244
440
  </Box>
245
- {/* <Text flex={0.2} color="gray.500">
246
- {lastMessage ? createdAtText(lastMessage?.createdAt) : ''}
247
- </Text> */}
248
441
  </HStack>
249
442
  </Pressable>
250
443
  );
251
444
  };
252
445
 
253
- const ServiceChannelWithLastMessage = React.memo(({ channel, lastMessage, subscribeToNewMessages }: any) => {
254
- React.useEffect(() => subscribeToNewMessages(), []);
255
- const count = 20;
256
- const title = channel?.title.slice(0, count) + (channel?.title.length > count ? '...' : '');
257
- return (
258
- <HStack space={'sm'} className="flex-1 justify-between">
259
- <Box className="flex-[0.8]">
260
- <Text color={colors.gray[600]} className="text-base text-wrap flex-wrap font-semibold">
261
- {title}
262
- </Text>
263
- <Text color={colors.gray[600]} numberOfLines={1}>
264
- {/* {creatorAndMembersId?.length && creatorAndMembersId?.includes(currentUser?.id)
265
- ? lastMessageCreatorAndMembers?.message ?? ''
266
- : lastMessage?.message ?? ''} */}
267
- {lastMessage?.message ?? ''}
268
- </Text>
269
- </Box>
270
-
271
- <Box className="flex-[0.2]">
272
- {/* {creatorAndMembersId?.length && creatorAndMembersId?.includes(currentUser?.id) ? (
273
- <Text color="gray.500">
274
- {lastMessageCreatorAndMembers
275
- ? createdAtText(lastMessageCreatorAndMembers?.createdAt)
276
- : ''}
277
- </Text>
278
- ) : (
279
- <Text color="gray.500">{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}</Text>
280
- )} */}
281
- <Text color={colors.gray[500]}>{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}</Text>
282
- </Box>
283
- </HStack>
284
- );
285
- });
286
-
287
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
+ });