@messenger-box/platform-mobile 10.0.3-alpha.36 → 10.0.3-alpha.37

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 (34) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/screens/inbox/components/CachedImage/index.js +125 -93
  3. package/lib/screens/inbox/components/CachedImage/index.js.map +1 -1
  4. package/lib/screens/inbox/components/DialogsListItem.js +75 -271
  5. package/lib/screens/inbox/components/DialogsListItem.js.map +1 -1
  6. package/lib/screens/inbox/components/ServiceDialogsListItem.js +184 -415
  7. package/lib/screens/inbox/components/ServiceDialogsListItem.js.map +1 -1
  8. package/lib/screens/inbox/components/SlackMessageContainer/SlackBubble.js +0 -2
  9. package/lib/screens/inbox/components/SlackMessageContainer/SlackBubble.js.map +1 -1
  10. package/lib/screens/inbox/containers/ConversationView.js +478 -944
  11. package/lib/screens/inbox/containers/ConversationView.js.map +1 -1
  12. package/lib/screens/inbox/containers/Dialogs.js +212 -628
  13. package/lib/screens/inbox/containers/Dialogs.js.map +1 -1
  14. package/lib/screens/inbox/containers/ThreadConversationView.js +409 -1364
  15. package/lib/screens/inbox/containers/ThreadConversationView.js.map +1 -1
  16. package/package.json +3 -3
  17. package/src/screens/inbox/components/CachedImage/index.tsx +191 -140
  18. package/src/screens/inbox/components/DialogsListItem.tsx +104 -368
  19. package/src/screens/inbox/components/ServiceDialogsListItem.tsx +69 -377
  20. package/src/screens/inbox/components/SlackMessageContainer/SlackBubble.tsx +2 -4
  21. package/src/screens/inbox/containers/ConversationView.tsx +660 -1060
  22. package/src/screens/inbox/containers/ConversationView.tsx.bk +1467 -0
  23. package/src/screens/inbox/containers/Dialogs.tsx +301 -763
  24. package/src/screens/inbox/containers/ThreadConversationView.tsx +661 -1887
  25. package/lib/screens/inbox/components/workflow/dialogs-list-item-xstate.js +0 -175
  26. package/lib/screens/inbox/components/workflow/dialogs-list-item-xstate.js.map +0 -1
  27. package/lib/screens/inbox/components/workflow/service-dialogs-list-item-xstate.js +0 -191
  28. package/lib/screens/inbox/components/workflow/service-dialogs-list-item-xstate.js.map +0 -1
  29. package/lib/screens/inbox/containers/workflow/conversation-xstate.js +0 -380
  30. package/lib/screens/inbox/containers/workflow/conversation-xstate.js.map +0 -1
  31. package/lib/screens/inbox/containers/workflow/dialogs-xstate.js +0 -211
  32. package/lib/screens/inbox/containers/workflow/dialogs-xstate.js.map +0 -1
  33. package/lib/screens/inbox/containers/workflow/thread-conversation-xstate.js +0 -438
  34. package/lib/screens/inbox/containers/workflow/thread-conversation-xstate.js.map +0 -1
@@ -23,7 +23,6 @@ import {
23
23
  } from 'common/graphql';
24
24
  import { startCase } from 'lodash-es';
25
25
  import colors from 'tailwindcss/colors';
26
- import { dialogsListItemXstate, Actions, BaseState } from './workflow/dialogs-list-item-xstate';
27
26
 
28
27
  const createdAtText = (value: string) => {
29
28
  if (!value) return '';
@@ -46,206 +45,6 @@ export interface IDialogListItemProps {
46
45
  forceRefresh?: boolean;
47
46
  }
48
47
 
49
- // Create a safer version of useMachine to handle potential errors
50
- function useSafeMachine(machine) {
51
- // Define the state type
52
- interface SafeStateType {
53
- context: {
54
- channelId: string | null;
55
- currentUser: any;
56
- messages: any[];
57
- loading: boolean;
58
- error: string | null;
59
- title: string;
60
- channelMembers: any[];
61
- lastMessage: any;
62
- };
63
- value: string;
64
- matches?: (stateValue: string) => boolean;
65
- }
66
-
67
- // Initialize state with default values to prevent undefined errors
68
- const [state, setState] = useState<SafeStateType>({
69
- context: {
70
- channelId: null,
71
- currentUser: null,
72
- messages: [],
73
- loading: false,
74
- error: null,
75
- title: '',
76
- channelMembers: [],
77
- lastMessage: null,
78
- },
79
- value: BaseState.Idle,
80
- matches: (stateValue) => stateValue === BaseState.Idle,
81
- });
82
-
83
- // Implement a send function that updates state safely
84
- const send = useCallback((event) => {
85
- try {
86
- // Handle specific events manually
87
- if (event.type === Actions.INITIAL_CONTEXT) {
88
- setState((prev) => ({
89
- ...prev,
90
- context: {
91
- ...prev.context,
92
- channelId: event.data?.channelId || null,
93
- currentUser: event.data?.currentUser || null,
94
- loading: true,
95
- },
96
- value: BaseState.FetchingMessages,
97
- }));
98
- } else if (event.type === Actions.UPDATE_MESSAGES) {
99
- setState((prev) => {
100
- if (event.data?.messages) {
101
- // Handling bulk messages (initial load)
102
- const messages = event.data.messages;
103
-
104
- // If a direct last message was provided (from subscription), use it
105
- if (event.data?.directLastMessage) {
106
- console.log('Using direct last message from event:', {
107
- id: event.data.directLastMessage.id,
108
- message: event.data.directLastMessage.message?.substring(0, 20) + '...',
109
- });
110
-
111
- return {
112
- ...prev,
113
- context: {
114
- ...prev.context,
115
- messages: messages,
116
- lastMessage: event.data.directLastMessage,
117
- loading: false,
118
- },
119
- };
120
- }
121
-
122
- // Otherwise sort messages by date to find the most recent
123
- const sortedMessages = [...messages].sort(
124
- (a, b) =>
125
- new Date(b?.updatedAt || b?.createdAt).getTime() -
126
- new Date(a?.updatedAt || a?.createdAt).getTime(),
127
- );
128
-
129
- const newLastMessage = sortedMessages.length > 0 ? sortedMessages[0] : null;
130
-
131
- // Log the update for debugging
132
- if (newLastMessage) {
133
- console.log('Setting last message from bulk update:', {
134
- id: newLastMessage.id,
135
- message: newLastMessage.message?.substring(0, 20) + '...',
136
- date: newLastMessage.updatedAt || newLastMessage.createdAt,
137
- });
138
- }
139
-
140
- return {
141
- ...prev,
142
- context: {
143
- ...prev.context,
144
- messages: messages,
145
- lastMessage: newLastMessage,
146
- loading: false,
147
- },
148
- };
149
- } else if (event.data?.message) {
150
- // Handling single message (from subscription)
151
- const newMessage = event.data.message;
152
- const updatedMessages = [...prev.context.messages, newMessage];
153
-
154
- // Use the new message directly as the last message
155
- // This ensures immediate update on subscription
156
- console.log('Setting new message as lastMessage from subscription:', {
157
- id: newMessage.id,
158
- message: newMessage.message?.substring(0, 20) + '...',
159
- date: newMessage.createdAt || newMessage.updatedAt,
160
- });
161
-
162
- return {
163
- ...prev,
164
- context: {
165
- ...prev.context,
166
- messages: updatedMessages,
167
- lastMessage: newMessage, // Use the new message directly
168
- loading: false,
169
- },
170
- };
171
- }
172
- return prev;
173
- });
174
- } else if (event.type === Actions.SET_TITLE) {
175
- setState((prev) => ({
176
- ...prev,
177
- context: {
178
- ...prev.context,
179
- title: event.data?.title || '',
180
- },
181
- }));
182
- } else if (event.type === Actions.START_LOADING) {
183
- setState((prev) => ({
184
- ...prev,
185
- context: {
186
- ...prev.context,
187
- loading: true,
188
- },
189
- }));
190
- } else if (event.type === Actions.STOP_LOADING) {
191
- setState((prev) => ({
192
- ...prev,
193
- context: {
194
- ...prev.context,
195
- loading: false,
196
- },
197
- }));
198
- }
199
- } catch (error) {
200
- console.error('Error sending event to state machine:', error);
201
- }
202
- }, []);
203
-
204
- // Helper function to compute the last message from a list of messages
205
- const computeLastMessage = (messages) => {
206
- if (!messages || !messages.length) return null;
207
-
208
- // Always sort by dates in descending order (newest first)
209
- const sortedMessages = [...messages].sort((a, b) => {
210
- const dateA = new Date(a?.updatedAt || a?.createdAt).getTime();
211
- const dateB = new Date(b?.updatedAt || b?.createdAt).getTime();
212
- return dateB - dateA; // Newest first
213
- });
214
-
215
- // Return the most recent message
216
- const latestMsg = sortedMessages[0];
217
-
218
- // Debug log to see if we're selecting the right message
219
- if (latestMsg) {
220
- console.log('Selected most recent message:', {
221
- id: latestMsg.id,
222
- date: latestMsg.updatedAt || latestMsg.createdAt,
223
- text: latestMsg.message?.substring(0, 20) + '...',
224
- });
225
- }
226
-
227
- return latestMsg;
228
- };
229
-
230
- // Add a matches function that works with the current state value
231
- const stateWithMatches = useMemo(() => {
232
- return {
233
- ...state,
234
- matches: (stateValue) => {
235
- try {
236
- return state.value === stateValue;
237
- } catch (error) {
238
- console.error(`Error in matches function:`, error);
239
- return false;
240
- }
241
- },
242
- };
243
- }, [state]);
244
-
245
- // Return as a tuple to match useMachine API
246
- return [stateWithMatches, send] as const;
247
- }
248
-
249
48
  // LastMessage component definition
250
49
  const LastMessageComponent = ({ subscribeToNewMessages, title, lastMessage, channelId }) => {
251
50
  // Subscribe to new messages when component mounts
@@ -334,41 +133,11 @@ export const DialogsListItemComponent: React.FC<IDialogListItemProps> = function
334
133
  // Define parentId early to avoid linter errors
335
134
  const parentId: any = null;
336
135
 
337
- // Use our safer custom implementation instead of the problematic useMachine
338
- const [state, send] = useSafeMachine(dialogsListItemXstate);
339
-
340
- // Define safe access functions
341
- const safeContext = useCallback(() => {
342
- try {
343
- return state?.context || {};
344
- } catch (error) {
345
- console.error('Error accessing state.context:', error);
346
- return {};
347
- }
348
- }, [state]);
349
-
350
- const safeContextProperty = useCallback(
351
- (property, defaultValue = null) => {
352
- try {
353
- return state?.context?.[property] ?? defaultValue;
354
- } catch (error) {
355
- console.error(`Error accessing state.context.${property}:`, error);
356
- return defaultValue;
357
- }
358
- },
359
- [state],
360
- );
361
-
362
- const safeSend = useCallback(
363
- (event) => {
364
- try {
365
- send(event);
366
- } catch (error) {
367
- console.error('Error sending event to state machine:', error, event);
368
- }
369
- },
370
- [send],
371
- );
136
+ // State for component data
137
+ const [loading, setLoading] = useState(false);
138
+ const [title, setTitle] = useState('');
139
+ const [messages, setMessages] = useState([]);
140
+ const [lastMessage, setLastMessage] = useState(null);
372
141
 
373
142
  // Query hooks for fetching messages
374
143
  const {
@@ -390,6 +159,21 @@ export const DialogsListItemComponent: React.FC<IDialogListItemProps> = function
390
159
  `Completed message query for channel ${channel?.id}:`,
391
160
  data?.messages?.data?.length ? 'Has messages' : 'No messages',
392
161
  );
162
+
163
+ if (data?.messages?.data) {
164
+ setMessages(data.messages.data);
165
+
166
+ // Find the newest message
167
+ const sortedMessages = [...data.messages.data].sort(
168
+ (a, b) =>
169
+ new Date(b?.updatedAt || b?.createdAt).getTime() -
170
+ new Date(a?.updatedAt || a?.createdAt).getTime(),
171
+ );
172
+
173
+ if (sortedMessages.length > 0) {
174
+ setLastMessage(sortedMessages[0]);
175
+ }
176
+ }
393
177
  },
394
178
  onError: (error) => {
395
179
  console.error(`Error fetching messages for channel ${channel?.id}:`, error);
@@ -406,27 +190,20 @@ export const DialogsListItemComponent: React.FC<IDialogListItemProps> = function
406
190
  },
407
191
  });
