Skip to content

Commit 805c45d

Browse files
committed
react-app: Implement useProposalVote and useProposalDeposit queries to detail api
refs oursky#128
1 parent a993988 commit 805c45d

File tree

4 files changed

+379
-4
lines changed

4 files changed

+379
-4
lines changed

react-app/src/components/ProposalDetailScreen/ProposalDetailScreen.graphql

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,54 @@
1+
fragment ProposalDetailVoterDepositorValidator on Validator {
2+
id
3+
operatorAddress
4+
moniker
5+
identity
6+
securityContact
7+
}
8+
9+
fragment ProposalDetailVoterDepositorAddress on StringObject {
10+
value
11+
}
12+
13+
fragment ProposalDetailProposalVoteVoter on ProposalVoter {
14+
... on StringObject {
15+
...ProposalDetailVoterDepositorAddress
16+
}
17+
... on Validator {
18+
...ProposalDetailVoterDepositorValidator
19+
}
20+
}
21+
22+
fragment ProposalDetailProposalDepositDepositor on ProposalDepositor {
23+
... on StringObject {
24+
...ProposalDetailVoterDepositorAddress
25+
}
26+
... on Validator {
27+
...ProposalDetailVoterDepositorValidator
28+
}
29+
}
30+
31+
fragment ProposalDetailProposalVote on ProposalVote {
32+
id
33+
proposalId
34+
voter {
35+
...ProposalDetailProposalVoteVoter
36+
}
37+
option
38+
}
39+
40+
fragment ProposalDetailProposalDeposit on ProposalDeposit {
41+
id
42+
proposalId
43+
depositor {
44+
...ProposalDetailProposalDepositDepositor
45+
}
46+
amount {
47+
denom
48+
amount
49+
}
50+
}
51+
152
fragment ProposalDetailScreenProposal on Proposal {
253
id
354
proposalId
@@ -26,6 +77,38 @@ fragment ProposalDetailScreenProposal on Proposal {
2677
myReaction
2778
}
2879

80+
query ProposalDetailVotesPanelQuery(
81+
$proposalId: ID!
82+
$input: QueryProposalVotesInput!
83+
) {
84+
proposalByID(id: $proposalId) {
85+
votes(input: $input) {
86+
totalCount
87+
edges {
88+
node {
89+
...ProposalDetailProposalVote
90+
}
91+
}
92+
}
93+
}
94+
}
95+
96+
query ProposalDetailDepositsPanelQuery(
97+
$proposalId: ID!
98+
$input: QueryProposalDepositsInput!
99+
) {
100+
proposalByID(id: $proposalId) {
101+
deposits(input: $input) {
102+
totalCount
103+
edges {
104+
node {
105+
...ProposalDetailProposalDeposit
106+
}
107+
}
108+
}
109+
}
110+
}
111+
29112
query ProposalDetailScreenQuery($id: ID!) {
30113
proposalByID(id: $id) {
31114
...ProposalDetailScreenProposal

react-app/src/components/ProposalDetailScreen/ProposalDetailScreenAPI.ts

Lines changed: 264 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,35 @@
1-
import { useCallback, useMemo } from "react";
1+
import { useCallback, useMemo, useState } from "react";
22
import BigNumber from "bignumber.js";
33
import { differenceInDays } from "date-fns";
44
import {
55
ProposalDetailScreenQuery,
66
ProposalDetailScreenQueryQuery,
77
ProposalDetailScreenQueryQueryVariables,
8+
ProposalVoteSort,
9+
ProposalDetailVotesPanelQuery,
10+
ProposalDetailVotesPanelQueryQuery,
11+
ProposalDetailVotesPanelQueryQueryVariables,
12+
ProposalDetailDepositsPanelQuery,
13+
ProposalDetailDepositsPanelQueryQuery,
14+
ProposalDetailDepositsPanelQueryQueryVariables,
15+
ProposalDepositSort,
816
} from "../../generated/graphql";
917
import { useLazyGraphQLQuery } from "../../hooks/graphql";
1018
import { mapRequestData, RequestState } from "../../models/RequestState";
1119
import { convertMinimalTokenToToken } from "../../utils/coin";
1220
import { getReactionType } from "../reactions/ReactionModel";
13-
import { Proposal, ReactionItem } from "./ProposalDetailScreenModel";
21+
import { useQueryClient } from "../../providers/QueryClientProvider";
22+
import { ConnectionStatus, useWallet } from "../../providers/WalletProvider";
23+
import {
24+
PaginatedProposalVotes,
25+
PaginatedProposalDeposits,
26+
Proposal,
27+
ReactionItem,
28+
ProposalVote,
29+
ProposalDeposit,
30+
ProposalDepositDepositor,
31+
ProposalVoteVoter,
32+
} from "./ProposalDetailScreenModel";
1433

1534
const calculateTurnout = (tallyResult: Proposal["tallyResult"]) => {
1635
if (!tallyResult) {
@@ -26,6 +45,249 @@ const calculateTurnout = (tallyResult: Proposal["tallyResult"]) => {
2645
return turnout.toNumber();
2746
};
2847

48+
const getVoterOrDepositorAddress = (
49+
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
50+
voterOrDepositor: ProposalVoteVoter | ProposalDepositDepositor
51+
): string | null => {
52+
if (voterOrDepositor.__typename === "StringObject") {
53+
return voterOrDepositor.value;
54+
}
55+
56+
if (voterOrDepositor.__typename === "Validator") {
57+
return voterOrDepositor.operatorAddress ?? null;
58+
}
59+
60+
return null;
61+
};
62+
63+
interface UseProposalVotesQuery {
64+
(proposalId: string, initialOffset: number, pageSize: number): {
65+
requestState: RequestState<PaginatedProposalVotes>;
66+
fetch: (variables: {
67+
first: number;
68+
after: number;
69+
order: ProposalVoteSort;
70+
}) => Promise<void>;
71+
};
72+
}
73+
74+
export const useProposalVotesQuery: UseProposalVotesQuery = (
75+
proposalId,
76+
initialOffset,
77+
pageSize
78+
) => {
79+
const wallet = useWallet();
80+
const { query } = useQueryClient();
81+
82+
const [delegatedValidators, setDelegatedValidators] = useState<string[]>([]);
83+
84+
const [fetch, { requestState }] = useLazyGraphQLQuery<
85+
ProposalDetailVotesPanelQueryQuery,
86+
ProposalDetailVotesPanelQueryQueryVariables
87+
>(ProposalDetailVotesPanelQuery, {
88+
variables: {
89+
proposalId,
90+
input: {
91+
after: initialOffset,
92+
first: pageSize,
93+
order: {},
94+
},
95+
},
96+
fetchPolicy: "cache-and-network",
97+
nextFetchPolicy: "cache-first",
98+
});
99+
100+
const callFetch = useCallback(
101+
async ({
102+
first,
103+
after,
104+
order,
105+
}: {
106+
first: number;
107+
after: number;
108+
order: ProposalVoteSort;
109+
}) => {
110+
let delegatedValidators: string[] = [];
111+
112+
if (wallet.status === ConnectionStatus.Connected) {
113+
try {
114+
const delegations = await query.staking.delegatorDelegations(
115+
wallet.account.address
116+
);
117+
118+
delegatedValidators = delegations.delegationResponses
119+
.filter((r) => r.delegation != null)
120+
.map((r) => r.delegation!.validatorAddress);
121+
122+
setDelegatedValidators(delegatedValidators);
123+
} catch (err: unknown) {
124+
console.error("Failed to get user delegations = ", err);
125+
}
126+
}
127+
await fetch({
128+
variables: {
129+
proposalId,
130+
input: {
131+
first,
132+
after,
133+
order,
134+
pinnedValidators: delegatedValidators,
135+
},
136+
},
137+
});
138+
},
139+
[fetch, proposalId, wallet, query]
140+
);
141+
142+
const data = useMemo(() => {
143+
return mapRequestData<
144+
ProposalDetailVotesPanelQueryQuery,
145+
PaginatedProposalVotes
146+
>(requestState, (r) => {
147+
const allVotes = r.proposalByID?.votes.edges.map((v) => v.node) ?? [];
148+
const [pinnedVotes, votes] = allVotes.reduce<
149+
[ProposalVote[], ProposalVote[]]
150+
>(
151+
(acc, curr) => {
152+
const address = getVoterOrDepositorAddress(curr.voter);
153+
if (!address) return acc;
154+
155+
acc[delegatedValidators.includes(address) ? 0 : 1].push(curr);
156+
return acc;
157+
},
158+
[[], []]
159+
);
160+
161+
return {
162+
pinnedVotes,
163+
votes,
164+
totalCount: r.proposalByID?.votes.totalCount ?? 0,
165+
};
166+
});
167+
}, [requestState, delegatedValidators]);
168+
169+
return {
170+
requestState: data,
171+
fetch: callFetch,
172+
};
173+
};
174+
175+
interface UseProposalDepositsQuery {
176+
(proposalId: string, initialOffset: number, pageSize: number): {
177+
requestState: RequestState<PaginatedProposalDeposits>;
178+
fetch: (variables: {
179+
first: number;
180+
after: number;
181+
order: ProposalDepositSort;
182+
}) => Promise<void>;
183+
};
184+
}
185+
186+
export const useProposalDepositsQuery: UseProposalDepositsQuery = (
187+
proposalId,
188+
initialOffset,
189+
pageSize
190+
) => {
191+
const wallet = useWallet();
192+
const { query } = useQueryClient();
193+
194+
const [delegatedValidators, setDelegatedValidators] = useState<string[]>([]);
195+
const [fetch, { requestState }] = useLazyGraphQLQuery<
196+
ProposalDetailDepositsPanelQueryQuery,
197+
ProposalDetailDepositsPanelQueryQueryVariables
198+
>(ProposalDetailDepositsPanelQuery, {
199+
variables: {
200+
proposalId,
201+
input: {
202+
after: initialOffset,
203+
first: pageSize,
204+
order: {},
205+
},
206+
},
207+
fetchPolicy: "cache-and-network",
208+
nextFetchPolicy: "cache-first",
209+
});
210+
211+
const callFetch = useCallback(
212+
async ({
213+
first,
214+
after,
215+
order,
216+
}: {
217+
first: number;
218+
after: number;
219+
order: ProposalDepositSort;
220+
}) => {
221+
let delegatedValidators: string[] = [];
222+
223+
if (wallet.status === ConnectionStatus.Connected) {
224+
try {
225+
const delegations = await query.staking.delegatorDelegations(
226+
wallet.account.address
227+
);
228+
229+
delegatedValidators = delegations.delegationResponses
230+
.filter((r) => r.delegation != null)
231+
.map((r) => r.delegation!.validatorAddress);
232+
233+
setDelegatedValidators(delegatedValidators);
234+
} catch (err: unknown) {
235+
console.error("Failed to get user delegations = ", err);
236+
}
237+
}
238+
239+
await fetch({
240+
variables: {
241+
proposalId,
242+
input: {
243+
first,
244+
after,
245+
order,
246+
pinnedValidators: delegatedValidators,
247+
},
248+
},
249+
});
250+
},
251+
[fetch, proposalId, wallet, query]
252+
);
253+
254+
const data = useMemo(() => {
255+
return mapRequestData<
256+
ProposalDetailDepositsPanelQueryQuery,
257+
PaginatedProposalDeposits
258+
>(requestState, (r) => {
259+
const allDeposits =
260+
r.proposalByID?.deposits.edges.map((v) => v.node) ?? [];
261+
const [pinnedDeposits, deposits] = allDeposits.reduce<
262+
[ProposalDeposit[], ProposalDeposit[]]
263+
>(
264+
(acc, curr) => {
265+
const address =
266+
curr.depositor != null
267+
? getVoterOrDepositorAddress(curr.depositor)
268+
: null;
269+
if (!address) return acc;
270+
271+
acc[delegatedValidators.includes(address) ? 0 : 1].push(curr);
272+
return acc;
273+
},
274+
[[], []]
275+
);
276+
277+
return {
278+
pinnedDeposits,
279+
deposits,
280+
totalCount: r.proposalByID?.deposits.totalCount ?? 0,
281+
};
282+
});
283+
}, [requestState, delegatedValidators]);
284+
285+
return {
286+
requestState: data,
287+
fetch: callFetch,
288+
};
289+
};
290+
29291
export function useProposalQuery(): {
30292
requestState: RequestState<Proposal | null>;
31293
fetch: (id: string) => void;

0 commit comments

Comments
 (0)