Как стать автором
Поиск
Написать публикацию
Обновить

Telegram Web Apps (Повторное открытие последней страницы)

Время на прочтение4 мин
Количество просмотров15K

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

Теперь у нас кеширование будет работать только в том случае, если приложение запущено из телеграм веб бота.

Спасибо за чтение!

Теги:
Хабы:
Всего голосов 4: ↑3 и ↓1+2
Комментарии4

Публикации

Ближайшие события