408
192
 
409
- // Initialize the state machine with context data
193
+ // Set mounted state on mount/unmount
410
194
  useEffect(() => {
411
- if (channel?.id) {
412
- safeSend({
413
- type: Actions.INITIAL_CONTEXT,
414
- data: { channelId: channel.id, currentUser },
415
- });
416
- }
417
-
418
- // Cleanup function
195
+ isMountedRef.current = true;
419
196
  return () => {
420
197
  isMountedRef.current = false;
421
198
  };
422
- }, [channel?.id, currentUser, safeSend]);
199
+ }, []);
423
200
 
424
201
  // Function to force refresh the component state
425
202
  const refreshDialogState = useCallback(() => {
426
203
  if (channel?.id && isMountedRef.current) {
427
204
  console.log('Forcing dialog state refresh for channel:', channel?.id);
428
205
  // First ensure we're in a loading state to trigger UI updates
429
- safeSend({ type: Actions.START_LOADING });
206
+ setLoading(true);
430
207
 
431
208
  // Set up the options for the query to force network fetch
432
209
  const options = {
@@ -461,26 +238,21 @@ export const DialogsListItemComponent: React.FC<IDialogListItemProps> = function
461
238
  });
462
239
  }
463
240
 
464
- safeSend({
465
- type: Actions.UPDATE_MESSAGES,
466
- data: { messages: result.data.messages.data },
467
- });
241
+ setMessages(result.data.messages.data);
242
+ setLastMessage(latestMessage);
468
243
  }
