Skip to content

Commit fa51e97

Browse files
committed
refactor(web): use hooks to avoid drilling props
1 parent 184322d commit fa51e97

File tree

7 files changed

+129
-157
lines changed

7 files changed

+129
-157
lines changed

web/src/components/Verdict/DisputeTimeline.tsx

Lines changed: 70 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
1-
import React from "react";
2-
import styled from "styled-components";
1+
import React, { useMemo } from "react";
2+
import { useParams } from "react-router-dom";
3+
import styled, { useTheme } from "styled-components";
34
import { _TimelineItem1, CustomTimeline } from "@kleros/ui-components-library";
45
import { Periods } from "consts/periods";
56
import { useVotingHistory } from "queries/useVotingHistory";
6-
import { DisputeDetailsQuery } from "queries/useDisputeDetailsQuery";
7-
import { lightTheme } from "~src/styles/themes";
7+
import { useDisputeTemplate } from "queries/useDisputeTemplate";
8+
import { DisputeDetailsQuery, useDisputeDetailsQuery } from "queries/useDisputeDetailsQuery";
89
import ClosedCaseIcon from "assets/svgs/icons/check-circle-outline.svg";
910
import AppealedCaseIcon from "assets/svgs/icons/close-circle.svg";
1011
import CalendarIcon from "assets/svgs/icons/calendar.svg";
11-
import { isUndefined } from "utils/index";
12+
1213
const Container = styled.div`
1314
display: flex;
1415
position: relative;
15-
margin-left: 16px;
16+
margin-left: 8px;
1617
`;
1718

1819
const StyledTimeline = styled(CustomTimeline)`
19-
width: calc(200px + (350 - 200) * (100vw - 375px) / (1250 - 375));
20+
width: 100%;
2021
margin-bottom: 32px;
2122
`;
2223

@@ -40,12 +41,6 @@ const StyledCalendarIcon = styled(CalendarIcon)`
4041
height: 14px;
4142
`;
4243

