Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve telegram mini app #1472

Merged
merged 14 commits into from
Jul 29, 2024
Merged
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"@cedelabs/widgets-universal": "^1.3.1",
"@metamask/detect-provider": "^2.0.0",
"@soramitsu/soraneo-wallet-web": "1.39.6",
"@tma.js/sdk": "^2.7.0",
"@walletconnect/ethereum-provider": "^2.13.3",
"@walletconnect/modal": "^2.6.2",
"core-js": "^3.37.1",
Expand Down Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<meta property="og:image:secure_url" content="https://polkaswap.io/<%= BASE_URL %>polkaswap-share-image.jpg" />
<meta property="og:image:width" content="1280" />
<meta property="og:image:height" content="630" />
<script src="https://telegram.org/js/telegram-web-app.js"></script>
</head>
<body>
<noscript>
Expand Down
14 changes: 3 additions & 11 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,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';

Expand All @@ -81,7 +80,7 @@ import { action, getter, mutation, state } from '@/store/decorators';
import { getMobileCssClasses, preloadFontFace, updateDocumentTitle } from '@/utils';
import type { NodesConnection } from '@/utils/connection';
import { calculateStorageUsagePercentage, clearLocalStorage } from '@/utils/storage';
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';
Expand Down Expand Up @@ -170,10 +169,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 {
Expand Down Expand Up @@ -261,11 +256,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);
}
tmaSdkService.init(data?.TG_BOT_URL);

