@ermis-network/ermis-chat-react 1.0.9 → 2.0.1

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 (113) hide show
  1. package/README.md +144 -0
  2. package/dist/index.cjs +8320 -3427
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.css +1277 -291
  5. package/dist/index.css.map +1 -1
  6. package/dist/index.d.mts +1131 -99
  7. package/dist/index.d.ts +1131 -99
  8. package/dist/index.mjs +8168 -3319
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +9 -4
  11. package/src/channelTypeUtils.ts +1 -1
  12. package/src/components/Avatar.tsx +2 -1
  13. package/src/components/Channel.tsx +6 -5
  14. package/src/components/ChannelActions.tsx +67 -3
  15. package/src/components/ChannelHeader.tsx +27 -37
  16. package/src/components/ChannelInfo/AddMemberModal.tsx +12 -2
  17. package/src/components/ChannelInfo/ChannelInfo.tsx +410 -187
  18. package/src/components/ChannelInfo/ChannelInfoTabs.tsx +59 -297
  19. package/src/components/ChannelInfo/ChannelSettingsPanel.tsx +30 -174
  20. package/src/components/ChannelInfo/EditChannelModal.tsx +6 -3
  21. package/src/components/ChannelInfo/MediaGridItem.tsx +215 -68
  22. package/src/components/ChannelInfo/MemberListItem.tsx +2 -3
  23. package/src/components/ChannelInfo/MessageSearchPanel.tsx +27 -126
  24. package/src/components/ChannelInfo/States.tsx +1 -1
  25. package/src/components/ChannelInfo/index.ts +3 -0
  26. package/src/components/ChannelInfo/useChannelInfoTabs.tsx +427 -0
  27. package/src/components/ChannelInfo/useChannelSettings.ts +212 -0
  28. package/src/components/ChannelInfo/useMessageSearch.tsx +141 -0
  29. package/src/components/ChannelList.tsx +247 -301
  30. package/src/components/CreateChannelModal.tsx +290 -93
  31. package/src/components/Dropdown.tsx +1 -16
  32. package/src/components/EditPreview.tsx +1 -0
  33. package/src/components/ErmisCallProvider.tsx +72 -17
  34. package/src/components/ErmisCallUI.tsx +43 -20
  35. package/src/components/FilesPreview.tsx +8 -12
  36. package/src/components/FlatTopicGroupItem.tsx +243 -0
  37. package/src/components/ForwardMessageModal.tsx +43 -81
  38. package/src/components/MediaLightbox.tsx +454 -292
  39. package/src/components/MentionSuggestions.tsx +47 -35
  40. package/src/components/MessageActionsBox.tsx +6 -1
  41. package/src/components/MessageInput.tsx +165 -17
  42. package/src/components/MessageInputDefaults.tsx +127 -1
  43. package/src/components/MessageItem.tsx +155 -43
  44. package/src/components/MessageQuickReactions.tsx +153 -23
  45. package/src/components/MessageReactions.tsx +49 -3
  46. package/src/components/MessageRenderers.tsx +1114 -445
  47. package/src/components/Panel.tsx +1 -14
  48. package/src/components/PinnedMessages.tsx +55 -15
  49. package/src/components/PreviewOverlay.tsx +24 -0
  50. package/src/components/QuotedMessagePreview.tsx +99 -8
  51. package/src/components/ReadReceipts.tsx +2 -1
  52. package/src/components/RecoveryPin/RecoveryPin.tsx +279 -0
  53. package/src/components/RecoveryPin/index.ts +19 -0
  54. package/src/components/TopicList.tsx +236 -0
  55. package/src/components/TopicModal.tsx +4 -1
  56. package/src/components/TypingIndicator.tsx +17 -8
  57. package/src/components/UserPicker.tsx +94 -16
  58. package/src/components/VirtualMessageList.tsx +419 -113
  59. package/src/context/ChatComponentsContext.tsx +14 -0
  60. package/src/context/ChatProvider.tsx +44 -14
  61. package/src/context/ErmisCallContext.tsx +4 -0
  62. package/src/hooks/useChannelCapabilities.ts +7 -4
  63. package/src/hooks/useChannelData.ts +10 -3
  64. package/src/hooks/useChannelListUpdates.ts +94 -21
  65. package/src/hooks/useChannelMessages.ts +391 -42
  66. package/src/hooks/useChannelRowUpdates.ts +36 -5
  67. package/src/hooks/useChatUser.ts +39 -0
  68. package/src/hooks/useContactChannels.ts +45 -0
  69. package/src/hooks/useContactCount.ts +50 -0
  70. package/src/hooks/useDownloadHandler.ts +36 -0
  71. package/src/hooks/useDragAndDrop.ts +79 -0
  72. package/src/hooks/useE2eeAttachmentRenderer.ts +204 -0
  73. package/src/hooks/useE2eeFileUpload.ts +38 -0
  74. package/src/hooks/useFileUpload.ts +25 -5
  75. package/src/hooks/useForwardMessage.ts +309 -0
  76. package/src/hooks/useInviteChannels.ts +88 -0
  77. package/src/hooks/useInviteCount.ts +104 -0
  78. package/src/hooks/useLoadMessages.ts +16 -4
  79. package/src/hooks/useMentions.ts +60 -7
  80. package/src/hooks/useMessageActions.ts +19 -10
  81. package/src/hooks/useMessageSend.ts +64 -12
  82. package/src/hooks/usePendingE2eeSends.ts +29 -0
  83. package/src/hooks/usePendingState.ts +21 -4
  84. package/src/hooks/usePreviewState.ts +69 -0
  85. package/src/hooks/useRecoveryPin.ts +287 -0
  86. package/src/hooks/useScrollToMessage.ts +29 -4
  87. package/src/hooks/useStickerPicker.ts +62 -0
  88. package/src/hooks/useTopicGroupUpdates.ts +235 -0
  89. package/src/index.ts +79 -6
  90. package/src/messageTypeUtils.ts +27 -1
  91. package/src/styles/_base.css +0 -1
  92. package/src/styles/_call-ui.css +59 -2
  93. package/src/styles/_channel-info.css +50 -4
  94. package/src/styles/_channel-list.css +131 -68
  95. package/src/styles/_create-channel-modal.css +10 -0
  96. package/src/styles/_forward-modal.css +16 -1
  97. package/src/styles/_media-lightbox.css +67 -2
  98. package/src/styles/_mentions.css +1 -1
  99. package/src/styles/_message-actions.css +3 -4
  100. package/src/styles/_message-bubble.css +631 -112
  101. package/src/styles/_message-input.css +139 -0
  102. package/src/styles/_message-list.css +91 -18
  103. package/src/styles/_message-quick-reactions.css +105 -32
  104. package/src/styles/_message-reactions.css +22 -32
  105. package/src/styles/_modal.css +2 -1
  106. package/src/styles/_preview-overlay.css +38 -0
  107. package/src/styles/_recovery-pin.css +97 -0
  108. package/src/styles/_tokens.css +22 -20
  109. package/src/styles/_typing-indicator.css +26 -10
  110. package/src/styles/index.css +2 -0
  111. package/src/types.ts +477 -15
  112. package/src/utils/avatarColors.ts +48 -0
  113. package/src/utils.ts +219 -16
