VersionModal.tsx 5.6 KB

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