npm package: @salutejs/canvas-example
Description: Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).
Author: Salute Frontend Team
Latest version: 1.0.24

# Пример Canvas App (front) + сценарий (back) на next.js

Проект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).

### Первый запуск
- Установить зависимости (`npm ci`)
- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))
- Запустить локальный сервер (`npm run dev`)
- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )
- Создать новый проект с типом `SmartApp -> Canvas App` в SmartMarket Studio
- В параметрах проекта указать
  - Тип проекта -> SmartApp API
  - Внешняя ссылка -> `<https ссылка туннеля>/api/hook`
  - Хостинг фронтенда -> `<https ссылка туннеля>`

### Отладка в браузере
К стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.

![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)

### Отладка в Салют/Sberbox

Для отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.

### Продакшен сборка

```bash
npm run build
npm start
```
## Внутреннее устройство

Шаблон основан на [nextjs](https://nextjs.org/)

### Получение данных

Все данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).

При этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.

### Статический рендеринг

В примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).

В урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`

#### Поверхность

Под поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.

Для каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.

Каждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.

В пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.

#### Персонаж

Персонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.