@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
@@ -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,223 +67,58 @@ export interface IDialogListItemProps {
68
67
  role: any;
69
68
  }
70
69
 
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 = [];
70
+ // Helper function to compute the last message from a list of messages
71
+ const computeLastMessage = (threadMessages) => {
72
+ if (!threadMessages || !threadMessages.length) return null;
224
73
 
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;
74
+ // Extract the post and replies from thread messages
75
+ let posts = [];
76
+ let replies = [];
228
77
 
229
- // If message is empty or only contains image-specific markers, ignore it
230
- if (!msg.message || msg.message === '') return false;
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;
231
81
 
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
- };
82
+ // If message is empty or only contains image-specific markers, ignore it
83
+ if (!msg.message || msg.message === '') return false;
243
84
 
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
- });
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/')));
252
92
 
253
- // Combine and sort all messages
254
- const allMessages = [...posts, ...replies];
255
- if (!allMessages.length) return null;
93
+ // Return true only for text messages (not image messages)
94
+ return !isImageMessage;
95
+ };
256
96
 
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);
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
+ });
261
105
 
262
- // If either date is invalid, prefer the message with valid date
263
- if (!dateA) return b;
264
- if (!dateB) return a;
106
+ // Combine and sort all messages
107
+ const allMessages = [...posts, ...replies];
108
+ if (!allMessages.length) return null;
265
109
 
266
- return dateA > dateB ? a : b;
267
- }, allMessages[0]);
268
- };
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);
269
114
 
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]);
115
+ // If either date is invalid, prefer the message with valid date
116
+ if (!dateA) return b;
117
+ if (!dateB) return a;
284
118
 
285
- // Return as a tuple to match useMachine API
286
- return [stateWithMatches, send] as const;
287
- }
119
+ return dateA > dateB ? a : b;
120
+ }, allMessages[0]);
121
+ };
288
122
 
289
123
  const ServiceChannelWithLastMessage = React.memo(
290
124
  ({ channel, lastMessage, subscribeToNewMessages, subscribeToChatMessages }: any) => {
@@ -397,35 +231,11 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
397
231
  }) {
398
232
  // Create a ref to track if component is mounted
399
233
  const isMountedRef = useRef(true);
400
- // Define safe access functions
401
- const [state, send] = useSafeMachine(serviceDialogsListItemXstate);
402
-
403
- const safeContextProperty = useCallback(
404
- (property, defaultValue = null) => {
405
- try {
406
- return state?.context?.[property] ?? defaultValue;
407
- } catch (error) {
408
- console.error(`Error accessing state.context.${property}:`, error);
409
- return defaultValue;
410
- }
411
- },
412
- [state],
413
- );
414
-
415
- const safeSend = useCallback(
416
- (event) => {
417
- try {
418
- send(event);
419
- } catch (error) {
420
- console.error('Error sending event to state machine:', error, event);
421
- }
422
- },
423
- [send],
424
- );
234
+ const [subscriptionsTimestamp, setSubscriptionsTimestamp] = useState(Date.now());
425
235
 
426
236
  // Query for thread messages
427
237
  const {
428
- data: threadMessages,
238
+ data: threadMessagesData,
429
239
  loading: threadMessageLoading,
430
240
  error,
431
241
  refetch: refetchThreadMessages,
@@ -448,71 +258,28 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
448
258
  },
449
259
  });
450
260
 
451
- // Update state when channel or currentUser changes
452
- React.useEffect(() => {
453
- if (channel?.id) {
454
- safeSend({
455
- type: Actions.INITIAL_CONTEXT,
456
- data: {
457
- channelId: channel?.id?.toString(),
458
- currentUser,
459
- role,
460
- },
461
- });
261
+ // Extract threadMessages data for easier access
262
+ const threadMessages = threadMessagesData?.threadMessages?.data || [];
462
263
 
463
- safeSend({
464
- type: Actions.SET_TITLE,
465
- data: {
466
- title: channel?.title || '',
467
- },
468
- });
469
- }
264
+ // Calculate lastMessage directly from threadMessages
265
+ const lastMessage = useMemo(() => {
266
+ return computeLastMessage(threadMessages);
267
+ }, [threadMessages]);
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]);
470
274
 
