@ant-design/icons#DisconnectOutlined JavaScript Examples

The following examples show how to use @ant-design/icons#DisconnectOutlined. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: RightContent.js    From acy-dex-interface with MIT License 4 votes vote down vote up
GlobalHeaderRight = props => {
  const { global } = props;
  // 选择货币的弹窗
  const [visible, setVisible] = useState(false);
  const [visibleMetaMask, setVisibleMetaMask] = useState(false);
  const [visibleSetting, setVisibleSetting] = useState(false);
  const [only, setOnly] = useState(true);
  // 连接钱包函数
  const { library } = useConstantLoader();
  const { account, chainId, activate, deactivate, active } = useWeb3React();

  const [wallet, setWallet] = useState(localStorage.getItem("wallet"));

  const [selectedNetwork, setSelectedNetwork] = useState("BSC");
  const [supportedWallets, setSupportWallets] = useState([]);

  // 连接钱包 根据localStorage
  useEffect(() => {
    setWallet(localStorage.getItem("wallet"))
    if (!account) {
      if (!wallet) {
        console.log("localStroage dosen't exist");
        localStorage.setItem('wallet', 'binance');
      }
    }
  }, [account]);

  useEffect(() => {
    console.log('test current active', active)
    if (!active)
      deactivate();
  }, [active]);

  // 判断设备
  const [userSystem, setuserSystem] = useState("other");
  const [broswer, setBroswer] = useState();

  function getBroswer() {
    var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串
    var isOpera = userAgent.indexOf("Opera") > -1;
    if (isOpera || userAgent.indexOf("OPR") > -1) {
      console.log(userAgent, 'ymj')
      return "Opera"
    }; //判断是否Opera浏览器
    if (userAgent.indexOf("Firefox") > -1) {
      return "FF";
    } //判断是否Firefox浏览器
    if (userAgent.indexOf("Chrome") > -1) {
      console.log(userAgent, 'ymj')
      return "Chrome";
    }
    if (userAgent.indexOf("Safari") > -1) {
      return "Safari";
    } //判断是否Safari浏览器
    if (userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1 && !isOpera) {
      return "IE";
    }; //判断是否IE浏览器

  }

  useEffect(() => {
    function fIsMobile() {
      return /Android|iPhone|iPad|iPod|BlackBerry|webOS|Windows Phone|SymbianOS|IEMobile|Opera Mini/i.test(navigator.userAgent);
    }
    var u = navigator.userAgent;
    var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端
    var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
    if (u) {
      setuserSystem("isPC");
    } else if (isAndroid) {
      setuserSystem("isAndroid");
    } else if (isiOS) {
      setuserSystem("isiOS");
    } else setuserSystem("other");
    var b = getBroswer();
    setBroswer(b);
  }, [])

  useEffect(() => {
    if (account) {
      localStorage.setItem("login_status", "on");
    }
    else {
      //localStorage.setItem("login_status", "off");
      setNetworkListIndex(0)
    }
  }, [account]);

  const getNoticeData = () => {
    const { notices = [] } = props;
    if (notices.length === 0) {
      return {};
    }
    const newNotices = notices.map(notice => {
      const newNotice = { ...notice };
      if (newNotice.datetime) {
        newNotice.datetime = moment(notice.datetime).fromNow();
      }
      if (newNotice.id) {
        newNotice.key = newNotice.id;
      }
      if (newNotice.extra && newNotice.status) {
        const color = {
          todo: '',
          processing: 'blue',
          urgent: 'red',
          doing: 'gold',
        }[newNotice.status];
        newNotice.extra = (
          <Tag color={color} style={{ marginRight: 0 }}>
            {newNotice.extra}
          </Tag>
        );
      }
      return newNotice;
    });
    return groupBy(newNotices, 'type');
  };

  const getUnreadData = noticeData => {
    const unreadMsg = {};
    Object.entries(noticeData).forEach(([key, value]) => {
      if (!unreadMsg[key]) {
        unreadMsg[key] = 0;
      }
      if (Array.isArray(value)) {
        unreadMsg[key] = value.filter(item => !item.read).length;
      }
    });
    return unreadMsg;
  };

  const onhandCancel = () => {
    setVisibleMetaMask(false);
  };
  const onhandMetaMask = () => {
    setVisibleMetaMask(true);
    setNetworkListIndex(n_index(chainId));
    console.log(broswer, 'ymj')
  };
  const onhandCancelMetaMask = () => {
    setVisibleMetaMask(false);
  };
  const onhandSetting = flag => {
    setVisibleSetting(!!flag);
  };
  const handleVisibleChange = () => { }

  const [isModalVisible, setIsModalVisible] = useState(false);

  const showModal = () => {
    setIsModalVisible(true);
  };

  // 选择钱包
  const selectWallet = walletName => {
    if (walletName != localStorage.getItem("wallet"))
      localStorage.setItem("login_status", "off");
    console.log('selecting wallet', walletName);
    if (walletName === 'metamask') {
      try {
        ethereum;
        activate(injected);
      } catch (error) {
        message.info('No provider was found');
        if (account) {
          localStorage.setItem("login_status", "on");
        }
        return
      }
    } else if (walletName === 'bitkeep') {
      try {
        ethereum;
        activate(injected);
        if (!window.isBitKeep) {
          message.info('Wrong wallet, please make sure other wallet extensions has been closed');
        }
      } catch (error) {
        message.info('No provider was found');
        if (account) {
          localStorage.setItem("login_status", "on");
        }
        return
      }
    }
    else if (walletName === 'opera') {
      activate(injected);
    }
    else if (walletName === 'walletconnect') {
      activate(walletconnect);
    } else if (walletName === 'coinbase') {
      activate(walletlink);
    } else if (walletName === 'fortmatic') {
      activate(fortmatic);
    } else if (walletName === 'portis') {
      activate(portis);
    } else if (walletName === 'torus') {
      activate(torus);
    } else if (walletName === 'trezor') {
      activate(trezor);
    } else if (walletName === 'ledger') {
      activate(ledger);
    } else if (walletName === 'binance') {
      try {
        BinanceChain
        activate(binance);
      } catch (error) {
        message.info('No provider was found');
        if (account) {
          localStorage.setItem("login_status", "on");
        }
        return
      }
    } else if (walletName === 'nabox') {
      try {
        NaboxWallet;
        activate(nabox);
      } catch (error) {
        message.info('No provider was found');
        if (account) {
          localStorage.setItem("login_status", "on");
        }
        return
      }
    } else {
      console.log("wallet ERROR");
    }
    localStorage.setItem('wallet', walletName);
    setVisibleMetaMask(false);
  };
  // 初始网络显示
  const n_index = chainId => {
    if (chainId == 56 || chainId == 97) {
      return 2
    }
    if (chainId == 137 || chainId == 80001) {
      return 3
    }
    if (chainId == 1) {
      return 4
    }
    if (chainId == undefined) {
      return 0
    }
    return 1
  }
  // 通知钱包连接成功
  const checkChainNetwork = (chainId) => {
    if (!chainId) {
      switchEthereumChain("0x38"); //返回默认56链
      return
    }
    console.log("networkChanged:", chainId)
    console.log("supported chain?", supportedChainIds, chainId, supportedChainIds.indexOf(chainId) == -1)
    if (supportedChainIds && supportedChainIds.indexOf(Number(chainId)) == -1) {
      console.log("ERROR: unsupport NetWork");
      setIsModalVisible(true);
      switchEthereumChain("0x38"); //返回默认56链
    }
    else if (chainId == 97) {
      // 测试网 不显示给用户
      switchEthereumChain("0x61");
      setNetworkListIndex(0)
    }
    else {
      setIsModalVisible(false);
      setVisibleMetaMask(false);
      setNetworkListIndex(n_index(chainId))
    }
  }
  useEffect(() => {
    // todo....
    if (account) {
      console.log(account);
      setVisibleMetaMask(false);
    }
    checkChainNetwork(chainId);
    // 监听钱包网络变化 metamask
    if (localStorage.getItem("wallet") == "metamask" || localStorage.getItem("wallet") == "bitkeep") {
      try {
        ethereum.on('networkChanged', function (chainId) {
          checkChainNetwork(chainId);
        })
      } catch (error) {
        console.log("no metamask plugin");
      }
    }
    if (localStorage.getItem("wallet") == "nabox") {
      // Nabox 监听
      try {
        NaboxWallet.on('networkChanged', function (chainId) {
          checkChainNetwork(chainId);
        })
      } catch (error) {
        console.log("no nabox plugin");
      }
    }
    if (localStorage.getItem("wallet") == "binance") {
      try {
        BinanceChain.on('networkChanged', function (chainId) {
          checkChainNetwork(chainId);
        })
      } catch (error) {
        console.log("no binance plugin");
      }
    }
  }, [account, chainId, supportedChainIds, wallet]);
  const {
    currentUser,
    fetchingMoreNotices,
    fetchingNotices,
    loadedAllNotices,
    onNoticeVisibleChange,
    onMenuClick,
    onNoticeClear,
    skeletonCount,
    theme,
    isMobile,
  } = props;
  // const { visible, visibleMetaMask, visibleSetting } = this.state;
  const menu = (
    <Menu className={styles.menu} selectedKeys={[]} onClick={onMenuClick}>
      <Menu.Item key="userCenter">
        <Icon type="user" />
        <FormattedMessage id="menu.account.center" defaultMessage="account center" />
      </Menu.Item>
      <Menu.Item key="userinfo">
        <Icon type="setting" />
        <FormattedMessage id="menu.account.settings" defaultMessage="account settings" />
      </Menu.Item>
      <Menu.Item key="triggerError">
        <Icon type="close-circle" />
        <FormattedMessage id="menu.account.trigger" defaultMessage="Trigger Error" />
      </Menu.Item>
      <Menu.Divider />
      <Menu.Item key="logout">
        <Icon type="logout" />
        <FormattedMessage id="menu.account.logout" defaultMessage="logout" />
      </Menu.Item>
    </Menu>
  );
  const loadMoreProps = {
    skeletonCount,
    loadedAll: loadedAllNotices,
    loading: fetchingMoreNotices,
  };
  // 货币列表
  const MetaMask = [
    {
      name: 'MetaMask',
      icon: 'Metamask',
      onClick: () => {
        selectWallet('metamask');
      },
    },
  ];
  const BinanceWallet = [
    {
      name: 'Binance Wallet',
      icon: 'Binance',
      onClick: () => {
        selectWallet('binance');
      },
    },
  ];
  const OperaWallet = [
    {
      name: 'Opera',
      icon: 'Opera',
      svgicon: true,
      onClick: () => {
        selectWallet('opera');
      },
    },
  ]
  const walletListAllSupported = [
    {
      name: 'MetaMask',
      icon: 'Metamask',
      onClick: () => {
        selectWallet('metamask');
      },
    },
    {
      name: 'Nabox Wallet',
      icon: 'Nabox',
      onClick: () => {
        selectWallet('nabox');
      },
    },
    {
      name: 'Bitkeep Wallet',
      icon: 'Bitkeep',
      onClick: () => {
        selectWallet('bitkeep');
      },
    },
    {
      name: 'WalletConnect',
      icon: 'WalletConnect',
      onClick: () => {
        selectWallet('walletconnect');
      },
    },
    {
      name: 'TrustWallet',
      icon: 'TrustWallet',
      onClick: () => {
        selectWallet('walletconnect');
      },
    },

  ]

  const walletList = [
    {
      name: 'Coinbase Wallet',
      icon: 'Coinbase',
      onClick: () => {
        selectWallet('coinbase');
      },
    },
    {
      name: 'Nabox Wallet',
      icon: 'Nabox',
      onClick: () => {
        selectWallet('nabox');
      },
    },
    {
      name: 'Bitkeep Wallet',
      icon: 'Bitkeep',
      onClick: () => {
        selectWallet('bitkeep');
      },
    },
    {
      name: 'WalletConnect',
      icon: 'WalletConnect',
      onClick: () => {
        selectWallet('walletconnect');
      },
    },
    {
      name: 'TrustWallet',
      icon: 'TrustWallet',
      onClick: () => {
        selectWallet('walletconnect');
      },
    },
    // {
    //   name: 'Trezor',
    //   icon: 'Trezor',
    //   onClick: () => {
    //     selectWallet('trezor');
    //   },
    // },
    // {
    //   name: 'Ledger',
    //   icon: 'Ledger',
    //   onClick: () => {
    //     selectWallet('ledger');
    //   },
    // },
    // {
    //   name: 'Fortmatic',
    //   icon: 'Fortmatic',
    //   onClick: () => {
    //     selectWallet('fortmatic');
    //   },
    // },
    {
      name: 'Portis',
      icon: 'Portis',
      onClick: () => {
        selectWallet('portis');
      },
    },
    {
      name: 'Torus',
      icon: 'Torus',
      onClick: () => {
        selectWallet('torus');
      },
    },
    {
      name: 'Opera',
      icon: 'Opera',
      svgicon: true,
      onClick: () => {
        selectWallet('opera');
      },
    },
  ];
  const supportedWalletEth = [
    {
      name: 'Binance Wallet',
      icon: 'Binance',
      onClick: () => {
        selectWallet('binance');
      },
    },
    {
      name: 'Coinbase Wallet',
      icon: 'Coinbase',
      onClick: () => {
        selectWallet('coinbase');
      },
    },
    {
      name: 'Portis',
      icon: 'Portis',
      onClick: () => {
        selectWallet('portis');
      },
    },
    {
      name: 'Torus',
      icon: 'Torus',
      onClick: () => {
        selectWallet('torus');
      },
    },
  ]
  const supportedWalletBsc = [
    {
      name: 'Binance Wallet',
      icon: 'Binance',
      onClick: () => {
        selectWallet('binance');
      },
    },
  ]
  const supportedWalletPolygon = [
    {
      name: 'Torus',
      icon: 'Torus',
      onClick: () => {
        selectWallet('torus');
      },
    },
  ]

  const networkParams = {
    "0x38": {
      chainId: '0x38',
      chainName: 'Binance Smart Chain Netwaok',
      nativeCurrency: {
        name: 'Binance',
        symbol: 'BNB', // 2-6 characters long
        decimals: 18
      },
      blockExplorerUrls: ['https://bscscan.com'],
      rpcUrls: ['https://bsc-dataseed.binance.org/'],
    },
    "0x61": {
      chainId: '0x61',
      chainName: 'Binance Smart Chain Testnet',
      nativeCurrency: {
        name: 'Binance',
        symbol: 'BNB', // 2-6 characters long
        decimals: 18
      },
      blockExplorerUrls: ['https://testnet.bscscan.com'],
      rpcUrls: ['https://data-seed-prebsc-1-s1.binance.org:8545/'],
    },
    "0x89": {
      chainId: '0x89',
      chainName: 'Polygon',
      nativeCurrency: {
        name: 'Matic',
        symbol: 'MATIC', // 2-6 characters long
        decimals: 18
      },
      blockExplorerUrls: ['https://polygonscan.com/'],
      rpcUrls: ['https://polygon-rpc.com/'],
    },
    "0xA4B1": {
      chainId: '0xA4B1',
      chainName: 'Arbitrum',
      nativeCurrency: {
        name: 'ETH',
        symbol: 'ETH', // 2-6 characters long
        decimals: 18
      },
      blockExplorerUrls: ['https://arbiscan.io/'],
      rpcUrls: ['https://arb1.arbitrum.io/rpc'],
    },
  };

  const switchEthereumChain = async (chainId) => {
    if (localStorage.getItem("wallet") == "metamask") {
      try {
        await ethereum.request({
          method: 'wallet_switchEthereumChain',
          params: [{ chainId: chainId }],
        });
      } catch (e) {
        if (e.code === 4902) {
          try {
            await ethereum.request({
              method: 'wallet_addEthereumChain',
              params: [
                networkParams[chainId]
              ],
            });
          } catch (addError) {
            console.error(addError);
          }
        }
      }
    }
    else if (localStorage.getItem("wallet") == "nabox") {
      try {
        await NaboxWallet.request({
          method: 'wallet_switchEthereumChain',
          params: [{ chainId: chainId }],
        });
      } catch (e) {
        if (e.code === 4902) {
          try {
            await NaboxWallet.request({
              method: 'wallet_addEthereumChain',
              params: [
                networkParams[chainId]
              ],
            });
          } catch (addError) {
            console.error(addError);
          }
        }
      }
    }
    else if (localStorage.getItem("wallet") == "binance") {
      try {
        await BinanceChain.request({
          method: 'wallet_switchEthereumChain',
          params: [{ chainId: chainId }],
        });
      } catch (e) {
        if (e.code === 4902) {
          try {
            await BinanceChain.request({
              method: 'wallet_addEthereumChain',
              params: [
                networkParams[chainId]
              ],
            });
          } catch (addError) {
            console.error(addError);
          }
        }
      }
    }
    if (chainId == 1) {
      setSelectedNetwork('ETH');
    }
    else if (chainId == 56) {
      setSelectedNetwork('BNBChain');
    }
    else if (chainId == 137) {
      setSelectedNetwork('Polygon');
    }
  }
  const showMore = () => {
    setOnly(!only);
  };
  const noticeData = getNoticeData();
  const unreadMsg = getUnreadData(noticeData);
  let className = styles.right;
  if (theme === 'dark') {
    className = `${styles.right}  ${styles.dark}`;
  }
  // 网络列表
  const [networkListIndex, setNetworkListIndex] = useState(0);
  const networkList = [
    {
      name: 'NO Network',
      //icon: 'Polygon',
      onClick: async () => {
        setVisibleMetaMask(false);
      },
      onClick_showSupportedWallet: async () => {

      },
    },
    {
      name: 'Wrong Network',
      //icon: 'Polygon',
      onClick: async () => {
        setVisibleMetaMask(false);
      },
      onClick_showSupportedWallet: async () => {

      },
    },
    {
      name: 'BNB Chain',
      icon: 'Binance',
      onClick: async () => {
        await switchEthereumChain("0x38");
        setVisibleMetaMask(false);
      },
      onClick_showSupportedWallet: async () => {
        setSelectedNetwork('BNB Chain');
        setSupportWallets(supportedWalletBsc);
      },
    },
    {
      name: 'Polygon',
      icon: 'Polygon',
      onClick: async () => {
        await switchEthereumChain("0x89");
        setVisibleMetaMask(false);
      },
      onClick_showSupportedWallet: async () => {
        setSelectedNetwork('Polygon');
        setSupportWallets(supportedWalletPolygon);
      },
    },
    {
      name: 'Ethereum',
      icon: 'Eth',
      onClick: async () => {
        await switchEthereumChain("0x1");
        setVisibleMetaMask(false);
      },
      onClick_showSupportedWallet: async () => {
        setSelectedNetwork('Ethereum');
        setSupportWallets(supportedWalletEth);
      },
    },
    // {
    //   name: 'Arbitrum',
    //   icon: 'Arbitrum',
    //   onClick: async () => {
    //     await switchEthereumChain("0xA4B1");
    //     setVisibleMetaMask(false);
    //   },
    // },
  ];
  const networkListInCardList = (
    <div className={styles.networkListBlock}>
      <div className={styles.networkTitle}>
        <span>Select a Network</span>
      </div>
      <AcyCardList>
        {networkList.slice(2,).map((item) => {
          return (
            <AcyCardList.Thin className={styles.networkListLayout} onClick={() => item.onClick()}>
              {
                <AcyIcon.MyIcon width={20} type={item.icon} />
              }
              <span>{item.name}</span>
            </AcyCardList.Thin>
          );
        }
        )}
      </AcyCardList>
    </div>
  );
  const deactivateTest = () => {
    deactivate();
    localStorage.setItem("login_status", 'off')
  }

  return (
    <div className={className}>
      <Row wrap={false} style={{display: "inline-flex", fontSize: "0.7rem"}}>
        <Col flex="none">
          {/* <Button onClick={deactivateTest}>disconnected</Button> */}
          <Dropdown
            overlay={networkListInCardList}
            trigger={['click']}
            placement="bottomLeft"  
          //className={styles.networkButton}
          >
            <div type="primary" shape="round" className={styles.networkButton}>
              {[networkList[networkListIndex]].map(item => (
                <div>
                  <AcyIcon.MyIcon type={item.icon} /> {item.name} <DownOutlined /></div>
                //<Icon><DownOutlined /></Icon>
              ))}
            </div>
          </Dropdown>
        </Col>
        <Col flex="auto">
        {/* <AcyIcon onClick={this.onhandConnect} name="acy" /> */}
          <AcyConnectWallet
            chainId={chainId} // this is the chainId from useWeb3React
            isMobile={isMobile}
            onClick={onhandMetaMask}
            pendingLength={
              props.transaction.transactions.length
            }
          />
        </Col>
      </Row>


      {false && (
        <Dropdown
          overlay={
            <div className={styles.setting} onClick={e => e.preventDefault()}>
              <ul className={styles.list}>
                <li>
                  <AcySeting title="Gas Price" current="Rapid(85.1gwei) ~3">
                    <AcyRadioButton data={['Rapid(85.1gwei) ~3', 'Rapid(67.1gwei) ~6']} />
                  </AcySeting>
                </li>
                <li>
                  <AcySeting title="Endpoint" current="Global 122.0ms">
                    <AcyRadioButton data={['Global 122.0ms', 'Custom']} />
                  </AcySeting>
                </li>
                <li>
                  <AcySeting title="Network" current="ETH">
                    <AcyRadioButton
                      data={['ETH', 'BSC', 'Heco', 'Polygon', 'Arbitrum', 'OkChain']}
                    />
                  </AcySeting>
                </li>
                {/* <li style={{borderBottom:'1px solid rgb(64, 64, 64)',paddingBottom:'20px'}}>
                <AcySeting title="Language" current="English">
                  <AcyRadioButton data={['English', '中文']} />
                </AcySeting>
              </li> */}
                {/* <li style={{marginTop:'30px'}}>
                  <a className={styles.setitem}>
                    <Icon type="question-circle" />Help
                  </a>
                  <a className={styles.setitem}>
                  <Icon type="notification" />Announcements
                  </a>
                  <a className={styles.setitem}>
                  <Icon type="message" />Forum
                  </a>
              </li> */}
              </ul>
            </div>
          }
          trigger={['click']}
          placement="bottomLeft"
          visible={visibleSetting}
          onVisibleChange={onhandSetting}
        >
          <AcyIcon
            width={30}
            name={visibleSetting ? 'colors-active' : 'colors'}
            onClick={() => onhandSetting(true)}
          />
        </Dropdown>
      )}


      {!account && <div>
        <AcyModal width={420} visible={visibleMetaMask} onCancel={onhandCancel}
          bodyStyle={{
            padding: '21px',
            background: 'black',
            // backgroundColor: '#1b1b1c',
            borderRadius: ' 10px',
            // boxShadow: '0 0 14px #2d2d2d'
            border: '0.75px solid #333333',
          }}>
          <div className={styles.networkTitle}>
            <span>1. Select a Network</span>
          </div>
          {/*ymj*/}
          <AcyCardList grid={true}>
            {networkList.slice(2,).map((item) => {
              return (
                <AcyCardList.Thin className={selectedNetwork == item.name ? styles.networkListLayout_selected : styles.networkListLayout} onClick={() => {
                  item.onClick_showSupportedWallet()
                }}>
                  {
                    <AcyIcon.MyIcon width={20} type={item.icon} />
                  } 
                  <span>{item.name}</span>
                  {selectedNetwork == item.name &&
                    <span className={styles.networkListLayout_selectedCheck}>
                      <svg xmlns="http://www.w3.org/2000/svg" fill="#eb5c20" style={{ height: '18px' }} viewBox="0 0 50 50">
                        <path d="M21.05 33.1 35.2 18.95 32.9 16.7 21.05 28.55 15.05 22.55 12.8 24.8ZM24 44Q19.75 44 16.1 42.475Q12.45 40.95 9.75 38.25Q7.05 35.55 5.525 31.9Q4 28.25 4 24Q4 19.8 5.525 16.15Q7.05 12.5 9.75 9.8Q12.45 7.1 16.1 5.55Q19.75 4 24 4Q28.2 4 31.85 5.55Q35.5 7.1 38.2 9.8Q40.9 12.5 42.45 16.15Q44 19.8 44 24Q44 28.25 42.45 31.9Q40.9 35.55 38.2 38.25Q35.5 40.95 31.85 42.475Q28.2 44 24 44ZM24 24Q24 24 24 24Q24 24 24 24Q24 24 24 24Q24 24 24 24Q24 24 24 24Q24 24 24 24Q24 24 24 24Q24 24 24 24ZM24 41Q31.25 41 36.125 36.125Q41 31.25 41 24Q41 16.75 36.125 11.875Q31.25 7 24 7Q16.75 7 11.875 11.875Q7 16.75 7 24Q7 31.25 11.875 36.125Q16.75 41 24 41Z"/>
                      </svg>
                      {/* <CheckCircleTwoTone style={{color: '#eb5c20'}} /> */}
                    </span>}
                </AcyCardList.Thin>
              );
            }
            )}
          </AcyCardList>
          <div className={styles.walltitle}>
            <span style={{ marginLeft: '10px' }}>2. Select a Wallet</span>
          </div>


          {/* <AcyCardList>
          {MetaMask.map(item => (
            <AcyCardList.Thin onClick={() => item.onClick()}>
              {(item.svgicon && <Opera width={32} style={{ margin: '5px' }} />) || (
                <AcyIcon.MyIcon width={32} type={item.icon} />
              )}
              <span className={styles.fontBold}>{item.name}</span>
            </AcyCardList.Thin>
          ))}
          {BinanceWallet.map(item => (
            <AcyCardList.Thin onClick={() => item.onClick()}>
              {(item.svgicon && <Opera width={32} style={{ margin: '5px' }} />) || (
                <AcyIcon.MyIcon width={32} type={item.icon} />
              )}
              <span className={styles.fontBold}>{item.name}</span>
            </AcyCardList.Thin>
          ))}
        </AcyCardList> */}

          <AcyCardList grid={true}>
            { /*ymj */}
            {broswer === 'Opera' && OperaWallet.map((item, index) => {

              return (
                <AcyCardList.Thin className={styles.walletList} onClick={() => item.onClick()}>
                  {(item.svgicon && <Opera width={32} style={{ margin: '5px' }} />) || (
                    <AcyIcon.MyIcon width={32} type={item.icon} />
                  )}
                  <span className={styles.fontBold}>{item.name}</span>
                </AcyCardList.Thin>
              );

            })}

            {walletListAllSupported.map((item, index) => {

              return (
                <AcyCardList.Thin className={styles.walletList} onClick={() => item.onClick()}>
                  {(item.svgicon && <Opera width={32} style={{ margin: '5px' }} />) || (
                    <AcyIcon.MyIcon width={32} type={item.icon} />
                  )}
                  <span className={styles.fontBold}>{item.name}</span>
                </AcyCardList.Thin>
              );

            })}
            {supportedWallets.map((item, index) => {

              return (
                <AcyCardList.Thin className={styles.walletList} onClick={() => item.onClick()}>
                  {(item.svgicon && <Opera width={32} style={{ margin: '5px' }} />) || (
                    <AcyIcon.MyIcon width={32} type={item.icon} />
                  )}
                  <span className={styles.fontBold}>{item.name}</span>
                </AcyCardList.Thin>
              );

            })}
          </AcyCardList>
        </AcyModal>
      </div>}
      {account && <div>
        <AcyModal width={200} height={100} visible={visibleMetaMask} onCancel={onhandCancel}
          bodyStyle={{
            padding: '21px',
            background: '#2e3032',
            borderRadius: ' 20px',
          }}>
          <div type="primary" shape="round" className={styles.disconnectBtn} onClick={deactivateTest}><DisconnectOutlined /> Disconnect</div>
        </AcyModal>
      </div>}

      {/* 错误弹窗*/}
      <AcyModal width={420} visible={isModalVisible}>
        <p>ERROR: UNSUPPORT NETWORKS</p>
        <p>We are not ready for this networks, please change your networks!</p>
      </AcyModal>

    </div>
  );
}
Example #2
Source File: App.js    From next-terminal with GNU Affero General Public License v3.0 4 votes vote down vote up
render() {

        const menu = (
            <Menu>

                <Menu.Item>
                    <Link to={'/info'}>
                        <SolutionOutlined/> 个人中心
                    </Link>
                </Menu.Item>
                <Menu.Divider/>

                <Menu.Item>

                    <Popconfirm
                        key='login-btn-pop'
                        title="您确定要退出登录吗?"
                        onConfirm={this.confirm}
                        okText="确定"
                        cancelText="取消"
                        placement="left"
                    >
                        <LogoutOutlined/> 退出登录
                    </Popconfirm>
                </Menu.Item>

            </Menu>
        );

        return (

            <Switch>
                <Route path="/access" component={Access}/>
                <Route path="/term" component={Term}/>
                <Route path="/login"><Login updateUser={this.updateUser}/></Route>

                <Route path="/">
                    <Layout className="layout" style={{minHeight: '100vh'}}>

                        {
                            isAdmin() ?
                                <>
                                    <Sider collapsible collapsed={this.state.collapsed} trigger={null}>
                                        <div className="logo">
                                            <img src={this.state.logo} alt='logo' width={this.state.logoWidth}/>
                                        </div>

                                        <Menu
                                            onClick={(e) => this.setCurrent(e.key)}
                                            selectedKeys={[this.state.current]}
                                            onOpenChange={this.subMenuChange}
                                            defaultOpenKeys={this.state.openKeys}
                                            theme="dark" mode="inline" defaultSelectedKeys={['dashboard']}
                                            inlineCollapsed={this.state.collapsed}
                                            style={{lineHeight: '64px'}}>

                                            <Menu.Item key="dashboard" icon={<DashboardOutlined/>}>
                                                <Link to={'/'}>
                                                    控制面板
                                                </Link>
                                            </Menu.Item>

                                            <SubMenu key='resource' title='资源管理' icon={<CloudServerOutlined/>}>
                                                <Menu.Item key="asset" icon={<DesktopOutlined/>}>
                                                    <Link to={'/asset'}>
                                                        资产列表
                                                    </Link>
                                                </Menu.Item>
                                                <Menu.Item key="credential" icon={<IdcardOutlined/>}>
                                                    <Link to={'/credential'}>
                                                        授权凭证
                                                    </Link>
                                                </Menu.Item>
                                                <Menu.Item key="dynamic-command" icon={<CodeOutlined/>}>
                                                    <Link to={'/dynamic-command'}>
                                                        动态指令
                                                    </Link>
                                                </Menu.Item>
                                                <Menu.Item key="access-gateway" icon={<ApiOutlined/>}>
                                                    <Link to={'/access-gateway'}>
                                                        接入网关
                                                    </Link>
                                                </Menu.Item>
                                            </SubMenu>

                                            <SubMenu key='audit' title='会话审计' icon={<AuditOutlined/>}>
                                                <Menu.Item key="online-session" icon={<LinkOutlined/>}>
                                                    <Link to={'/online-session'}>
                                                        在线会话
                                                    </Link>
                                                </Menu.Item>

                                                <Menu.Item key="offline-session" icon={<DisconnectOutlined/>}>
                                                    <Link to={'/offline-session'}>
                                                        历史会话
                                                    </Link>
                                                </Menu.Item>
                                            </SubMenu>
                                            <SubMenu key='ops' title='系统运维' icon={<ControlOutlined/>}>
                                                <Menu.Item key="login-log" icon={<LoginOutlined/>}>
                                                    <Link to={'/login-log'}>
                                                        登录日志
                                                    </Link>
                                                </Menu.Item>

                                                <Menu.Item key="job" icon={<BlockOutlined/>}>
                                                    <Link to={'/job'}>
                                                        计划任务
                                                    </Link>
                                                </Menu.Item>

                                                <Menu.Item key="access-security" icon={<SafetyCertificateOutlined/>}>
                                                    <Link to={'/access-security'}>
                                                        访问安全
                                                    </Link>
                                                </Menu.Item>
                                                <Menu.Item key="storage" icon={<HddOutlined/>}>
                                                    <Link to={'/storage'}>
                                                        磁盘空间
                                                    </Link>
                                                </Menu.Item>
                                            </SubMenu>

                                            <SubMenu key='user-manage' title='用户管理' icon={<UserSwitchOutlined/>}>
                                                <Menu.Item key="user" icon={<UserOutlined/>}>
                                                    <Link to={'/user'}>
                                                        用户管理
                                                    </Link>
                                                </Menu.Item>
                                                <Menu.Item key="user-group" icon={<TeamOutlined/>}>
                                                    <Link to={'/user-group'}>
                                                        用户组管理
                                                    </Link>
                                                </Menu.Item>
                                                <Menu.Item key="strategy" icon={<InsuranceOutlined/>}>
                                                    <Link to={'/strategy'}>
                                                        授权策略
                                                    </Link>
                                                </Menu.Item>
                                            </SubMenu>
                                            <Menu.Item key="my-file" icon={<FolderOutlined/>}>
                                                <Link to={'/my-file'}>
                                                    我的文件
                                                </Link>
                                            </Menu.Item>
                                            <Menu.Item key="info" icon={<SolutionOutlined/>}>
                                                <Link to={'/info'}>
                                                    个人中心
                                                </Link>
                                            </Menu.Item>
                                            <Menu.Item key="setting" icon={<SettingOutlined/>}>
                                                <Link to={'/setting'}>
                                                    系统设置
                                                </Link>
                                            </Menu.Item>
                                        </Menu>
                                    </Sider>

                                    <Layout className="site-layout">
                                        <Header className="site-layout-background"
                                                style={{padding: 0, height: headerHeight, zIndex: 20}}>
                                            <div className='layout-header'>
                                                <div className='layout-header-left'>
                                                    {React.createElement(this.state.collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
                                                        className: 'trigger',
                                                        onClick: this.onCollapse,
                                                    })}
                                                </div>

                                                <div className='layout-header-right'>
                                                    <div className={'layout-header-right-item'}>
                                                        <a style={{color: 'black'}} target='_blank'
                                                           href='https://github.com/dushixiang/next-terminal'
                                                           rel='noreferrer noopener'>
                                                            <GithubOutlined/>
                                                        </a>
                                                    </div>
                                                </div>

                                                <div className='layout-header-right'>
                                                    <Dropdown overlay={menu}>
                                                        <div className='nickname layout-header-right-item'>
                                                            {getCurrentUser()['nickname']} &nbsp;<DownOutlined/>
                                                        </div>
                                                    </Dropdown>
                                                </div>
                                            </div>
                                        </Header>

                                        <Route path="/" exact component={Dashboard}/>
                                        <Route path="/user" component={User}/>
                                        <Route path="/user-group" component={UserGroup}/>
                                        <Route path="/asset" component={Asset}/>
                                        <Route path="/credential" component={Credential}/>
                                        <Route path="/dynamic-command" component={DynamicCommand}/>
                                        <Route path="/batch-command" component={BatchCommand}/>
                                        <Route path="/online-session" component={OnlineSession}/>
                                        <Route path="/offline-session" component={OfflineSession}/>
                                        <Route path="/login-log" component={LoginLog}/>
                                        <Route path="/info" component={Info}/>
                                        <Route path="/setting" component={Setting}/>
                                        <Route path="/job" component={Job}/>
                                        <Route path="/access-security" component={Security}/>
                                        <Route path="/access-gateway" component={AccessGateway}/>
                                        <Route path="/my-file" component={MyFile}/>
                                        <Route path="/storage" component={Storage}/>
                                        <Route path="/strategy" component={Strategy}/>

                                        <Footer style={{textAlign: 'center'}}>
                                            Copyright © 2020-2022 dushixiang, All Rights Reserved.
                                            Version:{this.state.package['version']}
                                        </Footer>
                                    </Layout>
                                </> :
                                <>
                                    <Header style={{padding: 0}}>
                                        <div className='km-header'>
                                            <div style={{flex: '1 1 0%'}}>
                                                <Link to={'/'}>
                                                    <img src={this.state.logo} alt='logo' width={120}/>
                                                </Link>

                                                <Link to={'/my-file'}>
                                                    <Button type="text" style={{color: 'white'}}
                                                            icon={<FolderOutlined/>}>
                                                        文件
                                                    </Button>
                                                </Link>

                                                <Link to={'/dynamic-command'}>
                                                    <Button type="text" style={{color: 'white'}}
                                                            icon={<CodeOutlined/>}>
                                                        指令
                                                    </Button>
                                                </Link>
                                            </div>
                                            <div className='km-header-right'>
                                                <Dropdown overlay={menu}>
                                                <span className={'km-header-right-item'}>
                                                    {getCurrentUser()['nickname']}
                                                </span>
                                                </Dropdown>
                                            </div>
                                        </div>
                                    </Header>
                                    <Content className='km-container'>
                                        <Layout>
                                            <Route path="/" exact component={MyAsset}/>
                                            <Content className={'kd-content'}>
                                                <Route path="/info" component={Info}/>
                                                <Route path="/my-file" component={MyFile}/>
                                                <Route path="/dynamic-command" component={DynamicCommand}/>
                                            </Content>
                                        </Layout>
                                    </Content>
                                    <Footer style={{textAlign: 'center'}}>
                                        Copyright © 2020-2022 dushixiang, All Rights Reserved.
                                        Version:{this.state.package['version']}
                                    </Footer>
                                </>
                        }


                    </Layout>
                </Route>
            </Switch>

        );
    }
