From 37b5880fa0bca201e04737e3e4de0d48283eb95a Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:30:59 -0800 Subject: [PATCH] feat: peers table can be filtered (#2181) --- src/peers/PeersTable/PeersTable.js | 274 ++++++++++++++++------------- 1 file changed, 150 insertions(+), 124 deletions(-) diff --git a/src/peers/PeersTable/PeersTable.js b/src/peers/PeersTable/PeersTable.js index e8fcc9255..93de11880 100644 --- a/src/peers/PeersTable/PeersTable.js +++ b/src/peers/PeersTable/PeersTable.js @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import classNames from 'classnames' import ms from 'milliseconds' import { connect } from 'redux-bundler-react' @@ -11,80 +11,60 @@ import { sortByProperty } from '../../lib/sort.js' import './PeersTable.css' -export class PeersTable extends React.Component { - /** - * - * @param {object} props - * @param {Promise} props.peerLocationsForSwarm - * @param {string} props.className - * @param {import('i18next').TFunction} props.t - */ - constructor (props) { - super(props) - - this.state = { - sortBy: 'latency', - sortDirection: SortDirection.ASC, - peerLocationsForSwarm: [] - } - - this.sort = this.sort.bind(this) - } - - flagRenderer = (flagCode, isPrivate) => { - // Check if the OS is Windows to render the flags as SVGs - // Windows doesn't render the flags as emojis ¯\_(ツ)_/¯ - const isWindows = window.navigator.appVersion.indexOf('Win') !== -1 - return ( +const flagRenderer = (flagCode, isPrivate) => { + // Check if the OS is Windows to render the flags as SVGs + // Windows doesn't render the flags as emojis ¯\_(ツ)_/¯ + const isWindows = window.navigator.appVersion.indexOf('Win') !== -1 + return ( {isPrivate ? '🤝' : flagCode ? : '🌐'} - ) - } + ) +} - locationCellRenderer = ({ rowData }) => { - const ref = React.createRef() - const location = rowData.isPrivate - ? this.props.t('localNetwork') - : rowData.location - ? rowData.isNearby - ? {rowData.location} ({this.props.t('nearby')}) - : rowData.location - : {this.props.t('app:terms.unknown')} - const value = rowData.location || this.props.t('app:terms.unknown') - return ( - copyFeedback(ref, this.props.t)}> +const locationCellRenderer = (t) => ({ rowData }) => { + const ref = React.createRef() + const location = rowData.isPrivate + ? t('localNetwork') + : rowData.location + ? rowData.isNearby + ? {rowData.location} ({t('nearby')}) + : rowData.location + : {t('app:terms.unknown')} + const value = rowData.location || t('app:terms.unknown') + return ( + copyFeedback(ref, t)}> - { this.flagRenderer(rowData.flagCode, rowData.isPrivate) } + { flagRenderer(rowData.flagCode, rowData.isPrivate) } { location } - ) - } + ) +} - latencyCellRenderer = ({ cellData, rowData }) => { - const style = { width: '60px' } - const latency = `${cellData}ms` - if (cellData == null) return (-) - return ({latency}) - } +const latencyCellRenderer = ({ cellData }) => { + const style = { width: '60px' } + const latency = `${cellData}ms` + if (cellData == null) return (-) + return ({latency}) +} - peerIdCellRenderer = ({ cellData: peerId }) => { - const ref = React.createRef() - const p2pMultiaddr = `/p2p/${peerId}` - return ( - copyFeedback(ref, this.props.t)}> +const peerIdCellRenderer = (t) => ({ cellData: peerId }) => { + const ref = React.createRef() + const p2pMultiaddr = `/p2p/${peerId}` + return ( + copyFeedback(ref, t)}> - ) - } + ) +} - protocolsCellRenderer = ({ rowData, cellData }) => { - const ref = React.createRef() - const { protocols } = rowData - const title = protocols.split(', ').join('\n') - return ( - copyFeedback(ref, this.props.t)}> +const protocolsCellRenderer = (t) => ({ rowData }) => { + const ref = React.createRef() + const { protocols } = rowData + const title = protocols.split(', ').join('\n') + return ( + copyFeedback(ref, t)}> - ) - } + ) +} - connectionCellRenderer = ({ rowData }) => { - const ref = React.createRef() - const { address, direction, peerId } = rowData - const p2pMultiaddr = `${address}/p2p/${peerId}` - const title = direction != null - ? `${address}\n(${renderDirection(direction, this.props.t)})` - : address +const connectionCellRenderer = (t) => ({ rowData }) => { + const ref = React.createRef() + const { address, direction, peerId } = rowData + const p2pMultiaddr = `${address}/p2p/${peerId}` + const title = direction != null + ? `${address}\n(${renderDirection(direction, t)})` + : address - return ( - copyFeedback(ref, this.props.t)}> + return ( + copyFeedback(ref, t)}> - ) - } - - rowClassRenderer = ({ index }, peers = []) => { - const { selectedPeers } = this.props - const shouldAddHoverEffect = selectedPeers?.peerIds?.includes(peers[index]?.peerId) - - return classNames('bb b--near-white peersTableItem', index === -1 && 'bg-near-white', shouldAddHoverEffect && 'bg-light-gray') - } - - sort ({ sortBy, sortDirection }) { - this.setState({ sortBy, sortDirection }) - } + ) +} - componentWillReceiveProps (nextProps) { - if (nextProps.peerLocationsForSwarm) { - nextProps.peerLocationsForSwarm?.then?.((peerLocationsForSwarm) => { - if (peerLocationsForSwarm !== this.state.peerLocationsForSwarm) { - this.setState({ peerLocationsForSwarm }) - } - }) - } - } +const rowClassRenderer = ({ index }, peers = [], selectedPeers) => { + const shouldAddHoverEffect = selectedPeers?.peerIds?.includes(peers[index]?.peerId) - render () { - const { className, t } = this.props - const { sortBy, sortDirection, peerLocationsForSwarm } = this.state + return classNames('bb b--near-white peersTableItem', index === -1 && 'bg-near-white', shouldAddHoverEffect && 'bg-light-gray') +} - const sortedList = peerLocationsForSwarm.sort(sortByProperty(sortBy, sortDirection === SortDirection.ASC ? 1 : -1)) - const tableHeight = 400 +const FilterInput = ({ setFilter, t, filteredCount }) => { + return ( +
+ setFilter(e.target.value)} + /> + {/* Now to display the total number of peers filtered out on the right side of the inside of the input */} +
{filteredCount}
+
+ ) +} - return ( -
- { peerLocationsForSwarm && +export const PeersTable = ({ className, t, peerLocationsForSwarm, selectedPeers }) => { + const tableHeight = 400 + const [awaitedPeerLocationsForSwarm, setAwaitedPeerLocationsForSwarm] = useState([]) + const [sortBy, setSortBy] = useState('latency') + const [sortDirection, setSortDirection] = useState(SortDirection.ASC) + const [filter, setFilter] = useState('') + + const sort = useCallback(({ sortBy, sortDirection }) => { + setSortBy(sortBy) + setSortDirection(sortDirection) + }, []) + const filterCb = useCallback((value) => { + setFilter(value) + }, []) + + useEffect(() => { + peerLocationsForSwarm?.then?.((peerLocationsForSwarm) => { + setAwaitedPeerLocationsForSwarm(peerLocationsForSwarm) + }) + }, [peerLocationsForSwarm]) + + const filteredPeerList = useMemo(() => { + const filterLower = filter.toLowerCase() + if (filterLower === '') return awaitedPeerLocationsForSwarm + return awaitedPeerLocationsForSwarm.filter(({ location, latency, peerId, connection, protocols }) => { + if (location != null && location.toLowerCase().includes(filterLower)) { + return true + } + if (latency != null && [latency, `${latency}ms`].some((str) => str.toString().includes(filterLower))) { + return true + } + if (peerId != null && peerId.toString().includes(filter)) { + return true + } + console.log('connection: ', connection) + if (connection != null && connection.toLowerCase().includes(filterLower)) { + return true + } + if (protocols != null && protocols.toLowerCase().includes(filterLower)) { + return true + } + + return false + }) + }, [awaitedPeerLocationsForSwarm, filter]) + + const sortedList = useMemo( + () => filteredPeerList.sort(sortByProperty(sortBy, sortDirection === SortDirection.ASC ? 1 : -1)), + [filteredPeerList, sortBy, sortDirection] + ) + + return ( +
+ + { awaitedPeerLocationsForSwarm && {({ width }) => ( - this.rowClassRenderer(rowInfo, peerLocationsForSwarm)} - width={width} - height={tableHeight} - headerHeight={32} - rowHeight={36} - rowCount={peerLocationsForSwarm.length} - rowGetter={({ index }) => sortedList[index]} - sort={this.sort} - sortBy={sortBy} - sortDirection={sortDirection}> - - - - - -
+ <> + rowClassRenderer(rowInfo, awaitedPeerLocationsForSwarm, selectedPeers)} + width={width} + height={tableHeight} + headerHeight={32} + rowHeight={36} + rowCount={sortedList.length} + rowGetter={({ index }) => sortedList[index]} + sort={sort} + sortBy={sortBy} + sortDirection={sortDirection}> + + + + + +
+ )}
}
- ) - } + ) } // API returns integer atm, but that may change in the future