@messenger-box/platform-mobile 10.0.3-alpha.19 → 10.0.3-alpha.22

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 (26) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/lib/screens/inbox/components/CachedImage/index.js +0 -19
  3. package/lib/screens/inbox/components/CachedImage/index.js.map +1 -1
  4. package/lib/screens/inbox/components/DialogsListItem.js +423 -50
  5. package/lib/screens/inbox/components/DialogsListItem.js.map +1 -1
  6. package/lib/screens/inbox/components/ServiceDialogsListItem.js +375 -51
  7. package/lib/screens/inbox/components/ServiceDialogsListItem.js.map +1 -1
  8. package/lib/screens/inbox/components/workflow/dialogs-list-item-xstate.js +175 -0
  9. package/lib/screens/inbox/components/workflow/dialogs-list-item-xstate.js.map +1 -0
  10. package/lib/screens/inbox/components/workflow/service-dialogs-list-item-xstate.js +191 -0
  11. package/lib/screens/inbox/components/workflow/service-dialogs-list-item-xstate.js.map +1 -0
  12. package/lib/screens/inbox/containers/Dialogs.js +536 -66
  13. package/lib/screens/inbox/containers/Dialogs.js.map +1 -1
  14. package/lib/screens/inbox/containers/ThreadConversationView.js +95 -23
  15. package/lib/screens/inbox/containers/ThreadConversationView.js.map +1 -1
  16. package/lib/screens/inbox/containers/workflow/dialogs-xstate.js +211 -0
  17. package/lib/screens/inbox/containers/workflow/dialogs-xstate.js.map +1 -0
  18. package/package.json +2 -2
  19. package/src/screens/inbox/components/CachedImage/index.tsx +9 -9
  20. package/src/screens/inbox/components/DialogsListItem.tsx +624 -107
  21. package/src/screens/inbox/components/ServiceDialogsListItem.tsx +506 -114
  22. package/src/screens/inbox/components/SupportServiceDialogsListItem.tsx +35 -17
  23. package/src/screens/inbox/components/workflow/dialogs-list-item-xstate.ts +145 -0
  24. package/src/screens/inbox/components/workflow/service-dialogs-list-item-xstate.ts +159 -0
  25. package/src/screens/inbox/containers/Dialogs.tsx +711 -169
  26. package/src/screens/inbox/containers/ThreadConversationView.tsx +151 -35
@@ -1,4 +1,4 @@
1
- import React, { useMemo } from 'react';
1
+ import React, { useMemo, useState, useCallback, useRef } from 'react';
2
2
  import {
3
3
  Text,
4
4
  Image,
@@ -24,13 +24,34 @@ import {
24
24
  } from 'common/graphql';
25
25
  import { startCase } from 'lodash-es';
26
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
+ };
27
46
 
28
47
  const createdAtText = (value: string) => {
29
48
  if (!value) return '';
30
- let date = new Date(value);
49
+ const date = safeDate(value);
50
+ if (!date) return '';
51
+
31
52
  if (isToday(date)) return 'Today';
32
53
  if (isYesterday(date)) return 'Yesterday';
33
- return format(new Date(value), 'MMM dd, yyyy');
54
+ return format(date, 'MMM dd, yyyy');
34
55
  };
35
56
 
36
57
  export interface IDialogListChannel extends IChannel {
@@ -47,6 +68,293 @@ export interface IDialogListItemProps {
47
68
  role: any;
48
69
  }
49
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
+
50
358
  /**
51
359
  * TODO:
52
360
  * - Get Reservation info: reservation date, status
@@ -61,7 +369,35 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
61
369
  refreshing,
62
370
  role,
63
371
  }) {
64
- const [servicePostParentId, setServicePostParentId] = React.useState(null);
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
65
401
  const {
66
402
  data: threadMessages,
67
403
  loading: threadMessageLoading,
@@ -75,52 +411,75 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
75
411
  limit: 2,
76
412
  },
77
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
+ },
78
423
  });
79
424
 
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
- );
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
+ });
91
436
 
92
- const lastMessage = useMemo(() => {
93
- if (!threadMessages?.threadMessages?.data?.length) {
94
- return null;
437
+ safeSend({
438
+ type: Actions.SET_TITLE,
439
+ data: {
440
+ title: channel?.title || '',
441
+ },
442
+ });
95
443
  }
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;
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
+ });
115
464
  }
116
- }, [threadMessages]);
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);
117
470
 
471
+ // Update servicePostParentId when lastMessage changes
118
472
  React.useEffect(() => {
119
473
  if (lastMessage) {
120
474
  const sParentId = lastMessage?.parentId ? lastMessage?.parentId : lastMessage?.id;
121
- setServicePostParentId(sParentId);
475
+ safeSend({
476
+ type: Actions.SET_SERVICE_POST_PARENT_ID,
477
+ data: {
478
+ servicePostParentId: sParentId,
479
+ },
480
+ });
122
481
  }
123
- }, [lastMessage]);
482
+ }, [lastMessage, safeSend]);
124
483
 
125
484
  const creatorAndMembersId = React.useMemo(() => {
126
485
  if (!channel?.members) return null;
@@ -131,7 +490,7 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
131
490
  const creatorId: any = channel?.creator?.id;
132
491
  const mergedIds: any = [].concat(membersIds, creatorId) ?? [];
133
492
  return mergedIds?.filter((m: any, pos: any) => mergedIds?.indexOf(m) === pos) ?? [];
134
- }, [channel]);
493
+ }, [channel, currentUser]);
135
494
 
136
495
  const postParentId = React.useMemo(() => {
137
496
  if (!creatorAndMembersId?.length) return null;
@@ -141,25 +500,93 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
141
500
  : lastMessage?.parentId
142
501
  ? lastMessage?.parentId
143
502
  : 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]);
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
+ );
163
590
 
164
591
  return (
165
592
  <Pressable
@@ -200,6 +627,14 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
200
627
 
201
628
  const newPostThreadData: any = subscriptionData?.data?.threadCreatedUpdated?.data;
202
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
+
203
638
  const data =
204
639
  prev?.threadMessages?.data?.map((t: any) =>
205
640
  t.id === newPostThreadData?.id
@@ -213,75 +648,32 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
213
648
  : t,
214
649
  ) ?? [];
215
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
+
216
662
  return Object.assign({}, prev, {
217
663
  threadMessages: {
218
664
  ...prev?.threadMessages,
219
665
  totalCount: newPostThreadData?.totalCount ?? 0,
220
666
  data: data?.length > 0 ? data : [newPostThreadData],
221
- // totalCount: prev?.threadMessages?.totalCount + 1,
222
- //data: data,
223
667
  },
224
668
  });
225
669
  },
226
670
  })
227
671
  }
228
672
  />
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> */}
244
673
  </Box>
245
- {/* <Text flex={0.2} color="gray.500">
246
- {lastMessage ? createdAtText(lastMessage?.createdAt) : ''}
247
- </Text> */}
248
674
  </HStack>
249
675
  </Pressable>
250
676
  );
251
677
  };
252
678
 
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
679
  export const ServiceDialogsListItem = React.memo(ServiceDialogsListItemComponent);