Refactor/context and scopes (#1602)
* Put onImport in a context * Refactored RecoilScopeContexts * Refactored naming * Fix tests --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,12 +1,13 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { BoardContext } from '@/companies/states/contexts/BoardContext';
|
||||
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { ViewBar, type ViewBarProps } from '@/ui/view-bar/components/ViewBar';
|
||||
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
import { ViewBar } from '@/ui/view-bar/components/ViewBar';
|
||||
import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext';
|
||||
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
|
||||
|
||||
@ -22,30 +23,33 @@ import { BoardOptionsDropdown } from './BoardOptionsDropdown';
|
||||
export type BoardHeaderProps = {
|
||||
className?: string;
|
||||
onStageAdd?: (boardColumn: BoardColumnDefinition) => void;
|
||||
} & Pick<ViewBarProps, 'scopeContext'>;
|
||||
};
|
||||
|
||||
export function BoardHeader({
|
||||
className,
|
||||
onStageAdd,
|
||||
scopeContext,
|
||||
}: BoardHeaderProps) {
|
||||
export function BoardHeader({ className, onStageAdd }: BoardHeaderProps) {
|
||||
const { onCurrentViewSubmit, ...viewBarContextProps } =
|
||||
useContext(ViewBarContext);
|
||||
const tableScopeId = useContextScopeId(scopeContext);
|
||||
|
||||
const BoardRecoilScopeContext =
|
||||
useContext(BoardContext).BoardRecoilScopeContext;
|
||||
|
||||
const ViewBarRecoilScopeContext =
|
||||
useContext(ViewBarContext).ViewBarRecoilScopeContext;
|
||||
|
||||
const boardRecoilScopeId = useRecoilScopeId(BoardRecoilScopeContext);
|
||||
|
||||
const currentViewId = useRecoilScopedValue(
|
||||
currentViewIdScopedState,
|
||||
scopeContext,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
const canPersistBoardCardFields = useRecoilValue(
|
||||
canPersistBoardCardFieldsScopedFamilySelector([
|
||||
tableScopeId,
|
||||
currentViewId,
|
||||
]),
|
||||
canPersistBoardCardFieldsScopedFamilySelector({
|
||||
recoilScopeId: boardRecoilScopeId,
|
||||
viewId: currentViewId,
|
||||
}),
|
||||
);
|
||||
const [boardCardFields, setBoardCardFields] = useRecoilScopedState(
|
||||
boardCardFieldsScopedState,
|
||||
scopeContext,
|
||||
BoardRecoilScopeContext,
|
||||
);
|
||||
const [savedBoardCardFields, setSavedBoardCardFields] = useRecoilState(
|
||||
savedBoardCardFieldsFamilyState(currentViewId),
|
||||
@ -59,9 +63,12 @@ export function BoardHeader({
|
||||
const savedBoardCardFields = await snapshot.getPromise(
|
||||
savedBoardCardFieldsFamilyState(viewId),
|
||||
);
|
||||
set(boardCardFieldsScopedState(tableScopeId), savedBoardCardFields);
|
||||
set(
|
||||
boardCardFieldsScopedState(boardRecoilScopeId),
|
||||
savedBoardCardFields,
|
||||
);
|
||||
},
|
||||
[tableScopeId],
|
||||
[boardRecoilScopeId],
|
||||
);
|
||||
|
||||
const handleCurrentViewSubmit = async () => {
|
||||
@ -73,7 +80,7 @@ export function BoardHeader({
|
||||
};
|
||||
|
||||
return (
|
||||
<RecoilScope SpecificContext={DropdownRecoilScopeContext}>
|
||||
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
...viewBarContextProps,
|
||||
@ -89,11 +96,9 @@ export function BoardHeader({
|
||||
<BoardOptionsDropdown
|
||||
customHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
|
||||
onStageAdd={onStageAdd}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
}
|
||||
optionsDropdownKey={BoardOptionsDropdownKey}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
</RecoilScope>
|
||||
|
||||
@ -10,13 +10,12 @@ import {
|
||||
|
||||
type BoardOptionsDropdownProps = Pick<
|
||||
BoardOptionsDropdownContentProps,
|
||||
'customHotkeyScope' | 'onStageAdd' | 'scopeContext'
|
||||
'customHotkeyScope' | 'onStageAdd'
|
||||
>;
|
||||
|
||||
export function BoardOptionsDropdown({
|
||||
customHotkeyScope,
|
||||
onStageAdd,
|
||||
scopeContext,
|
||||
}: BoardOptionsDropdownProps) {
|
||||
return (
|
||||
<DropdownButton
|
||||
@ -25,7 +24,6 @@ export function BoardOptionsDropdown({
|
||||
<BoardOptionsDropdownContent
|
||||
customHotkeyScope={customHotkeyScope}
|
||||
onStageAdd={onStageAdd}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
}
|
||||
dropdownHotkeyScope={customHotkeyScope}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { type Context, useRef, useState } from 'react';
|
||||
import { useContext, useRef, useState } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { BoardContext } from '@/companies/states/contexts/BoardContext';
|
||||
import { DropdownMenuHeader } from '@/ui/dropdown/components/DropdownMenuHeader';
|
||||
import { DropdownMenuInput } from '@/ui/dropdown/components/DropdownMenuInput';
|
||||
import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu';
|
||||
@ -23,8 +24,8 @@ import { MenuItemNavigate } from '@/ui/menu-item/components/MenuItemNavigate';
|
||||
import { ThemeColor } from '@/ui/theme/constants/colors';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/ui/view-bar/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { useUpsertView } from '@/ui/view-bar/hooks/useUpsertView';
|
||||
import { viewsByIdScopedSelector } from '@/ui/view-bar/states/selectors/viewsByIdScopedSelector';
|
||||
@ -42,7 +43,6 @@ import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey';
|
||||
export type BoardOptionsDropdownContentProps = {
|
||||
customHotkeyScope: HotkeyScope;
|
||||
onStageAdd?: (boardColumn: BoardColumnDefinition) => void;
|
||||
scopeContext: Context<string | null>;
|
||||
};
|
||||
|
||||
const StyledIconSettings = styled(IconSettings)`
|
||||
@ -61,10 +61,13 @@ type ColumnForCreate = {
|
||||
export function BoardOptionsDropdownContent({
|
||||
customHotkeyScope,
|
||||
onStageAdd,
|
||||
scopeContext,
|
||||
}: BoardOptionsDropdownContentProps) {
|
||||
const theme = useTheme();
|
||||
const scopeId = useContextScopeId(scopeContext);
|
||||
|
||||
const BoardRecoilScopeContext =
|
||||
useContext(BoardContext).BoardRecoilScopeContext;
|
||||
|
||||
const boardRecoilScopeId = useRecoilScopeId(BoardRecoilScopeContext);
|
||||
|
||||
const stageInputRef = useRef<HTMLInputElement>(null);
|
||||
const viewEditInputRef = useRef<HTMLInputElement>(null);
|
||||
@ -77,16 +80,19 @@ export function BoardOptionsDropdownContent({
|
||||
|
||||
const hiddenBoardCardFields = useRecoilScopedValue(
|
||||
hiddenBoardCardFieldsScopedSelector,
|
||||
scopeContext,
|
||||
BoardRecoilScopeContext,
|
||||
);
|
||||
const hasHiddenFields = hiddenBoardCardFields.length > 0;
|
||||
const visibleBoardCardFields = useRecoilScopedValue(
|
||||
visibleBoardCardFieldsScopedSelector,
|
||||
scopeContext,
|
||||
BoardRecoilScopeContext,
|
||||
);
|
||||
const hasVisibleFields = visibleBoardCardFields.length > 0;
|
||||
|
||||
const viewsById = useRecoilScopedValue(viewsByIdScopedSelector, scopeContext);
|
||||
const viewsById = useRecoilScopedValue(
|
||||
viewsByIdScopedSelector,
|
||||
BoardRecoilScopeContext, // TODO: replace with ViewBarRecoilScopeContext
|
||||
);
|
||||
const viewEditMode = useRecoilValue(viewEditModeState);
|
||||
|
||||
const handleStageSubmit = () => {
|
||||
@ -107,13 +113,13 @@ export function BoardOptionsDropdownContent({
|
||||
onStageAdd?.(columnToCreate);
|
||||
};
|
||||
|
||||
const { upsertView } = useUpsertView({ scopeContext });
|
||||
const { upsertView } = useUpsertView();
|
||||
|
||||
const handleViewNameSubmit = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async () => {
|
||||
const boardCardFields = await snapshot.getPromise(
|
||||
boardCardFieldsScopedState(scopeId),
|
||||
boardCardFieldsScopedState(boardRecoilScopeId),
|
||||
);
|
||||
const isCreateMode = viewEditMode.mode === 'create';
|
||||
const name = viewEditInputRef.current?.value;
|
||||
@ -123,7 +129,7 @@ export function BoardOptionsDropdownContent({
|
||||
set(savedBoardCardFieldsFamilyState(view.id), boardCardFields);
|
||||
}
|
||||
},
|
||||
[scopeId, upsertView, viewEditMode.mode],
|
||||
[boardRecoilScopeId, upsertView, viewEditMode.mode],
|
||||
);
|
||||
|
||||
const resetMenu = () => setCurrentMenu(undefined);
|
||||
@ -133,7 +139,7 @@ export function BoardOptionsDropdownContent({
|
||||
setCurrentMenu(menu);
|
||||
};
|
||||
|
||||
const { handleFieldVisibilityChange } = useBoardCardFields({ scopeContext });
|
||||
const { handleFieldVisibilityChange } = useBoardCardFields();
|
||||
|
||||
const { closeDropdownButton } = useDropdownButton({
|
||||
dropdownId: BoardOptionsDropdownKey,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type Context, useCallback, useRef } from 'react';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import styled from '@emotion/styled';
|
||||
import { DragDropContext, OnDragEndResponder } from '@hello-pangea/dnd'; // Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350
|
||||
@ -35,7 +35,6 @@ export type EntityBoardProps = {
|
||||
onColumnAdd?: (boardColumn: BoardColumnDefinition) => void;
|
||||
onColumnDelete?: (boardColumnId: string) => void;
|
||||
onEditColumnTitle: (columnId: string, title: string, color: string) => void;
|
||||
scopeContext: Context<string | null>;
|
||||
};
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
@ -54,7 +53,6 @@ export function EntityBoard({
|
||||
onColumnAdd,
|
||||
onColumnDelete,
|
||||
onEditColumnTitle,
|
||||
scopeContext,
|
||||
}: EntityBoardProps) {
|
||||
const [boardColumns] = useRecoilState(boardColumnsState);
|
||||
const setCardSelected = useSetCardSelected();
|
||||
@ -130,14 +128,14 @@ export function EntityBoard({
|
||||
|
||||
return (boardColumns?.length ?? 0) > 0 ? (
|
||||
<StyledWrapper>
|
||||
<StyledBoardHeader onStageAdd={onColumnAdd} scopeContext={scopeContext} />
|
||||
<StyledBoardHeader onStageAdd={onColumnAdd} />
|
||||
<ScrollWrapper>
|
||||
<StyledBoard ref={boardRef}>
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
{sortedBoardColumns.map((column) => (
|
||||
<BoardColumnIdContext.Provider value={column.id} key={column.id}>
|
||||
<RecoilScope
|
||||
SpecificContext={BoardColumnRecoilScopeContext}
|
||||
CustomRecoilScopeContext={BoardColumnRecoilScopeContext}
|
||||
key={column.id}
|
||||
>
|
||||
<EntityBoardColumn
|
||||
@ -145,7 +143,6 @@ export function EntityBoard({
|
||||
column={column}
|
||||
onDelete={onColumnDelete}
|
||||
onTitleEdit={onEditColumnTitle}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
</RecoilScope>
|
||||
</BoardColumnIdContext.Provider>
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import type { Context } from 'react';
|
||||
import { Draggable } from '@hello-pangea/dnd';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
@ -12,12 +11,10 @@ export function EntityBoardCard({
|
||||
boardOptions,
|
||||
cardId,
|
||||
index,
|
||||
scopeContext,
|
||||
}: {
|
||||
boardOptions: BoardOptions;
|
||||
cardId: string;
|
||||
index: number;
|
||||
scopeContext: Context<string | null>;
|
||||
}) {
|
||||
const setContextMenuPosition = useSetRecoilState(contextMenuPositionState);
|
||||
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
|
||||
@ -46,7 +43,7 @@ export function EntityBoardCard({
|
||||
data-select-disable
|
||||
onContextMenu={handleContextMenu}
|
||||
>
|
||||
{<boardOptions.CardComponent scopeContext={scopeContext} />}
|
||||
{<boardOptions.CardComponent />}
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type Context, useContext } from 'react';
|
||||
import { useContext } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Draggable, Droppable, DroppableProvided } from '@hello-pangea/dnd';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
@ -52,13 +52,11 @@ export function EntityBoardColumn({
|
||||
column,
|
||||
onDelete,
|
||||
onTitleEdit,
|
||||
scopeContext,
|
||||
}: {
|
||||
boardOptions: BoardOptions;
|
||||
column: BoardColumnDefinition;
|
||||
onDelete?: (columnId: string) => void;
|
||||
onTitleEdit: (columnId: string, title: string, color: string) => void;
|
||||
scopeContext: Context<string | null>;
|
||||
}) {
|
||||
const boardColumnId = useContext(BoardColumnIdContext) ?? '';
|
||||
|
||||
@ -94,7 +92,6 @@ export function EntityBoardColumn({
|
||||
index={index}
|
||||
cardId={cardId}
|
||||
boardOptions={boardOptions}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
</BoardCardIdContext.Provider>
|
||||
))}
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import type { Context } from 'react';
|
||||
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
@ -10,18 +8,18 @@ import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoi
|
||||
import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState';
|
||||
import { boardCardFieldsByKeyScopedSelector } from '../states/selectors/boardCardFieldsByKeyScopedSelector';
|
||||
|
||||
export const useBoardCardFields = ({
|
||||
scopeContext,
|
||||
}: {
|
||||
scopeContext: Context<string | null>;
|
||||
}) => {
|
||||
import { useBoardContext } from './useBoardContext';
|
||||
|
||||
export const useBoardCardFields = () => {
|
||||
const { BoardRecoilScopeContext } = useBoardContext();
|
||||
|
||||
const [boardCardFields, setBoardCardFields] = useRecoilScopedState(
|
||||
boardCardFieldsScopedState,
|
||||
scopeContext,
|
||||
BoardRecoilScopeContext,
|
||||
);
|
||||
const boardCardFieldsByKey = useRecoilScopedValue(
|
||||
boardCardFieldsByKeyScopedSelector,
|
||||
scopeContext,
|
||||
BoardRecoilScopeContext,
|
||||
);
|
||||
|
||||
const handleFieldVisibilityChange = (
|
||||
|
||||
7
front/src/modules/ui/board/hooks/useBoardContext.ts
Normal file
7
front/src/modules/ui/board/hooks/useBoardContext.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { BoardContext } from '@/companies/states/contexts/BoardContext';
|
||||
|
||||
export const useBoardContext = () => {
|
||||
return useContext(BoardContext);
|
||||
};
|
||||
@ -8,10 +8,16 @@ import { savedBoardCardFieldsFamilyState } from '../savedBoardCardFieldsFamilySt
|
||||
export const canPersistBoardCardFieldsScopedFamilySelector = selectorFamily({
|
||||
key: 'canPersistBoardCardFieldsScopedFamilySelector',
|
||||
get:
|
||||
([scopeId, viewId]: [string, string | undefined]) =>
|
||||
({
|
||||
recoilScopeId,
|
||||
viewId,
|
||||
}: {
|
||||
recoilScopeId: string;
|
||||
viewId: string | undefined;
|
||||
}) =>
|
||||
({ get }) =>
|
||||
!isDeeplyEqual(
|
||||
get(savedBoardCardFieldsFamilyState(viewId)),
|
||||
get(boardCardFieldsScopedState(scopeId)),
|
||||
get(boardCardFieldsScopedState(recoilScopeId)),
|
||||
),
|
||||
});
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { ComponentType, Context } from 'react';
|
||||
import type { ComponentType } from 'react';
|
||||
|
||||
import { FilterDefinitionByEntity } from '@/ui/view-bar/types/FilterDefinitionByEntity';
|
||||
import { SortDefinition } from '@/ui/view-bar/types/SortDefinition';
|
||||
@ -6,7 +6,7 @@ import { PipelineProgress } from '~/generated/graphql';
|
||||
|
||||
export type BoardOptions = {
|
||||
newCardComponent: React.ReactNode;
|
||||
CardComponent: ComponentType<{ scopeContext: Context<string | null> }>;
|
||||
CardComponent: ComponentType;
|
||||
filters: FilterDefinitionByEntity<PipelineProgress>[];
|
||||
sorts: SortDefinition[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user