Skip to content
Open
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
125 changes: 84 additions & 41 deletions src/flow/Flow.tsx
Original file line number Diff line number Diff line change
@@ -1,76 +1,119 @@
import React from 'react'
import { useState, FC } from 'react'

export type GenericStepProps<S> = {
next?: () => void
previous?: () => void
updateState: (s: Partial<S>) => void
skip?: () => void
hidden?: boolean
} & S

type Direction = 'forwards' | 'backwards'

export function Flow<S>(
props: { steps: Array<IStep<GenericStepProps<S>>> } & {
updateState: (state: Partial<S>) => void
next?: () => void
previous?: () => void
name: string
hidden?: boolean
} & S,
) {
const parentPrevious = props.previous
const parentNext = props.next

let nextPreviousCalledThisRender = false
// let nextPreviousCalledThisRender = false
const [currentStepIndex, setCurrentStepIndex] = useState(0)
const [direction, setDirection] = useState<Direction>('forwards')

const isNestedFlow = parentPrevious || parentNext
const canProgress = currentStepIndex < props.steps.length - 1
const canRegress = currentStepIndex > 0

const next = canProgress
? () => {
nextPreviousCalledThisRender = true
setDirection('forwards')
setCurrentStepIndex((currentStep) => currentStep + 1)
}
: isNestedFlow
? parentNext
: undefined

const previous = canRegress
? () => {
nextPreviousCalledThisRender = true
setDirection('backwards')
setCurrentStepIndex((currentStep) => currentStep - 1)
}
: isNestedFlow
? parentPrevious
: undefined

const skip = direction === 'forwards' ? next : previous

const currentStep = props.steps[currentStepIndex]

if (
nextPreviousCalledThisRender &&
isSkippable(currentStep) &&
skip &&
currentStep.canSkip(props)
// calculate next step
let potentialNextStepIndex = currentStepIndex + 1
let potentialNextStep = props.steps[potentialNextStepIndex]

while (potentialNextStep && isSkippable(potentialNextStep) && potentialNextStep.canSkip(props)) {
potentialNextStepIndex = potentialNextStepIndex + 1
potentialNextStep = props.steps[potentialNextStepIndex]
}

// calculate previous step
let potentialPreviousStepIndex = currentStepIndex - 1
let potentialPreviousStep = props.steps[potentialPreviousStepIndex]

while (
potentialPreviousStep &&
isSkippable(potentialPreviousStep) &&
potentialPreviousStep.canSkip(props)
) {
console.log(`skipping step with index ${currentStepIndex} in flow ${props.name}`)
skip()
potentialPreviousStepIndex = potentialPreviousStepIndex - 1
potentialPreviousStep = props.steps[potentialPreviousStepIndex]
}

const isNestedFlow = parentPrevious || parentNext

const getNext = (stepIndex: number) => {
const canProgress = stepIndex === currentStepIndex && currentStepIndex < props.steps.length - 1
const next = canProgress
? () => setCurrentStepIndex(potentialNextStepIndex)
: isNestedFlow
? parentNext
: undefined

return next
}

return <currentStep.Component {...props} next={next} previous={previous} skip={skip} />
const getPrevious = (stepIndex: number) => {
const canRegress = stepIndex === currentStepIndex && currentStepIndex > 0

const previous = canRegress
? () => setCurrentStepIndex(potentialPreviousStepIndex)
: isNestedFlow
? parentPrevious
: undefined

return previous
}

return (
<>
{props.steps.map((Step, stepIndex) => {
const hideStep = stepIndex !== currentStepIndex

const next = getNext(stepIndex)
const previous = getPrevious(stepIndex)

return (
<Hidden
key={`${Step.Component.displayName} - ${stepIndex}`}
hiddenIf={() => props.hidden || hideStep}
>
<Step.Component
{...props}
next={next}
previous={previous}
hidden={props.hidden || hideStep}
/>
</Hidden>
)
})}
</>
)
}

export const Hidden: React.FC<{
hiddenIf: () => boolean
children?: React.ReactNode
}> = ({ hiddenIf, children }) => (
<div
style={{ visibility: hiddenIf() ? 'hidden' : 'visible', maxHeight: hiddenIf() ? 0 : 'initial' }}
>
{children}
</div>
)

function isSkippable<T>(step: IStep<T> | ISkippableStep<T>): step is ISkippableStep<T> {
return (step as ISkippableStep<T>).canSkip !== undefined
}

export interface IStep<StepSpecificProps> {
Component: FC<GenericStepProps<StepSpecificProps>>
displayName: string
}

export interface ISkippableStep<StepSpecificProps> extends IStep<StepSpecificProps> {
Expand Down
2 changes: 1 addition & 1 deletion src/flow/OpenBorrowVault.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function OpenBorrowVault() {
setViewState((oldState) => {
return calculateViewModal({ ...oldState, ethPrice: Math.floor(Math.random() * 10000) })
})
}, 1000)
}, 10000)
return () => clearInterval(i)
})

Expand Down
18 changes: 4 additions & 14 deletions src/flow/allowance/Allowance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,21 @@ import {
ConfigureAllowanceAmountProps,
} from './steps/ConfigureAllowanceAmount'
import { Done, DoneProps } from './steps/Done'
import { useEffect, useState } from 'react'

export type AllowanceProps = ConfigureAllowanceAmountProps & DoneProps

