diff --git a/packages/frontend/src/components/Chain.css b/packages/frontend/src/components/Chain.css index 121502c..a10f18f 100644 --- a/packages/frontend/src/components/Chain.css +++ b/packages/frontend/src/components/Chain.css @@ -54,20 +54,6 @@ bottom: 0; } -.Chain-map-node { - width: 10px; - height: 10px; - background: #d64ca8; - border: 1px solid #000; - border-radius: 6px; - margin-left: -6px; - margin-top: -6px; - position: absolute; - top: 50%; - left: 50%; - cursor: pointer; -} - .Chain-node-list { width: 100%; border-collapse: collapse; diff --git a/packages/frontend/src/components/Chain.tsx b/packages/frontend/src/components/Chain.tsx index 3ea4d59..504df3f 100644 --- a/packages/frontend/src/components/Chain.tsx +++ b/packages/frontend/src/components/Chain.tsx @@ -4,14 +4,8 @@ import { formatNumber, secondsWithPrecision, viewport } from '../utils'; import { Tile, Icon, Node, Ago } from './'; import { Types } from '@dotstats/common'; -import nodeIcon from '../icons/server.svg'; -import nodeTypeIcon from '../icons/terminal.svg'; -import peersIcon from '../icons/broadcast.svg'; -import transactionsIcon from '../icons/inbox.svg'; import blockIcon from '../icons/package.svg'; -import blockHashIcon from '../icons/file-binary.svg'; import blockTimeIcon from '../icons/history.svg'; -import propagationTimeIcon from '../icons/dashboard.svg'; import lastTimeIcon from '../icons/watch.svg'; import worldIcon from '../icons/globe.svg'; @@ -130,14 +124,10 @@ export class Chain extends React.Component { // const location = [48.8589507, 2.2770201] as Types.NodeLocation; // Paris // const location = [36.7183391, -4.5193071]as Types.NodeLocation; // Malaga + const { left, top } = this.pixelPosition(location[0], location[1]); + return ( - + ); }) } @@ -148,19 +138,7 @@ export class Chain extends React.Component { private renderTable() { return ( - - - - - - - - - - - - - + { this.nodes().sort(sortNodes).map((node) => ) diff --git a/packages/frontend/src/components/Node.css b/packages/frontend/src/components/Node.css new file mode 100644 index 0000000..15a9e25 --- /dev/null +++ b/packages/frontend/src/components/Node.css @@ -0,0 +1,43 @@ +.Node-Location { + width: 10px; + height: 10px; + background: #d64ca8; + border: 1px solid #000; + border-radius: 6px; + margin-left: -6px; + margin-top: -6px; + position: absolute; + top: 50%; + left: 50%; + cursor: pointer; +} + +.Node-details { + display: none; + min-width: 335px; + position: absolute; + left: 16px; + top: -4px; + font-family: monospace, sans-serif; + background: #222; + color: #fff; + box-shadow: 0 3px 20px rgba(0,0,0,0.5); + border-collapse: collapse; +} + +.Node-details td { + text-align: left; + padding: 0.5em 1em; +} + +.Node-details td:nth-child(odd) { + padding-right: 0.2em; +} + +.Node-details td:nth-child(even) { + padding-left: 0.2em; +} + +.Node-Location:hover .Node-details { + display: initial; +} diff --git a/packages/frontend/src/components/Node.tsx b/packages/frontend/src/components/Node.tsx index 66f94d1..9f475bd 100644 --- a/packages/frontend/src/components/Node.tsx +++ b/packages/frontend/src/components/Node.tsx @@ -1,8 +1,20 @@ import * as React from 'react'; import { formatNumber, trimHash, milliOrSecond, secondsWithPrecision } from '../utils'; -import { Ago } from './Ago'; +import { Ago, Icon } from './'; import { Types, Maybe } from '@dotstats/common'; +import nodeIcon from '../icons/server.svg'; +import nodeTypeIcon from '../icons/terminal.svg'; +import peersIcon from '../icons/broadcast.svg'; +import transactionsIcon from '../icons/inbox.svg'; +import blockIcon from '../icons/package.svg'; +import blockHashIcon from '../icons/file-binary.svg'; +import blockTimeIcon from '../icons/history.svg'; +import propagationTimeIcon from '../icons/dashboard.svg'; +import lastTimeIcon from '../icons/watch.svg'; + +import './Node.css'; + export namespace Node { export interface Props { id: Types.NodeId; @@ -17,6 +29,24 @@ export namespace Node { top: number; } + export function Header() { + return ( + + + + + + + + + + + + + + ) + } + export function Row(props: Props) { const [name, implementation, version] = props.nodeDetails; const [height, hash, blockTime, blockTimestamp, propagationTime] = props.blockDetails; @@ -39,14 +69,36 @@ export namespace Node { export function Location(props: Props & PixelPosition) { const { left, top } = props; + const [name, implementation, version] = props.nodeDetails; + const [height, hash, blockTime, blockTimestamp, propagationTime] = props.blockDetails; return ( - +
+
+ + + + + + + + + + + + + + + + + + + + + + +
{name}
{implementation} v{version}
#{formatNumber(height)}
{trimHash(hash, 20)}
{secondsWithPrecision(blockTime/1000)}{propagationTime === null ? '∞' : milliOrSecond(propagationTime as number)}
+ ); } } diff --git a/packages/frontend/src/index.tsx b/packages/frontend/src/index.tsx index a7b1306..a7563e0 100644 --- a/packages/frontend/src/index.tsx +++ b/packages/frontend/src/index.tsx @@ -2,10 +2,11 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import App from './App'; import './index.css'; -import registerServiceWorker from './registerServiceWorker'; +import { unregister } from './registerServiceWorker'; ReactDOM.render( , document.getElementById('root') as HTMLElement ); -registerServiceWorker(); + +unregister(); diff --git a/packages/frontend/src/registerServiceWorker.ts b/packages/frontend/src/registerServiceWorker.ts index 28c89f7..0b85282 100644 --- a/packages/frontend/src/registerServiceWorker.ts +++ b/packages/frontend/src/registerServiceWorker.ts @@ -9,111 +9,6 @@ // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. // This link also includes instructions on opting out of this behavior. -const isLocalhost = Boolean( - window.location.hostname === 'localhost' || - // [::1] is the IPv6 localhost address. - window.location.hostname === '[::1]' || - // 127.0.0.1/8 is considered localhost for IPv4. - window.location.hostname.match( - /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ - ) -); - -export default function register() { - if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { - // The URL constructor is available in all browsers that support SW. - const publicUrl = new URL( - process.env.PUBLIC_URL!, - window.location.toString() - ); - if (publicUrl.origin !== window.location.origin) { - // Our service worker won't work if PUBLIC_URL is on a different origin - // from what our page is served on. This might happen if a CDN is used to - // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 - return; - } - - window.addEventListener('load', () => { - const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; - - if (isLocalhost) { - // This is running on localhost. Lets check if a service worker still exists or not. - checkValidServiceWorker(swUrl); - - // Add some additional logging to localhost, pointing developers to the - // service worker/PWA documentation. - navigator.serviceWorker.ready.then(() => { - console.log( - 'This web app is being served cache-first by a service ' + - 'worker. To learn more, visit https://goo.gl/SC7cgQ' - ); - }); - } else { - // Is not local host. Just register service worker - registerValidSW(swUrl); - } - }); - } -} - -function registerValidSW(swUrl: string) { - navigator.serviceWorker - .register(swUrl) - .then(registration => { - registration.onupdatefound = () => { - const installingWorker = registration.installing; - if (installingWorker) { - installingWorker.onstatechange = () => { - if (installingWorker.state === 'installed') { - if (navigator.serviceWorker.controller) { - // At this point, the old content will have been purged and - // the fresh content will have been added to the cache. - // It's the perfect time to display a 'New content is - // available; please refresh.' message in your web app. - console.log('New content is available; please refresh.'); - } else { - // At this point, everything has been precached. - // It's the perfect time to display a - // 'Content is cached for offline use.' message. - console.log('Content is cached for offline use.'); - } - } - }; - } - }; - }) - .catch(error => { - console.error('Error during service worker registration:', error); - }); -} - -function checkValidServiceWorker(swUrl: string) { - // Check if the service worker can be found. If it can't reload the page. - fetch(swUrl) - .then(response => { - // Ensure service worker exists, and that we really are getting a JS file. - if ( - response.status === 404 || - response.headers.get('content-type')!.indexOf('javascript') === -1 - ) { - // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then(registration => { - registration.unregister().then(() => { - window.location.reload(); - }); - }); - } else { - // Service worker found. Proceed as normal. - registerValidSW(swUrl); - } - }) - .catch(() => { - console.log( - 'No internet connection found. App is running in offline mode.' - ); - }); -} - export function unregister() { if ('serviceWorker' in navigator) { navigator.serviceWorker.ready.then(registration => {