import React from "react"; import "bootstrap/dist/css/bootstrap.min.css"; import "./index.css"; import { Modals, Routes, Loading, Footer, Status } from "./components"; import * as get from "./lib/getters"; import { bootstrap, getTokenomics, queryJstats } from "./lib/queries"; import { getMints, updateOpenings, updateWorkers } from "./lib/groups"; import { updateElection, getCouncilApplicants, getCouncilSize, getVotes, } from "./lib/election"; import { getStashes, getNominators, getValidators, getValidatorStakes, getEraRewardPoints, getLastReward, getTotalStake, } from "./lib/validators"; import { apiLocation, wsLocation, historyDepth } from "./config"; import { initialState } from "./state"; import axios from "axios"; // types import { Api, IState } from "./types"; import { types } from "@joystream/types"; import { ApiPromise, WsProvider } from "@polkadot/api"; import { Header } from "@polkadot/types/interfaces"; interface IProps {} class App extends React.Component { initializeSocket() { socket.on("disconnect", () => setTimeout(this.initializeSocket, 1000)); socket.on("connect", () => { if (!socket.id) return console.log("no websocket connection"); console.log("my socketId:", socket.id); socket.emit("get posts", this.state.posts.length); }); socket.on("posts", (posts: Post[]) => { console.log(`received ${posts.length} posts`); this.setState({ posts }); }); } // sync via joystream-api async updateStatus(api: ApiPromise, id: number): Promise { console.debug(`#${id}: Updating status`); this.updateActiveProposals(); getMints(api).then((mints) => this.save(`mints`, mints)); getTokenomics().then((tokenomics) => this.save(`tokenomics`, tokenomics)); let { status, councils } = this.state; status.election = await updateElection(api); if (status.election?.stage) this.getElectionStatus(api); councils.forEach((c) => { if (c?.round > status.council) status.council = c; }); let hash: string = await api.rpc.chain.getBlockHash(1); if (hash) status.startTime = (await api.query.timestamp.now.at(hash)).toNumber(); const nextMemberId = await await api.query.members.nextMemberId(); status.members = nextMemberId - 1; status.proposals = await get.proposalCount(api); status.posts = await get.currentPostId(api); status.threads = await get.currentThreadId(api); status.categories = await get.currentCategoryId(api); status.proposalPosts = await api.query.proposalsDiscussion.postCount(); await this.updateEra(api, status.era).then(async (era) => { status.era = era; status.lastReward = await getLastReward(api, era); status.validatorStake = await getTotalStake(api, era); this.save("status", status); }); return status; } async getElectionStatus(api: ApiPromise): Promise { getCouncilSize(api).then((councilSize) => { let election = this.state.election; election.councilSize = councilSize; this.save("election", election); }); getVotes(api).then((votes) => { let election = this.state.election; election.votes = votes; this.save("election", election); }); getCouncilApplicants(api).then((applicants) => { let election = this.state.election; election.applicants = applicants; this.save("election", election); }); } updateActiveProposals() { const active = this.state.proposals.filter((p) => p.result === "Pending"); if (!active.length) return; const s = active.length > 1 ? `s` : ``; console.log(`Updating ${active.length} active proposal${s}`); active.forEach(async (a) => { const { data } = await axios.get(`${apiLocation}/v2/proposals/${a.id}`); if (!data || data.error) return console.error(`failed to fetch proposal from API`); this.save( "proposals", this.state.proposals.map((p) => (p.id === a.id ? data : p)) ); }); } async updateEra(api: Api, old: number) { const { status, validators } = this.state; const era = Number(await api.query.staking.currentEra()); if (era === old) return era; this.updateWorkingGroups(api); this.updateValidatorPoints(api, status.era); if (era > status.era || !validators.length) this.updateValidators(api); return era; } async updateWorkingGroups(api: ApiPromise) { const { members, openings, workers } = this.state; updateWorkers(api, workers, members).then((workers) => { this.save("workers", workers); updateOpenings(api, openings, members).then((openings) => this.save("openings", openings) ); }); return this.save("council", await api.query.council.activeCouncil()); } updateValidators(api: ApiPromise) { getValidators(api).then((validators) => { this.save("validators", validators); getNominators(api).then((nominators) => { this.save("nominators", nominators); getStashes(api).then((stashes) => { this.save("stashes", stashes); const { status, members } = this.state; const { era } = status; getValidatorStakes(api, era, stashes, members, this.save).then( (stakes) => this.save("stakes", stakes) ); }); }); }); } async updateValidatorPoints(api: ApiPromise, currentEra: number) { let points = this.state.rewardPoints; const updateTotal = (eraTotals) => { let total = 0; Object.keys(eraTotals).forEach((era) => (total += eraTotals[era])); return total; }; for (let era = currentEra; era > currentEra - historyDepth; --era) { if (era < currentEra && points.eraTotals[era]) continue; getEraRewardPoints(api, era).then((eraPoints) => { console.debug(`era ${era}: ${eraPoints.total} points`); points.eraTotals[era] = eraPoints.total; points.total = updateTotal(points.eraTotals); Object.keys(eraPoints.individual).forEach((validator: string) => { if (!points.validators[validator]) points.validators[validator] = {}; points.validators[validator][era] = eraPoints.individual[validator]; }); this.save("rewardPoints", points); }); } } async updateCouncils() { queryJstats(`v1/councils`).then((councils) => { this.save(`councils`, councils); // TODO OPTIMIZE find max round let council = { round: 0 }; councils.forEach((c) => { if (c.round > council.round) council = c; }); let { status } = this.state; status.council = council; // needed by dashboard this.save("status", status); }); } // interface interactions toggleStar(account: string) { let { stars } = this.state; stars[account] = !stars[account]; this.save("stars", stars); } toggleEditKpi(editKpi) { this.setState({ editKpi }); } toggleShowStatus() { this.setState({ showStatus: !this.state.showStatus }); } toggleFooter() { this.setState({ hideFooter: !this.state.hideFooter }); } getMember(handle: string) { const { members } = this.state; const member = members.find((m) => m.handle === handle); if (member) return member; return members.find((m) => m.rootKey === handle); } render() { const { connected, fetching, loading, hideFooter } = this.state; if (loading) return ; return ( <>