Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Extract the code for building categorize operator coordinates to hooks.ts #1739 #2005

Merged
merged 1 commit into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 12 additions & 60 deletions web/src/pages/flow/canvas/node/categorize-node.tsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,21 @@
import { useTranslate } from '@/hooks/common-hooks';
import { Flex } from 'antd';
import classNames from 'classnames';
import { pick } from 'lodash';
import get from 'lodash/get';
import intersectionWith from 'lodash/intersectionWith';
import isEqual from 'lodash/isEqual';
import lowerFirst from 'lodash/lowerFirst';
import { useEffect, useMemo, useState } from 'react';
import { Handle, NodeProps, Position, useUpdateNodeInternals } from 'reactflow';
import { Handle, NodeProps, Position } from 'reactflow';
import { Operator, operatorMap } from '../../constant';
import { IPosition, NodeData } from '../../interface';
import { NodeData } from '../../interface';
import OperatorIcon from '../../operator-icon';
import { buildNewPositionMap } from '../../utils';
import CategorizeHandle from './categorize-handle';
import NodeDropdown from './dropdown';
import { useBuildCategorizeHandlePositions } from './hooks';
import styles from './index.less';
import NodePopover from './popover';

export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
const updateNodeInternals = useUpdateNodeInternals();
const [postionMap, setPositionMap] = useState<Record<string, IPosition>>({});
const categoryData = useMemo(
() => get(data, 'form.category_description') ?? {},
[data],
);
const style = operatorMap[data.label as Operator];
const { t } = useTranslate('flow');

useEffect(() => {
// Cache used coordinates
setPositionMap((state) => {
// index in use
const indexesInUse = Object.values(state).map((x) => x.idx);
const categoryDataKeys = Object.keys(categoryData);
const stateKeys = Object.keys(state);
if (!isEqual(categoryDataKeys.sort(), stateKeys.sort())) {
const intersectionKeys = intersectionWith(
stateKeys,
categoryDataKeys,
(categoryDataKey, postionMapKey) => categoryDataKey === postionMapKey,
);
const newPositionMap = buildNewPositionMap(
categoryDataKeys.filter(
(x) => !intersectionKeys.some((y) => y === x),
),
indexesInUse,
);

const nextPostionMap = {
...pick(state, intersectionKeys),
...newPositionMap,
};

return nextPostionMap;
}
return state;
});
}, [categoryData]);

useEffect(() => {
updateNodeInternals(id);
}, [id, updateNodeInternals, postionMap]);
const { positions } = useBuildCategorizeHandlePositions({ data, id });

