import './Connections.css'; import React from 'react'; import { Pause, Play, RefreshCcw, Settings, Tag, X as IconClose } from '~/components/shared/FeatherIcons'; import { useTranslation } from 'react-i18next'; import { Tab, TabList, TabPanel, Tabs } from 'react-tabs'; import Select from '~/components/shared/Select'; import { useConnectionColumns, useConnectionFilters, useConnectionsStream, useSourceMapState, } from '~/modules/connections/hooks'; import { CONNECTIONS_PADDING_BOTTOM } from '~/modules/connections/utils'; import { FormattedConn } from '~/store/connections'; import { ClashAPIConfig } from '~/types'; import * as connAPI from '../api/connections'; import useRemainingViewPortHeight from '../hooks/useRemainingViewPortHeight'; import s from './Connections.module.scss'; import ConnectionTable from './ConnectionTable'; import ContentHeader from './ContentHeader'; import Input from './Input'; import ModalCloseAllConnections from './ModalCloseAllConnections'; import ModalManageConnectionColumns from './ModalManageConnectionColumns'; import ModalSourceIP from './ModalSourceIP'; import { Fab, position as fabPosition } from './shared/Fab'; import SvgYacd from './SvgYacd'; const { useState, useCallback } = React; function renderTableOrPlaceholder( columns, hiddenColumns, conns: FormattedConn[], height: number, apiConfig: ClashAPIConfig ) { return conns.length > 0 ? ( ) : (
); } function ConnQty({ qty }) { return qty < 100 ? '' + qty : '99+'; } type Props = { apiConfig: ClashAPIConfig; }; export default function Connections({ apiConfig }: Props) { const { t } = useTranslation(); const [showModalColumn, setModalColumn] = useState(false); const { hiddenColumns, setHiddenColumns, columns, setColumns, resetColumns } = useConnectionColumns(); const closeModalColumn = () => { setModalColumn(false); }; const { sourceMapModal, sourceMap, setSourceMap, openModalSource, closeModalSource } = useSourceMapState(); const [refContainer, containerHeight] = useRemainingViewPortHeight(); const { conns, closedConns, isRefreshPaused, toggleIsRefreshPaused, closeAllConnections } = useConnectionsStream(apiConfig, sourceMap); const { filterKeyword, setFilterKeyword, filterSourceIpStr, setFilterSourceIpStr, filteredConns, filteredClosedConns, connIpSet, } = useConnectionFilters({ conns, closedConns, sourceMap, t }); const [isCloseFilterModalOpen, setIsCloseFilterModalOpen] = useState(false); const openCloseFilterModal = useCallback(() => setIsCloseFilterModalOpen(true), []); const closeCloseFilterModal = useCallback(() => setIsCloseFilterModalOpen(false), []); const closeFilterConnections = useCallback(async () => { for (const connection of filteredConns) { await connAPI.closeConnById(apiConfig, connection.id); } closeCloseFilterModal(); }, [apiConfig, filteredConns, closeCloseFilterModal]); const [isCloseAllModalOpen, setIsCloseAllModalOpen] = useState(false); const openCloseAllModal = useCallback(() => setIsCloseAllModalOpen(true), []); const closeCloseAllModal = useCallback(() => setIsCloseAllModalOpen(false), []); const handleCloseAllConnections = useCallback(() => { closeAllConnections(); closeCloseAllModal(); }, [closeAllConnections, closeCloseAllModal]); return (
{t('Active')} {t('Closed')} setFilterKeyword(e.target.value)} />
{renderTableOrPlaceholder( columns, hiddenColumns, filteredConns, containerHeight - CONNECTIONS_PADDING_BOTTOM, apiConfig )} : } mainButtonStyles={isRefreshPaused ? { background: '#e74c3c' } : {}} style={fabPosition} text={isRefreshPaused ? t('Resume Refresh') : t('Pause Refresh')} onClick={toggleIsRefreshPaused} /> {renderTableOrPlaceholder( columns, hiddenColumns, filteredClosedConns, containerHeight - CONNECTIONS_PADDING_BOTTOM, apiConfig )}
); }