diff --git a/changelogs/3.2.5.txt b/changelogs/3.2.5.txt new file mode 100644 index 00000000..619f4cd5 --- /dev/null +++ b/changelogs/3.2.5.txt @@ -0,0 +1 @@ +Bug fixes and performance improvements diff --git a/package-lock.json b/package-lock.json index 54e6e587..d14643b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mytonwallet", - "version": "3.2.4", + "version": "3.2.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mytonwallet", - "version": "3.2.4", + "version": "3.2.5", "license": "GPL-3.0-or-later", "dependencies": { "@awesome-cordova-plugins/core": "6.9.0", diff --git a/package.json b/package.json index 35953ec4..ad72760a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mytonwallet", - "version": "3.2.4", + "version": "3.2.5", "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": { diff --git a/public/version.txt b/public/version.txt index 351227fc..5ae69bd5 100644 --- a/public/version.txt +++ b/public/version.txt @@ -1 +1 @@ -3.2.4 +3.2.5 diff --git a/src/components/App.tsx b/src/components/App.tsx index ca8396c6..f1068821 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1,5 +1,4 @@ import React, { memo, useEffect, useLayoutEffect } from '../lib/teact/teact'; -import { setExtraStyles } from '../lib/teact/teact-dom'; import { getActions, withGlobal } from '../global'; import type { Theme } from '../global/types'; @@ -7,7 +6,7 @@ import { AppState } from '../global/types'; import { INACTIVE_MARKER, IS_ANDROID_DIRECT, IS_CAPACITOR } from '../config'; import { selectCurrentAccountSettings } from '../global/selectors'; -import { ACCENT_COLORS } from '../util/accentColor'; +import { useAccentColor } from '../util/accentColor'; import { setActiveTabChangeListener } from '../util/activeTabMonitor'; import buildClassName from '../util/buildClassName'; import { resolveRender } from '../util/renderPromise'; @@ -73,8 +72,6 @@ interface StateProps { accentColorIndex?: number; } -const HEX_80_PERCENT = 'CC'; -const HEX_10_PERCENT = '1A'; const APP_UPDATE_INTERVAL = (IS_ELECTRON && !IS_LINUX) || IS_ANDROID_DIRECT ? 5 * 60 * 1000 // 5 min : undefined; @@ -149,16 +146,7 @@ function App({ }, [accountId]); const appTheme = useAppTheme(theme); - const accentColor = accentColorIndex ? ACCENT_COLORS[appTheme][accentColorIndex] : undefined; - - useLayoutEffect(() => { - setExtraStyles(document.body, { - '--color-accent': accentColor || 'inherit', - '--color-accent-10o': accentColor ? `${accentColor}${HEX_10_PERCENT}` : 'inherit', - '--color-accent-button-background': accentColor || 'inherit', - '--color-accent-button-background-hover': accentColor ? `${accentColor}${HEX_80_PERCENT}` : 'inherit', - }); - }); + useAccentColor('body', appTheme, accentColorIndex); // eslint-disable-next-line consistent-return function renderContent(isActive: boolean, isFrom: boolean, currentKey: number) { diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index 24f3801b..f697e21f 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -8,7 +8,13 @@ import { ActiveTab, ContentTab, type Theme } from '../../global/types'; import { IS_CAPACITOR } from '../../config'; import { getStakingStateStatus } from '../../global/helpers/staking'; -import { selectAccountStakingState, selectCurrentAccount, selectCurrentAccountState } from '../../global/selectors'; +import { + selectAccountStakingState, + selectCurrentAccount, + selectCurrentAccountSettings, + selectCurrentAccountState, +} from '../../global/selectors'; +import { useAccentColor } from '../../util/accentColor'; import buildClassName from '../../util/buildClassName'; import { getStatusBarHeight } from '../../util/capacitor'; import { captureEvents, SwipeDirection } from '../../util/captureEvents'; @@ -18,6 +24,7 @@ import { } from '../../util/windowEnvironment'; import windowSize from '../../util/windowSize'; +import useAppTheme from '../../hooks/useAppTheme'; import useBackgroundMode, { isBackgroundModeActive } from '../../hooks/useBackgroundMode'; import { useOpenFromMainBottomSheet } from '../../hooks/useDelegatedBottomSheet'; import { useDeviceScreen } from '../../hooks/useDeviceScreen'; @@ -59,6 +66,7 @@ type StateProps = { isOnRampDisabled?: boolean; isMediaViewerOpen?: boolean; theme: Theme; + accentColorIndex?: number; }; const STICKY_CARD_INTERSECTION_THRESHOLD = -3.75 * REM; @@ -76,6 +84,7 @@ function Main({ isOnRampDisabled, isMediaViewerOpen, theme, + accentColorIndex, }: OwnProps & StateProps) { const { selectToken, @@ -94,6 +103,8 @@ function Main({ const cardRef = useRef(null); // eslint-disable-next-line no-null/no-null const portraitContainerRef = useRef(null); + // eslint-disable-next-line no-null/no-null + const landscapeContainerRef = useRef(null); const [canRenderStickyCard, setCanRenderStickyCard] = useState(false); const [shouldRenderDarkStatusBar, setShouldRenderDarkStatusBar] = useState(false); const safeAreaTop = IS_CAPACITOR ? getStatusBarHeight() : windowSize.get().safeAreaTop; @@ -179,6 +190,9 @@ function Main({ }); }, [currentTokenSlug, handleTokenCardClose, isPortrait]); + const appTheme = useAppTheme(theme); + useAccentColor(isPortrait ? portraitContainerRef : landscapeContainerRef, appTheme, accentColorIndex); + const handleEarnClick = useLastCallback((stakingId?: string) => { if (stakingId) changeCurrentStaking({ stakingId }); @@ -222,7 +236,7 @@ function Main({ function renderLandscapeLayout() { return ( -
+
@@ -275,6 +289,7 @@ export default memo( isSwapDisabled, isOnRampDisabled, theme: global.settings.theme, + accentColorIndex: selectCurrentAccountSettings(global)?.accentColorIndex, }; }, (global, _, stickToFirst) => stickToFirst(global.currentAccountId), diff --git a/src/components/main/modals/QrScannerModal.module.scss b/src/components/main/modals/QrScannerModal.module.scss index 4ca7be44..ab0e206c 100644 --- a/src/components/main/modals/QrScannerModal.module.scss +++ b/src/components/main/modals/QrScannerModal.module.scss @@ -147,6 +147,7 @@ .documentRoot { --color-app-background: tranparent !important; --color-background-second: transparent !important; + --color-background-window: transparent !important; visibility: hidden; diff --git a/src/components/main/sections/Card/AccountSelector.module.scss b/src/components/main/sections/Card/AccountSelector.module.scss index 9522fcc5..739fc764 100644 --- a/src/components/main/sections/Card/AccountSelector.module.scss +++ b/src/components/main/sections/Card/AccountSelector.module.scss @@ -412,7 +412,7 @@ } :global(.MtwCard__standard.MtwCard__lightText) & { - text-shadow: 0 0 1.5rem #000000B2; + text-shadow: 0 0 1.5rem #000; } :global(.MtwCard__standard.MtwCard__darkText) & { diff --git a/src/components/main/sections/Content/Explore.module.scss b/src/components/main/sections/Content/Explore.module.scss index 945acf94..16f95f0c 100644 --- a/src/components/main/sections/Content/Explore.module.scss +++ b/src/components/main/sections/Content/Explore.module.scss @@ -143,7 +143,7 @@ font-size: 0.75rem; font-weight: 700; line-height: 1.125rem; - color: var(--color-white); + color: var(--color-accent-button-text); background: var(--color-accent); border-radius: 0.3125rem; diff --git a/src/components/settings/SettingsSecurity.tsx b/src/components/settings/SettingsSecurity.tsx index f0352d29..12fc93ac 100644 --- a/src/components/settings/SettingsSecurity.tsx +++ b/src/components/settings/SettingsSecurity.tsx @@ -173,6 +173,11 @@ function SettingsSecurity({ }); const openBackupPage = useLastCallback(() => { + if (!isMultichainAccount) { + openSettingsSlide(); + return; + } + setCurrentSlide(SLIDES.backup); setNextKey(SLIDES.safetyRules); }); @@ -256,12 +261,6 @@ function SettingsSecurity({ openNewPasswordSlide(); }); - const handleOpenBackupWallet = useLastCallback(() => { - setCurrentSlide(SLIDES.backup); - // Resetting next key to undefined unmounts and destroys components with mnemonic and private key - setNextKey(undefined); - }); - const handleOpenPrivateKeySafetyRules = useLastCallback(() => { setBackupType('key'); setCurrentSlide(SLIDES.safetyRules); @@ -274,6 +273,18 @@ function SettingsSecurity({ setNextKey(SLIDES.secretWords); }); + const handleOpenBackupWallet = useLastCallback(() => { + if (!isMultichainAccount) { + if (hasMnemonicWallet) handleOpenSecretWordsSafetyRules(); + else handleOpenPrivateKeySafetyRules(); + return; + } + + setCurrentSlide(SLIDES.backup); + // Resetting next key to undefined unmounts and destroys components with mnemonic and private key + setNextKey(undefined); + }); + const handleOpenPrivateKey = useLastCallback(() => { setCurrentSlide(SLIDES.privateKey); setNextKey(SLIDES.backup); diff --git a/src/components/swap/SwapDexChooser.tsx b/src/components/swap/SwapDexChooser.tsx index 40d5cb80..5555ab67 100644 --- a/src/components/swap/SwapDexChooser.tsx +++ b/src/components/swap/SwapDexChooser.tsx @@ -220,7 +220,7 @@ function SwapDexChooser({
diff --git a/src/components/swap/SwapInitial.tsx b/src/components/swap/SwapInitial.tsx index d2a3613f..c6457a9b 100644 --- a/src/components/swap/SwapInitial.tsx +++ b/src/components/swap/SwapInitial.tsx @@ -4,7 +4,10 @@ import React, { import { getActions, getGlobal, withGlobal } from '../../global'; import type { ApiChain } from '../../api/types'; -import type { Account, GlobalState, UserSwapToken } from '../../global/types'; +import type { + Account, ActionPayloads, AssetPairs, GlobalState, UserSwapToken, +} from '../../global/types'; +import type { LangFn } from '../../hooks/useLang'; import { SwapInputSource, SwapState, SwapType } from '../../global/types'; import { @@ -109,6 +112,7 @@ function SwapInitial({ setSwapCexAddress, toggleSwapSettingsModal, authorizeDiesel, + showNotification, } = getActions(); const lang = useLang(); @@ -232,9 +236,14 @@ function SwapInitial({ const isPriceImpactError = priceImpact >= MAX_PRICE_IMPACT_VALUE; const isCrosschain = swapType === SwapType.CrosschainFromWallet || swapType === SwapType.CrosschainToWallet; - const isReverseProhibited = useMemo(() => { - return isCrosschain || pairs?.bySlug?.[currentTokenInSlug]?.[currentTokenOutSlug]?.isReverseProhibited; - }, [currentTokenInSlug, currentTokenOutSlug, isCrosschain, pairs?.bySlug]); + const [isBuyAmountInputDisabled, handleBuyAmountInputClick] = useReverseProhibited( + isCrosschain, + pairs?.bySlug, + currentTokenInSlug, + currentTokenOutSlug, + showNotification, + lang, + ); const handleEstimateSwap = useLastCallback((shouldBlock: boolean) => { if (!isActive || isBackgroundModeActive()) return; @@ -553,9 +562,10 @@ function SwapInitial({ className={styles.amountInputBuy} value={amountOutValue} isLoading={isEstimating && inputSource === SwapInputSource.In} - disabled={isReverseProhibited} + disabled={isBuyAmountInputDisabled} onChange={handleAmountOutChange} onPressEnter={handleSubmit} + onInputClick={handleBuyAmountInputClick} decimals={tokenOut?.decimals} labelClassName={styles.inputLabel} inputClassName={styles.amountInputInner} @@ -640,6 +650,30 @@ function useTokenTransitionKey(tokenSlug: string) { return transitionKeyRef.current; } +function useReverseProhibited( + isCrosschain: boolean, + pairsBySlug: Record | undefined, + currentTokenInSlug: string, + currentTokenOutSlug: string, + showNotification: (arg: ActionPayloads['showNotification']) => void, + lang: LangFn, +) { + const isReverseProhibited = isCrosschain + || pairsBySlug?.[currentTokenInSlug]?.[currentTokenOutSlug]?.isReverseProhibited; + const isBuyAmountInputDisabled = isReverseProhibited; + + const handleBuyAmountInputClick = useMemo(() => { + return isReverseProhibited + ? () => { + void vibrate(); + showNotification({ message: lang('$swap_reverse_prohibited') }); + } + : undefined; + }, [isReverseProhibited, lang, showNotification]); + + return [isBuyAmountInputDisabled, handleBuyAmountInputClick] as const; +} + function AnimatedArrows({ onClick }: { onClick?: NoneToVoidFunction }) { const animationLevel = getGlobal().settings.animationLevel; const shouldAnimate = (animationLevel === ANIMATION_LEVEL_MAX); diff --git a/src/components/ui/Input.module.scss b/src/components/ui/Input.module.scss index 00925d53..8618e0cb 100644 --- a/src/components/ui/Input.module.scss +++ b/src/components/ui/Input.module.scss @@ -154,7 +154,6 @@ } &.disabled { - pointer-events: none; cursor: default !important; opacity: 1; @@ -166,7 +165,7 @@ &:hover, &:focus { - &.isEmpty::before { + &.isEmpty:not(.disabled)::before { color: var(--color-interactive-input-text-hover-active); } } diff --git a/src/components/ui/RichNumberInput.tsx b/src/components/ui/RichNumberInput.tsx index 3b032653..07e4339c 100644 --- a/src/components/ui/RichNumberInput.tsx +++ b/src/components/ui/RichNumberInput.tsx @@ -1,7 +1,5 @@ import type { TeactNode } from '../../lib/teact/teact'; -import React, { - memo, useLayoutEffect, useRef, useState, -} from '../../lib/teact/teact'; +import React, { memo, useLayoutEffect, useRef } from '../../lib/teact/teact'; import { FRACTION_DIGITS, WHOLE_PART_DELIMITER } from '../../config'; import { requestMutation } from '../../lib/fasterdom/fasterdom'; @@ -33,6 +31,8 @@ type OwnProps = { onBlur?: NoneToVoidFunction; onFocus?: NoneToVoidFunction; onPressEnter?: (e: React.KeyboardEvent) => void; + /** Expected to fire when regardless of the `disabled` prop value */ + onInputClick?: NoneToVoidFunction; decimals?: number; disabled?: boolean; isStatic?: boolean; @@ -57,6 +57,7 @@ function RichNumberInput({ onBlur, onFocus, onPressEnter, + onInputClick, decimals = FRACTION_DIGITS, disabled = false, isStatic = false, @@ -65,17 +66,8 @@ function RichNumberInput({ const inputRef = useRef(null); const lang = useLang(); const [hasFocus, markHasFocus, unmarkHasFocus] = useFlag(false); - const [isContentEditable, setContentEditable] = useState(!disabled); const { updateFontScale, isFontChangedRef } = useFontScale(inputRef); - const handleLoadingHtml = useLastCallback((input: HTMLInputElement, parts?: RegExpMatchArray) => { - const newHtml = parts ? buildContentHtml({ values: parts, suffix, decimals }) : ''; - input.innerHTML = newHtml; - setContentEditable(false); - - return newHtml; - }); - const handleNumberHtml = useLastCallback((input: HTMLInputElement, parts?: RegExpMatchArray) => { const newHtml = parts ? buildContentHtml({ values: parts, suffix, decimals }) : ''; const restoreCaretPosition = document.activeElement === inputRef.current @@ -83,7 +75,6 @@ function RichNumberInput({ : undefined; input.innerHTML = newHtml; - setContentEditable(!disabled); restoreCaretPosition?.(); return newHtml; @@ -91,17 +82,13 @@ function RichNumberInput({ const updateHtml = useLastCallback((parts?: RegExpMatchArray) => { const input = inputRef.current!; - const content = isLoading ? handleLoadingHtml(input, parts) : handleNumberHtml(input, parts); + const content = handleNumberHtml(input, parts); const textContent = parts?.[0] || ''; if (textContent.length > MIN_LENGTH_FOR_SHRINK || isFontChangedRef.current) { updateFontScale(content); } - if (content.length) { - input.classList.remove(styles.isEmpty); - } else { - input.classList.add(styles.isEmpty); - } + input.classList.toggle(styles.isEmpty, !content.length); }); useLayoutEffect(() => { @@ -163,6 +150,7 @@ function RichNumberInput({ styles.input_rich, !value && styles.isEmpty, valueClassName, + disabled && styles.disabled, isLoading && styles.isLoading, 'rounded-font', ); @@ -192,7 +180,7 @@ function RichNumberInput({ {/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
{children}
diff --git a/src/config.ts b/src/config.ts index 2c96af54..60e40beb 100644 --- a/src/config.ts +++ b/src/config.ts @@ -134,7 +134,7 @@ export const PROXY_HOSTS = process.env.PROXY_HOSTS; export const TINY_TRANSFER_MAX_COST = 0.01; export const IMAGE_CACHE_NAME = 'mtw-image'; -export const LANG_CACHE_NAME = 'mtw-lang-156'; +export const LANG_CACHE_NAME = 'mtw-lang-158'; export const LANG_LIST: LangItem[] = [{ langCode: 'en', diff --git a/src/global/actions/ui/misc.ts b/src/global/actions/ui/misc.ts index 4f0d163e..fdf9f5bd 100644 --- a/src/global/actions/ui/misc.ts +++ b/src/global/actions/ui/misc.ts @@ -571,7 +571,6 @@ addActionHandler('requestOpenQrScanner', async (global, actions) => { addActionHandler('closeQrScanner', (global) => { if (IS_DELEGATED_BOTTOM_SHEET) { callActionInMain('closeQrScanner'); - return undefined; } return { diff --git a/src/i18n/de.yaml b/src/i18n/de.yaml index 804a3f71..679c71eb 100644 --- a/src/i18n/de.yaml +++ b/src/i18n/de.yaml @@ -710,7 +710,7 @@ You will spend %amount% **less**.: Sie werden %amount% **weniger** ausgeben. Best: Beste Use Best Rate: Beste Rate nutzen Switch to %dex_name%: Zu %dex_name% wechseln -Built-in Dex Aggregator: Integrierter Dex-Aggregator +Built-in DEX Aggregator: Integrierter DEX-Aggregator Add tokens: Token hinzufügen No tokens yet: Noch keine Token $no_tokens_description: Erhalten Sie Token von einer anderen Wallet oder aktivieren Sie einfach bevorzugte Token in den Einstellungen zur Überwachung. @@ -724,3 +724,4 @@ Push Notifications: Push-Benachrichtigungen Select up to %count% wallets for notifications: Wählen Sie bis zu %count% Wallets für Benachrichtigungen aus $swap_aggregator_fee_tooltip: Ein integrierter **DEX-Aggregator** findet den **besten Kurs** über verfügbare DEXs. Die Servicegebühr beträgt %percent%. Accumulated Rewards: Akkumulierte Belohnungen +$swap_reverse_prohibited: Das Festlegen des Kaufbetrags ist für dieses Tokenpaar nicht möglich diff --git a/src/i18n/en.yaml b/src/i18n/en.yaml index 8105df49..3cbd9213 100644 --- a/src/i18n/en.yaml +++ b/src/i18n/en.yaml @@ -710,7 +710,7 @@ You will spend %amount% **less**.: You will spend %amount% **less**. Best: Best Use Best Rate: Use Best Rate Switch to %dex_name%: Switch to %dex_name% -Built-in Dex Aggregator: Built-in Dex Aggregator +Built-in DEX Aggregator: Built-in DEX Aggregator Add tokens: Add tokens No tokens yet: No tokens yet $no_tokens_description: Receive tokens from another wallet, or simply enable preferred tokens in the settings for monitoring. @@ -724,3 +724,4 @@ Push Notifications: Push Notifications Select up to %count% wallets for notifications: Select up to %count% wallets for notifications $swap_aggregator_fee_tooltip: A built-in **DEX aggregator** finds the **best rate** across available DEXes. Service fee is %percent%. Accumulated Rewards: Accumulated Rewards +$swap_reverse_prohibited: Setting the buy amount is impossible for this pair of tokens diff --git a/src/i18n/es.yaml b/src/i18n/es.yaml index b9c5cc6e..449d71d3 100644 --- a/src/i18n/es.yaml +++ b/src/i18n/es.yaml @@ -708,7 +708,7 @@ You will spend %amount% **less**.: Gastarás %amount% **menos**. Best: Mejor Use Best Rate: Usar la mejor tasa Switch to %dex_name%: Cambiar a %dex_name% -Built-in Dex Aggregator: Agregador Dex incorporado +Built-in DEX Aggregator: Agregador DEX incorporado Add tokens: Añadir tokens No tokens yet: Aún no hay tokens $no_tokens_description: Recibe tokens de otra billetera o simplemente habilita los tokens preferidos en la configuración para monitorear. @@ -717,8 +717,9 @@ Install Card: Instalar tarjeta Reset Card: Restablecer tarjeta Apply Palette: Aplicar paleta Reset Palette: Restablecer paleta -Notifications & Sounds: Notificaciones y Sonidos +Notifications & Sounds: Notificaciones y sonidos Push Notifications: Notificaciones Push Select up to %count% wallets for notifications: Selecciona hasta %count% billeteras para notificaciones $swap_aggregator_fee_tooltip: Un **agregador DEX** incorporado encuentra la **mejor tasa** entre los DEX disponibles. La tarifa de servicio es del %percent%. Accumulated Rewards: Recompensas acumuladas +$swap_reverse_prohibited: Configurar la cantidad de compra es imposible para este par de tokens diff --git a/src/i18n/pl.yaml b/src/i18n/pl.yaml index e5c943b7..a4182889 100644 --- a/src/i18n/pl.yaml +++ b/src/i18n/pl.yaml @@ -714,7 +714,7 @@ You will spend %amount% **less**.: Wydasz %amount% **mniej**. Best: Najlepszy Use Best Rate: Użyj najlepszego kursu Switch to %dex_name%: Przełącz na %dex_name% -Built-in Dex Aggregator: Wbudowany agregator Dex +Built-in DEX Aggregator: Wbudowany agregator DEX Add tokens: Dodaj tokeny No tokens yet: Brak tokenów $no_tokens_description: Otrzymaj tokeny z innego portfela lub po prostu włącz preferowane tokeny w ustawieniach, aby je monitorować. @@ -723,8 +723,9 @@ Install Card: Zainstaluj kartę Reset Card: Zresetuj kartę Apply Palette: Zastosuj paletę Reset Palette: Zresetuj paletę -Notifications & Sounds: Powiadomienia i Dźwięki +Notifications & Sounds: Powiadomienia i dźwięki Push Notifications: Powiadomienia Push Select up to %count% wallets for notifications: Wybierz do %count% portfeli do powiadomień $swap_aggregator_fee_tooltip: Wbudowany **agregator DEX** znajduje **najlepszy kurs** w dostępnych DEX-ach. Opłata za usługę wynosi %percent%. Accumulated Rewards: Nagromadzone nagrody +$swap_reverse_prohibited: Ustawienie kwoty zakupu jest niemożliwe dla tej pary tokenów diff --git a/src/i18n/ru.yaml b/src/i18n/ru.yaml index 8af6d080..33ab349a 100644 --- a/src/i18n/ru.yaml +++ b/src/i18n/ru.yaml @@ -703,13 +703,13 @@ Biometric authentication failed: Ошибка биометрической ау Reset biometrics in security settings, or use a passcode.: Сбросьте биометрию в настройках безопасности или используйте пароль. Best Rate: Лучший курс via %dex_name%: через %dex_name% -$swap_dex_chooser_rate_title: Проанализированные актуальные курсы на **DeDust** и **STON.fi** для использования **лучшего курса**. +$swap_dex_chooser_rate_title: Мы проанализировали актуальные курсы на **DeDust** и **STON.fi**, чтобы выбрать **наиболее выгодный курс**. You will receive %amount% **more**.: Вы получите на %amount% **больше**. You will spend %amount% **less**.: Вы потратите на %amount% **меньше**. Best: Лучший Use Best Rate: Использовать лучший курс Switch to %dex_name%: Переключиться на %dex_name% -Built-in Dex Aggregator: Встроенный агрегатор Dex +Built-in DEX Aggregator: Встроенный DEX-агрегатор Add tokens: Добавить токены No tokens yet: Токенов пока нет $no_tokens_description: Получите токены из другого кошелька или просто включите нужные токены в настройках для мониторинга. @@ -718,8 +718,9 @@ Install Card: Установить карту Reset Card: Сбросить карту Apply Palette: Применить палитру Reset Palette: Сбросить палитру -Notifications & Sounds: Уведомления и Звук +Notifications & Sounds: Уведомления и звуки Push Notifications: Уведомления Select up to %count% wallets for notifications: Выберите до %count% кошельков $swap_aggregator_fee_tooltip: Встроенный **DEX-агрегатор** помогает выбрать **лучший курс** среди доступных бирж. Комиссия сервиса — %percent%. Accumulated Rewards: Накопленные награды +$swap_reverse_prohibited: Указание суммы покупки невозможно для этой пары токенов diff --git a/src/i18n/th.yaml b/src/i18n/th.yaml index d489d05f..68ec1732 100644 --- a/src/i18n/th.yaml +++ b/src/i18n/th.yaml @@ -712,7 +712,7 @@ You will spend %amount% **less**.: คุณจะใช้จ่าย %amount% Best: ที่ดีที่สุด Use Best Rate: ใช้อัตราที่ดีที่สุด Switch to %dex_name%: เปลี่ยนเป็น %dex_name% -Built-in Dex Aggregator: ตัวรวบรวม Dex ในตัว +Built-in DEX Aggregator: ตัวรวบรวม DEX ในตัว Add tokens: เพิ่มโทเค็น No tokens yet: ยังไม่มีโทเคน $no_tokens_description: รับโทเคนจากกระเป๋าเงินอื่น หรือเพียงแค่เปิดใช้งานโทเคนที่ต้องการในการตั้งค่าเพื่อการติดตาม @@ -726,3 +726,4 @@ Push Notifications: การแจ้งเตือนแบบพุช Select up to %count% wallets for notifications: เลือกกระเป๋าเงินได้สูงสุด %count% ใบสำหรับการแจ้งเตือน $swap_aggregator_fee_tooltip: ตัวรวบรวม **DEX ในตัว** จะค้นหา **อัตราที่ดีที่สุด** จาก DEX ที่มีอยู่ ค่าบริการคือ %percent%. Accumulated Rewards: รางวัลที่สะสม +$swap_reverse_prohibited: การตั้งค่าจำนวนซื้อเป็นไปไม่ได้สำหรับคู่โทเค็นนี้ diff --git a/src/i18n/tr.yaml b/src/i18n/tr.yaml index 8384ad44..621e52f9 100644 --- a/src/i18n/tr.yaml +++ b/src/i18n/tr.yaml @@ -709,7 +709,7 @@ You will spend %amount% **less**.: "%amount% **daha az** harcayacaksınız." Best: En İyi Use Best Rate: En İyi Oranı Kullan Switch to %dex_name%: "%dex_name%'e Geç" -Built-in Dex Aggregator: Dahili Dex Toplayıcı +Built-in DEX Aggregator: Dahili DEX Toplayıcı Add tokens: Jeton ekle No tokens yet: Henüz jeton yok $no_tokens_description: Başka bir cüzdandan jeton alın veya izleme için tercih edilen jetonları ayarlardan etkinleştirin. @@ -718,8 +718,9 @@ Install Card: Kartı yükle Reset Card: Kartı sıfırla Apply Palette: Paleti uygula Reset Palette: Paleti sıfırla -Notifications & Sounds: Bildirimler ve Sesler +Notifications & Sounds: Bildirimler ve sesler Push Notifications: Anlık Bildirimler Select up to %count% wallets for notifications: Bildirimler için en fazla %count% cüzdan seçin $swap_aggregator_fee_tooltip: Dahili bir **DEX toplayıcı**, mevcut DEX'ler arasında **en iyi oranı** bulur. Hizmet ücreti %percent%. Accumulated Rewards: Birikmiş ödüller +$swap_reverse_prohibited: Bu token çifti için satın alma miktarını ayarlamak imkansız diff --git a/src/i18n/uk.yaml b/src/i18n/uk.yaml index bbb6427c..49ea6871 100644 --- a/src/i18n/uk.yaml +++ b/src/i18n/uk.yaml @@ -714,7 +714,7 @@ You will spend %amount% **less**.: Ви витратите на %amount% **ме Best: Найкращий Use Best Rate: Використати найкращий курс Switch to %dex_name%: Перемкнутися на %dex_name% -Built-in Dex Aggregator: Вбудований агрегатор Dex +Built-in DEX Aggregator: Вбудований агрегатор DEX Add tokens: Додати токени No tokens yet: Ще немає токенів $no_tokens_description: Отримайте токени з іншого гаманця або просто увімкніть потрібні токени в налаштуваннях для моніторингу. @@ -723,8 +723,9 @@ Install Card: Встановити карту Reset Card: Скинути карту Apply Palette: Застосувати палітру Reset Palette: Скинути палітру -Notifications & Sounds: Сповіщення та Звуки +Notifications & Sounds: Сповіщення та звуки Push Notifications: Сповіщення Select up to %count% wallets for notifications: Виберіть до %count% гаманців для сповіщень $swap_aggregator_fee_tooltip: Вбудований **агрегатор DEX** знаходить **найкращий курс** серед доступних DEX. Плата за послугу становить %percent%. Accumulated Rewards: Накоплені нагороди +$swap_reverse_prohibited: Встановити суму покупки для цієї пари токенів неможливо diff --git a/src/i18n/zh-Hans.yaml b/src/i18n/zh-Hans.yaml index 10bb3634..c8854411 100644 --- a/src/i18n/zh-Hans.yaml +++ b/src/i18n/zh-Hans.yaml @@ -698,7 +698,7 @@ You will spend %amount% **less**.: 您将少花 %amount%。 Best: 最佳 Use Best Rate: 使用最佳汇率 Switch to %dex_name%: 切换到 %dex_name% -Built-in Dex Aggregator: 内置Dex聚合器 +Built-in DEX Aggregator: 内置Dex聚合器 Add tokens: 添加令牌 No tokens yet: 尚无代币 $no_tokens_description: 从其他钱包接收代币,或者在设置中启用首选代币进行监控。 @@ -712,3 +712,4 @@ Push Notifications: 推送通知 Select up to %count% wallets for notifications: 选择最多 %count% 个钱包接收通知 $swap_aggregator_fee_tooltip: 内置的 **DEX 聚合器** 会在可用的 DEX 中找到 **最佳汇率**。服务费为 %percent%。 Accumulated Rewards: 累积奖励 +$swap_reverse_prohibited: 无法为这对代币设置购买金额 diff --git a/src/i18n/zh-Hant.yaml b/src/i18n/zh-Hant.yaml index f9198321..231e845f 100644 --- a/src/i18n/zh-Hant.yaml +++ b/src/i18n/zh-Hant.yaml @@ -698,7 +698,7 @@ You will spend %amount% **less**.: 您將少花 %amount%。 Best: 最佳 Use Best Rate: 使用最佳匯率 Switch to %dex_name%: 切換到 %dex_name% -Built-in Dex Aggregator: 內建Dex聚合器 +Built-in DEX Aggregator: 內建Dex聚合器 Add tokens: 添加令牌 No tokens yet: 尚無代幣 $no_tokens_description: 從其他錢包接收代幣,或者在設定中啟用首選代幣進行監控。 @@ -712,3 +712,4 @@ Push Notifications: 推送通知 Select up to %count% wallets for notifications: 選擇最多 %count% 個錢包接收通知 $swap_aggregator_fee_tooltip: 內建的 **DEX 聚合器** 會在可用的 DEX 中找到 **最佳匯率**。服務費為 %percent%。 Accumulated Rewards: 累積獎勵 +$swap_reverse_prohibited: 無法為這對代幣設置購買金額 diff --git a/src/styles/mtwCustomCard.scss b/src/styles/mtwCustomCard.scss index f70ec740..29f8b40f 100644 --- a/src/styles/mtwCustomCard.scss +++ b/src/styles/mtwCustomCard.scss @@ -18,7 +18,7 @@ .MtwCard__lightText { --main-text: #FFF; --second-text: #FFFFFF99; - --action-color: #FFFFFF99; + --action-color: #FFFFFFBB; --action-color-hover: #FFF; --color-card-default-text: #FFF; --color-card-green-text: #FFF; diff --git a/src/util/accentColor.ts b/src/util/accentColor.ts index 13212aee..f5e6a528 100644 --- a/src/util/accentColor.ts +++ b/src/util/accentColor.ts @@ -1,3 +1,9 @@ +import type { RefObject } from '../lib/teact/teact'; +import { useLayoutEffect } from '../lib/teact/teact'; +import { setExtraStyles } from '../lib/teact/teact-dom'; + +import type { AppTheme } from '../global/types'; + import quantize from '../lib/quantize'; import { euclideanDistance, hex2rgb } from './colors'; import { logDebugError } from './logs'; @@ -26,6 +32,28 @@ export const ACCENT_SILVER_INDEX = 14; export const ACCENT_GOLD_INDEX = 15; export const ACCENT_BNW_INDEX = 16; +const HEX_80_PERCENT = 'CC'; +const HEX_10_PERCENT = '1A'; + +export function useAccentColor( + elementRefOrBody: RefObject | 'body', + appTheme: AppTheme, + accentColorIndex: number | undefined, +) { + const accentColor = accentColorIndex ? ACCENT_COLORS[appTheme][accentColorIndex] : undefined; + + useLayoutEffect(() => { + setExtraStyles(elementRefOrBody === 'body' ? document.body : elementRefOrBody.current, { + '--color-accent': accentColor || 'inherit', + '--color-accent-10o': accentColor ? `${accentColor}${HEX_10_PERCENT}` : 'inherit', + '--color-accent-button-background': accentColor || 'inherit', + '--color-accent-button-background-hover': accentColor ? `${accentColor}${HEX_80_PERCENT}` : 'inherit', + '--color-accent-button-text': accentColor === '#FFFFFF' ? '#000000' : 'inherit', + '--color-accent-button-text-hover': accentColor === '#FFFFFF' ? '#000000' : 'inherit', + }); + }, [elementRefOrBody, accentColor]); +} + export function extractAccentColorIndex(img: HTMLImageElement) { try { const palette = extractPaletteFromImage(img);