Created
June 10, 2025 13:00
-
-
Save milksense/60a875f8e7e4ea1a2b5c6e83f530cde8 to your computer and use it in GitHub Desktop.
Max bridge (beta)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var d = Object.defineProperty; | |
| var v = (o, e, t) => e in o ? d(o, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[e] = t; | |
| var n = (o, e, t) => v(o, typeof e != "symbol" ? e + "" : e, t); | |
| class w { | |
| constructor({ | |
| postBackButtonEvent: e, | |
| onClick: t, | |
| offClick: s | |
| }) { | |
| n(this, "visible", !1); | |
| n(this, "postBackButtonEvent"); | |
| n(this, "onClick"); | |
| n(this, "offClick"); | |
| this.postBackButtonEvent = e, this.onClick = t, this.offClick = s; | |
| } | |
| get isVisible() { | |
| return this.visible; | |
| } | |
| show() { | |
| this.visible = !0, this.postBackButtonEvent(!0); | |
| } | |
| hide() { | |
| this.visible = !1, this.postBackButtonEvent(!1); | |
| } | |
| } | |
| class u { | |
| constructor({ | |
| postSwipesBehaviorEvent: e | |
| }) { | |
| n(this, "enabled", !0); | |
| n(this, "postSwipesBehaviorEvent"); | |
| this.postSwipesBehaviorEvent = e; | |
| } | |
| get isEnabled() { | |
| return this.enabled; | |
| } | |
| enable() { | |
| this.enabled = !0, this.postSwipesBehaviorEvent(!0); | |
| } | |
| disable() { | |
| this.enabled = !1, this.postSwipesBehaviorEvent(!1); | |
| } | |
| } | |
| const p = class p { | |
| constructor() { | |
| n(this, "rawInitData"); | |
| const e = this.extractInitData(); | |
| if (e) | |
| this.rawInitData = e, sessionStorage.setItem(p.WEB_APP_DATA_KEY, e); | |
| else { | |
| const t = sessionStorage.getItem(p.WEB_APP_DATA_KEY); | |
| this.rawInitData = t; | |
| } | |
| } | |
| /** | |
| * Геттер для получения сырых данных инициализации. | |
| */ | |
| get initData() { | |
| return this.rawInitData; | |
| } | |
| /** | |
| * Геттер для получения распарсенных данных инициализации. | |
| */ | |
| get initDataUnsafe() { | |
| return this.parseInitData(this.rawInitData); | |
| } | |
| extractInitData() { | |
| try { | |
| const e = location.hash.toString().replace(/^#/, ""), s = new URLSearchParams(e).get(p.WEB_APP_DATA_KEY); | |
| if (s) return s; | |
| const r = performance.getEntriesByType("navigation")[0], i = r && new URL(r.name).searchParams.get(p.WEB_APP_DATA_KEY); | |
| return i || null; | |
| } catch (e) { | |
| return console.warn("Ошибка при извлечении init data:", e), null; | |
| } | |
| } | |
| getUserInitData(e) { | |
| try { | |
| const t = JSON.parse(e); | |
| if (t && typeof t == "object") | |
| return { | |
| first_name: t.first_name, | |
| last_name: t.last_name, | |
| username: t.username, | |
| language_code: t.language_code, | |
| photo_url: t.photo_url, | |
| id: Number(t.id) | |
| }; | |
| } catch (t) { | |
| console.warn("Ошибка при парсинге данных пользователя:", t); | |
| } | |
| } | |
| getChatInitData(e) { | |
| try { | |
| const t = JSON.parse(e); | |
| if (t && typeof t == "object") | |
| return { | |
| id: t.id, | |
| type: t.type | |
| }; | |
| } catch (t) { | |
| console.warn("Ошибка при парсинге данных чата:", t); | |
| } | |
| } | |
| parseInitData(e) { | |
| const t = {}; | |
| if (!e) | |
| return t; | |
| try { | |
| const s = decodeURIComponent(e); | |
| new URLSearchParams(s).forEach((i, a) => { | |
| switch (a) { | |
| case "hash": | |
| case "ip": | |
| case "query_id": { | |
| t[a] = i; | |
| break; | |
| } | |
| case "auth_date": { | |
| t[a] = Number(i); | |
| break; | |
| } | |
| case "user": { | |
| t[a] = this.getUserInitData(i); | |
| break; | |
| } | |
| case "chat": { | |
| t[a] = this.getChatInitData(i); | |
| break; | |
| } | |
| default: | |
| console.warn(`Неизвестный параметр: ${a}`); | |
| } | |
| }); | |
| } catch (s) { | |
| console.warn("Ошибка при парсинге init data:", s); | |
| } | |
| return t; | |
| } | |
| }; | |
| n(p, "WEB_APP_DATA_KEY", "WebAppData"); | |
| let h = p; | |
| const g = 1e4, f = /^https:\/\/.*\.(?:max|oneme)\.ru$/; | |
| class E { | |
| constructor() { | |
| n(this, "eventHandlers", /* @__PURE__ */ new Map()); | |
| // Храним запросы, ожидающие ответа | |
| n(this, "pendingRequests", /* @__PURE__ */ new Map()); | |
| n(this, "webviewBridge"); | |
| n(this, "swipesBehaviorManager"); | |
| n(this, "initDataManager"); | |
| n(this, "BackButton"); | |
| this.isIframe() ? window.addEventListener("message", async (e) => { | |
| if (!(!f.test(e.origin) || typeof e.data != "string")) | |
| try { | |
| const { type: t, ...s } = JSON.parse(e.data); | |
| t != null && t.startsWith("WebApp") && await this.receiveEvent(t, s); | |
| } catch (t) { | |
| console.error("Ошибка при обработке сообщения:", t); | |
| } | |
| }) : this.webviewBridge = window.WebViewHandler, this.BackButton = new w({ | |
| postBackButtonEvent: (e) => this.postEvent("WebAppSetupBackButton", { isVisible: e }), | |
| onClick: (e) => this.onEvent("WebAppBackButtonPressed", e), | |
| offClick: (e) => this.offEvent("WebAppBackButtonPressed", e) | |
| }), this.swipesBehaviorManager = new u({ | |
| postSwipesBehaviorEvent: (e) => this.postEvent("WebAppSetupSwipesBehavior", { allowVerticalSwipes: e }) | |
| }), this.initDataManager = new h(); | |
| } | |
| /** | |
| * Сырые данные инициализации | |
| */ | |
| get initData() { | |
| return this.initDataManager.initData; | |
| } | |
| /** | |
| * Распарсенные данные инициализации | |
| */ | |
| get initDataUnsafe() { | |
| return this.initDataManager.initDataUnsafe; | |
| } | |
| /** | |
| * Состояние вертикальных свайпов | |
| */ | |
| get isVerticalSwipesEnabled() { | |
| return this.swipesBehaviorManager.isEnabled; | |
| } | |
| isIframe() { | |
| return window.self !== window.top; | |
| } | |
| /** | |
| * Стандартная отправка событий (без ожидания ответа) | |
| */ | |
| postEvent(e, t = {}, s) { | |
| this.sendEventByClientType(e, t), s == null || s(); | |
| } | |
| /** | |
| * Отправка события с ожиданием ответа | |
| */ | |
| postEventAsync(e, t = {}) { | |
| return new Promise((s, r) => { | |
| const i = crypto.randomUUID(); | |
| this.pendingRequests.set(i, { resolve: s, reject: r }), this.sendEventByClientType(e, { ...t, requestId: i }), setTimeout(r, g); | |
| }); | |
| } | |
| /** | |
| * Отправка события в зависимости от типа | |
| */ | |
| sendEventByClientType(e, t) { | |
| this.isIframe() ? window.parent.postMessage(JSON.stringify({ type: e, ...t }), "*") : this.webviewBridge && this.webviewBridge.postEvent(e, JSON.stringify(t)); | |
| } | |
| /** | |
| * Обработка полученного события | |
| */ | |
| async receiveEvent(e, { requestId: t, ...s }) { | |
| var r, i; | |
| if (t && "error" in s) | |
| console.log(`[WebApp] Получена ошибка: ${e}`, s), (r = this.pendingRequests.get(t)) == null || r.reject(s), this.pendingRequests.delete(t); | |
| else if (t && !("error" in s)) | |
| console.log(`[WebApp] Получено событие: ${e}`, s), (i = this.pendingRequests.get(t)) == null || i.resolve(s), this.pendingRequests.delete(t); | |
| else { | |
| const a = this.eventHandlers.get(e); | |
| a == null || a.forEach((c) => { | |
| try { | |
| c(s); | |
| } catch (l) { | |
| console.error(`Ошибка в обработке события "${e}":`, l); | |
| } | |
| }); | |
| } | |
| } | |
| /** | |
| * Функция, которую используют нативные клиенты для отправки ответа | |
| */ | |
| async sendEvent(e, t = "{}") { | |
| try { | |
| this.receiveEvent(e, JSON.parse(t)); | |
| } catch (s) { | |
| console.warn(s); | |
| } | |
| } | |
| /** | |
| * Подписка на событие с использованием колбэка | |
| */ | |
| onEvent(e, t) { | |
| let s = this.eventHandlers.get(e); | |
| return s || (s = /* @__PURE__ */ new Set(), this.eventHandlers.set(e, s)), s.add(t), () => { | |
| this.offEvent(e, t); | |
| }; | |
| } | |
| /** | |
| * Удаление подписки на событие | |
| */ | |
| offEvent(e, t) { | |
| const s = this.eventHandlers.get(e); | |
| s && (s.delete(t), s.size === 0 && this.eventHandlers.delete(e)); | |
| } | |
| /** | |
| * Закрытие приложения | |
| */ | |
| close() { | |
| this.postEvent("WebAppClose", {}, () => { | |
| console.log("Приложение закрыто"); | |
| }); | |
| } | |
| /** | |
| * Инициализация WebApp API | |
| */ | |
| ready() { | |
| this.postEvent("WebAppReady", {}, () => { | |
| console.log("WebApp готово к работе"); | |
| }); | |
| } | |
| /** | |
| * Запрос номера телефона | |
| */ | |
| requestContact() { | |
| return this.postEventAsync("WebAppRequestPhone"); | |
| } | |
| /** | |
| * Подтверждать закрытие миниаппа с помощью всплывающего окна | |
| */ | |
| enableClosingConfirmation() { | |
| this.postEvent("WebAppSetupClosingBehavior", { needConfirmation: !0 }); | |
| } | |
| /** | |
| * Отключение подтверждения закрытия миниаппа | |
| */ | |
| disableClosingConfirmation() { | |
| this.postEvent("WebAppSetupClosingBehavior", { needConfirmation: !1 }); | |
| } | |
| /** | |
| * Открытие ссылки во внешнем браузере | |
| */ | |
| openLink(e) { | |
| this.postEvent("WebAppOpenLink", { url: e }); | |
| } | |
| /** | |
| * Открытие диплинка связанного с max.ru | |
| */ | |
| openMaxLink(e) { | |
| this.postEvent("WebAppOpenMaxLink", { url: e }); | |
| } | |
| /** | |
| * Включение вертикальных свайпов | |
| */ | |
| enableVerticalSwipes() { | |
| this.swipesBehaviorManager.enable(); | |
| } | |
| /** | |
| * Отключение вертикальных свайпов | |
| */ | |
| disableVerticalSwipes() { | |
| this.swipesBehaviorManager.disable(); | |
| } | |
| } | |
| window.WebApp = new E(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment