Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "kuest-prediction-market",
"type": "module",
"private": true,
"packageManager": "pnpm@11.13.0",
"packageManager": "pnpm@11.15.1",
"description": "Launch Your Own Decentralized Prediction Market in Minutes.",
"repository": {
"type": "git",
Expand Down Expand Up @@ -65,7 +65,7 @@
"fumadocs-ui": "16.11.2",
"input-otp": "1.4.2",
"lucide-react": "1.24.0",
"next": "16.3.0-preview.6",
"next": "16.3.0-preview.8",
"next-intl": "4.13.2",
"next-themes": "0.4.6",
"postgres": "3.4.9",
Expand Down
1,375 changes: 553 additions & 822 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
minimumReleaseAge: 1440
minimumReleaseAgeExclude:
- next
- '@next/env@16.3.0-preview.8'
- '@next/swc-darwin-arm64@16.3.0-preview.8'
- '@next/swc-darwin-x64@16.3.0-preview.8'
- '@next/swc-linux-arm64-gnu@16.3.0-preview.8'
- '@next/swc-linux-arm64-musl@16.3.0-preview.8'
- '@next/swc-linux-x64-gnu@16.3.0-preview.8'
- '@next/swc-linux-x64-musl@16.3.0-preview.8'
- '@next/swc-win32-arm64-msvc@16.3.0-preview.8'
- '@next/swc-win32-x64-msvc@16.3.0-preview.8'
shellEmulator: true
blockExoticSubdeps: true
trustPolicy: no-downgrade
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ function getCurrentTimestampSnapshot() {
return Math.floor(Date.now() / TIMESTAMP_REFRESH_MS) * TIMESTAMP_REFRESH_MS
}

function resolvePrimaryMarket(event: Event): Market | null {
function resolvePrimaryMarket(event: Event, isResolvedEvent: boolean): Market | null {
if (event.markets.length === 0) {
return null
}

if (event.status === 'resolved') {
if (isResolvedEvent) {
return event.markets[0] ?? null
}

Expand All @@ -82,10 +82,12 @@ function resolvePrimaryMarket(event: Event): Market | null {
?? null
}

function buildDateLabel(event: Event, currentTimestamp: number | null) {
if (event.status === 'resolved' && event.resolved_at) {
const resolvedAt = new Date(event.resolved_at)
return Number.isNaN(resolvedAt.getTime()) ? 'Resolved' : `Resolved ${formatDate(resolvedAt)}`
function buildDateLabel(event: Event, currentTimestamp: number | null, isResolvedEvent: boolean) {
if (isResolvedEvent) {
const resolvedAt = event.resolved_at ? new Date(event.resolved_at) : null
return resolvedAt && !Number.isNaN(resolvedAt.getTime())
? `Resolved ${formatDate(resolvedAt)}`
: 'Resolved'
}

if (event.end_date) {
Expand Down Expand Up @@ -127,7 +129,7 @@ function buildDateLabel(event: Event, currentTimestamp: number | null) {
return 'Ends soon'
}

return event.status === 'resolved' ? 'Resolved' : 'Active'
return 'Active'
}

function getEventRecentVolume(event: Event) {
Expand Down Expand Up @@ -280,7 +282,6 @@ function usePredictionResultsFilters({
key: routeScopeKey,
value: initialStatus,
})
const [searchParamsString, setSearchParamsString] = useState('')
const searchDebounceTimeoutRef = useRef<number | null>(null)

const currentTimestamp = useSyncExternalStore(
Expand Down Expand Up @@ -309,13 +310,11 @@ function usePredictionResultsFilters({
isBookmarked,
isDrawerOpen,
searchDebounceTimeoutRef,
searchParamsString,
searchValue,
selectedSort,
selectedStatus,
setIsBookmarkedState,
setIsDrawerOpenState,
setSearchParamsString,
setSearchValueState,
setSelectedSortState,
setSelectedStatusState,
Expand Down Expand Up @@ -417,16 +416,14 @@ function useResolvedResultDisplay({
isResolvedEvent,
normalizeOutcomeLabel,
resolvedLabel,
showResolvedOutcomeLayout,
}: {
event: Event
isResolvedEvent: boolean
normalizeOutcomeLabel: (label: string) => string | null
resolvedLabel: string
showResolvedOutcomeLayout: boolean
}) {
return useMemo(() => {
if (!showResolvedOutcomeLayout || !isResolvedEvent) {
if (!isResolvedEvent) {
return {
label: null,
outcomeIndex: null,
Expand All @@ -438,7 +435,7 @@ function useResolvedResultDisplay({
label: resolvedDisplay.label ? (normalizeOutcomeLabel(resolvedDisplay.label) || resolvedDisplay.label) : resolvedLabel,
outcomeIndex: resolvedDisplay.outcomeIndex,
}
}, [event, isResolvedEvent, normalizeOutcomeLabel, resolvedLabel, showResolvedOutcomeLayout])
}, [event, isResolvedEvent, normalizeOutcomeLabel, resolvedLabel])
}

export default function PredictionResultsClient({
Expand Down Expand Up @@ -466,13 +463,11 @@ export default function PredictionResultsClient({
isBookmarked,
isDrawerOpen,
searchDebounceTimeoutRef,
searchParamsString,
searchValue,
selectedSort,
selectedStatus,
setIsBookmarkedState,
setIsDrawerOpenState,
setSearchParamsString,
setSearchValueState,
setSelectedSortState,
setSelectedStatusState,
Expand Down Expand Up @@ -549,16 +544,10 @@ export default function PredictionResultsClient({
selectedStatus,
})

const handleSearchParamsChange = useCallback(({
searchParamsString: nextSearchParamsString,
sort,
status,
}: {
searchParamsString: string
const handleSearchParamsChange = useCallback(({ sort, status }: {
sort: PredictionResultsSortOption
status: PredictionResultsStatusOption
}) => {
setSearchParamsString(current => current === nextSearchParamsString ? current : nextSearchParamsString)
setSelectedSortState((current) => {
const currentValue = current.key === routeScopeKey ? current.value : initialSort
return currentValue === sort ? current : { key: routeScopeKey, value: sort }
Expand All @@ -571,40 +560,35 @@ export default function PredictionResultsClient({
initialSort,
initialStatus,
routeScopeKey,
setSearchParamsString,
setSelectedSortState,
setSelectedStatusState,
])

const isEmptyState = !isPending && !isFetching && visibleEvents.length === 0
const showInitialSkeleton = visibleEvents.length === 0 && (isPending || isFetching)

function replaceRoute({
nextSearchValue = null,
function replaceSearchRoute({
nextSearchValue,
nextSort = selectedSort,
nextStatus = selectedStatus,
immediate = true,
}: {
nextSearchValue?: string | null
nextSearchValue: string
nextSort?: PredictionResultsSortOption
nextStatus?: PredictionResultsStatusOption
immediate?: boolean
}) {
const nextPath = nextSearchValue == null
? pathname
: buildPredictionResultsPath(nextSearchValue)
const nextPath = buildPredictionResultsPath(nextSearchValue)

if (!nextPath) {
return
}

const nextParams = buildPredictionResultsUrlSearchParams(searchParamsString, {
const nextParams = buildPredictionResultsUrlSearchParams(window.location.search, {
sort: nextSort,
status: nextStatus,
})
const nextQuery = nextParams.toString()
const nextUrl = nextQuery ? `${nextPath}?${nextQuery}` : nextPath
const currentUrl = searchParamsString ? `${pathname}?${searchParamsString}` : pathname
const currentUrl = `${pathname}${window.location.search}`

if (nextUrl === currentUrl) {
return
Expand All @@ -616,11 +600,6 @@ export default function PredictionResultsClient({
})
}

if (immediate) {
runReplace()
return
}

if (searchDebounceTimeoutRef.current) {
window.clearTimeout(searchDebounceTimeoutRef.current)
}
Expand All @@ -630,6 +609,30 @@ export default function PredictionResultsClient({
}, 300)
}

function replaceFilterSearchParams({
nextSort = selectedSort,
nextStatus = selectedStatus,
}: {
nextSort?: PredictionResultsSortOption
nextStatus?: PredictionResultsStatusOption
}) {
const nextParams = buildPredictionResultsUrlSearchParams(window.location.search, {
sort: nextSort,
status: nextStatus,
})
const nextQuery = nextParams.toString()
const currentUrl = `${window.location.pathname}${window.location.search}${window.location.hash}`
const nextUrl = `${window.location.pathname}${nextQuery ? `?${nextQuery}` : ''}${window.location.hash}`

if (nextUrl === currentUrl) {
return
}

startTransition(() => {
window.history.replaceState(null, '', nextUrl)
})
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
}

function handleRetryLoadMore() {
setCanRetryLoadMoreState({ key: infiniteScrollScopeKey, value: true })
setInfiniteScrollErrorState({ key: infiniteScrollScopeKey, value: null })
Expand All @@ -641,25 +644,27 @@ export default function PredictionResultsClient({

function handleSearchValueChange(nextValue: string) {
setSearchValueState({ key: searchScopeKey, value: nextValue })
replaceRoute({
replaceSearchRoute({
nextSearchValue: nextValue,
nextSort: selectedSort,
nextStatus: selectedStatus,
immediate: false,
})
}

function handleClearFilters() {
if (searchDebounceTimeoutRef.current) {
window.clearTimeout(searchDebounceTimeoutRef.current)
searchDebounceTimeoutRef.current = null
}

setIsBookmarkedState({ key: routeScopeKey, value: false })
setIsDrawerOpenState({ key: routeScopeKey, value: false })
setSearchValueState({ key: searchScopeKey, value: initialInputValue })
setSelectedSortState({ key: routeScopeKey, value: DEFAULT_PREDICTION_RESULTS_SORT })
setSelectedStatusState({ key: routeScopeKey, value: DEFAULT_PREDICTION_RESULTS_STATUS })
replaceRoute({
nextSearchValue: initialInputValue,
replaceFilterSearchParams({
nextSort: DEFAULT_PREDICTION_RESULTS_SORT,
nextStatus: DEFAULT_PREDICTION_RESULTS_STATUS,
immediate: true,
})
}

Expand All @@ -686,11 +691,11 @@ export default function PredictionResultsClient({
onSearchValueChange={handleSearchValueChange}
onSortChange={((value) => {
setSelectedSortState({ key: routeScopeKey, value })
replaceRoute({ nextSort: value, immediate: true })
replaceFilterSearchParams({ nextSort: value })
})}
onStatusChange={((value) => {
setSelectedStatusState({ key: routeScopeKey, value })
replaceRoute({ nextStatus: value, immediate: true })
replaceFilterSearchParams({ nextStatus: value })
})}
/>
)
Expand Down Expand Up @@ -811,7 +816,6 @@ export default function PredictionResultsClient({
key={event.id}
event={event}
currentTimestamp={currentTimestamp}
showResolvedOutcomeLayout={selectedStatus === 'resolved'}
/>
))}
</div>
Expand Down Expand Up @@ -875,34 +879,31 @@ export default function PredictionResultsClient({
function PredictionResultRow({
currentTimestamp,
event,
showResolvedOutcomeLayout = false,
}: {
currentTimestamp: number | null
event: Event
showResolvedOutcomeLayout?: boolean
}) {
const t = useExtracted()
const locale = useLocale()
const normalizeOutcomeLabel = useOutcomeLabel()
const { data: commentMetrics } = useCommentMetrics(event.slug)
const primaryMarket = resolvePrimaryMarket(event)
const isResolvedEvent = isEventResolvedLike(event)
const primaryMarket = resolvePrimaryMarket(event, isResolvedEvent)
const primaryProbability = primaryMarket?.probability ?? 0
const supportingTags = event.tags.slice(0, 2)
const isMultiMarket = Math.max(event.total_markets_count ?? 0, event.markets.length) > 1
const isResolvedEvent = isEventResolvedLike(event)
const recentVolume = getEventRecentVolume(event)
const commentsCount = commentMetrics?.comments_count ?? null
const eventPath = resolveEventPagePath(event)
const resolvedLabel = t('Resolved')
const selectedMarketLabel = primaryMarket?.short_title?.trim()
|| primaryMarket?.title?.trim()
|| (event.status === 'resolved' ? resolvedLabel : t('Market'))
|| (isResolvedEvent ? resolvedLabel : t('Market'))
const resolvedResultDisplay = useResolvedResultDisplay({
event,
isResolvedEvent,
normalizeOutcomeLabel,
resolvedLabel,
showResolvedOutcomeLayout,
})
const resolvedBadgeOutcome = resolvedResultDisplay.outcomeIndex === OUTCOME_INDEX.NO
? 'no'
Expand Down Expand Up @@ -1002,13 +1003,13 @@ function PredictionResultRow({
</a>
<span className="flex items-center gap-1 whitespace-nowrap">
<Clock3Icon className="size-3.5 text-muted-foreground" />
<span>{buildDateLabel(event, currentTimestamp)}</span>
<span>{buildDateLabel(event, currentTimestamp, isResolvedEvent)}</span>
</span>
</div>
</div>

<div className="flex max-w-[42%] min-w-[112px] shrink-0 items-center gap-3 self-center">
{showResolvedOutcomeLayout && isResolvedEvent
{isResolvedEvent
? (
<div className="flex min-w-0 flex-1 flex-col items-end justify-center text-right">
<div className="flex max-w-full items-center gap-2">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {

function useSyncFiltersFromSearchParams(
onChange: (nextState: {
searchParamsString: string
sort: PredictionResultsSortOption
status: PredictionResultsStatusOption
}) => void,
Expand All @@ -28,7 +27,6 @@ export default function PredictionResultsSearchParamsSync({
onChange,
}: {
onChange: (nextState: {
searchParamsString: string
sort: PredictionResultsSortOption
status: PredictionResultsStatusOption
}) => void
Expand Down
Loading