Repository: vkruglikov/react-telegram-web-app
Branch: master
Commit: fc30a5c41ca4
Files: 93
Total size: 135.7 KB
Directory structure:
gitextract_e2fmb7zc/
├── .changeset/
│ └── config.json
├── .eslintignore
├── .eslintrc.json
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ └── bug_report.md
│ └── workflows/
│ ├── contributing.yml
│ ├── release.yml
│ ├── static.yml
│ └── tests.yml
├── .gitignore
├── .husky/
│ └── pre-commit
├── .nvmrc
├── .prettierignore
├── .prettierrc
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── demo/
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── public/
│ │ └── index.html
│ ├── src/
│ │ ├── BackButtonDemo.tsx
│ │ ├── ExpandDemo.tsx
│ │ ├── HapticFeedbackDemo.tsx
│ │ ├── MainButtonDemo.tsx
│ │ ├── ScanQrPopupDemo.tsx
│ │ ├── ShowPopupDemo.tsx
│ │ ├── index.css
│ │ ├── index.tsx
│ │ ├── react-app-env.d.ts
│ │ ├── spy.mjs
│ │ └── useBetaVersion.ts
│ └── tsconfig.json
├── docs/
│ ├── .nojekyll
│ ├── README.md
│ └── interfaces/
│ ├── BackButtonProps.md
│ ├── MainButtonProps.md
│ ├── ScanQrPopupParams.md
│ ├── SettingsButtonProps.md
│ ├── ShowPopupButton.md
│ ├── ShowPopupParams.md
│ └── ThemeParams.md
├── global.d.ts
├── jest.config.json
├── package.json
├── src/
│ ├── BackButton.tsx
│ ├── MainButton.tsx
│ ├── SettingsButton.tsx
│ ├── WebAppProvider.tsx
│ ├── core/
│ │ ├── context.ts
│ │ ├── index.ts
│ │ ├── twa-types/
│ │ │ ├── WebApp.d.ts
│ │ │ ├── WebAppVersion_6.1.d.ts
│ │ │ ├── WebAppVersion_6.2.d.ts
│ │ │ ├── WebAppVersion_6.4.d.ts
│ │ │ ├── WebAppVersion_6.7.d.ts
│ │ │ ├── WebAppVersion_6.9.d.ts
│ │ │ ├── WebAppVersion_7.0.d.ts
│ │ │ └── index.d.ts
│ │ ├── useAsyncMode.ts
│ │ ├── useSmoothButtonsTransition.ts
│ │ └── useWebApp.ts
│ ├── index.ts
│ ├── useCloudStorage.ts
│ ├── useExpand.ts
│ ├── useHapticFeedback.ts
│ ├── useInitData.ts
│ ├── useReadTextFromClipboard.ts
│ ├── useScanQrPopup.ts
│ ├── useShowPopup.ts
│ ├── useSwitchInlineQuery.ts
│ ├── useThemeParams.ts
│ └── useWebApp.ts
├── tests/
│ ├── BackButton.test.tsx
│ ├── MainButton.test.tsx
│ ├── __snapshots__/
│ │ └── package.test.ts.snap
│ ├── core/
│ │ ├── __mocks__/
│ │ │ └── useWebApp.ts
│ │ └── useSmoothButtonsTransition.test.ts
│ ├── package.test.ts
│ ├── setupTests.ts
│ ├── useCloudStorage.test.ts
│ ├── useExpand.test.tsx
│ ├── useHapticFeedback.test.ts
│ ├── useInitData.test.ts
│ ├── useReadTextFromClipboard.test.ts
│ ├── useScanQrPopup.test.ts
│ ├── useShowPopup.test.ts
│ ├── useSwitchInlineQuery.test.ts
│ ├── useThemeParams.test.ts
│ └── utils.ts
├── tsconfig.json
└── typedoc.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .changeset/config.json
================================================
{
"$schema": "https://unpkg.com/@changesets/config@2.2.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "master",
"updateInternalDependencies": "patch",
"ignore": []
}
================================================
FILE: .eslintignore
================================================
/tests/**/*.*
================================================
FILE: .eslintrc.json
================================================
{
"root": true,
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
"react"
],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "error",
"@typescript-eslint/no-explicit-any": ["warn", { "ignoreRestArgs": true }]
}
}
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
<!--
Please provide a clear and concise description of what the bug is. Include
screenshots if needed. Please test using the latest version of the relevant
@vkruglikov/react-telegram-web-app package to make sure your issue has not already been fixed.
-->
Package version:
React version:
Telegram.WebApp version: <!--If the error occurs in the WebApp -->
## Steps To Reproduce
1.
2.
<!--
Your bug will get fixed much faster if we can run your code and it doesn't
have dependencies other than React. Issues without reproduction steps or
code examples may be immediately closed as not actionable.
-->
Link to code example:
<!--
Please provide a CodeSandbox (https://codesandbox.io/s/new), a link to a
repository on GitHub, or provide a minimal code example that reproduces the
problem. You may provide a screenshot of the application if you think it is
relevant to your bug report. Here are some tips for providing a minimal
example: https://stackoverflow.com/help/mcve.
-->
## The current behavior
## The expected behavior
================================================
FILE: .github/workflows/contributing.yml
================================================
name: Generate Contributors Images
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Generate Contributors Images
uses: jaywcjlove/github-action-contributors@main
id: contributors
with:
filter-author: (renovate\[bot\]|renovate-bot|dependabot\[bot\])
avatarSize: 42
- name: Modify README.md
uses: jaywcjlove/github-action-modify-file-content@main
with:
path: README.md
body: '${{steps.contributors.outputs.htmlList}}'
================================================
FILE: .github/workflows/release.yml
================================================
name: Release package
on:
push:
branches:
- master
paths-ignore:
- demo/**
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@master
with:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0
- name: Use Node.js 16.x
uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Install Dependencies
run: npm install
- name: Publish
uses: changesets/action@master
with:
# This expects you to have a script called release which does a build for your packages and calls changeset publish
publish: npm run release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
================================================
FILE: .github/workflows/static.yml
================================================
# Simple workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ['master']
paths:
- demo/**
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow one concurrent deployment
concurrency:
group: 'pages'
cancel-in-progress: true
jobs:
# Single deploy job since we're just deploying
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Use Node.js 16.x
uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Install Dependencies and Build
working-directory: './demo'
run: npm install && npm run build
- name: Setup Pages
uses: actions/configure-pages@v2
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
path: './demo/build/.'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1
================================================
FILE: .github/workflows/tests.yml
================================================
name: Run tests
on:
push:
paths:
- src/**
- tests/**
- jest.config.json
jobs:
test:
runs-on: ubuntu-latest
name: Run tests
steps:
- name: Checkout Repo
uses: actions/checkout@master
with:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0
- name: Use Node.js 16.x
uses: actions/setup-node@v3
with:
node-version: 16.x
- name: Install Dependencies
run: npm install
- name: Run tests
run: npm run test:all
- name: Run package tests
run: npm run build && npm run test:package
================================================
FILE: .gitignore
================================================
/lib
node_modules
.idea
================================================
FILE: .husky/pre-commit
================================================
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
================================================
FILE: .nvmrc
================================================
v16.14.2
================================================
FILE: .prettierignore
================================================
lib
demo/build
package.json
package-lock.json
================================================
FILE: .prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all",
"useTabs": false,
"arrowParens": "avoid",
"overrides": [
{
"files": "package.json",
"options": {
"useTabs": false,
"parser": "json-stringify"
}
}
]
}
================================================
FILE: CHANGELOG.md
================================================
# @vkruglikov/react-telegram-web-app
## 2.2.0
### Minor Changes
- e3bcaf0: Add SettingsButton
## 2.1.9
### Patch Changes
- 5f49e9b: test fix
## 2.1.8
### Patch Changes
- f187824: test fix
## 2.1.7
### Patch Changes
- 94b5e28: Added experimental `async` mode
## 2.1.6
### Patch Changes
- 6631ede: useInitData returns undefined if window is unavailable
## 2.1.5
### Patch Changes
- 1e3beb1: Update docs
## 2.1.4
### Patch Changes
- f705506: Fix signature bug
## 2.1.3
### Patch Changes
- e4b17fb: Fix types
## 2.1.2
### Patch Changes
- e856e14: Fix bug with types
## 2.3.0
### Minor Changes
- c28cc2e: useWebApp hook
## 2.2.0
### Minor Changes
- 0339c94: Added `useCloudStorage` that provides `CloudStorage` object as Promise functions
- 1589011: Added useInitData hook
## 2.1.1
### Patch Changes
- 54f5400: Fix smoothButtonsTransition restore state after refresh page
## 2.1.0
### Minor Changes
- bcf3a0e: Added props `disabled` to MainButton component. It is just an alias on the `MainButtonProps.disable`
Props `disable` marked as deprecated and will be removed
### Patch Changes
- 05d7b34: - Reduced default value for `smoothButtonsTransitionMs`
## 2.0.1
### Patch Changes
- 24545c3: Fix package missed export types
## 2.0.0
### Major Changes
- 4084742: - Have added unnecessary provider `WebAppProvider`
- Fixed all components to works inside `WebAppProvider`
- Fix incorrect types `useExpand`
- Added `smoothButtonsTransition` options to `WebAppProvider`
### Patch Changes
- 3221966: Support dynamic webApp in context
- 5e82b6c: Fix documentation format
- 0bc91cf: Fix structure and types context
- c402a76: Added documentation for WebAppProvider
- 4f18299: Describe all WebApp types
- 111549b: Added jest tests for package and public api
## 1.11.0
### Minor Changes
- af6ccfb: Added useExpand hook, that hook provided isExpanded state, and expand() handle. Remove unsafe_useExpand
## 1.10.1
### Patch Changes
- ef7d3e9: Change readme
## 1.10.0
### Minor Changes
- 8af00e3: Added unsafe_useExpand hook
## 1.9.1
### Patch Changes
- dd8ab05: Update readme
## 1.9.0
### Minor Changes
- 5b1f85f: Added hook useReadTextFromClipboard, that provide function that read text from clipboard
- 2e16b48: Added hook useSwitchInlineQuery
## 1.8.0
### Minor Changes
- 458e93d: Added docs and demo, describes work useScanQrPopup hook
## 1.7.0
### Minor Changes
- c8b49cf: Added useScanQrPopup hook. This hook provide functions showScanQrPopup, closeScanQrPopup
## 1.6.0
### Minor Changes
- 9ed6405: Add useThemeParams hook
## 1.5.4
### Patch Changes
- 5a248e3: Fix .npmignore paths
## 1.5.3
### Patch Changes
- 7a5106c: Fix MainButton uniq effects
## 1.5.2
### Patch Changes
- 2006b71: Add docst and example with useHapticFeedback
## 1.5.1
### Patch Changes
- 92a21c9: Change interfeces
## 1.5.0
### Minor Changes
- dd146bc: Add useHapticFeedback hook
## 1.4.1
### Patch Changes
- 6a7c5b4: Add CRA demo webapp
## 1.4.0
### Minor Changes
- fdbfcc8: Fix window typed error
## 1.3.0
### Minor Changes
- 28c08e4: Add useShowPopup hook
## 1.2.0
### Minor Changes
- b7bfbcb: Add BackButton component
## 1.1.2
### Patch Changes
- 4e36d80: Update documentation
## 1.1.1
### Patch Changes
- 395c91f: Documentation fix
## 1.1.0
### Minor Changes
- 3f5bd48: Changes types, add documentation
## 1.0.3
### Patch Changes
- 81546ee: Add docs pages
## 1.0.2
### Patch Changes
- 6e8043f: Fix ci files, add README and LICENCE
## 1.0.1
### Patch Changes
- ea7807b: Inited project and added MainButton component
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
Hi there! Interested in contributing to `react-telegram-web-app`? We'd love your help
## Goals
The main goal of the project is to make it convenient to use Telegram Mini Apps in react applications
## Ways to contribute
- [Install package](./README.md#-installation--get-started) and kick the tires. Does it work? Does it do what you'd expect? If not, [open an issue](https://github.com/vkruglikov/react-telegram-web-app/issues) and let us know.
- Comment on some of the project's [open issues](https://github.com/vkruglikov/react-telegram-web-app/issues). Have you experienced the same problem? Know a workaround? Do you have a suggestion for how the feature could be better?
- Browse through the [discussions](https://github.com/vkruglikov/react-telegram-web-app/discussions), and lend a hand answering questions. There's a good chance you've already experienced what another user is experiencing.
## Submitting a pull request
### Before you create pull request
- Make sure your changes meet the project's goals
- Before you do something, make sure that other members of the community really need it
- Do not change the basic logic of Telegram Mini Apps, even if there are errors in it. Instead you need to open an issue at https://bugs.telegram.org/
### Pull requests generally
- The smaller the proposed change, the better. If you'd like to propose two unrelated changes, submit two pull requests.
- The more information, the better. Make judicious use of the pull request body. Describe what changes were made, why you made them, and what impact they will have for users.
- If this is your first pull request, it may help to [understand GitHub Flow](https://guides.github.com/introduction/flow/).
## Proposing updates to the documentation
Documentation is generated using [typedoc](https://typedoc.org/guides/overview/).
Look at the examples and describe the documentation sufficiently fully
## Respect the work of other developers
- Don't change the basic styling settings without a good reason
- Do not change basic project settings such as title and personal links
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2022 Kruglikov Valentin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# React components for Telegram MiniApp
[](https://www.npmjs.com/package/@vkruglikov/react-telegram-web-app)
[](https://npmjs.org/package/@vkruglikov/react-telegram-web-app)
[](https://github.com/vkruglikov/react-telegram-web-app/actions/workflows/release.yml)
[](https://github.com/vkruglikov/react-telegram-web-app/blob/master/LICENSE)

## 🔴 Live Demo & Code Examples
You can try open demo telegram bot with React WebApp [@react_telegram_web_app_bot](https://t.me/react_telegram_web_app_bot/demo).
Also, you can look demo [source code](./demo/src).
## 🔧 Installation & Get started
1️⃣ **Foremost**, you have to do [initializing web apps](https://core.telegram.org/bots/webapps#initializing-mini-apps) step, because package has dependency of Telegram Web App context.
2️⃣ **Install** by running: `npm i @vkruglikov/react-telegram-web-app --save`. Today we support React^18.
3️⃣ **Try it out** by writing code.
```jsx
import { MainButton, useShowPopup } from '@vkruglikov/react-telegram-web-app';
const Content = () => {
const showPopup = useShowPopup();
const handleClick = () =>
showPopup({
message: 'Hello, I am popup',
});
return <MainButton text="SHOW POPUP" onClick={handleClick} />;
};
```
## ✨ Short Documentation
### Components
- [MainButton](./docs/README.md#mainbutton) -
The component controls the main button, which is displayed at the bottom of the Web App in the Telegram interface.
- [BackButton](./docs/README.md#backbutton) -
This component controls the back button, which can be displayed in the header of the Web App in the Telegram interface.
- [WebAppProvider](./docs/README.md#webappprovider) -
WebAppProvider provide context with WebApp for components and hooks.
You can try to pass an object with options
- [SettingsButton](./docs/README.md#settingsbutton) Settings button (mvp)
```jsx
import { WebAppProvider, MainButton, BackButton } from '@vkruglikov/react-telegram-web-app';
<WebAppProvider
options={{
smoothButtonsTransition: true,
}}
>
{/** Use components inside provider */}
<MainButton {...} />
<BackButton {...} />
</WebAppProvider>
```
### Hooks
- [useShowPopup](./docs/README.md#useshowpopup) -
This hook provides `showPopup` function that shows a native popup.
- [useHapticFeedback](./docs/README.md#usehapticfeedback) -
This hook provides `impactOccurred`, `notificationOccurred` and `selectionChanged` functions that controls haptic feedback.
- [useThemeParams](./docs/README.md#usethemeparams) -
This hook provides `colorScheme` and `themeParams` object.
- [useScanQrPopup](./docs/README.md#usescanqrpopup) -
This hook provides `showScanQrPopup` and `closeScanQrPopup` functions.
- [useReadTextFromClipboard](./docs/README.md#usereadtextfromclipboard) -
This hook provides `readTextFromClipboard` function.
- [useSwitchInlineQuery](./docs/README.md#useswitchinlinequery) -
This hook provides `switchInlineQuery` function.
- [useExpand](./docs/README.md#useexpand) -
This hook provides `isExpanded` state, and `expand()` handle.
- [useCloudStorage](./docs/README.md#usecloudstorage) -
This hook provides `CloudStorage` object as Promise functions
- [useInitData](./docs/README.md#useinitdata) -
This hook provides `InitDataUnsafe` and `InitData` object
- [useWebApp](./docs/README.md#usewebapp) -
This hook just provides native `WebApp` object
## 🛣 Roadmap
Here's what's coming up:
- [ ] In the future, We would like to use us components also in Web application, without Telegram context.
- [ ] All Telegram WebApp feature support
- [x] Main Telegram WebApp feature support
## Contributors
As always, thanks to our amazing contributors!
<!--GAMFC--><a href="https://github.com/vkruglikov" title="Valentin"><img src="https://avatars.githubusercontent.com/u/9719024?v=4" width="42;" alt="Valentin"/></a>
<a href="https://github.com/xsa-dev" title="Alxy Savin"><img src="https://avatars.githubusercontent.com/u/16959353?v=4" width="42;" alt="Alxy Savin"/></a>
<a href="https://github.com/New-dev0" title="Devesh Pal"><img src="https://avatars.githubusercontent.com/u/69723581?v=4" width="42;" alt="Devesh Pal"/></a>
<a href="https://github.com/alexgoto" title="Alexandr Gotovtsev"><img src="https://avatars.githubusercontent.com/u/43707500?v=4" width="42;" alt="Alexandr Gotovtsev"/></a><!--GAMFC-END-->
Made with [contributors](https://github.com/jaywcjlove/github-action-contributors).
## Contributing
- Read up about its [🛣 Roadmap](#-roadmap) and [official documentation](https://core.telegram.org/bots/webapps) Telegram Mini Apps
- Have questions? Check out our [examples](#-live-demo--code-examples), [duscussions](https://github.com/vkruglikov/react-telegram-web-app/discussions) and [issues](https://github.com/vkruglikov/react-telegram-web-app/discussions)
- [Fork](https://github.com/vkruglikov/react-telegram-web-app/fork) and [Contribute](./CONTRIBUTING.md) your own modifications
## 🥂 License
[MIT](./LICENSE)
## 💻👞🙊📚 Join to discussions
Create discussions, ask questions, share experiences and discuss ideas with everyone together
https://github.com/vkruglikov/react-telegram-web-app/discussions
================================================
FILE: demo/.gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
/*.pem
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
================================================
FILE: demo/README.md
================================================
## install
Install certificate
```bash
mkcert react-telegram-web-app.domain
```
Add domain to /etc/hosts as 127.0.0.1
## OR you can edit .env file, and will configure how you want
================================================
FILE: demo/package.json
================================================
{
"name": "demo",
"version": "0.1.0",
"private": false,
"dependencies": {
"@types/node": "^16.18.3",
"@types/react": "^18.0.25",
"@types/react-dom": "^18.0.9",
"@vkruglikov/react-telegram-web-app": "^2.1.1",
"antd": "^5.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"typescript": "^4.9.3"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app"
]
},
"homepage": "./",
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
================================================
FILE: demo/public/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
/>
<title>react-telegram-web-app</title>
<script src="https://telegram.org/js/telegram-web-app.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
================================================
FILE: demo/src/BackButtonDemo.tsx
================================================
import { Button, Form, Typography } from 'antd';
import { FC, useState } from 'react';
import { BackButton, useShowPopup } from '@vkruglikov/react-telegram-web-app';
const BackButtonDemo: FC = () => {
const [buttonState, setButtonState] = useState<{
show: boolean;
}>();
const showPopup = useShowPopup();
return (
<>
<Typography.Title level={3}>BackButton</Typography.Title>
<Form
labelCol={{ span: 6 }}
name="BackButtonDemo"
layout="horizontal"
autoComplete="off"
>
<Form.Item>
<Button
block
type="primary"
onClick={() =>
setButtonState({
show: !buttonState?.show,
})
}
>
{buttonState?.show ? 'Hide BackButton' : 'Show BackButton'}
</Button>
</Form.Item>
</Form>
<div>
{buttonState?.show && (
<BackButton
onClick={() => {
showPopup({
message: 'back button click',
});
}}
/>
)}
</div>
</>
);
};
export default BackButtonDemo;
================================================
FILE: demo/src/ExpandDemo.tsx
================================================
import { Button, Form, Typography } from 'antd';
import { useExpand } from '@vkruglikov/react-telegram-web-app';
const ExpandDemo = () => {
const [isExpanded, expand] = useExpand();
return (
<>
<Typography.Title level={3}>useExpand</Typography.Title>
<Form
labelCol={{ span: 6 }}
name="ExpandDemo"
layout="horizontal"
autoComplete="off"
>
<Form.Item name="isExpanded">
<Typography.Text>isExpanded: {`${isExpanded}`}</Typography.Text>
</Form.Item>
<Form.Item>
<Button block type="primary" onClick={expand}>
{`isExpanded: ${isExpanded}, call expand()`}
</Button>
</Form.Item>
</Form>
</>
);
};
export default ExpandDemo;
================================================
FILE: demo/src/HapticFeedbackDemo.tsx
================================================
import { Button, Form, Typography, Select } from 'antd';
import { FC, useState } from 'react';
import {
ImpactOccurredFunction,
NotificationOccurredFunction,
useHapticFeedback,
} from '@vkruglikov/react-telegram-web-app';
const HapticFeedbackDemo: FC = () => {
const [impactOccurred, notificationOccurred, selectionChanged] =
useHapticFeedback();
const [style, setStyle] =
useState<Parameters<ImpactOccurredFunction>[0]>('light');
const [type, setType] =
useState<Parameters<NotificationOccurredFunction>[0]>('error');
return (
<>
<Typography.Title level={3}>useHapticFeedback</Typography.Title>
<Form
labelCol={{ span: 6 }}
name="HapticFeedbackDemo"
layout="horizontal"
autoComplete="off"
>
<Form.Item label="style">
<Select value={style} onChange={value => setStyle(value)}>
<Select.Option value="light">light</Select.Option>
<Select.Option value="medium">medium</Select.Option>
<Select.Option value="heavy">heavy</Select.Option>
<Select.Option value="rigid">rigid</Select.Option>
<Select.Option value="soft">soft</Select.Option>
</Select>
</Form.Item>
<Form.Item>
<Button block type="primary" onClick={() => impactOccurred(style)}>
impactOccurred
</Button>
</Form.Item>
<Form.Item label="type">
<Select value={type} onChange={value => setType(value)}>
<Select.Option value="error">error</Select.Option>
<Select.Option value="success">success</Select.Option>
<Select.Option value="warning">warning</Select.Option>
</Select>
</Form.Item>
<Form.Item>
<Button
block
type="primary"
onClick={() => notificationOccurred(type)}
>
notificationOccurred
</Button>
</Form.Item>
<Form.Item>
<Button block type="primary" onClick={() => selectionChanged()}>
selectionChanged
</Button>
</Form.Item>
</Form>
</>
);
};
export default HapticFeedbackDemo;
================================================
FILE: demo/src/MainButtonDemo.tsx
================================================
import { Button, Form, Input, Typography, Switch } from 'antd';
import { FC, useState } from 'react';
import {
MainButton,
MainButtonProps,
} from '@vkruglikov/react-telegram-web-app';
const MainButtonDemo: FC<{
initialValues?: Partial<MainButtonProps> & { show?: boolean };
}> = ({ initialValues }) => {
const [buttonState, setButtonState] = useState<
{
show: boolean;
} & Pick<MainButtonProps, 'text' | 'progress' | 'disable'>
>({
text: 'BUTTON TEXT',
show: false,
progress: false,
disable: false,
...initialValues,
});
const onFinish = (values: any) => setButtonState(values);
return (
<>
<Typography.Title level={3}>MainButton</Typography.Title>
<Form
labelCol={{ span: 6 }}
name="MainButtonDemo"
layout="horizontal"
initialValues={buttonState}
onFinish={onFinish}
autoComplete="off"
>
<Form.Item label="Text" name="text">
<Input disabled />
</Form.Item>
<Form.Item name="progress" label="progress" valuePropName="checked">
<Switch
onChange={value =>
setButtonState({
...buttonState,
progress: value,
})
}
/>
</Form.Item>
<Form.Item name="disable" label="disable" valuePropName="checked">
<Switch
onChange={value =>
setButtonState({
...buttonState,
disable: value,
})
}
/>
</Form.Item>
<Form.Item>
<Button
block
type="primary"
onClick={() =>
setButtonState({
...buttonState,
show: !buttonState?.show,
})
}
>
{buttonState?.show ? 'Hide MainButton' : 'Show MainButton'}
</Button>
</Form.Item>
</Form>
<div>{buttonState?.show && <MainButton {...buttonState} />}</div>
</>
);
};
export default MainButtonDemo;
================================================
FILE: demo/src/ScanQrPopupDemo.tsx
================================================
import { Button, Form, Typography } from 'antd';
import {
useScanQrPopup,
useShowPopup,
} from '@vkruglikov/react-telegram-web-app';
const ScanQrPopupDemo = () => {
const [showQrPopup, closeQrPopup] = useScanQrPopup();
const showPopup = useShowPopup();
return (
<>
<Typography.Title level={3}>useScanQrPopup</Typography.Title>
<Form
labelCol={{ span: 6 }}
name="ScanQrPopupDemo"
layout="horizontal"
autoComplete="off"
>
<Form.Item>
<Button
block
type="primary"
htmlType="button"
onClick={() =>
showQrPopup(
{
text: 'Привет друг',
},
text => {
closeQrPopup();
showPopup({
message: text,
});
},
)
}
>
showScanQrPopup
</Button>
</Form.Item>
</Form>
</>
);
};
export default ScanQrPopupDemo;
================================================
FILE: demo/src/ShowPopupDemo.tsx
================================================
import { Button, Form, Input, Typography } from 'antd';
import { FC, useState } from 'react';
import {
ShowPopupParams,
useShowPopup,
} from '@vkruglikov/react-telegram-web-app';
const ShowPopupDemo: FC = () => {
const showPopup = useShowPopup();
const [popupState, setPopupState] = useState<
Pick<ShowPopupParams, 'title' | 'message'>
>({
title: 'title',
message: 'message',
});
const onFinish = (values: any) => {
setPopupState(values);
showPopup({
...values,
buttons: [
{
type: 'ok',
},
{
type: 'close',
},
{
type: 'destructive',
text: 'destructive',
},
],
}).catch(e => {
showPopup({
title: 'error',
message: e,
});
});
};
return (
<>
<Typography.Title level={3}>useShowPopup</Typography.Title>
<Form
labelCol={{ span: 6 }}
name="ShowPopupDemo"
layout="horizontal"
initialValues={popupState}
onFinish={onFinish}
autoComplete="off"
>
<Form.Item label="title" name="title">
<Input />
</Form.Item>
<Form.Item label="message" name="message">
<Input />
</Form.Item>
<Form.Item label="buttons" name="buttons">
<Typography.Text>
{JSON.stringify([
{
type: 'ok',
},
{
type: 'close',
},
{
type: 'destructive',
text: 'destructive',
},
])}
</Typography.Text>
</Form.Item>
<Form.Item>
<Button block type="primary" htmlType="submit">
Show popup
</Button>
</Form.Item>
</Form>
</>
);
};
export default ShowPopupDemo;
================================================
FILE: demo/src/index.css
================================================
:root {
--tg-theme-bg-color: #fff;
--tg-theme-text-color: #0a0a0a;
--tg-theme-hint-color: #929292;
--tg-theme-link-color: #207ae4;
--tg-theme-button-color: #5bc8fb;
--tg-theme-button-text-color: #fffeec;
--tg-theme-secondary-bg-color: #f3f2f9;
--default-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol';
}
body {
background: var(--tg-theme-secondary-bg-color);
padding: 20px 20px;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
.App-logo {
height: 40vmin;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.betaVersion {
padding: 10px 10px;
margin-bottom: 20px;
border: 1px solid #1777ff;
}
.App-header {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.contentWrapper {
background: var(--tg-theme-bg-color);
color: var(--tg-theme-text-color) !important;
border-radius: 10px;
margin: 5px 0;
padding: 20px;
box-sizing: border-box;
}
.ant-input,
.ant-select,
.ant-select-item {
background-color: unset !important;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
================================================
FILE: demo/src/index.tsx
================================================
import React, { DispatchWithoutAction, FC, useState } from 'react';
import ReactDOM from 'react-dom/client';
import {
useThemeParams,
WebAppProvider,
} from '@vkruglikov/react-telegram-web-app';
import { ConfigProvider, theme } from 'antd';
import 'antd/dist/reset.css';
import './index.css';
import logo from './logo.svg';
import MainButtonDemo from './MainButtonDemo';
import BackButtonDemo from './BackButtonDemo';
import ShowPopupDemo from './ShowPopupDemo';
import HapticFeedbackDemo from './HapticFeedbackDemo';
import ScanQrPopupDemo from './ScanQrPopupDemo';
import ExpandDemo from './ExpandDemo';
import useBetaVersion from './useBetaVersion';
const DemoApp: FC<{
onChangeTransition: DispatchWithoutAction;
}> = ({ onChangeTransition }) => {
const [colorScheme, themeParams] = useThemeParams();
const [isBetaVersion, handleRequestBeta] = useBetaVersion(false);
const [activeBtn, setActiveBtn] = useState(true);
return (
<div>
<ConfigProvider
theme={
themeParams.text_color
? {
algorithm:
colorScheme === 'dark'
? theme.darkAlgorithm
: theme.defaultAlgorithm,
token: {
colorText: themeParams.text_color,
colorPrimary: themeParams.button_color,
colorBgBase: themeParams.bg_color,
},
}
: undefined
}
>
<header className="App-header">
<img
onClick={handleRequestBeta}
src={logo}
className="App-logo"
alt="logo"
/>
</header>
<div className="contentWrapper">
{isBetaVersion && (
<div className="betaVersion">
<h3>WARNING: BETA VERSION</h3>
<button onClick={() => setActiveBtn(state => !state)}>
change button
</button>
<button onClick={onChangeTransition}>change </button>
</div>
)}
<ExpandDemo />
{!activeBtn ? (
<MainButtonDemo
initialValues={{
show: isBetaVersion,
text: 'SECOND BUTTON',
progress: true,
}}
key="1"
/>
) : (
<MainButtonDemo
key="2"
initialValues={{
show: isBetaVersion,
}}
/>
)}
<BackButtonDemo />
<ShowPopupDemo />
<HapticFeedbackDemo />
<ScanQrPopupDemo />
</div>
</ConfigProvider>
</div>
);
};
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement,
);
const App = () => {
const [smoothButtonsTransition, setSmoothButtonsTransition] = useState(false);
return (
<WebAppProvider options={{ smoothButtonsTransition }}>
<DemoApp
onChangeTransition={() => setSmoothButtonsTransition(state => !state)}
/>
</WebAppProvider>
);
};
root.render(<App />);
================================================
FILE: demo/src/react-app-env.d.ts
================================================
/// <reference types="react-scripts" />
================================================
FILE: demo/src/spy.mjs
================================================
// @ts-ignore
const WebView = window.Telegram.WebView;
const postEvent = WebView.postEvent;
const receiveEvent = WebView.postEvent;
const element = document.createElement('div');
WebView.postEvent = (...args) => {
const [eventType, callback, eventData] = args;
const asd = document.createElement('div');
asd.innerHTML = `${eventType} -> ${JSON.stringify(eventData)}`
element.appendChild(asd)
element.scrollTop = element.scrollHeight;
return postEvent(...args);
};
WebView.receiveEvent = (...args) => {
const [eventType, callback, eventData] = args;
console.log('--ФЫВ------------', eventType, callback, eventData);
return receiveEvent(...args);
};
element.setAttribute(
'style',
'position:fixed;top:0;left:0;right:0;z-index:1000;background:#fff;height:80px;border-bottom:2px solid red;overflow: auto;',
);
document.body.appendChild(element);
================================================
FILE: demo/src/useBetaVersion.ts
================================================
import {
useHapticFeedback,
useShowPopup,
} from '@vkruglikov/react-telegram-web-app';
import { useRef, useState } from 'react';
const useBetaVersion = (initialState = false) => {
const showPopup = useShowPopup();
const [, notification] = useHapticFeedback();
const [isBeta, setIsBeta] = useState(
initialState || process.env.NODE_ENV === 'development',
);
const isDevModeCounter = useRef(0);
const handleRequestBeta = () => {
if (++isDevModeCounter.current >= 10) {
setIsBeta(!isBeta);
isDevModeCounter.current = 0;
showPopup({ message: `isDevMode: ${!isBeta}` });
notification('success');
}
if (isDevModeCounter.current > 7) {
showPopup({
message: `${10 - isDevModeCounter.current}`,
});
}
};
return [isBeta, handleRequestBeta] as const;
};
export default useBetaVersion;
================================================
FILE: demo/tsconfig.json
================================================
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"]
}
================================================
FILE: docs/.nojekyll
================================================
TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false.
================================================
FILE: docs/README.md
================================================
@vkruglikov/react-telegram-web-app
# @vkruglikov/react-telegram-web-app
## Table of contents
### Interfaces
- [BackButtonProps](interfaces/BackButtonProps.md)
- [MainButtonProps](interfaces/MainButtonProps.md)
- [ScanQrPopupParams](interfaces/ScanQrPopupParams.md)
- [SettingsButtonProps](interfaces/SettingsButtonProps.md)
- [ShowPopupButton](interfaces/ShowPopupButton.md)
- [ShowPopupParams](interfaces/ShowPopupParams.md)
- [ThemeParams](interfaces/ThemeParams.md)
### Type Aliases
- [CloseScanQrPopupFunction](README.md#closescanqrpopupfunction)
- [ColorScheme](README.md#colorscheme)
- [GetItemFunction](README.md#getitemfunction)
- [GetItemsFunction](README.md#getitemsfunction)
- [GetKeysFunction](README.md#getkeysfunction)
- [ImpactOccurredFunction](README.md#impactoccurredfunction)
- [InitData](README.md#initdata)
- [InitDataUnsafe](README.md#initdataunsafe)
- [NotificationOccurredFunction](README.md#notificationoccurredfunction)
- [Options](README.md#options)
- [ReadTextFromClipboardFunction](README.md#readtextfromclipboardfunction)
- [RemoveItemFunction](README.md#removeitemfunction)
- [RemoveItemsFunction](README.md#removeitemsfunction)
- [ScanQrPopupCallback](README.md#scanqrpopupcallback)
- [SelectionChangedFunction](README.md#selectionchangedfunction)
- [SetItemFunction](README.md#setitemfunction)
- [ShowPopupFunction](README.md#showpopupfunction)
- [ShowScanQrPopupFunction](README.md#showscanqrpopupfunction)
- [SwitchInlineQueryFunction](README.md#switchinlinequeryfunction)
- [WebAppChat](README.md#webappchat)
- [WebAppProviderProps](README.md#webappproviderprops)
- [WebAppUser](README.md#webappuser)
### Hooks
- [useCloudStorage](README.md#usecloudstorage)
- [useExpand](README.md#useexpand)
- [useHapticFeedback](README.md#usehapticfeedback)
- [useInitData](README.md#useinitdata)
- [useReadTextFromClipboard](README.md#usereadtextfromclipboard)
- [useScanQrPopup](README.md#usescanqrpopup)
- [useShowPopup](README.md#useshowpopup)
- [useSwitchInlineQuery](README.md#useswitchinlinequery)
- [useThemeParams](README.md#usethemeparams)
- [useWebApp](README.md#usewebapp)
### React Components
- [BackButton](README.md#backbutton)
- [MainButton](README.md#mainbutton)
- [SettingsButton](README.md#settingsbutton)
- [WebAppProvider](README.md#webappprovider)
## Type Aliases
### CloseScanQrPopupFunction
Ƭ **CloseScanQrPopupFunction**: () => `void`
#### Type declaration
▸ (): `void`
A method that closes the native popup for scanning a QR code opened with the showScanQrPopup method
##### Returns
`void`
---
### ColorScheme
Ƭ **ColorScheme**: `"light"` \| `"dark"` \| `undefined`
The color scheme currently used in the Telegram app. Either “light” or “dark”.
Can `undefined`, if `window` is undefined.
---
### GetItemFunction
Ƭ **GetItemFunction**: (`key`: `string`) => `Promise`<`string`\>
#### Type declaration
▸ (`key`): `Promise`<`string`\>
This function provides `getItem` method from [telegram!CloudStorage](https://core.telegram.org/bots/webapps#cloudstorage) as Promise
**`Throws`**
##### Parameters
| Name | Type |
| :---- | :------- |
| `key` | `string` |
##### Returns
`Promise`<`string`\>
---
### GetItemsFunction
Ƭ **GetItemsFunction**: (`keys`: `string`[]) => `Promise`<`string`[]\>
#### Type declaration
▸ (`keys`): `Promise`<`string`[]\>
This function provides `getItems` method from [telegram!CloudStorage](https://core.telegram.org/bots/webapps#cloudstorage) as Promise
**`Throws`**
##### Parameters
| Name | Type |
| :----- | :--------- |
| `keys` | `string`[] |
##### Returns
`Promise`<`string`[]\>
---
### GetKeysFunction
Ƭ **GetKeysFunction**: () => `Promise`<`string`[]\>
#### Type declaration
▸ (): `Promise`<`string`[]\>
This function provides `getKeys` method from [telegram!CloudStorage](https://core.telegram.org/bots/webapps#cloudstorage) as Promise
**`Throws`**
##### Returns
`Promise`<`string`[]\>
---
### ImpactOccurredFunction
Ƭ **ImpactOccurredFunction**: (`style`: `"light"` \| `"medium"` \| `"heavy"` \| `"rigid"` \| `"soft"`) => `void`
#### Type declaration
▸ (`style`): `void`
A method tells that an impact occurred. The Telegram app may play the appropriate haptics based on style value passed. Style can be one of these values:
- light, indicates a collision between small or lightweight UI objects,
- medium, indicates a collision between medium-sized or medium-weight UI objects,
- heavy, indicates a collision between large or heavyweight UI objects,
- rigid, indicates a collision between hard or inflexible UI objects,
- soft, indicates a collision between soft or flexible UI objects.
[telegram!HapticFeedback](https://core.telegram.org/bots/webapps#hapticfeedback)
##### Parameters
| Name | Type |
| :------ | :------------------------------------------------------------ |
| `style` | `"light"` \| `"medium"` \| `"heavy"` \| `"rigid"` \| `"soft"` |
##### Returns
`void`
---
### InitData
Ƭ **InitData**: `string`
---
### InitDataUnsafe
Ƭ **InitDataUnsafe**: `Object`
[telegram!WebAppInitData](https://core.telegram.org/bots/webapps#webappinitdata)
#### Type declaration
| Name | Type |
| :---------------- | :---------------------------------------------------------------------- |
| `auth_date` | `number` |
| `can_send_after?` | `number` |
| `chat?` | [`WebAppChat`](README.md#webappchat) |
| `chat_instance?` | `string` |
| `chat_type?` | `"sender"` \| `"private"` \| `"group"` \| `"supergroup"` \| `"channel"` |
| `hash` | `string` |
| `query_id?` | `string` |
| `receiver?` | [`WebAppUser`](README.md#webappuser) |
| `start_param?` | `string` |
| `user?` | [`WebAppUser`](README.md#webappuser) |
---
### NotificationOccurredFunction
Ƭ **NotificationOccurredFunction**: (`type`: `"error"` \| `"success"` \| `"warning"`) => `void`
#### Type declaration
▸ (`type`): `void`
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. Type can be one of these values:
- error, indicates that a task or action has failed,
- success, indicates that a task or action has completed successfully,
- warning, indicates that a task or action produced a warning.
[telegram!HapticFeedback](https://core.telegram.org/bots/webapps#hapticfeedback)
##### Parameters
| Name | Type |
| :----- | :-------------------------------------- |
| `type` | `"error"` \| `"success"` \| `"warning"` |
##### Returns
`void`
---
### Options
Ƭ **Options**: `Object`
This object describe options be able to set through WebAppProvider
#### Type declaration
| Name | Type | Description |
| :--------------------------- | :-------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `async?` | `boolean` | - |
| `smoothButtonsTransition?` | `boolean` | When is `true`, we can smooth button transitions due to show(), hide() calls. So when you use MainButton or BackButton on multiple pages, there will be no noticeable flickering of the button during transitions **`Default Value`** `false` |
| `smoothButtonsTransitionMs?` | `number` | **`Default Value`** `10` **`Remarks`** |
---
### ReadTextFromClipboardFunction
Ƭ **ReadTextFromClipboardFunction**: () => `Promise`<`string`\>
#### Type declaration
▸ (): `Promise`<`string`\>
This function provided Promise function that read text from clipboard
##### Returns
`Promise`<`string`\>
---
### RemoveItemFunction
Ƭ **RemoveItemFunction**: (`key`: `string`) => `Promise`<`void`\>
#### Type declaration
▸ (`key`): `Promise`<`void`\>
This function provides `removeItem` method from [telegram!CloudStorage](https://core.telegram.org/bots/webapps#cloudstorage) as Promise
**`Throws`**
##### Parameters
| Name | Type |
| :---- | :------- |
| `key` | `string` |
##### Returns
`Promise`<`void`\>
---
### RemoveItemsFunction
Ƭ **RemoveItemsFunction**: (`key`: `string`[]) => `Promise`<`void`\>
#### Type declaration
▸ (`key`): `Promise`<`void`\>
This function provides `removeItems` method from [telegram!CloudStorage](https://core.telegram.org/bots/webapps#cloudstorage) as Promise
**`Throws`**
##### Parameters
| Name | Type |
| :---- | :--------- |
| `key` | `string`[] |
##### Returns
`Promise`<`void`\>
---
### ScanQrPopupCallback
Ƭ **ScanQrPopupCallback**: (`text`: `string`) => `true` \| `void`
#### Type declaration
▸ (`text`): `true` \| `void`
If an optional callback parameter was passed, the callback function will be called and the text from the QR
code will be passed as the first argument.
Returning true inside this callback function causes the popup to be closed.
##### Parameters
| Name | Type |
| :----- | :------- |
| `text` | `string` |
##### Returns
`true` \| `void`
---
### SelectionChangedFunction
Ƭ **SelectionChangedFunction**: () => `void`
#### Type declaration
▸ (): `void`
A method tells that the user has changed a selection. The Telegram app may play the appropriate haptics.
[telegram!HapticFeedback](https://core.telegram.org/bots/webapps#hapticfeedback)
##### Returns
`void`
---
### SetItemFunction
Ƭ **SetItemFunction**: (`key`: `string`, `value`: `string`) => `Promise`<`void`\>
#### Type declaration
▸ (`key`, `value`): `Promise`<`void`\>
This function provides `setItem` method from [telegram!CloudStorage](https://core.telegram.org/bots/webapps#cloudstorage) as Promise
**`Throws`**
##### Parameters
| Name | Type |
| :------ | :------- |
| `key` | `string` |
| `value` | `string` |
##### Returns
`Promise`<`void`\>
---
### ShowPopupFunction
Ƭ **ShowPopupFunction**: (`params`: [`ShowPopupParams`](interfaces/ShowPopupParams.md)) => `Promise`<`string`\>
#### Type declaration
▸ (`params`): `Promise`<`string`\>
This function provides Promise, and resolve the field id of the pressed button will be passed.
**`Throws`**
##### Parameters
| Name | Type |
| :------- | :------------------------------------------------- |
| `params` | [`ShowPopupParams`](interfaces/ShowPopupParams.md) |
##### Returns
`Promise`<`string`\>
Button id as string, it was described by [ShowPopupButton](interfaces/ShowPopupButton.md)
---
### ShowScanQrPopupFunction
Ƭ **ShowScanQrPopupFunction**: (`params`: [`ScanQrPopupParams`](interfaces/ScanQrPopupParams.md), `callback?`: [`ScanQrPopupCallback`](README.md#scanqrpopupcallback)) => `void`
#### Type declaration
▸ (`params`, `callback?`): `void`
A method that shows a native popup for scanning a QR code described
by the params argument of the type [ScanQrPopupParams](interfaces/ScanQrPopupParams.md).
##### Parameters
| Name | Type |
| :---------- | :----------------------------------------------------- |
| `params` | [`ScanQrPopupParams`](interfaces/ScanQrPopupParams.md) |
| `callback?` | [`ScanQrPopupCallback`](README.md#scanqrpopupcallback) |
##### Returns
`void`
---
### SwitchInlineQueryFunction
Ƭ **SwitchInlineQueryFunction**: (`query`: `string`, `chatType?`: (`"users"` \| `"bots"` \| `"groups"` \| `"channels"`)[]) => `void`
#### Type declaration
▸ (`query`, `chatType?`): `void`
This function that inserts the bot's username and the specified inline query in the current chat's input field
You have to look original description switchInlineQuery in [telegram!WebApp](https://core.telegram.org/bots/webapps#initializing-mini-apps) for more information
##### Parameters
| Name | Type |
| :---------- | :------------------------------------------------------ |
| `query` | `string` |
| `chatType?` | (`"users"` \| `"bots"` \| `"groups"` \| `"channels"`)[] |
##### Returns
`void`
---
### WebAppChat
Ƭ **WebAppChat**: `Object`
[telegram!WebAppChat](https://core.telegram.org/bots/webapps#webappchat)
#### Type declaration
| Name | Type |
| :----------- | :----------------------------------------- |
| `id` | `number` |
| `photo_url?` | `string` |
| `title` | `string` |
| `type` | `"group"` \| `"supergroup"` \| `"channel"` |
| `username?` | `string` |
---
### WebAppProviderProps
Ƭ **WebAppProviderProps**: `PropsWithChildren`<{ `options?`: [`Options`](README.md#options) }\>
---
### WebAppUser
Ƭ **WebAppUser**: `Object`
[telegram!WebAppUser](https://core.telegram.org/bots/webapps#webappuser)
#### Type declaration
| Name | Type |
| :-------------------------- | :-------- |
| `added_to_attachment_menu?` | `true` |
| `allows_write_to_pm?` | `true` |
| `first_name` | `string` |
| `id` | `number` |
| `is_bot?` | `boolean` |
| `is_premium?` | `boolean` |
| `language_code?` | `string` |
| `last_name?` | `string` |
| `photo_url?` | `string` |
| `username?` | `string` |
## Hooks
### useCloudStorage
▸ **useCloudStorage**(): `Object`
This hook provides [telegram!CloudStorage](https://core.telegram.org/bots/webapps#cloudstorage) object with promises functions,
so you don't have to pass `callback` argument
You have to look original description CloudStorage object in [telegram!CloudStorage](https://core.telegram.org/bots/webapps#cloudstorage)
#### Returns
`Object`
| Name | Type |
| :----------- | :--------------------------------------------------- |
| `getItem` | [`GetItemFunction`](README.md#getitemfunction) |
| `getItems` | [`GetItemsFunction`](README.md#getitemsfunction) |
| `getKeys` | [`GetKeysFunction`](README.md#getkeysfunction) |
| `removeItem` | [`RemoveItemFunction`](README.md#removeitemfunction) |
| `setItem` | [`SetItemFunction`](README.md#setitemfunction) |
---
### useExpand
▸ **useExpand**(): readonly [`undefined` \| `boolean`, `DispatchWithoutAction`]
This hook provided isExpanded state, and expand() handle
You have to look original description in [telegram!WebApp](https://core.telegram.org/bots/webapps#initializing-mini-apps) for more information
`isExpanded` can be `undefined`
```tsx
import { useExpand } from "@vkruglikov/react-telegram-web-app";
const [isExpanded, expand] = useExpand();
const handleClick = () => !isExpanded && expand();
<button onClick={handleClick}>
{showTextWhenScreenExpanded && 'expanded' : 'to expand'}
</button>
```
#### Returns
readonly [`undefined` \| `boolean`, `DispatchWithoutAction`]
---
### useHapticFeedback
▸ **useHapticFeedback**(): readonly [[`ImpactOccurredFunction`](README.md#impactoccurredfunction), [`NotificationOccurredFunction`](README.md#notificationoccurredfunction), [`SelectionChangedFunction`](README.md#selectionchangedfunction)]
This hook that provided [ImpactOccurredFunction](README.md#impactoccurredfunction), [NotificationOccurredFunction](README.md#notificationoccurredfunction) and [SelectionChangedFunction](README.md#selectionchangedfunction) functions that controls haptic feedback.
You have to look original telegram description [telegram!HapticFeedback](https://core.telegram.org/bots/webapps#hapticfeedback), because it Hook implementing his.
```tsx
import { useHapticFeedback } from '@vkruglikov/react-telegram-web-app';
const [impactOccurred, notificationOccurred, selectionChanged] =
useHapticFeedback();
// const [,notificationOccurred] = useHapticFeedback();
impactOccurred('heavy');
notificationOccurred('success');
```
#### Returns
readonly [[`ImpactOccurredFunction`](README.md#impactoccurredfunction), [`NotificationOccurredFunction`](README.md#notificationoccurredfunction), [`SelectionChangedFunction`](README.md#selectionchangedfunction)]
---
### useInitData
▸ **useInitData**(): readonly [`undefined` \| [`InitDataUnsafe`](README.md#initdataunsafe), `undefined` \| `string`]
This hook provides `initDataUnsafe` and `initData`
You have to look original description in [telegram!WebApp](https://core.telegram.org/bots/webapps#initializing-mini-apps), because hook just return this.
```tsx
import { useInitData } from '@vkruglikov/react-telegram-web-app';
const [initDataUnsafe] = useInitData();
const [initDataUnsafe, initData] = useInitData();
```
#### Returns
readonly [`undefined` \| [`InitDataUnsafe`](README.md#initdataunsafe), `undefined` \| `string`]
---
### useReadTextFromClipboard
▸ **useReadTextFromClipboard**(): [`ReadTextFromClipboardFunction`](README.md#readtextfromclipboardfunction)
This hook that provided [ReadTextFromClipboardFunction](README.md#readtextfromclipboardfunction) Promise function that read text from clipboard.
You have to look original description readTextFromClipboard in [telegram!WebApp](https://core.telegram.org/bots/webapps#initializing-mini-apps), because hook just implements his.
```tsx
import { useReadTextFromClipboard } from '@vkruglikov/react-telegram-web-app';
const readText = useReadTextFromClipboard();
readText().then(console.log);
// or
await readText();
```
#### Returns
[`ReadTextFromClipboardFunction`](README.md#readtextfromclipboardfunction)
---
### useScanQrPopup
▸ **useScanQrPopup**(): readonly [[`ShowScanQrPopupFunction`](README.md#showscanqrpopupfunction), [`CloseScanQrPopupFunction`](README.md#closescanqrpopupfunction)]
The hook provided showScanQrPopup function of the type [ShowScanQrPopupFunction](README.md#showscanqrpopupfunction) and closeScanQrPopup [CloseScanQrPopupFunction](README.md#closescanqrpopupfunction).
#### Returns
readonly [[`ShowScanQrPopupFunction`](README.md#showscanqrpopupfunction), [`CloseScanQrPopupFunction`](README.md#closescanqrpopupfunction)]
---
### useShowPopup
▸ **useShowPopup**(): [`ShowPopupFunction`](README.md#showpopupfunction)
The hook provided showPopup function of the type [ShowPopupFunction](README.md#showpopupfunction).
The function that shows a native popup described by the params argument of the type [ShowPopupParams](interfaces/ShowPopupParams.md).
```tsx
import { useShowPopup } from '@vkruglikov/react-telegram-web-app';
const showPopup = useShowPopup();
showPopup({ message: 'Hello world' }).then(buttonId => console.log(buttonId));
```
#### Returns
[`ShowPopupFunction`](README.md#showpopupfunction)
---
### useSwitchInlineQuery
▸ **useSwitchInlineQuery**(): [`SwitchInlineQueryFunction`](README.md#switchinlinequeryfunction)
This hook that provided [SwitchInlineQueryFunction](README.md#switchinlinequeryfunction)
You have to look original description switchInlineQuery in [telegram!WebApp](https://core.telegram.org/bots/webapps#initializing-mini-apps), because hook just implements his.
#### Returns
[`SwitchInlineQueryFunction`](README.md#switchinlinequeryfunction)
---
### useThemeParams
▸ **useThemeParams**(): readonly [[`ColorScheme`](README.md#colorscheme), [`ThemeParams`](interfaces/ThemeParams.md)]
The hook provided colorScheme and themeParams values of the type [ColorScheme](README.md#colorscheme) and [ThemeParams](interfaces/ThemeParams.md).
```tsx
import { useThemeParams } from '@vkruglikov/react-telegram-web-app';
const [colorScheme, themeParams] = useThemeParams();
console.log(colorScheme === 'dark');
console.log({
text_color: themeParams.text_color,
button_color: themeParams.button_color,
bg_color: themeParams.bg_color,
});
```
#### Returns
readonly [[`ColorScheme`](README.md#colorscheme), [`ThemeParams`](interfaces/ThemeParams.md)]
---
### useWebApp
▸ **useWebApp**(): `any`
This hook just provides native [telegram!WebApp](https://core.telegram.org/bots/webapps#initializing-mini-apps) object
```tsx
import { useWebApp } from '@vkruglikov/react-telegram-web-app';
const WebApp = useWebApp();
console.log(WebApp.version);
```
#### Returns
`any`
## React Components
### BackButton
▸ **BackButton**(`props`): `null`
Renders a [telegram!BackButton](https://core.telegram.org/bots/webapps#backbutton) component in React app as [react!Component](https://reactjs.org/docs/react-component.html)
```tsx
import { BackButton } from '@vkruglikov/react-telegram-web-app';
<BackButton onClick={() => console.log('Hello, I am back button!')} />;
```
#### Parameters
| Name | Type |
| :------ | :------------------------------------------------- |
| `props` | [`BackButtonProps`](interfaces/BackButtonProps.md) |
#### Returns
`null`
---
### MainButton
▸ **MainButton**(`props`): `null`
Renders a [telegram!MainButton](https://core.telegram.org/bots/webapps#mainbutton) component in React app as [react!Component](https://reactjs.org/docs/react-component.html)
```tsx
import { MainButton } from '@vkruglikov/react-telegram-web-app';
<MainButton
text="CLICK ME"
onClick={() => console.log('Hello, I am button!')}
/>;
```
#### Parameters
| Name | Type |
| :------ | :------------------------------------------------- |
| `props` | [`MainButtonProps`](interfaces/MainButtonProps.md) |
#### Returns
`null`
---
### SettingsButton
▸ **SettingsButton**(`props`): `null`
Renders a [telegram!SettingsButton](https://core.telegram.org/bots/webapps#settingsbutton) component in React app as [react!Component](https://reactjs.org/docs/react-component.html)
```tsx
import { SettingsButton } from '@vkruglikov/react-telegram-web-app';
<SettingsButton onClick={() => console.log('Hello, I am settings button!')} />;
```
#### Parameters
| Name | Type |
| :------ | :--------------------------------------------------------- |
| `props` | [`SettingsButtonProps`](interfaces/SettingsButtonProps.md) |
#### Returns
`null`
---
### WebAppProvider
▸ **WebAppProvider**(`props`): `ReactElement`<`any`, `string` \| `JSXElementConstructor`<`any`\>\>
WebAppProvider provide context with WebApp for components and hooks.
Necessary to use only if you want to override `options`
```tsx
import { WebAppProvider } from "@vkruglikov/react-telegram-web-app";
<WebAppProvider>
<YourAppComponent />
</WebAppProvider>
// You can pass options {@link Options}
<WebAppProvider
options={{
smoothButtonsTransition: true
}}
>
<YourAppComponent />
</WebAppProvider>
```
#### Parameters
| Name | Type |
| :------ | :----------------------------------------------------- |
| `props` | [`WebAppProviderProps`](README.md#webappproviderprops) |
#### Returns
`ReactElement`<`any`, `string` \| `JSXElementConstructor`<`any`\>\>
================================================
FILE: docs/interfaces/BackButtonProps.md
================================================
[@vkruglikov/react-telegram-web-app](../README.md) / BackButtonProps
# Interface: BackButtonProps
The props type of [`BackButton`](../README.md#backbutton).
## Table of contents
### Properties
- [onClick](BackButtonProps.md#onclick)
## Properties
### onClick
• `Optional` **onClick**: () => `void`
#### Type declaration
▸ (): `void`
The back button press event handler
##### Returns
`void`
================================================
FILE: docs/interfaces/MainButtonProps.md
================================================
[@vkruglikov/react-telegram-web-app](../README.md) / MainButtonProps
# Interface: MainButtonProps
The props type of [`MainButton`](../README.md#mainbutton).
## Table of contents
### Properties
- [color](MainButtonProps.md#color)
- [disabled](MainButtonProps.md#disabled)
- [onClick](MainButtonProps.md#onclick)
- [progress](MainButtonProps.md#progress)
- [text](MainButtonProps.md#text)
- [textColor](MainButtonProps.md#textcolor)
## Properties
### color
• `Optional` **color**: `string`
Current button color.
**`Default Value`**
Set to themeParams.button_color by default
---
### disabled
• `Optional` **disabled**: `boolean`
The button disable state.
**`Default Value`**
Set to `false` y defaults
---
### onClick
• `Optional` **onClick**: () => `void`
#### Type declaration
▸ (): `void`
The button press event handler
##### Returns
`void`
---
### progress
• `Optional` **progress**: `boolean`
The button progress state indicator.
**`Default Value`**
Set to `false` by default
---
### text
• `Optional` **text**: `string`
Current button text
**`Default Value`**
Set to `CONTINUE` by default
---
### textColor
• `Optional` **textColor**: `string`
Current button text color
**`Default Value`**
Set to themeParams.button_text_color by default
================================================
FILE: docs/interfaces/ScanQrPopupParams.md
================================================
[@vkruglikov/react-telegram-web-app](../README.md) / ScanQrPopupParams
# Interface: ScanQrPopupParams
This object describes the native popup for scanning QR codes.
You have to see original interface [telegram!ScanQrPopupParams](https://core.telegram.org/bots/webapps#scanqrpopupparams).
## Table of contents
### Properties
- [text](ScanQrPopupParams.md#text)
## Properties
### text
• **text**: `string`
The text to be displayed under the 'Scan QR' heading, 0-64 characters.
================================================
FILE: docs/interfaces/SettingsButtonProps.md
================================================
[@vkruglikov/react-telegram-web-app](../README.md) / SettingsButtonProps
# Interface: SettingsButtonProps
The props type of [`SettingsButton`](../README.md#settingsbutton).
## Table of contents
### Properties
- [onClick](SettingsButtonProps.md#onclick)
## Properties
### onClick
• `Optional` **onClick**: () => `void`
#### Type declaration
▸ (): `void`
##### Returns
`void`
================================================
FILE: docs/interfaces/ShowPopupButton.md
================================================
[@vkruglikov/react-telegram-web-app](../README.md) / ShowPopupButton
# Interface: ShowPopupButton
You have to see original interface [telegram!PopupButton](https://core.telegram.org/bots/webapps#popupbutton).
## Hierarchy
- `Record`<`string`, `unknown`\>
↳ **`ShowPopupButton`**
## Table of contents
### Properties
- [id](ShowPopupButton.md#id)
- [text](ShowPopupButton.md#text)
- [type](ShowPopupButton.md#type)
## Properties
### id
• `Optional` **id**: `string`
Optional. Identifier of the button, 0-64 characters.
Set to empty string by default.
If the button is pressed, its id is returned in the callback and the popupClosed event.
---
### text
• `Optional` **text**: `string`
Optional. The text to be displayed on the button, 0-64 characters.
Required if type is default or destructive.
Irrelevant for other types.
---
### type
• `Optional` **type**: `string`
Optional. Type of the button.
Set to default by default.
================================================
FILE: docs/interfaces/ShowPopupParams.md
================================================
[@vkruglikov/react-telegram-web-app](../README.md) / ShowPopupParams
# Interface: ShowPopupParams
You have to see original interface [telegram!PopupParams](https://core.telegram.org/bots/webapps#popupparams).
## Hierarchy
- `Record`<`string`, `unknown`\>
↳ **`ShowPopupParams`**
## Table of contents
### Properties
- [buttons](ShowPopupParams.md#buttons)
- [message](ShowPopupParams.md#message)
- [title](ShowPopupParams.md#title)
## Properties
### buttons
• `Optional` **buttons**: [`ShowPopupButton`](ShowPopupButton.md)[]
Optional. List of buttons to be displayed in the popup, 1-3 buttons
---
### message
• **message**: `string`
The message to be displayed in the body of the popup, 1-256 characters.
---
### title
• `Optional` **title**: `string`
Optional. The text to be displayed in the popup title, 0-64 characters.
================================================
FILE: docs/interfaces/ThemeParams.md
================================================
[@vkruglikov/react-telegram-web-app](../README.md) / ThemeParams
# Interface: ThemeParams
This object contains the user's current theme settings.
This object implement original Telegram WebApp type of [telegram!ThemeParams](https://core.telegram.org/bots/webapps#themeparams)
## Table of contents
### Properties
- [bg_color](ThemeParams.md#bg_color)
- [button_color](ThemeParams.md#button_color)
- [button_text_color](ThemeParams.md#button_text_color)
- [hint_color](ThemeParams.md#hint_color)
- [link_color](ThemeParams.md#link_color)
- [secondary_bg_color](ThemeParams.md#secondary_bg_color)
- [text_color](ThemeParams.md#text_color)
## Properties
### bg_color
• `Optional` **bg_color**: `string`
Background color in the #RRGGBB format.
---
### button_color
• `Optional` **button_color**: `string`
Button color in the #RRGGBB format.
---
### button_text_color
• `Optional` **button_text_color**: `string`
Button text color in the #RRGGBB format.
---
### hint_color
• `Optional` **hint_color**: `string`
Hint text color in the #RRGGBB format.
---
### link_color
• `Optional` **link_color**: `string`
Link color in the #RRGGBB format.
---
### secondary_bg_color
• `Optional` **secondary_bg_color**: `string`
Secondary background color in the #RRGGBB format.
---
### text_color
• `Optional` **text_color**: `string`
Main text color in the #RRGGBB format.
================================================
FILE: global.d.ts
================================================
import { WebApp } from './src/core/twa-types';
declare global {
interface Window {
Telegram?: {
WebApp: WebApp;
};
TelegramWebviewProxy?: any;
}
}
================================================
FILE: jest.config.json
================================================
{
"transform": {
"\\.[jt]sx?$": "ts-jest"
},
"testEnvironment": "jsdom",
"setupFilesAfterEnv": ["./tests/setupTests.ts"]
}
================================================
FILE: package.json
================================================
{
"name": "@vkruglikov/react-telegram-web-app",
"version": "2.2.0",
"description": "React components for Telegram WebApp",
"source": "./src/index.ts",
"type": "module",
"keywords": [
"react",
"telegram",
"telegram-bot"
],
"exports": {
"types": "./lib/index.d.ts",
"require": "./lib/react-telegram-web-app.cjs",
"default": "./lib/react-telegram-web-app.modern.js"
},
"files": [
"/lib"
],
"main": "lib/react-telegram-web-app.cjs",
"module": "lib/react-telegram-web-app.module.js",
"unpkg": "lib/react-telegram-web-app.umd.js",
"types": "lib/index.d.ts",
"scripts": {
"build": "rm -rf ./lib/*; microbundle build --sourcemap=false && cp -R src/core/twa-types lib/core/twa-types",
"dev": "npm run build && microbundle watch --compress=false",
"prepare": "husky install",
"changeset": "changeset",
"docs": "typedoc && prettier --write ./docs",
"release": "npm run test:all && npm run build && npm run test:package && changeset publish",
"format": "prettier --write .",
"eslint": "npx eslint src",
"test": "NODE_ENV=test jest",
"test:all": "NODE_ENV=test jest --testPathIgnorePatterns package",
"test:package": "NODE_ENV=test jest package"
},
"repository": {
"type": "git",
"url": "git+https://github.com/vkruglikov/react-telegram-web-app.git"
},
"publishConfig": {
"access": "public"
},
"author": "Valentin Kruglikov dev.n@bk.ru",
"license": "MIT",
"bugs": {
"url": "https://github.com/vkruglikov/react-telegram-web-app/issues"
},
"homepage": "https://github.com/vkruglikov/react-telegram-web-app#readme",
"devDependencies": {
"@changesets/cli": "^2.25.2",
"@testing-library/react": "^14.0.0",
"@types/jest": "^29.5.2",
"@types/react": "^18",
"@types/react-test-renderer": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^6.1.0",
"@typescript-eslint/parser": "^6.1.0",
"eslint": "^8.45.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"husky": "^8.0.2",
"jest": "^29.6.1",
"jest-environment-jsdom": "^29.6.1",
"lint-staged": "^13.0.3",
"microbundle": "^0.15.1",
"prettier": "2.8.0",
"react-test-renderer": "^18.2.0",
"ts-jest": "^29.1.1",
"typedoc": "^0.24.8",
"typedoc-plugin-markdown": "^3.15.3",
"typescript": "^5.1.6"
},
"peerDependencies": {
"react": "^18",
"react-dom": "^18"
},
"lint-staged": {
"*.(tsx|ts)": [
"eslint",
"prettier --write"
],
"*.md": [
"prettier --write"
]
}
}
================================================
FILE: src/BackButton.tsx
================================================
import { useContext, useEffect, useId } from 'react';
import { useWebApp, useSmoothButtonsTransition, systemContext } from './core';
/**
* The props type of {@link BackButton | `BackButton`}.
*/
export interface BackButtonProps {
/** The back button press event handler */
onClick?: () => void;
}
/**
* Renders a {@link telegram!BackButton} component in React app as {@link react!Component}
*
* ```tsx
* import { BackButton } from "@vkruglikov/react-telegram-web-app";
*
* <BackButton
* onClick={() => console.log('Hello, I am back button!')}
* />
* ```
* @param props
* @group React Components
*/
const BackButton = ({ onClick }: BackButtonProps): null => {
const system = useContext(systemContext);
const buttonId = useId();
const WebApp = useWebApp();
const BackButton = WebApp?.BackButton;
useEffect(() => {
if (!onClick || !BackButton) {
return;
}
BackButton.onClick(onClick);
return () => {
BackButton.offClick(onClick);
};
}, [onClick, BackButton]);
useSmoothButtonsTransition({
show: BackButton?.show,
hide: BackButton?.hide,
currentShowedIdRef: system.BackButton,
id: buttonId,
});
return null;
};
export default BackButton;
================================================
FILE: src/MainButton.tsx
================================================
import { useContext, useEffect, useId } from 'react';
import { useWebApp, useSmoothButtonsTransition, systemContext } from './core';
/**
* The props type of {@link MainButton | `MainButton`}.
*/
export interface MainButtonProps {
/**
* Current button text
* @defaultValue Set to `CONTINUE` by default
*/
text?: string;
/**
* The button progress state indicator.
* @defaultValue Set to `false` by default
*/
progress?: boolean;
/**
* Just an alias on the {@link MainButtonProps.disabled}
* @deprecated Use {@link MainButtonProps.disabled} instead, will be removed
* @ignore
*/
disable?: boolean;
/**
* The button disable state.
* @defaultValue Set to `false` y defaults
*/
disabled?: boolean;
/** The button press event handler */
onClick?: () => void;
/**
* Current button color.
* @defaultValue Set to themeParams.button_color by default
*/
color?: string;
/**
* Current button text color
* @defaultValue Set to themeParams.button_text_color by default
*/
textColor?: string;
}
/**
* Renders a {@link telegram!MainButton} component in React app as {@link react!Component}
*
* ```tsx
* import { MainButton } from "@vkruglikov/react-telegram-web-app";
*
* <MainButton
* text="CLICK ME"
* onClick={() => console.log('Hello, I am button!')}
* />
* ```
* @param props
* @group React Components
*/
const MainButton = ({
text = 'CONTINUE',
progress = false,
disable: disable_old,
disabled: disable_new = false,
color,
textColor,
onClick,
}: MainButtonProps): null => {
const system = useContext(systemContext);
const buttonId = useId();
const WebApp = useWebApp();
const MainButton = WebApp?.MainButton;
const themeParams = WebApp?.themeParams;
const disabled = disable_old || disable_new;
useEffect(() => {
MainButton?.setParams({
color: color || themeParams?.button_color || '#fff',
});
}, [color, themeParams, MainButton]);
useEffect(() => {
MainButton?.setParams({
text_color: textColor || themeParams?.button_text_color || '#000',
});
}, [MainButton, themeParams, textColor]);
useEffect(() => {
MainButton?.setText(text);
}, [text, MainButton]);
useEffect(() => {
if (disabled) {
MainButton?.disable();
} else if (!disabled) {
MainButton?.enable();
}
}, [disabled, MainButton]);
useEffect(() => {
if (progress) {
MainButton?.showProgress(false);
} else if (!progress) {
MainButton?.hideProgress();
}
}, [progress, MainButton]);
useEffect(() => {
if (!onClick) {
return;
}
MainButton?.onClick(onClick);
return () => {
MainButton?.offClick(onClick);
};
}, [onClick, MainButton]);
useSmoothButtonsTransition({
show: MainButton?.show,
hide: MainButton?.hide,
currentShowedIdRef: system.MainButton,
id: buttonId,
});
return null;
};
export default MainButton;
================================================
FILE: src/SettingsButton.tsx
================================================
import { useEffect } from 'react';
import { useWebApp } from './core';
/**
* The props type of {@link SettingsButton | `SettingsButton`}.
*/
export interface SettingsButtonProps {
onClick?: () => void;
}
/**
* Renders a {@link telegram!SettingsButton} component in React app as {@link react!Component}
*
* ```tsx
* import { SettingsButton } from "@vkruglikov/react-telegram-web-app";
*
* <SettingsButton
* onClick={() => console.log('Hello, I am settings button!')}
* />
* ```
* @param props
* @group React Components
*/
const SettingsButton = ({ onClick }: SettingsButtonProps): null => {
const WebApp = useWebApp();
useEffect(() => {
if (!onClick || !WebApp?.SettingsButton) {
return;
}
WebApp.SettingsButton.onClick(onClick);
return () => {
WebApp.SettingsButton.offClick(onClick);
};
}, [onClick, WebApp]);
useEffect(() => {
if (!WebApp?.SettingsButton) {
return;
}
WebApp.SettingsButton.show?.();
return () => {
WebApp.SettingsButton.hide?.();
};
}, [WebApp]);
return null;
};
export default SettingsButton;
================================================
FILE: src/WebAppProvider.tsx
================================================
import React, {
PropsWithChildren,
ReactElement,
useEffect,
useMemo,
} from 'react';
import {
webAppContext,
optionsContext,
systemContext,
Options,
DEFAULT_OPTIONS,
createSystemContextValue,
getWebAppFromGlobal,
useAsyncMode,
} from './core';
export type WebAppProviderProps = PropsWithChildren<{
options?: Options;
}>;
/**
* WebAppProvider provide context with WebApp for components and hooks.
* Necessary to use only if you want to override `options`
*
* ```tsx
* import { WebAppProvider } from "@vkruglikov/react-telegram-web-app";
*
* <WebAppProvider>
* <YourAppComponent />
* </WebAppProvider>
*
* // You can pass options {@link Options}
* <WebAppProvider
* options={{
* smoothButtonsTransition: true
* }}
* >
* <YourAppComponent />
* </WebAppProvider>
* ```
* @param props
* @group React Components
*/
const WebAppProvider = ({
children,
options,
}: WebAppProviderProps): ReactElement => {
const isLoadedWithAsyncMode = useAsyncMode(!!options?.async);
const mergedOptions = useMemo(
() => ({
...DEFAULT_OPTIONS,
...options,
}),
[options],
);
const systemValue = useMemo(createSystemContextValue, []);
const globalWebApp = useMemo(getWebAppFromGlobal, [isLoadedWithAsyncMode]);
useEffect(() => {
if (!options?.smoothButtonsTransition) return;
const forceHideButtons = () => {
globalWebApp?.MainButton?.hide();
globalWebApp?.BackButton?.hide();
globalWebApp?.SettingsButton?.hide();
};
window.addEventListener('beforeunload', forceHideButtons);
return () => window.removeEventListener('beforeunload', forceHideButtons);
}, [globalWebApp, options?.smoothButtonsTransition]);
return (
<systemContext.Provider value={systemValue}>
<webAppContext.Provider value={globalWebApp}>
<optionsContext.Provider value={mergedOptions}>
{children}
</optionsContext.Provider>
</webAppContext.Provider>
</systemContext.Provider>
);
};
export default WebAppProvider;
================================================
FILE: src/core/context.ts
================================================
import { createContext, MutableRefObject } from 'react';
export const getWebAppFromGlobal = () =>
typeof window !== 'undefined' && window?.Telegram?.WebApp
? window.Telegram.WebApp
: null;
export const webAppContext = createContext<
ReturnType<typeof getWebAppFromGlobal>
>(getWebAppFromGlobal());
/**
* This object describe options be able to set through WebAppProvider
*/
export type Options = {
/**
* When is `true`, we can smooth button transitions due to show(), hide() calls.
* So when you use MainButton or BackButton on multiple pages, there will be
* no noticeable flickering of the button during transitions
* @defaultValue `false`
*/
smoothButtonsTransition?: boolean;
async?: boolean;
/**
* @defaultValue `10`
* @remarks
*/
smoothButtonsTransitionMs?: number;
};
export const DEFAULT_OPTIONS: Options = {
smoothButtonsTransition: false,
smoothButtonsTransitionMs: 10,
};
export const optionsContext = createContext<Options>(DEFAULT_OPTIONS);
type SystemContext = {
MainButton: MutableRefObject<null | string>;
BackButton: MutableRefObject<null | string>;
};
export const createSystemContextValue = () => ({
MainButton: { current: null },
BackButton: { current: null },
});
export const systemContext = createContext<SystemContext>(
createSystemContextValue(),
);
================================================
FILE: src/core/index.ts
================================================
export { default as useSmoothButtonsTransition } from './useSmoothButtonsTransition';
export { default as useWebApp } from './useWebApp';
export { default as useAsyncMode } from './useAsyncMode';
export * from './context';
================================================
FILE: src/core/twa-types/WebApp.d.ts
================================================
export declare namespace TelegramWebApp {
/**
* {@link https://core.telegram.org/bots/webapps#themeparams}
*/
interface ThemeParams {
bg_color?: string;
text_color?: string;
hint_color?: string;
link_color?: string;
button_color?: string;
button_text_color?: string;
}
/**
* {@link https://core.telegram.org/bots/webapps#mainbutton}
*/
interface MainButton {
text: string;
color: string;
textColor: string;
isVisible: boolean;
isActive: boolean;
readonly isProgressVisible: boolean;
setText(text: string): MainButton;
show(): MainButton;
hide(): MainButton;
enable(): MainButton;
disable(): MainButton;
showProgress(leaveActive?: boolean): MainButton;
hideProgress(): MainButton;
onClick(callback: () => void);
setParams(params: {
text?: MainButton['text'];
color?: MainButton['color'];
text_color?: MainButton['textColor'];
is_active?: MainButton['isActive'];
is_visible?: MainButton['isVisible'];
}): MainButton;
}
/**
* {@link https://core.telegram.org/bots/webapps#webappuser}
*/
interface WebAppUser {
id: number;
is_bot?: boolean;
first_name: string;
last_name?: string;
username?: string;
language_code?: string;
photo_url?: string;
}
/**
* {@link https://core.telegram.org/bots/webapps#webappinitdata}
*/
interface WebAppInitData {
query_id?: string;
user?: WebAppUser;
receiver?: WebAppUser;
chat_type?: 'sender' | 'private' | 'group' | 'supergroup' | 'channel';
chat_instance?: string;
start_param?: string;
auth_date: number;
hash: string;
}
interface Event {
onEvent(
eventType: 'viewportChanged',
eventHandler: (payload: { isStateStable: boolean }) => void,
);
onEvent(
eventType: 'themeChanged' | 'mainButtonClicked',
eventHandler: () => void,
);
offEvent(
eventType: 'viewportChanged' | 'themeChanged' | 'mainButtonClicked',
eventHandler: (...args: any[]) => void,
);
}
/**
* {@link https://core.telegram.org/bots/webapps#initializing-web-apps}
*/
interface WebApp extends Event {
initData: string;
initDataUnsafe: WebAppInitData;
platform: string;
colorScheme: 'dark' | 'light';
themeParams: ThemeParams;
isExpanded: boolean;
viewportHeight: number;
viewportStableHeight: number;
MainButton: MainButton;
sendData(data: unknown);
ready();
expand();
close();
}
}
================================================
FILE: src/core/twa-types/WebAppVersion_6.1.d.ts
================================================
import { TelegramWebApp } from './WebApp';
export declare namespace TelegramWebAppVersion6_1 {
interface ThemeParams extends TelegramWebApp.ThemeParams {
secondary_bg_color?: string;
}
/**
* {@link https://core.telegram.org/bots/webapps#backbutton}
*/
interface BackButton {
isVisible: boolean;
onClick(cb: () => void): BackButton;
offClick(cb: () => void): BackButton;
show(): BackButton;
hide(): BackButton;
}
interface MainButton extends TelegramWebApp.MainButton {
offClick(text: () => void): MainButton;
}
/**
* {@link https://core.telegram.org/bots/webapps#webappchat}
*/
interface WebAppChat {
id: number;
type: 'group' | 'supergroup' | 'channel';
title: string;
username?: string;
photo_url?: string;
}
interface WebAppInitData extends TelegramWebApp.WebAppInitData {
chat?: WebAppChat;
can_send_after?: number;
}
/**
* {@link https://core.telegram.org/bots/webapps#hapticfeedback}
*/
interface HapticFeedback {
impactOccurred(
style: 'light' | 'medium' | 'heavy' | 'rigid' | 'soft',
): HapticFeedback;
notificationOccurred(type: 'error' | 'success' | 'warning'): HapticFeedback;
selectionChanged(): HapticFeedback;
}
interface Event {
onEvent(
eventType: 'backButtonClicked' | 'settingsButtonClicked',
eventHandler: () => void,
);
onEvent(
eventType: 'invoiceClosed',
eventHandler: (payload: {
url: string;
status: 'paid' | 'cancelled' | 'failed' | 'pending';
}) => void,
);
offEvent(
eventType:
| 'backButtonClicked'
| 'settingsButtonClicked'
| 'invoiceClosed',
eventHandler: (...args: any[]) => void,
);
}
interface WebApp extends TelegramWebApp.WebApp, Event {
themeParams: ThemeParams;
initDataUnsafe: WebAppInitData;
MainButton: MainButton;
version: string;
headerColor: string;
backgroundColor: string;
BackButton: BackButton;
HapticFeedback: HapticFeedback;
isVersionAtLeast(version: string | number): boolean;
setHeaderColor(color: 'bg_color' | 'secondary_bg_color' | string);
setBackgroundColor(color: 'bg_color' | 'secondary_bg_color' | string);
openLink(url: string);
openTelegramLink(url: string);
openInvoice(
url: string,
callback?: (status: 'paid' | 'cancelled' | 'failed' | 'pending') => void,
);
}
}
================================================
FILE: src/core/twa-types/WebAppVersion_6.2.d.ts
================================================
import { TelegramWebAppVersion6_1 } from './WebAppVersion_6.1';
import { TelegramWebApp } from './WebApp';
export declare namespace TelegramWebAppVersion6_2 {
/**
* {@link https://core.telegram.org/bots/webapps#webappuser}
*/
interface WebAppUser extends TelegramWebApp.WebAppUser {
is_premium?: true;
}
/**
* {@link https://core.telegram.org/bots/webapps#webappinitdata}
*/
interface WebAppInitData extends TelegramWebAppVersion6_1.WebAppInitData {
user?: WebAppUser;
receiver?: WebAppUser;
}
/**
* {@link https://core.telegram.org/bots/webapps#popupbutton}
*/
interface PopupButton {
id?: string;
type?: string;
text?: string;
}
/**
* {@link https://core.telegram.org/bots/webapps#popupparams}
*/
interface PopupParams {
title?: string;
message: string;
buttons?: PopupButton[];
}
interface Event {
onEvent(
eventType: 'popupClosed',
eventHandler: (payload: { button_id: string }) => void,
);
offEvent(eventType: 'popupClosed', eventHandler: (...args: any[]) => void);
}
interface WebApp extends TelegramWebAppVersion6_1.WebApp, Event {
initDataUnsafe: WebAppInitData;
isClosingConfirmationEnabled: string;
enableClosingConfirmation();
disableClosingConfirmation();
showPopup(params: PopupParams, callback?: (id: string) => void);
showAlert(message: string, callback?: () => void);
showConfirm(message: string, callback?: (isOk: boolean) => void);
}
}
================================================
FILE: src/core/twa-types/WebAppVersion_6.4.d.ts
================================================
import { TelegramWebAppVersion6_2 } from './WebAppVersion_6.2';
export declare namespace TelegramWebAppVersion6_4 {
/**
* {@link https://core.telegram.org/bots/webapps#scanqrpopupparams}
*/
interface ScanQrPopupParams {
text?: string;
}
interface Event {
onEvent(eventType: 'qrTextReceived', eventHandler: (data: string) => void);
onEvent(
eventType: 'clipboardTextReceived',
eventHandler: (payload: { data: string | null }) => void,
);
offEvent(
eventType: 'qrTextReceived' | 'clipboardTextReceived',
eventHandler: (...args: any[]) => void,
);
}
interface WebApp extends TelegramWebAppVersion6_2.WebApp, Event {
openLink(url: string, options?: { try_instant_view: true });
platform: string;
showScanQrPopup(
params: ScanQrPopupParams,
callback?: (data: string) => void | boolean,
);
closeScanQrPopup();
readTextFromClipboard(callback?: (data: string) => void);
}
}
================================================
FILE: src/core/twa-types/WebAppVersion_6.7.d.ts
================================================
import { TelegramWebAppVersion6_4 } from './WebAppVersion_6.4';
export declare namespace TelegramWebAppVersion6_7 {
interface WebApp extends TelegramWebAppVersion6_4.WebApp {
switchInlineQuery(query: string, choose_chat_types?: unknown);
}
}
================================================
FILE: src/core/twa-types/WebAppVersion_6.9.d.ts
================================================
import { TelegramWebAppVersion6_7 } from './WebAppVersion_6.7';
import { TelegramWebAppVersion6_2 } from './WebAppVersion_6.2';
export declare namespace TelegramWebAppVersion6_9 {
interface WebAppUser extends TelegramWebAppVersion6_2.WebAppUser {
added_to_attachment_menu?: true;
allows_write_to_pm?: true;
}
interface Event {
onEvent(
eventType: 'writeAccessRequested',
eventHandler: (data: { status: 'allowed' | 'cancelled' }) => void,
);
onEvent(
eventType: 'contactRequested',
eventHandler: (payload: { data: 'sent' | 'cancelled' }) => void,
);
offEvent(
eventType: 'writeAccessRequested' | 'contactRequested',
eventHandler: (...args: any[]) => void,
);
}
type StorageKey = string;
type CloudStorageCallback<T> =
| ((error: Error) => void)
| ((error: null, result: T) => void);
/**
* {@link https://core.telegram.org/bots/webapps#cloudstorage}
*/
interface CloudStorage {
setItem(
key: StorageKey,
value: string,
callback?: CloudStorageCallback<boolean>,
): void;
getItem(key: StorageKey, callback: CloudStorageCallback<string>): void;
getItems(
keys: StorageKey[],
callback: CloudStorageCallback<string[]>,
): void;
removeItem(key: StorageKey, callback?: CloudStorageCallback<boolean>): void;
removeItems(
keys: StorageKey[],
callback?: CloudStorageCallback<boolean>,
): void;
getKeys(callback: CloudStorageCallback<string[]>): void;
}
interface WebApp extends TelegramWebAppVersion6_7.WebApp, Event {
CloudStorage: CloudStorage;
}
}
================================================
FILE: src/core/twa-types/WebAppVersion_7.0.d.ts
================================================
export declare namespace TelegramWebAppVersion7_0 {
interface WebApp extends TelegramWebAppVersion6_9.WebApp {
// TODO Fix types SettingsButton
SettingsButton?: any;
}
}
================================================
FILE: src/core/twa-types/index.d.ts
================================================
import { TelegramWebApp } from './WebApp';
import { TelegramWebAppVersion6_1 } from './WebAppVersion_6.1';
import { TelegramWebAppVersion6_2 } from './WebAppVersion_6.2';
import { TelegramWebAppVersion6_4 } from './WebAppVersion_6.4';
import { TelegramWebAppVersion6_7 } from './WebAppVersion_6.7';
import { TelegramWebAppVersion6_9 } from './WebAppVersion_6.9';
import { TelegramWebAppVersion7_0 } from './WebAppVersion_7.0';
export type WebApp = TelegramWebApp.WebApp &
Partial<TelegramWebAppVersion6_1.WebApp> &
Partial<TelegramWebAppVersion6_2.WebApp> &
Partial<TelegramWebAppVersion6_4.WebApp> &
Partial<TelegramWebAppVersion6_7.WebApp> &
Partial<TelegramWebAppVersion6_9.WebApp> &
Partial<TelegramWebAppVersion7_0.WebApp>;
================================================
FILE: src/core/useAsyncMode.ts
================================================
import { useEffect, useState } from 'react';
const useAsyncMode = (enabled: boolean) => {
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
if (!enabled) return;
if (window.Telegram?.WebApp) {
setIsLoaded(true);
return;
}
const nativeProxyPostHandle =
typeof window !== 'undefined' &&
window.TelegramWebviewProxy &&
window.TelegramWebviewProxy.postEvent;
if (window.TelegramWebviewProxy) {
window.TelegramWebviewProxy.postEvent = (...args: any[]) => {
nativeProxyPostHandle?.(...args);
if (window.Telegram?.WebApp) {
setIsLoaded(true);
window.TelegramWebviewProxy.postEvent = nativeProxyPostHandle;
}
};
} else {
window.TelegramWebviewProxy = {
postEvent: (...args: any[]) => {
nativeProxyPostHandle?.(...args);
if (window.Telegram?.WebApp) {
setIsLoaded(true);
delete window.TelegramWebviewProxy;
}
},
};
}
}, [enabled]);
return isLoaded;
};
export default useAsyncMode;
================================================
FILE: src/core/useSmoothButtonsTransition.ts
================================================
import { MutableRefObject, useContext, useEffect } from 'react';
import { optionsContext } from './context';
const _noop = () => {};
const useSmoothButtonsTransition = ({
id,
show = _noop,
hide = _noop,
currentShowedIdRef,
}: {
id: string;
show: typeof _noop | undefined;
hide: typeof _noop | undefined;
currentShowedIdRef: MutableRefObject<string | null>;
}) => {
const { smoothButtonsTransition, smoothButtonsTransitionMs } =
useContext(optionsContext);
useEffect(() => {
show();
currentShowedIdRef.current = id;
return () => {
if (smoothButtonsTransition) {
currentShowedIdRef.current = null;
setTimeout(() => {
if (currentShowedIdRef.current) return;
hide();
}, smoothButtonsTransitionMs);
} else {
hide();
currentShowedIdRef.current = null;
}
};
}, [
hide,
id,
currentShowedIdRef,
show,
smoothButtonsTransition,
smoothButtonsTransitionMs,
]);
};
export default useSmoothButtonsTransition;
================================================
FILE: src/core/useWebApp.ts
================================================
import { useContext } from 'react';
import { webAppContext } from './context';
/**
* @private
* @ignore
*/
const useWebApp = () => {
const context = useContext(webAppContext);
return context;
};
export default useWebApp;
================================================
FILE: src/index.ts
================================================
export { default as MainButton, MainButtonProps } from './MainButton';
export { default as BackButton, BackButtonProps } from './BackButton';
export {
default as SettingsButton,
SettingsButtonProps,
} from './SettingsButton';
export {
default as useShowPopup,
ShowPopupFunction,
ShowPopupParams,
ShowPopupButton,
} from './useShowPopup';
export {
default as useHapticFeedback,
ImpactOccurredFunction,
NotificationOccurredFunction,
SelectionChangedFunction,
} from './useHapticFeedback';
export {
default as useThemeParams,
ThemeParams,
ColorScheme,
} from './useThemeParams';
export {
default as useScanQrPopup,
ScanQrPopupCallback,
ScanQrPopupParams,
ShowScanQrPopupFunction,
CloseScanQrPopupFunction,
} from './useScanQrPopup';
export {
default as useReadTextFromClipboard,
ReadTextFromClipboardFunction,
} from './useReadTextFromClipboard';
export {
default as useSwitchInlineQuery,
SwitchInlineQueryFunction,
} from './useSwitchInlineQuery';
export { default as useExpand } from './useExpand';
export {
default as useCloudStorage,
GetKeysFunction,
GetItemFunction,
GetItemsFunction,
RemoveItemFunction,
SetItemFunction,
RemoveItemsFunction,
} from './useCloudStorage';
export {
default as WebAppProvider,
WebAppProviderProps,
} from './WebAppProvider';
export {
default as useInitData,
WebAppChat,
WebAppUser,
InitData,
InitDataUnsafe,
} from './useInitData';
export type { Options } from './core';
export { default as useWebApp } from './useWebApp';
================================================
FILE: src/useCloudStorage.ts
================================================
import { useWebApp } from './core';
import { useCallback, useMemo } from 'react';
/**
* This function provides `getItem` method from {@link telegram!CloudStorage} as Promise
* @throws
*/
export type GetItemFunction = (key: string) => Promise<string>;
/**
* This function provides `setItem` method from {@link telegram!CloudStorage} as Promise
* @throws
*/
export type SetItemFunction = (key: string, value: string) => Promise<void>;
/**
* This function provides `getItems` method from {@link telegram!CloudStorage} as Promise
* @throws
*/
export type GetItemsFunction = (keys: string[]) => Promise<string[]>;
/**
* This function provides `removeItem` method from {@link telegram!CloudStorage} as Promise
* @throws
*/
export type RemoveItemFunction = (key: string) => Promise<void>;
/**
* This function provides `removeItems` method from {@link telegram!CloudStorage} as Promise
* @throws
*/
export type RemoveItemsFunction = (key: string[]) => Promise<void>;
/**
* This function provides `getKeys` method from {@link telegram!CloudStorage} as Promise
* @throws
*/
export type GetKeysFunction = () => Promise<string[]>;
/**
* This hook provides {@link telegram!CloudStorage} object with promises functions,
* so you don't have to pass `callback` argument
* You have to look original description CloudStorage object in {@link telegram!CloudStorage}
* @group Hooks
*/
const useCloudStorage = (): {
getItem: GetItemFunction;
setItem: SetItemFunction;
getItems: GetItemsFunction;
removeItem: RemoveItemFunction;
getKeys: GetKeysFunction;
} => {
const cloudStorage = useWebApp()?.CloudStorage;
const getItem: GetItemFunction = useCallback(
key =>
new Promise((resolve, reject) => {
cloudStorage?.getItem(key, (error, value) => {
if (!error) {
resolve(value);
} else {
reject(error);
}
});
}),
[cloudStorage],
);
const setItem: SetItemFunction = useCallback(
(key, value) =>
new Promise((resolve, reject) => {
cloudStorage?.setItem(key, value, (error, state) => {
if (!error && state) {
resolve();
} else {
reject(error);
}
});
}),
[cloudStorage],
);
const getItems: GetItemsFunction = useCallback(
key =>
new Promise((resolve, reject) => {
cloudStorage?.getItems(key, (error, value) => {
if (!error && value) {
resolve(value);
} else {
reject(error);
}
});
}),
[cloudStorage],
);
const removeItem: RemoveItemFunction = useCallback(
key =>
new Promise((resolve, reject) => {
cloudStorage?.removeItem(key, (error, state) => {
if (!error && state) {
resolve();
} else {
reject(error);
}
});
}),
[cloudStorage],
);
const removeItems: RemoveItemsFunction = useCallback(
key =>
new Promise((resolve, reject) => {
cloudStorage?.removeItems(key, (error, state) => {
if (!error && state) {
resolve();
} else {
reject(error);
}
});
}),
[cloudStorage],
);
const getKeys: GetKeysFunction = useCallback(
() =>
new Promise((resolve, reject) => {
cloudStorage?.getKeys((error, state) => {
if (!error && state) {
resolve(state);
} else {
reject(error);
}
});
}),
[cloudStorage],
);
return useMemo(
() => ({
getItem,
setItem,
getItems,
removeItem,
removeItems,
getKeys,
}),
// Осознанно в зависимостях только cloudStorage
// eslint-disable-next-line react-hooks/exhaustive-deps
[cloudStorage],
);
};
export default useCloudStorage;
================================================
FILE: src/useExpand.ts
================================================
import { DispatchWithoutAction, useCallback, useEffect, useState } from 'react';
import { useWebApp } from './core';
/**
* This hook provided isExpanded state, and expand() handle
* You have to look original description in {@link telegram!WebApp} for more information
*
* `isExpanded` can be `undefined`
*
* ```tsx
* import { useExpand } from "@vkruglikov/react-telegram-web-app";
*
* const [isExpanded, expand] = useExpand();
* const handleClick = () => !isExpanded && expand();
*
* <button onClick={handleClick}>
* {showTextWhenScreenExpanded && 'expanded' : 'to expand'}
* </button>
* ```
*
* @privateRemarks
* Api doesn't provide event for listening isExpanded, so we use
* viewportChanged, but it is an unsafe way
*
* @group Hooks
*/
const useExpand = (): readonly [boolean | undefined, DispatchWithoutAction] => {
const WebApp = useWebApp();
const [isExpanded, setIsExpanded] = useState(WebApp?.isExpanded);
useEffect(() => {
if (!WebApp) return;
const handleEvent = (payload: { isStateStable: boolean }) => {
if (payload.isStateStable) {
setIsExpanded(WebApp.isExpanded);
}
};
WebApp.onEvent('viewportChanged', handleEvent);
return () => WebApp.offEvent('viewportChanged', handleEvent);
}, [WebApp]);
const handleExpand = useCallback(() => WebApp?.expand?.(), [WebApp]);
return [isExpanded, handleExpand] as const;
};
export default useExpand;
================================================
FILE: src/useHapticFeedback.ts
================================================
import { useWebApp } from './core';
import { useCallback } from 'react';
/**
* A method tells that an impact occurred. The Telegram app may play the appropriate haptics based on style value passed. Style can be one of these values:
* - light, indicates a collision between small or lightweight UI objects,
* - medium, indicates a collision between medium-sized or medium-weight UI objects,
* - heavy, indicates a collision between large or heavyweight UI objects,
* - rigid, indicates a collision between hard or inflexible UI objects,
* - soft, indicates a collision between soft or flexible UI objects.
* {@link telegram!HapticFeedback}
*/
export type ImpactOccurredFunction = (
style: 'light' | 'medium' | 'heavy' | 'rigid' | 'soft',
) => void;
/**
* 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. Type can be one of these values:
* - error, indicates that a task or action has failed,
* - success, indicates that a task or action has completed successfully,
* - warning, indicates that a task or action produced a warning.
* {@link telegram!HapticFeedback}
*/
export type NotificationOccurredFunction = (
type: 'error' | 'success' | 'warning',
) => void;
/**
* A method tells that the user has changed a selection. The Telegram app may play the appropriate haptics.
* {@link telegram!HapticFeedback}
*/
export type SelectionChangedFunction = () => void;
/**
* This hook that provided {@link ImpactOccurredFunction}, {@link NotificationOccurredFunction} and {@link SelectionChangedFunction} functions that controls haptic feedback.
* You have to look original telegram description {@link telegram!HapticFeedback}, because it Hook implementing his.
*
* ```tsx
* import { useHapticFeedback } from "@vkruglikov/react-telegram-web-app";
*
* const [impactOccurred, notificationOccurred, selectionChanged] =
* useHapticFeedback();
* // const [,notificationOccurred] = useHapticFeedback();
*
* impactOccurred('heavy');
* notificationOccurred('success');
* ```
*
* @group Hooks
*/
const useHapticFeedback = (): readonly [
ImpactOccurredFunction,
NotificationOccurredFunction,
SelectionChangedFunction,
] => {
const WebApp = useWebApp();
const HapticFeedback = WebApp?.HapticFeedback;
const impactOccurred: ImpactOccurredFunction = useCallback(
(...args) => HapticFeedback?.impactOccurred(...args),
[HapticFeedback],
);
const notificationOccurred: NotificationOccurredFunction = useCallback(
(...args) => HapticFeedback?.notificationOccurred(...args),
[HapticFeedback],
);
const selectionChanged: SelectionChangedFunction = useCallback(
(...args) => HapticFeedback?.selectionChanged(...args),
[HapticFeedback],
);
return [impactOccurred, notificationOccurred, selectionChanged] as const;
};
export default useHapticFeedback;
================================================
FILE: src/useInitData.ts
================================================
import { useWebApp } from './core';
/**
* {@link telegram!WebAppChat}
*/
export type WebAppChat = {
id: number;
type: 'group' | 'supergroup' | 'channel';
title: string;
username?: string;
photo_url?: string;
};
/**
* {@link telegram!WebAppUser}
*/
export type WebAppUser = {
id: number;
is_bot?: boolean;
first_name: string;
last_name?: string;
username?: string;
language_code?: string;
photo_url?: string;
is_premium?: boolean;
added_to_attachment_menu?: true;
allows_write_to_pm?: true;
};
export type InitData = string;
/**
* {@link telegram!WebAppInitData}
*/
export type InitDataUnsafe = {
query_id?: string;
user?: WebAppUser;
receiver?: WebAppUser;
chat_type?: 'sender' | 'private' | 'group' | 'supergroup' | 'channel';
chat_instance?: string;
start_param?: string;
auth_date: number;
hash: string;
chat?: WebAppChat;
can_send_after?: number;
};
/**
* This hook provides `initDataUnsafe` and `initData`
* You have to look original description in {@link telegram!WebApp}, because hook just return this.
*
* ```tsx
* import { useInitData } from "@vkruglikov/react-telegram-web-app";
*
* const [initDataUnsafe] = useInitData();
* const [initDataUnsafe, initData] = useInitData();
*
* ```
* @group Hooks
*/
const useInitData = (): readonly [
InitDataUnsafe | undefined,
InitData | undefined,
] => {
const WebApp = useWebApp();
return [WebApp?.initDataUnsafe, WebApp?.initData] as const;
};
export default useInitData;
================================================
FILE: src/useReadTextFromClipboard.ts
================================================
import { useCallback } from 'react';
import { useWebApp } from './core';
/**
* This function provided Promise function that read text from clipboard
* @return {Promise<string>}
*/
export type ReadTextFromClipboardFunction = () => Promise<string>;
/**
* This hook that provided {@link ReadTextFromClipboardFunction} Promise function that read text from clipboard.
* You have to look original description readTextFromClipboard in {@link telegram!WebApp}, because hook just implements his.
*
* ```tsx
* import { useReadTextFromClipboard } from "@vkruglikov/react-telegram-web-app";
*
* const readText = useReadTextFromClipboard();
*
* readText().then(console.log);
* // or
* await readText()
* ```
*
* @return {ReadTextFromClipboardFunction}
* @group Hooks
*/
const useReadTextFromClipboard = (): ReadTextFromClipboardFunction => {
const WebApp = useWebApp();
return useCallback(
() =>
new Promise(resolve => {
WebApp?.readTextFromClipboard?.(resolve);
}),
[WebApp],
);
};
export default useReadTextFromClipboard;
================================================
FILE: src/useScanQrPopup.ts
================================================
import { useWebApp } from './core';
import { useCallback } from 'react';
/**
* If an optional callback parameter was passed, the callback function will be called and the text from the QR
* code will be passed as the first argument.
* Returning true inside this callback function causes the popup to be closed.
*/
export type ScanQrPopupCallback = (text: string) => true | void;
/**
* This object describes the native popup for scanning QR codes.
* You have to see original interface {@link telegram!ScanQrPopupParams}.
*/
export interface ScanQrPopupParams {
/**
* The text to be displayed under the 'Scan QR' heading, 0-64 characters.
*/
text: string;
}
/**
* A method that shows a native popup for scanning a QR code described
* by the params argument of the type {@link ScanQrPopupParams}.
*/
export type ShowScanQrPopupFunction = (
params: ScanQrPopupParams,
callback?: ScanQrPopupCallback,
) => void;
/**
* A method that closes the native popup for scanning a QR code opened with the showScanQrPopup method
*/
export type CloseScanQrPopupFunction = () => void;
/**
* The hook provided showScanQrPopup function of the type {@link ShowScanQrPopupFunction} and closeScanQrPopup {@link CloseScanQrPopupFunction}.
* @group Hooks
*/
const useScanQrPopup = (): readonly [
ShowScanQrPopupFunction,
CloseScanQrPopupFunction,
] => {
const WebApp = useWebApp();
const showScanQrPopup: ShowScanQrPopupFunction = useCallback(
(...args) => WebApp?.showScanQrPopup?.(...args),
[WebApp],
);
const closeScanQrPopup: CloseScanQrPopupFunction = useCallback(
() => WebApp?.closeScanQrPopup?.(),
[WebApp],
);
return [showScanQrPopup, closeScanQrPopup] as const;
};
export default useScanQrPopup;
================================================
FILE: src/useShowPopup.ts
================================================
import { useCallback } from 'react';
import { useWebApp } from './core';
/**
* You have to see original interface {@link telegram!PopupButton}.
*/
export interface ShowPopupButton extends Record<string, unknown> {
/**
* Optional. Identifier of the button, 0-64 characters.
* Set to empty string by default.
* If the button is pressed, its id is returned in the callback and the popupClosed event.
*/
id?: string;
/**
* Optional. Type of the button.
* Set to default by default.
*/
type?: 'default' | 'ok' | 'close' | 'cancel' | 'destructive' | string;
/**
* Optional. The text to be displayed on the button, 0-64 characters.
* Required if type is default or destructive.
* Irrelevant for other types.
*/
text?: string;
}
/**
* You have to see original interface {@link telegram!PopupParams}.
*/
export interface ShowPopupParams extends Record<string, unknown> {
/**
* Optional. The text to be displayed in the popup title, 0-64 characters.
*/
title?: string;
/**
* The message to be displayed in the body of the popup, 1-256 characters.
*/
message: string;
/**
* Optional. List of buttons to be displayed in the popup, 1-3 buttons
*/
buttons?: ShowPopupButton[];
}
/**
* This function provides Promise, and resolve the field id of the pressed button will be passed.
* @return Button id as string, it was described by {@link ShowPopupButton}
* @throws
*/
export type ShowPopupFunction = (params: ShowPopupParams) => Promise<string>;
/**
* The hook provided showPopup function of the type {@link ShowPopupFunction}.
* The function that shows a native popup described by the params argument of the type {@link ShowPopupParams}.
*
* ```tsx
* import { useShowPopup } from "@vkruglikov/react-telegram-web-app";
*
* const showPopup = useShowPopup();
*
* showPopup({ message: 'Hello world' }).then((buttonId) => console.log(buttonId));
* ```
*
* @group Hooks
*/
const useShowPopup: () => ShowPopupFunction = () => {
const WebApp = useWebApp();
return useCallback(
params =>
new Promise((resolve, reject) => {
try {
WebApp?.showPopup?.(params, (buttonId: string) => {
resolve(buttonId);
});
} catch (e) {
reject(e);
}
}),
[WebApp],
);
};
export default useShowPopup;
================================================
FILE: src/useSwitchInlineQuery.ts
================================================
import { useWebApp } from './core';
import { useCallback } from 'react';
/**
* This function that inserts the bot's username and the specified inline query in the current chat's input field
* You have to look original description switchInlineQuery in {@link telegram!WebApp} for more information
*/
export type SwitchInlineQueryFunction = (
query: string,
chatType?: ('users' | 'bots' | 'groups' | 'channels')[],
) => void;
/**
* This hook that provided {@link SwitchInlineQueryFunction}
* You have to look original description switchInlineQuery in {@link telegram!WebApp}, because hook just implements his.
* @return {SwitchInlineQueryFunction}
* @group Hooks
*/
const useSwitchInlineQuery = (): SwitchInlineQueryFunction => {
const WebApp = useWebApp();
return useCallback(
(...args) => WebApp?.switchInlineQuery?.(...args),
[WebApp],
);
};
export default useSwitchInlineQuery;
================================================
FILE: src/useThemeParams.ts
================================================
import { useEffect, useState } from 'react';
import { useWebApp } from './core';
/**
* This object contains the user's current theme settings.
* This object implement original Telegram WebApp type of {@link telegram!ThemeParams}
*/
export interface ThemeParams {
/**
* Background color in the #RRGGBB format.
*/
bg_color?: string;
/**
* Main text color in the #RRGGBB format.
*/
text_color?: string;
/**
* Hint text color in the #RRGGBB format.
*/
hint_color?: string;
/**
* Link color in the #RRGGBB format.
*/
link_color?: string;
/**
* Button color in the #RRGGBB format.
*/
button_color?: string;
/**
* Button text color in the #RRGGBB format.
*/
button_text_color?: string;
/**
* Secondary background color in the #RRGGBB format.
*/
secondary_bg_color?: string;
}
/**
* The color scheme currently used in the Telegram app. Either “light” or “dark”.
* Can `undefined`, if `window` is undefined.
*/
export type ColorScheme = 'light' | 'dark' | undefined;
/**
* The hook provided colorScheme and themeParams values of the type {@link ColorScheme} and {@link ThemeParams}.
*
* ```tsx
* import { useThemeParams } from "@vkruglikov/react-telegram-web-app";
*
* const [colorScheme, themeParams] = useThemeParams();
*
* console.log(colorScheme === 'dark');
* console.log({
* text_color: themeParams.text_color,
* button_color: themeParams.button_color,
* bg_color: themeParams.bg_color,
* });
* ```
* @group Hooks
*/
const useThemeParams: () => readonly [ColorScheme, ThemeParams] = () => {
const WebApp = useWebApp();
const [colorScheme, setColor] = useState<ColorScheme>(WebApp?.colorScheme);
const [themeParams, setThemeParams] = useState<ThemeParams>(
WebApp?.themeParams || {},
);
useEffect(() => {
if (!WebApp) return;
const eventHandler = () => {
setColor(WebApp.colorScheme);
setThemeParams(WebApp.themeParams);
};
WebApp.onEvent('themeChanged', eventHandler);
return () => {
WebApp.offEvent('themeChanged', eventHandler);
};
}, [WebApp]);
return [colorScheme, themeParams] as const;
};
export default useThemeParams;
================================================
FILE: src/useWebApp.ts
================================================
import { useWebApp as _useWebApp } from './core';
/**
* This hook just provides native {@link telegram!WebApp} object
*
* ```tsx
* import { useWebApp } from "@vkruglikov/react-telegram-web-app";
*
* const WebApp = useWebApp();
*
* console.log(WebApp.version);
* ```
* @group Hooks
*/
const useWebApp = () => _useWebApp() as any;
export default useWebApp;
================================================
FILE: tests/BackButton.test.tsx
================================================
import * as React from 'react';
import BackButton from '../src/BackButton';
import { useWebApp } from '../src/core';
import { renderComponentTree } from './utils';
describe('BackButton', () => {
it('checks call show(), hide() WebApp.BackButton api', () => {
const WebApp = useWebApp();
renderComponentTree(() => <BackButton />);
expect(WebApp?.BackButton?.show).toBeCalledTimes(1);
expect(WebApp?.BackButton?.hide).toBeCalledTimes(1);
});
it('checks correct bind cached onClick', () => {
const WebApp = useWebApp();
const handleClick = jest.fn();
renderComponentTree(() => <BackButton onClick={handleClick} />);
expect(WebApp?.BackButton?.onClick).toBeCalledTimes(1);
expect(WebApp?.BackButton?.offClick).toBeCalledTimes(1);
expect(WebApp?.BackButton?.onClick).toBeCalledWith(handleClick);
expect(WebApp?.BackButton?.offClick).toBeCalledWith(handleClick);
});
it('checks correct bind uncached onClick', () => {
const WebApp = useWebApp();
renderComponentTree(() => <BackButton onClick={jest.fn()} />);
expect(WebApp?.BackButton?.onClick).toBeCalledTimes(3);
expect(WebApp?.BackButton?.offClick).toBeCalledTimes(3);
});
});
================================================
FILE: tests/MainButton.test.tsx
================================================
import * as React from 'react';
import MainButton from '../src/MainButton';
import { useWebApp } from '../src/core';
import { renderComponentTree } from './utils';
describe('MainButton', () => {
it('checks call show(), hide() WebApp.MainButton api', () => {
const WebApp = useWebApp();
renderComponentTree(() => <MainButton />);
expect(WebApp?.MainButton.show).toBeCalledTimes(1);
expect(WebApp?.MainButton.hide).toBeCalledTimes(1);
});
it('checks correct bind cached onClick', () => {
const WebApp = useWebApp();
const handleClick = jest.fn();
renderComponentTree(() => <MainButton onClick={handleClick} />);
expect(WebApp?.MainButton.onClick).toBeCalledTimes(1);
expect(WebApp?.MainButton.offClick).toBeCalledTimes(1);
expect(WebApp?.MainButton.onClick).toBeCalledWith(handleClick);
expect(WebApp?.MainButton.offClick).toBeCalledWith(handleClick);
});
it('checks correct bind uncached onClick', () => {
const WebApp = useWebApp();
renderComponentTree(() => <MainButton onClick={jest.fn()} />);
expect(WebApp?.MainButton.onClick).toBeCalledTimes(3);
expect(WebApp?.MainButton.offClick).toBeCalledTimes(3);
});
it('checks call showProgress(), hideProgress() WebApp.MainButton api', () => {
const WebApp = useWebApp();
const updates = [
<MainButton progress />,
<MainButton />,
<MainButton progress />,
];
renderComponentTree(() => updates.shift() || <MainButton progress />);
expect(WebApp?.MainButton.showProgress).toBeCalledTimes(2);
expect(WebApp?.MainButton.hideProgress).toBeCalledTimes(1);
});
it('checks call disable(), enable() WebApp.MainButton api', () => {
const WebApp = useWebApp();
const updates = [
<MainButton disable />,
<MainButton />,
<MainButton disable />,
];
renderComponentTree(() => updates.shift() || <MainButton disable />);
expect(WebApp?.MainButton.disable).toBeCalledTimes(2);
expect(WebApp?.MainButton.enable).toBeCalledTimes(1);
});
it('checks call setText() WebApp.MainButton api', () => {
const WebApp = useWebApp();
const updates = [
<MainButton text="Hello" />,
<MainButton />,
<MainButton text="My friend" />,
];
renderComponentTree(() => updates.shift() || <MainButton />);
expect((WebApp?.MainButton.setText as jest.Mock).mock.calls).toEqual([
['Hello'],
['CONTINUE'],
['My friend'],
]);
});
it('checks textColor props', () => {
const WebApp = useWebApp();
const updates = [
<MainButton textColor="#fff" />,
<MainButton />,
<MainButton textColor="#0f0" />,
];
renderComponentTree(() => updates.shift() || <MainButton />);
expect(WebApp?.MainButton.setParams).toBeCalledWith(
expect.objectContaining({
text_color: '#fff',
}),
);
expect(WebApp?.MainButton.setParams).toBeCalledWith(
expect.objectContaining({
text_color: '#000',
}),
);
expect(WebApp?.MainButton.setParams).toBeCalledWith(
expect.objectContaining({
text_color: '#0f0',
}),
);
});
it('checks color props', () => {
const WebApp = useWebApp();
const updates = [
<MainButton color="#f0f" />,
<MainButton />,
<MainButton color="#0f0" />,
];
renderComponentTree(() => updates.shift() || <MainButton />);
expect(WebApp?.MainButton.setParams).toBeCalledWith(
expect.objectContaining({
color: '#f0f',
}),
);
expect(WebApp?.MainButton.setParams).toBeCalledWith(
expect.objectContaining({
color: '#fff',
}),
);
expect(WebApp?.MainButton.setParams).toBeCalledWith(
expect.objectContaining({
color: '#0f0',
}),
);
});
});
================================================
FILE: tests/__snapshots__/package.test.ts.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Package /lib should have correct /lib structure 1`] = `
[
"/BackButton.d.ts",
"/MainButton.d.ts",
"/SettingsButton.d.ts",
"/WebAppProvider.d.ts",
[
"/core/context.d.ts",
"/core/index.d.ts",
[
"/core/twa-types/WebApp.d.ts",
"/core/twa-types/WebAppVersion_6.1.d.ts",
"/core/twa-types/WebAppVersion_6.2.d.ts",
"/core/twa-types/WebAppVersion_6.4.d.ts",
"/core/twa-types/WebAppVersion_6.7.d.ts",
"/core/twa-types/WebAppVersion_6.9.d.ts",
"/core/twa-types/WebAppVersion_7.0.d.ts",
"/core/twa-types/index.d.ts",
],
"/core/useAsyncMode.d.ts",
"/core/useSmoothButtonsTransition.d.ts",
"/core/useWebApp.d.ts",
],
"/index.d.ts",
"/react-telegram-web-app.cjs",
"/react-telegram-web-app.modern.js",
"/react-telegram-web-app.module.js",
"/react-telegram-web-app.umd.js",
"/useCloudStorage.d.ts",
"/useExpand.d.ts",
"/useHapticFeedback.d.ts",
"/useInitData.d.ts",
"/useReadTextFromClipboard.d.ts",
"/useScanQrPopup.d.ts",
"/useShowPopup.d.ts",
"/useSwitchInlineQuery.d.ts",
"/useThemeParams.d.ts",
"/useWebApp.d.ts",
]
`;
exports[`Package /lib should have exports from modules 1`] = `
{
"BackButton": [Function],
"MainButton": [Function],
"SettingsButton": [Function],
"WebAppProvider": [Function],
"useCloudStorage": [Function],
"useExpand": [Function],
"useHapticFeedback": [Function],
"useInitData": [Function],
"useReadTextFromClipboard": [Function],
"useScanQrPopup": [Function],
"useShowPopup": [Function],
"useSwitchInlineQuery": [Function],
"useThemeParams": [Function],
"useWebApp": [Function],
}
`;
================================================
FILE: tests/core/__mocks__/useWebApp.ts
================================================
const instance = {
BackButton: {
show: jest.fn(),
hide: jest.fn(),
onClick: jest.fn(),
offClick: jest.fn(),
},
MainButton: {
show: jest.fn(),
hide: jest.fn(),
setParams: jest.fn(),
setText: jest.fn(),
disable: jest.fn(),
enable: jest.fn(),
onClick: jest.fn(),
offClick: jest.fn(),
showProgress: jest.fn(),
hideProgress: jest.fn(),
},
HapticFeedback: {
impactOccurred: jest.fn(),
selectionChanged: jest.fn(),
notificationOccurred: jest.fn(),
},
themeParams: {},
onEvent: jest.fn(),
offEvent: jest.fn(),
isExpanded: undefined,
expand: jest.fn(),
readTextFromClipboard: jest.fn(),
showScanQrPopup: jest.fn(),
closeScanQrPopup: jest.fn(),
showPopup: jest.fn(),
switchInlineQuery: jest.fn(),
colorScheme: undefined,
};
export default () => instance;
================================================
FILE: tests/core/useSmoothButtonsTransition.test.ts
================================================
import { useSmoothButtonsTransition } from '../../src/core';
import { act, renderHook } from '@testing-library/react';
describe('useSmoothButtonsTransition', () => {
afterEach(() => {
jest.useRealTimers();
});
it.each([
[{ smoothButtonsTransition: false, smoothButtonsTransitionMs: undefined }],
[{ smoothButtonsTransition: true, smoothButtonsTransitionMs: 50 }],
[{ smoothButtonsTransition: true, smoothButtonsTransitionMs: 100 }],
[{ smoothButtonsTransition: false, smoothButtonsTransitionMs: 100 }],
[{ smoothButtonsTransition: undefined, smoothButtonsTransitionMs: 100 }],
])(
'checks correct call show(),hide() with options %p',
({ smoothButtonsTransition, smoothButtonsTransitionMs }) => {
jest.spyOn(require('react'), 'useContext').mockImplementation(() => ({
smoothButtonsTransition,
smoothButtonsTransitionMs,
}));
const setTimeoutSpy = jest
.spyOn(global, 'setTimeout')
.mockImplementation(handler => {
handler();
return undefined as unknown as ReturnType<typeof setTimeout>;
});
const initialProps = {
show: jest.fn(),
hide: jest.fn(),
currentShowedIdRef: {
current: null,
},
id: 'idButton',
};
const { rerender, unmount } = renderHook(useSmoothButtonsTransition, {
initialProps,
});
expect(initialProps.show).toBeCalledTimes(1);
expect(initialProps.hide).toBeCalledTimes(0);
expect(initialProps.currentShowedIdRef.current).toBe('idButton');
act(() => {
rerender(initialProps);
});
expect(initialProps.currentShowedIdRef.current).toBe('idButton');
act(() => {
rerender(initialProps);
});
unmount();
expect(initialProps.currentShowedIdRef.current).toBe(null);
expect(initialProps.show).toBeCalledTimes(1);
expect(initialProps.hide).toBeCalledTimes(1);
expect(setTimeoutSpy).toBeCalledTimes(smoothButtonsTransition ? 1 : 0);
if (smoothButtonsTransition) {
expect(setTimeoutSpy).toBeCalledWith(
expect.any(Function),
smoothButtonsTransitionMs,
);
}
},
);
});
================================================
FILE: tests/package.test.ts
================================================
import * as fsAsync from 'fs/promises';
import * as fs from 'fs';
import * as path from 'path';
const BUILD_PATH = path.resolve(__dirname, '../lib');
const COMMON_JS_MODULE = 'react-telegram-web-app.cjs';
const walk = async (dirPath: string): Promise<any> =>
Promise.all(
await fsAsync.readdir(dirPath, { withFileTypes: true }).then(entries =>
entries.map(entry => {
const childPath = path.join(dirPath, entry.name);
return entry.isDirectory()
? walk(childPath)
: childPath.replace(BUILD_PATH, '');
}),
),
);
describe('Package /lib', () => {
beforeEach(() => {
jest.resetModules();
});
it('should have correct /lib structure', async () => {
expect(await walk(BUILD_PATH)).toMatchSnapshot();
});
it('should have exports from modules', () => {
const indexModule = require(path.join(BUILD_PATH, COMMON_JS_MODULE));
expect(indexModule).toMatchSnapshot();
});
});
describe('Public API documentation', () => {
it('should have describe in README.md', () => {
const indexModule = Object.keys(
require(path.join(BUILD_PATH, COMMON_JS_MODULE)),
);
const mdFile = fs.readFileSync(
path.resolve(__dirname, '../README.md'),
'utf8',
);
expect(
indexModule
.map(name => `[${name}](./docs/README.md#${name.toLowerCase()})`)
.filter(name => !mdFile.includes(name)),
).toStrictEqual([]);
});
});
================================================
FILE: tests/setupTests.ts
================================================
jest.mock(
'../src/core/useWebApp',
() => require('./core/__mocks__/useWebApp').default,
);
global.beforeEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
});
================================================
FILE: tests/useCloudStorage.test.ts
================================================
import { renderHook } from '@testing-library/react';
import useCloudStorage from '../src/useCloudStorage';
// TODO Написать юниты на хук
describe('useCloudStorage', () => {
it('TODO', () => {
renderHook(useCloudStorage);
});
});
================================================
FILE: tests/useExpand.test.tsx
================================================
import { act, renderHook } from '@testing-library/react';
import { useWebApp } from '../src/core';
import { WebApp } from '../src/core/twa-types';
import useExpand from '../src/useExpand';
describe('useExpand', () => {
it.each([
[true, true],
[false, false],
[undefined, undefined],
])(
'checks WebApp.isExpanded = %p hook return isExpanded default value = %p',
(isExpanded, expectedIsExpanded) => {
jest.replaceProperty(
useWebApp() as WebApp,
'isExpanded',
isExpanded as boolean,
);
const { result } = renderHook(useExpand);
act(() => {
expect(result.current[0]).toBe(expectedIsExpanded);
});
},
);
/**
* TODO перенести тест перебинда в тест useWebApp
*/
it('checks correct rebind event handler', () => {
const { rerender, unmount } = renderHook(useExpand);
act(() => {
rerender();
});
/**
* After one rerender, should be called onEvent once
*/
expect(useWebApp()?.onEvent).toBeCalledTimes(1);
expect(useWebApp()?.offEvent).toBeCalledTimes(0);
expect(useWebApp()?.onEvent).toBeCalledWith(
'viewportChanged',
expect.any(Function),
);
(useWebApp()?.onEvent as jest.Mock).mockClear();
act(() => {
rerender();
});
expect(useWebApp()?.onEvent).toBeCalledTimes(0);
expect(useWebApp()?.offEvent).toBeCalledTimes(0);
(useWebApp()?.offEvent as jest.Mock).mockClear();
act(() => {
unmount();
});
expect(useWebApp()?.onEvent).toBeCalledTimes(0);
expect(useWebApp()?.offEvent).toBeCalledTimes(1);
expect(useWebApp()?.offEvent).toBeCalledWith(
'viewportChanged',
expect.any(Function),
);
});
it('checks correct change state isExpanded', () => {
jest.replaceProperty(useWebApp() as WebApp, 'isExpanded', true);
const fireEventHandler = (state: boolean) => {
jest.replaceProperty(useWebApp() as WebApp, 'isExpanded', state);
act(() => {
(useWebApp()?.onEvent as jest.Mock).mock.lastCall?.[1]?.({
isStateStable: true,
});
rerender();
});
};
const { result, rerender } = renderHook(useExpand);
act(() => {
expect(result.current[0]).toBe(true);
});
fireEventHandler(false);
expect(result.current[0]).toBe(false);
fireEventHandler(true);
expect(result.current[0]).toBe(true);
});
it('checks correct call WebApp.expand() from hook', () => {
const { result } = renderHook(useExpand);
act(() => {
result.current[1]();
});
expect(useWebApp()?.expand as jest.Mock).toBeCalledTimes(1);
});
});
================================================
FILE: tests/useHapticFeedback.test.ts
================================================
import { act, renderHook } from '@testing-library/react';
import useHapticFeedback from '../src/useHapticFeedback';
import { useWebApp } from '../src/core';
describe('useHapticFeedback', () => {
it('checks correct call WebApp.HapticFeedback api', () => {
const { result } = renderHook(useHapticFeedback);
const [impactOccurred, notificationOccurred, selectionChanged] =
result.current;
act(() => {
impactOccurred('soft');
notificationOccurred('warning');
selectionChanged();
});
expect(useWebApp()?.HapticFeedback?.impactOccurred).toBeCalledTimes(1);
expect(useWebApp()?.HapticFeedback?.impactOccurred).toBeCalledWith('soft');
expect(useWebApp()?.HapticFeedback?.notificationOccurred).toBeCalledTimes(
1,
);
expect(useWebApp()?.HapticFeedback?.notificationOccurred).toBeCalledWith(
'warning',
);
expect(useWebApp()?.HapticFeedback?.selectionChanged).toBeCalledTimes(1);
expect(useWebApp()?.HapticFeedback?.selectionChanged).toBeCalledWith();
});
});
================================================
FILE: tests/useInitData.test.ts
================================================
import { renderHook } from '@testing-library/react';
import useCloudStorage from '../src/useCloudStorage';
// TODO Написать юниты на хук
describe('useInitData', () => {
it('TODO', () => {
renderHook(useCloudStorage);
});
});
================================================
FILE: tests/useReadTextFromClipboard.test.ts
================================================
import { renderHook } from '@testing-library/react';
import useReadTextFromClipboard from '../src/useReadTextFromClipboard';
import { useWebApp } from '../src/core';
import { WebApp } from '../src/core/twa-types';
describe('useReadTextFromClipboard', () => {
it('checks correct call WebApp.readTextFromClipboard api', async () => {
const { result } = renderHook(useReadTextFromClipboard);
const readTextFromClickBoard = result.current;
jest
.spyOn(useWebApp() as WebApp, 'readTextFromClipboard')
.mockImplementation(resolve => {
resolve!('TEST_CLICK');
});
const text = await readTextFromClickBoard();
expect(text).toBe('TEST_CLICK');
});
});
================================================
FILE: tests/useScanQrPopup.test.ts
================================================
import { renderHook } from '@testing-library/react';
import useScanQrPopup from '../src/useScanQrPopup';
import { useWebApp } from '../src/core';
describe('useScanQrPopup', () => {
it('checks correct call WebApp.showScanQrPopup api', () => {
const { result } = renderHook(useScanQrPopup);
const [show] = result.current;
show({
text: 'Text under QR',
});
expect(useWebApp()?.showScanQrPopup).toBeCalledTimes(1);
expect(useWebApp()?.showScanQrPopup).toBeCalledWith({
text: 'Text under QR',
});
});
it('checks correct call WebApp.closeScanQrPopup api', () => {
const { result } = renderHook(useScanQrPopup);
const [_, close] = result.current;
close();
expect(useWebApp()?.closeScanQrPopup).toBeCalledTimes(1);
expect(useWebApp()?.closeScanQrPopup).toBeCalledWith();
});
});
================================================
FILE: tests/useShowPopup.test.ts
================================================
import { renderHook } from '@testing-library/react';
import useShowPopup from '../src/useShowPopup';
import { useWebApp } from '../src/core';
import { WebApp } from '../src/core/twa-types';
describe('useShowPopup', () => {
it('checks correct call WebApp.showPopup api', async () => {
const { result } = renderHook(useShowPopup);
const showPopup = result.current;
const spyShowPopup = jest
.spyOn(useWebApp() as WebApp, 'showPopup')
.mockImplementation((_, callback) => {
callback!('buttonId');
});
const params = {
title: 'title',
message: 'message',
buttons: [
{
id: 'buttonId',
type: 'cancel',
text: 'textButton',
},
],
};
const button = await showPopup(params);
expect(spyShowPopup).toBeCalledWith(params, expect.any(Function));
expect(button).toBe('buttonId');
});
});
================================================
FILE: tests/useSwitchInlineQuery.test.ts
================================================
import { renderHook } from '@testing-library/react';
import { useWebApp } from '../src/core';
import useSwitchInlineQuery from '../src/useSwitchInlineQuery';
describe('useSwitchInlineQuery', () => {
it('checks correct call WebApp.switchInlineQuery api', () => {
const { result } = renderHook(useSwitchInlineQuery);
const switchInlineQuery = result.current;
switchInlineQuery('Test string');
expect(useWebApp()!.switchInlineQuery).toBeCalledWith('Test string');
switchInlineQuery('Test string 2', ['groups']);
expect(useWebApp()!.switchInlineQuery).toBeCalledWith('Test string 2', [
'groups',
]);
expect(useWebApp()!.switchInlineQuery).toBeCalledTimes(2);
});
});
================================================
FILE: tests/useThemeParams.test.ts
================================================
import { act, renderHook } from '@testing-library/react';
import { useWebApp } from '../src/core';
import { WebApp } from '../src/core/twa-types';
import useThemeParams from '../src/useThemeParams';
describe('useThemeParams', () => {
it('checks is correct initial value return', () => {
jest.replaceProperty(useWebApp() as WebApp, 'colorScheme', 'dark');
jest.replaceProperty(useWebApp() as WebApp, 'themeParams', {
bg_color: '#fff',
});
const { result } = renderHook(useThemeParams);
const [colorScheme, themeParams] = result.current;
expect(colorScheme).toBe('dark');
expect(themeParams).toEqual({
bg_color: '#fff',
});
});
it('checks correct return value after fire onEvent', function () {
let handleOnEvent = () => {};
jest
.spyOn(useWebApp() as WebApp, 'onEvent')
.mockImplementation((_, handler) => {
handleOnEvent = handler;
});
const { result } = renderHook(useThemeParams);
expect(result.current[0]).toBe(undefined);
expect(result.current[1]).toEqual({});
expect(useWebApp()?.onEvent).toBeCalledWith(
'themeChanged',
expect.any(Function),
);
expect(useWebApp()?.onEvent).toBeCalledTimes(1);
jest.replaceProperty(useWebApp() as WebApp, 'colorScheme', 'dark');
jest.replaceProperty(useWebApp() as WebApp, 'themeParams', {
bg_color: '#000',
});
act(() => {
handleOnEvent();
});
act(() => {
expect(result.current[0]).toBe('dark');
expect(result.current[1]).toEqual({
bg_color: '#000',
});
});
});
});
================================================
FILE: tests/utils.ts
================================================
import * as React from 'react';
import { ReactTestRenderer } from 'react-test-renderer';
import * as renderer from 'react-test-renderer';
export const renderComponentTree = (fabric: () => React.ReactElement) => {
let tree: ReactTestRenderer | undefined = undefined;
renderer.act(() => {
tree = renderer.create(fabric());
});
renderer.act(() => {
tree!.update(fabric());
});
renderer.act(() => {
tree!.update(fabric());
});
renderer.act(() => {
tree!.unmount();
});
return tree as unknown as ReactTestRenderer;
};
================================================
FILE: tsconfig.json
================================================
{
"include": ["src", "global.d.ts"],
"exclude": ["tests"],
"compilerOptions": {
"skipLibCheck": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"esModuleInterop": true,
"jsx": "react",
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"noUncheckedIndexedAccess": true,
"importHelpers": true,
"strictNullChecks": true
}
}
================================================
FILE: typedoc.json
================================================
{
"entryPoints": ["src/index.ts"],
"githubPages": true,
"plugin": ["typedoc-plugin-markdown"],
"out": "docs",
"readme": "none",
"disableSources": true,
"externalSymbolLinkMappings": {
"telegram": {
"CloudStorage": "https://core.telegram.org/bots/webapps#cloudstorage",
"MainButton": "https://core.telegram.org/bots/webapps#mainbutton",
"SettingsButton": "https://core.telegram.org/bots/webapps#settingsbutton",
"ScanQrPopupParams": "https://core.telegram.org/bots/webapps#scanqrpopupparams",
"BackButton": "https://core.telegram.org/bots/webapps#backbutton",
"PopupParams": "https://core.telegram.org/bots/webapps#popupparams",
"PopupButton": "https://core.telegram.org/bots/webapps#popupbutton",
"HapticFeedback": "https://core.telegram.org/bots/webapps#hapticfeedback",
"ThemeParams": "https://core.telegram.org/bots/webapps#themeparams",
"WebApp": "https://core.telegram.org/bots/webapps#initializing-mini-apps",
"WebAppInitData": "https://core.telegram.org/bots/webapps#webappinitdata",
"WebAppUser": "https://core.telegram.org/bots/webapps#webappuser",
"WebAppChat": "https://core.telegram.org/bots/webapps#webappchat"
},
"react": {
"Component": "https://reactjs.org/docs/react-component.html"
}
}
}
gitextract_e2fmb7zc/ ├── .changeset/ │ └── config.json ├── .eslintignore ├── .eslintrc.json ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ └── bug_report.md │ └── workflows/ │ ├── contributing.yml │ ├── release.yml │ ├── static.yml │ └── tests.yml ├── .gitignore ├── .husky/ │ └── pre-commit ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── demo/ │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public/ │ │ └── index.html │ ├── src/ │ │ ├── BackButtonDemo.tsx │ │ ├── ExpandDemo.tsx │ │ ├── HapticFeedbackDemo.tsx │ │ ├── MainButtonDemo.tsx │ │ ├── ScanQrPopupDemo.tsx │ │ ├── ShowPopupDemo.tsx │ │ ├── index.css │ │ ├── index.tsx │ │ ├── react-app-env.d.ts │ │ ├── spy.mjs │ │ └── useBetaVersion.ts │ └── tsconfig.json ├── docs/ │ ├── .nojekyll │ ├── README.md │ └── interfaces/ │ ├── BackButtonProps.md │ ├── MainButtonProps.md │ ├── ScanQrPopupParams.md │ ├── SettingsButtonProps.md │ ├── ShowPopupButton.md │ ├── ShowPopupParams.md │ └── ThemeParams.md ├── global.d.ts ├── jest.config.json ├── package.json ├── src/ │ ├── BackButton.tsx │ ├── MainButton.tsx │ ├── SettingsButton.tsx │ ├── WebAppProvider.tsx │ ├── core/ │ │ ├── context.ts │ │ ├── index.ts │ │ ├── twa-types/ │ │ │ ├── WebApp.d.ts │ │ │ ├── WebAppVersion_6.1.d.ts │ │ │ ├── WebAppVersion_6.2.d.ts │ │ │ ├── WebAppVersion_6.4.d.ts │ │ │ ├── WebAppVersion_6.7.d.ts │ │ │ ├── WebAppVersion_6.9.d.ts │ │ │ ├── WebAppVersion_7.0.d.ts │ │ │ └── index.d.ts │ │ ├── useAsyncMode.ts │ │ ├── useSmoothButtonsTransition.ts │ │ └── useWebApp.ts │ ├── index.ts │ ├── useCloudStorage.ts │ ├── useExpand.ts │ ├── useHapticFeedback.ts │ ├── useInitData.ts │ ├── useReadTextFromClipboard.ts │ ├── useScanQrPopup.ts │ ├── useShowPopup.ts │ ├── useSwitchInlineQuery.ts │ ├── useThemeParams.ts │ └── useWebApp.ts ├── tests/ │ ├── BackButton.test.tsx │ ├── MainButton.test.tsx │ ├── __snapshots__/ │ │ └── package.test.ts.snap │ ├── core/ │ │ ├── __mocks__/ │ │ │ └── useWebApp.ts │ │ └── useSmoothButtonsTransition.test.ts │ ├── package.test.ts │ ├── setupTests.ts │ ├── useCloudStorage.test.ts │ ├── useExpand.test.tsx │ ├── useHapticFeedback.test.ts │ ├── useInitData.test.ts │ ├── useReadTextFromClipboard.test.ts │ ├── useScanQrPopup.test.ts │ ├── useShowPopup.test.ts │ ├── useSwitchInlineQuery.test.ts │ ├── useThemeParams.test.ts │ └── utils.ts ├── tsconfig.json └── typedoc.json
SYMBOL INDEX (66 symbols across 23 files)
FILE: global.d.ts
type Window (line 4) | interface Window {
FILE: src/BackButton.tsx
type BackButtonProps (line 7) | interface BackButtonProps {
FILE: src/MainButton.tsx
type MainButtonProps (line 7) | interface MainButtonProps {
FILE: src/SettingsButton.tsx
type SettingsButtonProps (line 7) | interface SettingsButtonProps {
FILE: src/WebAppProvider.tsx
type WebAppProviderProps (line 18) | type WebAppProviderProps = PropsWithChildren<{
FILE: src/core/context.ts
type Options (line 15) | type Options = {
constant DEFAULT_OPTIONS (line 31) | const DEFAULT_OPTIONS: Options = {
type SystemContext (line 38) | type SystemContext = {
FILE: src/core/twa-types/WebApp.d.ts
type ThemeParams (line 5) | interface ThemeParams {
type MainButton (line 17) | interface MainButton {
type WebAppUser (line 52) | interface WebAppUser {
type WebAppInitData (line 65) | interface WebAppInitData {
type Event (line 76) | interface Event {
type WebApp (line 96) | interface WebApp extends Event {
FILE: src/core/twa-types/WebAppVersion_6.1.d.ts
type ThemeParams (line 4) | interface ThemeParams extends TelegramWebApp.ThemeParams {
type BackButton (line 11) | interface BackButton {
type MainButton (line 23) | interface MainButton extends TelegramWebApp.MainButton {
type WebAppChat (line 30) | interface WebAppChat {
type WebAppInitData (line 38) | interface WebAppInitData extends TelegramWebApp.WebAppInitData {
type HapticFeedback (line 46) | interface HapticFeedback {
type Event (line 56) | interface Event {
type WebApp (line 79) | interface WebApp extends TelegramWebApp.WebApp, Event {
FILE: src/core/twa-types/WebAppVersion_6.2.d.ts
type WebAppUser (line 8) | interface WebAppUser extends TelegramWebApp.WebAppUser {
type WebAppInitData (line 15) | interface WebAppInitData extends TelegramWebAppVersion6_1.WebAppInitData {
type PopupButton (line 23) | interface PopupButton {
type PopupParams (line 32) | interface PopupParams {
type Event (line 38) | interface Event {
type WebApp (line 47) | interface WebApp extends TelegramWebAppVersion6_1.WebApp, Event {
FILE: src/core/twa-types/WebAppVersion_6.4.d.ts
type ScanQrPopupParams (line 7) | interface ScanQrPopupParams {
type Event (line 11) | interface Event {
type WebApp (line 25) | interface WebApp extends TelegramWebAppVersion6_2.WebApp, Event {
FILE: src/core/twa-types/WebAppVersion_6.7.d.ts
type WebApp (line 4) | interface WebApp extends TelegramWebAppVersion6_4.WebApp {
FILE: src/core/twa-types/WebAppVersion_6.9.d.ts
type WebAppUser (line 5) | interface WebAppUser extends TelegramWebAppVersion6_2.WebAppUser {
type Event (line 10) | interface Event {
type StorageKey (line 27) | type StorageKey = string;
type CloudStorageCallback (line 28) | type CloudStorageCallback<T> =
type CloudStorage (line 35) | interface CloudStorage {
type WebApp (line 54) | interface WebApp extends TelegramWebAppVersion6_7.WebApp, Event {
FILE: src/core/twa-types/WebAppVersion_7.0.d.ts
type WebApp (line 2) | interface WebApp extends TelegramWebAppVersion6_9.WebApp {
FILE: src/core/twa-types/index.d.ts
type WebApp (line 9) | type WebApp = TelegramWebApp.WebApp &
FILE: src/useCloudStorage.ts
type GetItemFunction (line 8) | type GetItemFunction = (key: string) => Promise<string>;
type SetItemFunction (line 14) | type SetItemFunction = (key: string, value: string) => Promise<void>;
type GetItemsFunction (line 20) | type GetItemsFunction = (keys: string[]) => Promise<string[]>;
type RemoveItemFunction (line 26) | type RemoveItemFunction = (key: string) => Promise<void>;
type RemoveItemsFunction (line 32) | type RemoveItemsFunction = (key: string[]) => Promise<void>;
type GetKeysFunction (line 38) | type GetKeysFunction = () => Promise<string[]>;
FILE: src/useHapticFeedback.ts
type ImpactOccurredFunction (line 13) | type ImpactOccurredFunction = (
type NotificationOccurredFunction (line 24) | type NotificationOccurredFunction = (
type SelectionChangedFunction (line 32) | type SelectionChangedFunction = () => void;
FILE: src/useInitData.ts
type WebAppChat (line 6) | type WebAppChat = {
type WebAppUser (line 17) | type WebAppUser = {
type InitData (line 30) | type InitData = string;
type InitDataUnsafe (line 35) | type InitDataUnsafe = {
FILE: src/useReadTextFromClipboard.ts
type ReadTextFromClipboardFunction (line 8) | type ReadTextFromClipboardFunction = () => Promise<string>;
FILE: src/useScanQrPopup.ts
type ScanQrPopupCallback (line 9) | type ScanQrPopupCallback = (text: string) => true | void;
type ScanQrPopupParams (line 15) | interface ScanQrPopupParams {
type ShowScanQrPopupFunction (line 26) | type ShowScanQrPopupFunction = (
type CloseScanQrPopupFunction (line 34) | type CloseScanQrPopupFunction = () => void;
FILE: src/useShowPopup.ts
type ShowPopupButton (line 7) | interface ShowPopupButton extends Record<string, unknown> {
type ShowPopupParams (line 30) | interface ShowPopupParams extends Record<string, unknown> {
type ShowPopupFunction (line 50) | type ShowPopupFunction = (params: ShowPopupParams) => Promise<string>;
FILE: src/useSwitchInlineQuery.ts
type SwitchInlineQueryFunction (line 8) | type SwitchInlineQueryFunction = (
FILE: src/useThemeParams.ts
type ThemeParams (line 8) | interface ThemeParams {
type ColorScheme (line 43) | type ColorScheme = 'light' | 'dark' | undefined;
FILE: tests/package.test.ts
constant BUILD_PATH (line 5) | const BUILD_PATH = path.resolve(__dirname, '../lib');
constant COMMON_JS_MODULE (line 6) | const COMMON_JS_MODULE = 'react-telegram-web-app.cjs';
Condensed preview — 93 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (150K chars).
[
{
"path": ".changeset/config.json",
"chars": 277,
"preview": "{\n \"$schema\": \"https://unpkg.com/@changesets/config@2.2.0/schema.json\",\n \"changelog\": \"@changesets/cli/changelog\",\n \""
},
{
"path": ".eslintignore",
"chars": 14,
"preview": "/tests/**/*.*\n"
},
{
"path": ".eslintrc.json",
"chars": 676,
"preview": "{\n \"root\": true,\n \"env\": {\n \"browser\": true,\n \"es2021\": true\n },\n \"extends\": [\n \"eslint"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 1159,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n<!--\n Please pr"
},
{
"path": ".github/workflows/contributing.yml",
"chars": 564,
"preview": "name: Generate Contributors Images\n\non:\n push:\n branches:\n - master\n\njobs:\n build:\n runs-on: ubuntu-latest\n"
},
{
"path": ".github/workflows/release.yml",
"chars": 904,
"preview": "name: Release package\n\non:\n push:\n branches:\n - master\n paths-ignore:\n - demo/**\n\njobs:\n build:\n ru"
},
{
"path": ".github/workflows/static.yml",
"chars": 1311,
"preview": "# Simple workflow for deploying static content to GitHub Pages\nname: Deploy static content to Pages\n\non:\n # Runs on pus"
},
{
"path": ".github/workflows/tests.yml",
"chars": 706,
"preview": "name: Run tests\non:\n push:\n paths:\n - src/**\n - tests/**\n - jest.config.json\njobs:\n test:\n runs-o"
},
{
"path": ".gitignore",
"chars": 24,
"preview": "/lib\nnode_modules\n.idea\n"
},
{
"path": ".husky/pre-commit",
"chars": 69,
"preview": "#!/usr/bin/env sh\n. \"$(dirname -- \"$0\")/_/husky.sh\"\n\nnpx lint-staged\n"
},
{
"path": ".nvmrc",
"chars": 8,
"preview": "v16.14.2"
},
{
"path": ".prettierignore",
"chars": 45,
"preview": "lib\ndemo/build\npackage.json\npackage-lock.json"
},
{
"path": ".prettierrc",
"chars": 250,
"preview": "{\n \"singleQuote\": true,\n \"trailingComma\": \"all\",\n \"useTabs\": false,\n \"arrowParens\": \"avoid\",\n \"overrides\": [\n {\n"
},
{
"path": "CHANGELOG.md",
"chars": 3606,
"preview": "# @vkruglikov/react-telegram-web-app\n\n## 2.2.0\n\n### Minor Changes\n\n- e3bcaf0: Add SettingsButton\n\n## 2.1.9\n\n### Patch Ch"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 5201,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": "CONTRIBUTING.md",
"chars": 2101,
"preview": "# Contributing\n\nHi there! Interested in contributing to `react-telegram-web-app`? We'd love your help\n\n## Goals\n\nThe mai"
},
{
"path": "LICENSE",
"chars": 1075,
"preview": "MIT License\n\nCopyright (c) 2022 Kruglikov Valentin\n\nPermission is hereby granted, free of charge, to any person obtainin"
},
{
"path": "README.md",
"chars": 5626,
"preview": "# React components for Telegram MiniApp\n\n[](h"
},
{
"path": "demo/.gitignore",
"chars": 296,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
},
{
"path": "demo/README.md",
"chars": 184,
"preview": "## install\n\nInstall certificate\n\n```bash\nmkcert react-telegram-web-app.domain\n```\n\nAdd domain to /etc/hosts as 127.0.0.1"
},
{
"path": "demo/package.json",
"chars": 845,
"preview": "{\n \"name\": \"demo\",\n \"version\": \"0.1.0\",\n \"private\": false,\n \"dependencies\": {\n \"@types/node\": \"^16.18.3\",\n \"@t"
},
{
"path": "demo/public/index.html",
"chars": 374,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta\n name=\"viewport\"\n content=\"wi"
},
{
"path": "demo/src/BackButtonDemo.tsx",
"chars": 1172,
"preview": "import { Button, Form, Typography } from 'antd';\nimport { FC, useState } from 'react';\nimport { BackButton, useShowPopup"
},
{
"path": "demo/src/ExpandDemo.tsx",
"chars": 766,
"preview": "import { Button, Form, Typography } from 'antd';\nimport { useExpand } from '@vkruglikov/react-telegram-web-app';\n\nconst "
},
{
"path": "demo/src/HapticFeedbackDemo.tsx",
"chars": 2191,
"preview": "import { Button, Form, Typography, Select } from 'antd';\nimport { FC, useState } from 'react';\nimport {\n ImpactOccurred"
},
{
"path": "demo/src/MainButtonDemo.tsx",
"chars": 2082,
"preview": "import { Button, Form, Input, Typography, Switch } from 'antd';\nimport { FC, useState } from 'react';\nimport {\n MainBut"
},
{
"path": "demo/src/ScanQrPopupDemo.tsx",
"chars": 1069,
"preview": "import { Button, Form, Typography } from 'antd';\nimport {\n useScanQrPopup,\n useShowPopup,\n} from '@vkruglikov/react-te"
},
{
"path": "demo/src/ShowPopupDemo.tsx",
"chars": 1878,
"preview": "import { Button, Form, Input, Typography } from 'antd';\nimport { FC, useState } from 'react';\nimport {\n ShowPopupParams"
},
{
"path": "demo/src/index.css",
"chars": 1664,
"preview": ":root {\n --tg-theme-bg-color: #fff;\n --tg-theme-text-color: #0a0a0a;\n --tg-theme-hint-color: #929292;\n --tg-theme-li"
},
{
"path": "demo/src/index.tsx",
"chars": 3098,
"preview": "import React, { DispatchWithoutAction, FC, useState } from 'react';\nimport ReactDOM from 'react-dom/client';\nimport {\n "
},
{
"path": "demo/src/react-app-env.d.ts",
"chars": 40,
"preview": "/// <reference types=\"react-scripts\" />\n"
},
{
"path": "demo/src/spy.mjs",
"chars": 871,
"preview": "// @ts-ignore\nconst WebView = window.Telegram.WebView;\n\nconst postEvent = WebView.postEvent;\nconst receiveEvent = WebVie"
},
{
"path": "demo/src/useBetaVersion.ts",
"chars": 864,
"preview": "import {\n useHapticFeedback,\n useShowPopup,\n} from '@vkruglikov/react-telegram-web-app';\nimport { useRef, useState } f"
},
{
"path": "demo/tsconfig.json",
"chars": 503,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es5\",\n \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n \"allowJs\": true,\n \"sk"
},
{
"path": "docs/.nojekyll",
"chars": 143,
"preview": "TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `github"
},
{
"path": "docs/README.md",
"chars": 24400,
"preview": "@vkruglikov/react-telegram-web-app\n\n# @vkruglikov/react-telegram-web-app\n\n## Table of contents\n\n### Interfaces\n\n- [BackB"
},
{
"path": "docs/interfaces/BackButtonProps.md",
"chars": 403,
"preview": "[@vkruglikov/react-telegram-web-app](../README.md) / BackButtonProps\n\n# Interface: BackButtonProps\n\nThe props type of [`"
},
{
"path": "docs/interfaces/MainButtonProps.md",
"chars": 1284,
"preview": "[@vkruglikov/react-telegram-web-app](../README.md) / MainButtonProps\n\n# Interface: MainButtonProps\n\nThe props type of [`"
},
{
"path": "docs/interfaces/ScanQrPopupParams.md",
"chars": 483,
"preview": "[@vkruglikov/react-telegram-web-app](../README.md) / ScanQrPopupParams\n\n# Interface: ScanQrPopupParams\n\nThis object desc"
},
{
"path": "docs/interfaces/SettingsButtonProps.md",
"chars": 386,
"preview": "[@vkruglikov/react-telegram-web-app](../README.md) / SettingsButtonProps\n\n# Interface: SettingsButtonProps\n\nThe props ty"
},
{
"path": "docs/interfaces/ShowPopupButton.md",
"chars": 945,
"preview": "[@vkruglikov/react-telegram-web-app](../README.md) / ShowPopupButton\n\n# Interface: ShowPopupButton\n\nYou have to see orig"
},
{
"path": "docs/interfaces/ShowPopupParams.md",
"chars": 846,
"preview": "[@vkruglikov/react-telegram-web-app](../README.md) / ShowPopupParams\n\n# Interface: ShowPopupParams\n\nYou have to see orig"
},
{
"path": "docs/interfaces/ThemeParams.md",
"chars": 1387,
"preview": "[@vkruglikov/react-telegram-web-app](../README.md) / ThemeParams\n\n# Interface: ThemeParams\n\nThis object contains the use"
},
{
"path": "global.d.ts",
"chars": 170,
"preview": "import { WebApp } from './src/core/twa-types';\n\ndeclare global {\n interface Window {\n Telegram?: {\n WebApp: Web"
},
{
"path": "jest.config.json",
"chars": 134,
"preview": "{\n \"transform\": {\n \"\\\\.[jt]sx?$\": \"ts-jest\"\n },\n \"testEnvironment\": \"jsdom\",\n \"setupFilesAfterEnv\": [\"./tests/set"
},
{
"path": "package.json",
"chars": 2586,
"preview": "{\n \"name\": \"@vkruglikov/react-telegram-web-app\",\n \"version\": \"2.2.0\",\n \"description\": \"React components for Telegram "
},
{
"path": "src/BackButton.tsx",
"chars": 1229,
"preview": "import { useContext, useEffect, useId } from 'react';\nimport { useWebApp, useSmoothButtonsTransition, systemContext } fr"
},
{
"path": "src/MainButton.tsx",
"chars": 2959,
"preview": "import { useContext, useEffect, useId } from 'react';\nimport { useWebApp, useSmoothButtonsTransition, systemContext } fr"
},
{
"path": "src/SettingsButton.tsx",
"chars": 1118,
"preview": "import { useEffect } from 'react';\nimport { useWebApp } from './core';\n\n/**\n * The props type of {@link SettingsButton |"
},
{
"path": "src/WebAppProvider.tsx",
"chars": 2049,
"preview": "import React, {\n PropsWithChildren,\n ReactElement,\n useEffect,\n useMemo,\n} from 'react';\nimport {\n webAppContext,\n "
},
{
"path": "src/core/context.ts",
"chars": 1345,
"preview": "import { createContext, MutableRefObject } from 'react';\n\nexport const getWebAppFromGlobal = () =>\n typeof window !== '"
},
{
"path": "src/core/index.ts",
"chars": 223,
"preview": "export { default as useSmoothButtonsTransition } from './useSmoothButtonsTransition';\nexport { default as useWebApp } fr"
},
{
"path": "src/core/twa-types/WebApp.d.ts",
"chars": 2550,
"preview": "export declare namespace TelegramWebApp {\n /**\n * {@link https://core.telegram.org/bots/webapps#themeparams}\n */\n "
},
{
"path": "src/core/twa-types/WebAppVersion_6.1.d.ts",
"chars": 2454,
"preview": "import { TelegramWebApp } from './WebApp';\n\nexport declare namespace TelegramWebAppVersion6_1 {\n interface ThemeParams "
},
{
"path": "src/core/twa-types/WebAppVersion_6.2.d.ts",
"chars": 1505,
"preview": "import { TelegramWebAppVersion6_1 } from './WebAppVersion_6.1';\nimport { TelegramWebApp } from './WebApp';\n\nexport decla"
},
{
"path": "src/core/twa-types/WebAppVersion_6.4.d.ts",
"chars": 976,
"preview": "import { TelegramWebAppVersion6_2 } from './WebAppVersion_6.2';\n\nexport declare namespace TelegramWebAppVersion6_4 {\n /"
},
{
"path": "src/core/twa-types/WebAppVersion_6.7.d.ts",
"chars": 251,
"preview": "import { TelegramWebAppVersion6_4 } from './WebAppVersion_6.4';\n\nexport declare namespace TelegramWebAppVersion6_7 {\n i"
},
{
"path": "src/core/twa-types/WebAppVersion_6.9.d.ts",
"chars": 1632,
"preview": "import { TelegramWebAppVersion6_7 } from './WebAppVersion_6.7';\nimport { TelegramWebAppVersion6_2 } from './WebAppVersio"
},
{
"path": "src/core/twa-types/WebAppVersion_7.0.d.ts",
"chars": 182,
"preview": "export declare namespace TelegramWebAppVersion7_0 {\n interface WebApp extends TelegramWebAppVersion6_9.WebApp {\n // "
},
{
"path": "src/core/twa-types/index.d.ts",
"chars": 742,
"preview": "import { TelegramWebApp } from './WebApp';\nimport { TelegramWebAppVersion6_1 } from './WebAppVersion_6.1';\nimport { Tele"
},
{
"path": "src/core/useAsyncMode.ts",
"chars": 1101,
"preview": "import { useEffect, useState } from 'react';\n\nconst useAsyncMode = (enabled: boolean) => {\n const [isLoaded, setIsLoade"
},
{
"path": "src/core/useSmoothButtonsTransition.ts",
"chars": 1046,
"preview": "import { MutableRefObject, useContext, useEffect } from 'react';\nimport { optionsContext } from './context';\n\nconst _noo"
},
{
"path": "src/core/useWebApp.ts",
"chars": 231,
"preview": "import { useContext } from 'react';\nimport { webAppContext } from './context';\n\n/**\n * @private\n * @ignore\n */\nconst use"
},
{
"path": "src/index.ts",
"chars": 1527,
"preview": "export { default as MainButton, MainButtonProps } from './MainButton';\nexport { default as BackButton, BackButtonProps }"
},
{
"path": "src/useCloudStorage.ts",
"chars": 3878,
"preview": "import { useWebApp } from './core';\nimport { useCallback, useMemo } from 'react';\n\n/**\n * This function provides `getIte"
},
{
"path": "src/useExpand.ts",
"chars": 1435,
"preview": "import { DispatchWithoutAction, useCallback, useEffect, useState } from 'react';\nimport { useWebApp } from './core';\n\n/*"
},
{
"path": "src/useHapticFeedback.ts",
"chars": 2929,
"preview": "import { useWebApp } from './core';\nimport { useCallback } from 'react';\n\n/**\n * A method tells that an impact occurred."
},
{
"path": "src/useInitData.ts",
"chars": 1505,
"preview": "import { useWebApp } from './core';\n\n/**\n * {@link telegram!WebAppChat}\n */\nexport type WebAppChat = {\n id: number;\n t"
},
{
"path": "src/useReadTextFromClipboard.ts",
"chars": 1068,
"preview": "import { useCallback } from 'react';\nimport { useWebApp } from './core';\n\n/**\n * This function provided Promise function"
},
{
"path": "src/useScanQrPopup.ts",
"chars": 1749,
"preview": "import { useWebApp } from './core';\nimport { useCallback } from 'react';\n\n/**\n * If an optional callback parameter was p"
},
{
"path": "src/useShowPopup.ts",
"chars": 2352,
"preview": "import { useCallback } from 'react';\nimport { useWebApp } from './core';\n\n/**\n * You have to see original interface {@li"
},
{
"path": "src/useSwitchInlineQuery.ts",
"chars": 910,
"preview": "import { useWebApp } from './core';\nimport { useCallback } from 'react';\n\n/**\n * This function that inserts the bot's us"
},
{
"path": "src/useThemeParams.ts",
"chars": 2194,
"preview": "import { useEffect, useState } from 'react';\nimport { useWebApp } from './core';\n\n/**\n * This object contains the user's"
},
{
"path": "src/useWebApp.ts",
"chars": 368,
"preview": "import { useWebApp as _useWebApp } from './core';\n\n/**\n * This hook just provides native {@link telegram!WebApp} object\n"
},
{
"path": "tests/BackButton.test.tsx",
"chars": 1206,
"preview": "import * as React from 'react';\n\nimport BackButton from '../src/BackButton';\nimport { useWebApp } from '../src/core';\nim"
},
{
"path": "tests/MainButton.test.tsx",
"chars": 3809,
"preview": "import * as React from 'react';\n\nimport MainButton from '../src/MainButton';\nimport { useWebApp } from '../src/core';\nim"
},
{
"path": "tests/__snapshots__/package.test.ts.snap",
"chars": 1694,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Package /lib should have correct /lib structure 1`] = `\n[\n \"/BackB"
},
{
"path": "tests/core/__mocks__/useWebApp.ts",
"chars": 848,
"preview": "const instance = {\n BackButton: {\n show: jest.fn(),\n hide: jest.fn(),\n onClick: jest.fn(),\n offClick: jest."
},
{
"path": "tests/core/useSmoothButtonsTransition.test.ts",
"chars": 2225,
"preview": "import { useSmoothButtonsTransition } from '../../src/core';\nimport { act, renderHook } from '@testing-library/react';\n\n"
},
{
"path": "tests/package.test.ts",
"chars": 1443,
"preview": "import * as fsAsync from 'fs/promises';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nconst BUILD_PATH = path"
},
{
"path": "tests/setupTests.ts",
"chars": 177,
"preview": "jest.mock(\n '../src/core/useWebApp',\n () => require('./core/__mocks__/useWebApp').default,\n);\n\nglobal.beforeEach(() =>"
},
{
"path": "tests/useCloudStorage.test.ts",
"chars": 238,
"preview": "import { renderHook } from '@testing-library/react';\nimport useCloudStorage from '../src/useCloudStorage';\n\n// TODO Напи"
},
{
"path": "tests/useExpand.test.tsx",
"chars": 2656,
"preview": "import { act, renderHook } from '@testing-library/react';\n\nimport { useWebApp } from '../src/core';\nimport { WebApp } fr"
},
{
"path": "tests/useHapticFeedback.test.ts",
"chars": 1043,
"preview": "import { act, renderHook } from '@testing-library/react';\nimport useHapticFeedback from '../src/useHapticFeedback';\nimpo"
},
{
"path": "tests/useInitData.test.ts",
"chars": 234,
"preview": "import { renderHook } from '@testing-library/react';\nimport useCloudStorage from '../src/useCloudStorage';\n\n// TODO Напи"
},
{
"path": "tests/useReadTextFromClipboard.test.ts",
"chars": 696,
"preview": "import { renderHook } from '@testing-library/react';\nimport useReadTextFromClipboard from '../src/useReadTextFromClipboa"
},
{
"path": "tests/useScanQrPopup.test.ts",
"chars": 845,
"preview": "import { renderHook } from '@testing-library/react';\nimport useScanQrPopup from '../src/useScanQrPopup';\nimport { useWeb"
},
{
"path": "tests/useShowPopup.test.ts",
"chars": 909,
"preview": "import { renderHook } from '@testing-library/react';\nimport useShowPopup from '../src/useShowPopup';\nimport { useWebApp "
},
{
"path": "tests/useSwitchInlineQuery.test.ts",
"chars": 710,
"preview": "import { renderHook } from '@testing-library/react';\n\nimport { useWebApp } from '../src/core';\nimport useSwitchInlineQue"
},
{
"path": "tests/useThemeParams.test.ts",
"chars": 1603,
"preview": "import { act, renderHook } from '@testing-library/react';\n\nimport { useWebApp } from '../src/core';\nimport { WebApp } fr"
},
{
"path": "tests/utils.ts",
"chars": 552,
"preview": "import * as React from 'react';\nimport { ReactTestRenderer } from 'react-test-renderer';\nimport * as renderer from 'reac"
},
{
"path": "tsconfig.json",
"chars": 512,
"preview": "{\n \"include\": [\"src\", \"global.d.ts\"],\n \"exclude\": [\"tests\"],\n \"compilerOptions\": {\n \"skipLibCheck\": true,\n \"tar"
},
{
"path": "typedoc.json",
"chars": 1321,
"preview": "{\n \"entryPoints\": [\"src/index.ts\"],\n \"githubPages\": true,\n \"plugin\": [\"typedoc-plugin-markdown\"],\n \"out\": \"docs\",\n "
}
]
About this extraction
This page contains the full source code of the vkruglikov/react-telegram-web-app GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 93 files (135.7 KB), approximately 36.9k tokens, and a symbol index with 66 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.