@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.
- package/dist/components/ui/image-attachment.d.ts +2 -1
- package/dist/index.css +1 -1
- package/dist/index.esm.js +295 -125
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +295 -125
- package/dist/index.js.map +1 -1
- package/dist/lib/types.d.ts +1 -0
- package/dist/services.esm.js +71 -14
- package/dist/services.esm.js.map +1 -1
- package/dist/services.js +71 -14
- package/dist/services.js.map +1 -1
- package/dist/ui/chatbot-popup/chat-window-screen/footer.d.ts +1 -0
- package/dist/ui/chatbot-popup/chat-window-screen/index.d.ts +2 -1
- package/package.json +1 -1
- package/src/components/ui/agent-response/agent-response.tsx +5 -3
- package/src/components/ui/image-attachment.tsx +29 -17
- package/src/components/ui/image-preview-dialog.tsx +46 -0
- package/src/core/AblyService.ts +59 -12
- package/src/lib/custom-hooks/useTypewriter.ts +5 -3
- package/src/lib/types.ts +2 -1
- package/src/ui/chatbot-popup/chat-window-screen/footer.tsx +71 -44
- package/src/ui/chatbot-popup/chat-window-screen/index.tsx +69 -18
- package/src/ui/help-center.tsx +13 -8
- package/src/ui/help-popup.tsx +3 -1
package/dist/index.esm.js
CHANGED
|
@@ -16245,10 +16245,19 @@ class ClientAblyService {
|
|
|
16245
16245
|
token: ablyToken,
|
|
16246
16246
|
autoConnect: true
|
|
16247
16247
|
});
|
|
16248
|
+
_this.client.connection.on('failed', stateChange => {
|
|
16249
|
+
var _a;
|
|
16250
|
+
console.error('[AblyService] Connection state: failed', {
|
|
16251
|
+
reason: (_a = stateChange.reason) === null || _a === void 0 ? void 0 : _a.message,
|
|
16252
|
+
error: stateChange.reason
|
|
16253
|
+
});
|
|
16254
|
+
});
|
|
16248
16255
|
// Wait for connection to be established
|
|
16249
16256
|
yield new Promise((resolve, reject) => {
|
|
16250
16257
|
if (!_this.client) {
|
|
16251
|
-
|
|
16258
|
+
var error = new Error('Failed to initialize Ably client');
|
|
16259
|
+
console.error('[AblyService]', error);
|
|
16260
|
+
reject(error);
|
|
16252
16261
|
return;
|
|
16253
16262
|
}
|
|
16254
16263
|
_this.client.connection.once('connected', () => {
|
|
@@ -16257,23 +16266,38 @@ class ClientAblyService {
|
|
|
16257
16266
|
resolve();
|
|
16258
16267
|
});
|
|
16259
16268
|
_this.client.connection.once('failed', stateChange => {
|
|
16260
|
-
var _a;
|
|
16261
|
-
|
|
16269
|
+
var _a, _b;
|
|
16270
|
+
var error = new Error("Ably connection failed: ".concat(((_a = stateChange.reason) === null || _a === void 0 ? void 0 : _a.message) || 'Unknown error'));
|
|
16271
|
+
console.error('[AblyService] Connection failed', {
|
|
16272
|
+
reason: (_b = stateChange.reason) === null || _b === void 0 ? void 0 : _b.message,
|
|
16273
|
+
error: stateChange.reason
|
|
16274
|
+
});
|
|
16275
|
+
reject(error);
|
|
16262
16276
|
});
|
|
16263
16277
|
_this.client.connection.once('disconnected', stateChange => {
|
|
16264
|
-
var _a;
|
|
16265
|
-
|
|
16278
|
+
var _a, _b;
|
|
16279
|
+
var error = new Error("Ably connection disconnected: ".concat(((_a = stateChange.reason) === null || _a === void 0 ? void 0 : _a.message) || 'Unknown error'));
|
|
16280
|
+
console.error('[AblyService] Connection disconnected', {
|
|
16281
|
+
reason: (_b = stateChange.reason) === null || _b === void 0 ? void 0 : _b.message
|
|
16282
|
+
});
|
|
16283
|
+
reject(error);
|
|
16266
16284
|
});
|
|
16267
16285
|
// Set a timeout for connection
|
|
16268
16286
|
setTimeout(() => {
|
|
16269
16287
|
if (!_this.isConnected) {
|
|
16270
|
-
|
|
16288
|
+
var _error = new Error('Ably connection timeout');
|
|
16289
|
+
console.error('[AblyService] Connection timeout after 10 seconds');
|
|
16290
|
+
reject(_error);
|
|
16271
16291
|
}
|
|
16272
16292
|
}, 10000);
|
|
16273
16293
|
});
|
|
16274
16294
|
// Subscribe to the session room
|
|
16275
16295
|
yield _this.joinChannel(sessionId, onMessageReceived, tenantId);
|
|
16276
16296
|
} catch (error) {
|
|
16297
|
+
console.error('[AblyService] Error in startConnection', {
|
|
16298
|
+
error,
|
|
16299
|
+
sessionId
|
|
16300
|
+
});
|
|
16277
16301
|
_this.isConnected = false;
|
|
16278
16302
|
_this.sessionId = null;
|
|
16279
16303
|
throw error;
|
|
@@ -16284,23 +16308,53 @@ class ClientAblyService {
|
|
|
16284
16308
|
var _this2 = this;
|
|
16285
16309
|
return _asyncToGenerator(function* () {
|
|
16286
16310
|
if (!_this2.client) {
|
|
16287
|
-
|
|
16311
|
+
var error = new Error('Chat client not initialized');
|
|
16312
|
+
console.error('[AblyService] joinChannel error:', error);
|
|
16313
|
+
throw error;
|
|
16288
16314
|
}
|
|
16289
16315
|
var roomName = "session:".concat(tenantId, ":").concat(sessionId);
|
|
16290
16316
|
// Set up raw channel subscription for server messages
|
|
16291
16317
|
if (_this2.client) {
|
|
16292
16318
|
_this2.channel = _this2.client.channels.get(roomName);
|
|
16319
|
+
_this2.channel.on('failed', stateChange => {
|
|
16320
|
+
var _a;
|
|
16321
|
+
console.error('[AblyService] Channel failed', {
|
|
16322
|
+
roomName,
|
|
16323
|
+
reason: (_a = stateChange.reason) === null || _a === void 0 ? void 0 : _a.message,
|
|
16324
|
+
error: stateChange.reason
|
|
16325
|
+
});
|
|
16326
|
+
});
|
|
16293
16327
|
// Subscribe to assistant/system responses
|
|
16294
16328
|
_this2.channel.subscribe('ReceiveMessage', message => {
|
|
16295
|
-
var _a, _b, _c, _d, _e, _f;
|
|
16329
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
16296
16330
|
try {
|
|
16297
|
-
|
|
16298
|
-
var
|
|
16299
|
-
var
|
|
16300
|
-
var
|
|
16301
|
-
|
|
16331
|
+
// Ensure messageContent is always a string (default to empty string if undefined)
|
|
16332
|
+
var messageContent = typeof message.data === 'string' ? message.data : (_d = (_b = (_a = message.data) === null || _a === void 0 ? void 0 : _a.content) !== null && _b !== void 0 ? _b : (_c = message.data) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : '';
|
|
16333
|
+
var senderType = ((_e = message.data) === null || _e === void 0 ? void 0 : _e.senderType) || 3; // Assistant
|
|
16334
|
+
var needsAgent = ((_f = message.data) === null || _f === void 0 ? void 0 : _f.needsAgent) || ((_g = message.data) === null || _g === void 0 ? void 0 : _g.actionType) == 'needs_agent' || false;
|
|
16335
|
+
var attachments = ((_h = message.data) === null || _h === void 0 ? void 0 : _h.attachments) || [];
|
|
16336
|
+
// Extract downloadUrl from attachments (Ably now returns downloadUrl directly)
|
|
16337
|
+
// Attachments can be: strings (URLs), objects with downloadUrl, or objects with id
|
|
16338
|
+
var attachmentUrls = attachments.map(attachment => {
|
|
16339
|
+
if (typeof attachment === 'string') {
|
|
16340
|
+
// If it's already a string, it's a URL
|
|
16341
|
+
return attachment;
|
|
16342
|
+
} else if (attachment === null || attachment === void 0 ? void 0 : attachment.downloadUrl) {
|
|
16343
|
+
// If it's an object with downloadUrl, use that
|
|
16344
|
+
return attachment.downloadUrl;
|
|
16345
|
+
} else if (attachment === null || attachment === void 0 ? void 0 : attachment.url) {
|
|
16346
|
+
// Fallback to url property
|
|
16347
|
+
return attachment.url;
|
|
16348
|
+
}
|
|
16349
|
+
// If it's an object with id, we'll need to keep it for backward compatibility
|
|
16350
|
+
return null;
|
|
16351
|
+
}).filter(url => url !== null);
|
|
16352
|
+
onMessageReceived(messageContent, senderType, needsAgent, attachmentUrls);
|
|
16302
16353
|
} catch (error) {
|
|
16303
|
-
|
|
16354
|
+
console.error('[AblyService] Error processing message', {
|
|
16355
|
+
error,
|
|
16356
|
+
message
|
|
16357
|
+
});
|
|
16304
16358
|
}
|
|
16305
16359
|
});
|
|
16306
16360
|
yield _this2.channel.attach();
|
|
@@ -16330,6 +16384,9 @@ class ClientAblyService {
|
|
|
16330
16384
|
_this3.isConnected = false;
|
|
16331
16385
|
_this3.sessionId = null;
|
|
16332
16386
|
} catch (error) {
|
|
16387
|
+
console.error('[AblyService] Error in stopConnection', {
|
|
16388
|
+
error
|
|
16389
|
+
});
|
|
16333
16390
|
// Reset state even if there's an error
|
|
16334
16391
|
_this3.isConnected = false;
|
|
16335
16392
|
_this3.sessionId = null;
|
|
@@ -16482,19 +16539,21 @@ function useTypewriter(text) {
|
|
|
16482
16539
|
var onType = arguments.length > 2 ? arguments[2] : undefined;
|
|
16483
16540
|
var [displayedText, setDisplayedText] = useState('');
|
|
16484
16541
|
useEffect(() => {
|
|
16542
|
+
// Ensure text is always a string to prevent errors
|
|
16543
|
+
var safeText = text !== null && text !== void 0 ? text : '';
|
|
16485
16544
|
var index = 0;
|
|
16486
16545
|
setDisplayedText('');
|
|
16487
16546
|
var interval = setInterval(() => {
|
|
16488
16547
|
setDisplayedText(() => {
|
|
16489
|
-
var next =
|
|
16548
|
+
var next = safeText.slice(0, index + 1);
|
|
16490
16549
|
index++;
|
|
16491
16550
|
if (onType) onType();
|
|
16492
|
-
if (index >=
|
|
16551
|
+
if (index >= safeText.length) clearInterval(interval);
|
|
16493
16552
|
return next;
|
|
16494
16553
|
});
|
|
16495
16554
|
}, speed);
|
|
16496
16555
|
return () => clearInterval(interval);
|
|
16497
|
-
}, [text]);
|
|
16556
|
+
}, [text, onType]);
|
|
16498
16557
|
return displayedText;
|
|
16499
16558
|
}
|
|
16500
16559
|
|
|
@@ -34281,11 +34340,13 @@ var AgentResponse = _ref => {
|
|
|
34281
34340
|
messageId,
|
|
34282
34341
|
onType
|
|
34283
34342
|
} = _ref;
|
|
34343
|
+
// Ensure messageContent is always a string to prevent errors
|
|
34344
|
+
var safeMessageContent = messageContent !== null && messageContent !== void 0 ? messageContent : '';
|
|
34284
34345
|
var shouldAnimate = (senderType === 2 || senderType === 3) && !seenMessagesRef.has(messageId);
|
|
34285
|
-
var animatedText = useTypewriter(
|
|
34286
|
-
var finalMessage = shouldAnimate ? animatedText :
|
|
34346
|
+
var animatedText = useTypewriter(safeMessageContent, 20, onType);
|
|
34347
|
+
var finalMessage = shouldAnimate ? animatedText : safeMessageContent;
|
|
34287
34348
|
// Mark message as "seen" after full animation
|
|
34288
|
-
if (shouldAnimate && finalMessage ===
|
|
34349
|
+
if (shouldAnimate && finalMessage === safeMessageContent) {
|
|
34289
34350
|
seenMessagesRef.add(messageId);
|
|
34290
34351
|
}
|
|
34291
34352
|
return jsx("div", {
|
|
@@ -34435,6 +34496,32 @@ var ImagePreviewDialog = _ref => {
|
|
|
34435
34496
|
y: 0
|
|
34436
34497
|
});
|
|
34437
34498
|
}, []);
|
|
34499
|
+
var handleDownload = useCallback(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
34500
|
+
if (!currentImageUrl) return;
|
|
34501
|
+
try {
|
|
34502
|
+
// Fetch the image as a blob
|
|
34503
|
+
var response = yield fetch(currentImageUrl);
|
|
34504
|
+
var blob = yield response.blob();
|
|
34505
|
+
// Create a temporary URL for the blob
|
|
34506
|
+
var blobUrl = URL.createObjectURL(blob);
|
|
34507
|
+
// Extract filename from URL or use a default
|
|
34508
|
+
var urlParts = currentImageUrl.split('/');
|
|
34509
|
+
var filename = urlParts[urlParts.length - 1].split('?')[0] || 'image.png';
|
|
34510
|
+
// Create a temporary anchor element and trigger download
|
|
34511
|
+
var link = document.createElement('a');
|
|
34512
|
+
link.href = blobUrl;
|
|
34513
|
+
link.download = filename;
|
|
34514
|
+
document.body.appendChild(link);
|
|
34515
|
+
link.click();
|
|
34516
|
+
// Cleanup
|
|
34517
|
+
document.body.removeChild(link);
|
|
34518
|
+
URL.revokeObjectURL(blobUrl);
|
|
34519
|
+
} catch (error) {
|
|
34520
|
+
console.error('Failed to download image:', error);
|
|
34521
|
+
// Fallback: open in new tab if download fails
|
|
34522
|
+
window.open(currentImageUrl, '_blank');
|
|
34523
|
+
}
|
|
34524
|
+
}), [currentImageUrl]);
|
|
34438
34525
|
var handleClose = useCallback(() => {
|
|
34439
34526
|
setZoomLevel(1);
|
|
34440
34527
|
setImagePosition({
|
|
@@ -34635,6 +34722,27 @@ var ImagePreviewDialog = _ref => {
|
|
|
34635
34722
|
"aria-label": 'Reset zoom',
|
|
34636
34723
|
type: 'button',
|
|
34637
34724
|
children: "Reset"
|
|
34725
|
+
}), jsx("div", {
|
|
34726
|
+
className: 'babylai-h-9 babylai-w-px babylai-bg-white/20 babylai-mx-1'
|
|
34727
|
+
}), jsx(Button, {
|
|
34728
|
+
variant: 'ghost',
|
|
34729
|
+
size: 'icon',
|
|
34730
|
+
onClick: handleDownload,
|
|
34731
|
+
className: 'babylai-text-white hover:babylai-text-white/80 hover:babylai-bg-white/10 babylai-h-9 babylai-w-9',
|
|
34732
|
+
"aria-label": 'Download image',
|
|
34733
|
+
type: 'button',
|
|
34734
|
+
children: jsx("svg", {
|
|
34735
|
+
className: 'babylai-w-5 babylai-h-5',
|
|
34736
|
+
fill: 'none',
|
|
34737
|
+
stroke: 'currentColor',
|
|
34738
|
+
viewBox: '0 0 24 24',
|
|
34739
|
+
children: jsx("path", {
|
|
34740
|
+
strokeLinecap: 'round',
|
|
34741
|
+
strokeLinejoin: 'round',
|
|
34742
|
+
strokeWidth: 2,
|
|
34743
|
+
d: 'M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4'
|
|
34744
|
+
})
|
|
34745
|
+
})
|
|
34638
34746
|
})]
|
|
34639
34747
|
}), hasMultipleImages && jsx("div", {
|
|
34640
34748
|
className: cn('babylai-absolute babylai-top-4 babylai-z-[60]', 'babylai-bg-black/50 babylai-backdrop-blur-sm babylai-rounded-lg babylai-px-4 babylai-py-2', dir === 'rtl' ? 'babylai-right-1/2 babylai-translate-x-1/2' : 'babylai-left-1/2 -babylai-translate-x-1/2'),
|
|
@@ -34668,6 +34776,7 @@ ImagePreviewDialog.displayName = 'ImagePreviewDialog';
|
|
|
34668
34776
|
var ImageAttachment = _ref => {
|
|
34669
34777
|
var {
|
|
34670
34778
|
fileId,
|
|
34779
|
+
imageUrl: propImageUrl,
|
|
34671
34780
|
className,
|
|
34672
34781
|
enablePreview = true,
|
|
34673
34782
|
onClick
|
|
@@ -34675,30 +34784,39 @@ var ImageAttachment = _ref => {
|
|
|
34675
34784
|
var {
|
|
34676
34785
|
i18n
|
|
34677
34786
|
} = useLocalTranslation();
|
|
34678
|
-
var [imageUrl, setImageUrl] = useState(null);
|
|
34679
|
-
var [loading, setLoading] = useState(
|
|
34787
|
+
var [imageUrl, setImageUrl] = useState(propImageUrl || null);
|
|
34788
|
+
var [loading, setLoading] = useState(!propImageUrl && !!fileId);
|
|
34680
34789
|
var [error, setError] = useState(false);
|
|
34681
34790
|
var [isPreviewOpen, setIsPreviewOpen] = useState(false);
|
|
34682
34791
|
useEffect(() => {
|
|
34683
|
-
|
|
34684
|
-
|
|
34685
|
-
|
|
34686
|
-
|
|
34687
|
-
|
|
34688
|
-
|
|
34689
|
-
|
|
34690
|
-
|
|
34691
|
-
|
|
34692
|
-
|
|
34693
|
-
|
|
34694
|
-
|
|
34695
|
-
|
|
34696
|
-
|
|
34697
|
-
|
|
34698
|
-
|
|
34699
|
-
|
|
34700
|
-
|
|
34701
|
-
|
|
34792
|
+
// If we have a direct URL, use it immediately
|
|
34793
|
+
if (propImageUrl) {
|
|
34794
|
+
setImageUrl(propImageUrl);
|
|
34795
|
+
setLoading(false);
|
|
34796
|
+
return;
|
|
34797
|
+
}
|
|
34798
|
+
// If we only have a fileId, fetch the URL using presignDownload
|
|
34799
|
+
if (fileId) {
|
|
34800
|
+
var fetchImageUrl = /*#__PURE__*/function () {
|
|
34801
|
+
var _ref2 = _asyncToGenerator(function* () {
|
|
34802
|
+
try {
|
|
34803
|
+
setLoading(true);
|
|
34804
|
+
setError(false);
|
|
34805
|
+
var response = yield presignDownload(fileId, i18n.language);
|
|
34806
|
+
setImageUrl(response.downloadUrl);
|
|
34807
|
+
} catch (err) {
|
|
34808
|
+
setError(true);
|
|
34809
|
+
} finally {
|
|
34810
|
+
setLoading(false);
|
|
34811
|
+
}
|
|
34812
|
+
});
|
|
34813
|
+
return function fetchImageUrl() {
|
|
34814
|
+
return _ref2.apply(this, arguments);
|
|
34815
|
+
};
|
|
34816
|
+
}();
|
|
34817
|
+
fetchImageUrl();
|
|
34818
|
+
}
|
|
34819
|
+
}, [fileId, propImageUrl, i18n.language]);
|
|
34702
34820
|
var handleImageClick = () => {
|
|
34703
34821
|
if (onClick) {
|
|
34704
34822
|
onClick();
|
|
@@ -38250,39 +38368,63 @@ var ChatWindowFooter = props => {
|
|
|
38250
38368
|
var _a;
|
|
38251
38369
|
(_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
38252
38370
|
}, []);
|
|
38253
|
-
var handleFileSelect = useCallback(
|
|
38254
|
-
var
|
|
38255
|
-
|
|
38256
|
-
|
|
38257
|
-
|
|
38258
|
-
|
|
38259
|
-
|
|
38260
|
-
|
|
38261
|
-
|
|
38262
|
-
|
|
38263
|
-
|
|
38264
|
-
|
|
38265
|
-
|
|
38266
|
-
|
|
38267
|
-
|
|
38268
|
-
|
|
38269
|
-
|
|
38270
|
-
|
|
38371
|
+
var handleFileSelect = useCallback(e => {
|
|
38372
|
+
var files = Array.from(e.target.files || []);
|
|
38373
|
+
// Validate that all files are images
|
|
38374
|
+
var imageFiles = files.filter(file => file.type.startsWith('image/'));
|
|
38375
|
+
// Create preview URLs and add to selected files (don't upload yet)
|
|
38376
|
+
var newFiles = imageFiles.map(file => ({
|
|
38377
|
+
file,
|
|
38378
|
+
previewUrl: URL.createObjectURL(file),
|
|
38379
|
+
uploading: false,
|
|
38380
|
+
uploadedId: null,
|
|
38381
|
+
error: null
|
|
38382
|
+
}));
|
|
38383
|
+
setSelectedFiles(prev => [...prev, ...newFiles]);
|
|
38384
|
+
// Clear the input
|
|
38385
|
+
if (fileInputRef.current) {
|
|
38386
|
+
fileInputRef.current.value = '';
|
|
38387
|
+
}
|
|
38388
|
+
// Don't upload files immediately - wait for send button click
|
|
38389
|
+
}, []);
|
|
38390
|
+
// Removed handleUploadFiles - files are now uploaded in handleSendMessageWithAttachments
|
|
38391
|
+
var handleRemoveFile = useCallback(previewUrl => {
|
|
38392
|
+
setSelectedFiles(prev => {
|
|
38393
|
+
var fileToRemove = prev.find(f => f.previewUrl === previewUrl);
|
|
38394
|
+
if (fileToRemove) {
|
|
38395
|
+
URL.revokeObjectURL(fileToRemove.previewUrl);
|
|
38271
38396
|
}
|
|
38272
|
-
|
|
38273
|
-
yield handleUploadFiles(newFiles);
|
|
38397
|
+
return prev.filter(f => f.previewUrl !== previewUrl);
|
|
38274
38398
|
});
|
|
38275
|
-
|
|
38276
|
-
|
|
38277
|
-
|
|
38278
|
-
|
|
38279
|
-
|
|
38280
|
-
|
|
38281
|
-
|
|
38282
|
-
|
|
38399
|
+
}, []);
|
|
38400
|
+
var handleSendMessageWithAttachments = useCallback(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
38401
|
+
// Prevent sending if already loading
|
|
38402
|
+
if (props.isLoading) {
|
|
38403
|
+
return;
|
|
38404
|
+
}
|
|
38405
|
+
// Get files that need to be uploaded (those without uploadedId)
|
|
38406
|
+
var filesToUpload = selectedFiles.filter(f => f.uploadedId === null && !f.error);
|
|
38407
|
+
var alreadyUploadedIds = selectedFiles.filter(f => f.uploadedId !== null).map(f => f.uploadedId);
|
|
38408
|
+
// Declare uploadedIds outside the if block so it's accessible later
|
|
38409
|
+
var uploadedIds = [];
|
|
38410
|
+
// If there are files to upload, upload them first
|
|
38411
|
+
if (filesToUpload.length > 0) {
|
|
38412
|
+
// Get session ID - only use existing, never create new one
|
|
38413
|
+
var sessionId = null;
|
|
38283
38414
|
try {
|
|
38284
|
-
sessionId
|
|
38415
|
+
// Only use existing sessionId, never call onEnsureSession
|
|
38416
|
+
if (props.sessionId) {
|
|
38417
|
+
sessionId = props.sessionId;
|
|
38418
|
+
} else {
|
|
38419
|
+
// Mark all files as error
|
|
38420
|
+
setSelectedFiles(prev => prev.map(f => filesToUpload.some(ftl => ftl.previewUrl === f.previewUrl) ? _objectSpread2(_objectSpread2({}, f), {}, {
|
|
38421
|
+
error: 'No session available',
|
|
38422
|
+
uploading: false
|
|
38423
|
+
}) : f));
|
|
38424
|
+
return;
|
|
38425
|
+
}
|
|
38285
38426
|
} catch (error) {
|
|
38427
|
+
console.error('[ChatWindowFooter] Failed to get sessionId for file upload:', error);
|
|
38286
38428
|
// Mark all files as error
|
|
38287
38429
|
setSelectedFiles(prev => prev.map(f => filesToUpload.some(ftl => ftl.previewUrl === f.previewUrl) ? _objectSpread2(_objectSpread2({}, f), {}, {
|
|
38288
38430
|
error: 'Failed to initialize session',
|
|
@@ -38290,7 +38432,9 @@ var ChatWindowFooter = props => {
|
|
|
38290
38432
|
}) : f));
|
|
38291
38433
|
return;
|
|
38292
38434
|
}
|
|
38293
|
-
// Upload each file
|
|
38435
|
+
// Upload each file and collect uploaded IDs
|
|
38436
|
+
uploadedIds = [];
|
|
38437
|
+
var hasUploadErrors = false;
|
|
38294
38438
|
var _loop = function* _loop(fileDto) {
|
|
38295
38439
|
try {
|
|
38296
38440
|
// Mark as uploading
|
|
@@ -38301,7 +38445,6 @@ var ChatWindowFooter = props => {
|
|
|
38301
38445
|
// Get presigned URL
|
|
38302
38446
|
var presignResponse = yield presignUpload(sessionId, fileDto.file, i18n.language);
|
|
38303
38447
|
// Upload file to presigned URL using axios
|
|
38304
|
-
// Important: Content-Type must match the file type (e.g., 'image/png'), not 'multipart/form-data'
|
|
38305
38448
|
var uploadResponse = yield axios$1.put(presignResponse.uploadUrl, fileDto.file, {
|
|
38306
38449
|
headers: {
|
|
38307
38450
|
'Content-Type': fileDto.file.type
|
|
@@ -38313,6 +38456,8 @@ var ChatWindowFooter = props => {
|
|
|
38313
38456
|
if (uploadResponse.status !== 200 && uploadResponse.status !== 204) {
|
|
38314
38457
|
throw new Error("Upload failed with status ".concat(uploadResponse.status));
|
|
38315
38458
|
}
|
|
38459
|
+
// Collect uploaded ID
|
|
38460
|
+
uploadedIds.push(presignResponse.id);
|
|
38316
38461
|
// Update with uploaded ID
|
|
38317
38462
|
setSelectedFiles(prev => prev.map(f => f.previewUrl === fileDto.previewUrl ? _objectSpread2(_objectSpread2({}, f), {}, {
|
|
38318
38463
|
uploading: false,
|
|
@@ -38320,6 +38465,8 @@ var ChatWindowFooter = props => {
|
|
|
38320
38465
|
error: null
|
|
38321
38466
|
}) : f));
|
|
38322
38467
|
} catch (error) {
|
|
38468
|
+
console.error('[ChatWindowFooter] File upload failed:', error);
|
|
38469
|
+
hasUploadErrors = true;
|
|
38323
38470
|
setSelectedFiles(prev => prev.map(f => f.previewUrl === fileDto.previewUrl ? _objectSpread2(_objectSpread2({}, f), {}, {
|
|
38324
38471
|
uploading: false,
|
|
38325
38472
|
error: 'Upload failed',
|
|
@@ -38330,43 +38477,28 @@ var ChatWindowFooter = props => {
|
|
|
38330
38477
|
for (var fileDto of filesToUpload) {
|
|
38331
38478
|
yield* _loop(fileDto);
|
|
38332
38479
|
}
|
|
38333
|
-
|
|
38334
|
-
|
|
38335
|
-
|
|
38336
|
-
|
|
38337
|
-
}(), [props.onEnsureSession, i18n.language]);
|
|
38338
|
-
var handleRemoveFile = useCallback(previewUrl => {
|
|
38339
|
-
setSelectedFiles(prev => {
|
|
38340
|
-
var fileToRemove = prev.find(f => f.previewUrl === previewUrl);
|
|
38341
|
-
if (fileToRemove) {
|
|
38342
|
-
URL.revokeObjectURL(fileToRemove.previewUrl);
|
|
38480
|
+
// If any uploads failed, don't send the message
|
|
38481
|
+
if (hasUploadErrors) {
|
|
38482
|
+
console.error('[ChatWindowFooter] Some files failed to upload, not sending message');
|
|
38483
|
+
return;
|
|
38343
38484
|
}
|
|
38344
|
-
|
|
38345
|
-
|
|
38346
|
-
|
|
38347
|
-
|
|
38348
|
-
// Only allow sending if all files have finished uploading (either successfully or with error)
|
|
38349
|
-
var hasUploadingFiles = selectedFiles.some(f => f.uploading);
|
|
38350
|
-
if (hasUploadingFiles) {
|
|
38351
|
-
return; // Prevent sending if any files are still uploading
|
|
38352
|
-
}
|
|
38353
|
-
// Get all successfully uploaded file IDs
|
|
38354
|
-
var attachmentIds = selectedFiles.filter(f => f.uploadedId !== null).map(f => f.uploadedId);
|
|
38485
|
+
}
|
|
38486
|
+
// Get all successfully uploaded file IDs (already uploaded + newly uploaded)
|
|
38487
|
+
// Use uploadedIds from the upload loop instead of reading from state
|
|
38488
|
+
var allAttachmentIds = [...alreadyUploadedIds, ...uploadedIds];
|
|
38355
38489
|
// Call the original send message with attachment IDs
|
|
38356
|
-
props.handleSendMessage(
|
|
38490
|
+
props.handleSendMessage(allAttachmentIds);
|
|
38357
38491
|
// Clear selected files and revoke URLs
|
|
38358
38492
|
selectedFiles.forEach(f => URL.revokeObjectURL(f.previewUrl));
|
|
38359
38493
|
setSelectedFiles([]);
|
|
38360
|
-
}, [selectedFiles, props]);
|
|
38494
|
+
}), [selectedFiles, props, i18n.language]);
|
|
38361
38495
|
// Check if any files are currently uploading
|
|
38362
38496
|
var hasUploadingFiles = selectedFiles.some(f => f.uploading);
|
|
38363
|
-
// Check if there are files
|
|
38364
|
-
|
|
38365
|
-
|
|
38366
|
-
|
|
38367
|
-
var
|
|
38368
|
-
var allFilesHaveErrors = selectedFiles.length > 0 && !hasSuccessfulUploads && !hasUploadingFiles && !hasPendingFiles;
|
|
38369
|
-
var isSendDisabled = props.isLoading || props.inputMessage.trim() === '' || hasUploadingFiles || hasPendingFiles || allFilesHaveErrors;
|
|
38497
|
+
// Check if there are files with errors
|
|
38498
|
+
var hasFileErrors = selectedFiles.some(f => f.error !== null);
|
|
38499
|
+
// Allow sending if there's text OR files selected (files will be uploaded on send)
|
|
38500
|
+
var hasContentToSend = props.inputMessage.trim() !== '' || selectedFiles.length > 0;
|
|
38501
|
+
var isSendDisabled = props.isLoading || !hasContentToSend || hasUploadingFiles || hasFileErrors;
|
|
38370
38502
|
var handleKeyDown = useCallback(e => {
|
|
38371
38503
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
38372
38504
|
e.preventDefault();
|
|
@@ -38531,10 +38663,12 @@ var MessageComponent = /*#__PURE__*/React__default.memo(_ref => {
|
|
|
38531
38663
|
var isFirstHumanAgentMessage = index === firstHumanAgentIndex && message.senderType === 2;
|
|
38532
38664
|
var textDirection = message.senderType === 1 ? 'babylai-justify-end' : 'babylai-justify-start';
|
|
38533
38665
|
var handleImageClick = useCallback(clickedIndex => {
|
|
38534
|
-
if (
|
|
38535
|
-
|
|
38666
|
+
// Use attachmentUrls if available (from Ably), otherwise use attachmentIds (user-sent)
|
|
38667
|
+
var attachments = message.attachmentUrls || message.attachmentIds || [];
|
|
38668
|
+
if (attachments.length > 0) {
|
|
38669
|
+
onImageClick(attachments, clickedIndex);
|
|
38536
38670
|
}
|
|
38537
|
-
}, [message.attachmentIds, onImageClick]);
|
|
38671
|
+
}, [message.attachmentIds, message.attachmentUrls, onImageClick]);
|
|
38538
38672
|
return jsxs("div", {
|
|
38539
38673
|
children: [isFirstHumanAgentMessage && jsx("div", {
|
|
38540
38674
|
className: 'babylai-flex babylai-justify-center babylai-items-center babylai-my-4',
|
|
@@ -38557,14 +38691,21 @@ var MessageComponent = /*#__PURE__*/React__default.memo(_ref => {
|
|
|
38557
38691
|
className: 'babylai-flex-shrink-0 babylai-me-3 babylai-w-8'
|
|
38558
38692
|
}), jsxs("div", {
|
|
38559
38693
|
className: 'babylai-flex babylai-flex-col babylai-gap-2',
|
|
38560
|
-
children: [message.
|
|
38694
|
+
children: [message.attachmentUrls && message.attachmentUrls.length > 0 && jsx("div", {
|
|
38695
|
+
className: 'babylai-flex babylai-flex-row babylai-flex-wrap babylai-gap-2 babylai-max-w-full',
|
|
38696
|
+
children: message.attachmentUrls.map((attachmentUrl, imgIndex) => jsx(ImageAttachment, {
|
|
38697
|
+
imageUrl: attachmentUrl,
|
|
38698
|
+
enablePreview: false,
|
|
38699
|
+
onClick: () => handleImageClick(imgIndex)
|
|
38700
|
+
}, attachmentUrl))
|
|
38701
|
+
}), message.attachmentIds && message.attachmentIds.length > 0 && jsx("div", {
|
|
38561
38702
|
className: 'babylai-flex babylai-flex-row babylai-flex-wrap babylai-gap-2 babylai-max-w-full',
|
|
38562
38703
|
children: message.attachmentIds.map((attachmentId, imgIndex) => jsx(ImageAttachment, {
|
|
38563
38704
|
fileId: attachmentId,
|
|
38564
38705
|
enablePreview: false,
|
|
38565
38706
|
onClick: () => handleImageClick(imgIndex)
|
|
38566
38707
|
}, attachmentId))
|
|
38567
|
-
}), jsx(AgentResponse$1, {
|
|
38708
|
+
}), message.messageContent && message.messageContent.trim() !== '' && jsx(AgentResponse$1, {
|
|
38568
38709
|
messageContent: message.messageContent,
|
|
38569
38710
|
senderType: message.senderType,
|
|
38570
38711
|
messageId: message.id,
|
|
@@ -38609,7 +38750,8 @@ var ChatWindow = /*#__PURE__*/React__default.memo(_ref3 => {
|
|
|
38609
38750
|
onEnsureSession,
|
|
38610
38751
|
messages,
|
|
38611
38752
|
assistantStatus = 'loading',
|
|
38612
|
-
needsAgent
|
|
38753
|
+
needsAgent,
|
|
38754
|
+
sessionId
|
|
38613
38755
|
} = _ref3;
|
|
38614
38756
|
var {
|
|
38615
38757
|
i18n
|
|
@@ -38652,7 +38794,8 @@ var ChatWindow = /*#__PURE__*/React__default.memo(_ref3 => {
|
|
|
38652
38794
|
};
|
|
38653
38795
|
}, []);
|
|
38654
38796
|
var handleSendMessage = useCallback(attachmentIds => {
|
|
38655
|
-
if
|
|
38797
|
+
// Allow sending if there's text OR attachments
|
|
38798
|
+
if (inputMessage.trim() || attachmentIds.length > 0) {
|
|
38656
38799
|
onSendMessage(inputMessage, attachmentIds);
|
|
38657
38800
|
setInputMessage('');
|
|
38658
38801
|
}
|
|
@@ -38663,13 +38806,32 @@ var ChatWindow = /*#__PURE__*/React__default.memo(_ref3 => {
|
|
|
38663
38806
|
}, [messages]);
|
|
38664
38807
|
// Handle image gallery opening
|
|
38665
38808
|
var handleImageClick = useCallback(/*#__PURE__*/function () {
|
|
38666
|
-
var _ref4 = _asyncToGenerator(function* (
|
|
38667
|
-
|
|
38809
|
+
var _ref4 = _asyncToGenerator(function* (attachmentIdsOrUrls, clickedIndex) {
|
|
38810
|
+
var _a, _b;
|
|
38811
|
+
if (!attachmentIdsOrUrls || attachmentIdsOrUrls.length === 0) {
|
|
38668
38812
|
return;
|
|
38669
38813
|
}
|
|
38670
38814
|
try {
|
|
38671
|
-
//
|
|
38672
|
-
|
|
38815
|
+
// Check if the first item is a URL (starts with http:// or https://)
|
|
38816
|
+
// If so, they're all URLs from Ably and can be used directly
|
|
38817
|
+
var isUrl = ((_a = attachmentIdsOrUrls[0]) === null || _a === void 0 ? void 0 : _a.startsWith('http://')) || ((_b = attachmentIdsOrUrls[0]) === null || _b === void 0 ? void 0 : _b.startsWith('https://'));
|
|
38818
|
+
var imageUrls;
|
|
38819
|
+
if (isUrl) {
|
|
38820
|
+
// These are already URLs from Ably, use them directly (no async needed)
|
|
38821
|
+
imageUrls = attachmentIdsOrUrls.filter(url => url !== null && url.length > 0);
|
|
38822
|
+
// Open gallery immediately with URLs
|
|
38823
|
+
if (imageUrls.length > 0) {
|
|
38824
|
+
var _adjustedIndex = Math.max(0, Math.min(clickedIndex, imageUrls.length - 1));
|
|
38825
|
+
setGalleryState({
|
|
38826
|
+
isOpen: true,
|
|
38827
|
+
imageUrls,
|
|
38828
|
+
initialIndex: _adjustedIndex
|
|
38829
|
+
});
|
|
38830
|
+
}
|
|
38831
|
+
return; // Exit early since we don't need to fetch anything
|
|
38832
|
+
}
|
|
38833
|
+
// These are file IDs, need to fetch URLs using presignDownload
|
|
38834
|
+
var imageUrlPromises = attachmentIdsOrUrls.map(fileId => {
|
|
38673
38835
|
if (!fileId || typeof fileId !== 'string') {
|
|
38674
38836
|
return Promise.resolve(null);
|
|
38675
38837
|
}
|
|
@@ -38683,7 +38845,7 @@ var ChatWindow = /*#__PURE__*/React__default.memo(_ref3 => {
|
|
|
38683
38845
|
return null;
|
|
38684
38846
|
});
|
|
38685
38847
|
});
|
|
38686
|
-
|
|
38848
|
+
imageUrls = (yield Promise.all(imageUrlPromises)).filter(url => url !== null && url.length > 0);
|
|
38687
38849
|
if (imageUrls.length === 0) {
|
|
38688
38850
|
return;
|
|
38689
38851
|
}
|
|
@@ -38745,7 +38907,8 @@ var ChatWindow = /*#__PURE__*/React__default.memo(_ref3 => {
|
|
|
38745
38907
|
handleSendMessage: handleSendMessage,
|
|
38746
38908
|
setInputMessage: setInputMessage,
|
|
38747
38909
|
isLoading: isLoading,
|
|
38748
|
-
onEnsureSession: onEnsureSession
|
|
38910
|
+
onEnsureSession: onEnsureSession,
|
|
38911
|
+
sessionId: sessionId
|
|
38749
38912
|
}), galleryState.isOpen && galleryState.imageUrls.length > 0 && jsx(ImagePreviewDialog, {
|
|
38750
38913
|
imageUrls: galleryState.imageUrls,
|
|
38751
38914
|
initialIndex: galleryState.initialIndex,
|
|
@@ -39364,7 +39527,8 @@ var HelpPopup = _ref => {
|
|
|
39364
39527
|
}
|
|
39365
39528
|
}, [onStartChat, setSelectedOption, sessionId, setStartNewChatConfirmation, setTempSelectedOption]);
|
|
39366
39529
|
var handleSendMessage = useCallback((message, attachmentIds) => {
|
|
39367
|
-
if
|
|
39530
|
+
// Allow sending if there's text OR attachments
|
|
39531
|
+
if (message.trim() || attachmentIds.length > 0) {
|
|
39368
39532
|
onSendMessage(message.trim(), attachmentIds);
|
|
39369
39533
|
}
|
|
39370
39534
|
}, [onSendMessage]);
|
|
@@ -39420,7 +39584,8 @@ var HelpPopup = _ref => {
|
|
|
39420
39584
|
messages: memoizedMessages,
|
|
39421
39585
|
assistantStatus: assistantStatus,
|
|
39422
39586
|
needsAgent: needsAgent,
|
|
39423
|
-
isAblyConnected: isAblyConnected
|
|
39587
|
+
isAblyConnected: isAblyConnected,
|
|
39588
|
+
sessionId: sessionId
|
|
39424
39589
|
})]
|
|
39425
39590
|
});
|
|
39426
39591
|
}
|
|
@@ -39559,7 +39724,7 @@ var HelpCenterContent = _ref => {
|
|
|
39559
39724
|
sentAt: new Date(),
|
|
39560
39725
|
isSeen: true
|
|
39561
39726
|
}, attachments.length > 0 && {
|
|
39562
|
-
|
|
39727
|
+
attachmentUrls: attachments
|
|
39563
39728
|
});
|
|
39564
39729
|
return [...prevMessages, newMessage];
|
|
39565
39730
|
});
|
|
@@ -39688,10 +39853,11 @@ var HelpCenterContent = _ref => {
|
|
|
39688
39853
|
var handleEnsureSession = /*#__PURE__*/function () {
|
|
39689
39854
|
var _ref7 = _asyncToGenerator(function* () {
|
|
39690
39855
|
// If we already have a session ID and connection, return it
|
|
39691
|
-
if
|
|
39856
|
+
// NEVER create a new session if one already exists
|
|
39857
|
+
if (sessionId) {
|
|
39692
39858
|
return sessionId;
|
|
39693
39859
|
}
|
|
39694
|
-
//
|
|
39860
|
+
// Only create a new session if we don't have one and have a selected option
|
|
39695
39861
|
if (selectedOption) {
|
|
39696
39862
|
var newSessionId = yield startNewChatSession(selectedOption);
|
|
39697
39863
|
return newSessionId;
|
|
@@ -39705,28 +39871,32 @@ var HelpCenterContent = _ref => {
|
|
|
39705
39871
|
var handleSendMessage = /*#__PURE__*/function () {
|
|
39706
39872
|
var _ref8 = _asyncToGenerator(function* (message) {
|
|
39707
39873
|
var attachmentIds = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
39708
|
-
|
|
39874
|
+
// Allow sending if there's text OR attachments
|
|
39875
|
+
if (message.trim() !== '' || attachmentIds.length > 0) {
|
|
39709
39876
|
try {
|
|
39710
39877
|
setAssistantStatus('typing');
|
|
39711
39878
|
var userMessage = {
|
|
39712
39879
|
id: Date.now(),
|
|
39713
39880
|
senderType: 1,
|
|
39714
|
-
messageContent: message,
|
|
39881
|
+
messageContent: message || '',
|
|
39882
|
+
// Use empty string if message is empty but attachments exist
|
|
39715
39883
|
sentAt: new Date(),
|
|
39716
39884
|
isSeen: false,
|
|
39717
39885
|
attachmentIds: attachmentIds.length > 0 ? attachmentIds : undefined
|
|
39718
39886
|
};
|
|
39719
39887
|
setMessages(prevMessages => [...prevMessages, userMessage]);
|
|
39720
|
-
// Handle session creation if needed
|
|
39888
|
+
// Handle session creation if needed - only create if no session exists
|
|
39721
39889
|
var currentSessionId = sessionId;
|
|
39722
|
-
if
|
|
39890
|
+
// Only create a new session if we don't have one and we have a selected option
|
|
39891
|
+
// This ensures session is only created once with the first message
|
|
39892
|
+
if (!currentSessionId && !isAblyConnected && selectedOption) {
|
|
39723
39893
|
currentSessionId = yield startNewChatSession(selectedOption);
|
|
39724
39894
|
}
|
|
39725
39895
|
if (!currentSessionId) {
|
|
39726
39896
|
throw new Error('No active session available');
|
|
39727
39897
|
}
|
|
39728
39898
|
var messageDto = _objectSpread2({
|
|
39729
|
-
messageContent: message
|
|
39899
|
+
messageContent: message || ''
|
|
39730
39900
|
}, attachmentIds.length > 0 && {
|
|
39731
39901
|
attachmentIds
|
|
39732
39902
|
});
|