@droppii-org/chat-mobile 0.2.106 → 0.2.108
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/components/ThreadCard/ThreadCard.js +2 -0
- package/lib/module/components/ThreadCard/ThreadCard.js.map +1 -1
- package/lib/module/components/messages/ExpandableMessageText.js +112 -0
- package/lib/module/components/messages/ExpandableMessageText.js.map +1 -0
- package/lib/module/components/messages/PreviewScrollableText.js +80 -0
- package/lib/module/components/messages/PreviewScrollableText.js.map +1 -0
- package/lib/module/components/messages/fileMessage/index.js +9 -2
- package/lib/module/components/messages/fileMessage/index.js.map +1 -1
- package/lib/module/components/messages/linkMessage/index.js +21 -12
- package/lib/module/components/messages/linkMessage/index.js.map +1 -1
- package/lib/module/components/messages/quotedMessage/index.js +18 -3
- package/lib/module/components/messages/quotedMessage/index.js.map +1 -1
- package/lib/module/components/messages/textMessage/index.js +7 -1
- package/lib/module/components/messages/textMessage/index.js.map +1 -1
- package/lib/module/components/messages/types.js.map +1 -1
- package/lib/module/context/TimelinePressGuardContext.js +14 -0
- package/lib/module/context/TimelinePressGuardContext.js.map +1 -0
- package/lib/module/hooks/useConversationList.js +4 -0
- package/lib/module/hooks/useConversationList.js.map +1 -1
- package/lib/module/hooks/useDismissKeyboardFirst.js +19 -0
- package/lib/module/hooks/useDismissKeyboardFirst.js.map +1 -0
- package/lib/module/hooks/useExpandableText.js +125 -0
- package/lib/module/hooks/useExpandableText.js.map +1 -0
- package/lib/module/hooks/useGuardedPress.js +19 -0
- package/lib/module/hooks/useGuardedPress.js.map +1 -0
- package/lib/module/screens/chat-detail/ChatComposer.js +89 -498
- package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatDetail.js +49 -20
- package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatListLegend.js +113 -76
- package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
- package/lib/module/screens/chat-detail/PinnedMessagesSheet.js +7 -0
- package/lib/module/screens/chat-detail/PinnedMessagesSheet.js.map +1 -1
- package/lib/module/screens/chat-detail/components/ChatComposerAccessorySlot.js +47 -0
- package/lib/module/screens/chat-detail/components/ChatComposerAccessorySlot.js.map +1 -0
- package/lib/module/screens/chat-detail/components/ChatComposerInputArea.js +149 -0
- package/lib/module/screens/chat-detail/components/ChatComposerInputArea.js.map +1 -0
- package/lib/module/screens/chat-detail/components/ChatComposerStatusBanner.js +104 -0
- package/lib/module/screens/chat-detail/components/ChatComposerStatusBanner.js.map +1 -0
- package/lib/module/screens/chat-detail/hooks/useChatComposerAnimation.js +0 -17
- package/lib/module/screens/chat-detail/hooks/useChatComposerAnimation.js.map +1 -1
- package/lib/module/screens/chat-detail/hooks/useChatComposerState.js +16 -2
- package/lib/module/screens/chat-detail/hooks/useChatComposerState.js.map +1 -1
- package/lib/module/screens/chat-detail/hooks/useComposerBotMenu.js +122 -0
- package/lib/module/screens/chat-detail/hooks/useComposerBotMenu.js.map +1 -0
- package/lib/module/screens/chat-detail/hooks/useComposerPanelActions.js +46 -0
- package/lib/module/screens/chat-detail/hooks/useComposerPanelActions.js.map +1 -0
- package/lib/module/screens/chat-detail/hooks/useComposerPanelDismiss.js +75 -0
- package/lib/module/screens/chat-detail/hooks/useComposerPanelDismiss.js.map +1 -0
- package/lib/module/screens/chat-detail/hooks/useComposerPanels.js +136 -0
- package/lib/module/screens/chat-detail/hooks/useComposerPanels.js.map +1 -0
- package/lib/module/screens/chat-detail/hooks/useComposerSend.js +74 -0
- package/lib/module/screens/chat-detail/hooks/useComposerSend.js.map +1 -0
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +12 -3
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -1
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxOverlay.js +11 -2
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxOverlay.js.map +1 -1
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxWrapper.js +79 -6
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxWrapper.js.map +1 -1
- package/lib/module/screens/chat-detail/messageToolbox/useClonePreview.js +30 -0
- package/lib/module/screens/chat-detail/messageToolbox/useClonePreview.js.map +1 -0
- package/lib/module/screens/chat-detail/types.js +5 -0
- package/lib/module/screens/chat-detail/types.js.map +1 -1
- package/lib/module/store/expandedMessage.js +28 -0
- package/lib/module/store/expandedMessage.js.map +1 -0
- package/lib/module/store/message.js +14 -0
- package/lib/module/store/message.js.map +1 -1
- package/lib/module/utils/composerPanelDismissGesture.js +52 -0
- package/lib/module/utils/composerPanelDismissGesture.js.map +1 -0
- package/lib/module/utils/expandableText.js +41 -0
- package/lib/module/utils/expandableText.js.map +1 -0
- package/lib/module/utils/inlineTruncation.js +51 -0
- package/lib/module/utils/inlineTruncation.js.map +1 -0
- package/lib/module/utils/perf-marker.js +44 -0
- package/lib/module/utils/perf-marker.js.map +1 -0
- package/lib/module/utils/truncateTextChildren.js +38 -0
- package/lib/module/utils/truncateTextChildren.js.map +1 -0
- package/lib/typescript/src/components/ThreadCard/ThreadCard.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/ExpandableMessageText.d.ts +25 -0
- package/lib/typescript/src/components/messages/ExpandableMessageText.d.ts.map +1 -0
- package/lib/typescript/src/components/messages/PreviewScrollableText.d.ts +29 -0
- package/lib/typescript/src/components/messages/PreviewScrollableText.d.ts.map +1 -0
- package/lib/typescript/src/components/messages/fileMessage/index.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/linkMessage/index.d.ts +1 -1
- package/lib/typescript/src/components/messages/linkMessage/index.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/quotedMessage/index.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/textMessage/index.d.ts +1 -1
- package/lib/typescript/src/components/messages/textMessage/index.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/types.d.ts +18 -0
- package/lib/typescript/src/components/messages/types.d.ts.map +1 -1
- package/lib/typescript/src/context/TimelinePressGuardContext.d.ts +3 -0
- package/lib/typescript/src/context/TimelinePressGuardContext.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useConversationList.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useDismissKeyboardFirst.d.ts +8 -0
- package/lib/typescript/src/hooks/useDismissKeyboardFirst.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useExpandableText.d.ts +13 -0
- package/lib/typescript/src/hooks/useExpandableText.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useGuardedPress.d.ts +9 -0
- package/lib/typescript/src/hooks/useGuardedPress.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts +7 -3
- package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts +5 -1
- package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/PinnedMessagesSheet.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/components/ChatComposerAccessorySlot.d.ts +12 -0
- package/lib/typescript/src/screens/chat-detail/components/ChatComposerAccessorySlot.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/components/ChatComposerInputArea.d.ts +30 -0
- package/lib/typescript/src/screens/chat-detail/components/ChatComposerInputArea.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/components/ChatComposerStatusBanner.d.ts +19 -0
- package/lib/typescript/src/screens/chat-detail/components/ChatComposerStatusBanner.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerAnimation.d.ts +0 -1
- package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerAnimation.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerState.d.ts +7 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerState.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/hooks/useComposerBotMenu.d.ts +7 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useComposerBotMenu.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useComposerPanelActions.d.ts +17 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useComposerPanelActions.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useComposerPanelDismiss.d.ts +12 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useComposerPanelDismiss.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useComposerPanels.d.ts +25 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useComposerPanels.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useComposerSend.d.ts +16 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useComposerSend.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts +11 -1
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxOverlay.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.d.ts +14 -1
- package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/messageToolbox/useClonePreview.d.ts +18 -0
- package/lib/typescript/src/screens/chat-detail/messageToolbox/useClonePreview.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/types.d.ts +8 -0
- package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
- package/lib/typescript/src/store/expandedMessage.d.ts +15 -0
- package/lib/typescript/src/store/expandedMessage.d.ts.map +1 -0
- package/lib/typescript/src/store/message.d.ts.map +1 -1
- package/lib/typescript/src/utils/composerPanelDismissGesture.d.ts +33 -0
- package/lib/typescript/src/utils/composerPanelDismissGesture.d.ts.map +1 -0
- package/lib/typescript/src/utils/expandableText.d.ts +18 -0
- package/lib/typescript/src/utils/expandableText.d.ts.map +1 -0
- package/lib/typescript/src/utils/inlineTruncation.d.ts +24 -0
- package/lib/typescript/src/utils/inlineTruncation.d.ts.map +1 -0
- package/lib/typescript/src/utils/perf-marker.d.ts +8 -0
- package/lib/typescript/src/utils/perf-marker.d.ts.map +1 -0
- package/lib/typescript/src/utils/truncateTextChildren.d.ts +3 -0
- package/lib/typescript/src/utils/truncateTextChildren.d.ts.map +1 -0
- package/package.json +2 -1
- package/src/components/ThreadCard/ThreadCard.tsx +2 -0
- package/src/components/messages/ExpandableMessageText.tsx +145 -0
- package/src/components/messages/PreviewScrollableText.tsx +98 -0
- package/src/components/messages/fileMessage/index.tsx +17 -3
- package/src/components/messages/linkMessage/index.tsx +30 -21
- package/src/components/messages/quotedMessage/index.tsx +19 -3
- package/src/components/messages/textMessage/index.tsx +11 -2
- package/src/components/messages/types.ts +18 -0
- package/src/context/TimelinePressGuardContext.tsx +15 -0
- package/src/hooks/useConversationList.ts +2 -0
- package/src/hooks/useDismissKeyboardFirst.ts +19 -0
- package/src/hooks/useExpandableText.ts +143 -0
- package/src/hooks/useGuardedPress.ts +20 -0
- package/src/screens/chat-detail/ChatComposer.tsx +105 -631
- package/src/screens/chat-detail/ChatDetail.tsx +49 -20
- package/src/screens/chat-detail/ChatListLegend.tsx +122 -71
- package/src/screens/chat-detail/PinnedMessagesSheet.tsx +8 -0
- package/src/screens/chat-detail/components/ChatComposerAccessorySlot.tsx +61 -0
- package/src/screens/chat-detail/components/ChatComposerInputArea.tsx +198 -0
- package/src/screens/chat-detail/components/ChatComposerStatusBanner.tsx +116 -0
- package/src/screens/chat-detail/hooks/useChatComposerAnimation.ts +0 -29
- package/src/screens/chat-detail/hooks/useChatComposerState.ts +25 -2
- package/src/screens/chat-detail/hooks/useComposerBotMenu.tsx +140 -0
- package/src/screens/chat-detail/hooks/useComposerPanelActions.ts +57 -0
- package/src/screens/chat-detail/hooks/useComposerPanelDismiss.ts +111 -0
- package/src/screens/chat-detail/hooks/useComposerPanels.ts +197 -0
- package/src/screens/chat-detail/hooks/useComposerSend.ts +98 -0
- package/src/screens/chat-detail/legend/LegendChatMessage.tsx +24 -2
- package/src/screens/chat-detail/messageToolbox/MessageToolboxOverlay.tsx +20 -1
- package/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.tsx +111 -5
- package/src/screens/chat-detail/messageToolbox/useClonePreview.ts +34 -0
- package/src/screens/chat-detail/types.ts +9 -0
- package/src/store/expandedMessage.ts +42 -0
- package/src/store/message.ts +14 -0
- package/src/utils/composerPanelDismissGesture.ts +72 -0
- package/src/utils/expandableText.ts +51 -0
- package/src/utils/inlineTruncation.ts +65 -0
- package/src/utils/perf-marker.ts +39 -0
- package/src/utils/truncateTextChildren.ts +46 -0
|
@@ -18,9 +18,13 @@ import {
|
|
|
18
18
|
useConversationStore,
|
|
19
19
|
useUserStore,
|
|
20
20
|
} from '../../store';
|
|
21
|
+
import { useExpandedMessageStore } from '../../store/expandedMessage';
|
|
22
|
+
import { clearExpandableTextCache } from '../../hooks/useExpandableText';
|
|
21
23
|
import ChatDetailHeader from './ChatDetailHeader';
|
|
22
24
|
import { ChatListLegend } from './ChatListLegend';
|
|
23
25
|
import { ChatComposer } from './ChatComposer';
|
|
26
|
+
import { useComposerPanelDismiss } from './hooks/useComposerPanelDismiss';
|
|
27
|
+
import { TimelinePressGuardProvider } from '../../context/TimelinePressGuardContext';
|
|
24
28
|
import { shouldShowAddMember } from './conversationHeader.utils';
|
|
25
29
|
import type { ChatDetailProps } from './types';
|
|
26
30
|
import { normalizeUrl } from '../../utils/url';
|
|
@@ -81,6 +85,15 @@ const ChatDetail = memo(
|
|
|
81
85
|
|
|
82
86
|
const conversation = useConversation(conversationId);
|
|
83
87
|
|
|
88
|
+
// Keyed on the conversation, not on unmount: this screen is mounted by the
|
|
89
|
+
// consumer's navigator, so whether it remounts per conversation or is
|
|
90
|
+
// reused with a new id is not knowable here. Reopening a conversation must
|
|
91
|
+
// start every message shortened again.
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
useExpandedMessageStore.getState().clearSurface('timeline');
|
|
94
|
+
clearExpandableTextCache();
|
|
95
|
+
}, [conversationId]);
|
|
96
|
+
|
|
84
97
|
const { data: target } = useGetConversationTarget(conversationId);
|
|
85
98
|
const isComposeDisabled = !!target && !target.isActive;
|
|
86
99
|
|
|
@@ -180,6 +193,15 @@ const ChatDetail = memo(
|
|
|
180
193
|
[onPressUrl]
|
|
181
194
|
);
|
|
182
195
|
|
|
196
|
+
const {
|
|
197
|
+
panelControllerRef,
|
|
198
|
+
isComposerPanelOpen,
|
|
199
|
+
onComposerPanelOpenChange,
|
|
200
|
+
dismissAreaTouchProps,
|
|
201
|
+
notifyScrollBeginDrag,
|
|
202
|
+
shouldSwallowPress,
|
|
203
|
+
} = useComposerPanelDismiss();
|
|
204
|
+
|
|
183
205
|
const handleMessageLongPress = useCallback<MessageToolboxShowFn>(
|
|
184
206
|
(message, rect, groupPosition) => {
|
|
185
207
|
messageToolboxRef.current?.show(message, rect, groupPosition);
|
|
@@ -238,26 +260,31 @@ const ChatDetail = memo(
|
|
|
238
260
|
onPress={handlePressPinnedMessageBar}
|
|
239
261
|
/>
|
|
240
262
|
|
|
241
|
-
<
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
263
|
+
<TimelinePressGuardProvider value={shouldSwallowPress}>
|
|
264
|
+
<ChatListLegend
|
|
265
|
+
messages={messages}
|
|
266
|
+
conversationId={conversationId}
|
|
267
|
+
customMessageItemFactory={customMessageItemFactory}
|
|
268
|
+
isLoading={initLoading}
|
|
269
|
+
onLoadMoreOld={getMoreOldMessages}
|
|
270
|
+
hasMoreOld={hasMoreOld}
|
|
271
|
+
moreOldLoading={moreOldLoading}
|
|
272
|
+
onLoadMoreNew={getMoreNewMessages}
|
|
273
|
+
hasMoreNew={hasMoreNew}
|
|
274
|
+
moreNewLoading={moreNewLoading}
|
|
275
|
+
onMediaPress={handleMediaPress}
|
|
276
|
+
onPressUrl={handlePressUrl}
|
|
277
|
+
onLongPress={handleMessageLongPress}
|
|
278
|
+
listRef={listRef}
|
|
279
|
+
freeze={freeze}
|
|
280
|
+
scrollMessageToEnd={scrollMessageToEnd}
|
|
281
|
+
keyboardOffset={insets.bottom}
|
|
282
|
+
renderConversationViewHeader={renderConversationViewHeader}
|
|
283
|
+
dismissAreaTouchProps={dismissAreaTouchProps}
|
|
284
|
+
isComposerPanelOpen={isComposerPanelOpen}
|
|
285
|
+
onDismissAreaScrollBeginDrag={notifyScrollBeginDrag}
|
|
286
|
+
/>
|
|
287
|
+
</TimelinePressGuardProvider>
|
|
261
288
|
|
|
262
289
|
<ChatComposer
|
|
263
290
|
isEmptyMessage={messages?.length === 0}
|
|
@@ -265,6 +292,8 @@ const ChatDetail = memo(
|
|
|
265
292
|
isComposeDisabled={isComposeDisabled}
|
|
266
293
|
onPressJoinGroup={handlePressJoinGroup}
|
|
267
294
|
isJoiningGroup={isJoiningGroup}
|
|
295
|
+
panelControllerRef={panelControllerRef}
|
|
296
|
+
onPanelOpenChange={onComposerPanelOpenChange}
|
|
268
297
|
/>
|
|
269
298
|
|
|
270
299
|
<MediaViewerModal ref={mediaViewerRef} />
|
|
@@ -69,6 +69,11 @@ import ChatImageDimens, {
|
|
|
69
69
|
CHAT_IMAGE_VIEW_SOURCE,
|
|
70
70
|
} from '../../utils/chatImageDimens';
|
|
71
71
|
import { ChatTypingIndicator } from './ChatTypingIndicator';
|
|
72
|
+
import type { DismissAreaTouchProps } from './hooks/useComposerPanelDismiss';
|
|
73
|
+
import { useGuardedPress } from '../../hooks/useGuardedPress';
|
|
74
|
+
import { useDismissKeyboardFirst } from '../../hooks/useDismissKeyboardFirst';
|
|
75
|
+
import { useTimelinePressGuard } from '../../context/TimelinePressGuardContext';
|
|
76
|
+
import { perfMark } from '../../utils/perf-marker';
|
|
72
77
|
|
|
73
78
|
// Cluster spacing: a wide gap after the message that closes a cluster, a tight
|
|
74
79
|
// one between messages inside it. These override the flat margin on the row
|
|
@@ -135,6 +140,12 @@ interface ChatListLegendInternalProps extends ChatListProps {
|
|
|
135
140
|
closeKeyboard: boolean;
|
|
136
141
|
}) => Promise<void>;
|
|
137
142
|
keyboardOffset: number;
|
|
143
|
+
// Tap-outside dismiss of the composer panel (useComposerPanelDismiss).
|
|
144
|
+
// Internal — not part of the public ChatListProps.
|
|
145
|
+
dismissAreaTouchProps?: DismissAreaTouchProps;
|
|
146
|
+
// While true the content is non-hittable: taps only dismiss the panel.
|
|
147
|
+
isComposerPanelOpen?: boolean;
|
|
148
|
+
onDismissAreaScrollBeginDrag?: () => void;
|
|
138
149
|
}
|
|
139
150
|
|
|
140
151
|
export const ChatListLegend = memo(
|
|
@@ -147,8 +158,8 @@ export const ChatListLegend = memo(
|
|
|
147
158
|
hasMoreOld,
|
|
148
159
|
onLoadMoreNew,
|
|
149
160
|
hasMoreNew,
|
|
150
|
-
onMediaPress,
|
|
151
|
-
onPressUrl,
|
|
161
|
+
onMediaPress: onMediaPressProp,
|
|
162
|
+
onPressUrl: onPressUrlProp,
|
|
152
163
|
onLongPress,
|
|
153
164
|
listRef,
|
|
154
165
|
freeze,
|
|
@@ -156,6 +167,9 @@ export const ChatListLegend = memo(
|
|
|
156
167
|
keyboardOffset,
|
|
157
168
|
isLoading,
|
|
158
169
|
renderConversationViewHeader,
|
|
170
|
+
dismissAreaTouchProps,
|
|
171
|
+
isComposerPanelOpen = false,
|
|
172
|
+
onDismissAreaScrollBeginDrag,
|
|
159
173
|
}: ChatListLegendInternalProps) => {
|
|
160
174
|
const currentUserId = useUserStore((s) => s.user?.userID);
|
|
161
175
|
const windowWidth = useWindowDimensions().width;
|
|
@@ -177,7 +191,19 @@ export const ChatListLegend = memo(
|
|
|
177
191
|
[messages, isBotConversation, currentUserId]
|
|
178
192
|
);
|
|
179
193
|
|
|
180
|
-
|
|
194
|
+
// True for presses of a touch that began with the panel open — covers the
|
|
195
|
+
// render gap before `isComposerPanelOpen` reaches the list.
|
|
196
|
+
const shouldSwallowPress = useTimelinePressGuard();
|
|
197
|
+
const { retryMessage: retryMessageRaw } = useSendMessage();
|
|
198
|
+
const retryMessage = useGuardedPress(retryMessageRaw, shouldSwallowPress);
|
|
199
|
+
const onMediaPress = useGuardedPress(
|
|
200
|
+
useDismissKeyboardFirst(onMediaPressProp),
|
|
201
|
+
shouldSwallowPress
|
|
202
|
+
);
|
|
203
|
+
const onPressUrl = useGuardedPress(
|
|
204
|
+
useDismissKeyboardFirst(onPressUrlProp),
|
|
205
|
+
shouldSwallowPress
|
|
206
|
+
);
|
|
181
207
|
|
|
182
208
|
const lastMessageId = useMemo(() => {
|
|
183
209
|
const last = messages[messages.length - 1];
|
|
@@ -267,6 +293,7 @@ export const ChatListLegend = memo(
|
|
|
267
293
|
|
|
268
294
|
const handleMessageLongPress = useCallback(
|
|
269
295
|
(messageId: string, message: MessageItem) => {
|
|
296
|
+
if (shouldSwallowPress()) return;
|
|
270
297
|
const node = rowRefsMap.current.get(messageId);
|
|
271
298
|
node?.measureInWindow((x, y, width, height) => {
|
|
272
299
|
if (x == null || y == null || width == null || height == null) return;
|
|
@@ -274,23 +301,29 @@ export const ChatListLegend = memo(
|
|
|
274
301
|
onLongPress?.(message, { x, y, width, height }, groupPosition);
|
|
275
302
|
});
|
|
276
303
|
},
|
|
277
|
-
[onLongPress, messageDataMap]
|
|
304
|
+
[onLongPress, messageDataMap, shouldSwallowPress]
|
|
278
305
|
);
|
|
279
306
|
|
|
280
307
|
const onRunFlow = useCallback(
|
|
281
308
|
(payload: IRunConversationFlowPayload) => {
|
|
309
|
+
if (shouldSwallowPress()) return;
|
|
282
310
|
runFlow(payload);
|
|
283
311
|
},
|
|
284
|
-
[runFlow]
|
|
312
|
+
[runFlow, shouldSwallowPress]
|
|
285
313
|
);
|
|
286
314
|
|
|
287
315
|
const onResetFlow = useCallback(() => {
|
|
316
|
+
if (shouldSwallowPress()) return;
|
|
288
317
|
resetFlow();
|
|
289
|
-
}, [resetFlow]);
|
|
318
|
+
}, [resetFlow, shouldSwallowPress]);
|
|
290
319
|
|
|
291
|
-
const handleMessagePress = useCallback(
|
|
292
|
-
|
|
293
|
-
|
|
320
|
+
const handleMessagePress = useCallback(
|
|
321
|
+
(messageId: string) => {
|
|
322
|
+
if (shouldSwallowPress()) return;
|
|
323
|
+
setRevealedMessageId((prev) => (prev === messageId ? null : messageId));
|
|
324
|
+
},
|
|
325
|
+
[shouldSwallowPress]
|
|
326
|
+
);
|
|
294
327
|
|
|
295
328
|
const handleRenderConversationViewHeader = useCallback(() => {
|
|
296
329
|
if (!renderConversationViewHeader) return null;
|
|
@@ -554,6 +587,7 @@ export const ChatListLegend = memo(
|
|
|
554
587
|
// load-more the user never asked for.
|
|
555
588
|
if (!hasUserScrolledRef.current) return;
|
|
556
589
|
if (hasMoreOld && !useMessageStore.getState().moreOldLoading) {
|
|
590
|
+
perfMark('history_start_reached');
|
|
557
591
|
onLoadMoreOld?.(true);
|
|
558
592
|
}
|
|
559
593
|
}, [hasMoreOld, onLoadMoreOld]);
|
|
@@ -567,7 +601,8 @@ export const ChatListLegend = memo(
|
|
|
567
601
|
|
|
568
602
|
const handleScrollBeginDrag = useCallback(() => {
|
|
569
603
|
hasUserScrolledRef.current = true;
|
|
570
|
-
|
|
604
|
+
onDismissAreaScrollBeginDrag?.();
|
|
605
|
+
}, [onDismissAreaScrollBeginDrag]);
|
|
571
606
|
|
|
572
607
|
const handleScroll = useCallback(
|
|
573
608
|
(e: any) => {
|
|
@@ -649,8 +684,11 @@ export const ChatListLegend = memo(
|
|
|
649
684
|
() => [
|
|
650
685
|
styles.content,
|
|
651
686
|
{ paddingBottom: CONTENT_PADDING_VERTICAL + keyboardInset },
|
|
687
|
+
// Composer panel open: bubbles stop receiving touches (a tap only
|
|
688
|
+
// dismisses the panel) while the scroll view itself still scrolls.
|
|
689
|
+
isComposerPanelOpen && styles.contentNotHittable,
|
|
652
690
|
],
|
|
653
|
-
[keyboardInset]
|
|
691
|
+
[keyboardInset, isComposerPanelOpen]
|
|
654
692
|
);
|
|
655
693
|
|
|
656
694
|
const animatedButtonStyle = useAnimatedStyle(() => ({
|
|
@@ -866,6 +904,7 @@ export const ChatListLegend = memo(
|
|
|
866
904
|
const handleListLoad = useCallback(() => {
|
|
867
905
|
if (hasCorrectedInitialPaddingRef.current) return;
|
|
868
906
|
hasCorrectedInitialPaddingRef.current = true;
|
|
907
|
+
perfMark('history_list_loaded');
|
|
869
908
|
|
|
870
909
|
scrollMessageToEnd({ animated: false, closeKeyboard: false });
|
|
871
910
|
}, [scrollMessageToEnd]);
|
|
@@ -942,66 +981,75 @@ export const ChatListLegend = memo(
|
|
|
942
981
|
return (
|
|
943
982
|
<KContainer.View flex background={KColors.white} style={styles.container}>
|
|
944
983
|
{handleRenderConversationViewHeader()}
|
|
945
|
-
<
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
984
|
+
<KContainer.View flex {...dismissAreaTouchProps}>
|
|
985
|
+
<KeyboardAwareLegendList
|
|
986
|
+
ref={listRef}
|
|
987
|
+
data={messages}
|
|
988
|
+
renderItem={renderItem}
|
|
989
|
+
// Deliberate lower bound (not a best-guess average) — see the
|
|
990
|
+
// comment above `hasCorrectedInitialPaddingRef`. A single-line
|
|
991
|
+
// bubble floors out around 38-44px, so this stays safely under it.
|
|
992
|
+
estimatedItemSize={36}
|
|
993
|
+
getItemType={getItemType}
|
|
994
|
+
getFixedItemSize={getFixedItemSize}
|
|
995
|
+
keyExtractor={(item) =>
|
|
996
|
+
String(item.clientMsgID || item.serverMsgID)
|
|
997
|
+
}
|
|
998
|
+
recycleItems
|
|
999
|
+
extraData={listExtraData}
|
|
1000
|
+
alignItemsAtEnd
|
|
1001
|
+
alwaysRender={{
|
|
1002
|
+
top: 10,
|
|
1003
|
+
}}
|
|
1004
|
+
maintainScrollAtEnd
|
|
1005
|
+
maintainVisibleContentPosition={maintainVisiblePositionConfig}
|
|
1006
|
+
// See the comment above `maintainVisiblePositionConfig` — this is
|
|
1007
|
+
// what keeps a history prepend's anchor item mounted through a slow
|
|
1008
|
+
// fetch instead of the default 250px window losing it.
|
|
1009
|
+
drawDistance={800}
|
|
1010
|
+
freeze={freeze}
|
|
1011
|
+
keyboardOffset={DISABLE_NATIVE_KEYBOARD_INSET}
|
|
1012
|
+
// iOS only. Left on, React Native recomputes and applies its own
|
|
1013
|
+
// `contentInset` every time the keyboard moves — measured here as
|
|
1014
|
+
// {top: 59, bottom: keyboardHeight}, both wrong. The bottom value
|
|
1015
|
+
// over-insets by the safe area, and the phantom top one is worse:
|
|
1016
|
+
// iOS rests a scroll view at `-contentInset.top`.
|
|
1017
|
+
// That resting offset is what put the content 59pt low and hid the
|
|
1018
|
+
// last message behind the composer the moment a touch let UIKit
|
|
1019
|
+
// re-clamp. legend-list's own KeyboardAvoidingLegendList disables
|
|
1020
|
+
// this for the same reason.
|
|
1021
|
+
automaticallyAdjustContentInsets={false}
|
|
1022
|
+
// No scroll shift from the library either — the content padding
|
|
1023
|
+
// above is the single compensator. Measured with it on: a 302px
|
|
1024
|
+
// keyboard produced a 604px lift, exactly double.
|
|
1025
|
+
keyboardLiftBehavior="never"
|
|
1026
|
+
// With the default 'never' the first tap while the keyboard is up
|
|
1027
|
+
// only dismisses it, so "Xem thêm", links and buttons don't fire.
|
|
1028
|
+
// 'handled': a touchable consumes the tap and the keyboard stays;
|
|
1029
|
+
// a tap on anything else still dismisses it.
|
|
1030
|
+
keyboardShouldPersistTaps="handled"
|
|
1031
|
+
scrollEventThrottle={16}
|
|
1032
|
+
onScroll={handleScroll}
|
|
1033
|
+
onScrollBeginDrag={handleScrollBeginDrag}
|
|
1034
|
+
onLoad={handleListLoad}
|
|
1035
|
+
contentContainerStyle={contentContainerStyle}
|
|
1036
|
+
scrollIndicatorInsets={{ right: 1 }}
|
|
1037
|
+
ListHeaderComponent={
|
|
1038
|
+
<LegendChatLoadEarlier isLoading={moreOldLoading} />
|
|
1039
|
+
}
|
|
1040
|
+
onStartReached={handleStartReached}
|
|
1041
|
+
// LegendList only re-fires onStartReached after scrolling away from
|
|
1042
|
+
// the threshold by 1.3x and back. At 0.5 that distance exceeded a
|
|
1043
|
+
// short conversation's whole scrollable range, so the mount-time
|
|
1044
|
+
// firing we suppress in handleStartReached "used up" the only
|
|
1045
|
+
// trigger. 0.3 keeps that reset distance safely under a
|
|
1046
|
+
// ~1.5-screen conversation; shorter ones can still hit it.
|
|
1047
|
+
onStartReachedThreshold={0.2}
|
|
1048
|
+
onEndReached={handleEndReached}
|
|
1049
|
+
onEndReachedThreshold={0.2}
|
|
1050
|
+
initialScrollAtEnd
|
|
1051
|
+
/>
|
|
1052
|
+
</KContainer.View>
|
|
1005
1053
|
<Animated.View
|
|
1006
1054
|
style={[styles.scrollToBottomButton, animatedButtonStyle]}
|
|
1007
1055
|
>
|
|
@@ -1033,6 +1081,9 @@ const styles = StyleSheet.create({
|
|
|
1033
1081
|
container: {
|
|
1034
1082
|
position: 'relative',
|
|
1035
1083
|
},
|
|
1084
|
+
contentNotHittable: {
|
|
1085
|
+
pointerEvents: 'none',
|
|
1086
|
+
},
|
|
1036
1087
|
header: {
|
|
1037
1088
|
position: 'absolute',
|
|
1038
1089
|
top: 0,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { UIUtils } from '../../utils/UIUtils';
|
|
13
13
|
import { trans } from '../../translation';
|
|
14
14
|
import { usePinnedMessageStore } from '../../store/pinnedMessage';
|
|
15
|
+
import { useExpandedMessageStore } from '../../store/expandedMessage';
|
|
15
16
|
import { LegendChatMessage } from './legend/LegendChatMessage';
|
|
16
17
|
import { formatMessageTime } from './legend/messageTypes';
|
|
17
18
|
|
|
@@ -122,6 +123,7 @@ const PinnedMessagesSheetContent = memo(
|
|
|
122
123
|
<LegendChatMessage
|
|
123
124
|
message={message}
|
|
124
125
|
messageId={messageId}
|
|
126
|
+
surface="pin"
|
|
125
127
|
isOutgoing={isOutgoing}
|
|
126
128
|
createdAtTime={createdAtTime}
|
|
127
129
|
onPress={handlePressMessage}
|
|
@@ -198,6 +200,12 @@ export const openPinnedMessagesSheet = ({
|
|
|
198
200
|
customMessageItemFactory,
|
|
199
201
|
onPressMessage,
|
|
200
202
|
}: OpenPinnedMessagesSheetParams) => {
|
|
203
|
+
// Reset on open, not on close. `UIUtils.bottomSheet` is an adapter the host
|
|
204
|
+
// app registers, so whether dismissing unmounts this content — or keeps it
|
|
205
|
+
// mounted and hidden — is not ours to know. Clearing here satisfies
|
|
206
|
+
// "reopened starts shortened" whatever the host does with the sheet.
|
|
207
|
+
useExpandedMessageStore.getState().clearSurface('pin');
|
|
208
|
+
|
|
201
209
|
UIUtils.bottomSheet.open({
|
|
202
210
|
header: {
|
|
203
211
|
title: { text: trans('chat:pinned_messages_title') },
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
import Reanimated from 'react-native-reanimated';
|
|
4
|
+
import { KContainer, KColors } from '@droppii/libs';
|
|
5
|
+
|
|
6
|
+
import { ChatAttachmentPanel } from '../ChatAttachmentPanel';
|
|
7
|
+
import { StickerPicker } from '../../../components/StickerPicker';
|
|
8
|
+
import { useSendMessage } from '../../../hooks/message/useSendMessage';
|
|
9
|
+
import type { StickerItem } from '../../../types/sticker';
|
|
10
|
+
import type { ActivePanelType } from '../hooks/useChatComposerState';
|
|
11
|
+
import type { DChatPannelAction } from '../types';
|
|
12
|
+
|
|
13
|
+
interface ChatComposerAccessorySlotProps {
|
|
14
|
+
visible: boolean;
|
|
15
|
+
accessorySlotStyle: any;
|
|
16
|
+
isAttachmentOpen: boolean;
|
|
17
|
+
activePanelType: ActivePanelType;
|
|
18
|
+
pannelActions: DChatPannelAction[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// The keyboard-area slot below the composer that hosts the attachment
|
|
22
|
+
// panel or the sticker picker; its height is driven by useComposerPanels.
|
|
23
|
+
export const ChatComposerAccessorySlot = ({
|
|
24
|
+
visible,
|
|
25
|
+
accessorySlotStyle,
|
|
26
|
+
isAttachmentOpen,
|
|
27
|
+
activePanelType,
|
|
28
|
+
pannelActions,
|
|
29
|
+
}: ChatComposerAccessorySlotProps) => {
|
|
30
|
+
const { sendMessage } = useSendMessage();
|
|
31
|
+
|
|
32
|
+
const handleStickerSelect = useCallback(
|
|
33
|
+
(sticker: StickerItem) => {
|
|
34
|
+
sendMessage({ sticker });
|
|
35
|
+
},
|
|
36
|
+
[sendMessage]
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<KContainer.VisibleView visible={visible}>
|
|
41
|
+
<Reanimated.View
|
|
42
|
+
style={[styles.accessorySlot, accessorySlotStyle]}
|
|
43
|
+
pointerEvents={isAttachmentOpen ? 'auto' : 'none'}
|
|
44
|
+
>
|
|
45
|
+
{activePanelType === 'attachment' && (
|
|
46
|
+
<ChatAttachmentPanel actions={pannelActions} />
|
|
47
|
+
)}
|
|
48
|
+
{activePanelType === 'sticker' && (
|
|
49
|
+
<StickerPicker onSelectSticker={handleStickerSelect} />
|
|
50
|
+
)}
|
|
51
|
+
</Reanimated.View>
|
|
52
|
+
</KContainer.VisibleView>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const styles = StyleSheet.create({
|
|
57
|
+
accessorySlot: {
|
|
58
|
+
overflow: 'hidden',
|
|
59
|
+
backgroundColor: KColors.palette.gray.w25,
|
|
60
|
+
},
|
|
61
|
+
});
|