diff --git a/changelogs/1.17.8.txt b/changelogs/1.17.8.txt new file mode 100644 index 00000000..e13dfb47 --- /dev/null +++ b/changelogs/1.17.8.txt @@ -0,0 +1 @@ +Bug fixes diff --git a/package-lock.json b/package-lock.json index d1f89d3c..77fd6385 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mytonwallet", - "version": "1.17.7", + "version": "1.17.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mytonwallet", - "version": "1.17.7", + "version": "1.17.8", "license": "GPL-3.0-or-later", "dependencies": { "@capacitor-mlkit/barcode-scanning": "^5.3.0", diff --git a/package.json b/package.json index f7d7f4ab..0bbaf4bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mytonwallet", - "version": "1.17.7", + "version": "1.17.8", "description": "The most feature-rich web wallet and browser extension for TON – with support of multi-accounts, tokens (jettons), NFT, TON DNS, TON Sites, TON Proxy, and TON Magic.", "main": "index.js", "scripts": { @@ -18,9 +18,9 @@ "extension-opera:package": "cross-env IS_OPERA_EXTENSION=1 IS_EXTENSION=1 webpack && bash ./deploy/package_extension.sh opera", "extension-opera:package:staging": "cross-env APP_ENV=staging npm run extension-opera:package", "extension-opera:package:production": "npm run extension-opera:package", - "electron:dev": "npm run electron:webpack && IS_ELECTRON_BUILD=1 concurrently -n main,renderer,electron \"npm run electron:webpack -- --watch\" \"npm run dev\" \"electronmon dist/electron\"", + "electron:dev": "npm run electron:webpack && IS_PACKAGED_ELECTRON=1 concurrently -n main,renderer,electron \"npm run electron:webpack -- --watch\" \"npm run dev\" \"electronmon dist/electron\"", "electron:webpack": "cross-env APP_ENV=$ENV webpack --config ./webpack-electron.config.ts", - "electron:build": "IS_ELECTRON_BUILD=1 npm run build:$ENV && electron-builder install-app-deps && electron-rebuild && ENV=$ENV npm run electron:webpack", + "electron:build": "IS_PACKAGED_ELECTRON=1 npm run build:$ENV && electron-builder install-app-deps && electron-rebuild && ENV=$ENV npm run electron:webpack", "electron:package": "npm run electron:build && npx rimraf dist-electron && electron-builder build --win --mac --linux --config src/electron/config.yml", "electron:package:staging": "ENV=staging npm run electron:package -- -p never", "electron:release:production": "ENV=production npm run electron:package -- -p always", diff --git a/public/electronVersion.txt b/public/electronVersion.txt index b5aaf81f..a23a1564 100644 --- a/public/electronVersion.txt +++ b/public/electronVersion.txt @@ -1 +1 @@ -1.17.4 +1.17.8 diff --git a/public/version.txt b/public/version.txt index 3661bc03..a23a1564 100644 --- a/public/version.txt +++ b/public/version.txt @@ -1 +1 @@ -1.17.7 +1.17.8 diff --git a/src/api/blockchains/ton/tokens.ts b/src/api/blockchains/ton/tokens.ts index 022d31af..221b7363 100644 --- a/src/api/blockchains/ton/tokens.ts +++ b/src/api/blockchains/ton/tokens.ts @@ -38,7 +38,8 @@ export type TokenBalanceParsed = { slug: string; balance: string; token: ApiTokenSimple; -} | undefined; + jettonWallet: string; +}; const KNOWN_TOKENS: ApiBaseToken[] = [ { @@ -61,17 +62,22 @@ export async function getAccountTokenBalances(accountId: string) { return balancesRaw.map(parseTokenBalance).filter(Boolean); } -function parseTokenBalance(balanceRaw: JettonBalance): TokenBalanceParsed { +function parseTokenBalance(balanceRaw: JettonBalance): TokenBalanceParsed | undefined { if (!balanceRaw.jetton) { return undefined; } try { - const { balance, jetton } = balanceRaw; + const { balance, jetton, walletAddress } = balanceRaw; const minterAddress = toBase64Address(jetton.address, true); const token = buildTokenByMetadata(minterAddress, jetton); - return { slug: token.slug, balance, token }; + return { + slug: token.slug, + balance, + token, + jettonWallet: toBase64Address(walletAddress.address), + }; } catch (err) { logDebugError('parseTokenBalance', err); return undefined; diff --git a/src/api/blockchains/ton/transactions.ts b/src/api/blockchains/ton/transactions.ts index 1df000e2..06460b7a 100644 --- a/src/api/blockchains/ton/transactions.ts +++ b/src/api/blockchains/ton/transactions.ts @@ -83,7 +83,7 @@ type SubmitMultiTransferResult = { const { Address, fromNano } = TonWeb.utils; -const DEFAULT_FEE = '10966001'; +const DEFAULT_FEE = '15000000'; const DEFAULT_EXPIRE_AT_TIMEOUT_SEC = 60; // 60 sec. const GET_TRANSACTIONS_LIMIT = 50; const GET_TRANSACTIONS_MAX_LIMIT = 100; diff --git a/src/api/blockchains/ton/util/tonapiio.ts b/src/api/blockchains/ton/util/tonapiio.ts index 0bf219d7..3dc91cf9 100644 --- a/src/api/blockchains/ton/util/tonapiio.ts +++ b/src/api/blockchains/ton/util/tonapiio.ts @@ -10,43 +10,55 @@ import type { ApiNetwork } from '../../../types'; import { TONAPIIO_MAINNET_URL, TONAPIIO_TESTNET_URL } from '../../../../config'; import { logDebugError } from '../../../../util/logs'; -import { API_HEADERS } from '../../../environment'; +import { getEnvironment } from '../../../environment'; const MAX_LIMIT = 1000; -const configurationMainnet = new Configuration({ - basePath: TONAPIIO_MAINNET_URL, - headers: API_HEADERS, -}); -const configurationTestnet = new Configuration({ - basePath: TONAPIIO_TESTNET_URL, - headers: API_HEADERS, -}); +let apiByNetwork: Record | undefined; -export const tonapiioByNetwork = { - mainnet: { - configuration: configurationMainnet, - blockchainApi: new BlockchainApi(configurationMainnet), - nftApi: new NFTApi(configurationMainnet), - accountsApi: new AccountsApi(configurationMainnet), - }, - testnet: { - configuration: configurationTestnet, - blockchainApi: new BlockchainApi(configurationTestnet), - nftApi: new NFTApi(configurationTestnet), - accountsApi: new AccountsApi(configurationTestnet), - }, -}; +function getApi(network: ApiNetwork) { + if (!apiByNetwork) { + const headers = getEnvironment().apiHeaders; + + const configurationMainnet = new Configuration({ + basePath: TONAPIIO_MAINNET_URL, + ...(headers && { headers }), + }); + const configurationTestnet = new Configuration({ + basePath: TONAPIIO_TESTNET_URL, + ...(headers && { headers }), + }); + + apiByNetwork = { + mainnet: { + blockchainApi: new BlockchainApi(configurationMainnet), + nftApi: new NFTApi(configurationMainnet), + accountsApi: new AccountsApi(configurationMainnet), + }, + testnet: { + blockchainApi: new BlockchainApi(configurationTestnet), + nftApi: new NFTApi(configurationTestnet), + accountsApi: new AccountsApi(configurationTestnet), + }, + }; + } + + return apiByNetwork[network]; +} export function fetchJettonBalances(network: ApiNetwork, account: string) { - const api = tonapiioByNetwork[network].accountsApi; + const api = getApi(network).accountsApi; return tonapiioErrorHandler(async () => { return (await api.getJettonsBalances({ accountId: account })).balances; }, []); } export function fetchNftItems(network: ApiNetwork, addresses: string[]) { - const api = tonapiioByNetwork[network].nftApi; + const api = getApi(network).nftApi; return tonapiioErrorHandler(async () => (await api.getNftItemsByAddresses({ getAccountsRequest: { accountIds: addresses }, })).nftItems, []); @@ -58,7 +70,7 @@ export function fetchAccountNfts(network: ApiNetwork, address: string, options?: limit?: number; }) { const { collection, offset, limit } = options ?? {}; - const api = tonapiioByNetwork[network].accountsApi; + const api = getApi(network).accountsApi; return tonapiioErrorHandler(async () => (await api.getNftItemsByOwner({ accountId: address, @@ -70,7 +82,7 @@ export function fetchAccountNfts(network: ApiNetwork, address: string, options?: } export function fetchAccountEvents(network: ApiNetwork, address: string, fromSec: number, limit?: number) { - const api = tonapiioByNetwork[network].accountsApi; + const api = getApi(network).accountsApi; return tonapiioErrorHandler(async () => (await api.getEventsByAccount({ accountId: address, limit: limit ?? MAX_LIMIT, diff --git a/src/api/blockchains/ton/util/tonweb.ts b/src/api/blockchains/ton/util/tonweb.ts index 0f014a9c..1293d512 100644 --- a/src/api/blockchains/ton/util/tonweb.ts +++ b/src/api/blockchains/ton/util/tonweb.ts @@ -19,7 +19,7 @@ import { import { logDebugError } from '../../../../util/logs'; import withCacheAsync from '../../../../util/withCacheAsync'; import { base64ToBytes, fetchJson, hexToBytes } from '../../../common/utils'; -import { API_HEADERS } from '../../../environment'; +import { getEnvironment } from '../../../environment'; import { DEFAULT_IS_BOUNCEABLE, JettonOpCode, @@ -37,16 +37,7 @@ export const { toNano, fromNano } = TonWeb.utils; const TON_MAX_COMMENT_BYTES = 127; -const tonwebByNetwork = { - mainnet: new TonWeb(new CustomHttpProvider(TONHTTPAPI_MAINNET_URL, { - apiKey: TONHTTPAPI_MAINNET_API_KEY, - headers: API_HEADERS, - })) as MyTonWeb, - testnet: new TonWeb(new CustomHttpProvider(TONHTTPAPI_TESTNET_URL, { - apiKey: TONHTTPAPI_TESTNET_API_KEY, - headers: API_HEADERS, - })) as MyTonWeb, -}; +let tonwebByNetwork: Record | undefined; export const resolveTokenWalletAddress = withCacheAsync( async (network: ApiNetwork, address: string, minterAddress: string) => { @@ -118,7 +109,7 @@ export async function fetchTransactions( }, { headers: { ...(apiKey && { 'X-Api-Key': apiKey }), - ...API_HEADERS, + ...getEnvironment().apiHeaders, }, }); @@ -180,6 +171,19 @@ function getRawBody(msg: any) { } export function getTonWeb(network: ApiNetwork = 'mainnet') { + if (!tonwebByNetwork) { + tonwebByNetwork = { + mainnet: new TonWeb(new CustomHttpProvider(TONHTTPAPI_MAINNET_URL, { + apiKey: TONHTTPAPI_MAINNET_API_KEY, + headers: getEnvironment().apiHeaders, + })) as MyTonWeb, + testnet: new TonWeb(new CustomHttpProvider(TONHTTPAPI_TESTNET_URL, { + apiKey: TONHTTPAPI_TESTNET_API_KEY, + headers: getEnvironment().apiHeaders, + })) as MyTonWeb, + }; + } + return tonwebByNetwork[network]; } diff --git a/src/api/environment.ts b/src/api/environment.ts index 29427764..e4074e79 100644 --- a/src/api/environment.ts +++ b/src/api/environment.ts @@ -2,13 +2,44 @@ * This module is to be used instead of /src/util/environment.ts * when `window` is not available (e.g. in a web worker). */ -import { APP_ENV, IS_ELECTRON_BUILD, IS_EXTENSION } from '../config'; +import type { ApiInitArgs } from './types'; -// eslint-disable-next-line no-restricted-globals -export const IS_CHROME_EXTENSION = Boolean(self?.chrome?.system); +import { + APP_ENV, + ELECTRON_TONHTTPAPI_MAINNET_API_KEY, + ELECTRON_TONHTTPAPI_TESTNET_API_KEY, + IS_CAPACITOR, + IS_EXTENSION, + TONHTTPAPI_MAINNET_API_KEY, + TONHTTPAPI_TESTNET_API_KEY, +} from '../config'; // eslint-disable-next-line no-restricted-globals -export const X_APP_ORIGIN = self.origin; -export const API_HEADERS = IS_EXTENSION || (IS_ELECTRON_BUILD && APP_ENV !== 'development') - ? { 'X-App-Origin': X_APP_ORIGIN } - : undefined; +export const X_APP_ORIGIN = self?.origin; + +let environment: ApiInitArgs & { + isDappSupported: boolean; + isSseSupported: boolean; + apiHeaders?: AnyLiteral; + tonhttpapiMainnetKey?: string; + tonhttpapiTestnetKey?: string; +}; + +export function setEnvironment(args: ApiInitArgs) { + environment = { + ...args, + isDappSupported: IS_EXTENSION || IS_CAPACITOR || args.isElectron, + isSseSupported: args.isElectron || IS_CAPACITOR, + apiHeaders: IS_EXTENSION || (args.isElectron && APP_ENV !== 'development') + // eslint-disable-next-line no-restricted-globals + ? { 'X-App-Origin': X_APP_ORIGIN } + : undefined, + tonhttpapiMainnetKey: args.isElectron ? ELECTRON_TONHTTPAPI_MAINNET_API_KEY : TONHTTPAPI_MAINNET_API_KEY, + tonhttpapiTestnetKey: args.isElectron ? ELECTRON_TONHTTPAPI_TESTNET_API_KEY : TONHTTPAPI_TESTNET_API_KEY, + }; + return environment; +} + +export function getEnvironment() { + return environment; +} diff --git a/src/api/methods/auth.ts b/src/api/methods/auth.ts index ae85d34e..7ef99f38 100644 --- a/src/api/methods/auth.ts +++ b/src/api/methods/auth.ts @@ -1,7 +1,6 @@ import type { LedgerWalletInfo } from '../../util/ledger/types'; import type { ApiAccount, ApiNetwork, ApiTxIdBySlug } from '../types'; -import { IS_DAPP_SUPPORTED } from '../../config'; import blockchains from '../blockchains'; import { toBase64Address } from '../blockchains/ton/util/tonweb'; import { @@ -13,6 +12,7 @@ import { } from '../common/accounts'; import { bytesToHex } from '../common/utils'; import { apiDb } from '../db'; +import { getEnvironment } from '../environment'; import { handleServerError } from '../errors'; import { storage } from '../storages'; import { activateAccount, deactivateAllAccounts, deactivateCurrentAccount } from './accounts'; @@ -131,7 +131,7 @@ export async function removeNetworkAccounts(network: ApiNetwork) { await Promise.all([ removeNetworkAccountsValue(network, 'mnemonicsEncrypted'), removeNetworkAccountsValue(network, 'accounts'), - IS_DAPP_SUPPORTED && removeNetworkDapps(network), + getEnvironment().isDappSupported && removeNetworkDapps(network), ]); } @@ -142,7 +142,7 @@ export async function resetAccounts() { storage.removeItem('mnemonicsEncrypted'), storage.removeItem('accounts'), storage.removeItem('currentAccountId'), - IS_DAPP_SUPPORTED && removeAllDapps(), + getEnvironment().isDappSupported && removeAllDapps(), apiDb.nfts.clear(), ]); } @@ -151,7 +151,7 @@ export async function removeAccount(accountId: string, nextAccountId: string, ne await Promise.all([ removeAccountValue(accountId, 'mnemonicsEncrypted'), removeAccountValue(accountId, 'accounts'), - IS_DAPP_SUPPORTED && removeAccountDapps(accountId), + getEnvironment().isDappSupported && removeAccountDapps(accountId), apiDb.nfts.where({ accountId }).delete(), ]); diff --git a/src/api/methods/init.ts b/src/api/methods/init.ts index 99153177..ebc9c6f8 100644 --- a/src/api/methods/init.ts +++ b/src/api/methods/init.ts @@ -1,7 +1,7 @@ import type { ApiInitArgs, OnApiUpdate } from '../types'; -import { IS_DAPP_SUPPORTED, IS_SSE_SUPPORTED } from '../../config'; import { connectUpdater, startStorageMigration } from '../common/helpers'; +import { setEnvironment } from '../environment'; import { addHooks } from '../hooks'; import * as tonConnect from '../tonConnect'; import { resetupSseConnection, sendSseDisconnect } from '../tonConnect/sse'; @@ -16,6 +16,7 @@ addHooks({ // eslint-disable-next-line @typescript-eslint/no-unused-vars export default async function init(onUpdate: OnApiUpdate, args: ApiInitArgs) { connectUpdater(onUpdate); + const environment = setEnvironment(args); methods.initPolling(onUpdate, methods.isAccountActive); methods.initTransactions(onUpdate); @@ -23,14 +24,14 @@ export default async function init(onUpdate: OnApiUpdate, args: ApiInitArgs) { methods.initSwap(onUpdate); methods.initNfts(onUpdate); - if (IS_DAPP_SUPPORTED) { + if (environment.isDappSupported) { methods.initDapps(onUpdate); tonConnect.initTonConnect(onUpdate); } await startStorageMigration(onUpdate); - if (IS_SSE_SUPPORTED) { + if (environment.isSseSupported) { void resetupSseConnection(); } } diff --git a/src/api/methods/polling.ts b/src/api/methods/polling.ts index 0a7f94e3..f469444a 100644 --- a/src/api/methods/polling.ts +++ b/src/api/methods/polling.ts @@ -24,6 +24,7 @@ import { import { parseAccountId } from '../../util/account'; import { areDeepEqual } from '../../util/areDeepEqual'; import { compareActivities } from '../../util/compareActivities'; +import { buildCollectionByKey } from '../../util/iteratees'; import { logDebugError } from '../../util/logs'; import { pause } from '../../util/schedulers'; import blockchains from '../blockchains'; @@ -129,6 +130,7 @@ export async function setupBalanceBasedPolling(accountId: string, newestTxIds: A let nftUpdates: ApiNftUpdate[]; let i = 0; let doubleCheckTokensTime: number | undefined; + let tokenBalances: TokenBalanceParsed[] | undefined; const localOnUpdate = onUpdate; @@ -160,7 +162,6 @@ export async function setupBalanceBasedPolling(accountId: string, newestTxIds: A const balancesToUpdate: Record = {}; if (isTonBalanceChanged) { - changedTokenSlugs.push(TON_TOKEN_SLUG); balancesToUpdate[TON_TOKEN_SLUG] = balance; lastBalanceCache[accountId] = { @@ -173,7 +174,7 @@ export async function setupBalanceBasedPolling(accountId: string, newestTxIds: A if (isTonBalanceChanged || (doubleCheckTokensTime && doubleCheckTokensTime < Date.now())) { doubleCheckTokensTime = isTonBalanceChanged ? Date.now() + DOUBLE_CHECK_TOKENS_PAUSE : undefined; - const tokenBalances = await blockchain.getAccountTokenBalances(accountId).catch(logAndRescue); + tokenBalances = await blockchain.getAccountTokenBalances(accountId).catch(logAndRescue); if (!isUpdaterAlive(localOnUpdate) || !isAccountActive(accountId)) return; @@ -206,12 +207,12 @@ export async function setupBalanceBasedPolling(accountId: string, newestTxIds: A } // Fetch transactions for tokens with a changed balance - if (changedTokenSlugs.length) { + if (isTonBalanceChanged || changedTokenSlugs.length) { if (lastTxId) { await blockchain.waitUntilTransactionAppears(network, address, lastTxId); } - const newTxIds = await processNewTokenActivities(accountId, newestTxIds, changedTokenSlugs); + const newTxIds = await processNewActivities(accountId, newestTxIds, changedTokenSlugs, tokenBalances); newestTxIds = { ...newestTxIds, ...newTxIds }; } @@ -287,21 +288,51 @@ export async function setupStakingPolling(accountId: string) { } } -async function processNewTokenActivities( +async function processNewActivities( accountId: string, newestTxIds: ApiTxIdBySlug, tokenSlugs: string[], + tokenBalances?: TokenBalanceParsed[], ): Promise { const blockchain = blockchains[resolveBlockchainKey(accountId)!]; - if (!tokenSlugs.length) { - return {}; - } - let allTransactions: ApiTransactionActivity[] = []; let allActivities: ApiActivity[] = []; - const entries = await Promise.all(tokenSlugs.map(async (slug) => { + const result: [string, string | undefined][] = []; + + // Process TON transactions first + { + const slug = TON_TOKEN_SLUG; + let newestTxId = newestTxIds[slug]; + + const transactions = await blockchain.getTokenTransactionSlice( + accountId, slug, undefined, newestTxId, FIRST_TRANSACTIONS_LIMIT, + ); + const activities = await swapReplaceTransactions(accountId, transactions, slug); + + if (transactions.length) { + newestTxId = transactions[0]!.txId; + + allActivities = allActivities.concat(activities); + allTransactions = allTransactions.concat(transactions); + } + + result.push([slug, newestTxId]); + + // Find affected token wallets + const tokenBalanceByAddress = buildCollectionByKey(tokenBalances ?? [], 'jettonWallet'); + transactions.forEach(({ isIncoming, toAddress, fromAddress }) => { + const address = isIncoming ? fromAddress : toAddress; + const tokenBalance: TokenBalanceParsed | undefined = tokenBalanceByAddress[address]; + + if (tokenBalance && !tokenSlugs.includes(tokenBalance.slug)) { + tokenSlugs = tokenSlugs.concat([tokenBalance.slug]); + } + }); + } + + await Promise.all(tokenSlugs.map(async (slug) => { let newestTxId = newestTxIds[slug]; const transactions = await blockchain.getTokenTransactionSlice( @@ -315,7 +346,8 @@ async function processNewTokenActivities( allActivities = allActivities.concat(activities); allTransactions = allTransactions.concat(transactions); } - return [slug, newestTxId]; + + result.push([slug, newestTxId]); })); allTransactions = allTransactions.sort((a, b) => compareActivities(a, b, true)); @@ -330,7 +362,7 @@ async function processNewTokenActivities( accountId, }); - return Object.fromEntries(entries); + return Object.fromEntries(result); } export async function setupBackendPolling() { diff --git a/src/api/types/misc.ts b/src/api/types/misc.ts index f362aece..51580541 100644 --- a/src/api/types/misc.ts +++ b/src/api/types/misc.ts @@ -16,6 +16,7 @@ export interface AccountIdParsed { } export interface ApiInitArgs { + isElectron: boolean; } export interface ApiBaseToken { diff --git a/src/components/auth/AuthImportMnemonic.tsx b/src/components/auth/AuthImportMnemonic.tsx index 2d904f8e..00ed4a40 100644 --- a/src/components/auth/AuthImportMnemonic.tsx +++ b/src/components/auth/AuthImportMnemonic.tsx @@ -47,18 +47,7 @@ const AuthImportMnemonic = ({ isActive, isLoading, error }: OwnProps & StateProp const [mnemonic, setMnemonic] = useState>({}); const { isPortrait } = useDeviceScreen(); - const handlePasteMnemonic = useLastCallback((pastedText: string) => { - const pastedMnemonic = parsePastedText(pastedText); - - if (pastedMnemonic.length === 1 && document.activeElement?.id.startsWith('import-mnemonic-')) { - (document.activeElement as HTMLInputElement).value = pastedMnemonic[0]; - - const event = new Event('input'); - (document.activeElement as HTMLInputElement).dispatchEvent(event); - - return; - } - + const handleMnemonicSet = useLastCallback((pastedMnemonic: string[]) => { if (pastedMnemonic.length !== MNEMONIC_COUNT) { return; } @@ -73,6 +62,21 @@ const AuthImportMnemonic = ({ isActive, isLoading, error }: OwnProps & StateProp } }); + const handlePasteMnemonic = useLastCallback((pastedText: string) => { + const pastedMnemonic = parsePastedText(pastedText); + + if (pastedMnemonic.length === 1 && document.activeElement?.id.startsWith('import-mnemonic-')) { + (document.activeElement as HTMLInputElement).value = pastedMnemonic[0]; + + const event = new Event('input'); + (document.activeElement as HTMLInputElement).dispatchEvent(event); + + return; + } + + handleMnemonicSet(pastedMnemonic); + }); + useClipboardPaste(Boolean(isActive), handlePasteMnemonic); const isSubmitDisabled = useMemo(() => { @@ -82,6 +86,12 @@ const AuthImportMnemonic = ({ isActive, isLoading, error }: OwnProps & StateProp }, [mnemonic]); const handleSetWord = useLastCallback((value: string, index: number) => { + const pastedMnemonic = parsePastedText(value); + if (pastedMnemonic.length === MNEMONIC_COUNT) { + handleMnemonicSet(pastedMnemonic); + return; + } + setMnemonic({ ...mnemonic, [index]: value?.toLowerCase(), diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index 348b361c..ecceb7ed 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -3,7 +3,7 @@ import React, { } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; -import { ContentTab } from '../../global/types'; +import { ActiveTab, ContentTab } from '../../global/types'; import { IS_CAPACITOR } from '../../config'; import { selectCurrentAccount, selectCurrentAccountState } from '../../global/selectors'; @@ -67,6 +67,7 @@ function Main({ changeTransferToken, openStakingInfo, closeStakingInfo, + setLandscapeActionsActiveTabIndex, } = getActions(); // eslint-disable-next-line no-null/no-null @@ -154,6 +155,11 @@ function Main({ }, [currentTokenSlug, handleTokenCardClose, isPortrait]); const handleEarnClick = useLastCallback(() => { + if (!isPortrait) { + setLandscapeActionsActiveTabIndex({ index: ActiveTab.Stake }); + return; + } + if (isStakingActive || isUnstakeRequested) { openStakingInfo(); } else { diff --git a/src/components/main/sections/Card/AccountSelector.module.scss b/src/components/main/sections/Card/AccountSelector.module.scss index fd25a188..4c96ef27 100644 --- a/src/components/main/sections/Card/AccountSelector.module.scss +++ b/src/components/main/sections/Card/AccountSelector.module.scss @@ -1,6 +1,6 @@ @import "../../../../styles/mixins"; -.addressTitle { +.accountTitle { cursor: var(--custom-cursor, pointer); position: absolute; @@ -58,7 +58,7 @@ } } -.addressTitleShort { +.accountTitleShort { max-width: calc(100% - 6rem); } diff --git a/src/components/main/sections/Card/AccountSelector.tsx b/src/components/main/sections/Card/AccountSelector.tsx index bd0cfa20..fa4777e4 100644 --- a/src/components/main/sections/Card/AccountSelector.tsx +++ b/src/components/main/sections/Card/AccountSelector.tsx @@ -28,6 +28,7 @@ interface OwnProps { accountSelectorClassName?: string; menuButtonClassName?: string; noSettingsButton?: boolean; + noAccountSelector?: boolean; onQrScanPress?: NoneToVoidFunction; } @@ -49,6 +50,7 @@ function AccountSelector({ accountSelectorClassName, menuButtonClassName, noSettingsButton, + noAccountSelector, accounts, onQrScanPress, }: OwnProps & StateProps) { @@ -69,7 +71,7 @@ function AccountSelector({ const accountsAmount = useMemo(() => Object.keys(accounts || {}).length, [accounts]); const shouldRenderQrScannerButton = Boolean(onQrScanPress); - const shouldRenderSettingsButton = !noSettingsButton || !shouldRenderQrScannerButton; + const shouldRenderSettingsButton = !noSettingsButton && !shouldRenderQrScannerButton; useEffect(() => { if (isOpen && forceClose) { @@ -158,10 +160,10 @@ function AccountSelector({ transitionClassNames, accountSelectorClassName, ); - const addressTitleClassName = buildClassName( - styles.addressTitle, + const accountTitleClassName = buildClassName( + styles.accountTitle, accountClassName, - shouldRenderQrScannerButton && shouldRenderSettingsButton && styles.addressTitleShort, + shouldRenderQrScannerButton && shouldRenderSettingsButton && styles.accountTitleShort, ); const settingsButtonClassName = buildClassName( styles.menuButton, @@ -172,9 +174,11 @@ function AccountSelector({ function renderCurrentAccount() { return ( <> -
- {currentAccount?.title || shortenAddress(currentAccount?.address || '')} -
+ {!noAccountSelector && ( +
+ {currentAccount?.title || shortenAddress(currentAccount?.address || '')} +
+ )} {shouldRenderSettingsButton && (