Example #3
Source File: OnlineSession.js    From next-terminal with GNU Affero General Public License v3.0 4 votes vote down vote up
render() {

        const columns = [{
            title: '序号',
            dataIndex: 'id',
            key: 'id',
            render: (id, record, index) => {
                return index + 1;
            }
        }, {
            title: '来源IP',
            dataIndex: 'clientIp',
            key: 'clientIp'
        }, {
            title: '接入方式',
            dataIndex: 'mode',
            key: 'mode',
            render: (text) => {
                return (
                    <Tag color={MODE_COLORS[text]}>{text}</Tag>
                )
            }
        }, {
            title: '用户昵称',
            dataIndex: 'creatorName',
            key: 'creatorName'
        }, {
            title: '资产名称',
            dataIndex: 'assetName',
            key: 'assetName'
        }, {
            title: '连接协议',
            dataIndex: 'protocol',
            key: 'protocol',
            render: (text, record) => {
                const title = `${record.username}@${record.ip}:${record.port}`;
                return (
                    <Tooltip title={title}>
                        <Tag color={PROTOCOL_COLORS[text]}>{text}</Tag>
                    </Tooltip>
                )
            }
        }, {
            title: '接入时间',
            dataIndex: 'connectedTime',
            key: 'connectedTime',
            render: (text, record) => {
                return (
                    <Tooltip title={text}>
                        {dayjs(text).fromNow()}
                    </Tooltip>
                )
            }
        }, {
            title: '接入时长',
            dataIndex: 'connectedTime',
            key: 'connectedTime',
            render: (text, record) => {
                return differTime(new Date(record['connectedTime']), new Date());
            }
        },
            {
                title: '操作',
                key: 'action',
                render: (text, record) => {

                    return (
                        <div>
                            <Button type="link" size='small' onClick={() => {
                                this.showMonitor(record)
                            }}>监控</Button>
                            <Button type="link" size='small' onClick={async () => {

                                confirm({
                                    title: '您确定要断开此会话吗?',
                                    content: '',
                                    okText: '确定',
                                    okType: 'danger',
                                    cancelText: '取消',
                                    onOk() {
                                        dis(record.id)
                                    }
                                });

                                const dis = async (id) => {
                                    const result = await request.post(`/sessions/${id}/disconnect`);
                                    if (result.code === 1) {
                                        notification['success']({
                                            message: '提示',
                                            description: '断开成功',
                                        });
                                        this.loadTableData();
                                    } else {
                                        notification['success']({
                                            message: '提示',
                                            description: result.message,
                                        });
                                    }
                                }

                            }}>断开</Button>
                        </div>
                    )
                },
            }
        ];

        const selectedRowKeys = this.state.selectedRowKeys;
        const rowSelection = {
            selectedRowKeys: selectedRowKeys,
            onChange: (selectedRowKeys, selectedRows) => {
                this.setState({selectedRowKeys});
            },
        };
        const hasSelected = selectedRowKeys.length > 0;

        const userOptions = this.state.users.map(d => <Select.Option key={d.id}
                                                                     value={d.id}>{d.nickname}</Select.Option>);
        const assetOptions = this.state.assets.map(d => <Select.Option key={d.id}
                                                                       value={d.id}>{d.name}</Select.Option>);

        return (
            <>
                <Content className="site-layout-background page-content">

                    <div style={{marginBottom: 20}}>
                        <Row justify="space-around" align="middle" gutter={24}>
                            <Col span={8} key={1}>
                                <Title level={3}>在线会话列表</Title>
                            </Col>
                            <Col span={16} key={2} style={{textAlign: 'right'}}>
                                <Space>

                                    <Search
                                        ref={this.inputRefOfClientIp}
                                        placeholder="来源IP"
                                        allowClear
                                        onSearch={this.handleSearchByClientIp}
                                    />

                                    <Select
                                        style={{width: 150}}
                                        showSearch
                                        value={this.state.queryParams.userId}
                                        placeholder='用户昵称'
                                        onSearch={this.handleSearchByNickname}
                                        onChange={this.handleChangeByUserId}
                                        filterOption={false}
                                        allowClear
                                    >
                                        {userOptions}
                                    </Select>

                                    <Select
                                        style={{width: 150}}
                                        showSearch
                                        value={this.state.queryParams.assetId}
                                        placeholder='资产名称'
                                        onSearch={this.handleSearchByAssetName}
                                        onChange={this.handleChangeByAssetId}
                                        filterOption={false}
                                    >
                                        {assetOptions}
                                    </Select>

                                    <Select onChange={this.handleChangeByProtocol}
                                            value={this.state.queryParams.protocol ? this.state.queryParams.protocol : ''}
                                            style={{width: 100}}>
                                        <Select.Option value="">全部协议</Select.Option>
                                        <Select.Option value="rdp">rdp</Select.Option>
                                        <Select.Option value="ssh">ssh</Select.Option>
                                        <Select.Option value="vnc">vnc</Select.Option>
                                        <Select.Option value="telnet">telnet</Select.Option>
                                        <Select.Option value="kubernetes">kubernetes</Select.Option>
                                    </Select>

                                    <Tooltip title='重置查询'>

                                        <Button icon={<UndoOutlined/>} onClick={() => {
                                            this.inputRefOfClientIp.current.setValue('');
                                            this.loadTableData({
                                                pageIndex: 1,
                                                pageSize: 10,
                                                protocol: '',
                                                userId: undefined,
                                                assetId: undefined
                                            })
                                        }}>

                                        </Button>
                                    </Tooltip>

                                    <Divider type="vertical"/>

                                    <Tooltip title="刷新列表">
                                        <Button icon={<SyncOutlined/>} onClick={() => {
                                            this.loadTableData(this.state.queryParams)
                                        }}>

                                        </Button>
                                    </Tooltip>

                                    <Tooltip title="批量断开">
                                        <Button type="primary" danger disabled={!hasSelected}
                                                icon={<DisconnectOutlined/>}
                                                loading={this.state.delBtnLoading}
                                                onClick={() => {
                                                    const content = <div>
                                                        您确定要断开选中的<Text style={{color: '#1890FF'}}
                                                                       strong>{this.state.selectedRowKeys.length}</Text>个会话吗?
                                                    </div>;
                                                    confirm({
                                                        icon: <ExclamationCircleOutlined/>,
                                                        content: content,
                                                        onOk: () => {
                                                            this.batchDis()
                                                        },
                                                        onCancel() {

                                                        },
                                                    });
                                                }}>

                                        </Button>
                                    </Tooltip>

                                </Space>
                            </Col>
                        </Row>
                    </div>

                    <Table rowSelection={rowSelection}
                           dataSource={this.state.items}
                           columns={columns}
                           position={'both'}
                           pagination={{
                               showSizeChanger: true,
                               current: this.state.queryParams.pageIndex,
                               pageSize: this.state.queryParams.pageSize,
                               onChange: this.handleChangPage,
                               total: this.state.total,
                               showTotal: total => `总计 ${total} 条`
                           }}
                           loading={this.state.loading}
                    />

                    {
                        this.state.accessVisible ?
                            <Modal
                                className='modal-no-padding'
                                title={this.state.sessionTitle}

                                maskClosable={false}
                                visible={this.state.accessVisible}
                                footer={null}
                                width={window.innerWidth * 0.8}
                                height={window.innerWidth * 0.8 / this.state.sessionWidth * this.state.sessionHeight}
                                onCancel={() => {
                                    message.destroy();
                                    this.setState({accessVisible: false})
                                }}
                            >
                                {
                                    this.state.sessionMode === 'guacd' ?
                                        <AccessMonitor sessionId={this.state.sessionId}
                                                       width={this.state.sessionWidth}
                                                       height={this.state.sessionHeight}
                                                       protocol={this.state.sessionProtocol}
                                                       rate={window.innerWidth * 0.8 / this.state.sessionWidth}>

                                        </AccessMonitor> :
                                        <TermMonitor sessionId={this.state.sessionId}
                                                     width={this.state.sessionWidth}
                                                     height={this.state.sessionHeight}>

                                        </TermMonitor>
                                }


                            </Modal> : undefined
                    }

                </Content>
            </>
        );
    }
