@@ -68,12 +68,12 @@ function getCurrentTimestampSnapshot() {
6868 return Math . floor ( Date . now ( ) / TIMESTAMP_REFRESH_MS ) * TIMESTAMP_REFRESH_MS
6969}
7070
71- function resolvePrimaryMarket ( event : Event ) : Market | null {
71+ function resolvePrimaryMarket ( event : Event , isResolvedEvent : boolean ) : Market | null {
7272 if ( event . markets . length === 0 ) {
7373 return null
7474 }
7575
76- if ( event . status === 'resolved' ) {
76+ if ( isResolvedEvent ) {
7777 return event . markets [ 0 ] ?? null
7878 }
7979
@@ -82,10 +82,12 @@ function resolvePrimaryMarket(event: Event): Market | null {
8282 ?? null
8383}
8484
85- function buildDateLabel ( event : Event , currentTimestamp : number | null ) {
86- if ( event . status === 'resolved' && event . resolved_at ) {
87- const resolvedAt = new Date ( event . resolved_at )
88- return Number . isNaN ( resolvedAt . getTime ( ) ) ? 'Resolved' : `Resolved ${ formatDate ( resolvedAt ) } `
85+ function buildDateLabel ( event : Event , currentTimestamp : number | null , isResolvedEvent : boolean ) {
86+ if ( isResolvedEvent ) {
87+ const resolvedAt = event . resolved_at ? new Date ( event . resolved_at ) : null
88+ return resolvedAt && ! Number . isNaN ( resolvedAt . getTime ( ) )
89+ ? `Resolved ${ formatDate ( resolvedAt ) } `
90+ : 'Resolved'
8991 }
9092
9193 if ( event . end_date ) {
@@ -127,7 +129,7 @@ function buildDateLabel(event: Event, currentTimestamp: number | null) {
127129 return 'Ends soon'
128130 }
129131
130- return event . status === 'resolved' ? 'Resolved' : 'Active'
132+ return 'Active'
131133}
132134
133135function getEventRecentVolume ( event : Event ) {
@@ -280,7 +282,6 @@ function usePredictionResultsFilters({
280282 key : routeScopeKey ,
281283 value : initialStatus ,
282284 } )
283- const [ searchParamsString , setSearchParamsString ] = useState ( '' )
284285 const searchDebounceTimeoutRef = useRef < number | null > ( null )
285286
286287 const currentTimestamp = useSyncExternalStore (
@@ -309,13 +310,11 @@ function usePredictionResultsFilters({
309310 isBookmarked,
310311 isDrawerOpen,
311312 searchDebounceTimeoutRef,
312- searchParamsString,
313313 searchValue,
314314 selectedSort,
315315 selectedStatus,
316316 setIsBookmarkedState,
317317 setIsDrawerOpenState,
318- setSearchParamsString,
319318 setSearchValueState,
320319 setSelectedSortState,
321320 setSelectedStatusState,
@@ -417,16 +416,14 @@ function useResolvedResultDisplay({
417416 isResolvedEvent,
418417 normalizeOutcomeLabel,
419418 resolvedLabel,
420- showResolvedOutcomeLayout,
421419} : {
422420 event : Event
423421 isResolvedEvent : boolean
424422 normalizeOutcomeLabel : ( label : string ) => string | null
425423 resolvedLabel : string
426- showResolvedOutcomeLayout : boolean
427424} ) {
428425 return useMemo ( ( ) => {
429- if ( ! showResolvedOutcomeLayout || ! isResolvedEvent ) {
426+ if ( ! isResolvedEvent ) {
430427 return {
431428 label : null ,
432429 outcomeIndex : null ,
@@ -438,7 +435,7 @@ function useResolvedResultDisplay({
438435 label : resolvedDisplay . label ? ( normalizeOutcomeLabel ( resolvedDisplay . label ) || resolvedDisplay . label ) : resolvedLabel ,
439436 outcomeIndex : resolvedDisplay . outcomeIndex ,
440437 }
441- } , [ event , isResolvedEvent , normalizeOutcomeLabel , resolvedLabel , showResolvedOutcomeLayout ] )
438+ } , [ event , isResolvedEvent , normalizeOutcomeLabel , resolvedLabel ] )
442439}
443440
444441export default function PredictionResultsClient ( {
@@ -466,13 +463,11 @@ export default function PredictionResultsClient({
466463 isBookmarked,
467464 isDrawerOpen,
468465 searchDebounceTimeoutRef,
469- searchParamsString,
470466 searchValue,
471467 selectedSort,
472468 selectedStatus,
473469 setIsBookmarkedState,
474470 setIsDrawerOpenState,
475- setSearchParamsString,
476471 setSearchValueState,
477472 setSelectedSortState,
478473 setSelectedStatusState,
@@ -549,16 +544,10 @@ export default function PredictionResultsClient({
549544 selectedStatus,
550545 } )
551546
552- const handleSearchParamsChange = useCallback ( ( {
553- searchParamsString : nextSearchParamsString ,
554- sort,
555- status,
556- } : {
557- searchParamsString : string
547+ const handleSearchParamsChange = useCallback ( ( { sort, status } : {
558548 sort : PredictionResultsSortOption
559549 status : PredictionResultsStatusOption
560550 } ) => {
561- setSearchParamsString ( current => current === nextSearchParamsString ? current : nextSearchParamsString )
562551 setSelectedSortState ( ( current ) => {
563552 const currentValue = current . key === routeScopeKey ? current . value : initialSort
564553 return currentValue === sort ? current : { key : routeScopeKey , value : sort }
@@ -571,40 +560,44 @@ export default function PredictionResultsClient({
571560 initialSort ,
572561 initialStatus ,
573562 routeScopeKey ,
574- setSearchParamsString ,
575563 setSelectedSortState ,
576564 setSelectedStatusState ,
577565 ] )
578566
579567 const isEmptyState = ! isPending && ! isFetching && visibleEvents . length === 0
580568 const showInitialSkeleton = visibleEvents . length === 0 && ( isPending || isFetching )
581569
582- function replaceRoute ( {
583- nextSearchValue = null ,
570+ function clearPendingSearchRoute ( ) {
571+ if ( ! searchDebounceTimeoutRef . current ) {
572+ return
573+ }
574+
575+ window . clearTimeout ( searchDebounceTimeoutRef . current )
576+ searchDebounceTimeoutRef . current = null
577+ }
578+
579+ function replaceSearchRoute ( {
580+ nextSearchValue,
584581 nextSort = selectedSort ,
585582 nextStatus = selectedStatus ,
586- immediate = true ,
587583 } : {
588- nextSearchValue ? : string | null
584+ nextSearchValue : string
589585 nextSort ?: PredictionResultsSortOption
590586 nextStatus ?: PredictionResultsStatusOption
591- immediate ?: boolean
592587 } ) {
593- const nextPath = nextSearchValue == null
594- ? pathname
595- : buildPredictionResultsPath ( nextSearchValue )
588+ const nextPath = buildPredictionResultsPath ( nextSearchValue )
596589
597590 if ( ! nextPath ) {
598591 return
599592 }
600593
601- const nextParams = buildPredictionResultsUrlSearchParams ( searchParamsString , {
594+ const nextParams = buildPredictionResultsUrlSearchParams ( window . location . search , {
602595 sort : nextSort ,
603596 status : nextStatus ,
604597 } )
605598 const nextQuery = nextParams . toString ( )
606599 const nextUrl = nextQuery ? `${ nextPath } ?${ nextQuery } ` : nextPath
607- const currentUrl = searchParamsString ? `${ pathname } ? ${ searchParamsString } ` : pathname
600+ const currentUrl = `${ pathname } ${ window . location . search } `
608601
609602 if ( nextUrl === currentUrl ) {
610603 return
@@ -616,11 +609,6 @@ export default function PredictionResultsClient({
616609 } )
617610 }
618611
619- if ( immediate ) {
620- runReplace ( )
621- return
622- }
623-
624612 if ( searchDebounceTimeoutRef . current ) {
625613 window . clearTimeout ( searchDebounceTimeoutRef . current )
626614 }
@@ -630,6 +618,32 @@ export default function PredictionResultsClient({
630618 } , 300 )
631619 }
632620
621+ function replaceFilterSearchParams ( {
622+ nextSort = selectedSort ,
623+ nextStatus = selectedStatus ,
624+ } : {
625+ nextSort ?: PredictionResultsSortOption
626+ nextStatus ?: PredictionResultsStatusOption
627+ } ) {
628+ const nextParams = buildPredictionResultsUrlSearchParams ( window . location . search , {
629+ sort : nextSort ,
630+ status : nextStatus ,
631+ } )
632+ const nextQuery = nextParams . toString ( )
633+ const currentUrl = `${ window . location . pathname } ${ window . location . search } ${ window . location . hash } `
634+ const nextUrl = `${ window . location . pathname } ${ nextQuery ? `?${ nextQuery } ` : '' } ${ window . location . hash } `
635+
636+ if ( nextUrl === currentUrl ) {
637+ return
638+ }
639+
640+ clearPendingSearchRoute ( )
641+
642+ startTransition ( ( ) => {
643+ window . history . replaceState ( null , '' , nextUrl )
644+ } )
645+ }
646+
633647 function handleRetryLoadMore ( ) {
634648 setCanRetryLoadMoreState ( { key : infiniteScrollScopeKey , value : true } )
635649 setInfiniteScrollErrorState ( { key : infiniteScrollScopeKey , value : null } )
@@ -641,25 +655,24 @@ export default function PredictionResultsClient({
641655
642656 function handleSearchValueChange ( nextValue : string ) {
643657 setSearchValueState ( { key : searchScopeKey , value : nextValue } )
644- replaceRoute ( {
658+ replaceSearchRoute ( {
645659 nextSearchValue : nextValue ,
646660 nextSort : selectedSort ,
647661 nextStatus : selectedStatus ,
648- immediate : false ,
649662 } )
650663 }
651664
652665 function handleClearFilters ( ) {
666+ clearPendingSearchRoute ( )
667+
653668 setIsBookmarkedState ( { key : routeScopeKey , value : false } )
654669 setIsDrawerOpenState ( { key : routeScopeKey , value : false } )
655670 setSearchValueState ( { key : searchScopeKey , value : initialInputValue } )
656671 setSelectedSortState ( { key : routeScopeKey , value : DEFAULT_PREDICTION_RESULTS_SORT } )
657672 setSelectedStatusState ( { key : routeScopeKey , value : DEFAULT_PREDICTION_RESULTS_STATUS } )
658- replaceRoute ( {
659- nextSearchValue : initialInputValue ,
673+ replaceFilterSearchParams ( {
660674 nextSort : DEFAULT_PREDICTION_RESULTS_SORT ,
661675 nextStatus : DEFAULT_PREDICTION_RESULTS_STATUS ,
662- immediate : true ,
663676 } )
664677 }
665678
@@ -686,11 +699,11 @@ export default function PredictionResultsClient({
686699 onSearchValueChange = { handleSearchValueChange }
687700 onSortChange = { ( ( value ) => {
688701 setSelectedSortState ( { key : routeScopeKey , value } )
689- replaceRoute ( { nextSort : value , immediate : true } )
702+ replaceFilterSearchParams ( { nextSort : value } )
690703 } ) }
691704 onStatusChange = { ( ( value ) => {
692705 setSelectedStatusState ( { key : routeScopeKey , value } )
693- replaceRoute ( { nextStatus : value , immediate : true } )
706+ replaceFilterSearchParams ( { nextStatus : value } )
694707 } ) }
695708 />
696709 )
@@ -811,7 +824,6 @@ export default function PredictionResultsClient({
811824 key = { event . id }
812825 event = { event }
813826 currentTimestamp = { currentTimestamp }
814- showResolvedOutcomeLayout = { selectedStatus === 'resolved' }
815827 />
816828 ) ) }
817829 </ div >
@@ -875,34 +887,31 @@ export default function PredictionResultsClient({
875887function PredictionResultRow ( {
876888 currentTimestamp,
877889 event,
878- showResolvedOutcomeLayout = false ,
879890} : {
880891 currentTimestamp : number | null
881892 event : Event
882- showResolvedOutcomeLayout ?: boolean
883893} ) {
884894 const t = useExtracted ( )
885895 const locale = useLocale ( )
886896 const normalizeOutcomeLabel = useOutcomeLabel ( )
887897 const { data : commentMetrics } = useCommentMetrics ( event . slug )
888- const primaryMarket = resolvePrimaryMarket ( event )
898+ const isResolvedEvent = isEventResolvedLike ( event )
899+ const primaryMarket = resolvePrimaryMarket ( event , isResolvedEvent )
889900 const primaryProbability = primaryMarket ?. probability ?? 0
890901 const supportingTags = event . tags . slice ( 0 , 2 )
891902 const isMultiMarket = Math . max ( event . total_markets_count ?? 0 , event . markets . length ) > 1
892- const isResolvedEvent = isEventResolvedLike ( event )
893903 const recentVolume = getEventRecentVolume ( event )
894904 const commentsCount = commentMetrics ?. comments_count ?? null
895905 const eventPath = resolveEventPagePath ( event )
896906 const resolvedLabel = t ( 'Resolved' )
897907 const selectedMarketLabel = primaryMarket ?. short_title ?. trim ( )
898908 || primaryMarket ?. title ?. trim ( )
899- || ( event . status === 'resolved' ? resolvedLabel : t ( 'Market' ) )
909+ || ( isResolvedEvent ? resolvedLabel : t ( 'Market' ) )
900910 const resolvedResultDisplay = useResolvedResultDisplay ( {
901911 event,
902912 isResolvedEvent,
903913 normalizeOutcomeLabel,
904914 resolvedLabel,
905- showResolvedOutcomeLayout,
906915 } )
907916 const resolvedBadgeOutcome = resolvedResultDisplay . outcomeIndex === OUTCOME_INDEX . NO
908917 ? 'no'
@@ -1002,13 +1011,13 @@ function PredictionResultRow({
10021011 </ a >
10031012 < span className = "flex items-center gap-1 whitespace-nowrap" >
10041013 < Clock3Icon className = "size-3.5 text-muted-foreground" />
1005- < span > { buildDateLabel ( event , currentTimestamp ) } </ span >
1014+ < span > { buildDateLabel ( event , currentTimestamp , isResolvedEvent ) } </ span >
10061015 </ span >
10071016 </ div >
10081017 </ div >
10091018
10101019 < div className = "flex max-w-[42%] min-w-[112px] shrink-0 items-center gap-3 self-center" >
1011- { showResolvedOutcomeLayout && isResolvedEvent
1020+ { isResolvedEvent
10121021 ? (
10131022 < div className = "flex min-w-0 flex-1 flex-col items-end justify-center text-right" >
10141023 < div className = "flex max-w-full items-center gap-2" >
0 commit comments