refactor(frontend): extract OutboundFormModal loopback/blackhole/dns field blocks

Moved the outbound-only protocol field blocks (loopback, blackhole, dns) out of the modal render body into outbound-only-fields.tsx. First render-body extraction behind the per-protocol snapshot net: loopback/blackhole/dns snapshots unchanged -> verified no behavior change. typecheck/lint/build green.
This commit is contained in:
MHSanaei 2026-05-30 16:37:25 +02:00
parent 42f943ddc8
commit cf9920593e
No known key found for this signature in database
GPG key ID: 7E4060F2FBE5AB7A
2 changed files with 94 additions and 80 deletions

View file

@ -36,7 +36,6 @@ import {
type OutboundFormValues, type OutboundFormValues,
} from '@/schemas/forms/outbound-form'; } from '@/schemas/forms/outbound-form';
import { import {
DNSRuleActions,
DOMAIN_STRATEGY_OPTION, DOMAIN_STRATEGY_OPTION,
OutboundDomainStrategies, OutboundDomainStrategies,
SNIFFING_OPTION, SNIFFING_OPTION,
@ -75,6 +74,7 @@ import {
isMuxAllowed, isMuxAllowed,
newStreamSlice, newStreamSlice,
} from './outbound-form-helpers'; } from './outbound-form-helpers';
import { OutboundOnlyProtocolFields } from './outbound-only-fields';
import './OutboundFormModal.css'; import './OutboundFormModal.css';
// Pattern A rewrite of OutboundFormModal. Built as a sibling `.new.tsx` // Pattern A rewrite of OutboundFormModal. Built as a sibling `.new.tsx`
@ -504,85 +504,7 @@ export default function OutboundFormModal({
</> </>
)} )}
{protocol === 'loopback' && ( <OutboundOnlyProtocolFields protocol={protocol} />
<Form.Item label={t('pages.xray.outboundForm.inboundTag')} name={['settings', 'inboundTag']}>
<Input placeholder={t('pages.xray.outboundForm.inboundTagPlaceholder')} />
</Form.Item>
)}
{protocol === 'blackhole' && (
<Form.Item label={t('pages.xray.outboundForm.responseType')} name={['settings', 'type']}>
<Select
options={[
{ value: '', label: '(empty)' },
{ value: 'none', label: 'none' },
{ value: 'http', label: 'http' },
]}
/>
</Form.Item>
)}
{protocol === 'dns' && (
<>
<Form.Item label={t('pages.xray.outboundForm.rewriteNetwork')} name={['settings', 'rewriteNetwork']}>
<Select
allowClear
placeholder={t('pages.xray.outboundForm.unchanged')}
options={[
{ value: 'udp', label: 'udp' },
{ value: 'tcp', label: 'tcp' },
]}
/>
</Form.Item>
<Form.Item label={t('pages.inbounds.form.rewriteAddress')} name={['settings', 'rewriteAddress']}>
<Input placeholder={t('pages.xray.outboundForm.unchangedAddress')} />
</Form.Item>
<Form.Item label={t('pages.inbounds.form.rewritePort')} name={['settings', 'rewritePort']}>
<InputNumber min={0} max={65535} style={{ width: '100%' }} />
</Form.Item>
<Form.Item label={t('pages.xray.tun.userLevel')} name={['settings', 'userLevel']}>
<InputNumber min={0} style={{ width: '100%' }} />
</Form.Item>
<Form.List name={['settings', 'rules']}>
{(fields, { add, remove }) => (
<>
<Form.Item label={t('pages.xray.outboundForm.rules')}>
<Button
size="small"
type="primary"
icon={<PlusOutlined />}
onClick={() => add({ action: 'direct', qtype: '', domain: '' })}
/>
</Form.Item>
{fields.map((field, index) => (
<div key={field.key}>
<Form.Item wrapperCol={{ md: { span: 14, offset: 8 } }}>
<div className="item-heading">
<span>{t('pages.xray.outboundForm.ruleN', { n: index + 1 })}</span>
<DeleteOutlined
className="danger-icon"
onClick={() => remove(field.name)}
/>
</div>
</Form.Item>
<Form.Item label={t('pages.xray.outboundForm.action')} name={[field.name, 'action']}>
<Select
options={DNSRuleActions.map((a) => ({ value: a, label: a }))}
/>
</Form.Item>
<Form.Item label="QType" name={[field.name, 'qtype']}>
<Input placeholder="1,3,23-24" />
</Form.Item>
<Form.Item label={t('domainName')} name={[field.name, 'domain']}>
<Input placeholder="domain:example.com" />
</Form.Item>
</div>
))}
</>
)}
</Form.List>
</>
)}
{protocol === 'freedom' && ( {protocol === 'freedom' && (
<> <>

View file

@ -0,0 +1,92 @@
import { useTranslation } from 'react-i18next';
import { Button, Form, Input, InputNumber, Select } from 'antd';
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import { DNSRuleActions } from '@/schemas/primitives';
export function OutboundOnlyProtocolFields({ protocol }: { protocol: string }) {
const { t } = useTranslation();
return (
<>
{protocol === 'loopback' && (
<Form.Item label={t('pages.xray.outboundForm.inboundTag')} name={['settings', 'inboundTag']}>
<Input placeholder={t('pages.xray.outboundForm.inboundTagPlaceholder')} />
</Form.Item>
)}
{protocol === 'blackhole' && (
<Form.Item label={t('pages.xray.outboundForm.responseType')} name={['settings', 'type']}>
<Select
options={[
{ value: '', label: '(empty)' },
{ value: 'none', label: 'none' },
{ value: 'http', label: 'http' },
]}
/>
</Form.Item>
)}
{protocol === 'dns' && (
<>
<Form.Item label={t('pages.xray.outboundForm.rewriteNetwork')} name={['settings', 'rewriteNetwork']}>
<Select
allowClear
placeholder={t('pages.xray.outboundForm.unchanged')}
options={[
{ value: 'udp', label: 'udp' },
{ value: 'tcp', label: 'tcp' },
]}
/>
</Form.Item>
<Form.Item label={t('pages.inbounds.form.rewriteAddress')} name={['settings', 'rewriteAddress']}>
<Input placeholder={t('pages.xray.outboundForm.unchangedAddress')} />
</Form.Item>
<Form.Item label={t('pages.inbounds.form.rewritePort')} name={['settings', 'rewritePort']}>
<InputNumber min={0} max={65535} style={{ width: '100%' }} />
</Form.Item>
<Form.Item label={t('pages.xray.tun.userLevel')} name={['settings', 'userLevel']}>
<InputNumber min={0} style={{ width: '100%' }} />
</Form.Item>
<Form.List name={['settings', 'rules']}>
{(fields, { add, remove }) => (
<>
<Form.Item label={t('pages.xray.outboundForm.rules')}>
<Button
size="small"
type="primary"
icon={<PlusOutlined />}
onClick={() => add({ action: 'direct', qtype: '', domain: '' })}
/>
</Form.Item>
{fields.map((field, index) => (
<div key={field.key}>
<Form.Item wrapperCol={{ md: { span: 14, offset: 8 } }}>
<div className="item-heading">
<span>{t('pages.xray.outboundForm.ruleN', { n: index + 1 })}</span>
<DeleteOutlined
className="danger-icon"
onClick={() => remove(field.name)}
/>
</div>
</Form.Item>
<Form.Item label={t('pages.xray.outboundForm.action')} name={[field.name, 'action']}>
<Select
options={DNSRuleActions.map((a) => ({ value: a, label: a }))}
/>
</Form.Item>
<Form.Item label="QType" name={[field.name, 'qtype']}>
<Input placeholder="1,3,23-24" />
</Form.Item>
<Form.Item label={t('domainName')} name={[field.name, 'domain']}>
<Input placeholder="domain:example.com" />
</Form.Item>
</div>
))}
</>
)}
</Form.List>
</>
)}
</>
);
}