Added winning ticket viewer

This commit is contained in:
Marco van Dijk 2022-04-16 18:31:15 +02:00
parent eb6db0c518
commit 36f32037d4
7 changed files with 236 additions and 7 deletions

View File

@ -3,15 +3,23 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@mantine/core": "^4.1.3",
"@mantine/dates": "^4.1.3",
"@mantine/form": "^4.1.3",
"@mantine/hooks": "^4.1.3",
"@mantine/modals": "^4.1.3",
"@mantine/next": "^4.1.3",
"@mantine/notifications": "^4.1.3",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"dayjs": "^1.11.1",
"ethers": "^5.4.4",
"http": "^0.0.1-security",
"https": "^1.0.0",
"md5": "^2.3.0",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-indiana-drag-scroll": "^2.1.0",
"react-markdown": "^7.1.1",
"react-paginate": "^8.1.2",

View File

@ -3,6 +3,7 @@ import Home from './pages/home.js';
import Startup from './pages/loadingScreen.js';
import Grafana from './pages/grafana.js';
import Livepeer from './pages/livepeer.js';
import Tickets from './pages/tickets.js';
import {
BrowserRouter as Router,
@ -16,6 +17,7 @@ export default function App() {
<Router>
<Routes>
<Route exact path='/livepeer' element={<Livepeer />} />
<Route exact path='/tickets' element={<Tickets />} />
<Route exact path='/orchestrator' element={<Grafana />} />
<Route path='/' element={<Home />} />
</Routes>

View File

@ -24,6 +24,7 @@ export const RECEIVE_CURRENT_ORCHESTRATOR = "RECEIVE_CURRENT_ORCHESTRATOR";
export const RECEIVE_ORCHESTRATOR = "RECEIVE_ORCHESTRATOR";
export const CLEAR_ORCHESTRATOR = "CLEAR_ORCHESTRATOR";
export const RECEIVE_TICKETS = "RECEIVE_TICKETS";
export const RECEIVE_WINNING_TICKETS = "RECEIVE_WINNING_TICKETS";
export const SET_ALL_ENS_INFO = "SET_ALL_ENS_INFO";
export const SET_ALL_ENS_DOMAINS = "SET_ALL_ENS_DOMAINS";
export const SET_ALL_THREEBOX_INFO = "SET_ALL_THREEBOX_INFO";
@ -49,6 +50,9 @@ const clearOrchestratorInfo = () => ({
const setTickets = message => ({
type: RECEIVE_TICKETS, message
});
const setWinningTickets = message => ({
type: RECEIVE_WINNING_TICKETS, message
});
const setAllEnsInfo = message => ({
type: SET_ALL_ENS_INFO, message
});
@ -428,6 +432,7 @@ export const getTickets = () => async dispatch => {
// Combine raw list of events into a list of useful Events
if (response.ok) {
let finalTicketList = [];
let winningTicketList = [];
// Current transaction we are processing
let txCounter = 0;
let currentTx = "";
@ -475,11 +480,9 @@ export const getTickets = () => async dispatch => {
eventValue: amount
});
} else if (eventObj.name === "WinningTicketTransfer") {
// For now lets just ignore these, they are boring
continue;
const amount = parseFloat(eventObj.data.amount) / 1000000000000000000;
const txt = " broadcaster payed out " + amount.toFixed(4) + " Eth";
finalTicketList.push({
winningTicketList.push({
eventType: "TransferTicket",
eventDescription: txt,
eventCaller: "",
@ -499,6 +502,7 @@ export const getTickets = () => async dispatch => {
}
// NOTE: We are throwing away the very oldest Ticket now, which should be fine.
// We can fix this once above wall of text becomes a separate function
dispatch(setWinningTickets(winningTicketList));
return dispatch(setTickets(finalTicketList));
}
return dispatch(receiveErrors(data));

View File

@ -75,7 +75,6 @@ const Address = (obj) => {
let thisIcon;
if (hasENS) {
thisName = thisInfo.domain;
console.log(thisInfo.avatar);
if (thisInfo.avatar) {
thisIcon =
<a className="selectOrchLight" style={{ padding: '0.2em', cursor: 'alias' }} target="_blank" rel="noopener noreferrer" href={"https://app.ens.domains/name/" + thisInfo.domain + "/details"} >

View File

@ -11,12 +11,16 @@ const Home = (obj) => {
const userstate = useSelector((state) => state.userstate);
const [redirectToGrafana, setRedirectToGrafana] = useState(false);
const [redirectToLPT, setRedirectToLPT] = useState(false);
const [redirectToTickets, setRedirectToTickets] = useState(false);
if (redirectToGrafana) {
return <Navigate push to="/orchestrator" />;
}
if (redirectToLPT) {
return <Navigate push to="/livepeer" />;
}
if (redirectToTickets) {
return <Navigate push to="/tickets" />;
}
// Get amount of unique IP's which have visited this website
var totalVisitorCount = 0;
if (userstate.visitorStats) {
@ -57,6 +61,13 @@ const Home = (obj) => {
<p>🔎 Blockchain 🕵</p>
</button>
</div>
<div className="row">
<button className="waveButton" onClick={() => {
setRedirectToTickets(true);
}}>
<p>💵 Tickets 💰</p>
</button>
</div>
<div className="verticalDivider" />
</div>
</div>

201
src/pages/tickets.js Normal file
View File

@ -0,0 +1,201 @@
import React, { useState, useEffect } from 'react'
import '../style.css';
import { Navigate } from "react-router-dom";
import { useSelector, useDispatch } from 'react-redux';
import { Accordion } from '@mantine/core';
import ScrollContainer from 'react-indiana-drag-scroll';
import Address from '../components/OrchAddressViewer';
const Tickets = (obj) => {
const livepeer = useSelector((state) => state.livepeerstate);
const [ticketsPerMonth, setTicketsPerMonth] = useState([]);
const [redirectToHome, setRedirectToHome] = useState(false);
console.log("Rendering Winning Ticket Viewer");
useEffect(() => {
// Process Winning tickets as:
// List of Months containing
// List of Orchestrators (sorted by earnings) containing
// List of winning tickets
let ticketsPerMonth = [];
let ticketIdx = livepeer.winningTickets.length - 1;
let currentMonth = 99;
let currentYear = 99;
let currentOrchCounter = [];
while (ticketIdx >= 0) {
const thisTicket = livepeer.winningTickets[ticketIdx];
const thisTime = new Date(thisTicket.transactionTime * 1000);
const thisYear = thisTime.getFullYear();
const thisMonth = thisTime.getMonth();
ticketIdx -= 1;
// On a new month
if (thisMonth != currentMonth) {
// Push this months data
if (currentOrchCounter.length) {
// Sort this months data
let sortedList = []
while (currentOrchCounter.length) {
let ticketIdx2 = currentOrchCounter.length - 1;
let largestIdx = 0;
let largestValue = 0;
// Find current O with most ticket wins in Eth
while (ticketIdx2 >= 0) {
const currentOrch = currentOrchCounter[ticketIdx2];
if (currentOrch.sum > largestValue) {
largestIdx = ticketIdx2;
largestValue = currentOrch.sum;
}
ticketIdx2 -= 1;
}
// Push current biggest list
sortedList.push(currentOrchCounter[largestIdx]);
// Remove from list
currentOrchCounter.splice(largestIdx, 1);
}
ticketsPerMonth.push(
{
year: currentYear,
month: currentMonth,
orchestrators: sortedList
}
);
}
// clear data
currentMonth = thisMonth;
currentYear = thisYear;
currentOrchCounter = [];
}
// Find orch in list
let thisIdx = 0;
let thisFound = false;
let ticketIdx2 = currentOrchCounter.length - 1;
while (ticketIdx2 >= 0) {
const currentOrch = currentOrchCounter[ticketIdx2];
if (currentOrch.address == thisTicket.eventTo) {
thisFound = true;
thisIdx = ticketIdx2;
break;
}
ticketIdx2 -= 1;
}
// If not in list, append at the end
if (!thisFound) {
currentOrchCounter.push({
address: thisTicket.eventTo,
sum: thisTicket.eventValue
});
} else {
// Else update that entry
currentOrchCounter[thisIdx].sum += thisTicket.eventValue;
}
}
setTicketsPerMonth(ticketsPerMonth);
}, [livepeer.winningTickets]);
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">Winning Tickets</h4>
</div>
</div>
<div id='bodyContent'>
<div className="mainContent">
<div className="strokeSmollLeft" style={{ height: 'calc( 100vh - 50px)' }}>
<div className="row" style={{ width: '100%', height: '100%' }}>
<div className="stroke roundedOpaque onlyVerticalScroll" style={{ width: '40vw', minWidth: '400px', height: 'calc( 100vh - 50px - 2em)', marginTop: '2em' }}>
<div className="content-wrapper" style={{ width: '100%' }}>
<ScrollContainer activationDistance={1} className="overflow-container" hideScrollbars={false}>
<div className="overflow-content" style={{ cursor: 'grab', paddingTop: 0 }}>
<div className={obj.forceVertical ? "flexContainer forceWrap" : "flexContainer"} >
<Accordion initialItem={0} className="stroke"
styles={{
item: { padding: 0 },
itemOpened: { padding: 0 },
itemTitle: { padding: 0 },
control: { padding: 0 },
label: { padding: 0 },
icon: { padding: 0 },
content: { padding: 0 },
contentInner: { padding: 0 },
}}>
<div className="verticalDivider" />
{
ticketsPerMonth.map(function (data) {
let thisMonth = "";
let monthAsNum = data.month;
if (monthAsNum == 0) {
thisMonth = "Januari";;
} else if (monthAsNum == 1) {
thisMonth = "Februari";;
} else if (monthAsNum == 2) {
thisMonth = "March";;
} else if (monthAsNum == 3) {
thisMonth = "April";
} else if (monthAsNum == 4) {
thisMonth = "May";
} else if (monthAsNum == 5) {
thisMonth = "June";
} else if (monthAsNum == 6) {
thisMonth = "July";
} else if (monthAsNum == 7) {
thisMonth = "August";
} else if (monthAsNum == 8) {
thisMonth = "September";
} else if (monthAsNum == 9) {
thisMonth = "October";
} else if (monthAsNum == 10) {
thisMonth = "November";
} else if (monthAsNum == 11) {
thisMonth = "December";;
}
return (
<Accordion.Item label={data.year + "-" + thisMonth + ": " + data.orchestrators.length + " orchestrators"} className="stroke" key={data.year + "-" + thisMonth}>
<div className="flexContainer forceWrap">
{
data.orchestrators.map(function (orch) {
return (
<div className="row" key={"delegator" + orch.address}>
<div className="rowAlignLeft">
<Address address={orch.address} seed={"delegator" + orch.address + orch.sum} />
</div>
<div className="rowAlignRight">
<h4>{orch.sum} Eth</h4>
</div>
</div>
)
})
}
</div>
<div className="verticalDivider" />
</Accordion.Item>
)
})
}
</Accordion>
</div>
</div>
</ScrollContainer>
</div>
</div>
</div>
</div>
</div>
</div>
</div >
);
}
export default Tickets;

View File

@ -6,6 +6,7 @@ import {
RECEIVE_CURRENT_ORCHESTRATOR,
CLEAR_ORCHESTRATOR,
RECEIVE_TICKETS,
RECEIVE_WINNING_TICKETS,
SET_ALL_ENS_INFO,
SET_ALL_ENS_DOMAINS,
SET_ALL_THREEBOX_INFO
@ -19,7 +20,8 @@ export default (state = {
selectedOrchestrator: null,
tickets: [],
ensInfoMapping: [],
ensDomainMapping: []
ensDomainMapping: [],
winningTickets: []
}, { type, message }) => {
Object.freeze(state);
switch (type) {
@ -37,6 +39,8 @@ export default (state = {
return { ...state, selectedOrchestrator: null };
case RECEIVE_TICKETS:
return { ...state, tickets: message };
case RECEIVE_WINNING_TICKETS:
return { ...state, winningTickets: message };
case SET_ALL_ENS_INFO:
return { ...state, ensInfoMapping: message };
case SET_ALL_ENS_DOMAINS: