@droppii-org/chat-mobile 0.2.80 → 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.
Files changed (95) hide show
  1. package/lib/module/config/api-endpoints.js +2 -1
  2. package/lib/module/config/api-endpoints.js.map +1 -1
  3. package/lib/module/hooks/contacts/useSearchContacts.js +50 -0
  4. package/lib/module/hooks/contacts/useSearchContacts.js.map +1 -0
  5. package/lib/module/hooks/query-keys.js +4 -0
  6. package/lib/module/hooks/query-keys.js.map +1 -1
  7. package/lib/module/screens/SearchConversation/ContactResultItem.js +119 -0
  8. package/lib/module/screens/SearchConversation/ContactResultItem.js.map +1 -0
  9. package/lib/module/screens/SearchConversation/SearchAllResult.js +26 -20
  10. package/lib/module/screens/SearchConversation/SearchAllResult.js.map +1 -1
  11. package/lib/module/screens/SearchConversation/SearchCommunityResult.js +79 -0
  12. package/lib/module/screens/SearchConversation/SearchCommunityResult.js.map +1 -0
  13. package/lib/module/screens/SearchConversation/SearchContactsResult.js +117 -0
  14. package/lib/module/screens/SearchConversation/SearchContactsResult.js.map +1 -0
  15. package/lib/module/screens/SearchConversation/SearchEmpty.js +2 -0
  16. package/lib/module/screens/SearchConversation/SearchEmpty.js.map +1 -1
  17. package/lib/module/screens/SearchConversation/SearchMessagesResult.js +1 -8
  18. package/lib/module/screens/SearchConversation/SearchMessagesResult.js.map +1 -1
  19. package/lib/module/screens/SearchConversation/SearchResultItem.js +2 -0
  20. package/lib/module/screens/SearchConversation/SearchResultItem.js.map +1 -1
  21. package/lib/module/screens/SearchConversation/TabResult.js +24 -7
  22. package/lib/module/screens/SearchConversation/TabResult.js.map +1 -1
  23. package/lib/module/screens/SearchConversation/index.js +6 -2
  24. package/lib/module/screens/SearchConversation/index.js.map +1 -1
  25. package/lib/module/screens/SearchConversation/types.js +1 -0
  26. package/lib/module/screens/SearchConversation/types.js.map +1 -1
  27. package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxWrapper.js +11 -1
  28. package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxWrapper.js.map +1 -1
  29. package/lib/module/services/endpoints.js +2 -1
  30. package/lib/module/services/endpoints.js.map +1 -1
  31. package/lib/module/translation/resources/i18n.js +6 -0
  32. package/lib/module/translation/resources/i18n.js.map +1 -1
  33. package/lib/module/types/chat.js +8 -0
  34. package/lib/module/types/chat.js.map +1 -1
  35. package/lib/module/utils/dateTimeUtils.js +10 -0
  36. package/lib/module/utils/dateTimeUtils.js.map +1 -1
  37. package/lib/module/utils/highlightSearch.js +6 -2
  38. package/lib/module/utils/highlightSearch.js.map +1 -1
  39. package/lib/module/utils/messageUtils.js +10 -2
  40. package/lib/module/utils/messageUtils.js.map +1 -1
  41. package/lib/typescript/src/config/api-endpoints.d.ts +1 -0
  42. package/lib/typescript/src/config/api-endpoints.d.ts.map +1 -1
  43. package/lib/typescript/src/hooks/contacts/useSearchContacts.d.ts +221 -0
  44. package/lib/typescript/src/hooks/contacts/useSearchContacts.d.ts.map +1 -0
  45. package/lib/typescript/src/hooks/query-keys.d.ts +4 -0
  46. package/lib/typescript/src/hooks/query-keys.d.ts.map +1 -1
  47. package/lib/typescript/src/screens/SearchConversation/ContactResultItem.d.ts +9 -0
  48. package/lib/typescript/src/screens/SearchConversation/ContactResultItem.d.ts.map +1 -0
  49. package/lib/typescript/src/screens/SearchConversation/SearchAllResult.d.ts +3 -2
  50. package/lib/typescript/src/screens/SearchConversation/SearchAllResult.d.ts.map +1 -1
  51. package/lib/typescript/src/screens/SearchConversation/SearchCommunityResult.d.ts +7 -0
  52. package/lib/typescript/src/screens/SearchConversation/SearchCommunityResult.d.ts.map +1 -0
  53. package/lib/typescript/src/screens/SearchConversation/SearchContactsResult.d.ts +8 -0
  54. package/lib/typescript/src/screens/SearchConversation/SearchContactsResult.d.ts.map +1 -0
  55. package/lib/typescript/src/screens/SearchConversation/SearchEmpty.d.ts.map +1 -1
  56. package/lib/typescript/src/screens/SearchConversation/SearchMessagesResult.d.ts.map +1 -1
  57. package/lib/typescript/src/screens/SearchConversation/SearchResultItem.d.ts.map +1 -1
  58. package/lib/typescript/src/screens/SearchConversation/TabResult.d.ts +4 -2
  59. package/lib/typescript/src/screens/SearchConversation/TabResult.d.ts.map +1 -1
  60. package/lib/typescript/src/screens/SearchConversation/index.d.ts +4 -2
  61. package/lib/typescript/src/screens/SearchConversation/index.d.ts.map +1 -1
  62. package/lib/typescript/src/screens/SearchConversation/types.d.ts +2 -1
  63. package/lib/typescript/src/screens/SearchConversation/types.d.ts.map +1 -1
  64. package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.d.ts.map +1 -1
  65. package/lib/typescript/src/services/endpoints.d.ts +1 -0
  66. package/lib/typescript/src/services/endpoints.d.ts.map +1 -1
  67. package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
  68. package/lib/typescript/src/types/chat.d.ts +27 -0
  69. package/lib/typescript/src/types/chat.d.ts.map +1 -1
  70. package/lib/typescript/src/utils/dateTimeUtils.d.ts +2 -0
  71. package/lib/typescript/src/utils/dateTimeUtils.d.ts.map +1 -1
  72. package/lib/typescript/src/utils/highlightSearch.d.ts.map +1 -1
  73. package/lib/typescript/src/utils/messageUtils.d.ts.map +1 -1
  74. package/package.json +3 -1
  75. package/src/build-ignore.d.ts +2 -0
  76. package/src/config/api-endpoints.ts +2 -0
  77. package/src/hooks/contacts/useSearchContacts.ts +66 -0
  78. package/src/hooks/query-keys.ts +6 -0
  79. package/src/screens/SearchConversation/ContactResultItem.tsx +128 -0
  80. package/src/screens/SearchConversation/SearchAllResult.tsx +38 -24
  81. package/src/screens/SearchConversation/SearchCommunityResult.tsx +96 -0
  82. package/src/screens/SearchConversation/SearchContactsResult.tsx +155 -0
  83. package/src/screens/SearchConversation/SearchEmpty.tsx +2 -0
  84. package/src/screens/SearchConversation/SearchMessagesResult.tsx +1 -6
  85. package/src/screens/SearchConversation/SearchResultItem.tsx +6 -1
  86. package/src/screens/SearchConversation/TabResult.tsx +40 -5
  87. package/src/screens/SearchConversation/index.tsx +10 -2
  88. package/src/screens/SearchConversation/types.tsx +1 -0
  89. package/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.tsx +22 -4
  90. package/src/services/endpoints.ts +1 -0
  91. package/src/translation/resources/i18n.ts +6 -0
  92. package/src/types/chat.ts +30 -0
  93. package/src/utils/dateTimeUtils.ts +15 -0
  94. package/src/utils/highlightSearch.ts +12 -2
  95. package/src/utils/messageUtils.ts +15 -9
