# Код у застосунку

Майже все в міні-застосунку збирається без коду. Коли готових елементів і блоків не вистачає, свій код можна додати в трьох місцях:

| Де | Коли виконується | Для чого |
|---|---|---|
| Блок «JavaScript» | Коли сценарій дійшов до блоку | Дія за натисканням: скопіювати текст, відкрити посилання, порахувати значення, поговорити з мостом месенджера |
| Код застосунку | Один раз під час запуску, до першого екрана, і далі — за подіями | Лічильники аналітики, свої стилі, зовнішні віджети |
| «HTML-код» — елемент або весь екран | Поки екран відкритий | Своя верстка, калькулятор, віджет стороннього сервісу |

Код виконується на пристрої людини. Тому людина, яка розуміється на браузері, може побачити його й підробити те, що код надсилає на сервер. Не зберігайте в коді секретні ключі й обмежуйте, які змінні та теги код може змінювати, — про це нижче.

## Блок «JavaScript»

Блок із категорії «Дані та логіка». Коли застосунок доходить до блоку, він виконує код і йде далі виходом «Виконано» або «Помилка».

| Налаштування | Що задає |
|---|---|
| «Код» | Тіло асинхронної функції: пишіть з `await` і об'єктом `app`. До 20 000 символів |
| «Час на виконання, секунд» | Від 1 до 60, за замовчуванням 10. Не встиг — сценарій піде виходом «Помилка», а сам код може допрацювати у фоні |
| «Змінна результату» | Що повернув `return`. Об'єкт запишеться як JSON |
| «Змінна помилки» | Текст помилки — для екрана після виходу «Помилка» |
| «Що може змінювати код» | Список дозволеного — див. нижче |

