From 41fba932ddd760ce74712d9f82ca8509fdbdb6ab Mon Sep 17 00:00:00 2001 From: huhuanming Date: Fri, 13 Dec 2024 16:33:44 +0800 Subject: [PATCH] feat: pre-emptively inject the tradingView script for desktop & disable search and numeric shortcuts in developer mode & allow navigation to swap interfaces with unsupported tokens. (#6344) --- apps/desktop/src-electron/app.ts | 13 ++++ apps/desktop/src-electron/config.ts | 1 + apps/desktop/src-electron/libs/shortcuts.ts | 77 ++++++++++++++----- apps/desktop/src-electron/libs/store.ts | 20 +++++ apps/desktop/src-electron/preload.ts | 9 +++ .../src/states/jotai/atoms/devSettings.ts | 7 +- .../TradingView/WebView.desktop.tsx | 16 ++-- .../Market/components/MarketHomeList.tsx | 2 +- .../Market/components/MarketTradeButton.tsx | 15 +--- .../src/views/Market/components/tradeHook.tsx | 71 +++++++++-------- .../DevSettingsSection/CrashDevSettings.tsx | 3 - .../SentryCrashSettings.tsx | 10 +++ .../pages/List/DevSettingsSection/index.tsx | 27 ++++++- .../modules3rdParty/sentry/index.desktop.ts | 7 +- .../src/modules3rdParty/sentry/index.ext.ts | 6 +- .../modules3rdParty/sentry/index.native.ts | 7 +- .../src/modules3rdParty/sentry/index.ts | 6 +- 17 files changed, 210 insertions(+), 87 deletions(-) diff --git a/apps/desktop/src-electron/app.ts b/apps/desktop/src-electron/app.ts index d0d10e03d18..368147ee2cd 100644 --- a/apps/desktop/src-electron/app.ts +++ b/apps/desktop/src-electron/app.ts @@ -536,6 +536,19 @@ function createMainWindow() { safelyBrowserWindow?.reload(); }); + ipcMain.on( + ipcMessageKeys.APP_UPDATE_DISABLE_SHORTCUTS, + ( + event, + params: { + disableNumberShortcuts: boolean; + disableSearchAndAccountSelectorShortcuts: boolean; + }, + ) => { + store.setDisableKeyboardShortcuts(params); + }, + ); + ipcMain.on( ipcMessageKeys.APP_GET_MEDIA_ACCESS_STATUS, (event, prefType: IMediaType) => { diff --git a/apps/desktop/src-electron/config.ts b/apps/desktop/src-electron/config.ts index 69afa031a12..280bb123d76 100644 --- a/apps/desktop/src-electron/config.ts +++ b/apps/desktop/src-electron/config.ts @@ -55,6 +55,7 @@ export const ipcMessageKeys = { APP_GET_BUNDLE_INFO: 'app/getBundleInfo', APP_OPEN_LOGGER_FILE: 'app/openLoggerFile', APP_TEST_CRASH: 'app/testCrash', + APP_UPDATE_DISABLE_SHORTCUTS: 'app/updateDisableShortcuts', // Theme THEME_UPDATE: 'theme/update', diff --git a/apps/desktop/src-electron/libs/shortcuts.ts b/apps/desktop/src-electron/libs/shortcuts.ts index 56cc32f9683..682f2251327 100644 --- a/apps/desktop/src-electron/libs/shortcuts.ts +++ b/apps/desktop/src-electron/libs/shortcuts.ts @@ -1,30 +1,65 @@ import { app, globalShortcut } from 'electron'; -import type { EShortcutEvents } from '@onekeyhq/shared/src/shortcuts/shortcuts.enum'; -import { shortcutsMap } from '@onekeyhq/shared/src/shortcuts/shortcuts.enum'; +import { + EShortcutEvents, + shortcutsMap, +} from '@onekeyhq/shared/src/shortcuts/shortcuts.enum'; import { shortcutsKeys } from '@onekeyhq/shared/src/shortcuts/shortcutsKeys.enum'; -export function registerShortcuts(callback: (event: EShortcutEvents) => void) { +import * as store from './store'; + +export function registerShortcuts( + callback: (eventName: EShortcutEvents) => void, +) { void app.whenReady().then(() => { - Object.entries(shortcutsMap).forEach(([event, { keys }]) => { + const { disableNumberShortcuts, disableSearchAndAccountSelectorShortcuts } = + store.getDisableKeyboardShortcuts(); + Object.entries(shortcutsMap).forEach(([eventName, { keys }]) => { + if (disableNumberShortcuts && keys?.includes(shortcutsKeys.CmdOrCtrl)) { + return; + } + if ( + disableSearchAndAccountSelectorShortcuts && + [ + EShortcutEvents.SearchInPage, + EShortcutEvents.AccountSelector, + ].includes(eventName as EShortcutEvents) + ) { + return; + } + + if ( + disableNumberShortcuts && + [ + EShortcutEvents.TabWallet, + EShortcutEvents.TabEarn, + EShortcutEvents.TabSwap, + EShortcutEvents.TabMarket, + EShortcutEvents.TabBrowser, + EShortcutEvents.TabPin6, + EShortcutEvents.TabPin7, + EShortcutEvents.TabPin8, + EShortcutEvents.TabPin9, + ].includes(eventName as EShortcutEvents) + ) { + return; + } if (keys?.length) { - globalShortcut.register( - keys - .map((key) => { - switch (key) { - case shortcutsKeys.CmdOrCtrl: - return 'CmdOrCtrl'; - case shortcutsKeys.Shift: - return 'Shift'; - default: - return key; - } - }) - .join('+'), - () => { - callback(event as EShortcutEvents); - }, - ); + const shortcutsKey = keys + .map((key) => { + switch (key) { + case shortcutsKeys.CmdOrCtrl: + return 'CmdOrCtrl'; + case shortcutsKeys.Shift: + return 'Shift'; + default: + return key; + } + }) + .join('+'); + globalShortcut.register(shortcutsKey, () => { + callback(eventName as EShortcutEvents); + }); } }); }); diff --git a/apps/desktop/src-electron/libs/store.ts b/apps/desktop/src-electron/libs/store.ts index 671b4e0e8a2..bf7de39c139 100644 --- a/apps/desktop/src-electron/libs/store.ts +++ b/apps/desktop/src-electron/libs/store.ts @@ -23,6 +23,7 @@ const configKeys = { Theme: 'theme', EncryptedData: 'EncryptedData', Language: 'language', + DisableKeyboardShortcuts: 'disableKeyboardShortcuts', }; export const getUpdateSettings = (): IUpdateSettings => @@ -40,6 +41,25 @@ export const setDevTools = (devTools: boolean) => { store.set(configKeys.DevTools, devTools); }; +export const getDisableKeyboardShortcuts = () => + store.get(configKeys.DisableKeyboardShortcuts, { + disableNumberShortcuts: false, + disableSearchAndAccountSelectorShortcuts: false, + }) as { + disableNumberShortcuts: boolean; + disableSearchAndAccountSelectorShortcuts: boolean; + }; + +export const setDisableKeyboardShortcuts = (config: { + disableNumberShortcuts: boolean; + disableSearchAndAccountSelectorShortcuts: boolean; +}) => { + store.set(configKeys.DisableKeyboardShortcuts, { + ...getDisableKeyboardShortcuts(), + ...config, + }); +}; + export const getTheme = () => store.get(configKeys.Theme, 'system') as string; export const setTheme = (theme: string) => store.set(configKeys.Theme, theme); diff --git a/apps/desktop/src-electron/preload.ts b/apps/desktop/src-electron/preload.ts index 9d879c87ccf..cb0edc7e407 100644 --- a/apps/desktop/src-electron/preload.ts +++ b/apps/desktop/src-electron/preload.ts @@ -73,6 +73,10 @@ export type IDesktopAPI = { // Updater checkForUpdates: (isManual?: boolean) => void; + disableShortcuts: (params: { + disableNumberShortcuts?: boolean; + disableSearchAndAccountSelectorShortcuts?: boolean; + }) => void; downloadUpdate: () => void; verifyUpdate: (event: IVerifyUpdateParams) => void; installUpdate: (event: IInstallUpdateParams) => void; @@ -168,6 +172,7 @@ const validChannels = [ ipcMessageKeys.TOUCH_UPDATE_RES_SUCCESS, ipcMessageKeys.TOUCH_UPDATE_PROGRESS, ipcMessageKeys.SHOW_ABOUT_WINDOW, + ipcMessageKeys.APP_UPDATE_DISABLE_SHORTCUTS, ]; const getChannel = () => { @@ -302,6 +307,10 @@ const desktopApi = Object.freeze({ // Updater checkForUpdates: (isManual?: boolean) => ipcRenderer.send(ipcMessageKeys.UPDATE_CHECK, isManual), + disableShortcuts: (params: { + disableNumberShortcuts?: boolean; + disableSearchAndAccountSelectorShortcuts?: boolean; + }) => ipcRenderer.send(ipcMessageKeys.APP_UPDATE_DISABLE_SHORTCUTS, params), downloadUpdate: () => ipcRenderer.send(ipcMessageKeys.UPDATE_DOWNLOAD), verifyUpdate: (params: IVerifyUpdateParams) => ipcRenderer.send(ipcMessageKeys.UPDATE_VERIFY, params), diff --git a/packages/kit-bg/src/states/jotai/atoms/devSettings.ts b/packages/kit-bg/src/states/jotai/atoms/devSettings.ts index 92e9eea218c..db8f4524aab 100644 --- a/packages/kit-bg/src/states/jotai/atoms/devSettings.ts +++ b/packages/kit-bg/src/states/jotai/atoms/devSettings.ts @@ -17,10 +17,10 @@ export interface IDevSettings { alwaysSignOnlySendTx?: boolean; // show dev export private key showDevExportPrivateKey?: boolean; - // show trading view - showTradingView?: boolean; // disable Solana priority fee disableSolanaPriorityFee?: boolean; + disableNumberShortcuts?: boolean; + disableSearchAndAccountSelectorShortcuts?: boolean; } export type IDevSettingsKeys = keyof IDevSettings; @@ -40,8 +40,9 @@ export const { settings: { enableTestEndpoint: !!platformEnv.isDev || !!platformEnv.isE2E, showDevOverlayWindow: platformEnv.isE2E ? true : undefined, - showTradingView: false, disableSolanaPriorityFee: false, + disableNumberShortcuts: false, + disableSearchAndAccountSelectorShortcuts: false, }, }, }); diff --git a/packages/kit/src/components/TradingView/WebView.desktop.tsx b/packages/kit/src/components/TradingView/WebView.desktop.tsx index 2f7f3310468..6c6e4758c63 100644 --- a/packages/kit/src/components/TradingView/WebView.desktop.tsx +++ b/packages/kit/src/components/TradingView/WebView.desktop.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from 'react'; +import { useLayoutEffect, useRef } from 'react'; import { Stack } from '@onekeyhq/components'; @@ -22,28 +22,26 @@ export function WebView({ }: IWebViewProps): JSX.Element | null { const ref = useRef(null); - useEffect(() => { + useLayoutEffect(() => { const webview = ref.current; if (webview) { - webview.addEventListener('dom-ready', () => { + webview.addEventListener('did-attach', () => { ( webview as unknown as { executeJavaScript: (code: string, userGesture: boolean) => void; } ).executeJavaScript(injectedJavaScript, true); + }); + webview.addEventListener('did-finish-load', () => { setTimeout(() => { onLoadEnd(); - }, 200); + }, 100); }); webview.addEventListener('will-navigate', (event) => { event.preventDefault(); }); } - - setTimeout(() => { - onLoadEnd(); - }, 5500); - }, [injectedJavaScript, onLoadEnd]); + }, [injectedJavaScript, onLoadEnd, uri]); return uri ? ( diff --git a/packages/kit/src/views/Market/components/MarketHomeList.tsx b/packages/kit/src/views/Market/components/MarketHomeList.tsx index 1fe7f17bbf0..264314d50d0 100644 --- a/packages/kit/src/views/Market/components/MarketHomeList.tsx +++ b/packages/kit/src/views/Market/components/MarketHomeList.tsx @@ -247,7 +247,7 @@ function MarketMdColumn({ items: [ { icon: 'SwitchHorOutline' as const, - label: intl.formatMessage({ id: ETranslations.global_swap }), + label: intl.formatMessage({ id: ETranslations.global_trade }), onPress: tradeActions.onSwapLazyModal, }, canStaking && { diff --git a/packages/kit/src/views/Market/components/MarketTradeButton.tsx b/packages/kit/src/views/Market/components/MarketTradeButton.tsx index 83361c67a3e..fc7c435a7ab 100644 --- a/packages/kit/src/views/Market/components/MarketTradeButton.tsx +++ b/packages/kit/src/views/Market/components/MarketTradeButton.tsx @@ -30,7 +30,6 @@ export function MarketTradeButton({ const network = useMarketTradeNetwork(token); const networkId = useMarketTradeNetworkId(network, token.symbol); const [disabled, setDisabled] = useState({ - trade: true, buy: true, sell: true, }); @@ -60,11 +59,7 @@ export function MarketTradeButton({ }) || {}; const contractAddress = isNative ? '' : network?.contract_address || ''; - const [swapResult, buyResult, sellResult] = await Promise.all([ - backgroundApiProxy.serviceSwap.checkSupportSwap({ - networkId, - contractAddress: isNative ? realContractAddress : contractAddress, - }), + const [buyResult, sellResult] = await Promise.all([ backgroundApiProxy.serviceFiatCrypto.isTokenSupported({ networkId, tokenAddress: contractAddress, @@ -77,7 +72,6 @@ export function MarketTradeButton({ }), ]); setDisabled({ - trade: !swapResult.isSupportCrossChain && !swapResult.isSupportSwap, buy: !buyResult, sell: !sellResult, }); @@ -95,12 +89,7 @@ export function MarketTradeButton({ return ( - {canStaking ? ( diff --git a/packages/kit/src/views/Market/components/tradeHook.tsx b/packages/kit/src/views/Market/components/tradeHook.tsx index e5a7d3a2369..5fae0158f74 100644 --- a/packages/kit/src/views/Market/components/tradeHook.tsx +++ b/packages/kit/src/views/Market/components/tradeHook.tsx @@ -69,23 +69,27 @@ export const useMarketTradeActions = (token: IMarketTokenDetail | null) => { ); const remindUnsupportedToken = useCallback( - (action: 'buy' | 'sell' | 'trade') => { + (action: 'buy' | 'sell' | 'trade', showDialog = true) => { defaultLogger.market.token.unsupportedToken({ name: symbol, action }); - Dialog.confirm({ - title: intl.formatMessage({ id: ETranslations.earn_unsupported_token }), - tone: 'warning', - icon: 'ErrorOutline', - renderContent: ( - - {intl.formatMessage({ - id: ETranslations.earn_unsupported_token_desc, - })} - - ), - onConfirmText: intl.formatMessage({ - id: ETranslations.explore_got_it, - }), - }); + if (showDialog) { + Dialog.confirm({ + title: intl.formatMessage({ + id: ETranslations.earn_unsupported_token, + }), + tone: 'warning', + icon: 'ErrorOutline', + renderContent: ( + + {intl.formatMessage({ + id: ETranslations.earn_unsupported_token_desc, + })} + + ), + onConfirmText: intl.formatMessage({ + id: ETranslations.explore_got_it, + }), + }); + } }, [intl, symbol], ); @@ -149,14 +153,23 @@ export const useMarketTradeActions = (token: IMarketTokenDetail | null) => { const handleSwap = useCallback( async (mode?: 'modal' | 'button') => { - const popPage = () => { + const navigateToSwapPage = ( + params: IModalSwapParamList[EModalSwapRoutes.SwapMainLand], + ) => { if (mode === 'modal') { - navigation.pop(); + navigation.replace(EModalSwapRoutes.SwapMainLand, params); + } else { + navigation.pushModal(EModalRoutes.SwapModal, { + screen: EModalSwapRoutes.SwapMainLand, + params, + }); } }; if (!networkId) { - remindUnsupportedToken('trade'); - popPage(); + remindUnsupportedToken('trade', false); + navigateToSwapPage({ + importNetworkId: 'unknown', + }); return; } const { isNative, realContractAddress = '' } = @@ -172,14 +185,16 @@ export const useMarketTradeActions = (token: IMarketTokenDetail | null) => { }); if (!isSupportSwap && !isSupportCrossChain) { - remindUnsupportedToken('trade'); - popPage(); + remindUnsupportedToken('trade', false); + navigateToSwapPage({ + importNetworkId: networkId, + }); return; } const onekeyNetwork = await backgroundApiProxy.serviceNetwork.getNetwork({ networkId, }); - const params = { + navigateToSwapPage({ importFromToken: { ...onekeyNetwork, logoURI: isNative ? onekeyNetwork.logoURI : undefined, @@ -193,15 +208,7 @@ export const useMarketTradeActions = (token: IMarketTokenDetail | null) => { swapTabSwitchType: isSupportSwap ? ESwapTabSwitchType.SWAP : ESwapTabSwitchType.BRIDGE, - }; - if (mode === 'modal') { - navigation.replace(EModalSwapRoutes.SwapMainLand, params); - } else { - navigation.pushModal(EModalRoutes.SwapModal, { - screen: EModalSwapRoutes.SwapMainLand, - params, - }); - } + }); }, [ contractAddress, diff --git a/packages/kit/src/views/Setting/pages/List/DevSettingsSection/CrashDevSettings.tsx b/packages/kit/src/views/Setting/pages/List/DevSettingsSection/CrashDevSettings.tsx index 7687b1390d7..92c846ca94a 100644 --- a/packages/kit/src/views/Setting/pages/List/DevSettingsSection/CrashDevSettings.tsx +++ b/packages/kit/src/views/Setting/pages/List/DevSettingsSection/CrashDevSettings.tsx @@ -1,9 +1,6 @@ -import { useState } from 'react'; - import { SectionPressItem } from './SectionPressItem'; export function CrashDevSettings() { - const [text, setText] = useState({ a: { b: { c: 'text' } } }); return ( <> , + { + setState(null as any); + }} + />, ]; if (platformEnv.isNative) { sections.push( diff --git a/packages/kit/src/views/Setting/pages/List/DevSettingsSection/index.tsx b/packages/kit/src/views/Setting/pages/List/DevSettingsSection/index.tsx index c2143aad639..b25a3504101 100644 --- a/packages/kit/src/views/Setting/pages/List/DevSettingsSection/index.tsx +++ b/packages/kit/src/views/Setting/pages/List/DevSettingsSection/index.tsx @@ -246,7 +246,32 @@ export const DevSettingsSection = () => { value={I18nManager.isRTL} /> - + { + globalThis.desktopApi.disableShortcuts({ + disableNumberShortcuts: value, + }); + setTimeout(() => { + backgroundApiProxy.serviceApp.restartApp(); + }, 300); + }} + > + + + { + globalThis.desktopApi.disableShortcuts({ + disableSearchAndAccountSelectorShortcuts: value, + }); + setTimeout(() => { + backgroundApiProxy.serviceApp.restartApp(); + }, 300); + }} + > {}; export const withSentryHOC = ( Component: ComponentType, -): ComponentType => withErrorBoundary(withProfiler(Component), {}); +): ComponentType => + withErrorBoundary(withProfiler(Component), { + onError: (error, info) => { + console.error('error', error, info); + }, + }); export const navigationIntegration = { registerNavigationContainer: (ref: any) => {}, diff --git a/packages/shared/src/modules3rdParty/sentry/index.ext.ts b/packages/shared/src/modules3rdParty/sentry/index.ext.ts index 857d48e5e58..62cb242342c 100644 --- a/packages/shared/src/modules3rdParty/sentry/index.ext.ts +++ b/packages/shared/src/modules3rdParty/sentry/index.ext.ts @@ -23,7 +23,11 @@ export const nativeCrash = () => {}; export const withSentryHOC = ( Component: ComponentType, ): ComponentType => - Sentry.withErrorBoundary(Sentry.withProfiler(Component), {}); + Sentry.withErrorBoundary(Sentry.withProfiler(Component), { + onError: (error, info) => { + console.error('error', error, info); + }, + }); export const navigationIntegration = { registerNavigationContainer: (ref: any) => {}, diff --git a/packages/shared/src/modules3rdParty/sentry/index.native.ts b/packages/shared/src/modules3rdParty/sentry/index.native.ts index ac673636ff7..bade6cd92a2 100644 --- a/packages/shared/src/modules3rdParty/sentry/index.native.ts +++ b/packages/shared/src/modules3rdParty/sentry/index.native.ts @@ -35,4 +35,9 @@ export const nativeCrash = sentryNativeCrash; export const withSentryHOC = ( Component: ComponentType, -): ComponentType => withErrorBoundary(withProfiler(wrap(Component)), {}); +): ComponentType => + withErrorBoundary(withProfiler(wrap(Component)), { + onError: (error, info) => { + console.error('error', error, info); + }, + }); diff --git a/packages/shared/src/modules3rdParty/sentry/index.ts b/packages/shared/src/modules3rdParty/sentry/index.ts index 455131fb344..f0365df7fc0 100644 --- a/packages/shared/src/modules3rdParty/sentry/index.ts +++ b/packages/shared/src/modules3rdParty/sentry/index.ts @@ -23,7 +23,11 @@ export const nativeCrash = () => {}; export const withSentryHOC = ( Component: ComponentType, ): ComponentType => - Sentry.withErrorBoundary(Sentry.withProfiler(Component), {}); + Sentry.withErrorBoundary(Sentry.withProfiler(Component), { + onError: (error, info) => { + console.error('error', error, info); + }, + }); export const navigationIntegration = { registerNavigationContainer: (ref: any) => {},