Skip to content

Commit fb71b8a

Browse files
committed
feat(proposals): add PythLazerProposals and PythCoreProposals components
1 parent b4fcc24 commit fb71b8a

File tree

3 files changed

+353
-260
lines changed

3 files changed

+353
-260
lines changed

governance/xc_admin/packages/xc_admin_frontend/components/tabs/Proposals/Proposals.tsx

Lines changed: 34 additions & 260 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,13 @@
1-
import { TransactionAccount } from '@sqds/mesh/lib/types'
21
import { useRouter } from 'next/router'
3-
import { useCallback, useContext, useEffect, useState, useMemo } from 'react'
4-
import { ClusterContext } from '../../../contexts/ClusterContext'
5-
import { useMultisigContext } from '../../../contexts/MultisigContext'
6-
import { PROPOSAL_STATUSES } from './utils'
7-
import ClusterSwitch from '../../ClusterSwitch'
2+
import { ProgramType } from '@pythnetwork/xc-admin-common'
3+
import { useProgramContext } from '../../../contexts/ProgramContext'
84
import ProgramSwitch from '../../ProgramSwitch'
9-
import Loadbar from '../../loaders/Loadbar'
10-
import { Select } from '../../Select'
11-
import { useQueryState, parseAsStringLiteral } from 'nuqs'
12-
13-
import { ProposalRow } from './ProposalRow'
14-
import { getProposalStatus } from './utils'
15-
import { Proposal } from './Proposal'
16-
import { useWallet } from '@solana/wallet-adapter-react'
17-
18-
type ProposalType = 'priceFeed' | 'governance'
19-
20-
const VOTE_STATUSES = [
21-
'any',
22-
'voted',
23-
'approved',
24-
'rejected',
25-
'cancelled',
26-
'notVoted',
27-
] as const
28-
const DEFAULT_VOTE_STATUS = 'any'
29-
30-
const PROPOSAL_STATUS_FILTERS = ['all', ...PROPOSAL_STATUSES] as const
31-
const DEFAULT_PROPOSAL_STATUS_FILTER = 'all'
5+
import PythCoreProposals from './PythCoreProposals'
6+
import PythLazerProposals from './PythLazerProposals'
327

