Slighly better stake overview

This commit is contained in:
Marco van Dijk 2022-04-17 13:16:35 +02:00
parent bf708e83c2
commit 124c81bce7
3 changed files with 228 additions and 103 deletions

View File

@ -0,0 +1,203 @@
import React, { useState, useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux';
import { VictoryPie } from 'victory';
import Address from '../components/OrchAddressViewer';
import {
getOrchestratorScores
} from "../actions/livepeer";
const StakeOverview = (obj) => {
const livepeer = useSelector((state) => state.livepeerstate);
const dispatch = useDispatch();
const [thisScores, setThisScores] = useState(null);
const [removeOnlyStakers, setRemoveOnlyStakers] = useState(false);
useEffect(() => {
const now = new Date().getTime();
let wasInCache = false;
// See if it is cached
for (const thisScore of livepeer.orchScores) {
if (thisScore.year === obj.year && thisScore.month === obj.month) {
// Check timeout
if (now - thisScore.timestamp < 360000) {
wasInCache = true;
}
if (!thisScores) {
setThisScores(thisScore);
}
}
}
if (!wasInCache) {
dispatch(getOrchestratorScores(obj.year, obj.month));
}
}, [livepeer.orchScores]);
const getName = (address) => {
let thisDomain = null;
// Lookup domain in cache
if (livepeer.ensDomainMapping) {
for (const thisAddr of livepeer.ensDomainMapping) {
if (thisAddr.address === address) {
thisDomain = thisAddr;
break;
}
}
}
// Lookup current info in cache only if this addr has a mapped ENS domain
if (thisDomain && thisDomain.domain) {
for (const thisAddr of livepeer.ensInfoMapping) {
if (thisAddr.domain === thisDomain.domain) {
return thisAddr.domain;
}
}
}
if (livepeer.threeBoxInfo) {
for (const thisAddr of livepeer.threeBoxInfo) {
if (thisAddr.address === address) {
if (thisAddr.name) {
return thisAddr.name;
} else {
return (address.substring(0, 10) + "..");
}
break;
}
}
}
return (address.substring(0, 10) + "..");
}
let orchList = [];
let pieList = [];
let otherSum = 0;
let pieObj = null;
if (livepeer.orchInfo) {
let orchIdx = livepeer.orchInfo.length - 1;
// calc sum of stake
let totalStake = 0;
while (orchIdx >= 0) {
const thisOrch = livepeer.orchInfo[orchIdx];
orchIdx -= 1;
if (removeOnlyStakers && !parseInt(thisOrch.totalVolumeUSD)) {
continue;
}
totalStake += parseInt(thisOrch.totalStake);
}
// create slices
orchIdx = livepeer.orchInfo.length - 1;
while (orchIdx >= 0) {
const thisOrch = livepeer.orchInfo[orchIdx];
const thisStake = parseInt(thisOrch.totalStake);
orchIdx -= 1;
if (removeOnlyStakers && !parseInt(thisOrch.totalVolumeUSD)) {
continue;
}
// Add orch stat descending
let idx = orchList.length - 1;
while (idx >= 0) {
const sel = orchList[idx];
idx--;
if (sel.sum < thisStake) {
continue;
} else {
break;
}
}
if (idx == orchList.length - 1) {
orchList.push({
address: thisOrch.id,
sum: thisStake,
ratio: (thisStake / totalStake) * 100
});
} else {
orchList.splice(idx + 1, 0, {
address: thisOrch.id,
sum: thisStake,
ratio: (thisStake / totalStake) * 100
});
}
// Add slice
if ((thisStake / totalStake) < 0.04) {
otherSum += thisStake;
} else {
pieList.push({
address: getName(thisOrch.id),
sum: thisStake
});
}
}
pieList.push({
address: "Other",
sum: otherSum
});
pieObj = <div className="stroke">
<h4>Active Orchestrators by Stake</h4>
<div className='row'>
<p>Only Transcoding?</p>
<div className="toggle-container" onClick={() => setRemoveOnlyStakers(!removeOnlyStakers)}>
<div className={`dialog-button ${removeOnlyStakers ? "" : "disabled"}`}>
{removeOnlyStakers ? "1" : "0"}
</div>
</div>
</div>
<VictoryPie padding={100} data={pieList} x="address" y="sum"
sortOrder="descending"
sortKey="sum"
colorScale={[
"#003f5c",
"#2f4b7c",
"#665191",
"#ff7c43",
"#ffa600",
"#5c3446",
"#83424e",
"#a6544e",
"#c16d46",
"#d18d3c",
"#d3b136",
"#c5d843",
"#a3ff69",
]}
style={{
data: {
fillOpacity: 0.9, stroke: "#636363", strokeWidth: 2
},
labels: {
fontSize: 10, zIndex: 999
}
}} />
</div>
}
return (
<div className="stroke">
{pieObj}
<div className="flexContainer forceWrap">
{
orchList.map(function (orch) {
return (
<div className="row" key={"staker" + orch.address + orch.sum}>
<div className="rowAlignLeft">
<Address address={orch.address} seed={"stake" + orch.address + orch.sum} />
</div>
<div className="strokeSmollLeft" style={{ minWidth: '100px' }} >
<div className="rowAlignLeft" >
<h4>{orch.sum.toFixed(2)} LPT</h4>
</div>
<div className="rowAlignRight" >
<span>({orch.ratio.toFixed(2)} %)</span>
</div>
</div>
</div>
)
})
}
</div>
<div className="verticalDivider" />
</div>
)
}
export default StakeOverview;

View File

@ -8,16 +8,15 @@ const EventButtonAddress = (obj) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const livepeer = useSelector((state) => state.livepeerstate); const livepeer = useSelector((state) => state.livepeerstate);
const [hasRefreshed, setRefresh] = useState(false); const [hasRefreshed, setRefresh] = useState(false);
const [hasENS, setHasENS] = useState(false);
const [hasThreeBox, setHasThreeBox] = useState(false);
const [hasThreeBoxRefreshed, setThreeBoxRefresh] = useState(false); const [hasThreeBoxRefreshed, setThreeBoxRefresh] = useState(false);
const [hasOrchInfo, setHasOrchInfo] = useState(false);
const [orchInfo, setOrchInfo] = useState(null); const [orchInfo, setOrchInfo] = useState(null);
const now = new Date().getTime(); const now = new Date().getTime();
useEffect(() => { useEffect(() => {
let thisInfo = null; let thisInfo = null;
let thisDomain = null; let thisDomain = null;
let hasENS = null;
let hasThreeBox = null
// Lookup domain in cache // Lookup domain in cache
if (livepeer.ensDomainMapping && !hasRefreshed) { if (livepeer.ensDomainMapping && !hasRefreshed) {
for (const thisAddr of livepeer.ensDomainMapping) { for (const thisAddr of livepeer.ensDomainMapping) {
@ -29,6 +28,7 @@ const EventButtonAddress = (obj) => {
} }
// Is outdated // Is outdated
if (!hasRefreshed) { if (!hasRefreshed) {
console.log("Refresh due to old ENS domain");
getEnsInfo(obj.address); getEnsInfo(obj.address);
setRefresh(true); setRefresh(true);
} }
@ -37,6 +37,7 @@ const EventButtonAddress = (obj) => {
} }
// If it was not cached at all // If it was not cached at all
if (thisDomain == null && !hasRefreshed) { if (thisDomain == null && !hasRefreshed) {
console.log("Refresh due to non-existing ENS domain");
setRefresh(true); setRefresh(true);
getEnsInfo(obj.address); getEnsInfo(obj.address);
} }
@ -46,13 +47,14 @@ const EventButtonAddress = (obj) => {
for (const thisAddr of livepeer.ensInfoMapping) { for (const thisAddr of livepeer.ensInfoMapping) {
if (thisAddr.domain === thisDomain.domain) { if (thisAddr.domain === thisDomain.domain) {
thisInfo = thisAddr; thisInfo = thisAddr;
setHasENS(true); hasENS = true;
// Check timeout // Check timeout
if (now - thisAddr.timestamp < 86400000) { if (now - thisAddr.timestamp < 86400000) {
break; break;
} }
// Is outdated // Is outdated
if (!hasRefreshed) { if (!hasRefreshed) {
console.log("Refresh due to old ENS info");
getEnsInfo(obj.address); getEnsInfo(obj.address);
setRefresh(true); setRefresh(true);
} }
@ -61,6 +63,7 @@ const EventButtonAddress = (obj) => {
} }
// If it was not cached at all // If it was not cached at all
if (thisInfo == null && !hasRefreshed) { if (thisInfo == null && !hasRefreshed) {
console.log("Refresh due to non-existing ENS info");
getEnsInfo(obj.address); getEnsInfo(obj.address);
setRefresh(true); setRefresh(true);
} }
@ -72,51 +75,41 @@ const EventButtonAddress = (obj) => {
for (const thisAddr of livepeer.threeBoxInfo) { for (const thisAddr of livepeer.threeBoxInfo) {
if (thisAddr.address === obj.address) { if (thisAddr.address === obj.address) {
thisInfo = thisAddr; thisInfo = thisAddr;
setHasThreeBox(true); hasThreeBox = true;
// Check timeout
if (now - thisAddr.timestamp < 86400000) {
break;
}
// Is outdated
if (!hasThreeBoxRefreshed) {
getThreeBoxInfo(obj.address);
setThreeBoxRefresh(true);
}
break; break;
} }
} }
// If it was not cached at all // If it was not cached at all
if (thisDomain == null && !hasThreeBoxRefreshed) { if (!hasThreeBox && !hasThreeBoxRefreshed) {
console.log("Refresh due to non-existing 3BOX info");
setThreeBoxRefresh(true); setThreeBoxRefresh(true);
getThreeBoxInfo(obj.address); getThreeBoxInfo(obj.address);
} }
} }
} }
if (thisInfo && thisInfo != orchInfo){ if (thisInfo && thisInfo != orchInfo){
console.log("Setting INFO obj");
setOrchInfo(thisInfo); setOrchInfo(thisInfo);
} }
}, [livepeer.ensDomainMapping, livepeer.threeBoxInfo, hasRefreshed, hasENS, hasThreeBoxRefreshed]); }, [livepeer.ensDomainMapping, livepeer.threeBoxInfo]);
useEffect(() => { useEffect(() => {
// Check if cached as an orchestrator // Check if cached as an orchestrator
if (livepeer.orchInfo && !hasOrchInfo) { if (livepeer.orchInfo) {
for (const thisOrch of livepeer.orchInfo) { for (const thisOrch of livepeer.orchInfo) {
if (thisOrch.id === obj.address) { if (thisOrch.id === obj.address) {
setHasOrchInfo(true); return;
break;
} }
} }
// Preload Orch info // Preload Orch info
if (!hasOrchInfo) { console.log("Refresh due to non-existing orch in global state");
dispatch(getOrchestratorInfoSilent(obj.address)); dispatch(getOrchestratorInfoSilent(obj.address));
setHasOrchInfo(true);
}
} }
}, [livepeer.orchInfo, hasOrchInfo]); }, [livepeer.orchInfo]);
let thisName; let thisName;
let thisIcon; let thisIcon;
if (hasENS) { if (orchInfo && orchInfo.domain) {
thisName = <h4 className="elipsText elipsOnMobileExtra">{orchInfo.domain}</h4>; thisName = <h4 className="elipsText elipsOnMobileExtra">{orchInfo.domain}</h4>;
if (orchInfo.avatar) { if (orchInfo.avatar) {
thisIcon = thisIcon =
@ -124,7 +117,7 @@ const EventButtonAddress = (obj) => {
<img alt="" src={orchInfo.avatar.url} width="20em" height="20em" style={{ margin: 0, padding: 0 }} /> <img alt="" src={orchInfo.avatar.url} width="20em" height="20em" style={{ margin: 0, padding: 0 }} />
</a > </a >
} }
} else if (hasThreeBox) { } else if (orchInfo && (orchInfo.name || orchInfo.image)) {
if (orchInfo.name) { if (orchInfo.name) {
thisName = <h4 className="elipsText elipsOnMobileExtra">{orchInfo.name}</h4>; thisName = <h4 className="elipsText elipsOnMobileExtra">{orchInfo.name}</h4>;
} else { } else {

View File

@ -5,13 +5,13 @@ import { useSelector, useDispatch } from 'react-redux';
import { Accordion } from '@mantine/core'; import { Accordion } from '@mantine/core';
import ScrollContainer from 'react-indiana-drag-scroll'; import ScrollContainer from 'react-indiana-drag-scroll';
import WinnerMonth from '../components/WinnerMonth'; import WinnerMonth from '../components/WinnerMonth';
import StakeOverview from '../components/StakeOverview';
import { VictoryPie } from 'victory'; import { VictoryPie } from 'victory';
const Tickets = (obj) => { const Tickets = (obj) => {
const livepeer = useSelector((state) => state.livepeerstate); const livepeer = useSelector((state) => state.livepeerstate);
const [ticketsPerMonth, setTicketsPerMonth] = useState([]); const [ticketsPerMonth, setTicketsPerMonth] = useState([]);
const [redirectToHome, setRedirectToHome] = useState(false); const [redirectToHome, setRedirectToHome] = useState(false);
const [removeOnlyStakers, setRemoveOnlyStakers] = useState(false);
console.log("Rendering Winning Ticket Viewer"); console.log("Rendering Winning Ticket Viewer");
@ -176,81 +176,6 @@ const Tickets = (obj) => {
return (address.substring(0, 10) + ".."); return (address.substring(0, 10) + "..");
} }
let pieList = [];
let otherSum = 0;
let pieObj = null;
if (livepeer.orchInfo) {
let orchIdx = livepeer.orchInfo.length - 1;
// calc sum of stake
let totalStake = 0;
while (orchIdx >= 0) {
const thisOrch = livepeer.orchInfo[orchIdx];
orchIdx -= 1;
if (removeOnlyStakers && !parseInt(thisOrch.totalVolumeUSD)) {
continue;
}
totalStake += parseInt(thisOrch.totalStake);
}
// create slices
orchIdx = livepeer.orchInfo.length - 1;
while (orchIdx >= 0) {
const thisOrch = livepeer.orchInfo[orchIdx];
orchIdx -= 1;
if (removeOnlyStakers && !parseInt(thisOrch.totalVolumeUSD)) {
continue;
}
if ((thisOrch.totalStake / totalStake) < 0.04) {
otherSum += thisOrch.totalStake;
} else {
pieList.push({
address: getName(thisOrch.id),
sum: thisOrch.totalStake
});
}
}
pieList.push({
address: "Other",
sum: otherSum
});
pieObj = <div className="stroke">
<h4>Active Orchestrators by Stake</h4>
<div className='row'>
<p>Show non-transcoding Orchestrators?</p>
<div className="toggle-container" onClick={() => setRemoveOnlyStakers(!removeOnlyStakers)}>
<div className={`dialog-button ${removeOnlyStakers ? "" : "disabled"}`}>
{removeOnlyStakers ? "Show" : "Hide"}
</div>
</div>
</div>
<VictoryPie padding={100} data={pieList} x="address" y="sum"
sortOrder="descending"
sortKey="sum"
colorScale={[
"#003f5c",
"#2f4b7c",
"#665191",
"#ff7c43",
"#ffa600",
"#5c3446",
"#83424e",
"#a6544e",
"#c16d46",
"#d18d3c",
"#d3b136",
"#c5d843",
"#a3ff69",
]}
style={{
data: {
fillOpacity: 0.9, stroke: "#636363", strokeWidth: 2
},
labels: {
fontSize: 10, zIndex: 999
}
}} />
</div>
}
return ( return (
<div style={{ margin: 0, padding: 0, height: '100%', width: '100%', overflow: 'hidden' }}> <div style={{ margin: 0, padding: 0, height: '100%', width: '100%', overflow: 'hidden' }}>
<div id='header'> <div id='header'>
@ -272,7 +197,6 @@ const Tickets = (obj) => {
<ScrollContainer activationDistance={1} className="overflow-container" hideScrollbars={false}> <ScrollContainer activationDistance={1} className="overflow-container" hideScrollbars={false}>
<div className="overflow-content" style={{ cursor: 'grab', paddingTop: 0 }}> <div className="overflow-content" style={{ cursor: 'grab', paddingTop: 0 }}>
<div className="flexContainer forceWrap" > <div className="flexContainer forceWrap" >
{pieObj}
<Accordion initialItem={0} className="stroke" <Accordion initialItem={0} className="stroke"
styles={{ styles={{
item: { padding: 0 }, item: { padding: 0 },
@ -284,6 +208,11 @@ const Tickets = (obj) => {
content: { padding: 0, paddingTop: '1em', paddingBottom: '1em' }, content: { padding: 0, paddingTop: '1em', paddingBottom: '1em' },
contentInner: { padding: 0 }, contentInner: { padding: 0 },
}}> }}>
<Accordion.Item
label={"Current Stake Overview"}
className="stroke">
<StakeOverview />
</Accordion.Item>
{ {
ticketsPerMonth.map(function (data) { ticketsPerMonth.map(function (data) {
let thisMonth = ""; let thisMonth = "";