15 апреля команда телеграм предоставила нам возможность разрабатывать веб ботов.
Теперь взаимодействие с ботами стало очень интерактивным, так как мы обладаем возможностью интегрировать в ботов полноценные веб-приложения.
В процессе разработки веб апп бота я столкнулся с проблемой, что после закрытия интерфейса веб апп и повторном его открытии мы теряем последнюю ссылку на которой находился пользователь, и веб приложение открывается с главной страницы.
Сперва, может показаться, что это не является проблемой, но на самом деле очень легко случайно закрыть веб-окно, так как оно не открывается на весь экран, а чтобы закрыть его, достаточно потянуть его вниз и мы теряем всё предыдущее состояние приложения.
В своём приложении я использую React, mobX и React Router Dom v6. Первое, что приходит в голову, это подписаться на изменение навигации и сохранять последнее значение в localStorage или cookies, я выбрал cookie, с временем жизни 10 минут.
Давайте посмотрим на код. Создаём location-provider.tsx
import React from "react" import { useLocation, useNavigate } from "react-router-dom" import Cookies from "js-cookie" interface LocationProviderProps { children: React.ReactNode } const LocationProvider: React.FC<LocationProviderProps> = ({ children }) => { const location = useLocation() const navigate = useNavigate() React.useEffect(() => { const currentLocation = Cookies.get("location_app") if (!currentLocation) return navigate(currentLocation) }, []) React.useEffect(() => { const tenMinutes = new Date(new Date().getTime() + 10 * 60 * 1000) Cookies.set("location_app", location.pathname, { expires: tenMinutes, }) }, [location]) return <>{children}</> } export default LocationProvider
Импортируем его в App.tsx и оборачиваем наши роуты
import React from "react" import { BrowserRouter, Route, Routes } from "react-router-dom" import LocationProvider from "providers/location-provider" import { AppsPage } from "./pages/apps" import { DevelopersPage } from "./pages/developers" import { FavoritesPage } from "./pages/favorites" import { SearchPage } from "./pages/search" function App() { return ( <BrowserRouter> <LocationProvider> <Routes> <Route index element={<AppsPage />} /> <Route path="/apps" element={<AppsPage />} /> <Route path="/apps/:appId" element={<AppDetailPage />} /> <Route path="/favorites" element={<FavoritesPage />} /> <Route path="/search" element={<SearchPage />} /> <Route path="/developers" element={<DevelopersPage />} /> </Routes> </LocationProvider> </BrowserRouter> ) } export default App
Отлично, теперь после закрытия и открытия веб приложения у нас будет открываться последняя страница, но!
Тут есть проблема, это то, что если при открытом приложении перейти на другую страницу через строку ввода URL, нас будет редиректить назад. Но у веб ботов нет адресной строки, значит на этом можно остановиться?
Да, если вам этого достаточно, то это будет работать в веб ботах. Но у вас будут проблемы при открытии вашего веб приложения вне веб ботов, например у вас есть онлайн магазин, вы бы хотели взаимодействовать с ним и в телеграм веб апп и просто как веб приложение доступное по адресу, что тогда?
Думаем дальше, в DOM API у объекта window есть event onbeforeunload, кажется это то, что нам нужно, будем сохранять последний URL перед закрытием веб приложения, пробуем.
Обновим наш location-provider.tsx
import React from "react" import { useLocation, useNavigate } from "react-router-dom" import Cookies from "js-cookie" interface LocationProviderProps { children: React.ReactNode } const LocationProvider: React.FC<LocationProviderProps> = ({ children }) => { const location = useLocation() const navigate = useNavigate() React.useEffect(() => { const currentLocation = Cookies.get("location_app") if (!currentLocation) return navigate(currentLocation) }, []) React.useEffect(() => { window.onbeforeunload = (e: BeforeUnloadEvent) => { e.preventDefault() const tenMinutes = new Date(new Date().getTime() + 10 * 60 * 1000) Cookies.set("location_app", location.pathname, { expires: tenMinutes, }) } }, [location]) return <>{children}</> } export default LocationProvider
Пробуем гулять по приложению через адресную строку, редиректа назад нет, отлично, так же сохраняется ссылка при закрытии вкладки с приложением, то что мы и хотели! Пробуем в телеграм, бац и тут это совсем не работает, почему?
Дело в том, что телеграм открывает браузерное окно в так называемом WebView, я не силен в разработке мобильных приложений, но знаю, что эта штука позволяет открывать браузер внутри мобильных приложений, но у них есть свои ограничения и это как раз одно из них.
Что тогда? Отличным решением будет вернуться к первому варианту, но включить кеширование только для телеграм веб апп. Давайте посмотрим на то, что у нас получилось.
import React from "react" import { useLocation, useNavigate } from "react-router-dom" import Cookies from "js-cookie" interface LocationProviderProps { children: React.ReactNode } const LocationProvider: React.FC<LocationProviderProps> = ({ children }) => { if (!window.Telegram.WebApp.initData.length) { return <>{children}</> } else { return <LocationProviderInner>{children}</LocationProviderInner> } } const LocationProviderInner: React.FC<LocationProviderProps> = ({ children, }) => { const location = useLocation() const navigate = useNavigate() const redirectToLastPage = () => { const currentLocation = Cookies.get("location_app") if (!currentLocation) return navigate(currentLocation) } React.useEffect(() => { if (location.key === "default") redirectToLastPage() Cookies.set("location_app", location.pathname, { expires: new Date(new Date().getTime() + 10 * 60 * 1000), // 10 min }) }, [location.key]) return <>{children}</> } export default LocationProvider
Теперь у нас кеширование будет работать только в том случае, если приложение запущено из телеграм веб бота.
Спасибо за чтение!
