Skip to content

Commit 7d5c8ec

Browse files
kemurujaybuidl
authored andcommitted
feat(web): add skeletons to improve UX, modularize code
1 parent 05e8b49 commit 7d5c8ec

File tree

19 files changed

+115
-92
lines changed

19 files changed

+115
-92
lines changed

web/src/components/DisputeCard/index.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from "react";
22
import styled from "styled-components";
33
import { useNavigate } from "react-router-dom";
44
import { formatEther } from "viem";
5-
import Skeleton from "react-loading-skeleton";
5+
import { StyledSkeleton } from "components/StyledSkeleton";
66
import { Card } from "@kleros/ui-components-library";
77
import { Periods } from "consts/periods";
88
import { CasesPageQuery } from "queries/useCasesQuery";
@@ -17,10 +17,6 @@ const StyledCard = styled(Card)`
1717
min-width: 312px;
1818
width: auto;
1919
height: 260px;
20-
21-
.react-loading-skeleton {
22-
z-index: 0;
23-
}
2420
`;
2521

2622
const Container = styled.div`
@@ -58,7 +54,7 @@ const DisputeCard: React.FC<CasesPageQuery["disputes"][number]> = ({
5854
: getPeriodEndTimestamp(lastPeriodChange, currentPeriodIndex, court.timesPerPeriod);
5955
const { data: disputeTemplate } = useDisputeTemplate(id, arbitrated.id as `0x${string}`);
6056
const title = isUndefined(disputeTemplate) ? (
61-
<Skeleton />
57+
<StyledSkeleton />
6258
) : (
6359
disputeTemplate?.title ?? "The dispute's template is not correct please vote refuse to arbitrate"
6460
);

web/src/components/StatDisplay.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ const createPair = (iconColor: string, backgroundColor: string) => ({
3636

3737
export interface IStatDisplay {
3838
title: string;
39-
text: string;
40-
subtext: string;
39+
text: string | React.ReactNode;
40+
subtext: string | React.ReactNode;
4141
icon: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
4242
color: "red" | "orange" | "green" | "blue" | "purple";
4343
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import styled from "styled-components";
2+
import Skeleton from "react-loading-skeleton";
3+
4+
export const StyledSkeleton = styled(Skeleton)`
5+
z-index: 0;
6+
`;

web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from "react";
22
import styled from "styled-components";
33
import { useLoserSideCountdownContext } from "hooks/useClassicAppealContext";
4+
import { StyledSkeleton } from "components/StyledSkeleton";
45
import StageOne from "./StageOne";
56
import StageTwo from "./StageTwo";
67
import { isUndefined } from "utils/index";
@@ -14,7 +15,7 @@ const Options: React.FC = () => {
1415
return !isUndefined(loserSideCountdown) ? (
1516
<Container>{loserSideCountdown > 0 ? <StageOne /> : <StageTwo />}</Container>
1617
) : (
17-
<h1>Loading...</h1>
18+
<StyledSkeleton />
1819
);
1920
};
2021

web/src/pages/Cases/CaseDetails/Overview.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import styled from "styled-components";
33
import { useParams } from "react-router-dom";
44
import ReactMarkdown from "react-markdown";
55
import { formatEther } from "viem";
6-
import Skeleton from "react-loading-skeleton";
76
import { useDisputeDetailsQuery } from "queries/useDisputeDetailsQuery";
87
import { useDisputeTemplate } from "queries/useDisputeTemplate";
98
import { useCourtPolicy } from "queries/useCourtPolicy";
@@ -12,6 +11,7 @@ import { isUndefined } from "utils/index";
1211
import { Periods } from "consts/periods";
1312
import { IPFS_GATEWAY } from "consts/index";
1413
import PolicyIcon from "svgs/icons/policy.svg";
14+
import { StyledSkeleton } from "components/StyledSkeleton";
1515
import DisputeInfo from "components/DisputeCard/DisputeInfo";
1616
import Verdict from "components/Verdict/index";
1717

@@ -95,7 +95,7 @@ const Overview: React.FC<IOverview> = ({ arbitrable, courtID, currentPeriodIndex
9595
<Container>
9696
<h1>
9797
{isUndefined(disputeTemplate) ? (
98-
<Skeleton />
98+
<StyledSkeleton />
9999
) : (
100100
disputeTemplate?.title ?? "The dispute's template is not correct please vote refuse to arbitrate"
101101
)}

web/src/pages/Cases/CaseDetails/Timeline.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import styled from "styled-components";
33
import { Periods } from "consts/periods";
44
import { DisputeDetailsQuery } from "queries/useDisputeDetailsQuery";
55
import { Box, Steps } from "@kleros/ui-components-library";
6+
import { StyledSkeleton } from "components/StyledSkeleton";
67
import { useCountdown } from "hooks/useCountdown";
78
import { secondsToDayHourMinute } from "utils/date";
89

@@ -46,7 +47,7 @@ const useTimeline = (dispute: DisputeDetailsQuery["dispute"], currentItemIndex:
4647
dispute?.court.timesPerPeriod
4748
);
4849
const countdown = useCountdown(deadlineCurrentPeriod);
49-
const getSubitems = (index: number): string[] => {
50+
const getSubitems = (index: number): string[] | React.ReactNode[] => {
5051
if (typeof countdown !== "undefined" && dispute) {
5152
if (index === currentItemIndex && countdown === 0) {
5253
return ["Time's up!"];
@@ -60,7 +61,7 @@ const useTimeline = (dispute: DisputeDetailsQuery["dispute"], currentItemIndex:
6061
return [secondsToDayHourMinute(dispute?.court.timesPerPeriod[index])];
6162
}
6263
}
63-
return ["Loading..."];
64+
return [<StyledSkeleton key={index} width={60} />];
6465
};
6566
return titles.map((title, i) => ({
6667
title,

web/src/pages/Courts/CourtDetails/Description.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import styled from "styled-components";
33
import ReactMarkdown from "react-markdown";
44
import { Routes, Route, Navigate, useParams, useNavigate, useLocation } from "react-router-dom";
55
import { Tabs } from "@kleros/ui-components-library";
6+
import { StyledSkeleton } from "components/StyledSkeleton";
67
import { useCourtPolicy } from "queries/useCourtPolicy";
78

89
const Container = styled.div`
@@ -101,7 +102,7 @@ const formatMarkdown = (markdown?: string) =>
101102
markdown ? (
102103
<ReactMarkdown>{typeof markdown === "string" ? markdown.replace(/\n/g, " \n") : markdown}</ReactMarkdown>
103104
) : (
104-
<p>Loading...</p>
105+
<StyledSkeleton />
105106
);
106107

