diff --git a/public/locales/en/peers.json b/public/locales/en/peers.json
index 4bcaf5c4a..b04301fe5 100644
--- a/public/locales/en/peers.json
+++ b/public/locales/en/peers.json
@@ -5,6 +5,9 @@
"address": "Address",
"location": "Location",
"unknownLocation": "Unknown",
+ "latency": "latency",
+ "bootstrapNode": "bootstrap node",
+ "viaRelay": "via <0>{node}0>",
"addConnection": "Add Connection",
"insertPeerAddress": "Insert the peer address you want to connect to.",
"add": "Add",
diff --git a/src/bundles/config.js b/src/bundles/config.js
index 044b348cb..43547220a 100644
--- a/src/bundles/config.js
+++ b/src/bundles/config.js
@@ -38,6 +38,11 @@ bundle.selectGatewayUrl = createSelector(
(config) => getURLFromAddress('Gateway', config) || 'https://ipfs.io'
)
+bundle.selectBootstrapPeers = createSelector(
+ `selectConfigObject`,
+ (config) => config && config.Bootstrap
+)
+
// TODO: this is a work-around for IPFS companion blocking the config API
// see: https://github.com/ipfs-shipyard/ipfs-companion/issues/454
bundle.selectIsConfigBlocked = createSelector(
diff --git a/src/bundles/peer-locations.js b/src/bundles/peer-locations.js
index f45475648..030c3432c 100644
--- a/src/bundles/peer-locations.js
+++ b/src/bundles/peer-locations.js
@@ -146,9 +146,9 @@ export default function (opts) {
selectPeerLocationsForSwarm: createSelector(
'selectPeers',
'selectPeerLocations',
- (peers, locations) => peers && peers.map((peer, idx) => {
+ 'selectBootstrapPeers',
+ (peers, locations, bootstrapPeers) => peers && peers.map(peer => {
const peerId = peer.peer.toB58String()
- const address = peer.addr.toString()
const locationObj = locations[peerId]
const location = toLocationString(locationObj)
const flagCode = locationObj && locationObj.country_code
@@ -156,13 +156,18 @@ export default function (opts) {
locationObj.longitude,
locationObj.latitude
]
+ const connection = parseConnection(peer.addr)
+ const latency = parseLatency(peer.latency)
+ const notes = parseNotes(peer, bootstrapPeers)
return {
peerId,
- address,
location,
flagCode,
- coordinates
+ coordinates,
+ connection,
+ latency,
+ notes
}
})
),
@@ -276,3 +281,37 @@ const toLocationString = loc => {
const { country_name: country, city } = loc
return city && country ? `${city}, ${country}` : country
}
+
+const parseConnection = (multiaddr) => {
+ const opts = multiaddr.toOptions()
+
+ return `${opts.family}・${opts.transport}`
+}
+
+const parseLatency = (latency) => {
+ if (latency === 'n/a') return
+
+ let value = parseInt(latency)
+ const unit = /(s|ms)/.exec(latency)[0]
+
+ value = unit === 's' ? value * 1000 : value
+
+ return `${value}ms`
+}
+
+const parseNotes = (peer, bootstrapPeers) => {
+ const peerId = peer.peer.toB58String()
+ const addr = peer.addr
+ const ipfsAddr = addr.encapsulate(`/ipfs/${peerId}`).toString()
+ const p2pAddr = addr.encapsulate(`/p2p/${peerId}`).toString()
+
+ if (bootstrapPeers.includes(ipfsAddr) || bootstrapPeers.includes(p2pAddr)) {
+ return { type: 'BOOTSTRAP_NODE' }
+ }
+
+ const opts = addr.toOptions()
+
+ if (opts.transport === 'p2p-circuit') {
+ return { type: 'RELAY_NODE', node: opts.host }
+ }
+}
diff --git a/src/bundles/peer-locations.test.js b/src/bundles/peer-locations.test.js
index 61739b054..643d76f8d 100644
--- a/src/bundles/peer-locations.test.js
+++ b/src/bundles/peer-locations.test.js
@@ -45,6 +45,13 @@ function createMockConnectedBundle () {
}
}
+function createMockConfigBundle () {
+ return {
+ name: 'config',
+ selectBootstrapPeers: () => []
+ }
+}
+
const mockPeersBundle = {
name: 'peers',
reducer (state = { data: [] }, action) {
@@ -115,7 +122,8 @@ it('should get locations for peers', async () => {
createPeerLocationsBundle({
// Ensure added peers are all processed concurrently
concurrency: 5
- })
+ }),
+ createMockConfigBundle()
)()
const peers = store.selectPeers()
@@ -151,7 +159,8 @@ it('should fail on non IPv4 address', async () => {
createPeerLocationsBundle({
// Ensure added peers are all processed concurrently
concurrency: 5
- })
+ }),
+ createMockConfigBundle()
)()
const peers = store.selectPeers()
@@ -190,7 +199,8 @@ it('should resolve alternative address for failed address lookup', async () => {
createMockConnectedBundle(),
createMockIpfsBundle(createMockIpfs({ maxLatency: 1 })),
mockPeersBundle,
- createPeerLocationsBundle()
+ createPeerLocationsBundle(),
+ createMockConfigBundle()
)()
const peers = store.selectPeers()
diff --git a/src/bundles/peers.js b/src/bundles/peers.js
index 93457a900..0c466c92a 100644
--- a/src/bundles/peers.js
+++ b/src/bundles/peers.js
@@ -4,7 +4,7 @@ import ms from 'milliseconds'
const bundle = createAsyncResourceBundle({
name: 'peers',
actionBaseType: 'PEERS',
- getPromise: ({ getIpfs }) => getIpfs().swarm.peers()
+ getPromise: ({ getIpfs }) => getIpfs().swarm.peers({ verbose: true })
.then((peers) => peers.sort((a, b) => {
const aAddr = a.addr.toString()
const bAddr = b.addr.toString()
diff --git a/src/peers/PeersTable/PeersTable.js b/src/peers/PeersTable/PeersTable.js
index 5db0b3221..8c749d6ea 100644
--- a/src/peers/PeersTable/PeersTable.js
+++ b/src/peers/PeersTable/PeersTable.js
@@ -1,10 +1,10 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'redux-bundler-react'
-import { translate } from 'react-i18next'
+import { translate, Trans } from 'react-i18next'
import { Table, Column, AutoSizer } from 'react-virtualized'
import CountryFlag from 'react-country-flag'
-import Address from '../../components/address/Address'
+import Cid from '../../components/cid/Cid'
export class PeersTable extends React.Component {
static propTypes = {
@@ -17,8 +17,8 @@ export class PeersTable extends React.Component {
// Windows doesn't render the flags as emojis ¯\_(ツ)_/¯
const isWindows = window.navigator.appVersion.indexOf('Win') !== -1
return (
-
- {flagCode ?