return (
<NodePopover nodeId={id}>
Expand Down Expand Up @@ -94,18 +49,15 @@ export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
className={styles.handle}
id={'c'}
></Handle>
{Object.keys(categoryData).map((x, idx) => {
const position = postionMap[x];
{positions.map((position, idx) => {
return (
position && (
<CategorizeHandle
top={position.top}
right={position.right}
key={idx}
text={x}
idx={idx}
></CategorizeHandle>
)
<CategorizeHandle
top={position.top}
right={position.right}
key={idx}
text={position.text}
idx={idx}
></CategorizeHandle>
);
})}
<Flex vertical align="center" justify="center" gap={6}>
Expand Down
77 changes: 77 additions & 0 deletions web/src/pages/flow/canvas/node/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import get from 'lodash/get';
import intersectionWith from 'lodash/intersectionWith';
import isEqual from 'lodash/isEqual';
import pick from 'lodash/pick';
import { useEffect, useMemo, useState } from 'react';
import { useUpdateNodeInternals } from 'reactflow';
import { IPosition, NodeData } from '../../interface';
import { buildNewPositionMap } from '../../utils';

export const useBuildCategorizeHandlePositions = ({
data,
id,
}: {
id: string;
data: NodeData;
}) => {
const updateNodeInternals = useUpdateNodeInternals();
const [positionMap, setPositionMap] = useState<Record<string, IPosition>>({});
const categoryData = useMemo(
() => get(data, 'form.category_description') ?? {},
[data],
);

const positions = useMemo(() => {
return Object.keys(categoryData)
.map((x) => {
const position = positionMap[x];
return { text: x, ...position };
})
.filter((x) => typeof x?.right === 'number');
}, [categoryData, positionMap]);

useEffect(() => {
// Cache used coordinates
setPositionMap((state) => {
// index in use
const indexesInUse = Object.values(state).map((x) => x.idx);
const categoryDataKeys = Object.keys(categoryData);
const stateKeys = Object.keys(state);
if (!isEqual(categoryDataKeys.sort(), stateKeys.sort())) {
const intersectionKeys = intersectionWith(
stateKeys,
categoryDataKeys,
(categoryDataKey, postionMapKey) => categoryDataKey === postionMapKey,
);
const newPositionMap = buildNewPositionMap(
categoryDataKeys.filter(
(x) => !intersectionKeys.some((y) => y === x),
),
indexesInUse,
);

const nextPositionMap = {
...pick(state, intersectionKeys),
...newPositionMap,
};

return nextPositionMap;
}
return state;
});
}, [categoryData]);

useEffect(() => {
updateNodeInternals(id);
}, [id, updateNodeInternals, positionMap]);

return { positions };
};

export const useBuildSwitchHandlePositions = ({
data,
id,
}: {
id: string;
data: NodeData;
}) => {};
2 changes: 1 addition & 1 deletion web/src/pages/flow/constant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ export const NodeMap = {
[Operator.BaiduFanyi]: 'ragNode',
[Operator.QWeather]: 'ragNode',
[Operator.ExeSQL]: 'ragNode',
[Operator.Switch]: 'logicNode',
[Operator.Switch]: 'categorizeNode',
};

export const LanguageOptions = [
Expand Down
6 changes: 4 additions & 2 deletions web/src/pages/flow/form-hooks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useTranslate } from '@/hooks/common-hooks';
import { useCallback, useMemo } from 'react';
import { Operator } from './constant';
import { Operator, RestrictedUpstreamMap } from './constant';
import useGraphStore from './store';

const ExcludedNodesMap = {
Expand All @@ -13,6 +13,7 @@ const ExcludedNodesMap = {
],
[Operator.Relevant]: [Operator.Begin, Operator.Answer, Operator.Relevant],
[Operator.Generate]: [Operator.Begin],
[Operator.Switch]: [Operator.Begin],
};

export const useBuildFormSelectOptions = (
Expand All @@ -23,7 +24,8 @@ export const useBuildFormSelectOptions = (

const buildCategorizeToOptions = useCallback(
(toList: string[]) => {
const excludedNodes: Operator[] = ExcludedNodesMap[operatorName] ?? [];
const excludedNodes: Operator[] =
RestrictedUpstreamMap[operatorName] ?? [];
return nodes
.filter(
(x) =>
Expand Down
1 change: 0 additions & 1 deletion web/src/pages/flow/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ const useGraphStore = create<RFState>()(
]);
}
},

addNode: (node: Node) => {
set({ nodes: get().nodes.concat(node) });
},
Expand Down
19 changes: 14 additions & 5 deletions web/src/pages/flow/switch-form/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { CloseOutlined } from '@ant-design/icons';
import { Button, Card, Form, Input, Typography } from 'antd';
import { Button, Card, Form, Input, Select, Typography } from 'antd';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Operator } from '../constant';
import { useBuildFormSelectOptions } from '../form-hooks';
import { IOperatorForm } from '../interface';

const subLabelCol = {
Expand All @@ -12,8 +14,16 @@ const subWrapperCol = {
span: 17,
};

const SwitchForm: React.FC = ({ form, onValuesChange }: IOperatorForm) => {
const SwitchForm: React.FC = ({
form,
onValuesChange,
nodeId,
}: IOperatorForm) => {
const { t } = useTranslation();
const buildCategorizeToOptions = useBuildFormSelectOptions(
Operator.Categorize,
nodeId,
);

return (
<Form
Expand All @@ -26,7 +36,7 @@ const SwitchForm: React.FC = ({ form, onValuesChange }: IOperatorForm) => {
onValuesChange={onValuesChange}
>
<Form.Item label={t('flow.to')} name={['end_cpn_id']}>
<Input />
<Select options={buildCategorizeToOptions([])} />
</Form.Item>
<Form.Item label={t('flow.no')} name={['no']}>
<Input />
Expand Down Expand Up @@ -55,9 +65,8 @@ const SwitchForm: React.FC = ({ form, onValuesChange }: IOperatorForm) => {
</Form.Item>

<Form.Item label={t('flow.to')} name={[field.name, 'to']}>
<Input />
<Select options={buildCategorizeToOptions([])} />
</Form.Item>
{/* Nest Form.List */}
<Form.Item label="Items">
<Form.List name={[field.name, 'items']}>
{(subFields, subOpt) => (
Expand Down
15 changes: 7 additions & 8 deletions web/src/utils/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { history } from 'umi';
import { RequestMethod, extend } from 'umi-request';
import { convertTheKeysOfTheObjectToSnake } from './common-util';

const ABORT_REQUEST_ERR_MESSAGE = 'The user aborted a request.';
const FAILED_TO_FETCH = 'Failed to fetch';

const RetcodeMessage = {
200: i18n.t('message.200'),
Expand Down Expand Up @@ -50,8 +50,11 @@ const errorHandler = (error: {
message: string;
}): Response => {
const { response } = error;
if (error.message === ABORT_REQUEST_ERR_MESSAGE) {
console.log('user abort request');
if (error.message === FAILED_TO_FETCH) {
notification.error({
description: i18n.t('message.networkAnomalyDescription'),
message: i18n.t('message.networkAnomaly'),
});
} else {
if (response && response.status) {
const errorText =
Expand All @@ -61,11 +64,6 @@ const errorHandler = (error: {
message: `${i18n.t('message.requestError')} ${status}: ${url}`,
description: errorText,
});
} else if (!response) {
notification.error({
description: i18n.t('message.networkAnomalyDescription'),
message: i18n.t('message.networkAnomaly'),
});
}
}
return response;
Expand Down Expand Up @@ -102,6 +100,7 @@ request.interceptors.response.use(async (response: any, options) => {
if (options.responseType === 'blob') {
return response;
}

const data: ResponseType = await response.clone().json();

if (data.retcode === 401 || data.retcode === 401) {
Expand Down