From c5e0a076a272dccd48528b1c64e4949ae33c2c94 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Tue, 20 Feb 2024 06:06:23 +0000 Subject: [PATCH] fic(nc-gui): allow duplicate file name file to upload in atttachment --- .../components/cell/attachment/utils.ts | 38 +++++++++++-------- .../useMultiSelect/convertCellData.ts | 19 ++++------ .../composables/useMultiSelect/index.ts | 36 ++++++++++-------- packages/nc-gui/utils/fileUtils.ts | 8 ++++ 4 files changed, 57 insertions(+), 44 deletions(-) diff --git a/packages/nc-gui/components/cell/attachment/utils.ts b/packages/nc-gui/components/cell/attachment/utils.ts index 1066c49b38..74bd18a1d6 100644 --- a/packages/nc-gui/components/cell/attachment/utils.ts +++ b/packages/nc-gui/components/cell/attachment/utils.ts @@ -108,7 +108,7 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState( ...parseProp(column?.value?.meta), } - const newAttachments = [] + const newAttachments: AttachmentType[] = [] const files: File[] = [] @@ -159,13 +159,12 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState( if (selectedFiles.length) { files.push(file as File) } else { - let fileName = (file as AttachmentReqType).fileName ?? '' - let count = 1 - while ([...attachments.value, ...imageUrls].some((el) => el.title === fileName)) { - fileName = fileName.replace(/(.+?)(\.[^.]+)$/, `$1(${count})$2`) - count++ - } - imageUrls.push({ ...(file as AttachmentReqType), fileName }) + const fileName = populateUniqueFileName((file as AttachmentReqType).fileName ?? '', [ + ...attachments.value, + ...imageUrls, + ]) + + imageUrls.push({ ...(file as AttachmentReqType), fileName, title: fileName }) } } @@ -221,13 +220,10 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState( ) // add suffix in duplicate file title for (const uploadedFile of data) { - let fileName = uploadedFile?.title - let count = 1 - while ([...attachments.value, ...newAttachments].some((el) => el.title === fileName)) { - fileName = fileName.replace(/(.+?)(\.[^.]+)$/, `$1(${count})$2`) - count++ - } - newAttachments.push({ ...uploadedFile, title: fileName }) + newAttachments.push({ + ...uploadedFile, + title: populateUniqueFileName(uploadedFile?.title, [...attachments.value, ...newAttachments]), + }) } } catch (e: any) { message.error(e.message || t('msg.error.internalError')) @@ -283,7 +279,17 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState( const imageData = imageUrl ? await getImageDataFromUrl(imageUrl) : '' if (imageData) { - await onFileSelect([], [{ ...imageData, url: imageUrl, fileName: `image.${imageData.mimetype?.split('/')[1]}` }]) + await onFileSelect( + [], + [ + { + ...imageData, + url: imageUrl, + fileName: `image.${imageData.mimetype?.split('/')[1]}`, + title: `image.${imageData.mimetype?.split('/')[1]}`, + }, + ], + ) } } } diff --git a/packages/nc-gui/composables/useMultiSelect/convertCellData.ts b/packages/nc-gui/composables/useMultiSelect/convertCellData.ts index 0325993020..79f5d2a590 100644 --- a/packages/nc-gui/composables/useMultiSelect/convertCellData.ts +++ b/packages/nc-gui/composables/useMultiSelect/convertCellData.ts @@ -1,5 +1,5 @@ import dayjs from 'dayjs' -import type { ColumnType, LinkToAnotherRecordType, SelectOptionsType } from 'nocodb-sdk' +import type { AttachmentType, ColumnType, LinkToAnotherRecordType, SelectOptionsType } from 'nocodb-sdk' import { UITypes } from 'nocodb-sdk' import type { AppInfo } from '~/composables/useGlobal' import { isBt, isMm, parseProp } from '#imports' @@ -191,16 +191,6 @@ export default function convertCellData( continue } } - // this prevent file with same names - const isFileNameAlreadyExist = oldAttachments.some((el) => el.title === (attachment?.title || attachment?.name)) - if (isFileNameAlreadyExist) { - if (isMultiple) { - message.error(`File with name ${attachment?.title || attachment?.name} already attached`) - continue - } else { - throw new Error(`File with name ${attachment?.title || attachment?.name} already attached`) - } - } attachments.push(attachment) } @@ -208,7 +198,12 @@ export default function convertCellData( if (oldAttachments.length && !attachments.length) { return undefined } else if (value && attachments.length) { - return JSON.stringify([...oldAttachments, ...attachments]) + const newAttachments: AttachmentType[] = [] + + for (const att of attachments) { + newAttachments.push({ ...att, title: populateUniqueFileName(att?.title, [...oldAttachments, ...newAttachments]) }) + } + return JSON.stringify([...oldAttachments, ...newAttachments]) } else if (files.length && attachments.length) { return attachments } else { diff --git a/packages/nc-gui/composables/useMultiSelect/index.ts b/packages/nc-gui/composables/useMultiSelect/index.ts index 8408dc5091..266887d2c1 100644 --- a/packages/nc-gui/composables/useMultiSelect/index.ts +++ b/packages/nc-gui/composables/useMultiSelect/index.ts @@ -2,7 +2,7 @@ import type { Ref } from 'vue' import { computed } from 'vue' import dayjs from 'dayjs' import type { MaybeRef } from '@vueuse/core' -import type { ColumnType, LinkToAnotherRecordType, PaginatedType, TableType, UserFieldRecordType, ViewType } from 'nocodb-sdk' +import type { AttachmentType, ColumnType, LinkToAnotherRecordType, PaginatedType, TableType, UserFieldRecordType, ViewType } from 'nocodb-sdk' import { UITypes, dateFormats, isDateMonthFormat, isSystemColumn, isVirtualCol, timeFormats } from 'nocodb-sdk' import { parse } from 'papaparse' import type { Cell } from './cellRange' @@ -1115,12 +1115,9 @@ export function useMultiSelect( ) if (columnObj.uidt === UITypes.Attachment && e.clipboardData?.files?.length && pasteValue?.length) { - const uploadedFiles = await handleFileUpload(pasteValue, columnObj.id!) + const newAttachments = await handleFileUploadAndGetCellValue(pasteValue, columnObj.id!, rowObj.row[columnObj.title!]) - rowObj.row[columnObj.title!] = - Array.isArray(uploadedFiles) && uploadedFiles.length - ? JSON.stringify([...handleParseAttachmentCellData(rowObj.row[columnObj.title!]), ...uploadedFiles]) - : null + rowObj.row[columnObj.title!] = newAttachments ? JSON.stringify(newAttachments) : null } else if (pasteValue !== undefined) { rowObj.row[columnObj.title!] = pasteValue } @@ -1177,12 +1174,9 @@ export function useMultiSelect( ) if (fileUploadPayload?.length) { - const uploadedFiles = await handleFileUpload(fileUploadPayload, col.id!) + const newAttachments = await handleFileUploadAndGetCellValue(fileUploadPayload, col.id!, row.row[col.title!]) - pasteValue = - Array.isArray(uploadedFiles) && uploadedFiles.length - ? JSON.stringify([...handleParseAttachmentCellData(row.row[col.title]), ...uploadedFiles]) - : null + pasteValue = newAttachments ? JSON.stringify(newAttachments) : null } } } else { @@ -1232,7 +1226,9 @@ export function useMultiSelect( event.preventDefault() } - async function handleFileUpload(files: File[], columnId: string) { + async function handleFileUploadAndGetCellValue(files: File[], columnId: string, oldValue: AttachmentType[]) { + const newAttachments: AttachmentType[] = [] + try { const data = await api.storage.upload( { @@ -1242,19 +1238,27 @@ export function useMultiSelect( files, }, ) - return data + + // add suffix in duplicate file title + for (const uploadedFile of data) { + newAttachments.push({ + ...uploadedFile, + title: populateUniqueFileName(uploadedFile?.title, [...handleParseAttachmentCellData(oldValue), ...newAttachments]), + }) + } + return newAttachments } catch (e: any) { message.error(e.message || t('msg.error.internalError')) } } - function handleParseAttachmentCellData(value: string | null) { + function handleParseAttachmentCellData(value: T): T { const parsedVal = parseProp(value) if (parsedVal && Array.isArray(parsedVal)) { - return parsedVal + return parsedVal as T } else { - return [] + return [] as T } } diff --git a/packages/nc-gui/utils/fileUtils.ts b/packages/nc-gui/utils/fileUtils.ts index f08f4a447c..d5d137ea2f 100644 --- a/packages/nc-gui/utils/fileUtils.ts +++ b/packages/nc-gui/utils/fileUtils.ts @@ -62,3 +62,11 @@ export function extractImageSrcFromRawHtml(rawText: string) { return imgElement.getAttribute('src') } } + +export function populateUniqueFileName(fn: string, attachments: any[]) { + let c = 1 + while (attachments.some((att) => att?.title === fn || att?.fileName === fn)) { + fn = fn.replace(/(.+?)(\.[^.]+)$/, `$1(${c++})$2`) + } + return fn +}