@droppii-org/chat-mobile 0.2.57 → 0.2.59
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/usePinMessage.js +30 -26
- package/lib/module/hooks/message/usePinMessage.js.map +1 -1
- 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 +2 -2
- 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/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/BotMenuSheet.js +4 -4
- package/lib/module/screens/chat-detail/BotMenuSheet.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatComposer.js +19 -4
- package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
- 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/usePinMessage.d.ts.map +1 -1
- 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/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/BotMenuSheet.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/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/usePinMessage.ts +34 -30
- 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 +2 -2
- package/src/screens/ChatInfo/ChatInfoMediaRow.tsx +1 -1
- 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/BotMenuSheet.tsx +2 -3
- package/src/screens/chat-detail/ChatComposer.tsx +27 -8
- 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,154 @@
|
|
|
1
|
+
import {
|
|
2
|
+
KButton,
|
|
3
|
+
KColors,
|
|
4
|
+
KContainer,
|
|
5
|
+
KDivider,
|
|
6
|
+
KImage,
|
|
7
|
+
KLabel,
|
|
8
|
+
KSpacingValue,
|
|
9
|
+
} from '@droppii/libs';
|
|
10
|
+
import { memo, useCallback, useMemo } from 'react';
|
|
11
|
+
import { useChatContext } from '../../context';
|
|
12
|
+
import { useSearchMessage } from '../../hooks/message/useSearchMessage';
|
|
13
|
+
import { SearchConversationTabResultType } from './types';
|
|
14
|
+
import type { ISearchMessageItem } from '../../types/chat';
|
|
15
|
+
import { trans } from '../../translation';
|
|
16
|
+
import { FlatList, StyleSheet } from 'react-native';
|
|
17
|
+
import { SearchEmpty } from './SearchEmpty';
|
|
18
|
+
import SearchResultItem from './SearchResultItem';
|
|
19
|
+
|
|
20
|
+
interface SearchAllResultProps {
|
|
21
|
+
searchTerm: string;
|
|
22
|
+
handleChangeTab: (key: SearchConversationTabResultType) => void;
|
|
23
|
+
onPressItem?: (item: ISearchMessageItem) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ISearchAllResultItem {
|
|
27
|
+
type: SearchConversationTabResultType;
|
|
28
|
+
data: ISearchMessageItem[];
|
|
29
|
+
title: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const PAGE_SIZE = 5;
|
|
33
|
+
|
|
34
|
+
const SearchAllResult = memo(
|
|
35
|
+
({ searchTerm, handleChangeTab, onPressItem }: SearchAllResultProps) => {
|
|
36
|
+
const { applicationType } = useChatContext();
|
|
37
|
+
const { dataFlatten: messagesData } = useSearchMessage({
|
|
38
|
+
query: searchTerm,
|
|
39
|
+
applicationType,
|
|
40
|
+
pageSize: PAGE_SIZE,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const data: ISearchAllResultItem[] = useMemo(() => {
|
|
44
|
+
const mData = [
|
|
45
|
+
{
|
|
46
|
+
type: SearchConversationTabResultType.Conversations,
|
|
47
|
+
data: [],
|
|
48
|
+
title: trans('contacts'),
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: SearchConversationTabResultType.Messages,
|
|
52
|
+
data: messagesData,
|
|
53
|
+
title: trans('messages'),
|
|
54
|
+
},
|
|
55
|
+
]?.filter((item) => item.data.length > 0);
|
|
56
|
+
return mData;
|
|
57
|
+
}, [messagesData]);
|
|
58
|
+
|
|
59
|
+
const onChangeTab = useCallback(
|
|
60
|
+
(key: SearchConversationTabResultType) => {
|
|
61
|
+
handleChangeTab(key);
|
|
62
|
+
},
|
|
63
|
+
[handleChangeTab]
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const renderSessionItem = useCallback(
|
|
67
|
+
({ item }: { item: ISearchAllResultItem }) => {
|
|
68
|
+
return (
|
|
69
|
+
<KContainer.View style={styles.itemContainer}>
|
|
70
|
+
<KContainer.Touchable
|
|
71
|
+
style={styles.header}
|
|
72
|
+
onPress={() => onChangeTab(item.type)}
|
|
73
|
+
>
|
|
74
|
+
<KLabel.Text typo="TextMdBold" flex>
|
|
75
|
+
{item.title}
|
|
76
|
+
<KLabel.Text
|
|
77
|
+
typo="TextNmMedium"
|
|
78
|
+
color={KColors.gray.light}
|
|
79
|
+
>{` (${item.data.length})`}</KLabel.Text>
|
|
80
|
+
</KLabel.Text>
|
|
81
|
+
<KImage.VectorIcons name="angle-right-o" size={20} />
|
|
82
|
+
</KContainer.Touchable>
|
|
83
|
+
{item?.data?.map((childItem) => {
|
|
84
|
+
return (
|
|
85
|
+
<KContainer.View
|
|
86
|
+
style={styles.childItemContainer}
|
|
87
|
+
key={`${item.type}-${childItem?.conversation?.conversationID}-${childItem?.chatLog?.clientMsgID}`}
|
|
88
|
+
>
|
|
89
|
+
<SearchResultItem
|
|
90
|
+
item={childItem}
|
|
91
|
+
tabResultType={item.type}
|
|
92
|
+
onPressItem={onPressItem}
|
|
93
|
+
/>
|
|
94
|
+
</KContainer.View>
|
|
95
|
+
);
|
|
96
|
+
})}
|
|
97
|
+
<KButton.Link
|
|
98
|
+
label={trans('view_all')}
|
|
99
|
+
icon={{
|
|
100
|
+
vectorName: 'angle-right-o',
|
|
101
|
+
}}
|
|
102
|
+
iconAlignment="right"
|
|
103
|
+
onPress={() => onChangeTab(item.type)}
|
|
104
|
+
size="sm"
|
|
105
|
+
enhanceStyle={styles.viewAllButton}
|
|
106
|
+
/>
|
|
107
|
+
</KContainer.View>
|
|
108
|
+
);
|
|
109
|
+
},
|
|
110
|
+
[onChangeTab, onPressItem]
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const keyExtractor = useCallback((item: ISearchAllResultItem) => {
|
|
114
|
+
return item.type;
|
|
115
|
+
}, []);
|
|
116
|
+
|
|
117
|
+
const ItemSeparatorComponent = useCallback(() => {
|
|
118
|
+
return <KDivider size={'xs'} background={KColors.pageBackground} />;
|
|
119
|
+
}, []);
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<FlatList
|
|
123
|
+
data={data}
|
|
124
|
+
renderItem={renderSessionItem}
|
|
125
|
+
keyExtractor={keyExtractor}
|
|
126
|
+
ListEmptyComponent={<SearchEmpty />}
|
|
127
|
+
ItemSeparatorComponent={ItemSeparatorComponent}
|
|
128
|
+
/>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
export default SearchAllResult;
|
|
134
|
+
|
|
135
|
+
const styles = StyleSheet.create({
|
|
136
|
+
itemContainer: {
|
|
137
|
+
paddingHorizontal: KSpacingValue['0.75rem'],
|
|
138
|
+
},
|
|
139
|
+
header: {
|
|
140
|
+
flexDirection: 'row',
|
|
141
|
+
paddingVertical: KSpacingValue['0.5rem'],
|
|
142
|
+
gap: KSpacingValue['1rem'],
|
|
143
|
+
alignItems: 'center',
|
|
144
|
+
borderBottomWidth: 1,
|
|
145
|
+
borderBottomColor: KColors.border.light,
|
|
146
|
+
},
|
|
147
|
+
childItemContainer: {
|
|
148
|
+
borderBottomWidth: 1,
|
|
149
|
+
borderBottomColor: KColors.border.light,
|
|
150
|
+
},
|
|
151
|
+
viewAllButton: {
|
|
152
|
+
height: 40,
|
|
153
|
+
},
|
|
154
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { KContainer, KImage, KLabel, KSpacingValue } from '@droppii/libs';
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
import Images from '../../assets/images';
|
|
4
|
+
import { trans } from '../../translation';
|
|
5
|
+
|
|
6
|
+
export const SearchEmpty = () => {
|
|
7
|
+
return (
|
|
8
|
+
<KContainer.View style={styles.container}>
|
|
9
|
+
<KImage.Base
|
|
10
|
+
uri={Images.SEARCH_EMPTY}
|
|
11
|
+
width={100}
|
|
12
|
+
height={100}
|
|
13
|
+
resizeMode="contain"
|
|
14
|
+
/>
|
|
15
|
+
<KLabel.Text typo="TextMdNormal" textAlign="center">
|
|
16
|
+
{trans('search:empty_description')}
|
|
17
|
+
</KLabel.Text>
|
|
18
|
+
</KContainer.View>
|
|
19
|
+
);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const styles = StyleSheet.create({
|
|
23
|
+
container: {
|
|
24
|
+
paddingHorizontal: KSpacingValue['2rem'],
|
|
25
|
+
paddingVertical: 42,
|
|
26
|
+
justifyContent: 'center',
|
|
27
|
+
alignItems: 'center',
|
|
28
|
+
gap: KSpacingValue['0.75rem'],
|
|
29
|
+
},
|
|
30
|
+
});
|
|
@@ -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;
|
|
@@ -31,9 +31,8 @@ const BotMenuSheetContent = memo(
|
|
|
31
31
|
return (
|
|
32
32
|
<KContainer.View paddingH="0.75rem" paddingV="0.25rem">
|
|
33
33
|
{options?.map((item, index) => (
|
|
34
|
-
|
|
34
|
+
<KContainer.View key={item.id}>
|
|
35
35
|
<KContainer.Touchable
|
|
36
|
-
key={item.id}
|
|
37
36
|
row
|
|
38
37
|
alignItems
|
|
39
38
|
justifyContent="space-between"
|
|
@@ -54,7 +53,7 @@ const BotMenuSheetContent = memo(
|
|
|
54
53
|
<KContainer.VisibleView visible={index !== options?.length - 1}>
|
|
55
54
|
<KDivider size="hairline" />
|
|
56
55
|
</KContainer.VisibleView>
|
|
57
|
-
|
|
56
|
+
</KContainer.View>
|
|
58
57
|
))}
|
|
59
58
|
</KContainer.View>
|
|
60
59
|
);
|
|
@@ -48,9 +48,13 @@ import { useChatComposerState } from './hooks/useChatComposerState';
|
|
|
48
48
|
import { useChatComposerAnimation } from './hooks/useChatComposerAnimation';
|
|
49
49
|
import { useChatDetailContext } from '../../context/ChatDetailContext';
|
|
50
50
|
import { trans } from '../../translation';
|
|
51
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
useCloseSupportSession,
|
|
53
|
+
useResetFlow,
|
|
54
|
+
useRunFlow,
|
|
55
|
+
} from '../../hooks/flows/useFlow';
|
|
52
56
|
import { ReactFlowsNodeTypes, type InputButtonItem } from '../../types/flows';
|
|
53
|
-
import { isContextButton } from '../../utils/flows';
|
|
57
|
+
import { DEFAULT_BUTTON_ID, isContextButton } from '../../utils/flows';
|
|
54
58
|
import { DFlowState } from '../../types/chat';
|
|
55
59
|
import { useAttachmentMapping } from './hooks/useAttachmentMapping';
|
|
56
60
|
import type { AttachmentType } from '../../types/attachment';
|
|
@@ -87,6 +91,7 @@ export const ChatComposer = memo(
|
|
|
87
91
|
flowState,
|
|
88
92
|
} = useChatDetailContext();
|
|
89
93
|
const { mutate: runFlow, isPending } = useRunFlow();
|
|
94
|
+
const { mutate: resetFlow } = useResetFlow();
|
|
90
95
|
const { mutateAsync: closeSupportSession } = useCloseSupportSession();
|
|
91
96
|
const { sendMessage } = useSendMessage();
|
|
92
97
|
const compose = useChatCompose();
|
|
@@ -215,18 +220,32 @@ export const ChatComposer = memo(
|
|
|
215
220
|
});
|
|
216
221
|
}, [runFlow, conversationFlow]);
|
|
217
222
|
|
|
218
|
-
const botMenuOptions = useMemo(
|
|
219
|
-
|
|
223
|
+
const botMenuOptions = useMemo(() => {
|
|
224
|
+
const apiOptions =
|
|
220
225
|
conversationFlowMainMenu?.filter(
|
|
221
226
|
(item) => !isContextButton(item.label)
|
|
222
|
-
) || []
|
|
223
|
-
|
|
224
|
-
|
|
227
|
+
) || [];
|
|
228
|
+
|
|
229
|
+
const hasDefaultButton = apiOptions.some(
|
|
230
|
+
(item) => item.id === DEFAULT_BUTTON_ID
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
if (hasDefaultButton) return apiOptions;
|
|
234
|
+
|
|
235
|
+
return [
|
|
236
|
+
...apiOptions,
|
|
237
|
+
{ id: DEFAULT_BUTTON_ID, label: trans('restart') },
|
|
238
|
+
];
|
|
239
|
+
}, [conversationFlowMainMenu]);
|
|
225
240
|
|
|
226
241
|
const hasBotMenuOptions = isBotConversation && botMenuOptions.length > 0;
|
|
227
242
|
|
|
228
243
|
const runBotMenuOption = useCallback(
|
|
229
244
|
(item: InputButtonItem) => {
|
|
245
|
+
if (item.id === DEFAULT_BUTTON_ID) {
|
|
246
|
+
resetFlow();
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
230
249
|
runFlow({
|
|
231
250
|
actionId: item.id,
|
|
232
251
|
groupId: '',
|
|
@@ -234,7 +253,7 @@ export const ChatComposer = memo(
|
|
|
234
253
|
plainText: item.label,
|
|
235
254
|
});
|
|
236
255
|
},
|
|
237
|
-
[runFlow]
|
|
256
|
+
[runFlow, resetFlow]
|
|
238
257
|
);
|
|
239
258
|
|
|
240
259
|
const handleSelectBotMenuOption = useCallback(
|
|
@@ -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
|
|