43-
interface IDisputeTimeline {
44-
id: string;
45-
disputeTemplate: any;
46-
disputeDetails: DisputeDetailsQuery;
47-
}
48-
4944
const getCaseEventTimes = (
5045
lastPeriodChange: string,
5146
currentPeriodIndex: number,
@@ -62,76 +57,77 @@ const getCaseEventTimes = (
6257
return formattedDate;
6358
};
6459

65-
const calculateLocalRoundJuror = (votes) => {
66-
const choiceCount = {};
67-
let maxVotes = 0;
68-
let winningChoice = null;
69-
70-
votes.forEach((vote) => {
71-
const { choice } = vote;
72-
73-
choiceCount[choice] = (choiceCount[choice] || 0) + 1;
60+
type TimelineItems = [_TimelineItem1, ..._TimelineItem1[]];
7461

75-
if (choiceCount[choice] > maxVotes) {
76-
maxVotes = choiceCount[choice];
77-
winningChoice = choice;
78-
}
79-
});
80-
81-
return winningChoice;
82-
};
83-
84-
const DisputeTimeline: React.FC<IDisputeTimeline> = ({ id, disputeTemplate, disputeDetails }) => {
62+
const useItems = (disputeDetails?: DisputeDetailsQuery) => {
63+
const { data: disputeTemplate } = useDisputeTemplate();
64+
const { id } = useParams();
8565
const { data: votingHistory } = useVotingHistory(id);
86-
const currentPeriodIndex = Periods[disputeDetails?.dispute?.period!];
87-
const lastPeriodChange = disputeDetails?.dispute?.lastPeriodChange;
88-
const courtTimePeriods = disputeDetails.dispute?.court.timesPerPeriod!;
89-
const rounds = votingHistory?.dispute?.rounds;
9066
const localRounds = votingHistory?.dispute?.disputeKitDispute?.localRounds;
67+
const theme = useTheme();
68+
69+
return useMemo<TimelineItems | undefined>(() => {
70+
if (disputeDetails?.dispute) {
71+
const currentPeriodIndex = disputeDetails?.dispute ? Periods[disputeDetails.dispute.period] : 0;
72+
const lastPeriodChange = disputeDetails?.dispute?.lastPeriodChange;
73+
const courtTimePeriods = disputeDetails.dispute?.court.timesPerPeriod;
74+
return localRounds?.reduce<TimelineItems>(
75+
(acc, { winningChoice }, index) => {
76+
const parsedWinningChoice = parseInt(winningChoice);
77+
const eventDate = getCaseEventTimes(lastPeriodChange, currentPeriodIndex, courtTimePeriods, false);
78+
const icon = disputeDetails?.dispute?.ruled && index === localRounds.length - 1 ? ClosedCaseIcon : "";
79+
80+
acc.push({
81+
title: `Jury Decision - Round ${index + 1}`,
82+
party:
83+
parsedWinningChoice !== 0
84+
? disputeTemplate?.answers?.[parseInt(winningChoice) - 1].title
85+
: "Refuse to Arbitrate",
86+
subtitle: eventDate,
87+
rightSided: true,
88+
variant: theme.secondaryPurple,
89+
Icon: icon !== "" ? icon : undefined,
90+
});
91+
92+
if (index < localRounds.length - 1) {
93+
acc.push({
94+
title: "Appealed",
95+
party: "",
96+
subtitle: eventDate,
97+
rightSided: true,
98+
Icon: AppealedCaseIcon,
99+
});
100+
}
101+
102+
return acc;
103+
},
104+
[
105+
{
106+
title: "Dispute created",
107+
party: "",
108+
subtitle: getCaseEventTimes(lastPeriodChange, currentPeriodIndex, courtTimePeriods, true),
109+
rightSided: true,
110+
variant: theme.secondaryPurple,
111+
},
112+
]
113+
);
114+
}
115+
return;
116+
}, [disputeDetails, disputeTemplate, localRounds, theme]);
117+
};
91118

92-
const dynamicItems: [_TimelineItem1, ..._TimelineItem1[]] = [
93-
{
94-
title: "Dispute created",
95-
party: "",
96-
subtitle: getCaseEventTimes(lastPeriodChange, currentPeriodIndex, courtTimePeriods, true),
97-
rightSided: true,
98-
variant: lightTheme.secondaryPurple,
99-
},
100-
];
101-
102-
if (rounds) {
103-
rounds.forEach((round, index) => {
104-
const localRuling = calculateLocalRoundJuror(!isUndefined(localRounds) && localRounds[index].votes);
105-
const eventDate = getCaseEventTimes(lastPeriodChange, currentPeriodIndex, courtTimePeriods, false);
106-
const variant = disputeDetails?.dispute?.ruled && index === rounds.length - 1 ? ClosedCaseIcon : "";
107-
108-
dynamicItems.push({
109-
title: `Jury Decision - Round ${index + 1}`,
110-
party: localRuling ? disputeTemplate?.answers?.[localRuling - 1].title : "Refuse to Arbitrate",
111-
subtitle: eventDate,
112-
rightSided: true,
113-
variant: lightTheme.secondaryPurple,
114-
Icon: variant !== "" ? variant : undefined,
115-
});
119+
const DisputeTimeline: React.FC = () => {
120+
const { id } = useParams();
121+
const { data: disputeDetails } = useDisputeDetailsQuery(id);
122+
const items = useItems(disputeDetails);
116123

117-
if (index < rounds.length - 1) {
118-
dynamicItems.push({
119-
title: "Appealed",
120-
party: "",
121-
subtitle: eventDate,
122-
rightSided: true,
123-
Icon: AppealedCaseIcon,
124-
});
125-
}
126-
});
127-
}
128124
return (
129125
<Container>
130-
<StyledTimeline items={dynamicItems} />
131-
{disputeDetails?.dispute?.ruled && (
126+
{items && <StyledTimeline {...{ items }} />}
127+
{disputeDetails?.dispute?.ruled && items && (
132128
<EnforcementContainer>
133129
<StyledCalendarIcon />
134-
<small>Enforcement: {dynamicItems[dynamicItems.length - 1].subtitle}</small>
130+
<small>Enforcement: {items.at(-1)?.subtitle}</small>
135131
</EnforcementContainer>
136132
)}
137133
</Container>
Lines changed: 47 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,22 @@
11
import React from "react";
2+
import { useNavigate, useParams } from "react-router-dom";
23
import styled from "styled-components";
34
import Identicon from "react-identicons";
4-
import { useNavigate } from "react-router-dom";
55
import ArrowIcon from "assets/svgs/icons/arrow.svg";
6+
import { useDisputeTemplate } from "queries/useDisputeTemplate";
7+
import { useDisputeDetailsQuery } from "queries/useDisputeDetailsQuery";
8+
import { useKlerosCoreCurrentRuling } from "hooks/contracts/generated";
69
import LightButton from "../LightButton";
710
import VerdictBanner from "./VerdictBanner";
8-
import { useKlerosCoreCurrentRuling } from "hooks/contracts/generated";
911

1012
const Container = styled.div`
11-
position: relative;
12-
width: calc(200px + (360 - 200) * (100vw - 375px) / (1250 - 375));
13-
14-
height: 400px;
15-
margin-left: 16px;
16-
.reverse-button {
17-
display: flex;
18-
flex-direction: row-reverse;
19-
gap: 8px;
20-
.button-text {
21-
color: ${({ theme }) => theme.primaryBlue};
22-
}
23-
}
13+
width: 100%;
2414
`;
2515

26-
const JuryContanier = styled.div`
16+
const JuryContainer = styled.div`
2717
display: flex;
2818
flex-direction: column;
2919
gap: 8px;
30-
margin-top: 32px;
3120
h3 {
3221
line-height: 21px;
3322
}
@@ -66,68 +55,72 @@ const StyledIdenticon = styled(Identicon)`
6655
`;
6756

6857
const Header = styled.h1`
69-
margin: 20px 0px 48px;
58+
margin: 20px 0px 32px 0px;
7059
`;
7160

7261
const Title = styled.small`
7362
color: ${({ theme }) => theme.secondaryText};
7463
`;
7564

7665
const StyledButton = styled(LightButton)`
77-
position: absolute;
78-
bottom: 0;
66+
display: flex;
67+
flex-direction: row-reverse;
68+
gap: 8px;
69+
> .button-text {
70+
color: ${({ theme }) => theme.primaryBlue};
71+
}
7972
`;
8073

81-
interface IDecisionText {
82-
ruled: boolean;
83-
}
84-
85-
const DecisionText: React.FC<IDecisionText> = ({ ruled }) => {
86-
return ruled ? <>Final Decision</> : <>Current Ruling</>;
87-
};
88-
89-
interface IFinalDecision {
90-
id: string;
91-
disputeTemplate: any;
92-
ruled: boolean;
93-
}
74+
const AnswerTitle = styled.h3`
75+
margin: 0;
76+
`;
9477

95-
const FinalDecision: React.FC<IFinalDecision> = ({ id, disputeTemplate, ruled }) => {
78+
const FinalDecision: React.FC = () => {
79+
const { id } = useParams();
80+
const { data: disputeTemplate } = useDisputeTemplate(id);
81+
const { data: disputeDetails } = useDisputeDetailsQuery(id);
82+
const ruled = disputeDetails?.dispute?.ruled ?? false;
9683
const navigate = useNavigate();
97-
const { data: currentRulingArray } = useKlerosCoreCurrentRuling({ args: [BigInt(id)], watch: true });
84+
const { data: currentRulingArray } = useKlerosCoreCurrentRuling({ args: [BigInt(id ?? 0)], watch: true });
9885
const currentRuling = Number(currentRulingArray?.[0]);
9986
const answer = disputeTemplate?.answers?.[currentRuling! - 1];
10087

101-
const handleClick = () => {
102-
navigate(`/cases/${id.toString()}/voting`);
103-
};
104-
10588
return (
10689
<Container>
10790
<VerdictBanner ruled={ruled} />
108-
<Header>
109-
<DecisionText ruled={ruled} />
110-
</Header>
111-
<JuryContanier>
91+
<Header> {ruled ? "Final Decision" : "Current Ruling"} </Header>
92+
<JuryContainer>
11293
<JuryDecisionTag>The jury decided in favor of:</JuryDecisionTag>
113-
{answer ? <h3>{`${answer.title}. ${answer.description}`}</h3> : <h3>Refuse to Arbitrate</h3>}
114-
</JuryContanier>
115-
<Divider />
116-
<UserContainer>
117-
<StyledIdenticon size="24" />
118-
<AliasTag>
119-
{disputeTemplate?.aliases?.challenger && <small>Alice.eth</small>}
120-
<Title>Claimant</Title>
121-
</AliasTag>
122-
</UserContainer>
94+
{answer ? (
95+
<div>
96+
<AnswerTitle>{answer.title}</AnswerTitle>
97+
<small>{answer.description}</small>
98+
</div>
99+
) : (
100+
<h3>Refuse to Arbitrate</h3>
101+
)}
102+
</JuryContainer>
123103
<Divider />
104+
{disputeTemplate?.aliases && (
105+
<>
106+
<UserContainer>
107+
<StyledIdenticon size="24" />
108+
<AliasTag>
109+
{disputeTemplate?.aliases?.challenger && <small>Alice.eth</small>}
110+
<Title>Claimant</Title>
111+
</AliasTag>
112+
</UserContainer>
113+
<Divider />
114+
</>
115+
)}
124116
<StyledButton
125-
onClick={handleClick}
117+
onClick={() => navigate(`/cases/${id?.toString()}/voting`)}
126118
text={"Check how the jury voted"}
127119
Icon={ArrowIcon}
128120
className="reverse-button"
129121
/>
130122
</Container>
131123
);
132124
};
125+
133126
export default FinalDecision;

web/src/components/Verdict/index.tsx

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React from "react";
22
import styled from "styled-components";
3-
import { DisputeDetailsQuery } from "queries/useDisputeDetailsQuery";
43
import FinalDecision from "./FinalDecision";
54
import DisputeTimeline from "./DisputeTimeline";
65

@@ -10,25 +9,13 @@ const Container = styled.div`
109
gap: 24px;
1110
`;
1211

13-
const VerticalDivider = styled.div`
14-
width: 1px;
15-
background-color: ${({ theme }) => theme.stroke};
16-
`;
17-
18-
interface IVerdict {
19-
id: string;
20-
disputeTemplate: any;
21-
disputeDetails: DisputeDetailsQuery;
22-
ruled: boolean;
23-
}
24-
25-
const Verdict: React.FC<IVerdict> = ({ id, disputeTemplate, disputeDetails, ruled }) => {
12+
const Verdict: React.FC = () => {
2613
return (
2714
<Container>
28-
<FinalDecision id={id} disputeTemplate={disputeTemplate} ruled={ruled} />
29-
<VerticalDivider />
30-
<DisputeTimeline id={id} disputeTemplate={disputeTemplate} disputeDetails={disputeDetails} />
15+
<FinalDecision />
16+
<DisputeTimeline />
3117
</Container>
3218
);
3319
};
20+
3421
export default Verdict;

web/src/graphql/gql.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const documents = {
3333
types.HomePageDocument,
3434
"\n query User($address: ID!) {\n user(id: $address) {\n totalDisputes\n totalResolvedDisputes\n totalCoherent\n tokens {\n court {\n id\n name\n }\n }\n shifts {\n tokenAmount\n ethAmount\n }\n }\n }\n":
3535
types.UserDocument,
36-
"\n query VotingHistory($disputeID: ID!) {\n dispute(id: $disputeID) {\n id\n rounds {\n nbVotes\n }\n disputeKitDispute {\n localRounds {\n ... on ClassicRound {\n totalVoted\n votes {\n id\n juror {\n id\n }\n ... on ClassicVote {\n choice\n justification\n }\n }\n }\n }\n }\n }\n }\n":
36+
"\n query VotingHistory($disputeID: ID!) {\n dispute(id: $disputeID) {\n id\n rounds {\n nbVotes\n }\n disputeKitDispute {\n localRounds {\n ... on ClassicRound {\n winningChoice\n totalVoted\n votes {\n id\n juror {\n id\n }\n ... on ClassicVote {\n choice\n justification\n }\n }\n }\n }\n }\n }\n }\n":
3737
types.VotingHistoryDocument,
3838
};
3939

@@ -115,8 +115,8 @@ export function graphql(
115115
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
116116
*/
117117
export function graphql(
118-
source: "\n query VotingHistory($disputeID: ID!) {\n dispute(id: $disputeID) {\n id\n rounds {\n nbVotes\n }\n disputeKitDispute {\n localRounds {\n ... on ClassicRound {\n totalVoted\n votes {\n id\n juror {\n id\n }\n ... on ClassicVote {\n choice\n justification\n }\n }\n }\n }\n }\n }\n }\n"
119-
): (typeof documents)["\n query VotingHistory($disputeID: ID!) {\n dispute(id: $disputeID) {\n id\n rounds {\n nbVotes\n }\n disputeKitDispute {\n localRounds {\n ... on ClassicRound {\n totalVoted\n votes {\n id\n juror {\n id\n }\n ... on ClassicVote {\n choice\n justification\n }\n }\n }\n }\n }\n }\n }\n"];
118+
source: "\n query VotingHistory($disputeID: ID!) {\n dispute(id: $disputeID) {\n id\n rounds {\n nbVotes\n }\n disputeKitDispute {\n localRounds {\n ... on ClassicRound {\n winningChoice\n totalVoted\n votes {\n id\n juror {\n id\n }\n ... on ClassicVote {\n choice\n justification\n }\n }\n }\n }\n }\n }\n }\n"
119+
): (typeof documents)["\n query VotingHistory($disputeID: ID!) {\n dispute(id: $disputeID) {\n id\n rounds {\n nbVotes\n }\n disputeKitDispute {\n localRounds {\n ... on ClassicRound {\n winningChoice\n totalVoted\n votes {\n id\n juror {\n id\n }\n ... on ClassicVote {\n choice\n justification\n }\n }\n }\n }\n }\n }\n }\n"];
120120

121121
export function graphql(source: string) {
122122
return (documents as any)[source] ?? {};

0 commit comments

Comments
 (0)