1
1
import { TransactionAccount } from '@sqds/mesh/lib/types'
2
2
import { useRouter } from 'next/router'
3
- import { useCallback , useContext , useEffect , useState } from 'react'
3
+ import { useCallback , useContext , useEffect , useState , useMemo } from 'react'
4
4
import { ClusterContext } from '../../../contexts/ClusterContext'
5
5
import { useMultisigContext } from '../../../contexts/MultisigContext'
6
- import { StatusFilterContext } from '../../../contexts/StatusFilterContext '
6
+ import { PROPOSAL_STATUSES } from './utils '
7
7
import ClusterSwitch from '../../ClusterSwitch'
8
- import ProposalStatusFilter from '../../ProposalStatusFilter'
9
8
import Loadbar from '../../loaders/Loadbar'
9
+ import { Select } from '../../Select'
10
+ import { useQueryState , parseAsStringLiteral } from 'nuqs'
10
11
11
12
import { ProposalRow } from './ProposalRow'
12
13
import { getProposalStatus } from './utils'
13
14
import { Proposal } from './Proposal'
15
+ import { useWallet } from '@solana/wallet-adapter-react'
14
16
15
17
type ProposalType = 'priceFeed' | 'governance'
16
18
19
+ const VOTE_STATUSES = [
20
+ 'any' ,
21
+ 'voted' ,
22
+ 'approved' ,
23
+ 'rejected' ,
24
+ 'cancelled' ,
25
+ 'notVoted' ,
26
+ ] as const
27
+ const DEFAULT_VOTE_STATUS = 'any'
28
+
29
+ const PROPOSAL_STATUS_FILTERS = [ 'all' , ...PROPOSAL_STATUSES ] as const
30
+ const DEFAULT_PROPOSAL_STATUS_FILTER = 'all'
31
+
17
32
const Proposals = ( ) => {
18
33
const router = useRouter ( )
19
34
const [ currentProposal , setCurrentProposal ] = useState < TransactionAccount > ( )
20
35
const [ currentProposalPubkey , setCurrentProposalPubkey ] = useState < string > ( )
36
+ const [ statusFilter , setStatusFilter ] = useQueryState (
37
+ 'status' ,
38
+ parseAsStringLiteral ( PROPOSAL_STATUS_FILTERS ) . withDefault (
39
+ DEFAULT_PROPOSAL_STATUS_FILTER
40
+ )
41
+ )
42
+ const [ voteStatus , setVoteStatus ] = useQueryState (
43
+ 'voteStatus' ,
44
+ parseAsStringLiteral ( VOTE_STATUSES ) . withDefault ( DEFAULT_VOTE_STATUS )
45
+ )
21
46
const { cluster } = useContext ( ClusterContext )
22
- const { statusFilter } = useContext ( StatusFilterContext )
47
+ const { publicKey : walletPublicKey } = useWallet ( )
23
48
24
49
const {
25
50
upgradeMultisigAccount,
@@ -40,9 +65,6 @@ const Proposals = () => {
40
65
proposalType === 'priceFeed'
41
66
? priceFeedMultisigProposals
42
67
: upgradeMultisigProposals
43
- const [ filteredProposals , setFilteredProposals ] = useState <
44
- TransactionAccount [ ]
45
- > ( [ ] )
46
68
47
69
const handleClickBackToProposals = ( ) => {
48
70
delete router . query . proposal
@@ -103,19 +125,60 @@ const Proposals = () => {
103
125
cluster ,
104
126
] )
105
127
106
- useEffect ( ( ) => {
107
- // filter price feed multisig proposals by status
108
- if ( statusFilter === 'all' ) {
109
- setFilteredProposals ( multisigProposals )
128
+ const proposalsFilteredByStatus = useMemo (
129
+ ( ) =>
130
+ statusFilter === 'all'
131
+ ? multisigProposals
132
+ : multisigProposals . filter (
133
+ ( proposal ) =>
134
+ getProposalStatus ( proposal , multisigAccount ) === statusFilter
135
+ ) ,
136
+ [ statusFilter , multisigAccount , multisigProposals ]
137
+ )
138
+
139
+ const filteredProposals = useMemo ( ( ) => {
140
+ if ( walletPublicKey ) {
141
+ switch ( voteStatus ) {
142
+ case 'any' :
143
+ return proposalsFilteredByStatus
144
+ case 'voted' : {
145
+ return proposalsFilteredByStatus . filter ( ( proposal ) =>
146
+ [
147
+ ...proposal . approved ,
148
+ ...proposal . rejected ,
149
+ ...proposal . cancelled ,
150
+ ] . some ( ( vote ) => vote . equals ( walletPublicKey ) )
151
+ )
152
+ }
153
+ case 'approved' : {
154
+ return proposalsFilteredByStatus . filter ( ( proposal ) =>
155
+ proposal . approved . some ( ( vote ) => vote . equals ( walletPublicKey ) )
156
+ )
157
+ }
158
+ case 'rejected' : {
159
+ return proposalsFilteredByStatus . filter ( ( proposal ) =>
160
+ proposal . rejected . some ( ( vote ) => vote . equals ( walletPublicKey ) )
161
+ )
162
+ }
163
+ case 'cancelled' : {
164
+ return proposalsFilteredByStatus . filter ( ( proposal ) =>
165
+ proposal . cancelled . some ( ( vote ) => vote . equals ( walletPublicKey ) )
166
+ )
167
+ }
168
+ case 'notVoted' : {
169
+ return proposalsFilteredByStatus . filter ( ( proposal ) =>
170
+ [
171
+ ...proposal . approved ,
172
+ ...proposal . rejected ,
173
+ ...proposal . cancelled ,
174
+ ] . every ( ( vote ) => ! vote . equals ( walletPublicKey ) )
175
+ )
176
+ }
177
+ }
110
178
} else {
111
- setFilteredProposals (
112
- multisigProposals . filter (
113
- ( proposal ) =>
114
- getProposalStatus ( proposal , multisigAccount ) === statusFilter
115
- )
116
- )
179
+ return proposalsFilteredByStatus
117
180
}
118
- } , [ statusFilter , multisigAccount , multisigProposals ] )
181
+ } , [ proposalsFilteredByStatus , walletPublicKey , voteStatus ] )
119
182
120
183
return (
121
184
< div className = "relative" >
@@ -167,11 +230,26 @@ const Proposals = () => {
167
230
</ div >
168
231
) : (
169
232
< >
170
- < div className = "flex items-center justify-between pb-4" >
233
+ < div className = "flex items-end md:flex-row-reverse justify-between pb-4" >
234
+ < div className = "flex flex-col md:flex-row md:items-center gap-4 text-sm" >
235
+ { walletPublicKey && (
236
+ < Select
237
+ label = "Your Vote"
238
+ value = { voteStatus }
239
+ options = { VOTE_STATUSES }
240
+ onChange = { setVoteStatus }
241
+ />
242
+ ) }
243
+ < Select
244
+ label = "Proposal Status"
245
+ value = { statusFilter }
246
+ options = { PROPOSAL_STATUS_FILTERS }
247
+ onChange = { setStatusFilter }
248
+ />
249
+ </ div >
171
250
< h4 className = "h4" >
172
251
Total Proposals: { filteredProposals . length }
173
252
</ h4 >
174
- < ProposalStatusFilter />
175
253
</ div >
176
254
{ filteredProposals . length > 0 ? (
177
255
< div className = "flex flex-col" >
0 commit comments