From bcfa4411643ae8f996359733475f0349a5b53565 Mon Sep 17 00:00:00 2001 From: Stefan Popov Date: Thu, 18 Jul 2024 19:39:57 +0200 Subject: [PATCH 1/9] Replace TG library with new one --- package.json | 2 +- src/App.vue | 13 ++-------- src/utils/telegram.ts | 60 ++++++++++++++++++++++++++++++++----------- yarn.lock | 34 +++++++++++++++++++----- 4 files changed, 76 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 899806d1b..4b10a9f59 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@cedelabs/widgets-universal": "^1.3.1", "@metamask/detect-provider": "^2.0.0", "@soramitsu/soraneo-wallet-web": "1.39.2", - "@tma.js/sdk": "^2.7.0", + "@telegram-apps/sdk": "^1.1.3", "@walletconnect/ethereum-provider": "^2.13.3", "@walletconnect/modal": "^2.6.2", "core-js": "^3.37.1", diff --git a/src/App.vue b/src/App.vue index c273156bc..b1a89f6f4 100644 --- a/src/App.vue +++ b/src/App.vue @@ -52,7 +52,6 @@ import { initWallet, waitForCore, } from '@soramitsu/soraneo-wallet-web'; -import { isTMA } from '@tma.js/sdk'; import debounce from 'lodash/debounce'; import { Component, Mixins, Watch } from 'vue-property-decorator'; @@ -68,7 +67,7 @@ import router, { goTo, lazyComponent } from '@/router'; import { action, getter, mutation, state } from '@/store/decorators'; import { getMobileCssClasses, preloadFontFace, updateDocumentTitle } from '@/utils'; import type { NodesConnection } from '@/utils/connection'; -import { TmaSdk } from '@/utils/telegram'; +import { tmaSdkService } from '@/utils/telegram'; import type { FeatureFlags } from './store/settings/types'; import type { EthBridgeSettings, SubNetworkApps } from './store/web3/types'; @@ -155,10 +154,6 @@ export default class App extends Mixins(mixins.TransactionMixin, NodeErrorMixin) @state.wallet.transactions.isSignTxDialogVisible public isSignTxDialogVisible!: boolean; @mutation.wallet.transactions.setSignTxDialogVisibility public setSignTxDialogVisibility!: (flag: boolean) => void; - // [DESKTOP] To Enable Desktop - @mutation.wallet.account.setIsDesktop private setIsDesktop!: (v: boolean) => void; - // [TMA] To Enable TMA - @mutation.settings.enableTMA private enableTMA!: FnWithoutArgs; @Watch('assetsToNotifyQueue') private handleNotifyOnDeposit(whitelistAssetArray: WhitelistArrayItem[]): void { @@ -235,11 +230,7 @@ export default class App extends Mixins(mixins.TransactionMixin, NodeErrorMixin) } // To start running as Telegram Web App (desktop capabilities) - if (await isTMA()) { - this.enableTMA(); - this.setIsDesktop(true); - await TmaSdk.init(data?.TG_BOT_URL, data.NETWORK_TYPE === WALLET_CONSTS.SoraNetwork.Dev); - } + await tmaSdkService.init(data?.TG_BOT_URL, data.NETWORK_TYPE === WALLET_CONSTS.SoraNetwork.Dev); await this.setApiKeys(data?.API_KEYS); await this.setEthBridgeSettings(data.ETH_BRIDGE); diff --git a/src/utils/telegram.ts b/src/utils/telegram.ts index 8a00f87c2..344971061 100644 --- a/src/utils/telegram.ts +++ b/src/utils/telegram.ts @@ -7,35 +7,51 @@ import { isIframe, retrieveLaunchParams, initUtils, + initSwipeBehavior, + isTMA, type MiniApp, type LaunchParams, type Utils, type Viewport, -} from '@tma.js/sdk'; + type SwipeBehavior, +} from '@telegram-apps/sdk'; import store from '@/store'; -export class TmaSdk { - public static miniApp: MiniApp; - public static launchParams: LaunchParams; - public static viewport: Nullable; - private static utils: Utils; +class TmaSdk { + public miniApp: Nullable; + public viewport: Nullable; + public launchParams: Nullable; + public swipeBehavior: Nullable; + private utils: Nullable; - public static async init(botUrl?: string, isDebug = false): Promise { + public async init(botUrl?: string, isDebug = false): Promise { try { + // Check if the current platform is Telegram Mini App + const isTma = await isTMA(); + if (!isTma) { + console.info('[TMA]: initTMA: Not a Telegram Mini App'); + return; + } + store.commit.settings.enableTMA(); + store.commit.wallet.account.setIsDesktop(true); // Initialize the app in the web version of Telegram if (isIframe()) { initWeb(); console.info('[TMA]: initTMA: Web version of Telegram'); } - // Init viewport + // Init viewport and expand it to the full screen await this.initViewport(); + this.viewport?.expand(); // Init mini app const [miniApp] = initMiniApp(); this.miniApp = miniApp; console.info('[TMA]: Mini app was initialized'); // Set theme this.updateTheme(); + // Init swipe behavior and disable vertical swipe + this.initSwipeBehavior(); + this.swipeBehavior?.disableVerticalSwipe(); // Enable debugging setDebug(isDebug); // Retrieve launch params @@ -60,28 +76,28 @@ export class TmaSdk { * * Update the theme of the Telegram Mini App using `var(--s-color-utility-body)` */ - public static updateTheme(): void { + public updateTheme(): void { try { const colorUtilityBody = (getComputedStyle(document.documentElement).getPropertyValue('--s-color-utility-body') as `#${string}`) || '#f7f3f4'; // Default color - this.miniApp.setHeaderColor(colorUtilityBody); - this.miniApp.setBgColor(colorUtilityBody); + this.miniApp?.setHeaderColor(colorUtilityBody); + this.miniApp?.setBgColor(colorUtilityBody); } catch (error) { console.warn('[TMA]: updateTheme', error); } } - public static shareLink(url: string, text?: string): void { + public shareLink(url: string, text?: string): void { try { - this.utils.shareURL(url, text ? encodeURIComponent(text) : text); + this.utils?.shareURL(url, text ? encodeURIComponent(text) : text); } catch (error) { console.warn('[TMA]: shareLink', error); } } - private static async initViewport(): Promise { + private async initViewport(): Promise { try { const [viewport] = initViewport(); this.viewport = await viewport; @@ -91,10 +107,24 @@ export class TmaSdk { } } - private static setReferrer(referrerAddress?: string): void { + private initSwipeBehavior(): void { + try { + const [swipeBehavior] = initSwipeBehavior(); + this.swipeBehavior = swipeBehavior; + console.info('[TMA]: Swipe behavior was initialized'); + } catch (error) { + console.warn('[TMA]: initSwipeBehavior', error); + } + } + + private setReferrer(referrerAddress?: string): void { if (referrerAddress && api.validateAddress(referrerAddress)) { store.commit.referrals.setStorageReferrer(referrerAddress); console.info('[TMA]: Referrer was set', referrerAddress); } } } + +const tmaSdkService = new TmaSdk(); + +export { tmaSdkService }; diff --git a/yarn.lock b/yarn.lock index 3fbec4a5b..92b2586fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1970,8 +1970,7 @@ "@polkadot/util-crypto" "^10.4.2" rxjs "^7.8.0" -"@polkadot/api@11.2.1", "polkadotApi@npm:@polkadot/api@11.2.1": - name polkadotApi +"@polkadot/api@11.2.1": version "11.2.1" resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-11.2.1.tgz#b19a8e22367703333e71f3613095f76f0dbbca70" integrity sha512-NwcWadMt+mrJ3T7RuwpnaIYtH4x0eix+GiKRtLMtIO32uAfhwVyMnqvLtxDxa4XDJ/es2rtSMYG+t0b1BTM+xQ== @@ -3081,10 +3080,10 @@ dependencies: defer-to-connect "^2.0.0" -"@tma.js/sdk@^2.7.0": - version "2.7.0" - resolved "https://registry.yarnpkg.com/@tma.js/sdk/-/sdk-2.7.0.tgz#1ae1fa6e5b21f6a5d959328ba84096bf5e6cc910" - integrity sha512-LRuJdoEbAqKmSYccvXx5emSnye2je8oIbBVPtV+BmcNoa/dCZ0gFVkaeuyBqzroGtfGDyGO/WebU54ie5MKmCQ== +"@telegram-apps/sdk@^1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@telegram-apps/sdk/-/sdk-1.1.3.tgz#e536b59968804ca3c29eacd7c29749221c6a32df" + integrity sha512-gvcXNttbCouDNTxjDUbO5FS/BdaHYO0ibSddQxiYitShPIDZDxGon0Eh3OSsgUIVPSQSNdXqkXaeqYgA+jeY0A== "@tootallnate/once@1": version "1.1.2" @@ -12133,6 +12132,29 @@ pngjs@^5.0.0: resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb" integrity sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw== +"polkadotApi@npm:@polkadot/api@11.2.1": + version "11.2.1" + resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-11.2.1.tgz#b19a8e22367703333e71f3613095f76f0dbbca70" + integrity sha512-NwcWadMt+mrJ3T7RuwpnaIYtH4x0eix+GiKRtLMtIO32uAfhwVyMnqvLtxDxa4XDJ/es2rtSMYG+t0b1BTM+xQ== + dependencies: + "@polkadot/api-augment" "11.2.1" + "@polkadot/api-base" "11.2.1" + "@polkadot/api-derive" "11.2.1" + "@polkadot/keyring" "^12.6.2" + "@polkadot/rpc-augment" "11.2.1" + "@polkadot/rpc-core" "11.2.1" + "@polkadot/rpc-provider" "11.2.1" + "@polkadot/types" "11.2.1" + "@polkadot/types-augment" "11.2.1" + "@polkadot/types-codec" "11.2.1" + "@polkadot/types-create" "11.2.1" + "@polkadot/types-known" "11.2.1" + "@polkadot/util" "^12.6.2" + "@polkadot/util-crypto" "^12.6.2" + eventemitter3 "^5.0.1" + rxjs "^7.8.1" + tslib "^2.6.2" + portfinder@^1.0.26: version "1.0.32" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.32.tgz#2fe1b9e58389712429dc2bea5beb2146146c7f81" From 1adf0097904fc0fdfd750d925adcf47a81b5026c Mon Sep 17 00:00:00 2001 From: Stefan Popov Date: Thu, 18 Jul 2024 22:16:48 +0200 Subject: [PATCH 2/9] Add haptic feedback support --- src/utils/telegram.ts | 46 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/utils/telegram.ts b/src/utils/telegram.ts index 344971061..c9bb22415 100644 --- a/src/utils/telegram.ts +++ b/src/utils/telegram.ts @@ -9,21 +9,34 @@ import { initUtils, initSwipeBehavior, isTMA, + initHapticFeedback, type MiniApp, type LaunchParams, type Utils, type Viewport, type SwipeBehavior, + type HapticFeedback, + type ImpactHapticFeedbackStyle, + type NotificationHapticFeedbackType, } from '@telegram-apps/sdk'; import store from '@/store'; +export type HapticFeedbackBinding = ImpactHapticFeedbackStyle | NotificationHapticFeedbackType; + +const HapticNotificationTypes = ['success', 'warning', 'error']; + +function isNotification(value: HapticFeedbackBinding): value is NotificationHapticFeedbackType { + return HapticNotificationTypes.includes(value); +} + class TmaSdk { public miniApp: Nullable; public viewport: Nullable; public launchParams: Nullable; public swipeBehavior: Nullable; private utils: Nullable; + private hapticFeedback: Nullable; public async init(botUrl?: string, isDebug = false): Promise { try { @@ -63,6 +76,8 @@ class TmaSdk { if (botUrl) { store.commit.settings.setTelegramBotUrl(botUrl); } + // Init haptic feedback + this.initHapticFeedback(); // Init utils this.utils = initUtils(); console.info('[TMA]: Utils were initialized'); @@ -97,6 +112,27 @@ class TmaSdk { } } + public useHaptic(type: HapticFeedbackBinding): void { + if (!this.hapticFeedback) { + return; + } + try { + if (isNotification(type)) { + this.hapticFeedback?.notificationOccurred(type); + return; + } + if (this.launchParams?.platform.startsWith('android')) { + // 'android' | 'android_x' + // Android doesn't support different types of haptic feedback except notifications + this.hapticFeedback?.notificationOccurred('success'); + } else { + this.hapticFeedback?.impactOccurred(type); + } + } catch (error) { + console.warn('[TMA]: useHapticFeedback', error); + } + } + private async initViewport(): Promise { try { const [viewport] = initViewport(); @@ -117,6 +153,16 @@ class TmaSdk { } } + private initHapticFeedback(): void { + try { + const hapticFeedback = initHapticFeedback(); + this.hapticFeedback = hapticFeedback; + console.info('[TMA]: Haptic feedback was initialized'); + } catch (error) { + console.warn('[TMA]: initHapticFeedback', error); + } + } + private setReferrer(referrerAddress?: string): void { if (referrerAddress && api.validateAddress(referrerAddress)) { store.commit.referrals.setStorageReferrer(referrerAddress); From ada48ac9c8bd9751d41559e456b67fae20726bc8 Mon Sep 17 00:00:00 2001 From: RustemYuzlibaev Date: Tue, 23 Jul 2024 10:15:13 +0300 Subject: [PATCH 3/9] rename service --- src/views/ReferralProgram.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/ReferralProgram.vue b/src/views/ReferralProgram.vue index a232660a1..bb6e4e498 100644 --- a/src/views/ReferralProgram.vue +++ b/src/views/ReferralProgram.vue @@ -181,7 +181,7 @@ import type { ReferrerRewards } from '@/indexer/queries/referrals'; import router, { lazyView } from '@/router'; import { action, getter, mutation, state } from '@/store/decorators'; import { formatAddress } from '@/utils'; -import { TmaSdk } from '@/utils/telegram'; +import { tmaSdkService } from '@/utils/telegram'; import type { CodecString } from '@sora-substrate/util'; import type { AccountAsset } from '@sora-substrate/util/build/assets/types'; @@ -410,7 +410,7 @@ export default class ReferralProgram extends Mixins( } const botUrl = `${this.telegramBotUrl}/app?startapp=${this.account.address}`; - TmaSdk.shareLink(botUrl, this.t('referralProgram.welcomeMessage')); + tmaSdkService.shareLink(botUrl, this.t('referralProgram.welcomeMessage')); } destroyed(): void { From 45e4bfb0b09b5dcf47332da8dd10fdeb1c3be326 Mon Sep 17 00:00:00 2001 From: RustemYuzlibaev Date: Tue, 23 Jul 2024 10:22:46 +0300 Subject: [PATCH 4/9] Update AppHeaderMenu.vue --- src/components/App/Header/AppHeaderMenu.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/App/Header/AppHeaderMenu.vue b/src/components/App/Header/AppHeaderMenu.vue index 016d1c174..4d721b9bd 100644 --- a/src/components/App/Header/AppHeaderMenu.vue +++ b/src/components/App/Header/AppHeaderMenu.vue @@ -43,7 +43,7 @@ import { Component, Mixins } from 'vue-property-decorator'; import TranslationMixin from '@/components/mixins/TranslationMixin'; import { getter, mutation, state } from '@/store/decorators'; -import { TmaSdk } from '@/utils/telegram'; +import { tmaSdkService } from '@/utils/telegram'; import type { Currency } from '@soramitsu/soraneo-wallet-web/lib/types/currency'; @@ -186,7 +186,7 @@ export default class AppHeaderMenu extends Mixins(TranslationMixin) { case HeaderMenuType.Theme: await switchTheme(); await this.$nextTick(); - TmaSdk.updateTheme(); + tmaSdkService.updateTheme(); break; case HeaderMenuType.Language: this.setLanguageDialogVisibility(true); From 98e0c4bfcfcb82998673018b65ee5becd19c3135 Mon Sep 17 00:00:00 2001 From: Stefan Popov Date: Sat, 27 Jul 2024 16:25:18 +0200 Subject: [PATCH 5/9] Add haptic global listener --- src/App.vue | 1 + src/utils/telegram.ts | 30 +++++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/App.vue b/src/App.vue index b1a89f6f4..97a2296ad 100644 --- a/src/App.vue +++ b/src/App.vue @@ -352,6 +352,7 @@ export default class App extends Mixins(mixins.TransactionMixin, NodeErrorMixin) async beforeDestroy(): Promise { window.removeEventListener('resize', this.setResponsiveClassDebounced); + tmaSdkService.destroy(); await this.resetInternalSubscriptions(); await this.resetNetworkSubscriptions(); this.resetBlockNumberSubscription(); diff --git a/src/utils/telegram.ts b/src/utils/telegram.ts index c9bb22415..4d77a747f 100644 --- a/src/utils/telegram.ts +++ b/src/utils/telegram.ts @@ -77,7 +77,7 @@ class TmaSdk { store.commit.settings.setTelegramBotUrl(botUrl); } // Init haptic feedback - this.initHapticFeedback(); + this.initHapticService(); // Init utils this.utils = initUtils(); console.info('[TMA]: Utils were initialized'); @@ -133,6 +133,25 @@ class TmaSdk { } } + private onTouchEnd(event: TouchEvent): void { + const clickableSelectors = 'button, a, [data-clickable], .el-button, .clickable, [role="button"]'; // Define your clickable selectors + const clickedElement = event.target as Nullable; + + // Check if the clicked element matches any of the clickable selectors + if (clickedElement?.matches(clickableSelectors)) { + // Trigger Telegram WebApp HapticFeedback + this.useHaptic('soft'); + } + } + + private addHapticListener(): void { + document.addEventListener('touchend', this.onTouchEnd); + } + + private removeHapticListener(): void { + document.removeEventListener('touchend', this.onTouchEnd); + } + private async initViewport(): Promise { try { const [viewport] = initViewport(); @@ -153,13 +172,14 @@ class TmaSdk { } } - private initHapticFeedback(): void { + private initHapticService(): void { try { const hapticFeedback = initHapticFeedback(); this.hapticFeedback = hapticFeedback; + this.addHapticListener(); console.info('[TMA]: Haptic feedback was initialized'); } catch (error) { - console.warn('[TMA]: initHapticFeedback', error); + console.warn('[TMA]: initHapticService', error); } } @@ -169,6 +189,10 @@ class TmaSdk { console.info('[TMA]: Referrer was set', referrerAddress); } } + + public destroy(): void { + this.removeHapticListener(); + } } const tmaSdkService = new TmaSdk(); From e3aa23d15387610d98debdebc48083d5d9f64935 Mon Sep 17 00:00:00 2001 From: Stefan Popov Date: Sat, 27 Jul 2024 16:25:58 +0200 Subject: [PATCH 6/9] Update App.vue --- src/App.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.vue b/src/App.vue index 9b60ff364..1a45926a7 100644 --- a/src/App.vue +++ b/src/App.vue @@ -79,8 +79,8 @@ import router, { goTo, lazyComponent } from '@/router'; import { action, getter, mutation, state } from '@/store/decorators'; import { getMobileCssClasses, preloadFontFace, updateDocumentTitle } from '@/utils'; import type { NodesConnection } from '@/utils/connection'; -import { tmaSdkService } from '@/utils/telegram'; import { calculateStorageUsagePercentage, clearLocalStorage } from '@/utils/storage'; +import { tmaSdkService } from '@/utils/telegram'; import type { FeatureFlags } from './store/settings/types'; import type { EthBridgeSettings, SubNetworkApps } from './store/web3/types'; From 2c8cf5d9d9f3991b9bf9d514679b43d5f16a5dc0 Mon Sep 17 00:00:00 2001 From: Stefan Popov Date: Sat, 27 Jul 2024 20:07:37 +0200 Subject: [PATCH 7/9] Migrate to native Telegram API --- package.json | 2 +- public/index.html | 1 + src/App.vue | 2 +- src/store/settings/mutations.ts | 3 + src/utils/telegram.ts | 141 ++++++++------------------------ tsconfig.json | 3 +- yarn.lock | 10 +-- 7 files changed, 48 insertions(+), 114 deletions(-) diff --git a/package.json b/package.json index da2081d6d..2e487a09a 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "@cedelabs/widgets-universal": "^1.3.1", "@metamask/detect-provider": "^2.0.0", "@soramitsu/soraneo-wallet-web": "1.39.6", - "@telegram-apps/sdk": "^1.1.3", "@walletconnect/ethereum-provider": "^2.13.3", "@walletconnect/modal": "^2.6.2", "core-js": "^3.37.1", @@ -58,6 +57,7 @@ "@types/jsdom": "^21.1.7", "@types/lodash": "^4.17.5", "@types/node": "^20.14.5", + "@types/telegram-web-app": "^7.3.1", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "@vue/cli-plugin-babel": "5.0.8", diff --git a/public/index.html b/public/index.html index 6add82d8a..a9d5666f7 100644 --- a/public/index.html +++ b/public/index.html @@ -39,6 +39,7 @@ +