@@ -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 type { ISearchMessageItem } from '../../types/chat';
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
- // { key: SearchConversationTabResultType.Conversations, title: trans('contacts') },
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
- ({ searchTerm, onPressItem }: ISearchConversationTabResultProps) => {
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 null;
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
- [searchTerm, handleChangeTab, onPressItem]
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
- ({ onPressItem }: SearchConversationScreenProps) => {
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
  );
@@ -2,4 +2,5 @@ export enum SearchConversationTabResultType {
2
2
  All = 'all',
3
3
  Conversations = 'conversations',
4
4
  Messages = 'messages',
5
+ Community = 'community',
5
6
  }
@@ -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
- rect.height +
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 pointerEvents="none">{message}</View>
93
- <View onLayout={onReactionLayout}>{reaction}</View>
94
- <View onLayout={onToolboxLayout}>{toolbox}</View>
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',
package/src/types/chat.ts CHANGED
@@ -341,3 +341,33 @@ export interface ISearchMessageItem {
341
341
  chatLog: MessageItem;
342
342
  conversation: ConversationItem;
343
343
  }
344
+
345
+ export enum ContactCategory {
346
+ Customer = 'CUSTOMER',
347
+ Pdp = 'PDP_BOT',
348
+ Group = 'GROUP',
349
+ Seller = 'USER',
350
+ }
351
+
352
+ export type IContactSearchRequest = {
353
+ applicationType: DChatApplicationType;
354
+ searchTerm: string;
355
+ page: number;
356
+ pageSize: number;
357
+ type?: ContactCategory;
358
+ };
359
+
360
+ export interface IContactSearchItem {
361
+ id: string;
362
+ type: ContactCategory;
363
+ targetUserId?: string;
364
+ targetCustomerId?: string;
365
+ targetGroupId?: string;
366
+ targetBotId?: string;
367
+ targetName: string;
368
+ targetAvatar?: string;
369
+ applicationType: DChatApplicationType;
370
+ lastOnlineTime?: string;
371
+ memberCount?: number;
372
+ description?: string;
373
+ }
@@ -2,6 +2,12 @@
2
2
  * Date and time formatting utilities
3
3
  */
4
4
 
5
+ import dayjs from 'dayjs';
6
+ import relativeTime from 'dayjs/plugin/relativeTime';
7
+ import 'dayjs/locale/vi';
8
+
9
+ dayjs.extend(relativeTime);
10
+
5
11
  export const formatDuration = (durationMs: number): string => {
6
12
  // Convert milliseconds to seconds
7
13
  const totalSeconds = Math.floor(durationMs / 1000);
@@ -46,3 +52,12 @@ export const formatTimestamp = (ts: number | string | null): string => {
46
52
  return '';
47
53
  }
48
54
  };
55
+
56
+ export const formatLastOnlineTime = (ts: number | string | null): string => {
57
+ if (!ts) return '';
58
+
59
+ const date = dayjs(ts).locale('vi');
60
+ if (!date.isValid()) return '';
61
+
62
+ return date.fromNow();
63
+ };
@@ -4,6 +4,16 @@ export interface HighlightResult {
4
4
  after: string;
5
5
  }
6
6
 
7
+ // Strips Vietnamese diacritics one base-character-at-a-time (NFD decomposition +
8
+ // combining-mark removal, plus đ/Đ which NFD doesn't decompose) so the result stays
9
+ // the same length and index-aligned with the input
10
+ const stripDiacritics = (input: string): string =>
11
+ input
12
+ .normalize('NFD')
13
+ .replace(/[\u0300-\u036f]/g, '')
14
+ .replace(/đ/g, 'd')
15
+ .replace(/Đ/g, 'D');
16
+
7
17
  export const highlightSearch = (
8
18
  text: string,
9
19
  keyword: string,
@@ -11,8 +21,8 @@ export const highlightSearch = (
11
21
  ): HighlightResult | null => {
12
22
  if (!keyword) return null;
13
23
 
14
- const lowerText = text.toLowerCase();
15
- const lowerKeyword = keyword.toLowerCase();
24
+ const lowerText = stripDiacritics(text.toLowerCase());
25
+ const lowerKeyword = stripDiacritics(keyword.toLowerCase());
16
26
  const index = lowerText.indexOf(lowerKeyword);
17
27
 
18
28
  if (index === -1) return null;
@@ -442,15 +442,21 @@ export const getSenderName = (
442
442
  currentUserId?: string,
443
443
  conversation?: ConversationItem
444
444
  ): string => {
445
- const sender =
446
- currentUserId && message.sendID === currentUserId
447
- ? 'Bạn'
448
- : message?.senderNickname ||
449
- getNotificationOpUserNickname(message) ||
450
- (conversation?.peerType === PeerType.Bot
451
- ? conversation?.showName || ''
452
- : undefined);
453
- return sender || '';
445
+ const isCurrentUser = currentUserId && message.sendID === currentUserId;
446
+
447
+ if (isCurrentUser) return 'Bạn';
448
+
449
+ switch (conversation?.peerType) {
450
+ case PeerType.Bot:
451
+ case PeerType.Customer:
452
+ case PeerType.User:
453
+ return '';
454
+
455
+ default:
456
+ return (
457
+ message?.senderNickname || getNotificationOpUserNickname(message) || ''
458
+ );
459
+ }
454
460
  };
455
461
 
456
462
  export interface GetLastMessageTextParams {