@messenger-box/platform-mobile 10.0.3-alpha.18 → 10.0.3-alpha.19

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.
@@ -14,7 +14,7 @@ import {
14
14
  Spinner,
15
15
  Text,
16
16
  } from '@admin-layout/gluestack-ui-mobile';
17
- import { Platform, Linking, SafeAreaView, View, TouchableHighlight } from 'react-native';
17
+ import { Platform, Linking, SafeAreaView, View, TouchableHighlight, Alert } from 'react-native';
18
18
  import { useFocusEffect, useNavigation, useRoute } from '@react-navigation/native';
19
19
  import { useSelector } from 'react-redux';
20
20
  import { orderBy, startCase, uniqBy } from 'lodash-es';
@@ -214,6 +214,8 @@ function useSafeMachine(machine) {
214
214
  context: {
215
215
  ...prev.context,
216
216
  loading: false,
217
+ loadingOldMessages:
218
+ event.data?.loadingOldMessages === false ? false : prev.context.loadingOldMessages,
217
219
  },
218
220
  }));
219
221
  } else if (event.type === ThreadActions.SEND_THREAD_MESSAGE) {
@@ -287,12 +289,76 @@ function useSafeMachine(machine) {
287
289
  } else if (event.type === 'FETCH_MORE_MESSAGES_SUCCESS') {
288
290
  setState((prev) => {
289
291
  const newMessages = event.data?.messages || [];
292
+ const apiTotalCount = event.data?.totalCount;
293
+ console.log(
294
+ `Merging ${newMessages.length} older messages with ${prev.context.threadMessages.length} existing messages`,
295
+ );
296
+
297
+ // Debug: Log the dates of the messages for troubleshooting
298
+ if (newMessages.length > 0) {
299
+ console.log('First new message date:', new Date(newMessages[0].createdAt).toISOString());
300
+ console.log(
301
+ 'Last new message date:',
302
+ new Date(newMessages[newMessages.length - 1].createdAt).toISOString(),
303
+ );
304
+ }
305
+
306
+ if (prev.context.threadMessages.length > 0) {
307
+ console.log(
308
+ 'First existing message date:',
309
+ new Date(prev.context.threadMessages[0].createdAt).toISOString(),
310
+ );
311
+ console.log(
312
+ 'Last existing message date:',
313
+ new Date(
314
+ prev.context.threadMessages[prev.context.threadMessages.length - 1].createdAt,
315
+ ).toISOString(),
316
+ );
317
+ }
318
+
319
+ // If no new messages returned but total count says there should be more
320
+ if (newMessages.length === 0 && prev.context.totalCount > prev.context.threadMessages.length) {
321
+ console.log('No new messages found despite totalCount indicating more should exist');
322
+
323
+ // Adjust totalCount to match reality
324
+ return {
325
+ ...prev,
326
+ context: {
327
+ ...prev.context,
328
+ loadingOldMessages: false,
329
+ totalCount: prev.context.threadMessages.length,
330
+ },
331
+ value: 'active',
332
+ };
333
+ }
334
+
335
+ // Make sure we don't add duplicate messages
336
+ const combinedMessages = uniqBy([...prev.context.threadMessages, ...newMessages], 'id');
337
+
338
+ // GiftedChat expects messages sorted by date (newest first)
339
+ const sortedMessages = orderBy(
340
+ combinedMessages,
341
+ [(msg) => new Date(msg.createdAt).getTime()],
342
+ ['desc'],
343
+ );
344
+
345
+ // Use the total count from the API response if available, otherwise keep the current count
346
+ const newTotalCount =
347
+ typeof apiTotalCount === 'number'
348
+ ? apiTotalCount
349
+ : Math.max(sortedMessages.length, prev.context.totalCount);
350
+
351
+ console.log(
352
+ `Total messages after merge and sort: ${sortedMessages.length}, totalCount: ${newTotalCount}`,
353
+ );
354
+
290
355
  return {
291
356
  ...prev,
292
357
  context: {
293
358
  ...prev.context,
294
359
  loadingOldMessages: false,
295
- threadMessages: uniqBy([...prev.context.threadMessages, ...newMessages], ({ id }) => id),
360
+ threadMessages: sortedMessages,
361
+ totalCount: newTotalCount,
296
362
  },
297
363
  value: 'active',
298
364
  };
@@ -432,6 +498,64 @@ const ThreadConversationViewComponent = ({ channelId, postParentId, isPostParent
432
498
  { data, loading: threadLoading, fetchMore: fetchMoreMessages, refetch: refetchThreadMessages, subscribeToMore },
433
499
  ] = useGetPostThreadLazyQuery({ fetchPolicy: 'cache-and-network' });
434
500
 
501
+ // Add a function to force refresh all messages
502
+ const forceRefreshMessages = useCallback(() => {
503
+ console.log('Force refreshing all messages');
504
+
505
+ // Clear the current messages first
506
+ safeSend({
507
+ type: ThreadActions.CLEAR_MESSAGES,
508
+ });
509
+
510
+ // Then trigger a refetch
511
+ if (channelId && parentId) {
512
+ safeSend({
513
+ type: ThreadActions.START_LOADING,
514
+ data: { loading: true },
515
+ });
516
+
517
+ getThreadMessages({
518
+ variables: {
519
+ channelId: channelId?.toString(),
520
+ role: role?.toString(),
521
+ postParentId: !parentId || parentId == 0 ? null : parentId?.toString(),
522
+ selectedFields: 'id channel post replies replyCount lastReplyAt createdAt updatedAt',
523
+ limit: 50, // Use larger limit that proved to work
524
+ },
525
+ })
526
+ .then(({ data }) => {
527
+ if (data?.getPostThread) {
528
+ const threads: any = data.getPostThread;
529
+ const threadPost = threads?.post ?? [];
530
+ const threadReplies = threads?.replies ?? [];
531
+ const messageTotalCount = threads?.replyCount ?? 0;
532
+ const messages = [...threadReplies];
533
+
534
+ console.log(
535
+ `Force refresh complete. Got ${messages.length} messages of ${messageTotalCount} total`,
536
+ );
537
+
538
+ safeSend({
539
+ type: ThreadActions.SET_THREAD_MESSAGES,
540
+ data: {
541
+ messages,
542
+ totalCount: messageTotalCount,
543
+ threadPost: threadPost,
544
+ postThread: threads,
545
+ },
546
+ });
547
+ }
548
+ })
549
+ .catch((error) => {
550
+ console.error('Error during force refresh:', error);
551
+ safeSend({
552
+ type: 'ERROR',
553
+ data: { message: error.message },
554
+ });
555
+ });
556
+ }
557
+ }, [channelId, parentId, getThreadMessages, safeSend]);
558
+
435
559
  useFocusEffect(
436
560
  React.useCallback(() => {
437
561
  // navigation?.setOptions({ title: params?.title ?? 'Thread' });
@@ -443,6 +567,11 @@ const ThreadConversationViewComponent = ({ channelId, postParentId, isPostParent
443
567
  <MaterialIcons size={20} name="arrow-back-ios" color={'black'} />
444
568
  </Button>
445
569
  ),
570
+ headerRight: () => (
571
+ <Button className="bg-transparent active:bg-gray-200 mr-2" onPress={forceRefreshMessages}>
572
+ <MaterialIcons size={20} name="refresh" color={'black'} />
573
+ </Button>
574
+ ),
446
575
  });
447
576
 
448
577
  // Set initial context when focused
@@ -462,7 +591,7 @@ const ThreadConversationViewComponent = ({ channelId, postParentId, isPostParent
462
591
  return () => {
463
592
  safeSend({ type: ThreadActions.CLEAR_MESSAGES });
464
593
  };
465
- }, [postParentId]),
594
+ }, [postParentId, forceRefreshMessages]),
466
595
  );
467
596
 
468
597
  // Effect for when in FetchThreadMessages state
@@ -500,13 +629,14 @@ const ThreadConversationViewComponent = ({ channelId, postParentId, isPostParent
500
629
  // Fetch thread messages function
501
630
  const fetchThreadMessages = useCallback(() => {
502
631
  if (channelId && parentId) {
632
+ console.log('Initial fetch of thread messages using larger limit (50)');
503
633
  getThreadMessages({
504
634
  variables: {
505
635
  channelId: channelId?.toString(),
506
636
  role: role?.toString(),
507
637
  postParentId: !parentId || parentId == 0 ? null : parentId?.toString(),
508
638
  selectedFields: 'id channel post replies replyCount lastReplyAt createdAt updatedAt',
509
- limit: MESSAGES_PER_PAGE,
639
+ limit: 50, // Use larger limit that proved to work
510
640
  },
511
641
  })
512
642
  .then(({ data }) => {
@@ -517,6 +647,10 @@ const ThreadConversationViewComponent = ({ channelId, postParentId, isPostParent
517
647
  const messageTotalCount = threads?.replyCount ?? 0;
518
648
  const messages = [...threadReplies];
519
649
 
650
+ console.log(
651
+ `Initial fetch complete. Got ${messages.length} messages of ${messageTotalCount} total`,
652
+ );
653
+
520
654
  safeSend({
521
655
  type: ThreadActions.SET_THREAD_MESSAGES,
522
656
  data: {
@@ -563,6 +697,74 @@ const ThreadConversationViewComponent = ({ channelId, postParentId, isPostParent
563
697
  }
564
698
  }, [safeContextProperty('selectedImage')]);
565
699
 
700
+ // Add a safety timeout to clear loading state if it gets stuck
701
+ React.useEffect(() => {
702
+ const isLoading = safeContextProperty('loadingOldMessages', false);
703
+
704
+ if (isLoading) {
705
+ console.log('Message loading timeout safety started');
706
+ const timeoutId = setTimeout(() => {
707
+ // Check if we're still loading after 10 seconds
708
+ if (safeContextProperty('loadingOldMessages', false)) {
709
+ console.log('Message loading timed out - resetting state');
710
+ safeSend({
711
+ type: ThreadActions.STOP_LOADING,
712
+ data: { loadingOldMessages: false },
713
+ });
714
+ }
715
+ }, 10000); // 10 second timeout
716
+
717
+ return () => clearTimeout(timeoutId);
718
+ }
719
+ }, [safeContextProperty('loadingOldMessages')]);
720
+
721
+ // Add a safety counter to detect and fix incorrect message counts automatically
722
+ const failedLoadAttemptsRef = useRef(0);
723
+
724
+ // Track failed attempts to load more messages
725
+ const registerLoadAttemptResult = useCallback((success: boolean) => {
726
+ if (!success) {
727
+ failedLoadAttemptsRef.current += 1;
728
+ console.log(`Failed load attempt registered, count: ${failedLoadAttemptsRef.current}`);
729
+ } else {
730
+ // Reset counter on successful load
731
+ failedLoadAttemptsRef.current = 0;
732
+ }
733
+ }, []);
734
+
735
+ // Watch for failed load attempts and auto-fix the count after 3 consecutive failures
736
+ React.useEffect(() => {
737
+ const isLoading = safeContextProperty('loadingOldMessages', false);
738
+ const totalCount = safeContextProperty('totalCount', 0);
739
+ const messagesCount = safeContextProperty('threadMessages', []).length;
740
+
741
+ // If we're not loading and there's a discrepancy in message counts
742
+ if (!isLoading && totalCount > messagesCount) {
743
+ // If we've had 3 consecutive failed attempts, fix the count
744
+ if (failedLoadAttemptsRef.current >= 2) {
745
+ console.log(
746
+ `Auto-fixing incorrect message count after ${
747
+ failedLoadAttemptsRef.current + 1
748
+ } failed load attempts`,
749
+ );
750
+ console.log(`Adjusting totalCount from ${totalCount} to ${messagesCount}`);
751
+
752
+ safeSend({
753
+ type: ThreadActions.SET_THREAD_MESSAGES,
754
+ data: {
755
+ messages: safeContextProperty('threadMessages', []),
756
+ totalCount: messagesCount,
757
+ threadPost: safeContextProperty('threadPost', []),
758
+ postThread: safeContextProperty('postThread', null),
759
+ },
760
+ });
761
+
762
+ // Reset the counter
763
+ failedLoadAttemptsRef.current = 0;
764
+ }
765
+ }
766
+ }, [safeContextProperty('loadingOldMessages'), safeContextProperty('threadMessages')]);
767
+
566
768
  const scrollToBottom = React.useCallback(() => {
567
769
  if (threadMessageListRef?.current) {
568
770
  threadMessageListRef.current.scrollToBottom();
@@ -578,36 +780,111 @@ const ThreadConversationViewComponent = ({ channelId, postParentId, isPostParent
578
780
 
579
781
  // Set the loading state specifically for old messages
580
782
  safeSend({
581
- type: ThreadActions.START_LOADING,
783
+ type: ThreadActions.FETCH_MORE_MESSAGES,
582
784
  data: { loadingOldMessages: true },
583
785
  });
584
786
 
787
+ // Since Skip=0, Limit=50 worked well, let's use that approach
788
+ console.log('Using proven approach: Skip=0, Limit=50');
789
+
790
+ // Try with a larger limit and skip=0, which we know works
791
+ const queryVariables = {
792
+ channelId: channelId?.toString(),
793
+ role: role?.toString(),
794
+ postParentId: !parentId || parentId == 0 ? null : parentId?.toString(),
795
+ selectedFields: 'id channel post replies replyCount lastReplyAt createdAt updatedAt',
796
+ limit: 50, // Use larger limit that proved to work
797
+ skip: 0, // Start from the beginning
798
+ };
799
+
800
+ console.log('Query variables:', JSON.stringify(queryVariables));
801
+
585
802
  fetchMoreMessages({
586
- variables: {
587
- channelId: channelId?.toString(),
588
- role: role?.toString(),
589
- postParentId: parentId?.toString(),
590
- selectedFields: 'id channel post replies replyCount lastReplyAt createdAt updatedAt',
591
- limit: MESSAGES_PER_PAGE,
592
- skip: threadMessages.length,
593
- },
803
+ variables: queryVariables,
594
804
  })
595
805
  .then((res: any) => {
806
+ console.log('API response received:', JSON.stringify(res?.data, null, 2));
807
+
596
808
  if (res?.data?.getPostThread) {
597
809
  const threads: any = res?.data?.getPostThread;
598
810
  const threadReplies = threads?.replies ?? [];
811
+ // Get the actual total count from the response
812
+ const actualTotalCount = threads?.replyCount ?? 0;
813
+
814
+ console.log('API response details:');
815
+ console.log('- replyCount:', threads?.replyCount);
816
+ console.log('- replies array length:', threadReplies.length);
817
+ console.log(
818
+ '- replies structure:',
819
+ threadReplies.length > 0
820
+ ? `First item has fields: ${Object.keys(threadReplies[0]).join(', ')}`
821
+ : 'No items',
822
+ );
823
+
824
+ if (threadReplies.length > 0) {
825
+ console.log('- Sample message:', JSON.stringify(threadReplies[0], null, 2));
826
+ }
827
+
828
+ console.log(
829
+ 'Successfully loaded more messages:',
830
+ threadReplies.length,
831
+ 'of total:',
832
+ actualTotalCount,
833
+ );
834
+ console.log('Thread reply IDs:', threadReplies.map((msg) => msg.id).join(', '));
835
+
836
+ // Compare with our existing messages to find the ones we're missing
837
+ const existingIds = new Set(threadMessages.map((msg) => msg.id));
838
+ const newUniqueMessages = threadReplies.filter((msg) => !existingIds.has(msg.id));
839
+
840
+ console.log(
841
+ `Found ${newUniqueMessages.length} unique new messages out of ${threadReplies.length} received`,
842
+ );
843
+
844
+ // If we've received all messages but our count doesn't match, update the total count
845
+ if (actualTotalCount !== totalCount) {
846
+ console.log(
847
+ `Updating totalCount from ${totalCount} to ${actualTotalCount} based on API response`,
848
+ );
849
+ // This will be applied below when we process the messages
850
+ }
851
+
852
+ // If no new unique messages, it means we already have everything
853
+ if (newUniqueMessages.length === 0) {
854
+ console.log('No new unique messages found, adjusting total count');
855
+
856
+ // Register this as a failed load attempt
857
+ registerLoadAttemptResult(false);
858
+
859
+ // Adjust total count to match what we have
860
+ safeSend({
861
+ type: ThreadActions.SET_THREAD_MESSAGES,
862
+ data: {
863
+ messages: threadMessages,
864
+ totalCount: threadMessages.length,
865
+ threadPost: safeContextProperty('threadPost', []),
866
+ postThread: safeContextProperty('postThread', null),
867
+ },
868
+ });
869
+ return;
870
+ }
871
+
872
+ // Register success since we found new messages
873
+ registerLoadAttemptResult(true);
599
874
 
600
- console.log('Successfully loaded more messages:', threadReplies.length);
875
+ console.log(`Adding ${newUniqueMessages.length} new messages to thread`);
601
876
 
602
877
  safeSend({
603
878
  type: 'FETCH_MORE_MESSAGES_SUCCESS',
604
879
  data: {
605
- messages: threadReplies,
880
+ messages: newUniqueMessages,
881
+ totalCount: actualTotalCount,
606
882
  loadingOldMessages: false,
607
883
  },
608
884
  });
609
885
  } else {
610
886
  console.log('No thread data returned when loading more messages');
887
+ registerLoadAttemptResult(false);
611
888
  safeSend({
612
889
  type: ThreadActions.STOP_LOADING,
613
890
  data: { loadingOldMessages: false },
@@ -616,6 +893,7 @@ const ThreadConversationViewComponent = ({ channelId, postParentId, isPostParent
616
893
  })
617
894
  .catch((error: any) => {
618
895
  console.error('Error fetching more messages:', error);
896
+ registerLoadAttemptResult(false);
619
897
  safeSend({
620
898
  type: 'ERROR',
621
899
  data: {
@@ -626,18 +904,31 @@ const ThreadConversationViewComponent = ({ channelId, postParentId, isPostParent
626
904
  });
627
905
  } else {
628
906
  console.log('No more messages to load or already loading');
907
+ // Make sure we're not stuck in loading state
908
+ if (safeContextProperty('loadingOldMessages', false)) {
909
+ safeSend({
910
+ type: ThreadActions.STOP_LOADING,
911
+ data: { loadingOldMessages: false },
912
+ });
913
+ }
629
914
  }
630
- }, [parentId, channelId, state.context]);
915
+ }, [parentId, channelId, state.context, registerLoadAttemptResult]);
631
916
 
632
917
  let onScroll = false;
633
918
 
634
919
  const handleScrollToTop = ({ nativeEvent }: any) => {
635
920
  // Check if we're near the top of the list
636
921
  if (isCloseToTop(nativeEvent)) {
637
- if (
638
- !safeContextProperty('loadingOldMessages', false) &&
639
- safeContextProperty('totalCount', 0) > safeContextProperty('threadMessages', []).length
640
- ) {
922
+ const isLoading = safeContextProperty('loadingOldMessages', false);
923
+ const totalCount = safeContextProperty('totalCount', 0);
924
+ const currentCount = safeContextProperty('threadMessages', []).length;
925
+ const hasMoreMessages = totalCount > currentCount;
926
+
927
+ console.log(
928
+ `Scroll near top - Loading state: ${isLoading}, Messages: ${currentCount}/${totalCount}, Has more: ${hasMoreMessages}`,
929
+ );
930
+
931
+ if (!isLoading && hasMoreMessages) {
641
932
  console.log('Near top of list - loading older messages');
642
933
  safeSend({ type: ThreadActions.FETCH_MORE_MESSAGES });
643
934
  }
@@ -650,8 +941,21 @@ const ThreadConversationViewComponent = ({ channelId, postParentId, isPostParent
650
941
  };
651
942
 
652
943
  const isCloseToTop = ({ layoutMeasurement, contentOffset, contentSize }) => {
653
- const paddingToTop = 80;
654
- return contentOffset.y <= paddingToTop;
944
+ // We consider being "close to top" when scrolled to top 15% of visible area
945
+ const visibleHeight = layoutMeasurement.height;
946
+ const topThreshold = Math.min(80, visibleHeight * 0.15);
947
+
948
+ // For debugging
949
+ const distanceFromTop = contentOffset.y;
950
+ const totalContentHeight = contentSize.height;
951
+
952
+ console.log(
953
+ `Scroll position: ${distanceFromTop.toFixed(0)}px from top, threshold: ${topThreshold.toFixed(
954
+ 0,
955
+ )}px, content height: ${totalContentHeight.toFixed(0)}px`,
956
+ );
957
+
958
+ return contentOffset.y <= topThreshold;
655
959
  };
656
960
 
657
961
  const onSelectImages = async () => {
@@ -994,7 +1298,18 @@ const ThreadConversationViewComponent = ({ channelId, postParentId, isPostParent
994
1298
  }
995
1299
 
996
1300
  // Sort messages by date (newest first as required by GiftedChat)
997
- const sortedMessages = res.sort((a, b) => b.createdAt - a.createdAt);
1301
+ const sortedMessages = orderBy(res, [(msg) => new Date(msg.createdAt).getTime()], ['desc']);
1302
+
1303
+ // Log message dates to help with debugging
1304
+ if (sortedMessages.length > 0) {
1305
+ console.log(
1306
+ 'Message date range:',
1307
+ new Date(sortedMessages[sortedMessages.length - 1].createdAt).toISOString(),
1308
+ 'to',
1309
+ new Date(sortedMessages[0].createdAt).toISOString(),
1310
+ );
1311
+ }
1312
+
998
1313
  return sortedMessages;
999
1314
  }, [safeContextProperty('threadMessages'), auth]);
1000
1315
 
@@ -1243,9 +1558,65 @@ const ThreadConversationViewComponent = ({ channelId, postParentId, isPostParent
1243
1558
  );
1244
1559
  };
1245
1560
 
1561
+ // Add a function to load messages with specific skip value for debugging
1562
+ const forceLoadMessages = useCallback(
1563
+ (skipValue: number) => {
1564
+ console.log(`Force loading messages with explicit skip=${skipValue}, limit=50`);
1565
+
1566
+ if (channelId && parentId) {
1567
+ safeSend({
1568
+ type: ThreadActions.START_LOADING,
1569
+ data: { loading: true },
1570
+ });
1571
+
1572
+ getThreadMessages({
1573
+ variables: {
1574
+ channelId: channelId?.toString(),
1575
+ role: role?.toString(),
1576
+ postParentId: !parentId || parentId == 0 ? null : parentId?.toString(),
1577
+ selectedFields: 'id channel post replies replyCount lastReplyAt createdAt updatedAt',
1578
+ skip: skipValue,
1579
+ limit: 50, // Use larger limit that proved to work
1580
+ },
1581
+ })
1582
+ .then(({ data }) => {
1583
+ if (data?.getPostThread) {
1584
+ const threads: any = data.getPostThread;
1585
+ const threadPost = threads?.post ?? [];
1586
+ const threadReplies = threads?.replies ?? [];
1587
+ const messageTotalCount = threads?.replyCount ?? 0;
1588
+ const messages = [...threadReplies];
1589
+
1590
+ console.log(
1591
+ `Force load with skip=${skipValue} complete. Got ${messages.length} messages of ${messageTotalCount} total`,
1592
+ );
1593
+
1594
+ safeSend({
1595
+ type: ThreadActions.SET_THREAD_MESSAGES,
1596
+ data: {
1597
+ messages,
1598
+ totalCount: messageTotalCount,
1599
+ threadPost: threadPost,
1600
+ postThread: threads,
1601
+ },
1602
+ });
1603
+ }
1604
+ })
1605
+ .catch((error) => {
1606
+ console.error('Error during force load:', error);
1607
+ safeSend({
1608
+ type: 'ERROR',
1609
+ data: { message: error.message },
1610
+ });
1611
+ });
1612
+ }
1613
+ },
1614
+ [channelId, parentId, getThreadMessages, safeSend],
1615
+ );
1616
+
1246
1617
  return (
1247
1618
  <SafeAreaView style={{ flex: 1 }}>
1248
- {safeContextProperty('loadingOldMessages', false) && (
1619
+ {safeContextProperty('loadingOldMessages', false) === true && (
1249
1620
  <Box className="absolute top-10 left-0 right-0 z-10 items-center">
1250
1621
  <Box className="bg-blue-500/20 rounded-full px-4 py-2 flex-row items-center">
1251
1622
  <Spinner color={colors.blue[500]} size="small" />
@@ -1253,6 +1624,294 @@ const ThreadConversationViewComponent = ({ channelId, postParentId, isPostParent
1253
1624
  </Box>
1254
1625
  </Box>
1255
1626
  )}
1627
+ {!safeContextProperty('loadingOldMessages', false) &&
1628
+ safeContextProperty('totalCount', 0) > safeContextProperty('threadMessages', []).length && (
1629
+ <Box className="absolute top-10 left-0 right-0 z-10 items-center">
1630
+ <HStack space={2} className="px-2">
1631
+ <TouchableHighlight
1632
+ onPress={() => {
1633
+ console.log('Manual load more pressed');
1634
+ Alert.alert('Load Options', 'Choose loading method', [
1635
+ {
1636
+ text: 'Normal Load',
1637
+ onPress: () => safeSend({ type: ThreadActions.FETCH_MORE_MESSAGES }),
1638
+ },
1639
+ {
1640
+ text: 'Try Skip=0',
1641
+ onPress: () => forceLoadMessages(0),
1642
+ },
1643
+ {
1644
+ text: 'Try Skip=0, Limit=50',
1645
+ onPress: () => {
1646
+ // Try with a larger limit
1647
+ console.log('Force loading with explicit skip=0, limit=50');
1648
+
1649
+ safeSend({
1650
+ type: ThreadActions.START_LOADING,
1651
+ data: { loadingOldMessages: true },
1652
+ });
1653
+
1654
+ fetchMoreMessages({
1655
+ variables: {
1656
+ channelId: channelId?.toString(),
1657
+ role: role?.toString(),
1658
+ postParentId:
1659
+ !parentId || parentId == 0 ? null : parentId?.toString(),
1660
+ selectedFields:
1661
+ 'id channel post replies replyCount lastReplyAt createdAt updatedAt',
1662
+ limit: 50, // Try a larger limit
1663
+ skip: 0,
1664
+ },
1665
+ })
1666
+ .then((res: any) => {
1667
+ console.log(
1668
+ 'LARGE LIMIT response:',
1669
+ JSON.stringify(res?.data, null, 2),
1670
+ );
1671
+
1672
+ if (res?.data?.getPostThread) {
1673
+ const threads: any = res?.data?.getPostThread;
1674
+ const threadReplies = threads?.replies ?? [];
1675
+ const actualTotalCount = threads?.replyCount ?? 0;
1676
+
1677
+ console.log(
1678
+ `Large limit load complete. Got ${threadReplies.length} messages of ${actualTotalCount} total`,
1679
+ );
1680
+
1681
+ if (threadReplies.length > 0) {
1682
+ // Reset our message list with all available messages
1683
+ safeSend({
1684
+ type: ThreadActions.SET_THREAD_MESSAGES,
1685
+ data: {
1686
+ messages: threadReplies,
1687
+ totalCount: actualTotalCount,
1688
+ threadPost: safeContextProperty(
1689
+ 'threadPost',
1690
+ [],
1691
+ ),
1692
+ postThread: threads,
1693
+ },
1694
+ });
1695
+ } else {
1696
+ // Reset count if no messages found
1697
+ safeSend({
1698
+ type: ThreadActions.SET_THREAD_MESSAGES,
1699
+ data: {
1700
+ messages: safeContextProperty(
1701
+ 'threadMessages',
1702
+ [],
1703
+ ),
1704
+ totalCount: safeContextProperty(
1705
+ 'threadMessages',
1706
+ [],
1707
+ ).length,
1708
+ threadPost: safeContextProperty(
1709
+ 'threadPost',
1710
+ [],
1711
+ ),
1712
+ postThread: safeContextProperty(
1713
+ 'postThread',
1714
+ null,
1715
+ ),
1716
+ },
1717
+ });
1718
+ }
1719
+ } else {
1720
+ safeSend({
1721
+ type: ThreadActions.STOP_LOADING,
1722
+ data: { loadingOldMessages: false },
1723
+ });
1724
+ }
1725
+ })
1726
+ .catch((error) => {
1727
+ console.error('Error in large limit load:', error);
1728
+ safeSend({
1729
+ type: ThreadActions.STOP_LOADING,
1730
+ data: { loadingOldMessages: false },
1731
+ });
1732
+ });
1733
+ },
1734
+ },
1735
+ {
1736
+ text: 'Try Direct Fetch',
1737
+ onPress: () => {
1738
+ // Get current info
1739
+ const currentCount = safeContextProperty('threadMessages', []).length;
1740
+ const totalCount = safeContextProperty('totalCount', 0);
1741
+ const missingCount = totalCount - currentCount;
1742
+
1743
+ if (missingCount <= 0) {
1744
+ Alert.alert('Info', 'No missing messages to fetch');
1745
+ return;
1746
+ }
1747
+
1748
+ console.log(
1749
+ `Attempting direct fetch of missing ${missingCount} messages`,
1750
+ );
1751
+
1752
+ // Try explicit query with exact parameters
1753
+ safeSend({
1754
+ type: ThreadActions.START_LOADING,
1755
+ data: { loadingOldMessages: true },
1756
+ });
1757
+
1758
+ // Special query directly for the missing messages
1759
+ getThreadMessages({
1760
+ variables: {
1761
+ channelId: channelId?.toString(),
1762
+ role: role?.toString(),
1763
+ postParentId:
1764
+ !parentId || parentId == 0 ? null : parentId?.toString(),
1765
+ selectedFields:
1766
+ 'id channel post replies replyCount lastReplyAt createdAt updatedAt',
1767
+ limit: missingCount, // Only get the exact number we need
1768
+ skip: 0, // Start from the beginning
1769
+ },
1770
+ })
1771
+ .then(({ data }) => {
1772
+ console.log(
1773
+ 'DIRECT FETCH response:',
1774
+ JSON.stringify(data, null, 2),
1775
+ );
1776
+
1777
+ if (data?.getPostThread) {
1778
+ const threads: any = data.getPostThread;
1779
+ const threadReplies = threads?.replies ?? [];
1780
+ const actualTotalCount = threads?.replyCount ?? 0;
1781
+
1782
+ console.log(
1783
+ `Direct fetch complete. Got ${threadReplies.length} messages of ${actualTotalCount} total`,
1784
+ );
1785
+ console.log(
1786
+ 'Message IDs:',
1787
+ threadReplies.map((msg) => msg.id).join(', '),
1788
+ );
1789
+
1790
+ // Compare with our existing messages to find the ones we're missing
1791
+ const existingIds = new Set(
1792
+ safeContextProperty('threadMessages', []).map(
1793
+ (msg) => msg.id,
1794
+ ),
1795
+ );
1796
+ const newMessages = threadReplies.filter(
1797
+ (msg) => !existingIds.has(msg.id),
1798
+ );
1799
+
1800
+ console.log(
1801
+ `Found ${newMessages.length} new messages that we didn't have before`,
1802
+ );
1803
+
1804
+ if (newMessages.length > 0) {
1805
+ safeSend({
1806
+ type: 'FETCH_MORE_MESSAGES_SUCCESS',
1807
+ data: {
1808
+ messages: newMessages,
1809
+ totalCount: actualTotalCount,
1810
+ loadingOldMessages: false,
1811
+ },
1812
+ });
1813
+
1814
+ // Show the results
1815
+ Alert.alert(
1816
+ 'Success',
1817
+ `Found ${newMessages.length} new messages`,
1818
+ );
1819
+ } else {
1820
+ // If we didn't find any new messages, adjust the total count
1821
+ console.log('No new messages found, adjusting count');
1822
+ safeSend({
1823
+ type: ThreadActions.SET_THREAD_MESSAGES,
1824
+ data: {
1825
+ messages: safeContextProperty(
1826
+ 'threadMessages',
1827
+ [],
1828
+ ),
1829
+ totalCount: safeContextProperty(
1830
+ 'threadMessages',
1831
+ [],
1832
+ ).length,
1833
+ threadPost: safeContextProperty(
1834
+ 'threadPost',
1835
+ [],
1836
+ ),
1837
+ postThread: safeContextProperty(
1838
+ 'postThread',
1839
+ null,
1840
+ ),
1841
+ },
1842
+ });
1843
+
1844
+ // Notify the user
1845
+ Alert.alert(
1846
+ 'Info',
1847
+ 'No new messages found. Count has been adjusted.',
1848
+ );
1849
+ }
1850
+ } else {
1851
+ Alert.alert('Error', 'Could not fetch thread messages');
1852
+ safeSend({
1853
+ type: ThreadActions.STOP_LOADING,
1854
+ data: { loadingOldMessages: false },
1855
+ });
1856
+ }
1857
+ })
1858
+ .catch((error) => {
1859
+ console.error('Error in direct fetch:', error);
1860
+ Alert.alert(
1861
+ 'Error',
1862
+ 'Failed to fetch messages: ' + error.message,
1863
+ );
1864
+ safeSend({
1865
+ type: ThreadActions.STOP_LOADING,
1866
+ data: { loadingOldMessages: false },
1867
+ });
1868
+ });
1869
+ },
1870
+ },
1871
+ {
1872
+ text: 'Reset Count',
1873
+ style: 'destructive',
1874
+ onPress: () => {
1875
+ console.log('Resetting message count to match reality');
1876
+ safeSend({
1877
+ type: ThreadActions.SET_THREAD_MESSAGES,
1878
+ data: {
1879
+ messages: safeContextProperty('threadMessages', []),
1880
+ totalCount: safeContextProperty('threadMessages', []).length,
1881
+ threadPost: safeContextProperty('threadPost', []),
1882
+ postThread: safeContextProperty('postThread', null),
1883
+ },
1884
+ });
1885
+ },
1886
+ },
1887
+ {
1888
+ text: 'Cancel',
1889
+ style: 'cancel',
1890
+ },
1891
+ ]);
1892
+ }}
1893
+ underlayColor="#e6e6e6"
1894
+ >
1895
+ <Box className="bg-gray-200 rounded-full px-4 py-2 flex-row items-center">
1896
+ <MaterialIcons name="arrow-upward" size={16} color={colors.blue[500]} />
1897
+ <Text className="text-sm font-medium color-blue-600 ml-2">
1898
+ Load More (
1899
+ {safeContextProperty('totalCount', 0) -
1900
+ safeContextProperty('threadMessages', []).length}
1901
+ )
1902
+ </Text>
1903
+ </Box>
1904
+ </TouchableHighlight>
1905
+
1906
+ <TouchableHighlight onPress={forceRefreshMessages} underlayColor="#e6e6e6">
1907
+ <Box className="bg-gray-200 rounded-full px-4 py-2 flex-row items-center">
1908
+ <MaterialIcons name="refresh" size={16} color={colors.blue[500]} />
1909
+ <Text className="text-sm font-medium color-blue-600 ml-2">Refresh All</Text>
1910
+ </Box>
1911
+ </TouchableHighlight>
1912
+ </HStack>
1913
+ </Box>
1914
+ )}
1256
1915
  {isPostParentIdThread && (
1257
1916
  <>
1258
1917
  {safeContextProperty('threadPost', [])?.length > 0 && (
@@ -1326,7 +1985,7 @@ const ThreadConversationViewComponent = ({ channelId, postParentId, isPostParent
1326
1985
  minIndexForVisible: 0,
1327
1986
  autoscrollToTopThreshold: 100,
1328
1987
  },
1329
- scrollEventThrottle: 100,
1988
+ scrollEventThrottle: 16,
1330
1989
  keyboardDismissMode: 'on-drag',
1331
1990
  keyboardShouldPersistTaps: 'handled',
1332
1991
  }}