@telegram-apps/sdk
Advanced tools
Comparing version 2.4.1 to 2.5.0
@@ -8,1 +8,3 @@ export declare const ERR_POPUP_INVALID_PARAMS = "ERR_POPUP_INVALID_PARAMS"; | ||
export declare const ERR_NOT_AVAILABLE = "ERR_NOT_AVAILABLE"; | ||
export declare const ERR_NOT_SUPPORTED = "ERR_NOT_SUPPORTED"; | ||
export declare const ERR_NOT_MOUNTED = "ERR_NOT_MOUNTED"; |
@@ -25,3 +25,3 @@ export { classNames } from './classnames/classNames.js'; | ||
export { isSSR } from './utils/isSSR.js'; | ||
export { ERR_POPUP_INVALID_PARAMS, ERR_INVALID_HOSTNAME, ERR_INVALID_SLUG, ERR_ACCESS_DENIED, ERR_DATA_INVALID_SIZE, ERR_NOT_AVAILABLE, ERR_ALREADY_CALLED, } from './errors.js'; | ||
export { ERR_POPUP_INVALID_PARAMS, ERR_INVALID_HOSTNAME, ERR_INVALID_SLUG, ERR_ACCESS_DENIED, ERR_DATA_INVALID_SIZE, ERR_NOT_AVAILABLE, ERR_ALREADY_CALLED, ERR_NOT_SUPPORTED, ERR_NOT_MOUNTED, } from './errors.js'; | ||
export { init, type InitOptions } from './init.js'; | ||
@@ -28,0 +28,0 @@ export { CancelablePromise, defineEventHandlers, emitMiniAppsEvent, isIframe, removeEventHandlers, compareVersions, createPostEvent, $debug, ERR_CANCELED, ERR_ABORTED, ERR_METHOD_PARAMETER_UNSUPPORTED, ERR_METHOD_UNSUPPORTED, ERR_TIMED_OUT, ERR_UNKNOWN_ENV, ERR_RETRIEVE_LP_FAILED, ERR_CUSTOM_METHOD_ERR_RESPONSE, invokeCustomMethod, isTMA, on, off, postEvent, $targetOrigin, request, subscribe, supports, unsubscribe, mockTelegramEnv, deleteCssVar, setCssVar, isAbortError, isTimeoutError, isCanceledError, addEventListener, retrieveLaunchParams, TypedError, } from '@telegram-apps/bridge'; |
import { EventListener } from '@telegram-apps/bridge'; | ||
/** | ||
* Hides the back button. | ||
* True if the component is currently mounted. | ||
*/ | ||
export declare function hide(): void; | ||
export declare const isMounted: import('@telegram-apps/signals').Signal<boolean>; | ||
/** | ||
* @returns True if the back button is supported. | ||
* @returns True if the Back Button is supported. | ||
*/ | ||
export declare function isSupported(): boolean; | ||
export declare const isSupported: import('@telegram-apps/signals').Computed<boolean>; | ||
/** | ||
* True if the component is currently visible. | ||
* Hides the Back Button. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare const isVisible: import('@telegram-apps/signals').Signal<boolean>; | ||
export declare const hide: () => void; | ||
/** | ||
* True if the component is currently mounted. | ||
* True if the Back Button is currently visible. | ||
*/ | ||
export declare const isMounted: import('@telegram-apps/signals').Signal<boolean>; | ||
export declare const isVisible: import('@telegram-apps/signals').Signal<boolean>; | ||
/** | ||
@@ -23,19 +24,23 @@ * Mounts the component. | ||
* if it changed. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function mount(): void; | ||
export declare const mount: import('../../toolkit/withIsSupported.js').WithIsSupported<() => void>; | ||
/** | ||
* Add a new back button click listener. | ||
* Add a new Back Button click listener. | ||
* @param fn - event listener. | ||
* @returns A function to remove bound listener. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function onClick(fn: EventListener<'back_button_pressed'>): VoidFunction; | ||
export declare const onClick: import('../../toolkit/withIsSupported.js').WithIsSupported<(fn: EventListener<'back_button_pressed'>) => VoidFunction>; | ||
/** | ||
* Removes the back button click listener. | ||
* Removes the Back Button click listener. | ||
* @param fn - an event listener. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function offClick(fn: EventListener<'back_button_pressed'>): void; | ||
export declare const offClick: import('../../toolkit/withIsSupported.js').WithIsSupported<(fn: EventListener<'back_button_pressed'>) => void>; | ||
/** | ||
* Shows the back button. | ||
* Shows the Back Button. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare function show(): void; | ||
export declare const show: () => void; | ||
/** | ||
@@ -42,0 +47,0 @@ * Unmounts the component, removing the listener, saving the component state in the local storage. |
import { CancelablePromise, BiometryTokenUpdateStatus, BiometryAuthRequestStatus } from '@telegram-apps/bridge'; | ||
import { AuthenticateOptions, RequestAccessOptions, UpdateTokenOptions } from './types.js'; | ||
/** | ||
* @returns True if the biometry manager is supported. | ||
*/ | ||
export declare const isSupported: import('@telegram-apps/signals').Computed<boolean>; | ||
/** | ||
* Attempts to authenticate a user using biometrics and fetch a previously stored | ||
@@ -11,4 +15,5 @@ * secure token. | ||
* @throws {TypedError} ERR_NOT_AVAILABLE | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare function authenticate(options?: AuthenticateOptions): CancelablePromise<{ | ||
export declare const authenticate: (options?: AuthenticateOptions) => CancelablePromise<{ | ||
/** | ||
@@ -24,6 +29,2 @@ * Authentication status. | ||
/** | ||
* @returns True if the biometry manager is supported. | ||
*/ | ||
export declare function isSupported(): boolean; | ||
/** | ||
* Opens the biometric access settings for bots. Useful when you need to request biometrics | ||
@@ -36,3 +37,3 @@ * access to users who haven't granted it yet. | ||
*/ | ||
export declare function openSettings(): void; | ||
export declare const openSettings: import('../../toolkit/withIsSupported.js').WithIsSupported<() => void>; | ||
/** | ||
@@ -44,7 +45,9 @@ * Requests permission to use biometrics. | ||
* @throws {TypedError} ERR_NOT_AVAILABLE | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare function requestAccess(options?: RequestAccessOptions): CancelablePromise<boolean>; | ||
export declare const requestAccess: (options?: RequestAccessOptions) => CancelablePromise<boolean>; | ||
/** | ||
* Mounts the component. | ||
* @throws {TypedError} ERR_ALREADY_CALLED | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
@@ -60,3 +63,4 @@ export declare const mount: (options?: import('@telegram-apps/bridge').AsyncOptions | undefined) => CancelablePromise<void>; | ||
* @returns Promise with `true`, if token was updated. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare function updateToken(options?: UpdateTokenOptions): CancelablePromise<BiometryTokenUpdateStatus>; | ||
export declare const updateToken: (options?: UpdateTokenOptions) => CancelablePromise<BiometryTokenUpdateStatus>; |
@@ -7,2 +7,2 @@ import { ExecuteWithOptions, CancelablePromise } from '@telegram-apps/bridge'; | ||
*/ | ||
export declare const requestBiometry: import('../../withIsSupported.js').WithIsSupported<(options?: ExecuteWithOptions) => CancelablePromise<State>>; | ||
export declare const requestBiometry: import('../../toolkit/withIsSupported.js').WithIsSupported<(options?: ExecuteWithOptions) => CancelablePromise<State>>; |
/** | ||
* Disables the confirmation dialog when closing the Mini App. | ||
*/ | ||
export declare function disableConfirmation(): void; | ||
/** | ||
* True if the component is currently mounted. | ||
@@ -10,2 +6,7 @@ */ | ||
/** | ||
* Disables the confirmation dialog when closing the Mini App. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare const disableConfirmation: () => void; | ||
/** | ||
* True if the confirmation dialog should be shown while the user is trying to close the Mini App. | ||
@@ -16,4 +17,5 @@ */ | ||
* Enables the confirmation dialog when closing the Mini App. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare function enableConfirmation(): void; | ||
export declare const enableConfirmation: () => void; | ||
/** | ||
@@ -20,0 +22,0 @@ * Mounts the component. |
import { CancelablePromise, ExecuteWithOptions } from '@telegram-apps/bridge'; | ||
/** | ||
* @returns True if the Cloud Storage is supported. | ||
*/ | ||
export declare const isSupported: import('@telegram-apps/signals').Computed<boolean>; | ||
/** | ||
* Deletes specified key or keys from the cloud storage. | ||
* @param keyOrKeys - key or keys to delete. | ||
* @param options - request execution options. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function deleteItem(keyOrKeys: string | string[], options?: ExecuteWithOptions): CancelablePromise<void>; | ||
export declare const deleteItem: import('../../toolkit/withIsSupported.js').WithIsSupported<(keyOrKeys: string | string[], options?: ExecuteWithOptions) => CancelablePromise<void>>; | ||
/** | ||
@@ -14,3 +19,3 @@ * @param keys - keys list. | ||
*/ | ||
export declare function getItem<K extends string>(keys: K[], options?: ExecuteWithOptions): CancelablePromise<Record<K, string>>; | ||
declare function _getItem<K extends string>(keys: K[], options?: ExecuteWithOptions): CancelablePromise<Record<K, string>>; | ||
/** | ||
@@ -22,13 +27,14 @@ * @param key - cloud storage key. | ||
*/ | ||
export declare function getItem(key: string, options?: ExecuteWithOptions): CancelablePromise<string>; | ||
declare function _getItem(key: string, options?: ExecuteWithOptions): CancelablePromise<string>; | ||
/** | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare const getItem: import('../../toolkit/withIsSupported.js').WithIsSupported<typeof _getItem>; | ||
/** | ||
* Returns a list of all keys presented in the cloud storage. | ||
* @param options - request execution options. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function getKeys(options?: ExecuteWithOptions): CancelablePromise<string[]>; | ||
export declare const getKeys: import('../../toolkit/withIsSupported.js').WithIsSupported<(options?: ExecuteWithOptions) => CancelablePromise<string[]>>; | ||
/** | ||
* @returns True if the cloud storage is supported. | ||
*/ | ||
export declare function isSupported(): boolean; | ||
/** | ||
* Saves specified value by key. | ||
@@ -38,3 +44,5 @@ * @param key - storage key. | ||
* @param options - request execution options. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function setItem(key: string, value: string, options?: ExecuteWithOptions): CancelablePromise<void>; | ||
export declare const setItem: import('../../toolkit/withIsSupported.js').WithIsSupported<(key: string, value: string, options?: ExecuteWithOptions) => CancelablePromise<void>>; | ||
export {}; |
import { ImpactHapticFeedbackStyle, NotificationHapticFeedbackType } from '@telegram-apps/bridge'; | ||
/** | ||
* @returns True if the Haptic Feedback is supported. | ||
*/ | ||
export declare const isSupported: import('@telegram-apps/signals').Computed<boolean>; | ||
/** | ||
* A method tells that an impact occurred. The Telegram app may play the appropriate haptics based | ||
* on style value passed. | ||
* @param style - impact style. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function impactOccurred(style: ImpactHapticFeedbackStyle): void; | ||
export declare const impactOccurred: import('../../toolkit/withIsSupported.js').WithIsSupported<(style: ImpactHapticFeedbackStyle) => void>; | ||
/** | ||
* @returns True if the haptic feedback is supported. | ||
*/ | ||
export declare function isSupported(): boolean; | ||
/** | ||
* A method tells that a task or action has succeeded, failed, or produced a warning. The Telegram | ||
* app may play the appropriate haptics based on type value passed. | ||
* @param type - notification type. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function notificationOccurred(type: NotificationHapticFeedbackType): void; | ||
export declare const notificationOccurred: import('../../toolkit/withIsSupported.js').WithIsSupported<(type: NotificationHapticFeedbackType) => void>; | ||
/** | ||
@@ -24,3 +26,4 @@ * A method tells that the user has changed a selection. The Telegram app may play the | ||
* selection changes. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function selectionChanged(): void; | ||
export declare const selectionChanged: import('../../toolkit/withIsSupported.js').WithIsSupported<() => void>; |
@@ -7,5 +7,5 @@ import { InvoiceStatus, ExecuteWithPostEvent } from '@telegram-apps/bridge'; | ||
/** | ||
* @returns True if the invoice is supported. | ||
* @returns True if the Invoice is supported. | ||
*/ | ||
export declare function isSupported(): boolean; | ||
export declare const isSupported: import('@telegram-apps/signals').Computed<boolean>; | ||
/** | ||
@@ -20,3 +20,3 @@ * Opens an invoice using its slug. | ||
*/ | ||
export declare function open(slug: string, options?: ExecuteWithPostEvent): Promise<InvoiceStatus>; | ||
export declare function _open(slug: string, options?: ExecuteWithPostEvent): Promise<InvoiceStatus>; | ||
/** | ||
@@ -36,2 +36,6 @@ * Opens an invoice using its url. | ||
*/ | ||
export declare function open(url: string, type: 'url', options?: ExecuteWithPostEvent): Promise<InvoiceStatus>; | ||
export declare function _open(url: string, type: 'url', options?: ExecuteWithPostEvent): Promise<InvoiceStatus>; | ||
/** | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare const open: import('../../toolkit/withIsSupported.js').WithIsSupported<typeof _open>; |
@@ -24,4 +24,5 @@ import { EventListener } from '@telegram-apps/bridge'; | ||
* @param updates - state changes to perform. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare function setParams(updates: Partial<State>): void; | ||
export declare const setParams: (updates: Partial<State>) => void; | ||
/** | ||
@@ -28,0 +29,0 @@ * Unmounts the component, removing the listener, saving the component state in the local storage. |
@@ -1,4 +0,4 @@ | ||
export { bindCssVars as bindMiniAppCssVars, close as closeMiniApp, mount as mountMiniApp, ready as miniAppReady, setHeaderColor as setMiniAppHeaderColor, setBackgroundColor as setMiniAppBackgroundColor, setBottomBarColor as setMiniAppBottomBarColor, unmount as unmountMiniApp, } from './methods.js'; | ||
export { bindCssVars as bindMiniAppCssVars, close as closeMiniApp, isSupported as isMiniAppSupported, mount as mountMiniApp, ready as miniAppReady, setHeaderColor as setMiniAppHeaderColor, setBackgroundColor as setMiniAppBackgroundColor, setBottomBarColor as setMiniAppBottomBarColor, unmount as unmountMiniApp, } from './methods.js'; | ||
export { backgroundColor as miniAppBackgroundColor, bottomBarColor as miniAppBottomBarColor, bottomBarColorRGB as miniAppBottomBarColorRGB, headerColor as miniAppHeaderColor, headerColorRGB as miniAppHeaderColorRGB, isMounted as isMiniAppMounted, isCssVarsBound as isMiniAppCssVarsBound, isDark as isMiniAppDark, state as miniAppState, } from './signals.js'; | ||
export type { HeaderColor as MiniAppHeaderColor, GetCssVarNameFn as MiniAppGetCssVarNameFn, State as MiniAppState, } from './types.js'; | ||
export * as miniApp from './exports.variable.js'; |
@@ -1,4 +0,10 @@ | ||
import { RGB, BottomBarColor } from '@telegram-apps/bridge'; | ||
import { BottomBarColor, BackgroundColor } from '@telegram-apps/bridge'; | ||
import { isRGB } from '@telegram-apps/transformers'; | ||
import { Computed } from '@telegram-apps/signals'; | ||
import { GetCssVarNameFn, HeaderColor } from './types.js'; | ||
/** | ||
* True if the Mini App component is supported. | ||
*/ | ||
export declare const isSupported: Computed<boolean>; | ||
/** | ||
* Creates CSS variables connected with the mini app. | ||
@@ -17,4 +23,6 @@ * | ||
* @throws {TypedError} ERR_ALREADY_CALLED | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare function bindCssVars(getCSSVarName?: GetCssVarNameFn): VoidFunction; | ||
export declare const bindCssVars: (getCSSVarName?: GetCssVarNameFn) => VoidFunction; | ||
/** | ||
@@ -33,4 +41,5 @@ * Closes the Mini App. | ||
* theme palette values. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function mount(): void; | ||
export declare const mount: import('../../toolkit/withIsSupported.js').WithIsSupported<() => void>; | ||
/** | ||
@@ -49,13 +58,19 @@ * Informs the Telegram app that the Mini App is ready to be displayed. | ||
* Updates the background color. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare const setBackgroundColor: import('../../withIsSupported.js').WithIsSupported<(color: RGB) => void>; | ||
export declare const setBackgroundColor: import('../../toolkit/withIsSupported.js').WithIsSupported<(color: BackgroundColor) => void>; | ||
/** | ||
* Updates the bottom bar background color. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare const setBottomBarColor: import('../../withIsSupported.js').WithIsSupported<(color: BottomBarColor) => void>; | ||
export declare const setBottomBarColor: import('../../toolkit/withIsSupported.js').WithIsSupported<(color: BottomBarColor) => void>; | ||
/** | ||
* Updates the header color. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare const setHeaderColor: import('../../withSupports.js').WithSupports<import('../../withIsSupported.js').WithIsSupported<(color: HeaderColor) => void>, { | ||
color: ["web_app_set_header_color", "color"]; | ||
export declare const setHeaderColor: import('../../toolkit/withSupports.js').WithSupports<import('../../toolkit/withIsSupported.js').WithIsSupported<(color: HeaderColor) => void>, { | ||
color: ["web_app_set_header_color", "color", typeof isRGB]; | ||
}>; | ||
@@ -62,0 +77,0 @@ /** |
import { OpenOptions } from './types.js'; | ||
/** | ||
* @returns True if the back button is supported. | ||
* @returns True if the Popup is supported. | ||
*/ | ||
export declare function isSupported(): boolean; | ||
export declare const isSupported: import('@telegram-apps/signals').Computed<boolean>; | ||
/** | ||
@@ -25,3 +25,4 @@ * True if a popup is currently opened. | ||
* @throws {TypedError} ERR_POPUP_INVALID_PARAMS: Invalid text length. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function open(options: OpenOptions): Promise<string | null>; | ||
export declare const open: import('../../toolkit/withIsSupported.js').WithIsSupported<(options: OpenOptions) => Promise<string | null>>; |
@@ -9,5 +9,10 @@ import { CancelablePromise, ExecuteWithOptions } from '@telegram-apps/bridge'; | ||
/** | ||
* @returns True if the QR scanner is supported. | ||
*/ | ||
export declare const isSupported: import('@telegram-apps/signals').Computed<boolean>; | ||
/** | ||
* Closes the scanner. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function close(): void; | ||
export declare const close: import('../../toolkit/withIsSupported.js').WithIsSupported<() => void>; | ||
/** | ||
@@ -18,6 +23,2 @@ * True if the scanner is currently opened. | ||
/** | ||
* @returns True if the QR scanner is supported. | ||
*/ | ||
export declare function isSupported(): boolean; | ||
/** | ||
* Opens the scanner and returns a promise which will be resolved with the QR content if the | ||
@@ -31,3 +32,3 @@ * `capture` function returned true. | ||
*/ | ||
export declare function open(options?: OpenSharedOptions & { | ||
declare function _open(options?: OpenSharedOptions & { | ||
/** | ||
@@ -47,3 +48,3 @@ * Function, which should return true if a scanned QR should be captured. | ||
*/ | ||
export declare function open(options: OpenSharedOptions & { | ||
declare function _open(options: OpenSharedOptions & { | ||
/** | ||
@@ -55,2 +56,6 @@ * Function which will be called if some QR code was scanned. | ||
}): CancelablePromise<void>; | ||
/** | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare const open: import('../../toolkit/withIsSupported.js').WithIsSupported<typeof _open>; | ||
export {}; |
@@ -6,3 +6,3 @@ import { EventListener } from '@telegram-apps/bridge'; | ||
*/ | ||
export declare function isSupported(): boolean; | ||
export declare const isSupported: import('@telegram-apps/signals').Computed<boolean>; | ||
/** | ||
@@ -13,4 +13,5 @@ * Mounts the component. | ||
* if it changed. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function mount(): void; | ||
export declare const mount: import('../../toolkit/withIsSupported.js').WithIsSupported<() => void>; | ||
/** | ||
@@ -20,14 +21,17 @@ * Adds a new main button click listener. | ||
* @returns A function to remove bound listener. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function onClick(fn: EventListener<'secondary_button_pressed'>): VoidFunction; | ||
export declare const onClick: import('../../toolkit/withIsSupported.js').WithIsSupported<(fn: EventListener<'secondary_button_pressed'>) => VoidFunction>; | ||
/** | ||
* Removes the main button click listener. | ||
* @param fn - an event listener. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function offClick(fn: EventListener<'secondary_button_pressed'>): void; | ||
export declare const offClick: import('../../toolkit/withIsSupported.js').WithIsSupported<(fn: EventListener<'secondary_button_pressed'>) => void>; | ||
/** | ||
* Updates the main button state. | ||
* @param updates - state changes to perform. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare function setParams(updates: Partial<State>): void; | ||
export declare const setParams: (updates: Partial<State>) => void; | ||
/** | ||
@@ -34,0 +38,0 @@ * Unmounts the component, removing the listener, saving the component state in the local storage. |
import { EventListener } from '@telegram-apps/bridge'; | ||
/** | ||
* Hides the settings button. | ||
* True if the component is currently mounted. | ||
*/ | ||
export declare function hide(): void; | ||
export declare const isMounted: import('@telegram-apps/signals').Signal<boolean>; | ||
/** | ||
* True if the component is currently visible. | ||
* @returns True if the settings button is supported. | ||
*/ | ||
export declare const isVisible: import('@telegram-apps/signals').Signal<boolean>; | ||
export declare const isSupported: import('@telegram-apps/signals').Computed<boolean>; | ||
/** | ||
* True if the component is currently mounted. | ||
* Hides the Settings Button. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare const isMounted: import('@telegram-apps/signals').Signal<boolean>; | ||
export declare const hide: () => void; | ||
/** | ||
* @returns True if the settings button is supported. | ||
* True if the component is currently visible. | ||
*/ | ||
export declare function isSupported(): boolean; | ||
export declare const isVisible: import('@telegram-apps/signals').Signal<boolean>; | ||
/** | ||
@@ -23,19 +24,23 @@ * Mounts the component. | ||
* if it changed. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function mount(): void; | ||
export declare const mount: import('../../toolkit/withIsSupported.js').WithIsSupported<() => void>; | ||
/** | ||
* Add a new settings button click listener. | ||
* Add a new Settings Button click listener. | ||
* @param fn - event listener. | ||
* @returns A function to remove bound listener. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function onClick(fn: EventListener<'settings_button_pressed'>): VoidFunction; | ||
export declare const onClick: import('../../toolkit/withIsSupported.js').WithIsSupported<(fn: EventListener<'settings_button_pressed'>) => VoidFunction>; | ||
/** | ||
* Removes the settings button click listener. | ||
* Removes the Settings Button click listener. | ||
* @param fn - an event listener. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function offClick(fn: EventListener<'settings_button_pressed'>): void; | ||
export declare const offClick: import('../../toolkit/withIsSupported.js').WithIsSupported<(fn: EventListener<'settings_button_pressed'>) => void>; | ||
/** | ||
* Shows the settings button. | ||
* Shows the Settings Button. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare function show(): void; | ||
export declare const show: () => void; | ||
/** | ||
@@ -42,0 +47,0 @@ * Unmounts the component, removing the listener, saving the component state in the local storage. |
/** | ||
* True if the component is currently mounted. | ||
*/ | ||
export declare const isMounted: import('@telegram-apps/signals').Signal<boolean>; | ||
/** | ||
* @returns True if the Swipe Behavior is supported. | ||
*/ | ||
export declare const isSupported: import('@telegram-apps/signals').Computed<boolean>; | ||
/** | ||
* Disables vertical swipes. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare function disableVertical(): void; | ||
export declare const disableVertical: () => void; | ||
/** | ||
* Enables vertical swipes. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare function enableVertical(): void; | ||
export declare const enableVertical: () => void; | ||
/** | ||
* True if the component is currently mounted. | ||
*/ | ||
export declare const isMounted: import('@telegram-apps/signals').Signal<boolean>; | ||
/** | ||
* True if vertical swipes are enabled. | ||
@@ -18,6 +24,2 @@ */ | ||
/** | ||
* @returns True if the back button is supported. | ||
*/ | ||
export declare function isSupported(): boolean; | ||
/** | ||
* Mounts the component. | ||
@@ -27,4 +29,5 @@ * | ||
* if it changed. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare function mount(): void; | ||
export declare const mount: import('../../toolkit/withIsSupported.js').WithIsSupported<() => void>; | ||
/** | ||
@@ -31,0 +34,0 @@ * Unmounts the component, removing the listener, saving the component state in the local storage. |
@@ -19,3 +19,3 @@ import { GetCssVarNameFn } from './types.js'; | ||
*/ | ||
export declare function bindCssVars(getCSSVarName?: GetCssVarNameFn): VoidFunction; | ||
export declare const bindCssVars: (getCSSVarName?: GetCssVarNameFn) => VoidFunction; | ||
/** | ||
@@ -22,0 +22,0 @@ * Mounts the component. |
@@ -18,5 +18,6 @@ import { GetCSSVarNameFn } from './static.js'; | ||
* @returns Function to stop updating variables. | ||
* @throws TypedError ERR_ALREADY_CALLED | ||
* @throws {TypedError} ERR_ALREADY_CALLED | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export declare function bindCssVars(getCSSVarName?: GetCSSVarNameFn): VoidFunction; | ||
export declare const bindCssVars: (getCSSVarName?: GetCSSVarNameFn) => VoidFunction; | ||
/** | ||
@@ -23,0 +24,0 @@ * A method that expands the Mini App to the maximum available height. To find out if the Mini |
@@ -10,7 +10,9 @@ import { Signal } from '@telegram-apps/signals'; | ||
* @param mountError - signal containing mount error. | ||
* @param isSupported - signal containing the component support flag. | ||
*/ | ||
export declare function createMountFn<T = void>(mount: (options: AsyncOptions) => (T | CancelablePromise<T>), onMounted: (result: T) => void, { isMounting, isMounted, mountError, }: { | ||
export declare function createMountFn<T = void>(mount: (options: AsyncOptions) => (T | CancelablePromise<T>), onMounted: (result: T) => void, { isMounting, isMounted, mountError, isSupported, }: { | ||
isMounted: Signal<boolean>; | ||
isMounting: Signal<boolean>; | ||
mountError: Signal<Error | undefined>; | ||
isSupported?: () => boolean; | ||
}): (options?: AsyncOptions) => CancelablePromise<void>; |
@@ -1,2 +0,2 @@ | ||
import { postEvent as _postEvent, request as _request, PostEventFn, Version, CreatePostEventMode, CancelablePromise, ExecuteWithOptions, CustomMethodParams, CustomMethodName } from '@telegram-apps/bridge'; | ||
import { postEvent as _postEvent, request as _request, PostEventFn, Version, CancelablePromise, ExecuteWithOptions, CustomMethodParams, CustomMethodName } from '@telegram-apps/bridge'; | ||
export interface ConfigureOptions { | ||
@@ -22,3 +22,3 @@ /** | ||
*/ | ||
postEvent?: PostEventFn | CreatePostEventMode; | ||
postEvent?: PostEventFn; | ||
} | ||
@@ -25,0 +25,0 @@ /** |
@@ -30,4 +30,5 @@ import { CancelablePromise, PhoneRequestedStatus, WriteAccessRequestedStatus, ExecuteWithOptions } from '@telegram-apps/bridge'; | ||
* @throws {TypedError} ERR_CUSTOM_METHOD_ERR_RESPONSE | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare const requestContact: import('../../withIsSupported.js').WithIsSupported<(options?: ExecuteWithOptions) => CancelablePromise<RequestedContact>>; | ||
export declare const requestContact: import('../../toolkit/withIsSupported.js').WithIsSupported<(options?: ExecuteWithOptions) => CancelablePromise<RequestedContact>>; | ||
/** | ||
@@ -42,4 +43,5 @@ * Requests current user phone access. Method returns promise, which resolves | ||
* @throws {TypedError} ERR_ALREADY_CALLED | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare const requestPhoneAccess: import('../../withIsSupported.js').WithIsSupported<(options?: ExecuteWithOptions) => Promise<PhoneRequestedStatus>>; | ||
export declare const requestPhoneAccess: import('../../toolkit/withIsSupported.js').WithIsSupported<(options?: ExecuteWithOptions) => Promise<PhoneRequestedStatus>>; | ||
/** | ||
@@ -49,3 +51,4 @@ * Requests write message access to the current user. | ||
* @throws {TypedError} ERR_ALREADY_CALLED | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare const requestWriteAccess: import('../../withIsSupported.js').WithIsSupported<(options?: ExecuteWithOptions) => Promise<WriteAccessRequestedStatus>>; | ||
export declare const requestWriteAccess: import('../../toolkit/withIsSupported.js').WithIsSupported<(options?: ExecuteWithOptions) => Promise<WriteAccessRequestedStatus>>; |
@@ -1,1 +0,1 @@ | ||
export { readTextFromClipboard, sendData, switchInlineQuery, shareStory } from './uncategorized.js'; | ||
export { readTextFromClipboard, sendData, switchInlineQuery, shareStory, type ShareStoryOptions, } from './uncategorized.js'; |
import { CancelablePromise, ExecuteWithOptions, SwitchInlineQueryChatType, ExecuteWithPostEvent } from '@telegram-apps/bridge'; | ||
interface ShareStoryOptions extends ExecuteWithPostEvent { | ||
export interface ShareStoryOptions extends ExecuteWithPostEvent { | ||
/** | ||
@@ -30,4 +30,5 @@ * The caption to be added to the media. | ||
* - Access to the clipboard is not granted. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare const readTextFromClipboard: import('../../withIsSupported.js').WithIsSupported<(options?: ExecuteWithOptions) => CancelablePromise<string | null>>; | ||
export declare const readTextFromClipboard: import('../../toolkit/withIsSupported.js').WithIsSupported<(options?: ExecuteWithOptions) => CancelablePromise<string | null>>; | ||
/** | ||
@@ -48,5 +49,5 @@ * A method used to send data to the bot. | ||
* A method that opens the native story editor. | ||
* @since v7.8 | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare const shareStory: import('../../withIsSupported.js').WithIsSupported<(mediaUrl: string, options?: ShareStoryOptions) => void>; | ||
export declare const shareStory: import('../../toolkit/withIsSupported.js').WithIsSupported<(mediaUrl: string, options?: ShareStoryOptions) => void>; | ||
/** | ||
@@ -61,4 +62,4 @@ * Inserts the bot's username and the specified inline query in the current chat's input field. | ||
* empty list. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export declare const switchInlineQuery: import('../../withIsSupported.js').WithIsSupported<(query: string, chatTypes?: SwitchInlineQueryChatType[]) => void>; | ||
export {}; | ||
export declare const switchInlineQuery: import('../../toolkit/withIsSupported.js').WithIsSupported<(query: string, chatTypes?: SwitchInlineQueryChatType[]) => void>; |
@@ -1,5 +0,5 @@ | ||
this.telegramApps=this.telegramApps||{};this.telegramApps.sdk=function(o){"use strict";let ue=class Wi extends Error{constructor(t,n,r){super(typeof n=="object"?n.message:n||t,{cause:typeof n=="object"?n.cause:r}),this.type=t,Object.setPrototypeOf(this,Wi.prototype)}};function Wt(e){return e.replace(/[A-Z]/g,t=>`_${t.toLowerCase()}`)}function Fi(e){return e.replace(/_[a-z]/g,t=>t[1].toUpperCase())}const Gt="ERR_INVALID_VALUE",zt="ERR_UNEXPECTED_VALUE",Ft="ERR_UNEXPECTED_TYPE",xe="ERR_PARSE";function Yt(e,t){const n={};for(const r in e){const i=e[r];if(!i)continue;let a,s;typeof i=="function"?(a=r,s=i):[a,s]=i;try{const c=s(t(a));c!==void 0&&(n[r]=c)}catch(c){throw new ue(xe,`Parser for "${r}" property failed${a===r?"":`. Source field: "${a}"`}`,c)}}return n}function et(e){let t=e;if(typeof t=="string")try{t=JSON.parse(t)}catch(n){throw new ue(Gt,{cause:n})}if(typeof t!="object"||!t||Array.isArray(t))throw new ue(zt);return t}function N(e,t){return n=>{const r=i=>{if(!(n&&i===void 0))try{return t(i)}catch(a){throw new ue(xe,{message:`"${e}" transformer failed to parse the value`,cause:a})}};return Object.assign(r,{isValid(i){try{return r(i),!0}catch{return!1}}})}}function Ie(e,t){return N(t||"object",n=>{const r=et(n);return Yt(e,i=>r[i])})}function le(e){throw new ue(Ft,`Unexpected value received: ${JSON.stringify(e)}`)}const Yi=N("boolean",e=>{if(typeof e=="boolean")return e;const t=String(e);if(t==="1"||t==="true")return!0;if(t==="0"||t==="false")return!1;le(e)}),L=N("string",e=>{if(typeof e=="string"||typeof e=="number")return e.toString();le(e)}),ke=N("number",e=>{if(typeof e=="number")return e;if(typeof e=="string"){const t=Number(e);if(!Number.isNaN(t))return t}le(e)}),Jt=N("date",e=>e instanceof Date?e:new Date(ke()(e)*1e3));function Kt(e,t){return N(t||"searchParams",n=>{typeof n!="string"&&!(n instanceof URLSearchParams)&&le(n);const r=typeof n=="string"?new URLSearchParams(n):n;return Yt(e,i=>{const a=r.get(i);return a===null?void 0:a})})}function tt(e){for(const t in e)e[t]=[Wt(t),e[t]];return e}const Ji=e=>{const t=ke(),n=ke(!0),r=L(),i=L(!0),a=Yi(!0),s=Ie(tt({addedToAttachmentMenu:a,allowsWriteToPm:a,firstName:r,id:t,isBot:a,isPremium:a,languageCode:i,lastName:i,photoUrl:i,username:i}),"User")(!0);return Kt(tt({authDate:Jt(),canSendAfter:n,chat:Ie(tt({id:t,type:r,title:r,photoUrl:i,username:i}),"Chat")(!0),chatInstance:i,chatType:i,hash:r,queryId:i,receiver:s,startParam:i,user:s}),"initData")(e)};function fe(e){return/^#[\da-f]{6}$/i.test(e)}function Qt(e){return/^#[\da-f]{3}$/i.test(e)}function nt(e){const t=e.replace(/\s/g,"").toLowerCase();if(fe(t))return t;if(Qt(t)){let r="#";for(let i=0;i<3;i+=1)r+=t[1+i].repeat(2);return r}const n=t.match(/^rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)$/)||t.match(/^rgba\((\d{1,3}),(\d{1,3}),(\d{1,3}),\d{1,3}\)$/);if(!n)throw new Error(`Value "${e}" does not satisfy any of known RGB formats.`);return n.slice(1).reduce((r,i)=>{const a=parseInt(i,10).toString(16);return r+(a.length===1?"0":"")+a},"#")}const Ki=N("rgb",e=>nt(L()(e))),Qi=N("themeParams",e=>{const t=Ki(!0);return Object.entries(et(e)).reduce((n,[r,i])=>(n[Fi(r)]=t(i),n),{})});function Zt(e){return JSON.stringify(Object.fromEntries(Object.entries(e).map(([t,n])=>[Wt(t),n])))}function Zi(e){const{initDataRaw:t,startParam:n,showSettings:r,botInline:i}=e,a=new URLSearchParams;return a.set("tgWebAppPlatform",e.platform),a.set("tgWebAppThemeParams",Zt(e.themeParams)),a.set("tgWebAppVersion",e.version),t&&a.set("tgWebAppData",t),n&&a.set("tgWebAppStartParam",n),typeof r=="boolean"&&a.set("tgWebAppShowSettings",r?"1":"0"),typeof i=="boolean"&&a.set("tgWebAppBotInline",i?"1":"0"),a.toString()}function Xi(e,t){return N("array",n=>{let r;if(Array.isArray(n))r=n;else if(typeof n=="string")try{const i=JSON.parse(n);Array.isArray(i)&&(r=i)}catch{}return r||le(n),r.map(e)})}function ot(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function Ve(...e){return e.map(t=>{if(typeof t=="string")return t;if(ot(t))return Ve(Object.entries(t).map(n=>n[1]&&n[0]));if(Array.isArray(t))return Ve(...t)}).filter(Boolean).join(" ")}function xi(...e){return e.reduce((t,n)=>(ot(n)&&Object.entries(n).forEach(([r,i])=>{const a=Ve(t[r],i);a&&(t[r]=a)}),t),{})}let _e=class Gi extends Error{constructor(t,n,r){super(typeof n=="object"?n.message:n||t,{cause:typeof n=="object"?n.cause:r}),this.type=t,Object.setPrototypeOf(this,Gi.prototype)}};function Xt(e){return e.replace(/[A-Z]/g,t=>`_${t.toLowerCase()}`)}function ea(e){return e.replace(/_[a-z]/g,t=>t[1].toUpperCase())}const ta="ERR_INVALID_VALUE",na="ERR_UNEXPECTED_VALUE",oa="ERR_UNEXPECTED_TYPE",xt="ERR_PARSE";function en(e,t){const n={};for(const r in e){const i=e[r];if(!i)continue;let a,s;typeof i=="function"?(a=r,s=i):[a,s]=i;try{const c=s(t(a));c!==void 0&&(n[r]=c)}catch(c){throw new _e(xt,`Parser for "${r}" property failed${a===r?"":`. Source field: "${a}"`}`,c)}}return n}function tn(e){let t=e;if(typeof t=="string")try{t=JSON.parse(t)}catch(n){throw new _e(ta,{cause:n})}if(typeof t!="object"||!t||Array.isArray(t))throw new _e(na);return t}function B(e,t){return n=>{const r=i=>{if(!(n&&i===void 0))try{return t(i)}catch(a){throw new _e(xt,{message:`"${e}" transformer failed to parse the value`,cause:a})}};return Object.assign(r,{isValid(i){try{return r(i),!0}catch{return!1}}})}}function O(e,t){return B(t||"object",n=>{const r=tn(n);return en(e,i=>r[i])})}function de(e){throw new _e(oa,`Unexpected value received: ${JSON.stringify(e)}`)}const Ne=B("boolean",e=>{if(typeof e=="boolean")return e;const t=String(e);if(t==="1"||t==="true")return!0;if(t==="0"||t==="false")return!1;de(e)}),T=B("string",e=>{if(typeof e=="string"||typeof e=="number")return e.toString();de(e)}),he=B("number",e=>{if(typeof e=="number")return e;if(typeof e=="string"){const t=Number(e);if(!Number.isNaN(t))return t}de(e)}),ra=B("date",e=>e instanceof Date?e:new Date(he()(e)*1e3));function nn(e,t){return B(t||"searchParams",n=>{typeof n!="string"&&!(n instanceof URLSearchParams)&&de(n);const r=typeof n=="string"?new URLSearchParams(n):n;return en(e,i=>{const a=r.get(i);return a===null?void 0:a})})}function rt(e){for(const t in e)e[t]=[Xt(t),e[t]];return e}const ia=e=>{const t=he(),n=he(!0),r=T(),i=T(!0),a=Ne(!0),s=O(rt({addedToAttachmentMenu:a,allowsWriteToPm:a,firstName:r,id:t,isBot:a,isPremium:a,languageCode:i,lastName:i,photoUrl:i,username:i}),"User")(!0);return nn(rt({authDate:ra(),canSendAfter:n,chat:O(rt({id:t,type:r,title:r,photoUrl:i,username:i}),"Chat")(!0),chatInstance:i,chatType:i,hash:r,queryId:i,receiver:s,startParam:i,user:s}),"initData")(e)};function aa(e){return/^#[\da-f]{6}$/i.test(e)}function sa(e){return/^#[\da-f]{3}$/i.test(e)}function ca(e){const t=e.replace(/\s/g,"").toLowerCase();if(aa(t))return t;if(sa(t)){let r="#";for(let i=0;i<3;i+=1)r+=t[1+i].repeat(2);return r}const n=t.match(/^rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)$/)||t.match(/^rgba\((\d{1,3}),(\d{1,3}),(\d{1,3}),\d{1,3}\)$/);if(!n)throw new Error(`Value "${e}" does not satisfy any of known RGB formats.`);return n.slice(1).reduce((r,i)=>{const a=parseInt(i,10).toString(16);return r+(a.length===1?"0":"")+a},"#")}const ua=B("rgb",e=>ca(T()(e))),la=B("themeParams",e=>{const t=ua(!0);return Object.entries(tn(e)).reduce((n,[r,i])=>(n[ea(r)]=t(i),n),{})});function on(e){return JSON.stringify(Object.fromEntries(Object.entries(e).map(([t,n])=>[Xt(t),n])))}const fa=e=>{const t=T(),n=T(!0),r=Ne(!0);return nn({botInline:["tgWebAppBotInline",r],initData:["tgWebAppData",ia(!0)],initDataRaw:["tgWebAppData",n],platform:["tgWebAppPlatform",t],showSettings:["tgWebAppShowSettings",r],startParam:["tgWebAppStartParam",n],themeParams:["tgWebAppThemeParams",la()],version:["tgWebAppVersion",t]},"launchParams")(e)};function _a(e){const{initDataRaw:t,startParam:n,showSettings:r,botInline:i}=e,a=new URLSearchParams;return a.set("tgWebAppPlatform",e.platform),a.set("tgWebAppThemeParams",on(e.themeParams)),a.set("tgWebAppVersion",e.version),t&&a.set("tgWebAppData",t),n&&a.set("tgWebAppStartParam",n),typeof r=="boolean"&&a.set("tgWebAppShowSettings",r?"1":"0"),typeof i=="boolean"&&a.set("tgWebAppBotInline",i?"1":"0"),a.toString()}const rn=O({eventType:T(),eventData:e=>e},"miniAppsMessage"),an=B("fn",e=>{if(typeof e=="function")return e;de(e)});function da(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}const ha=O({TelegramWebviewProxy:O({postEvent:an()})()});function sn(e){return ha().isValid(e)}function cn(){try{return window.self!==window.top}catch{return!0}}var pa=Object.defineProperty,ba=(e,t,n)=>t in e?pa(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,un=(e,t,n)=>ba(e,typeof t!="symbol"?t+"":t,n);let f=class zi extends Error{constructor(t,n,r){super(typeof n=="object"?n.message:n||t,{cause:typeof n=="object"?n.cause:r}),this.type=t,Object.setPrototypeOf(this,zi.prototype)}};function Le(e,t,n){return e.addEventListener(t,n),()=>e.removeEventListener(t,n)}function x(...e){const t=e.flat(1);return[t.push.bind(t),()=>{t.forEach(n=>{n()})}]}function ma(e,t){return e instanceof f&&e.type===t}function it(e){return t=>ma(t,e)}const at="ERR_ABORTED",st="ERR_CANCELED",ct="ERR_TIMED_OUT";function ln(e){return new f(at,{cause:e})}const ga=it(ct),wa=it(at),Ea=it(st);function fn(e,t){return e.reject=t.reject,e}class p extends Promise{constructor(t,n){let r,i;typeof t=="function"?(r=t,i=n):i=t;let a,s;super((c,_)=>{i||(i={});const{abortSignal:l}=i;if(l&&l.aborted)return _(ln(l.reason));const[h,C]=x(),$=ce=>(...As)=>(C(),ce(...As)),se=new AbortController,{signal:X}=se;s=$(ce=>{se.abort(ce),_(ce)}),a=$(c),l&&h(Le(l,"abort",()=>{s(ln(l.reason))}));const{timeout:Ut}=i;if(Ut){const ce=setTimeout(()=>{s(new f(ct,`Timeout reached: ${Ut}ms`))},Ut);h(()=>{clearTimeout(ce)})}r&&r(a,s,X)}),un(this,"reject"),this.reject=s}static withFn(t,n){return new p((r,i,a)=>{try{const s=t(a);return s instanceof Promise?s.then(r,i):r(s)}catch(s){i(s)}},n)}static resolve(t){return new p(n=>{n(t)})}static reject(t){return new p((n,r)=>{r(t)})}cancel(){this.reject(new f(st))}catch(t){return this.then(void 0,t)}finally(t){return fn(super.finally(t),this)}then(t,n){return fn(super.then(t,n),this)}}function _n(e,t){return e.resolve=t.resolve,e}class pe extends p{constructor(t,n){let r,i;typeof t=="function"?(r=t,i=n):i=t;let a;super((s,c,_)=>{a=s,r&&r(s,c,_)},i),un(this,"resolve"),this.resolve=a}static withFn(t,n){return new pe((r,i,a)=>p.withFn(t,{abortSignal:a}).then(r,i),n)}static resolve(t){return new pe(n=>{n(t)})}static reject(t){return new pe((n,r)=>{r(t)})}catch(t){return this.then(void 0,t)}finally(t){return _n(super.finally(t),this)}then(t,n){return _n(super.then(t,n),this)}}function ya(e,t){return new p(n=>{setTimeout(n,e)},{abortSignal:t})}function dn(e){return`tapps/${e}`}function A(e,t){sessionStorage.setItem(dn(e),JSON.stringify(t))}function R(e){const t=sessionStorage.getItem(dn(e));try{return t?JSON.parse(t):void 0}catch{}}function ut(e){return e.replace(/[A-Z]/g,t=>`-${t.toLowerCase()}`)}function Sa(e,t){t||(t={});const{textColor:n,bgColor:r,shouldLog:i=!0}=t;function a(s,...c){if(!i||typeof i=="function"&&!i())return;const _="font-weight:bold;padding:0 5px;border-radius:5px";console[s](`%c${Intl.DateTimeFormat("en-GB",{hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3,timeZone:"UTC"}).format(new Date)}%c / %c${e}`,`${_};background-color: lightblue;color:black`,"",`${_};${n?`color:${n};`:""}${r?`background-color:${r}`:""}`,...c)}return[function(...s){a("log",...s)},function(...s){a("error",...s)}]}function je(e,t){document.documentElement.style.setProperty(e,t)}function He(e){document.documentElement.style.removeProperty(e)}function Ca(e,t){t()}function ee(e,t){t||(t={});const n=t.equals||Object.is;let r=[],i=e;const a=l=>{if(!n(i,l)){const h=i;i=l,Ca(_,()=>{[...r].forEach(([C,$])=>{C(l,h),$&&c(C,!0)})})}};function s(l){const h=typeof l!="object"?{once:l}:l;return{once:h.once||!1,signal:h.signal||!1}}const c=(l,h)=>{const C=s(h),$=r.findIndex(([se,X])=>se===l&&X.once===C.once&&X.signal===C.signal);$>=0&&r.splice($,1)},_=Object.assign(function(){return va(_),i},{destroy(){r=[]},set:a,reset(){a(e)},sub(l,h){return r.push([l,s(h)]),()=>c(l,h)},unsub:c,unsubAll(){r=r.filter(l=>l[1].signal)}});return _}const lt=[];function va(e){lt.length&<[lt.length-1].add(e)}const hn=ee(!1),[ft,$a]=Sa("Bridge",{bgColor:"#9147ff",textColor:"white",shouldLog:hn}),Ta={clipboard_text_received:O({req_id:T(),data:e=>e===null?e:T(!0)(e)},"clipboard_text_received"),custom_method_invoked:O({req_id:T(),result:e=>e,error:T(!0)},"custom_method_invoked"),popup_closed:B("popup_closed",e=>e?O({button_id:t=>t==null?void 0:T()(t)})()(e):{}),viewport_changed:O({height:he(),width:e=>e==null?window.innerWidth:he()(e),is_state_stable:Ne(),is_expanded:Ne()},"viewport_changed")};function Aa(e){const t=window,[,n]=x(Le(t,"resize",()=>{e(["viewport_changed",{width:window.innerWidth,height:window.innerHeight,is_state_stable:!0,is_expanded:!0}])}),Le(t,"message",r=>{if(r.source!==t.parent)return;let i;try{i=rn()(r.data)}catch{return}const{eventType:a,eventData:s}=i,c=Ta[a];try{const _=c?c()(s):s;ft("Event received:",_?{eventType:a,eventData:_}:{eventType:a}),e([a,_])}catch(_){$a([`An error occurred processing the "${a}" event from the Telegram application.`,"Please, file an issue here:","https://github.com/Telegram-Mini-Apps/telegram-apps/issues/new/choose"].join(` | ||
`),i,_)}}));return n}const _t=ee(),pn=ee();function bn(){return pn()||pn.set(Aa(_t.set)),_t}const dt=ee({});function mn(e){let t=dt()[e];return t||(t=ee(void 0,{equals(){return!1}}),bn().sub(n=>{n&&n[0]===e&&t.set(n[1])}),dt.set({...dt(),[e]:t})),t}function S(e,t,n){return mn(e).sub(t,n)}const gn="ERR_METHOD_UNSUPPORTED",wn="ERR_RETRIEVE_LP_FAILED",En="ERR_METHOD_PARAMETER_UNSUPPORTED",yn="ERR_UNKNOWN_ENV",Sn="ERR_INVOKE_CUSTOM_METHOD_RESPONSE",Cn=ee("https://web.telegram.org");function qe(e,t){ft("Posting event:",t?{eventType:e,eventData:t}:{eventType:e});const n=window;if(sn(n)){n.TelegramWebviewProxy.postEvent(e,JSON.stringify(t));return}const r=JSON.stringify({eventType:e,eventData:t});if(cn())return n.parent.postMessage(r,Cn());const{external:i}=n;if(O({notify:an()})().isValid(i)){i.notify(r);return}throw new f(yn)}function Ue(e,t,n){n||(n={});const{capture:r}=n,[i,a]=x();return new p(s=>{(Array.isArray(t)?t:[t]).forEach(c=>{i(S(c,_=>{(!r||(Array.isArray(t)?r({event:c,payload:_}):r(_)))&&s(_)}))}),(n.postEvent||qe)(e,n.params)},n).finally(a)}function ht(e){return fa()(e)}function vn(e){return ht(e.replace(/^[^?#]*[?#]/,"").replace(/[?#]/g,"&"))}function Ra(){return vn(window.location.href)}function Pa(){const e=performance.getEntriesByType("navigation")[0];if(!e)throw new Error("Unable to get first navigation entry.");return vn(e.name)}const Ba="launchParams";function Oa(){return ht(R(Ba)||"")}function $n(e){A("launchParams",_a(e))}function Tn(e){return e instanceof Error?e.message+(e.cause?` | ||
${Tn(e.cause)}`:""):JSON.stringify(e)}function K(){const e=[];for(const t of[Ra,Pa,Oa])try{const n=t();return $n(n),n}catch(n){e.push(n)}throw new f(wn,["Unable to retrieve launch parameters from any known source. Perhaps, you have opened your app outside Telegram?","📖 Refer to docs for more information:","https://docs.telegram-mini-apps.com/packages/telegram-apps-sdk/environment","Collected errors:",...e.map(t=>`— ${Tn(t)}`)].join(` | ||
`))}function Da(e){if(e==="simple")try{return K(),!0}catch{return!1}return p.withFn(async()=>{if(sn(window))return!0;try{return await Ue("web_app_request_theme","theme_changed",{timeout:100}),!0}catch{return!1}},e)}function We(e,t){window.dispatchEvent(new MessageEvent("message",{data:JSON.stringify({eventType:e,eventData:t}),source:window.parent}))}function Ma(e,t){if(typeof t=="string")try{const{eventType:n}=rn()(t);n==="web_app_request_theme"&&We("theme_changed",{theme_params:JSON.parse(on(e))}),n==="web_app_request_viewport"&&We("viewport_changed",{width:window.innerWidth,height:window.innerHeight,is_state_stable:!0,is_expanded:!0})}catch{}}function Ia(e){var t;const n=typeof e=="string"?ht(e):e;$n(n);const r=(t=window.TelegramWebviewProxy)==null?void 0:t.postEvent;window.TelegramWebviewProxy={postEvent(i,a){Ma(n.themeParams,JSON.stringify({eventType:i,eventData:a})),r==null||r(i,a)}},ft("Environment was mocked by the mockTelegramEnv function")}function An(){[["TelegramGameProxy_receiveEvent"],["TelegramGameProxy","receiveEvent"],["Telegram","WebView","receiveEvent"]].forEach(e=>{let t=window;e.forEach((n,r,i)=>{if(r===i.length-1){t[n]=We;return}n in t||(t[n]={}),t=t[n]})})}function Rn(){["TelegramGameProxy_receiveEvent","TelegramGameProxy","Telegram"].forEach(e=>{delete window[e]})}function W(e,t,n){mn(e).unsub(t,n)}function ka(e,t){return bn().sub(e,t)}function Va(e,t){_t.unsub(e,t)}function Pn(e){return({req_id:t})=>t===e}function Bn(e){return e.split(".").map(Number)}function On(e,t){const n=Bn(e),r=Bn(t),i=Math.max(n.length,r.length);for(let a=0;a<i;a+=1){const s=n[a]||0,c=r[a]||0;if(s!==c)return s>c?1:-1}return 0}function w(e,t){return On(e,t)<=0}function b(e,t,n){if(typeof n=="string"){if(e==="web_app_open_link"){if(t==="try_instant_view")return w("6.4",n);if(t==="try_browser")return w("7.6",n)}if(e==="web_app_set_header_color"&&t==="color")return w("6.9",n);if(e==="web_app_close"&&t==="return_back")return w("7.6",n);if(e==="web_app_setup_main_button"&&t==="has_shine_effect")return w("7.10",n)}switch(e){case"web_app_open_tg_link":case"web_app_open_invoice":case"web_app_setup_back_button":case"web_app_set_background_color":case"web_app_set_header_color":case"web_app_trigger_haptic_feedback":return w("6.1",t);case"web_app_open_popup":return w("6.2",t);case"web_app_close_scan_qr_popup":case"web_app_open_scan_qr_popup":case"web_app_read_text_from_clipboard":return w("6.4",t);case"web_app_switch_inline_query":return w("6.7",t);case"web_app_invoke_custom_method":case"web_app_request_write_access":case"web_app_request_phone":return w("6.9",t);case"web_app_setup_settings_button":return w("6.10",t);case"web_app_biometry_get_info":case"web_app_biometry_open_settings":case"web_app_biometry_request_access":case"web_app_biometry_request_auth":case"web_app_biometry_update_token":return w("7.2",t);case"web_app_setup_swipe_behavior":return w("7.7",t);case"web_app_setup_secondary_button":case"web_app_set_bottom_bar_color":return w("7.10",t);default:return["iframe_ready","iframe_will_reload","web_app_close","web_app_data_send","web_app_expand","web_app_open_link","web_app_ready","web_app_request_theme","web_app_request_viewport","web_app_setup_main_button","web_app_setup_closing_behavior"].includes(e)}}function Dn(e,t){t||(t="strict");const n=typeof t=="function"?t:r=>{const{method:i,version:a}=r;let s,c;if("param"in r?(s=`Parameter "${r.param}" of "${i}" method is unsupported in Mini Apps version ${a}`,c=En):(s=`Method "${i}" is unsupported in Mini Apps version ${a}`,c=gn),t==="strict")throw new f(c,s);return console.warn(s)};return(r,i)=>b(r,e)?da(i)&&r==="web_app_set_header_color"&&"color"in i&&!b(r,"color",e)?n({version:e,method:r,param:"color"}):qe(r,i):n({version:e,method:r})}function Mn(e,t,n,r){return Ue("web_app_invoke_custom_method","custom_method_invoked",{...r||{},params:{method:e,params:t,req_id:n},capture:Pn(n)}).then(({result:i,error:a})=>{if(a)throw new f(Sn,a);return i})}function Na(){return performance.getEntriesByType("navigation")[0]}function D(){const e=Na();return!!e&&e.type==="reload"}function In(e,t){return e.startsWith(t)?e:`${t}${e}`}function La(e){return new URL(typeof e=="string"?e:[e.pathname||"",In(e.search||"","?"),In(e.hash||"","#")].join(""),"http://a")}let te;function ja(e,t){te&&te.set(e,t)||t()}function Ha(e){if(te)return e();te=new Map;try{e()}finally{te.forEach(t=>t()),te=void 0}}function u(e,t){t||(t={});const n=t.equals||Object.is;let r=[],i=e;const a=l=>{if(!n(i,l)){const h=i;i=l,ja(_,()=>{[...r].forEach(([C,$])=>{C(l,h),$&&c(C,!0)})})}};function s(l){const h=typeof l!="object"?{once:l}:l;return{once:h.once||!1,signal:h.signal||!1}}const c=(l,h)=>{const C=s(h),$=r.findIndex(([se,X])=>se===l&&X.once===C.once&&X.signal===C.signal);$>=0&&r.splice($,1)},_=Object.assign(function(){return qa(_),i},{destroy(){r=[]},set:a,reset(){a(e)},sub(l,h){return r.push([l,s(h)]),()=>c(l,h)},unsub:c,unsubAll(){r=r.filter(l=>l[1].signal)}});return _}const be=[];function qa(e){be.length&&be[be.length-1].add(e)}function E(e,t){let n=new Set;const r=u(a(),t);function i(){r.set(a())}function a(){n.forEach(_=>_.unsub(i,{signal:!0}));const s=new Set;let c;be.push(s);try{c=e()}finally{be.pop()}return s.forEach(_=>{_.sub(i,{signal:!0})}),n=s,c}return Object.assign(function(){return r()},{destroy:r.destroy,sub:r.sub,unsub:r.unsub,unsubAll:r.unsubAll})}const kn=u((()=>{let e=0;return()=>(e+=1).toString()})()),pt=u(qe),m=u("0.0");function Ua(e){e||(e={});const{postEvent:t}=e,n=e.version||K().version;m.set(n),pt.set(typeof t=="function"?t:Dn(n,t))}function Vn(){return kn()()}function me(e,t,n){return Mn(e,t,Vn(),{...n||{},postEvent:d})}const M=(e,t,n)=>(n||(n={}),n.postEvent||(n.postEvent=d),Ue(e,t,n)),d=(e,t)=>pt()(e,t);function P(e,t){t(),e.sub(t)}const Nn="web_app_setup_back_button",Ln="back_button_pressed",jn="backButton";function Hn(){G.set(!1)}function qn(){return b(Nn,m())}const G=u(!1),ge=u(!1);function Un(){ge()||(G.set(D()&&R(jn)||!1),P(G,Wn),ge.set(!0))}function Wn(){const e=G();d(Nn,{is_visible:e}),A(jn,e)}function Gn(e){return S(Ln,e)}function zn(e){W(Ln,e)}function Fn(){G.set(!0)}function Yn(){G.unsub(Wn),ge.set(!1)}const Wa=Object.freeze(Object.defineProperty({__proto__:null,hide:Hn,isMounted:ge,isSupported:qn,isVisible:G,mount:Un,offClick:zn,onClick:Gn,show:Fn,unmount:Yn},Symbol.toStringTag,{value:"Module"})),ne="ERR_POPUP_INVALID_PARAMS",bt="ERR_INVALID_HOSTNAME",Jn="ERR_INVALID_SLUG",Kn="ERR_DATA_INVALID_SIZE",Qn="ERR_ACCESS_DENIED",v="ERR_ALREADY_CALLED",mt="ERR_NOT_AVAILABLE";function Zn(e,t,{isMounting:n,isMounted:r,mountError:i}){return function(s){if(r())return p.resolve();if(n())throw new f(v);return n.set(!0),p.withFn(c=>e({abortSignal:c}),s).then(c=>[!0,c],c=>[!1,c]).then(c=>{Ha(()=>{if(n.set(!1),r.set(!0),c[0])t(c[1]);else{const _=c[1];throw i.set(_),_}})})}}const I=u(),we=u(!1),Ee=u(!1),gt=u(!1),wt=u(!1),Et=u(void 0);function k(e,t,n){return Object.assign(e,{isSupported(){return b(t,m())&&(!n||n())}})}function yt(e){return e.available?{available:!0,tokenSaved:e.token_saved,deviceId:e.device_id,accessRequested:e.access_requested,type:e.type,accessGranted:e.access_granted}:{available:!1}}const Xn="web_app_biometry_get_info",xn=k(e=>M(Xn,"biometry_info_received",e).then(yt),Xn),eo="web_app_biometry_request_auth",Ga="web_app_biometry_request_access",za="web_app_biometry_open_settings",Fa="web_app_biometry_update_token",St="biometry_info_received",to="biometry";function no(e){if(we())return p.reject(new f(v));const t=I();return!t||!t.available?p.reject(new f(mt)):(we.set(!0),e||(e={}),M(eo,"biometry_auth_requested",{...e,params:{reason:(e.reason||"").trim()}}).then(n=>{const{token:r}=n;return typeof r=="string"&&I.set({...t,token:r}),n}).finally(()=>{we.set(!1)}))}function Ct(){return b(eo,m())}function oo(){d(za)}function ro(e){return Ee()?p.reject(new f(v)):(Ee.set(!0),e||(e={}),M(Ga,St,{...e,params:{reason:e.reason||""}}).then(yt).then(t=>{if(!t.available)throw new f(mt);return I.set(t),t.accessGranted}).finally(()=>{Ee.set(!1)}))}const io=Zn(e=>{if(!Ct())return{available:!1};const t=D()&&R(to);return t||xn(e)},e=>{S(St,ao),P(I,so),I.set(e)},{isMounted:gt,mountError:Et,isMounting:wt}),ao=e=>{I.set(yt(e))};function so(){const e=I();e&&A(to,e)}function co(){W(St,ao),I.unsub(so)}function uo(e){return e||(e={}),M(Fa,"biometry_token_updated",{...e,params:{token:e.token||"",reason:e.reason}}).then(t=>t.status)}const Ya=Object.freeze(Object.defineProperty({__proto__:null,authenticate:no,isAuthenticating:we,isMounted:gt,isMounting:wt,isRequestingAccess:Ee,isSupported:Ct,mount:io,mountError:Et,openSettings:oo,requestAccess:ro,state:I,unmount:co,updateToken:uo},Symbol.toStringTag,{value:"Module"})),lo="closingConfirmation";function fo(){z.set(!1)}const ye=u(!1),z=u(!1);function _o(){z.set(!0)}function ho(){ye()||(z.set(D()&&R(lo)||!1),P(z,po),ye.set(!0))}function po(){const e=z();d("web_app_setup_closing_behavior",{need_confirmation:e}),A(lo,e)}function bo(){z.unsub(po),ye.set(!1)}const Ja=Object.freeze(Object.defineProperty({__proto__:null,disableConfirmation:fo,enableConfirmation:_o,isConfirmationEnabled:z,isMounted:ye,mount:ho,unmount:bo},Symbol.toStringTag,{value:"Module"})),Ka="web_app_invoke_custom_method";function mo(e,t){const n=Array.isArray(e)?e:[e];return n.length?me("deleteStorageValues",{keys:n},t).then():p.resolve()}function go(e,t){const n=Array.isArray(e)?e:[e];return n.length?me("getStorageValues",{keys:n},t).then(r=>{const i=Ie(Object.fromEntries(n.map(a=>[a,L()])))()(r);return Array.isArray(e)?i:i[e]}):p.resolve(typeof e=="string"?"":{})}function wo(e){return me("getStorageKeys",{},e).then(Xi(L())())}function Eo(){return b(Ka,m())}function yo(e,t,n){return me("saveStorageValue",{key:e,value:t},n).then()}const Qa=Object.freeze(Object.defineProperty({__proto__:null,deleteItem:mo,getItem:go,getKeys:wo,isSupported:Eo,setItem:yo},Symbol.toStringTag,{value:"Module"})),Ge="web_app_trigger_haptic_feedback";function So(e){d(Ge,{type:"impact",impact_style:e})}function Co(){return b(Ge,m())}function vo(e){d(Ge,{type:"notification",notification_type:e})}function $o(){d(Ge,{type:"selection_change"})}const Za=Object.freeze(Object.defineProperty({__proto__:null,impactOccurred:So,isSupported:Co,notificationOccurred:vo,selectionChanged:$o},Symbol.toStringTag,{value:"Module"})),ze=u(void 0);function V(e){return E(()=>{const t=ze();return t?t[e]:void 0})}const vt=V("authDate"),$t=V("canSendAfter"),To=E(()=>{const e=vt(),t=$t();return t&&e?new Date(e.getTime()+t*1e3):void 0}),Ao=V("chat"),Ro=V("chatType"),Po=V("chatInstance"),Bo=V("hash"),Oo=V("queryId"),Tt=u(),Do=V("receiver");function Mo(){const e=K();ze.set(e.initData),Tt.set(e.initDataRaw)}const Io=V("startParam"),ko=V("user"),Xa=Object.freeze(Object.defineProperty({__proto__:null,authDate:vt,canSendAfter:$t,canSendAfterDate:To,chat:Ao,chatInstance:Po,chatType:Ro,hash:Bo,queryId:Oo,raw:Tt,receiver:Do,restore:Mo,startParam:Io,state:ze,user:ko},Symbol.toStringTag,{value:"Module"}));function xa(e){return Ji()(e)}const Vo="web_app_open_invoice",Se=u(!1);function No(){return b(Vo,m())}async function Lo(e,t,n){if(Se())throw new f(v);let r;if(t==="url"){const{hostname:i,pathname:a}=new URL(e,window.location.href);if(i!=="t.me")throw new f(bt);const s=a.match(/^\/(\$|invoice\/)([A-Za-z0-9\-_=]+)$/);if(!s)throw new f(Jn);[,,r]=s}else r=e,n=t;return Se.set(!0),M(Vo,"invoice_closed",{...n,params:{slug:r},capture:i=>r===i.slug}).then(i=>i.status).finally(()=>{Se.set(!1)})}const es=Object.freeze(Object.defineProperty({__proto__:null,isOpened:Se,isSupported:No,open:Lo},Symbol.toStringTag,{value:"Module"}));function At(e){const t=nt(e);return Math.sqrt([.299,.587,.114].reduce((n,r,i)=>{const a=parseInt(t.slice(1+i*2,1+(i+1)*2),16);return n+a*a*r},0))<120}const Ce=u(!1),ve=u(!1),j=u({});function g(e){return E(()=>j()[e])}const jo=g("accentTextColor"),Fe=g("bgColor"),Ye=g("buttonColor"),Rt=g("buttonTextColor"),Pt=g("bottomBarBgColor"),Ho=g("destructiveTextColor"),Bt=g("headerBgColor"),qo=g("hintColor"),Uo=E(()=>{const{bgColor:e}=j();return!e||At(e)}),Wo=g("linkColor"),$e=g("secondaryBgColor"),Go=g("sectionBgColor"),zo=g("sectionHeaderTextColor"),Fo=g("sectionSeparatorColor"),Yo=g("subtitleTextColor"),Jo=g("textColor");function Q(e){return E(()=>re()[e])}const oe=u({hasShineEffect:!1,isEnabled:!0,isLoaderVisible:!1,isVisible:!1,text:"Continue"}),re=E(()=>{const e=oe();return{...e,backgroundColor:e.backgroundColor||Ye()||"#2481cc",textColor:e.textColor||Rt()||"#ffffff"}}),Te=u(!1),Ko=Q("backgroundColor"),Qo=Q("hasShineEffect"),Zo=Q("isEnabled"),Xo=Q("isLoaderVisible"),xo=Q("isVisible"),er=Q("text"),tr=Q("textColor"),ts="web_app_setup_main_button",nr="main_button_pressed",or="mainButton";function rr(){if(!Te()){const e=D()&&R(or);e&&oe.set(e),oe.sub(sr),P(re,cr),Te.set(!0)}}function ir(e){return S(nr,e)}function ar(e){W(nr,e)}function sr(e){A(or,e)}function cr(){const e=re();e.text&&d(ts,{color:e.backgroundColor,has_shine_effect:e.hasShineEffect,is_active:e.isEnabled,is_progress_visible:e.isLoaderVisible,is_visible:e.isVisible,text:e.text,text_color:e.textColor})}function ur(e){oe.set({...oe(),...Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0))})}function lr(){oe.unsub(sr),re.unsub(cr),Te.set(!1)}const ns=Object.freeze(Object.defineProperty({__proto__:null,backgroundColor:Ko,hasShineEffect:Qo,isEnabled:Zo,isLoaderVisible:Xo,isMounted:Te,isVisible:xo,mount:rr,offClick:ar,onClick:ir,setParams:ur,state:re,text:er,textColor:tr,unmount:lr},Symbol.toStringTag,{value:"Module"}));function os(e,t){return Object.assign(e,{supports(n){const r=t[n];return b(r[0],r[1],m())}})}function fr(e){return Qi()(e)}const _r="themeParams",dr="theme_changed";function hr(e){if(ve())throw new f(v);e||(e=r=>`--tg-theme-${ut(r)}`);function t(r){Object.entries(j()).forEach(([i,a])=>{a&&r(i,a)})}function n(){t((r,i)=>{je(e(r),i)})}return n(),j.sub(n),ve.set(!0),()=>{t(He),j.unsub(n),ve.set(!1)}}function Ot(){Ce()||(S(dr,pr),j.set(D()&&R(_r)||K().themeParams),Ce.set(!0))}const pr=e=>{const t=fr(e.theme_params);j.set(t),A(_r,t)};function br(){W(dr,pr),Ce.set(!1)}function mr(e){return E(()=>{const t=e();return fe(t)?t:t==="bg_color"?Fe():$e()})}const H=u("bg_color"),Dt=mr(H),q=u("bottom_bar_bg_color"),Je=E(()=>{const e=q();return fe(e)?e:e==="bottom_bar_bg_color"?Pt()||$e():e==="secondary_bg_color"?$e():Fe()}),U=u("bg_color"),Mt=mr(U),Ae=u(!1),Re=u(!1),gr=E(()=>{const e=Dt();return e?At(e):!1}),It=E(()=>({backgroundColor:H(),bottomBarColor:q(),headerColor:U()})),wr="web_app_set_background_color",Er="web_app_set_bottom_bar_color",kt="web_app_set_header_color",yr="miniApp";function Sr(e){if(Re())throw new f(v);const[t,n]=x();function r(i,a){function s(){je(i,a()||null)}s(),t(a.sub(s),He.bind(null,i))}return e||(e=i=>`--tg-${ut(i)}`),r(e("bgColor"),Dt),r(e("bottomBarColor"),Je),r(e("headerColor"),Mt),t(()=>{Re.set(!1)}),Re.set(!0),n}function Cr(e){d("web_app_close",{return_back:e})}function vr(){if(!Ae()){const e=D()&&R(yr);Ot(),H.set(e?e.backgroundColor:"bg_color"),q.set(e?e.bottomBarColor:"bottom_bar_bg_color"),U.set(e?e.headerColor:Bt()||"bg_color"),P(H,$r),P(q,Tr),P(U,Ar),Ae.set(!0)}}function $r(){const e=H();Vt(),d(wr,{color:e})}function Tr(){Vt(),d(Er,{color:q()})}function Ar(){const e=U();Vt(),d(kt,fe(e)?{color:e}:{color_key:e})}function Rr(){d("web_app_ready")}function Vt(){A(yr,It())}const Pr=k(e=>{H.set(e)},wr),Br=k(e=>{q.set(e)},Er),Or=os(k(e=>{U.set(e)},kt),{color:[kt,"color"]});function Dr(){H.unsub($r),q.unsub(Tr),U.unsub(Ar),Ae.set(!1)}const rs=Object.freeze(Object.defineProperty({__proto__:null,backgroundColor:H,backgroundColorRGB:Dt,bindCssVars:Sr,bottomBarColor:q,bottomBarColorRGB:Je,close:Cr,headerColor:U,headerColorRGB:Mt,isCssVarsBound:Re,isDark:gr,isMounted:Ae,mount:vr,ready:Rr,setBackgroundColor:Pr,setBottomBarColor:Br,setHeaderColor:Or,state:It,unmount:Dr},Symbol.toStringTag,{value:"Module"}));function is(e){const t=e.message.trim(),n=(e.title||"").trim(),r=e.buttons||[];if(n.length>64)throw new f(ne,`Invalid title: ${n}`);if(!t||t.length>256)throw new f(ne,`Invalid message: ${t}`);if(r.length>3)throw new f(ne,`Invalid buttons count: ${r.length}`);return{title:n,message:t,buttons:r.length?r.map((i,a)=>{const s=i.id||"";if(s.length>64)throw new f(ne,`Button with index ${a} has invalid id: ${s}`);if(!i.type||i.type==="default"||i.type==="destructive"){const c=i.text.trim();if(!c||c.length>64)throw new f(ne,`Button with index ${a} has invalid text: ${c}`);return{type:i.type,text:c,id:s}}return{type:i.type,id:s}}):[{type:"close",id:""}]}}const Mr="web_app_open_popup";function Ir(){return b(Mr,m())}const Pe=u(!1);async function kr(e){if(Pe())throw new f(v);Pe.set(!0);try{const{button_id:t=null}=await M(Mr,"popup_closed",{...e,params:is(e)});return t}finally{Pe.set(!1)}}const as=Object.freeze(Object.defineProperty({__proto__:null,isOpened:Pe,isSupported:Ir,open:kr},Symbol.toStringTag,{value:"Module"})),ss="web_app_close_scan_qr_popup",Vr="web_app_open_scan_qr_popup",cs="scan_qr_popup_closed",us="qr_text_received";function Ke(){Z.set(!1),d(ss)}const Z=u(!1);function Nr(){return b(Vr,m())}function Lr(e){return p.withFn(t=>{if(Z())throw new f(v);Z.set(!0),e||(e={});const{onCaptured:n,text:r,capture:i}=e,[,a]=x(Z.sub(()=>{s.resolve()}),S(cs,()=>{Z.set(!1)}),S(us,c=>{n?n(c.data):(!i||i(c.data))&&(s.resolve(c.data),Ke())})),s=new pe({abortSignal:t}).catch(Ke).finally(a);return(e.postEvent||d)(Vr,{text:r}),s},e)}const ls=Object.freeze(Object.defineProperty({__proto__:null,close:Ke,isOpened:Z,isSupported:Nr,open:Lr},Symbol.toStringTag,{value:"Module"}));function F(e){return E(()=>ae()[e])}const ie=u({hasShineEffect:!1,isEnabled:!0,isLoaderVisible:!1,isVisible:!1,position:"left",text:"Cancel"}),ae=E(()=>{const e=ie();return{...e,backgroundColor:e.backgroundColor||Je()||"#000000",textColor:e.textColor||Ye()||"#2481cc"}}),Be=u(!1),jr=F("backgroundColor"),Hr=F("hasShineEffect"),qr=F("isEnabled"),Ur=F("isLoaderVisible"),Wr=F("isVisible"),Gr=F("position"),zr=F("text"),Fr=F("textColor"),Yr="web_app_setup_secondary_button",Jr="secondary_button_pressed",Kr="secondaryButton";function Qr(){return b(Yr,m())}function Zr(){if(!Be()){const e=D()&&R(Kr);e&&ie.set(e),ie.sub(ei),P(ae,ti),Be.set(!0)}}function Xr(e){return S(Jr,e)}function xr(e){W(Jr,e)}function ei(e){A(Kr,e)}function ti(){const e=ae();e.text&&d(Yr,{color:e.backgroundColor,has_shine_effect:e.hasShineEffect,is_active:e.isEnabled,is_progress_visible:e.isLoaderVisible,is_visible:e.isVisible,position:e.position,text:e.text,text_color:e.textColor})}function ni(e){ie.set({...ie(),...Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0))})}function oi(){ie.unsub(ei),ae.unsub(ti),Be.set(!1)}const fs=Object.freeze(Object.defineProperty({__proto__:null,backgroundColor:jr,hasShineEffect:Hr,isEnabled:qr,isLoaderVisible:Ur,isMounted:Be,isSupported:Qr,isVisible:Wr,mount:Zr,offClick:xr,onClick:Xr,position:Gr,setParams:ni,state:ae,text:zr,textColor:Fr,unmount:oi},Symbol.toStringTag,{value:"Module"})),ri="web_app_setup_settings_button",ii="settings_button_pressed",ai="settingsButton";function si(){Y.set(!1)}const Y=u(!1),Oe=u(!1);function ci(){return b(ri,m())}function ui(){Oe()||(Y.set(D()&&R(ai)||!1),P(Y,li),Oe.set(!0))}function li(){const e=Y();d(ri,{is_visible:e}),A(ai,e)}function fi(e){return S(ii,e)}function _i(e){W(ii,e)}function di(){Y.set(!0)}function hi(){Y.unsub(li),Oe.set(!1)}const _s=Object.freeze(Object.defineProperty({__proto__:null,hide:si,isMounted:Oe,isSupported:ci,isVisible:Y,mount:ui,offClick:_i,onClick:fi,show:di,unmount:hi},Symbol.toStringTag,{value:"Module"})),pi="web_app_setup_swipe_behavior",bi="swipeBehavior";function mi(){J.set(!1)}function gi(){J.set(!0)}const De=u(!1),J=u(!1);function wi(){return b(pi,m())}function Ei(){De()||(J.set(D()&&R(bi)||!1),P(J,yi),De.set(!0))}function yi(){const e=J();d(pi,{allow_vertical_swipe:e}),A(bi,e)}function Si(){J.unsub(yi),De.set(!1)}const ds=Object.freeze(Object.defineProperty({__proto__:null,disableVertical:mi,enableVertical:gi,isMounted:De,isSupported:wi,isVerticalEnabled:J,mount:Ei,unmount:Si},Symbol.toStringTag,{value:"Module"})),hs=Object.freeze(Object.defineProperty({__proto__:null,accentTextColor:jo,backgroundColor:Fe,bindCssVars:hr,bottomBarBgColor:Pt,buttonColor:Ye,buttonTextColor:Rt,destructiveTextColor:Ho,headerBackgroundColor:Bt,hintColor:qo,isCssVarsBound:ve,isDark:Uo,isMounted:Ce,linkColor:Wo,mount:Ot,secondaryBackgroundColor:$e,sectionBackgroundColor:Go,sectionHeaderTextColor:zo,sectionSeparatorColor:Fo,state:j,subtitleTextColor:Yo,textColor:Jo,unmount:br},Symbol.toStringTag,{value:"Module"})),y=u({height:0,width:0,isExpanded:!1,stableHeight:0}),Nt=u(!1),Me=u(!1),Lt=u(!1),jt=u(void 0);function Qe(e){return E(()=>y()[e])}const Ci=Qe("height"),vi=Qe("isExpanded"),$i=E(()=>{const e=y();return e.height===e.stableHeight}),Ti=Qe("stableHeight"),Ai=Qe("width");function ps(e){return M("web_app_request_viewport","viewport_changed",e).then(t=>({height:t.height,width:t.width,isExpanded:t.is_expanded,isStable:t.is_state_stable}))}function Ri(e){if(Me())throw new f(v);e||(e=r=>`--tg-viewport-${ut(r)}`);const t=["height","width","stableHeight"];function n(){t.forEach(r=>{je(e(r),`${y()[r]}px`)})}return n(),y.sub(n),Me.set(!0),()=>{t.forEach(He),y.unsub(n),Me.set(!1)}}function Pi(){d("web_app_expand")}function Bi(e){return{isExpanded:e.isExpanded,height:Ht(e.height),width:Ht(e.width),stableHeight:Ht(e.stableHeight)}}const Oi=Zn(e=>{const t=D()&&R("viewport");if(t)return t;if(["macos","tdesktop","unigram","webk","weba","web"].includes(K().platform)){const n=window;return{isExpanded:!0,height:n.innerHeight,width:n.innerWidth,stableHeight:n.innerHeight}}return e.timeout||(e.timeout=1e3),ps(e).then(n=>({height:n.height,isExpanded:n.isExpanded,stableHeight:n.isStable?n.height:y().stableHeight,width:n.width}))},e=>{S("viewport_changed",Di),P(y,Mi),y.set(Bi(e))},{isMounted:Nt,isMounting:Lt,mountError:jt}),Di=e=>{y.set(Bi({height:e.height,width:e.width,isExpanded:e.is_expanded,stableHeight:e.is_state_stable?e.height:y().stableHeight}))};function Mi(){A("viewport",y())}function Ht(e){return Math.max(e,0)}function Ii(){W("viewport_changed",Di),y.unsub(Mi)}const bs=Object.freeze(Object.defineProperty({__proto__:null,bindCssVars:Ri,expand:Pi,height:Ci,isCssVarsBound:Me,isExpanded:vi,isMounted:Nt,isMounting:Lt,isStable:$i,mount:Oi,mountError:jt,stableHeight:Ti,state:y,unmount:Ii,width:Ai},Symbol.toStringTag,{value:"Module"})),ki="web_app_open_tg_link";function ms(e,t){t||(t={}),d("web_app_open_link",{url:La(e).toString(),try_browser:t.tryBrowser,try_instant_view:t.tryInstantView})}function Vi(e){const{hostname:t,pathname:n,search:r}=new URL(e,"https://t.me");if(t!=="t.me")throw new f(bt);if(!b(ki,m())){window.location.href=e;return}d(ki,{path_full:n+r})}function gs(e,t){Vi("https://t.me/share/url?"+new URLSearchParams({url:e,text:t||""}).toString().replace(/\+/g,"%20"))}const qt="web_app_request_phone",Ni="web_app_request_write_access",Ze=u(!1),Xe=u(!1);function Li(e){return e||(e={}),me("getRequestedContact",{},{...e,timeout:e.timeout||5e3}).then(Kt({contact:Ie({userId:["user_id",ke()],phoneNumber:["phone_number",L()],firstName:["first_name",L()],lastName:["last_name",L(!0)]})(),authDate:["auth_date",Jt()],hash:L()})())}const ws=k(e=>new p(async(t,n,r)=>{const i={postEvent:(e||{}).postEvent,abortSignal:r};try{return t(await Li(i))}catch{}if(await ji(i)!=="sent")throw new f(Qn);let s=50;for(;!r.aborted;){try{return t(await Li(i))}catch{}await ya(s),s+=50}},e),qt),ji=k(e=>{if(Ze())throw new f(v);return Ze.set(!0),M(qt,"phone_requested",e).then(t=>t.status).finally(()=>{Ze.set(!1)})},qt),Es=k(e=>{if(Xe())throw new f(v);return Xe.set(!0),M(Ni,"write_access_requested",e).then(t=>t.status).finally(()=>{Xe.set(!1)})},Ni),Hi="web_app_read_text_from_clipboard",qi="web_app_switch_inline_query",Ui="web_app_share_to_story",ys=k(e=>{const t=Vn();return M(Hi,"clipboard_text_received",{...e,params:{req_id:t},capture:Pn(t)}).then(({data:n=null})=>n)},Hi);function Ss(e){const{size:t}=new Blob([e]);if(!t||t>4096)throw new f(Kn);d("web_app_data_send",{data:e})}const Cs=k((e,t)=>{t||(t={}),(t.postEvent||d)(Ui,{text:t.text,media_url:e,widget_link:t.widgetLink})},Ui),vs=k((e,t)=>{d(qi,{query:e,chat_types:t||[]})},qi,()=>!!K().botInline);function $s(){return typeof window>"u"}function Ts(e){Ua(e),An();const[t,n]=x(S("reload_iframe",()=>{d("iframe_will_reload"),window.location.reload()}),Rn),{acceptCustomStyles:r=!0}=e||{};if(r){const i=document.createElement("style");i.id="telegram-custom-styles",document.head.appendChild(i),t(S("set_custom_style",a=>{i.innerHTML=a}),()=>{document.head.removeChild(i)})}return d("iframe_ready",{reload_supported:!0}),n}return o.$createRequestId=kn,o.$debug=hn,o.$postEvent=pt,o.$targetOrigin=Cn,o.$version=m,o.CancelablePromise=p,o.ERR_ABORTED=at,o.ERR_ACCESS_DENIED=Qn,o.ERR_ALREADY_CALLED=v,o.ERR_CANCELED=st,o.ERR_CUSTOM_METHOD_ERR_RESPONSE=Sn,o.ERR_DATA_INVALID_SIZE=Kn,o.ERR_INVALID_HOSTNAME=bt,o.ERR_INVALID_SLUG=Jn,o.ERR_INVALID_VALUE=Gt,o.ERR_METHOD_PARAMETER_UNSUPPORTED=En,o.ERR_METHOD_UNSUPPORTED=gn,o.ERR_NOT_AVAILABLE=mt,o.ERR_PARSE=xe,o.ERR_POPUP_INVALID_PARAMS=ne,o.ERR_RETRIEVE_LP_FAILED=wn,o.ERR_TIMED_OUT=ct,o.ERR_UNEXPECTED_TYPE=Ft,o.ERR_UNEXPECTED_VALUE=zt,o.ERR_UNKNOWN_ENV=yn,o.TypedError=f,o.addEventListener=Le,o.authenticateBiometry=no,o.backButton=Wa,o.bindMiniAppCssVars=Sr,o.bindThemeParamsCssVars=hr,o.bindViewportCssVars=Ri,o.biometry=Ya,o.biometryMountError=Et,o.biometryState=I,o.classNames=Ve,o.closeMiniApp=Cr,o.closeQrScanner=Ke,o.closingBehavior=Ja,o.cloudStorage=Qa,o.compareVersions=On,o.createPostEvent=Dn,o.defineEventHandlers=An,o.deleteCloudStorageItem=mo,o.deleteCssVar=He,o.disableClosingConfirmation=fo,o.disableVerticalSwipes=mi,o.emitMiniAppsEvent=We,o.enableClosingConfirmation=_o,o.enableVerticalSwipes=gi,o.expandViewport=Pi,o.getCloudStorageItem=go,o.getCloudStorageKeys=wo,o.hapticFeedback=Za,o.hapticFeedbackImpactOccurred=So,o.hapticFeedbackNotificationOccurred=vo,o.hapticFeedbackSelectionChanged=$o,o.hideBackButton=Hn,o.hideSettingsButton=si,o.init=Ts,o.initData=Xa,o.initDataAuthDate=vt,o.initDataCanSendAfter=$t,o.initDataCanSendAfterDate=To,o.initDataChat=Ao,o.initDataChatInstance=Po,o.initDataChatType=Ro,o.initDataHash=Bo,o.initDataQueryId=Oo,o.initDataRaw=Tt,o.initDataReceiver=Do,o.initDataStartParam=Io,o.initDataState=ze,o.initDataUser=ko,o.invoice=es,o.invokeCustomMethod=Mn,o.isAbortError=wa,o.isAuthenticatingBiometry=we,o.isBackButtonMounted=ge,o.isBackButtonSupported=qn,o.isBackButtonVisible=G,o.isBiometryMounted=gt,o.isBiometryMounting=wt,o.isBiometrySupported=Ct,o.isCanceledError=Ea,o.isClosingBehaviorMounted=ye,o.isClosingConfirmationEnabled=z,o.isCloudStorageSupported=Eo,o.isColorDark=At,o.isHapticFeedbackSupported=Co,o.isIframe=cn,o.isInvoiceOpened=Se,o.isInvoiceSupported=No,o.isMainButtonEnabled=Zo,o.isMainButtonLoaderVisible=Xo,o.isMainButtonMounted=Te,o.isMainButtonVisible=xo,o.isMiniAppCssVarsBound=Re,o.isMiniAppDark=gr,o.isMiniAppMounted=Ae,o.isPopupOpened=Pe,o.isPopupSupported=Ir,o.isQrScannerOpened=Z,o.isQrScannerSupported=Nr,o.isRGB=fe,o.isRGBShort=Qt,o.isRecord=ot,o.isRequestingBiometryAccess=Ee,o.isRequestingPhoneAccess=Ze,o.isRequestingWriteAccess=Xe,o.isSSR=$s,o.isSecondaryButtonEnabled=qr,o.isSecondaryButtonLoaderVisible=Ur,o.isSecondaryButtonMounted=Be,o.isSecondaryButtonSupported=Qr,o.isSecondaryButtonVisible=Wr,o.isSettingsButtonMounted=Oe,o.isSettingsButtonSupported=ci,o.isSettingsButtonVisible=Y,o.isSwipeBehaviorMounted=De,o.isSwipeBehaviorSupported=wi,o.isTMA=Da,o.isThemeParamsCssVarsBound=ve,o.isThemeParamsDark=Uo,o.isThemeParamsMounted=Ce,o.isTimeoutError=ga,o.isVerticalSwipesEnabled=J,o.isViewportCssVarsBound=Me,o.isViewportExpanded=vi,o.isViewportMounted=Nt,o.isViewportMounting=Lt,o.isViewportStable=$i,o.mainButton=ns,o.mainButtonBackgroundColor=Ko,o.mainButtonHasShineEffect=Qo,o.mainButtonState=re,o.mainButtonText=er,o.mainButtonTextColor=tr,o.mergeClassNames=xi,o.miniApp=rs,o.miniAppBackgroundColor=H,o.miniAppBottomBarColor=q,o.miniAppBottomBarColorRGB=Je,o.miniAppHeaderColor=U,o.miniAppHeaderColorRGB=Mt,o.miniAppReady=Rr,o.miniAppState=It,o.mockTelegramEnv=Ia,o.mountBackButton=Un,o.mountBiometry=io,o.mountClosingBehavior=ho,o.mountMainButton=rr,o.mountMiniApp=vr,o.mountSecondaryButton=Zr,o.mountSettingsButton=ui,o.mountSwipeBehavior=Ei,o.mountThemeParams=Ot,o.mountViewport=Oi,o.off=W,o.offBackButtonClick=zn,o.offMainButtonClick=ar,o.offSecondaryButtonClick=xr,o.offSettingsButtonClick=_i,o.on=S,o.onBackButtonClick=Gn,o.onMainButtonClick=ir,o.onSecondaryButtonClick=Xr,o.onSettingsButtonClick=fi,o.openBiometrySettings=oo,o.openInvoice=Lo,o.openLink=ms,o.openPopup=kr,o.openQrScanner=Lr,o.openTelegramLink=Vi,o.parseInitData=xa,o.parseThemeParams=fr,o.popup=as,o.postEvent=qe,o.qrScanner=ls,o.readTextFromClipboard=ys,o.removeEventHandlers=Rn,o.request=Ue,o.requestBiometry=xn,o.requestBiometryAccess=ro,o.requestContact=ws,o.requestPhoneAccess=ji,o.requestWriteAccess=Es,o.restoreInitData=Mo,o.retrieveLaunchParams=K,o.secondaryButton=fs,o.secondaryButtonBackgroundColor=jr,o.secondaryButtonHasShineEffect=Hr,o.secondaryButtonPosition=Gr,o.secondaryButtonState=ae,o.secondaryButtonText=zr,o.secondaryButtonTextColor=Fr,o.sendData=Ss,o.serializeLaunchParams=Zi,o.serializeThemeParams=Zt,o.setCloudStorageItem=yo,o.setCssVar=je,o.setMainButtonParams=ur,o.setMiniAppBackgroundColor=Pr,o.setMiniAppBottomBarColor=Br,o.setMiniAppHeaderColor=Or,o.setSecondaryButtonParams=ni,o.settingsButton=_s,o.shareStory=Cs,o.shareURL=gs,o.showBackButton=Fn,o.showSettingsButton=di,o.subscribe=ka,o.supports=b,o.swipeBehavior=ds,o.switchInlineQuery=vs,o.themeParams=hs,o.themeParamsAccentTextColor=jo,o.themeParamsBackgroundColor=Fe,o.themeParamsBottomBarBgColor=Pt,o.themeParamsButtonColor=Ye,o.themeParamsButtonTextColor=Rt,o.themeParamsDestructiveTextColor=Ho,o.themeParamsHeaderBackgroundColor=Bt,o.themeParamsHintColor=qo,o.themeParamsLinkColor=Wo,o.themeParamsSecondaryBackgroundColor=$e,o.themeParamsSectionBackgroundColor=Go,o.themeParamsSectionHeaderTextColor=zo,o.themeParamsSectionSeparatorColor=Fo,o.themeParamsState=j,o.themeParamsSubtitleTextColor=Yo,o.themeParamsTextColor=Jo,o.toRGB=nt,o.toRecord=et,o.unmountBackButton=Yn,o.unmountBiometry=co,o.unmountClosingBehavior=bo,o.unmountMainButton=lr,o.unmountMiniApp=Dr,o.unmountSecondaryButton=oi,o.unmountSettingsButton=hi,o.unmountSwipeBehavior=Si,o.unmountThemeParams=br,o.unmountViewport=Ii,o.unsubscribe=Va,o.updateBiometryToken=uo,o.viewport=bs,o.viewportHeight=Ci,o.viewportMountError=jt,o.viewportStableHeight=Ti,o.viewportState=y,o.viewportWidth=Ai,Object.defineProperty(o,Symbol.toStringTag,{value:"Module"}),o}({}); | ||
this.telegramApps=this.telegramApps||{};this.telegramApps.sdk=function(o){"use strict";let Se=class ui extends Error{constructor(t,n,r){super(typeof n=="object"?n.message:n||t,{cause:typeof n=="object"?n.cause:r}),this.type=t,Object.setPrototypeOf(this,ui.prototype)}};function mn(e){return e.replace(/[A-Z]/g,t=>`_${t.toLowerCase()}`)}function di(e){return e.replace(/_[a-z]/g,t=>t[1].toUpperCase())}const gn="ERR_INVALID_VALUE",En="ERR_UNEXPECTED_VALUE",wn="ERR_UNEXPECTED_TYPE",ct="ERR_PARSE";function Sn(e,t){const n={};for(const r in e){const s=e[r];if(!s)continue;let i,a;typeof s=="function"?(i=r,a=s):[i,a]=s;try{const c=a(t(i));c!==void 0&&(n[r]=c)}catch(c){throw new Se(ct,`Parser for "${r}" property failed${i===r?"":`. Source field: "${i}"`}`,c)}}return n}function ut(e){let t=e;if(typeof t=="string")try{t=JSON.parse(t)}catch(n){throw new Se(gn,{cause:n})}if(typeof t!="object"||!t||Array.isArray(t))throw new Se(En);return t}function N(e,t){return n=>{const r=s=>{if(!(n&&s===void 0))try{return t(s)}catch(i){throw new Se(ct,{message:`"${e}" transformer failed to parse the value`,cause:i})}};return Object.assign(r,{isValid(s){try{return r(s),!0}catch{return!1}}})}}function Ve(e,t){return N(t||"object",n=>{const r=ut(n);return Sn(e,s=>r[s])})}function ye(e){throw new Se(wn,`Unexpected value received: ${JSON.stringify(e)}`)}const fi=N("boolean",e=>{if(typeof e=="boolean")return e;const t=String(e);if(t==="1"||t==="true")return!0;if(t==="0"||t==="false")return!1;ye(e)}),V=N("string",e=>{if(typeof e=="string"||typeof e=="number")return e.toString();ye(e)}),Le=N("number",e=>{if(typeof e=="number")return e;if(typeof e=="string"){const t=Number(e);if(!Number.isNaN(t))return t}ye(e)}),yn=N("date",e=>e instanceof Date?e:new Date(Le()(e)*1e3));function Cn(e,t){return N(t||"searchParams",n=>{typeof n!="string"&&!(n instanceof URLSearchParams)&&ye(n);const r=typeof n=="string"?new URLSearchParams(n):n;return Sn(e,s=>{const i=r.get(s);return i===null?void 0:i})})}function lt(e){for(const t in e)e[t]=[mn(t),e[t]];return e}const pi=e=>{const t=Le(),n=Le(!0),r=V(),s=V(!0),i=fi(!0),a=Ve(lt({addedToAttachmentMenu:i,allowsWriteToPm:i,firstName:r,id:t,isBot:i,isPremium:i,languageCode:s,lastName:s,photoUrl:s,username:s}),"User")(!0);return Cn(lt({authDate:yn(),canSendAfter:n,chat:Ve(lt({id:t,type:r,title:r,photoUrl:s,username:s}),"Chat")(!0),chatInstance:s,chatType:s,hash:r,queryId:s,receiver:a,startParam:s,user:a}),"initData")(e)};function ne(e){return/^#[\da-f]{6}$/i.test(e)}function Pn(e){return/^#[\da-f]{3}$/i.test(e)}function _t(e){const t=e.replace(/\s/g,"").toLowerCase();if(ne(t))return t;if(Pn(t)){let r="#";for(let s=0;s<3;s+=1)r+=t[1+s].repeat(2);return r}const n=t.match(/^rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)$/)||t.match(/^rgba\((\d{1,3}),(\d{1,3}),(\d{1,3}),\d{1,3}\)$/);if(!n)throw new Error(`Value "${e}" does not satisfy any of known RGB formats.`);return n.slice(1).reduce((r,s)=>{const i=parseInt(s,10).toString(16);return r+(i.length===1?"0":"")+i},"#")}const hi=N("rgb",e=>_t(V()(e))),bi=N("themeParams",e=>{const t=hi(!0);return Object.entries(ut(e)).reduce((n,[r,s])=>(n[di(r)]=t(s),n),{})});function vn(e){return JSON.stringify(Object.fromEntries(Object.entries(e).map(([t,n])=>[mn(t),n])))}function mi(e){const{initDataRaw:t,startParam:n,showSettings:r,botInline:s}=e,i=new URLSearchParams;return i.set("tgWebAppPlatform",e.platform),i.set("tgWebAppThemeParams",vn(e.themeParams)),i.set("tgWebAppVersion",e.version),t&&i.set("tgWebAppData",t),n&&i.set("tgWebAppStartParam",n),typeof r=="boolean"&&i.set("tgWebAppShowSettings",r?"1":"0"),typeof s=="boolean"&&i.set("tgWebAppBotInline",s?"1":"0"),i.toString()}function gi(e,t){return N("array",n=>{let r;if(Array.isArray(n))r=n;else if(typeof n=="string")try{const s=JSON.parse(n);Array.isArray(s)&&(r=s)}catch{}return r||ye(n),r.map(e)})}function dt(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function Ue(...e){return e.map(t=>{if(typeof t=="string")return t;if(dt(t))return Ue(Object.entries(t).map(n=>n[1]&&n[0]));if(Array.isArray(t))return Ue(...t)}).filter(Boolean).join(" ")}function Ei(...e){return e.reduce((t,n)=>(dt(n)&&Object.entries(n).forEach(([r,s])=>{const i=Ue(t[r],s);i&&(t[r]=i)}),t),{})}let Ce=class li extends Error{constructor(t,n,r){super(typeof n=="object"?n.message:n||t,{cause:typeof n=="object"?n.cause:r}),this.type=t,Object.setPrototypeOf(this,li.prototype)}};function An(e){return e.replace(/[A-Z]/g,t=>`_${t.toLowerCase()}`)}function wi(e){return e.replace(/_[a-z]/g,t=>t[1].toUpperCase())}const Si="ERR_INVALID_VALUE",yi="ERR_UNEXPECTED_VALUE",Ci="ERR_UNEXPECTED_TYPE",Rn="ERR_PARSE";function Tn(e,t){const n={};for(const r in e){const s=e[r];if(!s)continue;let i,a;typeof s=="function"?(i=r,a=s):[i,a]=s;try{const c=a(t(i));c!==void 0&&(n[r]=c)}catch(c){throw new Ce(Rn,`Parser for "${r}" property failed${i===r?"":`. Source field: "${i}"`}`,c)}}return n}function $n(e){let t=e;if(typeof t=="string")try{t=JSON.parse(t)}catch(n){throw new Ce(Si,{cause:n})}if(typeof t!="object"||!t||Array.isArray(t))throw new Ce(yi);return t}function $(e,t){return n=>{const r=s=>{if(!(n&&s===void 0))try{return t(s)}catch(i){throw new Ce(Rn,{message:`"${e}" transformer failed to parse the value`,cause:i})}};return Object.assign(r,{isValid(s){try{return r(s),!0}catch{return!1}}})}}function B(e,t){return $(t||"object",n=>{const r=$n(n);return Tn(e,s=>r[s])})}function Pe(e){throw new Ce(Ci,`Unexpected value received: ${JSON.stringify(e)}`)}const je=$("boolean",e=>{if(typeof e=="boolean")return e;const t=String(e);if(t==="1"||t==="true")return!0;if(t==="0"||t==="false")return!1;Pe(e)}),v=$("string",e=>{if(typeof e=="string"||typeof e=="number")return e.toString();Pe(e)}),ve=$("number",e=>{if(typeof e=="number")return e;if(typeof e=="string"){const t=Number(e);if(!Number.isNaN(t))return t}Pe(e)}),Pi=$("date",e=>e instanceof Date?e:new Date(ve()(e)*1e3));function Bn(e,t){return $(t||"searchParams",n=>{typeof n!="string"&&!(n instanceof URLSearchParams)&&Pe(n);const r=typeof n=="string"?new URLSearchParams(n):n;return Tn(e,s=>{const i=r.get(s);return i===null?void 0:i})})}function ft(e){for(const t in e)e[t]=[An(t),e[t]];return e}const vi=e=>{const t=ve(),n=ve(!0),r=v(),s=v(!0),i=je(!0),a=B(ft({addedToAttachmentMenu:i,allowsWriteToPm:i,firstName:r,id:t,isBot:i,isPremium:i,languageCode:s,lastName:s,photoUrl:s,username:s}),"User")(!0);return Bn(ft({authDate:Pi(),canSendAfter:n,chat:B(ft({id:t,type:r,title:r,photoUrl:s,username:s}),"Chat")(!0),chatInstance:s,chatType:s,hash:r,queryId:s,receiver:a,startParam:s,user:a}),"initData")(e)};function Ai(e){return/^#[\da-f]{6}$/i.test(e)}function Ri(e){return/^#[\da-f]{3}$/i.test(e)}function Ti(e){const t=e.replace(/\s/g,"").toLowerCase();if(Ai(t))return t;if(Ri(t)){let r="#";for(let s=0;s<3;s+=1)r+=t[1+s].repeat(2);return r}const n=t.match(/^rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)$/)||t.match(/^rgba\((\d{1,3}),(\d{1,3}),(\d{1,3}),\d{1,3}\)$/);if(!n)throw new Error(`Value "${e}" does not satisfy any of known RGB formats.`);return n.slice(1).reduce((r,s)=>{const i=parseInt(s,10).toString(16);return r+(i.length===1?"0":"")+i},"#")}const $i=$("rgb",e=>Ti(v()(e))),Bi=$("themeParams",e=>{const t=$i(!0);return Object.entries($n(e)).reduce((n,[r,s])=>(n[wi(r)]=t(s),n),{})});function On(e){return JSON.stringify(Object.fromEntries(Object.entries(e).map(([t,n])=>[An(t),n])))}const Oi=e=>{const t=v(),n=v(!0),r=je(!0);return Bn({botInline:["tgWebAppBotInline",r],initData:["tgWebAppData",vi(!0)],initDataRaw:["tgWebAppData",n],platform:["tgWebAppPlatform",t],showSettings:["tgWebAppShowSettings",r],startParam:["tgWebAppStartParam",n],themeParams:["tgWebAppThemeParams",Bi()],version:["tgWebAppVersion",t]},"launchParams")(e)};function Ii(e){const{initDataRaw:t,startParam:n,showSettings:r,botInline:s}=e,i=new URLSearchParams;return i.set("tgWebAppPlatform",e.platform),i.set("tgWebAppThemeParams",On(e.themeParams)),i.set("tgWebAppVersion",e.version),t&&i.set("tgWebAppData",t),n&&i.set("tgWebAppStartParam",n),typeof r=="boolean"&&i.set("tgWebAppShowSettings",r?"1":"0"),typeof s=="boolean"&&i.set("tgWebAppBotInline",s?"1":"0"),i.toString()}const In=B({eventType:v(),eventData:e=>e},"miniAppsMessage"),Dn=$("fn",e=>{if(typeof e=="function")return e;Pe(e)});function Di(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}const Mi=B({TelegramWebviewProxy:B({postEvent:Dn()})()});function Mn(e){return Mi().isValid(e)}function kn(){try{return window.self!==window.top}catch{return!0}}var ki=Object.defineProperty,Ni=(e,t,n)=>t in e?ki(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,Nn=(e,t,n)=>Ni(e,typeof t!="symbol"?t+"":t,n);let _=class _i extends Error{constructor(t,n,r){super(typeof n=="object"?n.message:n||t,{cause:typeof n=="object"?n.cause:r}),this.type=t,Object.setPrototypeOf(this,_i.prototype)}};function We(e,t,n){return e.addEventListener(t,n),()=>e.removeEventListener(t,n)}function oe(...e){const t=e.flat(1);return[t.push.bind(t),()=>{t.forEach(n=>{n()})}]}function Vi(e,t){return e instanceof _&&e.type===t}function pt(e){return t=>Vi(t,e)}const ht="ERR_ABORTED",bt="ERR_CANCELED",mt="ERR_TIMED_OUT";function Vn(e){return new _(ht,{cause:e})}const Li=pt(mt),Ui=pt(ht),ji=pt(bt);function Ln(e,t){return e.reject=t.reject,e}class h extends Promise{constructor(t,n){let r,s;typeof t=="function"?(r=t,s=n):s=t;let i,a;super((c,d)=>{s||(s={});const{abortSignal:l}=s;if(l&&l.aborted)return d(Vn(l.reason));const[p,y]=oe(),P=we=>(...xa)=>(y(),we(...xa)),Ee=new AbortController,{signal:te}=Ee;a=P(we=>{Ee.abort(we),d(we)}),i=P(c),l&&p(We(l,"abort",()=>{a(Vn(l.reason))}));const{timeout:bn}=s;if(bn){const we=setTimeout(()=>{a(new _(mt,`Timeout reached: ${bn}ms`))},bn);p(()=>{clearTimeout(we)})}r&&r(i,a,te)}),Nn(this,"reject"),this.reject=a}static withFn(t,n){return new h((r,s,i)=>{try{const a=t(i);return a instanceof Promise?a.then(r,s):r(a)}catch(a){s(a)}},n)}static resolve(t){return new h(n=>{n(t)})}static reject(t){return new h((n,r)=>{r(t)})}cancel(){this.reject(new _(bt))}catch(t){return this.then(void 0,t)}finally(t){return Ln(super.finally(t),this)}then(t,n){return Ln(super.then(t,n),this)}}function Un(e,t){return e.resolve=t.resolve,e}class Ae extends h{constructor(t,n){let r,s;typeof t=="function"?(r=t,s=n):s=t;let i;super((a,c,d)=>{i=a,r&&r(a,c,d)},s),Nn(this,"resolve"),this.resolve=i}static withFn(t,n){return new Ae((r,s,i)=>h.withFn(t,{abortSignal:i}).then(r,s),n)}static resolve(t){return new Ae(n=>{n(t)})}static reject(t){return new Ae((n,r)=>{r(t)})}catch(t){return this.then(void 0,t)}finally(t){return Un(super.finally(t),this)}then(t,n){return Un(super.then(t,n),this)}}function Wi(e,t){return new h(n=>{setTimeout(n,e)},{abortSignal:t})}function jn(e){return`tapps/${e}`}function A(e,t){sessionStorage.setItem(jn(e),JSON.stringify(t))}function R(e){const t=sessionStorage.getItem(jn(e));try{return t?JSON.parse(t):void 0}catch{}}function gt(e){return e.replace(/[A-Z]/g,t=>`-${t.toLowerCase()}`)}function qi(e,t){t||(t={});const{textColor:n,bgColor:r,shouldLog:s=!0}=t;function i(a,...c){if(!s||typeof s=="function"&&!s())return;const d="font-weight:bold;padding:0 5px;border-radius:5px";console[a](`%c${Intl.DateTimeFormat("en-GB",{hour:"2-digit",minute:"2-digit",second:"2-digit",fractionalSecondDigits:3,timeZone:"UTC"}).format(new Date)}%c / %c${e}`,`${d};background-color: lightblue;color:black`,"",`${d};${n?`color:${n};`:""}${r?`background-color:${r}`:""}`,...c)}return[function(...a){i("log",...a)},function(...a){i("error",...a)}]}function qe(e,t){document.documentElement.style.setProperty(e,t)}function He(e){document.documentElement.style.removeProperty(e)}function Hi(e,t){t()}function re(e,t){t||(t={});const n=t.equals||Object.is;let r=[],s=e;const i=l=>{if(!n(s,l)){const p=s;s=l,Hi(d,()=>{[...r].forEach(([y,P])=>{y(l,p),P&&c(y,!0)})})}};function a(l){const p=typeof l!="object"?{once:l}:l;return{once:p.once||!1,signal:p.signal||!1}}const c=(l,p)=>{const y=a(p),P=r.findIndex(([Ee,te])=>Ee===l&&te.once===y.once&&te.signal===y.signal);P>=0&&r.splice(P,1)},d=Object.assign(function(){return Gi(d),s},{destroy(){r=[]},set:i,reset(){i(e)},sub(l,p){return r.push([l,a(p)]),()=>c(l,p)},unsub:c,unsubAll(){r=r.filter(l=>l[1].signal)}});return d}const Et=[];function Gi(e){Et.length&&Et[Et.length-1].add(e)}const Wn=re(!1),[wt,zi]=qi("Bridge",{bgColor:"#9147ff",textColor:"white",shouldLog:Wn}),Yi={clipboard_text_received:B({req_id:v(),data:e=>e===null?e:v(!0)(e)},"clipboard_text_received"),custom_method_invoked:B({req_id:v(),result:e=>e,error:v(!0)},"custom_method_invoked"),popup_closed:$("popup_closed",e=>e?B({button_id:t=>t==null?void 0:v()(t)})()(e):{}),viewport_changed:B({height:ve(),width:e=>e==null?window.innerWidth:ve()(e),is_state_stable:je(),is_expanded:je()},"viewport_changed")};function Fi(e){const t=window,[,n]=oe(We(t,"resize",()=>{e(["viewport_changed",{width:window.innerWidth,height:window.innerHeight,is_state_stable:!0,is_expanded:!0}])}),We(t,"message",r=>{if(r.source!==t.parent)return;let s;try{s=In()(r.data)}catch{return}const{eventType:i,eventData:a}=s,c=Yi[i];try{const d=c?c()(a):a;wt("Event received:",d?{eventType:i,eventData:d}:{eventType:i}),e([i,d])}catch(d){zi([`An error occurred processing the "${i}" event from the Telegram application.`,"Please, file an issue here:","https://github.com/Telegram-Mini-Apps/telegram-apps/issues/new/choose"].join(` | ||
`),s,d)}}));return n}const St=re(),qn=re();function Hn(){return qn()||qn.set(Fi(St.set)),St}const yt=re({});function Gn(e){let t=yt()[e];return t||(t=re(void 0,{equals(){return!1}}),Hn().sub(n=>{n&&n[0]===e&&t.set(n[1])}),yt.set({...yt(),[e]:t})),t}function S(e,t,n){return Gn(e).sub(t,n)}const zn="ERR_METHOD_UNSUPPORTED",Yn="ERR_RETRIEVE_LP_FAILED",Fn="ERR_METHOD_PARAMETER_UNSUPPORTED",Kn="ERR_UNKNOWN_ENV",Qn="ERR_INVOKE_CUSTOM_METHOD_RESPONSE",Jn=re("https://web.telegram.org");function Ge(e,t){wt("Posting event:",t?{eventType:e,eventData:t}:{eventType:e});const n=window;if(Mn(n)){n.TelegramWebviewProxy.postEvent(e,JSON.stringify(t));return}const r=JSON.stringify({eventType:e,eventData:t});if(kn())return n.parent.postMessage(r,Jn());const{external:s}=n;if(B({notify:Dn()})().isValid(s)){s.notify(r);return}throw new _(Kn)}function ze(e,t,n){n||(n={});const{capture:r}=n,[s,i]=oe();return new h(a=>{(Array.isArray(t)?t:[t]).forEach(c=>{s(S(c,d=>{(!r||(Array.isArray(t)?r({event:c,payload:d}):r(d)))&&a(d)}))}),(n.postEvent||Ge)(e,n.params)},n).finally(i)}function Ct(e){return Oi()(e)}function Xn(e){return Ct(e.replace(/^[^?#]*[?#]/,"").replace(/[?#]/g,"&"))}function Ki(){return Xn(window.location.href)}function Qi(){const e=performance.getEntriesByType("navigation")[0];if(!e)throw new Error("Unable to get first navigation entry.");return Xn(e.name)}const Ji="launchParams";function Xi(){return Ct(R(Ji)||"")}function Zn(e){A("launchParams",Ii(e))}function xn(e){return e instanceof Error?e.message+(e.cause?` | ||
${xn(e.cause)}`:""):JSON.stringify(e)}function Z(){const e=[];for(const t of[Ki,Qi,Xi])try{const n=t();return Zn(n),n}catch(n){e.push(n)}throw new _(Yn,["Unable to retrieve launch parameters from any known source. Perhaps, you have opened your app outside Telegram?","📖 Refer to docs for more information:","https://docs.telegram-mini-apps.com/packages/telegram-apps-sdk/environment","Collected errors:",...e.map(t=>`— ${xn(t)}`)].join(` | ||
`))}function Zi(e){if(e==="simple")try{return Z(),!0}catch{return!1}return h.withFn(async()=>{if(Mn(window))return!0;try{return await ze("web_app_request_theme","theme_changed",{timeout:100}),!0}catch{return!1}},e)}function Ye(e,t){window.dispatchEvent(new MessageEvent("message",{data:JSON.stringify({eventType:e,eventData:t}),source:window.parent}))}function xi(e,t){if(typeof t=="string")try{const{eventType:n}=In()(t);n==="web_app_request_theme"&&Ye("theme_changed",{theme_params:JSON.parse(On(e))}),n==="web_app_request_viewport"&&Ye("viewport_changed",{width:window.innerWidth,height:window.innerHeight,is_state_stable:!0,is_expanded:!0})}catch{}}function ea(e){var t;const n=typeof e=="string"?Ct(e):e;Zn(n);const r=(t=window.TelegramWebviewProxy)==null?void 0:t.postEvent;window.TelegramWebviewProxy={postEvent(s,i){xi(n.themeParams,JSON.stringify({eventType:s,eventData:i})),r==null||r(s,i)}},wt("Environment was mocked by the mockTelegramEnv function")}function eo(){[["TelegramGameProxy_receiveEvent"],["TelegramGameProxy","receiveEvent"],["Telegram","WebView","receiveEvent"]].forEach(e=>{let t=window;e.forEach((n,r,s)=>{if(r===s.length-1){t[n]=Ye;return}n in t||(t[n]={}),t=t[n]})})}function to(){["TelegramGameProxy_receiveEvent","TelegramGameProxy","Telegram"].forEach(e=>{delete window[e]})}function G(e,t,n){Gn(e).unsub(t,n)}function ta(e,t){return Hn().sub(e,t)}function na(e,t){St.unsub(e,t)}function no(e){return({req_id:t})=>t===e}function oo(e){return e.split(".").map(Number)}function ro(e,t){const n=oo(e),r=oo(t),s=Math.max(n.length,r.length);for(let i=0;i<s;i+=1){const a=n[i]||0,c=r[i]||0;if(a!==c)return a>c?1:-1}return 0}function m(e,t){return ro(e,t)<=0}function L(e,t,n){if(typeof n=="string"){if(e==="web_app_open_link"){if(t==="try_instant_view")return m("6.4",n);if(t==="try_browser")return m("7.6",n)}if(e==="web_app_set_header_color"&&t==="color")return m("6.9",n);if(e==="web_app_close"&&t==="return_back")return m("7.6",n);if(e==="web_app_setup_main_button"&&t==="has_shine_effect")return m("7.10",n)}switch(e){case"web_app_open_tg_link":case"web_app_open_invoice":case"web_app_setup_back_button":case"web_app_set_background_color":case"web_app_set_header_color":case"web_app_trigger_haptic_feedback":return m("6.1",t);case"web_app_open_popup":return m("6.2",t);case"web_app_close_scan_qr_popup":case"web_app_open_scan_qr_popup":case"web_app_read_text_from_clipboard":return m("6.4",t);case"web_app_switch_inline_query":return m("6.7",t);case"web_app_invoke_custom_method":case"web_app_request_write_access":case"web_app_request_phone":return m("6.9",t);case"web_app_setup_settings_button":return m("6.10",t);case"web_app_biometry_get_info":case"web_app_biometry_open_settings":case"web_app_biometry_request_access":case"web_app_biometry_request_auth":case"web_app_biometry_update_token":return m("7.2",t);case"web_app_setup_swipe_behavior":return m("7.7",t);case"web_app_share_to_story":return m("7.8",t);case"web_app_setup_secondary_button":case"web_app_set_bottom_bar_color":return m("7.10",t);default:return["iframe_ready","iframe_will_reload","web_app_close","web_app_data_send","web_app_expand","web_app_open_link","web_app_ready","web_app_request_theme","web_app_request_viewport","web_app_setup_main_button","web_app_setup_closing_behavior"].includes(e)}}function so(e,t){t||(t="strict");const n=typeof t=="function"?t:r=>{const{method:s,version:i}=r;let a,c;if("param"in r?(a=`Parameter "${r.param}" of "${s}" method is unsupported in Mini Apps version ${i}`,c=Fn):(a=`Method "${s}" is unsupported in Mini Apps version ${i}`,c=zn),t==="strict")throw new _(c,a);return console.warn(a)};return(r,s)=>L(r,e)?Di(s)&&r==="web_app_set_header_color"&&"color"in s&&!L(r,"color",e)?n({version:e,method:r,param:"color"}):Ge(r,s):n({version:e,method:r})}function io(e,t,n,r){return ze("web_app_invoke_custom_method","custom_method_invoked",{...r||{},params:{method:e,params:t,req_id:n},capture:no(n)}).then(({result:s,error:i})=>{if(i)throw new _(Qn,i);return s})}function oa(){return performance.getEntriesByType("navigation")[0]}function O(){const e=oa();return!!e&&e.type==="reload"}function ao(e,t){return e.startsWith(t)?e:`${t}${e}`}function ra(e){return new URL(typeof e=="string"?e:[e.pathname||"",ao(e.search||"","?"),ao(e.hash||"","#")].join(""),"http://a")}let se;function sa(e,t){se&&se.set(e,t)||t()}function ia(e){if(se)return e();se=new Map;try{e()}finally{se.forEach(t=>t()),se=void 0}}function u(e,t){t||(t={});const n=t.equals||Object.is;let r=[],s=e;const i=l=>{if(!n(s,l)){const p=s;s=l,sa(d,()=>{[...r].forEach(([y,P])=>{y(l,p),P&&c(y,!0)})})}};function a(l){const p=typeof l!="object"?{once:l}:l;return{once:p.once||!1,signal:p.signal||!1}}const c=(l,p)=>{const y=a(p),P=r.findIndex(([Ee,te])=>Ee===l&&te.once===y.once&&te.signal===y.signal);P>=0&&r.splice(P,1)},d=Object.assign(function(){return aa(d),s},{destroy(){r=[]},set:i,reset(){i(e)},sub(l,p){return r.push([l,a(p)]),()=>c(l,p)},unsub:c,unsubAll(){r=r.filter(l=>l[1].signal)}});return d}const Re=[];function aa(e){Re.length&&Re[Re.length-1].add(e)}function b(e,t){let n=new Set;const r=u(i(),t);function s(){r.set(i())}function i(){n.forEach(d=>d.unsub(s,{signal:!0}));const a=new Set;let c;Re.push(a);try{c=e()}finally{Re.pop()}return a.forEach(d=>{d.sub(s,{signal:!0})}),n=a,c}return Object.assign(function(){return r()},{destroy:r.destroy,sub:r.sub,unsub:r.unsub,unsubAll:r.unsubAll})}const co=u((()=>{let e=0;return()=>(e+=1).toString()})()),Pt=u(Ge),z=u("0.0");function ca(e){e||(e={});const{postEvent:t}=e,n=e.version||Z().version;z.set(n),Pt.set(typeof t=="function"?t:so(n))}function uo(){return co()()}function Te(e,t,n){return io(e,t,uo(),{...n||{},postEvent:f})}const I=(e,t,n)=>(n||(n={}),n.postEvent||(n.postEvent=f),ze(e,t,n)),f=(e,t)=>Pt()(e,t),ie="ERR_POPUP_INVALID_PARAMS",vt="ERR_INVALID_HOSTNAME",lo="ERR_INVALID_SLUG",_o="ERR_DATA_INVALID_SIZE",fo="ERR_ACCESS_DENIED",C="ERR_ALREADY_CALLED",At="ERR_NOT_AVAILABLE",Rt="ERR_NOT_SUPPORTED",po="ERR_NOT_MOUNTED";function E(e,t){function n(){return typeof t=="string"?L(t,z()):t()}return Object.assign((...r)=>{if(!n())throw new _(Rt);return e(...r)},{isSupported:n})}function U(e){return t=>E(t,e)}function T(e,t){t(),e.sub(t)}function D(e){return b(()=>L(e,z()))}function Tt(e,t){return(...n)=>{if(!t())throw new _(po);return e(...n)}}function Y(e){return t=>Tt(t,e)}const ho="web_app_setup_back_button",bo="back_button_pressed",mo="backButton",ae=u(!1),$t=D(ho),Bt=U($t),go=Y(ae),Eo=go(()=>{F.set(!1)}),F=u(!1),wo=Bt(()=>{ae()||(F.set(O()&&R(mo)||!1),T(F,So),ae.set(!0))});function So(){const e=F();f(ho,{is_visible:e}),A(mo,e)}const yo=Bt(e=>S(bo,e)),Co=Bt(e=>{G(bo,e)}),Po=go(()=>{F.set(!0)});function vo(){F.unsub(So),ae.set(!1)}const ua=Object.freeze(Object.defineProperty({__proto__:null,hide:Eo,isMounted:ae,isSupported:$t,isVisible:F,mount:wo,offClick:Co,onClick:yo,show:Po,unmount:vo},Symbol.toStringTag,{value:"Module"}));function Ao(e,t,{isMounting:n,isMounted:r,mountError:s,isSupported:i}){return E(a=>{if(r())return h.resolve();if(n())throw new _(C);return n.set(!0),h.withFn(c=>e({abortSignal:c}),a).then(c=>[!0,c],c=>[!1,c]).then(c=>{ia(()=>{if(n.set(!1),r.set(!0),c[0])t(c[1]);else{const d=c[1];throw s.set(d),d}})})},i||(()=>!0))}const M=u(),$e=u(!1),Be=u(!1),Fe=u(!1),Ot=u(!1),It=u(void 0);function Dt(e){return e.available?{available:!0,tokenSaved:e.token_saved,deviceId:e.device_id,accessRequested:e.access_requested,type:e.type,accessGranted:e.access_granted}:{available:!1}}const Ro="web_app_biometry_get_info",To=E(e=>I(Ro,"biometry_info_received",e).then(Dt),Ro),$o="web_app_biometry_request_auth",la="web_app_biometry_request_access",_a="web_app_biometry_open_settings",da="web_app_biometry_update_token",Mt="biometry_info_received",Bo="biometry",Ke=D($o),fa=U(Ke),kt=Y(Fe),Oo=kt(e=>{if($e())return h.reject(new _(C));const t=M();return!t||!t.available?h.reject(new _(At)):($e.set(!0),e||(e={}),I($o,"biometry_auth_requested",{...e,params:{reason:(e.reason||"").trim()}}).then(n=>{const{token:r}=n;return typeof r=="string"&&M.set({...t,token:r}),n}).finally(()=>{$e.set(!1)}))}),Io=fa(()=>{f(_a)}),Do=kt(e=>Be()?h.reject(new _(C)):(Be.set(!0),e||(e={}),I(la,Mt,{...e,params:{reason:e.reason||""}}).then(Dt).then(t=>{if(!t.available)throw new _(At);return M.set(t),t.accessGranted}).finally(()=>{Be.set(!1)}))),Mo=Ao(e=>{const t=O()&&R(Bo);return t||To(e)},e=>{S(Mt,ko),T(M,No),M.set(e)},{isMounted:Fe,mountError:It,isMounting:Ot,isSupported:Ke}),ko=e=>{M.set(Dt(e))};function No(){const e=M();e&&A(Bo,e)}function Vo(){G(Mt,ko),M.unsub(No)}const Lo=kt(e=>(e||(e={}),I(da,"biometry_token_updated",{...e,params:{token:e.token||"",reason:e.reason}}).then(t=>t.status))),pa=Object.freeze(Object.defineProperty({__proto__:null,authenticate:Oo,isAuthenticating:$e,isMounted:Fe,isMounting:Ot,isRequestingAccess:Be,isSupported:Ke,mount:Mo,mountError:It,openSettings:Io,requestAccess:Do,state:M,unmount:Vo,updateToken:Lo},Symbol.toStringTag,{value:"Module"})),Uo="closingConfirmation",ce=u(!1),jo=Y(ce),Wo=jo(()=>{K.set(!1)}),K=u(!1),qo=jo(()=>{K.set(!0)});function Ho(){ce()||(K.set(O()&&R(Uo)||!1),T(K,Go),ce.set(!0))}function Go(){const e=K();f("web_app_setup_closing_behavior",{need_confirmation:e}),A(Uo,e)}function zo(){K.unsub(Go),ce.set(!1)}const ha=Object.freeze(Object.defineProperty({__proto__:null,disableConfirmation:Wo,enableConfirmation:qo,isConfirmationEnabled:K,isMounted:ce,mount:Ho,unmount:zo},Symbol.toStringTag,{value:"Module"})),Nt=D("web_app_invoke_custom_method"),Qe=U(Nt),Yo=Qe((e,t)=>{const n=Array.isArray(e)?e:[e];return n.length?Te("deleteStorageValues",{keys:n},t).then():h.resolve()});function ba(e,t){const n=Array.isArray(e)?e:[e];return n.length?Te("getStorageValues",{keys:n},t).then(r=>{const s=Ve(Object.fromEntries(n.map(i=>[i,V()])))()(r);return Array.isArray(e)?s:s[e]}):h.resolve(typeof e=="string"?"":{})}const Fo=Qe(ba),Ko=Qe(e=>Te("getStorageKeys",{},e).then(gi(V())())),Qo=Qe((e,t,n)=>Te("saveStorageValue",{key:e,value:t},n).then()),ma=Object.freeze(Object.defineProperty({__proto__:null,deleteItem:Yo,getItem:Fo,getKeys:Ko,isSupported:Nt,setItem:Qo},Symbol.toStringTag,{value:"Module"})),Je="web_app_trigger_haptic_feedback",Vt=D(Je),Lt=U(Vt),Jo=Lt(e=>{f(Je,{type:"impact",impact_style:e})}),Xo=Lt(e=>{f(Je,{type:"notification",notification_type:e})}),Zo=Lt(()=>{f(Je,{type:"selection_change"})}),ga=Object.freeze(Object.defineProperty({__proto__:null,impactOccurred:Jo,isSupported:Vt,notificationOccurred:Xo,selectionChanged:Zo},Symbol.toStringTag,{value:"Module"})),Xe=u(void 0);function k(e){return b(()=>{const t=Xe();return t?t[e]:void 0})}const Ut=k("authDate"),jt=k("canSendAfter"),xo=b(()=>{const e=Ut(),t=jt();return t&&e?new Date(e.getTime()+t*1e3):void 0}),er=k("chat"),tr=k("chatType"),nr=k("chatInstance"),or=k("hash"),rr=k("queryId"),Wt=u(),sr=k("receiver");function ir(){const e=Z();Xe.set(e.initData),Wt.set(e.initDataRaw)}const ar=k("startParam"),cr=k("user"),Ea=Object.freeze(Object.defineProperty({__proto__:null,authDate:Ut,canSendAfter:jt,canSendAfterDate:xo,chat:er,chatInstance:nr,chatType:tr,hash:or,queryId:rr,raw:Wt,receiver:sr,restore:ir,startParam:ar,state:Xe,user:cr},Symbol.toStringTag,{value:"Module"}));function wa(e){return pi()(e)}const ur="web_app_open_invoice",Oe=u(!1),qt=D(ur);async function lr(e,t,n){if(Oe())throw new _(C);let r;if(t==="url"){const{hostname:s,pathname:i}=new URL(e,window.location.href);if(s!=="t.me")throw new _(vt);const a=i.match(/^\/(\$|invoice\/)([A-Za-z0-9\-_=]+)$/);if(!a)throw new _(lo);[,,r]=a}else r=e,n=t;return Oe.set(!0),I(ur,"invoice_closed",{...n,params:{slug:r},capture:s=>r===s.slug}).then(s=>s.status).finally(()=>{Oe.set(!1)})}const _r=E(lr,qt),Sa=Object.freeze(Object.defineProperty({__proto__:null,_open:lr,isOpened:Oe,isSupported:qt,open:_r},Symbol.toStringTag,{value:"Module"}));function Ht(e){const t=_t(e);return Math.sqrt([.299,.587,.114].reduce((n,r,s)=>{const i=parseInt(t.slice(1+s*2,1+(s+1)*2),16);return n+i*i*r},0))<120}const ue=u(!1),Ie=u(!1),j=u({});function g(e){return b(()=>j()[e])}const dr=g("accentTextColor"),Ze=g("bgColor"),xe=g("buttonColor"),Gt=g("buttonTextColor"),zt=g("bottomBarBgColor"),fr=g("destructiveTextColor"),Yt=g("headerBgColor"),pr=g("hintColor"),hr=b(()=>{const{bgColor:e}=j();return!e||Ht(e)}),br=g("linkColor"),De=g("secondaryBgColor"),mr=g("sectionBgColor"),gr=g("sectionHeaderTextColor"),Er=g("sectionSeparatorColor"),wr=g("subtitleTextColor"),Sr=g("textColor");function x(e){return b(()=>_e()[e])}const le=u({hasShineEffect:!1,isEnabled:!0,isLoaderVisible:!1,isVisible:!1,text:"Continue"}),_e=b(()=>{const e=le();return{...e,backgroundColor:e.backgroundColor||xe()||"#2481cc",textColor:e.textColor||Gt()||"#ffffff"}}),de=u(!1),yr=x("backgroundColor"),Cr=x("hasShineEffect"),Pr=x("isEnabled"),vr=x("isLoaderVisible"),Ar=x("isVisible"),Rr=x("text"),Tr=x("textColor"),ya="web_app_setup_main_button",$r="main_button_pressed",Br="mainButton",Ca=Y(de);function Or(){if(!de()){const e=O()&&R(Br);e&&le.set(e),le.sub(Mr),T(_e,kr),de.set(!0)}}function Ir(e){return S($r,e)}function Dr(e){G($r,e)}function Mr(e){A(Br,e)}function kr(){const e=_e();e.text&&f(ya,{color:e.backgroundColor,has_shine_effect:e.hasShineEffect,is_active:e.isEnabled,is_progress_visible:e.isLoaderVisible,is_visible:e.isVisible,text:e.text,text_color:e.textColor})}const Nr=Ca(e=>{le.set({...le(),...Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0))})});function Vr(){le.unsub(Mr),_e.unsub(kr),de.set(!1)}const Pa=Object.freeze(Object.defineProperty({__proto__:null,backgroundColor:yr,hasShineEffect:Cr,isEnabled:Pr,isLoaderVisible:vr,isMounted:de,isVisible:Ar,mount:Or,offClick:Dr,onClick:Ir,setParams:Nr,state:_e,text:Rr,textColor:Tr,unmount:Vr},Symbol.toStringTag,{value:"Module"}));function Lr(e){return bi()(e)}const Ur="themeParams",jr="theme_changed",Wr=Tt(e=>{if(Ie())throw new _(C);e||(e=r=>`--tg-theme-${gt(r)}`);function t(r){Object.entries(j()).forEach(([s,i])=>{i&&r(s,i)})}function n(){t((r,s)=>{qe(e(r),s)})}return n(),j.sub(n),Ie.set(!0),()=>{t(He),j.unsub(n),Ie.set(!1)}},ue);function Ft(){ue()||(S(jr,qr),j.set(O()&&R(Ur)||Z().themeParams),ue.set(!0))}const qr=e=>{const t=Lr(e.theme_params);j.set(t),A(Ur,t)};function Hr(){G(jr,qr),ue.set(!1)}function va(e,t){function n(r){const s=t[r];return L(s[0],s[1],z())}return Object.assign((...r)=>{for(const s in t)if(t[s][2](...r)&&!n(s))throw new _(Rt,`Parameter "${s}" is not supported`);return e(...r)},e,{supports:n})}function Gr(e){return b(()=>{const t=e();return ne(t)?t:t==="bg_color"?Ze():De()})}const W=u("bg_color"),Kt=Gr(W),q=u("bottom_bar_bg_color"),et=b(()=>{const e=q();return ne(e)?e:e==="bottom_bar_bg_color"?zt()||De():e==="secondary_bg_color"?De():Ze()}),H=u("bg_color"),Qt=Gr(H),fe=u(!1),Me=u(!1),zr=b(()=>{const e=Kt();return e?Ht(e):!1}),Jt=b(()=>({backgroundColor:W(),bottomBarColor:q(),headerColor:H()})),Xt="web_app_set_background_color",Zt="web_app_set_bottom_bar_color",tt="web_app_set_header_color",Yr="miniApp",xt=b(()=>[Xt,Zt,tt].some(e=>L(e,z()))),Aa=U(xt),nt=Y(fe),Fr=nt(e=>{if(Me())throw new _(C);const[t,n]=oe();function r(s,i){function a(){qe(s,i()||null)}a(),t(i.sub(a),He.bind(null,s))}return e||(e=s=>`--tg-${gt(s)}`),r(e("bgColor"),Kt),r(e("bottomBarColor"),et),r(e("headerColor"),Qt),t(()=>{Me.set(!1)}),Me.set(!0),n});function Kr(e){f("web_app_close",{return_back:e})}const Qr=Aa(()=>{if(!fe()){const e=O()&&R(Yr);Ft(),W.set(e?e.backgroundColor:"bg_color"),q.set(e?e.bottomBarColor:"bottom_bar_bg_color"),H.set(e?e.headerColor:Yt()||"bg_color"),tn.isSupported()&&T(W,Jr),nn.isSupported()&&T(q,Xr),on.isSupported()&&T(H,Zr),fe.set(!0)}});function Jr(){en(),f(Xt,{color:W()})}function Xr(){en(),f(Zt,{color:q()})}function Zr(){const e=H();en(),f(tt,ne(e)?{color:e}:{color_key:e})}function xr(){f("web_app_ready")}function en(){A(Yr,Jt())}const tn=E(nt(e=>{W.set(e)}),Xt),nn=E(nt(e=>{q.set(e)}),Zt),on=va(E(nt(e=>{H.set(e)}),tt),{color:[tt,"color",ne]});function es(){W.unsub(Jr),q.unsub(Xr),H.unsub(Zr),fe.set(!1)}const Ra=Object.freeze(Object.defineProperty({__proto__:null,backgroundColor:W,backgroundColorRGB:Kt,bindCssVars:Fr,bottomBarColor:q,bottomBarColorRGB:et,close:Kr,headerColor:H,headerColorRGB:Qt,isCssVarsBound:Me,isDark:zr,isMounted:fe,isSupported:xt,mount:Qr,ready:xr,setBackgroundColor:tn,setBottomBarColor:nn,setHeaderColor:on,state:Jt,unmount:es},Symbol.toStringTag,{value:"Module"}));function Ta(e){const t=e.message.trim(),n=(e.title||"").trim(),r=e.buttons||[];if(n.length>64)throw new _(ie,`Invalid title: ${n}`);if(!t||t.length>256)throw new _(ie,`Invalid message: ${t}`);if(r.length>3)throw new _(ie,`Invalid buttons count: ${r.length}`);return{title:n,message:t,buttons:r.length?r.map((s,i)=>{const a=s.id||"";if(a.length>64)throw new _(ie,`Button with index ${i} has invalid id: ${a}`);if(!s.type||s.type==="default"||s.type==="destructive"){const c=s.text.trim();if(!c||c.length>64)throw new _(ie,`Button with index ${i} has invalid text: ${c}`);return{type:s.type,text:c,id:a}}return{type:s.type,id:a}}):[{type:"close",id:""}]}}const ts="web_app_open_popup",rn=D(ts),ke=u(!1),ns=E(async e=>{if(ke())throw new _(C);ke.set(!0);try{const{button_id:t=null}=await I(ts,"popup_closed",{...e,params:Ta(e)});return t}finally{ke.set(!1)}},rn),$a=Object.freeze(Object.defineProperty({__proto__:null,isOpened:ke,isSupported:rn,open:ns},Symbol.toStringTag,{value:"Module"})),Ba="web_app_close_scan_qr_popup",os="web_app_open_scan_qr_popup",Oa="scan_qr_popup_closed",Ia="qr_text_received",sn=D(os),rs=U(sn),ot=rs(()=>{ee.set(!1),f(Ba)}),ee=u(!1);function Da(e){return h.withFn(t=>{if(ee())throw new _(C);ee.set(!0),e||(e={});const{onCaptured:n,text:r,capture:s}=e,[,i]=oe(ee.sub(()=>{a.resolve()}),S(Oa,()=>{ee.set(!1)}),S(Ia,c=>{n?n(c.data):(!s||s(c.data))&&(a.resolve(c.data),ot())})),a=new Ae({abortSignal:t}).catch(ot).finally(i);return(e.postEvent||f)(os,{text:r}),a},e)}const ss=rs(Da),Ma=Object.freeze(Object.defineProperty({__proto__:null,close:ot,isOpened:ee,isSupported:sn,open:ss},Symbol.toStringTag,{value:"Module"}));function Q(e){return b(()=>he()[e])}const pe=u({hasShineEffect:!1,isEnabled:!0,isLoaderVisible:!1,isVisible:!1,position:"left",text:"Cancel"}),he=b(()=>{const e=pe();return{...e,backgroundColor:e.backgroundColor||et()||"#000000",textColor:e.textColor||xe()||"#2481cc"}}),be=u(!1),is=Q("backgroundColor"),as=Q("hasShineEffect"),cs=Q("isEnabled"),us=Q("isLoaderVisible"),ls=Q("isVisible"),_s=Q("position"),ds=Q("text"),fs=Q("textColor"),ps="web_app_setup_secondary_button",hs="secondary_button_pressed",bs="secondaryButton",an=D(ps),cn=U(an),ka=Y(be),ms=cn(()=>{if(!be()){const e=O()&&R(bs);e&&pe.set(e),pe.sub(ws),T(he,Ss),be.set(!0)}}),gs=cn(e=>S(hs,e)),Es=cn(e=>{G(hs,e)});function ws(e){A(bs,e)}function Ss(){const e=he();e.text&&f(ps,{color:e.backgroundColor,has_shine_effect:e.hasShineEffect,is_active:e.isEnabled,is_progress_visible:e.isLoaderVisible,is_visible:e.isVisible,position:e.position,text:e.text,text_color:e.textColor})}const ys=ka(e=>{pe.set({...pe(),...Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0))})});function Cs(){pe.unsub(ws),he.unsub(Ss),be.set(!1)}const Na=Object.freeze(Object.defineProperty({__proto__:null,backgroundColor:is,hasShineEffect:as,isEnabled:cs,isLoaderVisible:us,isMounted:be,isSupported:an,isVisible:ls,mount:ms,offClick:Es,onClick:gs,position:_s,setParams:ys,state:he,text:ds,textColor:fs,unmount:Cs},Symbol.toStringTag,{value:"Module"})),Ps="web_app_setup_settings_button",vs="settings_button_pressed",As="settingsButton",me=u(!1),un=D(Ps),ln=U(un),Rs=Y(me),Ts=Rs(()=>{J.set(!1)}),J=u(!1),$s=ln(()=>{me()||(J.set(O()&&R(As)||!1),T(J,Bs),me.set(!0))});function Bs(){const e=J();f(Ps,{is_visible:e}),A(As,e)}const Os=ln(e=>S(vs,e)),Is=ln(e=>{G(vs,e)}),Ds=Rs(()=>{J.set(!0)});function Ms(){J.unsub(Bs),me.set(!1)}const Va=Object.freeze(Object.defineProperty({__proto__:null,hide:Ts,isMounted:me,isSupported:un,isVisible:J,mount:$s,offClick:Is,onClick:Os,show:Ds,unmount:Ms},Symbol.toStringTag,{value:"Module"})),ks="web_app_setup_swipe_behavior",Ns="swipeBehavior",ge=u(!1),_n=D(ks),La=U(_n),Vs=Y(ge),Ls=Vs(()=>{X.set(!1)}),Us=Vs(()=>{X.set(!0)}),X=u(!1),js=La(()=>{ge()||(X.set(O()&&R(Ns)||!1),T(X,Ws),ge.set(!0))});function Ws(){const e=X();f(ks,{allow_vertical_swipe:e}),A(Ns,e)}function qs(){X.unsub(Ws),ge.set(!1)}const Ua=Object.freeze(Object.defineProperty({__proto__:null,disableVertical:Ls,enableVertical:Us,isMounted:ge,isSupported:_n,isVerticalEnabled:X,mount:js,unmount:qs},Symbol.toStringTag,{value:"Module"})),ja=Object.freeze(Object.defineProperty({__proto__:null,accentTextColor:dr,backgroundColor:Ze,bindCssVars:Wr,bottomBarBgColor:zt,buttonColor:xe,buttonTextColor:Gt,destructiveTextColor:fr,headerBackgroundColor:Yt,hintColor:pr,isCssVarsBound:Ie,isDark:hr,isMounted:ue,linkColor:br,mount:Ft,secondaryBackgroundColor:De,sectionBackgroundColor:mr,sectionHeaderTextColor:gr,sectionSeparatorColor:Er,state:j,subtitleTextColor:wr,textColor:Sr,unmount:Hr},Symbol.toStringTag,{value:"Module"})),w=u({height:0,width:0,isExpanded:!1,stableHeight:0}),rt=u(!1),Ne=u(!1),dn=u(!1),fn=u(void 0);function st(e){return b(()=>w()[e])}const Hs=st("height"),Gs=st("isExpanded"),zs=b(()=>{const e=w();return e.height===e.stableHeight}),Ys=st("stableHeight"),Fs=st("width");function Wa(e){return I("web_app_request_viewport","viewport_changed",e).then(t=>({height:t.height,width:t.width,isExpanded:t.is_expanded,isStable:t.is_state_stable}))}const Ks=Tt(e=>{if(Ne())throw new _(C);e||(e=r=>`--tg-viewport-${gt(r)}`);const t=["height","width","stableHeight"];function n(){t.forEach(r=>{qe(e(r),`${w()[r]}px`)})}return n(),w.sub(n),Ne.set(!0),()=>{t.forEach(He),w.unsub(n),Ne.set(!1)}},rt);function Qs(){f("web_app_expand")}function Js(e){return{isExpanded:e.isExpanded,height:pn(e.height),width:pn(e.width),stableHeight:pn(e.stableHeight)}}const Xs=Ao(e=>{const t=O()&&R("viewport");if(t)return t;if(["macos","tdesktop","unigram","webk","weba","web"].includes(Z().platform)){const n=window;return{isExpanded:!0,height:n.innerHeight,width:n.innerWidth,stableHeight:n.innerHeight}}return e.timeout||(e.timeout=1e3),Wa(e).then(n=>({height:n.height,isExpanded:n.isExpanded,stableHeight:n.isStable?n.height:w().stableHeight,width:n.width}))},e=>{S("viewport_changed",Zs),T(w,xs),w.set(Js(e))},{isMounted:rt,isMounting:dn,mountError:fn}),Zs=e=>{w.set(Js({height:e.height,width:e.width,isExpanded:e.is_expanded,stableHeight:e.is_state_stable?e.height:w().stableHeight}))};function xs(){A("viewport",w())}function pn(e){return Math.max(e,0)}function ei(){G("viewport_changed",Zs),w.unsub(xs)}const qa=Object.freeze(Object.defineProperty({__proto__:null,bindCssVars:Ks,expand:Qs,height:Hs,isCssVarsBound:Ne,isExpanded:Gs,isMounted:rt,isMounting:dn,isStable:zs,mount:Xs,mountError:fn,stableHeight:Ys,state:w,unmount:ei,width:Fs},Symbol.toStringTag,{value:"Module"})),ti="web_app_open_tg_link";function Ha(e,t){t||(t={}),f("web_app_open_link",{url:ra(e).toString(),try_browser:t.tryBrowser,try_instant_view:t.tryInstantView})}function ni(e){const{hostname:t,pathname:n,search:r}=new URL(e,"https://t.me");if(t!=="t.me")throw new _(vt);if(!L(ti,z())){window.location.href=e;return}f(ti,{path_full:n+r})}function Ga(e,t){ni("https://t.me/share/url?"+new URLSearchParams({url:e,text:t||""}).toString().replace(/\+/g,"%20"))}const hn="web_app_request_phone",oi="web_app_request_write_access",it=u(!1),at=u(!1);function ri(e){return e||(e={}),Te("getRequestedContact",{},{...e,timeout:e.timeout||5e3}).then(Cn({contact:Ve({userId:["user_id",Le()],phoneNumber:["phone_number",V()],firstName:["first_name",V()],lastName:["last_name",V(!0)]})(),authDate:["auth_date",yn()],hash:V()})())}const za=E(e=>new h(async(t,n,r)=>{const s={postEvent:(e||{}).postEvent,abortSignal:r};try{return t(await ri(s))}catch{}if(await si(s)!=="sent")throw new _(fo);let a=50;for(;!r.aborted;){try{return t(await ri(s))}catch{}await Wi(a),a+=50}},e),hn),si=E(e=>{if(it())throw new _(C);return it.set(!0),I(hn,"phone_requested",e).then(t=>t.status).finally(()=>{it.set(!1)})},hn),Ya=E(e=>{if(at())throw new _(C);return at.set(!0),I(oi,"write_access_requested",e).then(t=>t.status).finally(()=>{at.set(!1)})},oi),ii="web_app_read_text_from_clipboard",ai="web_app_switch_inline_query",ci="web_app_share_to_story",Fa=E(e=>{const t=uo();return I(ii,"clipboard_text_received",{...e,params:{req_id:t},capture:no(t)}).then(({data:n=null})=>n)},ii);function Ka(e){const{size:t}=new Blob([e]);if(!t||t>4096)throw new _(_o);f("web_app_data_send",{data:e})}const Qa=E((e,t)=>{t||(t={}),(t.postEvent||f)(ci,{text:t.text,media_url:e,widget_link:t.widgetLink})},ci),Ja=E((e,t)=>{f(ai,{query:e,chat_types:t||[]})},()=>L(ai,z())&&!!Z().botInline);function Xa(){return typeof window>"u"}function Za(e){ca(e),eo();const[t,n]=oe(S("reload_iframe",()=>{f("iframe_will_reload"),window.location.reload()}),to),{acceptCustomStyles:r=!0}=e||{};if(r){const s=document.createElement("style");s.id="telegram-custom-styles",document.head.appendChild(s),t(S("set_custom_style",i=>{s.innerHTML=i}),()=>{document.head.removeChild(s)})}return f("iframe_ready",{reload_supported:!0}),n}return o.$createRequestId=co,o.$debug=Wn,o.$postEvent=Pt,o.$targetOrigin=Jn,o.$version=z,o.CancelablePromise=h,o.ERR_ABORTED=ht,o.ERR_ACCESS_DENIED=fo,o.ERR_ALREADY_CALLED=C,o.ERR_CANCELED=bt,o.ERR_CUSTOM_METHOD_ERR_RESPONSE=Qn,o.ERR_DATA_INVALID_SIZE=_o,o.ERR_INVALID_HOSTNAME=vt,o.ERR_INVALID_SLUG=lo,o.ERR_INVALID_VALUE=gn,o.ERR_METHOD_PARAMETER_UNSUPPORTED=Fn,o.ERR_METHOD_UNSUPPORTED=zn,o.ERR_NOT_AVAILABLE=At,o.ERR_NOT_MOUNTED=po,o.ERR_NOT_SUPPORTED=Rt,o.ERR_PARSE=ct,o.ERR_POPUP_INVALID_PARAMS=ie,o.ERR_RETRIEVE_LP_FAILED=Yn,o.ERR_TIMED_OUT=mt,o.ERR_UNEXPECTED_TYPE=wn,o.ERR_UNEXPECTED_VALUE=En,o.ERR_UNKNOWN_ENV=Kn,o.TypedError=_,o.addEventListener=We,o.authenticateBiometry=Oo,o.backButton=ua,o.bindMiniAppCssVars=Fr,o.bindThemeParamsCssVars=Wr,o.bindViewportCssVars=Ks,o.biometry=pa,o.biometryMountError=It,o.biometryState=M,o.classNames=Ue,o.closeMiniApp=Kr,o.closeQrScanner=ot,o.closingBehavior=ha,o.cloudStorage=ma,o.compareVersions=ro,o.createPostEvent=so,o.defineEventHandlers=eo,o.deleteCloudStorageItem=Yo,o.deleteCssVar=He,o.disableClosingConfirmation=Wo,o.disableVerticalSwipes=Ls,o.emitMiniAppsEvent=Ye,o.enableClosingConfirmation=qo,o.enableVerticalSwipes=Us,o.expandViewport=Qs,o.getCloudStorageItem=Fo,o.getCloudStorageKeys=Ko,o.hapticFeedback=ga,o.hapticFeedbackImpactOccurred=Jo,o.hapticFeedbackNotificationOccurred=Xo,o.hapticFeedbackSelectionChanged=Zo,o.hideBackButton=Eo,o.hideSettingsButton=Ts,o.init=Za,o.initData=Ea,o.initDataAuthDate=Ut,o.initDataCanSendAfter=jt,o.initDataCanSendAfterDate=xo,o.initDataChat=er,o.initDataChatInstance=nr,o.initDataChatType=tr,o.initDataHash=or,o.initDataQueryId=rr,o.initDataRaw=Wt,o.initDataReceiver=sr,o.initDataStartParam=ar,o.initDataState=Xe,o.initDataUser=cr,o.invoice=Sa,o.invokeCustomMethod=io,o.isAbortError=Ui,o.isAuthenticatingBiometry=$e,o.isBackButtonMounted=ae,o.isBackButtonSupported=$t,o.isBackButtonVisible=F,o.isBiometryMounted=Fe,o.isBiometryMounting=Ot,o.isBiometrySupported=Ke,o.isCanceledError=ji,o.isClosingBehaviorMounted=ce,o.isClosingConfirmationEnabled=K,o.isCloudStorageSupported=Nt,o.isColorDark=Ht,o.isHapticFeedbackSupported=Vt,o.isIframe=kn,o.isInvoiceOpened=Oe,o.isInvoiceSupported=qt,o.isMainButtonEnabled=Pr,o.isMainButtonLoaderVisible=vr,o.isMainButtonMounted=de,o.isMainButtonVisible=Ar,o.isMiniAppCssVarsBound=Me,o.isMiniAppDark=zr,o.isMiniAppMounted=fe,o.isMiniAppSupported=xt,o.isPopupOpened=ke,o.isPopupSupported=rn,o.isQrScannerOpened=ee,o.isQrScannerSupported=sn,o.isRGB=ne,o.isRGBShort=Pn,o.isRecord=dt,o.isRequestingBiometryAccess=Be,o.isRequestingPhoneAccess=it,o.isRequestingWriteAccess=at,o.isSSR=Xa,o.isSecondaryButtonEnabled=cs,o.isSecondaryButtonLoaderVisible=us,o.isSecondaryButtonMounted=be,o.isSecondaryButtonSupported=an,o.isSecondaryButtonVisible=ls,o.isSettingsButtonMounted=me,o.isSettingsButtonSupported=un,o.isSettingsButtonVisible=J,o.isSwipeBehaviorMounted=ge,o.isSwipeBehaviorSupported=_n,o.isTMA=Zi,o.isThemeParamsCssVarsBound=Ie,o.isThemeParamsDark=hr,o.isThemeParamsMounted=ue,o.isTimeoutError=Li,o.isVerticalSwipesEnabled=X,o.isViewportCssVarsBound=Ne,o.isViewportExpanded=Gs,o.isViewportMounted=rt,o.isViewportMounting=dn,o.isViewportStable=zs,o.mainButton=Pa,o.mainButtonBackgroundColor=yr,o.mainButtonHasShineEffect=Cr,o.mainButtonState=_e,o.mainButtonText=Rr,o.mainButtonTextColor=Tr,o.mergeClassNames=Ei,o.miniApp=Ra,o.miniAppBackgroundColor=W,o.miniAppBottomBarColor=q,o.miniAppBottomBarColorRGB=et,o.miniAppHeaderColor=H,o.miniAppHeaderColorRGB=Qt,o.miniAppReady=xr,o.miniAppState=Jt,o.mockTelegramEnv=ea,o.mountBackButton=wo,o.mountBiometry=Mo,o.mountClosingBehavior=Ho,o.mountMainButton=Or,o.mountMiniApp=Qr,o.mountSecondaryButton=ms,o.mountSettingsButton=$s,o.mountSwipeBehavior=js,o.mountThemeParams=Ft,o.mountViewport=Xs,o.off=G,o.offBackButtonClick=Co,o.offMainButtonClick=Dr,o.offSecondaryButtonClick=Es,o.offSettingsButtonClick=Is,o.on=S,o.onBackButtonClick=yo,o.onMainButtonClick=Ir,o.onSecondaryButtonClick=gs,o.onSettingsButtonClick=Os,o.openBiometrySettings=Io,o.openInvoice=_r,o.openLink=Ha,o.openPopup=ns,o.openQrScanner=ss,o.openTelegramLink=ni,o.parseInitData=wa,o.parseThemeParams=Lr,o.popup=$a,o.postEvent=Ge,o.qrScanner=Ma,o.readTextFromClipboard=Fa,o.removeEventHandlers=to,o.request=ze,o.requestBiometry=To,o.requestBiometryAccess=Do,o.requestContact=za,o.requestPhoneAccess=si,o.requestWriteAccess=Ya,o.restoreInitData=ir,o.retrieveLaunchParams=Z,o.secondaryButton=Na,o.secondaryButtonBackgroundColor=is,o.secondaryButtonHasShineEffect=as,o.secondaryButtonPosition=_s,o.secondaryButtonState=he,o.secondaryButtonText=ds,o.secondaryButtonTextColor=fs,o.sendData=Ka,o.serializeLaunchParams=mi,o.serializeThemeParams=vn,o.setCloudStorageItem=Qo,o.setCssVar=qe,o.setMainButtonParams=Nr,o.setMiniAppBackgroundColor=tn,o.setMiniAppBottomBarColor=nn,o.setMiniAppHeaderColor=on,o.setSecondaryButtonParams=ys,o.settingsButton=Va,o.shareStory=Qa,o.shareURL=Ga,o.showBackButton=Po,o.showSettingsButton=Ds,o.subscribe=ta,o.supports=L,o.swipeBehavior=Ua,o.switchInlineQuery=Ja,o.themeParams=ja,o.themeParamsAccentTextColor=dr,o.themeParamsBackgroundColor=Ze,o.themeParamsBottomBarBgColor=zt,o.themeParamsButtonColor=xe,o.themeParamsButtonTextColor=Gt,o.themeParamsDestructiveTextColor=fr,o.themeParamsHeaderBackgroundColor=Yt,o.themeParamsHintColor=pr,o.themeParamsLinkColor=br,o.themeParamsSecondaryBackgroundColor=De,o.themeParamsSectionBackgroundColor=mr,o.themeParamsSectionHeaderTextColor=gr,o.themeParamsSectionSeparatorColor=Er,o.themeParamsState=j,o.themeParamsSubtitleTextColor=wr,o.themeParamsTextColor=Sr,o.toRGB=_t,o.toRecord=ut,o.unmountBackButton=vo,o.unmountBiometry=Vo,o.unmountClosingBehavior=zo,o.unmountMainButton=Vr,o.unmountMiniApp=es,o.unmountSecondaryButton=Cs,o.unmountSettingsButton=Ms,o.unmountSwipeBehavior=qs,o.unmountThemeParams=Hr,o.unmountViewport=ei,o.unsubscribe=na,o.updateBiometryToken=Lo,o.viewport=qa,o.viewportHeight=Hs,o.viewportMountError=fn,o.viewportStableHeight=Ys,o.viewportState=w,o.viewportWidth=Fs,Object.defineProperty(o,Symbol.toStringTag,{value:"Module"}),o}({}); | ||
//# sourceMappingURL=index.iife.js.map |
{ | ||
"name": "@telegram-apps/sdk", | ||
"version": "2.4.1", | ||
"version": "2.5.0", | ||
"description": "TypeScript Source Development Kit for Telegram Mini Apps client application.", | ||
@@ -40,5 +40,5 @@ "author": "Vladislav Kibenko <wolfram.deus@gmail.com>", | ||
"dependencies": { | ||
"@telegram-apps/bridge": "^1.3.0", | ||
"@telegram-apps/transformers": "^1.0.1", | ||
"@telegram-apps/bridge": "^1.2.1", | ||
"@telegram-apps/navigation": "^1.0.3", | ||
"@telegram-apps/navigation": "^1.0.4", | ||
"@telegram-apps/signals": "^1.0.1" | ||
@@ -45,0 +45,0 @@ }, |
@@ -8,1 +8,3 @@ export const ERR_POPUP_INVALID_PARAMS = 'ERR_POPUP_INVALID_PARAMS'; | ||
export const ERR_NOT_AVAILABLE = 'ERR_NOT_AVAILABLE'; | ||
export const ERR_NOT_SUPPORTED = 'ERR_NOT_SUPPORTED'; | ||
export const ERR_NOT_MOUNTED = 'ERR_NOT_MOUNTED'; |
@@ -36,2 +36,4 @@ export { classNames } from '@/classnames/classNames.js'; | ||
ERR_ALREADY_CALLED, | ||
ERR_NOT_SUPPORTED, | ||
ERR_NOT_MOUNTED, | ||
} from '@/errors.js'; | ||
@@ -38,0 +40,0 @@ export { init, type InitOptions } from '@/init.js'; |
@@ -1,4 +0,4 @@ | ||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { mockSessionStorageGetItem, mockPageReload, mockSessionStorageSetItem } from 'test-utils'; | ||
import { emitMiniAppsEvent } from '@telegram-apps/bridge'; | ||
import { emitMiniAppsEvent, TypedError } from '@telegram-apps/bridge'; | ||
@@ -27,70 +27,8 @@ import { mockPostEvent } from '@test-utils/mockPostEvent.js'; | ||
describe('mounted', () => { | ||
beforeEach(mount); | ||
afterEach(unmount); | ||
describe('hide', () => { | ||
it('should call postEvent with "web_app_setup_back_button" and { is_visible: false }', () => { | ||
isVisible.set(true); | ||
const spy = mockPostEvent(); | ||
hide(); | ||
hide(); | ||
hide(); | ||
expect(spy).toBeCalledTimes(1); | ||
expect(spy).toBeCalledWith('web_app_setup_back_button', { is_visible: false }); | ||
}); | ||
describe('hide', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
isMounted.set(true); | ||
}); | ||
describe('show', () => { | ||
it('should call postEvent with "web_app_setup_back_button" and { is_visible: true }', () => { | ||
isVisible.set(false); | ||
const spy = mockPostEvent(); | ||
show(); | ||
show(); | ||
show(); | ||
expect(spy).toBeCalledTimes(1); | ||
expect(spy).toBeCalledWith('web_app_setup_back_button', { is_visible: true }); | ||
}); | ||
}); | ||
}); | ||
describe('not mounted', () => { | ||
describe('hide', () => { | ||
it('should not call postEvent', () => { | ||
isVisible.set(true); | ||
const spy = mockPostEvent(); | ||
hide(); | ||
expect(spy).toBeCalledTimes(0); | ||
}); | ||
it('should not save state in storage', () => { | ||
isVisible.set(true); | ||
const spy = mockSessionStorageSetItem(); | ||
hide(); | ||
expect(spy).toBeCalledTimes(0); | ||
}); | ||
}); | ||
describe('show', () => { | ||
it('should not call postEvent', () => { | ||
isVisible.set(false); | ||
const spy = mockPostEvent(); | ||
show(); | ||
show(); | ||
show(); | ||
expect(spy).toBeCalledTimes(0); | ||
}); | ||
it('should not save state in storage', () => { | ||
isVisible.set(false); | ||
const spy = mockSessionStorageSetItem(); | ||
show(); | ||
show(); | ||
show(); | ||
expect(spy).toBeCalledTimes(0); | ||
}); | ||
}); | ||
}); | ||
describe('hide', () => { | ||
it('should set isVisible = false', () => { | ||
@@ -118,2 +56,6 @@ isVisible.set(true); | ||
describe('mount', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
it('should call postEvent with "web_app_setup_back_button"', () => { | ||
@@ -164,24 +106,8 @@ const spy = mockPostEvent(); | ||
describe('unmount', () => { | ||
beforeEach(mount); | ||
it('should stop calling postEvent function and session storage updates when isVisible changes', () => { | ||
const postEventSpy = mockPostEvent(); | ||
const storageSpy = mockSessionStorageSetItem(); | ||
isVisible.set(true); | ||
expect(postEventSpy).toHaveBeenCalledTimes(1); | ||
expect(storageSpy).toHaveBeenCalledTimes(1); | ||
postEventSpy.mockClear(); | ||
storageSpy.mockClear(); | ||
unmount(); | ||
isVisible.set(false); | ||
expect(postEventSpy).toHaveBeenCalledTimes(0); | ||
expect(storageSpy).toHaveBeenCalledTimes(0); | ||
describe('onClick', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
isMounted.set(true); | ||
}); | ||
}); | ||
describe('onClick', () => { | ||
it('should add click listener', () => { | ||
@@ -204,2 +130,7 @@ const fn = vi.fn(); | ||
describe('offClick', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
isMounted.set(true); | ||
}); | ||
it('should remove click listener', () => { | ||
@@ -214,3 +145,32 @@ const fn = vi.fn(); | ||
describe('unmount', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
mount(); | ||
}); | ||
it('should stop calling postEvent function and session storage updates when isVisible changes', () => { | ||
const postEventSpy = mockPostEvent(); | ||
const storageSpy = mockSessionStorageSetItem(); | ||
isVisible.set(true); | ||
expect(postEventSpy).toHaveBeenCalledTimes(1); | ||
expect(storageSpy).toHaveBeenCalledTimes(1); | ||
postEventSpy.mockClear(); | ||
storageSpy.mockClear(); | ||
unmount(); | ||
isVisible.set(false); | ||
expect(postEventSpy).toHaveBeenCalledTimes(0); | ||
expect(storageSpy).toHaveBeenCalledTimes(0); | ||
}); | ||
}); | ||
describe('show', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
isMounted.set(true); | ||
}); | ||
it('should set isVisible = true', () => { | ||
@@ -223,1 +183,65 @@ isVisible.set(false); | ||
}); | ||
describe('mounted', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
mount(); | ||
}); | ||
describe('hide', () => { | ||
it('should call postEvent with "web_app_setup_back_button" and { is_visible: false }', () => { | ||
isVisible.set(true); | ||
const spy = mockPostEvent(); | ||
hide(); | ||
hide(); | ||
hide(); | ||
expect(spy).toBeCalledTimes(1); | ||
expect(spy).toBeCalledWith('web_app_setup_back_button', { is_visible: false }); | ||
}); | ||
}); | ||
describe('show', () => { | ||
it('should call postEvent with "web_app_setup_back_button" and { is_visible: true }', () => { | ||
isVisible.set(false); | ||
const spy = mockPostEvent(); | ||
show(); | ||
show(); | ||
show(); | ||
expect(spy).toBeCalledTimes(1); | ||
expect(spy).toBeCalledWith('web_app_setup_back_button', { is_visible: true }); | ||
}); | ||
}); | ||
}); | ||
describe('support check', () => { | ||
beforeEach(() => { | ||
isMounted.set(true); | ||
}); | ||
it.each([ | ||
{ fn: mount, name: 'mount' }, | ||
{ fn: () => onClick(console.log), name: 'onClick' }, | ||
{ fn: () => offClick(console.log), name: 'offClick' }, | ||
])('$name function should throw ERR_NOT_SUPPORTED if version is less than 6.1', ({ fn }) => { | ||
$version.set('6.0'); | ||
expect(fn).toThrow(new TypedError('ERR_NOT_SUPPORTED')); | ||
$version.set('6.1'); | ||
expect(fn).not.toThrow(); | ||
}); | ||
}); | ||
describe('mount check', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
it.each([ | ||
{ fn: hide, name: 'hide' }, | ||
{ fn: show, name: 'show' }, | ||
])('$name function should throw ERR_NOT_MOUNTED if component was not mounted', ({ fn }) => { | ||
expect(fn).toThrow(new TypedError('ERR_NOT_MOUNTED')); | ||
isMounted.set(true); | ||
expect(fn).not.toThrow(); | ||
}); | ||
}); |
@@ -6,3 +6,2 @@ import { | ||
setStorageValue, | ||
supports, | ||
type EventListener, | ||
@@ -13,35 +12,39 @@ } from '@telegram-apps/bridge'; | ||
import { $version, postEvent } from '@/scopes/globals.js'; | ||
import { postEvent } from '@/scopes/globals.js'; | ||
import { createWithIsSupported } from '@/scopes/toolkit/createWithIsSupported.js'; | ||
import { subAndCall } from '@/utils/subAndCall.js'; | ||
import { createIsSupported } from '@/scopes/toolkit/createIsSupported.js'; | ||
import { createWithIsMounted } from '@/scopes/toolkit/createWithIsMounted.js'; | ||
type StorageValue = boolean; | ||
const MINI_APPS_METHOD = 'web_app_setup_back_button'; | ||
const CLICK_EVENT = 'back_button_pressed'; | ||
const WEB_APP_SETUP_BACK_BUTTON = 'web_app_setup_back_button'; | ||
const BACK_BUTTON_PRESSED = 'back_button_pressed'; | ||
const STORAGE_KEY = 'backButton'; | ||
/** | ||
* Hides the back button. | ||
* True if the component is currently mounted. | ||
*/ | ||
export function hide(): void { | ||
isVisible.set(false) | ||
} | ||
export const isMounted = signal(false); | ||
/** | ||
* @returns True if the back button is supported. | ||
* @returns True if the Back Button is supported. | ||
*/ | ||
export function isSupported(): boolean { | ||
return supports(MINI_APPS_METHOD, $version()); | ||
} | ||
export const isSupported = createIsSupported(WEB_APP_SETUP_BACK_BUTTON); | ||
const withIsSupported = createWithIsSupported(isSupported); | ||
const withIsMounted = createWithIsMounted(isMounted); | ||
/** | ||
* True if the component is currently visible. | ||
* Hides the Back Button. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export const isVisible = signal(false); | ||
export const hide = withIsMounted((): void => { | ||
isVisible.set(false); | ||
}); | ||
/** | ||
* True if the component is currently mounted. | ||
* True if the Back Button is currently visible. | ||
*/ | ||
export const isMounted = signal(false); | ||
export const isVisible = signal(false); | ||
@@ -53,4 +56,5 @@ /** | ||
* if it changed. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function mount(): void { | ||
export const mount = withIsSupported((): void => { | ||
if (!isMounted()) { | ||
@@ -61,7 +65,7 @@ isVisible.set(isPageReload() && getStorageValue<StorageValue>(STORAGE_KEY) || false); | ||
} | ||
} | ||
}); | ||
function onStateChanged(): void { | ||
const value = isVisible(); | ||
postEvent(MINI_APPS_METHOD, { is_visible: value }); | ||
postEvent(WEB_APP_SETUP_BACK_BUTTON, { is_visible: value }); | ||
setStorageValue<StorageValue>(STORAGE_KEY, value); | ||
@@ -71,24 +75,27 @@ } | ||
/** | ||
* Add a new back button click listener. | ||
* Add a new Back Button click listener. | ||
* @param fn - event listener. | ||
* @returns A function to remove bound listener. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function onClick(fn: EventListener<'back_button_pressed'>): VoidFunction { | ||
return on(CLICK_EVENT, fn); | ||
} | ||
export const onClick = withIsSupported( | ||
(fn: EventListener<'back_button_pressed'>): VoidFunction => on(BACK_BUTTON_PRESSED, fn), | ||
); | ||
/** | ||
* Removes the back button click listener. | ||
* Removes the Back Button click listener. | ||
* @param fn - an event listener. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function offClick(fn: EventListener<'back_button_pressed'>): void { | ||
off(CLICK_EVENT, fn); | ||
} | ||
export const offClick = withIsSupported((fn: EventListener<'back_button_pressed'>): void => { | ||
off(BACK_BUTTON_PRESSED, fn); | ||
}); | ||
/** | ||
* Shows the back button. | ||
* Shows the Back Button. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export function show(): void { | ||
isVisible.set(true) | ||
} | ||
export const show = withIsMounted((): void => { | ||
isVisible.set(true); | ||
}); | ||
@@ -95,0 +102,0 @@ /** |
import { | ||
supports, | ||
on, | ||
@@ -15,3 +14,3 @@ off, | ||
import { $version, postEvent, request } from '@/scopes/globals.js'; | ||
import { postEvent, request } from '@/scopes/globals.js'; | ||
import { createMountFn } from '@/scopes/createMountFn.js'; | ||
@@ -37,13 +36,24 @@ import { subAndCall } from '@/utils/subAndCall.js'; | ||
} from './types.js'; | ||
import { createIsSupported } from '@/scopes/toolkit/createIsSupported.js'; | ||
import { createWithIsSupported } from '@/scopes/toolkit/createWithIsSupported.js'; | ||
import { createWithIsMounted } from '@/scopes/toolkit/createWithIsMounted.js'; | ||
type StorageValue = State; | ||
const REQUEST_AUTH_METHOD = 'web_app_biometry_request_auth'; | ||
const REQUEST_ACCESS_METHOD = 'web_app_biometry_request_access'; | ||
const OPEN_SETTINGS_METHOD = 'web_app_biometry_open_settings'; | ||
const UPDATE_TOKEN_METHOD = 'web_app_biometry_update_token'; | ||
const BIOMETRY_INFO_RECEIVED_EVENT = 'biometry_info_received'; | ||
const WEB_APP_BIOMETRY_REQUEST_AUTH = 'web_app_biometry_request_auth'; | ||
const WEB_APP_BIOMETRY_REQUEST_ACCESS = 'web_app_biometry_request_access'; | ||
const WEB_APP_BIOMETRY_OPEN_SETTINGS = 'web_app_biometry_open_settings'; | ||
const WEB_APP_BIOMETRY_UPDATE_TOKEN = 'web_app_biometry_update_token'; | ||
const BIOMETRY_INFO_RECEIVED = 'biometry_info_received'; | ||
const STORAGE_KEY = 'biometry'; | ||
/** | ||
* @returns True if the biometry manager is supported. | ||
*/ | ||
export const isSupported = createIsSupported(WEB_APP_BIOMETRY_REQUEST_AUTH); | ||
const withIsSupported = createWithIsSupported(isSupported); | ||
const withIsMounted = createWithIsMounted(isMounted); | ||
/** | ||
* Attempts to authenticate a user using biometrics and fetch a previously stored | ||
@@ -56,49 +66,45 @@ * secure token. | ||
* @throws {TypedError} ERR_NOT_AVAILABLE | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export function authenticate(options?: AuthenticateOptions): CancelablePromise<{ | ||
/** | ||
* Authentication status. | ||
*/ | ||
status: BiometryAuthRequestStatus; | ||
/** | ||
* Token from the local secure storage saved previously. | ||
*/ | ||
token?: string; | ||
}> { | ||
if (isAuthenticating()) { | ||
return CancelablePromise.reject(new TypedError(ERR_ALREADY_CALLED)); | ||
} | ||
export const authenticate = withIsMounted( | ||
(options?: AuthenticateOptions): CancelablePromise<{ | ||
/** | ||
* Authentication status. | ||
*/ | ||
status: BiometryAuthRequestStatus; | ||
/** | ||
* Token from the local secure storage saved previously. | ||
*/ | ||
token?: string; | ||
}> => { | ||
if (isAuthenticating()) { | ||
return CancelablePromise.reject(new TypedError(ERR_ALREADY_CALLED)); | ||
} | ||
const s = state(); | ||
if (!s || !s.available) { | ||
return CancelablePromise.reject(new TypedError(ERR_NOT_AVAILABLE)); | ||
} | ||
const s = state(); | ||
if (!s || !s.available) { | ||
return CancelablePromise.reject(new TypedError(ERR_NOT_AVAILABLE)); | ||
} | ||
isAuthenticating.set(true); | ||
isAuthenticating.set(true); | ||
options ||= {}; | ||
return request(REQUEST_AUTH_METHOD, 'biometry_auth_requested', { | ||
...options, | ||
params: { reason: (options.reason || '').trim() }, | ||
}) | ||
.then(response => { | ||
const { token } = response; | ||
if (typeof token === 'string') { | ||
state.set({ ...s, token }); | ||
} | ||
return response; | ||
options ||= {}; | ||
return request(WEB_APP_BIOMETRY_REQUEST_AUTH, 'biometry_auth_requested', { | ||
...options, | ||
params: { reason: (options.reason || '').trim() }, | ||
}) | ||
.finally(() => { | ||
isAuthenticating.set(false); | ||
}); | ||
} | ||
.then(response => { | ||
const { token } = response; | ||
if (typeof token === 'string') { | ||
state.set({ ...s, token }); | ||
} | ||
return response; | ||
}) | ||
.finally(() => { | ||
isAuthenticating.set(false); | ||
}); | ||
}, | ||
); | ||
/** | ||
* @returns True if the biometry manager is supported. | ||
*/ | ||
export function isSupported(): boolean { | ||
return supports(REQUEST_AUTH_METHOD, $version()); | ||
} | ||
/** | ||
* Opens the biometric access settings for bots. Useful when you need to request biometrics | ||
@@ -111,5 +117,5 @@ * access to users who haven't granted it yet. | ||
*/ | ||
export function openSettings(): void { | ||
postEvent(OPEN_SETTINGS_METHOD); | ||
} | ||
export const openSettings = withIsSupported((): void => { | ||
postEvent(WEB_APP_BIOMETRY_OPEN_SETTINGS); | ||
}); | ||
@@ -122,26 +128,29 @@ /** | ||
* @throws {TypedError} ERR_NOT_AVAILABLE | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export function requestAccess(options?: RequestAccessOptions): CancelablePromise<boolean> { | ||
if (isRequestingAccess()) { | ||
return CancelablePromise.reject(new TypedError(ERR_ALREADY_CALLED)); | ||
} | ||
isRequestingAccess.set(true); | ||
export const requestAccess = withIsMounted( | ||
(options?: RequestAccessOptions): CancelablePromise<boolean> => { | ||
if (isRequestingAccess()) { | ||
return CancelablePromise.reject(new TypedError(ERR_ALREADY_CALLED)); | ||
} | ||
isRequestingAccess.set(true); | ||
options ||= {}; | ||
return request(REQUEST_ACCESS_METHOD, BIOMETRY_INFO_RECEIVED_EVENT, { | ||
...options, | ||
params: { reason: options.reason || '' }, | ||
}) | ||
.then(eventToState) | ||
.then((info) => { | ||
if (!info.available) { | ||
throw new TypedError(ERR_NOT_AVAILABLE); | ||
} | ||
state.set(info); | ||
return info.accessGranted; | ||
options ||= {}; | ||
return request(WEB_APP_BIOMETRY_REQUEST_ACCESS, BIOMETRY_INFO_RECEIVED, { | ||
...options, | ||
params: { reason: options.reason || '' }, | ||
}) | ||
.finally(() => { | ||
isRequestingAccess.set(false); | ||
}); | ||
} | ||
.then(eventToState) | ||
.then((info) => { | ||
if (!info.available) { | ||
throw new TypedError(ERR_NOT_AVAILABLE); | ||
} | ||
state.set(info); | ||
return info.accessGranted; | ||
}) | ||
.finally(() => { | ||
isRequestingAccess.set(false); | ||
}); | ||
}, | ||
); | ||
@@ -151,10 +160,6 @@ /** | ||
* @throws {TypedError} ERR_ALREADY_CALLED | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export const mount = createMountFn<State>( | ||
(options) => { | ||
// May be not supported. | ||
if (!isSupported()) { | ||
return { available: false }; | ||
} | ||
// Try to restore the state using the storage. | ||
@@ -171,7 +176,7 @@ const s = isPageReload() && getStorageValue<StorageValue>(STORAGE_KEY); | ||
result => { | ||
on(BIOMETRY_INFO_RECEIVED_EVENT, onBiometryInfoReceived); | ||
on(BIOMETRY_INFO_RECEIVED, onBiometryInfoReceived); | ||
subAndCall(state, onStateChanged); | ||
state.set(result); | ||
}, | ||
{ isMounted, mountError, isMounting }, | ||
{ isMounted, mountError, isMounting, isSupported }, | ||
); | ||
@@ -191,4 +196,4 @@ | ||
*/ | ||
export function unmount(): void { | ||
off(BIOMETRY_INFO_RECEIVED_EVENT, onBiometryInfoReceived); | ||
export function unmount() { | ||
off(BIOMETRY_INFO_RECEIVED, onBiometryInfoReceived); | ||
state.unsub(onStateChanged); | ||
@@ -201,12 +206,15 @@ } | ||
* @returns Promise with `true`, if token was updated. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export function updateToken(options?: UpdateTokenOptions): CancelablePromise<BiometryTokenUpdateStatus> { | ||
options ||= {}; | ||
return request(UPDATE_TOKEN_METHOD, 'biometry_token_updated', { | ||
...options, | ||
params: { | ||
token: options.token || '', | ||
reason: options.reason, | ||
}, | ||
}).then(r => r.status); | ||
} | ||
export const updateToken = withIsMounted( | ||
(options?: UpdateTokenOptions): CancelablePromise<BiometryTokenUpdateStatus> => { | ||
options ||= {}; | ||
return request(WEB_APP_BIOMETRY_UPDATE_TOKEN, 'biometry_token_updated', { | ||
...options, | ||
params: { | ||
token: options.token || '', | ||
reason: options.reason, | ||
}, | ||
}).then(r => r.status); | ||
}, | ||
); |
import type { ExecuteWithOptions, CancelablePromise } from '@telegram-apps/bridge'; | ||
import { request } from '@/scopes/globals.js'; | ||
import { withIsSupported } from '@/scopes/withIsSupported.js'; | ||
import { withIsSupported } from '@/scopes/toolkit/withIsSupported.js'; | ||
@@ -9,3 +9,3 @@ import { eventToState } from './eventToState.js'; | ||
const GET_INFO_METHOD = 'web_app_biometry_get_info'; | ||
const WEB_APP_BIOMETRY_GET_INFO = 'web_app_biometry_get_info'; | ||
@@ -18,4 +18,4 @@ /** | ||
(options?: ExecuteWithOptions): CancelablePromise<State> => { | ||
return request(GET_INFO_METHOD, 'biometry_info_received', options).then(eventToState); | ||
}, GET_INFO_METHOD, | ||
return request(WEB_APP_BIOMETRY_GET_INFO, 'biometry_info_received', options).then(eventToState); | ||
}, WEB_APP_BIOMETRY_GET_INFO, | ||
); |
@@ -1,6 +0,8 @@ | ||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { mockSessionStorageGetItem, mockPageReload, mockSessionStorageSetItem } from 'test-utils'; | ||
import { TypedError } from '@telegram-apps/bridge'; | ||
import { mockPostEvent } from '@test-utils/mockPostEvent.js'; | ||
import { resetPackageState } from '@test-utils/reset/reset.js'; | ||
import { $version } from '@/scopes/globals.js'; | ||
@@ -22,65 +24,2 @@ import { | ||
describe('mounted', () => { | ||
beforeEach(mount); | ||
afterEach(unmount); | ||
describe('disableConfirmation', () => { | ||
it('should call postEvent with "web_app_setup_closing_behavior" and { need_confirmation: false }', () => { | ||
isConfirmationEnabled.set(true); | ||
const spy = mockPostEvent(); | ||
disableConfirmation(); | ||
disableConfirmation(); | ||
disableConfirmation(); | ||
expect(spy).toBeCalledTimes(1); | ||
expect(spy).toBeCalledWith('web_app_setup_closing_behavior', { need_confirmation: false }); | ||
}); | ||
}); | ||
describe('enableConfirmation', () => { | ||
it('should call postEvent with "web_app_setup_closing_behavior" and { need_confirmation: true }', () => { | ||
isConfirmationEnabled.set(false); | ||
const spy = mockPostEvent(); | ||
enableConfirmation(); | ||
enableConfirmation(); | ||
enableConfirmation(); | ||
expect(spy).toBeCalledTimes(1); | ||
expect(spy).toBeCalledWith('web_app_setup_closing_behavior', { need_confirmation: true }); | ||
}); | ||
}); | ||
}); | ||
describe('not mounted', () => { | ||
describe('disableConfirmation', () => { | ||
it('should not call postEvent', () => { | ||
isConfirmationEnabled.set(true); | ||
const spy = mockPostEvent(); | ||
disableConfirmation(); | ||
disableConfirmation(); | ||
disableConfirmation(); | ||
expect(spy).toBeCalledTimes(0); | ||
}); | ||
}); | ||
describe('enableConfirmation', () => { | ||
it('should not call postEvent', () => { | ||
isConfirmationEnabled.set(false); | ||
const spy = mockPostEvent(); | ||
enableConfirmation(); | ||
enableConfirmation(); | ||
enableConfirmation(); | ||
expect(spy).toBeCalledTimes(0); | ||
}); | ||
}); | ||
}); | ||
describe('disableConfirmation', () => { | ||
it('should set isConfirmationNeeded = false', () => { | ||
isConfirmationEnabled.set(true); | ||
expect(isConfirmationEnabled()).toBe(true); | ||
disableConfirmation(); | ||
expect(isConfirmationEnabled()).toBe(false); | ||
}); | ||
}); | ||
describe('mount', () => { | ||
@@ -153,9 +92,57 @@ it('should call postEvent with "web_app_setup_closing_behavior"', () => { | ||
describe('enableConfirmation', () => { | ||
it('should set isConfirmationNeeded = true', () => { | ||
isConfirmationEnabled.set(false); | ||
expect(isConfirmationEnabled()).toBe(false); | ||
enableConfirmation(); | ||
expect(isConfirmationEnabled()).toBe(true); | ||
describe('mounted', () => { | ||
beforeEach(mount); | ||
describe('disableConfirmation', () => { | ||
it('should set isConfirmationNeeded = false', () => { | ||
isConfirmationEnabled.set(true); | ||
expect(isConfirmationEnabled()).toBe(true); | ||
disableConfirmation(); | ||
expect(isConfirmationEnabled()).toBe(false); | ||
}); | ||
it('should call postEvent with "web_app_setup_closing_behavior" and { need_confirmation: false }', () => { | ||
isConfirmationEnabled.set(true); | ||
const spy = mockPostEvent(); | ||
disableConfirmation(); | ||
disableConfirmation(); | ||
disableConfirmation(); | ||
expect(spy).toBeCalledTimes(1); | ||
expect(spy).toBeCalledWith('web_app_setup_closing_behavior', { need_confirmation: false }); | ||
}); | ||
}); | ||
describe('enableConfirmation', () => { | ||
it('should set isConfirmationNeeded = true', () => { | ||
isConfirmationEnabled.set(false); | ||
expect(isConfirmationEnabled()).toBe(false); | ||
enableConfirmation(); | ||
expect(isConfirmationEnabled()).toBe(true); | ||
}); | ||
it('should call postEvent with "web_app_setup_closing_behavior" and { need_confirmation: true }', () => { | ||
isConfirmationEnabled.set(false); | ||
const spy = mockPostEvent(); | ||
enableConfirmation(); | ||
enableConfirmation(); | ||
enableConfirmation(); | ||
expect(spy).toBeCalledTimes(1); | ||
expect(spy).toBeCalledWith('web_app_setup_closing_behavior', { need_confirmation: true }); | ||
}); | ||
}); | ||
}); | ||
describe('unmounted', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
it.each([ | ||
{ fn: disableConfirmation, name: 'disableConfirmation' }, | ||
{ fn: enableConfirmation, name: 'enableConfirmation' }, | ||
])('$name function should throw ERR_NOT_MOUNTED if component was not mounted', ({ fn }) => { | ||
expect(fn).toThrow(new TypedError('ERR_NOT_MOUNTED')); | ||
isMounted.set(true); | ||
expect(fn).not.toThrow(); | ||
}); | ||
}); |
@@ -7,2 +7,3 @@ import { isPageReload } from '@telegram-apps/navigation'; | ||
import { subAndCall } from '@/utils/subAndCall.js'; | ||
import { createWithIsMounted } from '@/scopes/toolkit/createWithIsMounted.js'; | ||
@@ -14,12 +15,15 @@ type StorageValue = boolean; | ||
/** | ||
* Disables the confirmation dialog when closing the Mini App. | ||
* True if the component is currently mounted. | ||
*/ | ||
export function disableConfirmation(): void { | ||
isConfirmationEnabled.set(false); | ||
} | ||
export const isMounted = signal(false); | ||
const withIsMounted = createWithIsMounted(isMounted); | ||
/** | ||
* True if the component is currently mounted. | ||
* Disables the confirmation dialog when closing the Mini App. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export const isMounted = signal(false); | ||
export const disableConfirmation = withIsMounted((): void => { | ||
isConfirmationEnabled.set(false); | ||
}); | ||
@@ -33,6 +37,7 @@ /** | ||
* Enables the confirmation dialog when closing the Mini App. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export function enableConfirmation(): void { | ||
export const enableConfirmation = withIsMounted((): void => { | ||
isConfirmationEnabled.set(true); | ||
} | ||
}); | ||
@@ -39,0 +44,0 @@ /** |
@@ -1,17 +0,27 @@ | ||
import { CancelablePromise, type ExecuteWithOptions, supports } from '@telegram-apps/bridge'; | ||
import { CancelablePromise, type ExecuteWithOptions } from '@telegram-apps/bridge'; | ||
import { array, object, string } from '@telegram-apps/transformers'; | ||
import { $version, invokeCustomMethod } from '@/scopes/globals.js'; | ||
import { invokeCustomMethod } from '@/scopes/globals.js'; | ||
import { createWithIsSupported } from '@/scopes/toolkit/createWithIsSupported.js'; | ||
import { createIsSupported } from '@/scopes/toolkit/createIsSupported.js'; | ||
const MINI_APPS_METHOD = 'web_app_invoke_custom_method'; | ||
const WEB_APP_INVOKE_CUSTOM_METHOD = 'web_app_invoke_custom_method'; | ||
/** | ||
* @returns True if the Cloud Storage is supported. | ||
*/ | ||
export const isSupported = createIsSupported(WEB_APP_INVOKE_CUSTOM_METHOD); | ||
const withIsSupported = createWithIsSupported(isSupported); | ||
/** | ||
* Deletes specified key or keys from the cloud storage. | ||
* @param keyOrKeys - key or keys to delete. | ||
* @param options - request execution options. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function deleteItem( | ||
export const deleteItem = withIsSupported(( | ||
keyOrKeys: string | string[], | ||
options?: ExecuteWithOptions, | ||
): CancelablePromise<void> { | ||
): CancelablePromise<void> => { | ||
const keys = Array.isArray(keyOrKeys) ? keyOrKeys : [keyOrKeys]; | ||
@@ -21,3 +31,3 @@ return keys.length | ||
: CancelablePromise.resolve(); | ||
} | ||
}); | ||
@@ -30,3 +40,3 @@ /** | ||
*/ | ||
export function getItem<K extends string>( | ||
function _getItem<K extends string>( | ||
keys: K[], | ||
@@ -42,5 +52,5 @@ options?: ExecuteWithOptions, | ||
*/ | ||
export function getItem(key: string, options?: ExecuteWithOptions): CancelablePromise<string>; | ||
function _getItem(key: string, options?: ExecuteWithOptions): CancelablePromise<string>; | ||
export function getItem( | ||
function _getItem( | ||
keyOrKeys: string | string[], | ||
@@ -63,15 +73,16 @@ options?: ExecuteWithOptions, | ||
/** | ||
* Returns a list of all keys presented in the cloud storage. | ||
* @param options - request execution options. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function getKeys(options?: ExecuteWithOptions): CancelablePromise<string[]> { | ||
return invokeCustomMethod('getStorageKeys', {}, options).then(array(string())()); | ||
} | ||
export const getItem = withIsSupported(_getItem); | ||
/** | ||
* @returns True if the cloud storage is supported. | ||
* Returns a list of all keys presented in the cloud storage. | ||
* @param options - request execution options. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function isSupported(): boolean { | ||
return supports(MINI_APPS_METHOD, $version()); | ||
} | ||
export const getKeys = withIsSupported( | ||
(options?: ExecuteWithOptions): CancelablePromise<string[]> => { | ||
return invokeCustomMethod('getStorageKeys', {}, options).then(array(string())()); | ||
}, | ||
); | ||
@@ -83,9 +94,11 @@ /** | ||
* @param options - request execution options. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function setItem( | ||
key: string, | ||
value: string, | ||
options?: ExecuteWithOptions, | ||
): CancelablePromise<void> { | ||
return invokeCustomMethod('saveStorageValue', { key, value }, options).then(); | ||
} | ||
export const setItem = withIsSupported( | ||
(key: string, value: string, options?: ExecuteWithOptions): CancelablePromise<void> => { | ||
return invokeCustomMethod('saveStorageValue', { | ||
key, | ||
value, | ||
}, options).then(); | ||
}, | ||
); |
import { beforeEach, describe, expect, it } from 'vitest'; | ||
import { TypedError } from '@telegram-apps/bridge'; | ||
@@ -22,2 +23,3 @@ import { mockPostEvent } from '@test-utils/mockPostEvent.js'; | ||
it('should call "web_app_trigger_haptic_feedback" method with { type: "impact", style: {{style}} }', () => { | ||
$version.set('10'); | ||
const spy = mockPostEvent(); | ||
@@ -48,2 +50,3 @@ impactOccurred('heavy'); | ||
it('should call "web_app_trigger_haptic_feedback" method with { type: "notification", notification_type: {{type}} }', () => { | ||
$version.set('10'); | ||
const spy = mockPostEvent(); | ||
@@ -61,2 +64,3 @@ notificationOccurred('success'); | ||
it('should call "web_app_trigger_haptic_feedback" method with { type: "selection_change" }', () => { | ||
$version.set('10'); | ||
const spy = mockPostEvent(); | ||
@@ -70,1 +74,15 @@ selectionChanged(); | ||
}); | ||
describe('support check', () => { | ||
it.each([ | ||
{ fn: impactOccurred, name: 'impactOccurred' }, | ||
{ fn: notificationOccurred, name: 'notificationOccurred' }, | ||
{ fn: selectionChanged, name: 'selectionChanged' }, | ||
])('$name function should throw ERR_NOT_SUPPORTED if version is less than 6.1', ({ fn }) => { | ||
$version.set('6.0'); | ||
expect(fn).toThrow(new TypedError('ERR_NOT_SUPPORTED')); | ||
$version.set('6.1'); | ||
expect(fn).not.toThrow(); | ||
}); | ||
}); |
@@ -1,35 +0,38 @@ | ||
import { | ||
supports, | ||
type ImpactHapticFeedbackStyle, | ||
type NotificationHapticFeedbackType, | ||
import type { | ||
ImpactHapticFeedbackStyle, | ||
NotificationHapticFeedbackType, | ||
} from '@telegram-apps/bridge'; | ||
import { $version, postEvent } from '@/scopes/globals.js'; | ||
import { postEvent } from '@/scopes/globals.js'; | ||
import { createWithIsSupported } from '@/scopes/toolkit/createWithIsSupported.js'; | ||
import { createIsSupported } from '@/scopes/toolkit/createIsSupported.js'; | ||
const MINI_APPS_METHOD = 'web_app_trigger_haptic_feedback'; | ||
const WEB_APP_TRIGGER_HAPTIC_FEEDBACK = 'web_app_trigger_haptic_feedback'; | ||
/** | ||
* @returns True if the Haptic Feedback is supported. | ||
*/ | ||
export const isSupported = createIsSupported(WEB_APP_TRIGGER_HAPTIC_FEEDBACK); | ||
const withIsSupported = createWithIsSupported(isSupported); | ||
/** | ||
* A method tells that an impact occurred. The Telegram app may play the appropriate haptics based | ||
* on style value passed. | ||
* @param style - impact style. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function impactOccurred(style: ImpactHapticFeedbackStyle): void { | ||
postEvent(MINI_APPS_METHOD, { type: 'impact', impact_style: style }); | ||
} | ||
export const impactOccurred = withIsSupported((style: ImpactHapticFeedbackStyle): void => { | ||
postEvent(WEB_APP_TRIGGER_HAPTIC_FEEDBACK, { type: 'impact', impact_style: style }); | ||
}); | ||
/** | ||
* @returns True if the haptic feedback is supported. | ||
*/ | ||
export function isSupported(): boolean { | ||
return supports(MINI_APPS_METHOD, $version()); | ||
} | ||
/** | ||
* A method tells that a task or action has succeeded, failed, or produced a warning. The Telegram | ||
* app may play the appropriate haptics based on type value passed. | ||
* @param type - notification type. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function notificationOccurred(type: NotificationHapticFeedbackType): void { | ||
postEvent(MINI_APPS_METHOD, { type: 'notification', notification_type: type }); | ||
} | ||
export const notificationOccurred = withIsSupported((type: NotificationHapticFeedbackType): void => { | ||
postEvent(WEB_APP_TRIGGER_HAPTIC_FEEDBACK, { type: 'notification', notification_type: type }); | ||
}); | ||
@@ -42,5 +45,6 @@ /** | ||
* selection changes. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function selectionChanged(): void { | ||
postEvent(MINI_APPS_METHOD, { type: 'selection_change' }); | ||
} | ||
export const selectionChanged = withIsSupported((): void => { | ||
postEvent(WEB_APP_TRIGGER_HAPTIC_FEEDBACK, { type: 'selection_change' }); | ||
}); |
import { | ||
TypedError, | ||
supports, | ||
type ExecuteWithOptions, | ||
@@ -10,6 +9,8 @@ type InvoiceStatus, | ||
import { $version, request } from '@/scopes/globals.js'; | ||
import { request } from '@/scopes/globals.js'; | ||
import { ERR_INVALID_HOSTNAME, ERR_INVALID_SLUG, ERR_ALREADY_CALLED } from '@/errors.js'; | ||
import { withIsSupported } from '@/scopes/toolkit/withIsSupported.js'; | ||
import { createIsSupported } from '@/scopes/toolkit/createIsSupported.js'; | ||
const MINI_APPS_METHOD = 'web_app_open_invoice'; | ||
const WEB_APP_OPEN_INVOICE = 'web_app_open_invoice'; | ||
@@ -22,7 +23,5 @@ /** | ||
/** | ||
* @returns True if the invoice is supported. | ||
* @returns True if the Invoice is supported. | ||
*/ | ||
export function isSupported(): boolean { | ||
return supports(MINI_APPS_METHOD, $version()); | ||
} | ||
export const isSupported = createIsSupported(WEB_APP_OPEN_INVOICE); | ||
@@ -38,3 +37,3 @@ /** | ||
*/ | ||
export function open(slug: string, options?: ExecuteWithPostEvent): Promise<InvoiceStatus>; | ||
export function _open(slug: string, options?: ExecuteWithPostEvent): Promise<InvoiceStatus>; | ||
@@ -55,3 +54,3 @@ /** | ||
*/ | ||
export function open( | ||
export function _open( | ||
url: string, | ||
@@ -62,3 +61,3 @@ type: 'url', | ||
export async function open( | ||
export async function _open( | ||
urlOrSlug: string, | ||
@@ -95,3 +94,3 @@ optionsOrType?: 'url' | ExecuteWithOptions, | ||
return request(MINI_APPS_METHOD, 'invoice_closed', { | ||
return request(WEB_APP_OPEN_INVOICE, 'invoice_closed', { | ||
...options, | ||
@@ -106,1 +105,6 @@ params: { slug }, | ||
} | ||
/** | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export const open = withIsSupported(_open, isSupported); |
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { mockSessionStorageSetItem } from 'test-utils'; | ||
import { emitMiniAppsEvent, type ThemeParams } from '@telegram-apps/bridge'; | ||
import { emitMiniAppsEvent, type ThemeParams, TypedError } from '@telegram-apps/bridge'; | ||
@@ -120,18 +120,2 @@ import { mockPostEvent } from '@test-utils/mockPostEvent.js'; | ||
describe('not mounted', () => { | ||
describe('setParams', () => { | ||
it('should not call postEvent', () => { | ||
const spy = mockPostEvent(); | ||
setParams({ text: 'ABC' }); | ||
expect(spy).toHaveBeenCalledTimes(0); | ||
}); | ||
it('should not save state in storage', () => { | ||
const spy = mockSessionStorageSetItem(); | ||
setParams({ text: 'ABC' }); | ||
expect(spy).toHaveBeenCalledTimes(0); | ||
}); | ||
}); | ||
}); | ||
describe('mount', () => { | ||
@@ -241,2 +225,6 @@ it('should call postEvent with "web_app_setup_main_button"', () => { | ||
describe('setParams', () => { | ||
beforeEach(() => { | ||
isMounted.set(true); | ||
}); | ||
it('should merge passed object with the state', () => { | ||
@@ -293,1 +281,11 @@ internalState.set({ | ||
}); | ||
describe('mount check', () => { | ||
it.each([ | ||
{ fn: () => setParams({}), name: 'setParams' }, | ||
])('$name function should throw ERR_NOT_MOUNTED if component was not mounted', ({ fn }) => { | ||
expect(fn).toThrow(new TypedError('ERR_NOT_MOUNTED')); | ||
isMounted.set(true); | ||
expect(fn).not.toThrow(); | ||
}); | ||
}); |
@@ -12,2 +12,3 @@ import { | ||
import { subAndCall } from '@/utils/subAndCall.js'; | ||
import { createWithIsMounted } from '@/scopes/toolkit/createWithIsMounted.js'; | ||
@@ -19,6 +20,8 @@ import { internalState, isMounted, state } from './signals.js'; | ||
const MINI_APPS_METHOD = 'web_app_setup_main_button'; | ||
const CLICK_EVENT = 'main_button_pressed'; | ||
const WEB_APP_SETUP_MAIN_BUTTON = 'web_app_setup_main_button'; | ||
const MAIN_BUTTON_PRESSED = 'main_button_pressed'; | ||
const STORAGE_KEY = 'mainButton'; | ||
const withIsMounted = createWithIsMounted(isMounted); | ||
/** | ||
@@ -47,3 +50,3 @@ * Mounts the component. | ||
export function onClick(fn: EventListener<'main_button_pressed'>): VoidFunction { | ||
return on(CLICK_EVENT, fn); | ||
return on(MAIN_BUTTON_PRESSED, fn); | ||
} | ||
@@ -56,3 +59,3 @@ | ||
export function offClick(fn: EventListener<'main_button_pressed'>): void { | ||
off(CLICK_EVENT, fn); | ||
off(MAIN_BUTTON_PRESSED, fn); | ||
} | ||
@@ -69,3 +72,3 @@ | ||
// Some version of Telegram will crash due to the empty value of the text. | ||
s.text && postEvent(MINI_APPS_METHOD, { | ||
s.text && postEvent(WEB_APP_SETUP_MAIN_BUTTON, { | ||
color: s.backgroundColor, | ||
@@ -84,4 +87,5 @@ has_shine_effect: s.hasShineEffect, | ||
* @param updates - state changes to perform. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export function setParams(updates: Partial<State>): void { | ||
export const setParams = withIsMounted((updates: Partial<State>): void => { | ||
internalState.set({ | ||
@@ -93,3 +97,3 @@ ...internalState(), | ||
}); | ||
} | ||
}); | ||
@@ -96,0 +100,0 @@ /** |
export { | ||
bindCssVars as bindMiniAppCssVars, | ||
close as closeMiniApp, | ||
isSupported as isMiniAppSupported, | ||
mount as mountMiniApp, | ||
@@ -5,0 +6,0 @@ ready as miniAppReady, |
import { beforeEach, describe, expect, it, MockInstance, vi } from 'vitest'; | ||
import { createWindow } from 'test-utils'; | ||
import type { ThemeParams } from '@telegram-apps/bridge'; | ||
import { ThemeParams, TypedError } from '@telegram-apps/bridge'; | ||
@@ -10,3 +10,3 @@ import { mockPostEvent } from '@test-utils/mockPostEvent.js'; | ||
import { headerColor, backgroundColor, bottomBarColor } from './signals.js'; | ||
import { headerColor, backgroundColor, bottomBarColor, isMounted } from './signals.js'; | ||
import { | ||
@@ -20,2 +20,3 @@ bindCssVars, | ||
setHeaderColor, | ||
isSupported, | ||
} from './methods.js'; | ||
@@ -51,2 +52,7 @@ | ||
describe('bindCssVars', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
isMounted.set(true); | ||
}); | ||
describe('background color', () => { | ||
@@ -80,3 +86,6 @@ it('should set --tg-bg-color == backgroundColorRGB()', () => { | ||
describe('mounted', () => { | ||
beforeEach(mount); | ||
beforeEach(() => { | ||
$version.set('10'); | ||
mount(); | ||
}); | ||
@@ -157,33 +166,21 @@ describe('background color', () => { | ||
describe('mounted', () => { | ||
beforeEach(mount); | ||
describe('setBackgroundColor', () => { | ||
it('should call "web_app_set_background_color" method with { color: {{color}} }', () => { | ||
const spy = mockPostEvent(); | ||
expect(spy).toHaveBeenCalledTimes(0); | ||
setBackgroundColor('#abcdef'); | ||
expect(spy).toHaveBeenCalledTimes(1); | ||
expect(spy).toHaveBeenCalledWith('web_app_set_background_color', { color: '#abcdef' }); | ||
}); | ||
describe('close', () => { | ||
it('should call "web_app_close" with "return_back" option', () => { | ||
const spy = mockPostEvent(); | ||
close(false); | ||
expect(spy).toHaveBeenCalledOnce(); | ||
expect(spy).toHaveBeenCalledWith('web_app_close', { return_back: false }); | ||
}); | ||
}); | ||
describe('setBottomBarColor', () => { | ||
it('should call "web_app_set_bottom_bar_color" method with { color: {{color}} }', () => { | ||
const spy = mockPostEvent(); | ||
expect(spy).toHaveBeenCalledTimes(0); | ||
setBottomBarColor('#abcdef'); | ||
expect(spy).toHaveBeenCalledTimes(1); | ||
expect(spy).toHaveBeenCalledWith('web_app_set_bottom_bar_color', { color: '#abcdef' }); | ||
}); | ||
}); | ||
describe('isSupported', () => { | ||
it('should return false if version is less than 6.1. True otherwise', () => { | ||
$version.set('6.0'); | ||
expect(isSupported()).toBe(false); | ||
describe('setHeaderColor', () => { | ||
it('should call "web_app_set_header_color" method with { color_key: {{color_key}} }', () => { | ||
const spy = mockPostEvent(); | ||
expect(spy).toHaveBeenCalledTimes(0); | ||
setHeaderColor('secondary_bg_color'); | ||
expect(spy).toHaveBeenCalledTimes(1); | ||
expect(spy).toHaveBeenCalledWith('web_app_set_header_color', { color_key: 'secondary_bg_color' }); | ||
}); | ||
$version.set('6.1'); | ||
expect(isSupported()).toBe(true); | ||
$version.set('6.2'); | ||
expect(isSupported()).toBe(true); | ||
}); | ||
@@ -193,30 +190,36 @@ }); | ||
describe('mount', () => { | ||
beforeEach(() => { | ||
$version.set('6.1'); | ||
}); | ||
it('should call postEvent with "web_app_set_background_color"', () => { | ||
const spy = mockPostEvent(); | ||
mount(); | ||
expect(spy).toHaveBeenCalledTimes(3); | ||
expect(spy).toHaveBeenCalledTimes(2); | ||
expect(spy).toHaveBeenNthCalledWith(1, 'web_app_set_background_color', { color: 'bg_color' }); | ||
}); | ||
it('should call postEvent with "web_app_set_bottom_bar_color"', () => { | ||
const spy = mockPostEvent(); | ||
mount(); | ||
expect(spy).toHaveBeenCalledTimes(3); | ||
expect(spy).toHaveBeenNthCalledWith(2, 'web_app_set_bottom_bar_color', { color: 'bottom_bar_bg_color' }); | ||
}); | ||
it('should call postEvent with "web_app_set_header_color"', () => { | ||
const spy = mockPostEvent(); | ||
mount(); | ||
expect(spy).toHaveBeenCalledTimes(3); | ||
expect(spy).toHaveBeenNthCalledWith(3, 'web_app_set_header_color', { color_key: 'bg_color' }); | ||
expect(spy).toHaveBeenCalledTimes(2); | ||
expect(spy).toHaveBeenNthCalledWith(2, 'web_app_set_header_color', { color_key: 'bg_color' }); | ||
}); | ||
}); | ||
describe('close', () => { | ||
it('should call "web_app_close" with "return_back" option', () => { | ||
const spy = mockPostEvent(); | ||
close(false); | ||
expect(spy).toHaveBeenCalledOnce(); | ||
expect(spy).toHaveBeenCalledWith('web_app_close', { return_back: false }); | ||
describe('web_app_set_bottom_bar_color', () => { | ||
it('should not call postEvent with method if version is 7.9 or less', () => { | ||
$version.set('7.9'); | ||
const spy = mockPostEvent(); | ||
mount(); | ||
expect(spy).toHaveBeenCalledTimes(2); | ||
expect(spy).not.toHaveBeenCalledWith('web_app_set_bottom_bar_color', expect.anything()); | ||
}); | ||
it('should call postEvent with method if version is 7.10 or more', () => { | ||
$version.set('7.10'); | ||
const spy = mockPostEvent(); | ||
mount(); | ||
expect(spy).toHaveBeenCalledTimes(3); | ||
expect(spy).toHaveBeenNthCalledWith(2, 'web_app_set_bottom_bar_color', { color: 'bottom_bar_bg_color' }); | ||
}); | ||
}); | ||
@@ -278,14 +281,70 @@ }); | ||
describe('supports "color"', () => { | ||
it('should return false if version is less than 6.9. True otherwise', () => { | ||
describe('supports', () => { | ||
beforeEach(() => { | ||
isMounted.set(true); | ||
}); | ||
it('should throw if version is less than 6.9', () => { | ||
$version.set('6.8'); | ||
expect(setHeaderColor.supports('color')).toBe(false); | ||
expect(() => setHeaderColor('#ffaaaa')).toThrow( | ||
new TypedError('ERR_NOT_SUPPORTED', 'Parameter "color" is not supported'), | ||
); | ||
$version.set('6.9'); | ||
expect(setHeaderColor.supports('color')).toBe(true); | ||
expect(() => setHeaderColor('#ffaaaa')).not.toThrow(); | ||
}); | ||
$version.set('6.10'); | ||
expect(setHeaderColor.supports('color')).toBe(true); | ||
describe('color', () => { | ||
it('should return false if version is less than 6.9. True otherwise', () => { | ||
$version.set('6.8'); | ||
expect(setHeaderColor.supports('color')).toBe(false); | ||
$version.set('6.9'); | ||
expect(setHeaderColor.supports('color')).toBe(true); | ||
$version.set('6.10'); | ||
expect(setHeaderColor.supports('color')).toBe(true); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('support check', () => { | ||
beforeEach(() => { | ||
isMounted.set(true); | ||
}); | ||
it.each([ | ||
{ fn: mount, name: 'mount', version: '6.1' }, | ||
{ fn: () => setBackgroundColor('bg_color'), name: 'setBackgroundColor', version: '6.1' }, | ||
{ fn: () => setHeaderColor('bg_color'), name: 'setHeaderColor', version: '6.1' }, | ||
{ fn: () => setBottomBarColor('bg_color'), name: 'setBottomBarColor', version: '7.10' }, | ||
])('$name function should throw ERR_NOT_SUPPORTED if version is less than $version', ({ | ||
fn, | ||
version, | ||
}) => { | ||
const [major, minor] = version.split('.').map(Number); | ||
$version.set(`${major}.${minor - 1}`); | ||
expect(fn).toThrow(new TypedError('ERR_NOT_SUPPORTED')); | ||
$version.set(version); | ||
expect(fn).not.toThrow(); | ||
}); | ||
}); | ||
describe('mount check', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
it.each([ | ||
{ fn: bindCssVars, name: 'bindCssVars' }, | ||
{ fn: () => setBackgroundColor('bg_color'), name: 'setBackgroundColor' }, | ||
{ fn: () => setHeaderColor('bg_color'), name: 'setHeaderColor' }, | ||
{ fn: () => setBottomBarColor('bg_color'), name: 'setBottomBarColor' }, | ||
])('$name function should throw ERR_NOT_MOUNTED if component was not mounted', ({ fn }) => { | ||
expect(fn).toThrow(new TypedError('ERR_NOT_MOUNTED')); | ||
isMounted.set(true); | ||
expect(fn).not.toThrow(); | ||
}); | ||
}); |
@@ -9,12 +9,12 @@ import { | ||
TypedError, | ||
supports, | ||
type RGB, | ||
type BottomBarColor, | ||
type BackgroundColor, | ||
} from '@telegram-apps/bridge'; | ||
import { isRGB } from '@telegram-apps/transformers'; | ||
import { isPageReload } from '@telegram-apps/navigation'; | ||
import type { Computed } from '@telegram-apps/signals'; | ||
import { computed, type Computed } from '@telegram-apps/signals'; | ||
import { postEvent } from '@/scopes/globals.js'; | ||
import { withIsSupported } from '@/scopes/withIsSupported.js'; | ||
import { withSupports } from '@/scopes/withSupports.js'; | ||
import { $version, postEvent } from '@/scopes/globals.js'; | ||
import { ERR_ALREADY_CALLED } from '@/errors.js'; | ||
@@ -25,2 +25,7 @@ import { mount as tpMount } from '@/scopes/components/theme-params/methods.js'; | ||
} from '@/scopes/components/theme-params/signals.js'; | ||
import { subAndCall } from '@/utils/subAndCall.js'; | ||
import { withSupports } from '@/scopes/toolkit/withSupports.js'; | ||
import { withIsSupported } from '@/scopes/toolkit/withIsSupported.js'; | ||
import { createWithIsSupported } from '@/scopes/toolkit/createWithIsSupported.js'; | ||
import { createWithIsMounted } from '@/scopes/toolkit/createWithIsMounted.js'; | ||
@@ -39,12 +44,25 @@ import { | ||
import type { GetCssVarNameFn, HeaderColor, State } from './types.js'; | ||
import { subAndCall } from '@/utils/subAndCall.js'; | ||
type StorageValue = State; | ||
const SET_BG_COLOR_METHOD = 'web_app_set_background_color'; | ||
const SET_BOTTOM_BAR_BG_COLOR_METHOD = 'web_app_set_bottom_bar_color'; | ||
const SET_HEADER_COLOR_METHOD = 'web_app_set_header_color'; | ||
const WEB_APP_SET_BACKGROUND_COLOR = 'web_app_set_background_color'; | ||
const WEB_APP_SET_BOTTOM_BAR_COLOR = 'web_app_set_bottom_bar_color'; | ||
const WEB_APP_SET_HEADER_COLOR = 'web_app_set_header_color'; | ||
const STORAGE_KEY = 'miniApp'; | ||
/** | ||
* True if the Mini App component is supported. | ||
*/ | ||
export const isSupported = computed(() => { | ||
return ([ | ||
WEB_APP_SET_BACKGROUND_COLOR, | ||
WEB_APP_SET_BOTTOM_BAR_COLOR, | ||
WEB_APP_SET_HEADER_COLOR, | ||
] as const).some(method => supports(method, $version())); | ||
}); | ||
const withComponentSupported = createWithIsSupported(isSupported); | ||
const withIsMounted = createWithIsMounted(isMounted); | ||
/** | ||
* Creates CSS variables connected with the mini app. | ||
@@ -63,4 +81,6 @@ * | ||
* @throws {TypedError} ERR_ALREADY_CALLED | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export function bindCssVars(getCSSVarName?: GetCssVarNameFn): VoidFunction { | ||
export const bindCssVars = withIsMounted((getCSSVarName?: GetCssVarNameFn): VoidFunction => { | ||
if (isCssVarsBound()) { | ||
@@ -99,3 +119,3 @@ throw new TypedError(ERR_ALREADY_CALLED); | ||
return cleanup; | ||
} | ||
}); | ||
@@ -118,4 +138,5 @@ /** | ||
* theme palette values. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function mount(): void { | ||
export const mount = withComponentSupported((): void => { | ||
if (!isMounted()) { | ||
@@ -129,14 +150,13 @@ const s = isPageReload() && getStorageValue<StorageValue>(STORAGE_KEY); | ||
subAndCall(backgroundColor, onBgColorChanged); | ||
subAndCall(bottomBarColor, onBottomBarBgColorChanged); | ||
subAndCall(headerColor, onHeaderColorChanged); | ||
setBackgroundColor.isSupported() && subAndCall(backgroundColor, onBgColorChanged); | ||
setBottomBarColor.isSupported() && subAndCall(bottomBarColor, onBottomBarBgColorChanged); | ||
setHeaderColor.isSupported() && subAndCall(headerColor, onHeaderColorChanged); | ||
isMounted.set(true); | ||
} | ||
} | ||
}); | ||
function onBgColorChanged(): void { | ||
const color = backgroundColor(); | ||
saveState(); | ||
postEvent(SET_BG_COLOR_METHOD, { color }); | ||
postEvent(WEB_APP_SET_BACKGROUND_COLOR, { color: backgroundColor() }); | ||
} | ||
@@ -146,3 +166,3 @@ | ||
saveState(); | ||
postEvent(SET_BOTTOM_BAR_BG_COLOR_METHOD, { color: bottomBarColor() }); | ||
postEvent(WEB_APP_SET_BOTTOM_BAR_COLOR, { color: bottomBarColor() }); | ||
} | ||
@@ -153,3 +173,3 @@ | ||
saveState(); | ||
postEvent(SET_HEADER_COLOR_METHOD, isRGB(color) ? { color } : { color_key: color }); | ||
postEvent(WEB_APP_SET_HEADER_COLOR, isRGB(color) ? { color } : { color_key: color }); | ||
} | ||
@@ -177,22 +197,39 @@ | ||
* Updates the background color. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export const setBackgroundColor = withIsSupported((color: RGB): void => { | ||
backgroundColor.set(color); | ||
}, SET_BG_COLOR_METHOD); | ||
export const setBackgroundColor = withIsSupported( | ||
withIsMounted((color: BackgroundColor): void => { | ||
backgroundColor.set(color); | ||
}), | ||
WEB_APP_SET_BACKGROUND_COLOR, | ||
); | ||
/** | ||
* Updates the bottom bar background color. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export const setBottomBarColor = withIsSupported((color: BottomBarColor) => { | ||
bottomBarColor.set(color); | ||
}, SET_BOTTOM_BAR_BG_COLOR_METHOD); | ||
export const setBottomBarColor = withIsSupported( | ||
withIsMounted((color: BottomBarColor) => { | ||
bottomBarColor.set(color); | ||
}), | ||
WEB_APP_SET_BOTTOM_BAR_COLOR, | ||
); | ||
/** | ||
* Updates the header color. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export const setHeaderColor = withSupports( | ||
withIsSupported((color: HeaderColor): void => { | ||
headerColor.set(color); | ||
}, SET_HEADER_COLOR_METHOD), | ||
{ color: [SET_HEADER_COLOR_METHOD, 'color'] }, | ||
withIsSupported( | ||
withIsMounted((color: HeaderColor): void => { | ||
headerColor.set(color); | ||
}), | ||
WEB_APP_SET_HEADER_COLOR, | ||
), | ||
{ | ||
color: [WEB_APP_SET_HEADER_COLOR, 'color', isRGB], | ||
}, | ||
); | ||
@@ -199,0 +236,0 @@ |
@@ -1,6 +0,8 @@ | ||
import { TypedError, supports } from '@telegram-apps/bridge'; | ||
import { TypedError } from '@telegram-apps/bridge'; | ||
import { signal } from '@telegram-apps/signals'; | ||
import { $version, request } from '@/scopes/globals.js'; | ||
import { request } from '@/scopes/globals.js'; | ||
import { ERR_ALREADY_CALLED } from '@/errors.js'; | ||
import { withIsSupported } from '@/scopes/toolkit/withIsSupported.js'; | ||
import { createIsSupported } from '@/scopes/toolkit/createIsSupported.js'; | ||
@@ -10,10 +12,8 @@ import { prepareParams } from './prepareParams.js'; | ||
const MINI_APPS_METHOD = 'web_app_open_popup'; | ||
const WEB_APP_OPEN_POPUP = 'web_app_open_popup'; | ||
/** | ||
* @returns True if the back button is supported. | ||
* @returns True if the Popup is supported. | ||
*/ | ||
export function isSupported(): boolean { | ||
return supports(MINI_APPS_METHOD, $version()); | ||
} | ||
export const isSupported = createIsSupported(WEB_APP_OPEN_POPUP); | ||
@@ -40,18 +40,22 @@ /** | ||
* @throws {TypedError} ERR_POPUP_INVALID_PARAMS: Invalid text length. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export async function open(options: OpenOptions): Promise<string | null> { | ||
if (isOpened()) { | ||
throw new TypedError(ERR_ALREADY_CALLED); | ||
} | ||
isOpened.set(true); | ||
export const open = withIsSupported( | ||
async (options: OpenOptions): Promise<string | null> => { | ||
if (isOpened()) { | ||
throw new TypedError(ERR_ALREADY_CALLED); | ||
} | ||
isOpened.set(true); | ||
try { | ||
const { button_id: buttonId = null } = await request(MINI_APPS_METHOD, 'popup_closed', { | ||
...options, | ||
params: prepareParams(options), | ||
}); | ||
return buttonId; | ||
} finally { | ||
isOpened.set(false); | ||
} | ||
} | ||
try { | ||
const { button_id: buttonId = null } = await request(WEB_APP_OPEN_POPUP, 'popup_closed', { | ||
...options, | ||
params: prepareParams(options), | ||
}); | ||
return buttonId; | ||
} finally { | ||
isOpened.set(false); | ||
} | ||
}, | ||
isSupported, | ||
); |
import { beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { dispatchMiniAppsEvent } from 'test-utils'; | ||
import { TypedError } from '@telegram-apps/bridge'; | ||
@@ -17,2 +18,14 @@ import { resetPackageState } from '@test-utils/reset/reset.js'; | ||
describe('close', () => { | ||
it('should throw if version is less than 6.4', () => { | ||
$version.set('6.3'); | ||
expect(close).toThrow(new TypedError('ERR_NOT_SUPPORTED')); | ||
$version.set('6.4'); | ||
expect(close).not.toThrow(); | ||
}); | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
it('should set isOpened = false', () => { | ||
@@ -48,2 +61,14 @@ isOpened.set(true); | ||
describe('open', () => { | ||
it('should throw if version is less than 6.4', () => { | ||
$version.set('6.3'); | ||
expect(() => open()).toThrow(new TypedError('ERR_NOT_SUPPORTED')); | ||
$version.set('6.4'); | ||
expect(() => open()).not.toThrow(); | ||
}); | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
describe('common mode', () => { | ||
@@ -138,1 +163,14 @@ it('should call "web_app_open_scan_qr_popup" method with { text: string } and catch "qr_text_received" event returning event "data" property if "capture" returned true', async () => { | ||
}); | ||
describe('support check', () => { | ||
it.each([ | ||
{ fn: close, name: 'close' }, | ||
{ fn: open, name: 'open' }, | ||
])('$name function should throw ERR_NOT_SUPPORTED if version is less than 6.4', ({ fn }) => { | ||
$version.set('6.3'); | ||
expect(fn).toThrow(new TypedError('ERR_NOT_SUPPORTED')); | ||
$version.set('6.4'); | ||
expect(fn).not.toThrow(); | ||
}); | ||
}); |
@@ -7,3 +7,2 @@ import { | ||
EnhancedPromise, | ||
supports, | ||
type ExecuteWithOptions, | ||
@@ -13,4 +12,6 @@ } from '@telegram-apps/bridge'; | ||
import { $version, postEvent } from '@/scopes/globals.js'; | ||
import { postEvent } from '@/scopes/globals.js'; | ||
import { ERR_ALREADY_CALLED } from '@/errors.js'; | ||
import { createWithIsSupported } from '@/scopes/toolkit/createWithIsSupported.js'; | ||
import { createIsSupported } from '@/scopes/toolkit/createIsSupported.js'; | ||
@@ -24,14 +25,22 @@ interface OpenSharedOptions extends ExecuteWithOptions { | ||
const CLOSE_METHOD = 'web_app_close_scan_qr_popup'; | ||
const OPEN_METHOD = 'web_app_open_scan_qr_popup'; | ||
const CLOSED_EVENT = 'scan_qr_popup_closed'; | ||
const SCANNED_EVENT = 'qr_text_received'; | ||
const WEB_APP_CLOSE_SCAN_QR_POPUP = 'web_app_close_scan_qr_popup'; | ||
const WEB_APP_OPEN_SCAN_QR_POPUP = 'web_app_open_scan_qr_popup'; | ||
const SCAN_QR_POPUP_CLOSED = 'scan_qr_popup_closed'; | ||
const QR_TEXT_RECEIVED = 'qr_text_received'; | ||
/** | ||
* @returns True if the QR scanner is supported. | ||
*/ | ||
export const isSupported = createIsSupported(WEB_APP_OPEN_SCAN_QR_POPUP); | ||
const withIsSupported = createWithIsSupported(isSupported); | ||
/** | ||
* Closes the scanner. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function close(): void { | ||
export const close = withIsSupported((): void => { | ||
isOpened.set(false); | ||
postEvent(CLOSE_METHOD); | ||
} | ||
postEvent(WEB_APP_CLOSE_SCAN_QR_POPUP); | ||
}); | ||
@@ -44,9 +53,2 @@ /** | ||
/** | ||
* @returns True if the QR scanner is supported. | ||
*/ | ||
export function isSupported(): boolean { | ||
return supports(OPEN_METHOD, $version()); | ||
} | ||
/** | ||
* Opens the scanner and returns a promise which will be resolved with the QR content if the | ||
@@ -60,3 +62,3 @@ * `capture` function returned true. | ||
*/ | ||
export function open(options?: OpenSharedOptions & { | ||
function _open(options?: OpenSharedOptions & { | ||
/** | ||
@@ -77,3 +79,3 @@ * Function, which should return true if a scanned QR should be captured. | ||
*/ | ||
export function open(options: OpenSharedOptions & { | ||
function _open(options: OpenSharedOptions & { | ||
/** | ||
@@ -86,3 +88,3 @@ * Function which will be called if some QR code was scanned. | ||
export function open(options?: OpenSharedOptions & { | ||
function _open(options?: OpenSharedOptions & { | ||
onCaptured?: (qr: string) => void; | ||
@@ -106,7 +108,7 @@ capture?: (qr: string) => boolean; | ||
// Whenever user closed the scanner, update the isOpened signal state. | ||
on(CLOSED_EVENT, () => { | ||
on(SCAN_QR_POPUP_CLOSED, () => { | ||
isOpened.set(false); | ||
}), | ||
// Whenever some QR was scanned, we should check if it must be captured. | ||
on(SCANNED_EVENT, (event) => { | ||
on(QR_TEXT_RECEIVED, (event) => { | ||
if (onCaptured) { | ||
@@ -125,3 +127,3 @@ onCaptured(event.data); | ||
(options.postEvent || postEvent)(OPEN_METHOD, { text }); | ||
(options.postEvent || postEvent)(WEB_APP_OPEN_SCAN_QR_POPUP, { text }); | ||
@@ -131,1 +133,6 @@ return promise; | ||
} | ||
/** | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export const open = withIsSupported(_open) |
@@ -1,4 +0,4 @@ | ||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { mockSessionStorageSetItem } from 'test-utils'; | ||
import { emitMiniAppsEvent, type ThemeParams } from '@telegram-apps/bridge'; | ||
import { emitMiniAppsEvent, type ThemeParams, TypedError } from '@telegram-apps/bridge'; | ||
@@ -84,76 +84,7 @@ import { mockPostEvent } from '@test-utils/mockPostEvent.js'; | ||
describe('mounted', () => { | ||
beforeEach(mount); | ||
afterEach(unmount); | ||
describe('setParams', () => { | ||
it('should save the state in storage key tapps/secondaryButton', () => { | ||
internalState.set({ | ||
backgroundColor: '#123456', | ||
hasShineEffect: true, | ||
isEnabled: true, | ||
isLoaderVisible: true, | ||
isVisible: true, | ||
position: 'top', | ||
text: 'TEXT', | ||
textColor: '#789abc', | ||
}); | ||
const spy = mockSessionStorageSetItem(); | ||
setParams({ | ||
backgroundColor: '#111111', | ||
}); | ||
expect(spy).toHaveBeenCalledTimes(1); | ||
expect(spy).toHaveBeenCalledWith('tapps/secondaryButton', '{"backgroundColor":"#111111","hasShineEffect":true,"isEnabled":true,"isLoaderVisible":true,"isVisible":true,"position":"top","text":"TEXT","textColor":"#789abc"}'); | ||
}); | ||
it('should call "web_app_setup_secondary_button" only if text is not empty', () => { | ||
const spy = mockPostEvent(); | ||
internalState.set({ | ||
backgroundColor: '#123456', | ||
hasShineEffect: false, | ||
isEnabled: true, | ||
isLoaderVisible: true, | ||
isVisible: true, | ||
position: 'bottom', | ||
text: '', | ||
textColor: '#789abc', | ||
}); | ||
setParams({ text: '' }); | ||
expect(spy).toHaveBeenCalledTimes(0); | ||
setParams({ text: 'abc' }); | ||
expect(spy).toHaveBeenCalledTimes(1); | ||
expect(spy).toHaveBeenCalledWith('web_app_setup_secondary_button', { | ||
color: '#123456', | ||
has_shine_effect: false, | ||
is_active: true, | ||
is_progress_visible: true, | ||
is_visible: true, | ||
position: 'bottom', | ||
text: 'abc', | ||
text_color: '#789abc', | ||
}); | ||
}); | ||
describe('mount', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
}); | ||
describe('not mounted', () => { | ||
describe('setParams', () => { | ||
it('should not call postEvent', () => { | ||
const spy = mockPostEvent(); | ||
setParams({ text: 'ABC' }); | ||
expect(spy).toHaveBeenCalledTimes(0); | ||
}); | ||
it('should not save state in storage', () => { | ||
const spy = mockSessionStorageSetItem(); | ||
setParams({ text: 'ABC' }); | ||
expect(spy).toHaveBeenCalledTimes(0); | ||
}); | ||
}); | ||
}); | ||
describe('mount', () => { | ||
it('should set isMounted = true', () => { | ||
@@ -218,2 +149,6 @@ expect(isMounted()).toBe(false); | ||
describe('onClick', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
it('should add click listener', () => { | ||
@@ -238,2 +173,6 @@ const fn = vi.fn(); | ||
describe('offClick', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
it('should remove click listener', () => { | ||
@@ -249,2 +188,7 @@ const fn = vi.fn(); | ||
describe('setParams', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
isMounted.set(true); | ||
}); | ||
it('should merge passed object with the state', () => { | ||
@@ -284,3 +228,6 @@ internalState.set({ | ||
describe('unmount', () => { | ||
beforeEach(mount); | ||
beforeEach(() => { | ||
$version.set('10'); | ||
mount(); | ||
}); | ||
@@ -304,1 +251,92 @@ it('should stop calling postEvent function and session storage updates when something changes', () => { | ||
}); | ||
describe('mounted', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
mount(); | ||
}); | ||
describe('setParams', () => { | ||
it('should save the state in storage key tapps/secondaryButton', () => { | ||
internalState.set({ | ||
backgroundColor: '#123456', | ||
hasShineEffect: true, | ||
isEnabled: true, | ||
isLoaderVisible: true, | ||
isVisible: true, | ||
position: 'top', | ||
text: 'TEXT', | ||
textColor: '#789abc', | ||
}); | ||
const spy = mockSessionStorageSetItem(); | ||
setParams({ | ||
backgroundColor: '#111111', | ||
}); | ||
expect(spy).toHaveBeenCalledTimes(1); | ||
expect(spy).toHaveBeenCalledWith('tapps/secondaryButton', '{"backgroundColor":"#111111","hasShineEffect":true,"isEnabled":true,"isLoaderVisible":true,"isVisible":true,"position":"top","text":"TEXT","textColor":"#789abc"}'); | ||
}); | ||
it('should call "web_app_setup_secondary_button" only if text is not empty', () => { | ||
const spy = mockPostEvent(); | ||
internalState.set({ | ||
backgroundColor: '#123456', | ||
hasShineEffect: false, | ||
isEnabled: true, | ||
isLoaderVisible: true, | ||
isVisible: true, | ||
position: 'bottom', | ||
text: '', | ||
textColor: '#789abc', | ||
}); | ||
setParams({ text: '' }); | ||
expect(spy).toHaveBeenCalledTimes(0); | ||
setParams({ text: 'abc' }); | ||
expect(spy).toHaveBeenCalledTimes(1); | ||
expect(spy).toHaveBeenCalledWith('web_app_setup_secondary_button', { | ||
color: '#123456', | ||
has_shine_effect: false, | ||
is_active: true, | ||
is_progress_visible: true, | ||
is_visible: true, | ||
position: 'bottom', | ||
text: 'abc', | ||
text_color: '#789abc', | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('support check', () => { | ||
beforeEach(() => { | ||
isMounted.set(true); | ||
}); | ||
it.each([ | ||
{ fn: mount, name: 'mount' }, | ||
{ fn: () => onClick(console.log), name: 'onClick' }, | ||
{ fn: () => offClick(console.log), name: 'offClick' }, | ||
])('$name function should throw ERR_NOT_SUPPORTED if version is less than 7.10', ({ fn }) => { | ||
$version.set('7.9'); | ||
expect(fn).toThrow(new TypedError('ERR_NOT_SUPPORTED')); | ||
$version.set('7.10'); | ||
expect(fn).not.toThrow(); | ||
}); | ||
}); | ||
describe('mount check', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
it.each([ | ||
{ fn: () => setParams({}), name: 'setParams' }, | ||
])('$name function should throw ERR_NOT_MOUNTED if component was not mounted', ({ fn }) => { | ||
expect(fn).toThrow(new TypedError('ERR_NOT_MOUNTED')); | ||
isMounted.set(true); | ||
expect(fn).not.toThrow(); | ||
}); | ||
}); |
@@ -6,8 +6,11 @@ import { | ||
setStorageValue, | ||
type EventListener, supports, | ||
type EventListener, | ||
} from '@telegram-apps/bridge'; | ||
import { isPageReload } from '@telegram-apps/navigation'; | ||
import { $version, postEvent } from '@/scopes/globals.js'; | ||
import { postEvent } from '@/scopes/globals.js'; | ||
import { subAndCall } from '@/utils/subAndCall.js'; | ||
import { createWithIsSupported } from '@/scopes/toolkit/createWithIsSupported.js'; | ||
import { createIsSupported } from '@/scopes/toolkit/createIsSupported.js'; | ||
import { createWithIsMounted } from '@/scopes/toolkit/createWithIsMounted.js'; | ||
@@ -19,4 +22,4 @@ import { internalState, isMounted, state } from './signals.js'; | ||
const MINI_APPS_METHOD = 'web_app_setup_secondary_button'; | ||
const CLICK_EVENT = 'secondary_button_pressed'; | ||
const WEB_APP_SETUP_SECONDARY_BUTTON = 'web_app_setup_secondary_button'; | ||
const SECONDARY_BUTTON_PRESSED = 'secondary_button_pressed'; | ||
const STORAGE_KEY = 'secondaryButton'; | ||
@@ -27,6 +30,7 @@ | ||
*/ | ||
export function isSupported(): boolean { | ||
return supports(MINI_APPS_METHOD, $version()); | ||
} | ||
export const isSupported = createIsSupported(WEB_APP_SETUP_SECONDARY_BUTTON); | ||
const withIsSupported = createWithIsSupported(isSupported); | ||
const withIsMounted = createWithIsMounted(isMounted); | ||
/** | ||
@@ -37,4 +41,5 @@ * Mounts the component. | ||
* if it changed. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function mount(): void { | ||
export const mount = withIsSupported((): void => { | ||
if (!isMounted()) { | ||
@@ -48,3 +53,3 @@ const prev = isPageReload() && getStorageValue<StorageValue>(STORAGE_KEY); | ||
} | ||
} | ||
}); | ||
@@ -55,6 +60,7 @@ /** | ||
* @returns A function to remove bound listener. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function onClick(fn: EventListener<'secondary_button_pressed'>): VoidFunction { | ||
return on(CLICK_EVENT, fn); | ||
} | ||
export const onClick = withIsSupported( | ||
(fn: EventListener<'secondary_button_pressed'>): VoidFunction => on(SECONDARY_BUTTON_PRESSED, fn), | ||
); | ||
@@ -64,6 +70,9 @@ /** | ||
* @param fn - an event listener. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function offClick(fn: EventListener<'secondary_button_pressed'>): void { | ||
off(CLICK_EVENT, fn); | ||
} | ||
export const offClick = withIsSupported( | ||
(fn: EventListener<'secondary_button_pressed'>): void => { | ||
off(SECONDARY_BUTTON_PRESSED, fn); | ||
}, | ||
); | ||
@@ -79,3 +88,3 @@ function onInternalStateChanged(s: State): void { | ||
// crash due to the empty value of the text. | ||
s.text && postEvent(MINI_APPS_METHOD, { | ||
s.text && postEvent(WEB_APP_SETUP_SECONDARY_BUTTON, { | ||
color: s.backgroundColor, | ||
@@ -95,4 +104,5 @@ has_shine_effect: s.hasShineEffect, | ||
* @param updates - state changes to perform. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export function setParams(updates: Partial<State>): void { | ||
export const setParams = withIsMounted((updates: Partial<State>): void => { | ||
internalState.set({ | ||
@@ -104,3 +114,3 @@ ...internalState(), | ||
}); | ||
} | ||
}); | ||
@@ -107,0 +117,0 @@ /** |
@@ -1,4 +0,4 @@ | ||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { mockSessionStorageGetItem, mockPageReload, mockSessionStorageSetItem } from 'test-utils'; | ||
import { emitMiniAppsEvent } from '@telegram-apps/bridge'; | ||
import { emitMiniAppsEvent, TypedError } from '@telegram-apps/bridge'; | ||
@@ -28,74 +28,8 @@ import { mockPostEvent } from '@test-utils/mockPostEvent.js'; | ||
describe('mounted', () => { | ||
beforeEach(mount); | ||
afterEach(unmount); | ||
describe('hide', () => { | ||
it('should call postEvent with "web_app_setup_settings_button" and { is_visible: false }', () => { | ||
isVisible.set(true); | ||
const spy = vi.fn(); | ||
mockPostEvent(spy); | ||
hide(); | ||
hide(); | ||
hide(); | ||
expect(spy).toBeCalledTimes(1); | ||
expect(spy).toBeCalledWith('web_app_setup_settings_button', { is_visible: false }); | ||
}); | ||
describe('hide', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
isMounted.set(true); | ||
}); | ||
describe('show', () => { | ||
it('should call postEvent with "web_app_setup_settings_button" and { is_visible: true }', () => { | ||
isVisible.set(false); | ||
const spy = vi.fn(); | ||
mockPostEvent(spy); | ||
show(); | ||
show(); | ||
show(); | ||
expect(spy).toBeCalledTimes(1); | ||
expect(spy).toBeCalledWith('web_app_setup_settings_button', { is_visible: true }); | ||
}); | ||
}); | ||
}); | ||
describe('not mounted', () => { | ||
describe('hide', () => { | ||
it('should not call postEvent', () => { | ||
isVisible.set(true); | ||
const spy = vi.fn(); | ||
mockPostEvent(spy); | ||
hide(); | ||
expect(spy).toBeCalledTimes(0); | ||
}); | ||
it('should not save state in storage', () => { | ||
isVisible.set(true); | ||
const spy = mockSessionStorageSetItem(); | ||
hide(); | ||
expect(spy).toBeCalledTimes(0); | ||
}); | ||
}); | ||
describe('show', () => { | ||
it('should not call postEvent', () => { | ||
isVisible.set(false); | ||
const spy = vi.fn(); | ||
mockPostEvent(spy); | ||
show(); | ||
show(); | ||
show(); | ||
expect(spy).toBeCalledTimes(0); | ||
}); | ||
it('should not save state in storage', () => { | ||
isVisible.set(false); | ||
const spy = mockSessionStorageSetItem(); | ||
show(); | ||
show(); | ||
show(); | ||
expect(spy).toBeCalledTimes(0); | ||
}); | ||
}); | ||
}); | ||
describe('hide', () => { | ||
it('should set isVisible = false', () => { | ||
@@ -121,2 +55,6 @@ isVisible.set(true); | ||
describe('mount', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
it('should call postEvent with "web_app_setup_settings_button"', () => { | ||
@@ -167,24 +105,7 @@ const spy = mockPostEvent(); | ||
describe('unmount', () => { | ||
beforeEach(mount); | ||
it('should stop calling postEvent function and session storage updates when isVisible changes', () => { | ||
const postEventSpy = mockPostEvent(); | ||
const storageSpy = mockSessionStorageSetItem(); | ||
isVisible.set(true); | ||
expect(postEventSpy).toHaveBeenCalledTimes(1); | ||
expect(storageSpy).toHaveBeenCalledTimes(1); | ||
postEventSpy.mockClear(); | ||
storageSpy.mockClear(); | ||
unmount(); | ||
isVisible.set(false); | ||
expect(postEventSpy).toHaveBeenCalledTimes(0); | ||
expect(storageSpy).toHaveBeenCalledTimes(0); | ||
describe('onClick', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
}); | ||
describe('onClick', () => { | ||
it('should add click listener', () => { | ||
@@ -207,2 +128,6 @@ const fn = vi.fn(); | ||
describe('offClick', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
it('should remove click listener', () => { | ||
@@ -217,3 +142,32 @@ const fn = vi.fn(); | ||
describe('unmount', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
mount(); | ||
}); | ||
it('should stop calling postEvent function and session storage updates when isVisible changes', () => { | ||
const postEventSpy = mockPostEvent(); | ||
const storageSpy = mockSessionStorageSetItem(); | ||
isVisible.set(true); | ||
expect(postEventSpy).toHaveBeenCalledTimes(1); | ||
expect(storageSpy).toHaveBeenCalledTimes(1); | ||
postEventSpy.mockClear(); | ||
storageSpy.mockClear(); | ||
unmount(); | ||
isVisible.set(false); | ||
expect(postEventSpy).toHaveBeenCalledTimes(0); | ||
expect(storageSpy).toHaveBeenCalledTimes(0); | ||
}); | ||
}); | ||
describe('show', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
isMounted.set(true); | ||
}); | ||
it('should set isVisible = true', () => { | ||
@@ -226,1 +180,67 @@ isVisible.set(false); | ||
}); | ||
describe('mounted', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
mount(); | ||
}); | ||
describe('hide', () => { | ||
it('should call postEvent with "web_app_setup_settings_button" and { is_visible: false }', () => { | ||
isVisible.set(true); | ||
const spy = vi.fn(); | ||
mockPostEvent(spy); | ||
hide(); | ||
hide(); | ||
hide(); | ||
expect(spy).toBeCalledTimes(1); | ||
expect(spy).toBeCalledWith('web_app_setup_settings_button', { is_visible: false }); | ||
}); | ||
}); | ||
describe('show', () => { | ||
it('should call postEvent with "web_app_setup_settings_button" and { is_visible: true }', () => { | ||
isVisible.set(false); | ||
const spy = vi.fn(); | ||
mockPostEvent(spy); | ||
show(); | ||
show(); | ||
show(); | ||
expect(spy).toBeCalledTimes(1); | ||
expect(spy).toBeCalledWith('web_app_setup_settings_button', { is_visible: true }); | ||
}); | ||
}); | ||
}); | ||
describe('support check', () => { | ||
beforeEach(() => { | ||
isMounted.set(true); | ||
}); | ||
it.each([ | ||
{ fn: mount, name: 'mount' }, | ||
{ fn: () => onClick(console.log), name: 'onClick' }, | ||
{ fn: () => offClick(console.log), name: 'offClick' }, | ||
])('$name function should throw ERR_NOT_SUPPORTED if version is less than 6.10', ({ fn }) => { | ||
$version.set('6.9'); | ||
expect(fn).toThrow(new TypedError('ERR_NOT_SUPPORTED')); | ||
$version.set('6.10'); | ||
expect(fn).not.toThrow(); | ||
}); | ||
}); | ||
describe('mount check', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
it.each([ | ||
{ fn: hide, name: 'hide' }, | ||
{ fn: show, name: 'show' }, | ||
])('$name function should throw ERR_NOT_MOUNTED if component was not mounted', ({ fn }) => { | ||
expect(fn).toThrow(new TypedError('ERR_NOT_MOUNTED')); | ||
isMounted.set(true); | ||
expect(fn).not.toThrow(); | ||
}); | ||
}); |
@@ -6,3 +6,2 @@ import { | ||
setStorageValue, | ||
supports, | ||
type EventListener, | ||
@@ -13,34 +12,39 @@ } from '@telegram-apps/bridge'; | ||
import { $version, postEvent } from '@/scopes/globals.js'; | ||
import { postEvent } from '@/scopes/globals.js'; | ||
import { subAndCall } from '@/utils/subAndCall.js'; | ||
import { createWithIsSupported } from '@/scopes/toolkit/createWithIsSupported.js'; | ||
import { createIsSupported } from '@/scopes/toolkit/createIsSupported.js'; | ||
import { createWithIsMounted } from '@/scopes/toolkit/createWithIsMounted.js'; | ||
type StorageValue = boolean; | ||
const MINI_APPS_METHOD = 'web_app_setup_settings_button'; | ||
const CLICK_EVENT = 'settings_button_pressed'; | ||
const WEB_APP_SETUP_SETTINGS_BUTTON = 'web_app_setup_settings_button'; | ||
const SETTINGS_BUTTON_PRESSED = 'settings_button_pressed'; | ||
const STORAGE_KEY = 'settingsButton'; | ||
/** | ||
* Hides the settings button. | ||
* True if the component is currently mounted. | ||
*/ | ||
export function hide(): void { | ||
isVisible.set(false); | ||
} | ||
export const isMounted = signal(false); | ||
/** | ||
* True if the component is currently visible. | ||
* @returns True if the settings button is supported. | ||
*/ | ||
export const isVisible = signal(false); | ||
export const isSupported = createIsSupported(WEB_APP_SETUP_SETTINGS_BUTTON); | ||
const withIsSupported = createWithIsSupported(isSupported); | ||
const withIsMounted = createWithIsMounted(isMounted); | ||
/** | ||
* True if the component is currently mounted. | ||
* Hides the Settings Button. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export const isMounted = signal(false); | ||
export const hide = withIsMounted((): void => { | ||
isVisible.set(false); | ||
}); | ||
/** | ||
* @returns True if the settings button is supported. | ||
* True if the component is currently visible. | ||
*/ | ||
export function isSupported(): boolean { | ||
return supports(MINI_APPS_METHOD, $version()); | ||
} | ||
export const isVisible = signal(false); | ||
@@ -52,4 +56,5 @@ /** | ||
* if it changed. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function mount(): void { | ||
export const mount = withIsSupported((): void => { | ||
if (!isMounted()) { | ||
@@ -60,7 +65,7 @@ isVisible.set(isPageReload() && getStorageValue<StorageValue>(STORAGE_KEY) || false); | ||
} | ||
} | ||
}); | ||
function onStateChanged() { | ||
const value = isVisible(); | ||
postEvent(MINI_APPS_METHOD, { is_visible: value }); | ||
postEvent(WEB_APP_SETUP_SETTINGS_BUTTON, { is_visible: value }); | ||
setStorageValue<StorageValue>(STORAGE_KEY, value); | ||
@@ -70,24 +75,29 @@ } | ||
/** | ||
* Add a new settings button click listener. | ||
* Add a new Settings Button click listener. | ||
* @param fn - event listener. | ||
* @returns A function to remove bound listener. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function onClick(fn: EventListener<'settings_button_pressed'>): VoidFunction { | ||
return on(CLICK_EVENT, fn); | ||
} | ||
export const onClick = withIsSupported( | ||
(fn: EventListener<'settings_button_pressed'>): VoidFunction => on(SETTINGS_BUTTON_PRESSED, fn), | ||
); | ||
/** | ||
* Removes the settings button click listener. | ||
* Removes the Settings Button click listener. | ||
* @param fn - an event listener. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function offClick(fn: EventListener<'settings_button_pressed'>): void { | ||
off(CLICK_EVENT, fn); | ||
} | ||
export const offClick = withIsSupported( | ||
(fn: EventListener<'settings_button_pressed'>): void => { | ||
off(SETTINGS_BUTTON_PRESSED, fn); | ||
}, | ||
); | ||
/** | ||
* Shows the settings button. | ||
* Shows the Settings Button. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export function show(): void { | ||
export const show = withIsMounted((): void => { | ||
isVisible.set(true); | ||
} | ||
}); | ||
@@ -94,0 +104,0 @@ /** |
@@ -1,2 +0,3 @@ | ||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { TypedError } from '@telegram-apps/bridge'; | ||
import { mockSessionStorageGetItem, mockPageReload, mockSessionStorageSetItem } from 'test-utils'; | ||
@@ -24,61 +25,27 @@ | ||
describe('mounted', () => { | ||
beforeEach(mount); | ||
afterEach(unmount); | ||
describe('disableVerticalSwipes', () => { | ||
it('should call postEvent with "web_app_setup_swipe_behavior" and { allow_vertical_swipe: false }', () => { | ||
isVerticalEnabled.set(true); | ||
const spy = mockPostEvent(); | ||
disableVertical(); | ||
disableVertical(); | ||
disableVertical(); | ||
expect(spy).toBeCalledTimes(1); | ||
expect(spy).toBeCalledWith('web_app_setup_swipe_behavior', { allow_vertical_swipe: false }); | ||
}); | ||
describe('disableVerticalSwipes', () => { | ||
beforeEach(() => { | ||
isMounted.set(true); | ||
$version.set('10'); | ||
}); | ||
describe('enableVerticalSwipes', () => { | ||
it('should call postEvent with "web_app_setup_swipe_behavior" and { allow_vertical_swipe: true }', () => { | ||
isVerticalEnabled.set(false); | ||
const spy = mockPostEvent(); | ||
enableVertical(); | ||
enableVertical(); | ||
enableVertical(); | ||
expect(spy).toBeCalledTimes(1); | ||
expect(spy).toBeCalledWith('web_app_setup_swipe_behavior', { allow_vertical_swipe: true }); | ||
}); | ||
it('should set isVerticalSwipesEnabled = false', () => { | ||
isVerticalEnabled.set(true); | ||
expect(isVerticalEnabled()).toBe(true); | ||
disableVertical(); | ||
expect(isVerticalEnabled()).toBe(false); | ||
}); | ||
}); | ||
describe('not mounted', () => { | ||
describe('disableVerticalSwipes', () => { | ||
it('should not call postEvent', () => { | ||
isVerticalEnabled.set(true); | ||
const spy = mockPostEvent(); | ||
disableVertical(); | ||
disableVertical(); | ||
disableVertical(); | ||
expect(spy).toBeCalledTimes(0); | ||
}); | ||
describe('enableVerticalSwipes', () => { | ||
beforeEach(() => { | ||
isMounted.set(true); | ||
$version.set('10'); | ||
}); | ||
describe('enableVerticalSwipes', () => { | ||
it('should not call postEvent', () => { | ||
isVerticalEnabled.set(false); | ||
const spy = mockPostEvent(); | ||
enableVertical(); | ||
enableVertical(); | ||
enableVertical(); | ||
expect(spy).toBeCalledTimes(0); | ||
}); | ||
}); | ||
}); | ||
describe('disableVerticalSwipes', () => { | ||
it('should set isVerticalSwipesEnabled = false', () => { | ||
isVerticalEnabled.set(true); | ||
it('should set isVerticalSwipesEnabled = true', () => { | ||
isVerticalEnabled.set(false); | ||
expect(isVerticalEnabled()).toBe(false); | ||
enableVertical(); | ||
expect(isVerticalEnabled()).toBe(true); | ||
disableVertical(); | ||
expect(isVerticalEnabled()).toBe(false); | ||
}); | ||
@@ -101,2 +68,14 @@ }); | ||
describe('mount', () => { | ||
it('should throw if version is less than 7.7', () => { | ||
$version.set('7.6'); | ||
expect(mount).toThrow(new TypedError('ERR_NOT_SUPPORTED')); | ||
$version.set('7.7'); | ||
expect(mount).not.toThrow(); | ||
}); | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
it('should set isMounted = true', () => { | ||
@@ -148,3 +127,6 @@ expect(isMounted()).toBe(false); | ||
describe('unmount', () => { | ||
beforeEach(mount); | ||
beforeEach(() => { | ||
$version.set('10'); | ||
mount(); | ||
}); | ||
@@ -169,9 +151,62 @@ it('should stop calling postEvent function and session storage updates when isVerticalSwipesEnabled changes', () => { | ||
describe('enableVerticalSwipes', () => { | ||
it('should set isVerticalSwipesEnabled = true', () => { | ||
isVerticalEnabled.set(false); | ||
expect(isVerticalEnabled()).toBe(false); | ||
enableVertical(); | ||
expect(isVerticalEnabled()).toBe(true); | ||
describe('mounted', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
mount(); | ||
}); | ||
describe('disableVerticalSwipes', () => { | ||
it('should call postEvent with "web_app_setup_swipe_behavior" and { allow_vertical_swipe: false }', () => { | ||
isVerticalEnabled.set(true); | ||
const spy = mockPostEvent(); | ||
disableVertical(); | ||
disableVertical(); | ||
disableVertical(); | ||
expect(spy).toBeCalledTimes(1); | ||
expect(spy).toBeCalledWith('web_app_setup_swipe_behavior', { allow_vertical_swipe: false }); | ||
}); | ||
}); | ||
describe('enableVerticalSwipes', () => { | ||
it('should call postEvent with "web_app_setup_swipe_behavior" and { allow_vertical_swipe: true }', () => { | ||
isVerticalEnabled.set(false); | ||
const spy = mockPostEvent(); | ||
enableVertical(); | ||
enableVertical(); | ||
enableVertical(); | ||
expect(spy).toBeCalledTimes(1); | ||
expect(spy).toBeCalledWith('web_app_setup_swipe_behavior', { allow_vertical_swipe: true }); | ||
}); | ||
}); | ||
}); | ||
describe('support check', () => { | ||
beforeEach(() => { | ||
isMounted.set(true); | ||
}); | ||
it.each([ | ||
{ fn: mount, name: 'mount' }, | ||
])('$name function should throw ERR_NOT_SUPPORTED if version is less than 7.7', ({ fn }) => { | ||
$version.set('7.6'); | ||
expect(fn).toThrow(new TypedError('ERR_NOT_SUPPORTED')); | ||
$version.set('7.7'); | ||
expect(fn).not.toThrow(); | ||
}); | ||
}); | ||
describe('mount check', () => { | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
it.each([ | ||
{ fn: disableVertical, name: 'disableVertical' }, | ||
{ fn: enableVertical, name: 'enableVertical' }, | ||
])('$name function should throw ERR_NOT_MOUNTED if component was not mounted', ({ fn }) => { | ||
expect(fn).toThrow(new TypedError('ERR_NOT_MOUNTED')); | ||
isMounted.set(true); | ||
expect(fn).not.toThrow(); | ||
}); | ||
}); |
import { isPageReload } from '@telegram-apps/navigation'; | ||
import { getStorageValue, setStorageValue, supports } from '@telegram-apps/bridge'; | ||
import { getStorageValue, setStorageValue } from '@telegram-apps/bridge'; | ||
import { signal } from '@telegram-apps/signals'; | ||
import { $version, postEvent } from '@/scopes/globals.js'; | ||
import { postEvent } from '@/scopes/globals.js'; | ||
import { subAndCall } from '@/utils/subAndCall.js'; | ||
import { createWithIsSupported } from '@/scopes/toolkit/createWithIsSupported.js'; | ||
import { createIsSupported } from '@/scopes/toolkit/createIsSupported.js'; | ||
import { createWithIsMounted } from '@/scopes/toolkit/createWithIsMounted.js'; | ||
type StorageValue = boolean; | ||
const MINI_APPS_METHOD = 'web_app_setup_swipe_behavior'; | ||
const WEB_APP_SETUP_SWIPE_BEHAVIOR = 'web_app_setup_swipe_behavior'; | ||
const STORAGE_KEY = 'swipeBehavior'; | ||
/** | ||
* True if the component is currently mounted. | ||
*/ | ||
export const isMounted = signal(false); | ||
/** | ||
* @returns True if the Swipe Behavior is supported. | ||
*/ | ||
export const isSupported = createIsSupported(WEB_APP_SETUP_SWIPE_BEHAVIOR); | ||
const withIsSupported = createWithIsSupported(isSupported); | ||
const withIsMounted = createWithIsMounted(isMounted); | ||
/** | ||
* Disables vertical swipes. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export function disableVertical(): void { | ||
export const disableVertical = withIsMounted((): void => { | ||
isVerticalEnabled.set(false); | ||
} | ||
}); | ||
/** | ||
* Enables vertical swipes. | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export function enableVertical(): void { | ||
export const enableVertical = withIsMounted((): void => { | ||
isVerticalEnabled.set(true); | ||
} | ||
}); | ||
/** | ||
* True if the component is currently mounted. | ||
*/ | ||
export const isMounted = signal(false); | ||
/** | ||
* True if vertical swipes are enabled. | ||
@@ -38,9 +51,2 @@ */ | ||
/** | ||
* @returns True if the back button is supported. | ||
*/ | ||
export function isSupported(): boolean { | ||
return supports(MINI_APPS_METHOD, $version()); | ||
} | ||
/** | ||
* Mounts the component. | ||
@@ -50,4 +56,5 @@ * | ||
* if it changed. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export function mount(): void { | ||
export const mount = withIsSupported((): void => { | ||
if (!isMounted()) { | ||
@@ -58,7 +65,7 @@ isVerticalEnabled.set(isPageReload() && getStorageValue<StorageValue>(STORAGE_KEY) || false); | ||
} | ||
} | ||
}); | ||
function onStateChanged(): void { | ||
const value = isVerticalEnabled(); | ||
postEvent(MINI_APPS_METHOD, { allow_vertical_swipe: value }); | ||
postEvent(WEB_APP_SETUP_SWIPE_BEHAVIOR, { allow_vertical_swipe: value }); | ||
setStorageValue<StorageValue>(STORAGE_KEY, value); | ||
@@ -65,0 +72,0 @@ } |
import { beforeEach, describe, expect, it, MockInstance, vi } from 'vitest'; | ||
import { createWindow } from 'test-utils'; | ||
import { emitMiniAppsEvent } from '@telegram-apps/bridge'; | ||
import { emitMiniAppsEvent, TypedError } from '@telegram-apps/bridge'; | ||
import { resetPackageState } from '@test-utils/reset/reset.js'; | ||
@@ -8,3 +8,3 @@ import { mockPostEvent } from '@test-utils/mockPostEvent.js'; | ||
import { bindCssVars, mount } from './methods.js'; | ||
import { state } from './signals.js'; | ||
import { isMounted, state } from './signals.js'; | ||
@@ -36,2 +36,4 @@ type SetPropertyFn = typeof document.documentElement.style.setProperty; | ||
describe('bindCssVars', () => { | ||
beforeEach(mount); | ||
it('should set --tg-theme-{key} CSS vars, where key is kebab-cased theme keys', () => { | ||
@@ -54,3 +56,2 @@ state.set({ | ||
bindCssVars(); | ||
mount(); | ||
@@ -90,3 +91,2 @@ setSpy.mockClear(); | ||
const cleanup = bindCssVars(); | ||
mount(); | ||
@@ -113,1 +113,11 @@ setSpy.mockClear(); | ||
}); | ||
describe('mount check', () => { | ||
it.each([ | ||
{ fn: bindCssVars, name: 'bindCssVars' }, | ||
])('$name function should throw ERR_NOT_MOUNTED if component was not mounted', ({ fn }) => { | ||
expect(fn).toThrow(new TypedError('ERR_NOT_MOUNTED')); | ||
isMounted.set(true); | ||
expect(fn).not.toThrow(); | ||
}); | ||
}); |
@@ -18,2 +18,3 @@ import { | ||
import { ERR_ALREADY_CALLED } from '@/errors.js'; | ||
import { withIsMounted } from '@/scopes/toolkit/withIsMounted.js'; | ||
@@ -46,3 +47,3 @@ import { isCssVarsBound, state, isMounted } from './signals.js'; | ||
*/ | ||
export function bindCssVars(getCSSVarName?: GetCssVarNameFn): VoidFunction { | ||
export const bindCssVars = withIsMounted((getCSSVarName?: GetCssVarNameFn): VoidFunction => { | ||
if (isCssVarsBound()) { | ||
@@ -74,3 +75,3 @@ throw new TypedError(ERR_ALREADY_CALLED); | ||
}; | ||
} | ||
}, isMounted); | ||
@@ -77,0 +78,0 @@ /** |
@@ -19,2 +19,3 @@ import { | ||
import { subAndCall } from '@/utils/subAndCall.js'; | ||
import { withIsMounted } from '@/scopes/toolkit/withIsMounted.js'; | ||
@@ -54,5 +55,6 @@ import { type GetCSSVarNameFn, request } from './static.js'; | ||
* @returns Function to stop updating variables. | ||
* @throws TypedError ERR_ALREADY_CALLED | ||
* @throws {TypedError} ERR_ALREADY_CALLED | ||
* @throws {TypedError} ERR_NOT_MOUNTED | ||
*/ | ||
export function bindCssVars(getCSSVarName?: GetCSSVarNameFn): VoidFunction { | ||
export const bindCssVars = withIsMounted((getCSSVarName?: GetCSSVarNameFn): VoidFunction => { | ||
if (isCssVarsBound()) { | ||
@@ -79,3 +81,3 @@ throw new TypedError(ERR_ALREADY_CALLED); | ||
}; | ||
} | ||
}, isMounted); | ||
@@ -82,0 +84,0 @@ /** |
import { batch, type Signal } from '@telegram-apps/signals'; | ||
import { type AsyncOptions, CancelablePromise, TypedError } from '@telegram-apps/bridge'; | ||
import { ERR_ALREADY_CALLED } from '@/errors.js'; | ||
import { withIsSupported } from '@/scopes/toolkit/withIsSupported.js'; | ||
@@ -12,2 +13,3 @@ /** | ||
* @param mountError - signal containing mount error. | ||
* @param isSupported - signal containing the component support flag. | ||
*/ | ||
@@ -22,2 +24,3 @@ // #__NO_SIDE_EFFECTS__ | ||
mountError, | ||
isSupported, | ||
}: { | ||
@@ -27,35 +30,38 @@ isMounted: Signal<boolean>, | ||
mountError: Signal<Error | undefined>, | ||
isSupported?: () => boolean, | ||
}, | ||
): (options?: AsyncOptions) => CancelablePromise<void> { | ||
return function mountFn(mountOptions) { | ||
if (isMounted()) { | ||
return CancelablePromise.resolve(); | ||
} | ||
return withIsSupported(mountOptions => { | ||
if (isMounted()) { | ||
return CancelablePromise.resolve(); | ||
} | ||
if (isMounting()) { | ||
throw new TypedError(ERR_ALREADY_CALLED); | ||
} | ||
isMounting.set(true); | ||
if (isMounting()) { | ||
throw new TypedError(ERR_ALREADY_CALLED); | ||
} | ||
isMounting.set(true); | ||
return CancelablePromise | ||
.withFn((abortSignal) => mount({ abortSignal }), mountOptions) | ||
.then<[true, T], [false, Error]>( | ||
r => [true, r], | ||
e => [false, e], | ||
) | ||
.then(tuple => { | ||
batch(() => { | ||
isMounting.set(false); | ||
isMounted.set(true); | ||
return CancelablePromise | ||
.withFn((abortSignal) => mount({ abortSignal }), mountOptions) | ||
.then<[true, T], [false, Error]>( | ||
r => [true, r], | ||
e => [false, e], | ||
) | ||
.then(tuple => { | ||
batch(() => { | ||
isMounting.set(false); | ||
isMounted.set(true); | ||
if (tuple[0]) { | ||
onMounted(tuple[1]); | ||
} else { | ||
const error = tuple[1]; | ||
mountError.set(error); | ||
throw error; | ||
} | ||
if (tuple[0]) { | ||
onMounted(tuple[1]); | ||
} else { | ||
const error = tuple[1]; | ||
mountError.set(error); | ||
throw error; | ||
} | ||
}); | ||
}); | ||
}); | ||
}; | ||
}, | ||
isSupported || (() => true), | ||
); | ||
} |
@@ -9,3 +9,2 @@ import { | ||
type Version, | ||
type CreatePostEventMode, | ||
type RequestFn, | ||
@@ -39,3 +38,3 @@ type CancelablePromise, | ||
*/ | ||
postEvent?: PostEventFn | CreatePostEventMode; | ||
postEvent?: PostEventFn; | ||
} | ||
@@ -73,3 +72,3 @@ | ||
? optionsPostEvent | ||
: createPostEvent(v, optionsPostEvent), | ||
: createPostEvent(v), | ||
); | ||
@@ -76,0 +75,0 @@ } |
@@ -12,3 +12,3 @@ import { | ||
import { invokeCustomMethod, request } from '@/scopes/globals.js'; | ||
import { withIsSupported } from '@/scopes/withIsSupported.js'; | ||
import { withIsSupported } from '@/scopes/toolkit/withIsSupported.js'; | ||
import { ERR_ACCESS_DENIED, ERR_ALREADY_CALLED } from '@/errors.js'; | ||
@@ -31,4 +31,4 @@ import { signal } from '@telegram-apps/signals'; | ||
const REQUEST_PHONE_METHOD = 'web_app_request_phone'; | ||
const REQUEST_WRITE_ACCESS_METHOD = 'web_app_request_write_access'; | ||
const WEB_APP_REQUEST_PHONE = 'web_app_request_phone'; | ||
const WEB_APP_REQUEST_WRITE_ACCESS = 'web_app_request_write_access'; | ||
@@ -77,2 +77,3 @@ /** | ||
* @throws {TypedError} ERR_CUSTOM_METHOD_ERR_RESPONSE | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
@@ -120,3 +121,3 @@ export const requestContact = withIsSupported( | ||
}, | ||
REQUEST_PHONE_METHOD, | ||
WEB_APP_REQUEST_PHONE, | ||
); | ||
@@ -133,2 +134,3 @@ | ||
* @throws {TypedError} ERR_ALREADY_CALLED | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
@@ -142,3 +144,3 @@ export const requestPhoneAccess = withIsSupported( | ||
return request(REQUEST_PHONE_METHOD, 'phone_requested', options) | ||
return request(WEB_APP_REQUEST_PHONE, 'phone_requested', options) | ||
.then(r => r.status) | ||
@@ -148,3 +150,3 @@ .finally(() => { | ||
}); | ||
}, REQUEST_PHONE_METHOD, | ||
}, WEB_APP_REQUEST_PHONE, | ||
); | ||
@@ -156,2 +158,3 @@ | ||
* @throws {TypedError} ERR_ALREADY_CALLED | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
@@ -165,3 +168,3 @@ export const requestWriteAccess = withIsSupported( | ||
return request(REQUEST_WRITE_ACCESS_METHOD, 'write_access_requested', options) | ||
return request(WEB_APP_REQUEST_WRITE_ACCESS, 'write_access_requested', options) | ||
.then(r => r.status) | ||
@@ -171,3 +174,3 @@ .finally(() => { | ||
}); | ||
}, REQUEST_WRITE_ACCESS_METHOD, | ||
}, WEB_APP_REQUEST_WRITE_ACCESS, | ||
); |
@@ -1,1 +0,7 @@ | ||
export { readTextFromClipboard, sendData, switchInlineQuery, shareStory } from './uncategorized.js'; | ||
export { | ||
readTextFromClipboard, | ||
sendData, | ||
switchInlineQuery, | ||
shareStory, | ||
type ShareStoryOptions, | ||
} from './uncategorized.js'; |
@@ -1,6 +0,8 @@ | ||
import { describe, vi, expect, it, afterEach, beforeEach } from 'vitest'; | ||
import { describe, vi, expect, it, beforeEach } from 'vitest'; | ||
import { dispatchMiniAppsEvent } from 'test-utils'; | ||
import { TypedError } from '@telegram-apps/bridge'; | ||
import { resetPackageState } from '@test-utils/reset/reset.js'; | ||
import { mockPostEvent } from '@test-utils/mockPostEvent.js'; | ||
import { $version } from '@/scopes/globals.js'; | ||
@@ -10,15 +12,31 @@ import { readTextFromClipboard, shareStory } from './uncategorized.js'; | ||
beforeEach(() => { | ||
mockPostEvent(); | ||
}); | ||
afterEach(() => { | ||
resetPackageState(); | ||
vi.restoreAllMocks(); | ||
mockPostEvent(); | ||
}); | ||
describe('readTextFromClipboard', () => { | ||
it('should throw if version is less than 6.4', () => { | ||
$version.set('6.3'); | ||
expect(readTextFromClipboard).toThrow(new TypedError('ERR_NOT_SUPPORTED')); | ||
$version.set('6.4'); | ||
expect(readTextFromClipboard).not.toThrow(); | ||
}); | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
it('should call "web_app_read_text_from_clipboard" method and receive "clipboard_text_received" event', async () => { | ||
const spy = mockPostEvent(); | ||
const promise = readTextFromClipboard(); | ||
expect(spy).toHaveBeenCalledTimes(1); | ||
expect(spy).toHaveBeenCalledWith('web_app_read_text_from_clipboard', { | ||
req_id: expect.stringMatching(/\d+/), | ||
}); | ||
dispatchMiniAppsEvent('clipboard_text_received', { | ||
req_id: 1, | ||
req_id: (spy.mock.calls[0][1] as any).req_id, | ||
data: 'Some text', | ||
@@ -29,5 +47,27 @@ }); | ||
}); | ||
describe('isSupported', () => { | ||
it('should return false if version is less than 6.4. True otherwise', () => { | ||
$version.set('6.3'); | ||
expect(readTextFromClipboard.isSupported()).toBe(false); | ||
$version.set('6.4'); | ||
expect(readTextFromClipboard.isSupported()).toBe(true); | ||
}); | ||
}); | ||
}); | ||
describe('shareStory', () => { | ||
it('should throw if version is less than 7.8', () => { | ||
$version.set('7.7'); | ||
expect(() => shareStory('')).toThrow(new TypedError('ERR_NOT_SUPPORTED')); | ||
$version.set('7.8'); | ||
expect(() => shareStory('')).not.toThrow(); | ||
}); | ||
beforeEach(() => { | ||
$version.set('10'); | ||
}); | ||
it('should call "web_app_share_to_story" method with parameters', () => { | ||
@@ -53,3 +93,13 @@ const postEvent = vi.fn(); | ||
}); | ||
describe('isSupported', () => { | ||
it('should return false if version is less than 7.8. True otherwise', () => { | ||
$version.set('7.7'); | ||
expect(shareStory.isSupported()).toBe(false); | ||
$version.set('7.8'); | ||
expect(shareStory.isSupported()).toBe(true); | ||
}); | ||
}); | ||
}); | ||
@@ -8,14 +8,14 @@ import { | ||
type SwitchInlineQueryChatType, | ||
type ExecuteWithPostEvent, | ||
type ExecuteWithPostEvent, supports, | ||
} from '@telegram-apps/bridge'; | ||
import { postEvent, createRequestId, request } from '@/scopes/globals.js'; | ||
import { withIsSupported } from '@/scopes/withIsSupported.js'; | ||
import { postEvent, createRequestId, request, $version } from '@/scopes/globals.js'; | ||
import { withIsSupported } from '@/scopes/toolkit/withIsSupported.js'; | ||
import { ERR_DATA_INVALID_SIZE } from '@/errors.js'; | ||
const READ_TEXT_FROM_CLIPBOARD_METHOD = 'web_app_read_text_from_clipboard'; | ||
const SWITCH_INLINE_QUERY_METHOD = 'web_app_switch_inline_query'; | ||
const SHARE_STORY_METHOD = 'web_app_share_to_story'; | ||
const WEB_APP_READ_TEXT_FROM_CLIPBOARD = 'web_app_read_text_from_clipboard'; | ||
const WEB_APP_SWITCH_INLINE_QUERY = 'web_app_switch_inline_query'; | ||
const WEB_APP_SHARE_TO_STORY = 'web_app_share_to_story'; | ||
interface ShareStoryOptions extends ExecuteWithPostEvent { | ||
export interface ShareStoryOptions extends ExecuteWithPostEvent { | ||
/** | ||
@@ -49,2 +49,3 @@ * The caption to be added to the media. | ||
* - Access to the clipboard is not granted. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
@@ -55,3 +56,3 @@ export const readTextFromClipboard = withIsSupported( | ||
return request(READ_TEXT_FROM_CLIPBOARD_METHOD, 'clipboard_text_received', { | ||
return request(WEB_APP_READ_TEXT_FROM_CLIPBOARD, 'clipboard_text_received', { | ||
...options, | ||
@@ -61,3 +62,3 @@ params: { req_id: reqId }, | ||
}).then(({ data = null }) => data); | ||
}, READ_TEXT_FROM_CLIPBOARD_METHOD, | ||
}, WEB_APP_READ_TEXT_FROM_CLIPBOARD, | ||
); | ||
@@ -87,3 +88,3 @@ | ||
* A method that opens the native story editor. | ||
* @since v7.8 | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
@@ -93,3 +94,3 @@ export const shareStory = withIsSupported( | ||
options ||= {}; | ||
(options.postEvent || postEvent)(SHARE_STORY_METHOD, { | ||
(options.postEvent || postEvent)(WEB_APP_SHARE_TO_STORY, { | ||
text: options.text, | ||
@@ -99,3 +100,3 @@ media_url: mediaUrl, | ||
}); | ||
}, SHARE_STORY_METHOD, | ||
}, WEB_APP_SHARE_TO_STORY, | ||
); | ||
@@ -112,6 +113,7 @@ | ||
* empty list. | ||
* @throws {TypedError} ERR_NOT_SUPPORTED | ||
*/ | ||
export const switchInlineQuery = withIsSupported( | ||
(query: string, chatTypes?: SwitchInlineQueryChatType[]) => { | ||
postEvent(SWITCH_INLINE_QUERY_METHOD, { | ||
postEvent(WEB_APP_SWITCH_INLINE_QUERY, { | ||
query: query, | ||
@@ -121,4 +123,5 @@ chat_types: chatTypes || [], | ||
}, | ||
SWITCH_INLINE_QUERY_METHOD, | ||
() => !!retrieveLaunchParams().botInline, | ||
() => { | ||
return supports(WEB_APP_SWITCH_INLINE_QUERY, $version()) && !!retrieveLaunchParams().botInline; | ||
} | ||
); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
1054482
217
11129