b11f77df2a
## PR Description - Uses `expr-eval` to enable front components (SDK plugins) to define conditional availability as declarative expressions. - Moves shared types and constants to `twenty-shared` - Introduces a `conditionalAvailabilityExpression` field on `CommandMenuItemEntity`, allowing command menu items to store an `expr-eval` compatible expression string that is evaluated against a CommandMenuContext to determine if the item should be shown. - Creates an esbuild transform plugin `conditional-availability-transform-plugin` in `twenty-sdk` that converts TypeScript conditional availability expressions into `expr-eval` compatible syntax at build time, so SDK developers can write natural TS expressions that get transformed to evaluable strings. - Removes deprecated `forceRegisteredActionsByKey` state and its usage. - Creates `useCommandMenuContext` hook that builds the full `CommandMenuContext` object from React state, which is then passed to `useCommandMenuItemFrontComponentActions` for evaluating conditional availability expressions.
93 lines
2.8 KiB
TypeScript
93 lines
2.8 KiB
TypeScript
import { useCallback } from 'react';
|
|
import { useStore } from 'jotai';
|
|
|
|
import { useActivityTargetsForTargetableObjects } from '@/activities/hooks/useActivityTargetsForTargetableObjects';
|
|
import { type ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
|
import { type Note } from '@/activities/types/Note';
|
|
import { type NoteTarget } from '@/activities/types/NoteTarget';
|
|
import { type Task } from '@/activities/types/Task';
|
|
import { type TaskTarget } from '@/activities/types/TaskTarget';
|
|
import {
|
|
type CoreObjectNameSingular,
|
|
type RecordGqlOperationOrderBy,
|
|
} from 'twenty-shared/types';
|
|
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
|
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
|
import { isDefined } from 'twenty-shared/utils';
|
|
|
|
export const useActivities = <T extends Task | Note>({
|
|
objectNameSingular,
|
|
targetableObjects,
|
|
activityTargetsOrderByVariables,
|
|
skip,
|
|
limit,
|
|
}: {
|
|
objectNameSingular: CoreObjectNameSingular.Note | CoreObjectNameSingular.Task;
|
|
targetableObjects: ActivityTargetableObject[];
|
|
activityTargetsOrderByVariables: RecordGqlOperationOrderBy;
|
|
skip?: boolean;
|
|
limit: number;
|
|
}) => {
|
|
const store = useStore();
|
|
const updateActivitiesInStore = useCallback(
|
|
(activityTargets: (TaskTarget | NoteTarget)[]) => {
|
|
for (const activityTarget of activityTargets) {
|
|
const activity = activityTarget[objectNameSingular];
|
|
store.set(recordStoreFamilyState.atomFamily(activity.id), activity);
|
|
}
|
|
},
|
|
[store, objectNameSingular],
|
|
);
|
|
|
|
const {
|
|
activityTargets,
|
|
loadingActivityTargets,
|
|
totalCountActivityTargets,
|
|
fetchMoreActivityTargets,
|
|
hasNextPage,
|
|
} = useActivityTargetsForTargetableObjects({
|
|
objectNameSingular,
|
|
targetableObjects,
|
|
skip: skip,
|
|
activityTargetsOrderByVariables,
|
|
onCompleted: updateActivitiesInStore,
|
|
limit,
|
|
});
|
|
|
|
const activities = activityTargets
|
|
.map((activityTarget) => {
|
|
return activityTarget[objectNameSingular];
|
|
})
|
|
.filter(isDefined) as T[];
|
|
|
|
const fetchMoreActivities = async () => {
|
|
const result = await fetchMoreActivityTargets();
|
|
|
|
if (!isDefined(result?.data)) {
|
|
return [];
|
|
}
|
|
|
|
const activityTargets = getRecordsFromRecordConnection<
|
|
TaskTarget | NoteTarget
|
|
>({
|
|
recordConnection: result.data,
|
|
});
|
|
|
|
updateActivitiesInStore(activityTargets);
|
|
|
|
return activityTargets
|
|
.map((activityTarget) => {
|
|
return activityTarget[objectNameSingular];
|
|
})
|
|
.filter(isDefined) as T[];
|
|
};
|
|
|
|
return {
|
|
activities: activities as T[],
|
|
loading: loadingActivityTargets,
|
|
totalCountActivities: totalCountActivityTargets,
|
|
fetchMoreActivities,
|
|
hasNextPage,
|
|
};
|
|
};
|