SubscriptionGeneralTab.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. import { Alert, Button, Input, InputNumber, Switch, Tabs } from 'antd';
  2. import { BranchesOutlined, CompassOutlined, IdcardOutlined, InfoCircleOutlined, NodeIndexOutlined, SafetyCertificateOutlined, SettingOutlined } from '@ant-design/icons';
  3. import { useTranslation } from 'react-i18next';
  4. import { useNavigate } from 'react-router';
  5. import type { AllSetting } from '@/models/setting';
  6. import { onNumber } from '@/utils/onNumber';
  7. import { DefaultSettingTag, SettingListItem } from '@/components/ui';
  8. import { RemarkTemplateField } from '@/components/form';
  9. import { useMediaQuery } from '@/hooks/useMediaQuery';
  10. import { catTabLabel } from './catTabLabel';
  11. import { sanitizePath, normalizePath } from './uriPath';
  12. interface SubscriptionGeneralTabProps {
  13. allSetting: AllSetting;
  14. updateSetting: (patch: Partial<AllSetting>) => void;
  15. }
  16. export default function SubscriptionGeneralTab({ allSetting, updateSetting }: SubscriptionGeneralTabProps) {
  17. const { t } = useTranslation();
  18. const navigate = useNavigate();
  19. const { isMobile } = useMediaQuery();
  20. return (
  21. <Tabs defaultActiveKey="1" items={[
  22. {
  23. key: '1',
  24. label: catTabLabel(<SettingOutlined />, t('pages.settings.panelSettings'), isMobile),
  25. children: (
  26. <>
  27. <SettingListItem paddings="small" title={t('pages.settings.subEnable')} description={t('pages.settings.subEnableDesc')}>
  28. <Switch checked={allSetting.subEnable} onChange={(v) => updateSetting({ subEnable: v })} />
  29. </SettingListItem>
  30. <SettingListItem paddings="small" title={t('pages.settings.subJsonEnableTitle')} description={t('pages.settings.subJsonEnable')}>
  31. <Switch checked={allSetting.subJsonEnable} onChange={(v) => updateSetting({ subJsonEnable: v })} />
  32. </SettingListItem>
  33. <SettingListItem paddings="small" title={t('pages.settings.subClashEnableTitle')}>
  34. <Switch checked={allSetting.subClashEnable} onChange={(v) => updateSetting({ subClashEnable: v })} />
  35. </SettingListItem>
  36. {(allSetting.subJsonEnable || allSetting.subClashEnable) && (
  37. <Alert
  38. type="info"
  39. showIcon
  40. style={{ margin: '12px 20px' }}
  41. title={t('pages.settings.subFormatsTipTitle')}
  42. description={t('pages.settings.subFormatsTipDesc')}
  43. action={(
  44. <Button size="small" onClick={() => navigate('/settings#subscription-formats')}>
  45. {t('pages.settings.subFormatsTipAction')}
  46. </Button>
  47. )}
  48. />
  49. )}
  50. <SettingListItem paddings="small" title={t('pages.settings.subListen')} description={t('pages.settings.subListenDesc')}>
  51. <Input value={allSetting.subListen} onChange={(e) => updateSetting({ subListen: e.target.value })} />
  52. </SettingListItem>
  53. <SettingListItem paddings="small" title={t('pages.settings.subDomain')} description={t('pages.settings.subDomainDesc')}>
  54. <Input value={allSetting.subDomain} onChange={(e) => updateSetting({ subDomain: e.target.value })} />
  55. </SettingListItem>
  56. <SettingListItem paddings="small" title={t('pages.settings.subPort')} badge={<DefaultSettingTag settingKey="subPort" value={allSetting.subPort} />} description={t('pages.settings.subPortDesc')}>
  57. <InputNumber value={allSetting.subPort} min={1} max={65535} style={{ width: '100%' }}
  58. onChange={onNumber((v) => updateSetting({ subPort: v }))} />
  59. </SettingListItem>
  60. <SettingListItem paddings="small" title={t('pages.settings.subPath')} description={t('pages.settings.subPathDesc')}>
  61. <Input
  62. value={allSetting.subPath}
  63. placeholder="/sub/"
  64. onChange={(e) => updateSetting({ subPath: sanitizePath(e.target.value) })}
  65. onBlur={() => updateSetting({ subPath: normalizePath(allSetting.subPath) })}
  66. />
  67. </SettingListItem>
  68. <SettingListItem paddings="small" title={t('pages.settings.subURI')} description={t('pages.settings.subURIDesc')}>
  69. <Input value={allSetting.subURI} placeholder="(http|https)://domain[:port]/path/"
  70. onChange={(e) => updateSetting({ subURI: e.target.value })} />
  71. </SettingListItem>
  72. </>
  73. ),
  74. },
  75. {
  76. key: '2',
  77. label: catTabLabel(<InfoCircleOutlined />, t('pages.settings.information'), isMobile),
  78. children: (
  79. <>
  80. <SettingListItem paddings="small" title={t('pages.settings.subEncrypt')} description={t('pages.settings.subEncryptDesc')}>
  81. <Switch checked={allSetting.subEncrypt} onChange={(v) => updateSetting({ subEncrypt: v })} />
  82. </SettingListItem>
  83. <SettingListItem
  84. paddings="small"
  85. title={t('pages.settings.remarkTemplate')}
  86. description={t('pages.settings.remarkTemplateDesc')}
  87. >
  88. <RemarkTemplateField
  89. value={allSetting.remarkTemplate}
  90. onChange={(v) => updateSetting({ remarkTemplate: v })}
  91. maxLength={256}
  92. />
  93. </SettingListItem>
  94. <SettingListItem
  95. paddings="small"
  96. title={t('pages.settings.subShowIdentityOnAllLinks')}
  97. description={t('pages.settings.subShowIdentityOnAllLinksDesc')}
  98. >
  99. <Switch
  100. checked={allSetting.subShowIdentityOnAllLinks}
  101. onChange={(v) => updateSetting({ subShowIdentityOnAllLinks: v })}
  102. />
  103. </SettingListItem>
  104. <SettingListItem paddings="small" title={t('pages.settings.subUpdates')} badge={<DefaultSettingTag settingKey="subUpdates" value={allSetting.subUpdates} />} description={t('pages.settings.subUpdatesDesc')}>
  105. <InputNumber value={allSetting.subUpdates} min={0} max={525600} style={{ width: '100%' }}
  106. onChange={onNumber((v) => updateSetting({ subUpdates: v }))} />
  107. </SettingListItem>
  108. </>
  109. ),
  110. },
  111. {
  112. key: '3',
  113. label: catTabLabel(<IdcardOutlined />, t('pages.settings.profile'), isMobile),
  114. children: (
  115. <>
  116. <SettingListItem paddings="small" title={t('pages.settings.subTitle')} description={t('pages.settings.subTitleDesc')}>
  117. <RemarkTemplateField
  118. value={allSetting.subTitle}
  119. onChange={(v) => updateSetting({ subTitle: v })}
  120. metadataOnly
  121. />
  122. </SettingListItem>
  123. <SettingListItem paddings="small" title={t('pages.settings.subSupportUrl')} description={t('pages.settings.subSupportUrlDesc')}>
  124. <RemarkTemplateField
  125. value={allSetting.subSupportUrl}
  126. placeholder="https://example.com"
  127. onChange={(v) => updateSetting({ subSupportUrl: v })}
  128. metadataOnly
  129. />
  130. </SettingListItem>
  131. <SettingListItem paddings="small" title={t('pages.settings.subProfileUrl')} description={t('pages.settings.subProfileUrlDesc')}>
  132. <RemarkTemplateField
  133. value={allSetting.subProfileUrl}
  134. placeholder="https://example.com"
  135. onChange={(v) => updateSetting({ subProfileUrl: v })}
  136. metadataOnly
  137. />
  138. </SettingListItem>
  139. <SettingListItem paddings="small" title={t('pages.settings.subAnnounce')} description={t('pages.settings.subAnnounceDesc')}>
  140. <RemarkTemplateField
  141. value={allSetting.subAnnounce}
  142. onChange={(v) => updateSetting({ subAnnounce: v })}
  143. multiline
  144. rows={3}
  145. metadataOnly
  146. />
  147. </SettingListItem>
  148. <SettingListItem
  149. paddings="small"
  150. title={t('pages.settings.subThemeDir')}
  151. description={(
  152. <>
  153. {t('pages.settings.subThemeDirDesc')}{' '}
  154. <a
  155. href="https://github.com/MHSanaei/3x-ui/blob/main/docs/custom-subscription-templates.md"
  156. target="_blank"
  157. rel="noopener noreferrer"
  158. >
  159. {t('pages.settings.subThemeDirDocs')}
  160. </a>
  161. </>
  162. )}
  163. >
  164. <Input value={allSetting.subThemeDir} placeholder="/etc/3x-ui/sub_templates/my-theme/"
  165. onChange={(e) => updateSetting({ subThemeDir: e.target.value })} />
  166. </SettingListItem>
  167. </>
  168. ),
  169. },
  170. {
  171. key: '4',
  172. label: catTabLabel(<SafetyCertificateOutlined />, t('pages.settings.certs'), isMobile),
  173. children: (
  174. <>
  175. <SettingListItem paddings="small" title={t('pages.settings.subCertPath')} description={t('pages.settings.subCertPathDesc')}>
  176. <Input value={allSetting.subCertFile} onChange={(e) => updateSetting({ subCertFile: e.target.value })} />
  177. </SettingListItem>
  178. <SettingListItem paddings="small" title={t('pages.settings.subKeyPath')} description={t('pages.settings.subKeyPathDesc')}>
  179. <Input value={allSetting.subKeyFile} onChange={(e) => updateSetting({ subKeyFile: e.target.value })} />
  180. </SettingListItem>
  181. </>
  182. ),
  183. },
  184. {
  185. key: '5',
  186. label: catTabLabel(<BranchesOutlined />, 'Happ', isMobile),
  187. children: (
  188. <>
  189. <SettingListItem paddings="small" title={t('pages.settings.subEnableRouting')} description={t('pages.settings.subEnableRoutingDesc')}>
  190. <Switch checked={allSetting.subEnableRouting} onChange={(v) => updateSetting({ subEnableRouting: v })} />
  191. </SettingListItem>
  192. <SettingListItem paddings="small" title={t('pages.settings.subRoutingRules')} description={t('pages.settings.subRoutingRulesDesc')}>
  193. <Input.TextArea value={allSetting.subRoutingRules} placeholder="happ://routing/add/..."
  194. onChange={(e) => updateSetting({ subRoutingRules: e.target.value })} />
  195. </SettingListItem>
  196. <SettingListItem paddings="small" title={t('pages.settings.subHideSettings')} description={t('pages.settings.subHideSettingsDesc')}>
  197. <Switch checked={allSetting.subHideSettings} onChange={(v) => updateSetting({ subHideSettings: v })} />
  198. </SettingListItem>
  199. </>
  200. ),
  201. },
  202. {
  203. key: '6',
  204. label: catTabLabel(<NodeIndexOutlined />, 'Clash / Mihomo', isMobile),
  205. children: (
  206. <>
  207. <SettingListItem paddings="small" title={t('pages.settings.subClashEnableRouting')} description={t('pages.settings.subClashEnableRoutingDesc')}>
  208. <Switch checked={allSetting.subClashEnableRouting} onChange={(v) => updateSetting({ subClashEnableRouting: v })} />
  209. </SettingListItem>
  210. <SettingListItem paddings="small" title={t('pages.settings.subClashRoutingRules')} description={t('pages.settings.subClashRoutingRulesDesc')}>
  211. <Input.TextArea
  212. value={allSetting.subClashRules}
  213. rows={8}
  214. placeholder={'GEOSITE,category-ir,DIRECT\nGEOIP,private,DIRECT'}
  215. onChange={(e) => updateSetting({ subClashRules: e.target.value })}
  216. />
  217. </SettingListItem>
  218. </>
  219. ),
  220. },
  221. {
  222. key: '7',
  223. label: catTabLabel(<CompassOutlined />, 'Incy', isMobile),
  224. children: (
  225. <>
  226. <SettingListItem paddings="small" title={t('pages.settings.subIncyEnableRouting')} description={t('pages.settings.subIncyEnableRoutingDesc')}>
  227. <Switch checked={allSetting.subIncyEnableRouting} onChange={(v) => updateSetting({ subIncyEnableRouting: v })} />
  228. </SettingListItem>
  229. <SettingListItem paddings="small" title={t('pages.settings.subIncyRoutingRules')} description={t('pages.settings.subIncyRoutingRulesDesc')}>
  230. <Input.TextArea value={allSetting.subIncyRoutingRules} placeholder="incy://routing/onadd/..."
  231. onChange={(e) => updateSetting({ subIncyRoutingRules: e.target.value })} />
  232. </SettingListItem>
  233. </>
  234. ),
  235. },
  236. ]} />
  237. );
  238. }