Files v2 - Use Files field in attachment (#17707)
- Add FILES field on attachment - Adapt Attachment logic in front to use new resolver/controller - Update files-field logic to infer applicationId from fieldMetadataId + ask for fieldMetadataId in upload resolver - Design update To do in next PR : - Adapt activity files logic
This commit is contained in:
+1
@@ -99,6 +99,7 @@ export const useOpenFieldInputEditMode = () => {
|
||||
if (isDefined(objectMetadataItem)) {
|
||||
openFilesFieldInput({
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
fieldMetadataId: fieldDefinition.fieldMetadataId,
|
||||
recordId,
|
||||
prefix,
|
||||
updateRecord: (updateInput) => {
|
||||
|
||||
+20
-4
@@ -1,14 +1,30 @@
|
||||
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
|
||||
import { useFilesFieldDisplay } from '@/object-record/record-field/ui/meta-types/hooks/useFilesFieldDisplay';
|
||||
import { filesFieldUploadState } from '@/object-record/record-field/ui/states/filesFieldUploadState';
|
||||
import { FilesDisplay } from '@/ui/field/display/components/FilesDisplay';
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export const FilesFieldDisplay = () => {
|
||||
const { recordId, fieldDefinition } = useContext(FieldContext);
|
||||
const { fieldValue, disableChipClick } = useFilesFieldDisplay();
|
||||
|
||||
if (!Array.isArray(fieldValue)) {
|
||||
return <></>;
|
||||
}
|
||||
const uploadState = useRecoilValue(
|
||||
filesFieldUploadState({
|
||||
recordId,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const isUploadWindowOpen = uploadState === 'UPLOAD_WINDOW_OPEN';
|
||||
const isFileUploading = uploadState === 'UPLOADING_FILE';
|
||||
|
||||
return (
|
||||
<FilesDisplay value={fieldValue} forceDisableClick={disableChipClick} />
|
||||
<FilesDisplay
|
||||
value={fieldValue}
|
||||
forceDisableClick={disableChipClick}
|
||||
isUploadWindowOpen={isUploadWindowOpen}
|
||||
isFileUploading={isFileUploading}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+2
-2
@@ -15,10 +15,10 @@ export const useUploadFilesFieldFile = () => {
|
||||
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
|
||||
const { t } = useLingui();
|
||||
|
||||
const uploadFile = async (file: File) => {
|
||||
const uploadFile = async (file: File, fieldMetadataId: string) => {
|
||||
try {
|
||||
const result = await uploadFilesFieldFile({
|
||||
variables: { file },
|
||||
variables: { file, fieldMetadataId },
|
||||
});
|
||||
|
||||
const uploadedFile = result?.data?.uploadFilesFieldFile;
|
||||
|
||||
+7
-1
@@ -61,9 +61,13 @@ export const FilesFieldInput = () => {
|
||||
const nextValue = parseFilesArrayToFilesValue(updatedFiles);
|
||||
if (isDefined(nextValue)) {
|
||||
setDraftValue(nextValue);
|
||||
|
||||
if (nextValue.length === 0) {
|
||||
onEnter?.({ newValue: nextValue });
|
||||
}
|
||||
}
|
||||
},
|
||||
[parseFilesArrayToFilesValue, setDraftValue],
|
||||
[parseFilesArrayToFilesValue, setDraftValue, onEnter],
|
||||
);
|
||||
|
||||
const handleUploadClick = useCallback(() => {
|
||||
@@ -89,6 +93,7 @@ export const FilesFieldInput = () => {
|
||||
try {
|
||||
const uploadedFiles = await uploadMultipleFiles(
|
||||
selectedFiles,
|
||||
fieldDefinition.fieldMetadataId,
|
||||
uploadFile,
|
||||
);
|
||||
|
||||
@@ -113,6 +118,7 @@ export const FilesFieldInput = () => {
|
||||
handleChange,
|
||||
onEnter,
|
||||
parseFilesArrayToFilesValue,
|
||||
fieldDefinition,
|
||||
]);
|
||||
|
||||
const setIsFieldInError = useSetRecoilComponentState(
|
||||
|
||||
+20
@@ -1,6 +1,7 @@
|
||||
import { useFileUpload } from '@/file-upload/hooks/useFileUpload';
|
||||
import { useUploadFilesFieldFile } from '@/object-record/record-field/ui/meta-types/hooks/useUploadFilesFieldFile';
|
||||
import { uploadMultipleFiles } from '@/object-record/record-field/ui/meta-types/utils/uploadMultipleFiles';
|
||||
import { filesFieldUploadState } from '@/object-record/record-field/ui/states/filesFieldUploadState';
|
||||
import { type FieldFilesValue } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
|
||||
@@ -36,6 +37,7 @@ export const useOpenFilesFieldInput = () => {
|
||||
({ snapshot, set }) =>
|
||||
async ({
|
||||
fieldName,
|
||||
fieldMetadataId,
|
||||
recordId,
|
||||
prefix,
|
||||
updateRecord,
|
||||
@@ -43,6 +45,7 @@ export const useOpenFilesFieldInput = () => {
|
||||
fieldDefinition,
|
||||
}: {
|
||||
fieldName: string;
|
||||
fieldMetadataId: string;
|
||||
recordId: string;
|
||||
prefix?: string;
|
||||
updateRecord: (updateInput: Record<string, unknown>) => void;
|
||||
@@ -92,6 +95,11 @@ export const useOpenFilesFieldInput = () => {
|
||||
|
||||
const currentFileCount = isDefined(fieldValue) ? fieldValue.length : 0;
|
||||
|
||||
set(
|
||||
filesFieldUploadState({ recordId, fieldName }),
|
||||
'UPLOAD_WINDOW_OPEN',
|
||||
);
|
||||
|
||||
openFileUpload({
|
||||
multiple: true,
|
||||
onUpload: async (selectedFiles: File[]) => {
|
||||
@@ -100,6 +108,8 @@ export const useOpenFilesFieldInput = () => {
|
||||
message: t`Cannot upload more than ${maxNumberOfValues} files`,
|
||||
});
|
||||
|
||||
set(filesFieldUploadState({ recordId, fieldName }), null);
|
||||
|
||||
if (isTableContext && isDefined(recordTableId)) {
|
||||
set(
|
||||
recordTableCellEditModePositionComponentState.atomFamily({
|
||||
@@ -117,9 +127,15 @@ export const useOpenFilesFieldInput = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
set(
|
||||
filesFieldUploadState({ recordId, fieldName }),
|
||||
'UPLOADING_FILE',
|
||||
);
|
||||
|
||||
try {
|
||||
const uploadedFiles = await uploadMultipleFiles(
|
||||
selectedFiles,
|
||||
fieldMetadataId,
|
||||
uploadFile,
|
||||
);
|
||||
|
||||
@@ -129,6 +145,8 @@ export const useOpenFilesFieldInput = () => {
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
set(filesFieldUploadState({ recordId, fieldName }), null);
|
||||
|
||||
if (isTableContext && isDefined(recordTableId)) {
|
||||
set(
|
||||
recordTableCellEditModePositionComponentState.atomFamily({
|
||||
@@ -146,6 +164,8 @@ export const useOpenFilesFieldInput = () => {
|
||||
}
|
||||
},
|
||||
onCancel: () => {
|
||||
set(filesFieldUploadState({ recordId, fieldName }), null);
|
||||
|
||||
if (isTableContext && isDefined(recordTableId)) {
|
||||
set(
|
||||
recordTableCellEditModePositionComponentState.atomFamily({
|
||||
|
||||
+6
-2
@@ -3,12 +3,16 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const uploadMultipleFiles = async (
|
||||
files: File[],
|
||||
uploadFile: (file: File) => Promise<FieldFilesValue | undefined>,
|
||||
fieldMetadataId: string,
|
||||
uploadFile: (
|
||||
file: File,
|
||||
fieldMetadataId: string,
|
||||
) => Promise<FieldFilesValue | undefined>,
|
||||
): Promise<FieldFilesValue[]> => {
|
||||
const uploadedFiles: FieldFilesValue[] = [];
|
||||
|
||||
for (const file of files) {
|
||||
const uploadedFile = await uploadFile(file);
|
||||
const uploadedFile = await uploadFile(file, fieldMetadataId);
|
||||
if (isDefined(uploadedFile)) {
|
||||
uploadedFiles.push(uploadedFile);
|
||||
}
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
type FilesFieldUploadStateKey = {
|
||||
recordId: string;
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
type FilesFieldUploadState = 'UPLOAD_WINDOW_OPEN' | 'UPLOADING_FILE' | null;
|
||||
|
||||
export const filesFieldUploadState = atomFamily<
|
||||
FilesFieldUploadState,
|
||||
FilesFieldUploadStateKey
|
||||
>({
|
||||
key: 'filesFieldUploadState',
|
||||
default: null,
|
||||
});
|
||||
Reference in New Issue
Block a user