@@ -0,0 +1,141 @@
1
+ import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
2
+ import type { Channel } from '@ermis-network/ermis-chat-sdk';
3
+ import { buildUserMap } from '../../utils';
4
+ import type { SearchResultMessage } from '../../types';
5
+
6
+ export type UseMessageSearchProps = {
7
+ channel: Channel;
8
+ isOpen: boolean;
9
+ debounceMs?: number;
10
+ };
11
+
12
+ export const useMessageSearch = ({ channel, isOpen, debounceMs = 500 }: UseMessageSearchProps) => {
13
+ const [query, setQuery] = useState('');
14
+ const [results, setResults] = useState<SearchResultMessage[]>([]);
15
+ const [loading, setLoading] = useState(false);
16
+ const [hasMore, setHasMore] = useState(false);
17
+ const [loadingMore, setLoadingMore] = useState(false);
18
+
19
+ const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
20
+ const offsetRef = useRef(0);
21
+ const queryRef = useRef('');
22
+
23
+ // Reset all state when the channel changes (or panel closes)
24
+ useEffect(() => {
25
+ setQuery('');
26
+ setResults([]);
27
+ setLoading(false);
28
+ setHasMore(false);
29
+ setLoadingMore(false);
30
+ offsetRef.current = 0;
31
+ queryRef.current = '';
32
+ }, [channel?.cid, isOpen]);
33
+
34
+ // Debounced search
35
+ const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
36
+ const value = e.target.value;
37
+ setQuery(value);
38
+
39
+ if (debounceRef.current) clearTimeout(debounceRef.current);
40
+
41
+ if (!value.trim()) {
42
+ setResults([]);
43
+ setLoading(false);
44
+ setHasMore(false);
45
+ offsetRef.current = 0;
46
+ queryRef.current = '';
47
+ return;
48
+ }
49
+
50
+ setLoading(true);
51
+
52
+ debounceRef.current = setTimeout(async () => {
53
+ queryRef.current = value;
54
+ offsetRef.current = 0;
55
+
56
+ try {
57
+ const response = await channel.searchMessage(value, 0);
58
+ // Only apply if this is still the latest query
59
+ if (queryRef.current !== value) return;
60
+
61
+ if (!response) {
62
+ setResults([]);
63
+ setHasMore(false);
64
+ } else {
65
+ setResults(response.messages || []);
66
+ setHasMore((response.messages?.length || 0) >= 25);
67
+ }
68
+ } catch (err) {
69
+ console.error('Search failed:', err);
70
+ setResults([]);
71
+ setHasMore(false);
72
+ } finally {
73
+ setLoading(false);
74
+ }
75
+ }, debounceMs);
76
+ }, [channel, debounceMs]);
77
+
78
+ const resetSearch = useCallback(() => {
79
+ setQuery('');
80
+ setResults([]);
81
+ setHasMore(false);
82
+ offsetRef.current = 0;
83
+ queryRef.current = '';
84
+ }, []);
85
+
86
+ // Infinite scroll: load more results
87
+ const handleLoadMore = useCallback(async () => {
88
+ if (loadingMore || !hasMore || !queryRef.current) return;
89
+
90
+ setLoadingMore(true);
91
+ const nextOffset = offsetRef.current + 25; // offset skips records, limit is 25
92
+
93
+ try {
94
+ const response = await channel.searchMessage(queryRef.current, nextOffset);
95
+
96
+ if (!response || !response.messages?.length) {
97
+ setHasMore(false);
98
+ } else {
99
+ offsetRef.current = nextOffset;
100
+ setResults((prev) => [...prev, ...response.messages]);
101
+ setHasMore(response.messages.length >= 25);
102
+ }
103
+ } catch (err) {
104
+ console.error('Load more search results failed:', err);
105
+ } finally {
106
+ setLoadingMore(false);
107
+ }
108
+ }, [channel, hasMore, loadingMore]);
109
+
110
+ // Scroll handler for infinite scroll container
111
+ const handleScroll = useCallback((e: React.UIEvent<HTMLElement>) => {
112
+ const el = e.currentTarget;
113
+ const threshold = 100;
114
+ if (el.scrollTop + el.clientHeight >= el.scrollHeight - threshold) {
115
+ handleLoadMore();
116
+ }
117
+ }, [handleLoadMore]);
118
+
119
+ // Derived userMap for resolving mentions, with a lowercase variant for fast lookup
120
+ const userMaps = useMemo(() => {
121
+ const original = buildUserMap(channel.state);
122
+ const lower: typeof original = {};
123
+ for (const [id, name] of Object.entries(original)) {
124
+ lower[id.toLowerCase()] = name;
125
+ }
126
+ return { original, lower };
127
+ }, [channel.state]);
128
+
129
+ return {
130
+ query,
131
+ setQuery,
132
+ results,
133
+ loading,
134
+ hasMore,
135
+ loadingMore,
136
+ handleInputChange,
137
+ handleScroll,
138
+ resetSearch,
139
+ userMaps,
140
+ };
141
+ };