![Налаштування блока «JavaScript»](https://support.watbot.org/_media/uk/02-mini-apps/_images/js-block.webp?v=1791781231)

Підстановки `{{ имя }}` у коді не працюють: значення змінних читайте через `app.variables.get()`.

Змінні, записані через `app.variables.set()`, і змінені теги йдуть на сервер разом із виходом блоку.

### «Що може змінювати код»

Перемикач «Змінювати лише перелічене» ввімкнено в нового блоку. З ним сервер приймає від коду лише:

- змінні зі списку «Змінні»;
- теги зі списків «Теги контакту бота» і «Теги користувача застосунку» — порожній список означає, що ці теги код не змінює взагалі;
- змінні результату й помилки — їх дозволено завжди.

Решту сервер відкине й запише в журнал помилок «Блок відкинув записане кодом». Якщо код пише імена, яких немає у списках, налаштування блоку попередять про це й запропонують «Додати до списків».

Вимикайте обмеження, лише якщо від змінних і тегів нічого не залежить: інакше людина зможе записати собі, наприклад, тег VIP або знижку.

### Об'єкт `app`

| Виклик | Що робить |
|---|---|
| `app.variables.get('name')` | Значення змінної |
| `app.variables.set('name', value)` | Записати змінну — піде на сервер із виходом блоку |
| `app.variables.all()` | Усі змінні об'єктом |
| `app.tags.has('vip')` / `add('vip')` / `remove('vip')` / `all()` | Теги користувача застосунку — є в кожного, навіть без чат-бота |
| `app.contact?.name` | Контакт чат-бота: `id`, `name`, `first_name`, `last_name`, `username`, `phone`, `email`, `address`, `source`, `created_at`, `telegram_id`, `max_id`. `null`, якщо застосунок відкрили не з бота |
| `app.contact.variables.get('name')` / `set('name', value)` | Власні змінні контакту, окремо від змінних застосунку. Записане піде в контакт із виходом блоку |
| `app.contact.tags.has('vip')` / `add` / `remove` / `all()` | Теги контакту бота |
| `app.platform` | Звідки відкрито: `telegram`, `max`, `webchat`, `pwa` або `browser` |
| `app.startParam` | Параметр посилання запуску (`startapp`) |
| `app.user` | `language`, `name` і `username` у месенджері |
| `app.device` | `mode` (`ios` або `md`), `dark`, `standalone`, `width`, `height`, `userAgent` |
| `await app.toast(message)` | Спливне повідомлення |
| `await app.alert(message, title)` | Повідомлення з кнопкою OK — чекає, поки його закриють |
| `await app.confirm(question)` | Запитання «так чи ні» — поверне `true` або `false` |
| `app.openLink('https://…')` | Відкрити посилання: у месенджері — його засобами, інакше в новій вкладці |
| `await app.copy(text)` | Скопіювати в буфер обміну |
| `await app.share({title, text, url})` | Системне меню «Поділитися», без нього — копія посилання |
| `app.vibrate('success')` | Вібрація: `light`, `medium`, `heavy`, `success`, `warning` або `error` |
| `app.localStorage.setItem('key', 'value')` | Сховище користувача на сервері — див. нижче |
| `app.storage.get('key')` / `set` / `remove` | Сховище на пристрої, своє в кожного застосунку |
| `await app.sleep(500)` | Зачекати вказану кількість мілісекунд |
| `app.close()` | Закрити застосунок |
| `app.telegram` | `Telegram.WebApp` як є, поза Telegram — `null` |
| `app.max` | Міст MAX як є, поза MAX — `null` |

Приклад із налаштувань блоку:

```js
// Тема і ширина екрана — у змінні
const device = app.device
app.variables.set('theme', device.dark ? 'dark' : 'light')

if (app.platform === 'telegram') {
    app.vibrate('success')
}

// Що повернули — потрапить у змінну результату
return device.width
```

Помилки коду видно в консолі браузера в попередньому перегляді, і вони потрапляють у [журнал помилок](https://support.watbot.org/uk/mini-apps/users) — «Помилка в коді JavaScript».

## Код застосунку

Код на весь застосунок: виконується один раз під час запуску, до першого екрана, і слухає події. Відкривається кнопкою «Код застосунку» (значок «</>») на панелі над схемою. Якщо код задано, значок зафарбовано.

У вікні «Код застосунку»:

- «Код» — до 100 000 символів;
- «Змінні, які код може записати» — до 50 імен. Інші змінні сервер не прийме;
- шпаргалка подій і «Приклад» із кнопкою «Вставити приклад».

![Вікно «Код застосунку»](https://support.watbot.org/_media/uk/02-mini-apps/_images/app-code.webp?v=1791781231)

Код зберігається у версії: нова версія та шаблон отримують його копію. Після збереження відкритий попередній перегляд перезапускається.

### Події

| Підписка | Коли спрацьовує |
|---|---|
| `app.on('start', ({platform, startParam}) => {})` | Застосунок запустився |
| `app.on('screen', ({id, name, type}) => {})` | Показано екран або вікно поверх нього |
| `app.on('event', ({blockId, eventId}) => {})` | Натискання на кнопку, пункт або елемент — до надсилання на сервер |
| `app.on('field', ({variable, value}) => {})` | Людина змінила поле введення |
| `app.on('error', ({message}) => {})` | Застосунок показав помилку |
| `app.on('hide', () => {})` / `app.on('show', () => {})` | Застосунок згорнули й повернули |
| `app.on('theme', ({dark}) => {})` | Змінилася світла або темна тема |
| `app.on('storage', ({key, newValue}) => {})` | Код елемента «HTML» записав у localStorage |

- Підписка на `start`, `screen` і `theme` одразу отримує поточне значення — навіть якщо підписалися після `await`.
- `app.on()` повертає функцію відписки: `const stop = app.on(…); stop()`. Є й `app.off(name, handler)`.
- `await app.loadScript('https://…')` підключає зовнішній скрипт: лічильник, віджет, бібліотеку. Адреса — лише `http://` або `https://`.

Решта — як у блоку «JavaScript»: `app.variables`, `app.toast`, `app.alert`, `app.openLink`, `app.storage`, `app.localStorage`, `app.telegram` та інші. `app.contact` і `app.tags` тут лише для читання: змінювати теги — справа блоків тегів.

Змінні, записані через `app.variables.set()`, ідуть на сервер із наступною подією.

Приклад із вікна — Google tag:

```js
// Підключаємо Google tag
window.dataLayer = window.dataLayer || []
function gtag() { dataLayer.push(arguments) }
gtag('js', new Date())
gtag('config', 'G-XXXXXXXXXX', {send_page_view: false})
await app.loadScript('https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX')

// Кожен екран — перегляд сторінки
app.on('screen', ({id, name}) => {
    gtag('event', 'page_view', {page_title: name, page_location: location.origin + '/screen/' + id})
})

// Натискання — події
app.on('event', ({eventId}) => {
    gtag('event', 'tap', {event_id: eventId})
})
```

Помилки коду застосунку — під час запуску та в обробниках — видно в консолі, і вони потрапляють у журнал помилок із причиною «Помилка в коді застосунку» та стеком викликів. Та сама помилка записується один раз.

## «HTML-код»: елемент і екран

Свій HTML зі скриптами та стилями буває двох видів:

- **елемент «HTML-код»** — у групі «Віджети», стає в область екрана поруч з іншими елементами;
- **екран «HTML-код»** — третій вид екрана після «Стандартного вікна» і «Вкладок знизу»: увесь екран — ваш код, без шапки й областей. Новий такий екран одразу отримує приклад із кнопкою та вихід `next`. Зробити його модальним вікном не можна.

Код працює в ізольованому фреймі: сесія, дані застосунку та решта екрана йому недоступні. Скрипти й стилі — всередині коду. Колір тексту фрейм бере з теми застосунку.

| Налаштування | Що задає |
|---|---|
| «Код» | HTML до 100 000 символів |
| «Висота» (в елемента) | «За вмістом» — підлаштовується під код, зокрема коли він сам змінює вміст; «Задати» — від 40 до 2000 пікселів, усе, що не вміститься, прокручується всередині |
| «Виходи» | До 20. У виходу — «Ключ» (латиниця, цифри, `_` і `-`) і «Назва на схемі». Вихід — перехід на інший блок схеми |
| «Змінні, які код може записати» | До 50 імен. Інші сервер не прийме |

![Елемент «HTML-код»: код, виходи й змінні](https://support.watbot.org/_media/uk/02-mini-apps/_images/html-element.webp?v=1791781231)

### Що доступно коду у фреймі

| Виклик | Що робить |
|---|---|
| `app.event('next')` | Перейти виходом із цим ключем |
| `app.event('next', {name: 'value'})` | Перейти й записати дозволені змінні |
| `app.variables.set('name', 'value')` | Записати дозволену змінну одразу, без переходу: екран не змінюється |
| `localStorage.setItem('key', 'value')` | Сховище користувача на сервері — див. нижче. `sessionStorage` живе, поки відкритий екран |
| `app.openLink('https://…')` | Відкрити посилання в браузері. Звичайні посилання `<a>` відкриваються так само |
| `{{ name }}` | Підставити значення змінної просто в код — як текст, з екрануванням |

На схемі код не запускається — замість нього заглушка. У редакторі екрана фрейм показує результат і перезапускається після редагування коду.

## Сховища

| | `localStorage` на сервері | `app.storage` |
|---|---|---|
| Де доступне | `localStorage` у фреймі «HTML-код», `app.localStorage` у блоці «JavaScript» і коді застосунку | Блок «JavaScript» і код застосунку |
| Де зберігається | На сервері, у користувача застосунку | У браузері людини |
| Переживає | Перезапуск, очищення телефона, зміну пристрою в тому самому месенджері | Поки не очищено дані браузера |
| Спільне | Для всіх елементів «HTML-код», коду застосунку та блоків «JavaScript» одного застосунку | Своє в кожного застосунку |
| Значення | Лише рядки, до 100 000 символів разом із ключами | Будь-які значення, зберігаються як JSON |

Сховище на сервері своє для кожного майданчика: в однієї людини в Telegram, MAX і браузері воно різне.

## Обмеження

- Код блоку «JavaScript» — до 20 000 символів, код застосунку й HTML-коду — до 100 000.
- Час на виконання блоку «JavaScript» — до 60 секунд.
- У журнал потрапляє не більше ніж 20 помилок на хвилину з одного блоку.
