File size: 4,320 Bytes
f0743f4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | import { useState, useRef, useCallback } from 'react';
import { Import } from 'lucide-react';
import { useQueryClient } from '@tanstack/react-query';
import { QueryKeys, TStartupConfig } from 'librechat-data-provider';
import { Spinner, useToastContext, Label, Button } from '@librechat/client';
import { useUploadConversationsMutation } from '~/data-provider';
import { NotificationSeverity } from '~/common';
import { useLocalize } from '~/hooks';
import { cn, logger } from '~/utils';
function ImportConversations() {
const localize = useLocalize();
const queryClient = useQueryClient();
const { showToast } = useToastContext();
const fileInputRef = useRef<HTMLInputElement>(null);
const [isUploading, setIsUploading] = useState(false);
const handleSuccess = useCallback(() => {
showToast({
message: localize('com_ui_import_conversation_success'),
status: NotificationSeverity.SUCCESS,
});
setIsUploading(false);
}, [localize, showToast]);
const handleError = useCallback(
(error: unknown) => {
logger.error('Import error:', error);
setIsUploading(false);
const isUnsupportedType = error?.toString().includes('Unsupported import type');
showToast({
message: localize(
isUnsupportedType
? 'com_ui_import_conversation_file_type_error'
: 'com_ui_import_conversation_error',
),
status: NotificationSeverity.ERROR,
});
},
[localize, showToast],
);
const uploadFile = useUploadConversationsMutation({
onSuccess: handleSuccess,
onError: handleError,
onMutate: () => setIsUploading(true),
});
const handleFileUpload = useCallback(
async (file: File) => {
try {
const startupConfig = queryClient.getQueryData<TStartupConfig>([QueryKeys.startupConfig]);
const maxFileSize = startupConfig?.conversationImportMaxFileSize;
if (maxFileSize && file.size > maxFileSize) {
const size = (maxFileSize / (1024 * 1024)).toFixed(2);
showToast({
message: localize('com_error_files_upload_too_large', { 0: size }),
status: NotificationSeverity.ERROR,
});
setIsUploading(false);
return;
}
const formData = new FormData();
formData.append('file', file, encodeURIComponent(file.name || 'File'));
uploadFile.mutate(formData);
} catch (error) {
logger.error('File processing error:', error);
setIsUploading(false);
showToast({
message: localize('com_ui_import_conversation_upload_error'),
status: NotificationSeverity.ERROR,
});
}
},
[uploadFile, showToast, localize, queryClient],
);
const handleFileChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
setIsUploading(true);
handleFileUpload(file);
}
event.target.value = '';
},
[handleFileUpload],
);
const handleImportClick = useCallback(() => {
fileInputRef.current?.click();
}, []);
const handleKeyDown = useCallback(
(event: React.KeyboardEvent<HTMLButtonElement>) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
handleImportClick();
}
},
[handleImportClick],
);
const isImportDisabled = isUploading;
return (
<div className="flex items-center justify-between">
<Label id="import-conversation-label">{localize('com_ui_import_conversation_info')}</Label>
<Button
variant="outline"
onClick={handleImportClick}
onKeyDown={handleKeyDown}
disabled={isImportDisabled}
aria-label={localize('com_ui_import')}
aria-labelledby="import-conversation-label"
>
{isUploading ? (
<Spinner className="mr-1 w-4" />
) : (
<Import className="mr-1 flex h-4 w-4 items-center stroke-1" />
)}
<span>{localize('com_ui_import')}</span>
</Button>
<input
ref={fileInputRef}
type="file"
className={cn('hidden')}
accept=".json"
onChange={handleFileChange}
aria-hidden="true"
/>
</div>
);
}
export default ImportConversations;
|