mirror of
https://github.com/stronk-dev/LivepeerEvents.git
synced 2025-07-05 10:45:10 +02:00
Add status text and prework for moroe graphing
This commit is contained in:
parent
5304f7252b
commit
00e84fe122
@ -4,7 +4,8 @@ import Startup from './pages/loadingScreen.js';
|
||||
import Grafana from './pages/grafana.js';
|
||||
import Livepeer from './pages/livepeer.js';
|
||||
import Stats from './pages/stats.js';
|
||||
import Summary from './pages/summary.js'
|
||||
import Summary from './pages/summary.js';
|
||||
import Graphs from './pages/graph.js';
|
||||
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
@ -22,6 +23,7 @@ export default function App() {
|
||||
<Route exact path='/tickets' element={<Stats />} />
|
||||
<Route exact path='/stats' element={<Stats />} />
|
||||
<Route exact path='/summary' element={<Summary />} />
|
||||
<Route exact path='/graphs' element={<Graphs />} />
|
||||
<Route exact path='/orchestrator' element={<Grafana />} />
|
||||
<Route path='/' element={<Home />} />
|
||||
</Routes>
|
||||
|
14
src/components/CommissionGraph.js
Normal file
14
src/components/CommissionGraph.js
Normal file
@ -0,0 +1,14 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
const CommissionGraph = (obj) => {
|
||||
|
||||
|
||||
return (
|
||||
<div className="stroke fullMargin insetEffect" style={{ padding: 0, margin: 0 }}>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CommissionGraph;
|
@ -10,7 +10,6 @@ import { Pagination } from "@mantine/core";
|
||||
const itemsPerPage = 10;
|
||||
|
||||
const MonthlyOrchestrators = (obj) => {
|
||||
const livepeer = useSelector((state) => state.livepeerstate);
|
||||
const [thisScores, setThisScores] = useState(null);
|
||||
const [activePage, setPage] = useState(1);
|
||||
|
||||
|
163
src/components/TotalStakeGraph.js
Normal file
163
src/components/TotalStakeGraph.js
Normal file
@ -0,0 +1,163 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { VictoryLine, VictoryChart, VictoryLegend } from 'victory';
|
||||
|
||||
const TotalStakeGraph = (obj) => {
|
||||
const livepeer = useSelector((state) => state.livepeerstate);
|
||||
|
||||
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) {
|
||||
if (thisAddr.domain.length > 18) {
|
||||
return (thisAddr.domain.substring(0, 16) + "..");
|
||||
}
|
||||
return thisAddr.domain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (livepeer.threeBoxInfo) {
|
||||
for (const thisAddr of livepeer.threeBoxInfo) {
|
||||
if (thisAddr.address === address) {
|
||||
if (thisAddr.name) {
|
||||
if (thisAddr.name.length > 18) {
|
||||
return (thisAddr.name.substring(0, 16) + "..");
|
||||
}
|
||||
return thisAddr.name;
|
||||
} else {
|
||||
return (address.substring(0, 16) + "..");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (address.substring(0, 16) + "..");
|
||||
}
|
||||
|
||||
const colors = [
|
||||
"#282678",
|
||||
"#56256c",
|
||||
"#872974",
|
||||
"#b63074",
|
||||
"#de416d",
|
||||
"#ff5c5f",
|
||||
"#ffe085",
|
||||
"#f0e575",
|
||||
"#daec69",
|
||||
"#bef262",
|
||||
"#97f964",
|
||||
"#5cff6f",
|
||||
"#14ff47",
|
||||
"#00fb94",
|
||||
"#00f3cd",
|
||||
"#00e7f3",
|
||||
"#00d8ff",
|
||||
"#24c8ff",
|
||||
"#0a78ff",
|
||||
"#5864d7",
|
||||
"#6952b1",
|
||||
"#69448d",
|
||||
"#61386c",
|
||||
"#522f50"
|
||||
]
|
||||
let listOfNames = [];
|
||||
let listOfLists = [];
|
||||
let listToParse = [...obj.data];
|
||||
|
||||
// Turn unprocessed list into a per orchestrator list
|
||||
while (listToParse.length) {
|
||||
// Take first orchestrator as the current orchestrator
|
||||
const currentOrchestrator = listToParse[0].address;
|
||||
let thisIdx = listToParse.length - 1;
|
||||
let curData = [];
|
||||
// Split of all orchestrators
|
||||
while (thisIdx >= 0) {
|
||||
const cur = listToParse[thisIdx];
|
||||
if (cur.address == currentOrchestrator) {
|
||||
const thisDate = new Date(cur.timestamp);
|
||||
curData.push({
|
||||
x: thisDate,
|
||||
y: cur.totalStake.toFixed(0)
|
||||
});
|
||||
listToParse.splice(thisIdx, 1);
|
||||
}
|
||||
thisIdx--;
|
||||
}
|
||||
if (curData.length) {
|
||||
listOfNames.push(currentOrchestrator);
|
||||
listOfLists.push(curData);
|
||||
}
|
||||
}
|
||||
|
||||
let legendData = [];
|
||||
|
||||
return (
|
||||
<div className="stroke fullMargin insetEffect" style={{ padding: 0, margin: 0 }}>
|
||||
<div className="row" style={{ marginTop: '1em', marginBottom: '1em', minHeight: '70vh', height: '100%' }}>
|
||||
<div className="stroke" style={{ width: 'unset' }}>
|
||||
<h4>Total stake over time</h4>
|
||||
<VictoryChart
|
||||
width={1920}
|
||||
height={1080}
|
||||
padding={100}
|
||||
fixLabelOverlap={true}
|
||||
>
|
||||
{listOfLists.map((thisData, thisIndex) => {
|
||||
if (thisIndex < 100) {
|
||||
legendData.push({ name: getName(listOfNames[thisIndex]), symbol: { fill: colors[thisIndex % colors.length] } });
|
||||
return (
|
||||
<VictoryLine
|
||||
key={"orch" + thisIndex}
|
||||
padding={0}
|
||||
data={thisData}
|
||||
standalone={false}
|
||||
style={{
|
||||
backgroundColor: 'rgba(122, 128, 127, 0.4)',
|
||||
data: {
|
||||
fillOpacity: 0.9, stroke: colors[thisIndex % colors.length], strokeWidth: 1.5
|
||||
},
|
||||
labels: {
|
||||
fontSize: 24, zIndex: 999
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</VictoryChart>
|
||||
<div className='row'>
|
||||
<VictoryLegend
|
||||
title="Legend"
|
||||
centerTitle
|
||||
orientation="horizontal"
|
||||
gutter={20}
|
||||
width={1920}
|
||||
fixLabelOverlap={true}
|
||||
height={1080}
|
||||
padding={100}
|
||||
style={{ border: { stroke: "black" }, title: { fontSize: 20 } }}
|
||||
data={legendData}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default TotalStakeGraph;
|
164
src/pages/graph.js
Normal file
164
src/pages/graph.js
Normal file
@ -0,0 +1,164 @@
|
||||
import React, { useState } from 'react'
|
||||
import '../style.css';
|
||||
import { Navigate } from "react-router-dom";
|
||||
import { useSelector } from 'react-redux';
|
||||
import { SegmentedControl } from "@mantine/core";
|
||||
import CommissionGraph from '../components/CommissionGraph';
|
||||
import TotalStakeGraph from '../components/TotalStakeGraph';
|
||||
|
||||
|
||||
const Graphs = (obj) => {
|
||||
const livepeer = useSelector((state) => state.livepeerstate);
|
||||
const [redirectToHome, setRedirectToHome] = useState(false);
|
||||
const [showOnlyTranscoders, setShowOnlyTranscoders] = useState(true);
|
||||
const [activePage, setPage] = useState(1);
|
||||
|
||||
let thisColour;
|
||||
if (activePage == 1) {
|
||||
thisColour = "teal";
|
||||
} else if (activePage == 2) {
|
||||
thisColour = "indigo";
|
||||
} else if (activePage == 3) {
|
||||
thisColour = "gray";
|
||||
}
|
||||
console.log("Rendering Graphs Viewer");
|
||||
|
||||
if (redirectToHome) {
|
||||
return <Navigate push to="/" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ margin: 0, padding: 0, height: '100%', width: '100%', overflow: 'hidden' }}>
|
||||
<div id='header'>
|
||||
<div className='rowAlignLeft'>
|
||||
<button className="homeButton" onClick={() => {
|
||||
setRedirectToHome(true);
|
||||
}}>
|
||||
<h1>🏠</h1>
|
||||
</button>
|
||||
<h4 className="rowAlignLeft withWrap showNeverOnMobile">Statistics</h4>
|
||||
</div>
|
||||
<div className='rowAlignRight'>
|
||||
<p>Hide non-earners</p>
|
||||
<div className="toggle-container" onClick={() => setShowOnlyTranscoders(!showOnlyTranscoders)}>
|
||||
<div className={`dialog-button ${showOnlyTranscoders ? "" : "disabled"}`}>
|
||||
{showOnlyTranscoders ? "Show" : "Hide"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id='bodyContent'>
|
||||
<div className="row">
|
||||
<div className="stroke roundedOpaque onlyVerticalScroll" style={{ width: '100vw', height: 'calc( 100vh - 50px - 2em)', marginTop: '2em' }}>
|
||||
<div className="content-wrapper" style={{ width: '100%' }}>
|
||||
<div className="overflow-container">
|
||||
<div className="overflow-content" style={{ padding: 0 }}>
|
||||
<div className="stroke" key={obj.seed + "menu"} style={{ marginTop: '0', paddingTop: '0' }}>
|
||||
<div className="row" style={{ margin: '0' }}>
|
||||
<SegmentedControl
|
||||
styles={{
|
||||
root: { backgroundColor: 'rgba(103, 103, 103, 0.6)', border: 'none', borderColor: 'transparent' },
|
||||
label: {
|
||||
color: 'black',
|
||||
border: 'none',
|
||||
'&:hover': {
|
||||
color: 'black',
|
||||
backgroundColor: 'rgba(56, 56, 56, 0.2)',
|
||||
border: 'none'
|
||||
},
|
||||
'&': {
|
||||
color: 'black',
|
||||
border: 'none'
|
||||
}
|
||||
},
|
||||
labelActive: {
|
||||
color: 'black',
|
||||
border: 'none',
|
||||
'&:hover': {
|
||||
color: 'black',
|
||||
backgroundColor: 'rgba(56, 56, 56, 0.2)',
|
||||
border: 'none'
|
||||
},
|
||||
'&': {
|
||||
color: 'black',
|
||||
border: 'none'
|
||||
}
|
||||
},
|
||||
input: {
|
||||
|
||||
},
|
||||
control: {
|
||||
color: 'black',
|
||||
border: 'none',
|
||||
'&:hover': {
|
||||
color: 'black',
|
||||
border: 'none'
|
||||
},
|
||||
'&': {
|
||||
color: 'black',
|
||||
border: 'none'
|
||||
},
|
||||
'&:not(:first-of-type)': {
|
||||
color: 'black',
|
||||
border: 'none'
|
||||
}
|
||||
|
||||
},
|
||||
controlActive: {
|
||||
color: 'black',
|
||||
border: 'none',
|
||||
'&:hover': {
|
||||
color: 'black',
|
||||
border: 'none'
|
||||
},
|
||||
'&': {
|
||||
color: 'black',
|
||||
border: 'none'
|
||||
}
|
||||
},
|
||||
active: {
|
||||
|
||||
},
|
||||
disabled: {
|
||||
|
||||
},
|
||||
}}
|
||||
value={activePage}
|
||||
onChange={setPage}
|
||||
spacing="lg"
|
||||
size="lg"
|
||||
radius={0}
|
||||
transitionDuration={200}
|
||||
transitionTimingFunction="linear"
|
||||
color={thisColour}
|
||||
data={[
|
||||
{ label: 'Commission', value: '1' },
|
||||
{ label: 'Stake', value: '2' }
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
activePage == 1 ? <CommissionGraph
|
||||
data={livepeer.allCommissions}
|
||||
seed={"commission"}
|
||||
/> : null
|
||||
}
|
||||
{
|
||||
activePage == 2 ? <TotalStakeGraph
|
||||
data={livepeer.allTotalStakes}
|
||||
seed={"totalstake"}
|
||||
/> : null
|
||||
}
|
||||
<div className="verticalDivider" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
export default Graphs;
|
@ -15,6 +15,7 @@ const Home = (obj) => {
|
||||
const [redirectToGrafana, setRedirectToGrafana] = useState(false);
|
||||
const [redirectToLPT, setRedirectToLPT] = useState(false);
|
||||
const [redirectToStats, setRedirectToStats] = useState(false);
|
||||
const [redirectToGraphs, setRedirectToGraphs] = useState(false);
|
||||
if (redirectToGrafana) {
|
||||
return <Navigate push to="/orchestrator" />;
|
||||
}
|
||||
@ -24,6 +25,10 @@ const Home = (obj) => {
|
||||
if (redirectToStats) {
|
||||
return <Navigate push to="/stats" />;
|
||||
}
|
||||
|
||||
if (redirectToGraphs) {
|
||||
return <Navigate push to="/graphs" />;
|
||||
}
|
||||
// Get amount of unique IP's which have visited this website
|
||||
var totalVisitorCount = 0;
|
||||
if (userstate.visitorStats) {
|
||||
@ -38,7 +43,7 @@ const Home = (obj) => {
|
||||
</div>
|
||||
<div className="verticalDivider" />
|
||||
<div className="flexContainer">
|
||||
<div className="stroke roundedOpaque">
|
||||
<div className="stroke roundedOpaque" style={{maxWidth: '400px'}}>
|
||||
<div className="verticalDivider" />
|
||||
<div className="row">
|
||||
<h3>Home</h3>
|
||||
@ -71,11 +76,25 @@ const Home = (obj) => {
|
||||
<p>📈 Statistics 💰</p>
|
||||
</button>
|
||||
</div>
|
||||
<div className="row">
|
||||
<button className="waveButton" onClick={() => {
|
||||
setRedirectToGraphs(true);
|
||||
}}>
|
||||
<p>📉 Graphs 📊</p>
|
||||
</button>
|
||||
</div>
|
||||
<div className="verticalDivider" />
|
||||
<div className="row">
|
||||
<ContractPrices quotes={livepeer.quotes} blockchains={livepeer.blockchains} />
|
||||
</div>
|
||||
<div className="verticalDivider" />
|
||||
<div className="row">
|
||||
<h3>Status</h3>
|
||||
</div>
|
||||
<div className="row">
|
||||
<p>Currently there is an issue with Events getting duplicated. The website will have inflated statistics and become unavailable from time to time while we are working on a fix</p>
|
||||
</div>
|
||||
<div className="verticalDivider" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="alwaysOnBottom showNeverOnMobile">
|
||||
|
Loading…
x
Reference in New Issue
Block a user