@droppii-org/chat-mobile 0.2.80 → 0.2.82
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/MentionBadge.js +24 -0
- package/lib/module/components/ThreadCard/MentionBadge.js.map +1 -0
- package/lib/module/components/ThreadCard/ThreadCard.js +5 -1
- package/lib/module/components/ThreadCard/ThreadCard.js.map +1 -1
- package/lib/module/components/messages/linkMessage/LinkPreviewCard.js +67 -0
- package/lib/module/components/messages/linkMessage/LinkPreviewCard.js.map +1 -0
- package/lib/module/components/messages/linkMessage/index.js +21 -68
- package/lib/module/components/messages/linkMessage/index.js.map +1 -1
- package/lib/module/components/messages/quotedMessage/QuotedMessagePreviewStrip.js +152 -0
- package/lib/module/components/messages/quotedMessage/QuotedMessagePreviewStrip.js.map +1 -0
- package/lib/module/components/messages/textMessage/index.js +102 -8
- package/lib/module/components/messages/textMessage/index.js.map +1 -1
- package/lib/module/config/api-endpoints.js +2 -1
- package/lib/module/config/api-endpoints.js.map +1 -1
- package/lib/module/core/useChatListener.js +1 -1
- package/lib/module/core/useChatListener.js.map +1 -1
- package/lib/module/hooks/contacts/useSearchContacts.js +50 -0
- package/lib/module/hooks/contacts/useSearchContacts.js.map +1 -0
- package/lib/module/hooks/message/useSendMessage.js +64 -3
- package/lib/module/hooks/message/useSendMessage.js.map +1 -1
- package/lib/module/hooks/query-keys.js +8 -0
- package/lib/module/hooks/query-keys.js.map +1 -1
- package/lib/module/hooks/useChatCompose.js +3 -2
- package/lib/module/hooks/useChatCompose.js.map +1 -1
- package/lib/module/screens/SearchConversation/ContactResultItem.js +119 -0
- package/lib/module/screens/SearchConversation/ContactResultItem.js.map +1 -0
- package/lib/module/screens/SearchConversation/SearchAllResult.js +26 -20
- package/lib/module/screens/SearchConversation/SearchAllResult.js.map +1 -1
- package/lib/module/screens/SearchConversation/SearchCommunityResult.js +79 -0
- package/lib/module/screens/SearchConversation/SearchCommunityResult.js.map +1 -0
- package/lib/module/screens/SearchConversation/SearchContactsResult.js +117 -0
- package/lib/module/screens/SearchConversation/SearchContactsResult.js.map +1 -0
- package/lib/module/screens/SearchConversation/SearchEmpty.js +2 -0
- package/lib/module/screens/SearchConversation/SearchEmpty.js.map +1 -1
- package/lib/module/screens/SearchConversation/SearchMessagesResult.js +1 -8
- package/lib/module/screens/SearchConversation/SearchMessagesResult.js.map +1 -1
- package/lib/module/screens/SearchConversation/SearchResultItem.js +2 -0
- package/lib/module/screens/SearchConversation/SearchResultItem.js.map +1 -1
- package/lib/module/screens/SearchConversation/TabResult.js +24 -7
- package/lib/module/screens/SearchConversation/TabResult.js.map +1 -1
- package/lib/module/screens/SearchConversation/index.js +6 -2
- package/lib/module/screens/SearchConversation/index.js.map +1 -1
- package/lib/module/screens/SearchConversation/types.js +1 -0
- package/lib/module/screens/SearchConversation/types.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatComposer.js +170 -48
- package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatListLegend.js +1 -0
- package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
- package/lib/module/screens/chat-detail/components/ChatMessageInput.js +23 -2
- package/lib/module/screens/chat-detail/components/ChatMessageInput.js.map +1 -1
- package/lib/module/screens/chat-detail/components/MentionHighlightAdapter.js +51 -0
- package/lib/module/screens/chat-detail/components/MentionHighlightAdapter.js.map +1 -0
- package/lib/module/screens/chat-detail/components/MentionSuggestionItem.js +49 -0
- package/lib/module/screens/chat-detail/components/MentionSuggestionItem.js.map +1 -0
- package/lib/module/screens/chat-detail/components/MentionSuggestionList.js +114 -0
- package/lib/module/screens/chat-detail/components/MentionSuggestionList.js.map +1 -0
- package/lib/module/screens/chat-detail/hooks/useChatComposerState.js +8 -0
- package/lib/module/screens/chat-detail/hooks/useChatComposerState.js.map +1 -1
- package/lib/module/screens/chat-detail/hooks/useMentionComposer.js +251 -0
- package/lib/module/screens/chat-detail/hooks/useMentionComposer.js.map +1 -0
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +5 -1
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -1
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxWrapper.js +11 -1
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxWrapper.js.map +1 -1
- package/lib/module/services/endpoints.js +2 -1
- package/lib/module/services/endpoints.js.map +1 -1
- package/lib/module/services/mentionSearch.js +49 -0
- package/lib/module/services/mentionSearch.js.map +1 -0
- package/lib/module/services/message.js +6 -1
- package/lib/module/services/message.js.map +1 -1
- package/lib/module/translation/resources/i18n.js +11 -0
- package/lib/module/translation/resources/i18n.js.map +1 -1
- package/lib/module/types/chat.js +8 -0
- package/lib/module/types/chat.js.map +1 -1
- package/lib/module/types/mention.js +2 -0
- package/lib/module/types/mention.js.map +1 -0
- package/lib/module/utils/dateTimeUtils.js +10 -0
- package/lib/module/utils/dateTimeUtils.js.map +1 -1
- package/lib/module/utils/highlightSearch.js +6 -2
- package/lib/module/utils/highlightSearch.js.map +1 -1
- package/lib/module/utils/mentionSerializer.js +113 -0
- package/lib/module/utils/mentionSerializer.js.map +1 -0
- package/lib/module/utils/mentions/index.js +7 -0
- package/lib/module/utils/mentions/index.js.map +1 -0
- package/lib/module/utils/mentions/mentionDetector.js +49 -0
- package/lib/module/utils/mentions/mentionDetector.js.map +1 -0
- package/lib/module/utils/mentions/mentionDiff.js +66 -0
- package/lib/module/utils/mentions/mentionDiff.js.map +1 -0
- package/lib/module/utils/mentions/mentionRange.js +188 -0
- package/lib/module/utils/mentions/mentionRange.js.map +1 -0
- package/lib/module/utils/mentions/mentionTokenize.js +50 -0
- package/lib/module/utils/mentions/mentionTokenize.js.map +1 -0
- package/lib/module/utils/messageUtils.js +16 -3
- package/lib/module/utils/messageUtils.js.map +1 -1
- package/lib/module/utils/url.js +33 -0
- package/lib/module/utils/url.js.map +1 -1
- package/lib/typescript/src/components/ThreadCard/MentionBadge.d.ts +7 -0
- package/lib/typescript/src/components/ThreadCard/MentionBadge.d.ts.map +1 -0
- package/lib/typescript/src/components/ThreadCard/ThreadCard.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/linkMessage/LinkPreviewCard.d.ts +16 -0
- package/lib/typescript/src/components/messages/linkMessage/LinkPreviewCard.d.ts.map +1 -0
- package/lib/typescript/src/components/messages/linkMessage/index.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/quotedMessage/QuotedMessagePreviewStrip.d.ts +13 -0
- package/lib/typescript/src/components/messages/quotedMessage/QuotedMessagePreviewStrip.d.ts.map +1 -0
- package/lib/typescript/src/components/messages/textMessage/index.d.ts +3 -1
- package/lib/typescript/src/components/messages/textMessage/index.d.ts.map +1 -1
- package/lib/typescript/src/config/api-endpoints.d.ts +1 -0
- package/lib/typescript/src/config/api-endpoints.d.ts.map +1 -1
- package/lib/typescript/src/hooks/contacts/useSearchContacts.d.ts +221 -0
- package/lib/typescript/src/hooks/contacts/useSearchContacts.d.ts.map +1 -0
- package/lib/typescript/src/hooks/message/useSendMessage.d.ts +5 -2
- package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
- package/lib/typescript/src/hooks/query-keys.d.ts +8 -0
- package/lib/typescript/src/hooks/query-keys.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useChatCompose.d.ts +2 -1
- package/lib/typescript/src/hooks/useChatCompose.d.ts.map +1 -1
- package/lib/typescript/src/screens/SearchConversation/ContactResultItem.d.ts +9 -0
- package/lib/typescript/src/screens/SearchConversation/ContactResultItem.d.ts.map +1 -0
- package/lib/typescript/src/screens/SearchConversation/SearchAllResult.d.ts +3 -2
- package/lib/typescript/src/screens/SearchConversation/SearchAllResult.d.ts.map +1 -1
- package/lib/typescript/src/screens/SearchConversation/SearchCommunityResult.d.ts +7 -0
- package/lib/typescript/src/screens/SearchConversation/SearchCommunityResult.d.ts.map +1 -0
- package/lib/typescript/src/screens/SearchConversation/SearchContactsResult.d.ts +8 -0
- package/lib/typescript/src/screens/SearchConversation/SearchContactsResult.d.ts.map +1 -0
- package/lib/typescript/src/screens/SearchConversation/SearchEmpty.d.ts.map +1 -1
- package/lib/typescript/src/screens/SearchConversation/SearchMessagesResult.d.ts.map +1 -1
- package/lib/typescript/src/screens/SearchConversation/SearchResultItem.d.ts.map +1 -1
- package/lib/typescript/src/screens/SearchConversation/TabResult.d.ts +4 -2
- package/lib/typescript/src/screens/SearchConversation/TabResult.d.ts.map +1 -1
- package/lib/typescript/src/screens/SearchConversation/index.d.ts +4 -2
- package/lib/typescript/src/screens/SearchConversation/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/SearchConversation/types.d.ts +2 -1
- package/lib/typescript/src/screens/SearchConversation/types.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/components/ChatMessageInput.d.ts +10 -2
- package/lib/typescript/src/screens/chat-detail/components/ChatMessageInput.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/components/MentionHighlightAdapter.d.ts +29 -0
- package/lib/typescript/src/screens/chat-detail/components/MentionHighlightAdapter.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/components/MentionSuggestionItem.d.ts +8 -0
- package/lib/typescript/src/screens/chat-detail/components/MentionSuggestionItem.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/components/MentionSuggestionList.d.ts +12 -0
- package/lib/typescript/src/screens/chat-detail/components/MentionSuggestionList.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerState.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerState.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/hooks/useMentionComposer.d.ts +48 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useMentionComposer.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.d.ts.map +1 -1
- package/lib/typescript/src/services/endpoints.d.ts +1 -0
- package/lib/typescript/src/services/endpoints.d.ts.map +1 -1
- package/lib/typescript/src/services/mentionSearch.d.ts +15 -0
- package/lib/typescript/src/services/mentionSearch.d.ts.map +1 -0
- package/lib/typescript/src/services/message.d.ts +1 -1
- package/lib/typescript/src/services/message.d.ts.map +1 -1
- package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
- package/lib/typescript/src/types/chat.d.ts +50 -0
- package/lib/typescript/src/types/chat.d.ts.map +1 -1
- package/lib/typescript/src/types/mention.d.ts +65 -0
- package/lib/typescript/src/types/mention.d.ts.map +1 -0
- package/lib/typescript/src/utils/dateTimeUtils.d.ts +2 -0
- package/lib/typescript/src/utils/dateTimeUtils.d.ts.map +1 -1
- package/lib/typescript/src/utils/highlightSearch.d.ts.map +1 -1
- package/lib/typescript/src/utils/mentionSerializer.d.ts +78 -0
- package/lib/typescript/src/utils/mentionSerializer.d.ts.map +1 -0
- package/lib/typescript/src/utils/mentions/index.d.ts +5 -0
- package/lib/typescript/src/utils/mentions/index.d.ts.map +1 -0
- package/lib/typescript/src/utils/mentions/mentionDetector.d.ts +18 -0
- package/lib/typescript/src/utils/mentions/mentionDetector.d.ts.map +1 -0
- package/lib/typescript/src/utils/mentions/mentionDiff.d.ts +20 -0
- package/lib/typescript/src/utils/mentions/mentionDiff.d.ts.map +1 -0
- package/lib/typescript/src/utils/mentions/mentionRange.d.ts +86 -0
- package/lib/typescript/src/utils/mentions/mentionRange.d.ts.map +1 -0
- package/lib/typescript/src/utils/mentions/mentionTokenize.d.ts +29 -0
- package/lib/typescript/src/utils/mentions/mentionTokenize.d.ts.map +1 -0
- package/lib/typescript/src/utils/messageUtils.d.ts.map +1 -1
- package/lib/typescript/src/utils/url.d.ts +12 -0
- package/lib/typescript/src/utils/url.d.ts.map +1 -1
- package/package.json +3 -1
- package/src/build-ignore.d.ts +2 -0
- package/src/components/ThreadCard/MentionBadge.tsx +28 -0
- package/src/components/ThreadCard/ThreadCard.tsx +3 -0
- package/src/components/messages/linkMessage/LinkPreviewCard.tsx +92 -0
- package/src/components/messages/linkMessage/index.tsx +25 -89
- package/src/components/messages/quotedMessage/QuotedMessagePreviewStrip.tsx +168 -0
- package/src/components/messages/textMessage/index.tsx +124 -4
- package/src/config/api-endpoints.ts +2 -0
- package/src/core/useChatListener.ts +1 -1
- package/src/hooks/contacts/useSearchContacts.ts +66 -0
- package/src/hooks/message/useSendMessage.ts +60 -1
- package/src/hooks/query-keys.ts +12 -0
- package/src/hooks/useChatCompose.ts +115 -107
- package/src/screens/SearchConversation/ContactResultItem.tsx +128 -0
- package/src/screens/SearchConversation/SearchAllResult.tsx +38 -24
- package/src/screens/SearchConversation/SearchCommunityResult.tsx +96 -0
- package/src/screens/SearchConversation/SearchContactsResult.tsx +155 -0
- package/src/screens/SearchConversation/SearchEmpty.tsx +2 -0
- package/src/screens/SearchConversation/SearchMessagesResult.tsx +1 -6
- package/src/screens/SearchConversation/SearchResultItem.tsx +6 -1
- package/src/screens/SearchConversation/TabResult.tsx +40 -5
- package/src/screens/SearchConversation/index.tsx +10 -2
- package/src/screens/SearchConversation/types.tsx +1 -0
- package/src/screens/chat-detail/ChatComposer.tsx +201 -67
- package/src/screens/chat-detail/ChatListLegend.tsx +1 -0
- package/src/screens/chat-detail/components/ChatMessageInput.tsx +35 -2
- package/src/screens/chat-detail/components/MentionHighlightAdapter.tsx +57 -0
- package/src/screens/chat-detail/components/MentionSuggestionItem.tsx +53 -0
- package/src/screens/chat-detail/components/MentionSuggestionList.tsx +133 -0
- package/src/screens/chat-detail/hooks/useChatComposerState.ts +7 -1
- package/src/screens/chat-detail/hooks/useMentionComposer.ts +344 -0
- package/src/screens/chat-detail/legend/LegendChatMessage.tsx +7 -1
- package/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.tsx +22 -4
- package/src/services/endpoints.ts +1 -0
- package/src/services/mentionSearch.ts +57 -0
- package/src/services/message.ts +9 -1
- package/src/translation/resources/i18n.ts +12 -0
- package/src/types/chat.ts +52 -0
- package/src/types/mention.ts +70 -0
- package/src/utils/dateTimeUtils.ts +15 -0
- package/src/utils/highlightSearch.ts +12 -2
- package/src/utils/mentionSerializer.ts +155 -0
- package/src/utils/mentions/index.ts +16 -0
- package/src/utils/mentions/mentionDetector.ts +58 -0
- package/src/utils/mentions/mentionDiff.ts +95 -0
- package/src/utils/mentions/mentionRange.ts +207 -0
- package/src/utils/mentions/mentionTokenize.ts +53 -0
- package/src/utils/messageUtils.ts +21 -10
- package/src/utils/url.ts +30 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../../../src/utils/url.ts"],"names":[],"mappings":"AA+BA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAElD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,UAKvC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGjD"}
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../../../src/utils/url.ts"],"names":[],"mappings":"AA+BA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAElD;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAgB3E;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,UAKvC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGjD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@droppii-org/chat-mobile",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.82",
|
|
4
4
|
"description": "Droppii chat mobile",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"@types/react-native-snap-carousel": "^3.8.5",
|
|
101
101
|
"@types/react-query": "^1.2.9",
|
|
102
102
|
"commitlint": "^20.5.0",
|
|
103
|
+
"dayjs": "1.11.18",
|
|
103
104
|
"del-cli": "^7.0.0",
|
|
104
105
|
"deprecated-react-native-prop-types": "5.0.0",
|
|
105
106
|
"eslint": "^9.39.4",
|
|
@@ -143,6 +144,7 @@
|
|
|
143
144
|
"@react-native-community/netinfo": "*",
|
|
144
145
|
"@react-native-documents/picker": "*",
|
|
145
146
|
"@tanstack/react-query": "*",
|
|
147
|
+
"dayjs": "*",
|
|
146
148
|
"react": "*",
|
|
147
149
|
"react-native": "*",
|
|
148
150
|
"react-native-blob-util": "*",
|
package/src/build-ignore.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ declare module '@droppii/libs' {
|
|
|
8
8
|
export type KToastBarProps = any;
|
|
9
9
|
export type KListItemBaseItemProps = any;
|
|
10
10
|
export type KSpacing = any;
|
|
11
|
+
export type KTagsChoiceSlideBarItemProps = any;
|
|
11
12
|
|
|
12
13
|
export const KColors: any;
|
|
13
14
|
export const KSpacingValue: any;
|
|
@@ -26,4 +27,5 @@ declare module '@droppii/libs' {
|
|
|
26
27
|
export const KFonts: any;
|
|
27
28
|
export const KBadge: any;
|
|
28
29
|
export const KNavigation: any;
|
|
30
|
+
export const KTagsChoiceSlideBar: any;
|
|
29
31
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { memo } from 'react';
|
|
2
|
+
import { KLabel, KColors } from '@droppii/libs';
|
|
3
|
+
import { GroupAtType } from '@droppii/openim-rn-client-sdk';
|
|
4
|
+
|
|
5
|
+
interface MentionBadgeProps {
|
|
6
|
+
groupAtType?: GroupAtType;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Figma (node 15177-302069): "@" renders as a standalone bold glyph, no
|
|
10
|
+
// background — same color token as the composer/bubble mention highlight
|
|
11
|
+
// (KColors.palette.primary.w400), not a new one-off value.
|
|
12
|
+
export const MentionBadge = memo(({ groupAtType }: MentionBadgeProps) => {
|
|
13
|
+
if (groupAtType === undefined || groupAtType === GroupAtType.AtNormal) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<KLabel.Text
|
|
19
|
+
typo="TextNmBold"
|
|
20
|
+
color={KColors.palette.primary.w400}
|
|
21
|
+
marginL="0.5rem"
|
|
22
|
+
>
|
|
23
|
+
@
|
|
24
|
+
</KLabel.Text>
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
MentionBadge.displayName = 'MentionBadge';
|
|
@@ -6,6 +6,7 @@ import { KContainer, KImage, KLabel, KColors } from '@droppii/libs';
|
|
|
6
6
|
import { AvatarSection } from './AvatarSection';
|
|
7
7
|
import { NamePrefixIcon } from './NamePrefixIcon';
|
|
8
8
|
import { UnreadBadge } from './UnreadBadge';
|
|
9
|
+
import { MentionBadge } from './MentionBadge';
|
|
9
10
|
import { formatTimestamp } from '../../utils/dateTimeUtils';
|
|
10
11
|
import {
|
|
11
12
|
getLastMessageText,
|
|
@@ -44,6 +45,7 @@ const ThreadCard = memo(({ item, onPress }: ThreadCardProps) => {
|
|
|
44
45
|
latestMsgSendTime,
|
|
45
46
|
latestMsg,
|
|
46
47
|
groupID,
|
|
48
|
+
groupAtType,
|
|
47
49
|
} = conversation || {};
|
|
48
50
|
|
|
49
51
|
const { data: isJoinedGroup } = useIsJoinedGroup(groupID);
|
|
@@ -183,6 +185,7 @@ const ThreadCard = memo(({ item, onPress }: ThreadCardProps) => {
|
|
|
183
185
|
/>
|
|
184
186
|
</KContainer.View>
|
|
185
187
|
</KContainer.VisibleView>
|
|
188
|
+
<MentionBadge groupAtType={groupAtType} />
|
|
186
189
|
<UnreadBadge count={unreadCount} />
|
|
187
190
|
</KContainer.View>
|
|
188
191
|
</KContainer.View>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { memo } from 'react';
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
import {
|
|
4
|
+
KContainer,
|
|
5
|
+
KImage,
|
|
6
|
+
KLabel,
|
|
7
|
+
KColors,
|
|
8
|
+
KSpacingValue,
|
|
9
|
+
} from '@droppii/libs';
|
|
10
|
+
import type { IUrlMetadata } from '../../../types/common';
|
|
11
|
+
|
|
12
|
+
interface LinkPreviewCardProps {
|
|
13
|
+
metadata: IUrlMetadata;
|
|
14
|
+
onPressUrl?: (url: string) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The rich link-preview card (thumbnail/title/url/description) — shared by
|
|
19
|
+
* LinkMessage (message.urlTextElem, a plain URL message) and TextMessage
|
|
20
|
+
* (mention+URL combo: message.ex.urlMetadata attached to an AtTextMessage,
|
|
21
|
+
* since OpenIM's AtTextMessage/UrlTextMessage content types are mutually
|
|
22
|
+
* exclusive — see useSendMessage.ts's doc comment for why the mention wins
|
|
23
|
+
* the content type and the URL card rides along in `ex` instead).
|
|
24
|
+
*/
|
|
25
|
+
export const LinkPreviewCard = memo(
|
|
26
|
+
({ metadata, onPressUrl }: LinkPreviewCardProps) => (
|
|
27
|
+
<KContainer.Touchable
|
|
28
|
+
style={styles.card}
|
|
29
|
+
onPress={() => metadata.url && onPressUrl?.(metadata.url)}
|
|
30
|
+
>
|
|
31
|
+
{!!metadata.image && (
|
|
32
|
+
<KContainer.View style={styles.cardThumb}>
|
|
33
|
+
<KImage.Base
|
|
34
|
+
uri={metadata.image}
|
|
35
|
+
style={StyleSheet.absoluteFill}
|
|
36
|
+
resizeMode="cover"
|
|
37
|
+
/>
|
|
38
|
+
</KContainer.View>
|
|
39
|
+
)}
|
|
40
|
+
<KContainer.View style={styles.cardBody}>
|
|
41
|
+
{!!metadata.title && (
|
|
42
|
+
<KLabel.Text
|
|
43
|
+
typo="TextNmMedium"
|
|
44
|
+
color={KColors.palette.gray.w900}
|
|
45
|
+
numberOfLines={1}
|
|
46
|
+
>
|
|
47
|
+
{metadata.title}
|
|
48
|
+
</KLabel.Text>
|
|
49
|
+
)}
|
|
50
|
+
{!!metadata.url && (
|
|
51
|
+
<KLabel.Text
|
|
52
|
+
typo="TextXsMedium"
|
|
53
|
+
color={KColors.palette.primary.w400}
|
|
54
|
+
numberOfLines={1}
|
|
55
|
+
>
|
|
56
|
+
{metadata.url}
|
|
57
|
+
</KLabel.Text>
|
|
58
|
+
)}
|
|
59
|
+
{!!metadata.description && (
|
|
60
|
+
<KLabel.Text
|
|
61
|
+
typo="TextXsNormal"
|
|
62
|
+
color={KColors.gray.normal}
|
|
63
|
+
numberOfLines={2}
|
|
64
|
+
>
|
|
65
|
+
{metadata.description}
|
|
66
|
+
</KLabel.Text>
|
|
67
|
+
)}
|
|
68
|
+
</KContainer.View>
|
|
69
|
+
</KContainer.Touchable>
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
LinkPreviewCard.displayName = 'LinkPreviewCard';
|
|
74
|
+
|
|
75
|
+
const styles = StyleSheet.create({
|
|
76
|
+
card: {
|
|
77
|
+
borderRadius: KSpacingValue['0.75rem'],
|
|
78
|
+
borderWidth: 1,
|
|
79
|
+
borderColor: 'rgba(57,62,64,0.1)',
|
|
80
|
+
backgroundColor: KColors.white,
|
|
81
|
+
overflow: 'hidden',
|
|
82
|
+
},
|
|
83
|
+
cardThumb: {
|
|
84
|
+
width: '100%',
|
|
85
|
+
aspectRatio: 16 / 9,
|
|
86
|
+
},
|
|
87
|
+
cardBody: {
|
|
88
|
+
paddingHorizontal: KSpacingValue['0.75rem'],
|
|
89
|
+
paddingVertical: KSpacingValue['0.5rem'],
|
|
90
|
+
gap: 2,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import { memo } from 'react';
|
|
2
2
|
import { StyleSheet } from 'react-native';
|
|
3
|
-
import {
|
|
4
|
-
KContainer,
|
|
5
|
-
KImage,
|
|
6
|
-
KLabel,
|
|
7
|
-
KColors,
|
|
8
|
-
KSpacingValue,
|
|
9
|
-
} from '@droppii/libs';
|
|
3
|
+
import { KContainer, KLabel, KColors, KSpacingValue } from '@droppii/libs';
|
|
10
4
|
import type { BaseLegendMessageProps } from '../types';
|
|
11
5
|
import { CHAT_BUBBLE_COLORS } from '../../../screens/chat-detail/constants';
|
|
12
6
|
import { parseUrlMetadata } from '../../../utils/messageUtils';
|
|
7
|
+
import { splitTextByUrls } from '../../../utils/url';
|
|
13
8
|
import { messageStyles } from '../styles';
|
|
9
|
+
import { LinkPreviewCard } from './LinkPreviewCard';
|
|
14
10
|
import { getTailCornerStyle } from '../bubbleTail.utils';
|
|
15
11
|
import { BubbleTailRow } from '../BubbleTailRow';
|
|
16
12
|
|
|
@@ -18,45 +14,26 @@ const renderTextWithLinks = (
|
|
|
18
14
|
content: string,
|
|
19
15
|
urls: string[],
|
|
20
16
|
onPressUrl?: (url: string) => void
|
|
21
|
-
) =>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
{parts.map((part, i) =>
|
|
42
|
-
part.isUrl ? (
|
|
43
|
-
<KLabel.Text
|
|
44
|
-
key={i}
|
|
45
|
-
onPress={() => onPressUrl?.(part.text)}
|
|
46
|
-
color={KColors.primary.normal}
|
|
47
|
-
typo="TextMdMedium"
|
|
48
|
-
>
|
|
49
|
-
{part.text}
|
|
50
|
-
</KLabel.Text>
|
|
51
|
-
) : (
|
|
52
|
-
<KLabel.Text typo="TextMdNormal" key={i}>
|
|
53
|
-
{part.text}
|
|
54
|
-
</KLabel.Text>
|
|
55
|
-
)
|
|
56
|
-
)}
|
|
57
|
-
</KLabel.Text>
|
|
58
|
-
);
|
|
59
|
-
};
|
|
17
|
+
) => (
|
|
18
|
+
<KLabel.Text typo="TextMdNormal">
|
|
19
|
+
{splitTextByUrls(content, urls).map((part, i) =>
|
|
20
|
+
part.isUrl ? (
|
|
21
|
+
<KLabel.Text
|
|
22
|
+
key={i}
|
|
23
|
+
onPress={() => onPressUrl?.(part.text)}
|
|
24
|
+
color={KColors.primary.normal}
|
|
25
|
+
typo="TextMdMedium"
|
|
26
|
+
>
|
|
27
|
+
{part.text}
|
|
28
|
+
</KLabel.Text>
|
|
29
|
+
) : (
|
|
30
|
+
<KLabel.Text typo="TextMdNormal" key={i}>
|
|
31
|
+
{part.text}
|
|
32
|
+
</KLabel.Text>
|
|
33
|
+
)
|
|
34
|
+
)}
|
|
35
|
+
</KLabel.Text>
|
|
36
|
+
);
|
|
60
37
|
|
|
61
38
|
export const LinkMessage = memo(
|
|
62
39
|
({
|
|
@@ -87,49 +64,7 @@ export const LinkMessage = memo(
|
|
|
87
64
|
)}
|
|
88
65
|
|
|
89
66
|
{!!metadata && (
|
|
90
|
-
<
|
|
91
|
-
style={styles.card}
|
|
92
|
-
onPress={() => metadata.url && onPressUrl?.(metadata.url)}
|
|
93
|
-
>
|
|
94
|
-
{!!metadata.image && (
|
|
95
|
-
<KContainer.View style={styles.cardThumb}>
|
|
96
|
-
<KImage.Base
|
|
97
|
-
uri={metadata.image}
|
|
98
|
-
style={StyleSheet.absoluteFill}
|
|
99
|
-
resizeMode="cover"
|
|
100
|
-
/>
|
|
101
|
-
</KContainer.View>
|
|
102
|
-
)}
|
|
103
|
-
<KContainer.View style={styles.cardBody}>
|
|
104
|
-
{!!metadata.title && (
|
|
105
|
-
<KLabel.Text
|
|
106
|
-
typo="TextNmMedium"
|
|
107
|
-
color={KColors.palette.gray.w900}
|
|
108
|
-
numberOfLines={1}
|
|
109
|
-
>
|
|
110
|
-
{metadata.title}
|
|
111
|
-
</KLabel.Text>
|
|
112
|
-
)}
|
|
113
|
-
{!!metadata.url && (
|
|
114
|
-
<KLabel.Text
|
|
115
|
-
typo="TextXsMedium"
|
|
116
|
-
color={KColors.palette.primary.w400}
|
|
117
|
-
numberOfLines={1}
|
|
118
|
-
>
|
|
119
|
-
{metadata.url}
|
|
120
|
-
</KLabel.Text>
|
|
121
|
-
)}
|
|
122
|
-
{!!metadata.description && (
|
|
123
|
-
<KLabel.Text
|
|
124
|
-
typo="TextXsNormal"
|
|
125
|
-
color={KColors.gray.normal}
|
|
126
|
-
numberOfLines={2}
|
|
127
|
-
>
|
|
128
|
-
{metadata.description}
|
|
129
|
-
</KLabel.Text>
|
|
130
|
-
)}
|
|
131
|
-
</KContainer.View>
|
|
132
|
-
</KContainer.Touchable>
|
|
67
|
+
<LinkPreviewCard metadata={metadata} onPressUrl={onPressUrl} />
|
|
133
68
|
)}
|
|
134
69
|
</KContainer.View>
|
|
135
70
|
</BubbleTailRow>
|
|
@@ -157,6 +92,7 @@ const styles = StyleSheet.create({
|
|
|
157
92
|
textRow: {
|
|
158
93
|
paddingHorizontal: KSpacingValue['0.25rem'],
|
|
159
94
|
},
|
|
95
|
+
timeLabel: { gap: 4, marginTop: 2 },
|
|
160
96
|
card: {
|
|
161
97
|
borderRadius: KSpacingValue['0.75rem'],
|
|
162
98
|
borderWidth: 1,
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { memo, useCallback, useMemo } from 'react';
|
|
2
|
+
import { DeviceEventEmitter, StyleSheet } from 'react-native';
|
|
3
|
+
import {
|
|
4
|
+
KColors,
|
|
5
|
+
KContainer,
|
|
6
|
+
KImage,
|
|
7
|
+
KLabel,
|
|
8
|
+
KRadiusValue,
|
|
9
|
+
KSpacingValue,
|
|
10
|
+
} from '@droppii/libs';
|
|
11
|
+
import { MessageType, type MessageItem } from '@droppii/openim-rn-client-sdk';
|
|
12
|
+
import {
|
|
13
|
+
getMessagePreviewParts,
|
|
14
|
+
getReplySenderName,
|
|
15
|
+
parseUrlMetadata,
|
|
16
|
+
} from '../../../utils/messageUtils';
|
|
17
|
+
import { getFileIcon, getMimeType } from '../../../utils/imageUtils';
|
|
18
|
+
import { useUserStore } from '../../../store';
|
|
19
|
+
import { useChatDetailContext } from '../../../context';
|
|
20
|
+
import { ChatEvent } from '../../../types/events';
|
|
21
|
+
|
|
22
|
+
interface QuotedMessagePreviewStripProps {
|
|
23
|
+
quotedMessage?: MessageItem;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The "replying to ..." strip — shared by QuotedMessage (message.quoteElem)
|
|
28
|
+
* and TextMessage (message.atTextElem?.quoteMessage, for the mention+quote
|
|
29
|
+
* combo). Extracted so both content types render the exact same preview
|
|
30
|
+
* instead of two copies drifting apart.
|
|
31
|
+
*/
|
|
32
|
+
export const QuotedMessagePreviewStrip = memo(
|
|
33
|
+
({ quotedMessage }: QuotedMessagePreviewStripProps) => {
|
|
34
|
+
const { conversation } = useChatDetailContext();
|
|
35
|
+
const currentUserId = useUserStore((s) => s.user?.userID);
|
|
36
|
+
const messagePreview = getMessagePreviewParts(quotedMessage);
|
|
37
|
+
|
|
38
|
+
const imageUrl = useMemo(() => {
|
|
39
|
+
switch (quotedMessage?.contentType) {
|
|
40
|
+
case MessageType.PictureMessage:
|
|
41
|
+
return quotedMessage?.pictureElem?.snapshotPicture?.url || '';
|
|
42
|
+
case MessageType.VideoMessage:
|
|
43
|
+
return quotedMessage?.videoElem?.snapshotUrl || '';
|
|
44
|
+
case MessageType.UrlTextMessage: {
|
|
45
|
+
const metadata = parseUrlMetadata(quotedMessage?.ex);
|
|
46
|
+
return metadata?.image || '';
|
|
47
|
+
}
|
|
48
|
+
default:
|
|
49
|
+
return '';
|
|
50
|
+
}
|
|
51
|
+
}, [quotedMessage]);
|
|
52
|
+
|
|
53
|
+
const iconSource = useMemo(() => {
|
|
54
|
+
if (quotedMessage?.contentType !== MessageType.FileMessage) return '';
|
|
55
|
+
const mimeType = getMimeType(quotedMessage?.fileElem?.fileName || '');
|
|
56
|
+
return getFileIcon(mimeType);
|
|
57
|
+
}, [quotedMessage]);
|
|
58
|
+
|
|
59
|
+
const handlePress = useCallback(() => {
|
|
60
|
+
DeviceEventEmitter.emit(ChatEvent.ScrollToMessage, {
|
|
61
|
+
clientMessageId: quotedMessage?.clientMsgID || '',
|
|
62
|
+
});
|
|
63
|
+
}, [quotedMessage]);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<KContainer.Touchable
|
|
67
|
+
style={styles.quotedContainer}
|
|
68
|
+
onPress={handlePress}
|
|
69
|
+
>
|
|
70
|
+
<KContainer.View style={styles.dot} />
|
|
71
|
+
<KContainer.VisibleView visible={!!imageUrl}>
|
|
72
|
+
<KContainer.View style={styles.imageContainer}>
|
|
73
|
+
<KImage.Base uri={imageUrl} style={styles.imageContainer} />
|
|
74
|
+
<KContainer.VisibleView
|
|
75
|
+
visible={quotedMessage?.contentType === MessageType.VideoMessage}
|
|
76
|
+
>
|
|
77
|
+
<KContainer.View style={styles.playIconContainer}>
|
|
78
|
+
<KContainer.View style={styles.playIcon}>
|
|
79
|
+
<KImage.VectorIcons
|
|
80
|
+
name="play-b"
|
|
81
|
+
size={12}
|
|
82
|
+
color={KColors.white}
|
|
83
|
+
/>
|
|
84
|
+
</KContainer.View>
|
|
85
|
+
</KContainer.View>
|
|
86
|
+
</KContainer.VisibleView>
|
|
87
|
+
</KContainer.View>
|
|
88
|
+
</KContainer.VisibleView>
|
|
89
|
+
<KContainer.VisibleView visible={!!iconSource}>
|
|
90
|
+
<KImage.Base
|
|
91
|
+
uri={iconSource}
|
|
92
|
+
style={styles.icon}
|
|
93
|
+
resizeMode="contain"
|
|
94
|
+
/>
|
|
95
|
+
</KContainer.VisibleView>
|
|
96
|
+
<KContainer.View flexS={1} style={styles.content}>
|
|
97
|
+
<KLabel.Text typo="TextNmMedium" numberOfLines={1}>
|
|
98
|
+
{getReplySenderName(
|
|
99
|
+
quotedMessage,
|
|
100
|
+
currentUserId,
|
|
101
|
+
conversation,
|
|
102
|
+
true
|
|
103
|
+
)}
|
|
104
|
+
</KLabel.Text>
|
|
105
|
+
<KLabel.Text
|
|
106
|
+
typo="TextSmNormal"
|
|
107
|
+
color={KColors.gray.light}
|
|
108
|
+
numberOfLines={1}
|
|
109
|
+
{...messagePreview.textProps}
|
|
110
|
+
>
|
|
111
|
+
{messagePreview.text}
|
|
112
|
+
</KLabel.Text>
|
|
113
|
+
</KContainer.View>
|
|
114
|
+
</KContainer.Touchable>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
QuotedMessagePreviewStrip.displayName = 'QuotedMessagePreviewStrip';
|
|
120
|
+
|
|
121
|
+
const styles = StyleSheet.create({
|
|
122
|
+
quotedContainer: {
|
|
123
|
+
backgroundColor: KColors.white,
|
|
124
|
+
flexDirection: 'row',
|
|
125
|
+
alignItems: 'center',
|
|
126
|
+
gap: KSpacingValue['0.5rem'],
|
|
127
|
+
borderTopRightRadius: KRadiusValue['3x'],
|
|
128
|
+
borderBottomRightRadius: KRadiusValue['3x'],
|
|
129
|
+
paddingRight: KSpacingValue['0.5rem'],
|
|
130
|
+
maxWidth: '100%',
|
|
131
|
+
},
|
|
132
|
+
dot: {
|
|
133
|
+
width: 3,
|
|
134
|
+
height: '100%',
|
|
135
|
+
backgroundColor: KColors.palette.grayCool.w200,
|
|
136
|
+
borderRadius: KRadiusValue.x,
|
|
137
|
+
marginRight: -2,
|
|
138
|
+
},
|
|
139
|
+
content: {
|
|
140
|
+
paddingHorizontal: KSpacingValue['0.25rem'],
|
|
141
|
+
paddingVertical: KSpacingValue['0.25rem'],
|
|
142
|
+
},
|
|
143
|
+
icon: {
|
|
144
|
+
width: 24,
|
|
145
|
+
height: 24,
|
|
146
|
+
},
|
|
147
|
+
imageContainer: {
|
|
148
|
+
width: 42,
|
|
149
|
+
height: 42,
|
|
150
|
+
borderRadius: KRadiusValue['2x'],
|
|
151
|
+
},
|
|
152
|
+
playIconContainer: {
|
|
153
|
+
position: 'absolute',
|
|
154
|
+
top: 0,
|
|
155
|
+
left: 0,
|
|
156
|
+
right: 0,
|
|
157
|
+
bottom: 0,
|
|
158
|
+
justifyContent: 'center',
|
|
159
|
+
alignItems: 'center',
|
|
160
|
+
},
|
|
161
|
+
playIcon: {
|
|
162
|
+
borderRadius: 8,
|
|
163
|
+
padding: KSpacingValue['0.25rem'],
|
|
164
|
+
backgroundColor: KColors.hexToRgba(KColors.black, 0.6),
|
|
165
|
+
justifyContent: 'center',
|
|
166
|
+
alignItems: 'center',
|
|
167
|
+
},
|
|
168
|
+
});
|
|
@@ -1,29 +1,116 @@
|
|
|
1
|
-
import { memo } from 'react';
|
|
2
|
-
import {
|
|
1
|
+
import { memo, useMemo } from 'react';
|
|
2
|
+
import { Text } from 'react-native';
|
|
3
|
+
import { MessageType } from '@droppii/openim-rn-client-sdk';
|
|
4
|
+
import { KContainer, KLabel, KColors } from '@droppii/libs';
|
|
3
5
|
import { getMessageText } from '../../../utils/legendListMessage';
|
|
4
6
|
import { CHAT_BUBBLE_COLORS } from '../../../screens/chat-detail/constants';
|
|
5
7
|
import { messageStyles } from '../styles';
|
|
6
8
|
import { getTailCornerStyle } from '../bubbleTail.utils';
|
|
7
9
|
import { BubbleTailRow } from '../BubbleTailRow';
|
|
8
10
|
import type { BaseLegendMessageProps } from '../types';
|
|
11
|
+
import { StyleSheet } from 'react-native';
|
|
12
|
+
import { tokenize } from '../../../utils/mentions';
|
|
13
|
+
import { deserializeMentionRanges } from '../../../utils/mentionSerializer';
|
|
14
|
+
import type { IMessageItemEx } from '../../../types/chat';
|
|
15
|
+
import { QuotedMessagePreviewStrip } from '../quotedMessage/QuotedMessagePreviewStrip';
|
|
16
|
+
import { LinkPreviewCard } from '../linkMessage/LinkPreviewCard';
|
|
17
|
+
import { extractUrls, splitTextByUrls } from '../../../utils/url';
|
|
18
|
+
|
|
19
|
+
const parseMessageEx = (ex?: string): IMessageItemEx | undefined => {
|
|
20
|
+
if (!ex) return undefined;
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(ex) as IMessageItemEx;
|
|
23
|
+
} catch {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
9
28
|
export const TextMessage = memo(
|
|
10
29
|
({
|
|
11
30
|
message,
|
|
12
31
|
isOutgoing,
|
|
13
32
|
isHighlighted,
|
|
33
|
+
onPressUrl,
|
|
14
34
|
groupPosition,
|
|
15
|
-
}: BaseLegendMessageProps) => {
|
|
35
|
+
}: BaseLegendMessageProps & { onPressUrl?: (url: string) => void }) => {
|
|
16
36
|
const messageText = getMessageText(message);
|
|
17
37
|
|
|
38
|
+
const isAtTextMessage = message.contentType === MessageType.AtTextMessage;
|
|
39
|
+
|
|
40
|
+
// Both the reply-preview strip and the link-preview card below piggyback
|
|
41
|
+
// on an AtTextMessage instead of getting their own content type — OpenIM
|
|
42
|
+
// content types are mutually exclusive (AtText vs Quote vs UrlText), so
|
|
43
|
+
// once mention picks AtText (to keep native tag/notify semantics — see
|
|
44
|
+
// useSendMessage.ts), quote rides along via the native
|
|
45
|
+
// AtTextElem.quoteMessage field and the URL card rides along via our own
|
|
46
|
+
// ex.urlMetadata channel (already attached unconditionally on send).
|
|
47
|
+
const quotedMessage = isAtTextMessage
|
|
48
|
+
? message.atTextElem?.quoteMessage
|
|
49
|
+
: undefined;
|
|
50
|
+
|
|
51
|
+
const parsedEx = useMemo(
|
|
52
|
+
() => (isAtTextMessage ? parseMessageEx(message.ex) : undefined),
|
|
53
|
+
[isAtTextMessage, message.ex]
|
|
54
|
+
);
|
|
55
|
+
const urlMetadata = parsedEx?.urlMetadata;
|
|
56
|
+
|
|
57
|
+
const mentions = useMemo(() => {
|
|
58
|
+
if (!isAtTextMessage || !message.atTextElem) return [];
|
|
59
|
+
// Wrapped under `offsets` — matches the wire shape other Droppii
|
|
60
|
+
// clients (e.g. the web/CRM app) already send (see
|
|
61
|
+
// IMessageItemEx.mentions doc comment for how this was confirmed).
|
|
62
|
+
return deserializeMentionRanges(
|
|
63
|
+
message.atTextElem,
|
|
64
|
+
parsedEx?.mentions?.offsets
|
|
65
|
+
);
|
|
66
|
+
}, [isAtTextMessage, message.atTextElem, parsedEx]);
|
|
67
|
+
|
|
68
|
+
const segments = useMemo(
|
|
69
|
+
() => (mentions.length > 0 ? tokenize(messageText, mentions) : null),
|
|
70
|
+
[mentions, messageText]
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Scoped to the mention+URL combo only (`segments` implies mentions are
|
|
74
|
+
// present) — a plain TextMessage with no mention falls through to
|
|
75
|
+
// `messageText` unchanged below, same as before this feature existed.
|
|
76
|
+
// Widening this to auto-linkify every TextMessage is a separate,
|
|
77
|
+
// deliberate product decision, not a side effect of this fix.
|
|
78
|
+
const urls = useMemo(
|
|
79
|
+
() => (segments ? extractUrls(messageText) : []),
|
|
80
|
+
[segments, messageText]
|
|
81
|
+
);
|
|
82
|
+
|
|
18
83
|
if (!messageText?.trim()) {
|
|
19
84
|
return null;
|
|
20
85
|
}
|
|
21
86
|
|
|
87
|
+
// A plain-text run (the space between two mentions) may itself contain
|
|
88
|
+
// a URL — split further so it gets the same tappable/colored treatment
|
|
89
|
+
// LinkMessage gives a pure URL message, instead of sitting there as
|
|
90
|
+
// unstyled black text next to a colored mention.
|
|
91
|
+
const renderTextRun = (text: string, keyPrefix: string) => {
|
|
92
|
+
if (!urls.length) return <Text key={keyPrefix}>{text}</Text>;
|
|
93
|
+
return splitTextByUrls(text, urls).map((part, i) =>
|
|
94
|
+
part.isUrl ? (
|
|
95
|
+
<Text
|
|
96
|
+
key={`${keyPrefix}-url-${i}`}
|
|
97
|
+
style={urlTextStyle}
|
|
98
|
+
onPress={() => onPressUrl?.(part.text)}
|
|
99
|
+
>
|
|
100
|
+
{part.text}
|
|
101
|
+
</Text>
|
|
102
|
+
) : (
|
|
103
|
+
<Text key={`${keyPrefix}-text-${i}`}>{part.text}</Text>
|
|
104
|
+
)
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
22
108
|
return (
|
|
23
109
|
<BubbleTailRow isOutgoing={isOutgoing} groupPosition={groupPosition}>
|
|
24
110
|
<KContainer.View
|
|
25
111
|
style={[
|
|
26
112
|
messageStyles.bubble,
|
|
113
|
+
styles.bubbleContent,
|
|
27
114
|
isOutgoing
|
|
28
115
|
? messageStyles.bubbleSent
|
|
29
116
|
: messageStyles.bubbleReceived,
|
|
@@ -31,9 +118,25 @@ export const TextMessage = memo(
|
|
|
31
118
|
getTailCornerStyle(isOutgoing, groupPosition),
|
|
32
119
|
]}
|
|
33
120
|
>
|
|
121
|
+
{quotedMessage && (
|
|
122
|
+
<QuotedMessagePreviewStrip quotedMessage={quotedMessage} />
|
|
123
|
+
)}
|
|
34
124
|
<KLabel.Text typo="TextMdNormal" color={CHAT_BUBBLE_COLORS.text}>
|
|
35
|
-
{
|
|
125
|
+
{segments
|
|
126
|
+
? segments.map((segment, index) =>
|
|
127
|
+
segment.type === 'mention' ? (
|
|
128
|
+
<Text key={segment.mention.id} style={mentionTextStyle}>
|
|
129
|
+
{`@${segment.mention.displayName}`}
|
|
130
|
+
</Text>
|
|
131
|
+
) : (
|
|
132
|
+
renderTextRun(segment.text, `seg-${index}`)
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
: messageText}
|
|
36
136
|
</KLabel.Text>
|
|
137
|
+
{!!urlMetadata && (
|
|
138
|
+
<LinkPreviewCard metadata={urlMetadata} onPressUrl={onPressUrl} />
|
|
139
|
+
)}
|
|
37
140
|
</KContainer.View>
|
|
38
141
|
</BubbleTailRow>
|
|
39
142
|
);
|
|
@@ -41,3 +144,20 @@ export const TextMessage = memo(
|
|
|
41
144
|
);
|
|
42
145
|
|
|
43
146
|
TextMessage.displayName = 'TextMessage';
|
|
147
|
+
|
|
148
|
+
const styles = StyleSheet.create({
|
|
149
|
+
bubbleContent: { gap: 4 },
|
|
150
|
+
timeLabel: { gap: 4, marginTop: 2 },
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Same highlight color as the composer (MentionHighlightAdapter) and the
|
|
154
|
+
// Figma screenshots. This is a read-only <Text>, not an editable
|
|
155
|
+
// TextInput, so none of the nested-Text-in-TextInput bug class applies —
|
|
156
|
+
// nesting styled Text here is unconditionally safe.
|
|
157
|
+
const mentionTextStyle = { color: KColors.palette.primary.w400 };
|
|
158
|
+
|
|
159
|
+
// Same color LinkMessage uses for a URL substring — a mention message's
|
|
160
|
+
// plain-text run can contain a URL too (mention+URL combo, see
|
|
161
|
+
// useSendMessage.ts's doc comment), and it should look tappable/colored
|
|
162
|
+
// the same way there as it does in a pure UrlTextMessage.
|
|
163
|
+
const urlTextStyle = { color: KColors.primary.normal };
|
|
@@ -22,6 +22,7 @@ export interface ApiEndpoints {
|
|
|
22
22
|
stickerHistory: string;
|
|
23
23
|
messagesResources: string;
|
|
24
24
|
messages: string;
|
|
25
|
+
contacts: string;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
const DEFAULT_ENDPOINTS: ApiEndpoints = {
|
|
@@ -35,6 +36,7 @@ const DEFAULT_ENDPOINTS: ApiEndpoints = {
|
|
|
35
36
|
stickerHistory: '/chat-service/v1/app/stickers/history',
|
|
36
37
|
messagesResources: '/chat-service/v1/app/messages/resources',
|
|
37
38
|
messages: '/chat-service/v1/app/messages',
|
|
39
|
+
contacts: '/chat-service/v1/app/contacts',
|
|
38
40
|
};
|
|
39
41
|
|
|
40
42
|
let endpoints: ApiEndpoints = { ...DEFAULT_ENDPOINTS };
|