VersionModal.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import { useCallback, useEffect, useState } from 'react';
  2. import { useTranslation } from 'react-i18next';
  3. import { Alert, Button, Collapse, Modal, Radio, Spin, Tag, Tooltip } from 'antd';
  4. import { ReloadOutlined } from '@ant-design/icons';
  5. import { HttpUtil } from '@/utils';
  6. import type { Status } from '@/models/status';
  7. import CustomGeoSection from './CustomGeoSection';
  8. import './VersionModal.css';
  9. interface BusyEvent {
  10. busy: boolean;
  11. tip?: string;
  12. }
  13. interface VersionModalProps {
  14. open: boolean;
  15. status: Status;
  16. onClose: () => void;
  17. onBusy: (e: BusyEvent) => void;
  18. }
  19. const GEOFILES = [
  20. 'geosite.dat',
  21. 'geoip.dat',
  22. 'geosite_IR.dat',
  23. 'geoip_IR.dat',
  24. 'geosite_RU.dat',
  25. 'geoip_RU.dat',
  26. ];
  27. export default function VersionModal({ open, status, onClose, onBusy }: VersionModalProps) {
  28. const { t } = useTranslation();
  29. const [modal, modalContextHolder] = Modal.useModal();
  30. const [activeKey, setActiveKey] = useState<string | string[]>('1');
  31. const [versions, setVersions] = useState<string[]>([]);
  32. const [loading, setLoading] = useState(false);
  33. const fetchVersions = useCallback(async () => {
  34. setLoading(true);
  35. try {
  36. const msg = await HttpUtil.get<string[]>('/panel/api/server/getXrayVersion');
  37. if (msg?.success) setVersions(msg.obj || []);
  38. } finally {
  39. setLoading(false);
  40. }
  41. }, []);
  42. useEffect(() => {
  43. if (open) fetchVersions();
  44. }, [open, fetchVersions]);
  45. function switchXrayVersion(version: string) {
  46. modal.confirm({
  47. title: t('pages.index.xraySwitchVersionDialog'),
  48. content: t('pages.index.xraySwitchVersionDialogDesc').replace('#version#', version),
  49. okText: t('confirm'),
  50. cancelText: t('cancel'),
  51. onOk: async () => {
  52. onClose();
  53. onBusy({ busy: true, tip: t('pages.index.dontRefresh') });
  54. try {
  55. await HttpUtil.post(`/panel/api/server/installXray/${version}`);
  56. } finally {
  57. onBusy({ busy: false });
  58. }
  59. },
  60. });
  61. }
  62. function updateGeofile(fileName: string) {
  63. const isSingle = !!fileName;
  64. modal.confirm({
  65. title: t('pages.index.geofileUpdateDialog'),
  66. content: isSingle
  67. ? t('pages.index.geofileUpdateDialogDesc').replace('#filename#', fileName)
  68. : t('pages.index.geofilesUpdateDialogDesc'),
  69. okText: t('confirm'),
  70. cancelText: t('cancel'),
  71. onOk: async () => {
  72. onClose();
  73. onBusy({ busy: true, tip: t('pages.index.dontRefresh') });
  74. const url = isSingle
  75. ? `/panel/api/server/updateGeofile/${fileName}`
  76. : '/panel/api/server/updateGeofile';
  77. try {
  78. await HttpUtil.post(url);
  79. } finally {
  80. onBusy({ busy: false });
  81. }
  82. },
  83. });
  84. }
  85. const activeKeyStr = Array.isArray(activeKey) ? activeKey[0] : activeKey;
  86. return (
  87. <Modal
  88. open={open}
  89. title={t('pages.index.xrayUpdates')}
  90. footer={null}
  91. onCancel={onClose}
  92. >
  93. {modalContextHolder}
  94. <Spin spinning={loading}>
  95. <Collapse
  96. accordion
  97. activeKey={activeKey}
  98. onChange={setActiveKey}
  99. items={[
  100. {
  101. key: '1',
  102. label: 'Xray',
  103. children: (
  104. <>
  105. <Alert
  106. type="warning"
  107. className="mb-12"
  108. title={t('pages.index.xraySwitchClickDesk')}
  109. showIcon
  110. />
  111. <div className="version-list">
  112. {versions.map((version, index) => (
  113. <div key={version} className="version-list-item">
  114. <Tag color={index % 2 === 0 ? 'purple' : 'green'}>{version}</Tag>
  115. <Radio
  116. checked={version === `v${status?.xray?.version}`}
  117. onClick={() => switchXrayVersion(version)}
  118. />
  119. </div>
  120. ))}
  121. </div>
  122. </>
  123. ),
  124. },
  125. {
  126. key: '2',
  127. label: 'Geofiles',
  128. children: (
  129. <>
  130. <div className="version-list">
  131. {GEOFILES.map((file, index) => (
  132. <div key={file} className="version-list-item">
  133. <Tag color={index % 2 === 0 ? 'purple' : 'green'}>{file}</Tag>
  134. <Tooltip title={t('update')}>
  135. <ReloadOutlined
  136. className="reload-icon"
  137. onClick={() => updateGeofile(file)}
  138. />
  139. </Tooltip>
  140. </div>
  141. ))}
  142. </div>
  143. <div className="actions-row">
  144. <Button onClick={() => updateGeofile('')}>
  145. {t('pages.index.geofilesUpdateAll')}
  146. </Button>
  147. </div>
  148. </>
  149. ),
  150. },
  151. {
  152. key: '3',
  153. label: t('pages.index.customGeoTitle'),
  154. children: <CustomGeoSection active={activeKeyStr === '3'} />,
  155. },
  156. ]}
  157. />
  158. </Spin>
  159. </Modal>
  160. );
  161. }