Refactor isReadOnly + introduce restrictedFields (#13533)

Closes https://github.com/twentyhq/core-team-issues/issues/1276 

We now need to take fields permissions into account when determining if
a cell is editable or not.

a record field (= a cell) is read only if one of the condition is met:
- the object of the record is read-only (can be determined at table
level) by permission setting
- the field of the object is read-only (can be determined at column
level) - either by permission settings or by system (some workflows
fields for intance)
- the record is deleted (can be determined at row level) 

we reorganized the code to avoid re-computing known information as much
as possible.

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Marie
2025-08-04 18:42:27 +02:00
committed by GitHub
parent 42045667a4
commit 932910e71e
74 changed files with 576 additions and 346 deletions
@@ -8,7 +8,6 @@ import { canCreateActivityState } from '@/activities/states/canCreateActivitySta
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { modifyRecordFromCache } from '@/object-record/cache/utils/modifyRecordFromCache';
import { isFieldValueReadOnly } from '@/object-record/record-field/utils/isFieldValueReadOnly';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { isNonTextWritingKey } from '@/ui/utilities/hotkey/utils/isNonTextWritingKey';
import { Key } from 'ts-key-enum';
@@ -29,7 +28,7 @@ import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords
import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords';
import { useRestoreManyRecords } from '@/object-record/hooks/useRestoreManyRecords';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import { useIsRecordReadOnly } from '@/object-record/record-field/hooks/useIsRecordReadOnly';
import { useIsRecordFieldReadOnly } from '@/object-record/record-field/hooks/read-only/useIsRecordFieldReadOnly';
import { isInlineCellInEditModeFamilyState } from '@/object-record/record-inline-cell/states/isInlineCellInEditModeFamilyState';
import { useRecordShowContainerData } from '@/object-record/record-show/hooks/useRecordShowContainerData';
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
@@ -67,16 +66,9 @@ export const ActivityRichTextEditor = ({
objectNameSingular: activityObjectNameSingular,
});
const isRecordReadOnly = useIsRecordReadOnly({
recordId: activityId,
objectMetadataId: objectMetadataItemActivity.id,
});
const isReadOnly = isFieldValueReadOnly({
objectNameSingular: activityObjectNameSingular,
isRecordReadOnly,
isCustom: objectMetadataItemActivity.isCustom,
});
const bodyV2FieldMetadataItemId = objectMetadataItemActivity.fields.find(
(field) => field.name === 'bodyV2',
)?.id;
const { deleteManyRecords: deleteAttachments } = useDeleteManyRecords({
objectNameSingular: CoreObjectNameSingular.Attachment,
@@ -106,8 +98,14 @@ export const ActivityRichTextEditor = ({
activityObjectNameSingular: activityObjectNameSingular,
});
const isRecordFieldReadOnly = useIsRecordFieldReadOnly({
recordId: activityId,
objectMetadataId: objectMetadataItemActivity.id,
fieldMetadataId: bodyV2FieldMetadataItemId,
});
const persistBodyDebounced = useDebouncedCallback((blocknote: string) => {
if (isReadOnly) return;
if (isRecordFieldReadOnly === true) return;
const input = {
bodyV2: {
@@ -430,7 +428,7 @@ export const ActivityRichTextEditor = ({
onBlur={handlerBlockEditorBlur}
onChange={handleEditorChange}
editor={editor}
readonly={isReadOnly}
readonly={isRecordFieldReadOnly}
/>
</>
);