export const Allowance: ISkippableStep<AllowanceProps> = {
Component: (props: GenericStepProps<AllowanceProps>) => {
const [viewState, setViewState] = useState<AllowanceProps>(props)

useEffect(() => {
if (props.configuredAllowance) {
props.skip!()
}
}, [])

return (
<Flow<AllowanceProps>
{...viewState}
{...props}
name="allowance"
steps={[ConfigureAllowanceAmount, Done]}
updateState={(newState) => {
setViewState((oldState) => ({ ...oldState, ...newState }))
props.updateState(newState)
}}
updateState={(newState) => props.updateState(newState)}
hidden={props.hidden}
/>
)
},
canSkip: (props) => !!props.configuredAllowance,
displayName: 'Allowance',
}
11 changes: 9 additions & 2 deletions src/flow/allowance/steps/ConfigureAllowanceAmount.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GenericStepProps, IStep } from '../../Flow'
import { GenericStepProps, ISkippableStep } from '../../Flow'
import { SyntheticEvent, useState } from 'react'
import { useLoadingDots } from '../../hooks/useLoadingDots'

Expand All @@ -9,7 +9,7 @@ export type ConfigureAllowanceAmountProps = {

type AllowanceRequestState = 'not sent' | 'in progress' | 'done'

export const ConfigureAllowanceAmount: IStep<ConfigureAllowanceAmountProps> = {
export const ConfigureAllowanceAmount: ISkippableStep<ConfigureAllowanceAmountProps> = {
Component: (props: GenericStepProps<ConfigureAllowanceAmountProps>) => {
const [allowanceRequestState, setAllowanceRequestState] =
useState<AllowanceRequestState>('not sent')
Expand Down Expand Up @@ -92,4 +92,11 @@ export const ConfigureAllowanceAmount: IStep<ConfigureAllowanceAmountProps> = {
</>
)
},
canSkip: (props: ConfigureAllowanceAmountProps) =>
!!(
props.configuredAllowance &&
props.depositAmount &&
props.configuredAllowance >= props.depositAmount
),
displayName: 'Allowance|ConfigureAllowanceAmount',
}
1 change: 1 addition & 0 deletions src/flow/allowance/steps/Done.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export const Done: IStep<DoneProps> = {
</>
)
},
displayName: 'Allowance|Done',
}
17 changes: 6 additions & 11 deletions src/flow/create-proxy/CreateProxy.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,24 @@
import { Flow, GenericStepProps, ISkippableStep } from '../Flow'
import { FC, useState } from 'react'
import { Flow, ISkippableStep } from '../Flow'
import { Explanation, ExplanationProps } from './steps/Explanation'
import { Creation, CreationProps } from './steps/Creation'
import { Done, DoneProps } from './steps/Done'

export type CreateProxyProps = ExplanationProps & CreationProps & DoneProps

export const CreateProxy: ISkippableStep<CreateProxyProps> = {
Component: (props) => {
const [viewState, setViewState] = useState<CreateProxyProps>(props)

Component: function CreateProxy(props) {
return (
<Flow<CreateProxyProps>
{...viewState}
{...props}
name="proxy"
steps={[Explanation, Creation, Done]}
updateState={(newState) => {
setViewState((oldState) => ({ ...oldState, ...newState }))
props.updateState(newState)
}}
updateState={(newState) => props.updateState(newState)}
hidden={props.hidden}
/>
)
},
canSkip: (props: CreateProxyProps) => {
console.log('here')
return !!props.proxyAddress
},
displayName: 'CreateProxy',
}
15 changes: 8 additions & 7 deletions src/flow/create-proxy/steps/Creation.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { GenericStepProps, IStep } from '../../Flow'
import { useEffect } from 'react'
import { useLoadingDots } from '../../hooks/useLoadingDots'
import { clear } from '@testing-library/user-event/dist/clear'

export type CreationProps = {
walletAddress: string
proxyAddress?: string
}

export const Creation: IStep<CreationProps> = {
Component: (props: GenericStepProps<CreationProps>) => {
Component: function Creation(props: GenericStepProps<CreationProps>) {
const dots = useLoadingDots()

useEffect(() => {
const i = setTimeout(() => {
console.log('updating step')
props.updateState({ proxyAddress: '0xProxyAddress' })
console.log('calling next')
props.next!()
if (!props.hidden) {
console.log('updating step')
props.updateState({ proxyAddress: '0xProxyAddress' })
props.next!()
}
}, 3000)
return () => clearTimeout(i)
}, [])
}, [props.hidden])

return (
<>
Expand All @@ -30,4 +30,5 @@ export const Creation: IStep<CreationProps> = {
</>
)
},
displayName: 'Creation',
}
1 change: 1 addition & 0 deletions src/flow/create-proxy/steps/Done.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ export const Done: IStep<DoneProps> = {
</>
)
},
displayName: 'Done',
}
1 change: 1 addition & 0 deletions src/flow/create-proxy/steps/Explanation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ export const Explanation: IStep<ExplanationProps> = {
</>
)
},
displayName: 'Explanation',
}
1 change: 1 addition & 0 deletions src/flow/steps/Complete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ export const Complete: IStep<CompleteProps> = {
</>
)
},
displayName: 'Complete',
}
1 change: 1 addition & 0 deletions src/flow/steps/Confirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ export const Confirmation: IStep<ConfirmationProps> = {
</>
)
},
displayName: 'Confirmation',
}
1 change: 1 addition & 0 deletions src/flow/steps/SimulateStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ export const SimulateStep: IStep<SimulateStepProps> = {
</>
)
},
displayName: 'SimulateStep',
}