469
- safeSend({ type: Actions.STOP_LOADING });
244
+ setLoading(false);
470
245
  })
471
246
  .catch((err) => {
472
247
  console.error('Error refreshing dialog state:', err);
473
- safeSend({ type: Actions.STOP_LOADING });
248
+ setLoading(false);
474
249
  });
475
250
  }
476
- }, [channel?.id, refetchMessages, safeSend, isMountedRef, parentId]);
251
+ }, [channel?.id, refetchMessages, isMountedRef, parentId]);
477
252
 
478
253
  // Track if this is the first time the component renders
479
254
  const firstRenderRef = useRef(true);
480
255
 
481
- // Track the last time we refreshed to prevent too frequent refreshes
482
- const lastRefreshRef = useRef(0);
483
-
484
256
  // Fix messages not refreshing when coming back from detail screen
485
257
  useFocusEffect(
486
258
  React.useCallback(() => {
@@ -499,7 +271,7 @@ export const DialogsListItemComponent: React.FC<IDialogListItemProps> = function
499
271
  console.log('FOCUS EFFECT: Force refetching messages on navigation back for channel:', channel?.id);
500
272
 
501
273
  // Show loading state
502
- safeSend({ type: Actions.START_LOADING });
274
+ setLoading(true);
503
275
 
504
276
  // Use a direct refetch with network-only policy to force fresh data
505
277
  const fetchFreshData = async () => {
@@ -524,7 +296,7 @@ export const DialogsListItemComponent: React.FC<IDialogListItemProps> = function
524
296
 
525
297
  if (result?.data?.messages?.data && isMountedRef.current) {
526
298
  // Compare with current state to check if we're getting fresh data
527
- const currentMessages = safeContextProperty('messages', []);
299
+ const currentMessages = messages;
528
300
  const fetchedMessages = result.data.messages.data;
529
301
 
530
302
  // Log comparison to see if we got new data
@@ -534,17 +306,24 @@ export const DialogsListItemComponent: React.FC<IDialogListItemProps> = function
534
306
  isDifferent: JSON.stringify(currentMessages) !== JSON.stringify(fetchedMessages),
535
307
  });
536
308
 
309
+ // Get the most recent message
310
+ const sortedMessages = [...fetchedMessages].sort(
311
+ (a, b) =>
312
+ new Date(b?.updatedAt || b?.createdAt).getTime() -
313
+ new Date(a?.updatedAt || a?.createdAt).getTime(),
314
+ );
315
+
316
+ const latestMessage = sortedMessages.length > 0 ? sortedMessages[0] : null;
317
+
537
318
  // Update state with fresh data
538
- safeSend({
539
- type: Actions.UPDATE_MESSAGES,
540
- data: { messages: fetchedMessages },
541
- });
319
+ setMessages(fetchedMessages);
320
+ setLastMessage(latestMessage);
542
321
  }
543
322
  } catch (error) {
544
323
  console.error('Error refetching messages on focus:', error);
545
324
  } finally {
546
325
  if (isMountedRef.current) {
547
- safeSend({ type: Actions.STOP_LOADING });
326
+ setLoading(false);
548
327
  }
549
328
  }
550
329
  };
