@messenger-box/platform-mobile 10.0.3-alpha.34 → 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 +8 -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 +80 -256
- package/lib/screens/inbox/components/DialogsListItem.js.map +1 -1
- package/lib/screens/inbox/components/ServiceDialogsListItem.js +222 -324
- 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 +487 -888
- package/lib/screens/inbox/containers/ConversationView.js.map +1 -1
- package/lib/screens/inbox/containers/Dialogs.js +243 -547
- 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 +4 -4
- package/src/screens/inbox/components/CachedImage/index.tsx +191 -140
- package/src/screens/inbox/components/DialogsListItem.tsx +112 -345
- package/src/screens/inbox/components/ServiceDialogsListItem.tsx +316 -437
- package/src/screens/inbox/components/SlackMessageContainer/SlackBubble.tsx +2 -4
- package/src/screens/inbox/containers/ConversationView.tsx +676 -993
- package/src/screens/inbox/containers/ConversationView.tsx.bk +1467 -0
- package/src/screens/inbox/containers/Dialogs.tsx +345 -636
- 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
|
@@ -24,7 +24,6 @@ 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
27
|
|
|
29
28
|
// Helper function to safely create a Date object
|
|
30
29
|
const safeDate = (dateValue) => {
|
|
@@ -68,292 +67,153 @@ export interface IDialogListItemProps {
|
|
|
68
67
|
role: any;
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
}
|
|
70
|
+
// Helper function to compute the last message from a list of messages
|
|
71
|
+
const computeLastMessage = (threadMessages) => {
|
|
72
|
+
if (!threadMessages || !threadMessages.length) return null;
|
|
144
73
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
}, []);
|
|
74
|
+
// Extract the post and replies from thread messages
|
|
75
|
+
let posts = [];
|
|
76
|
+
let replies = [];
|
|
216
77
|
|
|
217
|
-
// Helper function to
|
|
218
|
-
const
|
|
219
|
-
if (!
|
|
78
|
+
// Helper function to check if a message is an image or contains only image content
|
|
79
|
+
const isTextMessage = (msg) => {
|
|
80
|
+
if (!msg) return false;
|
|
220
81
|
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
let replies = [];
|
|
82
|
+
// If message is empty or only contains image-specific markers, ignore it
|
|
83
|
+
if (!msg.message || msg.message === '') return false;
|
|
224
84
|
|
|
225
|
-
//
|
|
226
|
-
const
|
|
227
|
-
|
|
85
|
+
// Check if message looks like an image URL or reference
|
|
86
|
+
const isImageMessage =
|
|
87
|
+
msg.message.includes('<img') ||
|
|
88
|
+
msg.message.includes('[Image]') ||
|
|
89
|
+
msg.message.includes('![') ||
|
|
90
|
+
(/\.(jpeg|jpg|gif|png|bmp|webp)/i.test(msg.message) &&
|
|
91
|
+
(msg.message.includes('http') || msg.message.includes('/images/')));
|
|
92
|
+
|
|
93
|
+
// Return true only for text messages (not image messages)
|
|
94
|
+
return !isImageMessage;
|
|
95
|
+
};
|
|
228
96
|
|
|
229
|
-
|
|
230
|
-
|
|
97
|
+
threadMessages.forEach((thread) => {
|
|
98
|
+
if (thread?.post && isTextMessage(thread.post)) {
|
|
99
|
+
posts.push(thread.post);
|
|
100
|
+
}
|
|
101
|
+
if (thread?.replies?.length) {
|
|
102
|
+
replies = [...replies, ...thread.replies.filter((r) => isTextMessage(r))];
|
|
103
|
+
}
|
|
104
|
+
});
|
|
231
105
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
};
|
|
106
|
+
// Combine and sort all messages
|
|
107
|
+
const allMessages = [...posts, ...replies];
|
|
108
|
+
if (!allMessages.length) return null;
|
|
243
109
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (thread?.replies?.length) {
|
|
249
|
-
replies = [...replies, ...thread.replies.filter((r) => isTextMessage(r))];
|
|
250
|
-
}
|
|
251
|
-
});
|
|
110
|
+
// Return the most recent message - use safe date comparison
|
|
111
|
+
return allMessages.reduce((a, b) => {
|
|
112
|
+
const dateA = safeDate(a?.createdAt);
|
|
113
|
+
const dateB = safeDate(b?.createdAt);
|
|
252
114
|
|
|
253
|
-
//
|
|
254
|
-
|
|
255
|
-
if (!
|
|
115
|
+
// If either date is invalid, prefer the message with valid date
|
|
116
|
+
if (!dateA) return b;
|
|
117
|
+
if (!dateB) return a;
|
|
256
118
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const dateB = safeDate(b?.createdAt);
|
|
119
|
+
return dateA > dateB ? a : b;
|
|
120
|
+
}, allMessages[0]);
|
|
121
|
+
};
|
|
261
122
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
123
|
+
const ServiceChannelWithLastMessage = React.memo(
|
|
124
|
+
({ channel, lastMessage, subscribeToNewMessages, subscribeToChatMessages }: any) => {
|
|
125
|
+
React.useEffect(() => {
|
|
126
|
+
console.log(`Setting up subscriptions for channel ${channel?.id}`);
|
|
127
|
+
// Subscribe and store the unsubscribe functions
|
|
128
|
+
let threadUnsubscribe;
|
|
129
|
+
let chatUnsubscribe;
|
|
265
130
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
131
|
+
try {
|
|
132
|
+
threadUnsubscribe = subscribeToNewMessages();
|
|
133
|
+
chatUnsubscribe = subscribeToChatMessages();
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error(`Error setting up subscriptions for channel ${channel?.id}:`, error);
|
|
136
|
+
}
|
|
269
137
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
return false;
|
|
138
|
+
return () => {
|
|
139
|
+
// Cleanup subscriptions on unmount
|
|
140
|
+
if (threadUnsubscribe && typeof threadUnsubscribe === 'function') {
|
|
141
|
+
try {
|
|
142
|
+
console.log(`Cleaning up thread subscription for channel ${channel?.id}`);
|
|
143
|
+
threadUnsubscribe();
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error(`Error cleaning up thread subscription for channel ${channel?.id}:`, error);
|
|
146
|
+
}
|
|
280
147
|
}
|
|
281
|
-
},
|
|
282
|
-
};
|
|
283
|
-
}, [state]);
|
|
284
148
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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]);
|
|
149
|
+
if (chatUnsubscribe && typeof chatUnsubscribe === 'function') {
|
|
150
|
+
try {
|
|
151
|
+
console.log(`Cleaning up chat subscription for channel ${channel?.id}`);
|
|
152
|
+
chatUnsubscribe();
|
|
153
|
+
} catch (error) {
|
|
154
|
+
console.error(`Error cleaning up chat subscription for channel ${channel?.id}:`, error);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}, [subscribeToNewMessages, subscribeToChatMessages, channel?.id]);
|
|
159
|
+
|
|
160
|
+
// Debug output for component rendering
|
|
161
|
+
React.useEffect(() => {
|
|
162
|
+
console.log(`ServiceChannelWithLastMessage rendered for channel ${channel?.id}:`, {
|
|
163
|
+
hasLastMessage: !!lastMessage,
|
|
164
|
+
messageId: lastMessage?.id,
|
|
165
|
+
messageText: lastMessage?.message?.substring(0, 20) + (lastMessage?.message?.length > 20 ? '...' : ''),
|
|
166
|
+
date: lastMessage?.createdAt ? safeDate(lastMessage.createdAt)?.toISOString() : 'none',
|
|
167
|
+
});
|
|
168
|
+
}, [lastMessage, channel?.id]);
|
|
310
169
|
|
|
311
|
-
|
|
312
|
-
|
|
170
|
+
const count = 30;
|
|
171
|
+
const title = channel?.title.slice(0, count) + (channel?.title.length > count ? '...' : '');
|
|
313
172
|
|
|
314
|
-
|
|
315
|
-
|
|
173
|
+
// Define message display text
|
|
174
|
+
let displayMessage = 'No messages yet';
|
|
316
175
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
176
|
+
if (lastMessage) {
|
|
177
|
+
// Check for file attachments
|
|
178
|
+
const hasFileAttachments = lastMessage.files?.data?.length > 0;
|
|
320
179
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
180
|
+
// Check if the message appears to be an image
|
|
181
|
+
const isImageMessage =
|
|
182
|
+
lastMessage.message?.includes('<img') ||
|
|
183
|
+
lastMessage.message?.includes('[Image]') ||
|
|
184
|
+
lastMessage.message?.includes('![') ||
|
|
185
|
+
(/\.(jpeg|jpg|gif|png|bmp|webp)/i.test(lastMessage.message || '') &&
|
|
186
|
+
((lastMessage.message || '').includes('http') || (lastMessage.message || '').includes('/images/')));
|
|
187
|
+
|
|
188
|
+
if (hasFileAttachments) {
|
|
189
|
+
displayMessage = '📎 File attachment';
|
|
190
|
+
} else if (isImageMessage) {
|
|
191
|
+
displayMessage = '[Image]';
|
|
192
|
+
} else if (lastMessage.message && lastMessage.message.trim() !== '') {
|
|
193
|
+
displayMessage = lastMessage.message;
|
|
194
|
+
} else {
|
|
195
|
+
displayMessage = '(Empty message)';
|
|
196
|
+
}
|
|
337
197
|
}
|
|
338
|
-
}
|
|
339
198
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
199
|
+
return (
|
|
200
|
+
<HStack space={'sm'} className="flex-1 justify-between">
|
|
201
|
+
<Box className="flex-[0.8]">
|
|
202
|
+
<Text color={colors.gray[600]} className="text-base text-wrap flex-wrap font-semibold">
|
|
203
|
+
{title}
|
|
204
|
+
</Text>
|
|
205
|
+
<Text color={colors.gray[600]} numberOfLines={1}>
|
|
206
|
+
{displayMessage}
|
|
207
|
+
</Text>
|
|
208
|
+
</Box>
|
|
209
|
+
|
|
210
|
+
<Box className="flex-[0.2]">
|
|
211
|
+
<Text color={colors.gray[500]}>{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}</Text>
|
|
212
|
+
</Box>
|
|
213
|
+
</HStack>
|
|
214
|
+
);
|
|
215
|
+
},
|
|
216
|
+
);
|
|
357
217
|
|
|
358
218
|
/**
|
|
359
219
|
* TODO:
|
|
@@ -371,35 +231,11 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
|
|
|
371
231
|
}) {
|
|
372
232
|
// Create a ref to track if component is mounted
|
|
373
233
|
const isMountedRef = useRef(true);
|
|
374
|
-
|
|
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
|
-
);
|
|
234
|
+
const [subscriptionsTimestamp, setSubscriptionsTimestamp] = useState(Date.now());
|
|
399
235
|
|
|
400
236
|
// Query for thread messages
|
|
401
237
|
const {
|
|
402
|
-
data:
|
|
238
|
+
data: threadMessagesData,
|
|
403
239
|
loading: threadMessageLoading,
|
|
404
240
|
error,
|
|
405
241
|
refetch: refetchThreadMessages,
|
|
@@ -422,66 +258,28 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
|
|
|
422
258
|
},
|
|
423
259
|
});
|
|
424
260
|
|
|
425
|
-
//
|
|
426
|
-
|
|
427
|
-
if (channel?.id) {
|
|
428
|
-
safeSend({
|
|
429
|
-
type: Actions.INITIAL_CONTEXT,
|
|
430
|
-
data: {
|
|
431
|
-
channelId: channel?.id?.toString(),
|
|
432
|
-
currentUser,
|
|
433
|
-
role,
|
|
434
|
-
},
|
|
435
|
-
});
|
|
261
|
+
// Extract threadMessages data for easier access
|
|
262
|
+
const threadMessages = threadMessagesData?.threadMessages?.data || [];
|
|
436
263
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
},
|
|
442
|
-
});
|
|
443
|
-
}
|
|
264
|
+
// Calculate lastMessage directly from threadMessages
|
|
265
|
+
const lastMessage = useMemo(() => {
|
|
266
|
+
return computeLastMessage(threadMessages);
|
|
267
|
+
}, [threadMessages]);
|
|
444
268
|
|
|
269
|
+
// Calculate servicePostParentId based on lastMessage
|
|
270
|
+
const servicePostParentId = useMemo(() => {
|
|
271
|
+
if (!lastMessage) return null;
|
|
272
|
+
return lastMessage?.parentId ? lastMessage?.parentId : lastMessage?.id;
|
|
273
|
+
}, [lastMessage]);
|
|
274
|
+
|
|
275
|
+
// Handle component unmount
|
|
276
|
+
React.useEffect(() => {
|
|
445
277
|
return () => {
|
|
446
278
|
isMountedRef.current = false;
|
|
447
279
|
};
|
|
448
|
-
}, [
|
|
449
|
-
|
|
450
|
-
// Update state when thread messages are loaded or refreshed
|
|
451
|
-
React.useEffect(() => {
|
|
452
|
-
if (threadMessages && isMountedRef.current) {
|
|
453
|
-
console.log(
|
|
454
|
-
`Updating thread messages for channel ${channel?.id}, count:`,
|
|
455
|
-
threadMessages?.threadMessages?.data?.length || 0,
|
|
456
|
-
);
|
|
457
|
-
|
|
458
|
-
safeSend({
|
|
459
|
-
type: Actions.UPDATE_THREAD_MESSAGES,
|
|
460
|
-
data: {
|
|
461
|
-
threadMessages: threadMessages?.threadMessages?.data || [],
|
|
462
|
-
},
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
}, [threadMessages, refreshing, channel?.id, safeSend]);
|
|
466
|
-
|
|
467
|
-
// Use computed values from state
|
|
468
|
-
const lastMessage = safeContextProperty('lastMessage', null);
|
|
469
|
-
const servicePostParentId = safeContextProperty('servicePostParentId', null);
|
|
470
|
-
|
|
471
|
-
// Update servicePostParentId when lastMessage changes
|
|
472
|
-
React.useEffect(() => {
|
|
473
|
-
if (lastMessage) {
|
|
474
|
-
const sParentId = lastMessage?.parentId ? lastMessage?.parentId : lastMessage?.id;
|
|
475
|
-
safeSend({
|
|
476
|
-
type: Actions.SET_SERVICE_POST_PARENT_ID,
|
|
477
|
-
data: {
|
|
478
|
-
servicePostParentId: sParentId,
|
|
479
|
-
},
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
}, [lastMessage, safeSend]);
|
|
280
|
+
}, []);
|
|
483
281
|
|
|
484
|
-
const creatorAndMembersId =
|
|
282
|
+
const creatorAndMembersId = useMemo(() => {
|
|
485
283
|
if (!channel?.members) return null;
|
|
486
284
|
const membersIds: any =
|
|
487
285
|
channel?.members
|
|
@@ -492,7 +290,7 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
|
|
|
492
290
|
return mergedIds?.filter((m: any, pos: any) => mergedIds?.indexOf(m) === pos) ?? [];
|
|
493
291
|
}, [channel, currentUser]);
|
|
494
292
|
|
|
495
|
-
const postParentId =
|
|
293
|
+
const postParentId = useMemo(() => {
|
|
496
294
|
if (!creatorAndMembersId?.length) return null;
|
|
497
295
|
|
|
498
296
|
return creatorAndMembersId?.length && creatorAndMembersId?.includes(currentUser?.id)
|
|
@@ -506,76 +304,40 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
|
|
|
506
304
|
const refreshThreadState = useCallback(() => {
|
|
507
305
|
if (channel?.id && isMountedRef.current) {
|
|
508
306
|
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
307
|
|
|
512
308
|
// Refetch messages from server
|
|
513
309
|
refetchThreadMessages({
|
|
514
310
|
channelId: channel?.id?.toString(),
|
|
515
311
|
role,
|
|
516
312
|
limit: 2,
|
|
517
|
-
})
|
|
518
|
-
.
|
|
519
|
-
|
|
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
|
-
});
|
|
313
|
+
}).catch((err) => {
|
|
314
|
+
console.error('Error refreshing thread state:', err);
|
|
315
|
+
});
|
|
536
316
|
}
|
|
537
|
-
}, [channel?.id, refetchThreadMessages,
|
|
317
|
+
}, [channel?.id, refetchThreadMessages, isMountedRef, role]);
|
|
538
318
|
|
|
539
|
-
//
|
|
319
|
+
// Reset subscriptions when the component gains focus
|
|
540
320
|
useFocusEffect(
|
|
541
321
|
React.useCallback(() => {
|
|
322
|
+
// Force subscription refresh
|
|
323
|
+
setSubscriptionsTimestamp(Date.now());
|
|
324
|
+
|
|
325
|
+
// Existing focus effect logic...
|
|
542
326
|
if (!channel?.id) return;
|
|
543
327
|
|
|
544
328
|
console.log('ServiceDialogsListItem focused for channel:', channel?.id);
|
|
545
329
|
|
|
546
|
-
// Show loading state
|
|
547
|
-
safeSend({ type: Actions.START_LOADING });
|
|
548
|
-
|
|
549
330
|
// Use a direct refetch with network-only policy to force fresh data
|
|
550
331
|
const fetchFreshData = async () => {
|
|
551
332
|
try {
|
|
552
333
|
// Force a network-only fetch
|
|
553
|
-
|
|
334
|
+
await refetchThreadMessages({
|
|
554
335
|
channelId: channel?.id?.toString(),
|
|
555
336
|
role,
|
|
556
337
|
limit: 2,
|
|
557
338
|
});
|
|
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
339
|
} catch (error) {
|
|
574
340
|
console.error('Error refetching thread messages on focus:', error);
|
|
575
|
-
} finally {
|
|
576
|
-
if (isMountedRef.current) {
|
|
577
|
-
safeSend({ type: Actions.STOP_LOADING });
|
|
578
|
-
}
|
|
579
341
|
}
|
|
580
342
|
};
|
|
581
343
|
|
|
@@ -585,12 +347,13 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
|
|
|
585
347
|
return () => {
|
|
586
348
|
// Cleanup function when unfocused
|
|
587
349
|
};
|
|
588
|
-
}, [refreshing, channel?.id, refetchThreadMessages,
|
|
350
|
+
}, [refreshing, channel?.id, refetchThreadMessages, role]),
|
|
589
351
|
);
|
|
590
352
|
|
|
591
353
|
return (
|
|
592
354
|
<Pressable
|
|
593
|
-
onPress={() => channel?.id !== selectedChannelId && onOpen(channel?.id, channel?.title, postParentId)}
|
|
355
|
+
// onPress={() => channel?.id !== selectedChannelId && onOpen(channel?.id, channel?.title, postParentId)}
|
|
356
|
+
onPress={() => onOpen(channel?.id, channel?.title, postParentId)}
|
|
594
357
|
className="flex-1 rounded-md border-gray-200 dark:border-gray-600 dark:bg-gray-700"
|
|
595
358
|
style={{ borderBottomWidth: 1, borderColor: '#e5e7eb', marginVertical: 0 }}
|
|
596
359
|
>
|
|
@@ -613,6 +376,7 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
|
|
|
613
376
|
</Box>
|
|
614
377
|
<Box className="flex-1">
|
|
615
378
|
<ServiceChannelWithLastMessage
|
|
379
|
+
key={`channel-${channel?.id}-${subscriptionsTimestamp}`}
|
|
616
380
|
channel={channel}
|
|
617
381
|
lastMessage={lastMessage}
|
|
618
382
|
subscribeToNewMessages={() =>
|
|
@@ -620,52 +384,167 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
|
|
|
620
384
|
document: THREAD_CHAT_ADDED,
|
|
621
385
|
variables: {
|
|
622
386
|
channelId: channel?.id?.toString(),
|
|
623
|
-
postParentId: postParentId
|
|
387
|
+
postParentId: postParentId || servicePostParentId || null,
|
|
624
388
|
},
|
|
625
389
|
updateQuery: (prev, { subscriptionData }: any) => {
|
|
626
|
-
if (!subscriptionData
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}
|
|
648
|
-
: t,
|
|
649
|
-
) ?? [];
|
|
650
|
-
|
|
651
|
-
// Use the safe state update function
|
|
652
|
-
if (isMountedRef.current) {
|
|
653
|
-
safeSend({
|
|
654
|
-
type: Actions.UPDATE_THREAD_MESSAGES,
|
|
655
|
-
data: {
|
|
656
|
-
threadMessages: data?.length > 0 ? data : [newPostThreadData],
|
|
657
|
-
directLastMessage: newMessage, // Directly set the new message
|
|
658
|
-
},
|
|
390
|
+
if (!subscriptionData?.data) {
|
|
391
|
+
console.log(`No subscription data for channel ${channel?.id}`);
|
|
392
|
+
return prev;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
const newPostThreadData: any =
|
|
397
|
+
subscriptionData?.data?.threadCreatedUpdated?.data;
|
|
398
|
+
const newMessage: any =
|
|
399
|
+
subscriptionData?.data?.threadCreatedUpdated?.lastMessage;
|
|
400
|
+
|
|
401
|
+
if (!newPostThreadData || !newMessage) {
|
|
402
|
+
console.log(`Missing data in subscription for channel ${channel?.id}`);
|
|
403
|
+
return prev;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
console.log('New thread message subscription update:', {
|
|
407
|
+
channelId: channel?.id,
|
|
408
|
+
threadId: newPostThreadData?.id,
|
|
409
|
+
hasMessage: !!newMessage,
|
|
410
|
+
message: newMessage?.message?.substring(0, 20) + '...',
|
|
659
411
|
});
|
|
412
|
+
|
|
413
|
+
// Create a safe copy of the previous data
|
|
414
|
+
const prevThreads = prev?.threadMessages?.data || [];
|
|
415
|
+
|
|
416
|
+
// Check if this thread already exists
|
|
417
|
+
const threadExists = prevThreads.some(
|
|
418
|
+
(t: any) => t.id === newPostThreadData?.id,
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
let updatedThreads;
|
|
422
|
+
if (threadExists) {
|
|
423
|
+
// Update existing thread
|
|
424
|
+
updatedThreads = prevThreads.map((t: any) =>
|
|
425
|
+
t.id === newPostThreadData?.id
|
|
426
|
+
? {
|
|
427
|
+
...t,
|
|
428
|
+
replies: [...(t?.replies || []), newMessage],
|
|
429
|
+
replyCount: newPostThreadData?.replyCount,
|
|
430
|
+
lastReplyAt: newPostThreadData?.lastReplyAt,
|
|
431
|
+
updatedAt: newPostThreadData?.updatedAt,
|
|
432
|
+
}
|
|
433
|
+
: t,
|
|
434
|
+
);
|
|
435
|
+
} else {
|
|
436
|
+
// Add new thread
|
|
437
|
+
updatedThreads = [...prevThreads, newPostThreadData];
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return {
|
|
441
|
+
...prev,
|
|
442
|
+
threadMessages: {
|
|
443
|
+
...prev?.threadMessages,
|
|
444
|
+
totalCount:
|
|
445
|
+
newPostThreadData?.totalCount ??
|
|
446
|
+
prev?.threadMessages?.totalCount ??
|
|
447
|
+
0,
|
|
448
|
+
data: updatedThreads,
|
|
449
|
+
},
|
|
450
|
+
};
|
|
451
|
+
} catch (error) {
|
|
452
|
+
console.error(
|
|
453
|
+
`Error processing subscription data for channel ${channel?.id}:`,
|
|
454
|
+
error,
|
|
455
|
+
);
|
|
456
|
+
return prev;
|
|
457
|
+
}
|
|
458
|
+
},
|
|
459
|
+
onError: (error) => {
|
|
460
|
+
console.error(`Thread subscription error for channel ${channel?.id}:`, error);
|
|
461
|
+
},
|
|
462
|
+
})
|
|
463
|
+
}
|
|
464
|
+
subscribeToChatMessages={() =>
|
|
465
|
+
subscribeToMore({
|
|
466
|
+
document: CHAT_MESSAGE_ADDED,
|
|
467
|
+
variables: {
|
|
468
|
+
channelId: channel?.id?.toString(),
|
|
469
|
+
postParentId: postParentId || servicePostParentId || null,
|
|
470
|
+
},
|
|
471
|
+
updateQuery: (prev, { subscriptionData }: any) => {
|
|
472
|
+
if (!subscriptionData?.data) {
|
|
473
|
+
console.log(`No chat message subscription data for channel ${channel?.id}`);
|
|
474
|
+
return prev;
|
|
660
475
|
}
|
|
661
476
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
477
|
+
try {
|
|
478
|
+
const newMessage = subscriptionData?.data?.chatMessageAdded;
|
|
479
|
+
|
|
480
|
+
if (!newMessage) {
|
|
481
|
+
console.log(
|
|
482
|
+
`Missing chat message data in subscription for channel ${channel?.id}`,
|
|
483
|
+
);
|
|
484
|
+
return prev;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
console.log('New chat message subscription update:', {
|
|
488
|
+
channelId: channel?.id,
|
|
489
|
+
messageId: newMessage?.id,
|
|
490
|
+
message: newMessage?.message?.substring(0, 20) + '...',
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
// Find the thread this message belongs to
|
|
494
|
+
const prevThreads = prev?.threadMessages?.data || [];
|
|
495
|
+
const threadIndex = prevThreads.findIndex((t: any) => {
|
|
496
|
+
// Check if this message belongs to this thread
|
|
497
|
+
if (newMessage.parentId && t.post?.id === newMessage.parentId) {
|
|
498
|
+
return true;
|
|
499
|
+
}
|
|
500
|
+
// Check replies
|
|
501
|
+
if (t.replies && t.replies.some((r: any) => r.id === newMessage.parentId)) {
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
return false;
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
if (threadIndex === -1) {
|
|
508
|
+
console.log(
|
|
509
|
+
`Cannot find thread for chat message in channel ${channel?.id}`,
|
|
510
|
+
);
|
|
511
|
+
return prev;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Update the thread with the new message
|
|
515
|
+
const updatedThreads = [...prevThreads];
|
|
516
|
+
const thread = { ...updatedThreads[threadIndex] };
|
|
517
|
+
|
|
518
|
+
// Add message to replies
|
|
519
|
+
thread.replies = [...(thread.replies || []), newMessage];
|
|
520
|
+
|
|
521
|
+
// Fix for arithmetic operation type error
|
|
522
|
+
const currentReplyCount =
|
|
523
|
+
typeof thread.replyCount === 'number' ? thread.replyCount : 0;
|
|
524
|
+
thread.replyCount = currentReplyCount + 1;
|
|
525
|
+
|
|
526
|
+
thread.lastReplyAt = newMessage.createdAt;
|
|
527
|
+
thread.updatedAt = newMessage.createdAt;
|
|
528
|
+
|
|
529
|
+
updatedThreads[threadIndex] = thread;
|
|
530
|
+
|
|
531
|
+
return {
|
|
532
|
+
...prev,
|
|
533
|
+
threadMessages: {
|
|
534
|
+
...prev?.threadMessages,
|
|
535
|
+
data: updatedThreads,
|
|
536
|
+
},
|
|
537
|
+
};
|
|
538
|
+
} catch (error) {
|
|
539
|
+
console.error(
|
|
540
|
+
`Error processing chat message subscription for channel ${channel?.id}:`,
|
|
541
|
+
error,
|
|
542
|
+
);
|
|
543
|
+
return prev;
|
|
544
|
+
}
|
|
545
|
+
},
|
|
546
|
+
onError: (error) => {
|
|
547
|
+
console.error(`Chat message subscription error for channel ${channel?.id}:`, error);
|
|
669
548
|
},
|
|
670
549
|
})
|
|
671
550
|
}
|