Activity injection into Apollo cache (#3665)
- Created addRecordInCache to inject a record in Apollo cache and inject single read query on this record - Created createOneRecordInCache and createManyRecordsInCache that uses this addRecordInCache - Created useOpenCreateActivityDrawerV2 hook to create an activity in cache and inject it into all other relevant requests in the app before opening activity drawer - Refactored DEFAULT_SEARCH_REQUEST_LIMIT constant and hardcoded arbitrary request limits - Added Apollo dev logs to see errors in the console when manipulating cache
This commit is contained in:
@ -6,6 +6,7 @@ import { RecoilRoot } from 'recoil';
|
|||||||
|
|
||||||
import { ApolloProvider } from '@/apollo/components/ApolloProvider';
|
import { ApolloProvider } from '@/apollo/components/ApolloProvider';
|
||||||
import { ClientConfigProvider } from '@/client-config/components/ClientConfigProvider';
|
import { ClientConfigProvider } from '@/client-config/components/ClientConfigProvider';
|
||||||
|
import { ApolloDevLogEffect } from '@/debug/components/ApolloDevLogEffect';
|
||||||
import { RecoilDebugObserverEffect } from '@/debug/components/RecoilDebugObserver';
|
import { RecoilDebugObserverEffect } from '@/debug/components/RecoilDebugObserver';
|
||||||
import { AppErrorBoundary } from '@/error-handler/components/AppErrorBoundary';
|
import { AppErrorBoundary } from '@/error-handler/components/AppErrorBoundary';
|
||||||
import { ExceptionHandlerProvider } from '@/error-handler/components/ExceptionHandlerProvider';
|
import { ExceptionHandlerProvider } from '@/error-handler/components/ExceptionHandlerProvider';
|
||||||
@ -35,6 +36,7 @@ root.render(
|
|||||||
<RecoilRoot>
|
<RecoilRoot>
|
||||||
<AppErrorBoundary>
|
<AppErrorBoundary>
|
||||||
<RecoilDebugObserverEffect />
|
<RecoilDebugObserverEffect />
|
||||||
|
<ApolloDevLogEffect />
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
|
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
|
||||||
<IconsProvider>
|
<IconsProvider>
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { useMemo } from 'react';
|
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { ActivityTargetObjectRecord } from '@/activities/types/ActivityTargetObject';
|
import { ActivityTargetObjectRecord } from '@/activities/types/ActivityTargetObject';
|
||||||
@ -15,41 +14,40 @@ export const useActivityTargetObjectRecords = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||||
|
|
||||||
const { records: activityTargets } = useFindManyRecords({
|
const { records: activityTargets, loading: loadingActivityTargets } =
|
||||||
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
useFindManyRecords({
|
||||||
filter: {
|
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||||
activityId: {
|
filter: {
|
||||||
eq: activityId,
|
activityId: {
|
||||||
|
eq: activityId,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
});
|
|
||||||
|
|
||||||
const activityTargetObjectRecords = useMemo(() => {
|
const activityTargetObjectRecords = activityTargets
|
||||||
return activityTargets
|
.map<Nullable<ActivityTargetObjectRecord>>((activityTarget) => {
|
||||||
.map<Nullable<ActivityTargetObjectRecord>>((activityTarget) => {
|
const correspondingObjectMetadataItem = objectMetadataItems.find(
|
||||||
const correspondingObjectMetadataItem = objectMetadataItems.find(
|
(objectMetadataItem) =>
|
||||||
(objectMetadataItem) =>
|
isDefined(activityTarget[objectMetadataItem.nameSingular]) &&
|
||||||
isDefined(activityTarget[objectMetadataItem.nameSingular]) &&
|
!objectMetadataItem.isSystem,
|
||||||
!objectMetadataItem.isSystem,
|
);
|
||||||
);
|
|
||||||
|
|
||||||
if (!correspondingObjectMetadataItem) {
|
if (!correspondingObjectMetadataItem) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activityTargetRecord: activityTarget,
|
activityTargetRecord: activityTarget,
|
||||||
targetObjectRecord:
|
targetObjectRecord:
|
||||||
activityTarget[correspondingObjectMetadataItem.nameSingular],
|
activityTarget[correspondingObjectMetadataItem.nameSingular],
|
||||||
targetObjectMetadataItem: correspondingObjectMetadataItem,
|
targetObjectMetadataItem: correspondingObjectMetadataItem,
|
||||||
targetObjectNameSingular:
|
targetObjectNameSingular: correspondingObjectMetadataItem.nameSingular,
|
||||||
correspondingObjectMetadataItem.nameSingular,
|
};
|
||||||
};
|
})
|
||||||
})
|
.filter(isDefined);
|
||||||
.filter(isDefined);
|
|
||||||
}, [activityTargets, objectMetadataItems]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activityTargetObjectRecords,
|
activityTargetObjectRecords,
|
||||||
|
loadingActivityTargets,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
import { getActivityTargetObjectFieldIdName } from '@/activities/utils/getTargetObjectFilterFieldName';
|
import { getActivityTargetObjectFieldIdName } from '@/activities/utils/getTargetObjectFilterFieldName';
|
||||||
@ -13,17 +15,26 @@ export const useActivityTargets = ({
|
|||||||
nameSingular: targetableObject.targetObjectNameSingular,
|
nameSingular: targetableObject.targetObjectNameSingular,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { records: activityTargets } = useFindManyRecords({
|
const [initialized, setInitialized] = useState(false);
|
||||||
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
|
||||||
filter: {
|
const { records: activityTargets, loading: loadingActivityTargets } =
|
||||||
[targetObjectFieldName]: {
|
useFindManyRecords({
|
||||||
eq: targetableObject.id,
|
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||||
|
filter: {
|
||||||
|
[targetObjectFieldName]: {
|
||||||
|
eq: targetableObject.id,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
onCompleted: () => {
|
||||||
skip: !targetableObject.id,
|
if (!initialized) {
|
||||||
});
|
setInitialized(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activityTargets: activityTargets as ActivityTarget[],
|
activityTargets: activityTargets as ActivityTarget[],
|
||||||
|
loadingActivityTargets,
|
||||||
|
initialized,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,76 +0,0 @@
|
|||||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
|
||||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
|
||||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
|
||||||
|
|
||||||
export const useHandleCheckableActivityTargetChange = ({
|
|
||||||
activityId,
|
|
||||||
currentActivityTargets,
|
|
||||||
}: {
|
|
||||||
activityId: string;
|
|
||||||
currentActivityTargets: any[];
|
|
||||||
}) => {
|
|
||||||
const { createOneRecord: createOneActivityTarget } =
|
|
||||||
useCreateOneRecord<ActivityTarget>({
|
|
||||||
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
|
||||||
});
|
|
||||||
const { deleteOneRecord: deleteOneActivityTarget } = useDeleteOneRecord({
|
|
||||||
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
|
||||||
});
|
|
||||||
|
|
||||||
return async (
|
|
||||||
entityValues: Record<string, boolean>,
|
|
||||||
entitiesToSelect: any,
|
|
||||||
selectedEntities: any,
|
|
||||||
) => {
|
|
||||||
if (!activityId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const currentActivityTargetRecordIds = currentActivityTargets.map(
|
|
||||||
({ companyId, personId }) => companyId ?? personId ?? '',
|
|
||||||
);
|
|
||||||
|
|
||||||
const idsToAdd = Object.entries(entityValues)
|
|
||||||
.filter(
|
|
||||||
([recordId, value]) =>
|
|
||||||
value && !currentActivityTargetRecordIds.includes(recordId),
|
|
||||||
)
|
|
||||||
.map(([id, _]) => id);
|
|
||||||
|
|
||||||
const idsToDelete = Object.entries(entityValues)
|
|
||||||
.filter(([_, value]) => !value)
|
|
||||||
.map(([id, _]) => id);
|
|
||||||
|
|
||||||
if (idsToAdd.length) {
|
|
||||||
idsToAdd.forEach((id) => {
|
|
||||||
const entityFromToSelect = entitiesToSelect.filter(
|
|
||||||
(entity: any) => entity.id === id,
|
|
||||||
).length
|
|
||||||
? entitiesToSelect.filter((entity: any) => entity.id === id)[0]
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const entityFromSelected = selectedEntities.filter(
|
|
||||||
(entity: any) => entity.id === id,
|
|
||||||
).length
|
|
||||||
? selectedEntities.filter((entity: any) => entity.id === id)[0]
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const entity = entityFromToSelect ?? entityFromSelected;
|
|
||||||
createOneActivityTarget?.({
|
|
||||||
activityId: activityId,
|
|
||||||
companyId: entity.record.__typename === 'Company' ? entity.id : null,
|
|
||||||
personId: entity.record.__typename === 'Person' ? entity.id : null,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idsToDelete.length) {
|
|
||||||
idsToDelete.forEach((id) => {
|
|
||||||
const currentActivityTargetId = currentActivityTargets.filter(
|
|
||||||
({ companyId, personId }) => companyId === id || personId === id,
|
|
||||||
)[0].id;
|
|
||||||
deleteOneActivityTarget?.(currentActivityTargetId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
|
||||||
|
import { Activity } from '@/activities/types/Activity';
|
||||||
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { useModifyRecordFromCache } from '@/object-record/cache/hooks/useModifyRecordFromCache';
|
||||||
|
import { getCacheReferenceFromRecord } from '@/object-record/cache/utils/getCacheReferenceFromRecord';
|
||||||
|
|
||||||
|
export const useModifyActivityOnActivityTargetsCache = () => {
|
||||||
|
const { objectMetadataItem: objectMetadataItemActivityTarget } =
|
||||||
|
useObjectMetadataItem({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||||
|
});
|
||||||
|
|
||||||
|
const modifyActivityTargetFromCache = useModifyRecordFromCache({
|
||||||
|
objectMetadataItem: objectMetadataItemActivityTarget,
|
||||||
|
});
|
||||||
|
|
||||||
|
const apolloClient = useApolloClient();
|
||||||
|
|
||||||
|
const modifyActivityOnActivityTargetsCache = ({
|
||||||
|
activityTargetIds,
|
||||||
|
activity,
|
||||||
|
}: {
|
||||||
|
activityTargetIds: string[];
|
||||||
|
activity: Activity;
|
||||||
|
}) => {
|
||||||
|
for (const activityTargetId of activityTargetIds) {
|
||||||
|
modifyActivityTargetFromCache(activityTargetId, {
|
||||||
|
activity: () => {
|
||||||
|
const newActivityReference = getCacheReferenceFromRecord({
|
||||||
|
apolloClient,
|
||||||
|
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||||
|
record: activity,
|
||||||
|
});
|
||||||
|
|
||||||
|
return newActivityReference;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifyActivityOnActivityTargetsCache,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
|
||||||
|
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||||
|
import { CachedObjectRecordConnection } from '@/apollo/types/CachedObjectRecordConnection';
|
||||||
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { useModifyRecordFromCache } from '@/object-record/cache/hooks/useModifyRecordFromCache';
|
||||||
|
import { getCachedRecordEdgesFromRecords } from '@/object-record/cache/utils/getCachedRecordEdgesFromRecords';
|
||||||
|
|
||||||
|
export const useModifyActivityTargetsOnActivityCache = () => {
|
||||||
|
const { objectMetadataItem: objectMetadataItemActivity } =
|
||||||
|
useObjectMetadataItem({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||||
|
});
|
||||||
|
|
||||||
|
const modifyActivityFromCache = useModifyRecordFromCache({
|
||||||
|
objectMetadataItem: objectMetadataItemActivity,
|
||||||
|
});
|
||||||
|
|
||||||
|
const apolloClient = useApolloClient();
|
||||||
|
|
||||||
|
const modifyActivityTargetsOnActivityCache = ({
|
||||||
|
activityId,
|
||||||
|
activityTargets,
|
||||||
|
}: {
|
||||||
|
activityId: string;
|
||||||
|
activityTargets: ActivityTarget[];
|
||||||
|
}) => {
|
||||||
|
modifyActivityFromCache(activityId, {
|
||||||
|
activityTargets: (
|
||||||
|
activityTargetsCachedConnection: CachedObjectRecordConnection,
|
||||||
|
) => {
|
||||||
|
const newActivityTargetsCachedRecordEdges =
|
||||||
|
getCachedRecordEdgesFromRecords({
|
||||||
|
apolloClient,
|
||||||
|
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||||
|
records: activityTargets,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...activityTargetsCachedConnection,
|
||||||
|
edges: newActivityTargetsCachedRecordEdges,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifyActivityTargetsOnActivityCache,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,8 +1,10 @@
|
|||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
import { ActivityType } from '@/activities/types/Activity';
|
import { ActivityType } from '@/activities/types/Activity';
|
||||||
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||||
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
import { ActivityTargetableObject } from '../types/ActivityTargetableEntity';
|
import { ActivityTargetableObject } from '../types/ActivityTargetableEntity';
|
||||||
|
|
||||||
@ -27,21 +29,35 @@ export const useOpenCreateActivityDrawerForSelectedRowIds = (
|
|||||||
getSelectedRowIdsSelector(),
|
getSelectedRowIdsSelector(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let activityTargetableEntityArray: ActivityTargetableObject[] =
|
let activityTargetableObjectArray: ActivityTargetableObject[] =
|
||||||
selectedRowIds.map((id: string) => ({
|
selectedRowIds
|
||||||
type: 'Custom',
|
.map((recordId: string) => {
|
||||||
targetObjectNameSingular: objectNameSingular,
|
const targetObjectRecord = getSnapshotValue(
|
||||||
id,
|
snapshot,
|
||||||
}));
|
recordStoreFamilyState(recordId),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!targetObjectRecord) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'Custom',
|
||||||
|
targetObjectNameSingular: objectNameSingular,
|
||||||
|
id: recordId,
|
||||||
|
targetObjectRecord,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter(isDefined);
|
||||||
|
|
||||||
if (relatedEntities) {
|
if (relatedEntities) {
|
||||||
activityTargetableEntityArray =
|
activityTargetableObjectArray =
|
||||||
activityTargetableEntityArray.concat(relatedEntities);
|
activityTargetableObjectArray.concat(relatedEntities);
|
||||||
}
|
}
|
||||||
|
|
||||||
openCreateActivityDrawer({
|
openCreateActivityDrawer({
|
||||||
type,
|
type,
|
||||||
targetableObjects: activityTargetableEntityArray,
|
targetableObjects: activityTargetableObjectArray,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[openCreateActivityDrawer, getSelectedRowIdsSelector],
|
[openCreateActivityDrawer, getSelectedRowIdsSelector],
|
||||||
|
|||||||
@ -0,0 +1,163 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
|
import { useActivityTargets } from '@/activities/hooks/useActivityTargets';
|
||||||
|
import { useModifyActivityOnActivityTargetsCache } from '@/activities/hooks/useModifyActivityOnActivityTargetCache';
|
||||||
|
import { useModifyActivityTargetsOnActivityCache } from '@/activities/hooks/useModifyActivityTargetsOnActivityCache';
|
||||||
|
import { useWriteActivityTargetsInCache } from '@/activities/hooks/useWriteActivityTargetsInCache';
|
||||||
|
import { useInjectIntoActivityTargetInlineCellCache } from '@/activities/inline-cell/hooks/useInjectIntoActivityTargetInlineCellCache';
|
||||||
|
import { useInjectIntoTimelineActivitiesQuery } from '@/activities/timeline/hooks/useInjectIntoTimelineActivitiesQuery';
|
||||||
|
import { Activity, ActivityType } from '@/activities/types/Activity';
|
||||||
|
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||||
|
import { getActivityTargetsToCreateFromTargetableObjects } from '@/activities/utils/getActivityTargetsToCreateFromTargetableObjects';
|
||||||
|
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { useCreateManyRecordsInCache } from '@/object-record/hooks/useCreateManyRecordsInCache';
|
||||||
|
import { useCreateOneRecordInCache } from '@/object-record/hooks/useCreateOneRecordInCache';
|
||||||
|
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||||
|
import { mapToRecordId } from '@/object-record/utils/mapToObjectId';
|
||||||
|
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||||
|
import { RightDrawerHotkeyScope } from '@/ui/layout/right-drawer/types/RightDrawerHotkeyScope';
|
||||||
|
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
|
||||||
|
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||||
|
|
||||||
|
import { activityTargetableEntityArrayState } from '../states/activityTargetableEntityArrayState';
|
||||||
|
import { viewableActivityIdState } from '../states/viewableActivityIdState';
|
||||||
|
import { ActivityTargetableObject } from '../types/ActivityTargetableEntity';
|
||||||
|
|
||||||
|
export const useOpenCreateActivityDrawerV2 = ({
|
||||||
|
targetableObject,
|
||||||
|
}: {
|
||||||
|
targetableObject: ActivityTargetableObject;
|
||||||
|
}) => {
|
||||||
|
const { openRightDrawer } = useRightDrawer();
|
||||||
|
|
||||||
|
const { createManyRecordsInCache: createManyActivityTargetsInCache } =
|
||||||
|
useCreateManyRecordsInCache<ActivityTarget>({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { createOneRecordInCache: createOneActivityInCache } =
|
||||||
|
useCreateOneRecordInCache<Activity>({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||||
|
|
||||||
|
const { record: workspaceMemberRecord } = useFindOneRecord({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.WorkspaceMember,
|
||||||
|
objectRecordId: currentWorkspaceMember?.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const setHotkeyScope = useSetHotkeyScope();
|
||||||
|
|
||||||
|
const [, setActivityTargetableEntityArray] = useRecoilState(
|
||||||
|
activityTargetableEntityArrayState,
|
||||||
|
);
|
||||||
|
const [, setViewableActivityId] = useRecoilState(viewableActivityIdState);
|
||||||
|
|
||||||
|
const { activityTargets } = useActivityTargets({
|
||||||
|
targetableObject,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { injectIntoTimelineActivitiesNextQuery } =
|
||||||
|
useInjectIntoTimelineActivitiesQuery();
|
||||||
|
|
||||||
|
const { injectIntoActivityTargetInlineCellCache } =
|
||||||
|
useInjectIntoActivityTargetInlineCellCache();
|
||||||
|
|
||||||
|
const { injectIntoUseActivityTargets } = useWriteActivityTargetsInCache();
|
||||||
|
|
||||||
|
const { modifyActivityTargetsOnActivityCache } =
|
||||||
|
useModifyActivityTargetsOnActivityCache();
|
||||||
|
|
||||||
|
const { modifyActivityOnActivityTargetsCache } =
|
||||||
|
useModifyActivityOnActivityTargetsCache();
|
||||||
|
|
||||||
|
return useCallback(
|
||||||
|
async ({
|
||||||
|
type,
|
||||||
|
targetableObjects,
|
||||||
|
assigneeId,
|
||||||
|
}: {
|
||||||
|
type: ActivityType;
|
||||||
|
targetableObjects: ActivityTargetableObject[];
|
||||||
|
assigneeId?: string;
|
||||||
|
}) => {
|
||||||
|
const activityId = v4();
|
||||||
|
|
||||||
|
const createdActivityInCache = await createOneActivityInCache({
|
||||||
|
id: activityId,
|
||||||
|
author: workspaceMemberRecord,
|
||||||
|
authorId: workspaceMemberRecord?.id,
|
||||||
|
assignee: !assigneeId ? workspaceMemberRecord : undefined,
|
||||||
|
assigneeId:
|
||||||
|
assigneeId ?? isNonEmptyString(workspaceMemberRecord?.id)
|
||||||
|
? workspaceMemberRecord?.id
|
||||||
|
: undefined,
|
||||||
|
type: type,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!createdActivityInCache) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const activityTargetsToCreate =
|
||||||
|
getActivityTargetsToCreateFromTargetableObjects({
|
||||||
|
activityId,
|
||||||
|
targetableObjects,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createdActivityTargetsInCache =
|
||||||
|
await createManyActivityTargetsInCache(activityTargetsToCreate);
|
||||||
|
|
||||||
|
injectIntoUseActivityTargets({
|
||||||
|
targetableObject,
|
||||||
|
activityTargetsToInject: createdActivityTargetsInCache,
|
||||||
|
});
|
||||||
|
|
||||||
|
injectIntoTimelineActivitiesNextQuery({
|
||||||
|
activityTargets,
|
||||||
|
activityToInject: createdActivityInCache,
|
||||||
|
});
|
||||||
|
|
||||||
|
injectIntoActivityTargetInlineCellCache({
|
||||||
|
activityId,
|
||||||
|
activityTargetsToInject: createdActivityTargetsInCache,
|
||||||
|
});
|
||||||
|
|
||||||
|
modifyActivityTargetsOnActivityCache({
|
||||||
|
activityId,
|
||||||
|
activityTargets: createdActivityTargetsInCache,
|
||||||
|
});
|
||||||
|
|
||||||
|
modifyActivityOnActivityTargetsCache({
|
||||||
|
activityTargetIds: createdActivityTargetsInCache.map(mapToRecordId),
|
||||||
|
activity: createdActivityInCache,
|
||||||
|
});
|
||||||
|
|
||||||
|
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
||||||
|
setViewableActivityId(activityId);
|
||||||
|
setActivityTargetableEntityArray(targetableObjects ?? []);
|
||||||
|
openRightDrawer(RightDrawerPages.CreateActivity);
|
||||||
|
},
|
||||||
|
[
|
||||||
|
openRightDrawer,
|
||||||
|
setActivityTargetableEntityArray,
|
||||||
|
createManyActivityTargetsInCache,
|
||||||
|
setHotkeyScope,
|
||||||
|
setViewableActivityId,
|
||||||
|
createOneActivityInCache,
|
||||||
|
workspaceMemberRecord,
|
||||||
|
activityTargets,
|
||||||
|
targetableObject,
|
||||||
|
injectIntoTimelineActivitiesNextQuery,
|
||||||
|
injectIntoActivityTargetInlineCellCache,
|
||||||
|
injectIntoUseActivityTargets,
|
||||||
|
modifyActivityTargetsOnActivityCache,
|
||||||
|
modifyActivityOnActivityTargetsCache,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
|
||||||
|
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||||
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
|
import { getActivityTargetObjectFieldIdName } from '@/activities/utils/getTargetObjectFilterFieldName';
|
||||||
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { getRecordConnectionFromRecords } from '@/object-record/cache/utils/getRecordConnectionFromRecords';
|
||||||
|
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
|
||||||
|
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
||||||
|
|
||||||
|
export const useWriteActivityTargetsInCache = () => {
|
||||||
|
const apolloClient = useApolloClient();
|
||||||
|
|
||||||
|
const {
|
||||||
|
objectMetadataItem: objectMetadataItemActivityTarget,
|
||||||
|
findManyRecordsQuery: findManyActivityTargetsQuery,
|
||||||
|
} = useObjectMetadataItem({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||||
|
});
|
||||||
|
|
||||||
|
const injectIntoUseActivityTargets = ({
|
||||||
|
targetableObject,
|
||||||
|
activityTargetsToInject,
|
||||||
|
}: {
|
||||||
|
targetableObject: Pick<
|
||||||
|
ActivityTargetableObject,
|
||||||
|
'id' | 'targetObjectNameSingular'
|
||||||
|
>;
|
||||||
|
activityTargetsToInject: ActivityTarget[];
|
||||||
|
}) => {
|
||||||
|
const targetObjectFieldName = getActivityTargetObjectFieldIdName({
|
||||||
|
nameSingular: targetableObject.targetObjectNameSingular,
|
||||||
|
});
|
||||||
|
|
||||||
|
const existingActivityTargetsForTargetableObjectQueryResult =
|
||||||
|
apolloClient.readQuery({
|
||||||
|
query: findManyActivityTargetsQuery,
|
||||||
|
variables: {
|
||||||
|
filter: {
|
||||||
|
[targetObjectFieldName]: {
|
||||||
|
eq: targetableObject.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const existingActivityTargetsForTargetableObject =
|
||||||
|
getRecordsFromRecordConnection({
|
||||||
|
recordConnection: existingActivityTargetsForTargetableObjectQueryResult[
|
||||||
|
objectMetadataItemActivityTarget.namePlural
|
||||||
|
] as ObjectRecordConnection<ActivityTarget>,
|
||||||
|
});
|
||||||
|
|
||||||
|
const newActivityTargetsForTargetableObject = [
|
||||||
|
...existingActivityTargetsForTargetableObject,
|
||||||
|
...activityTargetsToInject,
|
||||||
|
];
|
||||||
|
|
||||||
|
const newActivityTargetsConnection = getRecordConnectionFromRecords({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||||
|
records: newActivityTargetsForTargetableObject,
|
||||||
|
});
|
||||||
|
|
||||||
|
apolloClient.writeQuery({
|
||||||
|
query: findManyActivityTargetsQuery,
|
||||||
|
variables: {
|
||||||
|
filter: {
|
||||||
|
[targetObjectFieldName]: {
|
||||||
|
eq: targetableObject.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
[objectMetadataItemActivityTarget.namePlural]:
|
||||||
|
newActivityTargetsConnection,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
injectIntoUseActivityTargets,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
|
||||||
|
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||||
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { getRecordConnectionFromEdges } from '@/object-record/cache/utils/getRecordConnectionFromEdges';
|
||||||
|
import { getRecordEdgeFromRecord } from '@/object-record/cache/utils/getRecordEdgeFromRecord';
|
||||||
|
|
||||||
|
export const useInjectIntoActivityTargetInlineCellCache = () => {
|
||||||
|
const apolloClient = useApolloClient();
|
||||||
|
|
||||||
|
const {
|
||||||
|
objectMetadataItem: objectMetadataItemActivityTarget,
|
||||||
|
findManyRecordsQuery: findManyActivityTargetsQuery,
|
||||||
|
} = useObjectMetadataItem({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||||
|
});
|
||||||
|
|
||||||
|
const injectIntoActivityTargetInlineCellCache = ({
|
||||||
|
activityId,
|
||||||
|
activityTargetsToInject,
|
||||||
|
}: {
|
||||||
|
activityId: string;
|
||||||
|
activityTargetsToInject: ActivityTarget[];
|
||||||
|
}) => {
|
||||||
|
const newActivityTargetEdgesForCache = activityTargetsToInject.map(
|
||||||
|
(activityTargetToInject) =>
|
||||||
|
getRecordEdgeFromRecord({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||||
|
record: activityTargetToInject,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const newActivityTargetConnectionForCache = getRecordConnectionFromEdges({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||||
|
edges: newActivityTargetEdgesForCache,
|
||||||
|
});
|
||||||
|
|
||||||
|
apolloClient.writeQuery({
|
||||||
|
query: findManyActivityTargetsQuery,
|
||||||
|
variables: {
|
||||||
|
filter: {
|
||||||
|
activityId: {
|
||||||
|
eq: activityId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
[objectMetadataItemActivityTarget.namePlural]:
|
||||||
|
newActivityTargetConnectionForCache,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
injectIntoActivityTargetInlineCellCache,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -14,6 +14,7 @@ export const PageAddTaskButton = ({
|
|||||||
const { selectedFilter } = useFilterDropdown({
|
const { selectedFilter } = useFilterDropdown({
|
||||||
filterDropdownId: filterDropdownId,
|
filterDropdownId: filterDropdownId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const openCreateActivity = useOpenCreateActivityDrawer();
|
const openCreateActivity = useOpenCreateActivityDrawer();
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
|
|||||||
@ -1,14 +1,9 @@
|
|||||||
import React from 'react';
|
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { isNonEmptyString } from '@sniptt/guards';
|
|
||||||
|
|
||||||
import { ActivityCreateButton } from '@/activities/components/ActivityCreateButton';
|
import { ActivityCreateButton } from '@/activities/components/ActivityCreateButton';
|
||||||
import { useActivityTargets } from '@/activities/hooks/useActivityTargets';
|
|
||||||
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
||||||
import { Activity } from '@/activities/types/Activity';
|
import { useTimelineActivities } from '@/activities/timeline/hooks/useTimelineActivities';
|
||||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
|
||||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
|
||||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||||
|
|
||||||
import { TimelineItemsContainer } from './TimelineItemsContainer';
|
import { TimelineItemsContainer } from './TimelineItemsContainer';
|
||||||
@ -55,26 +50,22 @@ export const Timeline = ({
|
|||||||
}: {
|
}: {
|
||||||
targetableObject: ActivityTargetableObject;
|
targetableObject: ActivityTargetableObject;
|
||||||
}) => {
|
}) => {
|
||||||
const { activityTargets } = useActivityTargets({ targetableObject });
|
const { activities, initialized } = useTimelineActivities({
|
||||||
|
targetableObject,
|
||||||
const { records: activities } = useFindManyRecords({
|
|
||||||
skip: !activityTargets?.length,
|
|
||||||
objectNameSingular: CoreObjectNameSingular.Activity,
|
|
||||||
filter: {
|
|
||||||
id: {
|
|
||||||
in: activityTargets
|
|
||||||
?.map((activityTarget) => activityTarget.activityId)
|
|
||||||
.filter(isNonEmptyString),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
orderBy: {
|
|
||||||
createdAt: 'AscNullsFirst',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const openCreateActivity = useOpenCreateActivityDrawer();
|
const openCreateActivity = useOpenCreateActivityDrawer();
|
||||||
|
|
||||||
if (!activities.length) {
|
const showEmptyState = initialized && activities.length === 0;
|
||||||
|
|
||||||
|
const showLoadingState = !initialized;
|
||||||
|
|
||||||
|
if (showLoadingState) {
|
||||||
|
// TODO: Display a beautiful loading page
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showEmptyState) {
|
||||||
return (
|
return (
|
||||||
<StyledTimelineEmptyContainer>
|
<StyledTimelineEmptyContainer>
|
||||||
<StyledEmptyTimelineTitle>No activity yet</StyledEmptyTimelineTitle>
|
<StyledEmptyTimelineTitle>No activity yet</StyledEmptyTimelineTitle>
|
||||||
@ -99,7 +90,7 @@ export const Timeline = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledMainContainer>
|
<StyledMainContainer>
|
||||||
<TimelineItemsContainer activities={activities as Activity[]} />
|
<TimelineItemsContainer activities={activities} />
|
||||||
</StyledMainContainer>
|
</StyledMainContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,89 @@
|
|||||||
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
|
|
||||||
|
import { makeTimelineActivitiesQueryVariables } from '@/activities/timeline/utils/makeTimelineActivitiesQueryVariables';
|
||||||
|
import { Activity } from '@/activities/types/Activity';
|
||||||
|
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||||
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { getRecordConnectionFromRecords } from '@/object-record/cache/utils/getRecordConnectionFromRecords';
|
||||||
|
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
|
||||||
|
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
||||||
|
|
||||||
|
export const useInjectIntoTimelineActivitiesQuery = () => {
|
||||||
|
const apolloClient = useApolloClient();
|
||||||
|
|
||||||
|
const {
|
||||||
|
objectMetadataItem: objectMetadataItemActivity,
|
||||||
|
findManyRecordsQuery: findManyActivitiesQuery,
|
||||||
|
} = useObjectMetadataItem({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||||
|
});
|
||||||
|
|
||||||
|
const injectIntoTimelineActivitiesQuery = ({
|
||||||
|
activityTargets,
|
||||||
|
activityToInject,
|
||||||
|
}: {
|
||||||
|
activityTargets: ActivityTarget[];
|
||||||
|
activityToInject: Activity;
|
||||||
|
}) => {
|
||||||
|
const activityIds = activityTargets
|
||||||
|
?.map((activityTarget) => activityTarget.activityId)
|
||||||
|
.filter(isNonEmptyString);
|
||||||
|
|
||||||
|
const timelineActivitiesQueryVariables =
|
||||||
|
makeTimelineActivitiesQueryVariables({
|
||||||
|
activityIds,
|
||||||
|
});
|
||||||
|
|
||||||
|
const exitistingActivitiesQueryResult = apolloClient.readQuery({
|
||||||
|
query: findManyActivitiesQuery,
|
||||||
|
variables: timelineActivitiesQueryVariables,
|
||||||
|
});
|
||||||
|
|
||||||
|
const extistingActivities = exitistingActivitiesQueryResult
|
||||||
|
? getRecordsFromRecordConnection({
|
||||||
|
recordConnection: exitistingActivitiesQueryResult[
|
||||||
|
objectMetadataItemActivity.namePlural
|
||||||
|
] as ObjectRecordConnection<Activity>,
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const newActivity = {
|
||||||
|
...activityToInject,
|
||||||
|
__typename: 'Activity',
|
||||||
|
};
|
||||||
|
|
||||||
|
const newActivitiesSortedAsActivitiesQuery = [
|
||||||
|
newActivity,
|
||||||
|
...extistingActivities,
|
||||||
|
];
|
||||||
|
|
||||||
|
const newActivityIdsSortedAsActivityTargetsQuery = [
|
||||||
|
...extistingActivities,
|
||||||
|
newActivity,
|
||||||
|
].map((activity) => activity.id);
|
||||||
|
|
||||||
|
const newTimelineActivitiesQueryVariables =
|
||||||
|
makeTimelineActivitiesQueryVariables({
|
||||||
|
activityIds: newActivityIdsSortedAsActivityTargetsQuery,
|
||||||
|
});
|
||||||
|
|
||||||
|
const newActivityConnectionForCache = getRecordConnectionFromRecords({
|
||||||
|
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||||
|
records: newActivitiesSortedAsActivitiesQuery,
|
||||||
|
});
|
||||||
|
|
||||||
|
apolloClient.writeQuery({
|
||||||
|
query: findManyActivitiesQuery,
|
||||||
|
variables: newTimelineActivitiesQueryVariables,
|
||||||
|
data: {
|
||||||
|
[objectMetadataItemActivity.namePlural]: newActivityConnectionForCache,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
injectIntoTimelineActivitiesNextQuery: injectIntoTimelineActivitiesQuery,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { isNonEmptyArray, isNonEmptyString } from '@sniptt/guards';
|
||||||
|
|
||||||
|
import { useActivityTargets } from '@/activities/hooks/useActivityTargets';
|
||||||
|
import { makeTimelineActivitiesQueryVariables } from '@/activities/timeline/utils/makeTimelineActivitiesQueryVariables';
|
||||||
|
import { Activity } from '@/activities/types/Activity';
|
||||||
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||||
|
|
||||||
|
export const useTimelineActivities = ({
|
||||||
|
targetableObject,
|
||||||
|
}: {
|
||||||
|
targetableObject: ActivityTargetableObject;
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
activityTargets,
|
||||||
|
loadingActivityTargets,
|
||||||
|
initialized: initializedActivityTargets,
|
||||||
|
} = useActivityTargets({
|
||||||
|
targetableObject,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [initialized, setInitialized] = useState(false);
|
||||||
|
|
||||||
|
const [activities, setActivities] = useState<Activity[]>([]);
|
||||||
|
|
||||||
|
const activityIds = activityTargets
|
||||||
|
?.map((activityTarget) => activityTarget.activityId)
|
||||||
|
.filter(isNonEmptyString);
|
||||||
|
|
||||||
|
const timelineActivitiesQueryVariables = makeTimelineActivitiesQueryVariables(
|
||||||
|
{
|
||||||
|
activityIds,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const { records: activitiesFromRequest, loading: loadingActivities } =
|
||||||
|
useFindManyRecords<Activity>({
|
||||||
|
skip: loadingActivityTargets || !isNonEmptyArray(activityTargets),
|
||||||
|
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||||
|
filter: timelineActivitiesQueryVariables.filter,
|
||||||
|
orderBy: timelineActivitiesQueryVariables.orderBy,
|
||||||
|
onCompleted: () => {
|
||||||
|
if (!initialized) {
|
||||||
|
setInitialized(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!loadingActivities) {
|
||||||
|
setActivities(activitiesFromRequest);
|
||||||
|
}
|
||||||
|
}, [activitiesFromRequest, loadingActivities]);
|
||||||
|
|
||||||
|
const noActivityTargets =
|
||||||
|
initializedActivityTargets && !isNonEmptyArray(activityTargets);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (noActivityTargets) {
|
||||||
|
setInitialized(true);
|
||||||
|
}
|
||||||
|
}, [noActivityTargets]);
|
||||||
|
|
||||||
|
const loading = loadingActivities || loadingActivityTargets;
|
||||||
|
|
||||||
|
return {
|
||||||
|
activities,
|
||||||
|
loading,
|
||||||
|
initialized,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
|
||||||
|
|
||||||
|
export const makeTimelineActivitiesQueryVariables = ({
|
||||||
|
activityIds,
|
||||||
|
}: {
|
||||||
|
activityIds: string[];
|
||||||
|
}): ObjectRecordQueryVariables => {
|
||||||
|
return {
|
||||||
|
filter: {
|
||||||
|
id: {
|
||||||
|
in: activityIds,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: 'AscNullsFirst',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,5 +1,8 @@
|
|||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
|
||||||
export type ActivityTargetableObject = {
|
export type ActivityTargetableObject = {
|
||||||
id: string;
|
id: string;
|
||||||
targetObjectNameSingular: string;
|
targetObjectNameSingular: string;
|
||||||
|
targetObjectRecord: ObjectRecord;
|
||||||
relatedTargetableObjects?: ActivityTargetableObject[];
|
relatedTargetableObjects?: ActivityTargetableObject[];
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
|
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||||
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
|
import { flattenTargetableObjectsAndTheirRelatedTargetableObjects } from '@/activities/utils/flattenTargetableObjectsAndTheirRelatedTargetableObjects';
|
||||||
|
import { getActivityTargetObjectFieldIdName } from '@/activities/utils/getTargetObjectFilterFieldName';
|
||||||
|
|
||||||
|
export const getActivityTargetsToCreateFromTargetableObjects = ({
|
||||||
|
targetableObjects,
|
||||||
|
activityId,
|
||||||
|
}: {
|
||||||
|
targetableObjects: ActivityTargetableObject[];
|
||||||
|
activityId: string;
|
||||||
|
}): Partial<ActivityTarget>[] => {
|
||||||
|
const activityTargetableObjects = targetableObjects
|
||||||
|
? flattenTargetableObjectsAndTheirRelatedTargetableObjects(
|
||||||
|
targetableObjects,
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const activityTargetsToCreate = activityTargetableObjects.map(
|
||||||
|
(targetableObject) => {
|
||||||
|
const targetableObjectFieldIdName = getActivityTargetObjectFieldIdName({
|
||||||
|
nameSingular: targetableObject.targetObjectNameSingular,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
[targetableObject.targetObjectNameSingular]:
|
||||||
|
targetableObject.targetObjectRecord,
|
||||||
|
[targetableObjectFieldIdName]: targetableObject.id,
|
||||||
|
activityId,
|
||||||
|
id: v4(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return activityTargetsToCreate;
|
||||||
|
};
|
||||||
@ -1,3 +1,5 @@
|
|||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
|
||||||
export type CachedObjectRecord = ObjectRecord & { __typename: string };
|
export type CachedObjectRecord<T extends ObjectRecord = ObjectRecord> = T & {
|
||||||
|
__typename: string;
|
||||||
|
};
|
||||||
|
|||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { loadDevMessages, loadErrorMessages } from '@apollo/client/dev';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
|
||||||
|
|
||||||
|
export const ApolloDevLogEffect = () => {
|
||||||
|
const isDebugMode = useRecoilValue(isDebugModeState);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isDebugMode) {
|
||||||
|
loadDevMessages();
|
||||||
|
loadErrorMessages();
|
||||||
|
}
|
||||||
|
}, [isDebugMode]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
@ -10,6 +10,8 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat
|
|||||||
import { getBasePathToShowPage } from '@/object-metadata/utils/getBasePathToShowPage';
|
import { getBasePathToShowPage } from '@/object-metadata/utils/getBasePathToShowPage';
|
||||||
import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem';
|
import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem';
|
||||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||||
|
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||||
|
import { useModifyRecordFromCache } from '@/object-record/cache/hooks/useModifyRecordFromCache';
|
||||||
import { useGenerateCreateManyRecordMutation } from '@/object-record/hooks/useGenerateCreateManyRecordMutation';
|
import { useGenerateCreateManyRecordMutation } from '@/object-record/hooks/useGenerateCreateManyRecordMutation';
|
||||||
import { useGenerateCreateOneRecordMutation } from '@/object-record/hooks/useGenerateCreateOneRecordMutation';
|
import { useGenerateCreateOneRecordMutation } from '@/object-record/hooks/useGenerateCreateOneRecordMutation';
|
||||||
import { useGenerateDeleteManyRecordMutation } from '@/object-record/hooks/useGenerateDeleteManyRecordMutation';
|
import { useGenerateDeleteManyRecordMutation } from '@/object-record/hooks/useGenerateDeleteManyRecordMutation';
|
||||||
@ -17,8 +19,6 @@ import { useGenerateExecuteQuickActionOnOneRecordMutation } from '@/object-recor
|
|||||||
import { useGenerateFindManyRecordsQuery } from '@/object-record/hooks/useGenerateFindManyRecordsQuery';
|
import { useGenerateFindManyRecordsQuery } from '@/object-record/hooks/useGenerateFindManyRecordsQuery';
|
||||||
import { useGenerateFindOneRecordQuery } from '@/object-record/hooks/useGenerateFindOneRecordQuery';
|
import { useGenerateFindOneRecordQuery } from '@/object-record/hooks/useGenerateFindOneRecordQuery';
|
||||||
import { useGenerateUpdateOneRecordMutation } from '@/object-record/hooks/useGenerateUpdateOneRecordMutation';
|
import { useGenerateUpdateOneRecordMutation } from '@/object-record/hooks/useGenerateUpdateOneRecordMutation';
|
||||||
import { useGetRecordFromCache } from '@/object-record/hooks/useGetRecordFromCache';
|
|
||||||
import { useModifyRecordFromCache } from '@/object-record/hooks/useModifyRecordFromCache';
|
|
||||||
import { generateDeleteOneRecordMutation } from '@/object-record/utils/generateDeleteOneRecordMutation';
|
import { generateDeleteOneRecordMutation } from '@/object-record/utils/generateDeleteOneRecordMutation';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
|
|||||||
72
packages/twenty-front/src/modules/object-record/cache/hooks/useAddRecordInCache.ts
vendored
Normal file
72
packages/twenty-front/src/modules/object-record/cache/hooks/useAddRecordInCache.ts
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
import gql from 'graphql-tag';
|
||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { useMapFieldMetadataToGraphQLQuery } from '@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery';
|
||||||
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
import { useGenerateFindOneRecordQuery } from '@/object-record/hooks/useGenerateFindOneRecordQuery';
|
||||||
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
|
export const useAddRecordInCache = ({
|
||||||
|
objectMetadataItem,
|
||||||
|
}: {
|
||||||
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
|
}) => {
|
||||||
|
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||||
|
const apolloClient = useApolloClient();
|
||||||
|
|
||||||
|
const generateFindOneRecordQuery = useGenerateFindOneRecordQuery();
|
||||||
|
|
||||||
|
const findOneRecordQuery = generateFindOneRecordQuery({
|
||||||
|
objectMetadataItem,
|
||||||
|
});
|
||||||
|
|
||||||
|
return useRecoilCallback(
|
||||||
|
({ set }) =>
|
||||||
|
(record: ObjectRecord) => {
|
||||||
|
apolloClient.writeFragment({
|
||||||
|
id: `${capitalize(objectMetadataItem.nameSingular)}:${record.id}`,
|
||||||
|
fragment: gql`
|
||||||
|
fragment Create${capitalize(
|
||||||
|
objectMetadataItem.nameSingular,
|
||||||
|
)}InCache on ${capitalize(objectMetadataItem.nameSingular)} {
|
||||||
|
__typename
|
||||||
|
id
|
||||||
|
${objectMetadataItem.fields
|
||||||
|
.map((field) => mapFieldMetadataToGraphQLQuery(field))
|
||||||
|
.join('\n')}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
data: {
|
||||||
|
__typename: `${capitalize(objectMetadataItem.nameSingular)}`,
|
||||||
|
...record,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Turn into injectIntoFindOneRecordQueryCache
|
||||||
|
apolloClient.writeQuery({
|
||||||
|
query: findOneRecordQuery,
|
||||||
|
variables: {
|
||||||
|
objectRecordId: record.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
[objectMetadataItem.nameSingular]: {
|
||||||
|
__typename: `${capitalize(objectMetadataItem.nameSingular)}`,
|
||||||
|
...record,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: remove this once we get rid of entityFieldsFamilyState
|
||||||
|
set(recordStoreFamilyState(record.id), record);
|
||||||
|
},
|
||||||
|
[
|
||||||
|
objectMetadataItem,
|
||||||
|
apolloClient,
|
||||||
|
mapFieldMetadataToGraphQLQuery,
|
||||||
|
findOneRecordQuery,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
};
|
||||||
31
packages/twenty-front/src/modules/object-record/cache/utils/getCacheReferenceFromRecord.ts
vendored
Normal file
31
packages/twenty-front/src/modules/object-record/cache/utils/getCacheReferenceFromRecord.ts
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { ApolloClient, makeReference, Reference } from '@apollo/client';
|
||||||
|
|
||||||
|
import { getCachedRecordFromRecord } from '@/object-record/cache/utils/getCachedRecordFromRecord';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
|
||||||
|
export const getCacheReferenceFromRecord = <T extends ObjectRecord>({
|
||||||
|
apolloClient,
|
||||||
|
objectNameSingular,
|
||||||
|
record,
|
||||||
|
}: {
|
||||||
|
apolloClient: ApolloClient<object>;
|
||||||
|
objectNameSingular: string;
|
||||||
|
record: T;
|
||||||
|
}): Reference => {
|
||||||
|
const cachedRecord = getCachedRecordFromRecord({
|
||||||
|
objectNameSingular,
|
||||||
|
record,
|
||||||
|
});
|
||||||
|
|
||||||
|
const id = apolloClient.cache.identify(cachedRecord);
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not identify record "${objectNameSingular}", id : "${record.id}"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const recordReference = makeReference(id);
|
||||||
|
|
||||||
|
return recordReference;
|
||||||
|
};
|
||||||
43
packages/twenty-front/src/modules/object-record/cache/utils/getCachedRecordEdgesFromRecords.ts
vendored
Normal file
43
packages/twenty-front/src/modules/object-record/cache/utils/getCachedRecordEdgesFromRecords.ts
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { ApolloClient, makeReference } from '@apollo/client';
|
||||||
|
|
||||||
|
import { CachedObjectRecordEdge } from '@/apollo/types/CachedObjectRecordEdge';
|
||||||
|
import { getCachedRecordFromRecord } from '@/object-record/cache/utils/getCachedRecordFromRecord';
|
||||||
|
import { getEdgeTypename } from '@/object-record/cache/utils/getEdgeTypename';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
|
||||||
|
export const getCachedRecordEdgesFromRecords = <T extends ObjectRecord>({
|
||||||
|
apolloClient,
|
||||||
|
objectNameSingular,
|
||||||
|
records,
|
||||||
|
}: {
|
||||||
|
apolloClient: ApolloClient<object>;
|
||||||
|
objectNameSingular: string;
|
||||||
|
records: T[];
|
||||||
|
}): CachedObjectRecordEdge[] => {
|
||||||
|
const cachedRecordEdges = records.map((record) => {
|
||||||
|
const cachedRecord = getCachedRecordFromRecord({
|
||||||
|
objectNameSingular,
|
||||||
|
record,
|
||||||
|
});
|
||||||
|
|
||||||
|
const id = apolloClient.cache.identify(cachedRecord);
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not identify record "${objectNameSingular}", id : "${record.id}"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const reference = makeReference(id);
|
||||||
|
|
||||||
|
const cachedObjectRecordEdge: CachedObjectRecordEdge = {
|
||||||
|
cursor: '',
|
||||||
|
node: reference,
|
||||||
|
__typename: getEdgeTypename({ objectNameSingular }),
|
||||||
|
};
|
||||||
|
|
||||||
|
return cachedObjectRecordEdge;
|
||||||
|
});
|
||||||
|
|
||||||
|
return cachedRecordEdges;
|
||||||
|
};
|
||||||
16
packages/twenty-front/src/modules/object-record/cache/utils/getCachedRecordFromRecord.ts
vendored
Normal file
16
packages/twenty-front/src/modules/object-record/cache/utils/getCachedRecordFromRecord.ts
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { CachedObjectRecord } from '@/apollo/types/CachedObjectRecord';
|
||||||
|
import { getNodeTypename } from '@/object-record/cache/utils/getNodeTypename';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
|
||||||
|
export const getCachedRecordFromRecord = <T extends ObjectRecord>({
|
||||||
|
objectNameSingular,
|
||||||
|
record,
|
||||||
|
}: {
|
||||||
|
objectNameSingular: string;
|
||||||
|
record: T;
|
||||||
|
}): CachedObjectRecord<T> => {
|
||||||
|
return {
|
||||||
|
__typename: getNodeTypename({ objectNameSingular }),
|
||||||
|
...record,
|
||||||
|
};
|
||||||
|
};
|
||||||
9
packages/twenty-front/src/modules/object-record/cache/utils/getConnectionTypename.ts
vendored
Normal file
9
packages/twenty-front/src/modules/object-record/cache/utils/getConnectionTypename.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
|
export const getConnectionTypename = ({
|
||||||
|
objectNameSingular,
|
||||||
|
}: {
|
||||||
|
objectNameSingular: string;
|
||||||
|
}) => {
|
||||||
|
return `${capitalize(objectNameSingular)}Connection`;
|
||||||
|
};
|
||||||
9
packages/twenty-front/src/modules/object-record/cache/utils/getEdgeTypename.ts
vendored
Normal file
9
packages/twenty-front/src/modules/object-record/cache/utils/getEdgeTypename.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
|
export const getEdgeTypename = ({
|
||||||
|
objectNameSingular,
|
||||||
|
}: {
|
||||||
|
objectNameSingular: string;
|
||||||
|
}) => {
|
||||||
|
return `${capitalize(objectNameSingular)}Edge`;
|
||||||
|
};
|
||||||
8
packages/twenty-front/src/modules/object-record/cache/utils/getEmptyPageInfo.ts
vendored
Normal file
8
packages/twenty-front/src/modules/object-record/cache/utils/getEmptyPageInfo.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export const getEmptyPageInfo = () => {
|
||||||
|
return {
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: null,
|
||||||
|
endCursor: null,
|
||||||
|
};
|
||||||
|
};
|
||||||
9
packages/twenty-front/src/modules/object-record/cache/utils/getNodeTypename.ts
vendored
Normal file
9
packages/twenty-front/src/modules/object-record/cache/utils/getNodeTypename.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
|
export const getNodeTypename = ({
|
||||||
|
objectNameSingular,
|
||||||
|
}: {
|
||||||
|
objectNameSingular: string;
|
||||||
|
}) => {
|
||||||
|
return capitalize(objectNameSingular);
|
||||||
|
};
|
||||||
19
packages/twenty-front/src/modules/object-record/cache/utils/getRecordConnectionFromEdges.ts
vendored
Normal file
19
packages/twenty-front/src/modules/object-record/cache/utils/getRecordConnectionFromEdges.ts
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { getConnectionTypename } from '@/object-record/cache/utils/getConnectionTypename';
|
||||||
|
import { getEmptyPageInfo } from '@/object-record/cache/utils/getEmptyPageInfo';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
||||||
|
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
|
||||||
|
|
||||||
|
export const getRecordConnectionFromEdges = <T extends ObjectRecord>({
|
||||||
|
objectNameSingular,
|
||||||
|
edges,
|
||||||
|
}: {
|
||||||
|
objectNameSingular: string;
|
||||||
|
edges: ObjectRecordEdge<T>[];
|
||||||
|
}) => {
|
||||||
|
return {
|
||||||
|
__typename: getConnectionTypename({ objectNameSingular }),
|
||||||
|
edges: edges,
|
||||||
|
pageInfo: getEmptyPageInfo(),
|
||||||
|
} as ObjectRecordConnection<T>;
|
||||||
|
};
|
||||||
24
packages/twenty-front/src/modules/object-record/cache/utils/getRecordConnectionFromRecords.ts
vendored
Normal file
24
packages/twenty-front/src/modules/object-record/cache/utils/getRecordConnectionFromRecords.ts
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { getConnectionTypename } from '@/object-record/cache/utils/getConnectionTypename';
|
||||||
|
import { getEmptyPageInfo } from '@/object-record/cache/utils/getEmptyPageInfo';
|
||||||
|
import { getRecordEdgeFromRecord } from '@/object-record/cache/utils/getRecordEdgeFromRecord';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
||||||
|
|
||||||
|
export const getRecordConnectionFromRecords = <T extends ObjectRecord>({
|
||||||
|
objectNameSingular,
|
||||||
|
records,
|
||||||
|
}: {
|
||||||
|
objectNameSingular: string;
|
||||||
|
records: T[];
|
||||||
|
}) => {
|
||||||
|
return {
|
||||||
|
__typename: getConnectionTypename({ objectNameSingular }),
|
||||||
|
edges: records.map((record) => {
|
||||||
|
return getRecordEdgeFromRecord({
|
||||||
|
objectNameSingular,
|
||||||
|
record,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
pageInfo: getEmptyPageInfo(),
|
||||||
|
} as ObjectRecordConnection<T>;
|
||||||
|
};
|
||||||
21
packages/twenty-front/src/modules/object-record/cache/utils/getRecordEdgeFromRecord.ts
vendored
Normal file
21
packages/twenty-front/src/modules/object-record/cache/utils/getRecordEdgeFromRecord.ts
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { getEdgeTypename } from '@/object-record/cache/utils/getEdgeTypename';
|
||||||
|
import { getNodeTypename } from '@/object-record/cache/utils/getNodeTypename';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
|
||||||
|
|
||||||
|
export const getRecordEdgeFromRecord = <T extends ObjectRecord>({
|
||||||
|
objectNameSingular,
|
||||||
|
record,
|
||||||
|
}: {
|
||||||
|
objectNameSingular: string;
|
||||||
|
record: T;
|
||||||
|
}) => {
|
||||||
|
return {
|
||||||
|
__typename: getEdgeTypename({ objectNameSingular }),
|
||||||
|
node: {
|
||||||
|
__typename: getNodeTypename({ objectNameSingular }),
|
||||||
|
...record,
|
||||||
|
},
|
||||||
|
cursor: '',
|
||||||
|
} as ObjectRecordEdge<T>;
|
||||||
|
};
|
||||||
10
packages/twenty-front/src/modules/object-record/cache/utils/getRecordsFromRecordConnection.ts
vendored
Normal file
10
packages/twenty-front/src/modules/object-record/cache/utils/getRecordsFromRecordConnection.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
||||||
|
|
||||||
|
export const getRecordsFromRecordConnection = <T extends ObjectRecord>({
|
||||||
|
recordConnection,
|
||||||
|
}: {
|
||||||
|
recordConnection: ObjectRecordConnection<T>;
|
||||||
|
}): T[] => {
|
||||||
|
return recordConnection.edges.map((edge) => edge.node);
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export const DEFAULT_SEARCH_REQUEST_LIMIT = 60;
|
||||||
@ -5,7 +5,7 @@ export const query = gql`
|
|||||||
$filter: PersonFilterInput
|
$filter: PersonFilterInput
|
||||||
$orderBy: PersonOrderByInput
|
$orderBy: PersonOrderByInput
|
||||||
$lastCursor: String
|
$lastCursor: String
|
||||||
$limit: Float = 30
|
$limit: Float
|
||||||
) {
|
) {
|
||||||
people(
|
people(
|
||||||
filter: $filter
|
filter: $filter
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { act, renderHook } from '@testing-library/react';
|
|||||||
import { RecoilRoot } from 'recoil';
|
import { RecoilRoot } from 'recoil';
|
||||||
|
|
||||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||||
import { useModifyRecordFromCache } from '@/object-record/hooks/useModifyRecordFromCache';
|
import { useModifyRecordFromCache } from '@/object-record/cache/hooks/useModifyRecordFromCache';
|
||||||
|
|
||||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||||
<MockedProvider addTypename={false}>
|
<MockedProvider addTypename={false}>
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { useApolloClient } from '@apollo/client';
|
|||||||
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||||
import { useGenerateCachedObjectRecord } from '@/object-record/hooks/useGenerateCachedObjectRecord';
|
import { useGenerateCachedObjectRecord } from '@/object-record/cache/hooks/useGenerateCachedObjectRecord';
|
||||||
import { getCreateManyRecordsMutationResponseField } from '@/object-record/hooks/useGenerateCreateManyRecordMutation';
|
import { getCreateManyRecordsMutationResponseField } from '@/object-record/hooks/useGenerateCreateManyRecordMutation';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
||||||
|
|||||||
@ -0,0 +1,48 @@
|
|||||||
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||||
|
import { useAddRecordInCache } from '@/object-record/cache/hooks/useAddRecordInCache';
|
||||||
|
import { useGenerateCachedObjectRecord } from '@/object-record/cache/hooks/useGenerateCachedObjectRecord';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
|
||||||
|
export const useCreateManyRecordsInCache = <T extends ObjectRecord>({
|
||||||
|
objectNameSingular,
|
||||||
|
}: ObjectMetadataItemIdentifier) => {
|
||||||
|
const { objectMetadataItem } = useObjectMetadataItem({
|
||||||
|
objectNameSingular,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { generateCachedObjectRecord } = useGenerateCachedObjectRecord({
|
||||||
|
objectMetadataItem,
|
||||||
|
});
|
||||||
|
|
||||||
|
const addRecordInCache = useAddRecordInCache({
|
||||||
|
objectMetadataItem,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createManyRecordsInCache = async (data: Partial<T>[]) => {
|
||||||
|
const recordsWithId = data.map((record) => ({
|
||||||
|
...record,
|
||||||
|
id: (record.id as string) ?? v4(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const createdRecordsInCache = [] as T[];
|
||||||
|
|
||||||
|
for (const record of recordsWithId) {
|
||||||
|
const generatedCachedObjectRecord = generateCachedObjectRecord<T>({
|
||||||
|
...record,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (generatedCachedObjectRecord) {
|
||||||
|
addRecordInCache(generatedCachedObjectRecord);
|
||||||
|
|
||||||
|
createdRecordsInCache.push(generatedCachedObjectRecord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return createdRecordsInCache;
|
||||||
|
};
|
||||||
|
|
||||||
|
return { createManyRecordsInCache };
|
||||||
|
};
|
||||||
@ -2,7 +2,7 @@ import { useApolloClient } from '@apollo/client';
|
|||||||
|
|
||||||
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { useGenerateCachedObjectRecord } from '@/object-record/hooks/useGenerateCachedObjectRecord';
|
import { useGenerateCachedObjectRecord } from '@/object-record/cache/hooks/useGenerateCachedObjectRecord';
|
||||||
import { getCreateOneRecordMutationResponseField } from '@/object-record/hooks/useGenerateCreateOneRecordMutation';
|
import { getCreateOneRecordMutationResponseField } from '@/object-record/hooks/useGenerateCreateOneRecordMutation';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import { useAddRecordInCache } from '@/object-record/cache/hooks/useAddRecordInCache';
|
||||||
|
import { useGenerateCachedObjectRecord } from '@/object-record/cache/hooks/useGenerateCachedObjectRecord';
|
||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
|
||||||
|
type useCreateOneRecordInCacheProps = {
|
||||||
|
objectNameSingular: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCreateOneRecordInCache = <T>({
|
||||||
|
objectNameSingular,
|
||||||
|
}: useCreateOneRecordInCacheProps) => {
|
||||||
|
const { objectMetadataItem } = useObjectMetadataItem({
|
||||||
|
objectNameSingular,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { generateCachedObjectRecord } = useGenerateCachedObjectRecord({
|
||||||
|
objectMetadataItem,
|
||||||
|
});
|
||||||
|
|
||||||
|
const addRecordInCache = useAddRecordInCache({
|
||||||
|
objectMetadataItem,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createOneRecordInCache = async (input: ObjectRecord) => {
|
||||||
|
const generatedCachedObjectRecord = generateCachedObjectRecord({
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
...input,
|
||||||
|
});
|
||||||
|
|
||||||
|
addRecordInCache(generatedCachedObjectRecord);
|
||||||
|
|
||||||
|
return generatedCachedObjectRecord as T;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
createOneRecordInCache,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -13,7 +13,6 @@ import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnec
|
|||||||
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
|
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
|
||||||
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
|
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
|
||||||
import { filterUniqueRecordEdgesByCursor } from '@/object-record/utils/filterUniqueRecordEdgesByCursor';
|
import { filterUniqueRecordEdgesByCursor } from '@/object-record/utils/filterUniqueRecordEdgesByCursor';
|
||||||
import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/search/hooks/useFilteredSearchEntityQuery';
|
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
import { logError } from '~/utils/logError';
|
import { logError } from '~/utils/logError';
|
||||||
import { capitalize } from '~/utils/string/capitalize';
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
@ -28,7 +27,7 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
|
|||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
filter,
|
filter,
|
||||||
orderBy,
|
orderBy,
|
||||||
limit = DEFAULT_SEARCH_REQUEST_LIMIT,
|
limit,
|
||||||
onCompleted,
|
onCompleted,
|
||||||
skip,
|
skip,
|
||||||
useRecordsWithoutConnection = false,
|
useRecordsWithoutConnection = false,
|
||||||
|
|||||||
@ -20,7 +20,7 @@ export const useGenerateFindManyRecordsQuery = () => {
|
|||||||
objectMetadataItem.nameSingular,
|
objectMetadataItem.nameSingular,
|
||||||
)}FilterInput, $orderBy: ${capitalize(
|
)}FilterInput, $orderBy: ${capitalize(
|
||||||
objectMetadataItem.nameSingular,
|
objectMetadataItem.nameSingular,
|
||||||
)}OrderByInput, $lastCursor: String, $limit: Float = 60) {
|
)}OrderByInput, $lastCursor: String, $limit: Float) {
|
||||||
${
|
${
|
||||||
objectMetadataItem.namePlural
|
objectMetadataItem.namePlural
|
||||||
}(filter: $filter, orderBy: $orderBy, first: $limit, after: $lastCursor){
|
}(filter: $filter, orderBy: $orderBy, first: $limit, after: $lastCursor){
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import {
|
|||||||
URLFilter,
|
URLFilter,
|
||||||
UUIDFilter,
|
UUIDFilter,
|
||||||
} from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
|
} from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
|
||||||
import { andFilterVariables } from '@/object-record/utils/andFilterVariables';
|
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
|
||||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||||
import { Field } from '~/generated/graphql';
|
import { Field } from '~/generated/graphql';
|
||||||
|
|
||||||
@ -260,5 +260,5 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return andFilterVariables(objectRecordFilters);
|
return makeAndFilterVariables(objectRecordFilters);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -238,16 +238,21 @@ export const RecordShowContainer = ({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</ShowPageLeftContainer>
|
</ShowPageLeftContainer>
|
||||||
<ShowPageRightContainer
|
{record ? (
|
||||||
targetableObject={{
|
<ShowPageRightContainer
|
||||||
id: record?.id ?? '',
|
targetableObject={{
|
||||||
targetObjectNameSingular: objectMetadataItem?.nameSingular,
|
id: record.id,
|
||||||
}}
|
targetObjectNameSingular: objectMetadataItem?.nameSingular,
|
||||||
timeline
|
targetObjectRecord: record,
|
||||||
tasks
|
}}
|
||||||
notes
|
timeline
|
||||||
emails
|
tasks
|
||||||
/>
|
notes
|
||||||
|
emails
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
</ShowPageContainer>
|
</ShowPageContainer>
|
||||||
</RecoilScope>
|
</RecoilScope>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/search/hooks/useFilteredSearchEntityQuery';
|
import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/object-record/constants/DefaultSearchRequestLimit';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
import { capitalize } from '~/utils/string/capitalize';
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { useMultiObjectSearchSelectedItemsQuery } from '@/object-record/relation
|
|||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { ObjectRecordIdentifier } from '@/object-record/types/ObjectRecordIdentifier';
|
import { ObjectRecordIdentifier } from '@/object-record/types/ObjectRecordIdentifier';
|
||||||
|
|
||||||
export const DEFAULT_SEARCH_REQUEST_LIMIT = 5;
|
export const MULTI_OBJECT_SEARCH_REQUEST_LIMIT = 5;
|
||||||
|
|
||||||
export type ObjectRecordForSelect = {
|
export type ObjectRecordForSelect = {
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import {
|
|||||||
import { SelectedObjectRecordId } from '@/object-record/relation-picker/hooks/useMultiObjectSearch';
|
import { SelectedObjectRecordId } from '@/object-record/relation-picker/hooks/useMultiObjectSearch';
|
||||||
import { useOrderByFieldPerMetadataItem } from '@/object-record/relation-picker/hooks/useOrderByFieldPerMetadataItem';
|
import { useOrderByFieldPerMetadataItem } from '@/object-record/relation-picker/hooks/useOrderByFieldPerMetadataItem';
|
||||||
import { useSearchFilterPerMetadataItem } from '@/object-record/relation-picker/hooks/useSearchFilterPerMetadataItem';
|
import { useSearchFilterPerMetadataItem } from '@/object-record/relation-picker/hooks/useSearchFilterPerMetadataItem';
|
||||||
import { andFilterVariables } from '@/object-record/utils/andFilterVariables';
|
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
import { capitalize } from '~/utils/string/capitalize';
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ export const useMultiObjectSearchMatchesSearchFilterAndToSelectQuery = ({
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
`filter${capitalize(nameSingular)}`,
|
`filter${capitalize(nameSingular)}`,
|
||||||
andFilterVariables(searchFilters),
|
makeAndFilterVariables(searchFilters),
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
.filter(isDefined),
|
.filter(isDefined),
|
||||||
|
|||||||
@ -2,13 +2,12 @@ import { isNonEmptyString } from '@sniptt/guards';
|
|||||||
|
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { OrderBy } from '@/object-metadata/types/OrderBy';
|
import { OrderBy } from '@/object-metadata/types/OrderBy';
|
||||||
|
import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/object-record/constants/DefaultSearchRequestLimit';
|
||||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||||
import { SelectableRecord } from '@/object-record/select/types/SelectableRecord';
|
import { SelectableRecord } from '@/object-record/select/types/SelectableRecord';
|
||||||
import { getObjectFilterFields } from '@/object-record/select/utils/getObjectFilterFields';
|
import { getObjectFilterFields } from '@/object-record/select/utils/getObjectFilterFields';
|
||||||
import { andFilterVariables } from '@/object-record/utils/andFilterVariables';
|
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
|
||||||
import { orFilterVariables } from '@/object-record/utils/orFilterVariables';
|
import { makeOrFilterVariables } from '@/object-record/utils/makeOrFilterVariables';
|
||||||
|
|
||||||
export const DEFAULT_SEARCH_REQUEST_LIMIT = 60;
|
|
||||||
|
|
||||||
export const useRecordsForSelect = ({
|
export const useRecordsForSelect = ({
|
||||||
searchFilterText,
|
searchFilterText,
|
||||||
@ -53,7 +52,7 @@ export const useRecordsForSelect = ({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return orFilterVariables(
|
return makeOrFilterVariables(
|
||||||
fieldNames.map((fieldName) => {
|
fieldNames.map((fieldName) => {
|
||||||
const [parentFieldName, subFieldName] = fieldName.split('.');
|
const [parentFieldName, subFieldName] = fieldName.split('.');
|
||||||
|
|
||||||
@ -81,7 +80,7 @@ export const useRecordsForSelect = ({
|
|||||||
loading: filteredSelectedRecordsLoading,
|
loading: filteredSelectedRecordsLoading,
|
||||||
records: filteredSelectedRecordsData,
|
records: filteredSelectedRecordsData,
|
||||||
} = useFindManyRecords({
|
} = useFindManyRecords({
|
||||||
filter: andFilterVariables([...searchFilters, selectedIdsFilter]),
|
filter: makeAndFilterVariables([...searchFilters, selectedIdsFilter]),
|
||||||
orderBy: orderByField,
|
orderBy: orderByField,
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
skip: !selectedIds.length,
|
skip: !selectedIds.length,
|
||||||
@ -93,7 +92,7 @@ export const useRecordsForSelect = ({
|
|||||||
: undefined;
|
: undefined;
|
||||||
const { loading: recordsToSelectLoading, records: recordsToSelectData } =
|
const { loading: recordsToSelectLoading, records: recordsToSelectData } =
|
||||||
useFindManyRecords({
|
useFindManyRecords({
|
||||||
filter: andFilterVariables([...searchFilters, notFilter]),
|
filter: makeAndFilterVariables([...searchFilters, notFilter]),
|
||||||
limit: limit ?? DEFAULT_SEARCH_REQUEST_LIMIT,
|
limit: limit ?? DEFAULT_SEARCH_REQUEST_LIMIT,
|
||||||
orderBy: orderByField,
|
orderBy: orderByField,
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
|
|
||||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
import { FieldMetadataType } from '~/generated/graphql';
|
import { FieldMetadataType } from '~/generated/graphql';
|
||||||
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
export const generateEmptyFieldValue = (
|
export const generateEmptyFieldValue = (
|
||||||
fieldMetadataItem: FieldMetadataItem,
|
fieldMetadataItem: FieldMetadataItem,
|
||||||
@ -39,7 +42,21 @@ export const generateEmptyFieldValue = (
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case FieldMetadataType.Relation: {
|
case FieldMetadataType.Relation: {
|
||||||
return null;
|
if (
|
||||||
|
!isNonEmptyString(
|
||||||
|
fieldMetadataItem.fromRelationMetadata?.toObjectMetadata
|
||||||
|
?.nameSingular,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
__typename: `${capitalize(
|
||||||
|
fieldMetadataItem.fromRelationMetadata.toObjectMetadata.nameSingular,
|
||||||
|
)}Connection`,
|
||||||
|
edges: [],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
case FieldMetadataType.Currency: {
|
case FieldMetadataType.Currency: {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
|
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
export const andFilterVariables = (
|
export const makeAndFilterVariables = (
|
||||||
filters: (ObjectRecordQueryFilter | undefined)[],
|
filters: (ObjectRecordQueryFilter | undefined)[],
|
||||||
): ObjectRecordQueryFilter | undefined => {
|
): ObjectRecordQueryFilter | undefined => {
|
||||||
const definedFilters = filters.filter(isDefined);
|
const definedFilters = filters.filter(isDefined);
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
|
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
export const orFilterVariables = (
|
export const makeOrFilterVariables = (
|
||||||
filters: (ObjectRecordQueryFilter | undefined)[],
|
filters: (ObjectRecordQueryFilter | undefined)[],
|
||||||
): ObjectRecordQueryFilter | undefined => {
|
): ObjectRecordQueryFilter | undefined => {
|
||||||
const definedFilters = filters.filter(isDefined);
|
const definedFilters = filters.filter(isDefined);
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
|
|
||||||
|
export const mapToRecordId = (objectRecord: ObjectRecord) => {
|
||||||
|
return objectRecord.id;
|
||||||
|
};
|
||||||
@ -5,7 +5,7 @@ export const query = gql`
|
|||||||
$filter: PersonFilterInput
|
$filter: PersonFilterInput
|
||||||
$orderBy: PersonOrderByInput
|
$orderBy: PersonOrderByInput
|
||||||
$lastCursor: String
|
$lastCursor: String
|
||||||
$limit: Float = 30
|
$limit: Float = 60
|
||||||
) {
|
) {
|
||||||
people(
|
people(
|
||||||
filter: $filter
|
filter: $filter
|
||||||
|
|||||||
@ -2,18 +2,17 @@ import { isNonEmptyString } from '@sniptt/guards';
|
|||||||
|
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { OrderBy } from '@/object-metadata/types/OrderBy';
|
import { OrderBy } from '@/object-metadata/types/OrderBy';
|
||||||
|
import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/object-record/constants/DefaultSearchRequestLimit';
|
||||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||||
import { EntitiesForMultipleEntitySelect } from '@/object-record/relation-picker/types/EntitiesForMultipleEntitySelect';
|
import { EntitiesForMultipleEntitySelect } from '@/object-record/relation-picker/types/EntitiesForMultipleEntitySelect';
|
||||||
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
|
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { andFilterVariables } from '@/object-record/utils/andFilterVariables';
|
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
|
||||||
import { orFilterVariables } from '@/object-record/utils/orFilterVariables';
|
import { makeOrFilterVariables } from '@/object-record/utils/makeOrFilterVariables';
|
||||||
import { assertNotNull } from '~/utils/assert';
|
import { assertNotNull } from '~/utils/assert';
|
||||||
|
|
||||||
type SearchFilter = { fieldNames: string[]; filter: string | number };
|
type SearchFilter = { fieldNames: string[]; filter: string | number };
|
||||||
|
|
||||||
export const DEFAULT_SEARCH_REQUEST_LIMIT = 60;
|
|
||||||
|
|
||||||
// TODO: use this for all search queries, because we need selectedEntities and entitiesToSelect each time we want to search
|
// TODO: use this for all search queries, because we need selectedEntities and entitiesToSelect each time we want to search
|
||||||
// Filtered entities to select are
|
// Filtered entities to select are
|
||||||
|
|
||||||
@ -56,7 +55,7 @@ export const useFilteredSearchEntityQuery = ({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return orFilterVariables(
|
return makeOrFilterVariables(
|
||||||
fieldNames.map((fieldName) => {
|
fieldNames.map((fieldName) => {
|
||||||
const [parentFieldName, subFieldName] = fieldName.split('.');
|
const [parentFieldName, subFieldName] = fieldName.split('.');
|
||||||
|
|
||||||
@ -85,7 +84,7 @@ export const useFilteredSearchEntityQuery = ({
|
|||||||
records: filteredSelectedRecords,
|
records: filteredSelectedRecords,
|
||||||
} = useFindManyRecords({
|
} = useFindManyRecords({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
filter: andFilterVariables([...searchFilters, selectedIdsFilter]),
|
filter: makeAndFilterVariables([...searchFilters, selectedIdsFilter]),
|
||||||
orderBy: { [orderByField]: sortOrder },
|
orderBy: { [orderByField]: sortOrder },
|
||||||
skip: !selectedIds.length,
|
skip: !selectedIds.length,
|
||||||
});
|
});
|
||||||
@ -97,7 +96,7 @@ export const useFilteredSearchEntityQuery = ({
|
|||||||
const { loading: recordsToSelectLoading, records: recordsToSelect } =
|
const { loading: recordsToSelectLoading, records: recordsToSelect } =
|
||||||
useFindManyRecords({
|
useFindManyRecords({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
filter: andFilterVariables([...searchFilters, notFilter]),
|
filter: makeAndFilterVariables([...searchFilters, notFilter]),
|
||||||
limit: limit ?? DEFAULT_SEARCH_REQUEST_LIMIT,
|
limit: limit ?? DEFAULT_SEARCH_REQUEST_LIMIT,
|
||||||
orderBy: { [orderByField]: sortOrder },
|
orderBy: { [orderByField]: sortOrder },
|
||||||
});
|
});
|
||||||
|
|||||||
@ -92,6 +92,7 @@ export const RecordShowPage = () => {
|
|||||||
entity={{
|
entity={{
|
||||||
id: record.id,
|
id: record.id,
|
||||||
targetObjectNameSingular: objectMetadataItem?.nameSingular,
|
targetObjectNameSingular: objectMetadataItem?.nameSingular,
|
||||||
|
targetObjectRecord: record,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ShowPageMoreButton
|
<ShowPageMoreButton
|
||||||
|
|||||||
Reference in New Issue
Block a user