@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.
- package/CHANGELOG.md +4 -0
- package/lib/screens/inbox/components/CachedImage/index.js +125 -93
- package/lib/screens/inbox/components/CachedImage/index.js.map +1 -1
- package/lib/screens/inbox/components/DialogsListItem.js +75 -271
- package/lib/screens/inbox/components/DialogsListItem.js.map +1 -1
- package/lib/screens/inbox/components/ServiceDialogsListItem.js +184 -415
- package/lib/screens/inbox/components/ServiceDialogsListItem.js.map +1 -1
- package/lib/screens/inbox/components/SlackMessageContainer/SlackBubble.js +0 -2
- package/lib/screens/inbox/components/SlackMessageContainer/SlackBubble.js.map +1 -1
- package/lib/screens/inbox/containers/ConversationView.js +478 -944
- package/lib/screens/inbox/containers/ConversationView.js.map +1 -1
- package/lib/screens/inbox/containers/Dialogs.js +212 -628
- package/lib/screens/inbox/containers/Dialogs.js.map +1 -1
- package/lib/screens/inbox/containers/ThreadConversationView.js +409 -1364
- package/lib/screens/inbox/containers/ThreadConversationView.js.map +1 -1
- package/package.json +3 -3
- package/src/screens/inbox/components/CachedImage/index.tsx +191 -140
- package/src/screens/inbox/components/DialogsListItem.tsx +104 -368
- package/src/screens/inbox/components/ServiceDialogsListItem.tsx +69 -377
- package/src/screens/inbox/components/SlackMessageContainer/SlackBubble.tsx +2 -4
- package/src/screens/inbox/containers/ConversationView.tsx +660 -1060
- package/src/screens/inbox/containers/ConversationView.tsx.bk +1467 -0
- package/src/screens/inbox/containers/Dialogs.tsx +301 -763
- package/src/screens/inbox/containers/ThreadConversationView.tsx +661 -1887
- package/lib/screens/inbox/components/workflow/dialogs-list-item-xstate.js +0 -175
- package/lib/screens/inbox/components/workflow/dialogs-list-item-xstate.js.map +0 -1
- package/lib/screens/inbox/components/workflow/service-dialogs-list-item-xstate.js +0 -191
- package/lib/screens/inbox/components/workflow/service-dialogs-list-item-xstate.js.map +0 -1
- package/lib/screens/inbox/containers/workflow/conversation-xstate.js +0 -380
- package/lib/screens/inbox/containers/workflow/conversation-xstate.js.map +0 -1
- package/lib/screens/inbox/containers/workflow/dialogs-xstate.js +0 -211
- package/lib/screens/inbox/containers/workflow/dialogs-xstate.js.map +0 -1
- package/lib/screens/inbox/containers/workflow/thread-conversation-xstate.js +0 -438
- 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
|
-
//
|
|
338
|
-
const [
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
const
|
|
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
|
-
//
|
|
193
|
+
// Set mounted state on mount/unmount
|
|
410
194
|
useEffect(() => {
|
|
411
|
-
|
|
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
|
-
}, [
|
|
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
|
-
|
|
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
|
-
|
|
465
|
-
|
|
466
|
-
data: { messages: result.data.messages.data },
|
|
467
|
-
});
|
|
241
|
+
setMessages(result.data.messages.data);
|
|
242
|
+
setLastMessage(latestMessage);
|
|
468
243
|
}
|
|
469
|
-
|
|
244
|
+
setLoading(false);
|
|
470
245
|
})
|
|
471
246
|
.catch((err) => {
|
|
472
247
|
console.error('Error refreshing dialog state:', err);
|
|
473
|
-
|
|
248
|
+
setLoading(false);
|
|
474
249
|
});
|
|
475
250
|
}
|
|
476
|
-
}, [channel?.id, refetchMessages,
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
539
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
//
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
663
|
-
type: Actions.SET_TITLE,
|
|
664
|
-
data: { title: titleString },
|
|
665
|
-
});
|
|
417
|
+
setTitle(titleString);
|
|
666
418
|
}
|
|
667
|
-
}, [channelMembers,
|
|
419
|
+
}, [channelMembers, isMountedRef]);
|
|
668
420
|
|
|
669
421
|
// Compute title with proper truncation
|
|
670
|
-
const
|
|
671
|
-
const titleString = safeContextProperty('title', '');
|
|
422
|
+
const displayTitle = useMemo(() => {
|
|
672
423
|
const length = 30;
|
|
673
|
-
return
|
|
674
|
-
}, [
|
|
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:
|
|
432
|
+
messagesCount: messages.length,
|
|
689
433
|
});
|
|
690
|
-
}, [channel?.id, lastMessage,
|
|
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
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
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
|
-
|
|
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,
|
|
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'}-${
|
|
778
|
-
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
|
-
//
|
|
552
|
+
// Direct optimistic update through local state
|
|
802
553
|
if (isMountedRef.current) {
|
|
803
|
-
// Update
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
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
|