Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(web): extra statistic on homepage #1671

Merged
merged 27 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7b615f4
feat(web): extra statistic on homepage
nikhilverma360 Aug 1, 2024
0bc4fd4
fix(web): fix HomePageExtraStatsTypes
nikhilverma360 Aug 7, 2024
8e5b49d
Merge branch 'dev' into feat(web)/Extra-statistics-on-the-Home-page
kemuru Sep 13, 2024
0d0a60e
feat: styling according to figma
kemuru Sep 17, 2024
154ea0c
fix: slight styling, and most cases fetching optimiz, delete ineffici…
kemuru Sep 17, 2024
9da8250
fix: one week in seconds
kemuru Sep 18, 2024
80912c4
chore: better naming for clarity
kemuru Sep 18, 2024
a774c26
feat: courtesy of green, activity stats, most drawing chance, most re…
kemuru Sep 19, 2024
20a27f8
fix: random style fix for margin bottom in case cards title skeletons
kemuru Sep 19, 2024
09ef75d
feat: change number of disputes for number of votes, subgraph changes…
kemuru Sep 19, 2024
e614e6c
fix: add missing number votes fields
kemuru Sep 19, 2024
7a648af
feat: add selector according to figma of past blocks and times
kemuru Sep 20, 2024
0dfb08a
feat: add support for all time filtering, add skeletons for styling,
kemuru Sep 20, 2024
be37823
chore: add staletime to the query, comment older days
kemuru Sep 20, 2024
0448987
fix: few code smells
kemuru Sep 20, 2024
56af5a7
chore: add subgraph endpoint back
kemuru Sep 25, 2024
0ec4f0f
chore: bump subgraph package json version
kemuru Sep 25, 2024
a1361c1
feat: add effectivestake to the subgraph, modify hook
kemuru Sep 26, 2024
48d7869
chore: add my subgraph endpoint for local testing
kemuru Sep 26, 2024
db1b451
chore: changed tosorted to prevent array mutations
kemuru Sep 26, 2024
c2d85fa
fix: always iterate through presentcourts and check if pastcourt exists
kemuru Sep 27, 2024
9ddd81a
fix: remove one unnecessary loop and move the variables to the return…
kemuru Sep 27, 2024
0075331
chore: update subgraph version
kemuru Sep 27, 2024
781c226
chore(web): add config to use sorting without mutation on arrays
alcercu Oct 2, 2024
e85461b
refactor(web): extra stats block query algorithm improvement
alcercu Oct 2, 2024
82d8e7a
chore: readd correct subgraph endpoint
kemuru Oct 4, 2024
94c4074
Merge branch 'dev' into feat(web)/Extra-statistics-on-the-Home-page
kemuru Oct 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions web/src/assets/svgs/icons/long-arrow-up.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 56 additions & 0 deletions web/src/components/ExtraStatsDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from "react";
import styled from "styled-components";

import { StyledSkeleton } from "components/StyledSkeleton";

const Container = styled.div`
display: flex;
gap: 8px;
align-items: center;
margin-top: 24px;
`;

const SVGContainer = styled.div`
display: flex;
height: 14px;
width: 14px;
align-items: center;
justify-content: center;
svg {
fill: ${({ theme }) => theme.secondaryPurple};
}
`;

const TextContainer = styled.div`
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
justify-content: center;
`;

const StyledP = styled.p`
font-size: 14px;
font-weight: 600;
margin: 0;
`;

export interface IExtraStatsDisplay {
title: string;
text: string;
icon: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
}

const ExtraStatsDisplay: React.FC<IExtraStatsDisplay> = ({ title, text, icon: Icon, ...props }) => {
return (
<Container {...props}>
<SVGContainer>{<Icon />}</SVGContainer>
<TextContainer>
<label>{title}:</label>
<StyledP>{text !== null ? text : <StyledSkeleton />}</StyledP>
</TextContainer>
</Container>
);
};

export default ExtraStatsDisplay;
3 changes: 3 additions & 0 deletions web/src/consts/averageBlockTimeInSeconds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { arbitrum, arbitrumSepolia } from "viem/chains";

export const averageBlockTimeInSeconds = { [arbitrum.id]: 0.26, [arbitrumSepolia.id]: 0.268 };
37 changes: 37 additions & 0 deletions web/src/hooks/queries/useHomePageBlockQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useQuery } from "@tanstack/react-query";

import { useGraphqlBatcher } from "context/GraphqlBatcher";

import { graphql } from "src/graphql";
import { HomePageBlockQuery } from "src/graphql/graphql";
export type { HomePageBlockQuery };

const homePageBlockQuery = graphql(`
query HomePageBlock($blockNumber: Int) {
courts(orderBy: id, orderDirection: asc, block: { number: $blockNumber }) {
id
name
numberDisputes
feeForJuror
stake
}
}
`);

export const useHomePageBlockQuery = (blockNumber: number) => {
const isEnabled = blockNumber != null;
const { graphqlBatcher } = useGraphqlBatcher();

return useQuery({
queryKey: [`homePageBlockQuery${blockNumber}`],
enabled: isEnabled,
queryFn: async () => {
const data = await graphqlBatcher.fetch({
id: crypto.randomUUID(),
document: homePageBlockQuery,
variables: { blockNumber },
});
return data;
},
});
};
70 changes: 70 additions & 0 deletions web/src/hooks/queries/useHomePageExtraStats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useEffect, useMemo, useState } from "react";

import { DEFAULT_CHAIN } from "consts/chains";

import { HomePageBlockQuery } from "src/graphql/graphql";
import { isUndefined } from "src/utils";

import { useHomePageContext } from "../useHomePageContext";

