2023-10-11 08:24:11 +03:00
|
|
|
import { MaybeAsync } from '@mtcute/client'
|
2022-06-30 16:32:56 +03:00
|
|
|
|
2023-10-16 19:23:53 +03:00
|
|
|
import { MessageContext } from './context/message.js'
|
|
|
|
import { Dispatcher } from './dispatcher.js'
|
|
|
|
import { filters } from './filters/index.js'
|
|
|
|
import { UpdateState } from './state/update-state.js'
|
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',
|
2022-06-30 16:32:56 +03:00
|
|
|
Exit = 'exit',
|
2021-06-14 19:01:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
interface WizardInternalState {
|
2023-10-23 11:43:24 +03:00
|
|
|
$step?: number
|
2021-06-14 19:01:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
|
2023-06-05 03:30:48 +03:00
|
|
|
private _defaultState: State & WizardInternalState = {} as State & WizardInternalState
|
2021-06-24 01:12:20 +03:00
|
|
|
|
2022-06-30 16:32:56 +03:00
|
|
|
setDefaultState(defaultState: State): void {
|
2021-06-24 01:12:20 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-10-23 11:43:24 +03:00
|
|
|
/**
|
|
|
|
* Go to the Nth step
|
|
|
|
*/
|
|
|
|
async goToStep(state: UpdateState<WizardInternalState, SceneName>, step: number) {
|
|
|
|
if (step >= this._steps) {
|
|
|
|
await state.exit()
|
|
|
|
} else {
|
|
|
|
await state.merge({ $step: step }, this._defaultState)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Skip N steps
|
|
|
|
*/
|
|
|
|
async skip(state: UpdateState<WizardInternalState, SceneName>, count = 1) {
|
|
|
|
const { $step } = (await state.get()) || {}
|
|
|
|
if ($step === undefined) throw new Error('Wizard state is not initialized')
|
|
|
|
|
|
|
|
return this.goToStep(state, $step + count)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Filter that will only pass if the current step is `step`
|
|
|
|
*/
|
|
|
|
static onNthStep(step: number) {
|
|
|
|
const filter = filters.state<WizardInternalState>((it) => it.$step === step)
|
|
|
|
|
|
|
|
if (step === 0) return filters.or(filters.stateEmpty, filter)
|
|
|
|
|
|
|
|
return filter
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Filter that will only pass if the current step is the one after last one added
|
|
|
|
*/
|
|
|
|
onCurrentStep() {
|
|
|
|
return WizardScene.onNthStep(this._steps)
|
|
|
|
}
|
|
|
|
|
2021-06-14 19:01:02 +03:00
|
|
|
/**
|
|
|
|
* Add a step to the wizard
|
|
|
|
*/
|
2022-06-30 16:32:56 +03:00
|
|
|
addStep(
|
2023-10-23 11:43:24 +03:00
|
|
|
handler: (
|
|
|
|
msg: MessageContext,
|
|
|
|
state: UpdateState<State & WizardInternalState, SceneName>,
|
|
|
|
) => MaybeAsync<WizardSceneAction | number>,
|
2022-06-30 16:32:56 +03:00
|
|
|
): void {
|
2021-06-14 19:01:02 +03:00
|
|
|
const step = this._steps++
|
|
|
|
|
2023-10-23 11:43:24 +03:00
|
|
|
this.onNewMessage(WizardScene.onNthStep(step), 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') {
|
2023-10-23 11:43:24 +03:00
|
|
|
await this.goToStep(state, result)
|
2023-06-05 03:30:48 +03:00
|
|
|
|
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': {
|
2023-10-23 11:43:24 +03:00
|
|
|
await this.goToStep(state, step + 1)
|
2023-09-24 01:32:22 +03:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|