275
+ // Handle component unmount
276
+ React.useEffect(() => {
471
277
  return () => {
472
278
  isMountedRef.current = false;
473
279
  };
474
- }, [channel?.id, currentUser, role, safeSend]);
475
-
476
- // Update state when thread messages are loaded or refreshed
477
- React.useEffect(() => {
478
- if (threadMessages && isMountedRef.current) {
479
- console.log(
480
- `Updating thread messages for channel ${channel?.id}, count:`,
481
- threadMessages?.threadMessages?.data?.length || 0,
482
- );
483
-
484
- safeSend({
485
- type: Actions.UPDATE_THREAD_MESSAGES,
486
- data: {
487
- threadMessages: threadMessages?.threadMessages?.data || [],
488
- },
489
- });
490
- }
491
- }, [threadMessages, refreshing, channel?.id, safeSend]);
492
-
493
- // Use computed values from state
494
- const lastMessage = safeContextProperty('lastMessage', null);
495
- const servicePostParentId = safeContextProperty('servicePostParentId', null);
496
-
497
- // Update servicePostParentId when lastMessage changes
498
- React.useEffect(() => {
499
- if (lastMessage) {
500
- const sParentId = lastMessage?.parentId ? lastMessage?.parentId : lastMessage?.id;
501
-
502
- if (sParentId !== safeContextProperty('servicePostParentId')) {
503
- console.log(`Updating servicePostParentId for channel ${channel?.id} to:`, sParentId);
504
-
505
- safeSend({
506
- type: Actions.SET_SERVICE_POST_PARENT_ID,
507
- data: {
508
- servicePostParentId: sParentId,
509
- },
510
- });
511
- }
512
- }
513
- }, [lastMessage, safeSend, channel?.id, safeContextProperty]);
280
+ }, []);
514
281
 
515
- const creatorAndMembersId = React.useMemo(() => {
282
+ const creatorAndMembersId = useMemo(() => {
516
283
  if (!channel?.members) return null;
517
284
  const membersIds: any =
518
285
  channel?.members
@@ -523,7 +290,7 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
523
290
  return mergedIds?.filter((m: any, pos: any) => mergedIds?.indexOf(m) === pos) ?? [];
524
291
  }, [channel, currentUser]);
525
292
 
526
- const postParentId = React.useMemo(() => {
293
+ const postParentId = useMemo(() => {
527
294
  if (!creatorAndMembersId?.length) return null;
528
295
 
529
296
  return creatorAndMembersId?.length && creatorAndMembersId?.includes(currentUser?.id)
@@ -537,38 +304,17 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
537
304
  const refreshThreadState = useCallback(() => {
538
305
  if (channel?.id && isMountedRef.current) {
539
306
  console.log('Forcing thread state refresh for channel:', channel?.id);
540
- // First ensure we're in a loading state to trigger UI updates
541
- safeSend({ type: Actions.START_LOADING });
542
307
 
543
308
  // Refetch messages from server
544
309
  refetchThreadMessages({
545
310
  channelId: channel?.id?.toString(),
546
311
  role,
547
312
  limit: 2,
548
- })
549
- .then((result) => {
550
- // Update the state with fresh data
551
- if (result.data?.threadMessages?.data && isMountedRef.current) {
552
- console.log(
553
- `Refreshed ${result.data.threadMessages.data.length} thread messages for channel ${channel?.id}`,
554
- );
555
-
556
- safeSend({
557
- type: Actions.UPDATE_THREAD_MESSAGES,
558
- data: { threadMessages: result.data.threadMessages.data },
559
- });
560
- }
561
- safeSend({ type: Actions.STOP_LOADING });
562
- })
563
- .catch((err) => {
564
- console.error('Error refreshing thread state:', err);
565
- safeSend({ type: Actions.STOP_LOADING });
566
- });
313
+ }).catch((err) => {
314
+ console.error('Error refreshing thread state:', err);
315
+ });
567
316
  }
568
- }, [channel?.id, refetchThreadMessages, safeSend, isMountedRef, role]);
569
-
570
- // Add a timestamp to track when subscriptions were last set up
571
- const [subscriptionsTimestamp, setSubscriptionsTimestamp] = useState(Date.now());
317
+ }, [channel?.id, refetchThreadMessages, isMountedRef, role]);
572
318
 
573
319
  // Reset subscriptions when the component gains focus
574
320
  useFocusEffect(
@@ -581,39 +327,17 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
581
327
 
582
328
  console.log('ServiceDialogsListItem focused for channel:', channel?.id);
583
329
 
584
- // Show loading state
585
- safeSend({ type: Actions.START_LOADING });
586
-
587
330
  // Use a direct refetch with network-only policy to force fresh data
588
331
  const fetchFreshData = async () => {
589
332
  try {
590
333
  // Force a network-only fetch
591
- const result = await refetchThreadMessages({
334
+ await refetchThreadMessages({
592
335
  channelId: channel?.id?.toString(),
593
336
  role,
594
337
  limit: 2,
595
338
  });
596
-
597
- // Log the refreshed data
598
- console.log(
599
- `FOCUS EFFECT: Refetched ${
600
- result?.data?.threadMessages?.data?.length || 0
601
- } messages for channel ${channel?.id}`,
602
- );
603
-
604
- if (result?.data?.threadMessages?.data && isMountedRef.current) {
605
- // Update state with fresh data
606
- safeSend({
607
- type: Actions.UPDATE_THREAD_MESSAGES,
608
- data: { threadMessages: result.data.threadMessages.data },
609
- });
610
- }
611
339
  } catch (error) {
612
340
  console.error('Error refetching thread messages on focus:', error);
613
- } finally {
614
- if (isMountedRef.current) {
615
- safeSend({ type: Actions.STOP_LOADING });
616
- }
617
341
  }
618
342
  };
619
343
 
@@ -623,12 +347,13 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
623
347
  return () => {
624
348
  // Cleanup function when unfocused
625
349
  };
626
- }, [refreshing, channel?.id, refetchThreadMessages, safeSend, role, isMountedRef]),
350
+ }, [refreshing, channel?.id, refetchThreadMessages, role]),
627
351
  );
