@droppii-org/chat-mobile 0.2.56 → 0.2.58
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/assets/images/index.js +2 -1
- package/lib/module/assets/images/index.js.map +1 -1
- package/lib/module/assets/images/search_empty.png +0 -0
- package/lib/module/components/Avatar/Avatar.utils.js +5 -3
- package/lib/module/components/Avatar/Avatar.utils.js.map +1 -1
- package/lib/module/components/Avatar/SingleAvatar.js +2 -0
- package/lib/module/components/Avatar/SingleAvatar.js.map +1 -1
- package/lib/module/components/ThreadCard/AvatarSection.js +8 -4
- package/lib/module/components/ThreadCard/AvatarSection.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/constants/index.js +4 -0
- package/lib/module/constants/index.js.map +1 -0
- package/lib/module/hooks/message/useSearchMessage.js +58 -0
- package/lib/module/hooks/message/useSearchMessage.js.map +1 -0
- package/lib/module/hooks/query-keys.js +4 -0
- package/lib/module/hooks/query-keys.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/screens/ChatInfo/ChatInfoActions.js +1 -1
- package/lib/module/screens/ChatInfo/ChatInfoActions.js.map +1 -1
- package/lib/module/screens/ChatInfo/ChatInfoMediaRow.js +1 -1
- package/lib/module/screens/ChatInfo/ChatInfoMediaRow.js.map +1 -1
- package/lib/module/screens/MediaView/VideoPlayer.js +13 -1
- package/lib/module/screens/MediaView/VideoPlayer.js.map +1 -1
- package/lib/module/screens/MediaView/index.js +11 -6
- package/lib/module/screens/MediaView/index.js.map +1 -1
- package/lib/module/screens/SearchConversation/SearchAllResult.js +123 -0
- package/lib/module/screens/SearchConversation/SearchAllResult.js.map +1 -0
- package/lib/module/screens/SearchConversation/SearchEmpty.js +32 -0
- package/lib/module/screens/SearchConversation/SearchEmpty.js.map +1 -0
- package/lib/module/screens/SearchConversation/SearchMessagesResult.js +71 -0
- package/lib/module/screens/SearchConversation/SearchMessagesResult.js.map +1 -0
- package/lib/module/screens/SearchConversation/SearchResultItem.js +102 -0
- package/lib/module/screens/SearchConversation/SearchResultItem.js.map +1 -0
- package/lib/module/screens/SearchConversation/TabResult.js +118 -0
- package/lib/module/screens/SearchConversation/TabResult.js.map +1 -0
- package/lib/module/screens/SearchConversation/index.js +57 -0
- package/lib/module/screens/SearchConversation/index.js.map +1 -0
- package/lib/module/screens/SearchConversation/types.js +9 -0
- package/lib/module/screens/SearchConversation/types.js.map +1 -0
- package/lib/module/screens/chat-detail/ChatDetail.js +3 -1
- package/lib/module/screens/chat-detail/ChatDetail.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 +7 -1
- package/lib/module/translation/resources/i18n.js.map +1 -1
- package/lib/module/types/chat.js.map +1 -1
- package/lib/module/utils/messageUtils.js +5 -5
- package/lib/module/utils/messageUtils.js.map +1 -1
- package/lib/typescript/src/assets/images/index.d.ts +1 -0
- package/lib/typescript/src/assets/images/index.d.ts.map +1 -1
- package/lib/typescript/src/components/Avatar/Avatar.types.d.ts +1 -1
- package/lib/typescript/src/components/Avatar/Avatar.types.d.ts.map +1 -1
- package/lib/typescript/src/components/Avatar/Avatar.utils.d.ts +2 -0
- package/lib/typescript/src/components/Avatar/Avatar.utils.d.ts.map +1 -1
- package/lib/typescript/src/components/Avatar/SingleAvatar.d.ts.map +1 -1
- package/lib/typescript/src/components/ThreadCard/AvatarSection.d.ts +2 -1
- package/lib/typescript/src/components/ThreadCard/AvatarSection.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/constants/index.d.ts +2 -0
- package/lib/typescript/src/constants/index.d.ts.map +1 -0
- package/lib/typescript/src/hooks/message/useSearchMessage.d.ts +221 -0
- package/lib/typescript/src/hooks/message/useSearchMessage.d.ts.map +1 -0
- 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/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/MediaView/VideoPlayer.d.ts.map +1 -1
- package/lib/typescript/src/screens/MediaView/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/SearchConversation/SearchAllResult.d.ts +10 -0
- package/lib/typescript/src/screens/SearchConversation/SearchAllResult.d.ts.map +1 -0
- package/lib/typescript/src/screens/SearchConversation/SearchEmpty.d.ts +2 -0
- package/lib/typescript/src/screens/SearchConversation/SearchEmpty.d.ts.map +1 -0
- package/lib/typescript/src/screens/SearchConversation/SearchMessagesResult.d.ts +8 -0
- package/lib/typescript/src/screens/SearchConversation/SearchMessagesResult.d.ts.map +1 -0
- package/lib/typescript/src/screens/SearchConversation/SearchResultItem.d.ts +10 -0
- package/lib/typescript/src/screens/SearchConversation/SearchResultItem.d.ts.map +1 -0
- package/lib/typescript/src/screens/SearchConversation/TabResult.d.ts +8 -0
- package/lib/typescript/src/screens/SearchConversation/TabResult.d.ts.map +1 -0
- package/lib/typescript/src/screens/SearchConversation/index.d.ts +7 -0
- package/lib/typescript/src/screens/SearchConversation/index.d.ts.map +1 -0
- package/lib/typescript/src/screens/SearchConversation/types.d.ts +6 -0
- package/lib/typescript/src/screens/SearchConversation/types.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/types.d.ts +1 -0
- package/lib/typescript/src/screens/chat-detail/types.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 +13 -1
- package/lib/typescript/src/types/chat.d.ts.map +1 -1
- package/lib/typescript/src/utils/messageUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/assets/images/index.ts +1 -0
- package/src/assets/images/search_empty.png +0 -0
- package/src/components/Avatar/Avatar.types.ts +1 -1
- package/src/components/Avatar/Avatar.utils.ts +5 -3
- package/src/components/Avatar/SingleAvatar.tsx +2 -0
- package/src/components/ThreadCard/AvatarSection.tsx +5 -2
- package/src/config/api-endpoints.ts +2 -0
- package/src/constants/index.ts +1 -0
- package/src/hooks/message/useSearchMessage.ts +78 -0
- package/src/hooks/query-keys.ts +6 -0
- package/src/index.tsx +1 -0
- package/src/screens/ChatInfo/ChatInfoActions.tsx +1 -1
- package/src/screens/ChatInfo/ChatInfoMediaRow.tsx +1 -1
- package/src/screens/MediaView/VideoPlayer.tsx +13 -2
- package/src/screens/MediaView/index.tsx +10 -6
- package/src/screens/SearchConversation/SearchAllResult.tsx +154 -0
- package/src/screens/SearchConversation/SearchEmpty.tsx +30 -0
- package/src/screens/SearchConversation/SearchMessagesResult.tsx +74 -0
- package/src/screens/SearchConversation/SearchResultItem.tsx +107 -0
- package/src/screens/SearchConversation/TabResult.tsx +144 -0
- package/src/screens/SearchConversation/index.tsx +61 -0
- package/src/screens/SearchConversation/types.tsx +5 -0
- package/src/screens/chat-detail/ChatDetail.tsx +2 -1
- package/src/screens/chat-detail/types.ts +1 -0
- package/src/services/endpoints.ts +1 -0
- package/src/translation/resources/i18n.ts +7 -0
- package/src/types/chat.ts +15 -0
- package/src/utils/messageUtils.ts +13 -6
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { KContainer, KDivider, KSpacingValue } from '@droppii/libs';
|
|
2
|
+
import { memo, useCallback } from 'react';
|
|
3
|
+
import { useSearchMessage } from '../../hooks/message/useSearchMessage';
|
|
4
|
+
import { useChatContext } from '../../context';
|
|
5
|
+
import { FlatList, StyleSheet } from 'react-native';
|
|
6
|
+
import type { ISearchMessageItem } from '../../types/chat';
|
|
7
|
+
import SearchResultItem from './SearchResultItem';
|
|
8
|
+
import { SearchConversationTabResultType } from './types';
|
|
9
|
+
import { SearchEmpty } from './SearchEmpty';
|
|
10
|
+
|
|
11
|
+
interface ISearchMessagesResultProps {
|
|
12
|
+
searchTerm: string;
|
|
13
|
+
onPressItem?: (item: ISearchMessageItem) => void;
|
|
14
|
+
}
|
|
15
|
+
const SearchMessagesResult = memo(
|
|
16
|
+
({ searchTerm, onPressItem }: ISearchMessagesResultProps) => {
|
|
17
|
+
const { applicationType } = useChatContext();
|
|
18
|
+
const { dataFlatten, hasNextPage, fetchNextPage, isFetchingNextPage } =
|
|
19
|
+
useSearchMessage({
|
|
20
|
+
query: searchTerm,
|
|
21
|
+
applicationType,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const renderItem = useCallback(
|
|
25
|
+
({ item }: { item: ISearchMessageItem }) => {
|
|
26
|
+
return (
|
|
27
|
+
<SearchResultItem
|
|
28
|
+
item={item}
|
|
29
|
+
tabResultType={SearchConversationTabResultType.Messages}
|
|
30
|
+
onPressItem={onPressItem}
|
|
31
|
+
/>
|
|
32
|
+
);
|
|
33
|
+
},
|
|
34
|
+
[onPressItem]
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const keyExtractor = useCallback((item: ISearchMessageItem) => {
|
|
38
|
+
return `${item?.conversation?.conversationID}-${item?.chatLog?.clientMsgID}`;
|
|
39
|
+
}, []);
|
|
40
|
+
|
|
41
|
+
const onEndReached = useCallback(() => {
|
|
42
|
+
if (hasNextPage && !isFetchingNextPage) {
|
|
43
|
+
fetchNextPage();
|
|
44
|
+
}
|
|
45
|
+
}, [hasNextPage, isFetchingNextPage, fetchNextPage]);
|
|
46
|
+
|
|
47
|
+
const ItemSeparatorComponent = useCallback(() => {
|
|
48
|
+
return <KDivider size={'hairline'} vertical={false} />;
|
|
49
|
+
}, []);
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<KContainer.View flex>
|
|
53
|
+
<FlatList
|
|
54
|
+
data={dataFlatten}
|
|
55
|
+
renderItem={renderItem}
|
|
56
|
+
keyExtractor={keyExtractor}
|
|
57
|
+
contentContainerStyle={styles.container}
|
|
58
|
+
showsVerticalScrollIndicator={false}
|
|
59
|
+
onEndReached={onEndReached}
|
|
60
|
+
ItemSeparatorComponent={ItemSeparatorComponent}
|
|
61
|
+
ListEmptyComponent={<SearchEmpty />}
|
|
62
|
+
/>
|
|
63
|
+
</KContainer.View>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
export default SearchMessagesResult;
|
|
69
|
+
|
|
70
|
+
const styles = StyleSheet.create({
|
|
71
|
+
container: {
|
|
72
|
+
paddingHorizontal: KSpacingValue['0.75rem'],
|
|
73
|
+
},
|
|
74
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
KColors,
|
|
3
|
+
KContainer,
|
|
4
|
+
KImage,
|
|
5
|
+
KLabel,
|
|
6
|
+
KSpacingValue,
|
|
7
|
+
} from '@droppii/libs';
|
|
8
|
+
import { memo, useCallback } from 'react';
|
|
9
|
+
import type { ISearchMessageItem } from '../../types/chat';
|
|
10
|
+
import { StyleSheet } from 'react-native';
|
|
11
|
+
import { SearchConversationTabResultType } from './types';
|
|
12
|
+
import { AvatarSection, NamePrefixIcon } from '../../components/ThreadCard';
|
|
13
|
+
import { getLastMessageText } from '../../utils/messageUtils';
|
|
14
|
+
import { useUserStore } from '../../store';
|
|
15
|
+
import { formatTimestamp } from '../../utils/dateTimeUtils';
|
|
16
|
+
|
|
17
|
+
interface ISearchResultItemProps {
|
|
18
|
+
item: ISearchMessageItem;
|
|
19
|
+
tabResultType: SearchConversationTabResultType;
|
|
20
|
+
onPressItem?: (item: ISearchMessageItem) => void;
|
|
21
|
+
}
|
|
22
|
+
const SearchResultItem = memo((props: ISearchResultItemProps) => {
|
|
23
|
+
const { item, tabResultType, onPressItem } = props || {};
|
|
24
|
+
const currentUserId = useUserStore((s) => s.user?.userID);
|
|
25
|
+
|
|
26
|
+
const timestamp = formatTimestamp(item?.chatLog?.sendTime ?? null);
|
|
27
|
+
const lastMessageText = getLastMessageText(
|
|
28
|
+
item?.chatLog,
|
|
29
|
+
currentUserId,
|
|
30
|
+
item?.conversation
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const handlePressItem = useCallback(() => {
|
|
34
|
+
onPressItem?.({ conversation: item?.conversation, chatLog: item?.chatLog });
|
|
35
|
+
}, [onPressItem, item?.conversation, item?.chatLog]);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<KContainer.Touchable style={styles.container} onPress={handlePressItem}>
|
|
39
|
+
<KContainer.View style={styles.leftContainer}>
|
|
40
|
+
<AvatarSection
|
|
41
|
+
avatar={item?.conversation?.faceURL ?? null}
|
|
42
|
+
fullName={item?.conversation?.showName}
|
|
43
|
+
peerType={item?.conversation?.peerType}
|
|
44
|
+
size="nm"
|
|
45
|
+
/>
|
|
46
|
+
<KContainer.View style={styles.contentContainer}>
|
|
47
|
+
<KContainer.View style={styles.messageContainer}>
|
|
48
|
+
<NamePrefixIcon peerType={item?.conversation?.peerType} />
|
|
49
|
+
<KLabel.Text typo="TextMdMedium" numberOfLines={1} flex>
|
|
50
|
+
{item?.conversation?.showName}
|
|
51
|
+
</KLabel.Text>
|
|
52
|
+
<KLabel.Text typo="TextXsNormal" color={KColors.gray.light}>
|
|
53
|
+
{timestamp}
|
|
54
|
+
</KLabel.Text>
|
|
55
|
+
</KContainer.View>
|
|
56
|
+
<KLabel.Text typo="TextNmNormal" numberOfLines={1} flex>
|
|
57
|
+
{lastMessageText}
|
|
58
|
+
</KLabel.Text>
|
|
59
|
+
</KContainer.View>
|
|
60
|
+
</KContainer.View>
|
|
61
|
+
<KContainer.VisibleView
|
|
62
|
+
visible={
|
|
63
|
+
tabResultType === SearchConversationTabResultType.Conversations
|
|
64
|
+
}
|
|
65
|
+
>
|
|
66
|
+
<KContainer.View style={styles.rightContainer}>
|
|
67
|
+
<KImage.VectorIcons
|
|
68
|
+
name="more-horizontal-b"
|
|
69
|
+
size={24}
|
|
70
|
+
color={KColors.gray.normal}
|
|
71
|
+
/>
|
|
72
|
+
</KContainer.View>
|
|
73
|
+
</KContainer.VisibleView>
|
|
74
|
+
</KContainer.Touchable>
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export default SearchResultItem;
|
|
79
|
+
|
|
80
|
+
const styles = StyleSheet.create({
|
|
81
|
+
container: {
|
|
82
|
+
flexDirection: 'row',
|
|
83
|
+
justifyContent: 'space-between',
|
|
84
|
+
alignItems: 'center',
|
|
85
|
+
gap: KSpacingValue['1rem'],
|
|
86
|
+
paddingVertical: KSpacingValue['0.5rem'],
|
|
87
|
+
},
|
|
88
|
+
leftContainer: {
|
|
89
|
+
flexDirection: 'row',
|
|
90
|
+
alignItems: 'center',
|
|
91
|
+
gap: KSpacingValue['0.5rem'],
|
|
92
|
+
flex: 1,
|
|
93
|
+
},
|
|
94
|
+
rightContainer: {
|
|
95
|
+
alignItems: 'center',
|
|
96
|
+
justifyContent: 'center',
|
|
97
|
+
},
|
|
98
|
+
contentContainer: {
|
|
99
|
+
flex: 1,
|
|
100
|
+
},
|
|
101
|
+
messageContainer: {
|
|
102
|
+
flexDirection: 'row',
|
|
103
|
+
alignItems: 'center',
|
|
104
|
+
gap: KSpacingValue['0.25rem'],
|
|
105
|
+
justifyContent: 'space-between',
|
|
106
|
+
},
|
|
107
|
+
});
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import {
|
|
2
|
+
KColors,
|
|
3
|
+
KContainer,
|
|
4
|
+
KLabel,
|
|
5
|
+
KRadiusValue,
|
|
6
|
+
KSpacingValue,
|
|
7
|
+
} from '@droppii/libs';
|
|
8
|
+
import { memo, useCallback, useState } from 'react';
|
|
9
|
+
import { StyleSheet } from 'react-native';
|
|
10
|
+
import { TabView } from 'react-native-tab-view';
|
|
11
|
+
import { SearchConversationTabResultType } from './types';
|
|
12
|
+
import { trans } from '../../translation';
|
|
13
|
+
import SearchMessagesResult from './SearchMessagesResult';
|
|
14
|
+
import SearchAllResult from './SearchAllResult';
|
|
15
|
+
import type { ISearchMessageItem } from '../../types/chat';
|
|
16
|
+
|
|
17
|
+
type TabRoute = { key: string; title: string };
|
|
18
|
+
|
|
19
|
+
interface ISearchConversationTabResultProps {
|
|
20
|
+
searchTerm: string;
|
|
21
|
+
onPressItem?: (item: ISearchMessageItem) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const TabRoutes: TabRoute[] = [
|
|
25
|
+
{ key: SearchConversationTabResultType.All, title: trans('all') },
|
|
26
|
+
// { key: SearchConversationTabResultType.Conversations, title: trans('contacts') },
|
|
27
|
+
{ key: SearchConversationTabResultType.Messages, title: trans('messages') },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
interface CustomTabBarProps {
|
|
31
|
+
routes: TabRoute[];
|
|
32
|
+
index: number;
|
|
33
|
+
onPress: (i: number) => void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const CustomTabBar = memo(({ routes, index, onPress }: CustomTabBarProps) => (
|
|
37
|
+
<KContainer.View style={styles.tabRow}>
|
|
38
|
+
{routes.map((route, i) => {
|
|
39
|
+
const isActive = i === index;
|
|
40
|
+
return (
|
|
41
|
+
<KContainer.Touchable
|
|
42
|
+
key={route.key}
|
|
43
|
+
onPress={() => onPress(i)}
|
|
44
|
+
style={styles.tabItem}
|
|
45
|
+
activeOpacity={0.7}
|
|
46
|
+
>
|
|
47
|
+
<KLabel.Text
|
|
48
|
+
typo="TextNmBold"
|
|
49
|
+
color={isActive ? KColors.primary.normal : KColors.gray.light}
|
|
50
|
+
>
|
|
51
|
+
{route.title}
|
|
52
|
+
</KLabel.Text>
|
|
53
|
+
{isActive && <KContainer.View style={styles.indicator} />}
|
|
54
|
+
</KContainer.Touchable>
|
|
55
|
+
);
|
|
56
|
+
})}
|
|
57
|
+
</KContainer.View>
|
|
58
|
+
));
|
|
59
|
+
|
|
60
|
+
const SearchConversationTabResult = memo(
|
|
61
|
+
({ searchTerm, onPressItem }: ISearchConversationTabResultProps) => {
|
|
62
|
+
const [index, setIndex] = useState(0);
|
|
63
|
+
const [routes] = useState<TabRoute[]>(TabRoutes);
|
|
64
|
+
|
|
65
|
+
const handleChangeTab = useCallback(
|
|
66
|
+
(key: SearchConversationTabResultType) => {
|
|
67
|
+
setIndex(TabRoutes.findIndex((route) => route.key === key));
|
|
68
|
+
},
|
|
69
|
+
[]
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const renderScene = useCallback(
|
|
73
|
+
({ route }: { route: TabRoute }) => {
|
|
74
|
+
switch (route.key) {
|
|
75
|
+
case SearchConversationTabResultType.All:
|
|
76
|
+
return (
|
|
77
|
+
<SearchAllResult
|
|
78
|
+
searchTerm={searchTerm}
|
|
79
|
+
handleChangeTab={handleChangeTab}
|
|
80
|
+
onPressItem={onPressItem}
|
|
81
|
+
/>
|
|
82
|
+
);
|
|
83
|
+
case SearchConversationTabResultType.Conversations:
|
|
84
|
+
return null;
|
|
85
|
+
case SearchConversationTabResultType.Messages:
|
|
86
|
+
return (
|
|
87
|
+
<SearchMessagesResult
|
|
88
|
+
searchTerm={searchTerm}
|
|
89
|
+
onPressItem={onPressItem}
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
default:
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
[searchTerm, handleChangeTab, onPressItem]
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const renderTabBar = useCallback(
|
|
100
|
+
() => <CustomTabBar routes={routes} index={index} onPress={setIndex} />,
|
|
101
|
+
[routes, index]
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<KContainer.View flex>
|
|
106
|
+
<TabView
|
|
107
|
+
navigationState={{ index, routes }}
|
|
108
|
+
renderScene={renderScene}
|
|
109
|
+
renderTabBar={renderTabBar}
|
|
110
|
+
onIndexChange={setIndex}
|
|
111
|
+
swipeEnabled={routes.length > 1}
|
|
112
|
+
lazy
|
|
113
|
+
/>
|
|
114
|
+
</KContainer.View>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
export default SearchConversationTabResult;
|
|
120
|
+
|
|
121
|
+
const styles = StyleSheet.create({
|
|
122
|
+
tabRow: {
|
|
123
|
+
flexDirection: 'row',
|
|
124
|
+
borderBottomWidth: 1,
|
|
125
|
+
borderBottomColor: KColors.border.normal,
|
|
126
|
+
},
|
|
127
|
+
tabItem: {
|
|
128
|
+
paddingVertical: KSpacingValue['0.5rem'],
|
|
129
|
+
height: 40,
|
|
130
|
+
marginHorizontal: KSpacingValue['0.75rem'],
|
|
131
|
+
justifyContent: 'center',
|
|
132
|
+
alignItems: 'center',
|
|
133
|
+
},
|
|
134
|
+
indicator: {
|
|
135
|
+
height: 3,
|
|
136
|
+
backgroundColor: KColors.primary.normal,
|
|
137
|
+
width: '100%',
|
|
138
|
+
borderRadius: KRadiusValue.x,
|
|
139
|
+
position: 'absolute',
|
|
140
|
+
bottom: -1,
|
|
141
|
+
left: 0,
|
|
142
|
+
right: 0,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { KColors, KContainer, KNavigation } from '@droppii/libs';
|
|
2
|
+
import { memo, useRef, useState } from 'react';
|
|
3
|
+
import { useNavigation } from '@react-navigation/native';
|
|
4
|
+
import { trans } from '../../translation';
|
|
5
|
+
import debounce from 'lodash/debounce';
|
|
6
|
+
import type { TextInput } from 'react-native';
|
|
7
|
+
import SearchConversationTabResult from './TabResult';
|
|
8
|
+
import type { ISearchMessageItem } from '../../types/chat';
|
|
9
|
+
|
|
10
|
+
interface SearchConversationScreenProps {
|
|
11
|
+
onPressItem?: (item: ISearchMessageItem) => void;
|
|
12
|
+
}
|
|
13
|
+
const SearchConversationScreen = memo(
|
|
14
|
+
({ onPressItem }: SearchConversationScreenProps) => {
|
|
15
|
+
const nav = useNavigation();
|
|
16
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
17
|
+
const searchInputRef = useRef<TextInput>(null);
|
|
18
|
+
|
|
19
|
+
const debouncedSearch = debounce(
|
|
20
|
+
(text: string) => {
|
|
21
|
+
setSearchTerm(text);
|
|
22
|
+
},
|
|
23
|
+
500,
|
|
24
|
+
{ leading: false, trailing: true }
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<KContainer.Page flex background={KColors.white} edges={['bottom']}>
|
|
29
|
+
<KNavigation.HeaderV2Body
|
|
30
|
+
theme="light"
|
|
31
|
+
size="large"
|
|
32
|
+
isNested
|
|
33
|
+
leftButton={{
|
|
34
|
+
id: 'back',
|
|
35
|
+
icon: { vectorName: 'arrow-left-o' },
|
|
36
|
+
onPress: nav.goBack,
|
|
37
|
+
}}
|
|
38
|
+
searchProps={{
|
|
39
|
+
position: 'center',
|
|
40
|
+
placeholder: trans('search'),
|
|
41
|
+
onChangeText: debouncedSearch,
|
|
42
|
+
ref: searchInputRef as any,
|
|
43
|
+
returnKeyType: 'search',
|
|
44
|
+
value: searchTerm,
|
|
45
|
+
autoFocus: true,
|
|
46
|
+
background: KColors.palette.gray.w25,
|
|
47
|
+
br: '2x',
|
|
48
|
+
searchIconLeftColor: KColors.gray.normal,
|
|
49
|
+
marginL: undefined,
|
|
50
|
+
}}
|
|
51
|
+
/>
|
|
52
|
+
<SearchConversationTabResult
|
|
53
|
+
searchTerm={searchTerm}
|
|
54
|
+
onPressItem={onPressItem}
|
|
55
|
+
/>
|
|
56
|
+
</KContainer.Page>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
export default SearchConversationScreen;
|
|
@@ -41,6 +41,7 @@ const ChatDetail = memo(
|
|
|
41
41
|
targetUserId = '',
|
|
42
42
|
targetGroupId = '',
|
|
43
43
|
targetBotId = '',
|
|
44
|
+
targetMessageId = '',
|
|
44
45
|
showAddMember,
|
|
45
46
|
onBack,
|
|
46
47
|
onPressSearch,
|
|
@@ -91,7 +92,7 @@ const ChatDetail = memo(
|
|
|
91
92
|
getMoreNewMessages,
|
|
92
93
|
hasMoreOld,
|
|
93
94
|
hasMoreNew,
|
|
94
|
-
} = useMessages({ conversationId });
|
|
95
|
+
} = useMessages({ conversationId, startClientMsgId: targetMessageId });
|
|
95
96
|
|
|
96
97
|
const setShowFLashMessage = useSetIsShowFLashMessage();
|
|
97
98
|
|
|
@@ -38,6 +38,7 @@ export const ENDPOINTS = {
|
|
|
38
38
|
stickersByCategoryId: (id: string) =>
|
|
39
39
|
`${getApiEndpoint('stickersByCategory')}/${id}/stickers`,
|
|
40
40
|
stickerHistory: () => getApiEndpoint('stickerHistory'),
|
|
41
|
+
searchMessages: () => `${getApiEndpoint('messages')}/search`,
|
|
41
42
|
},
|
|
42
43
|
uploaderService: {
|
|
43
44
|
get uploadImage() {
|
|
@@ -118,6 +118,13 @@ const i18nKey: Record<string, string> = {
|
|
|
118
118
|
'Bạn đang trong phiên hỗ trợ với hỗ trợ viên, bạn có muốn <bold>kết thúc phiên hỗ trợ</bold> ngay bây giờ?',
|
|
119
119
|
'chat:end_support_session_primary': 'Kết thúc',
|
|
120
120
|
'chat:end_support_session_secondary': 'Tiếp tục',
|
|
121
|
+
'search': 'Tìm kiếm',
|
|
122
|
+
'all': 'Tất cả',
|
|
123
|
+
'contacts': 'Liên hệ',
|
|
124
|
+
'messages': 'Tin nhắn',
|
|
125
|
+
'search:empty_description':
|
|
126
|
+
'Không tìm thấy kết quả nào\nHãy thử lại với cách viết hoặc kết hợp cụm từ khác phù hợp hơn.',
|
|
127
|
+
'view_all': 'Xem tất cả',
|
|
121
128
|
};
|
|
122
129
|
|
|
123
130
|
export default {
|
package/src/types/chat.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
MessageType,
|
|
2
3
|
type ConversationItem,
|
|
3
4
|
type MessageItem,
|
|
4
5
|
} from '@droppii/openim-rn-client-sdk';
|
|
@@ -317,3 +318,17 @@ export interface DThreadResourceRawRow {
|
|
|
317
318
|
tag: unknown;
|
|
318
319
|
displayTag: unknown;
|
|
319
320
|
}
|
|
321
|
+
|
|
322
|
+
export type ISearchMessagesRequest = {
|
|
323
|
+
conversationId?: string;
|
|
324
|
+
contentTypes: MessageType[];
|
|
325
|
+
page: number;
|
|
326
|
+
pageSize: number;
|
|
327
|
+
applicationType: DChatApplicationType;
|
|
328
|
+
searchTerm: string;
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
export interface ISearchMessageItem {
|
|
332
|
+
chatLog: MessageItem;
|
|
333
|
+
conversation: ConversationItem;
|
|
334
|
+
}
|
|
@@ -20,10 +20,14 @@ export const isMessageRevoked = (message: MessageItem): boolean =>
|
|
|
20
20
|
export const extractTextContent = (message: MessageItem): string => {
|
|
21
21
|
if (message.textElem?.content) return message.textElem.content;
|
|
22
22
|
try {
|
|
23
|
-
const parsed = JSON.parse(message.content) as {
|
|
24
|
-
|
|
23
|
+
const parsed = JSON.parse(message.content) as {
|
|
24
|
+
content?: string;
|
|
25
|
+
text?: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
};
|
|
28
|
+
return parsed?.content || parsed?.text || parsed?.description || '';
|
|
25
29
|
} catch {
|
|
26
|
-
return message.content
|
|
30
|
+
return message.content || '';
|
|
27
31
|
}
|
|
28
32
|
};
|
|
29
33
|
|
|
@@ -75,14 +79,17 @@ export const getMessagePreview = (message?: MessageItem): string => {
|
|
|
75
79
|
case MessageType.UrlTextMessage:
|
|
76
80
|
// Preview link message - show with [Link] prefix
|
|
77
81
|
const urlText =
|
|
78
|
-
message?.urlTextElem?.content ||
|
|
82
|
+
message?.urlTextElem?.content ||
|
|
83
|
+
message?.urlTextElem?.urls?.[0] ||
|
|
84
|
+
extractTextContent(message) ||
|
|
85
|
+
'';
|
|
79
86
|
return urlText ? urlText : '';
|
|
80
87
|
|
|
81
88
|
case MessageType.AtTextMessage:
|
|
82
89
|
return extractTextContent(message);
|
|
83
90
|
|
|
84
91
|
case MessageType.QuoteMessage:
|
|
85
|
-
return message?.quoteElem?.text || '';
|
|
92
|
+
return message?.quoteElem?.text || extractTextContent(message) || '';
|
|
86
93
|
|
|
87
94
|
case MessageType.RevokeMessage:
|
|
88
95
|
return trans('msg_type_revoke');
|
|
@@ -91,7 +98,7 @@ export const getMessagePreview = (message?: MessageItem): string => {
|
|
|
91
98
|
if (message?.customElem?.description) {
|
|
92
99
|
return message?.customElem?.description;
|
|
93
100
|
}
|
|
94
|
-
return '';
|
|
101
|
+
return extractTextContent(message) || '';
|
|
95
102
|
|
|
96
103
|
case MessageType.StickerMessage:
|
|
97
104
|
return '[Sticker]';
|