@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
@@ -1,11 +1,11 @@
1
1
  import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
2
- import { createForwardMessagePayload } from '@ermis-network/ermis-chat-sdk';
3
- import type { Channel } from '@ermis-network/ermis-chat-sdk';
4
2
  import { useChatClient } from '../hooks/useChatClient';
5
3
  import { Avatar } from './Avatar';
6
- import { Modal } from './Modal';
7
- import type { ForwardMessageModalProps, ForwardChannelItemProps, AvatarProps } from '../types';
4
+ import { Modal as DefaultModal } from './Modal';
5
+ import { useChatComponents } from '../context/ChatComponentsContext';
6
+ import type { ForwardMessageModalProps, ForwardChannelItemProps } from '../types';
8
7
  import { isTopicChannel } from '../channelTypeUtils';
8
+ import { useForwardMessage } from '../hooks/useForwardMessage';
9
9
 
10
10
  export type { ForwardMessageModalProps, ForwardChannelItemProps } from '../types';
11
11
 
@@ -18,12 +18,20 @@ const DefaultForwardChannelItem: React.FC<ForwardChannelItemProps> = React.memo(
18
18
  onToggle,
19
19
  AvatarComponent,
20
20
  }) => {
21
+ const { client } = useChatClient();
22
+ const isTopic = isTopicChannel(channel);
23
+ const parentCid = channel.data?.parent_cid as string | undefined;
24
+ const parent = parentCid ? client.activeChannels[parentCid] : null;
25
+ const parentName = parent?.data?.name || '';
26
+
21
27
  const name = (channel.data?.name || channel.cid) as string;
22
28
  const rawImage = channel.data?.image as string | undefined;
23
29
  // Parse emoji:// format → extract just the emoji for avatar fallback
24
30
  const isEmoji = rawImage?.startsWith('emoji://');
25
31
  const image = isEmoji ? undefined : rawImage;
26
- const emojiIcon = isEmoji ? rawImage!.replace('emoji://', '') : undefined;
32
+
33
+ // Use # for topics without explicit emoji/image
34
+ const emojiIcon = isEmoji ? rawImage!.replace('emoji://', '') : (isTopic && !image ? '#' : undefined);
27
35
 
28
36
  return (
29
37
  <div
@@ -35,7 +43,12 @@ const DefaultForwardChannelItem: React.FC<ForwardChannelItemProps> = React.memo(
35
43
  ) : (
36
44
  <AvatarComponent image={image} name={name} size={36} />
37
45
  )}
38
- <span className="ermis-forward-modal__channel-name">{name}</span>
46
+ <div className="ermis-forward-modal__channel-name-container">
47
+ {isTopic && parentName && (
48
+ <span className="ermis-forward-modal__channel-parent-name">{parentName}</span>
49
+ )}
50
+ <span className="ermis-forward-modal__channel-name">{name}</span>
51
+ </div>
39
52
  <div className={`ermis-forward-modal__checkbox ${selected ? 'ermis-forward-modal__checkbox--checked' : ''}`}>
40
53
  {selected && (
41
54
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
@@ -57,79 +70,21 @@ export const ForwardMessageModal: React.FC<ForwardMessageModalProps> = ({
57
70
  ChannelItemComponent = DefaultForwardChannelItem,
58
71
  SearchInputComponent,
59
72
  }) => {
60
- const { client, activeChannel } = useChatClient();
61
- const [selectedChannels, setSelectedChannels] = useState<Set<string>>(new Set());
62
- const [search, setSearch] = useState('');
63
- const [sending, setSending] = useState(false);
64
- const [results, setResults] = useState<{ success: string[]; failed: string[] } | null>(null);
73
+ const { ModalComponent } = useChatComponents();
74
+ const Modal = ModalComponent || DefaultModal;
65
75
  const backdropRef = useRef<HTMLDivElement>(null);
66
-
67
- /* ---------- Get channels from client state (exclude topics) ---------- */
68
- const channels = useMemo(() => {
69
- return (Object.values(client.activeChannels) as Channel[]).filter(
70
- (ch) => !isTopicChannel(ch),
71
- );
72
- }, [client.activeChannels]);
73
-
74
- /* ---------- Filter by search ---------- */
75
- const filteredChannels = useMemo(() => {
76
- if (!search.trim()) return channels;
77
- const q = search.toLowerCase();
78
- return channels.filter((ch) => {
79
- const name = ((ch.data?.name || ch.cid) as string).toLowerCase();
80
- return name.includes(q);
81
- });
82
- }, [channels, search]);
83
-
84
- /* ---------- Toggle selection ---------- */
85
- const toggleChannel = useCallback((channel: Channel) => {
86
- setSelectedChannels((prev) => {
87
- const next = new Set(prev);
88
- if (next.has(channel.cid)) {
89
- next.delete(channel.cid);
90
- } else {
91
- next.add(channel.cid);
92
- }
93
- return next;
94
- });
95
- }, []);
96
-
97
- /* ---------- Send forward ---------- */
98
- const handleSend = useCallback(async () => {
99
- if (!activeChannel || selectedChannels.size === 0 || sending) return;
100
- setSending(true);
101
- const success: string[] = [];
102
- const failed: string[] = [];
103
-
104
- for (const cid of selectedChannels) {
105
- const targetChannel = channels.find((c) => c.cid === cid);
106
- if (!targetChannel) continue;
107
- try {
108
- const forwardPayload = createForwardMessagePayload(
109
- message,
110
- targetChannel.cid as string,
111
- activeChannel.cid as string,
112
- );
113
-
114
- await activeChannel.forwardMessage(forwardPayload, {
115
- type: targetChannel.type,
116
- channelID: targetChannel.id!,
117
- });
118
- success.push((targetChannel.data?.name || targetChannel.cid) as string);
119
- } catch (err) {
120
- console.error(`Failed to forward to ${cid}`, err);
121
- failed.push((targetChannel.data?.name || targetChannel.cid) as string);
122
- }
123
- }
124
-
125
- setResults({ success, failed });
126
- setSending(false);
127
-
128
- // Auto-close after success (short delay)
129
- if (failed.length === 0) {
130
- setTimeout(() => onDismiss(), 1200);
131
- }
132
- }, [activeChannel, selectedChannels, channels, message, sending, onDismiss]);
76
+ const { client } = useChatClient();
77
+
78
+ const {
79
+ search,
80
+ setSearch,
81
+ selectedChannels,
82
+ toggleChannel,
83
+ sending,
84
+ results,
85
+ filteredChannels,
86
+ handleSend,
87
+ } = useForwardMessage(message, onDismiss);
133
88
 
134
89
  /* ---------- Keyboard / backdrop close ---------- */
135
90
  useEffect(() => {
@@ -145,9 +100,16 @@ export const ForwardMessageModal: React.FC<ForwardMessageModalProps> = ({
145
100
  }, [onDismiss]);
146
101
 
147
102
  /* ---------- Message preview ---------- */
148
- const previewText = message.text
149
- ? (message.text.length > 120 ? message.text.slice(0, 120) + '…' : message.text)
150
- : '';
103
+ let previewText = message.text || '';
104
+
105
+ if (previewText && message.mentioned_users && message.mentioned_users.length > 0) {
106
+ message.mentioned_users.forEach((userId) => {
107
+ const name = client.state.users[userId]?.name || userId;
108
+ previewText = previewText.replace(new RegExp(`@${userId}`, 'g'), `@${name}`);
109
+ });
110
+ }
111
+
112
+ previewText = previewText.length > 120 ? previewText.slice(0, 120) + '…' : previewText;
151
113
  const attachmentCount = message.attachments?.length ?? 0;
152
114
 
153
115
  const footer = (