@droppii-org/chat-mobile 0.2.108 → 0.2.110

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 (114) hide show
  1. package/lib/module/screens/ChatInfo/ChatInfo.js +12 -3
  2. package/lib/module/screens/ChatInfo/ChatInfo.js.map +1 -1
  3. package/lib/module/screens/ChatInfo/ChatInfoActions.js +45 -40
  4. package/lib/module/screens/ChatInfo/ChatInfoActions.js.map +1 -1
  5. package/lib/module/screens/ChatInfo/ChatInfoProfile.js +11 -7
  6. package/lib/module/screens/ChatInfo/ChatInfoProfile.js.map +1 -1
  7. package/lib/module/screens/chat-detail/ChatDetail.js +42 -7
  8. package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
  9. package/lib/module/screens/chat-detail/ChatListLegend.js +249 -49
  10. package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
  11. package/lib/module/screens/chat-detail/hooks/createUnreadMarkReadGate.js +32 -0
  12. package/lib/module/screens/chat-detail/hooks/createUnreadMarkReadGate.js.map +1 -0
  13. package/lib/module/screens/chat-detail/hooks/useMarkConversationRead.js +180 -0
  14. package/lib/module/screens/chat-detail/hooks/useMarkConversationRead.js.map +1 -0
  15. package/lib/module/screens/chat-detail/hooks/useUnreadAnchorSeen.js +88 -0
  16. package/lib/module/screens/chat-detail/hooks/useUnreadAnchorSeen.js.map +1 -0
  17. package/lib/module/screens/chat-detail/hooks/useUnreadDeeplinkCheck.js +59 -0
  18. package/lib/module/screens/chat-detail/hooks/useUnreadDeeplinkCheck.js.map +1 -0
  19. package/lib/module/screens/chat-detail/hooks/useUnreadIndicator.js +105 -0
  20. package/lib/module/screens/chat-detail/hooks/useUnreadIndicator.js.map +1 -0
  21. package/lib/module/screens/chat-detail/hooks/useUnreadIndicatorInList.js +157 -0
  22. package/lib/module/screens/chat-detail/hooks/useUnreadIndicatorInList.js.map +1 -0
  23. package/lib/module/screens/chat-detail/legend/LegendChatUnreadButton.js +140 -0
  24. package/lib/module/screens/chat-detail/legend/LegendChatUnreadButton.js.map +1 -0
  25. package/lib/module/screens/chat-detail/legend/LegendChatUnreadDivider.js +51 -0
  26. package/lib/module/screens/chat-detail/legend/LegendChatUnreadDivider.js.map +1 -0
  27. package/lib/module/services/message.js +30 -0
  28. package/lib/module/services/message.js.map +1 -1
  29. package/lib/module/store/message.js +25 -4
  30. package/lib/module/store/message.js.map +1 -1
  31. package/lib/module/store/unreadIndicator.js +26 -0
  32. package/lib/module/store/unreadIndicator.js.map +1 -0
  33. package/lib/module/translation/resources/i18n.js +2 -0
  34. package/lib/module/translation/resources/i18n.js.map +1 -1
  35. package/lib/module/types/chat.js.map +1 -1
  36. package/lib/module/types/unreadIndicator.js +2 -0
  37. package/lib/module/types/unreadIndicator.js.map +1 -0
  38. package/lib/module/utils/markReadPolicy.js +25 -0
  39. package/lib/module/utils/markReadPolicy.js.map +1 -0
  40. package/lib/module/utils/promise.js +16 -0
  41. package/lib/module/utils/promise.js.map +1 -1
  42. package/lib/module/utils/unreadAnchor.js +57 -0
  43. package/lib/module/utils/unreadAnchor.js.map +1 -0
  44. package/lib/module/utils/unreadIndicatorState.js +96 -0
  45. package/lib/module/utils/unreadIndicatorState.js.map +1 -0
  46. package/lib/typescript/src/screens/ChatInfo/ChatInfo.d.ts.map +1 -1
  47. package/lib/typescript/src/screens/ChatInfo/ChatInfoActions.d.ts +2 -1
  48. package/lib/typescript/src/screens/ChatInfo/ChatInfoActions.d.ts.map +1 -1
  49. package/lib/typescript/src/screens/ChatInfo/ChatInfoProfile.d.ts +2 -1
  50. package/lib/typescript/src/screens/ChatInfo/ChatInfoProfile.d.ts.map +1 -1
  51. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
  52. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts +4 -1
  53. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -1
  54. package/lib/typescript/src/screens/chat-detail/hooks/createUnreadMarkReadGate.d.ts +9 -0
  55. package/lib/typescript/src/screens/chat-detail/hooks/createUnreadMarkReadGate.d.ts.map +1 -0
  56. package/lib/typescript/src/screens/chat-detail/hooks/useMarkConversationRead.d.ts +39 -0
  57. package/lib/typescript/src/screens/chat-detail/hooks/useMarkConversationRead.d.ts.map +1 -0
  58. package/lib/typescript/src/screens/chat-detail/hooks/useUnreadAnchorSeen.d.ts +26 -0
  59. package/lib/typescript/src/screens/chat-detail/hooks/useUnreadAnchorSeen.d.ts.map +1 -0
  60. package/lib/typescript/src/screens/chat-detail/hooks/useUnreadDeeplinkCheck.d.ts +17 -0
  61. package/lib/typescript/src/screens/chat-detail/hooks/useUnreadDeeplinkCheck.d.ts.map +1 -0
  62. package/lib/typescript/src/screens/chat-detail/hooks/useUnreadIndicator.d.ts +21 -0
  63. package/lib/typescript/src/screens/chat-detail/hooks/useUnreadIndicator.d.ts.map +1 -0
  64. package/lib/typescript/src/screens/chat-detail/hooks/useUnreadIndicatorInList.d.ts +35 -0
  65. package/lib/typescript/src/screens/chat-detail/hooks/useUnreadIndicatorInList.d.ts.map +1 -0
  66. package/lib/typescript/src/screens/chat-detail/legend/LegendChatUnreadButton.d.ts +8 -0
  67. package/lib/typescript/src/screens/chat-detail/legend/LegendChatUnreadButton.d.ts.map +1 -0
  68. package/lib/typescript/src/screens/chat-detail/legend/LegendChatUnreadDivider.d.ts +7 -0
  69. package/lib/typescript/src/screens/chat-detail/legend/LegendChatUnreadDivider.d.ts.map +1 -0
  70. package/lib/typescript/src/services/message.d.ts +8 -0
  71. package/lib/typescript/src/services/message.d.ts.map +1 -1
  72. package/lib/typescript/src/store/message.d.ts.map +1 -1
  73. package/lib/typescript/src/store/unreadIndicator.d.ts +10 -0
  74. package/lib/typescript/src/store/unreadIndicator.d.ts.map +1 -0
  75. package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
  76. package/lib/typescript/src/types/chat.d.ts +3 -0
  77. package/lib/typescript/src/types/chat.d.ts.map +1 -1
  78. package/lib/typescript/src/types/events.d.ts +17 -1
  79. package/lib/typescript/src/types/events.d.ts.map +1 -1
  80. package/lib/typescript/src/types/unreadIndicator.d.ts +45 -0
  81. package/lib/typescript/src/types/unreadIndicator.d.ts.map +1 -0
  82. package/lib/typescript/src/utils/markReadPolicy.d.ts +27 -0
  83. package/lib/typescript/src/utils/markReadPolicy.d.ts.map +1 -0
  84. package/lib/typescript/src/utils/promise.d.ts +6 -0
  85. package/lib/typescript/src/utils/promise.d.ts.map +1 -1
  86. package/lib/typescript/src/utils/unreadAnchor.d.ts +33 -0
  87. package/lib/typescript/src/utils/unreadAnchor.d.ts.map +1 -0
  88. package/lib/typescript/src/utils/unreadIndicatorState.d.ts +26 -0
  89. package/lib/typescript/src/utils/unreadIndicatorState.d.ts.map +1 -0
  90. package/package.json +2 -2
  91. package/src/screens/ChatInfo/ChatInfo.tsx +10 -1
  92. package/src/screens/ChatInfo/ChatInfoActions.tsx +47 -39
  93. package/src/screens/ChatInfo/ChatInfoProfile.tsx +17 -12
  94. package/src/screens/chat-detail/ChatDetail.tsx +58 -5
  95. package/src/screens/chat-detail/ChatListLegend.tsx +314 -46
  96. package/src/screens/chat-detail/hooks/createUnreadMarkReadGate.ts +41 -0
  97. package/src/screens/chat-detail/hooks/useMarkConversationRead.ts +197 -0
  98. package/src/screens/chat-detail/hooks/useUnreadAnchorSeen.ts +105 -0
  99. package/src/screens/chat-detail/hooks/useUnreadDeeplinkCheck.ts +68 -0
  100. package/src/screens/chat-detail/hooks/useUnreadIndicator.ts +112 -0
  101. package/src/screens/chat-detail/hooks/useUnreadIndicatorInList.ts +181 -0
  102. package/src/screens/chat-detail/legend/LegendChatUnreadButton.tsx +165 -0
  103. package/src/screens/chat-detail/legend/LegendChatUnreadDivider.tsx +52 -0
  104. package/src/services/message.ts +42 -0
  105. package/src/store/message.ts +23 -5
  106. package/src/store/unreadIndicator.ts +38 -0
  107. package/src/translation/resources/i18n.ts +2 -0
  108. package/src/types/chat.ts +3 -0
  109. package/src/types/events.ts +18 -1
  110. package/src/types/unreadIndicator.ts +49 -0
  111. package/src/utils/markReadPolicy.ts +55 -0
  112. package/src/utils/promise.ts +20 -0
  113. package/src/utils/unreadAnchor.ts +87 -0
  114. package/src/utils/unreadIndicatorState.ts +131 -0
