diff --git a/src/App.tsx b/src/App.tsx index df8f894..ddc8073 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,6 +7,7 @@ import { setupWebsockets } from './helpers/api'; import store from './store'; import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; import SingleBlock from './components/SingleBlock'; +import SearchKernel from './components/SearchKernel'; export default function App() { useEffect(() => { @@ -26,6 +27,7 @@ export default function App() { + diff --git a/src/components/SearchKernel.css b/src/components/SearchKernel.css new file mode 100644 index 0000000..a52481d --- /dev/null +++ b/src/components/SearchKernel.css @@ -0,0 +1,20 @@ +.SearchKernel { + padding: 2em; + min-height: 800px; +} + +.SearchKernel h1 { + font-weight: bold; + font-family: Avenir, sans-serif; + font-size: 30px; +} + +.SearchKernel h2 { + font-weight: bold; + font-family: Avenir, sans-serif; + font-size: 20px; +} + +.SearchKernel a { + text-decoration: none; +} diff --git a/src/components/SearchKernel.tsx b/src/components/SearchKernel.tsx new file mode 100644 index 0000000..4bc9271 --- /dev/null +++ b/src/components/SearchKernel.tsx @@ -0,0 +1,92 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import React, { useEffect, useState } from 'react'; +import './SearchKernel.css'; +import { useParams, Link, Redirect } from 'react-router-dom'; +import { searchKernel } from '../helpers/api'; +import { ReactComponent as LoadingBars } from '../assets/bars.svg'; + +interface Status { + status: string; + message: string; + hash?: string; +} + +const renderStatus = (status: Status) => { + switch (status.status) { + case 'redirect': + return ; + case 'complete': + return ( +
+ +

{status.message}

+

Redirecting...

+
+ ); + case 'loading': + return ( +
+ +

{status.message}

+
+ ); + default: + return ( +

+ {status.message} Go Back +

+ ); + } +}; + +const SearchKernel = () => { + const { nonce, signature } = useParams(); + const [status, setStatus] = useState({ status: 'loading', message: '', hash: '' } as Status); + useEffect(() => { + let timer; + + if (!nonce || !signature) { + setStatus({ + status: 'error', + message: 'Invalid nonce or signature' + }); + return; + } + setStatus({ + status: 'loading', + message: 'Searching for transaction...' + }); + searchKernel(nonce, signature) + .then((response) => { + if (response.blocks.length > 0) { + const block = response.blocks[0]; + const height = block.block.header.height; + const hash = block.block.header.hash; + const message = `Found in block #${height}.`; + setStatus({ + status: 'complete', + message, + hash + }); + timer = setTimeout(() => setStatus({ status: 'redirect', message, hash }), 2000); + } else { + setStatus({ + status: 'error', + message: 'There was an error finding that transaction.' + }); + } + }) + .catch((e) => { + console.error(e); + setStatus({ + status: 'error', + message: 'Transaction not found' + }); + }); + if (timer) return window.clearTimeout(timer); + }, [nonce, signature]); + + return
{renderStatus(status)}
; +}; + +export default SearchKernel; diff --git a/src/helpers/api.ts b/src/helpers/api.ts index bc4367d..ff6f29f 100644 --- a/src/helpers/api.ts +++ b/src/helpers/api.ts @@ -89,6 +89,15 @@ export async function fetchSingleBlock(blockId: string | number): Promise { + try { + const response = await fetch(`${apiUrl}/kernel/${publicNonce}/${signature}`); + return await response.json(); + } catch (e) { + return e; + } +} + export interface Constants { coinbase_lock_height: number; blockchain_version: number;