Skip to content

Commit

Permalink
add DAO proposal search bar
Browse files Browse the repository at this point in the history
  • Loading branch information
NoahSaso committed Sep 25, 2024
1 parent d30619f commit 6198303
Show file tree
Hide file tree
Showing 15 changed files with 136 additions and 40 deletions.
38 changes: 31 additions & 7 deletions packages/state/indexer/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,35 @@ export type DaoProposalSearchResult = {
}

export type SearchDaoProposalsOptions = WithChainId<{
limit: number
/**
* Search query.
*/
query?: string
/**
* Limit number of search results.
*/
limit?: number
/**
* Only search proposals in a specific DAO.
*/
dao?: string
/**
* Whether or not to sort by recently created first. Defaults to false.
*/
recentFirst?: boolean
/**
* Exclude hidden DAOs. Defaults to false.
*/
excludeHidden?: boolean
}>

export const getRecentDaoProposals = async ({
export const searchDaoProposals = async ({
chainId,
query,
limit,
dao,
recentFirst,
excludeHidden,
}: SearchDaoProposalsOptions): Promise<DaoProposalSearchResult[]> => {
const client = await loadMeilisearchClient()

Expand All @@ -106,20 +129,21 @@ export const getRecentDaoProposals = async ({
const index = client.index(chainId + '_proposals')

const results = await index.search<Omit<DaoProposalSearchResult, 'chainId'>>(
null,
query,
{
limit,
filter: [
// Exclude hidden DAOs.
'value.hideFromSearch NOT EXISTS OR value.hideFromSearch != true',
...(excludeHidden
? ['value.hideFromSearch NOT EXISTS OR value.hideFromSearch != true']
: []),
// Ensure DAO and proposal ID exist.
'value.dao EXISTS',
dao ? `value.dao = "${dao}"` : 'value.dao EXISTS',
'value.daoProposalId EXISTS',
]
.map((filter) => `(${filter})`)
.join(' AND '),
// Most recently created first.
sort: ['value.proposal.start_height:desc'],
sort: recentFirst ? ['value.proposal.start_height:desc'] : undefined,
}
)

Expand Down
10 changes: 10 additions & 0 deletions packages/state/query/queries/dao.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
FetchQueryOptions,
QueryClient,
queryOptions,
skipToken,
} from '@tanstack/react-query'

Expand All @@ -22,6 +23,7 @@ import {
isConfiguredChainName,
} from '@dao-dao/utils'

import { SearchDaoProposalsOptions, searchDaoProposals } from '../../indexer'
import { accountQueries } from './account'
import { chainQueries } from './chain'
import { contractQueries } from './contract'
Expand Down Expand Up @@ -410,4 +412,12 @@ export const daoQueries = {
formula: 'daoCore/approvalDaos',
noFallback: true,
}),
/**
* Search DAO proposals.
*/
searchProposals: (options: SearchDaoProposalsOptions) =>
queryOptions({
queryKey: ['dao', 'searchProposals', options],
queryFn: () => searchDaoProposals(options),
}),
}
11 changes: 8 additions & 3 deletions packages/state/recoil/selectors/indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ import {
QuerySnapperOptions,
SearchDaoProposalsOptions,
SearchDaosOptions,
getRecentDaoProposals,
loadMeilisearchClient,
queryIndexer,
queryIndexerUpStatus,
querySnapper,
searchDaoProposals,
searchDaos,
} from '../../indexer'
import {
Expand Down Expand Up @@ -224,10 +224,15 @@ export const searchDaosSelector = selectorFamily<
*/
export const chainRecentDaoProposalsSelector = selectorFamily<
DaoProposalSearchResult[],
SearchDaoProposalsOptions
Omit<SearchDaoProposalsOptions, 'recentFirst' | 'excludeHidden'>
>({
key: 'chainRecentDaoProposals',
get: (options) => async () => await getRecentDaoProposals(options),
get: (options) => async () =>
await searchDaoProposals({
...options,
recentFirst: true,
excludeHidden: true,
}),
})

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ export const ExecuteProposalComponent: ActionComponent<
<ProposalLine
chainId={chainId}
coreAddress={coreAddress}
isPreProposeProposal={false}
openInNewTab
proposalId={`${selectedProposalModule.prefix}${proposalId}`}
proposalViewUrl={getDaoProposalPath(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ export const NeutronOverruleSubDaoProposalComponent: ActionComponent<
<ProposalLine
chainId={chainId}
coreAddress={coreAddress}
isPreProposeProposal={false}
openInNewTab
proposalId={proposalId}
proposalViewUrl={getDaoProposalPath(coreAddress, proposalId)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,6 @@ export const VetoProposalComponent: ActionComponent<VetoProposalOptions> = ({
<ProposalLine
chainId={chainId}
coreAddress={coreAddress}
isPreProposeProposal={false}
openInNewTab
proposalId={`${selectedProposalModule.prefix}${proposalId}`}
proposalViewUrl={getDaoProposalPath(
Expand Down Expand Up @@ -275,7 +274,6 @@ export const VetoProposalComponent: ActionComponent<VetoProposalOptions> = ({
<ProposalLine
chainId={chainId}
coreAddress={coreAddress}
isPreProposeProposal={false}
openInNewTab
proposalId={`${selectedProposalModule.prefix}${selectedProposal.id}`}
proposalViewUrl=""
Expand Down
9 changes: 4 additions & 5 deletions packages/stateful/components/ProposalLine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,12 @@ import { SuspenseLoader } from './SuspenseLoader'
export const ProposalLine = ({
chainId,
coreAddress,
proposalId,
...props
}: StatefulProposalLineProps) => {
const existingDao = useDaoContextIfAvailable()?.dao

const content = (
<ProposalModuleAdapterProvider proposalId={proposalId}>
<ProposalModuleAdapterProvider proposalId={props.proposalId}>
<InnerProposalLine {...props} />
</ProposalModuleAdapterProvider>
)
Expand All @@ -51,21 +50,21 @@ export const ProposalLine = ({

type InnerProposalLineProps = Pick<
StatefulProposalLineProps,
'proposalViewUrl' | 'isPreProposeProposal' | 'onClick' | 'openInNewTab'
'proposalId' | 'proposalViewUrl' | 'onClick' | 'openInNewTab'
>

const InnerProposalLine = ({
proposalId,
proposalViewUrl,
onClick,
isPreProposeProposal,
openInNewTab,
}: InnerProposalLineProps) => {
const { t } = useTranslation()
const {
components: { ProposalLine, PreProposeApprovalProposalLine },
} = useProposalModuleAdapter()

const Component = isPreProposeProposal
const Component = proposalId.includes('*')
? PreProposeApprovalProposalLine
: ProposalLine
if (!Component) {
Expand Down
87 changes: 72 additions & 15 deletions packages/stateful/components/ProposalList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useRecoilCallback, useSetRecoilState } from 'recoil'

import { daoQueries } from '@dao-dao/state/query'
import {
daoVetoableDaosSelector,
refreshProposalsIdAtom,
Expand All @@ -25,13 +26,15 @@ import {
} from '@dao-dao/types'
import {
NEUTRON_GOVERNANCE_DAO,
chainIsIndexed,
webSocketChannelNameForDao,
} from '@dao-dao/utils'

import {
useMembership,
useOnCurrentDaoWebSocketMessage,
useOnWebSocketMessage,
useQueryLoadingDataWithError,
} from '../hooks'
import { matchAndLoadCommon } from '../proposal-module-adapter'
import { daosWithDropdownVetoableProposalListSelector } from '../recoil'
Expand Down Expand Up @@ -157,7 +160,7 @@ export const ProposalList = ({
limit: PROP_PAGINATE_LIMIT,
})
)
: undefined
: []

const preProposeCompletedProposalInfos =
selectors.reversePreProposeCompletedProposalInfos
Expand All @@ -167,7 +170,7 @@ export const ProposalList = ({
limit: PROP_PAGINATE_LIMIT,
})
)
: undefined
: []

return [
...proposalInfos.map(
Expand All @@ -176,18 +179,18 @@ export const ProposalList = ({
...info,
})
),
...(preProposePendingProposalInfos?.map(
...preProposePendingProposalInfos.map(
(info): CommonProposalListInfoWithType => ({
type: ProposalType.PreProposePending,
...info,
})
) ?? []),
...(preProposeCompletedProposalInfos?.map(
),
...preProposeCompletedProposalInfos.map(
(info): CommonProposalListInfoWithType => ({
type: ProposalType.PreProposeCompleted,
...info,
})
) ?? []),
),
].map((info) => ({
...info,
proposalModule,
Expand Down Expand Up @@ -270,7 +273,6 @@ export const ProposalList = ({

const transformIntoProps = ({
id,
type,
status,
}: typeof newProposalInfos[number]): StatefulProposalLineProps & {
status: ProposalStatus
Expand All @@ -284,9 +286,6 @@ export const ProposalList = ({
onClick: onClickRef.current
? () => onClickRef.current?.({ proposalId: id })
: undefined,
isPreProposeProposal:
type === ProposalType.PreProposePending ||
type === ProposalType.PreProposeCompleted,
status,
})

Expand Down Expand Up @@ -374,6 +373,21 @@ export const ProposalList = ({
() => setRefreshProposalsId((id) => id + 1)
)

const [search, setSearch] = useState('')
// Cannot search without an indexer on the chain.
const canSearch = chainIsIndexed(dao.chainId)
const showingSearchResults = canSearch && !!search && search.length > 0
const searchedProposals = useQueryLoadingDataWithError(
showingSearchResults
? daoQueries.searchProposals({
chainId: dao.chainId,
dao: dao.coreAddress,
query: search,
limit: 20,
})
: undefined
)

return (
<StatelessProposalList
{...props}
Expand All @@ -390,23 +404,65 @@ export const ProposalList = ({
? []
: daosWithVetoableProposals.data
}
error={
showingSearchResults && searchedProposals.errored
? searchedProposals.error
: undefined
}
isMember={isMember}
loadMore={
// Force no arguments.
() => loadMore()
}
loadingMore={loading}
loadingMore={
showingSearchResults
? searchedProposals.loading || !!searchedProposals.updating
: loading
}
openProposals={
// Show executable proposals at the top in place of open proposals.
onlyExecutable
showingSearchResults
? []
: // Show executable proposals at the top in place of open proposals.
onlyExecutable
? historyProposals.filter(
({ status }) => status === ProposalStatusEnum.Passed
)
: openProposals
}
searchBarProps={
canSearch
? {
value: search,
onChange: (e) => setSearch(e.target.value),
}
: undefined
}
sections={
// Show executable proposals at the top in place of open proposals.
onlyExecutable
showingSearchResults
? [
{
title: t('title.results'),
proposals:
searchedProposals.loading || searchedProposals.errored
? []
: searchedProposals.data.flatMap(
(proposal): StatefulProposalLineProps | [] =>
proposal.value.daoProposalId
? {
chainId: dao.chainId,
coreAddress: dao.coreAddress,
proposalId: proposal.value.daoProposalId,
proposalViewUrl: getDaoProposalPath(
dao.coreAddress,
proposal.value.daoProposalId
),
}
: []
),
},
]
: // Show executable proposals at the top in place of open proposals.
onlyExecutable
? []
: [
{
Expand All @@ -422,6 +478,7 @@ export const ProposalList = ({
},
]
}
showingSearchResults={showingSearchResults}
/>
)
}
1 change: 0 additions & 1 deletion packages/stateful/feed/sources/OpenProposals/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,6 @@ export const feedOpenProposalsSelector = selectorFamily<
coreAddress,
`${proposalModule.prefix}${id}`
),
isPreProposeProposal: false,
},
},
pending:
Expand Down
1 change: 0 additions & 1 deletion packages/stateful/recoil/selectors/dao.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,6 @@ export const daosWithDropdownVetoableProposalListSelector = selectorFamily<
dao,
`${prefix}${id}`
),
isPreProposeProposal: false,
})
)
),
Expand Down
Loading

0 comments on commit 6198303

Please sign in to comment.