338
const Proposals = () => {
349
const router = useRouter()
35-
const [currentProposal, setCurrentProposal] = useState<TransactionAccount>()
36-
const [currentProposalPubkey, setCurrentProposalPubkey] = useState<string>()
37-
const [statusFilter, setStatusFilter] = useQueryState(
38-
'status',
39-
parseAsStringLiteral(PROPOSAL_STATUS_FILTERS).withDefault(
40-
DEFAULT_PROPOSAL_STATUS_FILTER
41-
)
42-
)
43-
const [voteStatus, setVoteStatus] = useQueryState(
44-
'voteStatus',
45-
parseAsStringLiteral(VOTE_STATUSES).withDefault(DEFAULT_VOTE_STATUS)
46-
)
47-
const { cluster } = useContext(ClusterContext)
48-
const { publicKey: walletPublicKey } = useWallet()
49-
50-
const {
51-
upgradeMultisigAccount,
52-
priceFeedMultisigAccount,
53-
priceFeedMultisigProposals,
54-
upgradeMultisigProposals,
55-
isLoading: isMultisigLoading,
56-
refreshData,
57-
} = useMultisigContext()
58-
59-
const [proposalType, setProposalType] = useState<ProposalType>('priceFeed')
60-
61-
const multisigAccount =
62-
proposalType === 'priceFeed'
63-
? priceFeedMultisigAccount
64-
: upgradeMultisigAccount
65-
const multisigProposals =
66-
proposalType === 'priceFeed'
67-
? priceFeedMultisigProposals
68-
: upgradeMultisigProposals
10+
const { programType } = useProgramContext()
6911

7012
const handleClickBackToProposals = () => {
7113
delete router.query.proposal
@@ -79,219 +21,51 @@ const Proposals = () => {
7921
)
8022
}
8123

82-
useEffect(() => {
83-
if (router.query.proposal) {
84-
setCurrentProposalPubkey(router.query.proposal as string)
85-
} else {
86-
setCurrentProposalPubkey(undefined)
87-
}
88-
}, [router.query.proposal])
89-
90-
const switchProposalType = useCallback(() => {
91-
if (proposalType === 'priceFeed') {
92-
setProposalType('governance')
93-
} else {
94-
setProposalType('priceFeed')
95-
}
96-
}, [proposalType])
97-
98-
useEffect(() => {
99-
if (currentProposalPubkey) {
100-
const currProposal = multisigProposals.find(
101-
(proposal) => proposal.publicKey.toBase58() === currentProposalPubkey
102-
)
103-
setCurrentProposal(currProposal)
104-
if (currProposal === undefined) {
105-
const otherProposals =
106-
proposalType !== 'priceFeed'
107-
? priceFeedMultisigProposals
108-
: upgradeMultisigProposals
109-
if (
110-
otherProposals.findIndex(
111-
(proposal) =>
112-
proposal.publicKey.toBase58() === currentProposalPubkey
113-
) !== -1
114-
) {
115-
switchProposalType()
116-
}
117-
}
118-
}
119-
}, [
120-
switchProposalType,
121-
priceFeedMultisigProposals,
122-
proposalType,
123-
upgradeMultisigProposals,
124-
currentProposalPubkey,
125-
multisigProposals,
126-
cluster,
127-
])
128-
129-
const proposalsFilteredByStatus = useMemo(
130-
() =>
131-
statusFilter === 'all'
132-
? multisigProposals
133-
: multisigProposals.filter(
134-
(proposal) =>
135-
getProposalStatus(proposal, multisigAccount) === statusFilter
136-
),
137-
[statusFilter, multisigAccount, multisigProposals]
138-
)
139-
140-
const filteredProposals = useMemo(() => {
141-
if (walletPublicKey) {
142-
switch (voteStatus) {
143-
case 'any':
144-
return proposalsFilteredByStatus
145-
case 'voted': {
146-
return proposalsFilteredByStatus.filter((proposal) =>
147-
[
148-
...proposal.approved,
149-
...proposal.rejected,
150-
...proposal.cancelled,
151-
].some((vote) => vote.equals(walletPublicKey))
152-
)
153-
}
154-
case 'approved': {
155-
return proposalsFilteredByStatus.filter((proposal) =>
156-
proposal.approved.some((vote) => vote.equals(walletPublicKey))
157-
)
158-
}
159-
case 'rejected': {
160-
return proposalsFilteredByStatus.filter((proposal) =>
161-
proposal.rejected.some((vote) => vote.equals(walletPublicKey))
24+
// Function to render the appropriate program component
25+
const renderProgramComponent = () => {
26+
try {
27+
const proposalPubkey = router.query.proposal as string | undefined
28+
29+
switch (programType) {
30+
case ProgramType.PYTH_CORE:
31+
return (
32+
<PythCoreProposals
33+
proposalPubkey={proposalPubkey}
34+
onBackToProposals={handleClickBackToProposals}
35+
/>
16236
)
163-
}
164-
case 'cancelled': {
165-
return proposalsFilteredByStatus.filter((proposal) =>
166-
proposal.cancelled.some((vote) => vote.equals(walletPublicKey))
37+
case ProgramType.PYTH_LAZER:
38+
return (
39+
<PythLazerProposals
40+
proposalPubkey={proposalPubkey}
41+
onBackToProposals={handleClickBackToProposals}
42+
/>
16743
)
168-
}
169-
case 'notVoted': {
170-
return proposalsFilteredByStatus.filter((proposal) =>
171-
[
172-
...proposal.approved,
173-
...proposal.rejected,
174-
...proposal.cancelled,
175-
].every((vote) => !vote.equals(walletPublicKey))
176-
)
177-
}
44+
default:
45+
return <div>Unknown program type</div>
17846
}
179-
} else {
180-
return proposalsFilteredByStatus
47+
} catch (error) {
48+
console.error('Error rendering program component:', error)
49+
return <div>An error occurred loading the program component</div>
18150
}
182-
}, [proposalsFilteredByStatus, walletPublicKey, voteStatus])
51+
}
18352

18453
return (
18554
<div className="relative">
18655
<div className="container flex flex-col items-center justify-between lg:flex-row">
18756
<div className="mb-4 w-full text-left lg:mb-0">
18857
<h1 className="h1 mb-4">
189-
{proposalType === 'priceFeed' ? 'Price Feed ' : 'Governance '}{' '}
19058
{router.query.proposal === undefined ? 'Proposals' : 'Proposal'}
19159
</h1>
19260
</div>
19361
</div>
19462
<div className="container min-h-[50vh]">
195-
{router.query.proposal === undefined ? (
196-
<>
197-
<div className="flex flex-col justify-between md:flex-row">
198-
<div className="mb-4 flex items-center md:mb-0">
199-
<div className="flex space-x-4 items-center">
200-
<ProgramSwitch />
201-
<ClusterSwitch />
202-
</div>
203-
</div>
204-
<div className="flex space-x-2">
205-
{refreshData && (
206-
<button
207-
disabled={isMultisigLoading}
208-
className="sub-action-btn text-base"
209-
onClick={() => {
210-
const { fetchData } = refreshData()
211-
fetchData()
212-
}}
213-
>
214-
Refresh
215-
</button>
216-
)}
217-
<button
218-
disabled={isMultisigLoading}
219-
className="action-btn text-base"
220-
onClick={switchProposalType}
221-
>
222-
Show
223-
{proposalType !== 'priceFeed'
224-
? ' Price Feed '
225-
: ' Governance '}
226-
Proposals
227-
</button>
228-
</div>
229-
</div>
230-
<div className="relative mt-6">
231-
{isMultisigLoading ? (
232-
<div className="mt-3">
233-
<Loadbar theme="light" />
234-
</div>
235-
) : (
236-
<>
237-
<div className="flex items-end md:flex-row-reverse justify-between pb-4">
238-
<div className="flex flex-col md:flex-row md:items-center gap-4 text-sm">
239-
{walletPublicKey && (
240-
<Select
241-
label="Your Vote"
242-
value={voteStatus}
243-
options={VOTE_STATUSES}
244-
onChange={setVoteStatus}
245-
/>
246-
)}
247-
<Select
248-
label="Proposal Status"
249-
value={statusFilter}
250-
options={PROPOSAL_STATUS_FILTERS}
251-
onChange={setStatusFilter}
252-
/>
253-
</div>
254-
<h4 className="h4">
255-
Total Proposals: {filteredProposals.length}
256-
</h4>
257-
</div>
258-
{filteredProposals.length > 0 ? (
259-
<div className="flex flex-col">
260-
{filteredProposals.map((proposal, _idx) => (
261-
<ProposalRow
262-
key={proposal.publicKey.toBase58()}
263-
proposal={proposal}
264-
multisig={multisigAccount}
265-
/>
266-
))}
267-
</div>
268-
) : (
269-
<div className="mt-4">
270-
No proposals found. If you&apos;re a member of the
271-
multisig, you can create a proposal.
272-
</div>
273-
)}
274-
</>
275-
)}
276-
</div>
277-
</>
278-
) : !isMultisigLoading && currentProposal !== undefined ? (
279-
<>
280-
<div
281-
className="max-w-fit cursor-pointer bg-darkGray2 p-3 text-xs font-semibold outline-none transition-colors hover:bg-darkGray3 md:text-base"
282-
onClick={handleClickBackToProposals}
283-
>
284-
&#8592; back to proposals
285-
</div>
286-
<div className="relative mt-6">
287-
<Proposal proposal={currentProposal} multisig={multisigAccount} />
288-
</div>
289-
</>
290-
) : (
291-
<div className="mt-3">
292-
<Loadbar theme="light" />
63+
<div className="mb-4 md:mb-0">
64+
<div className="flex space-x-4 mb-4 items-center">
65+
<ProgramSwitch />
29366
</div>
294-
)}
67+
</div>
68+
{renderProgramComponent()}
29569
</div>
29670
</div>
29771
)

0 commit comments

Comments
 (0)