diff --git a/graphql-schema.json b/graphql-schema.json index ff3de61..c841320 100644 --- a/graphql-schema.json +++ b/graphql-schema.json @@ -10,12 +10,12 @@ { "kind": "OBJECT", "name": "AggregateData", - "description": "", + "description": null, "specifiedByUrl": null, "fields": [ { "name": "name", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -31,7 +31,7 @@ }, { "name": "count", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -65,12 +65,12 @@ { "kind": "OBJECT", "name": "ClientVersionAggregation", - "description": "", + "description": null, "specifiedByUrl": null, "fields": [ { "name": "client", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -86,7 +86,7 @@ }, { "name": "count", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -102,7 +102,7 @@ }, { "name": "versions", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -144,12 +144,12 @@ { "kind": "OBJECT", "name": "HeatmapData", - "description": "", + "description": null, "specifiedByUrl": null, "fields": [ { "name": "networkType", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -165,7 +165,7 @@ }, { "name": "clientType", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -181,7 +181,7 @@ }, { "name": "syncStatus", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -197,7 +197,7 @@ }, { "name": "latitude", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -213,7 +213,7 @@ }, { "name": "longitude", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -229,7 +229,7 @@ }, { "name": "city", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -245,7 +245,7 @@ }, { "name": "country", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -287,15 +287,75 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "OBJECT", + "name": "NextHardforkAggregation", + "description": null, + "specifiedByUrl": null, + "fields": [ + { + "name": "version", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "epoch", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "count", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, { "kind": "OBJECT", "name": "NodeStats", - "description": "", + "description": null, "specifiedByUrl": null, "fields": [ { "name": "totalNodes", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -311,7 +371,7 @@ }, { "name": "nodeSyncedPercentage", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -327,7 +387,7 @@ }, { "name": "nodeUnsyncedPercentage", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -350,12 +410,12 @@ { "kind": "OBJECT", "name": "NodeStatsOverTime", - "description": "", + "description": null, "specifiedByUrl": null, "fields": [ { "name": "time", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -371,7 +431,7 @@ }, { "name": "totalNodes", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -387,7 +447,7 @@ }, { "name": "syncedNodes", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -403,7 +463,7 @@ }, { "name": "unsyncedNodes", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -426,12 +486,12 @@ { "kind": "OBJECT", "name": "Query", - "description": "", + "description": null, "specifiedByUrl": null, "fields": [ { "name": "aggregateByAgentName", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -455,7 +515,7 @@ }, { "name": "aggregateByCountry", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -479,7 +539,7 @@ }, { "name": "aggregateByOperatingSystem", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -503,7 +563,7 @@ }, { "name": "aggregateByNetwork", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -525,9 +585,33 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "aggregateByHardforkSchedule", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "NextHardforkAggregation", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "aggregateByClientVersion", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -551,7 +635,7 @@ }, { "name": "getHeatmapData", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -575,7 +659,7 @@ }, { "name": "getNodeStats", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -591,11 +675,11 @@ }, { "name": "getNodeStatsOverTime", - "description": "", + "description": null, "args": [ { "name": "start", - "description": "", + "description": null, "type": { "kind": "NON_NULL", "name": null, @@ -611,7 +695,7 @@ }, { "name": "end", - "description": "", + "description": null, "type": { "kind": "NON_NULL", "name": null, @@ -648,7 +732,7 @@ }, { "name": "getRegionalStats", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -664,7 +748,7 @@ }, { "name": "getAltairUpgradePercentage", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -687,12 +771,12 @@ { "kind": "OBJECT", "name": "RegionalStats", - "description": "", + "description": null, "specifiedByUrl": null, "fields": [ { "name": "totalParticipatingCountries", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -708,7 +792,7 @@ }, { "name": "hostedNodePercentage", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -724,7 +808,7 @@ }, { "name": "nonhostedNodePercentage", - "description": "", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -1661,16 +1745,18 @@ "directives": [ { "name": "deprecated", - "description": "The @deprecated directive is used within the type system definition language to indicate deprecated portions of a GraphQL service’s schema, such as deprecated fields on a type or deprecated enum values.", + "description": "The @deprecated built-in directive is used within the type system definition language to indicate deprecated portions of a GraphQL service's schema, such as deprecated fields on a type, arguments on a field, input fields on an input type, or values of an enum type.", "isRepeatable": false, "locations": [ "FIELD_DEFINITION", + "ARGUMENT_DEFINITION", + "INPUT_FIELD_DEFINITION", "ENUM_VALUE" ], "args": [ { "name": "reason", - "description": "", + "description": null, "type": { "kind": "SCALAR", "name": "String", @@ -1694,7 +1780,7 @@ "args": [ { "name": "if", - "description": "", + "description": null, "type": { "kind": "NON_NULL", "name": null, @@ -1722,7 +1808,7 @@ "args": [ { "name": "if", - "description": "", + "description": null, "type": { "kind": "NON_NULL", "name": null, @@ -1737,6 +1823,32 @@ "deprecationReason": null } ] + }, + { + "name": "specifiedBy", + "description": "The @specifiedBy built-in directive is used within the type system definition language to provide a scalar specification URL for specifying the behavior of custom scalar types.", + "isRepeatable": false, + "locations": [ + "SCALAR" + ], + "args": [ + { + "name": "url", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ] } ] } diff --git a/src/Components/Modules/SoftwareStats/HardforkSchedule.tsx b/src/Components/Modules/SoftwareStats/HardforkSchedule.tsx new file mode 100644 index 0000000..8f1db1a --- /dev/null +++ b/src/Components/Modules/SoftwareStats/HardforkSchedule.tsx @@ -0,0 +1,84 @@ +/* +Copyright 2021 ChainSafe Systems +SPDX-License-Identifier: LGPL-3.0-only +*/ +import React from "react" +import { createStyles, makeStyles } from "@chainsafe/common-theme" +import { ECTheme } from "../../Themes/types" +import { useEth2CrawlerApi } from "../../../Contexts/Eth2CrawlerContext" +import { Typography } from "@chainsafe/common-components" +import { GetNextHardForkSchedule_aggregateByHardforkSchedule } from "../../../GraphQL/types/GetNextHardForkSchedule" + + +const useStyles = makeStyles(({ constants, palette }: ECTheme) => { + return createStyles({ + root: { + border: `1px solid ${palette.background.paper}`, + borderRadius: "3px", + padding: constants.generalUnit * 2, + width: "inherit", + height: "inherit", + }, + container: { + color: palette.text.primary, + }, + title: { + marginBottom: constants.generalUnit * 4, + color: palette.text.primary, + }, + }) +}) + +const TableRow: React.FC<{ item: GetNextHardForkSchedule_aggregateByHardforkSchedule }> = ({ item }) => { + return ( + + {item.version} + {item.epoch} + {item.count} + + ) +} + + +const ScheduleData: React.FC<{ data: GetNextHardForkSchedule_aggregateByHardforkSchedule[] }> = ({ data }) => { + if (!data) { + return (
no nodes with scheduled HF
) + } + return(
+ + + + + + + + + + {data.map( + (item: GetNextHardForkSchedule_aggregateByHardforkSchedule) => + )} + +
Next Fork VersionNext Fork Epoch# of nodes
+
) +} + + +const HardforkSchedule: React.FC = () => { + const classes = useStyles() + + const { nextHardForkSchedule, isLoadingNextHardForkSchedule } = useEth2CrawlerApi() + + return ( +
+ + Hardfork Schedule + +
+ {isLoadingNextHardForkSchedule &&
Loading...
} + {!isLoadingNextHardForkSchedule && nextHardForkSchedule && } +
+
+ ) +} + +export default HardforkSchedule diff --git a/src/Components/Pages/HomePage.tsx b/src/Components/Pages/HomePage.tsx index 5bece15..d832e2f 100644 --- a/src/Components/Pages/HomePage.tsx +++ b/src/Components/Pages/HomePage.tsx @@ -18,6 +18,7 @@ import GridLayoutWrapper from "../Layouts/GridLayout/GridLayoutWrapper" import { Typography } from "@chainsafe/common-components" import CountryStats from "../Modules/CountryStats" import AltAirPercentage from "../Modules/SoftwareStats/AltAirPercentage" +import HardforkSchedule from "../Modules/SoftwareStats/HardforkSchedule" const useStyles = makeStyles(({ constants, breakpoints, palette }: ECTheme) => { return createStyles({ @@ -154,6 +155,7 @@ function HomePage() {
+ diff --git a/src/Contexts/Eth2CrawlerContext.tsx b/src/Contexts/Eth2CrawlerContext.tsx index 4f6f8ce..d7c57f4 100644 --- a/src/Contexts/Eth2CrawlerContext.tsx +++ b/src/Contexts/Eth2CrawlerContext.tsx @@ -29,6 +29,7 @@ import { LOAD_REGIONAL_STATS, LOAD_NODES_BY_COUNTRIES, LOAD_ALTAIR_UPGRADE_PERCENTAGE, + LOAD_NEXT_HARDFORK_SCHEDULE, } from "../GraphQL/Queries" import { GetNodeStats, GetNodeStats_getNodeStats } from "../GraphQL/types/GetNodeStats" import { @@ -45,6 +46,7 @@ import { GetNodesByCountries_aggregateByCountry, } from "../GraphQL/types/GetNodesByCountries" import { GetAltAirUpgradePercentage } from "../GraphQL/types/GetAltAirUpgradePercentage" +import { GetNextHardForkSchedule, GetNextHardForkSchedule_aggregateByHardforkSchedule } from "../GraphQL/types/GetNextHardForkSchedule" type Eth2CrawlerContextProps = { children: React.ReactNode | React.ReactNode[] @@ -60,6 +62,7 @@ interface IEth2CrawlerContext { nodeStatsOverTime: GetNodeStatsOverTime_getNodeStatsOverTime[] nodeRegionalStats: GetRegionalStats_getRegionalStats | undefined nodeCountByCountries: GetNodesByCountries_aggregateByCountry[] + nextHardForkSchedule: GetNextHardForkSchedule_aggregateByHardforkSchedule[] altAirPercentage: number | undefined isLoadingClients: boolean isLoadingOperatingSystems: boolean @@ -71,6 +74,7 @@ interface IEth2CrawlerContext { isLoadingNodeRegionalStats: boolean isLoadingNodeCountByCountries: boolean isLoadingAltAirPercentage: boolean + isLoadingNextHardForkSchedule: boolean } const Eth2CrawlerContext = React.createContext(undefined) @@ -100,6 +104,10 @@ const Eth2CrawlerProvider = ({ children }: Eth2CrawlerContextProps) => { >([]) const [altAirPercentage, setAltAirPercentage] = useState(undefined) + const [nextHardForkSchedule, setNextHardForkSchedule] = useState< + GetNextHardForkSchedule_aggregateByHardforkSchedule[] + >([]) + const [isLoadingClients, setIsLoadingClients] = useState(true) const [isLoadingOperatingSystems, setIsLoadingOperatingSystems] = useState(true) const [isLoadingNetworks, setIsLoadingNetworks] = useState(true) @@ -110,6 +118,7 @@ const Eth2CrawlerProvider = ({ children }: Eth2CrawlerContextProps) => { const [isLoadingNodeRegionalStats, setIsLoadingNodeRegionalStats] = useState(true) const [isLoadingNodeCountByCountries, setIsLoadingNodeCountByCountries] = useState(true) const [isLoadingAltAirPercentage, setIsLoadingAltAirPercentage] = useState(true) + const [isLoadingNextHardForkSchedule, setIsLoadingNextHardForkSchedule] = useState(true) const getInitialData = async () => { graphClient @@ -187,6 +196,13 @@ const Eth2CrawlerProvider = ({ children }: Eth2CrawlerContextProps) => { }) .catch(console.error) .finally(() => setIsLoadingAltAirPercentage(false)) + graphClient + .request(LOAD_NEXT_HARDFORK_SCHEDULE) + .then((result) => { + setNextHardForkSchedule(result.aggregateByHardforkSchedule) + }) + .catch(console.error) + .finally(() => setIsLoadingNextHardForkSchedule(false)) } useEffect(() => { @@ -206,6 +222,7 @@ const Eth2CrawlerProvider = ({ children }: Eth2CrawlerContextProps) => { nodeCountByCountries, clientVersions, altAirPercentage, + nextHardForkSchedule, isLoadingNodeStats, isLoadingClients, isLoadingOperatingSystems, @@ -216,6 +233,7 @@ const Eth2CrawlerProvider = ({ children }: Eth2CrawlerContextProps) => { isLoadingNodeRegionalStats, isLoadingNodeCountByCountries, isLoadingAltAirPercentage, + isLoadingNextHardForkSchedule, }} > {children} diff --git a/src/GraphQL/Queries.ts b/src/GraphQL/Queries.ts index 8df6540..2e00756 100644 --- a/src/GraphQL/Queries.ts +++ b/src/GraphQL/Queries.ts @@ -101,3 +101,13 @@ export const LOAD_ALTAIR_UPGRADE_PERCENTAGE = gql` getAltairUpgradePercentage } ` + +export const LOAD_NEXT_HARDFORK_SCHEDULE = gql` + query GetNextHardForkSchedule { + aggregateByHardforkSchedule { + version + epoch + count + } + } +` \ No newline at end of file diff --git a/src/GraphQL/types/GetNextHardForkSchedule.ts b/src/GraphQL/types/GetNextHardForkSchedule.ts new file mode 100644 index 0000000..09e390d --- /dev/null +++ b/src/GraphQL/types/GetNextHardForkSchedule.ts @@ -0,0 +1,23 @@ +/* +Copyright 2022 ChainSafe Systems +SPDX-License-Identifier: LGPL-3.0-only +*/ +/* tslint:disable */ +/* eslint-disable */ +// @generated +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL query operation: GetNextHardForkSchedule +// ==================================================== + +export interface GetNextHardForkSchedule_aggregateByHardforkSchedule { + __typename: "NextHardforkAggregation"; + version: string; + epoch: string; + count: number; +} + +export interface GetNextHardForkSchedule { + aggregateByHardforkSchedule: GetNextHardForkSchedule_aggregateByHardforkSchedule[]; +}