@aslaluroba/help-center-react 3.2.1 → 3.2.3

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.
@@ -17,6 +17,7 @@ interface ChatWindowProps {
17
17
  assistantStatus: string;
18
18
  needsAgent: boolean;
19
19
  isAblyConnected: boolean;
20
+ sessionId?: string | null;
20
21
  }
21
22
 
22
23
  // Memoize individual message component to prevent unnecessary re-renders
@@ -34,7 +35,7 @@ const MessageComponent = React.memo(
34
35
  messages: Message[];
35
36
  firstHumanAgentIndex: number;
36
37
  onType: () => void;
37
- onImageClick: (attachmentIds: string[], clickedIndex: number) => void;
38
+ onImageClick: (attachmentIdsOrUrls: string[], clickedIndex: number) => void;
38
39
  }) => {
39
40
  const isFirstInSequence = index === 0 || messages[index - 1].senderType !== message.senderType;
40
41
  const isFirstHumanAgentMessage = index === firstHumanAgentIndex && message.senderType === 2;
@@ -42,11 +43,13 @@ const MessageComponent = React.memo(
42
43
 
43
44
  const handleImageClick = useCallback(
44
45
  (clickedIndex: number) => {
45
- if (message.attachmentIds && message.attachmentIds.length > 0) {
46
- onImageClick(message.attachmentIds, clickedIndex);
46
+ // Use attachmentUrls if available (from Ably), otherwise use attachmentIds (user-sent)
47
+ const attachments = message.attachmentUrls || message.attachmentIds || [];
48
+ if (attachments.length > 0) {
49
+ onImageClick(attachments, clickedIndex);
47
50
  }
48
51
  },
49
- [message.attachmentIds, onImageClick]
52
+ [message.attachmentIds, message.attachmentUrls, onImageClick]
50
53
  );
51
54
 
52
55
  return (
@@ -75,6 +78,20 @@ const MessageComponent = React.memo(
75
78
  {!isFirstInSequence && <div className='babylai-flex-shrink-0 babylai-me-3 babylai-w-8'></div>}
76
79
 
77
80
  <div className='babylai-flex babylai-flex-col babylai-gap-2'>
81
+ {/* Display attachment URLs (from Ably) */}
82
+ {message.attachmentUrls && message.attachmentUrls.length > 0 && (
83
+ <div className='babylai-flex babylai-flex-row babylai-flex-wrap babylai-gap-2 babylai-max-w-full'>
84
+ {message.attachmentUrls.map((attachmentUrl, imgIndex) => (
85
+ <ImageAttachment
86
+ key={attachmentUrl}
87
+ imageUrl={attachmentUrl}
88
+ enablePreview={false}
89
+ onClick={() => handleImageClick(imgIndex)}
90
+ />
91
+ ))}
92
+ </div>
93
+ )}
94
+ {/* Display attachment IDs (user-sent messages) */}
78
95
  {message.attachmentIds && message.attachmentIds.length > 0 && (
79
96
  <div className='babylai-flex babylai-flex-row babylai-flex-wrap babylai-gap-2 babylai-max-w-full'>
80
97
  {message.attachmentIds.map((attachmentId, imgIndex) => (
@@ -87,12 +104,15 @@ const MessageComponent = React.memo(
87
104
  ))}
88
105
  </div>
89
106
  )}
90
- <AgentResponse
91
- messageContent={message.messageContent}
92
- senderType={message.senderType}
93
- messageId={message.id}
94
- onType={onType}
95
- />
107
+ {/* Only show chat bubble if there's message content */}
108
+ {message.messageContent && message.messageContent.trim() !== '' && (
109
+ <AgentResponse
110
+ messageContent={message.messageContent}
111
+ senderType={message.senderType}
112
+ messageId={message.id}
113
+ onType={onType}
114
+ />
115
+ )}
96
116
  </div>
97
117
  </div>
98
118
  </div>
@@ -123,7 +143,14 @@ const TypingIndicator = React.memo(({ firstHumanAgentIndex }: { firstHumanAgentI
123
143
  TypingIndicator.displayName = 'TypingIndicator';
124
144
 
125
145
  export const ChatWindow = React.memo(
126
- ({ onSendMessage, onEnsureSession, messages, assistantStatus = 'loading', needsAgent }: ChatWindowProps) => {
146
+ ({
147
+ onSendMessage,
148
+ onEnsureSession,
149
+ messages,
150
+ assistantStatus = 'loading',
151
+ needsAgent,
152
+ sessionId,
153
+ }: ChatWindowProps) => {
127
154
  const { i18n } = useLocalTranslation();
128
155
  const [inputMessage, setInputMessage] = useState('');
129
156
  const messagesEndRef = useRef<HTMLDivElement>(null);
@@ -172,7 +199,8 @@ export const ChatWindow = React.memo(
172
199
 
173
200
  const handleSendMessage = useCallback(
174
201
  (attachmentIds: string[]) => {
175
- if (inputMessage.trim()) {
202
+ // Allow sending if there's text OR attachments
203
+ if (inputMessage.trim() || attachmentIds.length > 0) {
176
204
  onSendMessage(inputMessage, attachmentIds);
177
205
  setInputMessage('');
178
206
  }
@@ -187,14 +215,36 @@ export const ChatWindow = React.memo(
187
215
 
188
216
  // Handle image gallery opening
189
217
  const handleImageClick = useCallback(
190
- async (attachmentIds: string[], clickedIndex: number) => {
191
- if (!attachmentIds || attachmentIds.length === 0) {
218
+ async (attachmentIdsOrUrls: string[], clickedIndex: number) => {
219
+ if (!attachmentIdsOrUrls || attachmentIdsOrUrls.length === 0) {
192
220
  return;
193
221
  }
194
222
 
195
223
  try {
196
- // Fetch all image URLs with comprehensive error handling
197
- const imageUrlPromises = attachmentIds.map((fileId) => {
224
+ // Check if the first item is a URL (starts with http:// or https://)
225
+ // If so, they're all URLs from Ably and can be used directly
226
+ const isUrl = attachmentIdsOrUrls[0]?.startsWith('http://') || attachmentIdsOrUrls[0]?.startsWith('https://');
227
+
228
+ let imageUrls: string[];
229
+
230
+ if (isUrl) {
231
+ // These are already URLs from Ably, use them directly (no async needed)
232
+ imageUrls = attachmentIdsOrUrls.filter((url): url is string => url !== null && url.length > 0);
233
+
234
+ // Open gallery immediately with URLs
235
+ if (imageUrls.length > 0) {
236
+ const adjustedIndex = Math.max(0, Math.min(clickedIndex, imageUrls.length - 1));
237
+ setGalleryState({
238
+ isOpen: true,
239
+ imageUrls,
240
+ initialIndex: adjustedIndex,
241
+ });
242
+ }
243
+ return; // Exit early since we don't need to fetch anything
244
+ }
245
+
246
+ // These are file IDs, need to fetch URLs using presignDownload
247
+ const imageUrlPromises = attachmentIdsOrUrls.map((fileId) => {
198
248
  if (!fileId || typeof fileId !== 'string') {
199
249
  return Promise.resolve(null);
200
250
  }
@@ -212,7 +262,7 @@ export const ChatWindow = React.memo(
212
262
  });
213
263
  });
214
264
 
215
- const imageUrls = (await Promise.all(imageUrlPromises)).filter(
265
+ imageUrls = (await Promise.all(imageUrlPromises)).filter(
216
266
  (url): url is string => url !== null && url.length > 0
217
267
  );
218
268
 
@@ -221,7 +271,7 @@ export const ChatWindow = React.memo(
221
271
  }
222
272
 
223
273
  // Adjust the initial index if some images failed to load
224
- let adjustedIndex = Math.max(0, Math.min(clickedIndex, imageUrls.length - 1));
274
+ const adjustedIndex = Math.max(0, Math.min(clickedIndex, imageUrls.length - 1));
225
275
 
226
276
  setGalleryState({
227
277
  isOpen: true,
@@ -288,6 +338,7 @@ export const ChatWindow = React.memo(
288
338
  setInputMessage={setInputMessage}
289
339
  isLoading={isLoading}
290
340
  onEnsureSession={onEnsureSession}
341
+ sessionId={sessionId}
291
342
  />
292
343
 
293
344
  {/* Gallery Preview Dialog */}
@@ -83,7 +83,8 @@ const HelpCenterContent = ({
83
83
  messageContent: message,
84
84
  sentAt: new Date(),
85
85
  isSeen: true,
86
- ...(attachments.length > 0 && { attachmentIds: attachments }),
86
+ // Ably now returns downloadUrls directly, not file IDs
87
+ ...(attachments.length > 0 && { attachmentUrls: attachments }),
87
88
  };
88
89
 
89
90
  return [...prevMessages, newMessage];
@@ -216,11 +217,12 @@ const HelpCenterContent = ({
216
217
 
217
218
  const handleEnsureSession = async (): Promise<string> => {
218
219
  // If we already have a session ID and connection, return it
219
- if (sessionId && isAblyConnected) {
220
+ // NEVER create a new session if one already exists
221
+ if (sessionId) {
220
222
  return sessionId;
221
223
  }
222
224
 
223
- // If we have a selected option but no session, create one
225
+ // Only create a new session if we don't have one and have a selected option
224
226
  if (selectedOption) {
225
227
  const newSessionId = await startNewChatSession(selectedOption);
226
228
  return newSessionId;
@@ -230,14 +232,15 @@ const HelpCenterContent = ({
230
232
  };
231
233
 
232
234
  const handleSendMessage = async (message: string, attachmentIds: string[] = []) => {
233
- if (message.trim() !== '') {
235
+ // Allow sending if there's text OR attachments
236
+ if (message.trim() !== '' || attachmentIds.length > 0) {
234
237
  try {
235
238
  setAssistantStatus('typing');
236
239
 
237
240
  const userMessage: Message = {
238
241
  id: Date.now(),
239
242
  senderType: 1,
240
- messageContent: message,
243
+ messageContent: message || '', // Use empty string if message is empty but attachments exist
241
244
  sentAt: new Date(),
242
245
  isSeen: false,
243
246
  attachmentIds: attachmentIds.length > 0 ? attachmentIds : undefined,
@@ -245,10 +248,12 @@ const HelpCenterContent = ({
245
248
 
246
249
  setMessages((prevMessages) => [...prevMessages, userMessage]);
247
250
 
248
- // Handle session creation if needed
251
+ // Handle session creation if needed - only create if no session exists
249
252
  let currentSessionId = sessionId;
250
253
 
251
- if (!isAblyConnected && selectedOption) {
254
+ // Only create a new session if we don't have one and we have a selected option
255
+ // This ensures session is only created once with the first message
256
+ if (!currentSessionId && !isAblyConnected && selectedOption) {
252
257
  currentSessionId = await startNewChatSession(selectedOption);
253
258
  }
254
259
 
@@ -257,7 +262,7 @@ const HelpCenterContent = ({
257
262
  }
258
263
 
259
264
  const messageDto = {
260
- messageContent: message,
265
+ messageContent: message || '', // Use empty string if message is empty but attachments exist
261
266
  ...(attachmentIds.length > 0 && { attachmentIds }),
262
267
  };
263
268
 
@@ -115,7 +115,8 @@ const HelpPopup = ({
115
115
 
116
116
  const handleSendMessage = useCallback(
117
117
  (message: string, attachmentIds: string[]) => {
118
- if (message.trim()) {
118
+ // Allow sending if there's text OR attachments
119
+ if (message.trim() || attachmentIds.length > 0) {
119
120
  onSendMessage(message.trim(), attachmentIds);
120
121
  }
121
122
  },
@@ -174,6 +175,7 @@ const HelpPopup = ({
174
175
  assistantStatus={assistantStatus}
175
176
  needsAgent={needsAgent}
176
177
  isAblyConnected={isAblyConnected}
178
+ sessionId={sessionId}
177
179
  />
178
180
  </>
179
181
  );