mirror of
https://github.com/stronk-dev/LivepeerEvents.git
synced 2025-07-05 02:35:09 +02:00
Added winning ticket viewer
This commit is contained in:
parent
eb6db0c518
commit
36f32037d4
12
package.json
12
package.json
@ -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",
|
||||
|
@ -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>
|
||||
|
@ -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));
|
||||
|
@ -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"} >
|
||||
|
@ -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
201
src/pages/tickets.js
Normal 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;
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user