@@ -555,40 +334,9 @@ export const DialogsListItemComponent: React.FC<IDialogListItemProps> = function
555
334
  return () => {
556
335
  // Cleanup function when unfocused
557
336
  };
558
- }, [channel?.id, refetchMessages, safeSend, parentId, isMountedRef]),
337
+ }, [channel?.id, refetchMessages, messages, isMountedRef, parentId]),
559
338
  );
560
339
 
561
- // Update messages in state when query data is available
562
- React.useEffect(() => {
563
- if (messagesQuery?.messages?.data && isMountedRef.current) {
564
- const messages = messagesQuery.messages.data;
565
-
566
- // Count messages with files
567
- const messagesWithFiles = messages.filter((msg) => msg?.files?.data?.length > 0);
568
-
569
- console.log(
570
- `Updating messages for channel ${channel?.id}, count:`,
571
- messages.length,
572
- `(${messagesWithFiles.length} with files)`,
573
- );
574
-
575
- if (messages.length > 0) {
576
- // Log the first message for debugging
577
- console.log('First message sample:', {
578
- id: messages[0].id,
579
- message: messages[0].message?.substring(0, 20) + '...',
580
- createdAt: messages[0].createdAt,
581
- hasFiles: messages[0]?.files?.data?.length > 0,
582
- });
583
- }
584
-
585
- safeSend({
586
- type: Actions.UPDATE_MESSAGES,
587
- data: { messages },
588
- });
589
- }
590
- }, [messagesQuery, safeSend, channel?.id, isMountedRef]);
591
-
592
340
  // Force a refresh on initial mount to ensure we have fresh data
593
341
  useEffect(() => {
594
342
  if (channel?.id && isMountedRef.current) {
@@ -625,11 +373,18 @@ export const DialogsListItemComponent: React.FC<IDialogListItemProps> = function
625
373
  `Force refresh completed for channel ${channel?.id} with ${result.data.messages.data.length} messages`,
626
374
  );
627
375
 
628
- // Update messages in state
629
- safeSend({
630
- type: Actions.UPDATE_MESSAGES,
631
- data: { messages: result.data.messages.data },
632
- });
376
+ // Get the most recent message
377
+ const sortedMessages = [...result.data.messages.data].sort(
378
+ (a, b) =>
379
+ new Date(b?.updatedAt || b?.createdAt).getTime() -
380
+ new Date(a?.updatedAt || a?.createdAt).getTime(),
381
+ );
382
+
383
+ const latestMessage = sortedMessages.length > 0 ? sortedMessages[0] : null;
384
+
385
+ // Update state with fresh data
386
+ setMessages(result.data.messages.data);
387
+ setLastMessage(latestMessage);
633
388
  }
634
389
  })
635
390
  .catch((error) => {
@@ -640,7 +395,7 @@ export const DialogsListItemComponent: React.FC<IDialogListItemProps> = function
640
395
 
641
396
  return () => clearTimeout(timer);
642
397
  }
643
- }, [channel?.id, forceRefresh, isMountedRef, refetchMessages, safeSend, parentId]);
398
+ }, [channel?.id, forceRefresh, isMountedRef, refetchMessages, parentId]);
644
399
 
645
400
  const channelMembers = useMemo(
646
401
  () =>
@@ -650,7 +405,7 @@ export const DialogsListItemComponent: React.FC<IDialogListItemProps> = function
650
405
  [currentUser, channel],
651
406
  );
652
407
 
653
- // Set title in state when channel members change
408
+ // Set title when channel members change
654
409
  useEffect(() => {
655
410
  if (channelMembers && isMountedRef.current) {
656
411
  const titleString =
@@ -659,70 +414,66 @@ export const DialogsListItemComponent: React.FC<IDialogListItemProps> = function
659
414
  ?.filter((mu: any) => mu)
660
415
  ?.join(', ') ?? '';
661
416
 
662
- safeSend({
663
- type: Actions.SET_TITLE,
664
- data: { title: titleString },
665
- });
417
+ setTitle(titleString);
666
418
  }
667
- }, [channelMembers, safeSend, isMountedRef]);
419
+ }, [channelMembers, isMountedRef]);
668
420
 
669
421
  // Compute title with proper truncation
670
- const title = useMemo(() => {
671
- const titleString = safeContextProperty('title', '');
422
+ const displayTitle = useMemo(() => {
672
423
  const length = 30;
673
- return titleString.length > length ? titleString.substring(0, length - 3) + '...' : titleString;
674
- }, [safeContextProperty]);
675
-
676
- // Get the last message directly from state context instead of computing it
677
- const lastMessage = useMemo(() => {
678
- const message = safeContextProperty('lastMessage', null);
679
- // Add dependency on the messages array length to ensure re-render on new messages
680
- return message;
681
- }, [safeContextProperty, state.context?.messages?.length]);
424
+ return title.length > length ? title.substring(0, length - 3) + '...' : title;
425
+ }, [title]);
682
426
 
683
427
  // Debug output for the component
684
428
  useEffect(() => {
685
429
  console.log(`DialogsListItem for channel ${channel?.id}: `, {
686
430
  hasLastMessage: !!lastMessage,
687
431
  message: lastMessage?.message?.substring(0, 20) + (lastMessage?.message?.length > 20 ? '...' : ''),
688
- messagesCount: safeContextProperty('messages', []).length,
432
+ messagesCount: messages.length,
689
433
  });
690
- }, [channel?.id, lastMessage, safeContextProperty]);
691
-
692
- // Optimize update logic for messages from channel-level subscription
693
- const handleChannelMessage = (channel, newMessage) => {
694
- // Check if we already have this message
695
- if (state.context.messages.some((msg) => msg.id === newMessage.id)) {
696
- console.log('Message already in local state, skipping update:', newMessage.id);
697
- return;
698
- }
434
+ }, [channel?.id, lastMessage, messages.length]);
699
435
 
700
- console.log('Handling subscription message update at channel level:', newMessage.id);
701
-
702
- // Always update with latest message to ensure dialog list stays current
703
- safeSend({
704
- type: Actions.UPDATE_MESSAGES,
705
- data: {
706
- // Use the directLastMessage option to force immediate UI update
707
- directLastMessage: newMessage,
708
- // Also update the messages array
709
- messages: [...state.context.messages, newMessage],
710
- },
711
- });
712
- };
436
+ // Handle new messages from subscription
437
+ useEffect(() => {
438
+ if (newMessage?.chatMessageAdded && channel?.id) {
439
+ const incomingMessage = newMessage.chatMessageAdded;
440
+
441
+ // Check if we already have this message
442
+ if (messages.some((msg) => msg.id === incomingMessage.id)) {
443
+ console.log('Message already in local state, skipping update:', incomingMessage.id);
444
+ return;
445
+ }
446
+
447
+ console.log('New message received from subscription:', {
448
+ channelId: channel.id,
449
+ messageId: incomingMessage.id,
450
+ message: incomingMessage.message?.substring(0, 20) + '...',
451
+ timestamp: incomingMessage.createdAt,
452
+ });
453
+
454
+ // Update messages and last message state
455
+ setMessages((prevMessages) => [...prevMessages, incomingMessage]);
456
+ setLastMessage(incomingMessage);
457
+ }
458
+ }, [newMessage, channel?.id, messages]);
713
459
 
714
460
  // Create listener for channel property updates
715
461
  useEffect(() => {
716
462
  // Check if channel has a lastMessage property (set by parent Dialogs component)
717
463
  if (channel?.lastMessage && channel.lastMessage.id) {
718
464
  console.log('Channel has lastMessage property:', channel.lastMessage.id);
719
- handleChannelMessage(channel, channel.lastMessage);
465
+
466
+ // Check if we already have this message
467
+ if (!messages.some((msg) => msg.id === channel.lastMessage.id)) {
468
+ setMessages((prevMessages) => [...prevMessages, channel.lastMessage]);
469
+ setLastMessage(channel.lastMessage);
470
+ }
720
471
  }
721
- }, [channel?.lastMessage?.id]);
472
+ }, [channel?.lastMessage?.id, messages]);
722
473
 
723
474
  return (
724
475
  <Pressable
725
- onPress={() => onOpen(channel?.id, title)}
476
+ onPress={() => onOpen(channel?.id, displayTitle)}
726
477
  className="flex-1 border-gray-200 rounded-md dark:border-gray-600 dark:bg-gray-700"
727
478
  style={{ borderBottomWidth: 1, borderColor: '#e5e7eb', marginVertical: 0, paddingHorizontal: 10 }}
728
479
  >
@@ -774,8 +525,8 @@ export const DialogsListItemComponent: React.FC<IDialogListItemProps> = function
774
525
  </Box>
775
526
  <Box className="flex-1">
776
527
  <LastMessageComponent
777
- key={`last-msg-${lastMessage?.id || 'none'}-${safeContextProperty('messages', []).length}`}
778
- title={title}
528
+ key={`last-msg-${lastMessage?.id || 'none'}-${messages.length}`}
529
+ title={displayTitle}
779
530
  lastMessage={lastMessage}
780
531
  channelId={channel?.id}
781
532
  subscribeToNewMessages={() =>
@@ -798,28 +549,13 @@ export const DialogsListItemComponent: React.FC<IDialogListItemProps> = function
798
549
  timestamp: newMessage.createdAt || newMessage.updatedAt,
799
550
  });
800
551
 
801
- // Force message update (double approach to ensure it updates)
552
+ // Direct optimistic update through local state
802
553
  if (isMountedRef.current) {
803
- // Update through state machine directly
804
- console.log('Updating messages with new subscription message');
805
-
806
- // First, update the messages array with the new message
807
- const currentMessages = safeContextProperty('messages', []);
808
- const updatedMessages = [...currentMessages, newMessage];
809
-
810
- // Then directly set the new message as the last message
811
- // This ensures the UI updates immediately
812
- safeSend({
813
- type: Actions.UPDATE_MESSAGES,
814
- data: {
815
- messages: updatedMessages,
816
- // Directly set this message as the last message
817
- directLastMessage: newMessage,
818
- },
819
- });
820
-
821
- // No need for additional refresh - the direct update is enough
822
- // This prevents unnecessary network requests and UI flicker
554
+ // Update messages array with the new message if it doesn't exist
555
+ if (!messages.some((msg) => msg.id === newMessage.id)) {
556
+ setMessages((prevMessages) => [...prevMessages, newMessage]);
557
+ setLastMessage(newMessage);
558
+ }
823
559
  }
824
560
 
825
561
  // Update the Apollo cache