@messenger-box/platform-mobile 10.0.3-alpha.20 → 10.0.3-alpha.201

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 (112) hide show
  1. package/lib/components/messages-container-ui/BuildModeView.js +428 -0
  2. package/lib/components/messages-container-ui/BuildModeView.js.map +1 -0
  3. package/lib/components/messages-container-ui/MessagesContainerUI.js +55 -0
  4. package/lib/components/messages-container-ui/MessagesContainerUI.js.map +1 -0
  5. package/lib/components/messages-container-ui/PlanModeView.js +336 -0
  6. package/lib/components/messages-container-ui/PlanModeView.js.map +1 -0
  7. package/lib/compute.js +2 -3
  8. package/lib/compute.js.map +1 -1
  9. package/lib/index.js +1 -1
  10. package/lib/index.js.map +1 -1
  11. package/lib/module.js.map +1 -1
  12. package/lib/queries/inboxQueries.js +62 -0
  13. package/lib/queries/inboxQueries.js.map +1 -0
  14. package/lib/routes.json +2 -3
  15. package/lib/screens/inbox/DialogMessages.js +8 -3
  16. package/lib/screens/inbox/DialogMessages.js.map +1 -1
  17. package/lib/screens/inbox/DialogThreadMessages.js +6 -11
  18. package/lib/screens/inbox/DialogThreadMessages.js.map +1 -1
  19. package/lib/screens/inbox/DialogThreads.js +58 -20
  20. package/lib/screens/inbox/DialogThreads.js.map +1 -1
  21. package/lib/screens/inbox/Inbox.js.map +1 -1
  22. package/lib/screens/inbox/components/CachedImage/consts.js +1 -1
  23. package/lib/screens/inbox/components/CachedImage/consts.js.map +1 -1
  24. package/lib/screens/inbox/components/CachedImage/index.js +125 -115
  25. package/lib/screens/inbox/components/CachedImage/index.js.map +1 -1
  26. package/lib/screens/inbox/components/DialogItem.js +160 -0
  27. package/lib/screens/inbox/components/DialogItem.js.map +1 -0
  28. package/lib/screens/inbox/components/GiftedChatInboxComponent.js +315 -0
  29. package/lib/screens/inbox/components/GiftedChatInboxComponent.js.map +1 -0
  30. package/lib/screens/inbox/components/SlackMessageContainer/ImageViewerModal.js +3 -1
  31. package/lib/screens/inbox/components/SlackMessageContainer/ImageViewerModal.js.map +1 -1
  32. package/lib/screens/inbox/components/SlackMessageContainer/PaymentMessage.js +194 -0
  33. package/lib/screens/inbox/components/SlackMessageContainer/PaymentMessage.js.map +1 -0
  34. package/lib/screens/inbox/components/SlackMessageContainer/SlackBubble.js +149 -36
  35. package/lib/screens/inbox/components/SlackMessageContainer/SlackBubble.js.map +1 -1
  36. package/lib/screens/inbox/components/SlackMessageContainer/SlackMessage.js +4 -5
  37. package/lib/screens/inbox/components/SlackMessageContainer/SlackMessage.js.map +1 -1
  38. package/lib/screens/inbox/components/SubscriptionHandler.js +22 -0
  39. package/lib/screens/inbox/components/SubscriptionHandler.js.map +1 -0
  40. package/lib/screens/inbox/components/ThreadsViewItem.js +67 -47
  41. package/lib/screens/inbox/components/ThreadsViewItem.js.map +1 -1
  42. package/lib/screens/inbox/config/config.js +4 -2
  43. package/lib/screens/inbox/config/config.js.map +1 -1
  44. package/lib/screens/inbox/containers/ConversationView.js +1099 -1094
  45. package/lib/screens/inbox/containers/ConversationView.js.map +1 -1
  46. package/lib/screens/inbox/containers/Dialogs.js +132 -534
  47. package/lib/screens/inbox/containers/Dialogs.js.map +1 -1
  48. package/lib/screens/inbox/containers/ThreadConversationView.js +876 -1357
  49. package/lib/screens/inbox/containers/ThreadConversationView.js.map +1 -1
  50. package/lib/screens/inbox/containers/ThreadsView.js +81 -54
  51. package/lib/screens/inbox/containers/ThreadsView.js.map +1 -1
  52. package/lib/screens/inbox/hooks/useInboxMessages.js +31 -0
  53. package/lib/screens/inbox/hooks/useInboxMessages.js.map +1 -0
  54. package/lib/screens/inbox/hooks/useSafeDialogThreadsMachine.js +108 -0
  55. package/lib/screens/inbox/hooks/useSafeDialogThreadsMachine.js.map +1 -0
  56. package/lib/screens/inbox/workflow/dialog-threads-xstate.js +151 -0
  57. package/lib/screens/inbox/workflow/dialog-threads-xstate.js.map +1 -0
  58. package/package.json +9 -7
  59. package/CHANGELOG.md +0 -164
  60. package/jest.config.js +0 -24
  61. package/lib/screens/inbox/components/DialogsListItem.js +0 -548
  62. package/lib/screens/inbox/components/DialogsListItem.js.map +0 -1
  63. package/lib/screens/inbox/components/ServiceDialogsListItem.js +0 -489
  64. package/lib/screens/inbox/components/ServiceDialogsListItem.js.map +0 -1
  65. package/lib/screens/inbox/components/workflow/dialogs-list-item-xstate.js +0 -175
  66. package/lib/screens/inbox/components/workflow/dialogs-list-item-xstate.js.map +0 -1
  67. package/lib/screens/inbox/components/workflow/service-dialogs-list-item-xstate.js +0 -191
  68. package/lib/screens/inbox/components/workflow/service-dialogs-list-item-xstate.js.map +0 -1
  69. package/lib/screens/inbox/containers/workflow/conversation-xstate.js +0 -380
  70. package/lib/screens/inbox/containers/workflow/conversation-xstate.js.map +0 -1
  71. package/lib/screens/inbox/containers/workflow/dialogs-xstate.js +0 -211
  72. package/lib/screens/inbox/containers/workflow/dialogs-xstate.js.map +0 -1
  73. package/lib/screens/inbox/containers/workflow/thread-conversation-xstate.js +0 -438
  74. package/lib/screens/inbox/containers/workflow/thread-conversation-xstate.js.map +0 -1
  75. package/rollup.config.mjs +0 -45
  76. package/src/components/index.ts +0 -0
  77. package/src/compute.ts +0 -63
  78. package/src/index.ts +0 -7
  79. package/src/module.ts +0 -10
  80. package/src/navigation/InboxNavigation.tsx +0 -102
  81. package/src/navigation/index.ts +0 -1
  82. package/src/screens/inbox/DialogMessages.tsx +0 -21
  83. package/src/screens/inbox/DialogThreadMessages.tsx +0 -97
  84. package/src/screens/inbox/DialogThreads.tsx +0 -129
  85. package/src/screens/inbox/Inbox.tsx +0 -17
  86. package/src/screens/inbox/components/CachedImage/consts.ts +0 -6
  87. package/src/screens/inbox/components/CachedImage/index.tsx +0 -223
  88. package/src/screens/inbox/components/DialogsHeader.tsx +0 -30
  89. package/src/screens/inbox/components/DialogsListItem.tsx +0 -819
  90. package/src/screens/inbox/components/ServiceDialogsListItem.tsx +0 -679
  91. package/src/screens/inbox/components/SlackMessageContainer/ImageViewerModal.tsx +0 -113
  92. package/src/screens/inbox/components/SlackMessageContainer/SlackBubble.tsx +0 -313
  93. package/src/screens/inbox/components/SlackMessageContainer/SlackMessage.tsx +0 -145
  94. package/src/screens/inbox/components/SlackMessageContainer/index.ts +0 -3
  95. package/src/screens/inbox/components/SupportServiceDialogsListItem.tsx +0 -301
  96. package/src/screens/inbox/components/ThreadsViewItem.tsx +0 -321
  97. package/src/screens/inbox/components/workflow/dialogs-list-item-xstate.ts +0 -145
  98. package/src/screens/inbox/components/workflow/service-dialogs-list-item-xstate.ts +0 -159
  99. package/src/screens/inbox/config/config.ts +0 -15
  100. package/src/screens/inbox/config/index.ts +0 -1
  101. package/src/screens/inbox/containers/ConversationView.tsx +0 -1782
  102. package/src/screens/inbox/containers/Dialogs.tsx +0 -794
  103. package/src/screens/inbox/containers/SupportServiceDialogs.tsx +0 -119
  104. package/src/screens/inbox/containers/ThreadConversationView.tsx +0 -2312
  105. package/src/screens/inbox/containers/ThreadsView.tsx +0 -305
  106. package/src/screens/inbox/containers/workflow/apollo/handleResult.ts +0 -20
  107. package/src/screens/inbox/containers/workflow/conversation-xstate.ts +0 -313
  108. package/src/screens/inbox/containers/workflow/dialogs-xstate.ts +0 -196
  109. package/src/screens/inbox/containers/workflow/thread-conversation-xstate.ts +0 -401
  110. package/src/screens/index.ts +0 -4
  111. package/tsconfig.json +0 -13
  112. package/webpack.config.js +0 -58
