mirror of
https://github.com/twentyhq/twenty
synced 2026-04-21 13:37:22 +00:00
Fully deprecate old recoil (#18210)
## Summary Removes the `recoil` dependency entirely from `package.json` and `twenty-front/package.json`, completing the migration to Jotai as the sole state management library. Removes all Recoil infrastructure: `RecoilRoot` wrapper from `App.tsx` and test decorators, `RecoilDebugObserver`, Recoil-specific ESLint rules (`use-getLoadable-and-getValue-to-get-atoms`, `useRecoilCallback-has-dependency-array`), and legacy Recoil utility hooks/types (`useRecoilComponentState`, `useRecoilComponentValue`, `createComponentState`, `createFamilyState`, `getSnapshotValue`, `cookieStorageEffect`, `localStorageEffect`, etc.). Renames all `V2`-suffixed Jotai state files and types to their canonical names (e.g., `ComponentStateV2` -> `ComponentState`, `agentChatInputStateV2` -> `agentChatInputState`, `SelectorCallbacksV2` -> `SelectorCallbacks`), and removes the now-redundant V1 counterparts. Updates ~433 files across the codebase to use the renamed Jotai imports, remove Recoil imports, and clean up test wrappers (`RecoilRootDecorator` -> `JotaiRootDecorator`).
This commit is contained in:
parent
ecb7270a7b
commit
121788c42f
439 changed files with 1714 additions and 3929 deletions
|
|
@ -22,7 +22,7 @@ This directory contains Twenty's development guidelines and best practices in th
|
|||
|
||||
### React Development
|
||||
- **react-general-guidelines.mdc** - Core React development principles (Auto-attached to React files)
|
||||
- **react-state-management.mdc** - State management approaches with Recoil (Auto-attached to state files)
|
||||
- **react-state-management.mdc** - State management approaches with Jotai (Auto-attached to state files)
|
||||
|
||||
### Testing & Quality
|
||||
- **testing-guidelines.mdc** - Testing strategies and best practices (Auto-attached to test files)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ alwaysApply: true
|
|||
# Twenty Architecture
|
||||
|
||||
## Tech Stack
|
||||
- **Frontend**: React 18, TypeScript, Recoil, Styled Components, Vite
|
||||
- **Frontend**: React 18, TypeScript, Jotai, Styled Components, Vite
|
||||
- **Backend**: NestJS, TypeORM, PostgreSQL, Redis, GraphQL
|
||||
- **Monorepo**: Nx workspace with yarn
|
||||
|
||||
|
|
|
|||
|
|
@ -4,16 +4,20 @@ alwaysApply: false
|
|||
---
|
||||
# React State Management
|
||||
|
||||
## Recoil Patterns
|
||||
## Jotai Patterns
|
||||
```typescript
|
||||
// ✅ Atoms for primitive state
|
||||
export const currentUserState = atom<User | null>({
|
||||
// ✅ Atoms for primitive state (use createAtomState for keyed state with optional persistence)
|
||||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
|
||||
export const currentUserState = createAtomState<User | null>({
|
||||
key: 'currentUserState',
|
||||
default: null,
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
// ✅ Selectors for derived state
|
||||
export const userDisplayNameSelector = selector({
|
||||
// ✅ Derived atoms for computed state (use createAtomSelector)
|
||||
import { createAtomSelector } from '@/ui/utilities/state/jotai/utils/createAtomSelector';
|
||||
|
||||
export const userDisplayNameSelector = createAtomSelector({
|
||||
key: 'userDisplayNameSelector',
|
||||
get: ({ get }) => {
|
||||
const user = get(currentUserState);
|
||||
|
|
@ -21,13 +25,30 @@ export const userDisplayNameSelector = selector({
|
|||
},
|
||||
});
|
||||
|
||||
// ✅ Atom families for dynamic atoms
|
||||
export const userByIdState = atomFamily<User | null, string>({
|
||||
// ✅ Atom factory pattern for dynamic atoms (use createAtomFamilyState)
|
||||
import { createAtomFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomFamilyState';
|
||||
|
||||
export const userByIdState = createAtomFamilyState<User | null, string>({
|
||||
key: 'userByIdState',
|
||||
default: null,
|
||||
defaultValue: null,
|
||||
});
|
||||
```
|
||||
|
||||
## Jotai Hooks
|
||||
```typescript
|
||||
// useAtomState - read and write (like useRecoilState)
|
||||
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
||||
|
||||
// useAtomStateValue - read only (like useRecoilValue)
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
|
||||
// useSetAtomState - write only (like useSetRecoilState)
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
```
|
||||
|
||||
## Provider
|
||||
Jotai works without a Provider by default (unlike Recoil's RecoilRoot). For scoped stores or testing, use `Provider` from `jotai`.
|
||||
|
||||
## Local State Guidelines
|
||||
```typescript
|
||||
// ✅ Multiple useState for unrelated state
|
||||
|
|
@ -74,7 +95,7 @@ const increment = useCallback(() => {
|
|||
```
|
||||
|
||||
## Performance Tips
|
||||
- Use atom families for dynamic data collections
|
||||
- Implement proper selector caching
|
||||
- Avoid heavy computations in selectors
|
||||
- Use atom factory pattern (createAtomFamilyState) for dynamic data collections
|
||||
- Derived atoms (createAtomSelector) are automatically memoized by Jotai
|
||||
- Avoid heavy computations in derived atoms
|
||||
- Batch state updates when possible
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ npx nx run twenty-front:graphql:generate --configuration=metadata
|
|||
## Architecture Overview
|
||||
|
||||
### Tech Stack
|
||||
- **Frontend**: React 18, TypeScript, Recoil (state management), Emotion (styling), Vite
|
||||
- **Frontend**: React 18, TypeScript, Jotai (state management), Emotion (styling), Vite
|
||||
- **Backend**: NestJS, TypeORM, PostgreSQL, Redis, GraphQL (with GraphQL Yoga)
|
||||
- **Monorepo**: Nx workspace managed with Yarn 4
|
||||
|
||||
|
|
@ -138,7 +138,7 @@ packages/
|
|||
- Multi-line comments use multiple `//` lines, not `/** */`
|
||||
|
||||
### State Management
|
||||
- **Recoil** for global state: atoms for primitive state, selectors for derived state, atom families for dynamic collections
|
||||
- **Jotai** for global state: atoms for primitive state, selectors for derived state, atom families for dynamic collections
|
||||
- Component-specific state with React hooks (`useState`, `useReducer` for complex logic)
|
||||
- GraphQL cache managed by Apollo Client
|
||||
- Use functional state updates: `setState(prev => prev + 1)`
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@
|
|||
"react-responsive": "^9.0.2",
|
||||
"react-router-dom": "^6.4.4",
|
||||
"react-tooltip": "^5.13.1",
|
||||
"recoil": "^0.7.7",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"rxjs": "^7.2.0",
|
||||
"semver": "^7.5.4",
|
||||
|
|
|
|||
|
|
@ -125,12 +125,7 @@ export default [
|
|||
|
||||
// React hooks rules
|
||||
'react-hooks/rules-of-hooks': 'error',
|
||||
'react-hooks/exhaustive-deps': [
|
||||
'warn',
|
||||
{
|
||||
additionalHooks: 'useRecoilCallback',
|
||||
},
|
||||
],
|
||||
'react-hooks/exhaustive-deps': 'warn',
|
||||
// Lingui - detect untranslated strings
|
||||
'lingui/no-unlocalized-strings': [
|
||||
'error',
|
||||
|
|
@ -246,7 +241,7 @@ export default [
|
|||
'componentInstanceId',
|
||||
'hotkeyScope',
|
||||
'dropdownId',
|
||||
'recoilScopeId',
|
||||
|
||||
'modalId',
|
||||
'dialogId',
|
||||
'itemId',
|
||||
|
|
@ -411,15 +406,6 @@ export default [
|
|||
// Navigation
|
||||
'useNavigationSection',
|
||||
|
||||
// Recoil
|
||||
'atom',
|
||||
'atomFamily',
|
||||
'selector',
|
||||
'selectorFamily',
|
||||
'useSetRecoilState',
|
||||
'useRecoilState',
|
||||
'useRecoilValue',
|
||||
|
||||
// GraphQL operations
|
||||
'gql',
|
||||
'useQuery',
|
||||
|
|
@ -578,8 +564,7 @@ export default [
|
|||
'twenty/no-state-useref': 'error',
|
||||
'twenty/component-props-naming': 'error',
|
||||
'twenty/explicit-boolean-predicates-in-if': 'error',
|
||||
'twenty/use-getLoadable-and-getValue-to-get-atoms': 'error',
|
||||
'twenty/useRecoilCallback-has-dependency-array': 'error',
|
||||
|
||||
'twenty/no-navigate-prefer-link': 'error',
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -58,14 +58,8 @@ import {
|
|||
rule as styledComponentsPrefixedWithStyled,
|
||||
RULE_NAME as styledComponentsPrefixedWithStyledName,
|
||||
} from './rules/styled-components-prefixed-with-styled';
|
||||
import {
|
||||
rule as useGetLoadableAndGetValueToGetAtoms,
|
||||
RULE_NAME as useGetLoadableAndGetValueToGetAtomsName,
|
||||
} from './rules/use-getLoadable-and-getValue-to-get-atoms';
|
||||
import {
|
||||
rule as useRecoilCallbackHasDependencyArray,
|
||||
RULE_NAME as useRecoilCallbackHasDependencyArrayName,
|
||||
} from './rules/useRecoilCallback-has-dependency-array';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Import your custom workspace rules at the top of this file.
|
||||
|
|
@ -102,11 +96,7 @@ module.exports = {
|
|||
[styledComponentsPrefixedWithStyledName]:
|
||||
styledComponentsPrefixedWithStyled,
|
||||
[explicitBooleanPredicatesInIfName]: explicitBooleanPredicatesInIf,
|
||||
[useGetLoadableAndGetValueToGetAtomsName]:
|
||||
useGetLoadableAndGetValueToGetAtoms,
|
||||
[maxConstsPerFileName]: maxConstsPerFile,
|
||||
[useRecoilCallbackHasDependencyArrayName]:
|
||||
useRecoilCallbackHasDependencyArray,
|
||||
[noNavigatePreferLinkName]: noNavigatePreferLink,
|
||||
[injectWorkspaceRepositoryName]: injectWorkspaceRepository,
|
||||
[restApiMethodsShouldBeGuardedName]: restApiMethodsShouldBeGuarded,
|
||||
|
|
|
|||
|
|
@ -1,54 +1,38 @@
|
|||
import { TSESLint } from '@typescript-eslint/utils';
|
||||
|
||||
import { rule, RULE_NAME } from './matching-state-variable';
|
||||
|
||||
const ruleTester = new TSESLint.RuleTester({
|
||||
parser: require.resolve('@typescript-eslint/parser'),
|
||||
});
|
||||
const ruleTester = new TSESLint.RuleTester();
|
||||
|
||||
ruleTester.run(RULE_NAME, rule, {
|
||||
valid: [
|
||||
{
|
||||
code: 'const variable = useRecoilValue(variableState);',
|
||||
code: 'const variable = useAtomStateValue(variableState);',
|
||||
},
|
||||
{
|
||||
code: 'const variable = useRecoilScopedValue(variableScopedState);',
|
||||
code: 'const [variable, setVariable] = useAtomState(variableState);',
|
||||
},
|
||||
// Component/Family hooks are not checked by this rule
|
||||
{
|
||||
code: 'const anything = useAtomComponentStateValue(variableComponentState);',
|
||||
},
|
||||
{
|
||||
code: 'const [variable, setVariable] = useRecoilState(variableScopedState);',
|
||||
code: 'const anything = useAtomFamilyStateValue(variableFamilyState);',
|
||||
},
|
||||
{
|
||||
code: 'const [variable, setVariable] = useRecoilScopedState(variableScopedState);',
|
||||
},
|
||||
{
|
||||
code: 'const [variable, setVariable] = useRecoilFamilyState(variableScopedState);',
|
||||
},
|
||||
{
|
||||
code: 'const [variable, setVariable] = useRecoilScopedFamilyState(variableScopedState);',
|
||||
code: 'const [anything, setAnything] = useAtomComponentFamilyState(variableComponentFamilyState);',
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: 'const myValue = useRecoilValue(variableState);',
|
||||
code: 'const myValue = useAtomStateValue(variableState);',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidVariableName',
|
||||
},
|
||||
],
|
||||
output: 'const variable = useRecoilValue(variableState);',
|
||||
},
|
||||
{
|
||||
code: 'const myValue = useRecoilScopedValue(variableState);',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidVariableName',
|
||||
},
|
||||
],
|
||||
output: 'const variable = useRecoilScopedValue(variableState);',
|
||||
},
|
||||
|
||||
{
|
||||
code: 'const [myValue, setMyValue] = useRecoilState(variableState);',
|
||||
code: 'const [myValue, setMyValue] = useAtomState(variableState);',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidVariableName',
|
||||
|
|
@ -57,122 +41,22 @@ ruleTester.run(RULE_NAME, rule, {
|
|||
messageId: 'invalidSetterName',
|
||||
},
|
||||
],
|
||||
output: 'const [variable, setVariable] = useRecoilState(variableState);',
|
||||
},
|
||||
{
|
||||
code: 'const [myValue] = useRecoilState(variableState);',
|
||||
code: 'const [myValue] = useAtomState(variableState);',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidVariableName',
|
||||
},
|
||||
],
|
||||
output: 'const [variable] = useRecoilState(variableState);',
|
||||
},
|
||||
{
|
||||
code: 'const [, setMyValue] = useRecoilState(variableState);',
|
||||
code: 'const [, setMyValue] = useAtomState(variableState);',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidSetterName',
|
||||
},
|
||||
],
|
||||
output: 'const [, setVariable] = useRecoilState(variableState);',
|
||||
},
|
||||
|
||||
{
|
||||
code: 'const [myValue, setMyValue] = useRecoilScopedState(variableState);',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidVariableName',
|
||||
},
|
||||
{
|
||||
messageId: 'invalidSetterName',
|
||||
},
|
||||
],
|
||||
output:
|
||||
'const [variable, setVariable] = useRecoilScopedState(variableState);',
|
||||
},
|
||||
{
|
||||
code: 'const [myValue] = useRecoilScopedState(variableState);',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidVariableName',
|
||||
},
|
||||
],
|
||||
output: 'const [variable] = useRecoilScopedState(variableState);',
|
||||
},
|
||||
{
|
||||
code: 'const [, setMyValue] = useRecoilScopedState(variableState);',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidSetterName',
|
||||
},
|
||||
],
|
||||
output: 'const [, setVariable] = useRecoilScopedState(variableState);',
|
||||
},
|
||||
|
||||
{
|
||||
code: 'const [myValue, setMyValue] = useRecoilFamilyState(variableState);',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidVariableName',
|
||||
},
|
||||
{
|
||||
messageId: 'invalidSetterName',
|
||||
},
|
||||
],
|
||||
output:
|
||||
'const [variable, setVariable] = useRecoilFamilyState(variableState);',
|
||||
},
|
||||
{
|
||||
code: 'const [myValue] = useRecoilFamilyState(variableState);',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidVariableName',
|
||||
},
|
||||
],
|
||||
output: 'const [variable] = useRecoilFamilyState(variableState);',
|
||||
},
|
||||
{
|
||||
code: 'const [, setMyValue] = useRecoilFamilyState(variableState);',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidSetterName',
|
||||
},
|
||||
],
|
||||
output: 'const [, setVariable] = useRecoilFamilyState(variableState);',
|
||||
},
|
||||
|
||||
{
|
||||
code: 'const [myValue, setMyValue] = useRecoilScopedFamilyState(variableState);',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidVariableName',
|
||||
},
|
||||
{
|
||||
messageId: 'invalidSetterName',
|
||||
},
|
||||
],
|
||||
output:
|
||||
'const [variable, setVariable] = useRecoilScopedFamilyState(variableState);',
|
||||
},
|
||||
{
|
||||
code: 'const [myValue] = useRecoilScopedFamilyState(variableState);',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidVariableName',
|
||||
},
|
||||
],
|
||||
output: 'const [variable] = useRecoilScopedFamilyState(variableState);',
|
||||
},
|
||||
{
|
||||
code: 'const [, setMyValue] = useRecoilScopedFamilyState(variableState);',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidSetterName',
|
||||
},
|
||||
],
|
||||
output:
|
||||
'const [, setVariable] = useRecoilScopedFamilyState(variableState);',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@ export const rule = ESLintUtils.RuleCreator(() => __filename)({
|
|||
type: 'problem',
|
||||
docs: {
|
||||
description:
|
||||
'Ensure recoil value and setter are named after their atom name',
|
||||
recommended: 'recommended',
|
||||
'Ensure state value and setter are named after their atom name',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [],
|
||||
|
|
@ -33,14 +32,9 @@ export const rule = ESLintUtils.RuleCreator(() => __filename)({
|
|||
if (
|
||||
node?.init?.type === AST_NODE_TYPES.CallExpression &&
|
||||
isIdentifier(node.init.callee) &&
|
||||
[
|
||||
'useRecoilState',
|
||||
'useRecoilScopedState',
|
||||
'useRecoilFamilyState',
|
||||
'useRecoilScopedFamilyState',
|
||||
'useRecoilValue',
|
||||
'useRecoilScopedValue',
|
||||
].includes(node.init.callee.name)
|
||||
['useAtomState', 'useAtomStateValue'].includes(
|
||||
node.init.callee.name,
|
||||
)
|
||||
) {
|
||||
const stateNameBase = isIdentifier(node.init.arguments[0])
|
||||
? node.init.arguments[0].name
|
||||
|
|
@ -51,7 +45,7 @@ export const rule = ESLintUtils.RuleCreator(() => __filename)({
|
|||
}
|
||||
|
||||
const expectedVariableNameBase = stateNameBase.replace(
|
||||
/(State|FamilyState|Selector|ScopedState|ScopedFamilyState|ScopedSelector)$/,
|
||||
/(State|Selector|ScopedState|ScopedFamilyState|ScopedSelector)$/,
|
||||
'',
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ const ALLOWED_TAGS = [
|
|||
'AccordionGroup',
|
||||
'Router',
|
||||
'BrowserRouter',
|
||||
'RecoilRoot',
|
||||
];
|
||||
|
||||
export const rule: Rule.RuleModule = {
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
import { TSESLint } from '@typescript-eslint/utils';
|
||||
|
||||
import { rule, RULE_NAME } from './use-getLoadable-and-getValue-to-get-atoms';
|
||||
|
||||
const ruleTester = new TSESLint.RuleTester({
|
||||
parser: require.resolve('@typescript-eslint/parser'),
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
ruleTester.run(RULE_NAME, rule, {
|
||||
valid: [
|
||||
{
|
||||
code: 'const atoms = snapshot.getLoadable(someState).getValue();',
|
||||
},
|
||||
{
|
||||
code: 'const atoms = snapshot.getLoadable(someState(viewId)).getValue();',
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: 'const atoms = await snapshot.getPromise(someState);',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidAccessorOnSnapshot',
|
||||
},
|
||||
],
|
||||
output: 'const atoms = await snapshot.getLoadable(someState);',
|
||||
},
|
||||
{
|
||||
code: 'const atoms = await snapshot.getPromise(someState(viewId));',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidAccessorOnSnapshot',
|
||||
},
|
||||
],
|
||||
output: 'const atoms = await snapshot.getLoadable(someState(viewId));',
|
||||
},
|
||||
{
|
||||
code: 'const atoms = snapshot.getLoadable(someState).anotherMethod();',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'invalidWayToGetAtoms',
|
||||
},
|
||||
],
|
||||
output: 'const atoms = snapshot.getLoadable(someState).getValue();',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
import { ESLintUtils } from '@typescript-eslint/utils';
|
||||
|
||||
// NOTE: The rule will be available in ESLint configs as "@nx/workspace-usage-getLoadable-and-getValue-to-get-atoms"
|
||||
export const RULE_NAME = 'use-getLoadable-and-getValue-to-get-atoms';
|
||||
|
||||
export const rule = ESLintUtils.RuleCreator(() => __filename)({
|
||||
name: RULE_NAME,
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'Ensure you are using getLoadable and getValue',
|
||||
recommended: 'recommended',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [],
|
||||
messages: {
|
||||
redundantAwait: 'Redundant await on non-promise',
|
||||
invalidAccessorOnSnapshot:
|
||||
"Expected to use method 'getLoadable()' on 'snapshot' but instead found '{{ propertyName }}'",
|
||||
invalidWayToGetAtoms:
|
||||
"Expected to use method 'getValue()' with 'getLoadable()' but instead found '{{ propertyName }}'",
|
||||
},
|
||||
},
|
||||
defaultOptions: [],
|
||||
create: (context) => ({
|
||||
AwaitExpression: (node) => {
|
||||
const { argument, range }: any = node;
|
||||
if (
|
||||
(argument.callee?.object?.callee?.object?.name === 'snapshot' &&
|
||||
argument?.callee?.object?.callee?.property?.name === 'getLoadable') ||
|
||||
(argument.callee?.object?.name === 'snapshot' &&
|
||||
argument?.callee?.property?.name === 'getLoadable')
|
||||
) {
|
||||
// remove await
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'redundantAwait',
|
||||
data: {
|
||||
propertyName: argument.callee.property.name,
|
||||
},
|
||||
fix: (fixer) => fixer.removeRange([range[0], range[0] + 5]),
|
||||
});
|
||||
}
|
||||
},
|
||||
MemberExpression: (node) => {
|
||||
const { object, property }: any = node;
|
||||
|
||||
if (
|
||||
object.callee?.type === 'MemberExpression' &&
|
||||
object.callee.object?.name === 'snapshot' &&
|
||||
object.callee.property?.name === 'getLoadable'
|
||||
) {
|
||||
const propertyName = property.name;
|
||||
|
||||
if (propertyName !== 'getValue') {
|
||||
context.report({
|
||||
node: property,
|
||||
messageId: 'invalidWayToGetAtoms',
|
||||
data: {
|
||||
propertyName,
|
||||
},
|
||||
// replace the property with `getValue`
|
||||
fix: (fixer) => fixer.replaceText(property, 'getValue'),
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
CallExpression: (node) => {
|
||||
const { callee }: any = node;
|
||||
|
||||
if (
|
||||
callee.type === 'MemberExpression' &&
|
||||
callee.object?.name === 'snapshot' &&
|
||||
callee.property?.name === 'getPromise'
|
||||
) {
|
||||
context.report({
|
||||
node: callee.property,
|
||||
messageId: 'invalidAccessorOnSnapshot',
|
||||
data: {
|
||||
propertyName: callee.property.name,
|
||||
},
|
||||
// Replace `getPromise` with `getLoadable`
|
||||
fix: (fixer) => fixer.replaceText(callee.property, 'getLoadable'),
|
||||
});
|
||||
}
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
import { TSESLint } from '@typescript-eslint/utils';
|
||||
|
||||
import { rule, RULE_NAME } from './useRecoilCallback-has-dependency-array';
|
||||
|
||||
const ruleTester = new TSESLint.RuleTester({
|
||||
parser: require.resolve('@typescript-eslint/parser'),
|
||||
});
|
||||
|
||||
ruleTester.run(RULE_NAME, rule, {
|
||||
valid: [
|
||||
{
|
||||
code: 'const someValue = useRecoilCallback(() => () => {}, []);',
|
||||
},
|
||||
{
|
||||
code: 'const someValue = useRecoilCallback(() => () => {}, [dependency]);',
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: 'const someValue = useRecoilCallback(({}) => () => {});',
|
||||
errors: [
|
||||
{
|
||||
messageId: 'isNecessaryDependencyArray',
|
||||
},
|
||||
],
|
||||
output: 'const someValue = useRecoilCallback(({}) => () => {}, []);',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
import { ESLintUtils } from '@typescript-eslint/utils';
|
||||
|
||||
// NOTE: The rule will be available in ESLint configs as "@nx/workspace-useRecoilCallback-has-dependency-array"
|
||||
export const RULE_NAME = 'useRecoilCallback-has-dependency-array';
|
||||
|
||||
export const rule = ESLintUtils.RuleCreator(() => __filename)({
|
||||
name: RULE_NAME,
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'Ensure `useRecoilCallback` is used with a dependency array',
|
||||
recommended: 'recommended',
|
||||
},
|
||||
schema: [],
|
||||
messages: {
|
||||
isNecessaryDependencyArray:
|
||||
'Is necessary dependency array with useRecoilCallback',
|
||||
},
|
||||
fixable: 'code',
|
||||
},
|
||||
defaultOptions: [],
|
||||
create: (context) => {
|
||||
return {
|
||||
CallExpression: (node) => {
|
||||
const { callee } = node;
|
||||
if (
|
||||
callee.type === 'Identifier' &&
|
||||
callee.name === 'useRecoilCallback'
|
||||
) {
|
||||
const depsArg = node.arguments;
|
||||
if (depsArg.length === 1) {
|
||||
context.report({
|
||||
node: callee,
|
||||
messageId: 'isNecessaryDependencyArray',
|
||||
data: {
|
||||
callee,
|
||||
deps: depsArg[0],
|
||||
},
|
||||
fix: (fixer) => fixer.insertTextAfter(depsArg[0], ', []'),
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
@ -110,7 +110,6 @@
|
|||
"react-responsive": "^9.0.2",
|
||||
"react-router-dom": "^6.4.4",
|
||||
"react-textarea-autosize": "^8.4.1",
|
||||
"recoil": "^0.7.7",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"transliteration": "^2.3.5",
|
||||
"twenty-sdk": "workspace:*",
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ const setupMockUseParams = (objectNamePlural?: string) => {
|
|||
};
|
||||
|
||||
jest.mock('@/ui/utilities/state/jotai/hooks/useAtomStateValue');
|
||||
const setupMockRecoil = (
|
||||
const setupMockState = (
|
||||
objectNamePlural?: string,
|
||||
verifyEmailRedirectPath?: string,
|
||||
calendarBookingPageId?: string | null,
|
||||
|
|
@ -340,7 +340,7 @@ describe('usePageChangeEffectNavigateLocation', () => {
|
|||
setupMockIsWorkspaceActivationStatusEqualsTo(isWorkspaceSuspended);
|
||||
setupMockIsLogged(isLoggedIn);
|
||||
setupMockUseParams(objectNamePluralFromParams);
|
||||
setupMockRecoil(objectNamePluralFromMetadata, verifyEmailRedirectPath);
|
||||
setupMockState(objectNamePluralFromMetadata, verifyEmailRedirectPath);
|
||||
|
||||
expect(usePageChangeEffectNavigateLocation()).toEqual(res);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/s
|
|||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useActiveWorkflowVersionsWithManualTrigger } from '@/workflow/hooks/useActiveWorkflowVersionsWithManualTrigger';
|
||||
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
||||
|
|
@ -31,16 +30,13 @@ export const useRunWorkflowRecordActions = ({
|
|||
const store = useStore();
|
||||
const { getIcon } = useIcons();
|
||||
const { enqueueWarningSnackBar } = useSnackBar();
|
||||
const { recordIndexId } = useRecordIndexIdFromCurrentContextStore();
|
||||
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const isPageInEditMode = useAtomComponentStateValue(
|
||||
contextStoreIsPageInEditModeComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const selectedRecordIds =
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { useRelatedRecordActions } from '@/action-menu/actions/record-agnostic-actions/hooks/useRelatedRecordActions';
|
||||
|
||||
jest.mock('@/object-metadata/hooks/useObjectMetadataItems', () => ({
|
||||
|
|
@ -24,10 +22,6 @@ jest.mock('@/object-metadata/hooks/useObjectMetadataItems', () => ({
|
|||
}),
|
||||
}));
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
);
|
||||
|
||||
describe('useRelatedRecordActions', () => {
|
||||
const mockGetIcon = jest.fn();
|
||||
|
||||
|
|
@ -42,14 +36,12 @@ describe('useRelatedRecordActions', () => {
|
|||
updatableFields: [],
|
||||
} as unknown as ObjectMetadataItem;
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useRelatedRecordActions({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
}),
|
||||
{ wrapper: Wrapper },
|
||||
const { result } = renderHook(() =>
|
||||
useRelatedRecordActions({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.current).toEqual({});
|
||||
|
|
@ -58,14 +50,12 @@ describe('useRelatedRecordActions', () => {
|
|||
it('should return empty object when objectMetadataItem is undefined', () => {
|
||||
const objectMetadataItem = undefined as unknown as ObjectMetadataItem;
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useRelatedRecordActions({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
}),
|
||||
{ wrapper: Wrapper },
|
||||
const { result } = renderHook(() =>
|
||||
useRelatedRecordActions({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.current).toEqual({});
|
||||
|
|
@ -104,14 +94,12 @@ describe('useRelatedRecordActions', () => {
|
|||
updatableFields: fields,
|
||||
} as unknown as ObjectMetadataItem;
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useRelatedRecordActions({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
}),
|
||||
{ wrapper: Wrapper },
|
||||
const { result } = renderHook(() =>
|
||||
useRelatedRecordActions({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(Object.keys(result.current)).toHaveLength(2);
|
||||
|
|
@ -156,14 +144,12 @@ describe('useRelatedRecordActions', () => {
|
|||
updatableFields: fields,
|
||||
} as unknown as ObjectMetadataItem;
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useRelatedRecordActions({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
}),
|
||||
{ wrapper: Wrapper },
|
||||
const { result } = renderHook(() =>
|
||||
useRelatedRecordActions({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(Object.keys(result.current)).toHaveLength(1);
|
||||
|
|
@ -204,14 +190,12 @@ describe('useRelatedRecordActions', () => {
|
|||
updatableFields: fields,
|
||||
} as unknown as ObjectMetadataItem;
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useRelatedRecordActions({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
}),
|
||||
{ wrapper: Wrapper },
|
||||
const { result } = renderHook(() =>
|
||||
useRelatedRecordActions({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.current['create-related-person'].position).toBe(18);
|
||||
|
|
@ -252,14 +236,12 @@ describe('useRelatedRecordActions', () => {
|
|||
updatableFields: fields,
|
||||
} as unknown as ObjectMetadataItem;
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useRelatedRecordActions({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
}),
|
||||
{ wrapper: Wrapper },
|
||||
const { result } = renderHook(() =>
|
||||
useRelatedRecordActions({
|
||||
sourceObjectMetadataItem: objectMetadataItem,
|
||||
getIcon: mockGetIcon,
|
||||
startPosition: 18,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(Object.keys(result.current)).toHaveLength(1);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { Action } from '@/action-menu/actions/components/Action';
|
|||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useActiveWorkflowVersionsWithManualTrigger } from '@/workflow/hooks/useActiveWorkflowVersionsWithManualTrigger';
|
||||
|
|
@ -17,7 +16,6 @@ export const useRunWorkflowRecordAgnosticActions = () => {
|
|||
|
||||
const isPageInEditMode = useAtomComponentStateValue(
|
||||
contextStoreIsPageInEditModeComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const { actionMenuType } = useContext(ActionMenuContext);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
import { CommandMenuActionMenuDropdown } from '@/action-menu/components/CommandMenuActionMenuDropdown';
|
||||
import { ActionMenuContextProvider } from '@/action-menu/contexts/ActionMenuContextProvider';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
|
||||
export const RecordShowRightDrawerActionMenu = () => {
|
||||
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { type ActionMenuContextType } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { ActionMenuContextProviderDefault } from '@/action-menu/contexts/ActionMenuContextProviderDefault';
|
||||
import { ActionMenuContextProviderWorkflowObjects } from '@/action-menu/contexts/ActionMenuContextProviderWorkflowObjects';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
|
|
@ -22,7 +21,6 @@ export const ActionMenuContextProvider = ({
|
|||
}) => {
|
||||
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const objectMetadataItems = useAtomStateValue(objectMetadataItemsState);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import {
|
|||
import { useRegisteredActions } from '@/action-menu/hooks/useRegisteredActions';
|
||||
import { useShouldActionBeRegisteredParams } from '@/action-menu/hooks/useShouldActionBeRegisteredParams';
|
||||
import { useCommandMenuItemFrontComponentActions } from '@/command-menu-item/hooks/useCommandMenuItemFrontComponentActions';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
|
|
@ -37,7 +36,6 @@ export const ActionMenuContextProviderDefault = ({
|
|||
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const isRecordSelection =
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import {
|
|||
import { useRegisteredActions } from '@/action-menu/hooks/useRegisteredActions';
|
||||
import { useShouldActionBeRegisteredParams } from '@/action-menu/hooks/useShouldActionBeRegisteredParams';
|
||||
import { useCommandMenuItemFrontComponentActions } from '@/command-menu-item/hooks/useCommandMenuItemFrontComponentActions';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
|
|
@ -125,7 +124,6 @@ export const ActionMenuContextProviderWorkflowObjects = ({
|
|||
}: ActionMenuContextProviderWorkflowObjectsProps) => {
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const recordId =
|
||||
|
|
|
|||
|
|
@ -4,11 +4,9 @@ import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
|||
import { type ShouldBeRegisteredFunctionParams } from '@/action-menu/actions/types/ShouldBeRegisteredFunctionParams';
|
||||
import { getActionConfig } from '@/action-menu/actions/utils/getActionConfig';
|
||||
import { getActionViewType } from '@/action-menu/actions/utils/getActionViewType';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
|
||||
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore';
|
||||
import { usePermissionFlagMap } from '@/settings/roles/hooks/usePermissionFlagMap';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
|
@ -24,19 +22,14 @@ export const useRegisteredActions = (
|
|||
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const contextStoreCurrentViewType = useAtomComponentStateValue(
|
||||
contextStoreCurrentViewTypeComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const { recordIndexId } = useRecordIndexIdFromCurrentContextStore();
|
||||
|
||||
const isFullTabWidgetInEditMode = useAtomComponentStateValue(
|
||||
contextStoreIsPageInEditModeComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const viewType = getActionViewType(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import { getActionViewType } from '@/action-menu/actions/utils/getActionViewType
|
|||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { objectPermissionsFamilySelector } from '@/auth/states/objectPermissionsFamilySelector';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
|
||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
|
|
@ -41,7 +40,6 @@ export const useShouldActionBeRegisteredParams = ({
|
|||
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const recordId =
|
||||
|
|
@ -97,19 +95,15 @@ export const useShouldActionBeRegisteredParams = ({
|
|||
);
|
||||
|
||||
const isShowPage =
|
||||
useAtomComponentStateValue(
|
||||
contextStoreCurrentViewTypeComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
) === ContextStoreViewType.ShowPage;
|
||||
useAtomComponentStateValue(contextStoreCurrentViewTypeComponentState) ===
|
||||
ContextStoreViewType.ShowPage;
|
||||
|
||||
const numberOfSelectedRecords = useAtomComponentStateValue(
|
||||
contextStoreNumberOfSelectedRecordsComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const contextStoreCurrentViewType = useAtomComponentStateValue(
|
||||
contextStoreCurrentViewTypeComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const viewType = getActionViewType(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import { renderHook } from '@testing-library/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useActivities } from '@/activities/hooks/useActivities';
|
||||
import { type Task } from '@/activities/types/Task';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
|
|
@ -40,10 +37,6 @@ const mockActivity = {
|
|||
taskTargets: [],
|
||||
} satisfies Task;
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
);
|
||||
|
||||
describe('useActivities', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
|
@ -60,21 +53,16 @@ describe('useActivities', () => {
|
|||
},
|
||||
);
|
||||
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const activities = useActivities({
|
||||
objectNameSingular: CoreObjectNameSingular.Task,
|
||||
targetableObjects: [
|
||||
{ targetObjectNameSingular: 'company', id: '123' },
|
||||
],
|
||||
skip: false,
|
||||
limit: 10,
|
||||
activityTargetsOrderByVariables: [{}],
|
||||
});
|
||||
return activities;
|
||||
},
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
const { result } = renderHook(() => {
|
||||
const activities = useActivities({
|
||||
objectNameSingular: CoreObjectNameSingular.Task,
|
||||
targetableObjects: [{ targetObjectNameSingular: 'company', id: '123' }],
|
||||
skip: false,
|
||||
limit: 10,
|
||||
activityTargetsOrderByVariables: [{}],
|
||||
});
|
||||
return activities;
|
||||
});
|
||||
|
||||
expect(result.current.activities).toEqual([mockActivity]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { MockedProvider } from '@apollo/client/testing';
|
|||
import { act, renderHook } from '@testing-library/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useActivityTargetObjectRecords } from '@/activities/hooks/useActivityTargetObjectRecords';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
|
|
@ -115,17 +114,15 @@ const task = {
|
|||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>
|
||||
<MockedProvider cache={cache}>
|
||||
<JestObjectMetadataItemSetter>
|
||||
<SnackBarComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'snack-bar-manager' }}
|
||||
>
|
||||
{children}
|
||||
</SnackBarComponentInstanceContext.Provider>
|
||||
</JestObjectMetadataItemSetter>
|
||||
</MockedProvider>
|
||||
</RecoilRoot>
|
||||
<MockedProvider cache={cache}>
|
||||
<JestObjectMetadataItemSetter>
|
||||
<SnackBarComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'snack-bar-manager' }}
|
||||
>
|
||||
{children}
|
||||
</SnackBarComponentInstanceContext.Provider>
|
||||
</JestObjectMetadataItemSetter>
|
||||
</MockedProvider>
|
||||
</JotaiProvider>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { usePrepareFindManyActivitiesQuery } from '@/activities/hooks/usePrepareFindManyActivitiesQuery';
|
||||
import { objectShowPageTargetableObjectStateV2 } from '@/activities/timeline-activities/states/objectShowPageTargetableObjectStateV2';
|
||||
import { objectShowPageTargetableObjectState } from '@/activities/timeline-activities/states/objectShowPageTargetableObjectState';
|
||||
import { type CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
|
@ -13,7 +13,7 @@ export const useRefreshShowPageFindManyActivitiesQueries = ({
|
|||
activityObjectNameSingular: CoreObjectNameSingular;
|
||||
}) => {
|
||||
const objectShowPageTargetableObject = useAtomStateValue(
|
||||
objectShowPageTargetableObjectStateV2,
|
||||
objectShowPageTargetableObjectState,
|
||||
);
|
||||
|
||||
const { prepareFindManyActivitiesQuery } = usePrepareFindManyActivitiesQuery({
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useCreateActivityInDB } from '@/activities/hooks/useCreateActivityInDB'
|
|||
import { useRefreshShowPageFindManyActivitiesQueries } from '@/activities/hooks/useRefreshShowPageFindManyActivitiesQueries';
|
||||
import { isActivityInCreateModeState } from '@/activities/states/isActivityInCreateModeState';
|
||||
import { isUpsertingActivityInDBState } from '@/activities/states/isCreatingActivityInDBState';
|
||||
import { objectShowPageTargetableObjectStateV2 } from '@/activities/timeline-activities/states/objectShowPageTargetableObjectStateV2';
|
||||
import { objectShowPageTargetableObjectState } from '@/activities/timeline-activities/states/objectShowPageTargetableObjectState';
|
||||
import { type Note } from '@/activities/types/Note';
|
||||
import { type Task } from '@/activities/types/Task';
|
||||
import { type CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
|
|
@ -31,7 +31,7 @@ export const useUpsertActivity = ({
|
|||
);
|
||||
|
||||
const objectShowPageTargetableObject = useAtomStateValue(
|
||||
objectShowPageTargetableObjectStateV2,
|
||||
objectShowPageTargetableObjectState,
|
||||
);
|
||||
|
||||
const { refreshShowPageFindManyActivitiesQueries } =
|
||||
|
|
|
|||
|
|
@ -10,20 +10,15 @@ jest.mock('@/activities/hooks/useActivities', () => ({
|
|||
})),
|
||||
}));
|
||||
|
||||
jest.mock('recoil', () => {
|
||||
const actualRecoil = jest.requireActual('recoil');
|
||||
return {
|
||||
...actualRecoil,
|
||||
useRecoilState: jest.fn(() => {
|
||||
const mockCurrentNotesQueryVariables = {
|
||||
filter: {},
|
||||
orderBy: 'mockOrderBy',
|
||||
};
|
||||
return [mockCurrentNotesQueryVariables, jest.fn()];
|
||||
}),
|
||||
atom: jest.fn(),
|
||||
};
|
||||
});
|
||||
jest.mock('@/ui/utilities/state/jotai/hooks/useAtomState', () => ({
|
||||
useAtomState: jest.fn(() => {
|
||||
const mockCurrentNotesQueryVariables = {
|
||||
filter: {},
|
||||
orderBy: 'mockOrderBy',
|
||||
};
|
||||
return [mockCurrentNotesQueryVariables, jest.fn()];
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('useNotes', () => {
|
||||
it('should return notes, and loading as expected', () => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect, useMemo } from 'react';
|
||||
|
||||
import { useActivities } from '@/activities/hooks/useActivities';
|
||||
import { currentNotesQueryVariablesStateV2 } from '@/activities/notes/states/currentNotesQueryVariablesStateV2';
|
||||
import { currentNotesQueryVariablesState } from '@/activities/notes/states/currentNotesQueryVariablesState';
|
||||
import { FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY } from '@/activities/timeline-activities/constants/FindManyTimelineActivitiesOrderBy';
|
||||
import { type Note } from '@/activities/types/Note';
|
||||
import { type RecordGqlOperationVariables } from 'twenty-shared/types';
|
||||
|
|
@ -34,7 +34,7 @@ export const useNotes = (targetableObject: ActivityTargetableObject) => {
|
|||
});
|
||||
|
||||
const [currentNotesQueryVariables, setCurrentNotesQueryVariables] =
|
||||
useAtomState(currentNotesQueryVariablesStateV2);
|
||||
useAtomState(currentNotesQueryVariablesState);
|
||||
|
||||
// TODO: fix useEffect, remove with better pattern
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { type RecordGqlOperationVariables } from 'twenty-shared/types';
|
|||
|
||||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
|
||||
export const currentNotesQueryVariablesStateV2 =
|
||||
export const currentNotesQueryVariablesState =
|
||||
createAtomState<RecordGqlOperationVariables | null>({
|
||||
key: 'currentNotesQueryVariablesStateV2',
|
||||
key: 'currentNotesQueryVariablesState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import { renderHook } from '@testing-library/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useActivities } from '@/activities/hooks/useActivities';
|
||||
import { useTasks } from '@/activities/tasks/hooks/useTasks';
|
||||
|
|
@ -45,13 +44,11 @@ jest.mock('@/activities/hooks/useActivities', () => ({
|
|||
(useActivities as jest.Mock).mockImplementation(useActivitiesMock);
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
<ObjectFilterDropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'entity-tasks-filter-instance' }}
|
||||
>
|
||||
{children}
|
||||
</ObjectFilterDropdownComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
<ObjectFilterDropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'entity-tasks-filter-instance' }}
|
||||
>
|
||||
{children}
|
||||
</ObjectFilterDropdownComponentInstanceContext.Provider>
|
||||
);
|
||||
|
||||
describe('useTasks', () => {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { type ActivityTargetableObject } from '@/activities/types/ActivityTarget
|
|||
|
||||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
|
||||
export const objectShowPageTargetableObjectStateV2 =
|
||||
export const objectShowPageTargetableObjectState =
|
||||
createAtomState<ActivityTargetableObject | null>({
|
||||
key: 'objectShowPageTargetableObjectStateV2',
|
||||
key: 'objectShowPageTargetableObjectState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { agentChatUsageStateV2 } from '@/ai/states/agentChatUsageStateV2';
|
||||
import { currentAIChatThreadStateV2 } from '@/ai/states/currentAIChatThreadStateV2';
|
||||
import { agentChatUsageState } from '@/ai/states/agentChatUsageState';
|
||||
import { currentAIChatThreadState } from '@/ai/states/currentAIChatThreadState';
|
||||
import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState';
|
||||
import { useOpenAskAIPageInCommandMenu } from '@/command-menu/hooks/useOpenAskAIPageInCommandMenu';
|
||||
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
||||
|
|
@ -79,11 +79,11 @@ export const AIChatThreadGroup = ({
|
|||
}) => {
|
||||
const { t } = useLingui();
|
||||
const theme = useTheme();
|
||||
const [, setCurrentAIChatThread] = useAtomState(currentAIChatThreadStateV2);
|
||||
const [, setCurrentAIChatThread] = useAtomState(currentAIChatThreadState);
|
||||
const setCurrentAIChatThreadTitle = useSetAtomState(
|
||||
currentAIChatThreadTitleState,
|
||||
);
|
||||
const setAgentChatUsage = useSetAtomState(agentChatUsageStateV2);
|
||||
const setAgentChatUsage = useSetAtomState(agentChatUsageState);
|
||||
const { openAskAIPage } = useOpenAskAIPageInCommandMenu();
|
||||
|
||||
const handleThreadClick = (thread: AgentChatThread) => {
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ jest.mock('~/hooks/useCopyToClipboard', () => ({
|
|||
}));
|
||||
|
||||
jest.mock(
|
||||
'@/ui/utilities/state/component-state/hooks/useRecoilComponentValue',
|
||||
'@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue',
|
||||
() => ({
|
||||
useRecoilComponentValue: () => 'output',
|
||||
useAtomComponentSelectorValue: () => 'output',
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ import { ProgressBar } from 'twenty-ui/feedback';
|
|||
import { ContextUsageProgressRing } from '@/ai/components/internal/ContextUsageProgressRing';
|
||||
import { SettingsBillingLabelValueItem } from '@/billing/components/internal/SettingsBillingLabelValueItem';
|
||||
import {
|
||||
agentChatUsageStateV2,
|
||||
agentChatUsageState,
|
||||
type AgentChatLastMessageUsage,
|
||||
} from '@/ai/states/agentChatUsageStateV2';
|
||||
} from '@/ai/states/agentChatUsageState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
|
|
@ -114,7 +114,7 @@ export const AIChatContextUsageButton = () => {
|
|||
const { t } = useLingui();
|
||||
const theme = useTheme();
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const agentChatUsage = useAtomStateValue(agentChatUsageStateV2);
|
||||
const agentChatUsage = useAtomStateValue(agentChatUsageState);
|
||||
|
||||
if (!agentChatUsage) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { agentChatSelectedFilesStateV2 } from '@/ai/states/agentChatSelectedFilesStateV2';
|
||||
import { agentChatUploadedFilesStateV2 } from '@/ai/states/agentChatUploadedFilesStateV2';
|
||||
import { agentChatSelectedFilesState } from '@/ai/states/agentChatSelectedFilesState';
|
||||
import { agentChatUploadedFilesState } from '@/ai/states/agentChatUploadedFilesState';
|
||||
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
||||
import styled from '@emotion/styled';
|
||||
import { AgentChatFilePreview } from './AgentChatFilePreview';
|
||||
|
|
@ -20,10 +20,10 @@ const StyledPreviewsContainer = styled.div`
|
|||
|
||||
export const AgentChatContextPreview = () => {
|
||||
const [agentChatSelectedFiles, setAgentChatSelectedFiles] = useAtomState(
|
||||
agentChatSelectedFilesStateV2,
|
||||
agentChatSelectedFilesState,
|
||||
);
|
||||
const [agentChatUploadedFiles, setAgentChatUploadedFiles] = useAtomState(
|
||||
agentChatUploadedFilesStateV2,
|
||||
agentChatUploadedFilesState,
|
||||
);
|
||||
|
||||
const handleRemoveUploadedFile = (fileIndex: number) => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useAIChatFileUpload } from '@/ai/hooks/useAIChatFileUpload';
|
||||
import { agentChatSelectedFilesStateV2 } from '@/ai/states/agentChatSelectedFilesStateV2';
|
||||
import { agentChatSelectedFilesState } from '@/ai/states/agentChatSelectedFilesState';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
|
@ -19,7 +19,7 @@ const StyledFileInput = styled.input`
|
|||
|
||||
export const AgentChatFileUploadButton = () => {
|
||||
const setAgentChatSelectedFiles = useSetAtomState(
|
||||
agentChatSelectedFilesStateV2,
|
||||
agentChatSelectedFilesState,
|
||||
);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const { uploadFiles } = useAIChatFileUpload();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useAgentChatContextOrThrow } from '@/ai/hooks/useAgentChatContextOrThrow';
|
||||
import { agentChatInputStateV2 } from '@/ai/states/agentChatInputStateV2';
|
||||
import { agentChatInputState } from '@/ai/states/agentChatInputState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { IconArrowUp, IconPlayerStop } from 'twenty-ui/display';
|
||||
import { RoundedIconButton } from 'twenty-ui/input';
|
||||
|
|
@ -9,7 +9,7 @@ type SendMessageButtonProps = {
|
|||
};
|
||||
|
||||
export const SendMessageButton = ({ onSend }: SendMessageButtonProps) => {
|
||||
const agentChatInput = useAtomStateValue(agentChatInputStateV2);
|
||||
const agentChatInput = useAtomStateValue(agentChatInputState);
|
||||
const { handleStop, isLoading, isStreaming } = useAgentChatContextOrThrow();
|
||||
|
||||
if (isStreaming) {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
DEFAULT_SUGGESTED_PROMPTS,
|
||||
type SuggestedPrompt,
|
||||
} from '@/ai/components/suggested-prompts/default-suggested-prompts';
|
||||
import { agentChatInputStateV2 } from '@/ai/states/agentChatInputStateV2';
|
||||
import { agentChatInputState } from '@/ai/states/agentChatInputState';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
|
|
@ -43,7 +43,7 @@ export const AIChatSuggestedPrompts = ({
|
|||
editor,
|
||||
}: AIChatSuggestedPromptsProps) => {
|
||||
const { t: resolveMessage } = useLingui();
|
||||
const setAgentChatInput = useSetAtomState(agentChatInputStateV2);
|
||||
const setAgentChatInput = useSetAtomState(agentChatInputState);
|
||||
|
||||
const handleClick = (prompt: SuggestedPrompt) => {
|
||||
const picked = pickRandom(prompt.prefillPrompts);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { useCallback, useMemo } from 'react';
|
|||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { AI_CHAT_INPUT_ID } from '@/ai/constants/AiChatInputId';
|
||||
import { agentChatInputStateV2 } from '@/ai/states/agentChatInputStateV2';
|
||||
import { agentChatInputState } from '@/ai/states/agentChatInputState';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import { MENTION_SUGGESTION_PLUGIN_KEY } from '@/mention/constants/MentionSuggestionPluginKey';
|
||||
import { MentionSuggestion } from '@/mention/extensions/MentionSuggestion';
|
||||
|
|
@ -25,7 +25,7 @@ type UseAIChatEditorProps = {
|
|||
};
|
||||
|
||||
export const useAIChatEditor = ({ onSendMessage }: UseAIChatEditorProps) => {
|
||||
const setAgentChatInput = useSetAtomState(agentChatInputStateV2);
|
||||
const setAgentChatInput = useSetAtomState(agentChatInputState);
|
||||
const { searchMentionRecords } = useMentionSearch();
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
const { removeFocusItemFromFocusStackById } =
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { agentChatSelectedFilesStateV2 } from '@/ai/states/agentChatSelectedFilesStateV2';
|
||||
import { agentChatUploadedFilesStateV2 } from '@/ai/states/agentChatUploadedFilesStateV2';
|
||||
import { agentChatSelectedFilesState } from '@/ai/states/agentChatSelectedFilesState';
|
||||
import { agentChatUploadedFilesState } from '@/ai/states/agentChatUploadedFilesState';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
|
|
@ -18,10 +18,10 @@ export const useAIChatFileUpload = () => {
|
|||
const { t } = useLingui();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const [agentChatSelectedFiles, setAgentChatSelectedFiles] = useAtomState(
|
||||
agentChatSelectedFilesStateV2,
|
||||
agentChatSelectedFilesState,
|
||||
);
|
||||
const [agentChatUploadedFiles, setAgentChatUploadedFiles] = useAtomState(
|
||||
agentChatUploadedFilesStateV2,
|
||||
agentChatUploadedFilesState,
|
||||
);
|
||||
|
||||
const sendFile = async (file: File): Promise<FileUIPart | null> => {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { useGetBrowsingContext } from '@/ai/hooks/useBrowsingContext';
|
||||
import { agentChatSelectedFilesStateV2 } from '@/ai/states/agentChatSelectedFilesStateV2';
|
||||
import { agentChatUploadedFilesStateV2 } from '@/ai/states/agentChatUploadedFilesStateV2';
|
||||
import { agentChatUsageStateV2 } from '@/ai/states/agentChatUsageStateV2';
|
||||
import { currentAIChatThreadStateV2 } from '@/ai/states/currentAIChatThreadStateV2';
|
||||
import { agentChatSelectedFilesState } from '@/ai/states/agentChatSelectedFilesState';
|
||||
import { agentChatUploadedFilesState } from '@/ai/states/agentChatUploadedFilesState';
|
||||
import { agentChatUsageState } from '@/ai/states/agentChatUsageState';
|
||||
import { currentAIChatThreadState } from '@/ai/states/currentAIChatThreadState';
|
||||
import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState';
|
||||
|
||||
import { agentChatInputStateV2 } from '@/ai/states/agentChatInputStateV2';
|
||||
import { agentChatInputState } from '@/ai/states/agentChatInputState';
|
||||
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
|
|
@ -22,26 +22,22 @@ import { cookieStorage } from '~/utils/cookie-storage';
|
|||
|
||||
export const useAgentChat = (uiMessages: ExtendedUIMessage[]) => {
|
||||
const setTokenPair = useSetAtomState(tokenPairState);
|
||||
const setAgentChatUsage = useSetAtomState(agentChatUsageStateV2);
|
||||
const setAgentChatUsage = useSetAtomState(agentChatUsageState);
|
||||
|
||||
const { getBrowsingContext } = useGetBrowsingContext();
|
||||
const setCurrentAIChatThreadTitle = useSetAtomState(
|
||||
currentAIChatThreadTitleState,
|
||||
);
|
||||
|
||||
const agentChatSelectedFiles = useAtomStateValue(
|
||||
agentChatSelectedFilesStateV2,
|
||||
);
|
||||
const agentChatSelectedFiles = useAtomStateValue(agentChatSelectedFilesState);
|
||||
|
||||
const currentAIChatThread = useAtomStateValue(currentAIChatThreadStateV2);
|
||||
const currentAIChatThread = useAtomStateValue(currentAIChatThreadState);
|
||||
|
||||
const [agentChatUploadedFiles, setAgentChatUploadedFiles] = useAtomState(
|
||||
agentChatUploadedFilesStateV2,
|
||||
agentChatUploadedFilesState,
|
||||
);
|
||||
|
||||
const [agentChatInput, setAgentChatInput] = useAtomState(
|
||||
agentChatInputStateV2,
|
||||
);
|
||||
const [agentChatInput, setAgentChatInput] = useAtomState(agentChatInputState);
|
||||
|
||||
const retryFetchWithRenewedToken = async (
|
||||
input: RequestInfo | URL,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import { useAgentChatScrollToBottom } from '@/ai/hooks/useAgentChatScrollToBottom';
|
||||
import {
|
||||
agentChatUsageStateV2,
|
||||
agentChatUsageState,
|
||||
type AgentChatUsageState,
|
||||
} from '@/ai/states/agentChatUsageStateV2';
|
||||
import { currentAIChatThreadStateV2 } from '@/ai/states/currentAIChatThreadStateV2';
|
||||
} from '@/ai/states/agentChatUsageState';
|
||||
import { currentAIChatThreadState } from '@/ai/states/currentAIChatThreadState';
|
||||
import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState';
|
||||
import { isCreatingChatThreadStateV2 } from '@/ai/states/isCreatingChatThreadStateV2';
|
||||
import { isCreatingChatThreadState } from '@/ai/states/isCreatingChatThreadState';
|
||||
import { mapDBMessagesToUIMessages } from '@/ai/utils/mapDBMessagesToUIMessages';
|
||||
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import { type SetterOrUpdater } from 'recoil';
|
||||
import { type SetStateAction } from 'jotai';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
type AgentChatThread,
|
||||
|
|
@ -20,7 +20,9 @@ import {
|
|||
|
||||
const setUsageFromThread = (
|
||||
thread: AgentChatThread,
|
||||
setAgentChatUsage: SetterOrUpdater<AgentChatUsageState | null>,
|
||||
setAgentChatUsage: (
|
||||
update: SetStateAction<AgentChatUsageState | null>,
|
||||
) => void,
|
||||
) => {
|
||||
const hasUsageData =
|
||||
(thread.conversationSize ?? 0) > 0 && isDefined(thread.contextWindowTokens);
|
||||
|
|
@ -42,14 +44,14 @@ const setUsageFromThread = (
|
|||
|
||||
export const useAgentChatData = () => {
|
||||
const [currentAIChatThread, setCurrentAIChatThread] = useAtomState(
|
||||
currentAIChatThreadStateV2,
|
||||
currentAIChatThreadState,
|
||||
);
|
||||
const setAgentChatUsage = useSetAtomState(agentChatUsageStateV2);
|
||||
const setAgentChatUsage = useSetAtomState(agentChatUsageState);
|
||||
const setCurrentAIChatThreadTitle = useSetAtomState(
|
||||
currentAIChatThreadTitleState,
|
||||
);
|
||||
const [isCreatingChatThread, setIsCreatingChatThread] = useAtomState(
|
||||
isCreatingChatThreadStateV2,
|
||||
isCreatingChatThreadState,
|
||||
);
|
||||
|
||||
const { scrollToBottom } = useAgentChatScrollToBottom();
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { contextStoreFiltersComponentState } from '@/context-store/states/contex
|
|||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { recordStoreFamilySelectorV2 } from '@/object-record/record-store/states/selectors/recordStoreFamilySelectorV2';
|
||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useStore } from 'jotai';
|
||||
|
|
@ -66,7 +66,7 @@ export const useGetBrowsingContext = () => {
|
|||
};
|
||||
|
||||
const pageLayoutId = store.get(
|
||||
recordStoreFamilySelectorV2.selectorFamily({
|
||||
recordStoreFamilySelector.selectorFamily({
|
||||
recordId: targetedRecordsRule.selectedRecordIds[0],
|
||||
fieldName: 'pageLayoutId',
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { agentChatUsageStateV2 } from '@/ai/states/agentChatUsageStateV2';
|
||||
import { currentAIChatThreadStateV2 } from '@/ai/states/currentAIChatThreadStateV2';
|
||||
import { agentChatUsageState } from '@/ai/states/agentChatUsageState';
|
||||
import { currentAIChatThreadState } from '@/ai/states/currentAIChatThreadState';
|
||||
import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState';
|
||||
import { useOpenAskAIPageInCommandMenu } from '@/command-menu/hooks/useOpenAskAIPageInCommandMenu';
|
||||
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
||||
|
|
@ -7,8 +7,8 @@ import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomStat
|
|||
import { useCreateChatThreadMutation } from '~/generated-metadata/graphql';
|
||||
|
||||
export const useCreateNewAIChatThread = () => {
|
||||
const [, setCurrentAIChatThread] = useAtomState(currentAIChatThreadStateV2);
|
||||
const setAgentChatUsage = useSetAtomState(agentChatUsageStateV2);
|
||||
const [, setCurrentAIChatThread] = useAtomState(currentAIChatThreadState);
|
||||
const setAgentChatUsage = useSetAtomState(agentChatUsageState);
|
||||
const setCurrentAIChatThreadTitle = useSetAtomState(
|
||||
currentAIChatThreadTitleState,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
|
||||
export const agentChatInputStateV2 = createAtomState<string>({
|
||||
key: 'agentChatInputStateV2',
|
||||
export const agentChatInputState = createAtomState<string>({
|
||||
key: 'agentChatInputState',
|
||||
defaultValue: '',
|
||||
});
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
|
||||
export const agentChatSelectedFilesState = createAtomState<File[]>({
|
||||
key: 'ai/agentChatSelectedFilesState',
|
||||
defaultValue: [],
|
||||
});
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
|
||||
export const agentChatSelectedFilesStateV2 = createAtomState<File[]>({
|
||||
key: 'ai/agentChatSelectedFilesStateV2',
|
||||
defaultValue: [],
|
||||
});
|
||||
|
|
@ -2,7 +2,7 @@ import { type FileUIPart } from 'ai';
|
|||
|
||||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
|
||||
export const agentChatUploadedFilesStateV2 = createAtomState<FileUIPart[]>({
|
||||
key: 'ai/agentChatUploadedFilesStateV2',
|
||||
export const agentChatUploadedFilesState = createAtomState<FileUIPart[]>({
|
||||
key: 'ai/agentChatUploadedFilesState',
|
||||
defaultValue: [],
|
||||
});
|
||||
|
|
@ -18,8 +18,7 @@ export type AgentChatUsageState = {
|
|||
outputCredits: number;
|
||||
};
|
||||
|
||||
export const agentChatUsageStateV2 =
|
||||
createAtomState<AgentChatUsageState | null>({
|
||||
key: 'agentChatUsageStateV2',
|
||||
defaultValue: null,
|
||||
});
|
||||
export const agentChatUsageState = createAtomState<AgentChatUsageState | null>({
|
||||
key: 'agentChatUsageState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
|
||||
export const currentAIChatThreadState = createAtomState<string | null>({
|
||||
key: 'ai/currentAIChatThreadState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
|
||||
export const currentAIChatThreadStateV2 = createAtomState<string | null>({
|
||||
key: 'ai/currentAIChatThreadStateV2',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
|
||||
export const isCreatingChatThreadStateV2 = createAtomState<boolean>({
|
||||
key: 'ai/isCreatingChatThreadStateV2',
|
||||
export const isCreatingChatThreadState = createAtomState<boolean>({
|
||||
key: 'ai/isCreatingChatThreadState',
|
||||
defaultValue: false,
|
||||
});
|
||||
|
|
@ -2,8 +2,6 @@ import { gql } from '@apollo/client';
|
|||
import { MockedProvider, type MockedResponse } from '@apollo/client/testing';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import {
|
||||
ANALYTICS_COOKIE_NAME,
|
||||
useEventTracker,
|
||||
|
|
@ -96,11 +94,9 @@ const mocks: MockedResponse[] = [
|
|||
];
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
<MockedProvider mocks={mocks} addTypename={false}>
|
||||
{children}
|
||||
</MockedProvider>
|
||||
</RecoilRoot>
|
||||
<MockedProvider mocks={mocks} addTypename={false}>
|
||||
{children}
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
describe('useEventTracker', () => {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ import { ApolloError, gql } from '@apollo/client';
|
|||
import { act, renderHook } from '@testing-library/react';
|
||||
import fetchMock, { enableFetchMocks } from 'jest-fetch-mock';
|
||||
import { MemoryRouter, useLocation } from 'react-router-dom';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { SnackBarComponentInstanceContext } from '@/ui/feedback/snack-bar-manager/contexts/SnackBarComponentInstanceContext';
|
||||
import { useApolloFactory } from '@/apollo/hooks/useApolloFactory';
|
||||
|
||||
|
|
@ -21,18 +19,16 @@ jest.mock('react-router-dom', () => {
|
|||
});
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
<MemoryRouter
|
||||
initialEntries={['/welcome', '/verify', '/opportunities']}
|
||||
initialIndex={2}
|
||||
<MemoryRouter
|
||||
initialEntries={['/welcome', '/verify', '/opportunities']}
|
||||
initialIndex={2}
|
||||
>
|
||||
<SnackBarComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'test-instance-id' }}
|
||||
>
|
||||
<SnackBarComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'test-instance-id' }}
|
||||
>
|
||||
{children}
|
||||
</SnackBarComponentInstanceContext.Provider>
|
||||
</MemoryRouter>
|
||||
</RecoilRoot>
|
||||
{children}
|
||||
</SnackBarComponentInstanceContext.Provider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
describe('useApolloFactory', () => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { AppRouter } from '@/app/components/AppRouter';
|
||||
import { ApolloDevLogEffect } from '@/debug/components/ApolloDevLogEffect';
|
||||
import { RecoilDebugObserverEffect } from '@/debug/components/RecoilDebugObserver';
|
||||
import { AppErrorBoundary } from '@/error-handler/components/AppErrorBoundary';
|
||||
import { AppRootErrorFallback } from '@/error-handler/components/AppRootErrorFallback';
|
||||
import { ExceptionHandlerProvider } from '@/error-handler/components/ExceptionHandlerProvider';
|
||||
|
|
@ -11,7 +10,6 @@ import { I18nProvider } from '@lingui/react';
|
|||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { IconsProvider } from 'twenty-ui/display';
|
||||
import { initialI18nActivate } from '~/utils/i18n/initialI18nActivate';
|
||||
|
||||
|
|
@ -20,32 +18,29 @@ initialI18nActivate();
|
|||
export const App = () => {
|
||||
return (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>
|
||||
<AppErrorBoundary
|
||||
resetOnLocationChange={false}
|
||||
FallbackComponent={AppRootErrorFallback}
|
||||
>
|
||||
<I18nProvider i18n={i18n}>
|
||||
<RecoilDebugObserverEffect />
|
||||
<ApolloDevLogEffect />
|
||||
<SnackBarComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'snack-bar-manager' }}
|
||||
>
|
||||
<IconsProvider>
|
||||
<ExceptionHandlerProvider>
|
||||
<HelmetProvider>
|
||||
<ClickOutsideListenerContext.Provider
|
||||
value={{ excludedClickOutsideId: undefined }}
|
||||
>
|
||||
<AppRouter />
|
||||
</ClickOutsideListenerContext.Provider>
|
||||
</HelmetProvider>
|
||||
</ExceptionHandlerProvider>
|
||||
</IconsProvider>
|
||||
</SnackBarComponentInstanceContext.Provider>
|
||||
</I18nProvider>
|
||||
</AppErrorBoundary>
|
||||
</RecoilRoot>
|
||||
<AppErrorBoundary
|
||||
resetOnLocationChange={false}
|
||||
FallbackComponent={AppRootErrorFallback}
|
||||
>
|
||||
<I18nProvider i18n={i18n}>
|
||||
<ApolloDevLogEffect />
|
||||
<SnackBarComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'snack-bar-manager' }}
|
||||
>
|
||||
<IconsProvider>
|
||||
<ExceptionHandlerProvider>
|
||||
<HelmetProvider>
|
||||
<ClickOutsideListenerContext.Provider
|
||||
value={{ excludedClickOutsideId: undefined }}
|
||||
>
|
||||
<AppRouter />
|
||||
</ClickOutsideListenerContext.Provider>
|
||||
</HelmetProvider>
|
||||
</ExceptionHandlerProvider>
|
||||
</IconsProvider>
|
||||
</SnackBarComponentInstanceContext.Provider>
|
||||
</I18nProvider>
|
||||
</AppErrorBoundary>
|
||||
</JotaiProvider>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { GoToHotkeyItemEffect } from '@/app/effect-components/GoToHotkeyItemEffect';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
||||
import { navigationDrawerExpandedMemorizedStateV2 } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedStateV2';
|
||||
import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState';
|
||||
import { useGoToHotkeys } from '@/ui/utilities/hotkey/hooks/useGoToHotkeys';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
|
|
@ -19,7 +19,7 @@ export const GotoHotkeysEffectsProvider = () => {
|
|||
location: getSettingsPath(SettingsPath.ProfilePage),
|
||||
preNavigateFunction: useCallback(() => {
|
||||
store.set(isNavigationDrawerExpandedState.atom, true);
|
||||
store.set(navigationDrawerExpandedMemorizedStateV2.atom, true);
|
||||
store.set(navigationDrawerExpandedMemorizedState.atom, true);
|
||||
}, [store]),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ import { BILLING_CHECKOUT_SESSION_DEFAULT_VALUE } from '@/billing/constants/Bill
|
|||
import deepEqual from 'deep-equal';
|
||||
import { useStore } from 'jotai';
|
||||
|
||||
// Initialize state that are hydrated from query parameters
|
||||
// We used to use recoil-sync to do this, but it was causing issues with Firefox
|
||||
export const useInitializeQueryParamState = () => {
|
||||
const store = useStore();
|
||||
const initializeQueryParamState = useCallback(() => {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,14 @@
|
|||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
|
||||
import { Logo } from '@/auth/components/Logo';
|
||||
import {
|
||||
ComponentDecorator,
|
||||
RecoilRootDecorator,
|
||||
RouterDecorator,
|
||||
} from 'twenty-ui/testing';
|
||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
||||
|
||||
const logoUrl = 'https://picsum.photos/192/192';
|
||||
|
||||
const meta: Meta<typeof Logo> = {
|
||||
title: 'Modules/Auth/Logo',
|
||||
component: Logo,
|
||||
decorators: [ComponentDecorator, RecoilRootDecorator, RouterDecorator],
|
||||
decorators: [ComponentDecorator, RouterDecorator],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { type VerifyEmailEffect } from '@/auth/components/VerifyEmailEffect';
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { MemoryRouter, Route, Routes } from 'react-router-dom';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
// Mock component that just renders the error state of VerifyEmailEffect directly
|
||||
// (since normal VerifyEmailEffect has async logic that's hard to test in Storybook)
|
||||
|
|
@ -23,9 +22,7 @@ const meta: Meta<typeof VerifyEmailEffectErrorState> = {
|
|||
decorators: [
|
||||
(Story) => (
|
||||
<div style={{ padding: '24px' }}>
|
||||
<RecoilRoot>
|
||||
<Story />
|
||||
</RecoilRoot>
|
||||
<Story />
|
||||
</div>
|
||||
),
|
||||
SnackBarDecorator,
|
||||
|
|
@ -48,20 +45,18 @@ export const ErrorState: Story = {
|
|||
|
||||
export const IntegratedExample: StoryObj<typeof VerifyEmailEffect> = {
|
||||
render: () => (
|
||||
<RecoilRoot>
|
||||
<MemoryRouter
|
||||
initialEntries={[
|
||||
'/verify-email?email=user@example.com&emailVerificationToken=invalid-token',
|
||||
]}
|
||||
>
|
||||
<Routes>
|
||||
<Route
|
||||
path="/verify-email"
|
||||
element={<VerifyEmailEffectErrorState email="user@example.com" />}
|
||||
/>
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
</RecoilRoot>
|
||||
<MemoryRouter
|
||||
initialEntries={[
|
||||
'/verify-email?email=user@example.com&emailVerificationToken=invalid-token',
|
||||
]}
|
||||
>
|
||||
<Routes>
|
||||
<Route
|
||||
path="/verify-email"
|
||||
element={<VerifyEmailEffectErrorState email="user@example.com" />}
|
||||
/>
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
),
|
||||
parameters: {
|
||||
docs: {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import { useApolloClient } from '@apollo/client';
|
|||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { type ReactNode, act } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import {
|
||||
email,
|
||||
|
|
@ -75,15 +74,13 @@ jest.mock('@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain', () => ({
|
|||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<MockedProvider mocks={Object.values(mocks)} addTypename={false}>
|
||||
<RecoilRoot>
|
||||
<MemoryRouter>
|
||||
<SnackBarComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'test-instance-id' }}
|
||||
>
|
||||
{children}
|
||||
</SnackBarComponentInstanceContext.Provider>
|
||||
</MemoryRouter>
|
||||
</RecoilRoot>
|
||||
<MemoryRouter>
|
||||
<SnackBarComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'test-instance-id' }}
|
||||
>
|
||||
{children}
|
||||
</SnackBarComponentInstanceContext.Provider>
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,19 @@
|
|||
import { act, renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useIsLogged } from '@/auth/hooks/useIsLogged';
|
||||
import { tokenPairState } from '@/auth/states/tokenPairState';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
|
||||
const renderHooks = () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const isLogged = useIsLogged();
|
||||
const setTokenPair = useSetAtomState(tokenPairState);
|
||||
const { result } = renderHook(() => {
|
||||
const isLogged = useIsLogged();
|
||||
const setTokenPair = useSetAtomState(tokenPairState);
|
||||
|
||||
return {
|
||||
isLogged,
|
||||
setTokenPair,
|
||||
};
|
||||
},
|
||||
{
|
||||
wrapper: RecoilRoot,
|
||||
},
|
||||
);
|
||||
return {
|
||||
isLogged,
|
||||
setTokenPair,
|
||||
};
|
||||
});
|
||||
return { result };
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { i18n } from '@lingui/core';
|
||||
import { I18nProvider } from '@lingui/react';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
|
|
@ -28,8 +27,7 @@ dynamicActivate(SOURCE_LOCALE);
|
|||
|
||||
const renderHooks = () => {
|
||||
const { result } = renderHook(() => useVerifyLogin(), {
|
||||
wrapper: ({ children }) =>
|
||||
RecoilRoot({ children: I18nProvider({ i18n, children }) }),
|
||||
wrapper: ({ children }) => I18nProvider({ i18n, children }),
|
||||
});
|
||||
return { result };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { ApolloError, useApolloClient } from '@apollo/client';
|
||||
import { useCallback } from 'react';
|
||||
import { snapshot_UNSTABLE, useGotoRecoilSnapshot } from 'recoil';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
|
|
@ -113,8 +112,6 @@ export const useAuth = () => {
|
|||
|
||||
const client = useApolloClient();
|
||||
|
||||
const goToRecoilSnapshot = useGotoRecoilSnapshot();
|
||||
|
||||
const [, setSearchParams] = useSearchParams();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
|
@ -124,8 +121,6 @@ export const useAuth = () => {
|
|||
|
||||
sseClient?.dispose();
|
||||
|
||||
const emptySnapshot = snapshot_UNSTABLE();
|
||||
|
||||
const authProvidersValue = store.get(workspaceAuthProvidersState.atom);
|
||||
const domainConfigurationValue = store.get(domainConfigurationState.atom);
|
||||
const workspacePublicDataValue = store.get(workspacePublicDataState.atom);
|
||||
|
|
@ -136,23 +131,15 @@ export const useAuth = () => {
|
|||
isCaptchaScriptLoadedState.atom,
|
||||
);
|
||||
|
||||
const initialSnapshot = emptySnapshot.map(() => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
sessionStorage.clear();
|
||||
localStorage.clear();
|
||||
|
||||
goToRecoilSnapshot(initialSnapshot);
|
||||
|
||||
store.set(workspaceAuthProvidersState.atom, authProvidersValue);
|
||||
store.set(workspacePublicDataState.atom, workspacePublicDataValue);
|
||||
store.set(domainConfigurationState.atom, domainConfigurationValue);
|
||||
store.set(isCaptchaScriptLoadedState.atom, isCaptchaScriptLoadedValue);
|
||||
store.set(lastAuthenticatedMethodState.atom, lastAuthenticatedMethod);
|
||||
|
||||
// Reset user-data Jotai states that were migrated from Recoil
|
||||
// (Recoil snapshot reset no longer handles these since they are Jotai V2)
|
||||
store.set(tokenPairState.atom, null);
|
||||
store.set(currentUserState.atom, null);
|
||||
store.set(currentWorkspaceState.atom, null);
|
||||
|
|
@ -172,7 +159,6 @@ export const useAuth = () => {
|
|||
await resetToMockedMetadata();
|
||||
navigate(AppPath.SignInUp);
|
||||
}, [
|
||||
goToRecoilSnapshot,
|
||||
client,
|
||||
setLastAuthenticateWorkspaceDomain,
|
||||
resetToMockedMetadata,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { I18nProvider } from '@lingui/react';
|
|||
import { act, renderHook } from '@testing-library/react';
|
||||
import { type ReactNode, createElement } from 'react';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useHandleResetPassword } from '@/auth/sign-in-up/hooks/useHandleResetPassword';
|
||||
import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState';
|
||||
|
|
@ -32,11 +31,7 @@ const renderHooks = () => {
|
|||
createElement(
|
||||
JotaiProvider,
|
||||
{ store: jotaiStore },
|
||||
createElement(
|
||||
RecoilRoot,
|
||||
null as any,
|
||||
createElement(I18nProvider, { i18n }, children),
|
||||
),
|
||||
createElement(I18nProvider, { i18n }, children),
|
||||
),
|
||||
});
|
||||
return { result };
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import { renderHook } from '@testing-library/react';
|
|||
import { type ReactNode } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
const TestWrapper = ({
|
||||
children,
|
||||
|
|
@ -25,9 +24,7 @@ const TestWrapper = ({
|
|||
);
|
||||
return (
|
||||
<MemoryRouter initialEntries={[initialEntry]}>
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</JotaiProvider>
|
||||
<JotaiProvider store={jotaiStore}>{children}</JotaiProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
import { act, renderHook } from '@testing-library/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { captchaTokenState } from '@/captcha/states/captchaTokenState';
|
||||
import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</JotaiProvider>
|
||||
<JotaiProvider store={jotaiStore}>{children}</JotaiProvider>
|
||||
);
|
||||
|
||||
describe('useReadCaptchaToken', () => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
|
||||
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
|
||||
|
|
@ -13,9 +12,7 @@ import { type Captcha, CaptchaDriverType } from '~/generated-metadata/graphql';
|
|||
jest.mock('@/captcha/utils/isCaptchaRequiredForPath');
|
||||
|
||||
const createWrapper = ({ children }: { children: ReactNode }) => (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</JotaiProvider>
|
||||
<JotaiProvider store={jotaiStore}>{children}</JotaiProvider>
|
||||
);
|
||||
|
||||
describe('useRequestFreshCaptchaToken', () => {
|
||||
|
|
|
|||
|
|
@ -21,19 +21,17 @@ import { isImapSmtpCaldavEnabledState } from '@/client-config/states/isImapSmtpC
|
|||
import { isMicrosoftCalendarEnabledState } from '@/client-config/states/isMicrosoftCalendarEnabledState';
|
||||
import { isMicrosoftMessagingEnabledState } from '@/client-config/states/isMicrosoftMessagingEnabledState';
|
||||
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
||||
import { labPublicFeatureFlagsStateV2 } from '@/client-config/states/labPublicFeatureFlagsStateV2';
|
||||
import { labPublicFeatureFlagsState } from '@/client-config/states/labPublicFeatureFlagsState';
|
||||
import { sentryConfigState } from '@/client-config/states/sentryConfigState';
|
||||
import { supportChatState } from '@/client-config/states/supportChatState';
|
||||
import { type ClientConfig } from '@/client-config/types/ClientConfig';
|
||||
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
||||
import { useCallback } from 'react';
|
||||
import { isAttachmentPreviewEnabledStateV2 } from '@/client-config/states/isAttachmentPreviewEnabledStateV2';
|
||||
import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState';
|
||||
import { getClientConfig } from '@/client-config/utils/getClientConfig';
|
||||
import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
||||
import { useStore } from 'jotai';
|
||||
|
||||
type UseClientConfigResult = {
|
||||
data: { clientConfig: ClientConfig } | undefined;
|
||||
|
|
@ -44,7 +42,6 @@ type UseClientConfigResult = {
|
|||
};
|
||||
|
||||
export const useClientConfig = (): UseClientConfigResult => {
|
||||
const store = useStore();
|
||||
const setIsAnalyticsEnabled = useSetAtomState(isAnalyticsEnabledState);
|
||||
const setDomainConfiguration = useSetAtomState(domainConfigurationState);
|
||||
const setAuthProviders = useSetAtomState(authProvidersState);
|
||||
|
|
@ -76,9 +73,7 @@ export const useClientConfig = (): UseClientConfigResult => {
|
|||
|
||||
const setCanManageFeatureFlags = useSetAtomState(canManageFeatureFlagsState);
|
||||
|
||||
const setLabPublicFeatureFlags = useSetAtomState(
|
||||
labPublicFeatureFlagsStateV2,
|
||||
);
|
||||
const setLabPublicFeatureFlags = useSetAtomState(labPublicFeatureFlagsState);
|
||||
|
||||
const setMicrosoftMessagingEnabled = useSetAtomState(
|
||||
isMicrosoftMessagingEnabledState,
|
||||
|
|
@ -188,10 +183,6 @@ export const useClientConfig = (): UseClientConfigResult => {
|
|||
setGoogleMessagingEnabled(clientConfig?.isGoogleMessagingEnabled);
|
||||
setGoogleCalendarEnabled(clientConfig?.isGoogleCalendarEnabled);
|
||||
setIsAttachmentPreviewEnabled(clientConfig?.isAttachmentPreviewEnabled);
|
||||
store.set(
|
||||
isAttachmentPreviewEnabledStateV2.atom,
|
||||
clientConfig?.isAttachmentPreviewEnabled,
|
||||
);
|
||||
setIsConfigVariablesInDbEnabled(
|
||||
clientConfig?.isConfigVariablesInDbEnabled,
|
||||
);
|
||||
|
|
@ -249,7 +240,6 @@ export const useClientConfig = (): UseClientConfigResult => {
|
|||
setSentryConfig,
|
||||
setSupportChat,
|
||||
setAllowRequestsToTwentyIcons,
|
||||
store,
|
||||
]);
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
|
||||
export const isAttachmentPreviewEnabledState = createAtomState<boolean>({
|
||||
key: 'isAttachmentPreviewEnabled',
|
||||
key: 'isAttachmentPreviewEnabledState',
|
||||
defaultValue: false,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
|
||||
export const isAttachmentPreviewEnabledStateV2 = createAtomState<boolean>({
|
||||
key: 'isAttachmentPreviewEnabledStateV2',
|
||||
defaultValue: false,
|
||||
});
|
||||
|
|
@ -2,9 +2,7 @@ import { type PublicFeatureFlag } from '~/generated-metadata/graphql';
|
|||
|
||||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
|
||||
export const labPublicFeatureFlagsStateV2 = createAtomState<
|
||||
PublicFeatureFlag[]
|
||||
>({
|
||||
key: 'labPublicFeatureFlagsStateV2',
|
||||
export const labPublicFeatureFlagsState = createAtomState<PublicFeatureFlag[]>({
|
||||
key: 'labPublicFeatureFlagsState',
|
||||
defaultValue: [],
|
||||
});
|
||||
|
|
@ -11,7 +11,7 @@ import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/constants/FolderIcon
|
|||
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
|
||||
import { useUpdateFolderInDraft } from '@/navigation-menu-item/hooks/useUpdateFolderInDraft';
|
||||
import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
|
||||
import { selectedNavigationMenuItemInEditModeStateV2 } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeStateV2';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors';
|
||||
import { IconPicker } from '@/ui/input/components/IconPicker';
|
||||
|
|
@ -33,7 +33,7 @@ export const CommandMenuFolderInfo = () => {
|
|||
commandMenuPageInfo.instanceId,
|
||||
);
|
||||
const selectedNavigationMenuItemInEditMode = useAtomStateValue(
|
||||
selectedNavigationMenuItemInEditModeStateV2,
|
||||
selectedNavigationMenuItemInEditModeState,
|
||||
);
|
||||
const items = useWorkspaceSectionItems();
|
||||
const { updateFolderInDraft } = useUpdateFolderInDraft();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { CommandMenuOpenContainer } from '@/command-menu/components/CommandMenuOpenContainer';
|
||||
import { CommandMenuRouter } from '@/command-menu/components/CommandMenuRouter';
|
||||
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
|
||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ const StyledCommandMenuMobileFullScreenContainer = styled.div`
|
|||
`;
|
||||
|
||||
export const CommandMenuForMobile = () => {
|
||||
const isCommandMenuOpened = useAtomStateValue(isCommandMenuOpenedStateV2);
|
||||
const isCommandMenuOpened = useAtomStateValue(isCommandMenuOpenedState);
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { type IconComponent } from 'twenty-ui/display';
|
|||
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { AddToNavigationDragHandle } from '@/navigation-menu-item/components/AddToNavigationDragHandle';
|
||||
import { addToNavPayloadRegistryStateV2 } from '@/navigation-menu-item/states/addToNavPayloadRegistryStateV2';
|
||||
import { addToNavPayloadRegistryState } from '@/navigation-menu-item/states/addToNavPayloadRegistryState';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import type { AddToNavigationDragPayload } from '@/navigation-menu-item/types/add-to-navigation-drag-payload';
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ export const CommandMenuItemWithAddToNavigationDrag = ({
|
|||
dragIndex,
|
||||
}: CommandMenuItemWithAddToNavigationDragProps) => {
|
||||
const { t } = useLingui();
|
||||
const setRegistry = useSetAtomState(addToNavPayloadRegistryStateV2);
|
||||
const setRegistry = useSetAtomState(addToNavPayloadRegistryState);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const contextualDescription = isHovered
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { NavigationMenuItemType } from '@/navigation-menu-item/constants/Navigat
|
|||
import { StyledNavigationMenuItemIconContainer } from '@/navigation-menu-item/components/NavigationMenuItemIconContainer';
|
||||
import { useUpdateLinkInDraft } from '@/navigation-menu-item/hooks/useUpdateLinkInDraft';
|
||||
import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
|
||||
import { selectedNavigationMenuItemInEditModeStateV2 } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeStateV2';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors';
|
||||
import { TitleInput } from '@/ui/input/components/TitleInput';
|
||||
|
|
@ -25,7 +25,7 @@ export const CommandMenuLinkInfo = () => {
|
|||
commandMenuPageInfo.instanceId,
|
||||
);
|
||||
const selectedNavigationMenuItemInEditMode = useAtomStateValue(
|
||||
selectedNavigationMenuItemInEditModeStateV2,
|
||||
selectedNavigationMenuItemInEditModeState,
|
||||
);
|
||||
const items = useWorkspaceSectionItems();
|
||||
const { updateLinkInDraft } = useUpdateLinkInDraft();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { COMMAND_MENU_ANIMATION_VARIANTS } from '@/command-menu/constants/Comman
|
|||
import { COMMAND_MENU_CLICK_OUTSIDE_ID } from '@/command-menu/constants/CommandMenuClickOutsideId';
|
||||
import { SIDE_PANEL_FOCUS_ID } from '@/command-menu/constants/SidePanelFocusId';
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { isSidePanelAnimatingStateV2 } from '@/command-menu/states/isSidePanelAnimatingStateV2';
|
||||
import { isSidePanelAnimatingState } from '@/command-menu/states/isSidePanelAnimatingState';
|
||||
import { type CommandMenuAnimationVariant } from '@/command-menu/types/CommandMenuAnimationVariant';
|
||||
import { RECORD_CHIP_CLICK_OUTSIDE_ID } from '@/object-record/record-table/constants/RecordChipClickOutsideId';
|
||||
import { MENTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID } from '@/ui/input/constants/MentionMenuDropdownClickOutsideId';
|
||||
|
|
@ -54,7 +54,7 @@ export const CommandMenuOpenContainer = ({
|
|||
const { closeCommandMenu } = useCommandMenu();
|
||||
|
||||
const commandMenuRef = useRef<HTMLDivElement>(null);
|
||||
const setIsSidePanelAnimating = useSetAtomState(isSidePanelAnimatingStateV2);
|
||||
const setIsSidePanelAnimating = useSetAtomState(isSidePanelAnimatingState);
|
||||
|
||||
const store = useStore();
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { CommandMenuRecordInfo } from '@/command-menu/components/CommandMenuReco
|
|||
import { CommandMenuWorkflowStepInfo } from '@/command-menu/components/CommandMenuWorkflowStepInfo';
|
||||
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
|
||||
import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
|
||||
import { selectedNavigationMenuItemInEditModeStateV2 } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeStateV2';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { CommandMenuPages } from 'twenty-shared/types';
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ type CommandMenuPageInfoProps = {
|
|||
|
||||
export const CommandMenuPageInfo = ({ pageChip }: CommandMenuPageInfoProps) => {
|
||||
const selectedNavigationMenuItemInEditMode = useAtomStateValue(
|
||||
selectedNavigationMenuItemInEditModeStateV2,
|
||||
selectedNavigationMenuItemInEditModeState,
|
||||
);
|
||||
const items = useWorkspaceSectionItems();
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import { useIsRecordFieldReadOnly } from '@/object-record/read-only/hooks/useIsR
|
|||
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
|
||||
import { useRecordShowContainerActions } from '@/object-record/record-show/hooks/useRecordShowContainerActions';
|
||||
import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage';
|
||||
import { recordStoreFamilySelectorV2 } from '@/object-record/record-store/states/selectors/recordStoreFamilySelectorV2';
|
||||
import { recordStoreIdentifierFamilySelectorV2 } from '@/object-record/record-store/states/selectors/recordStoreIdentifierFamilySelectorV2';
|
||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||
import { recordStoreIdentifierFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreIdentifierFamilySelector';
|
||||
import { RecordTitleCell } from '@/object-record/record-title-cell/components/RecordTitleCell';
|
||||
import { RecordTitleCellContainerType } from '@/object-record/record-title-cell/types/RecordTitleCellContainerType';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
|
|
@ -50,7 +50,7 @@ export const CommandMenuRecordInfo = ({
|
|||
);
|
||||
|
||||
const recordCreatedAt = useAtomFamilySelectorValue(
|
||||
recordStoreFamilySelectorV2,
|
||||
recordStoreFamilySelector,
|
||||
{
|
||||
recordId: objectRecordId,
|
||||
fieldName: 'createdAt',
|
||||
|
|
@ -62,7 +62,7 @@ export const CommandMenuRecordInfo = ({
|
|||
);
|
||||
|
||||
const recordIdentifier = useAtomFamilySelectorValue(
|
||||
recordStoreIdentifierFamilySelectorV2,
|
||||
recordStoreIdentifierFamilySelector,
|
||||
{
|
||||
recordId: objectRecordId,
|
||||
allowRequestsToTwentyIcons,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
commandMenuWidthState,
|
||||
} from '@/command-menu/states/commandMenuWidthState';
|
||||
import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState';
|
||||
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
|
||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||
import { tableWidthResizeIsActiveState } from '@/object-record/record-table/states/tableWidthResizeIsActivedState';
|
||||
import { ModalContainerContext } from '@/ui/layout/modal/contexts/ModalContainerContext';
|
||||
import { ResizablePanelGap } from '@/ui/layout/resizable-panel/components/ResizablePanelGap';
|
||||
|
|
@ -56,7 +56,7 @@ const StyledModalContainer = styled.div`
|
|||
const GAP_WIDTH = 8;
|
||||
|
||||
export const CommandMenuSidePanelForDesktop = () => {
|
||||
const isCommandMenuOpened = useAtomStateValue(isCommandMenuOpenedStateV2);
|
||||
const isCommandMenuOpened = useAtomStateValue(isCommandMenuOpenedState);
|
||||
const isCommandMenuClosing = useAtomStateValue(isCommandMenuClosingState);
|
||||
const [commandMenuWidth, setCommandMenuWidth] = useAtomState(
|
||||
commandMenuWidthState,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import { CommandMenuRouter } from '@/command-menu/components/CommandMenuRouter';
|
|||
import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId';
|
||||
import { SIDE_PANEL_FOCUS_ID } from '@/command-menu/constants/SidePanelFocusId';
|
||||
import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
|
||||
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
|
||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
|
||||
|
|
@ -86,7 +86,7 @@ const meta: Meta<typeof CommandMenu> = {
|
|||
currentUserWorkspaceState.atom,
|
||||
mockedUserData.currentUserWorkspace,
|
||||
);
|
||||
jotaiStore.set(isCommandMenuOpenedStateV2.atom, true);
|
||||
jotaiStore.set(isCommandMenuOpenedState.atom, true);
|
||||
jotaiStore.set(commandMenuNavigationStackState.atom, [
|
||||
{
|
||||
page: CommandMenuPages.Root,
|
||||
|
|
|
|||
|
|
@ -2,27 +2,24 @@ import { renderHook } from '@testing-library/react';
|
|||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { act } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
|
||||
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState';
|
||||
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
|
||||
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
|
||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { CommandMenuPages } from 'twenty-shared/types';
|
||||
import { IconDotsVertical } from 'twenty-ui/display';
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>
|
||||
<MemoryRouter
|
||||
initialEntries={['/one', '/two', { pathname: '/three' }]}
|
||||
initialIndex={1}
|
||||
>
|
||||
{children}
|
||||
</MemoryRouter>
|
||||
</RecoilRoot>
|
||||
<MemoryRouter
|
||||
initialEntries={['/one', '/two', { pathname: '/three' }]}
|
||||
initialIndex={1}
|
||||
>
|
||||
{children}
|
||||
</MemoryRouter>
|
||||
</JotaiProvider>
|
||||
);
|
||||
|
||||
|
|
@ -54,31 +51,31 @@ describe('useCommandMenu', () => {
|
|||
result.current.commandMenu.openCommandMenu();
|
||||
});
|
||||
|
||||
expect(jotaiStore.get(isCommandMenuOpenedStateV2.atom)).toBe(true);
|
||||
expect(jotaiStore.get(isCommandMenuOpenedState.atom)).toBe(true);
|
||||
|
||||
act(() => {
|
||||
result.current.commandMenu.closeCommandMenu();
|
||||
});
|
||||
|
||||
expect(jotaiStore.get(isCommandMenuOpenedStateV2.atom)).toBe(false);
|
||||
expect(jotaiStore.get(isCommandMenuOpenedState.atom)).toBe(false);
|
||||
});
|
||||
|
||||
it('should toggle the command menu', () => {
|
||||
const { result } = renderHooks();
|
||||
|
||||
expect(jotaiStore.get(isCommandMenuOpenedStateV2.atom)).toBe(false);
|
||||
expect(jotaiStore.get(isCommandMenuOpenedState.atom)).toBe(false);
|
||||
|
||||
act(() => {
|
||||
result.current.commandMenu.toggleCommandMenu();
|
||||
});
|
||||
|
||||
expect(jotaiStore.get(isCommandMenuOpenedStateV2.atom)).toBe(true);
|
||||
expect(jotaiStore.get(isCommandMenuOpenedState.atom)).toBe(true);
|
||||
|
||||
act(() => {
|
||||
result.current.commandMenu.toggleCommandMenu();
|
||||
});
|
||||
|
||||
expect(jotaiStore.get(isCommandMenuOpenedStateV2.atom)).toBe(false);
|
||||
expect(jotaiStore.get(isCommandMenuOpenedState.atom)).toBe(false);
|
||||
});
|
||||
|
||||
it('should navigate command menu and reset navigation stack when resetNavigationStack is true', () => {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ import { renderHook } from '@testing-library/react';
|
|||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { act } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId';
|
||||
import { COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID } from '@/command-menu/constants/CommandMenuContextChipGroupsDropdownId';
|
||||
import { COMMAND_MENU_PREVIOUS_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuPreviousComponentInstanceId';
|
||||
|
|
@ -14,7 +12,7 @@ import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState
|
|||
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
|
||||
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
|
||||
import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState';
|
||||
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
|
||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
|
|
@ -53,9 +51,7 @@ jest.mock('@/ui/layout/right-drawer/utils/emitSidePanelCloseEvent', () => ({
|
|||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>
|
||||
<MemoryRouter>{children}</MemoryRouter>
|
||||
</RecoilRoot>
|
||||
<MemoryRouter>{children}</MemoryRouter>
|
||||
</JotaiProvider>
|
||||
);
|
||||
|
||||
|
|
@ -97,7 +93,7 @@ describe('useCommandMenuCloseAnimationCompleteCleanup', () => {
|
|||
Icon: IconList,
|
||||
instanceId: 'test-id',
|
||||
});
|
||||
jotaiStore.set(isCommandMenuOpenedStateV2.atom, true);
|
||||
jotaiStore.set(isCommandMenuOpenedState.atom, true);
|
||||
jotaiStore.set(commandMenuSearchState.atom, 'test search');
|
||||
jotaiStore.set(commandMenuNavigationStackState.atom, [
|
||||
{
|
||||
|
|
@ -120,7 +116,7 @@ describe('useCommandMenuCloseAnimationCompleteCleanup', () => {
|
|||
Icon: IconList,
|
||||
instanceId: 'test-id',
|
||||
});
|
||||
expect(jotaiStore.get(isCommandMenuOpenedStateV2.atom)).toBe(true);
|
||||
expect(jotaiStore.get(isCommandMenuOpenedState.atom)).toBe(true);
|
||||
expect(jotaiStore.get(commandMenuSearchState.atom)).toBe('test search');
|
||||
expect(jotaiStore.get(commandMenuNavigationStackState.atom)).toEqual([
|
||||
{
|
||||
|
|
@ -146,7 +142,7 @@ describe('useCommandMenuCloseAnimationCompleteCleanup', () => {
|
|||
Icon: undefined,
|
||||
instanceId: '',
|
||||
});
|
||||
expect(jotaiStore.get(isCommandMenuOpenedStateV2.atom)).toBe(false);
|
||||
expect(jotaiStore.get(isCommandMenuOpenedState.atom)).toBe(false);
|
||||
expect(jotaiStore.get(commandMenuSearchState.atom)).toBe('');
|
||||
expect(jotaiStore.get(commandMenuNavigationStackState.atom)).toEqual([]);
|
||||
expect(jotaiStore.get(hasUserSelectedCommandState.atom)).toBe(false);
|
||||
|
|
|
|||
|
|
@ -2,24 +2,20 @@ import { renderHook } from '@testing-library/react';
|
|||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { act } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { useCommandMenuCloseAnimationCompleteCleanup } from '@/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup';
|
||||
import { useCommandMenuHistory } from '@/command-menu/hooks/useCommandMenuHistory';
|
||||
import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
|
||||
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState';
|
||||
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
|
||||
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
|
||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { CommandMenuPages } from 'twenty-shared/types';
|
||||
import { IconList, IconSearch } from 'twenty-ui/display';
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>
|
||||
<MemoryRouter>{children}</MemoryRouter>
|
||||
</RecoilRoot>
|
||||
<MemoryRouter>{children}</MemoryRouter>
|
||||
</JotaiProvider>
|
||||
);
|
||||
|
||||
|
|
@ -116,7 +112,7 @@ describe('useCommandMenuHistory', () => {
|
|||
instanceId: '',
|
||||
Icon: undefined,
|
||||
});
|
||||
expect(jotaiStore.get(isCommandMenuOpenedStateV2.atom)).toBe(false);
|
||||
expect(jotaiStore.get(isCommandMenuOpenedState.atom)).toBe(false);
|
||||
});
|
||||
|
||||
it('should navigate to a page in history', () => {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,15 @@
|
|||
import { renderHook } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { useCommandMenuOnItemClick } from '@/command-menu/hooks/useCommandMenuOnItemClick';
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
<MemoryRouter
|
||||
initialEntries={['/one', '/two', { pathname: '/three' }]}
|
||||
initialIndex={1}
|
||||
>
|
||||
{children}
|
||||
</MemoryRouter>
|
||||
</RecoilRoot>
|
||||
<MemoryRouter
|
||||
initialEntries={['/one', '/two', { pathname: '/three' }]}
|
||||
initialIndex={1}
|
||||
>
|
||||
{children}
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
const renderHooks = () => {
|
||||
|
|
|
|||
|
|
@ -4,15 +4,11 @@ import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
|||
import { renderHook } from '@testing-library/react';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { act } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
const pageId = 'merge-page-id';
|
||||
const objectMetadataId = 'company-metadata-id';
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</JotaiProvider>
|
||||
<JotaiProvider store={jotaiStore}>{children}</JotaiProvider>
|
||||
);
|
||||
|
||||
const renderHooks = (initialRecordIds: string[]) => {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import { act, renderHook } from '@testing-library/react';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { type ReactNode } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useOpenAskAIPageInCommandMenu } from '@/command-menu/hooks/useOpenAskAIPageInCommandMenu';
|
||||
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
|
||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { CommandMenuPages } from 'twenty-shared/types';
|
||||
import { IconSparkles } from 'twenty-ui/display';
|
||||
|
|
@ -21,15 +20,13 @@ jest.mock('@/command-menu/hooks/useCommandMenu', () => ({
|
|||
}));
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</JotaiProvider>
|
||||
<JotaiProvider store={jotaiStore}>{children}</JotaiProvider>
|
||||
);
|
||||
|
||||
describe('useOpenAskAIPageInCommandMenu', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jotaiStore.set(isCommandMenuOpenedStateV2.atom, false);
|
||||
jotaiStore.set(isCommandMenuOpenedState.atom, false);
|
||||
});
|
||||
|
||||
it('should navigate to AskAI page with correct defaults', () => {
|
||||
|
|
@ -51,7 +48,7 @@ describe('useOpenAskAIPageInCommandMenu', () => {
|
|||
});
|
||||
|
||||
it('should use resetNavigationStack from argument when provided', () => {
|
||||
jotaiStore.set(isCommandMenuOpenedStateV2.atom, true);
|
||||
jotaiStore.set(isCommandMenuOpenedState.atom, true);
|
||||
|
||||
const { result } = renderHook(() => useOpenAskAIPageInCommandMenu(), {
|
||||
wrapper: Wrapper,
|
||||
|
|
@ -69,7 +66,7 @@ describe('useOpenAskAIPageInCommandMenu', () => {
|
|||
});
|
||||
|
||||
it('should default resetNavigationStack to isCommandMenuOpened', () => {
|
||||
jotaiStore.set(isCommandMenuOpenedStateV2.atom, true);
|
||||
jotaiStore.set(isCommandMenuOpenedState.atom, true);
|
||||
|
||||
const { result } = renderHook(() => useOpenAskAIPageInCommandMenu(), {
|
||||
wrapper: Wrapper,
|
||||
|
|
|
|||
|
|
@ -20,22 +20,21 @@ import { getPeopleRecordConnectionMock } from '~/testing/mock-data/people';
|
|||
import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems';
|
||||
import { contextStoreFilterGroupsComponentState } from '@/context-store/states/contextStoreFilterGroupsComponentState';
|
||||
|
||||
const mockCopyContextStoreStates = jest.fn();
|
||||
jest.mock(
|
||||
'@/command-menu/hooks/useCopyContextStoreAndActionMenuStates',
|
||||
() => ({
|
||||
useCopyContextStoreStates: () => ({
|
||||
copyContextStoreStates: mockCopyContextStoreStates,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
const personMockObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
)!;
|
||||
|
||||
const peopleMock = getPeopleRecordConnectionMock();
|
||||
|
||||
jotaiStore.set(
|
||||
recordStoreFamilyState.atomFamily(peopleMock[0].id),
|
||||
peopleMock[0],
|
||||
);
|
||||
jotaiStore.set(
|
||||
recordStoreFamilyState.atomFamily(peopleMock[1].id),
|
||||
peopleMock[1],
|
||||
);
|
||||
|
||||
const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
||||
apolloMocks: [],
|
||||
componentInstanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
||||
|
|
@ -48,16 +47,6 @@ const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
|
|||
},
|
||||
contextStoreNumberOfSelectedRecords: 2,
|
||||
contextStoreCurrentViewType: ContextStoreViewType.Table,
|
||||
onInitializeRecoilSnapshot: (_snapshot) => {
|
||||
jotaiStore.set(
|
||||
recordStoreFamilyState.atomFamily(peopleMock[0].id),
|
||||
peopleMock[0],
|
||||
);
|
||||
jotaiStore.set(
|
||||
recordStoreFamilyState.atomFamily(peopleMock[1].id),
|
||||
peopleMock[1],
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
describe('useSetGlobalCommandMenuContext', () => {
|
||||
|
|
@ -161,18 +150,41 @@ describe('useSetGlobalCommandMenuContext', () => {
|
|||
expect(hasUserSelectedCommandAfter).toBe(false);
|
||||
});
|
||||
|
||||
it('should call copyContextStoreStates with correct parameters', () => {
|
||||
const { result } = renderHook(() => useSetGlobalCommandMenuContext(), {
|
||||
wrapper,
|
||||
});
|
||||
it('should copy context store states to previous instance before resetting', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const { setGlobalCommandMenuContext } =
|
||||
useSetGlobalCommandMenuContext();
|
||||
|
||||
const previousTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
COMMAND_MENU_PREVIOUS_COMPONENT_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const previousNumberOfSelectedRecords = useAtomComponentStateValue(
|
||||
contextStoreNumberOfSelectedRecordsComponentState,
|
||||
COMMAND_MENU_PREVIOUS_COMPONENT_INSTANCE_ID,
|
||||
);
|
||||
|
||||
return {
|
||||
setGlobalCommandMenuContext,
|
||||
previousTargetedRecordsRule,
|
||||
previousNumberOfSelectedRecords,
|
||||
};
|
||||
},
|
||||
{
|
||||
wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setGlobalCommandMenuContext();
|
||||
});
|
||||
|
||||
expect(mockCopyContextStoreStates).toHaveBeenCalledWith({
|
||||
instanceIdToCopyFrom: COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
||||
instanceIdToCopyTo: COMMAND_MENU_PREVIOUS_COMPONENT_INSTANCE_ID,
|
||||
expect(result.current.previousTargetedRecordsRule).toEqual({
|
||||
mode: 'selection',
|
||||
selectedRecordIds: [peopleMock[0].id, peopleMock[1].id],
|
||||
});
|
||||
expect(result.current.previousNumberOfSelectedRecords).toBe(2);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
|||
import { renderHook } from '@testing-library/react';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { act } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { CommandMenuPages } from 'twenty-shared/types';
|
||||
import { IconArrowDown, IconDotsVertical } from 'twenty-ui/display';
|
||||
|
||||
|
|
@ -25,9 +24,7 @@ const mockedNavigationStack = [
|
|||
];
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</JotaiProvider>
|
||||
<JotaiProvider store={jotaiStore}>{children}</JotaiProvider>
|
||||
);
|
||||
|
||||
describe('useUpdateCommandMenuPageInfo', () => {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { SIDE_PANEL_FOCUS_ID } from '@/command-menu/constants/SidePanelFocusId';
|
|||
import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu';
|
||||
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
|
||||
import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState';
|
||||
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
|
||||
import { addToNavPayloadRegistryStateV2 } from '@/navigation-menu-item/states/addToNavPayloadRegistryStateV2';
|
||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||
import { addToNavPayloadRegistryState } from '@/navigation-menu-item/states/addToNavPayloadRegistryState';
|
||||
import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown';
|
||||
import { emitSidePanelOpenEvent } from '@/ui/layout/right-drawer/utils/emitSidePanelOpenEvent';
|
||||
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
|
||||
|
|
@ -22,11 +22,11 @@ export const useCommandMenu = () => {
|
|||
useRemoveFocusItemFromFocusStackById();
|
||||
|
||||
const closeCommandMenu = useCallback(() => {
|
||||
const isCommandMenuOpened = store.get(isCommandMenuOpenedStateV2.atom);
|
||||
const isCommandMenuOpened = store.get(isCommandMenuOpenedState.atom);
|
||||
|
||||
if (isCommandMenuOpened) {
|
||||
store.set(addToNavPayloadRegistryStateV2.atom, new Map());
|
||||
store.set(isCommandMenuOpenedStateV2.atom, false);
|
||||
store.set(addToNavPayloadRegistryState.atom, new Map());
|
||||
store.set(isCommandMenuOpenedState.atom, false);
|
||||
store.set(isCommandMenuClosingState.atom, true);
|
||||
closeAnyOpenDropdown();
|
||||
removeFocusItemFromFocusStackById({
|
||||
|
|
@ -47,7 +47,7 @@ export const useCommandMenu = () => {
|
|||
}, [closeAnyOpenDropdown, navigateCommandMenu]);
|
||||
|
||||
const toggleCommandMenu = useCallback(() => {
|
||||
const isCommandMenuOpened = store.get(isCommandMenuOpenedStateV2.atom);
|
||||
const isCommandMenuOpened = store.get(isCommandMenuOpenedState.atom);
|
||||
|
||||
store.set(commandMenuSearchState.atom, '');
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState
|
|||
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
|
||||
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
|
||||
import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState';
|
||||
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
|
||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
|
|
@ -97,7 +97,7 @@ export const useCommandMenuCloseAnimationCompleteCleanup = () => {
|
|||
Icon: undefined,
|
||||
instanceId: '',
|
||||
});
|
||||
store.set(isCommandMenuOpenedStateV2.atom, false);
|
||||
store.set(isCommandMenuOpenedState.atom, false);
|
||||
store.set(commandMenuSearchState.atom, '');
|
||||
store.set(commandMenuNavigationMorphItemsByPageState.atom, new Map());
|
||||
store.set(commandMenuNavigationStackState.atom, []);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { commandMenuNavigationStackState } from '@/command-menu/states/commandMe
|
|||
import { CommandMenuPages } from 'twenty-shared/types';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { recordStoreIdentifiersFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreIdentifiersSelector';
|
||||
import { recordStoreRecordsSelectorV2 } from '@/object-record/record-store/states/selectors/recordStoreRecordsSelectorV2';
|
||||
import { recordStoreRecordsSelector } from '@/object-record/record-store/states/selectors/recordStoreRecordsSelector';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useMemo } from 'react';
|
||||
|
|
@ -54,7 +54,7 @@ export const useCommandMenuContextChips = () => {
|
|||
allowRequestsToTwentyIcons,
|
||||
},
|
||||
);
|
||||
const records = useAtomFamilySelectorValue(recordStoreRecordsSelectorV2, {
|
||||
const records = useAtomFamilySelectorValue(recordStoreRecordsSelector, {
|
||||
recordIds: allRecordIds,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState
|
|||
import { commandMenuShouldFocusTitleInputComponentState } from '@/command-menu/states/commandMenuShouldFocusTitleInputComponentState';
|
||||
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
|
||||
import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState';
|
||||
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
|
||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
|
|
@ -37,7 +37,7 @@ export const useNavigateCommandMenu = () => {
|
|||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
|
||||
const openCommandMenu = useCallback(() => {
|
||||
const isCommandMenuOpened = store.get(isCommandMenuOpenedStateV2.atom);
|
||||
const isCommandMenuOpened = store.get(isCommandMenuOpenedState.atom);
|
||||
|
||||
const isCommandMenuClosing = store.get(isCommandMenuClosingState.atom);
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ export const useNavigateCommandMenu = () => {
|
|||
instanceIdToCopyTo: COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
||||
});
|
||||
|
||||
store.set(isCommandMenuOpenedStateV2.atom, true);
|
||||
store.set(isCommandMenuOpenedState.atom, true);
|
||||
store.set(hasUserSelectedCommandState.atom, false);
|
||||
}, [
|
||||
copyContextStoreStates,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
|
||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useCallback } from 'react';
|
||||
|
|
@ -9,7 +9,7 @@ import { v4 } from 'uuid';
|
|||
|
||||
export const useOpenAskAIPageInCommandMenu = () => {
|
||||
const { navigateCommandMenu } = useCommandMenu();
|
||||
const isCommandMenuOpened = useAtomStateValue(isCommandMenuOpenedStateV2);
|
||||
const isCommandMenuOpened = useAtomStateValue(isCommandMenuOpenedState);
|
||||
|
||||
const openAskAIPage = useCallback(
|
||||
({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
|
||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { CommandMenuPages } from 'twenty-shared/types';
|
||||
|
|
@ -8,7 +8,7 @@ import { v4 } from 'uuid';
|
|||
|
||||
export const useOpenRecordsSearchPageInCommandMenu = () => {
|
||||
const { navigateCommandMenu } = useCommandMenu();
|
||||
const isCommandMenuOpened = useAtomStateValue(isCommandMenuOpenedStateV2);
|
||||
const isCommandMenuOpened = useAtomStateValue(isCommandMenuOpenedState);
|
||||
|
||||
const openRecordsSearchPage = () => {
|
||||
navigateCommandMenu({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { viewableRichTextComponentStateV2 } from '@/command-menu/pages/rich-text-page/states/viewableRichTextComponentStateV2';
|
||||
import { viewableRichTextComponentState } from '@/command-menu/pages/rich-text-page/states/viewableRichTextComponentState';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
|
|
@ -19,7 +19,7 @@ export const useRichTextCommandMenu = () => {
|
|||
activityId: string;
|
||||
activityObjectNameSingular: string;
|
||||
}) => {
|
||||
store.set(viewableRichTextComponentStateV2.atom, {
|
||||
store.set(viewableRichTextComponentState.atom, {
|
||||
activityId,
|
||||
activityObjectNameSingular,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,81 +1,180 @@
|
|||
import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId';
|
||||
import { COMMAND_MENU_PREVIOUS_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuPreviousComponentInstanceId';
|
||||
import { useCopyContextStoreStates } from '@/command-menu/hooks/useCopyContextStoreAndActionMenuStates';
|
||||
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState';
|
||||
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
|
||||
import { contextStoreAnyFieldFilterValueComponentState } from '@/context-store/states/contextStoreAnyFieldFilterValueComponentState';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
|
||||
import { contextStoreFilterGroupsComponentState } from '@/context-store/states/contextStoreFilterGroupsComponentState';
|
||||
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
|
||||
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
|
||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
|
||||
import { atom, useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { useStore } from 'jotai';
|
||||
|
||||
export const useSetGlobalCommandMenuContext = () => {
|
||||
const store = useStore();
|
||||
const { copyContextStoreStates } = useCopyContextStoreStates();
|
||||
|
||||
const setGlobalCommandMenuContext = useCallback(() => {
|
||||
copyContextStoreStates({
|
||||
instanceIdToCopyFrom: COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
||||
instanceIdToCopyTo: COMMAND_MENU_PREVIOUS_COMPONENT_INSTANCE_ID,
|
||||
});
|
||||
|
||||
store.set(
|
||||
contextStoreTargetedRecordsRuleComponentState.atomFamily({
|
||||
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
||||
atom(null, (get, batchSet) => {
|
||||
const fromId = COMMAND_MENU_COMPONENT_INSTANCE_ID;
|
||||
const toId = COMMAND_MENU_PREVIOUS_COMPONENT_INSTANCE_ID;
|
||||
|
||||
batchSet(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
|
||||
instanceId: toId,
|
||||
}),
|
||||
get(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
|
||||
instanceId: fromId,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
batchSet(
|
||||
contextStoreTargetedRecordsRuleComponentState.atomFamily({
|
||||
instanceId: toId,
|
||||
}),
|
||||
get(
|
||||
contextStoreTargetedRecordsRuleComponentState.atomFamily({
|
||||
instanceId: fromId,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
batchSet(
|
||||
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
|
||||
instanceId: toId,
|
||||
}),
|
||||
get(
|
||||
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
|
||||
instanceId: fromId,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
batchSet(
|
||||
contextStoreFiltersComponentState.atomFamily({
|
||||
instanceId: toId,
|
||||
}),
|
||||
get(
|
||||
contextStoreFiltersComponentState.atomFamily({
|
||||
instanceId: fromId,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
batchSet(
|
||||
contextStoreFilterGroupsComponentState.atomFamily({
|
||||
instanceId: toId,
|
||||
}),
|
||||
get(
|
||||
contextStoreFilterGroupsComponentState.atomFamily({
|
||||
instanceId: fromId,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
batchSet(
|
||||
contextStoreAnyFieldFilterValueComponentState.atomFamily({
|
||||
instanceId: toId,
|
||||
}),
|
||||
get(
|
||||
contextStoreAnyFieldFilterValueComponentState.atomFamily({
|
||||
instanceId: fromId,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
batchSet(
|
||||
contextStoreCurrentViewIdComponentState.atomFamily({
|
||||
instanceId: toId,
|
||||
}),
|
||||
get(
|
||||
contextStoreCurrentViewIdComponentState.atomFamily({
|
||||
instanceId: fromId,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
batchSet(
|
||||
contextStoreCurrentViewTypeComponentState.atomFamily({
|
||||
instanceId: toId,
|
||||
}),
|
||||
get(
|
||||
contextStoreCurrentViewTypeComponentState.atomFamily({
|
||||
instanceId: fromId,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
batchSet(
|
||||
contextStoreIsPageInEditModeComponentState.atomFamily({
|
||||
instanceId: toId,
|
||||
}),
|
||||
get(
|
||||
contextStoreIsPageInEditModeComponentState.atomFamily({
|
||||
instanceId: fromId,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
batchSet(
|
||||
contextStoreTargetedRecordsRuleComponentState.atomFamily({
|
||||
instanceId: fromId,
|
||||
}),
|
||||
{ mode: 'selection', selectedRecordIds: [] },
|
||||
);
|
||||
|
||||
batchSet(
|
||||
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
|
||||
instanceId: fromId,
|
||||
}),
|
||||
0,
|
||||
);
|
||||
|
||||
batchSet(
|
||||
contextStoreFiltersComponentState.atomFamily({
|
||||
instanceId: fromId,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
batchSet(
|
||||
contextStoreFilterGroupsComponentState.atomFamily({
|
||||
instanceId: fromId,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
batchSet(
|
||||
contextStoreAnyFieldFilterValueComponentState.atomFamily({
|
||||
instanceId: fromId,
|
||||
}),
|
||||
'',
|
||||
);
|
||||
|
||||
batchSet(
|
||||
contextStoreCurrentViewTypeComponentState.atomFamily({
|
||||
instanceId: fromId,
|
||||
}),
|
||||
ContextStoreViewType.Table,
|
||||
);
|
||||
|
||||
batchSet(commandMenuPageInfoState.atom, {
|
||||
title: undefined,
|
||||
Icon: undefined,
|
||||
instanceId: '',
|
||||
});
|
||||
|
||||
batchSet(hasUserSelectedCommandState.atom, false);
|
||||
}),
|
||||
{
|
||||
mode: 'selection',
|
||||
selectedRecordIds: [],
|
||||
},
|
||||
);
|
||||
|
||||
store.set(
|
||||
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
|
||||
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
||||
}),
|
||||
0,
|
||||
);
|
||||
|
||||
store.set(
|
||||
contextStoreFiltersComponentState.atomFamily({
|
||||
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
store.set(
|
||||
contextStoreFilterGroupsComponentState.atomFamily({
|
||||
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
store.set(
|
||||
contextStoreAnyFieldFilterValueComponentState.atomFamily({
|
||||
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
||||
}),
|
||||
'',
|
||||
);
|
||||
|
||||
store.set(
|
||||
contextStoreCurrentViewTypeComponentState.atomFamily({
|
||||
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
||||
}),
|
||||
ContextStoreViewType.Table,
|
||||
);
|
||||
|
||||
store.set(commandMenuPageInfoState.atom, {
|
||||
title: undefined,
|
||||
Icon: undefined,
|
||||
instanceId: '',
|
||||
});
|
||||
|
||||
store.set(hasUserSelectedCommandState.atom, false);
|
||||
}, [copyContextStoreStates, store]);
|
||||
}, [store]);
|
||||
|
||||
return {
|
||||
setGlobalCommandMenuContext,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { useSelectedNavigationMenuItemEditItem } from '@/navigation-menu-item/ho
|
|||
import { useSelectedNavigationMenuItemEditItemLabel } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItemLabel';
|
||||
import { useSelectedNavigationMenuItemEditItemObjectMetadata } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItemObjectMetadata';
|
||||
import { useUpdateLinkInDraft } from '@/navigation-menu-item/hooks/useUpdateLinkInDraft';
|
||||
import { selectedNavigationMenuItemInEditModeStateV2 } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeStateV2';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useState } from 'react';
|
||||
|
|
@ -31,7 +31,7 @@ export const CommandMenuNavigationMenuItemEditPage = () => {
|
|||
const { t } = useLingui();
|
||||
|
||||
const selectedNavigationMenuItemInEditMode = useAtomStateValue(
|
||||
selectedNavigationMenuItemInEditModeStateV2,
|
||||
selectedNavigationMenuItemInEditModeState,
|
||||
);
|
||||
const { selectedItemLabel } = useSelectedNavigationMenuItemEditItemLabel();
|
||||
const { selectedItem } = useSelectedNavigationMenuItemEditItem();
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue