Activity as standard object (#6219)

In this PR I layout the first steps to migrate Activity to a traditional
Standard objects

Since this is a big transition, I'd rather split it into several
deployments / PRs

<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/012e2bbf-9d1b-4723-aaf6-269ef588b050">

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: bosiraphael <71827178+bosiraphael@users.noreply.github.com>
Co-authored-by: Weiko <corentin@twenty.com>
Co-authored-by: Faisal-imtiyaz123 <142205282+Faisal-imtiyaz123@users.noreply.github.com>
Co-authored-by: Prateek Jain <prateekj1171998@gmail.com>
This commit is contained in:
Félix Malfait
2024-07-31 15:36:11 +02:00
committed by GitHub
parent defcee2a02
commit 80c0fc7ff1
239 changed files with 18418 additions and 8671 deletions

View File

@ -1,133 +0,0 @@
import styled from '@emotion/styled';
import { isNonEmptyArray, isNonEmptyString } from '@sniptt/guards';
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
import { IconTrash } from 'twenty-ui';
import { useRefreshShowPageFindManyActivitiesQueries } from '@/activities/hooks/useRefreshShowPageFindManyActivitiesQueries';
import { activityIdInDrawerState } from '@/activities/states/activityIdInDrawerState';
import { isActivityInCreateModeState } from '@/activities/states/isActivityInCreateModeState';
import { isUpsertingActivityInDBState } from '@/activities/states/isCreatingActivityInDBState';
import { temporaryActivityForEditorState } from '@/activities/states/temporaryActivityForEditorState';
import { Activity } from '@/activities/types/Activity';
import { ActivityTarget } from '@/activities/types/ActivityTarget';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useDeleteRecordFromCache } from '@/object-record/cache/hooks/useDeleteRecordFromCache';
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { mapToRecordId } from '@/object-record/utils/mapToObjectId';
import { IconButton } from '@/ui/input/button/components/IconButton';
import { isRightDrawerOpenState } from '@/ui/layout/right-drawer/states/isRightDrawerOpenState';
import { isDefined } from '~/utils/isDefined';
const StyledButtonContainer = styled.div`
display: inline-flex;
gap: ${({ theme }) => theme.spacing(2)};
`;
export const ActivityActionBar = () => {
const viewableRecordId = useRecoilValue(viewableRecordIdState);
const activityIdInDrawer = useRecoilValue(activityIdInDrawerState);
const [, setIsRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
const { deleteOneRecord: deleteOneActivity } = useDeleteOneRecord({
objectNameSingular: CoreObjectNameSingular.Activity,
});
const { deleteManyRecords: deleteManyActivityTargets } = useDeleteManyRecords(
{
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
},
);
const [temporaryActivityForEditor, setTemporaryActivityForEditor] =
useRecoilState(temporaryActivityForEditorState);
const deleteActivityFromCache = useDeleteRecordFromCache({
objectNameSingular: CoreObjectNameSingular.Activity,
});
const deleteActivityTargetFromCache = useDeleteRecordFromCache({
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
});
const [isActivityInCreateMode] = useRecoilState(isActivityInCreateModeState);
const [isUpsertingActivityInDB] = useRecoilState(
isUpsertingActivityInDBState,
);
const { refreshShowPageFindManyActivitiesQueries } =
useRefreshShowPageFindManyActivitiesQueries();
const deleteActivity = useRecoilCallback(
({ snapshot }) =>
async () => {
if (!activityIdInDrawer) {
throw new Error(
'activityIdInDrawer is not defined, this should not happen',
);
}
const activity = snapshot
.getLoadable(recordStoreFamilyState(activityIdInDrawer))
.getValue() as Activity;
setIsRightDrawerOpen(false);
if (!isNonEmptyString(viewableRecordId)) {
return;
}
if (isActivityInCreateMode && isDefined(temporaryActivityForEditor)) {
deleteActivityFromCache(temporaryActivityForEditor);
setTemporaryActivityForEditor(null);
return;
}
if (isNonEmptyString(activityIdInDrawer)) {
const activityTargetIdsToDelete: string[] =
activity.activityTargets.map(mapToRecordId) ?? [];
deleteActivityFromCache(activity);
activity.activityTargets.forEach((activityTarget: ActivityTarget) => {
deleteActivityTargetFromCache(activityTarget);
});
refreshShowPageFindManyActivitiesQueries();
if (isNonEmptyArray(activityTargetIdsToDelete)) {
await deleteManyActivityTargets(activityTargetIdsToDelete);
}
await deleteOneActivity?.(viewableRecordId);
}
},
[
activityIdInDrawer,
setIsRightDrawerOpen,
viewableRecordId,
isActivityInCreateMode,
temporaryActivityForEditor,
deleteActivityFromCache,
setTemporaryActivityForEditor,
refreshShowPageFindManyActivitiesQueries,
deleteOneActivity,
deleteActivityTargetFromCache,
deleteManyActivityTargets,
],
);
const actionsAreDisabled = isUpsertingActivityInDB;
return (
<StyledButtonContainer>
<IconButton
Icon={IconTrash}
onClick={deleteActivity}
size="medium"
variant="secondary"
disabled={actionsAreDisabled}
/>
</StyledButtonContainer>
);
};

View File

@ -1,39 +0,0 @@
import styled from '@emotion/styled';
import { ActivityEditor } from '@/activities/components/ActivityEditor';
import { ActivityEditorEffect } from '@/activities/components/ActivityEditorEffect';
import { RecordValueSetterEffect } from '@/object-record/record-store/components/RecordValueSetterEffect';
const StyledContainer = styled.div`
box-sizing: border-box;
display: flex;
flex-direction: column;
height: 100%;
justify-content: space-between;
overflow-y: auto;
position: relative;
`;
type RightDrawerActivityProps = {
activityId: string;
showComment?: boolean;
fillTitleFromBody?: boolean;
};
export const RightDrawerActivity = ({
activityId,
showComment = false,
fillTitleFromBody = false,
}: RightDrawerActivityProps) => {
return (
<StyledContainer>
<RecordValueSetterEffect recordId={activityId} />
<ActivityEditorEffect activityId={activityId} />
<ActivityEditor
activityId={activityId}
showComment={showComment}
fillTitleFromBody={fillTitleFromBody}
/>
</StyledContainer>
);
};

View File

@ -1,21 +0,0 @@
import { useRecoilValue } from 'recoil';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { RightDrawerActivity } from '../RightDrawerActivity';
export const RightDrawerCreateActivity = () => {
const viewableRecordId = useRecoilValue(viewableRecordIdState);
return (
<>
{viewableRecordId && (
<RightDrawerActivity
activityId={viewableRecordId}
showComment={false}
fillTitleFromBody={true}
/>
)}
</>
);
};

View File

@ -1,17 +0,0 @@
import { useRecoilValue } from 'recoil';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { RightDrawerActivity } from '../RightDrawerActivity';
export const RightDrawerEditActivity = () => {
const viewableRecordId = useRecoilValue(viewableRecordIdState);
return (
<>
{viewableRecordId && (
<RightDrawerActivity activityId={viewableRecordId} />
)}
</>
);
};