@@ -1,679 +0,0 @@
1
- import React, { useMemo, useState, useCallback, useRef } from 'react';
2
- import {
3
- Text,
4
- Image,
5
- Pressable,
6
- HStack,
7
- Box,
8
- AvatarGroup,
9
- Avatar,
10
- AvatarFallbackText,
11
- AvatarImage,
12
- AvatarBadge,
13
- View,
14
- } from '@admin-layout/gluestack-ui-mobile';
15
- import { format, isToday, isYesterday } from 'date-fns';
16
- import { useFocusEffect } from '@react-navigation/native';
17
- 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';
25
- import { startCase } from 'lodash-es';
26
- import colors from 'tailwindcss/colors';
27
- import { serviceDialogsListItemXstate, Actions, BaseState } from './workflow/service-dialogs-list-item-xstate';
28
-
29
- // Helper function to safely create a Date object
30
- const safeDate = (dateValue) => {
31
- if (!dateValue) return null;
32
-
33
- try {
34
- const date = new Date(dateValue);
35
- // Check if the date is valid by seeing if it returns a valid time value
36
- if (isNaN(date.getTime())) {
37
- console.warn('Invalid date value detected:', dateValue);
38
- return null;
39
- }
40
- return date;
41
- } catch (error) {
42
- console.warn('Error creating date from value:', dateValue, error);
43
- return null;
44
- }
45
- };
46
-
47
- const createdAtText = (value: string) => {
48
- if (!value) return '';
49
- const date = safeDate(value);
50
- if (!date) return '';
51
-
52
- if (isToday(date)) return 'Today';
53
- if (isYesterday(date)) return 'Yesterday';
54
- return format(date, 'MMM dd, yyyy');
55
- };
56
-
57
- export interface IDialogListChannel extends IChannel {
58
- users: IUserAccount[];
59
- }
60
-
61
- export interface IDialogListItemProps {
62
- currentUser?: any;
63
- users?: any;
64
- selectedChannelId?: any;
65
- channel?: any;
66
- onOpen: (id: any, title: any, postParentId?: any) => void;
67
- refreshing: boolean;
68
- role: any;
69
- }
70
-
71
- // Create a safer version of useMachine to handle potential errors
72
- function useSafeMachine(machine) {
73
- // Define the state type
74
- interface SafeStateType {
75
- context: {
76
- channelId: string | null;
77
- currentUser: any;
78
- threadMessages: any[];
79
- loading: boolean;
80
- error: string | null;
81
- title: string;
82
- role: string;
83
- servicePostParentId: string | null;
84
- lastMessage: any;
85
- };
86
- value: string;
87
- matches?: (stateValue: string) => boolean;
88
- }
89
-
90
- // Initialize state with default values to prevent undefined errors
91
- const [state, setState] = useState<SafeStateType>({
92
- context: {
93
- channelId: null,
94
- currentUser: null,
95
- threadMessages: [],
96
- loading: false,
97
- error: null,
98
- title: '',
99
- role: '',
100
- servicePostParentId: null,
101
- lastMessage: null,
102
- },
103
- value: BaseState.Idle,
104
- matches: (stateValue) => stateValue === BaseState.Idle,
105
- });
106
-
107
- // Implement a send function that updates state safely
108
- const send = useCallback((event) => {
109
- try {
110
- // Handle specific events manually
111
- if (event.type === Actions.INITIAL_CONTEXT) {
112
- setState((prev) => ({
113
- ...prev,
114
- context: {
115
- ...prev.context,
116
- channelId: event.data?.channelId || null,
117
- currentUser: event.data?.currentUser || null,
118
- role: event.data?.role || null,
119
- loading: true,
120
- },
121
- value: BaseState.FetchingThreadMessages,
122
- }));
123
- } else if (event.type === Actions.UPDATE_THREAD_MESSAGES) {
124
- setState((prev) => {
125
- if (event.data?.threadMessages) {
126
- // Handling bulk messages (initial load)
127
- // If a direct last message was provided (from subscription), use it
128
- if (event.data?.directLastMessage) {
129
- console.log('Using direct last message from event:', {
130
- id: event.data.directLastMessage.id,
131
- message: event.data.directLastMessage.message?.substring(0, 20) + '...',
132
- });
133
-
134
- return {
135
- ...prev,
136
- context: {
137
- ...prev.context,
138
- threadMessages: event.data.threadMessages,
139
- lastMessage: event.data.directLastMessage,
140
- loading: false,
141
- },
142
- };
143
- }
144
-
145
- return {
146
- ...prev,
147
- context: {
148
- ...prev.context,
149
- threadMessages: event.data.threadMessages,
150
- lastMessage: computeLastMessage(event.data.threadMessages),
151
- loading: false,
152
- },
153
- };
154
- } else if (event.data?.message) {
155
- // Handling single message (from subscription)
156
- const newMessage = event.data.message;
157
- const updatedMessages = [...prev.context.threadMessages, newMessage];
158
-
159
- // Use the new message directly as the last message
160
- // This ensures immediate update on subscription
161
- console.log('Setting new message as lastMessage from subscription:', {
162
- id: newMessage.id,
163
- message: newMessage.message?.substring(0, 20) + '...',
164
- date: newMessage.createdAt || newMessage.updatedAt,
165
- });
166
-
167
- return {
168
- ...prev,
169
- context: {
170
- ...prev.context,
171
- threadMessages: updatedMessages,
172
- lastMessage: newMessage, // Use the new message directly
173
- loading: false,
174
- },
175
- };
176
- }
177
- return prev;
178
- });
179
- } else if (event.type === Actions.SET_TITLE) {
180
- setState((prev) => ({
181
- ...prev,
182
- context: {
183
- ...prev.context,
184
- title: event.data?.title || '',
185
- },
186
- }));
187
- } else if (event.type === Actions.SET_SERVICE_POST_PARENT_ID) {
188
- setState((prev) => ({
189
- ...prev,
190
- context: {
191
- ...prev.context,
192
- servicePostParentId: event.data?.servicePostParentId || null,
193
- },
194
- }));
195
- } else if (event.type === Actions.START_LOADING) {
196
- setState((prev) => ({
197
- ...prev,
198
- context: {
199
- ...prev.context,
200
- loading: true,
201
- },
202
- }));
203
- } else if (event.type === Actions.STOP_LOADING) {
204
- setState((prev) => ({
205
- ...prev,
206
- context: {
207
- ...prev.context,
208
- loading: false,
209
- },
210
- }));
211
- }
212
- } catch (error) {
213
- console.error('Error sending event to state machine:', error);
214
- }
215
- }, []);
216
-
217
- // Helper function to compute the last message from a list of messages
218
- const computeLastMessage = (threadMessages) => {
219
- if (!threadMessages || !threadMessages.length) return null;
220
-
221
- // Extract the post and replies from thread messages
222
- let posts = [];
223
- let replies = [];
224
-
225
- // Helper function to check if a message is an image or contains only image content
226
- const isTextMessage = (msg) => {
227
- if (!msg) return false;
228
-
229
- // If message is empty or only contains image-specific markers, ignore it
230
- if (!msg.message || msg.message === '') return false;
231
-
232
- // Check if message looks like an image URL or reference
233
- const isImageMessage =
234
- msg.message.includes('<img') ||
235
- msg.message.includes('[Image]') ||
236
- msg.message.includes('![') ||
237
- (/\.(jpeg|jpg|gif|png|bmp|webp)/i.test(msg.message) &&
238
- (msg.message.includes('http') || msg.message.includes('/images/')));
239
-
240
- // Return true only for text messages (not image messages)
241
- return !isImageMessage;
242
- };
243
-
244
- threadMessages.forEach((thread) => {
245
- if (thread?.post && isTextMessage(thread.post)) {
246
- posts.push(thread.post);
247
- }
248
- if (thread?.replies?.length) {
249
- replies = [...replies, ...thread.replies.filter((r) => isTextMessage(r))];
250
- }
251
- });
252
-
253
- // Combine and sort all messages
254
- const allMessages = [...posts, ...replies];
255
- if (!allMessages.length) return null;
256
-
257
- // Return the most recent message - use safe date comparison
258
- return allMessages.reduce((a, b) => {
259
- const dateA = safeDate(a?.createdAt);
260
- const dateB = safeDate(b?.createdAt);
261
-
262
- // If either date is invalid, prefer the message with valid date
263
- if (!dateA) return b;
264
- if (!dateB) return a;
265
-
266
- return dateA > dateB ? a : b;
267
- }, allMessages[0]);
268
- };
269
-
270
- // Add a matches function that works with the current state value
271
- const stateWithMatches = useMemo(() => {
272
- return {
273
- ...state,
274
- matches: (stateValue) => {
275
- try {
276
- return state.value === stateValue;
277
- } catch (error) {
278
- console.error(`Error in matches function:`, error);
279
- return false;
280
- }
281
- },
282
- };
283
- }, [state]);
284
-
285
- // Return as a tuple to match useMachine API
286
- return [stateWithMatches, send] as const;
287
- }
288
-
289
- const ServiceChannelWithLastMessage = React.memo(({ channel, lastMessage, subscribeToNewMessages }: any) => {
290
- React.useEffect(() => {
291
- // Subscribe and store the unsubscribe function
292
- const unsubscribe = subscribeToNewMessages();
293
- return () => {
294
- // Cleanup subscription on unmount
295
- if (unsubscribe && typeof unsubscribe === 'function') {
296
- unsubscribe();
297
- }
298
- };
299
- }, [subscribeToNewMessages]);
300
-
301
- // Debug output for component rendering
302
- React.useEffect(() => {
303
- console.log(`ServiceChannelWithLastMessage rendered for channel ${channel?.id}:`, {
304
- hasLastMessage: !!lastMessage,
305
- messageId: lastMessage?.id,
306
- messageText: lastMessage?.message?.substring(0, 20) + (lastMessage?.message?.length > 20 ? '...' : ''),
307
- date: lastMessage?.createdAt ? safeDate(lastMessage.createdAt)?.toISOString() : 'none',
308
- });
309
- }, [lastMessage, channel?.id]);
310
-
311
- const count = 30;
312
- const title = channel?.title.slice(0, count) + (channel?.title.length > count ? '...' : '');
313
-
314
- // Define message display text
315
- let displayMessage = 'No messages yet';
316
-
317
- if (lastMessage) {
318
- // Check for file attachments
319
- const hasFileAttachments = lastMessage.files?.data?.length > 0;
320
-
321
- // Check if the message appears to be an image
322
- const isImageMessage =
323
- lastMessage.message?.includes('<img') ||
324
- lastMessage.message?.includes('[Image]') ||
325
- lastMessage.message?.includes('![') ||
326
- (/\.(jpeg|jpg|gif|png|bmp|webp)/i.test(lastMessage.message || '') &&
327
- ((lastMessage.message || '').includes('http') || (lastMessage.message || '').includes('/images/')));
328
-
329
- if (hasFileAttachments) {
330
- displayMessage = '📎 File attachment';
331
- } else if (isImageMessage) {
332
- displayMessage = '[Image]';
333
- } else if (lastMessage.message && lastMessage.message.trim() !== '') {
334
- displayMessage = lastMessage.message;
335
- } else {
336
- displayMessage = '(Empty message)';
337
- }
338
- }
339
-
340
- return (
341
- <HStack space={'sm'} className="flex-1 justify-between">
342
- <Box className="flex-[0.8]">
343
- <Text color={colors.gray[600]} className="text-base text-wrap flex-wrap font-semibold">
344
- {title}
345
- </Text>
346
- <Text color={colors.gray[600]} numberOfLines={1}>
347
- {displayMessage}
348
- </Text>
349
- </Box>
350
-
351
- <Box className="flex-[0.2]">
352
- <Text color={colors.gray[500]}>{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}</Text>
353
- </Box>
354
- </HStack>
355
- );
356
- });
357
-
358
- /**
359
- * TODO:
360
- * - Get Reservation info: reservation date, status
361
- * - Add ability to get property information: name, logo
362
- */
363
- export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = function DialogsListItem({
364
- currentUser,
365
- // users,
366
- selectedChannelId,
367
- channel,
368
- onOpen,
369
- refreshing,
370
- role,
371
- }) {
372
- // Create a ref to track if component is mounted
373
- const isMountedRef = useRef(true);
374
- // Define safe access functions
375
- const [state, send] = useSafeMachine(serviceDialogsListItemXstate);
376
-
377
- const safeContextProperty = useCallback(
378
- (property, defaultValue = null) => {
379
- try {
380
- return state?.context?.[property] ?? defaultValue;
381
- } catch (error) {
382
- console.error(`Error accessing state.context.${property}:`, error);
383
- return defaultValue;
384
- }
385
- },
386
- [state],
387
- );
388
-
389
- const safeSend = useCallback(
390
- (event) => {
391
- try {
392
- send(event);
393
- } catch (error) {
394
- console.error('Error sending event to state machine:', error, event);
395
- }
396
- },
397
- [send],
398
- );
399
-
400
- // Query for thread messages
401
- const {
402
- data: threadMessages,
403
- loading: threadMessageLoading,
404
- error,
405
- refetch: refetchThreadMessages,
406
- subscribeToMore,
407
- } = useThreadMessagesQuery({
408
- variables: {
409
- channelId: channel?.id?.toString(),
410
- role,
411
- limit: 2,
412
- },
413
- fetchPolicy: 'cache-and-network',
414
- onCompleted: (data) => {
415
- console.log(
416
- `Completed thread messages query for channel ${channel?.id}:`,
417
- data?.threadMessages?.data?.length ? 'Has messages' : 'No messages',
418
- );
419
- },
420
- onError: (error) => {
421
- console.error(`Error fetching thread messages for channel ${channel?.id}:`, error);
422
- },
423
- });
424
-
425
- // Update state when channel or currentUser changes
426
- React.useEffect(() => {
427
- if (channel?.id) {
428
- safeSend({
429
- type: Actions.INITIAL_CONTEXT,
430
- data: {
431
- channelId: channel?.id?.toString(),
432
- currentUser,
433
- role,
434
- },
435
- });
436
-
437
- safeSend({
438
- type: Actions.SET_TITLE,
439
- data: {
440
- title: channel?.title || '',
441
- },
442
- });
443
- }
444
-
445
- return () => {
446
- isMountedRef.current = false;
447
- };
448
- }, [channel?.id, currentUser, role, safeSend]);
449
-
450
- // Update state when thread messages are loaded or refreshed
451
- React.useEffect(() => {
452
- if (threadMessages && isMountedRef.current) {
453
- console.log(
454
- `Updating thread messages for channel ${channel?.id}, count:`,
455
- threadMessages?.threadMessages?.data?.length || 0,
456
- );
457
-
458
- safeSend({
459
- type: Actions.UPDATE_THREAD_MESSAGES,
460
- data: {
461
- threadMessages: threadMessages?.threadMessages?.data || [],
462
- },
463
- });
464
- }
465
- }, [threadMessages, refreshing, channel?.id, safeSend]);
466
-
467
- // Use computed values from state
468
- const lastMessage = safeContextProperty('lastMessage', null);
469
- const servicePostParentId = safeContextProperty('servicePostParentId', null);
470
-
471
- // Update servicePostParentId when lastMessage changes
472
- React.useEffect(() => {
473
- if (lastMessage) {
474
- const sParentId = lastMessage?.parentId ? lastMessage?.parentId : lastMessage?.id;
475
- safeSend({
476
- type: Actions.SET_SERVICE_POST_PARENT_ID,
477
- data: {
478
- servicePostParentId: sParentId,
479
- },
480
- });
481
- }
482
- }, [lastMessage, safeSend]);
483
-
484
- const creatorAndMembersId = React.useMemo(() => {
485
- if (!channel?.members) return null;
486
- const membersIds: any =
487
- channel?.members
488
- ?.filter((m: any) => m !== null && m?.user?.id !== currentUser?.id)
489
- ?.map((mu: any) => mu?.user?.id) ?? [];
490
- const creatorId: any = channel?.creator?.id;
491
- const mergedIds: any = [].concat(membersIds, creatorId) ?? [];
492
- return mergedIds?.filter((m: any, pos: any) => mergedIds?.indexOf(m) === pos) ?? [];
493
- }, [channel, currentUser]);
494
-
495
- const postParentId = React.useMemo(() => {
496
- if (!creatorAndMembersId?.length) return null;
497
-
498
- return creatorAndMembersId?.length && creatorAndMembersId?.includes(currentUser?.id)
499
- ? null
500
- : lastMessage?.parentId
501
- ? lastMessage?.parentId
502
- : lastMessage?.id ?? 0;
503
- }, [creatorAndMembersId, lastMessage, currentUser]);
504
-
505
- // Function to force refresh the component state
506
- const refreshThreadState = useCallback(() => {
507
- if (channel?.id && isMountedRef.current) {
508
- console.log('Forcing thread state refresh for channel:', channel?.id);
509
- // First ensure we're in a loading state to trigger UI updates
510
- safeSend({ type: Actions.START_LOADING });
511
-
512
- // Refetch messages from server
513
- refetchThreadMessages({
514
- channelId: channel?.id?.toString(),
515
- role,
516
- limit: 2,
517
- })
518
- .then((result) => {
519
- // Update the state with fresh data
520
- if (result.data?.threadMessages?.data && isMountedRef.current) {
521
- console.log(
522
- `Refreshed ${result.data.threadMessages.data.length} thread messages for channel ${channel?.id}`,
523
- );
524
-
525
- safeSend({
526
- type: Actions.UPDATE_THREAD_MESSAGES,
527
- data: { threadMessages: result.data.threadMessages.data },
528
- });
529
- }
530
- safeSend({ type: Actions.STOP_LOADING });
531
- })
532
- .catch((err) => {
533
- console.error('Error refreshing thread state:', err);
534
- safeSend({ type: Actions.STOP_LOADING });
535
- });
536
- }
537
- }, [channel?.id, refetchThreadMessages, safeSend, isMountedRef, role]);
538
-
539
- // Refetch messages when screen is focused or refreshing
540
- useFocusEffect(
541
- React.useCallback(() => {
542
- if (!channel?.id) return;
543
-
544
- console.log('ServiceDialogsListItem focused for channel:', channel?.id);
545
-
546
- // Show loading state
547
- safeSend({ type: Actions.START_LOADING });
548
-
549
- // Use a direct refetch with network-only policy to force fresh data
550
- const fetchFreshData = async () => {
551
- try {
552
- // Force a network-only fetch
553
- const result = await refetchThreadMessages({
554
- channelId: channel?.id?.toString(),
555
- role,
556
- limit: 2,
557
- });
558
-
559
- // Log the refreshed data
560
- console.log(
561
- `FOCUS EFFECT: Refetched ${
562
- result?.data?.threadMessages?.data?.length || 0
563
- } messages for channel ${channel?.id}`,
564
- );
565
-
566
- if (result?.data?.threadMessages?.data && isMountedRef.current) {
567
- // Update state with fresh data
568
- safeSend({
569
- type: Actions.UPDATE_THREAD_MESSAGES,
570
- data: { threadMessages: result.data.threadMessages.data },
571
- });
572
- }
573
- } catch (error) {
574
- console.error('Error refetching thread messages on focus:', error);
575
- } finally {
576
- if (isMountedRef.current) {
577
- safeSend({ type: Actions.STOP_LOADING });
578
- }
579
- }
580
- };
581
-
582
- // Execute fetch
583
- fetchFreshData();
584
-
585
- return () => {
586
- // Cleanup function when unfocused
587
- };
588
- }, [refreshing, channel?.id, refetchThreadMessages, safeSend, role, isMountedRef]),
589
- );
590
-
591
- return (
592
- <Pressable
593
- onPress={() => channel?.id !== selectedChannelId && onOpen(channel?.id, channel?.title, postParentId)}
594
- className="flex-1 rounded-md border-gray-200 dark:border-gray-600 dark:bg-gray-700"
595
- style={{ borderBottomWidth: 1, borderColor: '#e5e7eb', marginVertical: 0 }}
596
- >
597
- <HStack space={'md'} className="flex-1 w-[100%] px-2 py-3 items-center">
598
- <Box className="flex-[0.1] items-start pl-3">
599
- <Avatar
600
- key={'service-channels-key-' + channel?.id}
601
- size={'sm'}
602
- className="bg-transparent top-0 right-0 z-[1]"
603
- >
604
- <AvatarFallbackText> {startCase(channel?.creator?.username?.charAt(0))}</AvatarFallbackText>
605
- <AvatarImage
606
- alt="user image"
607
- style={{ borderRadius: 6, borderWidth: 2, borderColor: '#fff' }}
608
- source={{
609
- uri: channel?.creator?.picture,
610
- }}
611
- />
612
- </Avatar>
613
- </Box>
614
- <Box className="flex-1">
615
- <ServiceChannelWithLastMessage
616
- channel={channel}
617
- lastMessage={lastMessage}
618
- subscribeToNewMessages={() =>
619
- subscribeToMore({
620
- document: THREAD_CHAT_ADDED,
621
- variables: {
622
- channelId: channel?.id?.toString(),
623
- postParentId: postParentId ? servicePostParentId : null,
624
- },
625
- updateQuery: (prev, { subscriptionData }: any) => {
626
- if (!subscriptionData.data) return prev;
627
-
628
- const newPostThreadData: any = subscriptionData?.data?.threadCreatedUpdated?.data;
629
- const newMessage: any = subscriptionData?.data?.threadCreatedUpdated?.lastMessage;
630
-
631
- console.log('New thread message subscription update:', {
632
- channelId: channel?.id,
633
- threadId: newPostThreadData?.id,
634
- hasMessage: !!newMessage,
635
- message: newMessage?.message?.substring(0, 20) + '...',
636
- });
637
-
638
- const data =
639
- prev?.threadMessages?.data?.map((t: any) =>
640
- t.id === newPostThreadData?.id
641
- ? {
642
- ...t,
643
- replies: [...t?.replies, newMessage],
644
- replyCount: newPostThreadData?.replyCount,
645
- lastReplyAt: newPostThreadData?.lastReplyAt,
646
- updatedAt: newPostThreadData?.updatedAt,
647
- }
648
- : t,
649
- ) ?? [];
650
-
651
- // Use the safe state update function
652
- if (isMountedRef.current) {
653
- safeSend({
654
- type: Actions.UPDATE_THREAD_MESSAGES,
655
- data: {
656
- threadMessages: data?.length > 0 ? data : [newPostThreadData],
657
- directLastMessage: newMessage, // Directly set the new message
658
- },
659
- });
660
- }
661
-
662
- return Object.assign({}, prev, {
663
- threadMessages: {
664
- ...prev?.threadMessages,
665
- totalCount: newPostThreadData?.totalCount ?? 0,
666
- data: data?.length > 0 ? data : [newPostThreadData],
667
- },
668
- });
669
- },
670
- })
671
- }
672
- />
673
- </Box>
674
- </HStack>
675
- </Pressable>
676
- );
677
- };
678
-
679
- export const ServiceDialogsListItem = React.memo(ServiceDialogsListItemComponent);