Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion ui/ts/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ export function App() {

const marketRouteContentProps: MarketRouteContentProps = {
accountState,
activeUniverseId,
activeView: activeZoltarView,
hasLoadedZoltarQuestions,
loadingZoltarForkAccess,
Expand All @@ -374,7 +375,7 @@ export function App() {
onApproveZoltarForkRep: amount => void approveZoltarForkRep(amount),
onCreateMarket: () => void createMarket(),
onForkZoltar: () => void forkZoltar(),
onLoadZoltarQuestions: () => void loadZoltarQuestions(),
onLoadZoltarQuestions: loadZoltarQuestions,
onMigrateInternalRep: () => void migrateInternalRep(),
onMarketFormChange: update => setMarketForm(current => ({ ...current, ...update })),
onPrepareRepForMigration: () => void prepareRepForMigration(),
Expand Down
10 changes: 8 additions & 2 deletions ui/ts/components/MarketQuestionsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type MarketQuestionsSectionProps = {
hasLoadedZoltarQuestions: boolean
loadingZoltarQuestionCount: boolean
loadingZoltarQuestions: boolean
onLoadZoltarQuestions: () => void
onLoadZoltarQuestions: () => Promise<void>
onOpenForkTab: () => void
onUseQuestionForFork: (questionId: string) => void
onUseQuestionForPool: (questionId: string) => void
Expand All @@ -26,7 +26,13 @@ export function MarketQuestionsSection({ hasForked, hasLoadedZoltarQuestions, lo
density='compact'
title='Questions'
actions={
<button className='secondary' onClick={onLoadZoltarQuestions} disabled={loadingZoltarQuestions || noQuestionsAvailable}>
<button
className='secondary'
onClick={() => {
void onLoadZoltarQuestions()
}}
disabled={loadingZoltarQuestions || noQuestionsAvailable}
>
{loadingZoltarQuestions ? <LoadingText>Loading Questions...</LoadingText> : noQuestionsAvailable ? 'No Questions' : hasLoadedZoltarQuestions ? 'Refresh Questions' : 'Fetch Questions'}
</button>
}
Expand Down
12 changes: 7 additions & 5 deletions ui/ts/components/MarketSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { MarketSectionProps } from '../types/components.js'

export function MarketSection({
accountState,
activeUniverseId,
activeView,
hasLoadedZoltarQuestions,
loadingZoltarForkAccess,
Expand Down Expand Up @@ -77,16 +78,17 @@ export function MarketSection({

useEffect(() => {
if (view !== 'questions') return
if (loadingZoltarQuestionCount) return
if (zoltarQuestionCount === undefined) return
if (zoltarQuestionCount === 0n) return
if (loadingZoltarQuestions) return
if (hasLoadedZoltarQuestions) return
if (zoltarQuestionCount === 0n) return
const currentUniverseId = zoltarUniverse?.universeId
if (currentUniverseId !== undefined && lastAutoLoadedQuestionsUniverseId.current === currentUniverseId) return
lastAutoLoadedQuestionsUniverseId.current = currentUniverseId
if (lastAutoLoadedQuestionsUniverseId.current === activeUniverseId) return
lastAutoLoadedQuestionsUniverseId.current = activeUniverseId
void Promise.resolve(onLoadZoltarQuestions()).catch(() => {
lastAutoLoadedQuestionsUniverseId.current = undefined
})
}, [hasLoadedZoltarQuestions, loadingZoltarQuestions, onLoadZoltarQuestions, view, zoltarQuestionCount, zoltarUniverse?.universeId])
}, [activeUniverseId, hasLoadedZoltarQuestions, loadingZoltarQuestionCount, loadingZoltarQuestions, onLoadZoltarQuestions, view, zoltarQuestionCount])

return (
<div className='route-view-flow'>
Expand Down
14 changes: 11 additions & 3 deletions ui/ts/hooks/useZoltarUniverse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,12 @@ export function useZoltarUniverse({ accountAddress, activeUniverseId, autoLoadIn
})
}

const loadQuestions = async () => {
const loadQuestions = async (): Promise<void> => {
if (!isMounted.current) return
const isCountCurrent = nextQuestionCountLoad()
const isQuestionsCurrent = nextQuestionsLoad()
const readClient = createConnectedReadClient()
let loadError: unknown

const countTask = questionCountLoad.run({
isCurrent: isCountCurrent,
Expand All @@ -128,7 +129,9 @@ export function useZoltarUniverse({ accountAddress, activeUniverseId, autoLoadIn
if (!isMounted.current) return
zoltarQuestionCount.value = questionCount
},
onError: () => undefined,
onError: error => {
loadError = loadError ?? error
},
})

const questionsTask = questionsLoad.run({
Expand All @@ -139,10 +142,15 @@ export function useZoltarUniverse({ accountAddress, activeUniverseId, autoLoadIn
zoltarQuestions.value = questions
hasLoadedZoltarQuestions.value = true
},
onError: () => undefined,
onError: error => {
loadError = loadError ?? error
},
})

await Promise.allSettled([countTask, questionsTask])
if (loadError !== undefined) {
throw loadError
}
}

const createChildUniverse = async (outcomeIndex: bigint) => {
Expand Down
126 changes: 118 additions & 8 deletions ui/ts/tests/marketSection.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ function createBinaryForkQuestion() {
function createMarketSectionProps(overrides: Partial<MarketSectionProps> = {}): MarketSectionProps {
return {
accountState: createAccountState(),
activeUniverseId: 1n,
activeView: 'questions',
hasLoadedZoltarQuestions: false,
loadingZoltarForkAccess: false,
Expand All @@ -78,7 +79,7 @@ function createMarketSectionProps(overrides: Partial<MarketSectionProps> = {}):
onCreateChildUniverseForOutcomeIndex: () => undefined,
onCreateMarket: () => undefined,
onForkZoltar: () => undefined,
onLoadZoltarQuestions: () => undefined,
onLoadZoltarQuestions: async () => undefined,
onMarketFormChange: () => undefined,
onMigrateInternalRep: () => undefined,
onPrepareRepForMigration: () => undefined,
Expand Down Expand Up @@ -146,9 +147,10 @@ describe('MarketSection', () => {
test('auto-loads questions once when opening the questions view without loaded data', async () => {
const calls: string[] = []
const initialProps = createMarketSectionProps({
activeUniverseId: 7n,
hasLoadedZoltarQuestions: false,
loadingZoltarQuestions: false,
onLoadZoltarQuestions: () => {
onLoadZoltarQuestions: async () => {
calls.push('load')
},
zoltarQuestionCount: 3n,
Expand All @@ -163,7 +165,7 @@ describe('MarketSection', () => {
render(
h(MarketSection, {
...initialProps,
onLoadZoltarQuestions: () => {
onLoadZoltarQuestions: async () => {
calls.push('rerender')
},
}),
Expand All @@ -174,14 +176,50 @@ describe('MarketSection', () => {
expect(calls).toEqual(['load'])
})

test('does not auto-load questions while the question count is unresolved', async () => {
const calls: string[] = []
const renderedComponent = await renderIntoDocument(
h(
MarketSection,
createMarketSectionProps({
onLoadZoltarQuestions: async () => {
calls.push('load')
},
zoltarQuestionCount: undefined,
}),
),
)
cleanupRenderedComponent = renderedComponent.cleanup

expect(calls).toEqual([])
})

test('does not auto-load questions when the resolved count is zero', async () => {
const calls: string[] = []
const renderedComponent = await renderIntoDocument(
h(
MarketSection,
createMarketSectionProps({
onLoadZoltarQuestions: async () => {
calls.push('load')
},
zoltarQuestionCount: 0n,
}),
),
)
cleanupRenderedComponent = renderedComponent.cleanup

expect(calls).toEqual([])
})

test('does not auto-load questions when they are already loaded', async () => {
const calls: string[] = []
const renderedComponent = await renderIntoDocument(
h(
MarketSection,
createMarketSectionProps({
hasLoadedZoltarQuestions: true,
onLoadZoltarQuestions: () => {
onLoadZoltarQuestions: async () => {
calls.push('load')
},
zoltarQuestionCount: 3n,
Expand All @@ -193,20 +231,90 @@ describe('MarketSection', () => {
expect(calls).toEqual([])
})

test('auto-loads questions once after the count resolves above zero even when the universe is unresolved', async () => {
const calls: string[] = []
const initialProps = createMarketSectionProps({
activeUniverseId: 12n,
loadingZoltarQuestionCount: true,
onLoadZoltarQuestions: async () => {
calls.push('load')
},
zoltarQuestionCount: undefined,
zoltarUniverse: undefined,
zoltarUniverseState: 'unknown',
})

const renderedComponent = await renderIntoDocument(h(MarketSection, initialProps))
cleanupRenderedComponent = renderedComponent.cleanup
expect(calls).toEqual([])

await act(() => {
render(
h(
MarketSection,
createMarketSectionProps({
...initialProps,
loadingZoltarQuestionCount: false,
zoltarQuestionCount: 3n,
}),
),
renderedComponent.container,
)
})

expect(calls).toEqual(['load'])
})

test('does not auto-load questions again on rerender when the active universe id is unchanged', async () => {
const calls: string[] = []
const initialProps = createMarketSectionProps({
activeUniverseId: 13n,
onLoadZoltarQuestions: async () => {
calls.push('load')
},
zoltarQuestionCount: 3n,
zoltarUniverse: undefined,
zoltarUniverseState: 'unknown',
})

const renderedComponent = await renderIntoDocument(h(MarketSection, initialProps))
cleanupRenderedComponent = renderedComponent.cleanup
expect(calls).toEqual(['load'])

await act(() => {
render(
h(
MarketSection,
createMarketSectionProps({
...initialProps,
onLoadZoltarQuestions: async () => {
calls.push('rerender')
},
}),
),
renderedComponent.container,
)
})

expect(calls).toEqual(['load'])
})

test('retries question auto-load when the previous automatic load fails', async () => {
const calls: string[] = []
const renderedComponent = await renderIntoDocument(
h(
MarketSection,
createMarketSectionProps({
activeUniverseId: 9n,
hasLoadedZoltarQuestions: false,
loadingZoltarQuestions: false,
onLoadZoltarQuestions: () => {
onLoadZoltarQuestions: async () => {
calls.push('load')
return Promise.reject(new Error('temporary failure'))
},
zoltarQuestionCount: 3n,
zoltarUniverse: createZoltarUniverse({ universeId: 9n }),
zoltarUniverse: undefined,
zoltarUniverseState: 'unknown',
}),
),
)
Expand All @@ -219,13 +327,15 @@ describe('MarketSection', () => {
h(
MarketSection,
createMarketSectionProps({
activeUniverseId: 9n,
hasLoadedZoltarQuestions: false,
loadingZoltarQuestions: false,
onLoadZoltarQuestions: () => {
onLoadZoltarQuestions: async () => {
calls.push('retry')
},
zoltarQuestionCount: 3n,
zoltarUniverse: createZoltarUniverse({ universeId: 9n }),
zoltarUniverse: undefined,
zoltarUniverseState: 'unknown',
}),
),
renderedComponent.container,
Expand Down
3 changes: 2 additions & 1 deletion ui/ts/types/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ export type DeploymentRouteContentProps = {

export type MarketRouteContentProps = {
accountState: AccountState
activeUniverseId: bigint
activeView: ZoltarView
onApproveZoltarForkRep: (amount?: bigint) => void
onCreateChildUniverseForOutcomeIndex: (outcomeIndex: bigint) => void
Expand All @@ -289,7 +290,7 @@ export type MarketRouteContentProps = {
zoltarForkActiveAction: 'approve' | 'fork' | undefined
loadingZoltarUniverse: boolean
zoltarUniverseState: LoadableValueState
onLoadZoltarQuestions: () => void
onLoadZoltarQuestions: () => Promise<void>
onMarketFormChange: (update: Partial<MarketFormState>) => void
onUseQuestionForFork: (questionId: string) => void
onUseQuestionForPool: (questionId: string) => void
Expand Down
Loading