Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Search sorting #1374

Merged
merged 4 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions src/back/routes/proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import CoauthorModel from '../../entities/Coauthor/model'
import { CoauthorStatus } from '../../entities/Coauthor/types'
import isDAOCommittee from '../../entities/Committee/isDAOCommittee'
import { hasOpenSlots } from '../../entities/Committee/utils'
import { GrantRequest, getGrantRequestSchema } from '../../entities/Grant/types'
import { GrantRequest, SubtypeOptions, getGrantRequestSchema } from '../../entities/Grant/types'
import {
SUBMISSION_THRESHOLD_DRAFT,
SUBMISSION_THRESHOLD_GOVERNANCE,
Expand Down Expand Up @@ -49,6 +49,7 @@ import {
ProposalRequiredVP,
ProposalStatus,
ProposalType,
SortingOrder,
UpdateProposalStatusProposal,
newProposalBanNameScheme,
newProposalCatalystScheme,
Expand All @@ -60,6 +61,7 @@ import {
newProposalPitchScheme,
newProposalPollScheme,
newProposalTenderScheme,
toSearchSorting,
updateProposalStatusScheme,
} from '../../entities/Proposal/types'
import {
Expand Down Expand Up @@ -118,15 +120,15 @@ export default routes((route) => {

export async function getProposals(req: WithAuth) {
const query = req.query
const type = query.type && String(query.type)
const subtype = query.subtype && String(query.subtype)
const status = query.status && String(query.status)
const type = query.type ? (String(query.type) as ProposalType) : undefined
const subtype = query.subtype ? (String(query.subtype) as SubtypeOptions) : undefined
const status = query.status ? (String(query.status) as ProposalStatus) : undefined
ncomerci marked this conversation as resolved.
Show resolved Hide resolved
const user = query.user && String(query.user)
const search = query.search && String(query.search)
const timeFrame = query.timeFrame && String(query.timeFrame)
const timeFrameKey = query.timeFrameKey && String(query.timeFrameKey)
const coauthor = (query.coauthor && Boolean(query.coauthor)) || false
const order = query.order && String(query.order) === 'ASC' ? 'ASC' : 'DESC'
const order = toSearchSorting(String(query.order))
const snapshotIds = query.snapshotIds && String(query.snapshotIds)
const subscribed = query.subscribed ? req.auth || '' : undefined
const offset = query.offset && Number.isFinite(Number(query.offset)) ? Number(query.offset) : MIN_PROPOSAL_OFFSET
Expand Down Expand Up @@ -436,7 +438,7 @@ export async function createProposalTender(req: WithAuth) {

const tenderProposals = await ProposalModel.getProposalList({
linkedProposalId: configuration.linked_proposal_id,
order: 'ASC',
order: SortingOrder.ASC,
})

if (hasTenderProcessFinished(tenderProposals)) {
Expand Down
18 changes: 3 additions & 15 deletions src/clients/Governance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import { BadgeCreationResult, RevokeOrReinstateResult, UserBadges } from '../ent
import { BidRequest, UnpublishedBidAttributes } from '../entities/Bid/types'
import { Budget, BudgetWithContestants, CategoryBudget } from '../entities/Budget/types'
import { CoauthorAttributes, CoauthorStatus } from '../entities/Coauthor/types'
import { GrantRequest, ProposalGrantCategory, SubtypeOptions } from '../entities/Grant/types'
import { GrantRequest, ProposalGrantCategory } from '../entities/Grant/types'
import {
CategorizedGrants,
FilterProposalList,
NewProposalBanName,
NewProposalCatalyst,
NewProposalDraft,
Expand All @@ -28,7 +29,6 @@ import {
ProposalAttributes,
ProposalCommentsInDiscourse,
ProposalStatus,
ProposalType,
} from '../entities/Proposal/types'
import { QuarterBudgetAttributes } from '../entities/QuarterBudget/types'
import { SubscriptionAttributes } from '../entities/Subscription/types'
Expand Down Expand Up @@ -66,21 +66,9 @@ type NewProposalMap = {
[`/proposals/hiring`]: NewProposalHiring
}

export type GetProposalsFilter = {
user: string
type: ProposalType
subtype?: SubtypeOptions
status: ProposalStatus
subscribed: boolean | string
coauthor: boolean
search?: string | null
timeFrame?: string | null
timeFrameKey?: string | null
order?: 'ASC' | 'DESC'
export type GetProposalsFilter = FilterProposalList & {
limit: number
offset: number
snapshotIds?: string
linkedProposalId?: string
}

const getGovernanceApiUrl = () => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/Modal/BidVotingModal/BidVotingModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Close } from 'decentraland-ui/dist/components/Close/Close'
import { Header } from 'decentraland-ui/dist/components/Header/Header'
import { Modal, ModalProps } from 'decentraland-ui/dist/components/Modal/Modal'

import { ProposalAttributes, ProposalType } from '../../../entities/Proposal/types'
import { ProposalAttributes, ProposalType, SortingOrder } from '../../../entities/Proposal/types'
import { SelectedVoteChoice } from '../../../entities/Votes/types'
import useFormatMessage from '../../../hooks/useFormatMessage'
import useProposals from '../../../hooks/useProposals'
Expand All @@ -28,7 +28,7 @@ function BidVotingModal({ onCastVote, castingVote, linkedTenderId, proposalPageS
const { proposals } = useProposals({
type: ProposalType.Bid,
linkedProposalId: linkedTenderId,
order: 'ASC',
order: SortingOrder.ASC,
})
const { selectedChoice, showBidVotingModal, showVotingError, retryTimer, showSnapshotRedirect } = proposalPageState
const { snapshot_id: currentProposal } = proposal
Expand Down
19 changes: 15 additions & 4 deletions src/components/Search/SortingMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import classNames from 'classnames'
import { Dropdown } from 'decentraland-ui/dist/components/Dropdown/Dropdown'
import { useMobileMediaQuery } from 'decentraland-ui/dist/components/Media/Media'

import { SortingOrder, toSearchSorting } from '../../entities/Proposal/types'
import { getUrlFilters } from '../../helpers'
import useFormatMessage from '../../hooks/useFormatMessage'
import { navigate } from '../../utils/locations'
Expand All @@ -16,8 +17,12 @@ const SORT_KEY = 'order'
export default function SortingMenu() {
const location = useLocation()
const params = useMemo(() => new URLSearchParams(location.search), [location.search])
const order = useMemo(() => (params.get('order') === 'ASC' ? 'ASC' : 'DESC'), [params])
const arrowDirection = useMemo(() => (order === 'ASC' ? 'Upwards' : 'Downwards'), [order])
const isSearching = useMemo(() => !!params.get('search'), [params])
const order = useMemo(
() => toSearchSorting(params.get('order')) ?? (isSearching ? 'RELEVANCE' : SortingOrder.DESC),
[isSearching, params]
)
const arrowDirection = useMemo(() => (order === SortingOrder.ASC ? 'Upwards' : 'Downwards'), [order])
ncomerci marked this conversation as resolved.
Show resolved Hide resolved
const isMobile = useMobileMediaQuery()

const t = useFormatMessage()
Expand All @@ -29,13 +34,19 @@ export default function SortingMenu() {
text={t(`navigation.search.sorting.${order}`) || ''}
>
<Dropdown.Menu>
{isSearching && (
<Dropdown.Item
text={t('navigation.search.sorting.RELEVANCE')}
onClick={() => navigate(getUrlFilters(SORT_KEY, params, 'RELEVANCE'))}
/>
)}
<Dropdown.Item
text={t('navigation.search.sorting.DESC')}
onClick={() => navigate(getUrlFilters(SORT_KEY, params, 'DESC'))}
onClick={() => navigate(getUrlFilters(SORT_KEY, params, SortingOrder.DESC))}
/>
<Dropdown.Item
text={t('navigation.search.sorting.ASC')}
onClick={() => navigate(getUrlFilters(SORT_KEY, params, 'ASC'))}
onClick={() => navigate(getUrlFilters(SORT_KEY, params, SortingOrder.ASC))}
/>
</Dropdown.Menu>
</Dropdown>
Expand Down
34 changes: 13 additions & 21 deletions src/entities/Proposal/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,23 @@ import { OldGrantCategory, SubtypeAlternativeOptions, isGrantSubtype } from '../
import SubscriptionModel from '../Subscription/model'

import tsquery from './tsquery'
import { ProposalAttributes, ProposalStatus, ProposalType, isProposalType } from './types'
import {
FilterProposalList,
ProposalAttributes,
ProposalStatus,
ProposalType,
SortingOrder,
isProposalType,
} from './types'
import { SITEMAP_ITEMS_PER_PAGE, isProposalStatus } from './utils'

export type FilterProposalList = {
type: string
subtype?: string
user: string
status: string
subscribed: string
coauthor: boolean
search?: string
timeFrame?: string
timeFrameKey?: string
order?: 'ASC' | 'DESC'
snapshotIds?: string
linkedProposalId?: string
}

export type FilterPagination = {
limit: number
offset: number
}

const VALID_TIMEFRAME_KEYS = ['created_at', 'finish_at']
const VALID_ORDER_DIRECTION = ['ASC', 'DESC']
const VALID_ORDER_DIRECTION = Object.values(SortingOrder)

export default class ProposalModel extends Model<ProposalAttributes> {
static tableName = 'proposals'
Expand Down Expand Up @@ -245,7 +237,7 @@ export default class ProposalModel extends Model<ProposalAttributes> {
return 0
}

if (subscribed && !isEthereumAddress(subscribed)) {
if (subscribed && !isEthereumAddress(String(subscribed))) {
return 0
}

Expand Down Expand Up @@ -336,7 +328,7 @@ export default class ProposalModel extends Model<ProposalAttributes> {
return []
}

if (subscribed && !isEthereumAddress(subscribed)) {
if (subscribed && !isEthereumAddress(String(subscribed))) {
return []
}

Expand All @@ -358,13 +350,13 @@ export default class ProposalModel extends Model<ProposalAttributes> {

const timeFrame = this.parseTimeframe(filter.timeFrame)
const timeFrameKey = filter.timeFrameKey || 'created_at'
const orderDirection = order || 'DESC'
const orderDirection = !order || order === 'RELEVANCE' ? SortingOrder.DESC : order
ncomerci marked this conversation as resolved.
Show resolved Hide resolved

if (!VALID_TIMEFRAME_KEYS.includes(timeFrameKey) || !VALID_ORDER_DIRECTION.includes(orderDirection)) {
return []
}

const orderBy = search ? '"rank"' : `p.${timeFrameKey}`
const orderBy = search && (!order || order === 'RELEVANCE') ? '"rank"' : `p.${timeFrameKey}`

const sqlSnapshotIds = snapshotIds?.split(',').map((id) => SQL`${id}`)
const sqlSnapshotIdsJoin = sqlSnapshotIds ? join(sqlSnapshotIds) : null
Expand Down
32 changes: 32 additions & 0 deletions src/entities/Proposal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
PaymentToken,
ProjectStatus,
ProposalGrantCategory,
SubtypeOptions,
VestingStartDate,
} from '../Grant/types'
import { IndexedUpdate } from '../Updates/types'
Expand Down Expand Up @@ -68,6 +69,21 @@ export type ProposalAttributes<C extends Record<string, unknown> = any> = {
textsearch: SQLStatement | string | null | undefined
}

export type FilterProposalList = {
ncomerci marked this conversation as resolved.
Show resolved Hide resolved
user: string
type: ProposalType
subtype?: SubtypeOptions
status: ProposalStatus
subscribed: boolean | string
coauthor: boolean
search?: string | null
timeFrame?: string | null
timeFrameKey?: string | null
order?: SortingOrder | 'RELEVANCE'
snapshotIds?: string
linkedProposalId?: string
}

export enum ProposalStatus {
Pending = 'pending',
Active = 'active',
Expand All @@ -94,6 +110,11 @@ export enum ProposalType {
Bid = 'bid',
}

export enum SortingOrder {
ASC = 'ASC',
DESC = 'DESC',
}

export type GovernanceProcessType = ProposalType.Poll | ProposalType.Draft | ProposalType.Governance
export type BiddingProcessType = ProposalType.Pitch | ProposalType.Tender | ProposalType.Bid

Expand Down Expand Up @@ -152,6 +173,17 @@ export function toPoiType(value: string | null | undefined): PoiType | null {
return isPoiType(value) ? (value as PoiType) : null
}

export function toSearchSorting(value: string | null | undefined): 'RELEVANCE' | SortingOrder | undefined {
switch (value) {
case 'RELEVANCE':
ncomerci marked this conversation as resolved.
Show resolved Hide resolved
case SortingOrder.ASC:
case SortingOrder.DESC:
return value
default:
return undefined
}
}

export function getPoiTypeAction(poiType: PoiType) {
return poiType.split('_')[0] // "add" | "remove"
}
Expand Down
6 changes: 3 additions & 3 deletions src/hooks/useProposalsSearchParams.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useMemo } from 'react'

import { SubtypeOptions, toGrantSubtype } from '../entities/Grant/types'
import { ProposalStatus, ProposalType, toProposalType } from '../entities/Proposal/types'
import { ProposalStatus, ProposalType, SortingOrder, toProposalType, toSearchSorting } from '../entities/Proposal/types'
import { toProposalStatus } from '../entities/Proposal/utils'
import { toProposalListPage } from '../utils/locations'

Expand All @@ -14,7 +14,7 @@ export type SearchParams = {
search: string
searching: boolean
timeFrame: string
order: 'ASC' | 'DESC' | undefined
order?: SortingOrder | 'RELEVANCE'
ncomerci marked this conversation as resolved.
Show resolved Hide resolved
page: number
}

Expand All @@ -27,7 +27,7 @@ export function useProposalsSearchParams(): SearchParams {
const status = toProposalStatus(params.get('status'), () => undefined)
const search = params.get('search') || ''
const timeFrame = params.get('timeFrame') || ''
const order = params.get('order') ? (params.get('order') === 'ASC' ? 'ASC' : 'DESC') : undefined
const order = toSearchSorting(params.get('order'))
const searching = !!search && search.length > 0
const page = toProposalListPage(params.get('page')) ?? undefined

Expand Down
3 changes: 2 additions & 1 deletion src/intl/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
"filter": "Filter",
"sorting": {
"DESC": "Latest",
"ASC": "Oldest"
"ASC": "Oldest",
"RELEVANCE": "Relevance"
},
"category_filter_title": "Filter by Category",
"legacy_filter_title": "Filter by Legacy Grants",
Expand Down
4 changes: 2 additions & 2 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import OpenProposals from '../components/Home/OpenProposals'
import LoadingView from '../components/Layout/LoadingView'
import MaintenanceLayout from '../components/Layout/MaintenanceLayout'
import Navigation, { NavigationTab } from '../components/Layout/Navigation'
import { ProposalStatus } from '../entities/Proposal/types'
import { ProposalStatus, SortingOrder } from '../entities/Proposal/types'
import useFormatMessage from '../hooks/useFormatMessage'
import useProposals from '../hooks/useProposals'
import { isUnderMaintenance } from '../utils/maintenance'
Expand All @@ -23,7 +23,7 @@ export default function HomePage() {
const t = useFormatMessage()

const { proposals: endingSoonProposals, isLoadingProposals } = useProposals({
order: 'ASC',
order: SortingOrder.ASC,
status: ProposalStatus.Active,
timeFrameKey: 'finish_at',
page: 1,
Expand Down
Loading