@@ -0,0 +1 @@
1
+ {"version":3,"names":["FIRST_UNREAD_TIMEOUT_MS","useUnreadIndicatorStore","getUnreadGateDeadline","isUnreadGateClosed","createUnreadMarkReadGate","tokenRef","isClosed","getState","current","Date","now","openDeadline","s","token","pending","subscribe","onOpen","onClear","next","prev","settledBy"],"sourceRoot":"../../../../../src","sources":["screens/chat-detail/hooks/createUnreadMarkReadGate.ts"],"mappings":";;AACA,SAASA,uBAAuB,QAAQ,8BAA2B;AACnE,SAASC,uBAAuB,QAAQ,mCAAgC;AACxE,SACEC,qBAAqB,EACrBC,kBAAkB,QACb,wCAAqC;AAG5C;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,wBAAwB,GACnCC,QAAkC,KAChB;EAClBC,QAAQ,EAAEA,CAAA,KACRH,kBAAkB,CAChBF,uBAAuB,CAACM,QAAQ,CAAC,CAAC,EAClCF,QAAQ,CAACG,OAAO,EAChBC,IAAI,CAACC,GAAG,CAAC,CAAC,EACVV,uBACF,CAAC;EACHW,YAAY,EAAEA,CAAA,KAAM;IAClB,MAAMC,CAAC,GAAGX,uBAAuB,CAACM,QAAQ,CAAC,CAAC;IAC5C,IAAIF,QAAQ,CAACG,OAAO,IAAI,IAAI,IAAII,CAAC,CAACC,KAAK,KAAKR,QAAQ,CAACG,OAAO,EAAE,OAAO,IAAI;IACzE,OAAOI,CAAC,CAACE,OAAO,GAAGZ,qBAAqB,CAACU,CAAC,EAAEZ,uBAAuB,CAAC,GAAG,IAAI;EAC7E,CAAC;EACDe,SAAS,EAAEA,CAAC;IAAEC,MAAM;IAAEC;EAAQ,CAAC,KAC7BhB,uBAAuB,CAACc,SAAS,CAAC,CAACG,IAAI,EAAEC,IAAI,KAAK;IAChD,IAAId,QAAQ,CAACG,OAAO,IAAI,IAAI,IAAIU,IAAI,CAACL,KAAK,KAAKR,QAAQ,CAACG,OAAO,EAAE;IACjE;IACA,IAAIW,IAAI,CAACN,KAAK,KAAKK,IAAI,CAACL,KAAK,EAAE;IAC/B;IACA,IAAIM,IAAI,CAACL,OAAO,IAAI,CAACI,IAAI,CAACJ,OAAO,IAAII,IAAI,CAACE,SAAS,KAAK,SAAS,EAAE;MACjEJ,MAAM,CAAC,CAAC;IACV;IACA,IAAIG,IAAI,CAACC,SAAS,KAAK,OAAO,IAAIF,IAAI,CAACE,SAAS,KAAK,OAAO,EAAEH,OAAO,CAAC,CAAC;EACzE,CAAC;AACL,CAAC,CAAC","ignoreList":[]}
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+
3
+ import { useCallback, useEffect, useRef } from 'react';
4
+ import { AppState } from 'react-native';
5
+ import { useNavigation } from '@react-navigation/native';
6
+ import { ChatMessageAPI } from "../../../services/message.js";
7
+ import { perfMark } from "../../../utils/perf-marker.js";
8
+ import { isListLoadReason, nextMarkReadDelay, shouldRequestMarkRead } from "../../../utils/markReadPolicy.js";
9
+
10
+ /**
11
+ * Something mark-read has to wait for. Optional, so the edge-triggered
12
+ * mark-read works on its own (gate open) without the unread indicator.
13
+ */
14
+
15
+ /**
16
+ * Edge-triggered, throttled mark-read. A trigger marks the conversation dirty
17
+ * and tries to flush; the flush only calls the SDK when `shouldRequestMarkRead`
18
+ * allows it, at most once per `MARK_READ_THROTTLE_MS` (trailing). The dirty
19
+ * flag is cleared only by a successful call, so a suppressed or failed one is
20
+ * retried by the next trigger. Everything is read through refs, so the
21
+ * returned callbacks are stable.
22
+ *
23
+ * Deeplink entry: the first list load of each deeplink target never marks the
24
+ * thread dirty — the list rests at the window end only until the jump runs.
25
+ */
26
+ export const useMarkConversationRead = ({
27
+ conversationId,
28
+ isAtBottomRef,
29
+ listLoadedRef,
30
+ hasMoreNew,
31
+ deeplinkTargetId,
32
+ gate
33
+ }) => {
34
+ const navigation = useNavigation();
35
+ const dirtyRef = useRef(false);
36
+ const dirtyWhileFocusedRef = useRef(false);
37
+ const lastCallAtRef = useRef(0);
38
+ const timerRef = useRef(undefined);
39
+ const timerDueRef = useRef(0);
40
+ // The next list load is a deeplink's first (pending the jump).
41
+ const deeplinkLoadHeldRef = useRef(!!deeplinkTargetId);
42
+ const latest = useRef({
43
+ conversationId,
44
+ hasMoreNew,
45
+ gate
46
+ });
47
+ latest.current = {
48
+ conversationId,
49
+ hasMoreNew,
50
+ gate
51
+ };
52
+ const tryFlushRef = useRef(() => {});
53
+ const clearTimer = () => {
54
+ clearTimeout(timerRef.current);
55
+ timerRef.current = undefined;
56
+ };
57
+
58
+ // Single timer: a sooner deadline replaces a later one; the timer
59
+ // re-evaluates everything when it fires.
60
+ const schedule = (delay, reason) => {
61
+ const due = Date.now() + delay;
62
+ if (timerRef.current && timerDueRef.current <= due) return;
63
+ clearTimer();
64
+ timerDueRef.current = due;
65
+ timerRef.current = setTimeout(() => {
66
+ timerRef.current = undefined;
67
+ tryFlushRef.current(reason);
68
+ }, delay);
69
+ };
70
+ const callMarkRead = () => {
71
+ const id = latest.current.conversationId;
72
+ if (!id) return;
73
+ const wasDirtyWhileFocused = dirtyWhileFocusedRef.current;
74
+ lastCallAtRef.current = Date.now();
75
+ dirtyRef.current = false;
76
+ dirtyWhileFocusedRef.current = false;
77
+ // At the SDK call site: 1 marker = 2 SDK requests.
78
+ perfMark('mark_read_call');
79
+ ChatMessageAPI.markConversationAsRead(id).catch(error => {
80
+ dirtyRef.current = true; // retried by the next trigger
81
+ dirtyWhileFocusedRef.current ||= wasDirtyWhileFocused;
82
+ if (__DEV__) console.log('[mark-read] failed', error);
83
+ });
84
+ };
85
+ tryFlushRef.current = reason => {
86
+ if (!dirtyRef.current) return;
87
+ const {
88
+ hasMoreNew: moreNew,
89
+ gate: currentGate
90
+ } = latest.current;
91
+ const gateClosed = !!currentGate?.isClosed();
92
+ const allowed = shouldRequestMarkRead({
93
+ reason,
94
+ gateClosed,
95
+ focused: navigation.isFocused(),
96
+ appActive: AppState.currentState === 'active',
97
+ listLoaded: !!listLoadedRef.current,
98
+ hasMoreNew: moreNew,
99
+ atBottom: !!isAtBottomRef.current,
100
+ isDeeplinkFirstLoad: false,
101
+ // held loads never reach a flush
102
+ dirtyWhileFocused: dirtyWhileFocusedRef.current
103
+ });
104
+ if (!allowed) {
105
+ // Fail-safe: re-check when the gate's watchdog opens it.
106
+ const deadline = gateClosed ? currentGate?.openDeadline() : null;
107
+ if (deadline != null) {
108
+ schedule(Math.max(0, deadline - Date.now()) + 1, 'gate-open');
109
+ }
110
+ return;
111
+ }
112
+ // Bypass the throttle: the component is going away.
113
+ if (reason === 'leave') return callMarkRead();
114
+ const delay = nextMarkReadDelay(Date.now(), lastCallAtRef.current);
115
+ if (delay === 0) return callMarkRead();
116
+ schedule(delay, reason);
117
+ };
118
+
119
+ /** A trigger: something may now be read. */
120
+ const requestMarkRead = useCallback(reason => {
121
+ if (isListLoadReason(reason) && deeplinkLoadHeldRef.current) {
122
+ deeplinkLoadHeldRef.current = false;
123
+ return;
124
+ }
125
+ dirtyRef.current = true;
126
+ if (navigation.isFocused()) dirtyWhileFocusedRef.current = true;
127
+ tryFlushRef.current(reason);
128
+ }, [navigation]);
129
+
130
+ /** Flush-only: retry a pending mark-read without marking anything new. */
131
+ const flushMarkRead = useCallback(reason => {
132
+ tryFlushRef.current(reason);
133
+ }, []);
134
+
135
+ // A new conversation / deeplink target on the same instance starts over.
136
+ useEffect(() => {
137
+ dirtyRef.current = false;
138
+ dirtyWhileFocusedRef.current = false;
139
+ deeplinkLoadHeldRef.current = !!deeplinkTargetId;
140
+ clearTimer();
141
+ }, [conversationId, deeplinkTargetId]);
142
+
143
+ // Back on the screen (after a blur) / back in the app: messages may have
144
+ // arrived meanwhile. The initial focus is not a return.
145
+ useEffect(() => {
146
+ let hasBlurred = false;
147
+ const offBlur = navigation.addListener('blur', () => {
148
+ hasBlurred = true;
149
+ });
150
+ const offFocus = navigation.addListener('focus', () => {
151
+ if (hasBlurred) requestMarkRead('focus');
152
+ });
153
+ return () => {
154
+ offBlur();
155
+ offFocus();
156
+ };
157
+ }, [navigation, requestMarkRead]);
158
+ useEffect(() => {
159
+ const sub = AppState.addEventListener('change', state => {
160
+ if (state === 'active') requestMarkRead('app-active');
161
+ });
162
+ return () => sub.remove();
163
+ }, [requestMarkRead]);
164
+ useEffect(() => {
165
+ if (!gate) return;
166
+ return gate.subscribe({
167
+ onOpen: () => tryFlushRef.current('gate-open'),
168
+ onClear: () => tryFlushRef.current('leave')
169
+ });
170
+ }, [gate]);
171
+ useEffect(() => () => {
172
+ clearTimer();
173
+ tryFlushRef.current('leave');
174
+ }, []);
175
+ return {
176
+ requestMarkRead,
177
+ flushMarkRead
178
+ };
179
+ };
180
+ //# sourceMappingURL=useMarkConversationRead.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useCallback","useEffect","useRef","AppState","useNavigation","ChatMessageAPI","perfMark","isListLoadReason","nextMarkReadDelay","shouldRequestMarkRead","useMarkConversationRead","conversationId","isAtBottomRef","listLoadedRef","hasMoreNew","deeplinkTargetId","gate","navigation","dirtyRef","dirtyWhileFocusedRef","lastCallAtRef","timerRef","undefined","timerDueRef","deeplinkLoadHeldRef","latest","current","tryFlushRef","clearTimer","clearTimeout","schedule","delay","reason","due","Date","now","setTimeout","callMarkRead","id","wasDirtyWhileFocused","markConversationAsRead","catch","error","__DEV__","console","log","moreNew","currentGate","gateClosed","isClosed","allowed","focused","isFocused","appActive","currentState","listLoaded","atBottom","isDeeplinkFirstLoad","dirtyWhileFocused","deadline","openDeadline","Math","max","requestMarkRead","flushMarkRead","hasBlurred","offBlur","addListener","offFocus","sub","addEventListener","state","remove","subscribe","onOpen","onClear"],"sourceRoot":"../../../../../src","sources":["screens/chat-detail/hooks/useMarkConversationRead.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+
3
+ import { useCallback, useEffect, useRef, useState } from 'react';
4
+ import { useUnreadIndicatorStore } from "../../../store/unreadIndicator.js";
5
+ import { isRowTopInViewport } from "../../../utils/unreadAnchor.js";
6
+
7
+ // A candidate filter only; `isRowTopInViewport` decides.
8
+ const VIEWABILITY_CONFIG = {
9
+ itemVisiblePercentThreshold: 1
10
+ };
11
+
12
+ /**
13
+ * "The divider has been seen" = the anchor row's TOP edge (where the divider
14
+ * sits) is in the viewport, after initial positioning has settled. Before
15
+ * settle — Old Arch corrects the estimated `initialScrollAtEnd` later — a
16
+ * visible divider only hides the pill (render-time check), it doesn't dismiss
17
+ * it for good.
18
+ */
19
+ export const useUnreadAnchorSeen = ({
20
+ rowId,
21
+ unreadToken,
22
+ isLoading,
23
+ listRef
24
+ }) => {
25
+ const rowIdRef = useRef(rowId);
26
+ rowIdRef.current = rowId;
27
+ const tokenRef = useRef(unreadToken);
28
+ tokenRef.current = unreadToken;
29
+ const viewableKeysRef = useRef(new Set());
30
+ const settledRef = useRef(false);
31
+ const [positionSettled, setPositionSettled] = useState(false);
32
+ const [viewabilityReady, setViewabilityReady] = useState(false);
33
+ const isAnchorTopVisibleNow = useCallback(() => {
34
+ const id = rowIdRef.current;
35
+ if (!id || !viewableKeysRef.current.has(id)) return false;
36
+ const state = listRef.current?.getState?.();
37
+ if (!state) return false;
38
+ const rowTop = state.positionByKey(id);
39
+ return isRowTopInViewport(rowTop, state.scroll, state.scrollLength);
40
+ }, [listRef]);
41
+ const checkAnchorSeen = useCallback(() => {
42
+ if (!settledRef.current || tokenRef.current == null) return;
43
+ if (!isAnchorTopVisibleNow()) return;
44
+ useUnreadIndicatorStore.getState().dismissPill(tokenRef.current);
45
+ }, [isAnchorTopVisibleNow]);
46
+
47
+ // Stable identity: LegendList reads it once.
48
+ const handleViewableItemsChanged = useCallback(({
49
+ viewableItems
50
+ }) => {
51
+ viewableKeysRef.current = new Set(viewableItems.map(t => t.key));
52
+ setViewabilityReady(true);
53
+ checkAnchorSeen();
54
+ }, [checkAnchorSeen]);
55
+
56
+ // handleListLoad's scrollToEnd + 1 frame | first drag | a finished jump.
57
+ const markPositionSettled = useCallback(() => {
58
+ if (!settledRef.current) {
59
+ settledRef.current = true;
60
+ setPositionSettled(true);
61
+ }
62
+ checkAnchorSeen(); // the CURRENT snapshot, not an older one
63
+ }, [checkAnchorSeen]);
64
+
65
+ // Late anchor / a new window.
66
+ useEffect(() => {
67
+ checkAnchorSeen();
68
+ }, [rowId, checkAnchorSeen]);
69
+
70
+ // The list remounts under the spinner: start over.
71
+ useEffect(() => {
72
+ if (!isLoading) return;
73
+ viewableKeysRef.current = new Set();
74
+ settledRef.current = false;
75
+ setPositionSettled(false);
76
+ setViewabilityReady(false);
77
+ }, [isLoading]);
78
+ return {
79
+ positionSettled,
80
+ viewabilityReady,
81
+ viewabilityConfig: VIEWABILITY_CONFIG,
82
+ isAnchorTopVisibleNow,
83
+ checkAnchorSeen,
84
+ handleViewableItemsChanged,
85
+ markPositionSettled
86
+ };
87
+ };
88
+ //# sourceMappingURL=useUnreadAnchorSeen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useCallback","useEffect","useRef","useState","useUnreadIndicatorStore","isRowTopInViewport","VIEWABILITY_CONFIG","itemVisiblePercentThreshold","useUnreadAnchorSeen","rowId","unreadToken","isLoading","listRef","rowIdRef","current","tokenRef","viewableKeysRef","Set","settledRef","positionSettled","setPositionSettled","viewabilityReady","setViewabilityReady","isAnchorTopVisibleNow","id","has","state","getState","rowTop","positionByKey","scroll","scrollLength","checkAnchorSeen","dismissPill","handleViewableItemsChanged","viewableItems","map","t","key","markPositionSettled","viewabilityConfig"],"sourceRoot":"../../../../../src","sources":["screens/chat-detail/hooks/useUnreadAnchorSeen.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+
3
+ import { useEffect, useRef } from 'react';
4
+ import { useUnreadIndicatorStore } from "../../../store/unreadIndicator.js";
5
+ import { useMessageStore } from "../../../store/message.js";
6
+ import { isDeeplinkTargetBeforeAnchor } from "../../../utils/unreadAnchor.js";
7
+
8
+ /**
9
+ * D11: a deeplink to a message OLDER than the first unread hides the
10
+ * indicator; a deeplink at/after it (or whose target never shows up) shows it.
11
+ * Decided explicitly once the lookup has answered and the window has loaded,
12
+ * and re-decided — against the existing anchor, with no SDK call — when
13
+ * `targetMessageId` changes on the same screen.
14
+ *
15
+ * Must run above `useMessages` in ChatDetail: the re-arm effect has to land
16
+ * before `useMessages`' `reset()` in the same commit.
17
+ */
18
+ export const useUnreadDeeplinkCheck = ({
19
+ targetMessageId,
20
+ unreadToken,
21
+ tokenRef
22
+ }) => {
23
+ const awaiting = useUnreadIndicatorStore(s => unreadToken != null && s.token === unreadToken && s.awaitingDeeplinkCheck);
24
+ const pending = useUnreadIndicatorStore(s => s.pending);
25
+ const initLoading = useMessageStore(s => s.initLoading);
26
+ const rawMessages = useMessageStore(s => s.messages);
27
+ const prevTargetRef = useRef(targetMessageId);
28
+ // After a re-arm, wait until the new search has been seen loading and then
29
+ // finished, so the rule is never decided against the old window.
30
+ const needLoadCycleRef = useRef(false);
31
+ useEffect(() => {
32
+ const prev = prevTargetRef.current;
33
+ prevTargetRef.current = targetMessageId;
34
+ const token = tokenRef.current;
35
+ if (!targetMessageId || targetMessageId === prev || token == null) return;
36
+ const store = useUnreadIndicatorStore.getState();
37
+ if (store.token !== token || !store.anchor) return;
38
+ store.rearmDeeplinkCheck(token);
39
+ needLoadCycleRef.current = true;
40
+ }, [targetMessageId, tokenRef]);
41
+ useEffect(() => {
42
+ const token = tokenRef.current;
43
+ if (!targetMessageId || !awaiting || pending || token == null) return;
44
+ if (needLoadCycleRef.current) {
45
+ if (initLoading) needLoadCycleRef.current = false;
46
+ return;
47
+ }
48
+ if (initLoading) return;
49
+ const store = useUnreadIndicatorStore.getState();
50
+ if (!store.anchor) {
51
+ store.confirmDeeplinkCheck(token, false);
52
+ return;
53
+ }
54
+ const target = rawMessages.find(m => m.clientMsgID === targetMessageId);
55
+ // A missing target (search failed / not in window) shows normally.
56
+ store.confirmDeeplinkCheck(token, isDeeplinkTargetBeforeAnchor(target?.seq, store.anchor.seq));
57
+ }, [targetMessageId, awaiting, pending, initLoading, rawMessages, tokenRef]);
58
+ };
59
+ //# sourceMappingURL=useUnreadDeeplinkCheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useEffect","useRef","useUnreadIndicatorStore","useMessageStore","isDeeplinkTargetBeforeAnchor","useUnreadDeeplinkCheck","targetMessageId","unreadToken","tokenRef","awaiting","s","token","awaitingDeeplinkCheck","pending","initLoading","rawMessages","messages","prevTargetRef","needLoadCycleRef","prev","current","store","getState","anchor","rearmDeeplinkCheck","confirmDeeplinkCheck","target","find","m","clientMsgID","seq"],"sourceRoot":"../../../../../src","sources":["screens/chat-detail/hooks/useUnreadDeeplinkCheck.ts"],"mappings":";;AAAA,SAASA,SAAS,EAAEC,MAAM,QAAwB,OAAO;AACzD,SAASC,uBAAuB,QAAQ,mCAAgC;AACxE,SAASC,eAAe,QAAQ,2BAAwB;AACxD,SAASC,4BAA4B,QAAQ,gCAA6B;;AAE1E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,sBAAsB,GAAGA,CAAC;EACrCC,eAAe;EACfC,WAAW;EACXC;AAKF,CAAC,KAAK;EACJ,MAAMC,QAAQ,GAAGP,uBAAuB,CACrCQ,CAAC,IACAH,WAAW,IAAI,IAAI,IAAIG,CAAC,CAACC,KAAK,KAAKJ,WAAW,IAAIG,CAAC,CAACE,qBACxD,CAAC;EACD,MAAMC,OAAO,GAAGX,uBAAuB,CAAEQ,CAAC,IAAKA,CAAC,CAACG,OAAO,CAAC;EACzD,MAAMC,WAAW,GAAGX,eAAe,CAAEO,CAAC,IAAKA,CAAC,CAACI,WAAW,CAAC;EACzD,MAAMC,WAAW,GAAGZ,eAAe,CAAEO,CAAC,IAAKA,CAAC,CAACM,QAAQ,CAAC;EACtD,MAAMC,aAAa,GAAGhB,MAAM,CAACK,eAAe,CAAC;EAC7C;EACA;EACA,MAAMY,gBAAgB,GAAGjB,MAAM,CAAC,KAAK,CAAC;EAEtCD,SAAS,CAAC,MAAM;IACd,MAAMmB,IAAI,GAAGF,aAAa,CAACG,OAAO;IAClCH,aAAa,CAACG,OAAO,GAAGd,eAAe;IACvC,MAAMK,KAAK,GAAGH,QAAQ,CAACY,OAAO;IAC9B,IAAI,CAACd,eAAe,IAAIA,eAAe,KAAKa,IAAI,IAAIR,KAAK,IAAI,IAAI,EAAE;IACnE,MAAMU,KAAK,GAAGnB,uBAAuB,CAACoB,QAAQ,CAAC,CAAC;IAChD,IAAID,KAAK,CAACV,KAAK,KAAKA,KAAK,IAAI,CAACU,KAAK,CAACE,MAAM,EAAE;IAC5CF,KAAK,CAACG,kBAAkB,CAACb,KAAK,CAAC;IAC/BO,gBAAgB,CAACE,OAAO,GAAG,IAAI;EACjC,CAAC,EAAE,CAACd,eAAe,EAAEE,QAAQ,CAAC,CAAC;EAE/BR,SAAS,CAAC,MAAM;IACd,MAAMW,KAAK,GAAGH,QAAQ,CAACY,OAAO;IAC9B,IAAI,CAACd,eAAe,IAAI,CAACG,QAAQ,IAAII,OAAO,IAAIF,KAAK,IAAI,IAAI,EAAE;IAC/D,IAAIO,gBAAgB,CAACE,OAAO,EAAE;MAC5B,IAAIN,WAAW,EAAEI,gBAAgB,CAACE,OAAO,GAAG,KAAK;MACjD;IACF;IACA,IAAIN,WAAW,EAAE;IACjB,MAAMO,KAAK,GAAGnB,uBAAuB,CAACoB,QAAQ,CAAC,CAAC;IAChD,IAAI,CAACD,KAAK,CAACE,MAAM,EAAE;MACjBF,KAAK,CAACI,oBAAoB,CAACd,KAAK,EAAE,KAAK,CAAC;MACxC;IACF;IACA,MAAMe,MAAM,GAAGX,WAAW,CAACY,IAAI,CAAEC,CAAC,IAAKA,CAAC,CAACC,WAAW,KAAKvB,eAAe,CAAC;IACzE;IACAe,KAAK,CAACI,oBAAoB,CACxBd,KAAK,EACLP,4BAA4B,CAACsB,MAAM,EAAEI,GAAG,EAAET,KAAK,CAACE,MAAM,CAACO,GAAG,CAC5D,CAAC;EACH,CAAC,EAAE,CAACxB,eAAe,EAAEG,QAAQ,EAAEI,OAAO,EAAEC,WAAW,EAAEC,WAAW,EAAEP,QAAQ,CAAC,CAAC;AAC9E,CAAC","ignoreList":[]}
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+
3
+ import { useCallback, useEffect, useRef, useState } from 'react';
4
+ import { useIsFocused, useNavigation, useRoute } from '@react-navigation/native';
5
+ import { ChatMessageAPI } from "../../../services/message.js";
6
+ import { useUnreadIndicatorStore } from "../../../store/unreadIndicator.js";
7
+ import { toUnreadAnchor } from "../../../utils/unreadAnchor.js";
8
+ import { useUnreadDeeplinkCheck } from "./useUnreadDeeplinkCheck.js";
9
+
10
+ // A blur within this long of a library-initiated child navigation (Thread
11
+ // Info, in-thread search, add member) keeps the indicator; any other blur
12
+ // (tab switch, another module) clears it.
13
+ export const CHILD_NAV_TTL_MS = 1000;
14
+
15
+ /**
16
+ * Owns this screen's unread-indicator session. `getFirstUnreadMessage` records
17
+ * the thread's open time server-side, so it's called exactly once per route
18
+ * visit: on the first focus after access is confirmed. A remount of ChatDetail
19
+ * inside the same route (access re-check, SDK reconnect) adopts the existing
20
+ * session instead of calling again; a new route key is a new visit.
21
+ *
22
+ * Must be called above `useMessages` (see `useUnreadDeeplinkCheck`).
23
+ */
24
+ export const useUnreadIndicator = ({
25
+ conversationId,
26
+ isBotConversation,
27
+ targetMessageId,
28
+ accessConfirmed
29
+ }) => {
30
+ const routeKey = useRoute().key;
31
+ const navigation = useNavigation();
32
+ const isFocused = useIsFocused();
33
+ // Ref for listeners and async code; state so token-scoped selectors re-run
34
+ // when it's set (adopting doesn't change the store).
35
+ const tokenRef = useRef(null);
36
+ const [unreadToken, setUnreadToken] = useState(null);
37
+ const childNavAtRef = useRef(0);
38
+ useEffect(() => {
39
+ if (!conversationId) return;
40
+ const s = useUnreadIndicatorStore.getState();
41
+ if (s.routeKey === routeKey && s.conversationId === conversationId) {
42
+ tokenRef.current = s.token;
43
+ setUnreadToken(s.token);
44
+ return;
45
+ }
46
+ if (!isFocused || !accessConfirmed) return;
47
+ const token = s.begin({
48
+ routeKey,
49
+ conversationId,
50
+ skip: isBotConversation,
51
+ awaitDeeplinkCheck: !!targetMessageId
52
+ });
53
+ tokenRef.current = token;
54
+ setUnreadToken(token);
55
+ if (isBotConversation) return;
56
+ let resolved = false;
57
+ (async () => {
58
+ try {
59
+ const message = await ChatMessageAPI.getFirstUnreadMessage(conversationId);
60
+ useUnreadIndicatorStore.getState().resolve(token, toUnreadAnchor(message, conversationId));
61
+ resolved = true;
62
+ } catch (error) {
63
+ if (__DEV__) console.log('[unread] resolve failed', error);
64
+ } finally {
65
+ // The mark-read gate must always open.
66
+ if (!resolved) useUnreadIndicatorStore.getState().resolve(token, null);
67
+ }
68
+ })();
69
+ // targetMessageId is deliberately not a dependency: a target change on
70
+ // the same screen re-arms D11, it doesn't start a new visit.
71
+ // eslint-disable-next-line react-hooks/exhaustive-deps
72
+ }, [conversationId, routeKey, isFocused, accessConfirmed, isBotConversation]);
73
+ const markChildNavigation = useCallback(() => {
74
+ childNavAtRef.current = Date.now();
75
+ }, []);
76
+ useEffect(() => {
77
+ const offBlur = navigation.addListener('blur', () => {
78
+ const isChildNavigation = Date.now() - childNavAtRef.current < CHILD_NAV_TTL_MS;
79
+ childNavAtRef.current = 0;
80
+ const token = tokenRef.current;
81
+ // Token-scoped: a no-op if another visit has superseded this session.
82
+ if (!isChildNavigation && token != null) {
83
+ useUnreadIndicatorStore.getState().clear(token);
84
+ }
85
+ });
86
+ const offFocus = navigation.addListener('focus', () => {
87
+ childNavAtRef.current = 0;
88
+ });
89
+ return () => {
90
+ offBlur();
91
+ offFocus();
92
+ };
93
+ }, [navigation]);
94
+ useUnreadDeeplinkCheck({
95
+ targetMessageId,
96
+ unreadToken,
97
+ tokenRef
98
+ });
99
+ return {
100
+ unreadToken,
101
+ unreadTokenRef: tokenRef,
102
+ markChildNavigation
103
+ };
104
+ };
105
+ //# sourceMappingURL=useUnreadIndicator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useCallback","useEffect","useRef","useState","useIsFocused","useNavigation","useRoute","ChatMessageAPI","useUnreadIndicatorStore","toUnreadAnchor","useUnreadDeeplinkCheck","CHILD_NAV_TTL_MS","useUnreadIndicator","conversationId","isBotConversation","targetMessageId","accessConfirmed","routeKey","key","navigation","isFocused","tokenRef","unreadToken","setUnreadToken","childNavAtRef","s","getState","current","token","begin","skip","awaitDeeplinkCheck","resolved","message","getFirstUnreadMessage","resolve","error","__DEV__","console","log","markChildNavigation","Date","now","offBlur","addListener","isChildNavigation","clear","offFocus","unreadTokenRef"],"sourceRoot":"../../../../../src","sources":["screens/chat-detail/hooks/useUnreadIndicator.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+
3
+ import { useCallback, useEffect, useMemo, useRef } from 'react';
4
+ import { DeviceEventEmitter } from 'react-native';
5
+ import { useUnreadIndicatorStore } from "../../../store/unreadIndicator.js";
6
+ import { useMessageStore } from "../../../store/message.js";
7
+ import { ChatEvent } from "../../../types/events.js";
8
+ import { resolveUnreadAnchorRowId } from "../../../utils/unreadAnchor.js";
9
+ import { useUnreadAnchorSeen } from "./useUnreadAnchorSeen.js";
10
+
11
+ /** Lands the divider ~100px below the list top, no highlight, never exits. */
12
+ export const UNREAD_JUMP_OPTIONS = {
13
+ highlight: false,
14
+ viewPosition: 0,
15
+ viewOffset: 100,
16
+ exitOnUnavailable: false
17
+ };
18
+ // Parity with searchMessages' own re-emit delay.
19
+ const POST_SEARCH_SCROLL_DELAY_MS = 300;
20
+ // Anchor not reachable by id (not in the local DB): page older history at
21
+ // most this many times (~75 messages) looking for the boundary.
22
+ const MAX_ANCHOR_PAGES = 5;
23
+ /**
24
+ * Wires the unread session into the list: which row carries the divider,
25
+ * whether the pill shows, and the pill's jump. The session is token-scoped —
26
+ * a second ChatDetail of the same thread never reads or dismisses this one's.
27
+ */
28
+ export const useUnreadIndicatorInList = ({
29
+ messages,
30
+ unreadToken,
31
+ isLoading,
32
+ hasMoreOld,
33
+ moreOldLoading,
34
+ onLoadMoreOld,
35
+ isBotConversation,
36
+ listRef,
37
+ jumpInFlightRef,
38
+ setJumpInFlight
39
+ }) => {
40
+ const own = useUnreadIndicatorStore(s => unreadToken != null && s.token === unreadToken);
41
+ const anchor = useUnreadIndicatorStore(s => s.anchor);
42
+ const pillDismissed = useUnreadIndicatorStore(s => s.pillDismissed);
43
+ const awaiting = useUnreadIndicatorStore(s => s.awaitingDeeplinkCheck);
44
+ // Raw window (before rendering filters) decides whether the boundary is in.
45
+ const oldestLoadedSeq = useMessageStore(s => s.messages[0]?.seq ?? 0);
46
+ const active = own && !isBotConversation && !awaiting && !!anchor;
47
+ const unreadAnchorRowId = useMemo(() => active ? resolveUnreadAnchorRowId(messages, anchor, {
48
+ oldestLoadedSeq,
49
+ hasMoreOld
50
+ }) : null, [active, messages, anchor, oldestLoadedSeq, hasMoreOld]);
51
+ const seen = useUnreadAnchorSeen({
52
+ rowId: unreadAnchorRowId,
53
+ unreadToken,
54
+ isLoading,
55
+ listRef
56
+ });
57
+ const latest = useRef({
58
+ unreadAnchorRowId,
59
+ unreadToken,
60
+ hasMoreOld
61
+ });
62
+ latest.current = {
63
+ unreadAnchorRowId,
64
+ unreadToken,
65
+ hasMoreOld
66
+ };
67
+ const onLoadMoreOldRef = useRef(onLoadMoreOld);
68
+ onLoadMoreOldRef.current = onLoadMoreOld;
69
+ const pendingJumpRef = useRef('idle');
70
+ const pagesRef = useRef(0);
71
+
72
+ // After the window has landed (or been kept): land on the row re-resolved
73
+ // by seq, page older history toward it, or give up silently (no toast).
74
+ const landOrPage = useCallback(() => {
75
+ const {
76
+ unreadAnchorRowId: id,
77
+ hasMoreOld: more
78
+ } = latest.current;
79
+ if (id) {
80
+ pendingJumpRef.current = 'idle';
81
+ DeviceEventEmitter.emit(ChatEvent.ScrollToMessage, {
82
+ ...UNREAD_JUMP_OPTIONS,
83
+ clientMessageId: id,
84
+ fromSearch: true
85
+ });
86
+ return;
87
+ }
88
+ if (more && pagesRef.current < MAX_ANCHOR_PAGES && onLoadMoreOldRef.current) {
89
+ pagesRef.current += 1;
90
+ pendingJumpRef.current = 'paging';
91
+ onLoadMoreOldRef.current(true);
92
+ return;
93
+ }
94
+ pendingJumpRef.current = 'idle';
95
+ setJumpInFlight(false);
96
+ }, [setJumpInFlight]);
97
+ useEffect(() => {
98
+ if (isLoading) {
99
+ if (pendingJumpRef.current === 'requested') {
100
+ pendingJumpRef.current = 'loading';
101
+ }
102
+ return;
103
+ }
104
+ if (pendingJumpRef.current !== 'loading') return;
105
+ const timer = setTimeout(landOrPage, POST_SEARCH_SCROLL_DELAY_MS);
106
+ return () => clearTimeout(timer);
107
+ }, [isLoading, landOrPage]);
108
+
109
+ // One older page landed while paging toward the anchor.
110
+ const prevMoreOldLoadingRef = useRef(moreOldLoading);
111
+ useEffect(() => {
112
+ const wasLoading = prevMoreOldLoadingRef.current;
113
+ prevMoreOldLoadingRef.current = moreOldLoading;
114
+ if (wasLoading && !moreOldLoading && pendingJumpRef.current === 'paging') {
115
+ landOrPage();
116
+ }
117
+ }, [moreOldLoading, landOrPage]);
118
+ const jumpToFirstUnread = useCallback(() => {
119
+ const s = useUnreadIndicatorStore.getState();
120
+ const {
121
+ unreadToken: token,
122
+ unreadAnchorRowId: id
123
+ } = latest.current;
124
+ if (token == null || s.token !== token || !s.anchor) return;
125
+ if (s.pillDismissed || jumpInFlightRef.current) return;
126
+ s.dismissPill(token);
127
+ setJumpInFlight(true);
128
+ if (id) {
129
+ DeviceEventEmitter.emit(ChatEvent.ScrollToMessage, {
130
+ ...UNREAD_JUMP_OPTIONS,
131
+ clientMessageId: id
132
+ });
133
+ return;
134
+ }
135
+ pendingJumpRef.current = 'requested';
136
+ pagesRef.current = 0;
137
+ DeviceEventEmitter.emit(ChatEvent.ScrollToMessage, {
138
+ ...UNREAD_JUMP_OPTIONS,
139
+ clientMessageId: s.anchor.clientMsgID,
140
+ reemitAfterSearch: false
141
+ });
142
+ }, [jumpInFlightRef, setJumpInFlight]);
143
+
144
+ // Evaluated in render, including "the divider is on screen right now", so a
145
+ // late anchor never flashes the pill for a frame.
146
+ const isPillVisible = active && !pillDismissed && !isLoading && seen.positionSettled && seen.viewabilityReady && !(unreadAnchorRowId && seen.isAnchorTopVisibleNow());
147
+ return {
148
+ unreadAnchorRowId,
149
+ isPillVisible,
150
+ viewabilityConfig: seen.viewabilityConfig,
151
+ handleViewableItemsChanged: seen.handleViewableItemsChanged,
152
+ checkAnchorSeen: seen.checkAnchorSeen,
153
+ markPositionSettled: seen.markPositionSettled,
154
+ jumpToFirstUnread
155
+ };
156
+ };
157
+ //# sourceMappingURL=useUnreadIndicatorInList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useCallback","useEffect","useMemo","useRef","DeviceEventEmitter","useUnreadIndicatorStore","useMessageStore","ChatEvent","resolveUnreadAnchorRowId","useUnreadAnchorSeen","UNREAD_JUMP_OPTIONS","highlight","viewPosition","viewOffset","exitOnUnavailable","POST_SEARCH_SCROLL_DELAY_MS","MAX_ANCHOR_PAGES","useUnreadIndicatorInList","messages","unreadToken","isLoading","hasMoreOld","moreOldLoading","onLoadMoreOld","isBotConversation","listRef","jumpInFlightRef","setJumpInFlight","own","s","token","anchor","pillDismissed","awaiting","awaitingDeeplinkCheck","oldestLoadedSeq","seq","active","unreadAnchorRowId","seen","rowId","latest","current","onLoadMoreOldRef","pendingJumpRef","pagesRef","landOrPage","id","more","emit","ScrollToMessage","clientMessageId","fromSearch","timer","setTimeout","clearTimeout","prevMoreOldLoadingRef","wasLoading","jumpToFirstUnread","getState","dismissPill","clientMsgID","reemitAfterSearch","isPillVisible","positionSettled","viewabilityReady","isAnchorTopVisibleNow","viewabilityConfig","handleViewableItemsChanged","checkAnchorSeen","markPositionSettled"],"sourceRoot":"../../../../../src","sources":["screens/chat-detail/hooks/useUnreadIndicatorInList.ts"],"mappings":"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","ignoreList":[]}