Files
twenty/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerOptionDropdown.tsx
T
Charles Bochet d81f006979 Rename Jotai state hooks and utilities to consistent Atom-based naming (#18209)
## Summary

Rename all Jotai-based state management hooks and utilities to a
consistent naming convention that:
1. Removes legacy Recoil naming (`useRecoilXxxV2`, `createXxxV2`)
2. Always includes `Atom` in the name to distinguish from jotai native
hooks
3. Follows a consistent ordering: `Atom` → `Component` → `Family` →
`Type` (`State`/`Selector`)
4. Includes the type qualifier (`State` or `Selector`) in all
value-reading hooks to avoid naming conflicts with jotai's native
`useAtomValue`

## Naming Convention

**Hooks**:
`use[Set]Atom[Component][Family][State|Selector][Value|State|CallbackState]`
**Utils**: `createAtom[Component][Family][State|Selector]`

## Changes

### Hooks renamed (definition files + all usages across ~1,500 files):

| Old Name (Recoil) | Intermediate (Atom) | Final Name |
|---|---|---|
| `useRecoilValueV2` | `useAtomValue` | **`useAtomStateValue`** |
| `useRecoilStateV2` | `useAtomState` | `useAtomState` |
| `useSetRecoilStateV2` | `useSetAtomState` | `useSetAtomState` |
| `useFamilyRecoilValueV2` | `useFamilyAtomValue` |
**`useAtomFamilyStateValue`** |
| `useSetFamilyRecoilStateV2` | `useSetFamilyAtomState` |
**`useSetAtomFamilyState`** |
| `useFamilySelectorValueV2` | `useFamilySelectorValue` |
**`useAtomFamilySelectorValue`** |
| `useFamilySelectorStateV2` | `useFamilySelectorState` |
**`useAtomFamilySelectorState`** |
| `useRecoilComponentValueV2` | `useAtomComponentValue` |
**`useAtomComponentStateValue`** |
| `useRecoilComponentStateV2` | `useAtomComponentState` |
`useAtomComponentState` |
| `useSetRecoilComponentStateV2` | `useSetAtomComponentState` |
`useSetAtomComponentState` |
| `useRecoilComponentFamilyValueV2` | `useAtomComponentFamilyValue` |
**`useAtomComponentFamilyStateValue`** |
| `useRecoilComponentFamilyStateV2` | `useAtomComponentFamilyState` |
`useAtomComponentFamilyState` |
| `useSetRecoilComponentFamilyStateV2` |
`useSetAtomComponentFamilyState` | `useSetAtomComponentFamilyState` |
| `useRecoilComponentSelectorValueV2` | `useAtomComponentSelectorValue`
| `useAtomComponentSelectorValue` |
| `useRecoilComponentFamilySelectorValueV2` |
`useAtomComponentFamilySelectorValue` |
`useAtomComponentFamilySelectorValue` |
| `useRecoilComponentStateCallbackStateV2` |
`useAtomComponentStateCallbackState` |
`useAtomComponentStateCallbackState` |
| `useRecoilComponentSelectorCallbackStateV2` |
`useAtomComponentSelectorCallbackState` |
`useAtomComponentSelectorCallbackState` |
| `useRecoilComponentFamilyStateCallbackStateV2` |
`useAtomComponentFamilyStateCallbackState` |
`useAtomComponentFamilyStateCallbackState` |
| `useRecoilComponentFamilySelectorCallbackStateV2` |
`useAtomComponentFamilySelectorCallbackState` |
`useAtomComponentFamilySelectorCallbackState` |

### Utilities renamed:

| Old Name (Recoil) | Final Name |
|---|---|
| `createStateV2` | **`createAtomState`** |
| `createFamilyStateV2` | **`createAtomFamilyState`** |
| `createSelectorV2` | **`createAtomSelector`** |
| `createFamilySelectorV2` | **`createAtomFamilySelector`** |
| `createWritableSelectorV2` | **`createAtomWritableSelector`** |
| `createWritableFamilySelectorV2` |
**`createAtomWritableFamilySelector`** |
| `createComponentStateV2` | **`createAtomComponentState`** |
| `createComponentFamilyStateV2` | **`createAtomComponentFamilyState`**
|
| `createComponentSelectorV2` | **`createAtomComponentSelector`** |
| `createComponentFamilySelectorV2` |
**`createAtomComponentFamilySelector`** |

## Details

- All definition files renamed to match new convention
- All import paths and usages updated across the entire `twenty-front`
package
- Jotai's native `useAtomValue` is aliased as `useJotaiAtomValue` in the
wrapper hook to avoid collision
- Legacy Recoil files in `state/utils/` and `component-state/utils/`
left untouched (separate naming scope)
- Typecheck passes cleanly
2026-02-25 01:55:46 +01:00

167 lines
5.6 KiB
TypeScript

import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite';
import { useFavorites } from '@/favorites/hooks/useFavorites';
import { usePrefetchedNavigationMenuItemsData } from '@/navigation-menu-item/hooks/usePrefetchedNavigationMenuItemsData';
import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFlag';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { MenuItemWithOptionDropdown } from '@/ui/navigation/menu-item/components/MenuItemWithOptionDropdown';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { type View } from '@/views/types/View';
import { useDestroyViewFromCurrentState } from '@/views/view-picker/hooks/useDestroyViewFromCurrentState';
import { useViewPickerMode } from '@/views/view-picker/hooks/useViewPickerMode';
import { viewPickerReferenceViewIdComponentState } from '@/views/view-picker/states/viewPickerReferenceViewIdComponentState';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useLingui } from '@lingui/react/macro';
import {
IconHeart,
IconLock,
IconPencil,
IconTrash,
useIcons,
} from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
import {
FeatureFlagKey,
PermissionFlagType,
ViewVisibility,
} from '~/generated-metadata/graphql';
type ViewPickerOptionDropdownProps = {
isIndexView: boolean;
view: Pick<
View,
| 'id'
| 'name'
| 'icon'
| '__typename'
| 'visibility'
| 'createdByUserWorkspaceId'
>;
onEdit: (event: React.MouseEvent<HTMLElement>, viewId: string) => void;
handleViewSelect: (viewId: string) => void;
};
export const ViewPickerOptionDropdown = ({
isIndexView,
onEdit,
view,
handleViewSelect,
}: ViewPickerOptionDropdownProps) => {
const dropdownId = `view-picker-options-${view.id}`;
const { t } = useLingui();
const { closeDropdown } = useCloseDropdown();
const { getIcon } = useIcons();
const { destroyViewFromCurrentState } = useDestroyViewFromCurrentState();
const setViewPickerReferenceViewId = useSetAtomComponentState(
viewPickerReferenceViewIdComponentState,
);
const { setViewPickerMode } = useViewPickerMode();
const hasViewsPermission = useHasPermissionFlag(PermissionFlagType.VIEWS);
const { sortedFavorites: favorites } = useFavorites();
const { createFavorite } = useCreateFavorite();
const isNavigationMenuItemEditingEnabled = useIsFeatureEnabled(
FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED,
);
const { navigationMenuItems, currentWorkspaceMemberId } =
usePrefetchedNavigationMenuItemsData();
// Users with VIEWS permission can edit all views
// Users without VIEWS permission can only edit unlisted views (which are always their own, filtered by backend)
const canEditView =
hasViewsPermission || view.visibility === ViewVisibility.UNLISTED;
const isFavorite = isNavigationMenuItemEditingEnabled
? navigationMenuItems.some(
(item) =>
item.viewId === view.id &&
item.userWorkspaceId === currentWorkspaceMemberId,
)
: favorites.some(
(favorite) =>
favorite.recordId === view.id && favorite.forWorkspaceMemberId,
);
const handleDelete = () => {
setViewPickerReferenceViewId(view.id);
destroyViewFromCurrentState();
closeDropdown(dropdownId);
};
const handleAddToFavorites = () => {
if (!isFavorite) {
createFavorite(view, 'view');
} else {
setViewPickerReferenceViewId(view.id);
setViewPickerMode('favorite-folders-picker');
}
closeDropdown(dropdownId);
};
const getVisibilityIcon = () => {
if (isIndexView) {
return IconLock;
}
return null;
};
const shouldShowIconAlways = isIndexView;
return (
<>
<MenuItemWithOptionDropdown
text={view.name}
LeftIcon={getIcon(view.icon)}
onClick={() => handleViewSelect(view.id)}
isIconDisplayedOnHoverOnly={!shouldShowIconAlways}
RightIcon={getVisibilityIcon()}
dropdownPlacement="bottom-start"
dropdownId={`view-picker-options-${view.id}`}
dropdownContent={
<DropdownContent>
<DropdownMenuItemsContainer>
{isIndexView ? (
<MenuItem
LeftIcon={IconHeart}
text={isFavorite ? t`Manage favorite` : t`Add to Favorite`}
onClick={handleAddToFavorites}
/>
) : (
<>
<MenuItem
LeftIcon={IconHeart}
text={isFavorite ? t`Manage favorite` : t`Add to Favorite`}
onClick={handleAddToFavorites}
/>
{canEditView && (
<>
<MenuItem
LeftIcon={IconPencil}
text={t`Edit`}
onClick={(event) => {
onEdit(event, view.id);
closeDropdown(dropdownId);
}}
/>
<MenuItem
LeftIcon={IconTrash}
text={t`Delete`}
onClick={handleDelete}
accent="danger"
/>
</>
)}
</>
)}
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
</>
);
};