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 Event from '../models/event';
const fs = require('fs');
const apiRouter = express.Router();
import {
API_CMC, API_L1_HTTP, API_L2_HTTP, API_L2_WS
} from "../config";
// Do API requests to other API's
const https = require('https');
// Read ABI files
const fs = require('fs');
// Used for the livepeer thegraph API
import { request, gql } from 'graphql-request';
// Get ETH price & LPT coin prices
// Gets ETH, LPT and other coin info
const CoinMarketCap = require('coinmarketcap-api');
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");
// Gets gas prices
const web3layer1 = createAlchemyWeb3(API_L1_HTTP);
const web3layer2 = createAlchemyWeb3(API_L2_HTTP);
// For listening to blockchain events
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;
let cmcPriceGet = 0;
let ethPrice = 0;
@ -25,7 +29,7 @@ let lptPrice = 0;
let cmcQuotes = {};
let cmcCache = {};
// Update alchemy related API calls every 2 seconds
// Update Alchemy related API calls every 2 seconds
const timeoutAlchemy = 2000;
let l2Gwei = 0;
let l1Gwei = 0;
@ -34,10 +38,10 @@ let l1block = 0;
let arbGet = 0;
// Gas limits on common contract interactions
// 50000 gas for approval when creating a new O
const redeemRewardGwei = 1053687;
const claimTicketGwei = 1333043;
const withdrawFeeGwei = 688913;
// 50000 gas for approval when creating a new O
const stakeFeeGwei = 680000;
const commissionFeeGwei = 140000;
const serviceUriFee = 51000;
@ -94,7 +98,7 @@ var BondingManagerProxyListener = contractInstance.events.allEvents(async (error
});
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 () {
try {
cmcCache = await cmcClient.getTickers({ limit: 200 });
@ -115,17 +119,7 @@ const parseCmc = async function () {
}
}
// Queries Alchemy for block info and fees
// Queries Alchemy for block info and gas fees
const parseL1Blockchain = async function () {
const l1Wei = await web3layer1.eth.getGasPrice();
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) => {
try {
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) => {
try {
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) => {
try {
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) => {
try {
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) {
reqAddr = reqAddr.toLowerCase();
const now = new Date().getTime();
@ -353,6 +351,7 @@ const parseOrchestrator = async function (reqAddr) {
return orchestratorObj;
}
// Exports info on a given Orchestrator
apiRouter.get("/getOrchestrator", async (req, res) => {
try {
let reqOrch = req.query.orch;
@ -365,7 +364,6 @@ apiRouter.get("/getOrchestrator", async (req, res) => {
res.status(400).send(err);
}
});
apiRouter.get("/getOrchestrator/:orch", async (req, res) => {
try {
const reqObj = await parseOrchestrator(req.params.orch);
@ -374,7 +372,6 @@ apiRouter.get("/getOrchestrator/:orch", async (req, res) => {
res.status(400).send(err);
}
});
apiRouter.post("/getOrchestrator", async (req, res) => {
try {
const reqObj = await parseOrchestrator(req.body.orchAddr);

View File

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

View File

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

View File

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

View File

@ -6,6 +6,8 @@ import {
import { useSelector } from 'react-redux'
import Orchestrator from "./orchestratorViewer";
// Displays orchestrator info and embedded grafana panels
const Grafana = (obj) => {
const livepeer = useSelector((state) => state.livepeerstate);
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 {
Navigate
} from "react-router-dom";
import { connect } from "react-redux";
import {
getVisitorStats
} from "./actions/user";
import { useSelector } from 'react-redux'
const mapStateToProps = (state) => {
return {
session: state.session,
userstate: state.userstate,
errors: state.errors
// Index of all subpages on this website
const Home = (obj) => {
const userstate = useSelector((state) => state.userstate);
const sessionstate = useSelector((state) => state.session);
const [redirectToGrafana, setRedirectToGrafana] = useState(false);
const [redirectToLPT, setRedirectToLPT] = useState(false);
if (redirectToGrafana) {
return <Navigate push to="/orchestrator" />;
}
};
const mapDispatchToProps = dispatch => ({
getVisitorStats: () => dispatch(getVisitorStats())
});
class Home extends React.Component {
state = {
redirectToGrafana: false,
redirectToLPT: false
};
constructor(props) {
super(props);
if (redirectToLPT) {
return <Navigate push to="/livepeer" />;
}
render() {
if (this.state.redirectToGrafana) {
return <Navigate push to="/orchestrator" />;
}
if (this.state.redirectToLPT) {
return <Navigate push to="/livepeer" />;
}
var totalVisitorCount = 0;
var activeVisitorCount = 0;
if (userstate.visitorStats) {
totalVisitorCount = userstate.visitorStats.totalVisitorCount;
activeVisitorCount = userstate.visitorStats.activeVisitorCount
}
var totalVisitorCount = 0;
var activeVisitorCount = 0;
if (this.props.userstate.visitorStats) {
totalVisitorCount = this.props.userstate.visitorStats.totalVisitorCount;
activeVisitorCount = this.props.userstate.visitorStats.activeVisitorCount
}
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>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>
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>Home</h3>
</div>
<div className="row">
<a href="https://github.com/stronk-dev/LivepeerEvents">
<button className="waveButton">
<p>🧱 Source Code 🏠</p>
</button>
</div>
<div className="row">
<button className="waveButton" onClick={() => {
this.setState({ redirectToLPT: true });
}}>
<p>🔎 Blockchain 🕵</p>
</button>
</div>
</a>
</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 {this.props.session.ip || "?"}
</h4>
<div className="row">
<button className="waveButton" onClick={() => {
setRedirectToGrafana(true);
}}>
<p>🚀 Orchestrator 🌑</p>
</button>
</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 className="row">
<button className="waveButton" onClick={() => {
setRedirectToLPT(true);
}}>
<p>🔎 Blockchain 🕵</p>
</button>
</div>
</div>
<div className="alwaysOnBottomRight" style={{ margin: 0, padding: 0 }}>
<h6 className="lightText" style={{ margin: 0, padding: 0 }}>
nframe.nl
</h6>
</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(
mapStateToProps,
mapDispatchToProps
)(Home);
export default Home;

View File

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

View File

@ -1,5 +1,5 @@
import * as React from "react";
import { connect, batch } from "react-redux";
import React, { useEffect, useState } from 'react'
import { useDispatch, batch } from 'react-redux'
import {
getVisitorStats
} from "./actions/user";
@ -8,41 +8,66 @@ import {
} from "./actions/livepeer";
import { login } from "./actions/session";
const mapStateToProps = (state) => {
return {
session: state.session,
userstate: state.userstate,
errors: state.errors
}
};
// Shows a loading screen on first load and gets fresh data every refreshInterval milliseconds
const mapDispatchToProps = dispatch => ({
getVisitorStats: () => dispatch(getVisitorStats()),
login: () => dispatch(login()),
getQuotes: () => dispatch(getQuotes()),
getEvents: () => dispatch(getEvents()),
getBlockchainData: () => dispatch(getBlockchainData()),
getCurrentOrchestratorInfo: () => dispatch(getCurrentOrchestratorInfo())
});
// Refresh every 30 seconds
const refreshInterval = 30000;
const Startup = (obj) => {
const [isLoaded, setIsLoaded] = useState(false);
const dispatch = useDispatch();
class Startup extends React.Component {
componentDidMount() {
const refreshAllZeData = () => {
console.log("Refreshing data...");
batch(() => {
this.props.login();
this.props.getVisitorStats();
this.props.getQuotes();
this.props.getBlockchainData();
this.props.getEvents();
this.props.getCurrentOrchestratorInfo();
dispatch(getQuotes());
dispatch(getEvents());
dispatch(getBlockchainData());
dispatch(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(
mapStateToProps,
mapDispatchToProps
)(Startup);
export default Startup;

View File

@ -3,8 +3,6 @@ import errors from "./errors/errors";
import session from "./session/session";
import userstate from "./userstate/userstate";
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({
session,
errors,

View File

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