import { useHomePageBlockQuery } from "./useHomePageBlockQuery";
import { useBlockNumber } from "wagmi";
import { averageBlockTimeInSeconds } from "consts/averageBlockTimeInSeconds";

type Court = HomePageBlockQuery["courts"][number];

const getCourtWithMaxDifference = (lastWeekCourts: Court[], currentCourts: Court[]): Court => {
const diffs = lastWeekCourts.map((court, idx) => {
return Number(currentCourts[idx].numberDisputes) - Number(court.numberDisputes);
});

const maxDiffCourtId = diffs.reduce((a, b) => (a > b ? a : b));

return lastWeekCourts[diffs.indexOf(maxDiffCourtId)];
};

const getCourtWithMaxDrawingChance = (currentCourts: Court[]): Court => {
return currentCourts.reduce((a, b) => (Number(a.stake) > Number(b.feeForJuror) ? b : a));
};

const getCourtWithMaxRewardChance = (currentCourts: Court[]): Court => {
return currentCourts.reduce((a, b) => (Number(a.feeForJuror) > Number(b.feeForJuror) ? a : b));
};

export interface HomePageExtraStatsType {
MostActiveCourt: string | null;
HighestDrawingChance: string | null;
HighestRewardChance: string | null;
}

export const useHomePageExtraStats = (): HomePageExtraStatsType => {
const { data } = useHomePageContext();
const [oneWeekAgoBlockNumber, setOneWeekAgoBlockNumber] = useState<number>();
const currentBlockNumber = useBlockNumber({ chainId: DEFAULT_CHAIN });

useEffect(() => {
if (currentBlockNumber?.data) {
const oneWeekInBlocks = Math.floor((7 * 24 * 3600) / averageBlockTimeInSeconds[DEFAULT_CHAIN]);
setOneWeekAgoBlockNumber(Number(currentBlockNumber.data) - oneWeekInBlocks);
}
}, [DEFAULT_CHAIN, currentBlockNumber]);

const { data: relData } = useHomePageBlockQuery(oneWeekAgoBlockNumber!);

const MostActiveCourt = useMemo(() => {
if (isUndefined(relData) || isUndefined(data)) {
return null;
}
return getCourtWithMaxDifference(relData.courts, data.courts).name ?? null;
}, [relData, data]);

const HighestDrawingChance = useMemo(() => {
return data ? getCourtWithMaxDrawingChance(data.courts).name ?? null : null;
}, [data]);

const HighestRewardChance = useMemo(() => {
return data ? getCourtWithMaxRewardChance(data.courts).name ?? null : null;
}, [data]);

return { MostActiveCourt, HighestDrawingChance, HighestRewardChance };
};
5 changes: 4 additions & 1 deletion web/src/hooks/queries/useHomePageQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ const homePageQuery = graphql(`
activeJurors
cases
}
courts {
courts(orderBy: id, orderDirection: asc) {
id
name
numberDisputes
feeForJuror
stake
}
}
`);
Expand Down
54 changes: 54 additions & 0 deletions web/src/pages/Home/CourtOverview/ExtraStats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from "react";
import styled from "styled-components";

import LawBalance from "svgs/icons/law-balance.svg";
import LongArrowUp from "svgs/icons/long-arrow-up.svg";

import { useHomePageExtraStats, HomePageExtraStatsType } from "hooks/queries/useHomePageExtraStats";

import ExtraStatsDisplay from "components/ExtraStatsDisplay";

const StyledCard = styled.div`
display: flex;
flex-wrap: wrap;
gap: 0 32px;
justify-content: center;
`;

interface IStat {
title: string;
getText: (data: HomePageExtraStatsType) => string | null;
icon: React.FC<React.SVGAttributes<SVGElement>>;
}

const stats: IStat[] = [
{
title: "Most Cases",
getText: (data) => data.MostActiveCourt,
icon: LongArrowUp,
},
{
title: "Highest drawing chance",
getText: (data) => data.HighestDrawingChance,
icon: LongArrowUp,
},
{
title: "Highest rewards chance",
getText: (data) => data.HighestRewardChance,
icon: LongArrowUp,
},
];

const ExtraStats = () => {
const data = useHomePageExtraStats();
return (
<StyledCard>
<ExtraStatsDisplay title="Activity (Last 7 days)" text="" icon={LawBalance} />
{stats.map(({ title, getText, icon }, i) => {
return <ExtraStatsDisplay key={i} {...{ title, icon }} text={getText(data)} />;
})}
</StyledCard>
);
};

export default ExtraStats;
2 changes: 2 additions & 0 deletions web/src/pages/Home/CourtOverview/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import { responsiveSize } from "styles/responsiveSize";

const StyledHeader = styled.div`
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 0 12px;
`;

const StyledH1 = styled.h1`
Expand Down
2 changes: 2 additions & 0 deletions web/src/pages/Home/CourtOverview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react";
import styled from "styled-components";

import Chart from "./Chart";
import ExtraStats from "./ExtraStats";
import Header from "./Header";
import Stats from "./Stats";

Expand All @@ -15,6 +16,7 @@ const CourtOverview: React.FC = () => (
<Header />
<Chart />
<Stats />
<ExtraStats />
</Container>
);

Expand Down
5 changes: 5 additions & 0 deletions web/src/utils/date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ export function getOneYearAgoTimestamp(): number {
return currentTime - 31536000; // One year in seconds
}

export function getOneWeekAgoTimestamp(): number {
const currentTime = new Date().getTime();
return currentTime - 604800; // One week in seconds
}

export function formatDate(unixTimestamp: number, withTime = false): string {
const date = new Date(unixTimestamp * 1000);
const options: Intl.DateTimeFormatOptions = withTime
Expand Down
Loading