feat(sub): add Incy client integration and routing tab

Add an Incy quick-import button (incy://add) to the Android and iOS app menus on the subscription page, and a new Incy settings tab with routing enable + rules. Incy routing is delivered by injecting an incy://routing/onadd line into the raw subscription body, avoiding a collision with Happ's Routing header. Includes backend settings, regenerated OpenAPI/zod schemas, and translations for all locales.
This commit is contained in:
MHSanaei
2026-06-24 12:51:22 +02:00
parent 3fa4eddae3
commit 48c2fb27b8
26 changed files with 181 additions and 17 deletions
@@ -1,5 +1,5 @@
import { Input, InputNumber, Switch, Tabs } from 'antd';
import { BranchesOutlined, IdcardOutlined, InfoCircleOutlined, NodeIndexOutlined, SafetyCertificateOutlined, SettingOutlined } from '@ant-design/icons';
import { BranchesOutlined, CompassOutlined, IdcardOutlined, InfoCircleOutlined, NodeIndexOutlined, SafetyCertificateOutlined, SettingOutlined } from '@ant-design/icons';
import { useTranslation } from 'react-i18next';
import type { AllSetting } from '@/models/setting';
import { SettingListItem } from '@/components/ui';
@@ -178,6 +178,21 @@ export default function SubscriptionGeneralTab({ allSetting, updateSetting }: Su
</>
),
},
{
key: '7',
label: catTabLabel(<CompassOutlined />, 'Incy', isMobile),
children: (
<>
<SettingListItem paddings="small" title={t('pages.settings.subIncyEnableRouting')} description={t('pages.settings.subIncyEnableRoutingDesc')}>
<Switch checked={allSetting.subIncyEnableRouting} onChange={(v) => updateSetting({ subIncyEnableRouting: v })} />
</SettingListItem>
<SettingListItem paddings="small" title={t('pages.settings.subIncyRoutingRules')} description={t('pages.settings.subIncyRoutingRulesDesc')}>
<Input.TextArea value={allSetting.subIncyRoutingRules} placeholder="incy://routing/onadd/..."
onChange={(e) => updateSetting({ subIncyRoutingRules: e.target.value })} />
</SettingListItem>
</>
),
},
]} />
);
}
+4 -1
View File
@@ -139,6 +139,7 @@ export default function SubPage() {
);
const streisandUrl = useMemo(() => `streisand://import/${encodeURIComponent(subUrl)}`, []);
const happUrl = useMemo(() => `happ://add/${subUrl}`, []);
const incyUrl = useMemo(() => `incy://add/${subUrl}`, []);
const pageClass = useMemo(() => {
const classes = ['subscription-page'];
@@ -200,6 +201,7 @@ export default function SubPage() {
{ key: 'android-v2raytun', label: 'V2RayTun', onClick: () => copy(subUrl) },
{ key: 'android-npvtunnel', label: 'NPV Tunnel', onClick: () => copy(subUrl) },
{ key: 'android-happ', label: 'Happ', onClick: () => open(`happ://add/${subUrl}`) },
{ key: 'android-incy', label: 'Incy', onClick: () => open(`incy://add/${subUrl}`) },
], [copy, open]);
const iosMenuItems = useMemo(() => [
@@ -209,7 +211,8 @@ export default function SubPage() {
{ key: 'ios-v2raytun', label: 'V2RayTun', onClick: () => copy(subUrl) },
{ key: 'ios-npvtunnel', label: 'NPV Tunnel', onClick: () => copy(subUrl) },
{ key: 'ios-happ', label: 'Happ', onClick: () => open(happUrl) },
], [copy, open, shadowrocketUrl, v2boxUrl, streisandUrl, happUrl]);
{ key: 'ios-incy', label: 'Incy', onClick: () => open(incyUrl) },
], [copy, open, shadowrocketUrl, v2boxUrl, streisandUrl, happUrl, incyUrl]);
const langMenuItems = useMemo(
() => (LanguageManager.supportedLanguages as { value: string; name: string; icon: string }[]).map((l) => ({