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: jurors page #1840

Merged
merged 21 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ed63810
feat: initial jurors page setup, jurors explore navlink, small stylin…
kemuru Jan 20, 2025
1b22294
feat: filter by totalresolvedvotes bigger than 0, types fix
kemuru Jan 21, 2025
63c308b
fix: subgraph bug which has not counting missed votes
kemuru Jan 22, 2025
ac069a3
Merge branch 'dev' into feat(web)/jurors-page
kemuru Jan 22, 2025
118feb6
Merge branch 'dev' into feat(web)/jurors-page
kemuru Jan 22, 2025
af51757
Merge branch 'dev' into feat(web)/jurors-page
kemuru Jan 22, 2025
db29a3d
feat: myprofile links, subgraph changes, pagination, query optimizati…
kemuru Jan 25, 2025
c95ee8a
Merge branch 'dev' into feat(web)/jurors-page
kemuru Jan 25, 2025
f2867a2
feat: add subgraph field, ranking icon, fix extra stats bug, filters,…
kemuru Jan 27, 2025
c749b2d
fix: nitpick text change
kemuru Jan 27, 2025
132743a
chore: add arrow back into the accordiontitle
kemuru Jan 27, 2025
07f83fa
chore: layout improvement
kemuru Jan 27, 2025
94c7637
fix: search to lower case in query
kemuru Jan 28, 2025
22e54d7
chore: move coherence percent function to utils, change name for clarity
kemuru Jan 28, 2025
81a3412
fix: layout shift if there is no rank, hide pagination if searching
kemuru Jan 28, 2025
b5d969b
Merge branch 'dev' into feat(web)/jurors-page
kemuru Jan 28, 2025
535508a
fix: few code smells
kemuru Jan 28, 2025
d04f0e1
chore: change label in mobile if search is active
kemuru Jan 28, 2025
876e208
Merge branch 'dev' into feat(web)/jurors-page
alcercu Jan 29, 2025
e6875eb
chore: subgraphs redeploy, scripts tweaks
jaybuidl Jan 30, 2025
2bb0a19
chore: txt
kemuru Jan 30, 2025
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
2 changes: 2 additions & 0 deletions subgraph/core/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ interface Evidence {

type User @entity {
id: ID! # address
userAddress: String!
tokens: [JurorTokensPerCourt!]! @derivedFrom(field: "juror")
totalStake: BigInt!
totalDelayed: BigInt!
Expand Down Expand Up @@ -237,6 +238,7 @@ type Counter @entity {
casesVoting: BigInt!
casesRuled: BigInt!
casesAppealing: BigInt!
totalLeaderboardJurors: BigInt!
}

type FeeToken @entity {
Expand Down
32 changes: 23 additions & 9 deletions subgraph/core/src/KlerosCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@ import { createCourtFromEvent } from "./entities/Court";
import { createDisputeKitFromEvent, filterSupportedDisputeKits } from "./entities/DisputeKit";
import { createDisputeFromEvent } from "./entities/Dispute";
import { createRoundFromRoundInfo, updateRoundTimeline } from "./entities/Round";
import { updateCases, updateCasesAppealing, updateCasesRuled, updateCasesVoting } from "./datapoint";
import {
updateCases,
updateCasesAppealing,
updateCasesRuled,
updateCasesVoting,
updateTotalLeaderboardJurors,
} from "./datapoint";
import { addUserActiveDispute, computeCoherenceScore, ensureUser } from "./entities/User";
import { updateJurorStake } from "./entities/JurorTokensPerCourt";
import { createDrawFromEvent } from "./entities/Draw";
import { updateTokenAndEthShiftFromEvent } from "./entities/TokenAndEthShift";
import { updateArbitrableCases } from "./entities/Arbitrable";
import { ClassicVote, Court, Dispute, Draw, Round, User } from "../generated/schema";
import { ClassicVote, Counter, Court, Dispute, Draw, Round, User } from "../generated/schema";
import { BigInt } from "@graphprotocol/graph-ts";
import { updatePenalty } from "./entities/Penalty";
import { ensureFeeToken } from "./entities/FeeToken";
Expand Down Expand Up @@ -139,13 +145,23 @@ export function handleNewPeriod(event: NewPeriod): void {
const draw = Draw.load(draws[j].id);
if (!draw) continue;

const juror = ensureUser(draw.juror);
juror.totalResolvedVotes = juror.totalResolvedVotes.plus(ONE);

// Increment totalLeaderboardJurors in the Counter entity if this is the first resolved vote for the juror
if (juror.totalResolvedVotes.equals(ONE)) {
updateTotalLeaderboardJurors(ONE, event.block.timestamp);
}

// Since this is a ClassicVote entity, this will only work for the Classic DisputeKit (which has ID "1").
const vote = ClassicVote.load(`${round.disputeKit}-${draw.id}`);

if (!vote) continue;

const juror = ensureUser(draw.juror);
juror.totalResolvedVotes = juror.totalResolvedVotes.plus(ONE);
if (!vote) {
// Recalculate coherenceScore
juror.coherenceScore = computeCoherenceScore(juror.totalCoherentVotes, juror.totalResolvedVotes);
juror.save();
continue;
}

if (vote.choice === null) continue;

Expand All @@ -155,9 +171,7 @@ export function handleNewPeriod(event: NewPeriod): void {
}

// Recalculate coherenceScore
if (juror.totalResolvedVotes.gt(ZERO)) {
juror.coherenceScore = computeCoherenceScore(juror.totalCoherentVotes, juror.totalResolvedVotes);
}
juror.coherenceScore = computeCoherenceScore(juror.totalCoherentVotes, juror.totalResolvedVotes);

juror.save();
}
Expand Down
6 changes: 6 additions & 0 deletions subgraph/core/src/datapoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const VARIABLES = [
"casesVoting",
"casesRuled",
"casesAppealing",
"totalLeaderboardJurors",
];

function updateDataPoint(delta: BigInt, timestamp: BigInt, variable: string): void {
Expand Down Expand Up @@ -43,6 +44,7 @@ function checkFirstDayActivity(): void {
counter.casesVoting = ZERO;
counter.casesRuled = ZERO;
counter.casesAppealing = ZERO;
counter.totalLeaderboardJurors = ZERO;
counter.save();
}
}
Expand Down Expand Up @@ -86,3 +88,7 @@ export function updateCasesRuled(delta: BigInt, timestamp: BigInt): void {
export function updateCasesAppealing(delta: BigInt, timestamp: BigInt): void {
updateDataPoint(delta, timestamp, "casesAppealing");
}

export function updateTotalLeaderboardJurors(delta: BigInt, timestamp: BigInt): void {
updateDataPoint(delta, timestamp, "totalLeaderboardJurors");
}
1 change: 1 addition & 0 deletions subgraph/core/src/entities/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export function ensureUser(id: string): User {

export function createUserFromAddress(id: string): User {
const user = new User(id);
user.userAddress = id.toLowerCase();
user.totalStake = ZERO;
user.totalDelayed = ZERO;
user.activeDisputes = ZERO;
Expand Down
2 changes: 1 addition & 1 deletion subgraph/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@kleros/kleros-v2-subgraph",
"version": "0.10.1",
"version": "0.10.2",
"license": "MIT",
"scripts": {
"update:core:arbitrum-sepolia-devnet": "./scripts/update.sh arbitrumSepoliaDevnet arbitrum-sepolia core/subgraph.yaml",
Expand Down
15 changes: 12 additions & 3 deletions web/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import QueryClientProvider from "context/QueryClientProvider";
import StyledComponentsProvider from "context/StyledComponentsProvider";
const Home = lazy(() => import("./pages/Home"));
const Cases = lazy(() => import("./pages/Cases"));
const Dashboard = lazy(() => import("./pages/Dashboard"));
const Profile = lazy(() => import("./pages/Profile"));
const Courts = lazy(() => import("./pages/Courts"));
const Jurors = lazy(() => import("./pages/Jurors"));
const DisputeResolver = lazy(() => import("./pages/Resolver"));
const GetPnk = lazy(() => import("./pages/GetPnk"));
const Settings = lazy(() => import("./pages/Settings"));
Expand Down Expand Up @@ -64,10 +65,18 @@ const App: React.FC = () => {
}
/>
<Route
path="dashboard/:page/:order/:filter"
path="jurors/:page/:order/:filter"
element={
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
<Dashboard />
<Jurors />
</Suspense>
}
/>
<Route
path="profile/:page/:order/:filter"
element={
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
<Profile />
</Suspense>
}
/>
Expand Down
3 changes: 3 additions & 0 deletions web/src/assets/svgs/icons/ranking.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions web/src/components/BlueIconTextButtonContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import styled from "styled-components";
import { hoverShortTransitionTiming } from "styles/commonStyles";

export const BlueIconTextButtonContainer = styled.div`
${hoverShortTransitionTiming}
display: flex;
align-items: center;
font-size: 14px;
font-weight: 400;
gap: 8px;
cursor: pointer;
color: ${({ theme }) => theme.primaryBlue};

svg path {
fill: ${({ theme }) => theme.primaryBlue};
}

&:hover {
color: ${({ theme }) => theme.secondaryBlue};
svg path {
fill: ${({ theme }) => theme.secondaryBlue};
}
}
`;
5 changes: 2 additions & 3 deletions web/src/components/EvidenceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { Card } from "@kleros/ui-components-library";

import AttachmentIcon from "svgs/icons/attachment.svg";

import { DEFAULT_CHAIN, getChain } from "consts/chains";
import { formatDate } from "utils/date";
import { getIpfsUrl } from "utils/getIpfsUrl";
import { shortenAddress } from "utils/shortenAddress";
Expand Down Expand Up @@ -224,7 +223,7 @@ const EvidenceCard: React.FC<IEvidenceCard> = ({
description,
fileURI,
}) => {
const dashboardLink = `/dashboard/1/desc/all?address=${sender}`;
const profileLink = `/profile/1/desc/all?address=${sender}`;

const transactionExplorerLink = useMemo(() => {
return getTxnExplorerLink(transactionHash ?? "");
Expand All @@ -249,7 +248,7 @@ const EvidenceCard: React.FC<IEvidenceCard> = ({
<BottomLeftContent>
<AccountContainer>
<Identicon size="24" string={sender} />
<InternalLink to={dashboardLink}>
<InternalLink to={profileLink}>
<Address>{shortenAddress(sender)}</Address>
</InternalLink>
</AccountContainer>
Expand Down
20 changes: 14 additions & 6 deletions web/src/components/ExtraStatsDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import { InternalLink } from "./InternalLink";
const Container = styled.div`
display: flex;
gap: 8px;
align-items: center;
justify-content: center;
flex-wrap: wrap;
`;

const TitleContainer = styled.div`
display: flex;
gap: 8px;
`;

const SVGContainer = styled.div`
Expand All @@ -22,12 +28,12 @@ const SVGContainer = styled.div`
}
`;

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

const StyledInternalLink = styled(InternalLink)`
Expand All @@ -49,17 +55,19 @@ export interface IExtraStatsDisplay {
const ExtraStatsDisplay: React.FC<IExtraStatsDisplay> = ({ title, courtId, text, content, icon: Icon, ...props }) => {
return (
<Container {...props}>
<SVGContainer>{<Icon />}</SVGContainer>
<TextContainer>
<TitleContainer>
<SVGContainer>{<Icon />}</SVGContainer>
<label>{title}:</label>
</TitleContainer>
<ContentContainer>
{content ? (
content
) : (
<StyledInternalLink to={`/courts/${courtId?.toString()}`}>
{!isUndefined(text) ? text : <StyledExtraStatTitleSkeleton />}
</StyledInternalLink>
)}
</TextContainer>
</ContentContainer>
</Container>
);
};
Expand Down
29 changes: 3 additions & 26 deletions web/src/components/HowItWorks.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,8 @@
import React from "react";
import styled from "styled-components";

import { hoverShortTransitionTiming } from "styles/commonStyles";

import BookOpenIcon from "svgs/icons/book-open.svg";

const Container = styled.div`
${hoverShortTransitionTiming}
display: flex;
align-items: center;
font-size: 14px;
font-weight: 400;
gap: 8px;
cursor: pointer;
color: ${({ theme }) => theme.primaryBlue};

svg path {
fill: ${({ theme }) => theme.primaryBlue};
}

&:hover {
color: ${({ theme }) => theme.secondaryBlue};
svg path {
fill: ${({ theme }) => theme.secondaryBlue};
}
}
`;
import { BlueIconTextButtonContainer } from "./BlueIconTextButtonContainer";

interface IHowItWorks {
isMiniGuideOpen: boolean;
Expand All @@ -36,10 +13,10 @@ interface IHowItWorks {
const HowItWorks: React.FC<IHowItWorks> = ({ isMiniGuideOpen, toggleMiniGuide, MiniGuideComponent }) => {
return (
<>
<Container onClick={toggleMiniGuide}>
<BlueIconTextButtonContainer onClick={toggleMiniGuide}>
<BookOpenIcon />
How it works
</Container>
</BlueIconTextButtonContainer>
{isMiniGuideOpen && <MiniGuideComponent toggleMiniGuide={toggleMiniGuide} />}
</>
);
Expand Down
21 changes: 21 additions & 0 deletions web/src/components/JurorsLeaderboardButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from "react";

import RankingIcon from "svgs/icons/ranking.svg";

import { BlueIconTextButtonContainer } from "./BlueIconTextButtonContainer";
import { InternalLink } from "./InternalLink";

const JurorsLeaderboardButton: React.FC = () => {
return (
<>
<InternalLink to={"/jurors/1/desc/all"}>
<BlueIconTextButtonContainer>
<RankingIcon />
Jurors Leaderboard
</BlueIconTextButtonContainer>
</InternalLink>
</>
);
};

export default JurorsLeaderboardButton;
10 changes: 5 additions & 5 deletions web/src/components/Popup/MiniGuides/JurorLevels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { Card as _Card } from "@kleros/ui-components-library";

import { landscapeStyle } from "styles/landscapeStyle";

import Coherence from "pages/Dashboard/JurorInfo/Coherence";
import PixelArt from "pages/Dashboard/JurorInfo/PixelArt";
import Coherence from "pages/Profile/JurorInfo/Coherence";
import PixelArt from "pages/Profile/JurorInfo/PixelArt";

import Template from "./MainStructureTemplate";
import { Title, ParagraphsContainer, LeftContentContainer } from "./PageContentsTemplate";
Expand Down Expand Up @@ -35,16 +35,16 @@ const leftPageContents = [
title: "Juror Level 1: Pythagoras",
paragraphs: [
"Jurors are classified into distinct levels according to their performance starting from Level 1.",
"Level 1: Jurors with 0 cases arbitrated, OR Jurors with ≥ 1 case arbitrated with 0-70% of coherent votes.",
"Level 1: Jurors with ≥ 1 case arbitrated with 0-70% of coherent votes.",
],
},
{
title: "Juror Level 2: Socrates",
paragraphs: ["Level 2: Jurors with ≥ 3 cases arbitrated with 70%-80% of coherent votes."],
paragraphs: ["Level 2: Jurors with ≥ 3 cases arbitrated with more than 70% coherent votes."],
},
{
title: "Juror Level 3: Plato",
paragraphs: ["Level 3: Jurors with ≥ 7 cases arbitrated with 80%-90% of coherent votes."],
paragraphs: ["Level 3: Jurors with ≥ 7 cases arbitrated with more than 80% of coherent votes."],
},
{
title: "Juror Level 4: Aristotle",
Expand Down
Loading
Loading