Skip to content

Commit db29a3d

Browse files
committed
feat: myprofile links, subgraph changes, pagination, query optimization, levels miniguides tweak etc
1 parent af51757 commit db29a3d

File tree

25 files changed

+362
-183
lines changed

25 files changed

+362
-183
lines changed

subgraph/core/schema.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ type Counter @entity {
237237
casesVoting: BigInt!
238238
casesRuled: BigInt!
239239
casesAppealing: BigInt!
240+
totalLeaderboardJurors: BigInt!
240241
}
241242

242243
type FeeToken @entity {

subgraph/core/src/KlerosCore.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,19 @@ import { createCourtFromEvent } from "./entities/Court";
1818
import { createDisputeKitFromEvent, filterSupportedDisputeKits } from "./entities/DisputeKit";
1919
import { createDisputeFromEvent } from "./entities/Dispute";
2020
import { createRoundFromRoundInfo, updateRoundTimeline } from "./entities/Round";
21-
import { updateCases, updateCasesAppealing, updateCasesRuled, updateCasesVoting } from "./datapoint";
21+
import {
22+
updateCases,
23+
updateCasesAppealing,
24+
updateCasesRuled,
25+
updateCasesVoting,
26+
updateTotalLeaderboardJurors,
27+
} from "./datapoint";
2228
import { addUserActiveDispute, computeCoherenceScore, ensureUser } from "./entities/User";
2329
import { updateJurorStake } from "./entities/JurorTokensPerCourt";
2430
import { createDrawFromEvent } from "./entities/Draw";
2531
import { updateTokenAndEthShiftFromEvent } from "./entities/TokenAndEthShift";
2632
import { updateArbitrableCases } from "./entities/Arbitrable";
27-
import { ClassicVote, Court, Dispute, Draw, Round, User } from "../generated/schema";
33+
import { ClassicVote, Counter, Court, Dispute, Draw, Round, User } from "../generated/schema";
2834
import { BigInt } from "@graphprotocol/graph-ts";
2935
import { updatePenalty } from "./entities/Penalty";
3036
import { ensureFeeToken } from "./entities/FeeToken";
@@ -142,6 +148,11 @@ export function handleNewPeriod(event: NewPeriod): void {
142148
const juror = ensureUser(draw.juror);
143149
juror.totalResolvedVotes = juror.totalResolvedVotes.plus(ONE);
144150

151+
// Increment totalLeaderboardJurors in the Counter entity if this is the first resolved vote for the juror
152+
if (juror.totalResolvedVotes.equals(ONE)) {
153+
updateTotalLeaderboardJurors(ONE, event.block.timestamp);
154+
}
155+
145156
// Since this is a ClassicVote entity, this will only work for the Classic DisputeKit (which has ID "1").
146157
const vote = ClassicVote.load(`${round.disputeKit}-${draw.id}`);
147158

subgraph/core/src/datapoint.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const VARIABLES = [
1515
"casesVoting",
1616
"casesRuled",
1717
"casesAppealing",
18+
"totalLeaderboardJurors",
1819
];
1920

2021
function updateDataPoint(delta: BigInt, timestamp: BigInt, variable: string): void {
@@ -43,6 +44,7 @@ function checkFirstDayActivity(): void {
4344
counter.casesVoting = ZERO;
4445
counter.casesRuled = ZERO;
4546
counter.casesAppealing = ZERO;
47+
counter.totalLeaderboardJurors = ZERO;
4648
counter.save();
4749
}
4850
}
@@ -86,3 +88,7 @@ export function updateCasesRuled(delta: BigInt, timestamp: BigInt): void {
8688
export function updateCasesAppealing(delta: BigInt, timestamp: BigInt): void {
8789
updateDataPoint(delta, timestamp, "casesAppealing");
8890
}
91+
92+
export function updateTotalLeaderboardJurors(delta: BigInt, timestamp: BigInt): void {
93+
updateDataPoint(delta, timestamp, "totalLeaderboardJurors");
94+
}

web/src/app.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ const App: React.FC = () => {
6565
}
6666
/>
6767
<Route
68-
path="jurors/*"
68+
path="jurors/:page/:order/:filter"
6969
element={
7070
<Suspense fallback={<Loader width={"48px"} height={"48px"} />}>
7171
<Jurors />

web/src/components/Popup/MiniGuides/JurorLevels.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,16 @@ const leftPageContents = [
3535
title: "Juror Level 1: Pythagoras",
3636
paragraphs: [
3737
"Jurors are classified into distinct levels according to their performance starting from Level 1.",
38-
"Level 1: Jurors with 0 cases arbitrated, OR Jurors with ≥ 1 case arbitrated with 0-70% of coherent votes.",
38+
"Level 1: Jurors with ≥ 1 case arbitrated with 0-70% of coherent votes.",
3939
],
4040
},
4141
{
4242
title: "Juror Level 2: Socrates",
43-
paragraphs: ["Level 2: Jurors with ≥ 3 cases arbitrated with 70%-80% of coherent votes."],
43+
paragraphs: ["Level 2: Jurors with ≥ 3 cases arbitrated with more than 70% coherent votes."],
4444
},
4545
{
4646
title: "Juror Level 3: Plato",
47-
paragraphs: ["Level 3: Jurors with ≥ 7 cases arbitrated with 80%-90% of coherent votes."],
47+
paragraphs: ["Level 3: Jurors with ≥ 7 cases arbitrated with more than 80% of coherent votes."],
4848
},
4949
{
5050
title: "Juror Level 4: Aristotle",
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { useQuery } from "@tanstack/react-query";
2+
3+
import { useGraphqlBatcher } from "context/GraphqlBatcher";
4+
import { isUndefined } from "utils/index";
5+
6+
import { graphql } from "src/graphql";
7+
import { JurorsByCoherenceScoreQuery } from "src/graphql/graphql";
8+
export type { JurorsByCoherenceScoreQuery };
9+
10+
const jurorsByCoherenceScoreQuery = graphql(`
11+
query JurorsByCoherenceScore($skip: Int, $first: Int, $orderBy: User_orderBy, $orderDirection: OrderDirection) {
12+
users(
13+
first: $first
14+
skip: $skip
15+
orderBy: $orderBy
16+
orderDirection: $orderDirection
17+
where: { totalResolvedVotes_gt: 0 }
18+
) {
19+
id
20+
coherenceScore
21+
totalCoherentVotes
22+
totalResolvedVotes
23+
totalResolvedDisputes
24+
}
25+
}
26+
`);
27+
28+
export const useJurorsByCoherenceScore = (skip = 0, first = 20, orderBy: string, orderDirection: string) => {
29+
const isEnabled = !isUndefined(first);
30+
const { graphqlBatcher } = useGraphqlBatcher();
31+
32+
return useQuery<JurorsByCoherenceScoreQuery>({
33+
queryKey: [`JurorsByCoherenceScore`, skip, first, orderBy, orderDirection],
34+
enabled: isEnabled,
35+
staleTime: Infinity,
36+
queryFn: async () =>
37+
isEnabled
38+
? await graphqlBatcher.fetch({
39+
id: crypto.randomUUID(),
40+
document: jurorsByCoherenceScoreQuery,
41+
variables: {
42+
skip,
43+
first,
44+
orderBy,
45+
orderDirection,
46+
},
47+
})
48+
: undefined,
49+
});
50+
};

web/src/hooks/queries/useTopUsersByCoherenceScore.ts

Lines changed: 0 additions & 43 deletions
This file was deleted.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { useQuery } from "@tanstack/react-query";
2+
3+
import { useGraphqlBatcher } from "context/GraphqlBatcher";
4+
5+
import { graphql } from "src/graphql";
6+
import { TotalLeaderboardJurorsQuery } from "src/graphql/graphql";
7+
export type { TotalLeaderboardJurorsQuery };
8+
9+
const totalLeaderboardJurorsQuery = graphql(`
10+
query TotalLeaderboardJurors($id: ID!) {
11+
counter(id: $id) {
12+
totalLeaderboardJurors
13+
}
14+
}
15+
`);
16+
17+
export const useTotalLeaderboardJurors = () => {
18+
const { graphqlBatcher } = useGraphqlBatcher();
19+
20+
return useQuery<TotalLeaderboardJurorsQuery>({
21+
queryKey: [`TotalLeaderboardJurors`],
22+
staleTime: Infinity,
23+
queryFn: async () =>
24+
await graphqlBatcher.fetch({
25+
id: crypto.randomUUID(),
26+
document: totalLeaderboardJurorsQuery,
27+
variables: {
28+
id: 0,
29+
},
30+
}),
31+
});
32+
};

web/src/layout/Header/navbar/Explore.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const links = [
5454
{ to: "/", text: "Home" },
5555
{ to: "/cases/display/1/desc/all", text: "Cases" },
5656
{ to: "/courts", text: "Courts" },
57-
{ to: "/jurors", text: "Jurors" },
57+
{ to: "/jurors/1/desc/all", text: "Jurors" },
5858
{ to: "/get-pnk", text: "Get PNK" },
5959
];
6060

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import React from "react";
2+
import styled from "styled-components";
3+
4+
import ArrowIcon from "svgs/icons/arrow.svg";
5+
6+
import { AddressOrName, IdenticonOrAvatar } from "components/ConnectWallet/AccountDisplay";
7+
import { StyledArrowLink } from "components/StyledArrowLink";
8+
import { ISettings } from "../../../index";
9+
10+
const Container = styled.div`
11+
display: flex;
12+
justify-content: center;
13+
align-items: center;
14+
padding: 16px 32px;
15+
gap: 24px;
16+
border: 1px solid ${({ theme }) => theme.stroke};
17+
border-radius: 30px;
18+
19+
> label {
20+
color: ${({ theme }) => theme.primaryText};
21+
font-size: 16px;
22+
font-weight: 600;
23+
}
24+
`;
25+
26+
const AvatarAndAddressContainer = styled.div`
27+
display: flex;
28+
flex-direction: row;
29+
gap: 8px;
30+
`;
31+
32+
const ReStyledArrowLink = styled(StyledArrowLink)`
33+
font-size: 14px;
34+
35+
> svg {
36+
height: 14px;
37+
width: 14px;
38+
}
39+
`;
40+
41+
const WalletAndProfile: React.FC<ISettings> = ({ toggleIsSettingsOpen }) => {
42+
return (
43+
<Container>
44+
<AvatarAndAddressContainer>
45+
<IdenticonOrAvatar />
46+
<AddressOrName />
47+
</AvatarAndAddressContainer>
48+
<ReStyledArrowLink to={"/profile/1/desc/all"} onClick={toggleIsSettingsOpen}>
49+
My Profile <ArrowIcon />
50+
</ReStyledArrowLink>
51+
</Container>
52+
);
53+
};
54+
export default WalletAndProfile;

web/src/layout/Header/navbar/Menu/Settings/General.tsx renamed to web/src/layout/Header/navbar/Menu/Settings/General/index.tsx

Lines changed: 7 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import React, { useMemo } from "react";
1+
import React from "react";
22
import styled from "styled-components";
33

44
import { useAccount, useDisconnect } from "wagmi";
55

66
import { Button } from "@kleros/ui-components-library";
77

8-
import { AddressOrName, ChainDisplay, IdenticonOrAvatar } from "components/ConnectWallet/AccountDisplay";
8+
import { ChainDisplay } from "components/ConnectWallet/AccountDisplay";
99
import { EnsureChain } from "components/EnsureChain";
10+
import WalletAndProfile from "./WalletAndProfile";
11+
import { ISettings } from "../../../index";
1012

1113
const Container = styled.div`
1214
display: flex;
@@ -33,22 +35,6 @@ const StyledChainContainer = styled.div`
3335
}
3436
`;
3537

36-
const StyledAddressContainer = styled.div`
37-
display: flex;
38-
justify-content: center;
39-
> label {
40-
color: ${({ theme }) => theme.primaryText};
41-
font-size: 16px;
42-
font-weight: 600;
43-
}
44-
`;
45-
46-
const StyledAvatarContainer = styled.div`
47-
display: flex;
48-
justify-content: center;
49-
margin-top: 12px;
50-
`;
51-
5238
const StyledButton = styled.div`
5339
display: flex;
5440
justify-content: center;
@@ -67,47 +53,24 @@ const UserContainer = styled.div`
6753
gap: 12px;
6854
`;
6955

70-
const StyledA = styled.a`
71-
text-decoration: none;
72-
label {
73-
cursor: pointer;
74-
color: ${({ theme }) => theme.primaryBlue};
75-
}
76-
77-
:hover {
78-
text-decoration: underline;
79-
}
80-
`;
81-
8256
export const DisconnectWalletButton: React.FC = () => {
8357
const { disconnect } = useDisconnect();
8458
return <Button text={`Disconnect`} onClick={() => disconnect()} />;
8559
};
8660

87-
const General: React.FC = () => {
88-
const { address, chain } = useAccount();
89-
90-
const addressExplorerLink = useMemo(() => {
91-
return `${chain?.blockExplorers?.default.url}/address/${address}`;
92-
}, [address, chain]);
61+
const General: React.FC<ISettings> = ({ toggleIsSettingsOpen }) => {
62+
const { address } = useAccount();
9363

9464
return (
9565
<EnsureChainContainer>
9666
<EnsureChain>
9767
<Container>
9868
{address && (
9969
<UserContainer>
100-
<StyledAvatarContainer>
101-
<IdenticonOrAvatar size="48" />
102-
</StyledAvatarContainer>
103-
<StyledAddressContainer>
104-
<StyledA href={addressExplorerLink} rel="noreferrer" target="_blank">
105-
<AddressOrName />
106-
</StyledA>
107-
</StyledAddressContainer>
10870
<StyledChainContainer>
10971
<ChainDisplay />
11072
</StyledChainContainer>
73+
<WalletAndProfile {...{ toggleIsSettingsOpen }} />
11174
<StyledButton>
11275
<DisconnectWalletButton />
11376
</StyledButton>

web/src/layout/Header/navbar/Menu/Settings/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,11 @@ const Settings: React.FC<ISettings> = ({ toggleIsSettingsOpen, initialTab }) =>
9191
setCurrentTab(n);
9292
}}
9393
/>
94-
{currentTab === 0 ? <General /> : <NotificationSettings {...{ toggleIsSettingsOpen }} />}
94+
{currentTab === 0 ? (
95+
<General {...{ toggleIsSettingsOpen }} />
96+
) : (
97+
<NotificationSettings {...{ toggleIsSettingsOpen }} />
98+
)}
9599
</Container>
96100
);
97101
};

web/src/pages/Home/TopJurors/JurorCard/Coherence.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const Container = styled.div`
1111
justify-content: center;
1212
`;
1313

14-
const getPercent = (num: number, den: number): string => {
14+
export const getPercent = (num: number, den: number): string => {
1515
if (den === 0) return "0%";
1616
return `${Math.floor((num * 100) / den)}%`;
1717
};

0 commit comments

Comments
 (0)