From 7a8296a8999a449de9d0186db38b89ca8507fc4f Mon Sep 17 00:00:00 2001 From: siandreev Date: Thu, 12 Dec 2024 18:00:25 +0100 Subject: [PATCH 01/22] fix: make widget storage not permanent (except swap settings) --- apps/web-swap-widget/src/libs/appSdk.ts | 4 +- apps/web-swap-widget/src/libs/storage.ts | 77 +++++++++++++++++++++++- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/apps/web-swap-widget/src/libs/appSdk.ts b/apps/web-swap-widget/src/libs/appSdk.ts index 5ddd73019..bed5f4c65 100644 --- a/apps/web-swap-widget/src/libs/appSdk.ts +++ b/apps/web-swap-widget/src/libs/appSdk.ts @@ -2,7 +2,7 @@ import { BaseApp } from '@tonkeeper/core/dist/AppSdk'; import copyToClipboard from 'copy-to-clipboard'; import packageJson from '../../package.json'; import { disableScroll, enableScroll, getScrollbarWidth } from './scroll'; -import { BrowserStorage } from './storage'; +import { SwapWidgetStorage } from './storage'; function iOS() { return ( @@ -16,7 +16,7 @@ function iOS() { export class BrowserAppSdk extends BaseApp { constructor() { - super(new BrowserStorage()); + super(new SwapWidgetStorage()); } copyToClipboard = (value: string, notification?: string) => { diff --git a/apps/web-swap-widget/src/libs/storage.ts b/apps/web-swap-widget/src/libs/storage.ts index 1ee17aba9..4297080a5 100644 --- a/apps/web-swap-widget/src/libs/storage.ts +++ b/apps/web-swap-widget/src/libs/storage.ts @@ -1,6 +1,46 @@ +// eslint-disable-next-line max-classes-per-file import { IStorage } from '@tonkeeper/core/dist/Storage'; +import { AppKey } from '@tonkeeper/core/dist/Keys'; -export class BrowserStorage implements IStorage { +export class SwapWidgetStorage implements IStorage { + private permanentStorage = new BrowserPermanentStorage(); + + private temporaryStorage = new BrowserTemporaryStorage(); + + private storageByKey = (key: string) => { + if (key === AppKey.SWAP_OPTIONS) { + return this.permanentStorage; + } + + return this.temporaryStorage; + }; + + get = async (key: string) => { + return this.storageByKey(key).get(key); + }; + + set = async (key: string, payload: R) => { + return this.storageByKey(key).set(key, payload); + }; + + setBatch = async >(values: V) => { + Object.entries(values).forEach(([key, payload]) => { + this.set(key, payload); + }); + return values; + }; + + delete = async (key: string) => { + return this.storageByKey(key).delete(key); + }; + + clear = async () => { + await this.temporaryStorage.clear(); + await this.permanentStorage.clear(); + }; +} + +class BrowserPermanentStorage implements IStorage { prefix = 'tonkeeper-swap-widget'; get = async (key: string) => { @@ -34,3 +74,38 @@ export class BrowserStorage implements IStorage { localStorage.clear(); }; } + +class BrowserTemporaryStorage implements IStorage { + private storage = new Map(); + + get = async (key: string) => { + const value = this.storage.get(key); + if (value == null) { + return null; + } + + return value as R; + }; + + set = async (key: string, payload: R) => { + this.storage.set(key, payload); + return payload; + }; + + setBatch = async >(values: V) => { + Object.entries(values).forEach(([key, payload]) => { + this.storage.set(key, payload); + }); + return values; + }; + + delete = async (key: string) => { + const payload = this.storage.get(key); + this.storage.delete(key); + return payload as R | null; + }; + + clear = async () => { + this.storage.clear(); + }; +} From 2947f5392d94fd902840429c503d3cbf5906800c Mon Sep 17 00:00:00 2001 From: siandreev Date: Thu, 12 Dec 2024 18:19:53 +0100 Subject: [PATCH 02/22] fix: i18n query params for widget --- apps/web-swap-widget/src/App.tsx | 42 +++++++++++++++++++++++++++++++- apps/web-swap-widget/src/i18n.ts | 31 +++++++++++------------ 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/apps/web-swap-widget/src/App.tsx b/apps/web-swap-widget/src/App.tsx index 1a32849cc..9c8b5a350 100644 --- a/apps/web-swap-widget/src/App.tsx +++ b/apps/web-swap-widget/src/App.tsx @@ -36,6 +36,7 @@ import { useAccountsStorage } from '@tonkeeper/uikit/dist/hooks/useStorage'; import { AccountTonWatchOnly } from '@tonkeeper/core/dist/entries/account'; import { getTonkeeperInjectionContext } from './libs/tonkeeper-injection-context'; import { Address } from '@ton/core'; +import { defaultLanguage } from './i18n'; const queryClient = new QueryClient({ defaultOptions: { @@ -49,13 +50,52 @@ const queryClient = new QueryClient({ const sdk = new BrowserAppSdk(); const TARGET_ENV = 'swap-widget-web'; +window.tonkeeperStonfi = { + address: 'UQD2NmD_lH5f5u1Kj3KfGyTvhZSX0Eg6qp2a5IQUKXxOGzCi', + sendTransaction: async params => { + console.log(params); + return 'boc'; + }, + close: () => { + console.log('close'); + } +}; + +const queryParams = new URLSearchParams(new URL(window.location.href).search); + +const queryParamLangKey = (supportedLanguages: string[]) => { + let key = queryParams.get('lang'); + + if (!key) { + return undefined; + } + + if (supportedLanguages.includes(key)) { + return key; + } + + if (key.includes('_')) { + key = key.split('_')[0].toLowerCase(); + + return supportedLanguages.includes(key) ? key : undefined; + } +}; + export const App: FC = () => { + const languages = (import.meta.env.VITE_APP_LOCALES ?? defaultLanguage).split(','); + const queryParamsLang = queryParamLangKey(languages); + const { t: tSimple, i18n } = useTranslation(); + useEffect(() => { + if (queryParamsLang && queryParamsLang !== defaultLanguage) { + i18n.reloadResources(queryParamsLang).then(() => i18n.changeLanguage(queryParamsLang)); + } + }, []); + const t = useTWithReplaces(tSimple); const translation = useMemo(() => { - const languages = (import.meta.env.VITE_APP_LOCALES ?? 'en').split(','); const client: I18nContext = { t, i18n: { diff --git a/apps/web-swap-widget/src/i18n.ts b/apps/web-swap-widget/src/i18n.ts index 58fede7d7..ca00d1a78 100644 --- a/apps/web-swap-widget/src/i18n.ts +++ b/apps/web-swap-widget/src/i18n.ts @@ -4,20 +4,21 @@ import LanguageDetector from 'i18next-browser-languagedetector'; import Backend from 'i18next-http-backend'; import { initReactI18next } from 'react-i18next'; -i18n - .use(Backend) - .use(LanguageDetector) - .use(initReactI18next) // passes i18n down to react-i18next - .init({ - resources, - debug: false, - lng: 'en', // language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources - // you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage - // if you're using a language detector, do not define the lng option - fallbackLng: 'en', - interpolation: { - escapeValue: false, // react already safes from xss - }, - }); +export const defaultLanguage = 'en'; + +i18n.use(Backend) + .use(LanguageDetector) + .use(initReactI18next) // passes i18n down to react-i18next + .init({ + resources, + debug: false, + lng: defaultLanguage, // language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources + // you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage + // if you're using a language detector, do not define the lng option + fallbackLng: defaultLanguage, + interpolation: { + escapeValue: false // react already safes from xss + } + }); export default i18n; From 6202bb1163ebe4bf87a98fd7d8119cfa1a3f4afe Mon Sep 17 00:00:00 2001 From: siandreev Date: Thu, 12 Dec 2024 18:29:25 +0100 Subject: [PATCH 03/22] fix: swap tx info tooltips --- .../uikit/src/components/shared/Accordion.tsx | 4 +-- .../components/swap/SwapTransactionInfo.tsx | 30 ++----------------- 2 files changed, 4 insertions(+), 30 deletions(-) diff --git a/packages/uikit/src/components/shared/Accordion.tsx b/packages/uikit/src/components/shared/Accordion.tsx index d9d1d75ec..f452f99ff 100644 --- a/packages/uikit/src/components/shared/Accordion.tsx +++ b/packages/uikit/src/components/shared/Accordion.tsx @@ -44,9 +44,9 @@ export const Accordion: FC { clearTimeout(timeoutRef.current); if (isOpened) { - setIsAnimationCompleted(false); - } else { timeoutRef.current = setTimeout(() => setIsAnimationCompleted(true), 200); + } else { + setIsAnimationCompleted(false); } }, [isOpened]); diff --git a/packages/uikit/src/components/swap/SwapTransactionInfo.tsx b/packages/uikit/src/components/swap/SwapTransactionInfo.tsx index a8fb256b2..0ce2bf2bd 100644 --- a/packages/uikit/src/components/swap/SwapTransactionInfo.tsx +++ b/packages/uikit/src/components/swap/SwapTransactionInfo.tsx @@ -1,5 +1,5 @@ import { css, styled } from 'styled-components'; -import { Body2Class, Body3 } from '../Text'; +import { Body2Class, Body3, Body3Class } from "../Text"; import { IconButton } from '../fields/IconButton'; import { useState } from 'react'; import { ChevronDownIcon, InfoCircleIcon } from '../Icon'; @@ -33,32 +33,6 @@ const TxInfoHeader = styled.div` } `; -const AccordionContent = styled.div` - transform: translateY(-100%); - visibility: hidden; - transition: transform 0.2s ease-in-out, visibility 0.2s ease-in-out; -`; - -const AccordionAnimation = styled.div<{ isOpened: boolean; animationCompleted: boolean }>` - display: grid; - grid-template-rows: ${p => (p.isOpened ? '1fr' : '0fr')}; - overflow: ${p => (p.animationCompleted && p.isOpened ? 'visible' : 'hidden')}; - transition: grid-template-rows 0.2s ease-in-out; - - ${AccordionContent} { - ${p => - p.isOpened && - css` - transform: translateY(0); - visibility: visible; - `} - } -`; - -const AccordionBody = styled.div` - min-height: 0; -`; - const AccordionButton = styled(IconButton)<{ isOpened: boolean }>` transform: ${p => (p.isOpened ? 'rotate(180deg)' : 'rotate(0deg)')}; transition: transform 0.2s ease-in-out; @@ -89,7 +63,7 @@ const Tooltip = styled.div<{ placement: 'top' | 'bottom' }>` background-color: ${p => p.theme.backgroundContentTint}; padding: 8px 12px; ${BorderSmallResponsive}; - ${Body2Class}; + ${Body3Class}; ${p => p.placement === 'top' From 4c82d7b39a2c8dd7eab45eaec57965f18b6fcceb Mon Sep 17 00:00:00 2001 From: siandreev Date: Thu, 12 Dec 2024 19:01:03 +0100 Subject: [PATCH 04/22] fix: tokens from swap list open explorer button --- packages/core/src/tonkeeperApi/tonendpoint.ts | 2 +- .../src/components/shared/ExternalLink.tsx | 53 +++++++++++++++++++ .../swap/tokens-list/SwapTokensList.tsx | 33 +++++------- 3 files changed, 68 insertions(+), 20 deletions(-) create mode 100644 packages/uikit/src/components/shared/ExternalLink.tsx diff --git a/packages/core/src/tonkeeperApi/tonendpoint.ts b/packages/core/src/tonkeeperApi/tonendpoint.ts index 9b8517201..be8590db6 100644 --- a/packages/core/src/tonkeeperApi/tonendpoint.ts +++ b/packages/core/src/tonkeeperApi/tonendpoint.ts @@ -5,7 +5,7 @@ import { DAppTrack } from '../service/urlService'; import { FetchAPI } from '../tonApiV2'; export interface BootParams { - platform: 'ios' | 'android' | 'web' | 'desktop' | 'tablet'; + platform: 'ios' | 'android' | 'web' | 'desktop' | 'tablet' | 'swap-widget-web'; lang: 'en' | 'ru' | string; build: string; // "2.8.0" network: Network; diff --git a/packages/uikit/src/components/shared/ExternalLink.tsx b/packages/uikit/src/components/shared/ExternalLink.tsx new file mode 100644 index 000000000..463efa86f --- /dev/null +++ b/packages/uikit/src/components/shared/ExternalLink.tsx @@ -0,0 +1,53 @@ +import { FC, MouseEventHandler, PropsWithChildren } from 'react'; +import { useAppPlatform } from '../../hooks/appContext'; +import { useAppSdk } from '../../hooks/appSdk'; +import styled from 'styled-components'; + +const AStyled = styled.a` + text-decoration: unset; + cursor: pointer; +`; + +const ButtonStyled = styled.button` + border: none; + outline: none; + background: transparent; + cursor: pointer; +`; + +export const ExternalLink: FC< + PropsWithChildren<{ + className?: string; + href: string; + onClick?: MouseEventHandler; + }> +> = ({ className, href, onClick, children }) => { + const platform = useAppPlatform(); + const sdk = useAppSdk(); + + if (platform === 'web' || platform === 'swap-widget-web') { + return ( + onClick?.(e)} + > + {children} + + ); + } + + return ( + { + onClick?.(e); + sdk.openPage(href); + }} + className={className} + > + {children} + + ); +}; diff --git a/packages/uikit/src/components/swap/tokens-list/SwapTokensList.tsx b/packages/uikit/src/components/swap/tokens-list/SwapTokensList.tsx index b76b383c5..d5f73c893 100644 --- a/packages/uikit/src/components/swap/tokens-list/SwapTokensList.tsx +++ b/packages/uikit/src/components/swap/tokens-list/SwapTokensList.tsx @@ -1,5 +1,5 @@ import { styled } from 'styled-components'; -import React, { FC, Fragment, useEffect, useRef, useState } from 'react'; +import React, { FC, Fragment, MouseEventHandler, useEffect, useRef, useState } from 'react'; import { Body2, Body3, Label2 } from '../../Text'; import { useAddUserCustomSwapAsset, @@ -11,9 +11,9 @@ import { useAppContext } from '../../../hooks/appContext'; import { isTon, TonAsset } from '@tonkeeper/core/dist/entries/crypto/asset/ton-asset'; import { LinkOutIcon, SpinnerIcon } from '../../Icon'; import { ConfirmImportNotification } from './ConfirmImportNotification'; -import { useAppSdk } from '../../../hooks/appSdk'; import { throttle } from '@tonkeeper/core/dist/utils/common'; import { useTranslation } from '../../../hooks/translation'; +import { ExternalLink } from '../../shared/ExternalLink'; const SwapTokensListWrapper = styled.div` overflow-y: auto; @@ -177,8 +177,7 @@ const TokenInfoLine = styled.div` } `; -const LinkOutIconWrapper = styled.div` - cursor: pointer; +const LinkOutIconWrapper = styled(ExternalLink)` &:hover { > svg { color: ${p => p.theme.iconSecondary}; @@ -204,22 +203,18 @@ const TokenListItem: FC<{ swapAsset: WalletSwapAsset; onClick: () => void }> = ( }) => { const isZeroBalance = swapAsset.assetAmount.relativeAmount.isZero(); const { fiat } = useAppContext(); - const sdk = useAppSdk(); - - const onOpenExplorer = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - let explorerUrl; - if (isTon(swapAsset.assetAmount.asset.address)) { - explorerUrl = 'https://tonviewer.com/price'; - } else { - explorerUrl = `https://tonviewer.com/${swapAsset.assetAmount.asset.address.toString({ - urlSafe: true - })}`; - } + let explorerUrl; + if (isTon(swapAsset.assetAmount.asset.address)) { + explorerUrl = 'https://tonviewer.com/price'; + } else { + explorerUrl = `https://tonviewer.com/${swapAsset.assetAmount.asset.address.toString({ + urlSafe: true + })}`; + } - sdk.openPage(explorerUrl); + const onClickExplorer: MouseEventHandler = e => { + e.stopPropagation(); }; return ( @@ -228,7 +223,7 @@ const TokenListItem: FC<{ swapAsset: WalletSwapAsset; onClick: () => void }> = ( {swapAsset.assetAmount.asset.symbol} - + From 70187822add5d48989149f07339bbe9227f65aa0 Mon Sep 17 00:00:00 2001 From: siandreev Date: Thu, 12 Dec 2024 19:13:12 +0100 Subject: [PATCH 05/22] fix: add advanced battery messages --- apps/web-swap-widget/src/App.tsx | 2 +- .../src/components/SwapWidgetPage.tsx | 16 ++++++++++++++-- .../src/libs/tonkeeper-injection-context.ts | 7 +++++++ packages/uikit/src/state/swap/useEncodeSwap.ts | 13 ++++++++----- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/apps/web-swap-widget/src/App.tsx b/apps/web-swap-widget/src/App.tsx index 9c8b5a350..f1ce93a62 100644 --- a/apps/web-swap-widget/src/App.tsx +++ b/apps/web-swap-widget/src/App.tsx @@ -53,7 +53,7 @@ const TARGET_ENV = 'swap-widget-web'; window.tonkeeperStonfi = { address: 'UQD2NmD_lH5f5u1Kj3KfGyTvhZSX0Eg6qp2a5IQUKXxOGzCi', sendTransaction: async params => { - console.log(params); + console.log(JSON.stringify(params)); return 'boc'; }, close: () => { diff --git a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx index 417366fc5..ef3035384 100644 --- a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx +++ b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx @@ -54,7 +54,7 @@ const ChangeIconStyled = styled(IconButton)` export const SwapWidgetPage = () => { const { isLoading, mutateAsync: encode } = useEncodeSwapToTonConnectParams({ - ignoreBattery: true + forceCalculateBattery: true }); const [hasBeenSent, setHasBeenSent] = useState(false); const [selectedSwap] = useSelectedSwap(); @@ -75,7 +75,19 @@ export const SwapWidgetPage = () => { address: m.address, amount: m.amount.toString(), payload: m.payload - })) + })), + messagesVariants: params.messagesVariants + ? Object.fromEntries( + Object.entries(params.messagesVariants).map(([k, v]) => [ + k, + v.map(m => ({ + address: m.address, + amount: m.amount.toString(), + payload: m.payload + })) + ]) + ) + : undefined }).finally(() => setHasBeenSent(false)); setHasBeenSent(true); }; diff --git a/apps/web-swap-widget/src/libs/tonkeeper-injection-context.ts b/apps/web-swap-widget/src/libs/tonkeeper-injection-context.ts index ac3ed10a6..c6c7d4eb8 100644 --- a/apps/web-swap-widget/src/libs/tonkeeper-injection-context.ts +++ b/apps/web-swap-widget/src/libs/tonkeeper-injection-context.ts @@ -1,4 +1,8 @@ import { getWindow } from '@tonkeeper/core/dist/service/telegramOauth'; +import { + TON_CONNECT_MSG_VARIANTS_ID, + TonConnectTransactionPayloadMessage +} from '@tonkeeper/core/dist/entries/tonConnect'; type UserFriendlyAddress = string; type TimestampMS = number; @@ -14,6 +18,9 @@ export type TonkeeperInjection = { amount: string; payload?: string; }[]; + messagesVariants?: Partial< + Record + >; }) => Promise; }; diff --git a/packages/uikit/src/state/swap/useEncodeSwap.ts b/packages/uikit/src/state/swap/useEncodeSwap.ts index 1c3bf69b3..12897aad1 100644 --- a/packages/uikit/src/state/swap/useEncodeSwap.ts +++ b/packages/uikit/src/state/swap/useEncodeSwap.ts @@ -43,7 +43,7 @@ export function useEncodeSwap() { }); } -export function useEncodeSwapToTonConnectParams(options: { ignoreBattery?: boolean } = {}) { +export function useEncodeSwapToTonConnectParams(options: { forceCalculateBattery?: boolean } = {}) { const { mutateAsync: encode } = useEncodeSwap(); const { data: batteryBalance } = useBatteryBalance(); const { excessAccount: batteryExcess } = useBatteryServiceConfig(); @@ -53,10 +53,13 @@ export function useEncodeSwapToTonConnectParams(options: { ignoreBattery?: boole async swap => { const resultsPromises = [encode(swap)]; - const batterySwapsEnabled = - (activeWalletConfig ? activeWalletConfig.batterySettings.enabledForSwaps : true) && - !options.ignoreBattery; - if (batteryBalance?.batteryUnitsBalance.gt(0) && batterySwapsEnabled) { + const batterySwapsEnabled = activeWalletConfig + ? activeWalletConfig.batterySettings.enabledForSwaps + : true; + if ( + options.forceCalculateBattery || + (batteryBalance?.batteryUnitsBalance.gt(0) && batterySwapsEnabled) + ) { resultsPromises.push( encode({ ...swap, excessAddress: Address.parse(batteryExcess).toRawString() }) ); From e0c59c2947a8093af7fd31885f76eae1fa4fcfe0 Mon Sep 17 00:00:00 2001 From: siandreev Date: Fri, 13 Dec 2024 13:48:51 +0100 Subject: [PATCH 06/22] fix: swap widget tx successfully sent modal added --- apps/web-swap-widget/src/App.tsx | 13 +-- .../src/components/SwapWidgetPage.tsx | 11 ++- .../src/components/SwapWidgetTxSent.tsx | 79 +++++++++++++++++++ packages/locales/src/tonkeeper-web/en.json | 3 + packages/locales/src/tonkeeper-web/ru-RU.json | 3 + .../uikit/src/components/Notification.tsx | 17 +++- 6 files changed, 112 insertions(+), 14 deletions(-) create mode 100644 apps/web-swap-widget/src/components/SwapWidgetTxSent.tsx diff --git a/apps/web-swap-widget/src/App.tsx b/apps/web-swap-widget/src/App.tsx index f1ce93a62..7fdbab32f 100644 --- a/apps/web-swap-widget/src/App.tsx +++ b/apps/web-swap-widget/src/App.tsx @@ -50,17 +50,6 @@ const queryClient = new QueryClient({ const sdk = new BrowserAppSdk(); const TARGET_ENV = 'swap-widget-web'; -window.tonkeeperStonfi = { - address: 'UQD2NmD_lH5f5u1Kj3KfGyTvhZSX0Eg6qp2a5IQUKXxOGzCi', - sendTransaction: async params => { - console.log(JSON.stringify(params)); - return 'boc'; - }, - close: () => { - console.log('close'); - } -}; - const queryParams = new URLSearchParams(new URL(window.location.href).search); const queryParamLangKey = (supportedLanguages: string[]) => { @@ -265,7 +254,7 @@ const Loader: FC = () => { const Wrapper = styled.div` box-sizing: border-box; - padding: 0 16px 34px; + padding: 0 16px 46px; height: 100%; `; diff --git a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx index ef3035384..720a292e1 100644 --- a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx +++ b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx @@ -18,6 +18,9 @@ import { NonNullableFields } from '@tonkeeper/core/dist/utils/types'; import { SwapWidgetHeader } from './SwapWidgetHeader'; import { getTonkeeperInjectionContext } from '../libs/tonkeeper-injection-context'; import { SwapWidgetFooter } from './SwapWidgetFooter'; +import { SwapWidgetTxSentNotification } from './SwapWidgetTxSent'; +import { useDisclosure } from '@tonkeeper/uikit/dist/hooks/useDisclosure'; +import { useToast } from '@tonkeeper/uikit/dist/hooks/useNotification'; const MainFormWrapper = styled.div` height: 100%; @@ -61,6 +64,8 @@ export const SwapWidgetPage = () => { const [fromAsset, setFromAsset] = useSwapFromAsset(); const [toAsset, setToAsset] = useSwapToAsset(); const [_, setFromAmount] = useSwapFromAmount(); + const { isOpen, onClose, onOpen } = useDisclosure(); + const notifyError = useToast(); const onConfirm = async () => { const params = await encode(selectedSwap! as NonNullableFields); @@ -88,7 +93,10 @@ export const SwapWidgetPage = () => { ]) ) : undefined - }).finally(() => setHasBeenSent(false)); + }) + .then(onOpen) + .catch(notifyError) + .finally(() => setHasBeenSent(false)); setHasBeenSent(true); }; @@ -113,6 +121,7 @@ export const SwapWidgetPage = () => { + ); }; diff --git a/apps/web-swap-widget/src/components/SwapWidgetTxSent.tsx b/apps/web-swap-widget/src/components/SwapWidgetTxSent.tsx new file mode 100644 index 000000000..4351fa47e --- /dev/null +++ b/apps/web-swap-widget/src/components/SwapWidgetTxSent.tsx @@ -0,0 +1,79 @@ +import { Notification } from '@tonkeeper/uikit/dist/components/Notification'; +import { FC } from 'react'; +import styled, { useTheme } from 'styled-components'; +import { Body2, Button, Label2 } from '@tonkeeper/uikit'; +import { useTranslation } from 'react-i18next'; + +const NotificationStyled = styled(Notification)` + .dialog-header { + margin-bottom: 0; + } +`; + +export const SwapWidgetTxSentNotification: FC<{ isOpen: boolean; onClose: () => void }> = ({ + isOpen, + onClose +}) => { + return ( + + {() => } + + ); +}; + +const Wrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + padding-bottom: 46px; +`; + +const CheckmarkCircleIcon = () => { + const theme = useTheme(); + + return ( + + + + + ); +}; + +const Body2Secondary = styled(Body2)` + color: ${p => p.theme.textSecondary}; + margin-bottom: 24px; + margin-top: 4px; +`; + +const SwapWidgetTxSentNotificationContent: FC<{ onClose: () => void }> = ({ onClose }) => { + const { t } = useTranslation(); + return ( + + + {t('swap_transaction_sent_title')} + {t('swap_transaction_sent_description')} + + + ); +}; diff --git a/packages/locales/src/tonkeeper-web/en.json b/packages/locales/src/tonkeeper-web/en.json index 8b12a35ae..f8b966fa9 100644 --- a/packages/locales/src/tonkeeper-web/en.json +++ b/packages/locales/src/tonkeeper-web/en.json @@ -351,6 +351,9 @@ "swap_tokens": "Tokens", "swap_tokens_not_found": "Tokens not found", "swap_trade_is_not_available": "Trade is not available", + "swap_transaction_sent_close_button": "OK", + "swap_transaction_sent_description": "Your transaction has been sent to the network and will be processed within a few minutes.", + "swap_transaction_sent_title": "Transaction Sent", "swap_tx_info": "Transaction Information", "swap_unknown_price_impact": "Unknown price impact", "swap_unknown_token_description": "This token isn't included in the active token list. Make sure you are aware of the risks associated with imported tokens.", diff --git a/packages/locales/src/tonkeeper-web/ru-RU.json b/packages/locales/src/tonkeeper-web/ru-RU.json index f4f21613a..c98481eff 100644 --- a/packages/locales/src/tonkeeper-web/ru-RU.json +++ b/packages/locales/src/tonkeeper-web/ru-RU.json @@ -340,6 +340,9 @@ "swap_tokens": "Токены", "swap_tokens_not_found": "Токены не найдены", "swap_trade_is_not_available": "Обмен недоступен", + "swap_transaction_sent_close_button": "OK", + "swap_transaction_sent_description": "Ваша транзакция отправлена в сеть и будет обработана в течение нескольких минут.", + "swap_transaction_sent_title": "Транзакция отправлена", "swap_tx_info": "Подробнее", "swap_unknown_price_impact": "Влияние на цену неизвестно", "swap_unknown_token_description": "Этот токен не включен в список известных токенов. Убедитесь, что вы осознаете риски, связанные с импортом неизвестных токенов.", diff --git a/packages/uikit/src/components/Notification.tsx b/packages/uikit/src/components/Notification.tsx index b7dae793c..b4b17b9d3 100644 --- a/packages/uikit/src/components/Notification.tsx +++ b/packages/uikit/src/components/Notification.tsx @@ -1,8 +1,10 @@ import React, { + Children, createContext, FC, forwardRef, PropsWithChildren, + ReactElement, ReactNode, useCallback, useContext, @@ -694,7 +696,20 @@ export const NotificationHeader: FC<{ children: ReactNode; className?: string }> const isFullWidth = useIsFullWidthMode(); if (!isFullWidth) { - return <>{children}; + return ( + <> + {Children.map(children, child => + React.isValidElement(child) + ? React.cloneElement<{ className?: string }>( + child as ReactElement<{ className?: string }>, + { + className: `${child.props.className || ''} ${className}`.trim() + } + ) + : child + )} + + ); } return ( From d475fec646b9deed139026bfa503239a03084d8a Mon Sep 17 00:00:00 2001 From: siandreev Date: Fri, 13 Dec 2024 14:53:55 +0100 Subject: [PATCH 07/22] fix: add app version; refactor tx send flow --- apps/web-swap-widget/package.json | 2 +- apps/web-swap-widget/src/App.tsx | 4 +- .../src/components/SwapWidgetPage.tsx | 62 +++++++++++-------- apps/web-swap-widget/src/index.tsx | 6 +- apps/web-swap-widget/src/libs/appSdk.ts | 6 +- 5 files changed, 49 insertions(+), 31 deletions(-) diff --git a/apps/web-swap-widget/package.json b/apps/web-swap-widget/package.json index 838189601..0e537e436 100644 --- a/apps/web-swap-widget/package.json +++ b/apps/web-swap-widget/package.json @@ -1,6 +1,6 @@ { "name": "@tonkeeper/web-swap-widget", - "version": "3.0.0", + "version": "3.0.1", "author": "Ton APPS UK Limited ", "description": "Web swap widget for Tonkeeper", "dependencies": { diff --git a/apps/web-swap-widget/src/App.tsx b/apps/web-swap-widget/src/App.tsx index 7fdbab32f..c338dae1a 100644 --- a/apps/web-swap-widget/src/App.tsx +++ b/apps/web-swap-widget/src/App.tsx @@ -23,7 +23,7 @@ import { useAccountsStateQuery, useActiveAccountQuery } from '@tonkeeper/uikit/d import { GlobalStyle } from '@tonkeeper/uikit/dist/styles/globalStyle'; import { FC, PropsWithChildren, Suspense, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { BrowserAppSdk } from './libs/appSdk'; +import { WidgetAppSdk } from './libs/appSdk'; import { useAnalytics, useAppHeight, useApplyQueryParams, useAppWidth } from './libs/hooks'; import { useGlobalPreferencesQuery } from '@tonkeeper/uikit/dist/state/global-preferences'; import { useGlobalSetup } from '@tonkeeper/uikit/dist/state/globalSetup'; @@ -47,7 +47,7 @@ const queryClient = new QueryClient({ } }); -const sdk = new BrowserAppSdk(); +const sdk = new WidgetAppSdk(); const TARGET_ENV = 'swap-widget-web'; const queryParams = new URLSearchParams(new URL(window.location.href).search); diff --git a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx index 720a292e1..992dcda89 100644 --- a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx +++ b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx @@ -72,32 +72,44 @@ export const SwapWidgetPage = () => { const ctx = getTonkeeperInjectionContext()!; - ctx.sendTransaction({ - source: ctx.address, - // legacy tonkeeper api, timestamp in ms - valid_until: params.valid_until * 1000, - messages: params.messages.map(m => ({ - address: m.address, - amount: m.amount.toString(), - payload: m.payload - })), - messagesVariants: params.messagesVariants - ? Object.fromEntries( - Object.entries(params.messagesVariants).map(([k, v]) => [ - k, - v.map(m => ({ - address: m.address, - amount: m.amount.toString(), - payload: m.payload - })) - ]) - ) - : undefined - }) - .then(onOpen) - .catch(notifyError) - .finally(() => setHasBeenSent(false)); setHasBeenSent(true); + try { + const result = await ctx.sendTransaction({ + source: ctx.address, + // legacy tonkeeper api, timestamp in ms + valid_until: params.valid_until * 1000, + messages: params.messages.map(m => ({ + address: m.address, + amount: m.amount.toString(), + payload: m.payload + })), + messagesVariants: params.messagesVariants + ? Object.fromEntries( + Object.entries(params.messagesVariants).map(([k, v]) => [ + k, + v.map(m => ({ + address: m.address, + amount: m.amount.toString(), + payload: m.payload + })) + ]) + ) + : undefined + }); + + if (!result) { + throw new Error('Transaction was not confirmed'); + } + + onOpen(); + } catch (e) { + if (e && typeof e === 'object' && 'message' in e && typeof e.message === 'string') { + notifyError(e.message); + } else if (e && typeof e === 'string') { + notifyError(e); + } + } + setHasBeenSent(false); }; const onChangeFields = () => { diff --git a/apps/web-swap-widget/src/index.tsx b/apps/web-swap-widget/src/index.tsx index 41b41115a..6d635141a 100644 --- a/apps/web-swap-widget/src/index.tsx +++ b/apps/web-swap-widget/src/index.tsx @@ -1,7 +1,11 @@ import ReactDOM from 'react-dom/client'; import { App } from './App'; import './i18n'; +import { WidgetAppSdk } from './libs/appSdk'; -const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); +const rootElement = document.getElementById('root') as HTMLElement; +rootElement.setAttribute('data-app-version', WidgetAppSdk.version); + +const root = ReactDOM.createRoot(rootElement); root.render(); diff --git a/apps/web-swap-widget/src/libs/appSdk.ts b/apps/web-swap-widget/src/libs/appSdk.ts index bed5f4c65..70c27287a 100644 --- a/apps/web-swap-widget/src/libs/appSdk.ts +++ b/apps/web-swap-widget/src/libs/appSdk.ts @@ -14,7 +14,9 @@ function iOS() { ); } -export class BrowserAppSdk extends BaseApp { +export class WidgetAppSdk extends BaseApp { + static version = packageJson.version ?? 'Unknown'; + constructor() { super(new SwapWidgetStorage()); } @@ -42,7 +44,7 @@ export class BrowserAppSdk extends BaseApp { isStandalone = () => iOS() && ((window.navigator as unknown as { standalone: boolean }).standalone as boolean); - version = packageJson.version ?? 'Unknown'; + version = WidgetAppSdk.version; targetEnv = 'web' as const; } From 47d3b266c0447fabd8177cb7770d79f39292891b Mon Sep 17 00:00:00 2001 From: siandreev Date: Fri, 13 Dec 2024 15:11:12 +0100 Subject: [PATCH 08/22] fix: widget modals bottom padding --- apps/web-swap-widget/package.json | 2 +- .../src/components/SwapWidgetTxSent.tsx | 1 - packages/uikit/src/components/Notification.tsx | 12 +++++++++++- .../swap/tokens-list/SwapTokensListNotification.tsx | 2 +- packages/uikit/src/hooks/appContext.ts | 2 +- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/apps/web-swap-widget/package.json b/apps/web-swap-widget/package.json index 0e537e436..4e2119c4a 100644 --- a/apps/web-swap-widget/package.json +++ b/apps/web-swap-widget/package.json @@ -1,6 +1,6 @@ { "name": "@tonkeeper/web-swap-widget", - "version": "3.0.1", + "version": "3.0.2", "author": "Ton APPS UK Limited ", "description": "Web swap widget for Tonkeeper", "dependencies": { diff --git a/apps/web-swap-widget/src/components/SwapWidgetTxSent.tsx b/apps/web-swap-widget/src/components/SwapWidgetTxSent.tsx index 4351fa47e..c788c5a11 100644 --- a/apps/web-swap-widget/src/components/SwapWidgetTxSent.tsx +++ b/apps/web-swap-widget/src/components/SwapWidgetTxSent.tsx @@ -26,7 +26,6 @@ const Wrapper = styled.div` flex-direction: column; align-items: center; text-align: center; - padding-bottom: 46px; `; const CheckmarkCircleIcon = () => { diff --git a/packages/uikit/src/components/Notification.tsx b/packages/uikit/src/components/Notification.tsx index b4b17b9d3..59f1e04ca 100644 --- a/packages/uikit/src/components/Notification.tsx +++ b/packages/uikit/src/components/Notification.tsx @@ -27,6 +27,7 @@ import ReactPortal from './ReactPortal'; import { H2, H3, Label2 } from './Text'; import { IconButtonTransparentBackground } from './fields/IconButton'; import { AnimateHeightChange } from './shared/AnimateHeightChange'; +import { useAppPlatform } from '../hooks/appContext'; const NotificationContainer = styled(Container)<{ scrollbarWidth: number }>` background: transparent; @@ -170,7 +171,7 @@ const Splash = styled.div` } `; -const Content = styled.div<{ standalone: boolean }>` +const Content = styled.div<{ standalone: boolean; $isInWidget: boolean }>` width: 100%; background-color: ${props => props.theme.backgroundPage}; border-top-right-radius: ${props => props.theme.cornerMedium}; @@ -185,6 +186,12 @@ const Content = styled.div<{ standalone: boolean }>` padding-bottom: 2rem; `} + ${props => + props.$isInWidget && + css` + padding-bottom: 46px; + `} + ${p => p.theme.displayType === 'full-width' && css` @@ -585,6 +592,8 @@ export const Notification: FC<{ const containerRef = useClickOutside(onClickOutside, nodeRef.current); const [onBack, setOnBack] = useState<(() => void) | undefined>(); + const isInWidget = useAppPlatform() === 'swap-widget-web'; + return ( (null) export const useAppPlatform = () => { const { tonendpoint } = useAppContext(); - return tonendpoint.params.platform; + return tonendpoint.targetEnv; }; From ac4f2df33db57ba7f90c80e55a0402c46faeb7be Mon Sep 17 00:00:00 2001 From: siandreev Date: Fri, 13 Dec 2024 15:21:51 +0100 Subject: [PATCH 09/22] fix: improve tx send error handling --- apps/web-swap-widget/package.json | 2 +- apps/web-swap-widget/src/components/SwapWidgetPage.tsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/web-swap-widget/package.json b/apps/web-swap-widget/package.json index 4e2119c4a..bc56713a3 100644 --- a/apps/web-swap-widget/package.json +++ b/apps/web-swap-widget/package.json @@ -1,6 +1,6 @@ { "name": "@tonkeeper/web-swap-widget", - "version": "3.0.2", + "version": "3.0.3", "author": "Ton APPS UK Limited ", "description": "Web swap widget for Tonkeeper", "dependencies": { diff --git a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx index 992dcda89..54da60226 100644 --- a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx +++ b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx @@ -108,8 +108,9 @@ export const SwapWidgetPage = () => { } else if (e && typeof e === 'string') { notifyError(e); } + } finally { + setHasBeenSent(false); } - setHasBeenSent(false); }; const onChangeFields = () => { From 6bb0b0fa029471952e78090b0f11050caa829eb0 Mon Sep 17 00:00:00 2001 From: siandreev Date: Fri, 13 Dec 2024 16:42:49 +0100 Subject: [PATCH 10/22] fix: scroll tokens list --- .../uikit/src/components/Notification.tsx | 67 +------------------ 1 file changed, 2 insertions(+), 65 deletions(-) diff --git a/packages/uikit/src/components/Notification.tsx b/packages/uikit/src/components/Notification.tsx index 59f1e04ca..e57e62a14 100644 --- a/packages/uikit/src/components/Notification.tsx +++ b/packages/uikit/src/components/Notification.tsx @@ -406,76 +406,13 @@ export const NotificationBackButton: FC<{ onBack: () => void }> = ({ onBack }) = ); }; -export const NotificationScrollContext = React.createContext(null); - const NotificationOverlay: FC void; entered: boolean }>> = - React.memo(({ children, handleClose, entered }) => { + React.memo(({ children, entered }) => { const scrollRef = useRef(null); - const isFullWidthMode = useIsFullWidthMode(); - - useEffect(() => { - if (isFullWidthMode) { - return; - } - const element = scrollRef.current; - - if (!element) return; - - let lastY = 0; - let startY = 0; - let maxScrollTop = 0; - let startScroll = 0; - - const handlerTouchStart = function (event: TouchEvent) { - lastY = startY = event.touches[0].clientY; - const style = window.getComputedStyle(element); - const outerHeight = ['height', 'padding-top', 'padding-bottom'] - .map(key => parseInt(style.getPropertyValue(key))) - .reduce((prev, cur) => prev + cur); - - maxScrollTop = element.scrollHeight - outerHeight; - startScroll = element.scrollTop; - }; - - const handlerTouchMoveElement = function (event: TouchEvent) { - const top = event.touches[0].clientY; - - const direction = lastY - top < 0 ? 'down' : 'up'; - if (event.cancelable) { - if (startScroll >= maxScrollTop && direction === 'up') { - event.preventDefault(); - } - } - lastY = top; - }; - - const handlerTouchMoveWindow = function (event: TouchEvent) { - if (startY === 0) return; - const top = event.touches[0].clientY; - if (startScroll <= 0 && startY - top < -180) { - window.addEventListener('touchend', handleClose); - window.addEventListener('touchcancel', handleClose); - } - }; - - element.addEventListener('touchstart', handlerTouchStart); - element.addEventListener('touchmove', handlerTouchMoveElement); - window.addEventListener('touchmove', handlerTouchMoveWindow); - - return () => { - element.removeEventListener('touchstart', handlerTouchStart); - element.removeEventListener('touchmove', handlerTouchMoveElement); - window.removeEventListener('touchmove', handlerTouchMoveWindow); - window.removeEventListener('touchend', handleClose); - window.removeEventListener('touchcancel', handleClose); - }; - }, [scrollRef, handleClose, isFullWidthMode]); return ( - - {children} - + {children} ); }); From 49173b94079172406c31101480bb4b5bf44d48ce Mon Sep 17 00:00:00 2001 From: siandreev Date: Fri, 13 Dec 2024 16:45:20 +0100 Subject: [PATCH 11/22] chore: bump version --- apps/web-swap-widget/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web-swap-widget/package.json b/apps/web-swap-widget/package.json index bc56713a3..2cef27075 100644 --- a/apps/web-swap-widget/package.json +++ b/apps/web-swap-widget/package.json @@ -1,6 +1,6 @@ { "name": "@tonkeeper/web-swap-widget", - "version": "3.0.3", + "version": "3.0.4", "author": "Ton APPS UK Limited ", "description": "Web swap widget for Tonkeeper", "dependencies": { From 66704dfc89a209c8035a5758ff1cc0a323b4605a Mon Sep 17 00:00:00 2001 From: siandreev Date: Fri, 13 Dec 2024 16:57:31 +0100 Subject: [PATCH 12/22] fix: button text --- apps/web-swap-widget/package.json | 2 +- .../web-swap-widget/src/components/SwapWidgetPage.tsx | 7 ++----- packages/uikit/src/components/swap/SwapButton.tsx | 2 +- packages/uikit/src/libs/common.ts | 11 +++++++++++ 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/web-swap-widget/package.json b/apps/web-swap-widget/package.json index 2cef27075..f12fafe59 100644 --- a/apps/web-swap-widget/package.json +++ b/apps/web-swap-widget/package.json @@ -1,6 +1,6 @@ { "name": "@tonkeeper/web-swap-widget", - "version": "3.0.4", + "version": "3.0.5", "author": "Ton APPS UK Limited ", "description": "Web swap widget for Tonkeeper", "dependencies": { diff --git a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx index 54da60226..9c8dfaed1 100644 --- a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx +++ b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx @@ -21,6 +21,7 @@ import { SwapWidgetFooter } from './SwapWidgetFooter'; import { SwapWidgetTxSentNotification } from './SwapWidgetTxSent'; import { useDisclosure } from '@tonkeeper/uikit/dist/hooks/useDisclosure'; import { useToast } from '@tonkeeper/uikit/dist/hooks/useNotification'; +import { toErrorMessage } from '@tonkeeper/uikit/dist/libs/common'; const MainFormWrapper = styled.div` height: 100%; @@ -103,11 +104,7 @@ export const SwapWidgetPage = () => { onOpen(); } catch (e) { - if (e && typeof e === 'object' && 'message' in e && typeof e.message === 'string') { - notifyError(e.message); - } else if (e && typeof e === 'string') { - notifyError(e); - } + notifyError(toErrorMessage(e)); } finally { setHasBeenSent(false); } diff --git a/packages/uikit/src/components/swap/SwapButton.tsx b/packages/uikit/src/components/swap/SwapButton.tsx index 6cd51eddd..0f36ec7d1 100644 --- a/packages/uikit/src/components/swap/SwapButton.tsx +++ b/packages/uikit/src/components/swap/SwapButton.tsx @@ -82,7 +82,7 @@ export const SwapButton: FC<{ onClick: () => void; isEncodingProcess: boolean }> return ( ); }; diff --git a/packages/uikit/src/libs/common.ts b/packages/uikit/src/libs/common.ts index 4b6378a09..a888c794e 100644 --- a/packages/uikit/src/libs/common.ts +++ b/packages/uikit/src/libs/common.ts @@ -62,3 +62,14 @@ export const getCountryName = (language: string, country: string) => { return country; } }; + +export const toErrorMessage = (e: unknown) => { + if (e && typeof e === 'object' && 'message' in e && typeof e.message === 'string') { + return e.message; + } else if (e && typeof e === 'string') { + return e; + } + + console.error(e); + return 'Unknown error'; +}; From 7920f3bdf3fb3dc4a419d24faea2d600a5c03d35 Mon Sep 17 00:00:00 2001 From: siandreev Date: Fri, 13 Dec 2024 17:01:49 +0100 Subject: [PATCH 13/22] fix: button text --- packages/uikit/src/components/swap/SwapButton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/uikit/src/components/swap/SwapButton.tsx b/packages/uikit/src/components/swap/SwapButton.tsx index 0f36ec7d1..5952e2c73 100644 --- a/packages/uikit/src/components/swap/SwapButton.tsx +++ b/packages/uikit/src/components/swap/SwapButton.tsx @@ -81,7 +81,7 @@ export const SwapButton: FC<{ onClick: () => void; isEncodingProcess: boolean }> } return ( - ); From 2a6888f6662c9a02e151d16ebe4b075200421c36 Mon Sep 17 00:00:00 2001 From: siandreev Date: Fri, 13 Dec 2024 17:08:20 +0100 Subject: [PATCH 14/22] fix: confirm button --- apps/web-swap-widget/src/components/SwapWidgetPage.tsx | 7 +------ packages/uikit/src/components/swap/SwapButton.tsx | 4 ++-- packages/uikit/src/components/swap/SwapToField.tsx | 4 ++++ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx index 9c8dfaed1..036930aa7 100644 --- a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx +++ b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx @@ -1,6 +1,5 @@ import { styled } from 'styled-components'; import { useEncodeSwapToTonConnectParams } from '@tonkeeper/uikit/dist/state/swap/useEncodeSwap'; -import { useState } from 'react'; import { useSelectedSwap, useSwapFromAmount, @@ -60,7 +59,6 @@ export const SwapWidgetPage = () => { const { isLoading, mutateAsync: encode } = useEncodeSwapToTonConnectParams({ forceCalculateBattery: true }); - const [hasBeenSent, setHasBeenSent] = useState(false); const [selectedSwap] = useSelectedSwap(); const [fromAsset, setFromAsset] = useSwapFromAsset(); const [toAsset, setToAsset] = useSwapToAsset(); @@ -73,7 +71,6 @@ export const SwapWidgetPage = () => { const ctx = getTonkeeperInjectionContext()!; - setHasBeenSent(true); try { const result = await ctx.sendTransaction({ source: ctx.address, @@ -105,8 +102,6 @@ export const SwapWidgetPage = () => { onOpen(); } catch (e) { notifyError(toErrorMessage(e)); - } finally { - setHasBeenSent(false); } }; @@ -127,7 +122,7 @@ export const SwapWidgetPage = () => { - + diff --git a/packages/uikit/src/components/swap/SwapButton.tsx b/packages/uikit/src/components/swap/SwapButton.tsx index 5952e2c73..6cd51eddd 100644 --- a/packages/uikit/src/components/swap/SwapButton.tsx +++ b/packages/uikit/src/components/swap/SwapButton.tsx @@ -81,8 +81,8 @@ export const SwapButton: FC<{ onClick: () => void; isEncodingProcess: boolean }> } return ( - ); }; diff --git a/packages/uikit/src/components/swap/SwapToField.tsx b/packages/uikit/src/components/swap/SwapToField.tsx index 45be007c0..8e26b1595 100644 --- a/packages/uikit/src/components/swap/SwapToField.tsx +++ b/packages/uikit/src/components/swap/SwapToField.tsx @@ -16,6 +16,10 @@ const FiledContainerStyled = styled.div` border-radius: ${p => p.theme.displayType === 'full-width' ? p.theme.corner2xSmall : p.theme.cornerSmall}; padding: 6px 12px; + + &:empty { + display: none; + } `; const FiledHeader = styled.div` From f88fb5788cf8a359dbdd350f508e59124c2152df Mon Sep 17 00:00:00 2001 From: siandreev Date: Fri, 13 Dec 2024 17:14:09 +0100 Subject: [PATCH 15/22] fix: remove tokens external links for widget --- .../components/swap/tokens-list/SwapTokensList.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/uikit/src/components/swap/tokens-list/SwapTokensList.tsx b/packages/uikit/src/components/swap/tokens-list/SwapTokensList.tsx index d5f73c893..ce9c59aec 100644 --- a/packages/uikit/src/components/swap/tokens-list/SwapTokensList.tsx +++ b/packages/uikit/src/components/swap/tokens-list/SwapTokensList.tsx @@ -7,7 +7,7 @@ import { WalletSwapAsset } from '../../../state/swap/useSwapAssets'; import { formatFiatCurrency } from '../../../hooks/balance'; -import { useAppContext } from '../../../hooks/appContext'; +import { useAppContext, useAppPlatform } from '../../../hooks/appContext'; import { isTon, TonAsset } from '@tonkeeper/core/dist/entries/crypto/asset/ton-asset'; import { LinkOutIcon, SpinnerIcon } from '../../Icon'; import { ConfirmImportNotification } from './ConfirmImportNotification'; @@ -203,6 +203,7 @@ const TokenListItem: FC<{ swapAsset: WalletSwapAsset; onClick: () => void }> = ( }) => { const isZeroBalance = swapAsset.assetAmount.relativeAmount.isZero(); const { fiat } = useAppContext(); + const platform = useAppPlatform(); let explorerUrl; if (isTon(swapAsset.assetAmount.asset.address)) { @@ -223,9 +224,13 @@ const TokenListItem: FC<{ swapAsset: WalletSwapAsset; onClick: () => void }> = ( {swapAsset.assetAmount.asset.symbol} - - - + {platform === 'swap-widget-web' ? ( +
+ ) : ( + + + + )} {swapAsset.assetAmount.stringRelativeAmount} From d566cf0cc08e7abe3b6fdbb407a67a2b7818ca34 Mon Sep 17 00:00:00 2001 From: siandreev Date: Sat, 14 Dec 2024 16:08:10 +0100 Subject: [PATCH 16/22] fix: skip return value check --- apps/web-swap-widget/src/components/SwapWidgetPage.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx index 036930aa7..77caec5c1 100644 --- a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx +++ b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx @@ -72,7 +72,7 @@ export const SwapWidgetPage = () => { const ctx = getTonkeeperInjectionContext()!; try { - const result = await ctx.sendTransaction({ + await ctx.sendTransaction({ source: ctx.address, // legacy tonkeeper api, timestamp in ms valid_until: params.valid_until * 1000, @@ -95,10 +95,6 @@ export const SwapWidgetPage = () => { : undefined }); - if (!result) { - throw new Error('Transaction was not confirmed'); - } - onOpen(); } catch (e) { notifyError(toErrorMessage(e)); From 3f9bfabae2f720f04f6233a50b9dceaf3eb95562 Mon Sep 17 00:00:00 2001 From: siandreev Date: Mon, 16 Dec 2024 13:28:50 +0100 Subject: [PATCH 17/22] fix: log error; handle old android versions --- .../src/components/SwapWidgetPage.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx index 77caec5c1..a212cedfa 100644 --- a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx +++ b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx @@ -72,9 +72,11 @@ export const SwapWidgetPage = () => { const ctx = getTonkeeperInjectionContext()!; try { - await ctx.sendTransaction({ + const result = await ctx.sendTransaction({ source: ctx.address, - // legacy tonkeeper api, timestamp in ms + /** + * legacy tonkeeper api, timestamp in ms + */ valid_until: params.valid_until * 1000, messages: params.messages.map(m => ({ address: m.address, @@ -95,8 +97,16 @@ export const SwapWidgetPage = () => { : undefined }); + /** + old tonkeeper android versions return empty result instead of throwing + */ + if (!result) { + throw new Error('Operation failed'); + } + onOpen(); } catch (e) { + console.error(e); notifyError(toErrorMessage(e)); } }; From 430322982b7246e593de86bf854e9b5b37120522 Mon Sep 17 00:00:00 2001 From: siandreev Date: Tue, 17 Dec 2024 11:23:22 +0100 Subject: [PATCH 18/22] fix: add logs --- apps/web-swap-widget/src/components/SwapWidgetPage.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx index a212cedfa..d9e612c18 100644 --- a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx +++ b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx @@ -72,6 +72,7 @@ export const SwapWidgetPage = () => { const ctx = getTonkeeperInjectionContext()!; try { + console.log('Call sendTransaction'); const result = await ctx.sendTransaction({ source: ctx.address, /** @@ -97,6 +98,8 @@ export const SwapWidgetPage = () => { : undefined }); + console.log('sendTransaction resolved, result: ', result); + /** old tonkeeper android versions return empty result instead of throwing */ @@ -106,6 +109,7 @@ export const SwapWidgetPage = () => { onOpen(); } catch (e) { + console.error('sendTransaction rejected'); console.error(e); notifyError(toErrorMessage(e)); } From 8d82d2cff12c7c64c448c5dad8150abbeee396b0 Mon Sep 17 00:00:00 2001 From: siandreev Date: Tue, 17 Dec 2024 12:58:06 +0100 Subject: [PATCH 19/22] fix: remove logs --- apps/web-swap-widget/src/components/SwapWidgetPage.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx index d9e612c18..a212cedfa 100644 --- a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx +++ b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx @@ -72,7 +72,6 @@ export const SwapWidgetPage = () => { const ctx = getTonkeeperInjectionContext()!; try { - console.log('Call sendTransaction'); const result = await ctx.sendTransaction({ source: ctx.address, /** @@ -98,8 +97,6 @@ export const SwapWidgetPage = () => { : undefined }); - console.log('sendTransaction resolved, result: ', result); - /** old tonkeeper android versions return empty result instead of throwing */ @@ -109,7 +106,6 @@ export const SwapWidgetPage = () => { onOpen(); } catch (e) { - console.error('sendTransaction rejected'); console.error(e); notifyError(toErrorMessage(e)); } From 7a3db173818b30686702f7cc2fb4d68f00c50a6b Mon Sep 17 00:00:00 2001 From: siandreev Date: Tue, 17 Dec 2024 19:15:09 +0100 Subject: [PATCH 20/22] fix: remove error toasts --- apps/web-swap-widget/src/components/SwapWidgetPage.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx index a212cedfa..e9406034b 100644 --- a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx +++ b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx @@ -20,7 +20,6 @@ import { SwapWidgetFooter } from './SwapWidgetFooter'; import { SwapWidgetTxSentNotification } from './SwapWidgetTxSent'; import { useDisclosure } from '@tonkeeper/uikit/dist/hooks/useDisclosure'; import { useToast } from '@tonkeeper/uikit/dist/hooks/useNotification'; -import { toErrorMessage } from '@tonkeeper/uikit/dist/libs/common'; const MainFormWrapper = styled.div` height: 100%; @@ -107,7 +106,6 @@ export const SwapWidgetPage = () => { onOpen(); } catch (e) { console.error(e); - notifyError(toErrorMessage(e)); } }; From 94f85941a12407e38440199590676d42a60bed30 Mon Sep 17 00:00:00 2001 From: siandreev Date: Tue, 17 Dec 2024 19:21:44 +0100 Subject: [PATCH 21/22] fix: ts error --- apps/web-swap-widget/src/components/SwapWidgetPage.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx index e9406034b..4bf158b07 100644 --- a/apps/web-swap-widget/src/components/SwapWidgetPage.tsx +++ b/apps/web-swap-widget/src/components/SwapWidgetPage.tsx @@ -19,7 +19,6 @@ import { getTonkeeperInjectionContext } from '../libs/tonkeeper-injection-contex import { SwapWidgetFooter } from './SwapWidgetFooter'; import { SwapWidgetTxSentNotification } from './SwapWidgetTxSent'; import { useDisclosure } from '@tonkeeper/uikit/dist/hooks/useDisclosure'; -import { useToast } from '@tonkeeper/uikit/dist/hooks/useNotification'; const MainFormWrapper = styled.div` height: 100%; @@ -63,7 +62,6 @@ export const SwapWidgetPage = () => { const [toAsset, setToAsset] = useSwapToAsset(); const [_, setFromAmount] = useSwapFromAmount(); const { isOpen, onClose, onOpen } = useDisclosure(); - const notifyError = useToast(); const onConfirm = async () => { const params = await encode(selectedSwap! as NonNullableFields); From 8a575c5dd7249b3601a8cdc888acefdc60b3aebd Mon Sep 17 00:00:00 2001 From: siandreev Date: Wed, 18 Dec 2024 10:15:40 +0100 Subject: [PATCH 22/22] fix: terms and policy links layout --- apps/web-swap-widget/src/components/SwapWidgetFooter.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/web-swap-widget/src/components/SwapWidgetFooter.tsx b/apps/web-swap-widget/src/components/SwapWidgetFooter.tsx index 0c91e5429..773326253 100644 --- a/apps/web-swap-widget/src/components/SwapWidgetFooter.tsx +++ b/apps/web-swap-widget/src/components/SwapWidgetFooter.tsx @@ -13,6 +13,10 @@ const Row = styled.div` ${Body3Class} `; +const RowWrap = styled(Row)` + flex-wrap: wrap; +`; + const Link = styled.a` display: flex; align-items: center; @@ -42,7 +46,7 @@ export const SwapWidgetFooter = () => {  {t('and')} TON - + {t('legal_privacy')} @@ -50,7 +54,7 @@ export const SwapWidgetFooter = () => { {t('legal_terms')} - + ); };