Example #4
Source File: Status.js    From network-rc with Apache License 2.0 4 votes vote down vote up
export default function Status({
  statusInfo,
  piPowerOff,
  wsConnected,
  delay = 0,
  isFullscreen,
  setting,
  isLogin,
  session,
  changeEditabled,
  channelStatus,
  changeChannel,
  serverConfig,
  version,
  connectType,
  onCarMicphoneChange,
  locked,
  onControllerMicphoneChange = () => {},
  enabledControllerMicphone = true,
}) {
  const isWebRTC = connectType === "webrtc";
  const { sharedEndTime } = serverConfig;

  const gamepadPress = ({ detail: { index, value } }) => {
    if (index === 3 && value > 0.5) {
      onControllerMicphoneChange(!enabledControllerMicphone);
    }
  };

  useKeyPress(
    "t",
    () => onControllerMicphoneChange(!enabledControllerMicphone),
    {
      events: ["keyup"],
    }
  );

  useEventListener("gamepadpress", gamepadPress);

  return (
    <Form layout="inline" className="app-status" size="small">
      <Form.Item>
        <Link to={`${process.env.PUBLIC_URL}/controller`}>
          <img className="logo" src="/logo-256.png" alt="N-RC" />
        </Link>
        <span>N RC</span>
      </Form.Item>
      <Form.Item>
        <Tag
          style={{
            width: "7em",
          }}
          icon={
            locked ? (
              <StopOutlined />
            ) : wsConnected ? (
              <LinkOutlined />
            ) : (
              <DisconnectOutlined />
            )
          }
          color={locked || delay > 80 || !wsConnected ? "red" : "green"}
          size="xs"
        >
          {isWebRTC ? "直连" : "中转"}:{delay.toFixed(0)}
        </Tag>
      </Form.Item>
      {(serverConfig.channelList || [])
        .filter(({ enabled, type }) => enabled && type === "switch")
        .map(({ pin, name }) => (
          <Form.Item key={pin}>
            <Switch
              checked={channelStatus[pin] || false}
              checkedChildren={name}
              unCheckedChildren={name}
              onChange={(value) => changeChannel({ pin, value })}
            />
          </Form.Item>
        ))}

      {isLogin && isWebRTC && (
        <Form.Item>
          <Switch
            checked={enabledControllerMicphone}
            onChange={onControllerMicphoneChange}
            checkedChildren={
              <>
                <DesktopOutlined /> <AudioOutlined />
              </>
            }
            unCheckedChildren={
              <>
                <DesktopOutlined /> <AudioMutedOutlined />
              </>
            }
          />
        </Form.Item>
      )}

      {isLogin && (
        <Form.Item>
          <AudioPlayer
            session={session}
            connectType={connectType}
            onMicphoneChange={onCarMicphoneChange}
            url={`${
              window.location.protocol === "https:" ? "wss://" : "ws://"
            }${setting.host}/microphone`}
          />
        </Form.Item>
      )}

      <Form.Item>
        <Switch
          checkedChildren={<FormOutlined />}
          unCheckedChildren={<FormOutlined />}
          onChange={changeEditabled}
        ></Switch>
      </Form.Item>

      <Form.Item>
        <Link to={`${process.env.PUBLIC_URL}/setting`}>
          <Button
            size="small"
            icon={<SettingOutlined />}
            shape="circle"
          ></Button>
        </Link>
      </Form.Item>

      {document.body.requestFullscreen && (
        <Form.Item>
          <Button
            type="primary"
            shape="circle"
            icon={
              isFullscreen ? <FullscreenExitOutlined /> : <FullscreenOutlined />
            }
            onClick={() => {
              if (isFullscreen) {
                document.exitFullscreen();
              } else {
                document.body.requestFullscreen();
              }
            }}
          ></Button>
        </Form.Item>
      )}

      {wsConnected && isLogin && (
        <Form.Item>
          <Button
            type="danger"
            shape="circle"
            icon={<PoweroffOutlined />}
            onClick={piPowerOff}
          ></Button>
        </Form.Item>
      )}

      {wsConnected &&
        isLogin &&
        Object.keys(statusInfo).map((key) => {
          const { color, label, value } = statusInfo[key];
          return !["gps"].includes(label) ? (
            <Form.Item key={key}>
              <Tag color={color}>
                {label}:{value}
              </Tag>
            </Form.Item>
          ) : undefined;
        })}

      {wsConnected && sharedEndTime && (
        <Form.Item>
          <Tag
            icon={<HourglassOutlined />}
            color={session && session.endTime && "orange"}
          >
            {((sharedEndTime - new Date().getTime()) / 1000).toFixed(0)}s
          </Tag>
        </Form.Item>
      )}

      {version && (
        <Form.Item>
          <Tag>v{version}</Tag>
        </Form.Item>
      )}
    </Form>
  );
}