@droppii-org/chat-mobile 0.2.79 → 0.2.81
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/messages/BubbleTail.js +31 -0
- package/lib/module/components/messages/BubbleTail.js.map +1 -0
- package/lib/module/components/messages/BubbleTailRow.js +46 -0
- package/lib/module/components/messages/BubbleTailRow.js.map +1 -0
- package/lib/module/components/messages/bubbleTail.utils.js +19 -0
- package/lib/module/components/messages/bubbleTail.utils.js.map +1 -0
- package/lib/module/components/messages/fileMessage/index.js +72 -65
- package/lib/module/components/messages/fileMessage/index.js.map +1 -1
- package/lib/module/components/messages/imageMessage/index.js +23 -17
- package/lib/module/components/messages/imageMessage/index.js.map +1 -1
- package/lib/module/components/messages/linkMessage/index.js +11 -23
- package/lib/module/components/messages/linkMessage/index.js.map +1 -1
- package/lib/module/components/messages/mergedMessage/index.js +14 -7
- package/lib/module/components/messages/mergedMessage/index.js.map +1 -1
- package/lib/module/components/messages/quotedMessage/index.js +11 -23
- package/lib/module/components/messages/quotedMessage/index.js.map +1 -1
- package/lib/module/components/messages/revokedMessage/index.js +14 -7
- package/lib/module/components/messages/revokedMessage/index.js.map +1 -1
- package/lib/module/components/messages/stickerMessage/index.js +13 -7
- package/lib/module/components/messages/stickerMessage/index.js.map +1 -1
- package/lib/module/components/messages/textMessage/index.js +11 -26
- package/lib/module/components/messages/textMessage/index.js.map +1 -1
- package/lib/module/components/messages/types.js.map +1 -1
- package/lib/module/components/messages/videoMessage/index.js +16 -8
- package/lib/module/components/messages/videoMessage/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/hooks/contacts/useSearchContacts.js +50 -0
- package/lib/module/hooks/contacts/useSearchContacts.js.map +1 -0
- package/lib/module/hooks/groups/useDismissGroup.js +2 -1
- package/lib/module/hooks/groups/useDismissGroup.js.map +1 -1
- package/lib/module/hooks/query-keys.js +4 -0
- package/lib/module/hooks/query-keys.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/ChatListLegend.js +71 -21
- package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
- package/lib/module/screens/chat-detail/legend/LegendChatDay.js +8 -2
- package/lib/module/screens/chat-detail/legend/LegendChatDay.js.map +1 -1
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +38 -13
- 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/translation/resources/i18n.js +6 -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/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/legendListMessage.js +86 -12
- package/lib/module/utils/legendListMessage.js.map +1 -1
- package/lib/module/utils/message.js +19 -1
- package/lib/module/utils/message.js.map +1 -1
- package/lib/module/utils/messageUtils.js +16 -2
- package/lib/module/utils/messageUtils.js.map +1 -1
- package/lib/typescript/src/components/messages/BubbleTail.d.ts +13 -0
- package/lib/typescript/src/components/messages/BubbleTail.d.ts.map +1 -0
- package/lib/typescript/src/components/messages/BubbleTailRow.d.ts +25 -0
- package/lib/typescript/src/components/messages/BubbleTailRow.d.ts.map +1 -0
- package/lib/typescript/src/components/messages/bubbleTail.utils.d.ts +11 -0
- package/lib/typescript/src/components/messages/bubbleTail.utils.d.ts.map +1 -0
- package/lib/typescript/src/components/messages/fileMessage/index.d.ts +1 -1
- package/lib/typescript/src/components/messages/fileMessage/index.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/imageMessage/index.d.ts +1 -1
- package/lib/typescript/src/components/messages/imageMessage/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/mergedMessage/index.d.ts +1 -1
- package/lib/typescript/src/components/messages/mergedMessage/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/revokedMessage/index.d.ts +1 -1
- package/lib/typescript/src/components/messages/revokedMessage/index.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/stickerMessage/index.d.ts +1 -1
- package/lib/typescript/src/components/messages/stickerMessage/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 +3 -0
- package/lib/typescript/src/components/messages/types.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/videoMessage/index.d.ts +1 -1
- package/lib/typescript/src/components/messages/videoMessage/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/groups/useDismissGroup.d.ts.map +1 -1
- package/lib/typescript/src/hooks/query-keys.d.ts +4 -0
- package/lib/typescript/src/hooks/query-keys.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/ChatListLegend.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatDay.d.ts +6 -1
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatDay.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts +14 -1
- 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/translation/resources/i18n.d.ts.map +1 -1
- package/lib/typescript/src/types/chat.d.ts +27 -0
- package/lib/typescript/src/types/chat.d.ts.map +1 -1
- 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/legendListMessage.d.ts +19 -4
- package/lib/typescript/src/utils/legendListMessage.d.ts.map +1 -1
- package/lib/typescript/src/utils/message.d.ts +17 -0
- package/lib/typescript/src/utils/message.d.ts.map +1 -1
- package/lib/typescript/src/utils/messageUtils.d.ts +1 -0
- package/lib/typescript/src/utils/messageUtils.d.ts.map +1 -1
- package/package.json +3 -1
- package/src/build-ignore.d.ts +2 -0
- package/src/components/messages/BubbleTail.tsx +35 -0
- package/src/components/messages/BubbleTailRow.tsx +64 -0
- package/src/components/messages/bubbleTail.utils.ts +21 -0
- package/src/components/messages/fileMessage/index.tsx +90 -83
- package/src/components/messages/imageMessage/index.tsx +26 -23
- package/src/components/messages/linkMessage/index.tsx +6 -18
- package/src/components/messages/mergedMessage/index.tsx +27 -15
- package/src/components/messages/quotedMessage/index.tsx +7 -19
- package/src/components/messages/revokedMessage/index.tsx +23 -14
- package/src/components/messages/stickerMessage/index.tsx +16 -11
- package/src/components/messages/textMessage/index.tsx +6 -22
- package/src/components/messages/types.ts +3 -0
- package/src/components/messages/videoMessage/index.tsx +13 -10
- package/src/config/api-endpoints.ts +2 -0
- package/src/hooks/contacts/useSearchContacts.ts +66 -0
- package/src/hooks/groups/useDismissGroup.tsx +1 -0
- package/src/hooks/query-keys.ts +6 -0
- 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/ChatListLegend.tsx +97 -28
- package/src/screens/chat-detail/legend/LegendChatDay.tsx +25 -15
- package/src/screens/chat-detail/legend/LegendChatMessage.tsx +62 -12
- package/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.tsx +22 -4
- package/src/services/endpoints.ts +1 -0
- package/src/translation/resources/i18n.ts +6 -0
- package/src/types/chat.ts +30 -0
- package/src/utils/dateTimeUtils.ts +15 -0
- package/src/utils/highlightSearch.ts +12 -2
- package/src/utils/legendListMessage.ts +123 -14
- package/src/utils/message.ts +19 -1
- package/src/utils/messageUtils.ts +23 -9
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KColors, KContainer,
|
|
1
|
+
import { KColors, KContainer, KSpacingValue } from '@droppii/libs';
|
|
2
2
|
import { memo, useCallback } from 'react';
|
|
3
3
|
import { useSearchMessage } from '../../hooks/message/useSearchMessage';
|
|
4
4
|
import { useChatContext } from '../../context';
|
|
@@ -52,10 +52,6 @@ const SearchMessagesResult = memo(
|
|
|
52
52
|
}
|
|
53
53
|
}, [hasNextPage, isFetchingNextPage, fetchNextPage]);
|
|
54
54
|
|
|
55
|
-
const ItemSeparatorComponent = useCallback(() => {
|
|
56
|
-
return <KDivider size={'hairline'} vertical={false} />;
|
|
57
|
-
}, []);
|
|
58
|
-
|
|
59
55
|
return (
|
|
60
56
|
<KContainer.View flex>
|
|
61
57
|
{isFetching && !isFetchingNextPage && !isRefetching ? (
|
|
@@ -70,7 +66,6 @@ const SearchMessagesResult = memo(
|
|
|
70
66
|
contentContainerStyle={styles.container}
|
|
71
67
|
showsVerticalScrollIndicator={false}
|
|
72
68
|
onEndReached={onEndReached}
|
|
73
|
-
ItemSeparatorComponent={ItemSeparatorComponent}
|
|
74
69
|
ListEmptyComponent={
|
|
75
70
|
<SearchEmpty
|
|
76
71
|
searchTerm={searchTerm}
|
|
@@ -68,7 +68,12 @@ const SearchResultItem = memo((props: ISearchResultItemProps) => {
|
|
|
68
68
|
}, [onPressItem, item?.conversation, item?.chatLog]);
|
|
69
69
|
|
|
70
70
|
return (
|
|
71
|
-
<KContainer.Touchable
|
|
71
|
+
<KContainer.Touchable
|
|
72
|
+
style={styles.container}
|
|
73
|
+
onPress={handlePressItem}
|
|
74
|
+
brBW={1}
|
|
75
|
+
brBC={KColors.border.light}
|
|
76
|
+
>
|
|
72
77
|
<KContainer.View style={styles.leftContainer}>
|
|
73
78
|
<AvatarSection
|
|
74
79
|
avatar={item?.conversation?.faceURL ?? null}
|
|
@@ -12,19 +12,30 @@ import { SearchConversationTabResultType } from './types';
|
|
|
12
12
|
import { trans } from '../../translation';
|
|
13
13
|
import SearchMessagesResult from './SearchMessagesResult';
|
|
14
14
|
import SearchAllResult from './SearchAllResult';
|
|
15
|
-
import
|
|
15
|
+
import SearchContactsResult from './SearchContactsResult';
|
|
16
|
+
import SearchCommunityResult from './SearchCommunityResult';
|
|
17
|
+
import type { IContactSearchItem, ISearchMessageItem } from '../../types/chat';
|
|
16
18
|
|
|
17
19
|
type TabRoute = { key: string; title: string };
|
|
18
20
|
|
|
19
21
|
interface ISearchConversationTabResultProps {
|
|
20
22
|
searchTerm: string;
|
|
21
23
|
onPressItem?: (item: ISearchMessageItem) => void;
|
|
24
|
+
onPressSearchContact?: (item: IContactSearchItem) => void;
|
|
25
|
+
onPressGroup?: (groupId: string) => void;
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
const TabRoutes: TabRoute[] = [
|
|
25
29
|
{ key: SearchConversationTabResultType.All, title: trans('all') },
|
|
26
|
-
|
|
30
|
+
{
|
|
31
|
+
key: SearchConversationTabResultType.Conversations,
|
|
32
|
+
title: trans('contacts'),
|
|
33
|
+
},
|
|
27
34
|
{ key: SearchConversationTabResultType.Messages, title: trans('messages') },
|
|
35
|
+
{
|
|
36
|
+
key: SearchConversationTabResultType.Community,
|
|
37
|
+
title: trans('community'),
|
|
38
|
+
},
|
|
28
39
|
];
|
|
29
40
|
|
|
30
41
|
interface CustomTabBarProps {
|
|
@@ -58,7 +69,12 @@ const CustomTabBar = memo(({ routes, index, onPress }: CustomTabBarProps) => (
|
|
|
58
69
|
));
|
|
59
70
|
|
|
60
71
|
const SearchConversationTabResult = memo(
|
|
61
|
-
({
|
|
72
|
+
({
|
|
73
|
+
searchTerm,
|
|
74
|
+
onPressItem,
|
|
75
|
+
onPressSearchContact,
|
|
76
|
+
onPressGroup,
|
|
77
|
+
}: ISearchConversationTabResultProps) => {
|
|
62
78
|
const [index, setIndex] = useState(0);
|
|
63
79
|
const [routes] = useState<TabRoute[]>(TabRoutes);
|
|
64
80
|
|
|
@@ -78,10 +94,16 @@ const SearchConversationTabResult = memo(
|
|
|
78
94
|
searchTerm={searchTerm}
|
|
79
95
|
handleChangeTab={handleChangeTab}
|
|
80
96
|
onPressItem={onPressItem}
|
|
97
|
+
onPressSearchContact={onPressSearchContact}
|
|
81
98
|
/>
|
|
82
99
|
);
|
|
83
100
|
case SearchConversationTabResultType.Conversations:
|
|
84
|
-
return
|
|
101
|
+
return (
|
|
102
|
+
<SearchContactsResult
|
|
103
|
+
searchTerm={searchTerm}
|
|
104
|
+
onPressSearchContact={onPressSearchContact}
|
|
105
|
+
/>
|
|
106
|
+
);
|
|
85
107
|
case SearchConversationTabResultType.Messages:
|
|
86
108
|
return (
|
|
87
109
|
<SearchMessagesResult
|
|
@@ -89,11 +111,24 @@ const SearchConversationTabResult = memo(
|
|
|
89
111
|
onPressItem={onPressItem}
|
|
90
112
|
/>
|
|
91
113
|
);
|
|
114
|
+
case SearchConversationTabResultType.Community:
|
|
115
|
+
return (
|
|
116
|
+
<SearchCommunityResult
|
|
117
|
+
searchTerm={searchTerm}
|
|
118
|
+
onPressGroup={onPressGroup}
|
|
119
|
+
/>
|
|
120
|
+
);
|
|
92
121
|
default:
|
|
93
122
|
return null;
|
|
94
123
|
}
|
|
95
124
|
},
|
|
96
|
-
[
|
|
125
|
+
[
|
|
126
|
+
searchTerm,
|
|
127
|
+
handleChangeTab,
|
|
128
|
+
onPressItem,
|
|
129
|
+
onPressSearchContact,
|
|
130
|
+
onPressGroup,
|
|
131
|
+
]
|
|
97
132
|
);
|
|
98
133
|
|
|
99
134
|
const renderTabBar = useCallback(
|
|
@@ -5,13 +5,19 @@ import { trans } from '../../translation';
|
|
|
5
5
|
import debounce from 'lodash/debounce';
|
|
6
6
|
import type { TextInput } from 'react-native';
|
|
7
7
|
import SearchConversationTabResult from './TabResult';
|
|
8
|
-
import type { ISearchMessageItem } from '../../types/chat';
|
|
8
|
+
import type { IContactSearchItem, ISearchMessageItem } from '../../types/chat';
|
|
9
9
|
|
|
10
10
|
interface SearchConversationScreenProps {
|
|
11
11
|
onPressItem?: (item: ISearchMessageItem) => void;
|
|
12
|
+
onPressSearchContact?: (item: IContactSearchItem) => void;
|
|
13
|
+
onPressGroup?: (groupId: string) => void;
|
|
12
14
|
}
|
|
13
15
|
const SearchConversationScreen = memo(
|
|
14
|
-
({
|
|
16
|
+
({
|
|
17
|
+
onPressItem,
|
|
18
|
+
onPressSearchContact,
|
|
19
|
+
onPressGroup,
|
|
20
|
+
}: SearchConversationScreenProps) => {
|
|
15
21
|
const nav = useNavigation();
|
|
16
22
|
const [inputValue, setInputValue] = useState('');
|
|
17
23
|
const [searchTerm, setSearchTerm] = useState('');
|
|
@@ -62,6 +68,8 @@ const SearchConversationScreen = memo(
|
|
|
62
68
|
<SearchConversationTabResult
|
|
63
69
|
searchTerm={searchTerm}
|
|
64
70
|
onPressItem={onPressItem}
|
|
71
|
+
onPressSearchContact={onPressSearchContact}
|
|
72
|
+
onPressGroup={onPressGroup}
|
|
65
73
|
/>
|
|
66
74
|
</KContainer.Page>
|
|
67
75
|
);
|
|
@@ -32,7 +32,11 @@ import {
|
|
|
32
32
|
KRadiusValue,
|
|
33
33
|
KImage,
|
|
34
34
|
} from '@droppii/libs';
|
|
35
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
precomputeLegendListData,
|
|
37
|
+
type GroupPosition,
|
|
38
|
+
} from '../../utils/legendListMessage';
|
|
39
|
+
import { getMessageRenderId } from '../../utils/message';
|
|
36
40
|
import type { ChatListProps } from './types';
|
|
37
41
|
import { LegendChatMessage } from './legend/LegendChatMessage';
|
|
38
42
|
import { LegendChatDay } from './legend/LegendChatDay';
|
|
@@ -50,7 +54,7 @@ import { ChatEvent, type ScrollToMessagePayload } from '../../types/events';
|
|
|
50
54
|
import { useMessageStore } from '../../store/message';
|
|
51
55
|
import { useChatContext } from '../../context';
|
|
52
56
|
import { messageStyles } from './legend/messageTypes';
|
|
53
|
-
import { MessageType } from '@droppii/openim-rn-client-sdk';
|
|
57
|
+
import { MessageType, PeerType } from '@droppii/openim-rn-client-sdk';
|
|
54
58
|
import type { MessageItem } from '@droppii/openim-rn-client-sdk';
|
|
55
59
|
import { useUserStore } from '../../store';
|
|
56
60
|
import { ChatMessageAPI } from '../../services';
|
|
@@ -60,10 +64,19 @@ import ChatImageDimens, {
|
|
|
60
64
|
CHAT_IMAGE_VIEW_SOURCE,
|
|
61
65
|
} from '../../utils/chatImageDimens';
|
|
62
66
|
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
+
// Cluster spacing: a wide gap after the message that closes a cluster, a tight
|
|
68
|
+
// one between messages inside it. These override the flat margin on the row
|
|
69
|
+
// wrapper (`messageRowLeft`/`messageRowRight` in `components/messages/styles.ts`),
|
|
70
|
+
// which adds only a bottom margin and no vertical padding — so a fixed-size
|
|
71
|
+
// item's true rendered height stays `content height + this margin`, and
|
|
72
|
+
// getFixedItemSize can keep folding it in.
|
|
73
|
+
const ROW_MARGIN_CLUSTER_END = KSpacingValue['0.5rem'];
|
|
74
|
+
const ROW_MARGIN_IN_CLUSTER = KSpacingValue['0.25rem'];
|
|
75
|
+
|
|
76
|
+
const getRowMarginBottom = (position: GroupPosition): number =>
|
|
77
|
+
position === 'last' || position === 'solo'
|
|
78
|
+
? ROW_MARGIN_CLUSTER_END
|
|
79
|
+
: ROW_MARGIN_IN_CLUSTER;
|
|
67
80
|
|
|
68
81
|
// KeyboardAwareLegendList auto scroll when modal opened
|
|
69
82
|
// TODO: Convert toolbox to absolute view
|
|
@@ -103,12 +116,23 @@ export const ChatListLegend = memo(
|
|
|
103
116
|
const currentUserId = useUserStore((s) => s.user?.userID);
|
|
104
117
|
const windowWidth = useWindowDimensions().width;
|
|
105
118
|
// Precompute all message data in single pass (O(n))
|
|
119
|
+
const { isBotConversation, conversationFlow, conversation } =
|
|
120
|
+
useChatDetailContext();
|
|
121
|
+
|
|
122
|
+
// Avatar and sender name only appear in real group chats — a bot thread is
|
|
123
|
+
// a Group session too, but reads as a single counterpart.
|
|
124
|
+
const isGroupConversation =
|
|
125
|
+
conversation?.peerType === PeerType.Group && !isBotConversation;
|
|
126
|
+
|
|
106
127
|
const messageDataMap = useMemo(
|
|
107
|
-
() =>
|
|
108
|
-
|
|
128
|
+
() =>
|
|
129
|
+
precomputeLegendListData(messages, {
|
|
130
|
+
isBotConversation,
|
|
131
|
+
currentUserId,
|
|
132
|
+
}),
|
|
133
|
+
[messages, isBotConversation, currentUserId]
|
|
109
134
|
);
|
|
110
135
|
|
|
111
|
-
const { isBotConversation, conversationFlow } = useChatDetailContext();
|
|
112
136
|
const { retryMessage } = useSendMessage();
|
|
113
137
|
|
|
114
138
|
const lastMessageId = useMemo(() => {
|
|
@@ -163,17 +187,22 @@ export const ChatListLegend = memo(
|
|
|
163
187
|
const isLastMessage =
|
|
164
188
|
(message.clientMsgID || message.serverMsgID) === lastMessageId;
|
|
165
189
|
const isLastIncoming = !isOutgoing && isLastMessage;
|
|
166
|
-
const messageId =
|
|
167
|
-
message.clientMsgID ||
|
|
168
|
-
message.serverMsgID ||
|
|
169
|
-
`temp-${message.sendTime || message.createTime}-${index}`;
|
|
190
|
+
const messageId = getMessageRenderId(message, index);
|
|
170
191
|
|
|
171
192
|
const messageData = messageDataMap.get(messageId);
|
|
172
193
|
if (!messageData) {
|
|
173
194
|
return null;
|
|
174
195
|
}
|
|
175
196
|
|
|
176
|
-
const { dayStart, createdAt } = messageData;
|
|
197
|
+
const { dayStart, timeBreak, createdAt, groupPosition } = messageData;
|
|
198
|
+
|
|
199
|
+
const showSenderGutter = isGroupConversation && !isOutgoing;
|
|
200
|
+
const showName =
|
|
201
|
+
showSenderGutter &&
|
|
202
|
+
(groupPosition === 'first' || groupPosition === 'solo');
|
|
203
|
+
const showAvatar =
|
|
204
|
+
showSenderGutter &&
|
|
205
|
+
(groupPosition === 'last' || groupPosition === 'solo');
|
|
177
206
|
|
|
178
207
|
let flowInputButtonsNodes: GroupChild<'input-buttons'>[] = [];
|
|
179
208
|
if (
|
|
@@ -188,17 +217,20 @@ export const ChatListLegend = memo(
|
|
|
188
217
|
|
|
189
218
|
return (
|
|
190
219
|
<>
|
|
191
|
-
{dayStart
|
|
220
|
+
{(dayStart || timeBreak) && (
|
|
221
|
+
<LegendChatDay createdAt={createdAt} timeOnly={!dayStart} />
|
|
222
|
+
)}
|
|
192
223
|
<KContainer.View
|
|
193
224
|
ref={(node: View | null) => {
|
|
194
225
|
if (node) rowRefsMap.current.set(messageId, node);
|
|
195
226
|
else rowRefsMap.current.delete(messageId);
|
|
196
227
|
}}
|
|
197
|
-
style={
|
|
228
|
+
style={[
|
|
198
229
|
isOutgoing
|
|
199
230
|
? messageStyles.messageRowRight
|
|
200
|
-
: messageStyles.messageRowLeft
|
|
201
|
-
|
|
231
|
+
: messageStyles.messageRowLeft,
|
|
232
|
+
{ marginBottom: getRowMarginBottom(groupPosition) },
|
|
233
|
+
]}
|
|
202
234
|
collapsable={false}
|
|
203
235
|
>
|
|
204
236
|
<LegendChatMessage
|
|
@@ -215,6 +247,10 @@ export const ChatListLegend = memo(
|
|
|
215
247
|
}
|
|
216
248
|
customMessageItemFactory={customMessageItemFactory}
|
|
217
249
|
isHighlighted={highlightedId === messageId}
|
|
250
|
+
showName={showName}
|
|
251
|
+
showAvatar={showAvatar}
|
|
252
|
+
showSenderGutter={showSenderGutter}
|
|
253
|
+
groupPosition={groupPosition}
|
|
218
254
|
/>
|
|
219
255
|
</KContainer.View>
|
|
220
256
|
{flowInputButtonsNodes && flowInputButtonsNodes.length > 0 && (
|
|
@@ -242,6 +278,7 @@ export const ChatListLegend = memo(
|
|
|
242
278
|
currentUserId,
|
|
243
279
|
lastMessageId,
|
|
244
280
|
messageDataMap,
|
|
281
|
+
isGroupConversation,
|
|
245
282
|
isBotConversation,
|
|
246
283
|
onMediaPress,
|
|
247
284
|
onPressUrl,
|
|
@@ -303,12 +340,22 @@ export const ChatListLegend = memo(
|
|
|
303
340
|
const getFixedItemSize = useCallback(
|
|
304
341
|
(item: MessageItem, index: number): number | undefined => {
|
|
305
342
|
const message = item;
|
|
306
|
-
const messageId =
|
|
307
|
-
message.clientMsgID ||
|
|
308
|
-
message.serverMsgID ||
|
|
309
|
-
`temp-${message.sendTime || message.createTime}-${index}`;
|
|
343
|
+
const messageId = getMessageRenderId(message, index);
|
|
310
344
|
|
|
311
|
-
|
|
345
|
+
const messageData = messageDataMap.get(messageId);
|
|
346
|
+
// Both separators add chrome above the row that this function does not
|
|
347
|
+
// account for, and a resolved size is never re-measured.
|
|
348
|
+
if (messageData?.dayStart || messageData?.timeBreak) return undefined;
|
|
349
|
+
|
|
350
|
+
// Incoming rows in a group chat carry an avatar column and a sender
|
|
351
|
+
// name whose height this function cannot predict — measure them.
|
|
352
|
+
if (isGroupConversation && message.sendID !== currentUserId) {
|
|
353
|
+
return undefined;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const rowMargin = getRowMarginBottom(
|
|
357
|
+
messageData?.groupPosition ?? 'solo'
|
|
358
|
+
);
|
|
312
359
|
|
|
313
360
|
// Resolve the content-type candidate FIRST — for every type other
|
|
314
361
|
// than these three the result is `undefined` regardless of anything
|
|
@@ -319,17 +366,16 @@ export const ChatListLegend = memo(
|
|
|
319
366
|
switch (message.contentType) {
|
|
320
367
|
case MessageType.PictureMessage:
|
|
321
368
|
candidateSize =
|
|
322
|
-
resolveImageDisplayDims(message, windowWidth).height +
|
|
323
|
-
ROW_MARGIN_BOTTOM;
|
|
369
|
+
resolveImageDisplayDims(message, windowWidth).height + rowMargin;
|
|
324
370
|
break;
|
|
325
371
|
case MessageType.VideoMessage:
|
|
326
372
|
candidateSize =
|
|
327
373
|
ChatImageDimens.getDimensions(
|
|
328
374
|
CHAT_IMAGE_VIEW_SOURCE.MessageListThumbnail
|
|
329
|
-
).height +
|
|
375
|
+
).height + rowMargin;
|
|
330
376
|
break;
|
|
331
377
|
case MessageType.StickerMessage:
|
|
332
|
-
candidateSize = STICKER_SIZE +
|
|
378
|
+
candidateSize = STICKER_SIZE + rowMargin;
|
|
333
379
|
break;
|
|
334
380
|
default:
|
|
335
381
|
return undefined;
|
|
@@ -361,6 +407,7 @@ export const ChatListLegend = memo(
|
|
|
361
407
|
messageDataMap,
|
|
362
408
|
customMessageItemFactory,
|
|
363
409
|
currentUserId,
|
|
410
|
+
isGroupConversation,
|
|
364
411
|
lastMessageId,
|
|
365
412
|
isBotConversation,
|
|
366
413
|
conversationFlow?.flowCurrentStep,
|
|
@@ -368,6 +415,28 @@ export const ChatListLegend = memo(
|
|
|
368
415
|
]
|
|
369
416
|
);
|
|
370
417
|
|
|
418
|
+
// LegendList memoises each rendered item on [itemKey, item, extraData], so
|
|
419
|
+
// an item whose own object is unchanged will not re-render. Cluster
|
|
420
|
+
// position depends on a message's NEIGHBOURS, which can change while the
|
|
421
|
+
// message itself does not — prepending older history is enough to turn a
|
|
422
|
+
// 'first' into a 'middle'. messageDataMap is memoised on exactly the inputs
|
|
423
|
+
// that can move those boundaries, so including it here is what keeps a
|
|
424
|
+
// stale tail from being left behind.
|
|
425
|
+
const listExtraData = useMemo(
|
|
426
|
+
() => ({
|
|
427
|
+
messageDataMap,
|
|
428
|
+
flowStepId: conversationFlow?.flowCurrentStep?.id ?? '',
|
|
429
|
+
lastMessageId,
|
|
430
|
+
highlightedId,
|
|
431
|
+
}),
|
|
432
|
+
[
|
|
433
|
+
messageDataMap,
|
|
434
|
+
conversationFlow?.flowCurrentStep?.id,
|
|
435
|
+
lastMessageId,
|
|
436
|
+
highlightedId,
|
|
437
|
+
]
|
|
438
|
+
);
|
|
439
|
+
|
|
371
440
|
const handleStartReached = useCallback(() => {
|
|
372
441
|
if (__DEV__) {
|
|
373
442
|
console.log('[LegendList] onStartReached triggered', {
|
|
@@ -525,7 +594,7 @@ export const ChatListLegend = memo(
|
|
|
525
594
|
getFixedItemSize={getFixedItemSize}
|
|
526
595
|
keyExtractor={(item) => String(item.clientMsgID || item.serverMsgID)}
|
|
527
596
|
recycleItems={false}
|
|
528
|
-
extraData={
|
|
597
|
+
extraData={listExtraData}
|
|
529
598
|
alignItemsAtEnd
|
|
530
599
|
maintainVisibleContentPosition={maintainVisiblePositionConfig}
|
|
531
600
|
freeze={freeze}
|
|
@@ -46,25 +46,35 @@ const formatDayLabel = (createdAt: number): string | null => {
|
|
|
46
46
|
|
|
47
47
|
interface LegendChatDayProps {
|
|
48
48
|
createdAt: number;
|
|
49
|
+
/**
|
|
50
|
+
* Render just the clock time, without the day. Used where a cluster broke on
|
|
51
|
+
* elapsed time inside a single day — the date is already on screen above.
|
|
52
|
+
*/
|
|
53
|
+
timeOnly?: boolean;
|
|
49
54
|
}
|
|
50
55
|
|
|
51
|
-
export const LegendChatDay = memo(
|
|
52
|
-
|
|
53
|
-
() =>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
export const LegendChatDay = memo(
|
|
57
|
+
({ createdAt, timeOnly = false }: LegendChatDayProps) => {
|
|
58
|
+
const dateStr = useMemo(() => {
|
|
59
|
+
if (createdAt == null) return null;
|
|
60
|
+
if (!timeOnly) return formatDayLabel(createdAt);
|
|
56
61
|
|
|
57
|
-
|
|
62
|
+
const date = new Date(createdAt);
|
|
63
|
+
return Number.isNaN(date.getTime()) ? null : formatTime(date);
|
|
64
|
+
}, [createdAt, timeOnly]);
|
|
58
65
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
if (!dateStr) return null;
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<KContainer.View center marginV="0.5rem">
|
|
70
|
+
<KContainer.View>
|
|
71
|
+
<KLabel.Text typo="TextXsNormal" color={CHAT_BUBBLE_COLORS.dayLabel}>
|
|
72
|
+
{dateStr}
|
|
73
|
+
</KLabel.Text>
|
|
74
|
+
</KContainer.View>
|
|
65
75
|
</KContainer.View>
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
);
|
|
69
79
|
|
|
70
80
|
LegendChatDay.displayName = 'LegendChatDay';
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
KContainer,
|
|
3
|
+
KImage,
|
|
4
|
+
KColors,
|
|
5
|
+
KLabel,
|
|
6
|
+
KSpacingValue,
|
|
7
|
+
} from '@droppii/libs';
|
|
2
8
|
import { StyleSheet } from 'react-native';
|
|
3
9
|
import { memo, useCallback, useMemo } from 'react';
|
|
4
10
|
import type { ComponentType, PropsWithChildren, ReactNode } from 'react';
|
|
@@ -20,7 +26,10 @@ import type { BaseLegendMessageProps } from '../../../components/messages/types'
|
|
|
20
26
|
import { messageStyles } from './messageTypes';
|
|
21
27
|
import QuotedMessage from '../../../components/messages/quotedMessage';
|
|
22
28
|
import { StickerMessage } from '../../../components/messages/stickerMessage';
|
|
23
|
-
import {
|
|
29
|
+
import { GROUP_NOTIFICATION_TYPES } from '../../../utils/messageUtils';
|
|
30
|
+
import type { GroupPosition } from '../../../utils/legendListMessage';
|
|
31
|
+
import { Avatar } from '../../../components/Avatar';
|
|
32
|
+
import { TAIL_WIDTH } from '../../../components/messages/BubbleTail';
|
|
24
33
|
|
|
25
34
|
type MessageProps = BaseLegendMessageProps & {
|
|
26
35
|
onPressUrl?: (url: string) => void;
|
|
@@ -38,18 +47,20 @@ interface LegendChatMessageProps {
|
|
|
38
47
|
onLongPress?: () => void;
|
|
39
48
|
customMessageItemFactory?: (m: MessageItem) => ReactNode;
|
|
40
49
|
isHighlighted?: boolean;
|
|
50
|
+
/** Sender name above the bubble — first message of an incoming cluster. */
|
|
51
|
+
showName?: boolean;
|
|
52
|
+
/** Sender avatar beside the bubble — last message of an incoming cluster. */
|
|
53
|
+
showAvatar?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Reserve the avatar column. True for every incoming message in a group
|
|
56
|
+
* conversation, not just the one carrying the avatar, so that the middle of
|
|
57
|
+
* a cluster stays aligned with its last message.
|
|
58
|
+
*/
|
|
59
|
+
showSenderGutter?: boolean;
|
|
60
|
+
/** Position within its same-sender cluster — drives the bubble tail. */
|
|
61
|
+
groupPosition?: GroupPosition;
|
|
41
62
|
}
|
|
42
63
|
|
|
43
|
-
// Notification-style messages (group created/renamed/member changes/etc.)
|
|
44
|
-
// are system banners, not user-authored text — they render centered,
|
|
45
|
-
// full-width, without a bubble, avatar/status chrome, or left/right
|
|
46
|
-
// alignment (see SYSTEM_MESSAGE_TYPES below). Driven by the same lookup
|
|
47
|
-
// messageUtils uses to build their preview/thread text, so adding a new
|
|
48
|
-
// notification content type there is enough to cover both places.
|
|
49
|
-
const GROUP_NOTIFICATION_TYPES = Object.keys(GROUP_NOTIFICATION_TEXT).map(
|
|
50
|
-
Number
|
|
51
|
-
) as MessageType[];
|
|
52
|
-
|
|
53
64
|
const messageComponentMap: Partial<
|
|
54
65
|
Record<MessageType, ComponentType<MessageProps>>
|
|
55
66
|
> = {
|
|
@@ -88,6 +99,10 @@ export const LegendChatMessage = memo(
|
|
|
88
99
|
onLongPress,
|
|
89
100
|
customMessageItemFactory,
|
|
90
101
|
isHighlighted,
|
|
102
|
+
showName = false,
|
|
103
|
+
showAvatar = false,
|
|
104
|
+
showSenderGutter = false,
|
|
105
|
+
groupPosition,
|
|
91
106
|
}: LegendChatMessageProps) => {
|
|
92
107
|
const handlePress = useCallback(() => {
|
|
93
108
|
onPress?.(message);
|
|
@@ -127,12 +142,34 @@ export const LegendChatMessage = memo(
|
|
|
127
142
|
|
|
128
143
|
return (
|
|
129
144
|
<KContainer.View style={messageStyles.wrapper} row alignItems>
|
|
145
|
+
{showSenderGutter && (
|
|
146
|
+
<KContainer.View style={styles.avatarGutter}>
|
|
147
|
+
{showAvatar && (
|
|
148
|
+
<Avatar
|
|
149
|
+
size="sm"
|
|
150
|
+
source={message.senderFaceUrl}
|
|
151
|
+
fullName={message.senderNickname}
|
|
152
|
+
/>
|
|
153
|
+
)}
|
|
154
|
+
</KContainer.View>
|
|
155
|
+
)}
|
|
130
156
|
<MessageStatusIndicator
|
|
131
157
|
message={message}
|
|
132
158
|
isOutgoing={isOutgoing}
|
|
133
159
|
onRetry={onRetry}
|
|
134
160
|
/>
|
|
135
161
|
<KContainer.View style={styles.content}>
|
|
162
|
+
{showName && !!message.senderNickname && (
|
|
163
|
+
<KLabel.Text
|
|
164
|
+
typo="TextXsNormal"
|
|
165
|
+
color={KColors.gray.normal}
|
|
166
|
+
numberOfLines={1}
|
|
167
|
+
marginB={2}
|
|
168
|
+
style={styles.senderName}
|
|
169
|
+
>
|
|
170
|
+
{message.senderNickname}
|
|
171
|
+
</KLabel.Text>
|
|
172
|
+
)}
|
|
136
173
|
<LongPressWrapper onPress={handlePress} onLongPress={handleLongPress}>
|
|
137
174
|
<MessageComponent
|
|
138
175
|
message={message}
|
|
@@ -141,6 +178,7 @@ export const LegendChatMessage = memo(
|
|
|
141
178
|
onMediaPress={hasMediaPress ? onMediaPress : undefined}
|
|
142
179
|
onPressUrl={hasUrlPress ? onPressUrl : undefined}
|
|
143
180
|
isHighlighted={isHighlighted}
|
|
181
|
+
groupPosition={groupPosition}
|
|
144
182
|
/>
|
|
145
183
|
</LongPressWrapper>
|
|
146
184
|
|
|
@@ -161,10 +199,22 @@ export const LegendChatMessage = memo(
|
|
|
161
199
|
|
|
162
200
|
LegendChatMessage.displayName = 'LegendChatMessage';
|
|
163
201
|
|
|
202
|
+
const AVATAR_SIZE = 24;
|
|
203
|
+
|
|
164
204
|
const styles = StyleSheet.create({
|
|
165
205
|
content: {
|
|
166
206
|
position: 'relative',
|
|
167
207
|
},
|
|
208
|
+
// Bubbles sit behind a TAIL_WIDTH gutter (see BubbleTailRow); the name has to
|
|
209
|
+
// clear the same gutter or it hangs to the left of the bubble it labels.
|
|
210
|
+
senderName: {
|
|
211
|
+
marginLeft: TAIL_WIDTH,
|
|
212
|
+
},
|
|
213
|
+
avatarGutter: {
|
|
214
|
+
width: AVATAR_SIZE,
|
|
215
|
+
marginRight: KSpacingValue['0.25rem'],
|
|
216
|
+
alignSelf: 'flex-end',
|
|
217
|
+
},
|
|
168
218
|
systemMessageWrapper: {
|
|
169
219
|
width: '100%',
|
|
170
220
|
marginVertical: KSpacingValue['0.25rem'],
|
|
@@ -35,11 +35,19 @@ export const MessageToolboxWrapper = memo(
|
|
|
35
35
|
toolbox,
|
|
36
36
|
}: MessageToolboxWrapperProps) => {
|
|
37
37
|
const insets = useSafeAreaInsets();
|
|
38
|
+
const messageHeight = useSharedValue(0);
|
|
38
39
|
const reactionHeight = useSharedValue(0);
|
|
39
40
|
const toolboxHeight = useSharedValue(0);
|
|
40
41
|
const topOffset = Platform.OS === 'android' ? insets.top : 0;
|
|
41
42
|
const { height: windowHeight } = useWindowDimensions();
|
|
42
43
|
|
|
44
|
+
const onMessageCloneLayout = useCallback(
|
|
45
|
+
({ nativeEvent }: LayoutChangeEvent) => {
|
|
46
|
+
messageHeight.value = nativeEvent.layout.height;
|
|
47
|
+
},
|
|
48
|
+
[messageHeight]
|
|
49
|
+
);
|
|
50
|
+
|
|
43
51
|
const onToolboxLayout = useCallback(
|
|
44
52
|
({ nativeEvent }: LayoutChangeEvent) => {
|
|
45
53
|
toolboxHeight.value = nativeEvent.layout.height;
|
|
@@ -62,7 +70,7 @@ export const MessageToolboxWrapper = memo(
|
|
|
62
70
|
const overflowBottom =
|
|
63
71
|
Math.max(rect.y, insets.top) +
|
|
64
72
|
topOffset +
|
|
65
|
-
|
|
73
|
+
messageHeight.value +
|
|
66
74
|
toolboxHeight.value +
|
|
67
75
|
(insets.bottom || 32) +
|
|
68
76
|
menuGap -
|
|
@@ -89,9 +97,19 @@ export const MessageToolboxWrapper = memo(
|
|
|
89
97
|
containerStyles,
|
|
90
98
|
]}
|
|
91
99
|
>
|
|
92
|
-
<View
|
|
93
|
-
|
|
94
|
-
|
|
100
|
+
<View
|
|
101
|
+
pointerEvents="none"
|
|
102
|
+
collapsable={false}
|
|
103
|
+
onLayout={onMessageCloneLayout}
|
|
104
|
+
>
|
|
105
|
+
{message}
|
|
106
|
+
</View>
|
|
107
|
+
<View collapsable={false} onLayout={onReactionLayout}>
|
|
108
|
+
{reaction}
|
|
109
|
+
</View>
|
|
110
|
+
<View collapsable={false} onLayout={onToolboxLayout}>
|
|
111
|
+
{toolbox}
|
|
112
|
+
</View>
|
|
95
113
|
</Animated.View>
|
|
96
114
|
);
|
|
97
115
|
}
|
|
@@ -39,6 +39,7 @@ export const ENDPOINTS = {
|
|
|
39
39
|
`${getApiEndpoint('stickersByCategory')}/${id}/stickers`,
|
|
40
40
|
stickerHistory: () => getApiEndpoint('stickerHistory'),
|
|
41
41
|
searchMessages: () => `${getApiEndpoint('messages')}/search`,
|
|
42
|
+
searchContacts: () => `${getApiEndpoint('contacts')}/search`,
|
|
42
43
|
},
|
|
43
44
|
uploaderService: {
|
|
44
45
|
get uploadImage() {
|
|
@@ -188,7 +188,13 @@ const i18nKey: Record<string, string> = {
|
|
|
188
188
|
'Bạn có thể tìm kiếm theo liên hệ hoặc nội dung tin nhắn',
|
|
189
189
|
'search_message:empty_hint': 'Bạn có thể tìm kiếm theo nội dung tin nhắn',
|
|
190
190
|
'search_conversation:empty_hint': 'Bạn có thể tìm kiếm theo liên hệ',
|
|
191
|
+
'search_community:empty_hint': 'Bạn có thể tìm kiếm theo tên nhóm cộng đồng',
|
|
191
192
|
'view_all': 'Xem tất cả',
|
|
193
|
+
'contact_type:customer': 'Khách hàng',
|
|
194
|
+
'contact_type:pdp': 'PDP',
|
|
195
|
+
'contact_type:group': 'Nhóm',
|
|
196
|
+
'contact_type:seller': 'Người bán',
|
|
197
|
+
'group:no_description': 'Chưa có mô tả nhóm',
|
|
192
198
|
'search_message:revoked_message': 'Tin nhắn đã được thu hồi',
|
|
193
199
|
'chat:no_permission': 'Bạn không có quyền này',
|
|
194
200
|
'chat:no_send_permission': 'Bạn không có quyền gửi tin nhắn',
|