fletcherist / yandex-dialogs-sdk

☂️Build your skill for Yandex.Alice with ease. (nodejs/typescript)
MIT License
122 stars 16 forks source link

Условный вход в сцену #61

Closed alexander-karpov closed 6 years ago

alexander-karpov commented 6 years ago

Было бы удобно иметь возможность перейти в сцену из другой команды:

const inBar = new Scene('in-the-bar');

alice.command('Алиса, пойдём в бар!', ctx => {
    if (ctx.profile.age < 18) {
        ctx.reply('Давайте лучше в кино.');
    } else {
        ctx.reply('Пойдёмте.');
        inBar.enter();
    }
});
popstas commented 6 years ago

Да, согласен, пока этого нет, я программно меняю сцену так:

ctx.session.setData('currentScene', null);
fletcherist commented 6 years ago

@alexander-karpov А как ты думаешь, как было бы удобнее менять сцену? 1 - как ты предложил, но тогда если у тебя сцены в разных файлах, то тебе надо одну в другую импортировать, чтобы заходить. 2 - а можно по названию при инициализации

alice.command('Алиса, пойдём в бар!', ctx => {
    if (ctx.profile.age < 18) {
        ctx.reply('Давайте лучше в кино.');
    } else {
        ctx.reply('Пойдёмте.');
        ctx.changeScene('in-the-bar');
        ctx.switchScene('in-the-bar'); // maybe better naming
    }
});
popstas commented 6 years ago

Еще хорошо бы сразу подумать о том, что в будущем могут появиться множественные активные сцены (когда сцена вызвана из сцены).

Мне кажется, что переключение сцены надо делать через события.

Я же правильно понимаю, что переключение сцен хорошо ложится на паттерн State Machine? Я сейчас прочитал это - https://ru.hexlet.io/courses/js-abp/lessons/state_machine_pattern/theory_unit, после чего подумал про события.

В моем представлении сцены похожи на экраны мобильного приложения. Например, заказ пиццы имел бы экраны:

То же самое я бы сделал в навыке, 3 сцены (главный экран - когда без сцены).

Сцена внутри сцены тут может быть такая: модальное окно с подтверждением отправки заказа в приложении и ctx.confirm() в навыке. То есть вложенные сцены в моем представлении похожи на модальные попапы на сайте, они дают выбрать из одного или нескольких вариантов и пока это не будет сделано, не дают сделать ничего другого. При таком раскладе (если больше 2 сцен не будет) может это и не вложенная сцена совсем, а просто отдельным полем завести ctx.session.data.currentModalScene. Я могу представить, когда нужна вложенность больше:

Вопрос в том, насколько глубоко возможно погружать пользователя в голосовом интерфейсе.

А вам какие сценарии приходят в голову, где нужны вложенные сцены?

alexander-karpov commented 6 years ago

@popstas Наверняка в сложных навыках понадобится вложенность и возможность переходить на предыдущий экран. Мое предложение - начать пока просто с возможности произвольно переключать сцены (хотя возможно потом это будет сложнее расширить).

@fletcherist Мне кажется лучше все же передавать ссылку на сцену - надежнее (рефакторить проще, труднее случайно сломать). Переключение можно сделать например вызовами

const inBar = new Scene();

alice.command('Алиса, пойдём в бар', ctx => {
    ctx.enterScene(inBar); // change, switch
    return 'Пойдёмте.';
});

inBar.command('Алиса, пойдём домой', ctx => {
    ctx.enterScene(alice); // или ctx.leaveScene()
    return 'Возвращаюсь.';
});

Кстати, как насчет отделить alice как сервер и alice как сцену?

fletcherist commented 6 years ago

Взял на себя

fletcherist commented 6 years ago

@popstas стейт-машины — это прикольно, но мне кажется, пускай разработчики сами их пишут. Мы дадим им апи перехода из сцены в сцену. А дальше пускай они сами разруливают, куда и из какого состояния можно переходить

fletcherist commented 6 years ago

Сделано в PR https://github.com/fletcherist/yandex-dialogs-sdk/pull/65

Теперь можно писать так:

const inBar = new Scene('in-bar');
alice.command('Алиса, пойдём в бар', ctx => {
    ctx.enterScene(inBar); // change, switch
    return 'Пойдёмте.';
});

inBar.command('Алиса, пойдём домой', ctx => {
    ctx.leaveScene()
    return 'Возвращаюсь.';
});

С таким же успехом можно прыгать из одной сцены в другую. ctx.leaveScene() всегда возвращает на главный диалог