await this.setApiKeys(data?.API_KEYS);
await this.setEthBridgeSettings(data.ETH_BRIDGE);
Expand Down Expand Up @@ -388,6 +379,7 @@ export default class App extends Mixins(mixins.TransactionMixin, NodeErrorMixin)
async beforeDestroy(): Promise<void> {
window.removeEventListener('localStorageUpdated', this.handleLocalStorageChange);
window.removeEventListener('resize', this.setResponsiveClassDebounced);
tmaSdkService.destroy();
await this.resetInternalSubscriptions();
await this.resetNetworkSubscriptions();
this.resetBlockNumberSubscription();
Expand Down
4 changes: 2 additions & 2 deletions src/components/App/Header/AppHeaderMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/store/settings/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ const mutations = defineMutations<SettingsState>()({
enableTMA(state): void {
state.isTMA = true;
},
disableTMA(state): void {
state.isTMA = false;
},
setTelegramBotUrl(state, url: Nullable<string>): void {
state.telegramBotUrl = url;
},
Expand Down
141 changes: 87 additions & 54 deletions src/utils/telegram.ts
Original file line number Diff line number Diff line change
@@ -1,100 +1,133 @@
import { api } from '@soramitsu/soraneo-wallet-web';
import {
setDebug,
initViewport,
initMiniApp,
initWeb,
isIframe,
retrieveLaunchParams,
initUtils,
type MiniApp,
type LaunchParams,
type Utils,
type Viewport,
} from '@tma.js/sdk';

import store from '@/store';

export class TmaSdk {
public static miniApp: MiniApp;
public static launchParams: LaunchParams;
public static viewport: Nullable<Viewport>;
private static utils: Utils;
enum HapticStatusValue {
success = 'success',
warning = 'warning',
error = 'error',
}

type HapticFeedbackStatus = keyof typeof HapticStatusValue;

export type HapticFeedbackBinding = 'light' | 'medium' | 'heavy' | 'rigid' | 'soft' | HapticFeedbackStatus;

const HapticNotificationTypes: string[] = Object.values(HapticStatusValue);

public static async init(botUrl?: string, isDebug = false): Promise<void> {
function isNotification(value: HapticFeedbackBinding): value is HapticFeedbackStatus {
return HapticNotificationTypes.includes(value);
}

function useHaptic(type: HapticFeedbackBinding): void {
const HapticFeedback = Telegram?.WebApp?.HapticFeedback;
if (!HapticFeedback) {
return;
}
try {
if (isNotification(type)) {
HapticFeedback?.notificationOccurred(type);
return;
}
HapticFeedback?.impactOccurred(type);
} catch (error) {
console.warn('[TMA]: useHapticFeedback', error);
}
}

class TmaSdk {
public init(botUrl?: string): void {
try {
// Initialize the app in the web version of Telegram
if (isIframe()) {
initWeb();
console.info('[TMA]: initTMA: Web version of Telegram');
// Check if the current platform is Telegram Mini App
const WebApp = Telegram?.WebApp;
if (!WebApp?.initData) {
console.info('[TMA]: Not a Telegram Mini App, skipping initialization');
return;
}
// Init viewport
await this.initViewport();
// Init mini app
const [miniApp] = initMiniApp();
this.miniApp = miniApp;
// Expand viewport to the full screen
WebApp?.expand?.();
// Disable vertical swipe if possible
WebApp?.disableVerticalSwipes?.();
store.commit.settings.enableTMA();
store.commit.wallet.account.setIsDesktop(true);
console.info('[TMA]: Mini app was initialized');
// Set theme
this.updateTheme();
// Enable debugging
setDebug(isDebug);
// Retrieve launch params
this.launchParams = retrieveLaunchParams();
console.info('[TMA]: Launch params were retrieved');
// Check the referrer
this.setReferrer(this.launchParams.startParam);
this.setReferrer(WebApp?.initDataUnsafe?.start_param);
// Set the Telegram bot URL
if (botUrl) {
store.commit.settings.setTelegramBotUrl(botUrl);
}
// Init utils
this.utils = initUtils();
console.info('[TMA]: Utils were initialized');
// Init haptic feedback
this.addHapticListener();
} catch (error) {
console.warn('[TMA]: init', error);
console.warn('[TMA]: disabling TMA mode because of the error:', error);
store.commit.settings.disableTMA();
store.commit.wallet.account.setIsDesktop(false);
}
}

/**
* **Should be used after `miniApp` init!**
*
* 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);
const WebApp = Telegram?.WebApp;
WebApp?.setHeaderColor(colorUtilityBody);
WebApp?.setBackgroundColor(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);
const desc = text ? encodeURIComponent(text) : text;
Telegram?.WebApp?.openLink(`https://t.me/share/url?url=${url}&text=${desc}`);
} catch (error) {
console.warn('[TMA]: shareLink', error);
}
}

private static async initViewport(): Promise<void> {
try {
const [viewport] = initViewport();
this.viewport = await viewport;
console.info('[TMA]: Viewport was initialized');
} catch (error) {
console.warn('[TMA]: initViewport', error);
public useHaptic(type: HapticFeedbackBinding): void {
useHaptic(type);
}

private onTouchEnd(event: TouchEvent): void {
const clickableSelectors = 'button, a, [data-clickable], .el-button, .clickable, [role="button"]';
const clickedElement = event.target as Nullable<HTMLElement>;

// Check if the clicked element matches any of the clickable selectors
if (clickedElement?.matches(clickableSelectors)) {
// Trigger Telegram WebApp HapticFeedback
useHaptic('soft');
}
}

private static setReferrer(referrerAddress?: string): void {
private addHapticListener(): void {
console.info('[TMA]: Haptic listener was added');
document.addEventListener('touchend', this.onTouchEnd);
}

private removeHapticListener(): void {
document.removeEventListener('touchend', this.onTouchEnd);
}

private setReferrer(referrerAddress?: string): void {
if (referrerAddress && api.validateAddress(referrerAddress)) {
store.commit.referrals.setStorageReferrer(referrerAddress);
console.info('[TMA]: Referrer was set', referrerAddress);
}
}

public destroy(): void {
this.removeHapticListener();
}
}

const tmaSdkService = new TmaSdk();

export { tmaSdkService };
4 changes: 2 additions & 2 deletions src/views/ReferralProgram.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"webpack-env",
"jest",
"node",
"element-ui"
"element-ui",
"telegram-web-app"
],
"paths": {
"@/*": [
Expand Down
36 changes: 29 additions & 7 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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==
Expand Down Expand Up @@ -3081,11 +3080,6 @@
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==

"@tootallnate/once@1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
Expand Down Expand Up @@ -3510,6 +3504,11 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==

"@types/telegram-web-app@^7.3.1":
version "7.3.1"
resolved "https://registry.yarnpkg.com/@types/telegram-web-app/-/telegram-web-app-7.3.1.tgz#9edf1c18674b29c5abcf8f699fb8f367f6dae8ae"
integrity sha512-88VN+61MM6R3G0hhaHGJR+hMAazmNcfg568cn2WcGWZU4iAjzmogWZTgmZmwfaexrCdZ6PLVwoiQRoOX5/Djcg==

"@types/tough-cookie@*":
version "4.0.2"
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397"
Expand Down Expand Up @@ -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"
Expand Down