File size: 3,745 Bytes
aa2e6af
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { useCallback } from 'react';
import {
  fileConfig as defaultFileConfig,
  checkOpenAIStorage,
  mergeFileConfig,
  megabyte,
  isAssistantsEndpoint,
} from 'librechat-data-provider';
import type { Row } from '@tanstack/react-table';
import type { TFile } from 'librechat-data-provider';
import { useFileMapContext, useChatContext, useToastContext } from '~/Providers';
import ImagePreview from '~/components/Chat/Input/Files/ImagePreview';
import FilePreview from '~/components/Chat/Input/Files/FilePreview';
import { useUpdateFiles, useLocalize } from '~/hooks';
import { useGetFileConfig } from '~/data-provider';
import { getFileType } from '~/utils';

export default function PanelFileCell({ row }: { row: Row<TFile> }) {
  const localize = useLocalize();
  const fileMap = useFileMapContext();
  const { showToast } = useToastContext();
  const { setFiles, conversation } = useChatContext();
  const { data: fileConfig = defaultFileConfig } = useGetFileConfig({
    select: (data) => mergeFileConfig(data),
  });
  const { addFile } = useUpdateFiles(setFiles);

  const handleFileClick = useCallback(() => {
    const file = row.original;
    const endpoint = conversation?.endpoint;
    const fileData = fileMap?.[file.file_id];

    if (!fileData) {
      return;
    }

    if (!endpoint) {
      return showToast({ message: localize('com_ui_attach_error'), status: 'error' });
    }

    if (checkOpenAIStorage(fileData?.source ?? '') && !isAssistantsEndpoint(endpoint)) {
      return showToast({
        message: localize('com_ui_attach_error_openai'),
        status: 'error',
      });
    } else if (!checkOpenAIStorage(fileData?.source ?? '') && isAssistantsEndpoint(endpoint)) {
      showToast({
        message: localize('com_ui_attach_warn_endpoint'),
        status: 'warning',
      });
    }

    const { fileSizeLimit, supportedMimeTypes } =
      fileConfig.endpoints[endpoint] ?? fileConfig.endpoints.default;

    if (fileData.bytes > fileSizeLimit) {
      return showToast({
        message: `${localize('com_ui_attach_error_size')} ${
          fileSizeLimit / megabyte
        } MB (${endpoint})`,
        status: 'error',
      });
    }

    const isSupportedMimeType = defaultFileConfig.checkType(file.type, supportedMimeTypes);

    if (!isSupportedMimeType) {
      return showToast({
        message: `${localize('com_ui_attach_error_type')} ${file.type} (${endpoint})`,
        status: 'error',
      });
    }

    addFile({
      progress: 1,
      attached: true,
      file_id: fileData.file_id,
      filepath: fileData.filepath,
      preview: fileData.filepath,
      type: fileData.type,
      height: fileData.height,
      width: fileData.width,
      filename: fileData.filename,
      source: fileData.source,
      size: fileData.bytes,
    });
  }, [addFile, fileMap, row.original, conversation, localize, showToast, fileConfig.endpoints]);

  const file = row.original;
  if (file.type?.startsWith('image')) {
    return (
      <div
        onClick={handleFileClick}
        className="flex cursor-pointer gap-2 rounded-md dark:hover:bg-gray-700"
      >
        <ImagePreview
          url={file.filepath}
          className="relative h-10 w-10 shrink-0 overflow-hidden rounded-md"
          source={file.source}
        />
        <span className="self-center truncate text-xs">{file.filename}</span>
      </div>
    );
  }

  const fileType = getFileType(file.type);
  return (
    <div
      onClick={handleFileClick}
      className="flex cursor-pointer gap-2 rounded-md dark:hover:bg-gray-700"
    >
      {fileType && <FilePreview fileType={fileType} className="relative" file={file} />}
      <span className="self-center truncate">{file.filename}</span>
    </div>
  );
}