From fcdc2740149c3a9f81bdf22f00ee2efc6b74d116 Mon Sep 17 00:00:00 2001 From: Marco van Dijk Date: Tue, 8 Mar 2022 17:02:15 +0100 Subject: [PATCH] Big layout update --- src/BlockViewer.js | 3 + src/OrchAddressViewer.js | 4 +- src/OrchDelegatorViewer.js | 47 +++++ src/OrchInfoViewer.js | 115 ++++++++++++ src/actions/livepeer.js | 12 +- src/eventButton.js | 58 +++--- src/eventViewer.js | 246 ++++++++++++------------- src/grafana.js | 2 +- src/livepeer.js | 90 ++++++--- src/orchestratorViewer.js | 161 ++++++---------- src/reducers/livepeer/livepeerstate.js | 5 +- src/statViewer.js | 10 +- src/style.css | 34 +++- 13 files changed, 489 insertions(+), 298 deletions(-) create mode 100644 src/OrchDelegatorViewer.js create mode 100644 src/OrchInfoViewer.js diff --git a/src/BlockViewer.js b/src/BlockViewer.js index 0ce2757..bb6eb8b 100644 --- a/src/BlockViewer.js +++ b/src/BlockViewer.js @@ -7,6 +7,9 @@ const Block = (obj) => { const [thisDate, thisTime] = dateObj.toISOString().split('T'); return (
+ + + 🔗{obj.block} diff --git a/src/OrchAddressViewer.js b/src/OrchAddressViewer.js index 9e02dff..3af74c7 100644 --- a/src/OrchAddressViewer.js +++ b/src/OrchAddressViewer.js @@ -3,9 +3,9 @@ import ReactTooltip from "react-tooltip"; const Address = (obj) => { return ( -
+
-
+
{obj.address}
diff --git a/src/OrchDelegatorViewer.js b/src/OrchDelegatorViewer.js new file mode 100644 index 0000000..05f3b4f --- /dev/null +++ b/src/OrchDelegatorViewer.js @@ -0,0 +1,47 @@ +import React from "react"; +import ScrollContainer from "react-indiana-drag-scroll"; +import Address from "./OrchAddressViewer"; + +const OrchDelegatorViewer = (obj) => { + let delegators = obj.delegators; + if (delegators && delegators.length) { + return ( +
+
+
+

{delegators.length} Current Delegators

+
+
+ +
+ { + delegators.map((delObj, idx) => { + return ( +
+
+
+

{parseFloat(delObj.bondedAmount).toFixed(2)} LPT since round {delObj.startRound}

+
+
+ ) + }) + } +
+
+
+
+
+ ) + } + return ( +
+
+
+

The selected Orchestrator has no Delegators

+
+
+
+ ) +} + +export default OrchDelegatorViewer; \ No newline at end of file diff --git a/src/OrchInfoViewer.js b/src/OrchInfoViewer.js new file mode 100644 index 0000000..e908ce9 --- /dev/null +++ b/src/OrchInfoViewer.js @@ -0,0 +1,115 @@ +import React from "react"; +import Stat from "./statViewer"; +import ReactTooltip from "react-tooltip"; +import Address from "./OrchAddressViewer"; + +function updateClipboard(newClip) { + navigator.clipboard.writeText(newClip).then( + () => { + console.log("Copied!"); + }, + () => { + console.log("Copy failed!"); + } + ); +} + +function copyLink(addr) { + navigator.permissions + .query({ name: "clipboard-write" }) + .then((result) => { + if (result.state === "granted" || result.state === "prompt") { + updateClipboard(addr); + } + }); +} + +const OrchInfoViewer = (obj) => { + let rewardCut = 0; + let feeCut = 0; + let totalStake = 0; + let totalVolumeETH = 0; + let totalVolumeUSD = 0; + let selfStake = 0; + let selfStakeRatio = 0; + let thisUrl = ""; + let thisID = ""; + if (obj.totalStake && obj.totalStake > 0) { + if (obj.rewardCut) { + rewardCut = (obj.rewardCut / 10000).toFixed(2); + } + if (obj.feeShare) { + feeCut = (100 - (obj.feeShare / 10000)).toFixed(2); + } + if (obj.totalStake) { + totalStake = parseFloat(obj.totalStake).toFixed(2); + } + if (obj.totalVolumeETH) { + totalVolumeETH = parseFloat(obj.totalVolumeETH * 1).toFixed(4); + } + if (obj.totalVolumeUSD) { + totalVolumeUSD = parseFloat(obj.totalVolumeUSD * 1).toFixed(2); + } + if (obj.delegator) { + selfStake = parseFloat(obj.delegator.bondedAmount); + selfStakeRatio = ((selfStake / totalStake) * 100).toFixed(2); + selfStake = selfStake.toFixed(2); + thisID = obj.delegator.id; + thisUrl = "https://explorer.livepeer.org/accounts/" + thisID; + } + + let shareUrl; + if (obj.rootOnly) { + shareUrl = window.location.href; + } else { + let thisFullPath = window.location.href; + if (thisFullPath.lastIndexOf("?") > -1) { + thisFullPath = thisFullPath.substring(0, thisFullPath.lastIndexOf("?")); + } + shareUrl = thisFullPath + "?orchAddr=" + thisID; + } + + return ( +
+
+ +
+ +
+
+ +
+
+ +
+
+ + + Copy to clipboard + +
+
+
+ ) + } + return ( +
+
+
+

The selected Orchestrator is currently inactive

+
+
+
+ ) +} + +export default OrchInfoViewer; \ No newline at end of file diff --git a/src/actions/livepeer.js b/src/actions/livepeer.js index 7fb2a87..acd018d 100644 --- a/src/actions/livepeer.js +++ b/src/actions/livepeer.js @@ -17,6 +17,7 @@ export const RECEIVE_BLOCKCHAIN_DATA = "RECEIVE_BLOCKCHAIN_DATA"; export const RECEIVE_EVENTS = "RECEIVE_EVENTS"; export const RECEIVE_CURRENT_ORCHESTRATOR = "RECEIVE_CURRENT_ORCHESTRATOR"; export const RECEIVE_ORCHESTRATOR = "RECEIVE_ORCHESTRATOR"; +export const CLEAR_ORCHESTRATOR = "CLEAR_ORCHESTRATOR"; const setQuotes = message => ({ type: RECEIVE_QUOTES, message @@ -33,6 +34,9 @@ const setCurrentOrchestratorInfo = message => ({ const setOrchestratorInfo = message => ({ type: RECEIVE_ORCHESTRATOR, message }); +const clearOrchestratorInfo = () => ({ + type: CLEAR_ORCHESTRATOR +}) export const getQuotes = () => async dispatch => { const response = await apiUtil.getQuotes(); @@ -127,7 +131,7 @@ export const getEvents = () => async dispatch => { else if (eventContainsRebond) { eventType = "Stake"; eventColour = stakeColour; - eventDescription = "increased their stake to " + tmpAmount.toFixed(2) + " LPT at"; + eventDescription = "is now staking " + tmpAmount.toFixed(2) + " LPT"; } // Fill description of Stake Event if it wasn't set yet @@ -136,7 +140,7 @@ export const getEvents = () => async dispatch => { eventDescription = "staked " + tmpAmount.toFixed(2) + " LPT"; } else if (eventFrom === eventTo) { eventFrom = ""; - eventDescription = "increased their stake to " + tmpAmount.toFixed(2) + " LPT"; + eventDescription = "is now staking " + tmpAmount.toFixed(2) + " LPT"; } else { eventDescription = "moved a " + tmpAmount.toFixed(2) + " LPT stake"; } @@ -369,4 +373,8 @@ export const getOrchestratorInfo = (orchAddr) => async dispatch => { } } return dispatch(receiveErrors(data)); +}; + +export const clearOrchestrator = () => async dispatch => { + return dispatch(clearOrchestratorInfo({})); }; \ No newline at end of file diff --git a/src/eventButton.js b/src/eventButton.js index c0c238d..bf41c1b 100644 --- a/src/eventButton.js +++ b/src/eventButton.js @@ -10,7 +10,7 @@ import Block from "./BlockViewer"; const EventButton = (obj) => { const dispatch = useDispatch(); - let eventArrow; + let eventTo; let eventFrom; let eventCaller; @@ -21,13 +21,14 @@ const EventButton = (obj) => { if (obj.eventObj.eventTo === "0x0000000000000000000000000000000000000000") { obj.eventObj.eventTo = ""; } - if (obj.eventObj.eventTo !== "" || obj.eventObj.eventFrom !== "") { - eventArrow =

; - } if (obj.eventObj.eventTo || obj.eventObj.eventFrom || obj.eventObj.eventCaller) { if (obj.eventObj.eventTo) { eventTo = -
+
+

To

+ + + @@ -38,7 +39,11 @@ const EventButton = (obj) => { } if (obj.eventObj.eventFrom) { eventFrom = -
+
+

From

+ + + @@ -49,7 +54,11 @@ const EventButton = (obj) => { } if (obj.eventObj.eventCaller) { eventCaller = -
+
+

Caller

+ + + @@ -58,40 +67,29 @@ const EventButton = (obj) => {
} - eventRightAddr =
- {eventFrom} - {eventArrow} - {eventTo} + eventRightAddr =
+
} let blockNumber; if (obj.isFirstOfBlock) { - blockNumber = + blockNumber = } return (
{blockNumber} -
-
- - - - - - -
- {eventCaller} -
+
+
+ {eventCaller} +

+ 💬 {obj.eventObj.eventDescription} +

+ {eventFrom} + {eventTo}
-
- - {obj.eventObj.eventDescription} - - {eventRightAddr} -
-
+
) } diff --git a/src/eventViewer.js b/src/eventViewer.js index d8341db..4981fe3 100644 --- a/src/eventViewer.js +++ b/src/eventViewer.js @@ -17,7 +17,6 @@ const defaultMaxShown = 100; const defaultIncrementMaxShown = 100; const EventViewer = (obj) => { - const [searchTerm, setSearchTerm] = useState(obj.prefill || ""); const [amountFilter, setAmountFilter] = useState("0"); const [maxAmount, setMaxAmount] = useState(defaultMaxShown); const [filterActivated, setFilterActivated] = useState(true); @@ -49,35 +48,29 @@ const EventViewer = (obj) => { let prevBlock = 0; let showMoreBlock; if (maxAmount < limitShown) { - showMoreBlock =
-

- -
+ showMoreBlock = } let showLessBlock; if (defaultMaxShown < maxAmount) { - showLessBlock =
-

- -
+ showLessBlock = } else { showLessBlock =
} let searchTermText; - if (searchTerm !== "") { - if (searchTerm.length > 15) { - searchTermText =

Only showing addresses containing {searchTerm.substring(0, 15)}...

+ if (obj.searchTerm !== "") { + if (obj.searchTerm.length > 15) { + searchTermText =

Only showing addresses containing {obj.searchTerm.substring(0, 15)}...

} else { - searchTermText =

Only showing addresses containing {searchTerm}

+ searchTermText =

Only showing addresses containing {obj.searchTerm}

} } else { searchTermText =

Filter by Orchestrator address

@@ -95,11 +88,11 @@ const EventViewer = (obj) => { } } // Filter name on from, to, caller - if (searchTerm !== "") { + if (obj.searchTerm !== "") { let isFiltered = true; - if (eventObj.eventCaller.toLowerCase().includes(searchTerm.toLowerCase())) isFiltered = false; - if (eventObj.eventFrom.toLowerCase().includes(searchTerm.toLowerCase())) isFiltered = false; - if (eventObj.eventTo.toLowerCase().includes(searchTerm.toLowerCase())) isFiltered = false; + if (eventObj.eventCaller.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false; + if (eventObj.eventFrom.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false; + if (eventObj.eventTo.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false; if (isFiltered) continue; } // Filter Events on filter buttons @@ -164,7 +157,7 @@ const EventViewer = (obj) => { eventList.push(); } else { prevBlock = eventObj.transactionBlock; @@ -173,81 +166,69 @@ const EventViewer = (obj) => { eventObj={eventObj} isFirstOfBlock={prevBlock} time={eventObj.transactionTime} - setSearchTerm={setSearchTerm} + setSearchTerm={obj.setSearchTerm} />); } } } - return ( -
-
-
- {showLessBlock} + let filterBit; + if (obj.showFilter) { + filterBit =
+
+

Showing max {maxAmount} results

- {showMoreBlock} -
-
-
- {searchTermText} - setSearchTerm(evt.target.value)} - placeholder='Filter by Orchestrator address' - type="text" - /> +
+
+ {showLessBlock} +
+
+ {showMoreBlock} +
-
+
+
+
+ {searchTermText} +
+
+ obj.setSearchTerm(evt.target.value)} + placeholder='Filter by Orchestrator address' + type="text" + /> +
+ +
+
+
+
+
+

{parseFloat(amountFilter) > 0 ? ("Only showing higher than " + amountFilter) : "Filter by minimum value"}

+
+
-

+ setAmountFilter(evt.target.value)} + placeholder='Filter by minimum value' + type="number" + />
-

- -
-
-

- -
-
-

{parseFloat(amountFilter) > 0 ? ("Only showing higher than " + amountFilter) : "Filter by minimum value"}

- setAmountFilter(evt.target.value)} - placeholder='Filter by minimum value' - type="number" - /> -
-
-

- -
-
-

-

-
- -
- {eventList} +
+ } + + return ( +
+ {filterBit} +
+
+
+ +
+
+ {eventList} +
+
+
+
+ + + + + + + +
- -
- - - - - - -
diff --git a/src/grafana.js b/src/grafana.js index 9173212..d1a19c9 100644 --- a/src/grafana.js +++ b/src/grafana.js @@ -44,7 +44,7 @@ const Grafana = (obj) => {
-
+

${lptPrice}

diff --git a/src/livepeer.js b/src/livepeer.js index 88f7cd6..c986d8f 100644 --- a/src/livepeer.js +++ b/src/livepeer.js @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react' import './style.css'; import { Navigate, useSearchParams } from "react-router-dom"; import { useSelector, useDispatch } from 'react-redux' -import { getOrchestratorInfo } from "./actions/livepeer"; +import { getOrchestratorInfo, clearOrchestrator } from "./actions/livepeer"; import EventViewer from "./eventViewer"; import Orchestrator from "./orchestratorViewer"; import Stat from "./statViewer"; @@ -11,14 +11,18 @@ import Stat from "./statViewer"; const Livepeer = (obj) => { const [prefill, setPrefill] = useSearchParams(); - const dispatch = useDispatch(); + const [searchTerm, setSearchTerm] = useState(""); + const dispatch = useDispatch(); const livepeer = useSelector((state) => state.livepeerstate); const [redirectToHome, setRedirectToHome] = useState(false); + const [showFilter, setShowFilter] = useState(false); + const [showSidebar, setShowSidebar] = useState(true); console.log("Rendering Livepeer"); useEffect(() => { if (prefill.get('orchAddr') && prefill.get('orchAddr') !== "") { dispatch(getOrchestratorInfo(prefill.get('orchAddr'))); + setSearchTerm(prefill.get('orchAddr')); } }, [prefill]); @@ -123,36 +127,72 @@ const Livepeer = (obj) => { } let thisOrchObj; + let headerString; if (livepeer.selectedOrchestrator) { thisOrchObj = livepeer.selectedOrchestrator; + headerString = "Inspecting " + thisOrchObj.id; + } else { + headerString = "Livepeer Orchestrator Explorer"; + } + + let sidebar; + if (showSidebar){ + sidebar =
+
+
+ +
+
+
+

Smart contract prices

+
+
+ + + + + +
+
+
+
} return ( -
-
- -
- +
+ ); diff --git a/src/orchestratorViewer.js b/src/orchestratorViewer.js index 8986f35..7d26280 100644 --- a/src/orchestratorViewer.js +++ b/src/orchestratorViewer.js @@ -3,6 +3,8 @@ import ScrollContainer from "react-indiana-drag-scroll"; import Stat from "./statViewer"; import ReactTooltip from "react-tooltip"; import Address from "./OrchAddressViewer"; +import OrchDelegatorViewer from "./OrchDelegatorViewer"; +import OrchInfoViewer from "./OrchInfoViewer"; function updateClipboard(newClip) { navigator.clipboard.writeText(newClip).then( @@ -26,118 +28,71 @@ function copyLink(addr) { } const Orchestrator = (obj) => { - let rewardCut = 0; - let feeCut = 0; - let totalStake = 0; - let totalVolumeETH = 0; - let totalVolumeUSD = 0; - let delegators = []; - let selfStake = 0; - let selfStakeRatio = 0; - let thisUrl = ""; - let thisID = ""; if (obj.thisOrchestrator) { - if (obj.thisOrchestrator.rewardCut) { - rewardCut = (obj.thisOrchestrator.rewardCut / 10000).toFixed(2); - } - if (obj.thisOrchestrator.feeShare) { - feeCut = (100 - (obj.thisOrchestrator.feeShare / 10000)).toFixed(2); - } - if (obj.thisOrchestrator.totalStake) { - totalStake = parseFloat(obj.thisOrchestrator.totalStake).toFixed(2); - } - if (obj.thisOrchestrator.totalVolumeETH) { - totalVolumeETH = parseFloat(obj.thisOrchestrator.totalVolumeETH * 1).toFixed(4); - } - if (obj.thisOrchestrator.totalVolumeUSD) { - totalVolumeUSD = parseFloat(obj.thisOrchestrator.totalVolumeUSD * 1).toFixed(2); - } - if (obj.thisOrchestrator.delegators && obj.thisOrchestrator.delegator) { - delegators = obj.thisOrchestrator.delegators; - selfStake = parseFloat(obj.thisOrchestrator.delegator.bondedAmount); - selfStakeRatio = ((selfStake / totalStake) * 100).toFixed(2); - selfStake = selfStake.toFixed(2); - thisID = obj.thisOrchestrator.delegator.id; - thisUrl = "https://explorer.livepeer.org/accounts/" + thisID; - } - - let shareUrl; - if (obj.rootOnly) { - shareUrl = window.location.href; - } else { - let thisFullPath = window.location.href; - if (thisFullPath.lastIndexOf("?") > -1) { - thisFullPath = thisFullPath.substring(0, thisFullPath.lastIndexOf("?")); - } - shareUrl = thisFullPath + "?orchAddr=" + thisID; - } - - return ( -
-
- -
- -
-
- -
-
- -
-
- - - Copy to clipboard - + if (obj.forceVertical) { + return ( +
+
+ +
-
-
-

{delegators.length} Current Delegators

+ ) + }else{ + return ( +
+
+ +
-
- -
- { - delegators.map((delObj, idx) => { - return ( -
-
-
-

{parseFloat(delObj.bondedAmount).toFixed(2)} LPT since round {delObj.startRound}

-
-
- ) - }) - } -
-
+
+ ) + } + } + if (obj.forceVertical) { + return ( +
+
+
+ +

Orchestrator Info

+
+
+

Inspect an Orchestrator by clicking on their address

+
+
+
+ ) + } else { + return ( +
+
+
+ +

Orchestrator Info

+
+
+

Inspect an Orchestrator by clicking on their address

) } - return ( -
-
- -

Orchestrator Info

-
-
-

Click on an orchestrator address in the list below!

-
-
- ) + } export default Orchestrator; \ No newline at end of file diff --git a/src/reducers/livepeer/livepeerstate.js b/src/reducers/livepeer/livepeerstate.js index 3d254e5..4c32e18 100644 --- a/src/reducers/livepeer/livepeerstate.js +++ b/src/reducers/livepeer/livepeerstate.js @@ -3,7 +3,8 @@ import { RECEIVE_BLOCKCHAIN_DATA, RECEIVE_EVENTS, RECEIVE_ORCHESTRATOR, - RECEIVE_CURRENT_ORCHESTRATOR + RECEIVE_CURRENT_ORCHESTRATOR, + CLEAR_ORCHESTRATOR } from "../../actions/livepeer"; export default (state = {}, { type, message }) => { @@ -19,6 +20,8 @@ export default (state = {}, { type, message }) => { return { ...state, thisOrchestrator: message }; case RECEIVE_ORCHESTRATOR: return { ...state, selectedOrchestrator: message }; + case CLEAR_ORCHESTRATOR: + return { ...state, selectedOrchestrator: null }; default: return { ...state }; } diff --git a/src/statViewer.js b/src/statViewer.js index 05b5880..52cde94 100644 --- a/src/statViewer.js +++ b/src/statViewer.js @@ -15,16 +15,16 @@ const Stat = (obj) => {

{obj.header}

-
-
+
+

{obj.title1}

-
-

{obj.content1}

+
+

{obj.content1}

{obj.title2}

-
+

{obj.content2}

diff --git a/src/style.css b/src/style.css index 38aed9b..1f2c4aa 100644 --- a/src/style.css +++ b/src/style.css @@ -140,10 +140,36 @@ svg { from { top: 0; } to { top: calc(100vh - 69px); } } -.metaSidebar { + +#header { + height: 50px; display: flex; - flex-direction: row; + align-items: center; + background-color: rgba(180, 175, 252, 0.80); } +#bodyContent { + height: calc( 100vh - 50px); + max-height: calc( 100vh - 50px); + display: flex; + flex-wrap: nowrap; + overflow: hidden; +} +#sideContent { + background-color: rgba(180, 175, 252, 0.80); + backdrop-filter: blur(6px); + box-shadow: 9px 13px 8px 8px rgba(8, 7, 56, 0.692); + border-bottom-right-radius: 1em; + width: 400px; +} +.mainContent { + overflow: hidden; + justify-content: center; + align-content: center; + align-items: center; + flex-basis: 0; + flex-grow: 999; +} + .fullGrafana { width: 100%; @@ -194,6 +220,10 @@ svg { width: auto; } +.forceWrap{ + flex-direction: column; +} + .stroke { box-sizing: border-box; padding-bottom: 20px;