summaryrefslogtreecommitdiff
path: root/src/components/Connections.js
blob: 70fe87043bea32e0c7a8fa8d180f9c7b88c8dc37 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import React from 'react';
import ContentHeader from './ContentHeader';
import ConnectionTable from './ConnectionTable';
import useRemainingViewPortHeight from '../hooks/useRemainingViewPortHeight';
import { getClashAPIConfig } from '../store/app';
import { X as IconClose } from 'react-feather';
import SvgYacd from './SvgYacd';
import Button from './Button';
import ModalCloseAllConnections from './ModalCloseAllConnections';
import { connect } from './StateProvider';
import * as connAPI from '../api/connections';

import s from './Connections.module.css';

const { useEffect, useState, useRef, useCallback, useMemo } = React;

const paddingBottom = 30;

function formatConnectionDataItem(i) {
  const { id, metadata, upload, download, start, chains, rule } = i;
  const { host, destinationPort } = metadata;
  const metadataNext = {
    ...metadata,
    // merge host and destinationPort into one column
    host: host + ':' + destinationPort
  };
  // const started = formatDistance(new Date(start), now);
  return {
    id,
    upload,
    download,
    start: 0 - new Date(start),
    chains: chains.reverse().join(' / '),
    rule,
    ...metadataNext
  };
}

function Conn({ apiConfig }) {
  const [refContainer, containerHeight] = useRemainingViewPortHeight();
  const [conns, setConns] = useState([]);
  const [isCloseAllModalOpen, setIsCloseAllModalOpen] = useState(false);
  const openCloseAllModal = useCallback(() => setIsCloseAllModalOpen(true), []);
  const closeCloseAllModal = useCallback(
    () => setIsCloseAllModalOpen(false),
    []
  );
  const closeAllConnections = useCallback(() => {
    connAPI.closeAllConnections(apiConfig);
    closeCloseAllModal();
  }, [apiConfig, closeCloseAllModal]);
  const iconClose = useMemo(() => <IconClose width={16} />, []);
  const prevConnsRef = useRef(conns);
  const read = useCallback(
    ({ connections }) => {
      const x = connections.map(c => formatConnectionDataItem(c));
      // if previous connections and current connections are both empty
      // arrays, we wont update state to avaoid rerender
      if (x && (x.length !== 0 || prevConnsRef.current.length !== 0)) {
        prevConnsRef.current = x;
        setConns(x);
      } else {
        prevConnsRef.current = x;
      }
    },
    [setConns]
  );
  useEffect(() => {
    return connAPI.fetchData(apiConfig, read);
  }, [apiConfig, read]);
  return (
    <div>
      <ContentHeader title="Connections" />
      <div
        ref={refContainer}
        style={{ padding: 30, paddingBottom, paddingTop: 0 }}
      >
        <div
          style={{ height: containerHeight - paddingBottom, overflow: 'auto' }}
        >
          {conns.length > 0 ? (
            <ConnectionTable data={conns} />
          ) : (
            <div className={s.placeHolder}>
              <SvgYacd width={200} height={200} c1="var(--color-text)" />
            </div>
          )}
        </div>
      </div>
      <div className="fabgrp">
        <Button text="Close" start={iconClose} onClick={openCloseAllModal} />
      </div>
      <ModalCloseAllConnections
        isOpen={isCloseAllModalOpen}
        primaryButtonOnTap={closeAllConnections}
        onRequestClose={closeCloseAllModal}
      />
    </div>
  );
}

const mapState = s => ({
  apiConfig: getClashAPIConfig(s)
});

export default connect(mapState)(Conn);