628
352
 
629
353
  return (
630
354
  <Pressable
631
- 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)}
632
357
  className="flex-1 rounded-md border-gray-200 dark:border-gray-600 dark:bg-gray-700"
633
358
  style={{ borderBottomWidth: 1, borderColor: '#e5e7eb', marginVertical: 0 }}
634
359
  >
@@ -712,17 +437,6 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
712
437
  updatedThreads = [...prevThreads, newPostThreadData];
713
438
  }
714
439
 
715
- // Use the safe state update function
716
- if (isMountedRef.current) {
717
- safeSend({
718
- type: Actions.UPDATE_THREAD_MESSAGES,
719
- data: {
720
- threadMessages: updatedThreads,
721
- directLastMessage: newMessage, // Directly set the new message
722
- },
723
- });
724
- }
725
-
726
440
  return {
727
441
  ...prev,
728
442
  threadMessages: {
@@ -794,17 +508,6 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
794
508
  console.log(
795
509
  `Cannot find thread for chat message in channel ${channel?.id}`,
796
510
  );
797
-
798
- // Even if we can't find the thread, update the UI with the new message
799
- if (isMountedRef.current) {
800
- safeSend({
801
- type: Actions.UPDATE_THREAD_MESSAGES,
802
- data: {
803
- directLastMessage: newMessage,
804
- },
805
- });
806
- }
807
-
808
511
  return prev;
809
512
  }
810
513
 
@@ -825,17 +528,6 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
825
528
 
826
529
  updatedThreads[threadIndex] = thread;
827
530
 
828
- // Use the safe state update function
829
- if (isMountedRef.current) {
830
- safeSend({
831
- type: Actions.UPDATE_THREAD_MESSAGES,
832
- data: {
833
- threadMessages: updatedThreads,
834
- directLastMessage: newMessage,
835
- },
836
- });
837
- }
838
-
839
531
  return {
840
532
  ...prev,
841
533
  threadMessages: {
@@ -65,13 +65,11 @@ export default class Bubble extends React.Component<any> {
65
65
 
66
66
  // Add validation for image URL
67
67
  if (!image || typeof image !== 'string') {
68
- console.log('Invalid image URL:', image);
69
68
  return null;
70
69
  }
71
70
 
72
- // Log the image URL for debugging
73
- console.log('Rendering message image:', image);
74
-
71
+ // Class components can't use hooks like useMemo
72
+ // Directly render the CachedImage instead
75
73
  return (
76
74
  <TouchableHighlight
77
75
  underlayColor={'transparent'}