@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.
- package/CHANGELOG.md +8 -0
- package/lib/screens/inbox/components/CachedImage/index.js +0 -19
- package/lib/screens/inbox/components/CachedImage/index.js.map +1 -1
- package/lib/screens/inbox/components/DialogsListItem.js +423 -50
- package/lib/screens/inbox/components/DialogsListItem.js.map +1 -1
- package/lib/screens/inbox/components/ServiceDialogsListItem.js +375 -51
- package/lib/screens/inbox/components/ServiceDialogsListItem.js.map +1 -1
- package/lib/screens/inbox/components/workflow/dialogs-list-item-xstate.js +175 -0
- package/lib/screens/inbox/components/workflow/dialogs-list-item-xstate.js.map +1 -0
- package/lib/screens/inbox/components/workflow/service-dialogs-list-item-xstate.js +191 -0
- package/lib/screens/inbox/components/workflow/service-dialogs-list-item-xstate.js.map +1 -0
- package/lib/screens/inbox/containers/Dialogs.js +536 -66
- package/lib/screens/inbox/containers/Dialogs.js.map +1 -1
- package/lib/screens/inbox/containers/ThreadConversationView.js +95 -23
- package/lib/screens/inbox/containers/ThreadConversationView.js.map +1 -1
- package/lib/screens/inbox/containers/workflow/dialogs-xstate.js +211 -0
- package/lib/screens/inbox/containers/workflow/dialogs-xstate.js.map +1 -0
- package/package.json +2 -2
- package/src/screens/inbox/components/CachedImage/index.tsx +9 -9
- package/src/screens/inbox/components/DialogsListItem.tsx +624 -107
- package/src/screens/inbox/components/ServiceDialogsListItem.tsx +506 -114
- package/src/screens/inbox/components/SupportServiceDialogsListItem.tsx +35 -17
- package/src/screens/inbox/components/workflow/dialogs-list-item-xstate.ts +145 -0
- package/src/screens/inbox/components/workflow/service-dialogs-list-item-xstate.ts +159 -0
- package/src/screens/inbox/containers/Dialogs.tsx +711 -169
- 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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
437
|
+
safeSend({
|
|
438
|
+
type: Actions.SET_TITLE,
|
|
439
|
+
data: {
|
|
440
|
+
title: channel?.title || '',
|
|
441
|
+
},
|
|
442
|
+
});
|
|
95
443
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
data?.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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);
|