mtcute/packages/dispatcher/src/wizard.ts

91 lines
2.3 KiB
TypeScript
Raw Normal View History

2023-10-11 08:24:11 +03:00
import { MaybeAsync } from '@mtcute/client'
2023-10-11 08:24:11 +03:00
import { MessageContext } from './context'
2021-06-14 19:01:02 +03:00
import { Dispatcher } from './dispatcher'
import { filters } from './filters'
import { UpdateState } from './state'
2021-06-14 19:01:02 +03:00
/**
* Action for the wizard scene.
*
* `Next`: Continue to the next registered step
* (or exit, if this is the last step)
*
* `Stay`: Stay on the same step
*
* `Exit`: Exit from the wizard scene
*
* You can also return a `number` to jump to that step
*/
export enum WizardSceneAction {
Next = 'next',
Stay = 'stay',
Exit = 'exit',
2021-06-14 19:01:02 +03:00
}
interface WizardInternalState {
$step: number
}
/**
* Wizard is a special type of Dispatcher
* that can be used to simplify implementing
* step-by-step scenes.
*/
2023-09-24 01:32:22 +03:00
export class WizardScene<State, SceneName extends string = string> extends Dispatcher<
State & WizardInternalState,
SceneName
> {
2021-06-14 19:01:02 +03:00
private _steps = 0
private _defaultState: State & WizardInternalState = {} as State & WizardInternalState
setDefaultState(defaultState: State): void {
this._defaultState = defaultState as State & WizardInternalState
}
2021-06-14 19:01:02 +03:00
/**
* Get the total number of registered steps
*/
get totalSteps(): number {
return this._steps
}
/**
* Add a step to the wizard
*/
addStep(
2023-10-11 08:24:11 +03:00
handler: (msg: MessageContext, state: UpdateState<State, SceneName>) => MaybeAsync<WizardSceneAction | number>,
): void {
2021-06-14 19:01:02 +03:00
const step = this._steps++
2023-09-24 01:32:22 +03:00
const filter = filters.state<WizardInternalState>((it) => it.$step === step)
2021-06-14 19:01:02 +03:00
2023-10-11 08:24:11 +03:00
this.onNewMessage(step === 0 ? filters.or(filters.stateEmpty, filter) : filter, async (msg, state) => {
2023-09-24 01:32:22 +03:00
const result = await handler(msg, state)
2021-06-14 19:01:02 +03:00
2023-09-24 01:32:22 +03:00
if (typeof result === 'number') {
await state.merge({ $step: result }, this._defaultState)
2023-09-24 01:32:22 +03:00
return
}
2021-06-14 19:01:02 +03:00
2023-09-24 01:32:22 +03:00
switch (result) {
case 'next': {
const next = step + 1
2023-09-24 01:32:22 +03:00
if (next === this._steps) {
2021-06-14 19:01:02 +03:00
await state.exit()
2023-09-24 01:32:22 +03:00
} else {
await state.merge({ $step: next }, this._defaultState)
}
break
2021-06-14 19:01:02 +03:00
}
2023-09-24 01:32:22 +03:00
case 'exit':
await state.exit()
break
}
})
2021-06-14 19:01:02 +03:00
}
}