file-upload-setting.tsx 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useCallback } from 'react'
  4. import useSWR from 'swr'
  5. import produce from 'immer'
  6. import { useTranslation } from 'react-i18next'
  7. import type { UploadFileSetting } from '../../../types'
  8. import { SupportUploadFileTypes } from '../../../types'
  9. import OptionCard from './option-card'
  10. import FileTypeItem from './file-type-item'
  11. import InputNumberWithSlider from './input-number-with-slider'
  12. import Field from '@/app/components/app/configuration/config-var/config-modal/field'
  13. import { TransferMethod } from '@/types/app'
  14. import { fetchFileUploadConfig } from '@/service/common'
  15. import { useFileSizeLimit } from '@/app/components/base/file-uploader/hooks'
  16. import { formatFileSize } from '@/utils/format'
  17. type Props = {
  18. payload: UploadFileSetting
  19. isMultiple: boolean
  20. inFeaturePanel?: boolean
  21. hideSupportFileType?: boolean
  22. onChange: (payload: UploadFileSetting) => void
  23. }
  24. const FileUploadSetting: FC<Props> = ({
  25. payload,
  26. isMultiple,
  27. inFeaturePanel = false,
  28. hideSupportFileType = false,
  29. onChange,
  30. }) => {
  31. const { t } = useTranslation()
  32. const {
  33. allowed_file_upload_methods,
  34. max_length,
  35. allowed_file_types,
  36. allowed_file_extensions,
  37. } = payload
  38. const { data: fileUploadConfigResponse } = useSWR({ url: '/files/upload' }, fetchFileUploadConfig)
  39. const {
  40. imgSizeLimit,
  41. docSizeLimit,
  42. audioSizeLimit,
  43. videoSizeLimit,
  44. maxFileUploadLimit,
  45. } = useFileSizeLimit(fileUploadConfigResponse)
  46. const handleSupportFileTypeChange = useCallback((type: SupportUploadFileTypes) => {
  47. const newPayload = produce(payload, (draft) => {
  48. if (type === SupportUploadFileTypes.custom) {
  49. if (!draft.allowed_file_types.includes(SupportUploadFileTypes.custom))
  50. draft.allowed_file_types = [SupportUploadFileTypes.custom]
  51. else
  52. draft.allowed_file_types = draft.allowed_file_types.filter(v => v !== type)
  53. }
  54. else {
  55. draft.allowed_file_types = draft.allowed_file_types.filter(v => v !== SupportUploadFileTypes.custom)
  56. if (draft.allowed_file_types.includes(type))
  57. draft.allowed_file_types = draft.allowed_file_types.filter(v => v !== type)
  58. else
  59. draft.allowed_file_types.push(type)
  60. }
  61. })
  62. onChange(newPayload)
  63. }, [onChange, payload])
  64. const handleUploadMethodChange = useCallback((method: TransferMethod) => {
  65. return () => {
  66. const newPayload = produce(payload, (draft) => {
  67. if (method === TransferMethod.all)
  68. draft.allowed_file_upload_methods = [TransferMethod.local_file, TransferMethod.remote_url]
  69. else
  70. draft.allowed_file_upload_methods = [method]
  71. })
  72. onChange(newPayload)
  73. }
  74. }, [onChange, payload])
  75. const handleCustomFileTypesChange = useCallback((customFileTypes: string[]) => {
  76. const newPayload = produce(payload, (draft) => {
  77. draft.allowed_file_extensions = customFileTypes.map((v) => {
  78. if (v.startsWith('.')) // Not start with dot
  79. return v.slice(1)
  80. return v
  81. })
  82. })
  83. onChange(newPayload)
  84. }, [onChange, payload])
  85. const handleMaxUploadNumLimitChange = useCallback((value: number) => {
  86. const newPayload = produce(payload, (draft) => {
  87. draft.max_length = value
  88. })
  89. onChange(newPayload)
  90. }, [onChange, payload])
  91. return (
  92. <div>
  93. {!inFeaturePanel && (
  94. <Field
  95. title={t('appDebug.variableConfig.file.supportFileTypes')}
  96. >
  97. <div className='space-y-1'>
  98. {
  99. [SupportUploadFileTypes.document, SupportUploadFileTypes.image, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => (
  100. <FileTypeItem
  101. key={type}
  102. type={type as SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video}
  103. selected={allowed_file_types.includes(type)}
  104. onToggle={handleSupportFileTypeChange}
  105. />
  106. ))
  107. }
  108. <FileTypeItem
  109. type={SupportUploadFileTypes.custom}
  110. selected={allowed_file_types.includes(SupportUploadFileTypes.custom)}
  111. onToggle={handleSupportFileTypeChange}
  112. customFileTypes={allowed_file_extensions?.map(item => `.${item}`)}
  113. onCustomFileTypesChange={handleCustomFileTypesChange}
  114. />
  115. </div>
  116. </Field>
  117. )}
  118. <Field
  119. title={t('appDebug.variableConfig.uploadFileTypes')}
  120. className='mt-4'
  121. >
  122. <div className='grid grid-cols-3 gap-2'>
  123. <OptionCard
  124. title={t('appDebug.variableConfig.localUpload')}
  125. selected={allowed_file_upload_methods.length === 1 && allowed_file_upload_methods.includes(TransferMethod.local_file)}
  126. onSelect={handleUploadMethodChange(TransferMethod.local_file)}
  127. />
  128. <OptionCard
  129. title="URL"
  130. selected={allowed_file_upload_methods.length === 1 && allowed_file_upload_methods.includes(TransferMethod.remote_url)}
  131. onSelect={handleUploadMethodChange(TransferMethod.remote_url)}
  132. />
  133. <OptionCard
  134. title={t('appDebug.variableConfig.both')}
  135. selected={allowed_file_upload_methods.includes(TransferMethod.local_file) && allowed_file_upload_methods.includes(TransferMethod.remote_url)}
  136. onSelect={handleUploadMethodChange(TransferMethod.all)}
  137. />
  138. </div>
  139. </Field>
  140. {isMultiple && (
  141. <Field
  142. className='mt-4'
  143. title={t('appDebug.variableConfig.maxNumberOfUploads')!}
  144. >
  145. <div>
  146. <div className='mb-1.5 text-text-tertiary body-xs-regular'>{t('appDebug.variableConfig.maxNumberTip', {
  147. imgLimit: formatFileSize(imgSizeLimit),
  148. docLimit: formatFileSize(docSizeLimit),
  149. audioLimit: formatFileSize(audioSizeLimit),
  150. videoLimit: formatFileSize(videoSizeLimit),
  151. })}</div>
  152. <InputNumberWithSlider
  153. value={max_length}
  154. min={1}
  155. max={maxFileUploadLimit}
  156. onChange={handleMaxUploadNumLimitChange}
  157. />
  158. </div>
  159. </Field>
  160. )}
  161. {inFeaturePanel && !hideSupportFileType && (
  162. <Field
  163. title={t('appDebug.variableConfig.file.supportFileTypes')}
  164. className='mt-4'
  165. >
  166. <div className='space-y-1'>
  167. {
  168. [SupportUploadFileTypes.document, SupportUploadFileTypes.image, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => (
  169. <FileTypeItem
  170. key={type}
  171. type={type as SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video}
  172. selected={allowed_file_types.includes(type)}
  173. onToggle={handleSupportFileTypeChange}
  174. />
  175. ))
  176. }
  177. <FileTypeItem
  178. type={SupportUploadFileTypes.custom}
  179. selected={allowed_file_types.includes(SupportUploadFileTypes.custom)}
  180. onToggle={handleSupportFileTypeChange}
  181. customFileTypes={allowed_file_extensions}
  182. onCustomFileTypesChange={handleCustomFileTypesChange}
  183. />
  184. </div>
  185. </Field>
  186. )}
  187. </div>
  188. )
  189. }
  190. export default React.memo(FileUploadSetting)