mtcute/packages/dispatcher/src/wizard.ts

101 lines
2.6 KiB
TypeScript
Raw Normal View History

2021-08-05 20:38:24 +03:00
import { MaybeAsync, Message } from '@mtcute/client'
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.
*/
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(
handler: (
msg: Message,
state: UpdateState<State, SceneName>
) => MaybeAsync<WizardSceneAction | number>,
): void {
2021-06-14 19:01:02 +03:00
const step = this._steps++
const filter = filters.state<WizardInternalState>(
(it) => it.$step === step,
)
2021-06-14 19:01:02 +03:00
this.onNewMessage(
step === 0 ? filters.or(filters.stateEmpty, filter) : filter,
2021-06-14 19:01:02 +03:00
async (msg: Message, state) => {
const result = await handler(msg, state)
if (typeof result === 'number') {
await state.merge({ $step: result }, this._defaultState)
2021-06-14 19:01:02 +03:00
return
}
switch (result) {
case 'next': {
const next = step + 1
2021-06-14 19:01:02 +03:00
if (next === this._steps) {
await state.exit()
} else {
await state.merge(
{ $step: next },
this._defaultState,
)
2021-06-14 19:01:02 +03:00
}
break
}
case 'exit':
await state.exit()
break
}
},
2021-06-14 19:01:02 +03:00
)
}
}