Перейти к содержанию

Telegram Mini Apps: SDK и объект WebApp

Первый экран выбирает курс без обращения к функциям Telegram. Теперь подключим окружение аккуратно: определим доступный объект WebApp, сообщим о готовности оболочки и сохраним обычный браузер как ограниченный режим чтения. Результат урока — небольшой адаптер, который не смешивает наличие SDK с подтверждением пользователя. В следующих модулях именно он передаст хосту тему, кнопки и события.

Официальный скрипт создаёт window.Telegram.WebApp. Объект содержит версию доступного API, платформу, параметры темы, данные запуска и методы. Его поля описаны в документации инициализации. Не копируйте в приложение вторую самодельную реализацию WebApp и не заменяйте глобальный объект ради обычного просмотра. Для упражнений нужен явный отдельный fake host, который не считается настоящим Telegram и никогда не подтверждает серверную идентичность.

Узкая граница адаптера

Создадим полный модуль host.js. Он принимает область глобальных объектов, поэтому в последующем самостоятельном сценарии можно передать известный учебный объект. По умолчанию используется window. Возвращается WebApp либо null, а не придуманный пользователь с ID 1:

export function resolveHost(scope = window) {
  const host = scope.Telegram?.WebApp;
  if (!host || typeof host.ready !== 'function' ||
      typeof host.onEvent !== 'function' || typeof host.offEvent !== 'function') {
    return null;
  }
  return host;
}

В screen.js после импорта добавляется вызов ready. Это точный фрагмент подключения к полному экрану предыдущего урока; каталог и renderSelection сохраняются:

import {resolveHost} from './host.js';
const host = resolveHost();
if (host) host.ready();

В архиве есть полная копия экрана после подключения, а не только эти строки. Функция не проверяет каждую возможность SDK, потому что разные модули требуют разного набора. Наличие ready и событий достаточно для этого учебного шага. Перед использованием MainButton, storage или новых safe-area свойств соответствующий модуль проверит свою зависимость отдельно. Большое логическое условие «поддерживается весь SDK» сделало бы интерфейс недоступным клиенту, которому не нужна конкретная новая функция.

Готовность интерфейса и готовность данных

Ready сообщает хосту, что приложение подготовило интерфейс. Его нужно связать с доступной оболочкой: заголовок, выбор курса и понятное состояние уже есть. Он не ждёт проверку initData, не создаёт cookie и не делает запросы авторизованными. Если приложение вызывает ready до появления хоть какого-то содержимого, пользователь может увидеть пустую область. Если ждёт бесконечный API, публичный каталог останется скрыт за загрузкой. Поэтому сначала готовая оболочка, затем отдельные состояния сетевых данных.

У WebApp есть version и platform. Эти поля полезны для выбора возможностей и диагностики клиента. Версия доступного API не обязательно совпадает с маркетинговой версией приложения Telegram, а platform не является доказательством типа устройства для критического доступа. Можно использовать их как наблюдаемые сведения, но нельзя выдавать административную роль пользователям «правильной платформы». Серверные права не зависят от значения, которое клиент способен отправить в своём JSON.

Обычный браузер может загрузить официальный SDK и получить объект без полноценного запуска. Поэтому resolveHost отвечает только на вопрос «есть ожидаемый API-объект», не «пользователь подтверждён». Пустой initData не заменяется строкой, собранной из параметров URL. В нашем экранном состоянии читатель по-прежнему выбирает публичный курс, а серверная часть личного плана остаётся недоступной до своего обмена. Не изменяйте текст статуса на «вы вошли», просто обнаружив global Telegram.

Подписка на событие

События позволяют обновлять интерфейс при изменении хоста. Уже сейчас определим правило жизненного цикла: подписка создаётся при подключении экрана, снимается при его уничтожении или замене. Нужна та же ссылка на callback. Две одинаково написанные анонимные функции являются разными объектами и не подходят для снятия друг друга. В модулях темы и viewport будем возвращать cleanup-функцию, чтобы этот договор был виден в коде.

Повторная инициализация без cleanup особенно опасна для действия сохранения. Один клик может вызвать несколько обработчиков, хотя кнопка выглядит одна. Для нашего первого экрана событие select также зарегистрировано один раз, после загрузки module. Когда появится внутренняя навигация, не выполняйте заново весь screen.js при каждом выборе курса. Состояние и подписки нужно обновлять осмысленно, а не размножать страницу кусками кода. Такое правило не требует фреймворка, но требует ответственности за монтаж.

Адаптер также не решает сетевую загрузку собственного каталога и не восстанавливает предыдущий выбор. Он предоставляет только интерфейс среды. Если расширять его одновременно хранением, запросами и навигацией, обычное отсутствие хоста станет причиной слишком многих побочных эффектов. В нашем проекте каждый модуль принимает явные зависимости: тема получает host и корень, кнопка — состояние и операцию, серверная проверка — raw строку и серверный секрет. Такой небольшой договор позволяет отдельно читать механизм и понимать, какие данные ему действительно нужны.

Ошибочный объект и ограниченный режим

Передайте в resolveHost учебный объект, у которого отсутствует offEvent. Ожидается null, потому что следующие модули должны уметь снять созданные подписки. Это проверка минимального контракта адаптера, не эмуляция Telegram. Обычный каталог остаётся доступным. Не создавайте заглушку, которая молча сообщает успех сохранения: запасное поведение должно ограничивать отсутствующую функцию, а не выдумывать выполненный серверный результат.

Если SDK появился позже загрузки экрана, текущая функция не следит за ним автоматически. Порядок script из первого урока нужен, чтобы собственный код выполнялся после ожидаемого подключения. Для сложного асинхронного loader понадобится отдельный механизм ожидания с отказом. Здесь нет скрытого poll по таймеру и бесконечного восстановления хоста. Небольшой явный порядок проще проверить на выбранной HTML-странице, чем обещать универсальную загрузку в любом окружении.

При диагностике записывайте наличие функций, platform и доступную version без raw initData. Строка запуска содержит пользовательский контекст и потенциально пригодна для повторного обмена в окне свежести. Обычный console.log всего WebApp может раскрыть больше, чем нужно. В учебном пакете нет автоматической телеметрии. Для рабочего приложения формат наблюдений и срок хранения сведений нужно определить отдельно, сохраняя связь с конкретным отказом.

Ожидаемый итог — один адаптер, один вызов ready после появления оболочки и публичный каталог в режиме без хоста. Исполнение JavaScript, WebView и fake-сценариев здесь не выполнялось. Следующий урок применит первое действительное свойство окружения — цветовую тему — и покажет, как обновлять её в уже открытом экране без повторной инициализации каталога или потери выбранного курса.

Оглавление серии