diff --git a/packages/mobile/src/config/index.ts b/packages/mobile/src/config/index.ts index 54327d9ea..b9a9a952e 100644 --- a/packages/mobile/src/config/index.ts +++ b/packages/mobile/src/config/index.ts @@ -48,6 +48,7 @@ export type AppConfigVars = { batteryHost: string; batteryTestnetHost: string; + batteryRefundEndpoint: string; batteryMeanFees: string; batteryReservedAmount: string; batteryMaxInputAmount: string; @@ -109,6 +110,7 @@ const defaultConfig: Partial = { batteryHost: 'https://battery.tonkeeper.com', batteryTestnetHost: 'https://testnet-battery.tonkeeper.com', + batteryRefundEndpoint: 'https://battery-refund-app.vercel.app', batteryMeanFees: '0.0055', batteryReservedAmount: '0.3', batteryMaxInputAmount: '3', diff --git a/packages/mobile/src/core/DAppBrowser/DAppBrowser.tsx b/packages/mobile/src/core/DAppBrowser/DAppBrowser.tsx index c23197bc9..d72ed377a 100644 --- a/packages/mobile/src/core/DAppBrowser/DAppBrowser.tsx +++ b/packages/mobile/src/core/DAppBrowser/DAppBrowser.tsx @@ -1,6 +1,6 @@ import { useDeeplinking } from '$libs/deeplinking'; import { openDAppsSearch } from '$navigation'; -import { getCorrectUrl, getSearchQuery, getUrlWithoutTonProxy, isIOS } from '$utils'; +import { getCorrectUrl, getSearchQuery, getUrlWithoutTonProxy } from '$utils'; import React, { FC, memo, useCallback, useState } from 'react'; import { Linking, StatusBar, useWindowDimensions } from 'react-native'; import { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; @@ -16,10 +16,12 @@ import { useDAppBridge } from './hooks/useDAppBridge'; import { useWallet } from '@tonkeeper/shared/hooks'; import { Address } from '@tonkeeper/shared/Address'; import { config } from '$config'; -import { Screen, isAndroid, useTheme } from '@tonkeeper/uikit'; +import { Screen, useTheme } from '@tonkeeper/uikit'; export interface DAppBrowserProps { url: string; + persistentQueryParams?: string; + disableSearchBar?: boolean; } const TONKEEPER_UTM = 'utm_source=tonkeeper'; @@ -30,12 +32,31 @@ const addUtmToUrl = (url: string) => { return `${url}${startChar}${TONKEEPER_UTM}`; }; +const addPersistentQueryParamsIfNeeded = ( + url: string, + persistentQueryParams?: string, +) => { + if (!persistentQueryParams) { + return url; + } + const startChar = url.includes('?') ? '&' : '?'; + + return `${url}${startChar}${persistentQueryParams}`; +}; + +const removeQueryParamsFromUrl = (url: string, params?: string) => { + if (!params) { + return url; + } + return url.replace(new RegExp(`[?|&]${params}`), ''); +}; + const removeUtmFromUrl = (url: string) => { - return url.replace(new RegExp(`[?|&]${TONKEEPER_UTM}`), ''); + return removeQueryParamsFromUrl(url, TONKEEPER_UTM); }; const DAppBrowserComponent: FC = (props) => { - const { url: initialUrl } = props; + const { url: initialUrl, persistentQueryParams } = props; const wallet = useWallet(); const walletAddress = wallet @@ -49,7 +70,9 @@ const DAppBrowserComponent: FC = (props) => { const [currentUrl, setCurrentUrl] = useState(getCorrectUrl(initialUrl)); - const [webViewSource, setWebViewSource] = useState({ uri: addUtmToUrl(currentUrl) }); + const [webViewSource, setWebViewSource] = useState({ + uri: addPersistentQueryParamsIfNeeded(addUtmToUrl(currentUrl), persistentQueryParams), + }); const app = useAppInfo(walletAddress, currentUrl); @@ -86,9 +109,16 @@ const DAppBrowserComponent: FC = (props) => { progress.value = withTiming(e.nativeEvent.progress); setTitle(e.nativeEvent.title); - setCurrentUrl(getUrlWithoutTonProxy(removeUtmFromUrl(e.nativeEvent.url))); + setCurrentUrl( + getUrlWithoutTonProxy( + removeQueryParamsFromUrl( + removeUtmFromUrl(e.nativeEvent.url), + persistentQueryParams, + ), + ), + ); }, - [progress], + [persistentQueryParams, progress], ); const handleNavigationStateChange = useCallback((e: WebViewNavigation) => { @@ -96,8 +126,14 @@ const DAppBrowserComponent: FC = (props) => { }, []); const openUrl = useCallback( - (url: string) => setWebViewSource({ uri: addUtmToUrl(getCorrectUrl(url)) }), - [], + (url: string) => + setWebViewSource({ + uri: addPersistentQueryParamsIfNeeded( + addUtmToUrl(getCorrectUrl(url)), + persistentQueryParams, + ), + }), + [persistentQueryParams], ); const handleOpenExternalLink = useCallback( @@ -150,6 +186,7 @@ const DAppBrowserComponent: FC = (props) => { void; disconnect: () => Promise; unsubscribeFromNotifications: () => Promise; + disableSearchBar?: boolean; } const BrowserNavBarComponent: FC = (props) => { @@ -147,7 +148,7 @@ const BrowserNavBarComponent: FC = (props) => { ) : null} - + {title || '...'} {isConnected ? ( diff --git a/packages/mobile/src/navigation/helper.ts b/packages/mobile/src/navigation/helper.ts index 8cc975e76..e290db792 100644 --- a/packages/mobile/src/navigation/helper.ts +++ b/packages/mobile/src/navigation/helper.ts @@ -48,8 +48,12 @@ export function openDAppsSearch( navigate(AppStackRouteNames.DAppsSearch, { initialQuery, onOpenUrl }); } -export function openDAppBrowser(url: string) { - const params = { url }; +export function openDAppBrowser( + url: string, + persistentQueryParams?: string, + disableSearchBar?: boolean, +) { + const params = { url, persistentQueryParams, disableSearchBar }; if (getCurrentRoute()?.name === AppStackRouteNames.DAppsSearch) { replace(AppStackRouteNames.DAppBrowser, params); } else { diff --git a/packages/shared/components/RefillBattery/RestorePurchases.tsx b/packages/shared/components/RefillBattery/RestorePurchases.tsx index ce6339bf7..2568c699b 100644 --- a/packages/shared/components/RefillBattery/RestorePurchases.tsx +++ b/packages/shared/components/RefillBattery/RestorePurchases.tsx @@ -4,6 +4,8 @@ import { t } from '../../i18n'; import { getPendingPurchasesIOS, finishTransaction } from 'react-native-iap'; import { Platform } from 'react-native'; import { tk } from '@tonkeeper/mobile/src/wallet'; +import { openDAppBrowser } from '@tonkeeper/mobile/src/navigation'; +import { config } from '@tonkeeper/mobile/src/config'; export const RestorePurchases = memo(() => { const handleRestorePurchases = useCallback(async () => { @@ -45,19 +47,51 @@ export const RestorePurchases = memo(() => { } }, []); + const openRefundsDApp = useCallback(() => { + openDAppBrowser( + config.get('batteryRefundEndpoint'), + `token=${encodeURIComponent(tk.wallet.tonProof.tonProofToken)}` + + `&testnet=${tk.wallet.isTestnet}`, + true, + ); + }, []); + return ( - - {t('battery.packages.disclaimer')}{' '} + <> + + {t('battery.packages.disclaimer')}{' '} + + {t('battery.packages.restore')} + + . + - {t('battery.packages.restore')} + + {t('battery.packages.refund')} + + . - . - + ); }); diff --git a/packages/shared/i18n/locales/tonkeeper/en.json b/packages/shared/i18n/locales/tonkeeper/en.json index c2bd0213c..550066899 100644 --- a/packages/shared/i18n/locales/tonkeeper/en.json +++ b/packages/shared/i18n/locales/tonkeeper/en.json @@ -211,6 +211,7 @@ }, "disclaimer": "One charge covers the average transaction fee. Some transactions may cost more.", "restore": "Restore Purchases", + "refund": "Request a refund", "buy": "Buy", "ok": "OK", "refilled": "Your battery is charged" diff --git a/packages/shared/i18n/locales/tonkeeper/ru-RU.json b/packages/shared/i18n/locales/tonkeeper/ru-RU.json index c3c62446a..ab606a3f4 100644 --- a/packages/shared/i18n/locales/tonkeeper/ru-RU.json +++ b/packages/shared/i18n/locales/tonkeeper/ru-RU.json @@ -1201,6 +1201,7 @@ }, "disclaimer": "Один заряд покрывает среднюю комиссию за транзакцию. Некоторые транзакции могут стоить дороже.", "restore": "Восстановить покупки", + "refund": "Запросить возврат", "ok": "OK", "refilled": "Ваша батарейка заряжена" }