@droppii-org/chat-mobile 0.2.109 → 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.
- package/lib/module/screens/chat-detail/ChatDetail.js +42 -7
- package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatListLegend.js +249 -49
- package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
- package/lib/module/screens/chat-detail/hooks/createUnreadMarkReadGate.js +32 -0
- package/lib/module/screens/chat-detail/hooks/createUnreadMarkReadGate.js.map +1 -0
- package/lib/module/screens/chat-detail/hooks/useMarkConversationRead.js +180 -0
- package/lib/module/screens/chat-detail/hooks/useMarkConversationRead.js.map +1 -0
- package/lib/module/screens/chat-detail/hooks/useUnreadAnchorSeen.js +88 -0
- package/lib/module/screens/chat-detail/hooks/useUnreadAnchorSeen.js.map +1 -0
- package/lib/module/screens/chat-detail/hooks/useUnreadDeeplinkCheck.js +59 -0
- package/lib/module/screens/chat-detail/hooks/useUnreadDeeplinkCheck.js.map +1 -0
- package/lib/module/screens/chat-detail/hooks/useUnreadIndicator.js +105 -0
- package/lib/module/screens/chat-detail/hooks/useUnreadIndicator.js.map +1 -0
- package/lib/module/screens/chat-detail/hooks/useUnreadIndicatorInList.js +157 -0
- package/lib/module/screens/chat-detail/hooks/useUnreadIndicatorInList.js.map +1 -0
- package/lib/module/screens/chat-detail/legend/LegendChatUnreadButton.js +140 -0
- package/lib/module/screens/chat-detail/legend/LegendChatUnreadButton.js.map +1 -0
- package/lib/module/screens/chat-detail/legend/LegendChatUnreadDivider.js +51 -0
- package/lib/module/screens/chat-detail/legend/LegendChatUnreadDivider.js.map +1 -0
- package/lib/module/services/message.js +30 -0
- package/lib/module/services/message.js.map +1 -1
- package/lib/module/store/message.js +25 -4
- package/lib/module/store/message.js.map +1 -1
- package/lib/module/store/unreadIndicator.js +26 -0
- package/lib/module/store/unreadIndicator.js.map +1 -0
- package/lib/module/translation/resources/i18n.js +2 -0
- package/lib/module/translation/resources/i18n.js.map +1 -1
- package/lib/module/types/chat.js.map +1 -1
- package/lib/module/types/unreadIndicator.js +2 -0
- package/lib/module/types/unreadIndicator.js.map +1 -0
- package/lib/module/utils/markReadPolicy.js +25 -0
- package/lib/module/utils/markReadPolicy.js.map +1 -0
- package/lib/module/utils/promise.js +16 -0
- package/lib/module/utils/promise.js.map +1 -1
- package/lib/module/utils/unreadAnchor.js +57 -0
- package/lib/module/utils/unreadAnchor.js.map +1 -0
- package/lib/module/utils/unreadIndicatorState.js +96 -0
- package/lib/module/utils/unreadIndicatorState.js.map +1 -0
- package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts +4 -1
- package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/hooks/createUnreadMarkReadGate.d.ts +9 -0
- package/lib/typescript/src/screens/chat-detail/hooks/createUnreadMarkReadGate.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useMarkConversationRead.d.ts +39 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useMarkConversationRead.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useUnreadAnchorSeen.d.ts +26 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useUnreadAnchorSeen.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useUnreadDeeplinkCheck.d.ts +17 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useUnreadDeeplinkCheck.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useUnreadIndicator.d.ts +21 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useUnreadIndicator.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useUnreadIndicatorInList.d.ts +35 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useUnreadIndicatorInList.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatUnreadButton.d.ts +8 -0
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatUnreadButton.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatUnreadDivider.d.ts +7 -0
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatUnreadDivider.d.ts.map +1 -0
- package/lib/typescript/src/services/message.d.ts +8 -0
- package/lib/typescript/src/services/message.d.ts.map +1 -1
- package/lib/typescript/src/store/message.d.ts.map +1 -1
- package/lib/typescript/src/store/unreadIndicator.d.ts +10 -0
- package/lib/typescript/src/store/unreadIndicator.d.ts.map +1 -0
- package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
- package/lib/typescript/src/types/chat.d.ts +3 -0
- package/lib/typescript/src/types/chat.d.ts.map +1 -1
- package/lib/typescript/src/types/events.d.ts +17 -1
- package/lib/typescript/src/types/events.d.ts.map +1 -1
- package/lib/typescript/src/types/unreadIndicator.d.ts +45 -0
- package/lib/typescript/src/types/unreadIndicator.d.ts.map +1 -0
- package/lib/typescript/src/utils/markReadPolicy.d.ts +27 -0
- package/lib/typescript/src/utils/markReadPolicy.d.ts.map +1 -0
- package/lib/typescript/src/utils/promise.d.ts +6 -0
- package/lib/typescript/src/utils/promise.d.ts.map +1 -1
- package/lib/typescript/src/utils/unreadAnchor.d.ts +33 -0
- package/lib/typescript/src/utils/unreadAnchor.d.ts.map +1 -0
- package/lib/typescript/src/utils/unreadIndicatorState.d.ts +26 -0
- package/lib/typescript/src/utils/unreadIndicatorState.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/screens/chat-detail/ChatDetail.tsx +58 -5
- package/src/screens/chat-detail/ChatListLegend.tsx +314 -46
- package/src/screens/chat-detail/hooks/createUnreadMarkReadGate.ts +41 -0
- package/src/screens/chat-detail/hooks/useMarkConversationRead.ts +197 -0
- package/src/screens/chat-detail/hooks/useUnreadAnchorSeen.ts +105 -0
- package/src/screens/chat-detail/hooks/useUnreadDeeplinkCheck.ts +68 -0
- package/src/screens/chat-detail/hooks/useUnreadIndicator.ts +112 -0
- package/src/screens/chat-detail/hooks/useUnreadIndicatorInList.ts +181 -0
- package/src/screens/chat-detail/legend/LegendChatUnreadButton.tsx +165 -0
- package/src/screens/chat-detail/legend/LegendChatUnreadDivider.tsx +52 -0
- package/src/services/message.ts +42 -0
- package/src/store/message.ts +23 -5
- package/src/store/unreadIndicator.ts +38 -0
- package/src/translation/resources/i18n.ts +2 -0
- package/src/types/chat.ts +3 -0
- package/src/types/events.ts +18 -1
- package/src/types/unreadIndicator.ts +49 -0
- package/src/utils/markReadPolicy.ts +55 -0
- package/src/utils/promise.ts +20 -0
- package/src/utils/unreadAnchor.ts +87 -0
- package/src/utils/unreadIndicatorState.ts +131 -0
|
@@ -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":[]}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { memo, useEffect } from 'react';
|
|
4
|
+
import { StyleSheet } from 'react-native';
|
|
5
|
+
import Animated, { Easing, cancelAnimation, useAnimatedStyle, useReducedMotion, useSharedValue, withDelay, withRepeat, withSequence, withTiming } from 'react-native-reanimated';
|
|
6
|
+
import { KColors, KContainer, KImage, KLabel, KRadiusValue } from '@droppii/libs';
|
|
7
|
+
import { trans } from "../../../translation/index.js";
|
|
8
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
|
+
const FADE = {
|
|
10
|
+
duration: 200,
|
|
11
|
+
easing: Easing.out(Easing.ease)
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// Chevron cascade (D5): the bottom chevron leads and the top one follows, an
|
|
15
|
+
// upward motion cue. Designer may tune these.
|
|
16
|
+
const CYCLE_MS = 1200;
|
|
17
|
+
const STEP_MS = CYCLE_MS / 4;
|
|
18
|
+
const DIM = 0.35;
|
|
19
|
+
const CHEVRON_SIZE = 14;
|
|
20
|
+
|
|
21
|
+
// The touch target is real padded bounds, not `hitSlop`: a hitSlop reaching
|
|
22
|
+
// outside the parent Animated.View is dead on Android. 23 + 2 * 11 = 45pt.
|
|
23
|
+
const TOUCH_PAD_V = 11;
|
|
24
|
+
const TOUCH_PAD_L = 8;
|
|
25
|
+
// The pill itself sits 16 below the list top.
|
|
26
|
+
const PILL_TOP = 16;
|
|
27
|
+
const wave = () => withRepeat(withSequence(withTiming(DIM, {
|
|
28
|
+
duration: STEP_MS
|
|
29
|
+
}), withTiming(1, {
|
|
30
|
+
duration: STEP_MS
|
|
31
|
+
})), -1);
|
|
32
|
+
const UnreadChevrons = /*#__PURE__*/memo(() => {
|
|
33
|
+
const reducedMotion = useReducedMotion();
|
|
34
|
+
const top = useSharedValue(1);
|
|
35
|
+
const bottom = useSharedValue(1);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (reducedMotion) return;
|
|
38
|
+
bottom.value = wave();
|
|
39
|
+
top.value = withDelay(STEP_MS, wave());
|
|
40
|
+
return () => {
|
|
41
|
+
cancelAnimation(top);
|
|
42
|
+
cancelAnimation(bottom);
|
|
43
|
+
};
|
|
44
|
+
}, [reducedMotion, top, bottom]);
|
|
45
|
+
const topStyle = useAnimatedStyle(() => ({
|
|
46
|
+
opacity: top.value
|
|
47
|
+
}));
|
|
48
|
+
const bottomStyle = useAnimatedStyle(() => ({
|
|
49
|
+
opacity: bottom.value
|
|
50
|
+
}));
|
|
51
|
+
return /*#__PURE__*/_jsxs(KContainer.View, {
|
|
52
|
+
style: styles.chevrons,
|
|
53
|
+
children: [/*#__PURE__*/_jsx(Animated.View, {
|
|
54
|
+
style: topStyle,
|
|
55
|
+
children: /*#__PURE__*/_jsx(KImage.VectorIcons, {
|
|
56
|
+
name: "angle-up-o",
|
|
57
|
+
size: CHEVRON_SIZE,
|
|
58
|
+
color: KColors.palette.gray.w900
|
|
59
|
+
})
|
|
60
|
+
}), /*#__PURE__*/_jsx(Animated.View, {
|
|
61
|
+
style: [styles.chevronOverlap, bottomStyle],
|
|
62
|
+
children: /*#__PURE__*/_jsx(KImage.VectorIcons, {
|
|
63
|
+
name: "angle-up-o",
|
|
64
|
+
size: CHEVRON_SIZE,
|
|
65
|
+
color: KColors.palette.gray.w900
|
|
66
|
+
})
|
|
67
|
+
})]
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
UnreadChevrons.displayName = 'UnreadChevrons';
|
|
71
|
+
/** "Tin chưa đọc" half-pill, top-right of the list; jumps to the divider. */
|
|
72
|
+
export const LegendChatUnreadButton = /*#__PURE__*/memo(({
|
|
73
|
+
visible,
|
|
74
|
+
onPress
|
|
75
|
+
}) => {
|
|
76
|
+
const opacity = useSharedValue(0);
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
opacity.value = withTiming(visible ? 1 : 0, FADE);
|
|
79
|
+
}, [visible, opacity]);
|
|
80
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
81
|
+
opacity: opacity.value
|
|
82
|
+
}));
|
|
83
|
+
return /*#__PURE__*/_jsx(Animated.View, {
|
|
84
|
+
style: [styles.anchor, animatedStyle]
|
|
85
|
+
// From the prop, not the animated opacity: deterministic on Old Arch.
|
|
86
|
+
,
|
|
87
|
+
pointerEvents: visible ? 'box-none' : 'none',
|
|
88
|
+
children: /*#__PURE__*/_jsx(KContainer.Touchable, {
|
|
89
|
+
onPress: onPress,
|
|
90
|
+
activeOpacity: 0.7,
|
|
91
|
+
style: styles.touchArea,
|
|
92
|
+
accessible: true,
|
|
93
|
+
accessibilityRole: "button",
|
|
94
|
+
accessibilityLabel: trans('chat:unread_jump_button'),
|
|
95
|
+
children: /*#__PURE__*/_jsxs(KContainer.View, {
|
|
96
|
+
style: styles.pill,
|
|
97
|
+
children: [visible && /*#__PURE__*/_jsx(UnreadChevrons, {}), /*#__PURE__*/_jsx(KLabel.Text, {
|
|
98
|
+
typo: "TextXsMedium",
|
|
99
|
+
color: KColors.palette.gray.w900,
|
|
100
|
+
children: trans('chat:unread_jump_button')
|
|
101
|
+
})]
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
LegendChatUnreadButton.displayName = 'LegendChatUnreadButton';
|
|
107
|
+
const styles = StyleSheet.create({
|
|
108
|
+
anchor: {
|
|
109
|
+
position: 'absolute',
|
|
110
|
+
top: PILL_TOP - TOUCH_PAD_V,
|
|
111
|
+
right: 0,
|
|
112
|
+
zIndex: 10
|
|
113
|
+
},
|
|
114
|
+
touchArea: {
|
|
115
|
+
paddingVertical: TOUCH_PAD_V,
|
|
116
|
+
paddingLeft: TOUCH_PAD_L
|
|
117
|
+
},
|
|
118
|
+
pill: {
|
|
119
|
+
flexDirection: 'row',
|
|
120
|
+
alignItems: 'center',
|
|
121
|
+
gap: 2,
|
|
122
|
+
paddingHorizontal: 6,
|
|
123
|
+
paddingVertical: 2,
|
|
124
|
+
backgroundColor: KColors.palette.grayCool.w25,
|
|
125
|
+
borderWidth: 1,
|
|
126
|
+
borderRightWidth: 0,
|
|
127
|
+
borderColor: KColors.white,
|
|
128
|
+
borderTopLeftRadius: KRadiusValue.round,
|
|
129
|
+
borderBottomLeftRadius: KRadiusValue.round
|
|
130
|
+
},
|
|
131
|
+
chevrons: {
|
|
132
|
+
width: CHEVRON_SIZE,
|
|
133
|
+
height: CHEVRON_SIZE,
|
|
134
|
+
justifyContent: 'center'
|
|
135
|
+
},
|
|
136
|
+
chevronOverlap: {
|
|
137
|
+
marginTop: -8
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
//# sourceMappingURL=LegendChatUnreadButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["memo","useEffect","StyleSheet","Animated","Easing","cancelAnimation","useAnimatedStyle","useReducedMotion","useSharedValue","withDelay","withRepeat","withSequence","withTiming","KColors","KContainer","KImage","KLabel","KRadiusValue","trans","jsx","_jsx","jsxs","_jsxs","FADE","duration","easing","out","ease","CYCLE_MS","STEP_MS","DIM","CHEVRON_SIZE","TOUCH_PAD_V","TOUCH_PAD_L","PILL_TOP","wave","UnreadChevrons","reducedMotion","top","bottom","value","topStyle","opacity","bottomStyle","View","style","styles","chevrons","children","VectorIcons","name","size","color","palette","gray","w900","chevronOverlap","displayName","LegendChatUnreadButton","visible","onPress","animatedStyle","anchor","pointerEvents","Touchable","activeOpacity","touchArea","accessible","accessibilityRole","accessibilityLabel","pill","Text","typo","create","position","right","zIndex","paddingVertical","paddingLeft","flexDirection","alignItems","gap","paddingHorizontal","backgroundColor","grayCool","w25","borderWidth","borderRightWidth","borderColor","white","borderTopLeftRadius","round","borderBottomLeftRadius","width","height","justifyContent","marginTop"],"sourceRoot":"../../../../../src","sources":["screens/chat-detail/legend/LegendChatUnreadButton.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
import { StyleSheet } from 'react-native';
|
|
5
|
+
import { KColors, KContainer, KDivider, KLabel, KSpacingValue } from '@droppii/libs';
|
|
6
|
+
import { trans } from "../../../translation/index.js";
|
|
7
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
|
+
const LINE_COLOR = KColors.opacity.grayDark[10];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* "TIN NHẮN CHƯA ĐỌC" row above the first unread message. Figma spacing: the
|
|
12
|
+
* previous row's 8 bottom margin + 8 top margin + 4 padding = 20 above the
|
|
13
|
+
* lines; 4 padding + 12 bottom margin + the time label's 8 = 24 below.
|
|
14
|
+
*/
|
|
15
|
+
export const LegendChatUnreadDivider = /*#__PURE__*/memo(() => /*#__PURE__*/_jsxs(KContainer.View, {
|
|
16
|
+
row: true,
|
|
17
|
+
center: true,
|
|
18
|
+
paddingH: "1rem",
|
|
19
|
+
paddingV: "0.25rem",
|
|
20
|
+
style: styles.root,
|
|
21
|
+
accessible: true,
|
|
22
|
+
accessibilityLabel: trans('chat:unread_divider'),
|
|
23
|
+
children: [/*#__PURE__*/_jsx(KContainer.View, {
|
|
24
|
+
flex: true,
|
|
25
|
+
children: /*#__PURE__*/_jsx(KDivider, {
|
|
26
|
+
type: "line",
|
|
27
|
+
size: "hairline",
|
|
28
|
+
background: LINE_COLOR
|
|
29
|
+
})
|
|
30
|
+
}), /*#__PURE__*/_jsx(KLabel.Text, {
|
|
31
|
+
typo: "TextXsBold",
|
|
32
|
+
color: KColors.palette.gray.w200,
|
|
33
|
+
marginH: "1rem",
|
|
34
|
+
children: trans('chat:unread_divider')
|
|
35
|
+
}), /*#__PURE__*/_jsx(KContainer.View, {
|
|
36
|
+
flex: true,
|
|
37
|
+
children: /*#__PURE__*/_jsx(KDivider, {
|
|
38
|
+
type: "line",
|
|
39
|
+
size: "hairline",
|
|
40
|
+
background: LINE_COLOR
|
|
41
|
+
})
|
|
42
|
+
})]
|
|
43
|
+
}));
|
|
44
|
+
LegendChatUnreadDivider.displayName = 'LegendChatUnreadDivider';
|
|
45
|
+
const styles = StyleSheet.create({
|
|
46
|
+
root: {
|
|
47
|
+
marginTop: KSpacingValue['0.5rem'],
|
|
48
|
+
marginBottom: KSpacingValue['0.75rem']
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
//# sourceMappingURL=LegendChatUnreadDivider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["memo","StyleSheet","KColors","KContainer","KDivider","KLabel","KSpacingValue","trans","jsx","_jsx","jsxs","_jsxs","LINE_COLOR","opacity","grayDark","LegendChatUnreadDivider","View","row","center","paddingH","paddingV","style","styles","root","accessible","accessibilityLabel","children","flex","type","size","background","Text","typo","color","palette","gray","w200","marginH","displayName","create","marginTop","marginBottom"],"sourceRoot":"../../../../../src","sources":["screens/chat-detail/legend/LegendChatUnreadDivider.tsx"],"mappings":";;AAAA,SAASA,IAAI,QAAQ,OAAO;AAC5B,SAASC,UAAU,QAAQ,cAAc;AACzC,SACEC,OAAO,EACPC,UAAU,EACVC,QAAQ,EACRC,MAAM,EACNC,aAAa,QACR,eAAe;AACtB,SAASC,KAAK,QAAQ,+BAAsB;AAAC,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AAE7C,MAAMC,UAAU,GAAGV,OAAO,CAACW,OAAO,CAACC,QAAQ,CAAC,EAAE,CAAC;;AAE/C;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,uBAAuB,gBAAGf,IAAI,CAAC,mBAC1CW,KAAA,CAACR,UAAU,CAACa,IAAI;EACdC,GAAG;EACHC,MAAM;EACNC,QAAQ,EAAC,MAAM;EACfC,QAAQ,EAAC,SAAS;EAClBC,KAAK,EAAEC,MAAM,CAACC,IAAK;EACnBC,UAAU;EACVC,kBAAkB,EAAElB,KAAK,CAAC,qBAAqB,CAAE;EAAAmB,QAAA,gBAEjDjB,IAAA,CAACN,UAAU,CAACa,IAAI;IAACW,IAAI;IAAAD,QAAA,eACnBjB,IAAA,CAACL,QAAQ;MAACwB,IAAI,EAAC,MAAM;MAACC,IAAI,EAAC,UAAU;MAACC,UAAU,EAAElB;IAAW,CAAE;EAAC,CACjD,CAAC,eAClBH,IAAA,CAACJ,MAAM,CAAC0B,IAAI;IACVC,IAAI,EAAC,YAAY;IACjBC,KAAK,EAAE/B,OAAO,CAACgC,OAAO,CAACC,IAAI,CAACC,IAAK;IACjCC,OAAO,EAAC,MAAM;IAAAX,QAAA,EAEbnB,KAAK,CAAC,qBAAqB;EAAC,CAClB,CAAC,eACdE,IAAA,CAACN,UAAU,CAACa,IAAI;IAACW,IAAI;IAAAD,QAAA,eACnBjB,IAAA,CAACL,QAAQ;MAACwB,IAAI,EAAC,MAAM;MAACC,IAAI,EAAC,UAAU;MAACC,UAAU,EAAElB;IAAW,CAAE;EAAC,CACjD,CAAC;AAAA,CACH,CAClB,CAAC;AAEFG,uBAAuB,CAACuB,WAAW,GAAG,yBAAyB;AAE/D,MAAMhB,MAAM,GAAGrB,UAAU,CAACsC,MAAM,CAAC;EAC/BhB,IAAI,EAAE;IACJiB,SAAS,EAAElC,aAAa,CAAC,QAAQ,CAAC;IAClCmC,YAAY,EAAEnC,aAAa,CAAC,SAAS;EACvC;AACF,CAAC,CAAC","ignoreList":[]}
|
|
@@ -3,7 +3,14 @@
|
|
|
3
3
|
import OpenIMSDK, { ViewType } from '@droppii/openim-rn-client-sdk';
|
|
4
4
|
import { useConversationStore } from "../store/index.js";
|
|
5
5
|
import { getReceiverId } from "../utils/message.js";
|
|
6
|
+
import { perfMark } from "../utils/perf-marker.js";
|
|
7
|
+
import { withTimeout } from "../utils/promise.js";
|
|
6
8
|
const DEFAULT_PAGE_SIZE = 20;
|
|
9
|
+
|
|
10
|
+
// Rendering never waits on this, only the read receipt does, while a timeout
|
|
11
|
+
// loses the indicator for good. Tune from the measured p95 of
|
|
12
|
+
// `unread_anchor_response.ms` before release.
|
|
13
|
+
export const FIRST_UNREAD_TIMEOUT_MS = 5000;
|
|
7
14
|
export let ChatMessageAPI;
|
|
8
15
|
(function (_ChatMessageAPI) {
|
|
9
16
|
async function fetchHistoryMessages(conversationID, options) {
|
|
@@ -45,6 +52,29 @@ export let ChatMessageAPI;
|
|
|
45
52
|
return Promise.all([OpenIMSDK.markConversationMessageAsRead(conversationID), OpenIMSDK.resetConversationGroupAtType(conversationID)]);
|
|
46
53
|
}
|
|
47
54
|
_ChatMessageAPI.markConversationAsRead = markConversationAsRead;
|
|
55
|
+
async function getFirstUnreadMessage(conversationID, timeoutMs = FIRST_UNREAD_TIMEOUT_MS) {
|
|
56
|
+
const startedAt = Date.now();
|
|
57
|
+
try {
|
|
58
|
+
perfMark('unread_anchor_request');
|
|
59
|
+
const res = await withTimeout(OpenIMSDK.getFirstUnreadMessage(conversationID), timeoutMs);
|
|
60
|
+
perfMark('unread_anchor_response', {
|
|
61
|
+
found: !!res?.message,
|
|
62
|
+
ms: Date.now() - startedAt
|
|
63
|
+
});
|
|
64
|
+
return res?.message ?? null;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
try {
|
|
67
|
+
perfMark('unread_anchor_response', {
|
|
68
|
+
found: false,
|
|
69
|
+
failed: true,
|
|
70
|
+
ms: Date.now() - startedAt
|
|
71
|
+
});
|
|
72
|
+
} catch {}
|
|
73
|
+
if (__DEV__) console.log('[unread] getFirstUnreadMessage failed', error);
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
_ChatMessageAPI.getFirstUnreadMessage = getFirstUnreadMessage;
|
|
48
78
|
async function getCurrentUserId() {
|
|
49
79
|
return OpenIMSDK.getLoginUserID();
|
|
50
80
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["OpenIMSDK","ViewType","useConversationStore","getReceiverId","DEFAULT_PAGE_SIZE","ChatMessageAPI","_ChatMessageAPI","fetchHistoryMessages","conversationID","options","getAdvancedHistoryMessageListApp","count","startClientMsgID","viewType","History","applicationType","sendTextMessage","params","currentConversation","getState","getCurrentConversation","message","createTextMessage","text","sentMessage","sendMessage","searchLocalMessages","keywordList","messageTypeList","pageIndex","markConversationAsRead","Promise","all","markConversationMessageAsRead","resetConversationGroupAtType","getCurrentUserId","getLoginUserID"],"sourceRoot":"../../../src","sources":["services/message.ts"],"mappings":";;AAAA,OAAOA,SAAS,IACdC,QAAQ,QAGH,+BAA+B;AACtC,SAASC,oBAAoB,QAAQ,mBAAU;AAC/C,SAASC,aAAa,QAAQ,qBAAkB;
|
|
1
|
+
{"version":3,"names":["OpenIMSDK","ViewType","useConversationStore","getReceiverId","perfMark","withTimeout","DEFAULT_PAGE_SIZE","FIRST_UNREAD_TIMEOUT_MS","ChatMessageAPI","_ChatMessageAPI","fetchHistoryMessages","conversationID","options","getAdvancedHistoryMessageListApp","count","startClientMsgID","viewType","History","applicationType","sendTextMessage","params","currentConversation","getState","getCurrentConversation","message","createTextMessage","text","sentMessage","sendMessage","searchLocalMessages","keywordList","messageTypeList","pageIndex","markConversationAsRead","Promise","all","markConversationMessageAsRead","resetConversationGroupAtType","getFirstUnreadMessage","timeoutMs","startedAt","Date","now","res","found","ms","error","failed","__DEV__","console","log","getCurrentUserId","getLoginUserID"],"sourceRoot":"../../../src","sources":["services/message.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -88,6 +88,10 @@ export const useMessageStore = create()((set, get) => ({
|
|
|
88
88
|
set(state => ({
|
|
89
89
|
messages: loadMore ? mergeMessages(state.messages, res.messageList || []) : res.messageList || [],
|
|
90
90
|
hasMoreOld: !res.isEnd,
|
|
91
|
+
// A fresh fetch is the newest page (FAB out of a jump window).
|
|
92
|
+
...(loadMore ? {} : {
|
|
93
|
+
hasMoreNew: false
|
|
94
|
+
}),
|
|
91
95
|
initLoading: false,
|
|
92
96
|
moreOldLoading: false
|
|
93
97
|
}));
|
|
@@ -145,9 +149,12 @@ export const useMessageStore = create()((set, get) => ({
|
|
|
145
149
|
searchMessages: async ({
|
|
146
150
|
applicationType,
|
|
147
151
|
conversationId,
|
|
148
|
-
startClientMsgId
|
|
152
|
+
startClientMsgId,
|
|
153
|
+
scrollOptions
|
|
149
154
|
}) => {
|
|
150
155
|
if (!conversationId || !startClientMsgId) return;
|
|
156
|
+
// Opted out of the toast-and-exit: the caller handles a missing target.
|
|
157
|
+
const keepOnUnavailable = scrollOptions?.exitOnUnavailable === false;
|
|
151
158
|
try {
|
|
152
159
|
set({
|
|
153
160
|
initLoading: true
|
|
@@ -176,20 +183,34 @@ export const useMessageStore = create()((set, get) => ({
|
|
|
176
183
|
if (conversationId === useConversationStore.getState().currentConversationId) {
|
|
177
184
|
const currentMessages = searchMessages?.findResultItems?.find(item => item.conversationID === conversationId)?.messageList || [];
|
|
178
185
|
const newMessages = mergeMessages([...(searchPrevRes?.messageList || []), ...currentMessages, ...(searchNextRes?.messageList || [])], []);
|
|
186
|
+
const targetMessage = newMessages.find(msg => msg.clientMsgID === startClientMsgId);
|
|
187
|
+
|
|
188
|
+
// Not in the local DB: keep the current window rather than swapping
|
|
189
|
+
// in one around a target that isn't there.
|
|
190
|
+
if (!targetMessage && keepOnUnavailable) {
|
|
191
|
+
set({
|
|
192
|
+
initLoading: false
|
|
193
|
+
});
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
179
196
|
set({
|
|
180
197
|
messages: newMessages,
|
|
181
198
|
hasMoreOld: !searchPrevRes.isEnd,
|
|
182
199
|
hasMoreNew: !searchNextRes.isEnd,
|
|
183
200
|
initLoading: false
|
|
184
201
|
});
|
|
185
|
-
const targetMessage = newMessages.find(msg => msg.clientMsgID === startClientMsgId);
|
|
186
202
|
if (!targetMessage || isMessageRevoked(targetMessage)) {
|
|
187
|
-
|
|
203
|
+
if (!keepOnUnavailable) {
|
|
204
|
+
DeviceEventEmitter.emit(ChatEvent.TargetMessageUnavailable);
|
|
205
|
+
}
|
|
188
206
|
return;
|
|
189
207
|
}
|
|
208
|
+
if (scrollOptions?.reemitAfterSearch === false) return;
|
|
190
209
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
191
210
|
DeviceEventEmitter.emit(ChatEvent.ScrollToMessage, {
|
|
192
|
-
|
|
211
|
+
...scrollOptions,
|
|
212
|
+
clientMessageId: startClientMsgId,
|
|
213
|
+
fromSearch: true
|
|
193
214
|
});
|
|
194
215
|
}
|
|
195
216
|
} catch (error) {
|