Background loading enabled, moved out of specific components

This commit is contained in:
Marco van Dijk 2022-03-04 14:46:39 +01:00
parent a98bd49e37
commit 3b4990865c
10 changed files with 159 additions and 175 deletions

View File

@ -1,23 +1,27 @@
import express from "express"; import express from "express";
import Event from '../models/event'; import Event from '../models/event';
const fs = require('fs');
const apiRouter = express.Router(); const apiRouter = express.Router();
import { import {
API_CMC, API_L1_HTTP, API_L2_HTTP, API_L2_WS API_CMC, API_L1_HTTP, API_L2_HTTP, API_L2_WS
} from "../config"; } from "../config";
// Do API requests to other API's
const https = require('https'); const https = require('https');
// Read ABI files
const fs = require('fs');
// Used for the livepeer thegraph API
import { request, gql } from 'graphql-request'; import { request, gql } from 'graphql-request';
// Gets ETH, LPT and other coin info
// Get ETH price & LPT coin prices
const CoinMarketCap = require('coinmarketcap-api'); const CoinMarketCap = require('coinmarketcap-api');
const cmcClient = new CoinMarketCap(API_CMC); const cmcClient = new CoinMarketCap(API_CMC);
// Get gas price on ETH (L2 already gets exported by the O's themselves) // Gets blockchain data
const { createAlchemyWeb3 } = require("@alch/alchemy-web3"); const { createAlchemyWeb3 } = require("@alch/alchemy-web3");
// Gets gas prices
const web3layer1 = createAlchemyWeb3(API_L1_HTTP); const web3layer1 = createAlchemyWeb3(API_L1_HTTP);
const web3layer2 = createAlchemyWeb3(API_L2_HTTP); const web3layer2 = createAlchemyWeb3(API_L2_HTTP);
// For listening to blockchain events
const web3layer2WS = createAlchemyWeb3(API_L2_WS); const web3layer2WS = createAlchemyWeb3(API_L2_WS);
// Update CMC related api calls every 5 minutes // Update CoinMarketCap related api calls every 5 minutes
const timeoutCMC = 300000; const timeoutCMC = 300000;
let cmcPriceGet = 0; let cmcPriceGet = 0;
let ethPrice = 0; let ethPrice = 0;
@ -25,7 +29,7 @@ let lptPrice = 0;
let cmcQuotes = {}; let cmcQuotes = {};
let cmcCache = {}; let cmcCache = {};
// Update alchemy related API calls every 2 seconds // Update Alchemy related API calls every 2 seconds
const timeoutAlchemy = 2000; const timeoutAlchemy = 2000;
let l2Gwei = 0; let l2Gwei = 0;
let l1Gwei = 0; let l1Gwei = 0;
@ -34,10 +38,10 @@ let l1block = 0;
let arbGet = 0; let arbGet = 0;
// Gas limits on common contract interactions // Gas limits on common contract interactions
// 50000 gas for approval when creating a new O
const redeemRewardGwei = 1053687; const redeemRewardGwei = 1053687;
const claimTicketGwei = 1333043; const claimTicketGwei = 1333043;
const withdrawFeeGwei = 688913; const withdrawFeeGwei = 688913;
// 50000 gas for approval when creating a new O
const stakeFeeGwei = 680000; const stakeFeeGwei = 680000;
const commissionFeeGwei = 140000; const commissionFeeGwei = 140000;
const serviceUriFee = 51000; const serviceUriFee = 51000;
@ -94,7 +98,7 @@ var BondingManagerProxyListener = contractInstance.events.allEvents(async (error
}); });
console.log("listening for events on", BondingManagerProxyAddr) console.log("listening for events on", BondingManagerProxyAddr)
// Splits of the big CMC object into separate datas // Splits of raw CMC object into coin quote data
const parseCmc = async function () { const parseCmc = async function () {
try { try {
cmcCache = await cmcClient.getTickers({ limit: 200 }); cmcCache = await cmcClient.getTickers({ limit: 200 });
@ -115,17 +119,7 @@ const parseCmc = async function () {
} }
} }
// Queries Alchemy for block info and gas fees
// Queries Alchemy for block info and fees
const parseL1Blockchain = async function () { const parseL1Blockchain = async function () {
const l1Wei = await web3layer1.eth.getGasPrice(); const l1Wei = await web3layer1.eth.getGasPrice();
l1block = await web3layer1.eth.getBlockNumber(); l1block = await web3layer1.eth.getBlockNumber();
@ -193,6 +187,7 @@ apiRouter.get("/grafana", async (req, res) => {
} }
}); });
// Exports raw CoinMarketCap info
apiRouter.get("/cmc", async (req, res) => { apiRouter.get("/cmc", async (req, res) => {
try { try {
const now = new Date().getTime(); const now = new Date().getTime();
@ -207,6 +202,7 @@ apiRouter.get("/cmc", async (req, res) => {
} }
}); });
// Exports gas fees and contract prices
apiRouter.get("/blockchains", async (req, res) => { apiRouter.get("/blockchains", async (req, res) => {
try { try {
const now = new Date().getTime(); const now = new Date().getTime();
@ -240,7 +236,7 @@ apiRouter.get("/blockchains", async (req, res) => {
} }
}); });
// Exports top 200 coin quotes
apiRouter.get("/quotes", async (req, res) => { apiRouter.get("/quotes", async (req, res) => {
try { try {
const now = new Date().getTime(); const now = new Date().getTime();
@ -256,6 +252,7 @@ apiRouter.get("/quotes", async (req, res) => {
} }
}); });
// Exports list of smart contract events
apiRouter.get("/getEvents", async (req, res) => { apiRouter.get("/getEvents", async (req, res) => {
try { try {
const now = new Date().getTime(); const now = new Date().getTime();
@ -277,6 +274,7 @@ apiRouter.get("/getEvents", async (req, res) => {
} }
}); });
// Gets info on a given Orchestrator
const parseOrchestrator = async function (reqAddr) { const parseOrchestrator = async function (reqAddr) {
reqAddr = reqAddr.toLowerCase(); reqAddr = reqAddr.toLowerCase();
const now = new Date().getTime(); const now = new Date().getTime();
@ -353,6 +351,7 @@ const parseOrchestrator = async function (reqAddr) {
return orchestratorObj; return orchestratorObj;
} }
// Exports info on a given Orchestrator
apiRouter.get("/getOrchestrator", async (req, res) => { apiRouter.get("/getOrchestrator", async (req, res) => {
try { try {
let reqOrch = req.query.orch; let reqOrch = req.query.orch;
@ -365,7 +364,6 @@ apiRouter.get("/getOrchestrator", async (req, res) => {
res.status(400).send(err); res.status(400).send(err);
} }
}); });
apiRouter.get("/getOrchestrator/:orch", async (req, res) => { apiRouter.get("/getOrchestrator/:orch", async (req, res) => {
try { try {
const reqObj = await parseOrchestrator(req.params.orch); const reqObj = await parseOrchestrator(req.params.orch);
@ -374,7 +372,6 @@ apiRouter.get("/getOrchestrator/:orch", async (req, res) => {
res.status(400).send(err); res.status(400).send(err);
} }
}); });
apiRouter.post("/getOrchestrator", async (req, res) => { apiRouter.post("/getOrchestrator", async (req, res) => {
try { try {
const reqObj = await parseOrchestrator(req.body.orchAddr); const reqObj = await parseOrchestrator(req.body.orchAddr);

View File

@ -10,7 +10,6 @@ import {
Route Route
} from "react-router-dom"; } from "react-router-dom";
export default function App() { export default function App() {
return ( return (
<Startup> <Startup>

View File

@ -2,7 +2,9 @@ import React from "react";
import { import {
getOrchestratorInfo getOrchestratorInfo
} from "./actions/livepeer"; } from "./actions/livepeer";
import { useDispatch } from 'react-redux' import { useDispatch } from 'react-redux';
/// Displays a single event. Sets selected Orchestrator info in the redux store
const activationColour = "rgba(23, 60, 122, 0.3)"; const activationColour = "rgba(23, 60, 122, 0.3)";
const rewardColour = "rgba(20, 99, 29, 0.3)"; const rewardColour = "rgba(20, 99, 29, 0.3)";
@ -29,7 +31,6 @@ const EventButton = (obj) => {
let isOnlyBond = true; let isOnlyBond = true;
let thisColour = ""; let thisColour = "";
// Which we will fill in by going over all of the events once // Which we will fill in by going over all of the events once
thisData.map(eventObj => { thisData.map(eventObj => {
// Bond: contains amount the transaction is about and who is participating // Bond: contains amount the transaction is about and who is participating
@ -132,6 +133,7 @@ const EventButton = (obj) => {
return null; return null;
} }
// Displays info specific to a type of transactions
let eventSpecificInfo; let eventSpecificInfo;
if (transactionName === "Reward") { if (transactionName === "Reward") {
if (transactionAmount - 69 < 1 && transactionAmount - 69 > 0) { if (transactionAmount - 69 < 1 && transactionAmount - 69 > 0) {

View File

@ -2,6 +2,8 @@ import React, { useState } from "react";
import EventButton from "./eventButton"; import EventButton from "./eventButton";
import ScrollContainer from 'react-indiana-drag-scroll'; import ScrollContainer from 'react-indiana-drag-scroll';
/// A scrollable and filterable list of EventButtons
const activationColour = "rgba(23, 60, 122, 0.3)"; const activationColour = "rgba(23, 60, 122, 0.3)";
const rewardColour = "rgba(20, 99, 29, 0.3)"; const rewardColour = "rgba(20, 99, 29, 0.3)";
const updateColour = "rgba(122, 63, 23, 0.3)"; const updateColour = "rgba(122, 63, 23, 0.3)";
@ -15,6 +17,7 @@ const EventViewer = (obj) => {
const [updateActivated, setUpdateActivated] = useState(false); const [updateActivated, setUpdateActivated] = useState(false);
const [withdrawActivated, setWithdrawActivated] = useState(false); const [withdrawActivated, setWithdrawActivated] = useState(false);
const [stakeActivated, setStakeActivated] = useState(false); const [stakeActivated, setStakeActivated] = useState(false);
console.log("Rendering EventViewer");
let txCounter = 0; let txCounter = 0;
let currentTx = ""; let currentTx = "";

View File

@ -6,6 +6,8 @@ import {
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import Orchestrator from "./orchestratorViewer"; import Orchestrator from "./orchestratorViewer";
// Displays orchestrator info and embedded grafana panels
const Grafana = (obj) => { const Grafana = (obj) => {
const livepeer = useSelector((state) => state.livepeerstate); const livepeer = useSelector((state) => state.livepeerstate);
const [redirectToHome, setRedirectToHome] = useState(false); const [redirectToHome, setRedirectToHome] = useState(false);

View File

@ -1,107 +1,84 @@
import * as React from "react"; import React, { useState } from 'react';
import './style.css'; import './style.css';
import { import {
Navigate Navigate
} from "react-router-dom"; } from "react-router-dom";
import { connect } from "react-redux"; import { useSelector } from 'react-redux'
import {
getVisitorStats
} from "./actions/user";
const mapStateToProps = (state) => { // Index of all subpages on this website
return {
session: state.session, const Home = (obj) => {
userstate: state.userstate, const userstate = useSelector((state) => state.userstate);
errors: state.errors const sessionstate = useSelector((state) => state.session);
const [redirectToGrafana, setRedirectToGrafana] = useState(false);
const [redirectToLPT, setRedirectToLPT] = useState(false);
if (redirectToGrafana) {
return <Navigate push to="/orchestrator" />;
} }
}; if (redirectToLPT) {
return <Navigate push to="/livepeer" />;
const mapDispatchToProps = dispatch => ({
getVisitorStats: () => dispatch(getVisitorStats())
});
class Home extends React.Component {
state = {
redirectToGrafana: false,
redirectToLPT: false
};
constructor(props) {
super(props);
} }
render() { var totalVisitorCount = 0;
if (this.state.redirectToGrafana) { var activeVisitorCount = 0;
return <Navigate push to="/orchestrator" />; if (userstate.visitorStats) {
} totalVisitorCount = userstate.visitorStats.totalVisitorCount;
if (this.state.redirectToLPT) { activeVisitorCount = userstate.visitorStats.activeVisitorCount
return <Navigate push to="/livepeer" />; }
}
var totalVisitorCount = 0; return (
var activeVisitorCount = 0; <div className="stroke" style={{ padding: 0 }}>
if (this.props.userstate.visitorStats) { <div className="row" style={{ margin: 0, padding: 0 }}>
totalVisitorCount = this.props.userstate.visitorStats.totalVisitorCount; <img alt="" src="livepeer.png" width="100em" height="100em" style={{ zIndex: 10 }} />
activeVisitorCount = this.props.userstate.visitorStats.activeVisitorCount </div>
} <div className="flexContainer">
<div className="stroke roundedOpaque">
return ( <div className="row">
<div className="stroke" style={{ padding: 0 }}> <h3>Home</h3>
<div className="row" style={{ margin: 0, padding: 0 }}> </div>
<img alt="" src="livepeer.png" width="100em" height="100em" style={{ zIndex: 10 }} /> <div className="row">
</div> <a href="https://github.com/stronk-dev/LivepeerEvents">
<div className="flexContainer"> <button className="waveButton">
<div className="stroke roundedOpaque"> <p>🧱 Source Code 🏠</p>
<div className="row">
<h3>Home</h3>
</div>
<div className="row">
<a href="https://github.com/stronk-dev/LivepeerEvents">
<button className="waveButton">
<p>🧱 Source Code 🏠</p>
</button>
</a>
</div>
<div className="row">
<button className="waveButton" onClick={() => {
this.setState({ redirectToGrafana: true });
}}>
<p>🚀 Orchestrator 🌑</p>
</button> </button>
</div> </a>
<div className="row">
<button className="waveButton" onClick={() => {
this.setState({ redirectToLPT: true });
}}>
<p>🔎 Blockchain 🕵</p>
</button>
</div>
</div> </div>
</div> <div className="row">
<div className="alwaysOnBottom showNeverOnMobile" style={{ margin: 0, padding: 0 }}> <button className="waveButton" onClick={() => {
<div className="row" style={{ margin: 0, padding: 0 }}> setRedirectToGrafana(true);
<h4 className="lightText" style={{ margin: 0, padding: 0 }}> }}>
Connected as {this.props.session.ip || "?"} <p>🚀 Orchestrator 🌑</p>
</h4> </button>
</div> </div>
<div className="row" style={{ margin: 0, padding: 0 }}> <div className="row">
<h3 className="lightText" style={{ margin: 0, padding: 0 }}> <button className="waveButton" onClick={() => {
{totalVisitorCount} unique visitors / {activeVisitorCount} of which have interacted with this website setRedirectToLPT(true);
</h3> }}>
<p>🔎 Blockchain 🕵</p>
</button>
</div> </div>
</div> </div>
<div className="alwaysOnBottomRight" style={{ margin: 0, padding: 0 }}>
<h6 className="lightText" style={{ margin: 0, padding: 0 }}>
nframe.nl
</h6>
</div>
</div> </div>
) <div className="alwaysOnBottom showNeverOnMobile" style={{ margin: 0, padding: 0 }}>
} <div className="row" style={{ margin: 0, padding: 0 }}>
<h4 className="lightText" style={{ margin: 0, padding: 0 }}>
Connected as {sessionstate.ip || "?"}
</h4>
</div>
<div className="row" style={{ margin: 0, padding: 0 }}>
<h3 className="lightText" style={{ margin: 0, padding: 0 }}>
{totalVisitorCount} unique visitors / {activeVisitorCount} of which have interacted with this website
</h3>
</div>
</div>
<div className="alwaysOnBottomRight" style={{ margin: 0, padding: 0 }}>
<h6 className="lightText" style={{ margin: 0, padding: 0 }}>
nframe.nl
</h6>
</div>
</div>
)
} }
export default connect( export default Home;
mapStateToProps,
mapDispatchToProps
)(Home);

View File

@ -1,43 +1,26 @@
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
import './style.css'; import './style.css';
import { import { Navigate, useSearchParams } from "react-router-dom";
Navigate, useSearchParams import { useSelector, useDispatch } from 'react-redux'
} from "react-router-dom"; import { getOrchestratorInfo } from "./actions/livepeer";
import { useSelector, useDispatch, batch } from 'react-redux'
import {
getQuotes, getBlockchainData, getEvents, getCurrentOrchestratorInfo, getOrchestratorInfo
} from "./actions/livepeer";
import EventViewer from "./eventViewer"; import EventViewer from "./eventViewer";
import Orchestrator from "./orchestratorViewer"; import Orchestrator from "./orchestratorViewer";
import Stat from "./statViewer"; import Stat from "./statViewer";
// Refresh every 30 seconds // Shows the EventViewer and other Livepeer related info
const refreshInterval = 30000;
const Livepeer = (obj) => { const Livepeer = (obj) => {
const [prefill, setPrefill] = useSearchParams(); const [prefill, setPrefill] = useSearchParams();
const dispatch = useDispatch(); const dispatch = useDispatch();
const livepeer = useSelector((state) => state.livepeerstate); const livepeer = useSelector((state) => state.livepeerstate);
const [redirectToHome, setRedirectToHome] = useState(false); const [redirectToHome, setRedirectToHome] = useState(false);
console.log("Rendering Livepeer");
const refreshAllZeData = () => {
batch(() => {
dispatch(getQuotes())
dispatch(getEvents())
dispatch(getBlockchainData())
dispatch(getCurrentOrchestratorInfo())
});
}
useEffect(() => { useEffect(() => {
if (prefill.get('orchAddr') != ""){ if (prefill.get('orchAddr') != "") {
dispatch(getOrchestratorInfo(prefill.get('orchAddr'))); dispatch(getOrchestratorInfo(prefill.get('orchAddr')));
} }
if (refreshInterval) { }, [prefill]);
const interval = setInterval(refreshAllZeData, refreshInterval);
return () => clearInterval(interval);
}
}, [refreshInterval]);
if (redirectToHome) { if (redirectToHome) {
return <Navigate push to="/" />; return <Navigate push to="/" />;

View File

@ -1,5 +1,5 @@
import * as React from "react"; import React, { useEffect, useState } from 'react'
import { connect, batch } from "react-redux"; import { useDispatch, batch } from 'react-redux'
import { import {
getVisitorStats getVisitorStats
} from "./actions/user"; } from "./actions/user";
@ -8,41 +8,66 @@ import {
} from "./actions/livepeer"; } from "./actions/livepeer";
import { login } from "./actions/session"; import { login } from "./actions/session";
const mapStateToProps = (state) => { // Shows a loading screen on first load and gets fresh data every refreshInterval milliseconds
return {
session: state.session,
userstate: state.userstate,
errors: state.errors
}
};
const mapDispatchToProps = dispatch => ({ // Refresh every 30 seconds
getVisitorStats: () => dispatch(getVisitorStats()), const refreshInterval = 30000;
login: () => dispatch(login()),
getQuotes: () => dispatch(getQuotes()),
getEvents: () => dispatch(getEvents()),
getBlockchainData: () => dispatch(getBlockchainData()),
getCurrentOrchestratorInfo: () => dispatch(getCurrentOrchestratorInfo())
});
const Startup = (obj) => {
const [isLoaded, setIsLoaded] = useState(false);
const dispatch = useDispatch();
class Startup extends React.Component { const refreshAllZeData = () => {
componentDidMount() { console.log("Refreshing data...");
batch(() => { batch(() => {
this.props.login(); dispatch(getQuotes());
this.props.getVisitorStats(); dispatch(getEvents());
this.props.getQuotes(); dispatch(getBlockchainData());
this.props.getBlockchainData(); dispatch(getCurrentOrchestratorInfo());
this.props.getEvents();
this.props.getCurrentOrchestratorInfo();
}); });
} }
render() {
return this.props.children; const refreshLogin = () => {
console.log("Logging in and getting visitor statistics...");
batch(() => {
dispatch(login());
dispatch(getVisitorStats());
});
setIsLoaded(true);
}
useEffect(() => {
refreshLogin();
refreshAllZeData();
if (refreshInterval) {
const interval = setInterval(refreshAllZeData, refreshInterval);
return () => clearInterval(interval);
}
}, [refreshInterval]);
if(isLoaded){
console.log("Rendering Application");
return obj.children;
}else{
console.log("Rendering Loading Screen");
return <div className="stroke" style={{ padding: 0 }}>
<div className="row" style={{ margin: 0, padding: 0 }}>
<img alt="" src="livepeer.png" width="100em" height="100em" style={{ zIndex: 10 }} />
</div>
<div className="flexContainer">
<div className="stroke roundedOpaque">
<div className="row">
<h3>Loading...</h3>
</div>
</div>
</div>
<div className="alwaysOnBottomRight" style={{ margin: 0, padding: 0 }}>
<h6 className="lightText" style={{ margin: 0, padding: 0 }}>
nframe.nl
</h6>
</div>
</div>
} }
} }
export default connect( export default Startup;
mapStateToProps,
mapDispatchToProps
)(Startup);

View File

@ -3,8 +3,6 @@ import errors from "./errors/errors";
import session from "./session/session"; import session from "./session/session";
import userstate from "./userstate/userstate"; import userstate from "./userstate/userstate";
import livepeerstate from "./livepeer/livepeerstate"; import livepeerstate from "./livepeer/livepeerstate";
{/*Reducers define how the state of the application changes when actions are sent into the store. They take in the current state and the action that was performed.
This file combines all reducers in use so they are all accessible for redux*/}
export default combineReducers({ export default combineReducers({
session, session,
errors, errors,

View File

@ -2,7 +2,6 @@ import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk"; import thunk from "redux-thunk";
import reducer from "../reducers/root"; import reducer from "../reducers/root";
function configureStore(preloadedState) { function configureStore(preloadedState) {
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, preloadedState, composeEnhancers( const store = createStore(reducer, preloadedState, composeEnhancers(
@ -12,7 +11,6 @@ function configureStore(preloadedState) {
return store; return store;
} }
export default (preloadedState) => ( export default (preloadedState) => (
configureStore(preloadedState) configureStore(preloadedState)
); );