107108
export default Description;

web/src/pages/Courts/CourtDetails/Stats.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import { useCourtDetails, CourtDetailsQuery } from "queries/useCourtDetails";
55
import { useCoinPrice } from "hooks/useCoinPrice";
66
import { KLEROS_CONTRACT_ADDRESS, WETH_CONTRACT_ADDRESS } from "consts/index";
77
import { formatETH, formatPNK, formatUnitsWei, formatUSD, isUndefined } from "utils/index";
8+
import { calculateSubtextRender } from "utils/calculateSubtextRender";
89
import StatDisplay, { IStatDisplay } from "components/StatDisplay";
10+
import { StyledSkeleton } from "components/StyledSkeleton";
911
import BalanceIcon from "svgs/icons/law-balance.svg";
1012
import MinStake from "svgs/icons/min-stake.svg";
1113
import VoteStake from "svgs/icons/vote-stake.svg";
@@ -25,7 +27,7 @@ interface IStat {
2527
title: string;
2628
coinId?: number;
2729
getText: (data: CourtDetailsQuery["court"]) => string;
28-
getSubtext: (data: CourtDetailsQuery["court"], coinPrice?: number) => string;
30+
getSubtext?: (data: CourtDetailsQuery["court"], coinPrice?: number) => string;
2931
color: IStatDisplay["color"];
3032
icon: React.FC<React.SVGAttributes<SVGElement>>;
3133
}
@@ -50,7 +52,6 @@ const stats: IStat[] = [
5052
{
5153
title: "Active Jurors",
5254
getText: (data) => data?.numberStakedJurors,
53-
getSubtext: () => "",
5455
color: "purple",
5556
icon: PNKRedistributedIcon,
5657
},
@@ -65,14 +66,12 @@ const stats: IStat[] = [
6566
{
6667
title: "Cases",
6768
getText: (data) => data?.numberDisputes,
68-
getSubtext: () => "",
6969
color: "orange",
7070
icon: BalanceIcon,
7171
},
7272
{
7373
title: "In Progress",
7474
getText: (data) => data?.numberDisputes,
75-
getSubtext: () => "",
7675
color: "orange",
7776
icon: BalanceIcon,
7877
},
@@ -108,12 +107,13 @@ const Stats = () => {
108107
<StyledCard>
109108
{stats.map(({ title, coinId, getText, getSubtext, color, icon }, i) => {
110109
const coinPrice = !isUndefined(pricesData) ? pricesData[coinIdToAddress[coinId!]]?.price : undefined;
110+
111111
return (
112112
<StatDisplay
113113
key={i}
114114
{...{ title, color, icon }}
115-
text={data ? getText(data.court) : "Fetching..."}
116-
subtext={data ? getSubtext(data.court, coinPrice) : "Fetching..."}
115+
text={data ? getText(data.court) : <StyledSkeleton />}
116+
subtext={calculateSubtextRender(data ? data.court : undefined, getSubtext, coinPrice)}
117117
/>
118118
);
119119
})}

web/src/pages/Courts/CourtDetails/index.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { DEFAULT_CHAIN } from "consts/chains";
1010
import { PNK_FAUCET_CONTRACT_ADDRESS } from "consts/index";
1111
import { wrapWithToast } from "utils/wrapWithToast";
1212
import { isUndefined } from "utils/index";
13+
import { StyledSkeleton } from "components/StyledSkeleton";
1314
import Stats from "./Stats";
1415
import Description from "./Description";
1516
import StakePanel from "./StakePanel";
@@ -35,6 +36,10 @@ const StyledBreadcrumb = styled(Breadcrumb)`
3536
margin: 0px 0 12px 0;
3637
`;
3738

39+
const StyledBreadcrumbSkeleton = styled.div`
40+
margin-top: 16px;
41+
`;
42+
3843
const CourtDetails: React.FC = () => {
3944
const { id } = useParams();
4045
const [isSending, setIsSending] = useState(false);
@@ -44,7 +49,7 @@ const CourtDetails: React.FC = () => {
4449
const { address } = useAccount();
4550
const { data: claimed } = usePnkFaucetWithdrewAlready({
4651
enabled: !isUndefined(address),
47-
args: [address],
52+
args: [address ?? "0x00"],
4853
watch: true,
4954
});
5055

@@ -80,9 +85,15 @@ const CourtDetails: React.FC = () => {
8085
return (
8186
<Container>
8287
<StyledCard>
83-
<h1>{policy ? policy.name : "Loading..."}</h1>
88+
<h1>{policy ? policy.name : <StyledSkeleton width={200} />}</h1>
8489
<ButtonContainer>
85-
{items && <StyledBreadcrumb items={items} />}
90+
{items ? (
91+
<StyledBreadcrumb items={items} />
92+
) : (
93+
<StyledBreadcrumbSkeleton>
94+
<StyledSkeleton width={100} />
95+
</StyledBreadcrumbSkeleton>
96+
)}
8697
{chain?.id === DEFAULT_CHAIN && !claimed && (
8798
<Button
8899
variant="primary"

web/src/pages/Courts/TopSearch.tsx

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,17 @@
11
import React, { useMemo } from "react";
22
import { useNavigate } from "react-router-dom";
33
import { DropdownCascader } from "@kleros/ui-components-library";
4+
import { StyledSkeleton } from "components/StyledSkeleton";
45
import { useCourtTree, CourtTreeQuery } from "queries/useCourtTree";
56

67
const TopSearch: React.FC = () => {
78
const { data } = useCourtTree();
89
const navigate = useNavigate();
9-
const items = useMemo(
10-
() => typeof data !== "undefined" && [rootToItems(data.court)],
11-
[data]
12-
);
10+
const items = useMemo(() => typeof data !== "undefined" && [rootToItems(data.court)], [data]);
1311
return items ? (
14-
<DropdownCascader
15-
items={items}
16-
onSelect={(path: string) => navigate(path)}
17-
placeholder="Select Court"
18-
/>
12+
<DropdownCascader items={items} onSelect={(path: string) => navigate(path)} placeholder="Select Court" />
1913
) : (
20-
<></>
14+
<StyledSkeleton width={240} height={42} />
2115
);
2216
};
2317

@@ -30,10 +24,7 @@ interface IItem {
3024
const rootToItems = (court: CourtTreeQuery["court"]): IItem => ({
3125
label: court!.name ? court!.name : "Unnamed Court",
3226
value: `/courts/${court!.id}`,
33-
children:
34-
court!.children.length > 0
35-
? court!.children.map((child) => rootToItems(child))
36-
: undefined,
27+
children: court!.children.length > 0 ? court!.children.map((child) => rootToItems(child)) : undefined,
3728
});
3829

3930
export default TopSearch;

0 commit comments

Comments
 (0)