From 49edb71496be79434b889e2f4af84e79aede9830 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Mon, 18 Nov 2024 10:07:23 +1100 Subject: [PATCH 01/18] [Event Tracking] Add mixpanel event tracking Fixes #94 --- package.json | 2 + src/background/controller/wallet.ts | 17 +++- src/background/service/index.ts | 1 + src/background/service/mixpanel.ts | 129 ++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 src/background/service/mixpanel.ts diff --git a/package.json b/package.json index d6de2f3e..630da2b1 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "@trustwallet/wallet-core": "^4.0.17", "@types/bignumber.js": "^5.0.0", "@types/lodash": "^4.14.172", + "@types/mixpanel-browser": "^2.50.2", "@walletconnect/client": "^1.5.5", "@walletconnect/core": "2.10.3", "@walletconnect/modal": "^2.6.2", @@ -95,6 +96,7 @@ "lodash": "^4.17.21", "loglevel": "^1.7.1", "lru-cache": "^6.0.0", + "mixpanel-browser": "^2.56.0", "nanoid": "^3.1.23", "obs-store": "^4.0.3", "process": "^0.11.10", diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index d00ed8b1..fd209e9b 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -30,6 +30,7 @@ import { stakingService, proxyService, newsService, + mixpanelTrack, } from 'background/service'; import i18n from 'background/service/i18n'; import { type DisplayedKeryring, KEYRING_CLASS } from 'background/service/keyring'; @@ -85,10 +86,15 @@ export class WalletController extends BaseController { constructor() { super(); this.storageEvaluator = new StorageEvaluator(); + // Initialize mixpanel when wallet controller is instantiated + mixpanelTrack.init(); } /* wallet */ - boot = (password) => keyringService.boot(password); + boot = async (password) => { + const result = await keyringService.boot(password); + return result; + }; isBooted = () => keyringService.isBooted(); loadMemStore = () => keyringService.loadMemStore(); verifyPassword = (password: string) => keyringService.verifyPassword(password); @@ -185,9 +191,9 @@ export class WalletController extends BaseController { await passwordService.setPassword(password); sessionService.broadcastEvent('unlock'); - // if (!alianNameInited && Object.values(alianNames).length === 0) { - // this.initAlianNames(); - // } + + // Track wallet unlock + mixpanelTrack.track('wallet_unlocked'); }; switchUnlock = async (password: string) => { @@ -272,6 +278,9 @@ export class WalletController extends BaseController { await passwordService.clear(); sessionService.broadcastEvent('accountsChanged', []); sessionService.broadcastEvent('lock'); + + // Track wallet lock + mixpanelTrack.track('wallet_locked'); }; // lockadd here diff --git a/src/background/service/index.ts b/src/background/service/index.ts index 2be228d7..d4e7dd08 100644 --- a/src/background/service/index.ts +++ b/src/background/service/index.ts @@ -20,3 +20,4 @@ export { default as stakingService } from './staking'; export { default as signTextHistoryService } from './signTextHistory'; export { default as i18n } from './i18n'; export { default as newsService } from './news'; +export { mixpanelTrack } from './mixpanel'; diff --git a/src/background/service/mixpanel.ts b/src/background/service/mixpanel.ts new file mode 100644 index 00000000..7086ae59 --- /dev/null +++ b/src/background/service/mixpanel.ts @@ -0,0 +1,129 @@ +import mixpanel from 'mixpanel-browser'; + +type BaseProperties = { + wallet_address?: string; + network?: string; + timestamp?: number; +}; + +type TrackingEvents = { + // Platform Events + app_launched: BaseProperties; + screen_view: BaseProperties & { + screen_name: string; + path?: string; + }; + + // Wallet Events + wallet_created: BaseProperties & { + wallet_type: 'hd' | 'imported' | 'hardware'; + is_first_wallet: boolean; + }; + wallet_imported: BaseProperties & { + wallet_type: 'private_key' | 'seed_phrase'; + }; + wallet_unlocked: BaseProperties; + wallet_locked: BaseProperties; + wallet_switched: BaseProperties & { + from_address: string; + to_address: string; + }; + + // Transaction Events + transaction_sent: BaseProperties & { + value: number; + token_symbol: string; + transaction_type: 'transfer' | 'swap' | 'contract_interaction'; + gas_price?: number; + }; + transaction_failed: BaseProperties & { + error: string; + transaction_type: string; + }; + + // NFT Events + nft_view: BaseProperties & { + collection_address: string; + token_id: string; + }; + nft_transfer: BaseProperties & { + collection_address: string; + token_id: string; + recipient_address: string; + }; + + // Settings Events + settings_changed: BaseProperties & { + setting_type: string; + new_value: unknown; // Look at tightening this up based on settings + old_value: unknown; + }; + network_switched: BaseProperties & { + from_network: string; + to_network: string; + }; +}; + +class MixpanelService { + private static instance: MixpanelService; + private initialized = false; + + private constructor() { + // Private constructor for singleton + } + + static getInstance(): MixpanelService { + if (!MixpanelService.instance) { + MixpanelService.instance = new MixpanelService(); + } + return MixpanelService.instance; + } + + init() { + if (this.initialized) return; + + const token = process.env.MIXPANEL_TOKEN; + if (!token) { + console.warn('Mixpanel token not found'); + return; + } + + mixpanel.init(token, { + debug: process.env.NODE_ENV !== 'production', + track_pageview: true, + persistence: 'localStorage', + }); + + this.initialized = true; + } + + track(eventName: T, properties?: TrackingEvents[T]) { + if (!this.initialized) { + console.warn('Mixpanel not initialized'); + return; + } + + const baseProps = { + timestamp: Date.now(), + // network: userWalletService.getNetwork(), + // wallet_address: userWalletService.getCurrentAddress(), + }; + + mixpanel.track(eventName, { + ...baseProps, + ...properties, + }); + } + + identify(userId: string) { + if (!this.initialized) return; + mixpanel.identify(userId); + } + + reset() { + if (!this.initialized) return; + mixpanel.reset(); + } +} + +export const mixpanelTrack = MixpanelService.getInstance(); From 61ba4c35cd2fca0fc88e64dca5de226ab0b5babb Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Mon, 18 Nov 2024 10:30:53 +1100 Subject: [PATCH 02/18] Registering super properties Fixes #94 --- src/background/service/mixpanel.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/background/service/mixpanel.ts b/src/background/service/mixpanel.ts index 7086ae59..41994407 100644 --- a/src/background/service/mixpanel.ts +++ b/src/background/service/mixpanel.ts @@ -1,11 +1,21 @@ import mixpanel from 'mixpanel-browser'; +import { version } from '../../../package.json'; + type BaseProperties = { wallet_address?: string; network?: string; timestamp?: number; }; +// Super properties that will be sent with every event +type SuperProperties = { + app_version: string; + platform: 'extension'; + environment: 'development' | 'production'; + wallet_type: 'flow'; +}; + type TrackingEvents = { // Platform Events app_launched: BaseProperties; @@ -79,6 +89,17 @@ class MixpanelService { return MixpanelService.instance; } + private registerSuperProperties() { + const superProperties: SuperProperties = { + app_version: version, + platform: 'extension', + environment: process.env.NODE_ENV === 'production' ? 'production' : 'development', + wallet_type: 'flow', + }; + + mixpanel.register(superProperties); + } + init() { if (this.initialized) return; @@ -92,8 +113,12 @@ class MixpanelService { debug: process.env.NODE_ENV !== 'production', track_pageview: true, persistence: 'localStorage', + batch_requests: true, + batch_size: 10, + batch_flush_interval_ms: 2000, }); + this.registerSuperProperties(); this.initialized = true; } From 93171f6ff4156139b7c1714a1efbba38258b94e6 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Mon, 18 Nov 2024 11:40:02 +1100 Subject: [PATCH 03/18] Defined events and properties as per gitbook --- src/background/service/mixpanel.ts | 133 +++++++++++++++++++---------- 1 file changed, 86 insertions(+), 47 deletions(-) diff --git a/src/background/service/mixpanel.ts b/src/background/service/mixpanel.ts index 41994407..5274cf21 100644 --- a/src/background/service/mixpanel.ts +++ b/src/background/service/mixpanel.ts @@ -2,11 +2,20 @@ import mixpanel from 'mixpanel-browser'; import { version } from '../../../package.json'; -type BaseProperties = { - wallet_address?: string; - network?: string; - timestamp?: number; -}; +type KeyType = 'passkey' | 'google_drive' | 'seed_phrase' | 'keystore' | 'private_key'; + +type RecoveryMechanismType = + | 'multi-backup' + | 'seed-phrase' + | 'private_key' + | 'KeyStore' + | 'device_backup'; + +type AddressType = 'flow' | 'evm' | 'child' | 'coa'; + +type SignAlgoType = 'ECDSA_P256' | 'ECDSA_secp256k1'; + +type HashAlgoType = 'SHA256' | 'SHA3_256'; // Super properties that will be sent with every event type SuperProperties = { @@ -15,62 +24,92 @@ type SuperProperties = { environment: 'development' | 'production'; wallet_type: 'flow'; }; - type TrackingEvents = { - // Platform Events - app_launched: BaseProperties; - screen_view: BaseProperties & { - screen_name: string; - path?: string; + // General Events + script_error: { + error: string; // Error message of the script, e.g., Rate limit exceeded + script_id: string; // Script name from script API, e.g. checkCoaLink }; - - // Wallet Events - wallet_created: BaseProperties & { - wallet_type: 'hd' | 'imported' | 'hardware'; - is_first_wallet: boolean; + delegation_created: { + address: string; // Address of the account that delegated + node_id: string; // ID of the node that was delegated to + amount: number; // Amount of FLOW. e.g. 200.12 }; - wallet_imported: BaseProperties & { - wallet_type: 'private_key' | 'seed_phrase'; + on_ramp_clicked: { + source: 'moonpay' | 'coinbase'; // The on ramp platform the user choose e.g. moonpay or coinbase }; - wallet_unlocked: BaseProperties; - wallet_locked: BaseProperties; - wallet_switched: BaseProperties & { - from_address: string; - to_address: string; + coa_creation: { + tx_id: string; // The transaction ID + flow_address: string; // + error_message: string; // Any error message + }; + security_tool: { + type: 'biometric' | 'pin' | 'none'; + }; + + // Backup Events + multi_backup_created: { + address: string; // Address of the account that set up multi-backup + providers: KeyType[]; // Providers used in the multi-backup, GoogleDrive, iCloud, Seed e.g. google_drive icloud seed_phrase + }; + multi_backup_creation_failed: { + address: string; // Address of the account that set up multi-backup + providers: KeyType[]; // Providers used in the multi-backup, GoogleDrive, iCloud, Seed e.g. google_drive icloud seed_phrase }; // Transaction Events - transaction_sent: BaseProperties & { - value: number; - token_symbol: string; - transaction_type: 'transfer' | 'swap' | 'contract_interaction'; - gas_price?: number; + + cadence_transaction_signed: { + cadence: string; // SHA256 Hashed Cadence that was signed. + tx_id: string; // String of the transaction ID. + authorizers: string[]; // Comma separated list of authorizer account address in the transaction + proposer: string; // Address of the transactions proposer. + payer: string; // Payer of the transaction. + success: boolean; // Boolean of if the transaction was sent successful or not. true/false + }; + evm_transaction_signed: { + success: boolean; // Boolean of if the transaction was sent successful or not. true/false + flow_address: string; // Address of the account that signed the transaction + evm_address: string; // EVM Address of the account that signed the transaction + tx_id: string; // transaction id + }; + ft_transfer: { + from_address: string; // Address of the account that transferred the FT + to_address: string; // Address of the account that received the FT + type: string; // Type of FT sent (e.g., "FLOW", "USDf") + amount: number; // The amount of FT + ft_identifier: string; // The identifier of fungible token }; - transaction_failed: BaseProperties & { - error: string; - transaction_type: string; + nft_transfer: { + from_address: string; // Address of the account that transferred the FT + to_address: string; // Address of the account that received the FT + nft_identifier: string; // The identifier of non fungible token + tx_id: string; // ID of the NFT that was transferred + from_type: AddressType; // The type of from address whether it's flow, child account, coa or evm account. + to_type: AddressType; // The type of to address whether it's flow, child account, coa or evm account. + isMove: boolean; // The transfer flow is triggerred from Move account }; - // NFT Events - nft_view: BaseProperties & { - collection_address: string; - token_id: string; + transaction_result: { + tx_id: string; // The transaction id + is_successful: boolean; // If the transaction is successful + error_message: string; // Error message of transaction }; - nft_transfer: BaseProperties & { - collection_address: string; - token_id: string; - recipient_address: string; + // Account Events + account_created: { + public_key: string; // The public key used for creating the new account + key_type: KeyType; // The key type of the account + sign_algo: SignAlgoType; // Signature algorithm of the key + hash_algo: HashAlgoType; // Hash algo Hash algorithm of the key }; - // Settings Events - settings_changed: BaseProperties & { - setting_type: string; - new_value: unknown; // Look at tightening this up based on settings - old_value: unknown; + account_creation_time: { + // Timing Events }; - network_switched: BaseProperties & { - from_network: string; - to_network: string; + account_recovered: { + address: string; // Address that was recovered + mechanism: RecoveryMechanismType; // The way the account was recovered + methods: KeyType[]; // Array of providers used in the multi-backup, GoogleDrive, iCloud, Seed }; }; From 5bce4c7cb5354e6c34fae93f94d65000611f4f59 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Mon, 18 Nov 2024 11:52:11 +1100 Subject: [PATCH 04/18] Moved mixpanel init to background service --- src/background/controller/wallet.ts | 9 ------ src/background/index.ts | 48 ++++++++++++++--------------- 2 files changed, 24 insertions(+), 33 deletions(-) diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index fd209e9b..7f25400b 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -30,7 +30,6 @@ import { stakingService, proxyService, newsService, - mixpanelTrack, } from 'background/service'; import i18n from 'background/service/i18n'; import { type DisplayedKeryring, KEYRING_CLASS } from 'background/service/keyring'; @@ -86,8 +85,6 @@ export class WalletController extends BaseController { constructor() { super(); this.storageEvaluator = new StorageEvaluator(); - // Initialize mixpanel when wallet controller is instantiated - mixpanelTrack.init(); } /* wallet */ @@ -191,9 +188,6 @@ export class WalletController extends BaseController { await passwordService.setPassword(password); sessionService.broadcastEvent('unlock'); - - // Track wallet unlock - mixpanelTrack.track('wallet_unlocked'); }; switchUnlock = async (password: string) => { @@ -278,9 +272,6 @@ export class WalletController extends BaseController { await passwordService.clear(); sessionService.broadcastEvent('accountsChanged', []); sessionService.broadcastEvent('lock'); - - // Track wallet lock - mixpanelTrack.track('wallet_locked'); }; // lockadd here diff --git a/src/background/index.ts b/src/background/index.ts index 81b8cffb..8b97c15b 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -1,9 +1,22 @@ import 'reflect-metadata'; +import { + getAuth, + signInAnonymously, + indexedDBLocalPersistence, + setPersistence, + onAuthStateChanged, +} from '@firebase/auth'; import { ethErrors } from 'eth-rpc-errors'; -import { WalletController } from 'background/controller/wallet'; -import { Message } from 'utils'; +import { initializeApp } from 'firebase/app'; + +import eventBus from '@/eventBus'; +import type { WalletController } from 'background/controller/wallet'; import { EVENTS } from 'consts'; -import { storage } from './webapi'; +import { Message } from 'utils'; + +import { providerController, walletController } from './controller'; +import { preAuthzServiceDefinition } from './controller/serviceDefinition'; +import { fclTestnetConfig, fclMainnetConfig } from './fclConfig'; import { permissionService, preferenceService, @@ -22,24 +35,10 @@ import { passwordService, flownsService, stakingService, + mixpanelTrack, } from './service'; -import { providerController, walletController } from './controller'; - -import eventBus from '@/eventBus'; - -import { initializeApp, getApp } from 'firebase/app'; -import { getMessaging, getToken } from 'firebase/messaging'; -import { - getAuth, - signInAnonymously, - indexedDBLocalPersistence, - setPersistence, - onAuthStateChanged, -} from '@firebase/auth'; -import { fclTestnetConfig, fclMainnetConfig } from './fclConfig'; import { getFirbaseConfig } from './utils/firebaseConfig'; -import { getRemoteConfig } from 'firebase/remote-config'; -import { preAuthzServiceDefinition } from './controller/serviceDefinition'; +import { storage } from './webapi'; const { PortMessage } = Message; const chromeWindow = await chrome.windows.getCurrent(); @@ -138,6 +137,7 @@ async function restoreAppState() { await passwordService.init(); await flownsService.init(); await stakingService.init(); + await mixpanelTrack.init(); // rpcCache.start(); appStoreLoaded = true; @@ -171,7 +171,7 @@ function deleteTimer(port) { } chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) { - if (request == 'ping') { + if (request === 'ping') { sendResponse('pong'); return; } @@ -182,7 +182,6 @@ chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) { chrome.runtime.onConnect.addListener((port) => { // openapiService.getConfig(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore port._timer = setTimeout(forceReconnect, 250e3, port); port.onDisconnect.addListener(deleteTimer); @@ -347,11 +346,12 @@ const extMessageHandler = (msg, sender, sendResponse) => { }) .then((tabs) => { const tabId = tabs[0].id; - tabId && + if (tabId) { chrome.tabs.sendMessage(tabId, { type: 'FCW:NETWORK', network: userWalletService.getNetwork(), }); + } }); } // Launches extension popup window @@ -380,7 +380,7 @@ const extMessageHandler = (msg, sender, sendResponse) => { params: { tabId, type: service.type }, approvalComponent: findPath(service), }, - { height: service.type == 'authz' ? 700 : 620 } + { height: service.type === 'authz' ? 700 : 620 } ) .then((res) => { if (res === 'unlocked') { @@ -389,7 +389,7 @@ const extMessageHandler = (msg, sender, sendResponse) => { params: { tabId, type: service.type }, approvalComponent: findPath(service), }, - { height: service.type == 'authz' ? 700 : 620 } + { height: service.type === 'authz' ? 700 : 620 } ); } }); From 1f8dab9bffc67f4e84eb40634fb3247d9feadd79 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:34:27 +1100 Subject: [PATCH 05/18] General event tracking --- src/background/controller/wallet.ts | 49 +++++++++++++++++++++++------ src/background/service/mixpanel.ts | 9 ++++-- src/ui/views/Wallet/OnRampList.tsx | 22 ++++++++----- 3 files changed, 60 insertions(+), 20 deletions(-) diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index 7f25400b..f4bd4f95 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -7,7 +7,7 @@ import BN from 'bignumber.js'; import { ethErrors } from 'eth-rpc-errors'; import * as ethUtil from 'ethereumjs-util'; import { getApp } from 'firebase/app'; -import web3 from 'web3'; +import web3, { TransactionError } from 'web3'; import eventBus from '@/eventBus'; import { @@ -30,6 +30,7 @@ import { stakingService, proxyService, newsService, + mixpanelTrack, } from 'background/service'; import i18n from 'background/service/i18n'; import { type DisplayedKeryring, KEYRING_CLASS } from 'background/service/keyring'; @@ -1681,7 +1682,17 @@ export class WalletController extends BaseController { const script = await getScripts('evm', 'createCoa'); - return await userWalletService.sendTransaction(script, [fcl.arg(formattedAmount, t.UFix64)]); + const txID = await userWalletService.sendTransaction(script, [ + fcl.arg(formattedAmount, t.UFix64), + ]); + mixpanelTrack.track('coa_creation', { + tx_id: txID, + flow_address: (await this.getCurrentAddress()) || '', + // Don't see how we can get error message here + // If any error is thrown, we won't get a transaction ID + // error_message: '', + }); + return txID; }; createCoaEmpty = async (): Promise => { @@ -3069,16 +3080,22 @@ export class WalletController extends BaseController { // Listen to the transaction until it's sealed. // This will throw an error if there is an error with the transaction await fcl.tx(txId).onceSealed(); - } catch (err) { + } catch (err: unknown) { // An error has occurred while listening to the transaction console.error('listenTransaction error ', err); + if (err instanceof TransactionError) { + // Tell the UI that there was an error + chrome.runtime.sendMessage({ + msg: 'transactionError', + errorMessage: err.message, + errorCode: err.code, + }); - // Tell the UI that there was an error - chrome.runtime.sendMessage({ - msg: 'transactionError', - errorMessage: err.message, - errorCode: err.code, - }); + // mixpanelTrack.track('script_error', { + // error: err.message, + // script_id: somewhere in the error message, + // }); + } } finally { // Remove the pending transaction from the UI await chrome.storage.session.remove('transactionPending'); @@ -3480,6 +3497,12 @@ export class WalletController extends BaseController { createDelegator = async (amount, node) => { const result = await stakingService.createDelegator(amount, node); + // Track delegation creation + mixpanelTrack.track('delegation_created', { + address: (await this.getCurrentAddress()) || '', + node_id: node, + amount: amount, + }); return result; }; @@ -3652,6 +3675,14 @@ export class WalletController extends BaseController { return this.storageEvaluator.canPerformTransaction(address, amount); }; + + // Tracking stuff + + onRampClicked = async (source: 'moonpay' | 'coinbase') => { + mixpanelTrack.track('on_ramp_clicked', { + source: source, + }); + }; } export default new WalletController(); diff --git a/src/background/service/mixpanel.ts b/src/background/service/mixpanel.ts index 5274cf21..6b0cd583 100644 --- a/src/background/service/mixpanel.ts +++ b/src/background/service/mixpanel.ts @@ -2,6 +2,8 @@ import mixpanel from 'mixpanel-browser'; import { version } from '../../../package.json'; +type OnRampSourceType = 'moonpay' | 'coinbase'; + type KeyType = 'passkey' | 'google_drive' | 'seed_phrase' | 'keystore' | 'private_key'; type RecoveryMechanismType = @@ -24,6 +26,7 @@ type SuperProperties = { environment: 'development' | 'production'; wallet_type: 'flow'; }; + type TrackingEvents = { // General Events script_error: { @@ -36,12 +39,12 @@ type TrackingEvents = { amount: number; // Amount of FLOW. e.g. 200.12 }; on_ramp_clicked: { - source: 'moonpay' | 'coinbase'; // The on ramp platform the user choose e.g. moonpay or coinbase + source: OnRampSourceType; // The on ramp platform the user choose e.g. moonpay or coinbase }; coa_creation: { tx_id: string; // The transaction ID flow_address: string; // - error_message: string; // Any error message + error_message?: string; // Any error message }; security_tool: { type: 'biometric' | 'pin' | 'none'; @@ -93,7 +96,7 @@ type TrackingEvents = { transaction_result: { tx_id: string; // The transaction id is_successful: boolean; // If the transaction is successful - error_message: string; // Error message of transaction + error_message?: string; // Error message of transaction }; // Account Events account_created: { diff --git a/src/ui/views/Wallet/OnRampList.tsx b/src/ui/views/Wallet/OnRampList.tsx index 6c26bb22..ddd1dea8 100644 --- a/src/ui/views/Wallet/OnRampList.tsx +++ b/src/ui/views/Wallet/OnRampList.tsx @@ -1,28 +1,31 @@ -import React, { useEffect, useState } from 'react'; +import { generateOnRampURL } from '@coinbase/cbpay-js'; +import CloseIcon from '@mui/icons-material/Close'; +import { Typography, ButtonBase, Grid, IconButton } from '@mui/material'; import { Box } from '@mui/system'; +import React, { useCallback, useEffect, useState } from 'react'; + import { useWallet } from 'ui/utils'; + // import theme from '../../style/LLTheme'; -import { Typography, ButtonBase, Grid, IconButton } from '@mui/material'; // import { initOnRamp } from '@coinbase/cbpay-js'; -import { generateOnRampURL } from '@coinbase/cbpay-js'; // import { LLHeader } from '@/ui/FRWComponent'; // import Coinbase from '../../FRWAssets/svg/coinbasepay-txt.svg'; -import CloseIcon from '@mui/icons-material/Close'; -import MoonPay from '../../FRWAssets/svg/moonpay.svg'; + import Coinbase from '../../FRWAssets/svg/coinbase-pay.svg'; +import MoonPay from '../../FRWAssets/svg/moonpay.svg'; const OnRampList = ({ close }) => { const wallet = useWallet(); const [address, setAddress] = useState(null); - const loadAddress = async () => { + const loadAddress = useCallback(async () => { const address = await wallet.getCurrentAddress(); setAddress(address); - }; + }, [wallet]); useEffect(() => { loadAddress(); - }, []); + }, [loadAddress]); const loadMoonPay = async () => { if (!address) { @@ -39,6 +42,8 @@ const OnRampList = ({ close }) => { url: response?.data?.url, }); } + + wallet.onRampClicked('moonpay'); }; const loadCoinbasePay = async () => { @@ -56,6 +61,7 @@ const OnRampList = ({ close }) => { url: onRampURL, }); } + wallet.onRampClicked('coinbase'); }; return ( From ba56b4d8eb2893793a840fd9aa95c5e8700f2869 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:52:17 +1100 Subject: [PATCH 06/18] Renamed to trackOnRampClicked --- src/background/controller/wallet.ts | 2 +- src/ui/views/Wallet/OnRampList.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index f4bd4f95..361caf33 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -3678,7 +3678,7 @@ export class WalletController extends BaseController { // Tracking stuff - onRampClicked = async (source: 'moonpay' | 'coinbase') => { + trackOnRampClicked = async (source: 'moonpay' | 'coinbase') => { mixpanelTrack.track('on_ramp_clicked', { source: source, }); diff --git a/src/ui/views/Wallet/OnRampList.tsx b/src/ui/views/Wallet/OnRampList.tsx index ddd1dea8..4e3aec85 100644 --- a/src/ui/views/Wallet/OnRampList.tsx +++ b/src/ui/views/Wallet/OnRampList.tsx @@ -43,7 +43,7 @@ const OnRampList = ({ close }) => { }); } - wallet.onRampClicked('moonpay'); + wallet.trackOnRampClicked('moonpay'); }; const loadCoinbasePay = async () => { @@ -61,7 +61,7 @@ const OnRampList = ({ close }) => { url: onRampURL, }); } - wallet.onRampClicked('coinbase'); + wallet.trackOnRampClicked('coinbase'); }; return ( From 0a5283772b64196b3651b7b70e15882a57ce1ad7 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Mon, 18 Nov 2024 14:08:37 +1100 Subject: [PATCH 07/18] Handles backup tracking Fixes #94 --- src/background/controller/wallet.ts | 57 ++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index 361caf33..aaa6fa14 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -1685,13 +1685,19 @@ export class WalletController extends BaseController { const txID = await userWalletService.sendTransaction(script, [ fcl.arg(formattedAmount, t.UFix64), ]); - mixpanelTrack.track('coa_creation', { - tx_id: txID, - flow_address: (await this.getCurrentAddress()) || '', - // Don't see how we can get error message here - // If any error is thrown, we won't get a transaction ID - // error_message: '', - }); + + // try to seal it + try { + const result = await fcl.tx(txID).onceSealed(); + console.log('coa creation result ', result); + // Track with success + await this.trackCoaCreation(txID); + } catch (error) { + console.error('Error sealing transaction:', error); + // Track with error + await this.trackCoaCreation(txID, error.message); + } + return txID; }; @@ -1700,7 +1706,29 @@ export class WalletController extends BaseController { const script = await getScripts('evm', 'createCoaEmpty'); - return await userWalletService.sendTransaction(script, []); + const txID = await userWalletService.sendTransaction(script, []); + + // try to seal it + try { + const result = await fcl.tx(txID).onceSealed(); + console.log('coa creation result ', result); + // Track with success + await this.trackCoaCreation(txID); + } catch (error) { + console.error('Error sealing transaction:', error); + // Track with error + await this.trackCoaCreation(txID, error.message); + } + + return txID; + }; + + trackCoaCreation = async (txID: string, errorMessage?: string) => { + mixpanelTrack.track('coa_creation', { + tx_id: txID, + flow_address: (await this.getCurrentAddress()) || '', + error_message: errorMessage, + }); }; transferFlowEvm = async ( @@ -3402,7 +3430,18 @@ export class WalletController extends BaseController { uploadMnemonicToGoogleDrive = async (mnemonic, username, password) => { const app = getApp(process.env.NODE_ENV!); const user = await getAuth(app).currentUser; - return googleDriveService.uploadMnemonicToGoogleDrive(mnemonic, username, user!.uid, password); + try { + await googleDriveService.uploadMnemonicToGoogleDrive(mnemonic, username, user!.uid, password); + mixpanelTrack.track('multi_backup_created', { + address: (await this.getCurrentAddress()) || '', + providers: ['google_drive'], + }); + } catch { + mixpanelTrack.track('multi_backup_creation_failed', { + address: (await this.getCurrentAddress()) || '', + providers: ['google_drive'], + }); + } }; loadBackupAccounts = async (): Promise => { From 1714a82a3b32baa827b38334ff32eff2fe59f204 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Mon, 18 Nov 2024 14:31:08 +1100 Subject: [PATCH 08/18] Added tracking for cadence signing --- src/background/controller/wallet.ts | 25 ++++++++++++++- src/background/service/userWallet.ts | 46 +++++++++++++++++++++------- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index aaa6fa14..c16a4851 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -695,7 +695,30 @@ export class WalletController extends BaseController { signTransaction = async (type: string, from: string, data: any, options?: any) => { const keyring = await keyringService.getKeyringForAccount(from, type); - return keyringService.signTransaction(keyring, data, options); + const res = await keyringService.signTransaction(keyring, data, options); + + /* + cadence_transaction_signed: { + cadence: string; // SHA256 Hashed Cadence that was signed. + tx_id: string; // String of the transaction ID. + authorizers: string[]; // Comma separated list of authorizer account address in the transaction + proposer: string; // Address of the transactions proposer. + payer: string; // Payer of the transaction. + success: boolean; // Boolean of if the transaction was sent successful or not. true/false + }; + evm_transaction_signed: { + success: boolean; // Boolean of if the transaction was sent successful or not. true/false + flow_address: string; // Address of the account that signed the transaction + evm_address: string; // EVM Address of the account that signed the transaction + tx_id: string; // transaction id + }; + mixpanelTrack.track('transaction_signed', { + address: from, + type, + ...res, + }); + */ + return res; }; requestKeyring = (type, methodName, keyringId: number | null, ...params) => { diff --git a/src/background/service/userWallet.ts b/src/background/service/userWallet.ts index cd86cf02..15882997 100644 --- a/src/background/service/userWallet.ts +++ b/src/background/service/userWallet.ts @@ -7,7 +7,7 @@ import { withPrefix } from '@/ui/utils/address'; import { findAddressWithSeed, findAddressWithPK } from '@/ui/utils/modules/findAddressWithPK'; import { signWithKey, seed2PubKey } from '@/ui/utils/modules/passkey.js'; import wallet from 'background/controller/wallet'; -import { keyringService, openapiService } from 'background/service'; +import { keyringService, mixpanelTrack, openapiService } from 'background/service'; import { createPersistStore } from 'background/utils'; import { getHashAlgo, getSignAlgo, getStoragedAccount } from 'ui/utils'; @@ -240,16 +240,40 @@ class UserWallet { sendTransaction = async (cadence: string, args: any[]): Promise => { //add proxy - const allowed = await wallet.allowLilicoPay(); - const txID = await fcl.mutate({ - cadence: cadence, - args: (arg, t) => args, - proposer: this.authorizationFunction, - authorizations: [this.authorizationFunction], - payer: allowed ? this.payerAuthFunction : this.authorizationFunction, - limit: 9999, - }); - return txID; + try { + const allowed = await wallet.allowLilicoPay(); + const txID = await fcl.mutate({ + cadence: cadence, + args: (arg, t) => args, + proposer: this.authorizationFunction, + authorizations: [this.authorizationFunction], + payer: allowed ? this.payerAuthFunction : this.authorizationFunction, + limit: 9999, + }); + + mixpanelTrack.track('cadence_transaction_signed', { + cadence: cadence, + tx_id: txID, + authorizers: [this.getCurrentAddress()], + proposer: this.getCurrentAddress(), + payer: + (allowed + ? fcl.withPrefix((await wallet.getPayerAddressAndKeyId()).address) + : this.getCurrentAddress()) || '', + success: true, + }); + return txID; + } catch (error) { + mixpanelTrack.track('cadence_transaction_signed', { + cadence: cadence, + tx_id: '', + authorizers: [this.getCurrentAddress()], + proposer: this.getCurrentAddress(), + payer: this.getCurrentAddress(), + success: false, + }); + throw error; + } }; sign = async (signableMessage: string): Promise => { From 55410720b779a45a178d3be7591c477d449fae4b Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:00:28 +1100 Subject: [PATCH 09/18] Implemented script_error tracking --- src/background/service/userWallet.ts | 33 +++++++++++++--------------- src/background/utils/index.ts | 29 +++++++++++++++++------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/background/service/userWallet.ts b/src/background/service/userWallet.ts index 15882997..5bd53d20 100644 --- a/src/background/service/userWallet.ts +++ b/src/background/service/userWallet.ts @@ -238,7 +238,19 @@ class UserWallet { return withPrefix(this.store.currentWallet.address) || ''; }; + private extractScriptName = (cadence: string): string => { + const scriptLines = cadence.split('\n'); + for (const line of scriptLines) { + if (line.includes('// Flow Wallet')) { + // ' // Flow Wallet - testnet Script sendNFT - v2.31' + const nameMatch = line.match(/\/\/ Flow Wallet -\s*(testnet|mainnet)\s*Script\s+(\w+)/); + return nameMatch ? nameMatch[2] : 'unknown_script'; + } + } + return 'unknown_script'; + }; sendTransaction = async (cadence: string, args: any[]): Promise => { + const scriptName = this.extractScriptName(cadence); //add proxy try { const allowed = await wallet.allowLilicoPay(); @@ -251,26 +263,11 @@ class UserWallet { limit: 9999, }); - mixpanelTrack.track('cadence_transaction_signed', { - cadence: cadence, - tx_id: txID, - authorizers: [this.getCurrentAddress()], - proposer: this.getCurrentAddress(), - payer: - (allowed - ? fcl.withPrefix((await wallet.getPayerAddressAndKeyId()).address) - : this.getCurrentAddress()) || '', - success: true, - }); return txID; } catch (error) { - mixpanelTrack.track('cadence_transaction_signed', { - cadence: cadence, - tx_id: '', - authorizers: [this.getCurrentAddress()], - proposer: this.getCurrentAddress(), - payer: this.getCurrentAddress(), - success: false, + mixpanelTrack.track('script_error', { + script_id: scriptName, + error: error, }); throw error; } diff --git a/src/background/utils/index.ts b/src/background/utils/index.ts index 7cb3474d..82a00f4b 100644 --- a/src/background/utils/index.ts +++ b/src/background/utils/index.ts @@ -1,9 +1,12 @@ import * as ethUtil from 'ethereumjs-util'; -import pageStateCache from '../service/pageStateCache'; + export { default as createPersistStore } from './persisitStore'; export { default as createSessionStore } from './sessionStore'; -import { storage } from '@/background/webapi'; import { version } from '@/../package.json'; +import { storage } from '@/background/webapi'; + +import { mixpanelTrack } from '../service'; +import pageStateCache from '../service/pageStateCache'; // {a:{b: string}} => {1: 'a.b'} // later same [source] value will override [result] key generated before @@ -93,12 +96,22 @@ export const isSameAddress = (a: string, b: string) => { }; export const getScripts = async (folder: string, scriptName: string) => { - const { data } = await storage.get('cadenceScripts'); - const files = data[folder]; - const script = files[scriptName]; - const scriptString = Buffer.from(script, 'base64').toString('utf-8'); - const modifiedScriptString = scriptString.replaceAll('', `Extension-${version}`); - return modifiedScriptString; + try { + const { data } = await storage.get('cadenceScripts'); + const files = data[folder]; + const script = files[scriptName]; + const scriptString = Buffer.from(script, 'base64').toString('utf-8'); + const modifiedScriptString = scriptString.replaceAll('', `Extension-${version}`); + return modifiedScriptString; + } catch (error) { + if (error instanceof Error) { + mixpanelTrack.track('script_error', { + script_id: scriptName, + error: error.message, + }); + } + throw error; + } }; export const findKeyAndInfo = (keys, publicKey) => { From 355402a067fd2662da6a298a77057f0a414f02f6 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Tue, 19 Nov 2024 10:20:09 +1100 Subject: [PATCH 10/18] Identifies users --- src/background/service/user.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/background/service/user.ts b/src/background/service/user.ts index dd4d2efa..4739a452 100644 --- a/src/background/service/user.ts +++ b/src/background/service/user.ts @@ -1,5 +1,7 @@ import { createPersistStore } from 'background/utils'; +import { mixpanelTrack } from './mixpanel'; + export interface UserInfoStore { avatar: string; nickname: string; @@ -56,10 +58,17 @@ class UserInfo { this.store.avatar = url.toString(); } this.store.avatar = data['avatar']; + + // identify the user + mixpanelTrack.identify(this.store.user_id); + + // TODO: track the user info if not in private mode }; addUserId = (userId: string) => { this.store.user_id = userId; + // identify the user + mixpanelTrack.identify(this.store.user_id); }; removeUserInfo = () => { @@ -68,6 +77,8 @@ class UserInfo { updateUserInfo = (data: UserInfoStore) => { this.store = data; + // identify the user + mixpanelTrack.identify(this.store.user_id); }; setDashIndex = (data: number) => { From dd12dfa4231f3a86803624cc19bdbfb6a6f8373b Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:22:21 +1100 Subject: [PATCH 11/18] Moved mixpanel connection to frontend --- src/background/service/mixpanel.ts | 175 ++----------------------- src/shared/types/tracking-types.ts | 105 +++++++++++++++ src/ui/utils/index.ts | 9 +- src/ui/utils/mixpanelBrowserService.ts | 136 +++++++++++++++++++ src/ui/utils/useMixpanel.ts | 28 ++++ src/ui/views/index.tsx | 33 +++-- 6 files changed, 310 insertions(+), 176 deletions(-) create mode 100644 src/shared/types/tracking-types.ts create mode 100644 src/ui/utils/mixpanelBrowserService.ts create mode 100644 src/ui/utils/useMixpanel.ts diff --git a/src/background/service/mixpanel.ts b/src/background/service/mixpanel.ts index 6b0cd583..f87fea08 100644 --- a/src/background/service/mixpanel.ts +++ b/src/background/service/mixpanel.ts @@ -1,121 +1,9 @@ -import mixpanel from 'mixpanel-browser'; - -import { version } from '../../../package.json'; - -type OnRampSourceType = 'moonpay' | 'coinbase'; - -type KeyType = 'passkey' | 'google_drive' | 'seed_phrase' | 'keystore' | 'private_key'; - -type RecoveryMechanismType = - | 'multi-backup' - | 'seed-phrase' - | 'private_key' - | 'KeyStore' - | 'device_backup'; - -type AddressType = 'flow' | 'evm' | 'child' | 'coa'; - -type SignAlgoType = 'ECDSA_P256' | 'ECDSA_secp256k1'; - -type HashAlgoType = 'SHA256' | 'SHA3_256'; - -// Super properties that will be sent with every event -type SuperProperties = { - app_version: string; - platform: 'extension'; - environment: 'development' | 'production'; - wallet_type: 'flow'; -}; - -type TrackingEvents = { - // General Events - script_error: { - error: string; // Error message of the script, e.g., Rate limit exceeded - script_id: string; // Script name from script API, e.g. checkCoaLink - }; - delegation_created: { - address: string; // Address of the account that delegated - node_id: string; // ID of the node that was delegated to - amount: number; // Amount of FLOW. e.g. 200.12 - }; - on_ramp_clicked: { - source: OnRampSourceType; // The on ramp platform the user choose e.g. moonpay or coinbase - }; - coa_creation: { - tx_id: string; // The transaction ID - flow_address: string; // - error_message?: string; // Any error message - }; - security_tool: { - type: 'biometric' | 'pin' | 'none'; - }; - - // Backup Events - multi_backup_created: { - address: string; // Address of the account that set up multi-backup - providers: KeyType[]; // Providers used in the multi-backup, GoogleDrive, iCloud, Seed e.g. google_drive icloud seed_phrase - }; - multi_backup_creation_failed: { - address: string; // Address of the account that set up multi-backup - providers: KeyType[]; // Providers used in the multi-backup, GoogleDrive, iCloud, Seed e.g. google_drive icloud seed_phrase - }; - - // Transaction Events - - cadence_transaction_signed: { - cadence: string; // SHA256 Hashed Cadence that was signed. - tx_id: string; // String of the transaction ID. - authorizers: string[]; // Comma separated list of authorizer account address in the transaction - proposer: string; // Address of the transactions proposer. - payer: string; // Payer of the transaction. - success: boolean; // Boolean of if the transaction was sent successful or not. true/false - }; - evm_transaction_signed: { - success: boolean; // Boolean of if the transaction was sent successful or not. true/false - flow_address: string; // Address of the account that signed the transaction - evm_address: string; // EVM Address of the account that signed the transaction - tx_id: string; // transaction id - }; - ft_transfer: { - from_address: string; // Address of the account that transferred the FT - to_address: string; // Address of the account that received the FT - type: string; // Type of FT sent (e.g., "FLOW", "USDf") - amount: number; // The amount of FT - ft_identifier: string; // The identifier of fungible token - }; - nft_transfer: { - from_address: string; // Address of the account that transferred the FT - to_address: string; // Address of the account that received the FT - nft_identifier: string; // The identifier of non fungible token - tx_id: string; // ID of the NFT that was transferred - from_type: AddressType; // The type of from address whether it's flow, child account, coa or evm account. - to_type: AddressType; // The type of to address whether it's flow, child account, coa or evm account. - isMove: boolean; // The transfer flow is triggerred from Move account - }; - - transaction_result: { - tx_id: string; // The transaction id - is_successful: boolean; // If the transaction is successful - error_message?: string; // Error message of transaction - }; - // Account Events - account_created: { - public_key: string; // The public key used for creating the new account - key_type: KeyType; // The key type of the account - sign_algo: SignAlgoType; // Signature algorithm of the key - hash_algo: HashAlgoType; // Hash algo Hash algorithm of the key - }; - - account_creation_time: { - // Timing Events - }; - account_recovered: { - address: string; // Address that was recovered - mechanism: RecoveryMechanismType; // The way the account was recovered - methods: KeyType[]; // Array of providers used in the multi-backup, GoogleDrive, iCloud, Seed - }; -}; +import eventBus from '@/eventBus'; +import type { TrackingEvents } from '@/shared/types/tracking-types'; +// TODO: Look at using a server side proxy service to send events to Mixpanel +// Note: Mixpanel is initialized in the browser side. Yes... it is possible for events to be lost if there is no listener. +// At some point, we should migrate to a more reliable event bus. class MixpanelService { private static instance: MixpanelService; private initialized = false; @@ -131,65 +19,30 @@ class MixpanelService { return MixpanelService.instance; } - private registerSuperProperties() { - const superProperties: SuperProperties = { - app_version: version, - platform: 'extension', - environment: process.env.NODE_ENV === 'production' ? 'production' : 'development', - wallet_type: 'flow', - }; - - mixpanel.register(superProperties); - } - init() { if (this.initialized) return; - const token = process.env.MIXPANEL_TOKEN; - if (!token) { - console.warn('Mixpanel token not found'); - return; - } - - mixpanel.init(token, { - debug: process.env.NODE_ENV !== 'production', - track_pageview: true, - persistence: 'localStorage', - batch_requests: true, - batch_size: 10, - batch_flush_interval_ms: 2000, - }); - - this.registerSuperProperties(); this.initialized = true; } - track(eventName: T, properties?: TrackingEvents[T]) { - if (!this.initialized) { - console.warn('Mixpanel not initialized'); - return; - } - - const baseProps = { - timestamp: Date.now(), - // network: userWalletService.getNetwork(), - // wallet_address: userWalletService.getCurrentAddress(), - }; - - mixpanel.track(eventName, { - ...baseProps, - ...properties, + eventBus.emit('track_event', { + eventName, + properties, }); } identify(userId: string) { if (!this.initialized) return; - mixpanel.identify(userId); + + eventBus.emit('track_user', { + userId, + }); } reset() { if (!this.initialized) return; - mixpanel.reset(); + + eventBus.emit('track_reset'); } } diff --git a/src/shared/types/tracking-types.ts b/src/shared/types/tracking-types.ts new file mode 100644 index 00000000..1a5ac83e --- /dev/null +++ b/src/shared/types/tracking-types.ts @@ -0,0 +1,105 @@ +type OnRampSourceType = 'moonpay' | 'coinbase'; + +type KeyType = 'passkey' | 'google_drive' | 'seed_phrase' | 'keystore' | 'private_key'; + +type RecoveryMechanismType = + | 'multi-backup' + | 'seed-phrase' + | 'private_key' + | 'KeyStore' + | 'device_backup'; + +type AddressType = 'flow' | 'evm' | 'child' | 'coa'; + +type SignAlgoType = 'ECDSA_P256' | 'ECDSA_secp256k1'; + +type HashAlgoType = 'SHA256' | 'SHA3_256'; + +export type TrackingEvents = { + // General Events + script_error: { + error: string; // Error message of the script, e.g., Rate limit exceeded + script_id: string; // Script name from script API, e.g. checkCoaLink + }; + delegation_created: { + address: string; // Address of the account that delegated + node_id: string; // ID of the node that was delegated to + amount: number; // Amount of FLOW. e.g. 200.12 + }; + on_ramp_clicked: { + source: OnRampSourceType; // The on ramp platform the user choose e.g. moonpay or coinbase + }; + coa_creation: { + tx_id: string; // The transaction ID + flow_address: string; // + error_message?: string; // Any error message + }; + security_tool: { + type: 'biometric' | 'pin' | 'none'; + }; + + // Backup Events + multi_backup_created: { + address: string; // Address of the account that set up multi-backup + providers: KeyType[]; // Providers used in the multi-backup, GoogleDrive, iCloud, Seed e.g. google_drive icloud seed_phrase + }; + multi_backup_creation_failed: { + address: string; // Address of the account that set up multi-backup + providers: KeyType[]; // Providers used in the multi-backup, GoogleDrive, iCloud, Seed e.g. google_drive icloud seed_phrase + }; + + // Transaction Events + + cadence_transaction_signed: { + cadence: string; // SHA256 Hashed Cadence that was signed. + tx_id: string; // String of the transaction ID. + authorizers: string[]; // Comma separated list of authorizer account address in the transaction + proposer: string; // Address of the transactions proposer. + payer: string; // Payer of the transaction. + success: boolean; // Boolean of if the transaction was sent successful or not. true/false + }; + evm_transaction_signed: { + success: boolean; // Boolean of if the transaction was sent successful or not. true/false + flow_address: string; // Address of the account that signed the transaction + evm_address: string; // EVM Address of the account that signed the transaction + tx_id: string; // transaction id + }; + ft_transfer: { + from_address: string; // Address of the account that transferred the FT + to_address: string; // Address of the account that received the FT + type: string; // Type of FT sent (e.g., "FLOW", "USDf") + amount: number; // The amount of FT + ft_identifier: string; // The identifier of fungible token + }; + nft_transfer: { + from_address: string; // Address of the account that transferred the FT + to_address: string; // Address of the account that received the FT + nft_identifier: string; // The identifier of non fungible token + tx_id: string; // ID of the NFT that was transferred + from_type: AddressType; // The type of from address whether it's flow, child account, coa or evm account. + to_type: AddressType; // The type of to address whether it's flow, child account, coa or evm account. + isMove: boolean; // The transfer flow is triggerred from Move account + }; + + transaction_result: { + tx_id: string; // The transaction id + is_successful: boolean; // If the transaction is successful + error_message?: string; // Error message of transaction + }; + // Account Events + account_created: { + public_key: string; // The public key used for creating the new account + key_type: KeyType; // The key type of the account + sign_algo: SignAlgoType; // Signature algorithm of the key + hash_algo: HashAlgoType; // Hash algo Hash algorithm of the key + }; + + account_creation_time: { + // Timing Events + }; + account_recovered: { + address: string; // Address that was recovered + mechanism: RecoveryMechanismType; // The way the account was recovered + methods: KeyType[]; // Array of providers used in the multi-backup, GoogleDrive, iCloud, Seed + }; +}; diff --git a/src/ui/utils/index.ts b/src/ui/utils/index.ts index a96c2e82..d49c45ab 100644 --- a/src/ui/utils/index.ts +++ b/src/ui/utils/index.ts @@ -1,6 +1,5 @@ import { IS_CHROME, CHECK_METAMASK_INSTALLED_URL } from 'consts'; -import { Account } from 'background/service/preference'; -// eslint-disable-next-line @typescript-eslint/no-empty-function + export const noop = () => {}; export * from './WalletContext'; @@ -18,6 +17,8 @@ export * from './options'; export * from './saveStorage'; +export * from './mixpanelBrowserService'; + const UI_TYPE = { Tab: 'index', Pop: 'popup', @@ -31,6 +32,7 @@ type UiTypeCheck = { }; export const getUiType = (): UiTypeCheck => { + // eslint-disable-next-line no-restricted-globals const { pathname } = window.location; return Object.entries(UI_TYPE).reduce((m, [key, value]) => { m[`is${key}`] = pathname === `/${value}.html`; @@ -121,11 +123,12 @@ export const isMetaMaskActive = async () => { if (!url) return false; try { + // eslint-disable-next-line no-restricted-globals const res = await window.fetch(url); await res.text(); return true; - } catch (e) { + } catch { return false; } }; diff --git a/src/ui/utils/mixpanelBrowserService.ts b/src/ui/utils/mixpanelBrowserService.ts new file mode 100644 index 00000000..e75f45e2 --- /dev/null +++ b/src/ui/utils/mixpanelBrowserService.ts @@ -0,0 +1,136 @@ +import mixpanel from 'mixpanel-browser'; + +import eventBus from '@/eventBus'; +import type { TrackingEvents } from '@/shared/types/tracking-types'; + +import { version } from '../../../package.json'; + +// Super properties that will be sent with every event +type SuperProperties = { + app_version: string; + platform: 'extension'; + environment: 'development' | 'production'; + wallet_type: 'flow'; +}; + +class MixpanelBrowserService { + private static instance: MixpanelBrowserService; + private initialized = false; + private boundTrackEventHandler: (params: { + eventName: T; + properties?: TrackingEvents[T]; + }) => void; + private boundTrackUserHandler: (params: { userId: string }) => void; + private boundTrackResetHandler: () => void; + + private constructor() { + this.initMixpanel(); + // Store bound handlers so we can remove them later + this.boundTrackEventHandler = (params: { + eventName: T; + properties?: TrackingEvents[T]; + }) => { + this.track(params.eventName, params.properties); + }; + + this.boundTrackUserHandler = (params: { userId: string }) => { + this.identify(params.userId); + }; + this.boundTrackResetHandler = () => { + this.reset(); + }; + + this.setupEventListener(); + } + + private setupEventListener() { + // Bind event handlers to the event bus + eventBus.addEventListener('track_event', this.boundTrackEventHandler); + eventBus.addEventListener('track_user', this.boundTrackUserHandler); + eventBus.addEventListener('track_reset', this.boundTrackResetHandler); + } + + init() { + // Don't need to do anything here + // Mixpanel is initialized in the constructor + } + cleanup() { + if (this.boundTrackEventHandler) { + eventBus.removeEventListener('track_event', this.boundTrackEventHandler); + } + if (this.boundTrackUserHandler) { + eventBus.removeEventListener('track_user', this.boundTrackUserHandler); + } + if (this.boundTrackResetHandler) { + eventBus.removeEventListener('track_reset', this.boundTrackResetHandler); + } + } + + static getInstance(): MixpanelBrowserService { + if (!MixpanelBrowserService.instance) { + MixpanelBrowserService.instance = new MixpanelBrowserService(); + } + return MixpanelBrowserService.instance; + } + + private registerSuperProperties() { + const superProperties: SuperProperties = { + app_version: version, + platform: 'extension', + environment: process.env.NODE_ENV === 'production' ? 'production' : 'development', + wallet_type: 'flow', + }; + + mixpanel.register(superProperties); + } + + private initMixpanel() { + if (this.initialized) return; + + const token = process.env.MIXPANEL_TOKEN; + if (!token) { + console.warn('Mixpanel token not found'); + return; + } + + mixpanel.init(token, { + debug: process.env.NODE_ENV !== 'production', + track_pageview: true, + persistence: 'localStorage', + batch_requests: true, + batch_size: 10, + batch_flush_interval_ms: 2000, + }); + + this.registerSuperProperties(); + this.initialized = true; + } + + track(eventName: T, properties?: TrackingEvents[T]) { + if (!this.initialized) { + console.warn('Mixpanel not initialized'); + return; + } + + const baseProps = { + timestamp: Date.now(), + }; + + mixpanel.track(eventName, { + ...baseProps, + ...properties, + }); + } + + identify(userId: string) { + if (!this.initialized) return; + mixpanel.identify(userId); + } + + reset() { + if (!this.initialized) return; + mixpanel.reset(); + } +} + +export const mixpanelBrowserService = MixpanelBrowserService.getInstance(); diff --git a/src/ui/utils/useMixpanel.ts b/src/ui/utils/useMixpanel.ts new file mode 100644 index 00000000..c93b7902 --- /dev/null +++ b/src/ui/utils/useMixpanel.ts @@ -0,0 +1,28 @@ +import { useCallback } from 'react'; + +import type { TrackingEvents } from '@/shared/types/tracking-types'; + +import { mixpanelBrowserService } from './mixpanelBrowserService'; + +export const useMixpanel = () => { + const track = useCallback( + (eventName: T, properties?: TrackingEvents[T]) => { + mixpanelBrowserService.track(eventName, properties); + }, + [] + ); + + const identify = useCallback((userId: string) => { + mixpanelBrowserService.identify(userId); + }, []); + + const reset = useCallback(() => { + mixpanelBrowserService.reset(); + }, []); + + return { + track, + identify, + reset, + }; +}; diff --git a/src/ui/views/index.tsx b/src/ui/views/index.tsx index 6867c6da..f322c456 100644 --- a/src/ui/views/index.tsx +++ b/src/ui/views/index.tsx @@ -1,25 +1,34 @@ +import GlobalStyles from '@mui/material/GlobalStyles'; +import { ThemeProvider } from '@mui/material/styles'; +import { createMemoryHistory } from 'history'; import React, { lazy, Suspense } from 'react'; import { HashRouter as Router, Route } from 'react-router-dom'; -import { createMemoryHistory } from 'history'; -import { WalletProvider } from 'ui/utils'; +import { NewsProvider } from '@/ui/utils/NewsContext'; +import { PrivateRoute } from 'ui/component'; +import { WalletProvider, mixpanelBrowserService } from 'ui/utils'; + +import theme from '../style/LLTheme'; + +import Approval from './Approval'; +import InnerRoute from './InnerRoute'; +import RetrievePK from './RetrievePK'; import SortHat from './SortHat'; -import Unlock from './Unlock'; import SwitchUnlock from './SwitchUnlock'; -import RetrievePK from './RetrievePK'; -import InnerRoute from './InnerRoute'; +import Unlock from './Unlock'; + const AsyncMainRoute = lazy(() => import('./MainRoute')); -import theme from '../style/LLTheme'; -import { ThemeProvider } from '@mui/material/styles'; -import GlobalStyles from '@mui/material/GlobalStyles'; -import { PrivateRoute } from 'ui/component'; -import Approval from './Approval'; -import { NewsProvider } from '@/ui/utils/NewsContext'; + // import Reset from './Reset'; function Main() { + React.useEffect(() => { + // Initialize mixpanel in the popup + // Note: Mixpanel is initialized in the constructor, just calling init here to make sure it is initialized + mixpanelBrowserService.init(); + }, []); + return ( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore From 60a531952317575e7ce3c013c1c8bb41c49c4d5c Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:55:06 +1100 Subject: [PATCH 12/18] Tracking FT & NFT transfers --- src/background/controller/wallet.ts | 342 +++++++++++++++++++++++++--- 1 file changed, 311 insertions(+), 31 deletions(-) diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index c16a4851..551383a3 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -1767,11 +1767,21 @@ export class WalletController extends BaseController { recipientEVMAddressHex = recipientEVMAddressHex.substring(2); } - return await userWalletService.sendTransaction(script, [ + const txID = await userWalletService.sendTransaction(script, [ fcl.arg(recipientEVMAddressHex, t.String), fcl.arg(formattedAmount, t.UFix64), fcl.arg(gasLimit, t.UInt64), ]); + + mixpanelTrack.track('ft_transfer', { + from_address: (await this.getCurrentAddress()) || '', + to_address: recipientEVMAddressHex, + amount: parseFloat(formattedAmount), + ft_identifier: 'FLOW', + type: 'evm', + }); + + return txID; }; transferFTToEvm = async ( @@ -1793,7 +1803,7 @@ export class WalletController extends BaseController { const regularArray = Array.from(dataArray); const gasLimit = 30000000; - return await userWalletService.sendTransaction(script, [ + const txID = await userWalletService.sendTransaction(script, [ fcl.arg(tokenContractAddress, t.Address), fcl.arg(tokenContractName, t.String), fcl.arg(formattedAmount, t.UFix64), @@ -1801,6 +1811,14 @@ export class WalletController extends BaseController { fcl.arg(regularArray, t.Array(t.UInt8)), fcl.arg(gasLimit, t.UInt64), ]); + mixpanelTrack.track('ft_transfer', { + from_address: (await this.getCurrentAddress()) || '', + to_address: tokenContractAddress, + amount: parseFloat(formattedAmount), + ft_identifier: tokenContractName, + type: 'evm', + }); + return txID; }; transferFTToEvmV2 = async ( @@ -1813,11 +1831,21 @@ export class WalletController extends BaseController { const script = await getScripts('bridge', 'bridgeTokensToEvmAddressV2'); - return await userWalletService.sendTransaction(script, [ + const txID = await userWalletService.sendTransaction(script, [ fcl.arg(vaultIdentifier, t.String), fcl.arg(formattedAmount, t.UFix64), fcl.arg(recipient, t.String), ]); + + mixpanelTrack.track('ft_transfer', { + from_address: (await this.getCurrentAddress()) || '', + to_address: recipient, + amount: parseFloat(formattedAmount), + ft_identifier: vaultIdentifier, + type: 'evm', + }); + + return txID; }; transferFTFromEvm = async ( @@ -1844,11 +1872,21 @@ export class WalletController extends BaseController { console.log('integerAmountStr amount ', integerAmountStr, amount); const script = await getScripts('bridge', 'bridgeTokensFromEvmToFlowV2'); - return await userWalletService.sendTransaction(script, [ + const txID = await userWalletService.sendTransaction(script, [ fcl.arg(flowidentifier, t.String), fcl.arg(integerAmountStr, t.UInt256), fcl.arg(receiver, t.Address), ]); + + mixpanelTrack.track('ft_transfer', { + from_address: (await this.getCurrentAddress()) || '', + to_address: receiver, + amount: parseFloat(integerAmountStr), + ft_identifier: flowidentifier, + type: 'evm', + }); + + return txID; }; withdrawFlowEvm = async (amount = '1.0', address: string): Promise => { @@ -1856,10 +1894,12 @@ export class WalletController extends BaseController { const formattedAmount = parseFloat(amount).toFixed(8); const script = await getScripts('evm', 'withdrawCoa'); - return await userWalletService.sendTransaction(script, [ + const txID = await userWalletService.sendTransaction(script, [ fcl.arg(formattedAmount, t.UFix64), fcl.arg(address, t.Address), ]); + + return txID; }; fundFlowEvm = async (amount = '1.0'): Promise => { @@ -1904,16 +1944,26 @@ export class WalletController extends BaseController { } }; - bridgeToEvm = async (flowIndentifier, amount = '1.0'): Promise => { + bridgeToEvm = async (flowIdentifier, amount = '1.0'): Promise => { await this.getNetwork(); const formattedAmount = parseFloat(amount).toFixed(8); const script = await getScripts('bridge', 'bridgeTokensToEvmV2'); - return await userWalletService.sendTransaction(script, [ - fcl.arg(flowIndentifier, t.String), + const txID = await userWalletService.sendTransaction(script, [ + fcl.arg(flowIdentifier, t.String), fcl.arg(formattedAmount, t.UFix64), ]); + + mixpanelTrack.track('ft_transfer', { + from_address: (await this.getCurrentAddress()) || '', + to_address: await this.getEvmAddress(), + amount: parseFloat(formattedAmount), + ft_identifier: flowIdentifier, + type: 'evm', + }); + + return txID; }; bridgeToFlow = async (flowIdentifier, amount = '1.0', tokenResult): Promise => { @@ -1932,10 +1982,20 @@ export class WalletController extends BaseController { const integerAmountStr = integerAmount.integerValue(BN.ROUND_DOWN).toFixed(); const script = await getScripts('bridge', 'bridgeTokensFromEvmV2'); - return await userWalletService.sendTransaction(script, [ + const txID = await userWalletService.sendTransaction(script, [ fcl.arg(flowIdentifier, t.String), fcl.arg(integerAmountStr, t.UInt256), ]); + + mixpanelTrack.track('ft_transfer', { + from_address: await this.getEvmAddress(), + to_address: (await this.getCurrentAddress()) || '', + amount: parseFloat(integerAmountStr), + ft_identifier: flowIdentifier, + type: 'evm', + }); + + return txID; }; queryEvmAddress = async (address: string): Promise => { @@ -2033,6 +2093,14 @@ export class WalletController extends BaseController { fcl.arg(gasLimit, t.UInt64), ]); + mixpanelTrack.track('ft_transfer', { + from_address: await this.getEvmAddress(), + to_address: to, + amount: parseFloat(amount), + ft_identifier: 'FLOW', + type: 'evm', + }); + return result; // const transaction = await fcl.tx(result).onceSealed(); // console.log('transaction ', transaction); @@ -2092,6 +2160,14 @@ export class WalletController extends BaseController { fcl.arg(gasLimit, t.UInt64), ]); + mixpanelTrack.track('ft_transfer', { + from_address: await this.getEvmAddress(), + to_address: to, + amount: parseFloat(amount), + ft_identifier: 'FLOW', + type: 'evm', + }); + console.log('result ', result); const res = await fcl.tx(result).onceSealed(); const transactionExecutedEvent = res.events.find((event) => @@ -2186,7 +2262,7 @@ export class WalletController extends BaseController { await this.getNetwork(); const script = await getScripts('ft', 'transferTokens'); - return await userWalletService.sendTransaction( + const txID = await userWalletService.sendTransaction( script .replaceAll('', token.contractName) .replaceAll('', token.path.balance) @@ -2195,6 +2271,16 @@ export class WalletController extends BaseController { .replaceAll('', token.address), [fcl.arg(amount, t.UFix64), fcl.arg(address, t.Address)] ); + + mixpanelTrack.track('ft_transfer', { + from_address: (await this.getCurrentAddress()) || '', + to_address: address, + amount: parseFloat(amount), + ft_identifier: token.contractName, + type: 'evm', + }); + + return txID; }; // TODO: Replace with generic token @@ -2211,7 +2297,7 @@ export class WalletController extends BaseController { } await this.getNetwork(); - return await userWalletService.sendTransaction( + const txID = await userWalletService.sendTransaction( script .replaceAll('', token.contractName) .replaceAll('', token.path.balance) @@ -2220,6 +2306,16 @@ export class WalletController extends BaseController { .replaceAll('', token.address), [fcl.arg(amount, t.UFix64), fcl.arg(address, t.Address)] ); + + mixpanelTrack.track('ft_transfer', { + from_address: (await this.getCurrentAddress()) || '', + to_address: address, + amount: parseFloat(amount), + ft_identifier: token.contractName, + type: 'evm', + }); + + return txID; }; revokeKey = async (index: string): Promise => { @@ -2382,7 +2478,7 @@ export class WalletController extends BaseController { } const script = await getScripts('hybridCustody', 'transferChildFT'); - return await userWalletService.sendTransaction( + const result = await userWalletService.sendTransaction( script .replaceAll('', token.contractName) .replaceAll('', token.path.balance) @@ -2391,6 +2487,14 @@ export class WalletController extends BaseController { .replaceAll('', token.address), [fcl.arg(childAddress, t.Address), fcl.arg(path, t.String), fcl.arg(amount, t.UFix64)] ); + mixpanelTrack.track('ft_transfer', { + from_address: (await this.getCurrentAddress()) || '', + to_address: childAddress, + amount: parseFloat(amount), + ft_identifier: token.contractName, + type: 'evm', + }); + return result; }; sendFTfromChild = async ( @@ -2407,7 +2511,7 @@ export class WalletController extends BaseController { const script = await getScripts('hybridCustody', 'sendChildFT'); - return await userWalletService.sendTransaction( + const result = await userWalletService.sendTransaction( script .replaceAll('', token.contractName) .replaceAll('', token.path.balance) @@ -2421,6 +2525,14 @@ export class WalletController extends BaseController { fcl.arg(amount, t.UFix64), ] ); + mixpanelTrack.track('ft_transfer', { + from_address: childAddress, + to_address: receiver, + amount: parseFloat(amount), + ft_identifier: token.contractName, + type: 'evm', + }); + return result; }; moveNFTfromChild = async ( @@ -2433,7 +2545,7 @@ export class WalletController extends BaseController { const script = await getScripts('hybridCustody', 'transferChildNFT'); - return await userWalletService.sendTransaction( + const txID = await userWalletService.sendTransaction( script .replaceAll('', token.contract_name) .replaceAll('', token.address) @@ -2446,6 +2558,16 @@ export class WalletController extends BaseController { fcl.arg(ids, t.UInt64), ] ); + mixpanelTrack.track('nft_transfer', { + tx_id: txID, + from_address: nftContractAddress, + to_address: (await this.getCurrentAddress()) || '', + nft_identifier: token.contractName, + from_type: 'flow', + to_type: 'flow', + isMove: true, + }); + return txID; }; sendNFTfromChild = async ( @@ -2457,7 +2579,7 @@ export class WalletController extends BaseController { ): Promise => { const script = await getScripts('hybridCustody', 'sendChildNFT'); - return await userWalletService.sendTransaction( + const txID = await userWalletService.sendTransaction( script .replaceAll('', token.contract_name) .replaceAll('', token.address) @@ -2471,6 +2593,16 @@ export class WalletController extends BaseController { fcl.arg(ids, t.UInt64), ] ); + mixpanelTrack.track('nft_transfer', { + tx_id: txID, + from_address: linkedAddress, + to_address: receiverAddress, + nft_identifier: token.contractName, + from_type: 'flow', + to_type: 'flow', + isMove: false, + }); + return txID; }; sendNFTtoChild = async ( @@ -2480,7 +2612,7 @@ export class WalletController extends BaseController { token ): Promise => { const script = await getScripts('hybridCustody', 'transferNFTToChild'); - return await userWalletService.sendTransaction( + const txID = await userWalletService.sendTransaction( script .replaceAll('', token.contract_name) .replaceAll('', token.address) @@ -2489,6 +2621,16 @@ export class WalletController extends BaseController { .replaceAll('', token.path.public_path), [fcl.arg(linkedAddress, t.Address), fcl.arg(path, t.String), fcl.arg(ids, t.UInt64)] ); + mixpanelTrack.track('nft_transfer', { + tx_id: txID, + from_address: linkedAddress, + to_address: (await this.getCurrentAddress()) || '', + nft_identifier: token.contractName, + from_type: 'flow', + to_type: 'flow', + isMove: false, + }); + return txID; }; getChildAccountAllowTypes = async (parent: string, child: string) => { @@ -2513,19 +2655,39 @@ export class WalletController extends BaseController { batchBridgeNftToEvm = async (flowIdentifier: string, ids: Array): Promise => { const script = await getScripts('bridge', 'batchBridgeNFTToEvmV2'); - return await userWalletService.sendTransaction(script, [ + const txID = await userWalletService.sendTransaction(script, [ fcl.arg(flowIdentifier, t.String), fcl.arg(ids, t.Array(t.UInt64)), ]); + mixpanelTrack.track('nft_transfer', { + tx_id: txID, + from_address: flowIdentifier, + to_address: (await this.getCurrentAddress()) || '', + nft_identifier: flowIdentifier, + from_type: 'flow', + to_type: 'flow', + isMove: false, + }); + return txID; }; batchBridgeNftFromEvm = async (flowIdentifier: string, ids: Array): Promise => { const script = await getScripts('bridge', 'batchBridgeNFTFromEvmV2'); - return await userWalletService.sendTransaction(script, [ + const txID = await userWalletService.sendTransaction(script, [ fcl.arg(flowIdentifier, t.String), fcl.arg(ids, t.Array(t.UInt256)), ]); + mixpanelTrack.track('nft_transfer', { + tx_id: txID, + from_address: flowIdentifier, + to_address: (await this.getCurrentAddress()) || '', + nft_identifier: flowIdentifier, + from_type: 'flow', + to_type: 'flow', + isMove: false, + }); + return txID; }; batchTransferNFTToChild = async ( @@ -2536,7 +2698,7 @@ export class WalletController extends BaseController { ): Promise => { const script = await getScripts('hybridCustody', 'batchTransferNFTToChild'); - return await userWalletService.sendTransaction( + const txID = await userWalletService.sendTransaction( script .replaceAll('', token.contract_name) .replaceAll('', token.address) @@ -2549,6 +2711,16 @@ export class WalletController extends BaseController { fcl.arg(ids, t.Array(t.UInt64)), ] ); + mixpanelTrack.track('nft_transfer', { + tx_id: txID, + from_address: childAddr, + to_address: (await this.getCurrentAddress()) || '', + nft_identifier: identifier, + from_type: 'flow', + to_type: 'flow', + isMove: false, + }); + return txID; }; batchTransferChildNft = async ( @@ -2558,7 +2730,7 @@ export class WalletController extends BaseController { token ): Promise => { const script = await getScripts('hybridCustody', 'batchTransferChildNFT'); - return await userWalletService.sendTransaction( + const txID = await userWalletService.sendTransaction( script .replaceAll('', token.contract_name) .replaceAll('', token.address) @@ -2571,6 +2743,16 @@ export class WalletController extends BaseController { fcl.arg(ids, t.Array(t.UInt64)), ] ); + mixpanelTrack.track('nft_transfer', { + tx_id: txID, + from_address: childAddr, + to_address: (await this.getCurrentAddress()) || '', + nft_identifier: identifier, + from_type: 'flow', + to_type: 'flow', + isMove: false, + }); + return txID; }; sendChildNFTToChild = async ( @@ -2582,7 +2764,7 @@ export class WalletController extends BaseController { ): Promise => { const script = await getScripts('hybridCustody', 'batchSendChildNFTToChild'); - return await userWalletService.sendTransaction( + const txID = await userWalletService.sendTransaction( script .replaceAll('', token.contract_name) .replaceAll('', token.address) @@ -2596,6 +2778,16 @@ export class WalletController extends BaseController { fcl.arg(ids, t.Array(t.UInt64)), ] ); + mixpanelTrack.track('nft_transfer', { + tx_id: txID, + from_address: childAddr, + to_address: (await this.getCurrentAddress()) || '', + nft_identifier: identifier, + from_type: 'flow', + to_type: 'flow', + isMove: false, + }); + return txID; }; batchBridgeChildNFTToEvm = async ( @@ -2606,7 +2798,7 @@ export class WalletController extends BaseController { ): Promise => { const script = await getScripts('hybridCustody', 'batchBridgeChildNFTToEvm'); - return await userWalletService.sendTransaction( + const txID = await userWalletService.sendTransaction( script .replaceAll('', token.contract_name) .replaceAll('', token.address) @@ -2619,6 +2811,16 @@ export class WalletController extends BaseController { fcl.arg(ids, t.Array(t.UInt64)), ] ); + mixpanelTrack.track('nft_transfer', { + tx_id: txID, + from_address: childAddr, + to_address: (await this.getCurrentAddress()) || '', + nft_identifier: identifier, + from_type: 'flow', + to_type: 'evm', + isMove: false, + }); + return txID; }; batchBridgeChildNFTFromEvm = async ( @@ -2628,11 +2830,21 @@ export class WalletController extends BaseController { ): Promise => { const script = await getScripts('hybridCustody', 'batchBridgeChildNFTFromEvm'); - return await userWalletService.sendTransaction(script, [ + const txID = await userWalletService.sendTransaction(script, [ fcl.arg(identifier, t.String), fcl.arg(childAddr, t.Address), fcl.arg(ids, t.Array(t.UInt256)), ]); + mixpanelTrack.track('nft_transfer', { + tx_id: txID, + from_address: childAddr, + to_address: (await this.getCurrentAddress()) || '', + nft_identifier: identifier, + from_type: 'evm', + to_type: 'flow', + isMove: false, + }); + return txID; }; bridgeChildFTToEvm = async ( @@ -2643,7 +2855,7 @@ export class WalletController extends BaseController { ): Promise => { const script = await getScripts('hybridCustody', 'bridgeChildFTToEvm'); - return await userWalletService.sendTransaction( + const txID = await userWalletService.sendTransaction( script .replaceAll('', token.contract_name) .replaceAll('', token.address) @@ -2652,17 +2864,26 @@ export class WalletController extends BaseController { .replaceAll('', token.path.public_path), [fcl.arg(identifier, t.String), fcl.arg(childAddr, t.Address), fcl.arg(amount, t.UFix64)] ); + mixpanelTrack.track('ft_transfer', { + from_address: childAddr, + to_address: (await this.getCurrentAddress()) || '', + ft_identifier: identifier, + type: 'evm', + amount: amount, + }); + return txID; }; bridgeChildFTFromEvm = async ( childAddr: string, vaultIdentifier: string, ids: Array, - token + token, + amount: number ): Promise => { const script = await getScripts('hybridCustody', 'bridgeChildFTFromEvm'); - return await userWalletService.sendTransaction( + const txID = await userWalletService.sendTransaction( script .replaceAll('', token.contract_name) .replaceAll('', token.address) @@ -2673,8 +2894,17 @@ export class WalletController extends BaseController { fcl.arg(vaultIdentifier, t.String), fcl.arg(childAddr, t.Address), fcl.arg(ids, t.Array(t.UInt256)), + fcl.arg(amount, t.UFix64), ] ); + mixpanelTrack.track('ft_transfer', { + from_address: childAddr, + to_address: (await this.getCurrentAddress()) || '', + ft_identifier: vaultIdentifier, + type: 'evm', + amount: amount, + }); + return txID; }; bridgeNftToEvmAddress = async ( @@ -2699,7 +2929,7 @@ export class WalletController extends BaseController { contractEVMAddress = contractEVMAddress.substring(2); } - return await userWalletService.sendTransaction(script, [ + const txID = await userWalletService.sendTransaction(script, [ fcl.arg(nftContractAddress, t.Address), fcl.arg(nftContractName, t.String), fcl.arg(ids, t.UInt64), @@ -2707,6 +2937,16 @@ export class WalletController extends BaseController { fcl.arg(regularArray, t.Array(t.UInt8)), fcl.arg(gasLimit, t.UInt64), ]); + mixpanelTrack.track('nft_transfer', { + tx_id: txID, + from_address: nftContractAddress, + to_address: (await this.getCurrentAddress()) || '', + nft_identifier: nftContractName, + from_type: 'evm', + to_type: 'evm', + isMove: false, + }); + return txID; }; bridgeNftFromEvmToFlow = async ( @@ -2716,11 +2956,21 @@ export class WalletController extends BaseController { ): Promise => { const script = await getScripts('bridge', 'bridgeNFTFromEvmToFlowV2'); - return await userWalletService.sendTransaction(script, [ + const txID = await userWalletService.sendTransaction(script, [ fcl.arg(flowIdentifier, t.String), fcl.arg(ids, t.UInt256), fcl.arg(receiver, t.Address), ]); + mixpanelTrack.track('nft_transfer', { + tx_id: txID, + from_address: flowIdentifier, + to_address: (await this.getCurrentAddress()) || '', + nft_identifier: flowIdentifier, + from_type: 'evm', + to_type: 'flow', + isMove: false, + }); + return txID; }; getAssociatedFlowIdentifier = async (address: string): Promise => { @@ -2736,7 +2986,7 @@ export class WalletController extends BaseController { await this.getNetwork(); const script = await getScripts('collection', 'sendNFT'); - return await userWalletService.sendTransaction( + const txID = await userWalletService.sendTransaction( script .replaceAll('', token.contract_name) .replaceAll('', token.address) @@ -2744,13 +2994,23 @@ export class WalletController extends BaseController { .replaceAll('', token.path.public_path), [fcl.arg(recipient, t.Address), fcl.arg(parseInt(id), t.UInt64)] ); + mixpanelTrack.track('nft_transfer', { + tx_id: txID, + from_address: (await this.getCurrentAddress()) || '', + to_address: recipient, + nft_identifier: token.contract_name, + from_type: 'flow', + to_type: 'flow', + isMove: false, + }); + return txID; }; sendNBANFT = async (recipient: string, id: any, token: NFTModel): Promise => { await this.getNetwork(); const script = await getScripts('collection', 'sendNbaNFT'); - return await userWalletService.sendTransaction( + const txID = await userWalletService.sendTransaction( script .replaceAll('', token.contract_name) .replaceAll('', token.address) @@ -2758,12 +3018,22 @@ export class WalletController extends BaseController { .replaceAll('', token.path.public_path), [fcl.arg(recipient, t.Address), fcl.arg(parseInt(id), t.UInt64)] ); + mixpanelTrack.track('nft_transfer', { + tx_id: txID, + from_address: (await this.getCurrentAddress()) || '', + to_address: recipient, + nft_identifier: token.contract_name, + from_type: 'flow', + to_type: 'flow', + isMove: false, + }); + return txID; }; sendInboxNFT = async (recipient: string, id: any, token: any): Promise => { const script = await getScripts('domain', 'sendInboxNFT'); - return await userWalletService.sendTransaction( + const txID = await userWalletService.sendTransaction( script .replaceAll('', token.contract_name) .replaceAll('', token.address) @@ -2771,6 +3041,16 @@ export class WalletController extends BaseController { .replaceAll('', token.path.public_path), [fcl.arg(recipient, t.Address), fcl.arg(parseInt(id), t.UInt64)] ); + mixpanelTrack.track('nft_transfer', { + tx_id: txID, + from_address: (await this.getCurrentAddress()) || '', + to_address: recipient, + nft_identifier: token.contract_name, + from_type: 'flow', + to_type: 'flow', + isMove: false, + }); + return txID; }; // SwapTokensForExactTokens From 2810127c539e07e09edd6378694da3d47fc808cc Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Wed, 20 Nov 2024 09:48:04 +1100 Subject: [PATCH 13/18] Transaction result tracking --- src/background/controller/wallet.ts | 41 ++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index 551383a3..a053f3f1 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -3397,6 +3397,7 @@ export class WalletController extends BaseController { } const address = (await this.getCurrentAddress()) || '0x'; const network = await this.getNetwork(); + try { chrome.storage.session.set({ transactionPending: { txId, network, date: new Date() }, @@ -3411,6 +3412,23 @@ export class WalletController extends BaseController { // Listen to the transaction until it's sealed. // This will throw an error if there is an error with the transaction await fcl.tx(txId).onceSealed(); + + // Track the transaction result + mixpanelTrack.track('transaction_result', { + tx_id: txId, + is_successful: true, + }); + + try { + // Send a notification to the user only on success + if (sendNotification) { + const baseURL = this.getFlowscanUrl(); + notification.create(`${baseURL}/transaction/${txId}`, title, body, icon); + } + } catch (err: unknown) { + // We don't want to throw an error if the notification fails + console.error('listenTransaction notification error ', err); + } } catch (err: unknown) { // An error has occurred while listening to the transaction console.error('listenTransaction error ', err); @@ -3422,10 +3440,19 @@ export class WalletController extends BaseController { errorCode: err.code, }); - // mixpanelTrack.track('script_error', { - // error: err.message, - // script_id: somewhere in the error message, - // }); + // Track the transaction result + mixpanelTrack.track('transaction_result', { + tx_id: txId, + is_successful: false, + error_message: err.message, + }); + } else { + // Track the transaction result + mixpanelTrack.track('transaction_result', { + tx_id: txId, + is_successful: false, + error_message: 'Unknown Error', + }); } } finally { // Remove the pending transaction from the UI @@ -3440,12 +3467,6 @@ export class WalletController extends BaseController { chrome.runtime.sendMessage({ msg: 'transactionDone', }); - - // Send a notification to the user - if (sendNotification) { - const baseURL = this.getFlowscanUrl(); - notification.create(`${baseURL}/transaction/${txId}`, title, body, icon); - } } }; From ece7a490fd052b9e98b3550ce011e7cadfc1c21c Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Wed, 20 Nov 2024 10:22:42 +1100 Subject: [PATCH 14/18] Track account created. Created algo util --- src/background/controller/wallet.ts | 3 +- src/background/service/openapi.ts | 9 +++ src/background/service/userWallet.ts | 3 +- src/shared/types/algo-types.ts | 3 + src/shared/types/tracking-types.ts | 8 +-- src/shared/utils/algo.ts | 65 +++++++++++++++++++ src/ui/utils/index.ts | 64 ------------------ src/ui/utils/modules/passkey.js | 19 +++--- .../AddWelcome/AddressImport/SetPassword.tsx | 26 ++++---- src/ui/views/AddressImport/SetPassword.tsx | 26 ++++---- 10 files changed, 123 insertions(+), 103 deletions(-) create mode 100644 src/shared/types/algo-types.ts create mode 100644 src/shared/utils/algo.ts diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index a053f3f1..7754e8b9 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -10,6 +10,7 @@ import { getApp } from 'firebase/app'; import web3, { TransactionError } from 'web3'; import eventBus from '@/eventBus'; +import { getHashAlgo, getSignAlgo } from '@/shared/utils/algo'; import { keyringService, preferenceService, @@ -42,7 +43,7 @@ import { notification, storage } from 'background/webapi'; import { openIndexPage } from 'background/webapi/tab'; import { INTERNAL_REQUEST_ORIGIN, EVENTS, KEYRING_TYPE } from 'consts'; import placeholder from 'ui/FRWAssets/image/placeholder.png'; -import { getHashAlgo, getSignAlgo, getStoragedAccount } from 'ui/utils'; +import { getStoragedAccount } from 'ui/utils'; import { isValidEthereumAddress, withPrefix } from 'ui/utils/address'; import { openInternalPageInTab } from 'ui/utils/webapi'; diff --git a/src/background/service/openapi.ts b/src/background/service/openapi.ts index b44831a6..12596f92 100644 --- a/src/background/service/openapi.ts +++ b/src/background/service/openapi.ts @@ -16,6 +16,7 @@ import type { TokenInfo } from 'flow-native-token-registry'; import log from 'loglevel'; import { storage } from '@/background/webapi'; +import { getStringFromHashAlgo, getStringFromSignAlgo } from '@/shared/utils/algo'; import { createPersistStore, getScripts, findKeyAndInfo } from 'background/utils'; import { getFirbaseConfig, getFirbaseFunctionUrl } from 'background/utils/firebaseConfig'; import fetchConfig from 'background/utils/remoteConfig'; @@ -51,6 +52,7 @@ import { transactionService, nftService, googleSafeHostService, + mixpanelTrack, } from './index'; // import { userInfo } from 'os'; @@ -694,6 +696,13 @@ class OpenApiService { ); await this._signWithCustom(data.data.custom_token); await storage.set('currentId', data.data.id); + + // Track the registration + mixpanelTrack.track('account_created', { + public_key: account_key.public_key, + sign_algo: getStringFromSignAlgo(account_key.sign_algo), + hash_algo: getStringFromHashAlgo(account_key.hash_algo), + }); return data; }; diff --git a/src/background/service/userWallet.ts b/src/background/service/userWallet.ts index 5bd53d20..834cd8b3 100644 --- a/src/background/service/userWallet.ts +++ b/src/background/service/userWallet.ts @@ -3,13 +3,14 @@ import * as secp from '@noble/secp256k1'; import * as fcl from '@onflow/fcl'; import { getApp } from 'firebase/app'; +import { getHashAlgo, getSignAlgo } from '@/shared/utils/algo'; import { withPrefix } from '@/ui/utils/address'; import { findAddressWithSeed, findAddressWithPK } from '@/ui/utils/modules/findAddressWithPK'; import { signWithKey, seed2PubKey } from '@/ui/utils/modules/passkey.js'; import wallet from 'background/controller/wallet'; import { keyringService, mixpanelTrack, openapiService } from 'background/service'; import { createPersistStore } from 'background/utils'; -import { getHashAlgo, getSignAlgo, getStoragedAccount } from 'ui/utils'; +import { getStoragedAccount } from 'ui/utils'; import { storage } from '../webapi'; diff --git a/src/shared/types/algo-types.ts b/src/shared/types/algo-types.ts new file mode 100644 index 00000000..707e68de --- /dev/null +++ b/src/shared/types/algo-types.ts @@ -0,0 +1,3 @@ +export type SignAlgoType = 'ECDSA_P256' | 'ECDSA_secp256k1' | 'unknown'; + +export type HashAlgoType = 'SHA256' | 'SHA2_256' | 'SHA3_256' | 'SHA2_384' | 'SHA3_384' | 'unknown'; diff --git a/src/shared/types/tracking-types.ts b/src/shared/types/tracking-types.ts index 1a5ac83e..29e032cc 100644 --- a/src/shared/types/tracking-types.ts +++ b/src/shared/types/tracking-types.ts @@ -1,3 +1,5 @@ +import type { HashAlgoType, SignAlgoType } from './algo-types'; + type OnRampSourceType = 'moonpay' | 'coinbase'; type KeyType = 'passkey' | 'google_drive' | 'seed_phrase' | 'keystore' | 'private_key'; @@ -11,10 +13,6 @@ type RecoveryMechanismType = type AddressType = 'flow' | 'evm' | 'child' | 'coa'; -type SignAlgoType = 'ECDSA_P256' | 'ECDSA_secp256k1'; - -type HashAlgoType = 'SHA256' | 'SHA3_256'; - export type TrackingEvents = { // General Events script_error: { @@ -89,7 +87,7 @@ export type TrackingEvents = { // Account Events account_created: { public_key: string; // The public key used for creating the new account - key_type: KeyType; // The key type of the account + key_type?: KeyType; // The key type of the account (if available) sign_algo: SignAlgoType; // Signature algorithm of the key hash_algo: HashAlgoType; // Hash algo Hash algorithm of the key }; diff --git a/src/shared/utils/algo.ts b/src/shared/utils/algo.ts new file mode 100644 index 00000000..d2565401 --- /dev/null +++ b/src/shared/utils/algo.ts @@ -0,0 +1,65 @@ +import type { HashAlgoType, SignAlgoType } from '@/shared/types/algo-types'; + +export function getHashAlgo(value: string): number { + switch (value) { + case 'unknown': + return 0; + case 'SHA2_256': + return 1; + case 'SHA2_384': + return 2; + case 'SHA3_256': + return 3; + case 'SHA3_384': + return 4; + default: + return -1; // Handle unknown values + } +} + +export function getSignAlgo(value: string): number { + switch (value) { + case 'unknown': + return 0; + case 'ECDSA_P256': + return 1; + case 'ECDSA_p256': + return 1; + case 'ECDSA_SECP256k1': + return 2; + case 'ECDSA_secp256k1': + return 2; + default: + return -1; // Handle unknown values + } +} + +export function getStringFromHashAlgo(value: number): HashAlgoType { + switch (value) { + case 0: + return 'unknown'; + case 1: + return 'SHA2_256'; + case 2: + return 'SHA2_384'; + case 3: + return 'SHA3_256'; + case 4: + return 'SHA3_384'; + default: + return 'unknown'; // Handle unknown values + } +} + +export function getStringFromSignAlgo(value: number): SignAlgoType { + switch (value) { + case 0: + return 'unknown'; + case 1: + return 'ECDSA_P256'; + case 2: + return 'ECDSA_secp256k1'; + default: + return 'unknown'; // Handle unknown values + } +} diff --git a/src/ui/utils/index.ts b/src/ui/utils/index.ts index d49c45ab..8bcf85ca 100644 --- a/src/ui/utils/index.ts +++ b/src/ui/utils/index.ts @@ -144,70 +144,6 @@ export const ellipsisOverflowedText = (str: string, length = 5, removeLastComma return `${cut}...`; }; -export function getHashAlgo(value: string): number { - switch (value) { - case 'unknown': - return 0; - case 'SHA2_256': - return 1; - case 'SHA2_384': - return 2; - case 'SHA3_256': - return 3; - case 'SHA3_384': - return 4; - default: - return -1; // Handle unknown values - } -} - -export function getSignAlgo(value: string): number { - switch (value) { - case 'unknown': - return 0; - case 'ECDSA_P256': - return 1; - case 'ECDSA_p256': - return 1; - case 'ECDSA_SECP256k1': - return 2; - case 'ECDSA_secp256k1': - return 2; - default: - return -1; // Handle unknown values - } -} - -export function getStringFromHashAlgo(value: number): string { - switch (value) { - case 0: - return 'unknown'; - case 1: - return 'SHA2_256'; - case 2: - return 'SHA2_384'; - case 3: - return 'SHA3_256'; - case 4: - return 'SHA3_384'; - default: - return 'unknown'; // Handle unknown values - } -} - -export function getStringFromSignAlgo(value: number): string { - switch (value) { - case 0: - return 'unknown'; - case 1: - return 'ECDSA_P256'; - case 2: - return 'ECDSA_SECP256k1'; - default: - return 'unknown'; // Handle unknown values - } -} - export const formatAddress = (address) => { if (address && address.length >= 30) { return `${address.substring(0, 6)}...${address.substring(address.length - 8)}`; diff --git a/src/ui/utils/modules/passkey.js b/src/ui/utils/modules/passkey.js index ab55c4af..89140e16 100644 --- a/src/ui/utils/modules/passkey.js +++ b/src/ui/utils/modules/passkey.js @@ -1,16 +1,17 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ // @ts-nocheck +import { initWasm } from '@trustwallet/wallet-core'; + +import { getStringFromHashAlgo, getStringFromSignAlgo } from '@/shared/utils/algo'; +import { storage } from 'background/webapi'; + +import { decodeArray, encodeArray } from './base64'; +import { FLOW_BIP44_PATH, HASH_ALGO, KEY_TYPE, SIGN_ALGO } from './constants'; +import { addCredential, readSettings } from './settings'; import { decodeAuthenticatorData, decodeClientDataJSON, decodeAttestationObject, } from './WebAuthnDecoder'; -import { decodeArray, encodeArray } from './base64'; -import { initWasm } from '@trustwallet/wallet-core'; -import { addCredential, readSettings } from './settings'; -import { FLOW_BIP44_PATH, HASH_ALGO, KEY_TYPE, SIGN_ALGO } from './constants'; -import { getStringFromHashAlgo, getStringFromSignAlgo } from 'ui/utils'; -import { storage } from 'background/webapi'; const jsonToKey = async (json, password) => { try { @@ -127,7 +128,7 @@ const seed2PubKeyTemp = async (seed) => { }; function getRandomBytes(length) { - var array = new Uint8Array(length ?? 32); + const array = new Uint8Array(length ?? 32); crypto.getRandomValues(array); return array; } @@ -138,6 +139,7 @@ const createPasskey = async (name, displayName) => { publicKey: { challenge: getRandomBytes(20), rp: { + // eslint-disable-next-line no-restricted-globals name: window.location.hostname, }, user: { @@ -173,6 +175,7 @@ const getPasskey = async (id) => { const setup = { publicKey: { challenge: getRandomBytes(20), + // eslint-disable-next-line no-restricted-globals rpId: window.location.hostname, }, }; diff --git a/src/ui/views/AddWelcome/AddressImport/SetPassword.tsx b/src/ui/views/AddWelcome/AddressImport/SetPassword.tsx index f5cb3640..9c5e1415 100644 --- a/src/ui/views/AddWelcome/AddressImport/SetPassword.tsx +++ b/src/ui/views/AddWelcome/AddressImport/SetPassword.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Button, Typography, @@ -14,19 +13,22 @@ import { LinearProgress, CssBaseline, } from '@mui/material'; -import CancelIcon from '../../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; -import VisibilityIcon from '@mui/icons-material/Visibility'; -import FormControlLabel from '@mui/material/FormControlLabel'; import Checkbox from '@mui/material/Checkbox'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import { makeStyles, styled } from '@mui/styles'; +import { Box, ThemeProvider } from '@mui/system'; +import React, { useEffect, useState } from 'react'; import { Presets } from 'react-component-transition'; import zxcvbn from 'zxcvbn'; -import theme from '../../../style/LLTheme'; -import { useWallet, getHashAlgo, getSignAlgo, saveIndex } from 'ui/utils'; -import { AccountKey } from 'background/service/networkModel'; -import { LLSpinner } from 'ui/FRWComponent'; + import { storage } from '@/background/webapi'; +import { getHashAlgo, getSignAlgo } from '@/shared/utils/algo'; +import { LLSpinner } from 'ui/FRWComponent'; +import { useWallet, saveIndex } from 'ui/utils'; + +import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../../components/iconfont/IconClose'; +import theme from '../../../style/LLTheme'; const useStyles = makeStyles(() => ({ customInputLabel: { diff --git a/src/ui/views/AddressImport/SetPassword.tsx b/src/ui/views/AddressImport/SetPassword.tsx index 395640e5..c1742704 100644 --- a/src/ui/views/AddressImport/SetPassword.tsx +++ b/src/ui/views/AddressImport/SetPassword.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Button, Typography, @@ -14,19 +13,22 @@ import { LinearProgress, CssBaseline, } from '@mui/material'; -import CancelIcon from '../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; -import VisibilityIcon from '@mui/icons-material/Visibility'; -import FormControlLabel from '@mui/material/FormControlLabel'; import Checkbox from '@mui/material/Checkbox'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import { makeStyles, styled } from '@mui/styles'; +import { Box, ThemeProvider } from '@mui/system'; +import React, { useEffect, useState } from 'react'; import { Presets } from 'react-component-transition'; import zxcvbn from 'zxcvbn'; -import theme from '../../style/LLTheme'; -import { useWallet, getHashAlgo, getSignAlgo, saveIndex } from 'ui/utils'; -import { AccountKey } from 'background/service/networkModel'; -import { LLSpinner } from 'ui/FRWComponent'; + import { storage } from '@/background/webapi'; +import { getHashAlgo, getSignAlgo } from '@/shared/utils/algo'; +import { LLSpinner } from 'ui/FRWComponent'; +import { useWallet, saveIndex } from 'ui/utils'; + +import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../components/iconfont/IconClose'; +import theme from '../../style/LLTheme'; const useStyles = makeStyles(() => ({ customInputLabel: { From c0a9c1b34b6571341807fae47b1f9593d5f924c5 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Wed, 20 Nov 2024 10:53:33 +1100 Subject: [PATCH 15/18] Added timing for account created --- src/background/service/mixpanel.ts | 6 ++++ src/ui/utils/mixpanelBrowserService.ts | 13 +++++++- src/ui/utils/useMixpanel.ts | 4 +++ .../AddWelcome/AddRegister/SetPassword.tsx | 30 ++++++++++-------- src/ui/views/RecoverRegister/SetPassword.tsx | 31 +++++++++++-------- src/ui/views/Register/SetPassword.tsx | 30 ++++++++++-------- 6 files changed, 74 insertions(+), 40 deletions(-) diff --git a/src/background/service/mixpanel.ts b/src/background/service/mixpanel.ts index f87fea08..10c2e158 100644 --- a/src/background/service/mixpanel.ts +++ b/src/background/service/mixpanel.ts @@ -31,6 +31,12 @@ class MixpanelService { }); } + time(eventName: T) { + eventBus.emit('track_time', { + eventName, + }); + } + identify(userId: string) { if (!this.initialized) return; diff --git a/src/ui/utils/mixpanelBrowserService.ts b/src/ui/utils/mixpanelBrowserService.ts index e75f45e2..1b08f09a 100644 --- a/src/ui/utils/mixpanelBrowserService.ts +++ b/src/ui/utils/mixpanelBrowserService.ts @@ -22,7 +22,7 @@ class MixpanelBrowserService { }) => void; private boundTrackUserHandler: (params: { userId: string }) => void; private boundTrackResetHandler: () => void; - + private boundTrackTimeHandler: (params: { eventName: keyof TrackingEvents }) => void; private constructor() { this.initMixpanel(); // Store bound handlers so we can remove them later @@ -39,6 +39,9 @@ class MixpanelBrowserService { this.boundTrackResetHandler = () => { this.reset(); }; + this.boundTrackTimeHandler = (params: { eventName: keyof TrackingEvents }) => { + this.time(params.eventName); + }; this.setupEventListener(); } @@ -48,6 +51,7 @@ class MixpanelBrowserService { eventBus.addEventListener('track_event', this.boundTrackEventHandler); eventBus.addEventListener('track_user', this.boundTrackUserHandler); eventBus.addEventListener('track_reset', this.boundTrackResetHandler); + eventBus.addEventListener('track_time', this.boundTrackTimeHandler); } init() { @@ -64,6 +68,9 @@ class MixpanelBrowserService { if (this.boundTrackResetHandler) { eventBus.removeEventListener('track_reset', this.boundTrackResetHandler); } + if (this.boundTrackTimeHandler) { + eventBus.removeEventListener('track_time', this.boundTrackTimeHandler); + } } static getInstance(): MixpanelBrowserService { @@ -122,6 +129,10 @@ class MixpanelBrowserService { }); } + time(eventName: T) { + mixpanel.time_event(eventName); + } + identify(userId: string) { if (!this.initialized) return; mixpanel.identify(userId); diff --git a/src/ui/utils/useMixpanel.ts b/src/ui/utils/useMixpanel.ts index c93b7902..24809766 100644 --- a/src/ui/utils/useMixpanel.ts +++ b/src/ui/utils/useMixpanel.ts @@ -11,6 +11,9 @@ export const useMixpanel = () => { }, [] ); + const time = useCallback((eventName: T) => { + mixpanelBrowserService.time(eventName); + }, []); const identify = useCallback((userId: string) => { mixpanelBrowserService.identify(userId); @@ -22,6 +25,7 @@ export const useMixpanel = () => { return { track, + time, identify, reset, }; diff --git a/src/ui/views/AddWelcome/AddRegister/SetPassword.tsx b/src/ui/views/AddWelcome/AddRegister/SetPassword.tsx index 598394c0..a619aa66 100644 --- a/src/ui/views/AddWelcome/AddRegister/SetPassword.tsx +++ b/src/ui/views/AddWelcome/AddRegister/SetPassword.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Button, Typography, @@ -14,20 +13,23 @@ import { LinearProgress, CssBaseline, } from '@mui/material'; -import CancelIcon from '../../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; -import VisibilityIcon from '@mui/icons-material/Visibility'; -import FormControlLabel from '@mui/material/FormControlLabel'; import Checkbox from '@mui/material/Checkbox'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import { makeStyles, styled } from '@mui/styles'; +import { Box, ThemeProvider } from '@mui/system'; +import HDWallet from 'ethereum-hdwallet'; +import React, { useEffect, useState } from 'react'; import { Presets } from 'react-component-transition'; import zxcvbn from 'zxcvbn'; -import theme from '../../../style/LLTheme'; -import { useWallet, saveIndex } from 'ui/utils'; -import { AccountKey } from 'background/service/networkModel'; -import HDWallet from 'ethereum-hdwallet'; -import { LLSpinner } from 'ui/FRWComponent'; + import { storage } from '@/background/webapi'; +import type { AccountKey } from 'background/service/networkModel'; +import { LLSpinner } from 'ui/FRWComponent'; +import { useWallet, saveIndex, mixpanelBrowserService } from 'ui/utils'; + +import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../../components/iconfont/IconClose'; +import theme from '../../../style/LLTheme'; const useStyles = makeStyles(() => ({ customInputLabel: { @@ -213,6 +215,8 @@ const SetPassword = ({ handleClick, mnemonic, username, setExPassword, tempPassw await saveIndex(username); const accountKey = getAccountKey(mnemonic); + // track the time until account_created is called + mixpanelBrowserService.time('account_created'); wallet.openapi .register(accountKey, username) .then((response) => { diff --git a/src/ui/views/RecoverRegister/SetPassword.tsx b/src/ui/views/RecoverRegister/SetPassword.tsx index 8eaf88a9..bc2d5a47 100644 --- a/src/ui/views/RecoverRegister/SetPassword.tsx +++ b/src/ui/views/RecoverRegister/SetPassword.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Button, Typography, @@ -14,20 +13,23 @@ import { FormGroup, LinearProgress, } from '@mui/material'; -import CancelIcon from '../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; -import VisibilityIcon from '@mui/icons-material/Visibility'; -import FormControlLabel from '@mui/material/FormControlLabel'; import Checkbox from '@mui/material/Checkbox'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import { makeStyles, styled } from '@mui/styles'; +import { Box, ThemeProvider } from '@mui/system'; +import HDWallet from 'ethereum-hdwallet'; +import React, { useEffect, useState } from 'react'; import { Presets } from 'react-component-transition'; import zxcvbn from 'zxcvbn'; -import theme from '../../style/LLTheme'; -import { useWallet, saveIndex } from 'ui/utils'; -import { AccountKey } from 'background/service/networkModel'; -import HDWallet from 'ethereum-hdwallet'; -import { LLSpinner } from '@/ui/FRWComponent'; + import { storage } from '@/background/webapi'; +import { LLSpinner } from '@/ui/FRWComponent'; +import type { AccountKey } from 'background/service/networkModel'; +import { useWallet, saveIndex, mixpanelBrowserService } from 'ui/utils'; + +import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../components/iconfont/IconClose'; +import theme from '../../style/LLTheme'; const useStyles = makeStyles(() => ({ customInputLabel: { @@ -215,6 +217,9 @@ const SetPassword = ({ handleClick, mnemonic, username }) => { await saveIndex(username); const accountKey = getAccountKey(mnemonic); + + // track the time until account_created is called + mixpanelBrowserService.time('account_created'); wallet.openapi .register(accountKey, username) .then((response) => { diff --git a/src/ui/views/Register/SetPassword.tsx b/src/ui/views/Register/SetPassword.tsx index c3c9a0c7..bf7730c8 100644 --- a/src/ui/views/Register/SetPassword.tsx +++ b/src/ui/views/Register/SetPassword.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Button, Typography, @@ -14,20 +13,23 @@ import { LinearProgress, CssBaseline, } from '@mui/material'; -import CancelIcon from '../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; -import VisibilityIcon from '@mui/icons-material/Visibility'; -import FormControlLabel from '@mui/material/FormControlLabel'; import Checkbox from '@mui/material/Checkbox'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import { makeStyles, styled } from '@mui/styles'; +import { Box, ThemeProvider } from '@mui/system'; +import HDWallet from 'ethereum-hdwallet'; +import React, { useEffect, useState } from 'react'; import { Presets } from 'react-component-transition'; import zxcvbn from 'zxcvbn'; -import theme from '../../style/LLTheme'; -import { useWallet, saveIndex } from 'ui/utils'; -import { AccountKey } from 'background/service/networkModel'; -import HDWallet from 'ethereum-hdwallet'; -import { LLSpinner } from 'ui/FRWComponent'; + import { storage } from '@/background/webapi'; +import type { AccountKey } from 'background/service/networkModel'; +import { LLSpinner } from 'ui/FRWComponent'; +import { useWallet, saveIndex, mixpanelBrowserService } from 'ui/utils'; + +import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../components/iconfont/IconClose'; +import theme from '../../style/LLTheme'; const useStyles = makeStyles(() => ({ customInputLabel: { @@ -217,6 +219,8 @@ const SetPassword = ({ handleClick, mnemonic, username, setExPassword }) => { await saveIndex(username); const accountKey = getAccountKey(mnemonic); + // track the time until account_created is called + mixpanelBrowserService.time('account_created'); wallet.openapi .register(accountKey, username) .then((response) => { From 4fc6a86342fde7585fba904304a9d1d49b7cb67c Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Wed, 20 Nov 2024 11:20:26 +1100 Subject: [PATCH 16/18] Tracking accont recovery --- src/ui/views/Register/AllSet.tsx | 34 ++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/ui/views/Register/AllSet.tsx b/src/ui/views/Register/AllSet.tsx index e92fbd75..80a68f0b 100644 --- a/src/ui/views/Register/AllSet.tsx +++ b/src/ui/views/Register/AllSet.tsx @@ -1,20 +1,34 @@ -import React, { useEffect } from 'react'; -import { useWallet } from 'ui/utils'; -import { Box, ThemeProvider } from '@mui/system'; import { Button, Typography, CssBaseline, CardMedia } from '@mui/material'; -import theme from '../../style/LLTheme'; +import { Box, ThemeProvider } from '@mui/system'; +import React, { useCallback, useEffect } from 'react'; + import AllSetIcon from 'ui/FRWAssets/svg/allset.svg'; +import { mixpanelBrowserService, useWallet } from 'ui/utils'; + +import theme from '../../style/LLTheme'; const AllSet = ({ handleClick }) => { - const wallets = useWallet(); + const wallet = useWallet(); + + const loadScript = useCallback(async () => { + await wallet.getCadenceScripts(); + }, [wallet]); - const loadScript = async () => { - await wallets.getCadenceScripts(); - }; + const trackAccountRecovered = useCallback(async () => { + // I'm not sure if this is the best way to track this event + // It's hard to know at which point the user recovers the account + mixpanelBrowserService.track('account_recovered', { + address: (await wallet.getMainAddress()) || '', + mechanism: 'multi-backup', + methods: [], + }); + }, [wallet]); useEffect(() => { - loadScript(); - }, []); + loadScript().then(() => { + trackAccountRecovered(); + }); + }, [loadScript, trackAccountRecovered]); return ( From 12536af70e2258d8ce5624c66c114414e04614a5 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Wed, 20 Nov 2024 11:52:22 +1100 Subject: [PATCH 17/18] Corrected a couple of ft tracking events --- src/background/controller/wallet.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index 7754e8b9..365900ff 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -1993,7 +1993,7 @@ export class WalletController extends BaseController { to_address: (await this.getCurrentAddress()) || '', amount: parseFloat(integerAmountStr), ft_identifier: flowIdentifier, - type: 'evm', + type: 'flow', }); return txID; @@ -2278,7 +2278,7 @@ export class WalletController extends BaseController { to_address: address, amount: parseFloat(amount), ft_identifier: token.contractName, - type: 'evm', + type: 'flow', }); return txID; @@ -2313,7 +2313,7 @@ export class WalletController extends BaseController { to_address: address, amount: parseFloat(amount), ft_identifier: token.contractName, - type: 'evm', + type: 'flow', }); return txID; @@ -2493,7 +2493,7 @@ export class WalletController extends BaseController { to_address: childAddress, amount: parseFloat(amount), ft_identifier: token.contractName, - type: 'evm', + type: 'flow', }); return result; }; @@ -2531,7 +2531,7 @@ export class WalletController extends BaseController { to_address: receiver, amount: parseFloat(amount), ft_identifier: token.contractName, - type: 'evm', + type: 'flow', }); return result; }; @@ -2685,7 +2685,7 @@ export class WalletController extends BaseController { to_address: (await this.getCurrentAddress()) || '', nft_identifier: flowIdentifier, from_type: 'flow', - to_type: 'flow', + to_type: 'evm', isMove: false, }); return txID; @@ -2841,8 +2841,8 @@ export class WalletController extends BaseController { from_address: childAddr, to_address: (await this.getCurrentAddress()) || '', nft_identifier: identifier, - from_type: 'evm', - to_type: 'flow', + from_type: 'flow', + to_type: 'evm', isMove: false, }); return txID; @@ -2967,8 +2967,8 @@ export class WalletController extends BaseController { from_address: flowIdentifier, to_address: (await this.getCurrentAddress()) || '', nft_identifier: flowIdentifier, - from_type: 'evm', - to_type: 'flow', + from_type: 'flow', + to_type: 'evm', isMove: false, }); return txID; From ddf30d6c6410235e6b4e5b8d0a6771355ad01bf3 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Fri, 29 Nov 2024 14:41:15 +1100 Subject: [PATCH 18/18] Merge branch 'dev' into 94-event-tracking-add-mixpanel-event-tracking --- eslint.config.mjs | 28 +- .../controller/provider/controller.ts | 80 ++- src/background/controller/provider/rpcFlow.ts | 28 +- src/background/controller/wallet.ts | 289 +++++----- src/background/fclConfig.ts | 3 + src/background/index.ts | 8 +- src/background/service/googleDrive.ts | 73 ++- src/background/service/openapi.ts | 111 ++-- src/background/service/permission.ts | 7 +- src/background/service/proxy.ts | 28 +- src/background/service/staking.ts | 14 +- src/background/service/storage-evaluator.ts | 6 +- src/background/service/userWallet.ts | 2 +- src/background/utils/index.ts | 8 +- src/background/utils/remoteConfig.ts | 64 +-- src/components/iconfont/IconAClose.js | 6 +- src/components/iconfont/IconAGroup762.js | 6 +- src/components/iconfont/IconAHamburgermenu.js | 6 +- .../iconfont/IconAVector11Stroke.js | 6 +- src/components/iconfont/IconAbout.js | 6 +- src/components/iconfont/IconAccount.js | 6 +- src/components/iconfont/IconAddressbook.d.ts | 4 +- src/components/iconfont/IconAddressbook.js | 8 +- src/components/iconfont/IconAppStore.js | 6 +- src/components/iconfont/IconBack.js | 6 +- src/components/iconfont/IconBackButton.js | 6 +- src/components/iconfont/IconBackup.js | 6 +- src/components/iconfont/IconBinance.js | 6 +- src/components/iconfont/IconCheckmark.js | 6 +- src/components/iconfont/IconChevronRight.js | 6 +- src/components/iconfont/IconClose.js | 6 +- src/components/iconfont/IconCoinbase.js | 6 +- src/components/iconfont/IconCopy.js | 6 +- src/components/iconfont/IconCreate.js | 6 +- src/components/iconfont/IconCurrency.js | 6 +- src/components/iconfont/IconDeveloper.js | 6 +- src/components/iconfont/IconDevices.js | 6 +- src/components/iconfont/IconDollar.js | 6 +- src/components/iconfont/IconDropdown.js | 6 +- src/components/iconfont/IconExec.js | 6 +- src/components/iconfont/IconEye.js | 6 +- src/components/iconfont/IconFlow.js | 6 +- src/components/iconfont/IconFlowns.js | 6 +- src/components/iconfont/IconFusd.js | 6 +- src/components/iconfont/IconGoogleDrive.js | 6 +- src/components/iconfont/IconHuobi.js | 6 +- src/components/iconfont/IconImport.js | 6 +- src/components/iconfont/IconInbox.js | 6 +- src/components/iconfont/IconKraken.js | 6 +- src/components/iconfont/IconKucoin.js | 6 +- src/components/iconfont/IconLock.js | 6 +- src/components/iconfont/IconLogo.js | 6 +- src/components/iconfont/IconMax.js | 6 +- src/components/iconfont/IconNfts.js | 6 +- src/components/iconfont/IconNotification.js | 6 +- src/components/iconfont/IconPlayStore.js | 6 +- src/components/iconfont/IconPlus.js | 6 +- src/components/iconfont/IconSearch.js | 6 +- src/components/iconfont/IconSecurity.js | 6 +- src/components/iconfont/IconSetting.js | 6 +- src/components/iconfont/IconSubtract.js | 6 +- src/components/iconfont/IconSwitch.js | 6 +- src/components/iconfont/IconTheme.js | 6 +- src/components/iconfont/IconUserSwitch.js | 6 +- src/components/iconfont/IconVector.js | 6 +- src/components/iconfont/IconVector2.js | 6 +- src/components/iconfont/IconWallet.js | 6 +- src/components/iconfont/index.d.ts | 2 +- src/components/iconfont/index.js | 6 +- src/constant/index.ts | 6 +- src/content-script/index.ts | 39 +- .../pageProvider/eth/utils/index.ts | 1 - .../eth/utils/message/portMessage.ts | 4 +- src/ui/FRWAssets/image/crown.png | Bin 0 -> 22759 bytes src/ui/FRWAssets/svg/crown.svg | 9 - src/ui/FRWAssets/svg/planetr.svg | 100 ---- src/ui/FRWComponent/Confetti.tsx | 171 ++++++ src/ui/FRWComponent/CredentialBox.tsx | 5 +- src/ui/FRWComponent/FRWChildProfile.tsx | 29 +- .../FRWComponent/FRWDropdownProfileCard.tsx | 15 +- src/ui/FRWComponent/FRWProfile.tsx | 15 +- src/ui/FRWComponent/FRWProfileCard.tsx | 15 +- src/ui/FRWComponent/FWContactCard.tsx | 15 +- src/ui/FRWComponent/FWDropDownProfile.tsx | 15 +- src/ui/FRWComponent/FWMoveDropdown.tsx | 15 +- src/ui/FRWComponent/KeyPathInputs.tsx | 12 +- src/ui/FRWComponent/LLComingSoonWarning.tsx | 4 +- src/ui/FRWComponent/LLConnectLoading.tsx | 3 +- src/ui/FRWComponent/LLContactCard.tsx | 15 +- src/ui/FRWComponent/LLContactEth.tsx | 15 +- src/ui/FRWComponent/LLDeleteBackupPopup.tsx | 4 +- src/ui/FRWComponent/LLFlownsPop.tsx | 17 +- src/ui/FRWComponent/LLFormHelperText.tsx | 42 +- src/ui/FRWComponent/LLHeader.tsx | 6 +- src/ui/FRWComponent/LLLinkingLoading.tsx | 9 +- src/ui/FRWComponent/LLNotFound.tsx | 12 +- src/ui/FRWComponent/LLPinAlert.tsx | 18 +- src/ui/FRWComponent/LLPrimaryButton.tsx | 4 +- src/ui/FRWComponent/LLProfile.tsx | 9 +- src/ui/FRWComponent/LLResetPopup.tsx | 4 +- src/ui/FRWComponent/LLSecondaryButton.tsx | 4 +- src/ui/FRWComponent/LLSpinner.tsx | 4 +- src/ui/FRWComponent/LLSwap.tsx | 8 +- src/ui/FRWComponent/LLWarningButton.tsx | 4 +- src/ui/FRWComponent/NumberTransition.tsx | 51 ++ src/ui/FRWComponent/PopupModal/errorModel.tsx | 3 +- .../PopupModal/importAddressModal.tsx | 5 +- src/ui/FRWComponent/PopupModal/resetModal.tsx | 3 +- src/ui/FRWComponent/SlideDown.tsx | 23 + src/ui/FRWComponent/SlideLeftRight.tsx | 26 + src/ui/FRWComponent/SlideRelative.tsx | 31 ++ src/ui/FRWComponent/StorageExceededAlert.tsx | 6 +- src/ui/FRWComponent/WarningSnackbar.tsx | 11 +- .../WarningStorageLowSnackbar.tsx | 7 +- src/ui/index.html | 3 + src/ui/index.tsx | 23 +- src/ui/popup.html | 3 + src/ui/style/LLTheme.ts | 9 +- src/ui/utils/hooks.ts | 233 ++++---- src/ui/utils/index.ts | 2 - src/ui/utils/number.ts | 12 +- src/ui/utils/options.ts | 125 ----- src/ui/utils/useStorageCheck.ts | 2 + .../views/AddWelcome/AddRegister/AllSet.tsx | 13 +- .../AddWelcome/AddRegister/GoogleBackup.tsx | 70 ++- .../AddWelcome/AddRegister/PickUsername.tsx | 108 ++-- .../AddWelcome/AddRegister/RecoveryPhrase.tsx | 380 +++++++------ .../AddWelcome/AddRegister/RegisterHeader.tsx | 11 +- .../AddWelcome/AddRegister/RepeatPhrase.tsx | 104 ++-- .../AddWelcome/AddRegister/SetPassword.tsx | 21 +- src/ui/views/AddWelcome/AddRegister/index.tsx | 76 +-- .../AddWelcome/AddressImport/GoogleBackup.tsx | 70 ++- .../GoogleImport/DecryptWallet.tsx | 34 +- .../GoogleImport/GoogleAccounts.tsx | 34 +- .../GoogleImport/RecoverPassword.tsx | 33 +- .../GoogleImport/RecoveryPhrase.tsx | 24 +- .../AddressImport/GoogleImport/index.tsx | 69 +-- .../AddWelcome/AddressImport/ImportPager.tsx | 20 +- .../AddWelcome/AddressImport/PickUsername.tsx | 134 ++--- .../AddressImport/RecoverPassword.tsx | 20 +- .../AddWelcome/AddressImport/SetPassword.tsx | 26 +- .../views/AddWelcome/AddressImport/index.tsx | 135 +++-- src/ui/views/AddWelcome/ProxySync/ProxyQr.tsx | 219 ++++---- .../AddWelcome/ProxySync/RegisterHeader.tsx | 11 +- .../AddWelcome/ProxySync/SetPassword.tsx | 33 +- src/ui/views/AddWelcome/ProxySync/index.tsx | 73 +-- .../views/AddWelcome/Sync/RegisterHeader.tsx | 11 +- src/ui/views/AddWelcome/Sync/SetPassword.tsx | 33 +- src/ui/views/AddWelcome/Sync/SyncQr.tsx | 285 +++++----- src/ui/views/AddWelcome/Sync/index.tsx | 72 +-- src/ui/views/AddWelcome/index.tsx | 65 +-- src/ui/views/AddressImport/GoogleBackup.tsx | 70 ++- .../GoogleImport/DecryptWallet.tsx | 34 +- .../GoogleImport/GoogleAccounts.tsx | 34 +- .../GoogleImport/RecoverPassword.tsx | 43 +- .../GoogleImport/RecoveryPhrase.tsx | 24 +- .../AddressImport/GoogleImport/index.tsx | 64 +-- src/ui/views/AddressImport/PickUsername.tsx | 171 +++--- .../views/AddressImport/RecoverPassword.tsx | 39 +- src/ui/views/AddressImport/SetPassword.tsx | 24 +- src/ui/views/AddressImport/index.tsx | 129 +++-- .../views/Approval/components/Confimation.tsx | 231 ++++---- src/ui/views/Approval/components/Connect.tsx | 6 +- .../Approval/components/DefaultBlock.tsx | 42 +- .../EthApproval/EthConfirm/DefaultBlock.tsx | 13 +- .../EthApproval/EthConfirm/index.tsx | 47 +- .../EthApproval/EthConnect/index.tsx | 66 ++- .../EthApproval/EthEnable/index.tsx | 34 +- .../EthApproval/EthMove/AccountBox.tsx | 171 ------ .../EthMove/EvmMove/MoveFromEvm/MoveToken.tsx | 294 ---------- .../EthMove/EvmMove/MoveFromEvm/index.tsx | 301 ----------- .../EvmMove/MoveFromFlow/MoveToken.tsx | 294 ---------- .../EthMove/EvmMove/MoveFromFlow/index.tsx | 319 ----------- .../EthMove/EvmMove/TransferFrom.tsx | 73 --- .../EthMove/EvmMove/TransferTo.tsx | 93 ---- .../EthMove/MoveCollectionSelect.tsx | 208 -------- .../EthApproval/EthMove/MoveEvm/index.tsx | 438 --------------- .../EthApproval/EthMove/MoveNfts/index.tsx | 464 ---------------- .../components/EthApproval/EthMove/index.tsx | 255 --------- .../EthApproval/EthSignType/DefaultBlock.tsx | 13 +- .../EthApproval/EthSignType/index.tsx | 31 +- .../EthApproval/EthSignV1/DefaultBlock.tsx | 13 +- .../EthApproval/EthSignV1/index.tsx | 31 +- .../EthApproval/EthSuggest/index.tsx | 149 +++--- .../EthApproval/EthSwitch/index.tsx | 40 +- .../views/Approval/components/SignMessage.tsx | 31 +- src/ui/views/Approval/components/index.ts | 3 +- src/ui/views/Dashboard/Header.tsx | 67 ++- src/ui/views/Dashboard/index.tsx | 35 +- .../views/EvmMove/MoveFromChild/MoveToken.tsx | 74 +-- src/ui/views/EvmMove/MoveFromChild/index.tsx | 139 ++--- .../views/EvmMove/MoveFromEvm/MoveToken.tsx | 75 ++- src/ui/views/EvmMove/MoveFromEvm/index.tsx | 159 +++--- .../views/EvmMove/MoveFromFlow/MoveToken.tsx | 75 +-- src/ui/views/EvmMove/MoveFromFlow/index.tsx | 144 ++--- .../EvmMove/MoveFromParent/MoveToken.tsx | 74 +-- src/ui/views/EvmMove/MoveFromParent/index.tsx | 149 +++--- src/ui/views/Forgot/Recover/RecoverPage.tsx | 39 +- src/ui/views/Forgot/Recover/ShowKey.tsx | 19 +- src/ui/views/Forgot/Recover/index.tsx | 70 +-- src/ui/views/Forgot/Reset/index.tsx | 77 +-- src/ui/views/Forgot/index.tsx | 17 +- .../Import/GoogleImport/DecryptWallet.tsx | 34 +- .../Import/GoogleImport/GoogleAccounts.tsx | 32 +- .../Import/GoogleImport/RecoverPassword.tsx | 45 +- .../Import/GoogleImport/RecoveryPhrase.tsx | 24 +- src/ui/views/Import/GoogleImport/index.tsx | 61 +-- .../Import/ImportComponent/PrivateKey.tsx | 32 +- .../Import/ImportComponent/SeedPhrase.tsx | 32 +- src/ui/views/Import/ImportPager.tsx | 55 +- src/ui/views/Import/ImportRecoveryPhrase.tsx | 209 ++++---- src/ui/views/Import/RecoverPassword.tsx | 37 +- src/ui/views/Import/options.ts | 125 ----- src/ui/views/Inbox/Nft.tsx | 18 +- src/ui/views/Inbox/Token.tsx | 26 +- src/ui/views/InnerRoute.tsx | 463 +++++++--------- src/ui/views/MainRoute.tsx | 106 +--- src/ui/views/MoveBoard/MoveEvm/index.tsx | 28 +- .../views/MoveBoard/MoveFromChild/index.tsx | 121 +++-- src/ui/views/MoveBoard/MoveToChild/index.tsx | 88 +-- .../views/NFT/NFTList/AddNFTConfirmation.tsx | 18 +- .../views/NFT/SendNFT/MoveNftConfirmation.tsx | 57 +- src/ui/views/NFT/SendNFT/MovefromParent.tsx | 62 ++- .../views/NFT/SendNFT/SendNFTConfirmation.tsx | 61 ++- src/ui/views/NFT/SendNFT/SendToAddress.tsx | 256 ++++----- src/ui/views/NFT/index.tsx | 52 +- src/ui/views/NftEvm/GridView.tsx | 16 +- .../NftEvm/SendNFT/MoveNftConfirmation.tsx | 49 +- .../NftEvm/SendNFT/SendNFTConfirmation.tsx | 49 +- src/ui/views/NftEvm/SendNFT/SendToAddress.tsx | 78 +-- src/ui/views/NftEvm/index.tsx | 50 +- src/ui/views/RecoverRegister/AllSet.tsx | 11 +- src/ui/views/RecoverRegister/PickUsername.tsx | 154 +++--- .../views/RecoverRegister/RecoveryPhrase.tsx | 15 +- .../views/RecoverRegister/RegisterHeader.tsx | 12 +- src/ui/views/RecoverRegister/SetPassword.tsx | 23 +- src/ui/views/RecoverRegister/index.tsx | 83 +-- src/ui/views/Register/AllSet.tsx | 14 +- src/ui/views/Register/GoogleBackup.tsx | 70 ++- src/ui/views/Register/PickUsername.tsx | 169 +++--- src/ui/views/Register/RecoveryPhrase.tsx | 24 +- src/ui/views/Register/RegisterHeader.tsx | 11 +- src/ui/views/Register/RegisterPager.tsx | 71 +-- src/ui/views/Register/RepeatPhrase.tsx | 85 ++- src/ui/views/Register/SetPassword.tsx | 25 +- src/ui/views/Reset/RecoverPassword.tsx | 43 +- src/ui/views/Reset/ResetRecoveryPhrase.tsx | 151 +++--- src/ui/views/Reset/index.tsx | 56 +- src/ui/views/RetrievePK/index.tsx | 39 +- src/ui/views/Send/AccountsList.tsx | 26 +- src/ui/views/Send/AddressBookList.tsx | 10 +- src/ui/views/Send/RecentList.tsx | 13 +- src/ui/views/Send/SearchList.tsx | 13 +- src/ui/views/Send/SendAmount.tsx | 135 ++--- src/ui/views/Send/SendEth/EvmConfirmation.tsx | 45 +- .../views/Send/SendEth/ToEthConfirmation.tsx | 57 +- src/ui/views/Send/SendEth/index.tsx | 181 ++++--- src/ui/views/Send/TransferAmount.tsx | 74 +-- src/ui/views/Send/TransferConfirmation.tsx | 57 +- src/ui/views/Setting/About/About.tsx | 15 +- src/ui/views/Setting/Account/index.tsx | 117 ++-- .../Setting/DeveloperMode/DeveloperMode.tsx | 504 +++++++----------- src/ui/views/Setting/Devices/DeviceInfo.tsx | 36 +- src/ui/views/Setting/Devices/Devices.tsx | 66 +-- .../views/Setting/Devices/WalletConnect.tsx | 35 +- .../views/Setting/Linked/LinkedCollection.tsx | 52 +- src/ui/views/Setting/Linked/LinkedDetail.tsx | 96 ++-- src/ui/views/Setting/Resetpwd.tsx | 48 +- src/ui/views/Setting/Wallet/WalletDetail.tsx | 121 +++-- src/ui/views/Setting/index.tsx | 127 +++-- .../Setting/privatekey/Privatekeypassword.tsx | 61 ++- .../recoveryphase/Recoveryphrasepassword.tsx | 44 +- src/ui/views/SortHat.tsx | 12 +- src/ui/views/Staking/NoStake.tsx | 61 ++- src/ui/views/Staking/NodeDetail.tsx | 29 +- src/ui/views/Staking/StakingPage.tsx | 98 ++-- src/ui/views/Staking/TransferList.tsx | 59 +- src/ui/views/Staking/UnstakePage.tsx | 97 ++-- .../views/Staking/components/StakeAmount.tsx | 34 +- .../Staking/components/UnstakeAmount.tsx | 34 +- src/ui/views/Staking/index.tsx | 19 +- src/ui/views/Swap/SelectToken.tsx | 34 +- src/ui/views/Swap/SwapTarget.tsx | 29 +- src/ui/views/Swap/TransferAmount.tsx | 18 +- src/ui/views/Swap/TransferConfirmation.tsx | 45 +- src/ui/views/Swap/index.tsx | 152 +++--- src/ui/views/SwitchUnlock/index.tsx | 42 +- src/ui/views/Sync/AllSet.tsx | 11 +- src/ui/views/Sync/RegisterHeader.tsx | 11 +- src/ui/views/Sync/SetPassword.tsx | 39 +- src/ui/views/Sync/SyncQr.tsx | 276 +++++----- src/ui/views/Sync/index.tsx | 71 +-- src/ui/views/TokenDetail/TokenInfoCard.tsx | 47 +- src/ui/views/TokenDetail/index.tsx | 5 +- .../views/TokenList/AddTokenConfirmation.tsx | 7 +- src/ui/views/Unlock/index.tsx | 79 +-- src/ui/views/Wallet/Coinlist.tsx | 22 +- src/ui/views/Wallet/TransferList.tsx | 79 ++- src/ui/views/Wallet/index.tsx | 283 +++++----- src/ui/views/WelcomePage/index.tsx | 17 +- src/ui/views/index.tsx | 23 +- src/utils/message/portMessage.ts | 1 - 302 files changed, 6646 insertions(+), 10345 deletions(-) create mode 100644 src/ui/FRWAssets/image/crown.png delete mode 100644 src/ui/FRWAssets/svg/crown.svg delete mode 100644 src/ui/FRWAssets/svg/planetr.svg create mode 100644 src/ui/FRWComponent/Confetti.tsx create mode 100644 src/ui/FRWComponent/NumberTransition.tsx create mode 100644 src/ui/FRWComponent/SlideDown.tsx create mode 100644 src/ui/FRWComponent/SlideLeftRight.tsx create mode 100644 src/ui/FRWComponent/SlideRelative.tsx delete mode 100644 src/ui/utils/options.ts delete mode 100644 src/ui/views/Approval/components/EthApproval/EthMove/AccountBox.tsx delete mode 100644 src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/MoveFromEvm/MoveToken.tsx delete mode 100644 src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/MoveFromEvm/index.tsx delete mode 100644 src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/MoveFromFlow/MoveToken.tsx delete mode 100644 src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/MoveFromFlow/index.tsx delete mode 100644 src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/TransferFrom.tsx delete mode 100644 src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/TransferTo.tsx delete mode 100644 src/ui/views/Approval/components/EthApproval/EthMove/MoveCollectionSelect.tsx delete mode 100644 src/ui/views/Approval/components/EthApproval/EthMove/MoveEvm/index.tsx delete mode 100644 src/ui/views/Approval/components/EthApproval/EthMove/MoveNfts/index.tsx delete mode 100644 src/ui/views/Approval/components/EthApproval/EthMove/index.tsx delete mode 100644 src/ui/views/Import/options.ts diff --git a/eslint.config.mjs b/eslint.config.mjs index 4b5b62ef..c7ad6bc5 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -49,13 +49,16 @@ export default [ '@typescript-eslint/no-explicit-any': 'warn', '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], - '@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }], + '@typescript-eslint/consistent-type-imports': [ + 'error', + { prefer: 'type-imports', fixStyle: 'inline-type-imports', disallowTypeAnnotations: false }, + ], // React rules 'react/react-in-jsx-scope': 'error', // Required for React 17 'react/prop-types': 'off', 'react-hooks/rules-of-hooks': 'error', - 'react-hooks/exhaustive-deps': 'warn', + 'react-hooks/exhaustive-deps': 'error', // Import rules 'import/no-unresolved': 'error', @@ -78,9 +81,6 @@ export default [ eqeqeq: ['error', 'always'], 'no-unused-expressions': 'error', 'no-duplicate-imports': 'error', - - // Chrome extension specific - 'no-restricted-globals': ['error', 'window', 'document'], }, }, @@ -92,4 +92,22 @@ export default [ '@typescript-eslint/no-explicit-any': 'off', }, }, + + // Background-specific config for chrome extension + { + files: ['**/src/background/**/*.{js,jsx,ts,tsx}'], + rules: { + 'no-restricted-globals': [ + 'error', + { + name: 'window', + message: 'Do not use window in background scripts - use globalThis instead', + }, + { + name: 'document', + message: 'DOM APIs are not available in background scripts', + }, + ], + }, + }, ]; diff --git a/src/background/controller/provider/controller.ts b/src/background/controller/provider/controller.ts index bf16f35c..47ce4179 100644 --- a/src/background/controller/provider/controller.ts +++ b/src/background/controller/provider/controller.ts @@ -1,29 +1,27 @@ +import { TypedDataUtils, SignTypedDataVersion, normalize } from '@metamask/eth-sig-util'; +import * as fcl from '@onflow/fcl'; +import BigNumber from 'bignumber.js'; import { ethErrors } from 'eth-rpc-errors'; +import { isHexString, intToHex } from 'ethereumjs-util'; +import { ethers } from 'ethers'; +import RLP from 'rlp'; +import Web3 from 'web3'; +import { stringToHex } from 'web3-utils'; -// import RpcCache from 'background/utils/rpcCache'; +import { ensureEvmAddressPrefix, isValidEthereumAddress } from '@/ui/utils/address'; +import { signWithKey } from '@/ui/utils/modules/passkey.js'; import { permissionService, - preferenceService, sessionService, signTextHistoryService, keyringService, notificationService, } from 'background/service'; -import Wallet from '../wallet'; -import BaseController from '../base'; import { EVM_ENDPOINT } from 'consts'; -import { stringToHex } from 'web3-utils'; -import { normalize as normalizeAddress } from 'eth-sig-util'; -import { ethers } from 'ethers'; -import { isHexString, intToHex } from 'ethereumjs-util'; -import BigNumber from 'bignumber.js'; -import RLP from 'rlp'; -import Web3 from 'web3'; -import { signWithKey } from '@/ui/utils/modules/passkey.js'; -import { ensureEvmAddressPrefix, isValidEthereumAddress } from '@/ui/utils/address'; + import { storage } from '../../webapi'; -import { TypedDataUtils, SignTypedDataVersion } from '@metamask/eth-sig-util'; -import * as fcl from '@onflow/fcl'; +import BaseController from '../base'; +import Wallet from '../wallet'; interface Web3WalletPermission { // The name of the method corresponding to the permission @@ -40,26 +38,6 @@ interface COAOwnershipProof { signatures: Uint8Array[]; } -const v1SignTypedDataVlidation = ({ - data: { - params: [_, from], - }, -}) => { - const currentAddress = preferenceService.getCurrentAccount()?.address.toLowerCase(); - if (from.toLowerCase() !== currentAddress) - throw ethErrors.rpc.invalidParams('from should be same as current address'); -}; - -const signTypedDataVlidation = ({ - data: { - params: [from, _], - }, -}) => { - const currentAddress = preferenceService.getCurrentAccount()?.address.toLowerCase(); - if (from.toLowerCase() !== currentAddress) - throw ethErrors.rpc.invalidParams('from should be same as current address'); -}; - function removeHexPrefix(hexString: string): string { return hexString.startsWith('0x') ? hexString.substring(2) : hexString; } @@ -197,9 +175,16 @@ class ProviderController extends BaseController { }); }; - ethRequestAccounts = async ({ session: { origin } }) => { + ethRequestAccounts = async ({ session: { origin, name, icon } }) => { if (!permissionService.hasPermission(origin)) { - throw ethErrors.provider.unauthorized(); + const { defaultChain, signPermission } = await notificationService.requestApproval( + { + params: { origin, name, icon }, + approvalComponent: 'EthConnect', + }, + { height: 599 } + ); + permissionService.addConnectedSite(origin, name, icon, defaultChain); } const currentWallet = await Wallet.getMainWallet(); @@ -224,14 +209,6 @@ class ProviderController extends BaseController { res = await Wallet.queryEvmAddress(currentWallet); } - await notificationService.requestApproval( - { - params: { origin }, - approvalComponent: 'EthConnect', - }, - { height: 599 } - ); - res = ensureEvmAddressPrefix(res); const account = res ? [res.toLowerCase()] : []; sessionService.broadcastEvent('accountsChanged', account); @@ -340,6 +317,16 @@ class ProviderController extends BaseController { return result; }; + walletRevokePermissions = async ({ session: { origin }, data: { params } }) => { + const isUnlocked = await Wallet.isUnlocked(); + if (isUnlocked && Wallet.getConnectedSite(origin)) { + if (params?.[0] && 'eth_accounts' in params[0]) { + Wallet.removeConnectedSite(origin); + } + } + return null; + }; + walletWatchAsset = async ({ data }) => { const result = await notificationService.requestApproval( { @@ -415,8 +402,7 @@ class ProviderController extends BaseController { }; private _checkAddress = async (address) => { - // eslint-disable-next-line prefer-const - return normalizeAddress(address).toLowerCase(); + return normalize(address).toLowerCase(); }; ethChainId = async ({ session }) => { diff --git a/src/background/controller/provider/rpcFlow.ts b/src/background/controller/provider/rpcFlow.ts index 4ff5271c..8540079b 100644 --- a/src/background/controller/provider/rpcFlow.ts +++ b/src/background/controller/provider/rpcFlow.ts @@ -1,4 +1,7 @@ import { ethErrors } from 'eth-rpc-errors'; + +import eventBus from '@/eventBus'; +import { isValidEthereumAddress } from '@/ui/utils/address'; import { keyringService, notificationService, @@ -7,10 +10,10 @@ import { } from 'background/service'; import { PromiseFlow, underline2Camelcase } from 'background/utils'; import { EVENTS } from 'consts'; -import providerController from './controller'; -import eventBus from '@/eventBus'; + import Wallet from '../wallet'; -import { isValidEthereumAddress } from '@/ui/utils/address'; + +import providerController from './controller'; const isSignApproval = (type: string) => { const SIGN_APPROVALS = ['SignText', 'SignTypedData', 'SignTx', 'EthConfirm']; @@ -62,25 +65,6 @@ const flowContext = flow if (mapMethod === 'ethAccounts' && (!site || !isUnlock)) { throw new Error('Origin not connected. Please connect first.'); } - // console.log('isUnlock ', isUnlock) - // await notificationService.requestApproval( - // { - // params: { origin, name }, - // approvalComponent: 'EthConnect', lock: true - // }, - // { height: 599 } - // ); - // if (!isUnlock) { - // ctx.request.requestedApproval = true; - // lockedOrigins.add(origin); - // try { - // await notificationService.requestApproval({ lock: true }); - // lockedOrigins.delete(origin); - // } catch (e) { - // lockedOrigins.delete(origin); - // throw e; - // } - // } } return next(); diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index 2e277fd7..9bc4dc0b 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -1,12 +1,13 @@ /* eslint-disable no-console */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { getAuth } from '@firebase/auth'; import * as fcl from '@onflow/fcl'; import * as t from '@onflow/types'; import BN from 'bignumber.js'; +import * as bip39 from 'bip39'; import { ethErrors } from 'eth-rpc-errors'; import * as ethUtil from 'ethereumjs-util'; import { getApp } from 'firebase/app'; +import { getAuth } from 'firebase/auth'; import web3, { TransactionError } from 'web3'; import eventBus from '@/eventBus'; @@ -1014,86 +1015,118 @@ export class WalletController extends BaseController { }; //coinList - getCoinList = async (_expiry = 5000): Promise => { - const network = await this.getNetwork(); - const now = new Date(); - const expiry = coinListService.getExpiry(); - let childType = await userWalletService.getActiveWallet(); - childType = childType === 'evm' ? 'evm' : 'coinItem'; - // compare the expiry time of the item with the current time - if (now.getTime() > expiry) { - if (childType === 'evm') { - await this.refreshEvmList(_expiry); - } else { - await this.refreshCoinList(_expiry); + getCoinList = async (_expiry = 60000, currentEnv = ''): Promise => { + try { + const network = await this.getNetwork(); + const now = new Date(); + const expiry = coinListService.getExpiry(); + + // Determine childType: use currentEnv if not empty, otherwise fallback to active wallet type + let childType = currentEnv || (await userWalletService.getActiveWallet()); + childType = childType === 'evm' ? 'evm' : 'coinItem'; + + // Otherwise, fetch from the coinListService + const listCoins = coinListService.listCoins(network, childType); + + // Validate and ensure listCoins is of type CoinItem[] + if ( + !listCoins || + !Array.isArray(listCoins) || + listCoins.length === 0 || + now.getTime() > expiry + ) { + console.log('listCoins is empty or invalid, refreshing...'); + let refreshedList; + if (childType === 'evm') { + refreshedList = await this.refreshEvmList(_expiry); + } else { + refreshedList = await this.refreshCoinList(_expiry); + } + if (refreshedList) { + return refreshedList; + } } + + return listCoins; + } catch (error) { + console.error('Error fetching coin list:', error); + throw new Error('Failed to fetch coin list'); // Re-throw the error with a custom message } - const listCoins = coinListService.listCoins(network, childType); - return listCoins; }; - private tokenPrice = async (tokenSymbol: string, address: string, data, contractName: string) => { + private async getFlowTokenPrice(flowPrice?: string): Promise { + const cachedFlowTokenPrice = await storage.getExpiry('flowTokenPrice'); + if (cachedFlowTokenPrice) { + if (flowPrice) { + cachedFlowTokenPrice.price.last = flowPrice; + } + return cachedFlowTokenPrice; + } + const result = await openapiService.getTokenPrice('flow'); + if (flowPrice) { + result.price.last = flowPrice; + } + await storage.setExpiry('flowTokenPrice', result, 300000); // Cache for 5 minutes + return result; + } + + private async getFixedTokenPrice(symbol: string): Promise { + if (symbol === 'usdc') { + return await openapiService.getUSDCPrice(); + } else if (symbol === 'fusd') { + return Promise.resolve({ + price: { last: '1.0', change: { percentage: '0.0' } }, + }); + } + return null; + } + + private async calculateTokenPrice(token: string, price: string | null): Promise { + if (price) { + return { price: { last: price, change: { percentage: '0.0' } } }; + } else { + return { price: { last: '0.0', change: { percentage: '0.0' } } }; + } + } + + private async tokenPrice( + tokenSymbol: string, + address: string, + data: Record, + contractName: string + ) { const token = tokenSymbol.toLowerCase(); - const key = contractName.toLowerCase() + '' + address.toLowerCase(); + const key = `${contractName.toLowerCase()}${address.toLowerCase()}`; const price = await openapiService.getPricesByKey(key, data); - switch (token) { - case 'flow': { - const flowTokenPrice = await storage.getExpiry('flowTokenPrice'); - if (flowTokenPrice) { - return flowTokenPrice; - } else { - const result = await openapiService.getTokenPrice('flow'); - await storage.setExpiry('flowTokenPrice', result, 300000); // 5 minutes in milliseconds - return result; - } - } - case 'usdc': - return await openapiService.getUSDCPrice(); - case 'fusd': - return Promise.resolve({ - price: { last: '1.0', change: { percentage: '0.0' } }, - }); - default: - if (price) { - return { price: { last: price, change: { percentage: '0.0' } } }; - } else { - return null; - } + if (token === 'flow') { + const flowPrice = price || data['FLOW']; + return this.getFlowTokenPrice(flowPrice); } - }; - private evmtokenPrice = async (tokeninfo, data) => { + const fixedTokenPrice = await this.getFixedTokenPrice(token); + if (fixedTokenPrice) return fixedTokenPrice; + + return this.calculateTokenPrice(token, price); + } + + private async evmtokenPrice(tokeninfo, data) { const token = tokeninfo.symbol.toLowerCase(); const price = await openapiService.getPricesByEvmaddress(tokeninfo.address, data); - switch (token) { - case 'flow': { - const flowTokenPrice = await storage.getExpiry('flowTokenPrice'); - if (flowTokenPrice) { - return flowTokenPrice; - } else { - const result = await openapiService.getTokenPrice('flow'); - await storage.setExpiry('flowTokenPrice', result, 300000); // 5 minutes in milliseconds - return result; - } - } - case 'usdc': - return await openapiService.getUSDCPrice(); - case 'fusd': - return Promise.resolve({ - price: { last: '1.0', change: { percentage: '0.0' } }, - }); - default: - if (price) { - return { price: { last: price, change: { percentage: '0.0' } } }; - } else { - return { price: { last: 0, change: { percentage: '0.0' } } }; - } + + if (token === 'flow') { + const flowPrice = price || data['FLOW']; + return this.getFlowTokenPrice(flowPrice); } - }; + + const fixedTokenPrice = await this.getFixedTokenPrice(token); + if (fixedTokenPrice) return fixedTokenPrice; + + return this.calculateTokenPrice(token, price); + } refreshCoinList = async ( - _expiry = 5000, + _expiry = 60000, { signal } = { signal: new AbortController().signal } ) => { try { @@ -1118,7 +1151,7 @@ export class WalletController extends BaseController { console.error('Error refresh token list balance:', error); throw new Error('Failed to refresh token list balance'); } - const data = await openapiService.getTokenPrices(); + const data = await openapiService.getTokenPrices('pricesMap'); // Map over tokenList to get prices and handle errors individually const pricesPromises = tokenList.map(async (token) => { try { @@ -1169,14 +1202,7 @@ export class WalletController extends BaseController { // Add all coins at once if (signal.aborted) throw new Error('Operation aborted'); coinListService.addCoins(coins, network); - - // const allTokens = await openapiService.getAllTokenInfo(); - // const enabledSymbols = tokenList.map((token) => token.symbol); - // const disableSymbols = allTokens.map((token) => token.symbol).filter((symbol) => !enabledSymbols.includes(symbol)); - // console.log('disableSymbols are these ', disableSymbols, enabledSymbols, coins) - // disableSymbols.forEach((coin) => coinListService.removeCoin(coin, network)); - const coinListResult = coinListService.listCoins(network); - return coinListResult; + return coins; } catch (err) { if (err.message === 'Operation aborted') { console.error('refreshCoinList operation aborted.'); @@ -1220,7 +1246,7 @@ export class WalletController extends BaseController { throw new Error('Failed to fetch token list balance'); } - const data = await openapiService.getTokenPrices(); + const data = await openapiService.getTokenPrices('pricesMap'); // Map over tokenList to get prices and handle errors individually const pricesPromises = tokenList.map(async (token) => { @@ -1304,7 +1330,7 @@ export class WalletController extends BaseController { } }; - refreshEvmList = async (_expiry = 5000) => { + refreshEvmList = async (_expiry = 60000) => { const now = new Date(); const exp = _expiry + now.getTime(); coinListService.setExpiry(exp); @@ -1345,37 +1371,6 @@ export class WalletController extends BaseController { }); }; - // const customToken = (mergedList, evmCustomToken) => { - // return mergedList.map(token => { - // const balanceInfo = evmCustomToken.map(customToken => { - // if (customToken.address.toLowerCase() === token.address.toLowerCase()) { - - // return { - // ...token, - // custom: true - // } - - // } else { - // return { - - // "chainId": 747, - // "address": customToken.address, - // "symbol": customToken.unit, - // "name": customToken.coin, - // "decimals": customToken.decimals, - // "logoURI": "", - // "flowIdentifier": "", - // "tags": [], - // "balance": 0, - // custom: true - - // } - // } - // }); - // return balanceInfo; - // }); - // }; - const customToken = (coins, evmCustomToken) => { const updatedList = [...coins]; @@ -1406,9 +1401,8 @@ export class WalletController extends BaseController { const mergedList = await mergeBalances(tokenList, allBalanceMap, flowBalance); - const data = await openapiService.getTokenEvmPrices(); + const data = await openapiService.getTokenPrices('evmPrice', true); const prices = tokenList.map((token) => this.evmtokenPrice(token, data)); - const allPrice = await Promise.all(prices); const coins: CoinItem[] = mergedList.map((token, index) => { return { @@ -1430,18 +1424,8 @@ export class WalletController extends BaseController { }); const coinWithCustom = await customToken(coins, evmCustomToken); - - coinWithCustom - .sort((a, b) => { - if (b.total === a.total) { - return b.balance - a.balance; - } else { - return b.total - a.total; - } - }) - .map((coin) => coinListService.addCoin(coin, network, 'evm')); - - return coinListService.listCoins(network, 'evm'); + coinWithCustom.map((coin) => coinListService.addCoin(coin, network, 'evm')); + return coinWithCustom; }; reqeustEvmNft = async () => { @@ -3432,29 +3416,44 @@ export class WalletController extends BaseController { } } catch (err: unknown) { // An error has occurred while listening to the transaction + console.log(typeof err); + console.log({ err }); console.error('listenTransaction error ', err); - if (err instanceof TransactionError) { - // Tell the UI that there was an error - chrome.runtime.sendMessage({ - msg: 'transactionError', - errorMessage: err.message, - errorCode: err.code, - }); + let errorMessage = 'unknown error'; + let errorCode: number | undefined = undefined; - // Track the transaction result - mixpanelTrack.track('transaction_result', { - tx_id: txId, - is_successful: false, - error_message: err.message, - }); - } else { - // Track the transaction result - mixpanelTrack.track('transaction_result', { - tx_id: txId, - is_successful: false, - error_message: 'Unknown Error', - }); + if (err instanceof TransactionError) { + errorCode = err.code; + errorMessage = err.message; + } else if (err instanceof Error) { + errorMessage = err.message; + } else if (typeof err === 'string') { + errorMessage = err; + // From fcl-core transaction-error.ts + const ERROR_CODE_REGEX = /\[Error Code: (\d+)\]/; + const match = errorMessage.match(ERROR_CODE_REGEX); + errorCode = match ? parseInt(match[1], 10) : undefined; } + + console.warn({ + msg: 'transactionError', + errorMessage, + errorCode, + }); + + // Track the transaction error + mixpanelTrack.track('transaction_result', { + tx_id: txId, + is_successful: false, + error_message: errorMessage, + }); + + // Tell the UI that there was an error + chrome.runtime.sendMessage({ + msg: 'transactionError', + errorMessage, + errorCode, + }); } finally { // Remove the pending transaction from the UI await chrome.storage.session.remove('transactionPending'); @@ -3753,6 +3752,10 @@ export class WalletController extends BaseController { }; uploadMnemonicToGoogleDrive = async (mnemonic, username, password) => { + const isValidMnemonic = bip39.validateMnemonic(mnemonic); + if (!isValidMnemonic) { + throw new Error('Invalid mnemonic'); + } const app = getApp(process.env.NODE_ENV!); const user = await getAuth(app).currentUser; try { diff --git a/src/background/fclConfig.ts b/src/background/fclConfig.ts index 30cf791b..404d0596 100644 --- a/src/background/fclConfig.ts +++ b/src/background/fclConfig.ts @@ -1,5 +1,6 @@ import * as fcl from '@onflow/fcl'; import { send as httpSend } from '@onflow/transport-http'; + import { storage } from './webapi'; const CONTRACTS_URL = @@ -103,6 +104,7 @@ export const fclMainnetConfig = async () => { const config = fcl .config() .put('accessNode.api', 'https://rest-mainnet.onflow.org') + // note this is the default transport. We don't really need to set this .put('sdk.transport', httpSend) .put('flow.network', 'mainnet'); @@ -122,6 +124,7 @@ export const fclTestnetConfig = async () => { const config = fcl .config() .put('accessNode.api', 'https://rest-testnet.onflow.org') + // note this is the default transport. We don't really need to set this .put('sdk.transport', httpSend) .put('flow.network', 'testnet'); diff --git a/src/background/index.ts b/src/background/index.ts index 8b97c15b..ef7179d1 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -1,13 +1,13 @@ import 'reflect-metadata'; +import { ethErrors } from 'eth-rpc-errors'; +import { initializeApp } from 'firebase/app'; import { getAuth, signInAnonymously, indexedDBLocalPersistence, setPersistence, onAuthStateChanged, -} from '@firebase/auth'; -import { ethErrors } from 'eth-rpc-errors'; -import { initializeApp } from 'firebase/app'; +} from 'firebase/auth'; import eventBus from '@/eventBus'; import type { WalletController } from 'background/controller/wallet'; @@ -415,3 +415,5 @@ chrome.runtime.onConnect.addListener((port) => { function onMessage(msg, port) { console.log('received', msg, 'from', port.sender); } + +console.log('Is fetch native?', fetch.toString().includes('[native code]')); diff --git a/src/background/service/googleDrive.ts b/src/background/service/googleDrive.ts index a934ab31..c8ec6481 100644 --- a/src/background/service/googleDrive.ts +++ b/src/background/service/googleDrive.ts @@ -1,4 +1,5 @@ import aesjs from 'aes-js'; + interface GoogleDriveFileModel { kind: string; id: string; @@ -32,7 +33,7 @@ class GoogleDriveService { hasBackup = async () => { const files = await this.listFiles(); - return files.length > 0; + return files; }; hasUserBackup = async (username: string): Promise => { @@ -43,7 +44,7 @@ class GoogleDriveService { hasGooglePremission = async (): Promise => { try { const token = await this.getAuthTokenWrapper(false); - return token != undefined || token != null; + return token !== undefined && token !== null; } catch (err) { return false; } @@ -74,6 +75,27 @@ class GoogleDriveService { }; }; + parseGoogleText = (encryptedData: string) => { + let encryptedHex; + + // Attempt to parse the data as JSON + try { + const sanitizedData = encryptedData.replace(/\s+/g, ''); + const parsedData = JSON.parse(sanitizedData); + encryptedHex = parsedData?.hex || parsedData; + } catch (error) { + console.warn('JSON parsing failed, checking if raw hex string:', error.message); + + const rawHex = encryptedData.replace(/\s+/g, ''); + if (/^[0-9a-fA-F]+$/.test(rawHex)) { + encryptedHex = rawHex; + } else { + throw new Error('Invalid input: not JSON and not a valid hex string'); + } + } + return encryptedHex; + }; + uploadMnemonicToGoogleDrive = async ( mnemonic: string, username: string, @@ -81,20 +103,16 @@ class GoogleDriveService { password: string ) => { // TODO: FIX this - // const hasBackup = await this.hasBackup() const item = this.encodeToDriveItem(mnemonic, username, uid, password); const files = await this.listFiles(); - if (files.length === 0) { - // No backup - const content = this.encrypt(JSON.stringify([item]), this.AES_KEY); - return await this.createFile(content); + if (!files) { + return []; } - // Has backup then append the file - const fileId = files[0].id; + const fileId = files.id; this.fileId = fileId; - const fileContent = await this.getFile(fileId); - const text = await fileContent.text(); - const decodeContent = await this.decrypt(JSON.parse(text), this.AES_KEY); + const text = await this.getFile(fileId); + const parsedText = this.parseGoogleText(text); + const decodeContent = await this.decrypt(parsedText, this.AES_KEY); const content: DriveItem[] = JSON.parse(decodeContent); const result = content.filter((file) => file.username !== username); result.unshift(item); @@ -104,20 +122,15 @@ class GoogleDriveService { loadBackup = async (): Promise => { const files = await this.listFiles(); - if (files.length === 0) { - return []; - } - const firstOutblockBackup = files.find((file) => file.name === 'outblock_backup'); - if (!firstOutblockBackup) { + if (!files) { return []; } - const fileId = firstOutblockBackup.id; + const fileId = files.id; this.fileId = fileId; - const fileContent = await this.getFile(fileId); - const text = await fileContent.text(); - const decodeContent = await this.decrypt(JSON.parse(text), this.AES_KEY); + const text = await this.getFile(fileId); + const parsedText = this.parseGoogleText(text); + const decodeContent = await this.decrypt(parsedText, this.AES_KEY); const content: DriveItem[] = JSON.parse(decodeContent); - console.log('content ', content); this.fileList = content; return content; }; @@ -163,15 +176,18 @@ class GoogleDriveService { return null; }; - listFiles = async (): Promise => { + listFiles = async (): Promise => { const { files } = await this.sendRequest('drive/v3/files/', 'GET', { spaces: 'appDataFolder', }).then((response) => response.json()); - return files; + const firstOutblockBackup = files.find((file) => file.name === 'outblock_backup'); + return firstOutblockBackup; }; getFile = async (fileId: string) => { - return await this.sendRequest(`drive/v3/files/${fileId}/`, 'GET', { alt: 'media' }); + const result = await this.sendRequest(`drive/v3/files/${fileId}/`, 'GET', { alt: 'media' }); + const text = await result.text(); + return text; }; createFile = async (content: string) => { @@ -189,7 +205,6 @@ class GoogleDriveService { const form = new FormData(); form.append('metadata', new Blob([JSON.stringify(metadata)], { type: 'application/json' })); form.append('file', file); - // return await this.makeXMLRequest('post', 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id', form) return await this.sendRequest( 'upload/drive/v3/files', @@ -249,7 +264,10 @@ class GoogleDriveService { deleteAllFile = async () => { const files = await this.listFiles(); - return Promise.all(files.map((file) => this.deleteFile(file.id))); + if (!files) { + return; + } + return this.deleteFile(files.id); }; sendRequest = async ( @@ -312,7 +330,6 @@ class GoogleDriveService { // console.log('decryptedBytes ->', decryptedBytes) // Convert our bytes back into text const decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes); - // console.log('decryptedText ->', decryptedText); return decryptedText.trim(); }; diff --git a/src/background/service/openapi.ts b/src/background/service/openapi.ts index 12596f92..eb687647 100644 --- a/src/background/service/openapi.ts +++ b/src/background/service/openapi.ts @@ -1,3 +1,7 @@ +import * as fcl from '@onflow/fcl'; +import type { Method } from 'axios'; +import dayjs from 'dayjs'; +import { initializeApp, getApp } from 'firebase/app'; import { getAuth, signInWithCustomToken, @@ -5,12 +9,8 @@ import { indexedDBLocalPersistence, signInAnonymously, onAuthStateChanged, -} from '@firebase/auth'; -import type { Unsubscribe } from '@firebase/util'; -import * as fcl from '@onflow/fcl'; -import type { Method } from 'axios'; -import dayjs from 'dayjs'; -import { initializeApp, getApp } from 'firebase/app'; + type Unsubscribe, +} from 'firebase/auth'; import { getInstallations, getId } from 'firebase/installations'; import type { TokenInfo } from 'flow-native-token-registry'; import log from 'loglevel'; @@ -21,6 +21,7 @@ import { createPersistStore, getScripts, findKeyAndInfo } from 'background/utils import { getFirbaseConfig, getFirbaseFunctionUrl } from 'background/utils/firebaseConfig'; import fetchConfig from 'background/utils/remoteConfig'; import { INITIAL_OPENAPI_URL, WEB_NEXT_URL } from 'consts'; +import { isValidEthereumAddress } from 'ui/utils/address'; import { getPeriodFrequency } from '../../utils'; import { fclMainnetConfig, fclTestnetConfig } from '../fclConfig'; @@ -41,9 +42,6 @@ import { PriceProvider, } from './networkModel'; -// import { getRemoteConfig, fetchAndActivate, getValue } from 'firebase/remote-config'; -// import configJson from 'background/utils/firebase.config.json'; - import { userWalletService, coinListService, @@ -85,10 +83,9 @@ const remoteFetch = fetchConfig; const pricesMap = {}; const waitForAuthInit = async () => { - let unsubscribe: Promise; + let unsubscribe: Unsubscribe; await new Promise((resolve) => { - // @ts-expect-error firebase auth function - unsubscribe = auth.onAuthStateChanged((user) => resolve()); + unsubscribe = auth.onAuthStateChanged((_user) => resolve()); }); (await unsubscribe!)(); }; @@ -518,61 +515,42 @@ class OpenApiService { } }; - getTokenPrices = async () => { - const tokenPriceMap = await storage.getExpiry('pricesMap'); - if (tokenPriceMap) { - return tokenPriceMap; - } else { - let data: any = []; - try { - const response = await this.sendRequest('GET', '/api/prices', {}, {}, WEB_NEXT_URL); - data = response.data || []; // Ensure data is set to an empty array if response.data is undefined - } catch (error) { - console.error('Error fetching prices:', error); - data = []; // Set data to empty array in case of an error - } - - if (pricesMap && pricesMap['FLOW']) { - return pricesMap; - } - data.map((d) => { - const { rateToUSD, contractName, contractAddress } = d; - const key = contractName.toLowerCase() + '' + contractAddress.toLowerCase(); - pricesMap[key] = rateToUSD.toFixed(8); - }); - await storage.setExpiry('pricesMap', pricesMap, 300000); // 5 minutes in milliseconds - return pricesMap; + getTokenPrices = async (storageKey: string, isEvm: boolean = false) => { + const cachedPrices = await storage.getExpiry(storageKey); + if (cachedPrices) { + return cachedPrices; } - }; - getTokenEvmPrices = async () => { - const tokenPriceMap = await storage.getExpiry('evmPrice'); - if (tokenPriceMap) { - return tokenPriceMap; - } else { - let data: any = []; - try { - const response = await this.sendRequest('GET', '/api/prices', {}, {}, WEB_NEXT_URL); - data = response.data || []; // Ensure data is set to an empty array if response.data is undefined - } catch (error) { - console.error('Error fetching prices:', error); - data = []; // Set data to empty array in case of an error - } + const pricesMap: Record = {}; + + try { + const response = await this.sendRequest('GET', '/api/prices', {}, {}, WEB_NEXT_URL); + const data = response?.data || []; - data.map((d) => { - if (d.evmAddress) { - const { rateToUSD, evmAddress } = d; + data.forEach((token) => { + if (isEvm && token.evmAddress) { + // EVM price + const { rateToUSD, evmAddress } = token; const key = evmAddress.toLowerCase(); - pricesMap[key] = rateToUSD.toFixed(5); - } else { - const { rateToUSD, symbol } = d; + pricesMap[key] = Number(rateToUSD).toFixed(8); + } else if (!isEvm && token.contractName && token.contractAddress) { + // Flow chain price + const { rateToUSD, contractName, contractAddress } = token; + const key = `${contractName.toLowerCase()}${contractAddress.toLowerCase()}`; + pricesMap[key] = Number(rateToUSD).toFixed(8); + } else if (isEvm && token.symbol) { + // Handle fallback for EVM tokens + const { rateToUSD, symbol } = token; const key = symbol.toUpperCase(); - pricesMap[key] = rateToUSD.toFixed(5); + pricesMap[key] = Number(rateToUSD).toFixed(8); } }); - await storage.setExpiry('evmPrice', pricesMap, 300000); // 5 minutes in milliseconds - return pricesMap; + } catch (error) { + console.error('Error fetching prices:', error); } + + await storage.setExpiry(storageKey, pricesMap, 300000); + return pricesMap; }; getPricesBySymbol = async (symbol: string, data) => { @@ -1318,13 +1296,20 @@ class OpenApiService { }; getEvmTokenInfo = async (name: string, network = ''): Promise => { - // FIX ME: Get defaultTokenList from firebase remote config if (!network) { network = await userWalletService.getNetwork(); } + const tokens = await this.getEvmListFromGithub(network); - // const coins = await remoteFetch.flowCoins(); - return tokens.find((item) => item.symbol.toLowerCase() === name.toLowerCase()); + + const tokenInfo = tokens.find((item) => item.symbol.toLowerCase() === name.toLowerCase()); + + if (tokenInfo && isValidEthereumAddress(tokenInfo.address)) { + return tokenInfo; + } + + const freshTokens = await this.refreshEvmGitToken(network); + return freshTokens.find((item) => item.symbol.toLowerCase() === name.toLowerCase()); }; getTokenInfoByContract = async (contractName: string): Promise => { @@ -1566,6 +1551,8 @@ class OpenApiService { this.mergeCustomTokens(gitToken, evmCustomToken); storage.setExpiry(`GitTokenList${network}${chainType}`, gitToken, 600000); + + return gitToken; }; refreshCustomEvmGitToken = async (network) => { diff --git a/src/background/service/permission.ts b/src/background/service/permission.ts index 0ba8dace..22cf674f 100644 --- a/src/background/service/permission.ts +++ b/src/background/service/permission.ts @@ -1,7 +1,8 @@ +import { max } from 'lodash'; import LRU from 'lru-cache'; + import { createPersistStore } from 'background/utils'; import { INTERNAL_REQUEST_ORIGIN } from 'consts'; -import { max } from 'lodash'; export interface ConnectedSite { origin: string; @@ -58,10 +59,9 @@ class PermissionService { origin: string, name: string, icon: string, - defaultChain = 545, + defaultChain = 747, isSigned = false ) => { - console.log('origin ', origin); if (!this.lruCache) return; this.lruCache.set(origin, { origin, @@ -71,7 +71,6 @@ class PermissionService { isSigned, isTop: false, }); - console.log('lruCache ', this.lruCache); this.sync(); }; diff --git a/src/background/service/proxy.ts b/src/background/service/proxy.ts index aa20ef3e..4d380a1f 100644 --- a/src/background/service/proxy.ts +++ b/src/background/service/proxy.ts @@ -1,23 +1,13 @@ -import { createPersistStore } from 'background/utils'; -import { - WalletResponse, - BlockchainResponse, - ChildAccount, - DeviceInfoRequest, -} from './networkModel'; -import * as fcl from '@onflow/fcl'; -import * as secp from '@noble/secp256k1'; -import { keyringService, openapiService } from 'background/service'; -import wallet from 'background/controller/wallet'; -import { getApp } from 'firebase/app'; -import { signWithKey, seed2PubKey } from '@/ui/utils/modules/passkey.js'; -import { findAddressWithSeed, findAddressWithPK } from '@/ui/utils/modules/findAddressWithPK'; -import { withPrefix } from '@/ui/utils/address'; -import { getAuth, signInAnonymously } from '@firebase/auth'; -import { storage } from '../webapi'; +import { Core } from '@walletconnect/core'; import SignClient from '@walletconnect/sign-client'; + import { FCLWalletConnectMethod } from '@/ui/utils/type'; -import { Core } from '@walletconnect/core'; +import wallet from 'background/controller/wallet'; +import { keyringService, openapiService } from 'background/service'; + +import { storage } from '../webapi'; + +import { type DeviceInfoRequest } from './networkModel'; class Proxy { proxySign = async (token: string, userId: string) => { @@ -49,7 +39,6 @@ class Proxy { const topicId = await storage.get(`${currentId}Topic`); try { const signwallet = await SignClient.init({ - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: Unreachable code error core: new Core({ projectId: process.env.WC_PROJECTID, @@ -113,7 +102,6 @@ class Proxy { const topicId = await storage.get(`${currentId}Topic`); try { const signwallet = await SignClient.init({ - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: Unreachable code error core: new Core({ projectId: process.env.WC_PROJECTID, diff --git a/src/background/service/staking.ts b/src/background/service/staking.ts index 4a31a8d4..af1e500b 100644 --- a/src/background/service/staking.ts +++ b/src/background/service/staking.ts @@ -1,14 +1,8 @@ -import { createPersistStore, getScripts } from 'background/utils'; -import * as t from '@onflow/types'; import * as fcl from '@onflow/fcl'; -import * as secp from '@noble/secp256k1'; -import { keyringService, openapiService, userWalletService } from 'background/service'; -import wallet from 'background/controller/wallet'; -import { getApp } from 'firebase/app'; -import { getAuth } from '@firebase/auth'; -import { withPrefix } from '@/ui/utils/address'; -import fetchConfig from 'background/utils/remoteConfig'; -import { storage } from '@/background/webapi'; +import * as t from '@onflow/types'; + +import { userWalletService } from 'background/service'; +import { createPersistStore, getScripts } from 'background/utils'; interface StakingStore { nodeList: Record; diff --git a/src/background/service/storage-evaluator.ts b/src/background/service/storage-evaluator.ts index 030167a9..18110b73 100644 --- a/src/background/service/storage-evaluator.ts +++ b/src/background/service/storage-evaluator.ts @@ -9,8 +9,7 @@ interface EvaluateStorageResult { storageInfo: StorageInfo; } export class StorageEvaluator { - private static MINIMUM_STORAGE_BUFFER = 100000; // minimum required storage buffer - private static MINIMUM_FLOW_BALANCE = 0.001; // minimum required FLOW balance + private static MINIMUM_STORAGE_BUFFER = 10000; // minimum required storage buffer (10,000 bytes) private static FIXED_MOVE_FEE = 0.001; private static AVERAGE_TX_FEE = 0.001; private static BYTES_PER_FLOW = 100 * 1024 * 1024; // 100 MB @@ -25,11 +24,12 @@ export class StorageEvaluator { const remainingStorage = storageInfo.capacity - storageInfo.used; const isStorageSufficient = remainingStorage >= StorageEvaluator.MINIMUM_STORAGE_BUFFER; + let noStorageAfterAction = false; if (isStorageSufficient) { // Check if there is enough storage after the action - if (sendAmount) { + if (sendAmount !== undefined) { // This is the amount of flow that will be used by the transaction const flowUsed = sendAmount + diff --git a/src/background/service/userWallet.ts b/src/background/service/userWallet.ts index 834cd8b3..71333ebc 100644 --- a/src/background/service/userWallet.ts +++ b/src/background/service/userWallet.ts @@ -1,7 +1,7 @@ -import { getAuth, signInAnonymously } from '@firebase/auth'; import * as secp from '@noble/secp256k1'; import * as fcl from '@onflow/fcl'; import { getApp } from 'firebase/app'; +import { getAuth, signInAnonymously } from 'firebase/auth'; import { getHashAlgo, getSignAlgo } from '@/shared/utils/algo'; import { withPrefix } from '@/ui/utils/address'; diff --git a/src/background/utils/index.ts b/src/background/utils/index.ts index 82a00f4b..85f6ac36 100644 --- a/src/background/utils/index.ts +++ b/src/background/utils/index.ts @@ -1,13 +1,15 @@ import * as ethUtil from 'ethereumjs-util'; -export { default as createPersistStore } from './persisitStore'; -export { default as createSessionStore } from './sessionStore'; -import { version } from '@/../package.json'; +import packageJson from '@/../package.json'; import { storage } from '@/background/webapi'; +const { version } = packageJson; import { mixpanelTrack } from '../service'; import pageStateCache from '../service/pageStateCache'; +export { default as createPersistStore } from './persisitStore'; +export { default as createSessionStore } from './sessionStore'; + // {a:{b: string}} => {1: 'a.b'} // later same [source] value will override [result] key generated before const retrieveValuePath = (obj) => { diff --git a/src/background/utils/remoteConfig.ts b/src/background/utils/remoteConfig.ts index c888a17d..117a0560 100644 --- a/src/background/utils/remoteConfig.ts +++ b/src/background/utils/remoteConfig.ts @@ -1,23 +1,19 @@ -import defaultTokenList from './defaultTokenList.json'; -import defaultConfig from './defaultConfig.json'; -import { TokenModel, NFTModel } from '../service/networkModel'; import { storage } from '@/background/webapi'; -import configJson from './firebase.config.json'; -import testnetNftList from './defaultNftList.testnet.json'; -import mainnetNftList from './defaultNftList.mainnet.json'; -import openapi from '../service/openapi'; + import { userWalletService } from '../service'; -interface RemoteConfigResponse { - flowCoins: TokenModel[]; - nftCollection: NFTModel[]; -} +import openapi from '../service/openapi'; + +import defaultConfig from './defaultConfig.json'; +import mainnetNftList from './defaultNftList.mainnet.json'; +import testnetNftList from './defaultNftList.testnet.json'; +import defaultTokenList from './defaultTokenList.json'; interface CacheState { result: any; expireTime: number; } -const baseURL = configJson.functions[process.env.NODE_ENV!]; +const BASE_FUNCTIONS_URL = process.env.FB_FUNCTIONS; class fetchRemoteConfig { coinState: CacheState = { result: {}, expireTime: 0 }; @@ -40,10 +36,8 @@ class fetchRemoteConfig { const exp = 1000 * 60 * 60 * 1 + now.getTime(); if (expire < now.getTime()) { try { - const result = await openapi.sendRequest('GET', '/fetchFTList', {}, {}, baseURL); - // fetch(`${baseURL}/fetchFTList`); - // const result = await coins.json(); - console.log(result, 'result coins'); + const result = await openapi.sendRequest('GET', '/fetchFTList', {}, {}, BASE_FUNCTIONS_URL); + this.coinState.result = result; this.coinState.expireTime = exp; return result; @@ -62,7 +56,7 @@ class fetchRemoteConfig { const now = new Date(); const exp = 1000 * 60 * 60 * 1 + now.getTime(); let defaultNftList = testnetNftList; - if (network == 'mainnet') { + if (network === 'mainnet') { defaultNftList = mainnetNftList; } if (expire < now.getTime()) { @@ -89,7 +83,7 @@ class fetchRemoteConfig { const now = new Date(); const exp = 1000 * 60 * 60 * 1 + now.getTime(); let defaultNftList = testnetNftList; - if (network == 'mainnet') { + if (network === 'mainnet') { defaultNftList = mainnetNftList; } if (expire < now.getTime()) { @@ -115,7 +109,7 @@ class fetchRemoteConfig { const exp = 1000 * 60 * 60 * 1 + now.getTime(); if (expire < now.getTime()) { try { - const result = await openapi.sendRequest('GET', '/config', {}, {}, baseURL); + const result = await openapi.sendRequest('GET', '/config', {}, {}, BASE_FUNCTIONS_URL); // fetch(`${baseURL}/config`); // const result = await config.json(); this.configState.result = result; @@ -135,36 +129,4 @@ class fetchRemoteConfig { } } -const fetchConfig = async () // remoteConfig -: Promise => { - // if (process.env.NODE_ENV === 'production') { - // remoteConfig.settings.minimumFetchIntervalMillis = 1000 * 60 * 10; - // } else { - // remoteConfig.settings.minimumFetchIntervalMillis = 1000 * 60 * 1; - // } - - // remoteConfig.defaultConfig = { - // 'nft_collections': defaultNftList, - // 'flow_coins': defaultTokenList - // } - - // await fetchAndActivate(remoteConfig); - // const nftCollection = JSON.parse(getValue(remoteConfig, 'nft_collections').asString()); - - // const flowCoins = JSON.parse(getValue(remoteConfig, 'flow_coins').asString()); - - // return {nftCollection, flowCoins}; - const network = await userWalletService.getNetwork(); - let defaultNftList = testnetNftList; - if (network == 'mainnet') { - defaultNftList = mainnetNftList; - } - return { flowCoins: defaultTokenList, nftCollection: defaultNftList }; -}; - -// export default { -// flowCoins: fetchCoin, -// nftCollection: fetchNFT -// }; - export default new fetchRemoteConfig(); diff --git a/src/components/iconfont/IconAClose.js b/src/components/iconfont/IconAClose.js index f8417798..6598026f 100644 --- a/src/components/iconfont/IconAClose.js +++ b/src/components/iconfont/IconAClose.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconAClose = ({ size, color, style: _style, ...rest }) => { +const IconAClose = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconAClose = ({ size, color, style: _style, ...rest }) => { ); }; -IconAClose.defaultProps = { - size: 18, -}; - export default IconAClose; diff --git a/src/components/iconfont/IconAGroup762.js b/src/components/iconfont/IconAGroup762.js index ad5b61f9..4dbea04e 100644 --- a/src/components/iconfont/IconAGroup762.js +++ b/src/components/iconfont/IconAGroup762.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconAGroup762 = ({ size, color, style: _style, ...rest }) => { +const IconAGroup762 = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -24,8 +24,4 @@ const IconAGroup762 = ({ size, color, style: _style, ...rest }) => { ); }; -IconAGroup762.defaultProps = { - size: 18, -}; - export default IconAGroup762; diff --git a/src/components/iconfont/IconAHamburgermenu.js b/src/components/iconfont/IconAHamburgermenu.js index f61cc98e..b9952991 100644 --- a/src/components/iconfont/IconAHamburgermenu.js +++ b/src/components/iconfont/IconAHamburgermenu.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconAHamburgermenu = ({ size, color, style: _style, ...rest }) => { +const IconAHamburgermenu = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconAHamburgermenu = ({ size, color, style: _style, ...rest }) => { ); }; -IconAHamburgermenu.defaultProps = { - size: 18, -}; - export default IconAHamburgermenu; diff --git a/src/components/iconfont/IconAVector11Stroke.js b/src/components/iconfont/IconAVector11Stroke.js index 096a5e03..c48312c7 100644 --- a/src/components/iconfont/IconAVector11Stroke.js +++ b/src/components/iconfont/IconAVector11Stroke.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconAVector11Stroke = ({ size, color, style: _style, ...rest }) => { +const IconAVector11Stroke = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconAVector11Stroke = ({ size, color, style: _style, ...rest }) => { ); }; -IconAVector11Stroke.defaultProps = { - size: 18, -}; - export default IconAVector11Stroke; diff --git a/src/components/iconfont/IconAbout.js b/src/components/iconfont/IconAbout.js index 152b33d9..d3226c2a 100644 --- a/src/components/iconfont/IconAbout.js +++ b/src/components/iconfont/IconAbout.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconAbout = ({ size, color, style: _style, ...rest }) => { +const IconAbout = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -24,8 +24,4 @@ const IconAbout = ({ size, color, style: _style, ...rest }) => { ); }; -IconAbout.defaultProps = { - size: 18, -}; - export default IconAbout; diff --git a/src/components/iconfont/IconAccount.js b/src/components/iconfont/IconAccount.js index 5c2eaafe..1df3d390 100644 --- a/src/components/iconfont/IconAccount.js +++ b/src/components/iconfont/IconAccount.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconAccount = ({ size, color, style: _style, ...rest }) => { +const IconAccount = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconAccount = ({ size, color, style: _style, ...rest }) => { ); }; -IconAccount.defaultProps = { - size: 18, -}; - export default IconAccount; diff --git a/src/components/iconfont/IconAddressbook.d.ts b/src/components/iconfont/IconAddressbook.d.ts index 47264394..13d8314f 100644 --- a/src/components/iconfont/IconAddressbook.d.ts +++ b/src/components/iconfont/IconAddressbook.d.ts @@ -7,6 +7,6 @@ interface Props extends Omit, 'color'> { color?: string | string[]; } -declare const IconAddressBook: FunctionComponent; +declare const IconAddressbook: FunctionComponent; -export default IconAddressBook; +export default IconAddressbook; diff --git a/src/components/iconfont/IconAddressbook.js b/src/components/iconfont/IconAddressbook.js index 0576f3cb..96927d0e 100644 --- a/src/components/iconfont/IconAddressbook.js +++ b/src/components/iconfont/IconAddressbook.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconAddressBook = ({ size, color, style: _style, ...rest }) => { +const IconAddressbook = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconAddressBook = ({ size, color, style: _style, ...rest }) => { ); }; -IconAddressBook.defaultProps = { - size: 18, -}; - -export default IconAddressBook; +export default IconAddressbook; diff --git a/src/components/iconfont/IconAppStore.js b/src/components/iconfont/IconAppStore.js index c825588e..8ad32a92 100644 --- a/src/components/iconfont/IconAppStore.js +++ b/src/components/iconfont/IconAppStore.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconAppStore = ({ size, color, style: _style, ...rest }) => { +const IconAppStore = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconAppStore = ({ size, color, style: _style, ...rest }) => { ); }; -IconAppStore.defaultProps = { - size: 18, -}; - export default IconAppStore; diff --git a/src/components/iconfont/IconBack.js b/src/components/iconfont/IconBack.js index 93ccff4c..88d9e470 100644 --- a/src/components/iconfont/IconBack.js +++ b/src/components/iconfont/IconBack.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconBack = ({ size, color, style: _style, ...rest }) => { +const IconBack = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconBack = ({ size, color, style: _style, ...rest }) => { ); }; -IconBack.defaultProps = { - size: 18, -}; - export default IconBack; diff --git a/src/components/iconfont/IconBackButton.js b/src/components/iconfont/IconBackButton.js index eddff8f0..73f0d899 100644 --- a/src/components/iconfont/IconBackButton.js +++ b/src/components/iconfont/IconBackButton.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconBackButton = ({ size, color, style: _style, ...rest }) => { +const IconBackButton = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconBackButton = ({ size, color, style: _style, ...rest }) => { ); }; -IconBackButton.defaultProps = { - size: 18, -}; - export default IconBackButton; diff --git a/src/components/iconfont/IconBackup.js b/src/components/iconfont/IconBackup.js index 62465358..b52dfd98 100644 --- a/src/components/iconfont/IconBackup.js +++ b/src/components/iconfont/IconBackup.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconBackup = ({ size, color, style: _style, ...rest }) => { +const IconBackup = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconBackup = ({ size, color, style: _style, ...rest }) => { ); }; -IconBackup.defaultProps = { - size: 18, -}; - export default IconBackup; diff --git a/src/components/iconfont/IconBinance.js b/src/components/iconfont/IconBinance.js index 999c6cba..0cb41765 100644 --- a/src/components/iconfont/IconBinance.js +++ b/src/components/iconfont/IconBinance.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconBinance = ({ size, color, style: _style, ...rest }) => { +const IconBinance = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconBinance = ({ size, color, style: _style, ...rest }) => { ); }; -IconBinance.defaultProps = { - size: 18, -}; - export default IconBinance; diff --git a/src/components/iconfont/IconCheckmark.js b/src/components/iconfont/IconCheckmark.js index 0df05cfc..f2ff8e59 100644 --- a/src/components/iconfont/IconCheckmark.js +++ b/src/components/iconfont/IconCheckmark.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconCheckmark = ({ size, color, style: _style, ...rest }) => { +const IconCheckmark = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconCheckmark = ({ size, color, style: _style, ...rest }) => { ); }; -IconCheckmark.defaultProps = { - size: 18, -}; - export default IconCheckmark; diff --git a/src/components/iconfont/IconChevronRight.js b/src/components/iconfont/IconChevronRight.js index 3f7b5436..973dac57 100644 --- a/src/components/iconfont/IconChevronRight.js +++ b/src/components/iconfont/IconChevronRight.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconChevronRight = ({ size, color, style: _style, ...rest }) => { +const IconChevronRight = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconChevronRight = ({ size, color, style: _style, ...rest }) => { ); }; -IconChevronRight.defaultProps = { - size: 18, -}; - export default IconChevronRight; diff --git a/src/components/iconfont/IconClose.js b/src/components/iconfont/IconClose.js index cb5d33a6..0493c2d9 100644 --- a/src/components/iconfont/IconClose.js +++ b/src/components/iconfont/IconClose.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconClose = ({ size, color, style: _style, ...rest }) => { +const IconClose = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconClose = ({ size, color, style: _style, ...rest }) => { ); }; -IconClose.defaultProps = { - size: 18, -}; - export default IconClose; diff --git a/src/components/iconfont/IconCoinbase.js b/src/components/iconfont/IconCoinbase.js index 87d94471..91f5110b 100644 --- a/src/components/iconfont/IconCoinbase.js +++ b/src/components/iconfont/IconCoinbase.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconCoinbase = ({ size, color, style: _style, ...rest }) => { +const IconCoinbase = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -24,8 +24,4 @@ const IconCoinbase = ({ size, color, style: _style, ...rest }) => { ); }; -IconCoinbase.defaultProps = { - size: 18, -}; - export default IconCoinbase; diff --git a/src/components/iconfont/IconCopy.js b/src/components/iconfont/IconCopy.js index 6531a5fb..03111486 100644 --- a/src/components/iconfont/IconCopy.js +++ b/src/components/iconfont/IconCopy.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconCopy = ({ size, color, style: _style, ...rest }) => { +const IconCopy = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -25,8 +25,4 @@ const IconCopy = ({ size, color, style: _style, ...rest }) => { ); }; -IconCopy.defaultProps = { - size: 18, -}; - export default IconCopy; diff --git a/src/components/iconfont/IconCreate.js b/src/components/iconfont/IconCreate.js index 6bb709d6..ef0688ce 100644 --- a/src/components/iconfont/IconCreate.js +++ b/src/components/iconfont/IconCreate.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconCreate = ({ size, color, style: _style, ...rest }) => { +const IconCreate = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconCreate = ({ size, color, style: _style, ...rest }) => { ); }; -IconCreate.defaultProps = { - size: 18, -}; - export default IconCreate; diff --git a/src/components/iconfont/IconCurrency.js b/src/components/iconfont/IconCurrency.js index 58de08fe..68cce554 100644 --- a/src/components/iconfont/IconCurrency.js +++ b/src/components/iconfont/IconCurrency.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconCurrency = ({ size, color, style: _style, ...rest }) => { +const IconCurrency = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconCurrency = ({ size, color, style: _style, ...rest }) => { ); }; -IconCurrency.defaultProps = { - size: 18, -}; - export default IconCurrency; diff --git a/src/components/iconfont/IconDeveloper.js b/src/components/iconfont/IconDeveloper.js index ebc5344c..eaba96b1 100644 --- a/src/components/iconfont/IconDeveloper.js +++ b/src/components/iconfont/IconDeveloper.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconDeveloper = ({ size, color, style: _style, ...rest }) => { +const IconDeveloper = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconDeveloper = ({ size, color, style: _style, ...rest }) => { ); }; -IconDeveloper.defaultProps = { - size: 18, -}; - export default IconDeveloper; diff --git a/src/components/iconfont/IconDevices.js b/src/components/iconfont/IconDevices.js index 87a925cd..37cbb89f 100644 --- a/src/components/iconfont/IconDevices.js +++ b/src/components/iconfont/IconDevices.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconDevices = ({ size, color, style: _style, ...rest }) => { +const IconDevices = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconDevices = ({ size, color, style: _style, ...rest }) => { ); }; -IconDevices.defaultProps = { - size: 18, -}; - export default IconDevices; diff --git a/src/components/iconfont/IconDollar.js b/src/components/iconfont/IconDollar.js index bd37c7a5..92c13000 100644 --- a/src/components/iconfont/IconDollar.js +++ b/src/components/iconfont/IconDollar.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconDollar = ({ size, color, style: _style, ...rest }) => { +const IconDollar = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconDollar = ({ size, color, style: _style, ...rest }) => { ); }; -IconDollar.defaultProps = { - size: 18, -}; - export default IconDollar; diff --git a/src/components/iconfont/IconDropdown.js b/src/components/iconfont/IconDropdown.js index 7fe04b6f..14fab4b4 100644 --- a/src/components/iconfont/IconDropdown.js +++ b/src/components/iconfont/IconDropdown.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconDropdown = ({ size, color, style: _style, ...rest }) => { +const IconDropdown = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -17,8 +17,4 @@ const IconDropdown = ({ size, color, style: _style, ...rest }) => { ); }; -IconDropdown.defaultProps = { - size: 18, -}; - export default IconDropdown; diff --git a/src/components/iconfont/IconExec.js b/src/components/iconfont/IconExec.js index 8afcf34c..975eb808 100644 --- a/src/components/iconfont/IconExec.js +++ b/src/components/iconfont/IconExec.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconExec = ({ size, color, style: _style, ...rest }) => { +const IconExec = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconExec = ({ size, color, style: _style, ...rest }) => { ); }; -IconExec.defaultProps = { - size: 18, -}; - export default IconExec; diff --git a/src/components/iconfont/IconEye.js b/src/components/iconfont/IconEye.js index 2ccd6d57..714be98b 100644 --- a/src/components/iconfont/IconEye.js +++ b/src/components/iconfont/IconEye.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconEye = ({ size, color, style: _style, ...rest }) => { +const IconEye = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -24,8 +24,4 @@ const IconEye = ({ size, color, style: _style, ...rest }) => { ); }; -IconEye.defaultProps = { - size: 18, -}; - export default IconEye; diff --git a/src/components/iconfont/IconFlow.js b/src/components/iconfont/IconFlow.js index 78c97680..a03075c0 100644 --- a/src/components/iconfont/IconFlow.js +++ b/src/components/iconfont/IconFlow.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconFlow = ({ size, color, style: _style, ...rest }) => { +const IconFlow = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -28,8 +28,4 @@ const IconFlow = ({ size, color, style: _style, ...rest }) => { ); }; -IconFlow.defaultProps = { - size: 18, -}; - export default IconFlow; diff --git a/src/components/iconfont/IconFlowns.js b/src/components/iconfont/IconFlowns.js index 822144cc..c26eda8e 100644 --- a/src/components/iconfont/IconFlowns.js +++ b/src/components/iconfont/IconFlowns.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconFlowns = ({ size, color, style: _style, ...rest }) => { +const IconFlowns = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -52,8 +52,4 @@ const IconFlowns = ({ size, color, style: _style, ...rest }) => { ); }; -IconFlowns.defaultProps = { - size: 18, -}; - export default IconFlowns; diff --git a/src/components/iconfont/IconFusd.js b/src/components/iconfont/IconFusd.js index ffc39ff2..91252e62 100644 --- a/src/components/iconfont/IconFusd.js +++ b/src/components/iconfont/IconFusd.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconFusd = ({ size, color, style: _style, ...rest }) => { +const IconFusd = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -28,8 +28,4 @@ const IconFusd = ({ size, color, style: _style, ...rest }) => { ); }; -IconFusd.defaultProps = { - size: 18, -}; - export default IconFusd; diff --git a/src/components/iconfont/IconGoogleDrive.js b/src/components/iconfont/IconGoogleDrive.js index 811a104e..6f96c3db 100644 --- a/src/components/iconfont/IconGoogleDrive.js +++ b/src/components/iconfont/IconGoogleDrive.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconGoogleDrive = ({ size, color, style: _style, ...rest }) => { +const IconGoogleDrive = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -28,8 +28,4 @@ const IconGoogleDrive = ({ size, color, style: _style, ...rest }) => { ); }; -IconGoogleDrive.defaultProps = { - size: 18, -}; - export default IconGoogleDrive; diff --git a/src/components/iconfont/IconHuobi.js b/src/components/iconfont/IconHuobi.js index adde9d62..e6a95b36 100644 --- a/src/components/iconfont/IconHuobi.js +++ b/src/components/iconfont/IconHuobi.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconHuobi = ({ size, color, style: _style, ...rest }) => { +const IconHuobi = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -24,8 +24,4 @@ const IconHuobi = ({ size, color, style: _style, ...rest }) => { ); }; -IconHuobi.defaultProps = { - size: 18, -}; - export default IconHuobi; diff --git a/src/components/iconfont/IconImport.js b/src/components/iconfont/IconImport.js index bf624163..22cea085 100644 --- a/src/components/iconfont/IconImport.js +++ b/src/components/iconfont/IconImport.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconImport = ({ size, color, style: _style, ...rest }) => { +const IconImport = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -24,8 +24,4 @@ const IconImport = ({ size, color, style: _style, ...rest }) => { ); }; -IconImport.defaultProps = { - size: 18, -}; - export default IconImport; diff --git a/src/components/iconfont/IconInbox.js b/src/components/iconfont/IconInbox.js index 90e88d24..dc2d9656 100644 --- a/src/components/iconfont/IconInbox.js +++ b/src/components/iconfont/IconInbox.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconInbox = ({ size, color, style: _style, ...rest }) => { +const IconInbox = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconInbox = ({ size, color, style: _style, ...rest }) => { ); }; -IconInbox.defaultProps = { - size: 18, -}; - export default IconInbox; diff --git a/src/components/iconfont/IconKraken.js b/src/components/iconfont/IconKraken.js index 04dca7b1..bf68d044 100644 --- a/src/components/iconfont/IconKraken.js +++ b/src/components/iconfont/IconKraken.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconKraken = ({ size, color, style: _style, ...rest }) => { +const IconKraken = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconKraken = ({ size, color, style: _style, ...rest }) => { ); }; -IconKraken.defaultProps = { - size: 18, -}; - export default IconKraken; diff --git a/src/components/iconfont/IconKucoin.js b/src/components/iconfont/IconKucoin.js index 68be3de6..eafd6e3a 100644 --- a/src/components/iconfont/IconKucoin.js +++ b/src/components/iconfont/IconKucoin.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconKucoin = ({ size, color, style: _style, ...rest }) => { +const IconKucoin = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -24,8 +24,4 @@ const IconKucoin = ({ size, color, style: _style, ...rest }) => { ); }; -IconKucoin.defaultProps = { - size: 18, -}; - export default IconKucoin; diff --git a/src/components/iconfont/IconLock.js b/src/components/iconfont/IconLock.js index cd8cd716..c92334e0 100644 --- a/src/components/iconfont/IconLock.js +++ b/src/components/iconfont/IconLock.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconLock = ({ size, color, style: _style, ...rest }) => { +const IconLock = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconLock = ({ size, color, style: _style, ...rest }) => { ); }; -IconLock.defaultProps = { - size: 18, -}; - export default IconLock; diff --git a/src/components/iconfont/IconLogo.js b/src/components/iconfont/IconLogo.js index 8713afad..f59323cb 100644 --- a/src/components/iconfont/IconLogo.js +++ b/src/components/iconfont/IconLogo.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconLogo = ({ size, color, style: _style, ...rest }) => { +const IconLogo = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -32,8 +32,4 @@ const IconLogo = ({ size, color, style: _style, ...rest }) => { ); }; -IconLogo.defaultProps = { - size: 18, -}; - export default IconLogo; diff --git a/src/components/iconfont/IconMax.js b/src/components/iconfont/IconMax.js index fcb40d36..daf53dba 100644 --- a/src/components/iconfont/IconMax.js +++ b/src/components/iconfont/IconMax.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconMax = ({ size, color, style: _style, ...rest }) => { +const IconMax = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -24,8 +24,4 @@ const IconMax = ({ size, color, style: _style, ...rest }) => { ); }; -IconMax.defaultProps = { - size: 18, -}; - export default IconMax; diff --git a/src/components/iconfont/IconNfts.js b/src/components/iconfont/IconNfts.js index 4d8acf45..8ce2ff0b 100644 --- a/src/components/iconfont/IconNfts.js +++ b/src/components/iconfont/IconNfts.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconNfts = ({ size, color, style: _style, ...rest }) => { +const IconNfts = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconNfts = ({ size, color, style: _style, ...rest }) => { ); }; -IconNfts.defaultProps = { - size: 18, -}; - export default IconNfts; diff --git a/src/components/iconfont/IconNotification.js b/src/components/iconfont/IconNotification.js index f04c17a3..d577f2da 100644 --- a/src/components/iconfont/IconNotification.js +++ b/src/components/iconfont/IconNotification.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconNotification = ({ size, color, style: _style, ...rest }) => { +const IconNotification = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconNotification = ({ size, color, style: _style, ...rest }) => { ); }; -IconNotification.defaultProps = { - size: 18, -}; - export default IconNotification; diff --git a/src/components/iconfont/IconPlayStore.js b/src/components/iconfont/IconPlayStore.js index dc731ff6..63965b68 100644 --- a/src/components/iconfont/IconPlayStore.js +++ b/src/components/iconfont/IconPlayStore.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconPlayStore = ({ size, color, style: _style, ...rest }) => { +const IconPlayStore = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconPlayStore = ({ size, color, style: _style, ...rest }) => { ); }; -IconPlayStore.defaultProps = { - size: 18, -}; - export default IconPlayStore; diff --git a/src/components/iconfont/IconPlus.js b/src/components/iconfont/IconPlus.js index 70469313..a91ca063 100644 --- a/src/components/iconfont/IconPlus.js +++ b/src/components/iconfont/IconPlus.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconPlus = ({ size, color, style: _style, ...rest }) => { +const IconPlus = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconPlus = ({ size, color, style: _style, ...rest }) => { ); }; -IconPlus.defaultProps = { - size: 18, -}; - export default IconPlus; diff --git a/src/components/iconfont/IconSearch.js b/src/components/iconfont/IconSearch.js index 6668ef5d..52b3b311 100644 --- a/src/components/iconfont/IconSearch.js +++ b/src/components/iconfont/IconSearch.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconSearch = ({ size, color, style: _style, ...rest }) => { +const IconSearch = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconSearch = ({ size, color, style: _style, ...rest }) => { ); }; -IconSearch.defaultProps = { - size: 18, -}; - export default IconSearch; diff --git a/src/components/iconfont/IconSecurity.js b/src/components/iconfont/IconSecurity.js index 15ef0a2b..49f62c76 100644 --- a/src/components/iconfont/IconSecurity.js +++ b/src/components/iconfont/IconSecurity.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconSecurity = ({ size, color, style: _style, ...rest }) => { +const IconSecurity = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -24,8 +24,4 @@ const IconSecurity = ({ size, color, style: _style, ...rest }) => { ); }; -IconSecurity.defaultProps = { - size: 18, -}; - export default IconSecurity; diff --git a/src/components/iconfont/IconSetting.js b/src/components/iconfont/IconSetting.js index 52bf6207..1e5da15f 100644 --- a/src/components/iconfont/IconSetting.js +++ b/src/components/iconfont/IconSetting.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconSetting = ({ size, color, style: _style, ...rest }) => { +const IconSetting = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconSetting = ({ size, color, style: _style, ...rest }) => { ); }; -IconSetting.defaultProps = { - size: 18, -}; - export default IconSetting; diff --git a/src/components/iconfont/IconSubtract.js b/src/components/iconfont/IconSubtract.js index 53e66074..1b7d9f29 100644 --- a/src/components/iconfont/IconSubtract.js +++ b/src/components/iconfont/IconSubtract.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconSubtract = ({ size, color, style: _style, ...rest }) => { +const IconSubtract = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconSubtract = ({ size, color, style: _style, ...rest }) => { ); }; -IconSubtract.defaultProps = { - size: 18, -}; - export default IconSubtract; diff --git a/src/components/iconfont/IconSwitch.js b/src/components/iconfont/IconSwitch.js index 2c7c1d4d..c4cc2646 100644 --- a/src/components/iconfont/IconSwitch.js +++ b/src/components/iconfont/IconSwitch.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconSwitch = ({ size, color, style: _style, ...rest }) => { +const IconSwitch = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconSwitch = ({ size, color, style: _style, ...rest }) => { ); }; -IconSwitch.defaultProps = { - size: 18, -}; - export default IconSwitch; diff --git a/src/components/iconfont/IconTheme.js b/src/components/iconfont/IconTheme.js index a8de08ef..e03d8b25 100644 --- a/src/components/iconfont/IconTheme.js +++ b/src/components/iconfont/IconTheme.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconTheme = ({ size, color, style: _style, ...rest }) => { +const IconTheme = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconTheme = ({ size, color, style: _style, ...rest }) => { ); }; -IconTheme.defaultProps = { - size: 18, -}; - export default IconTheme; diff --git a/src/components/iconfont/IconUserSwitch.js b/src/components/iconfont/IconUserSwitch.js index 63ddc41a..54665a09 100644 --- a/src/components/iconfont/IconUserSwitch.js +++ b/src/components/iconfont/IconUserSwitch.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconUserSwitch = ({ size, color, style: _style, ...rest }) => { +const IconUserSwitch = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -24,8 +24,4 @@ const IconUserSwitch = ({ size, color, style: _style, ...rest }) => { ); }; -IconUserSwitch.defaultProps = { - size: 18, -}; - export default IconUserSwitch; diff --git a/src/components/iconfont/IconVector.js b/src/components/iconfont/IconVector.js index a5b3099c..391d97f0 100644 --- a/src/components/iconfont/IconVector.js +++ b/src/components/iconfont/IconVector.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconVector = ({ size, color, style: _style, ...rest }) => { +const IconVector = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconVector = ({ size, color, style: _style, ...rest }) => { ); }; -IconVector.defaultProps = { - size: 18, -}; - export default IconVector; diff --git a/src/components/iconfont/IconVector2.js b/src/components/iconfont/IconVector2.js index 8a8bd407..9e445211 100644 --- a/src/components/iconfont/IconVector2.js +++ b/src/components/iconfont/IconVector2.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconVector2 = ({ size, color, style: _style, ...rest }) => { +const IconVector2 = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconVector2 = ({ size, color, style: _style, ...rest }) => { ); }; -IconVector2.defaultProps = { - size: 18, -}; - export default IconVector2; diff --git a/src/components/iconfont/IconWallet.js b/src/components/iconfont/IconWallet.js index 0ea9dbf7..b7364401 100644 --- a/src/components/iconfont/IconWallet.js +++ b/src/components/iconfont/IconWallet.js @@ -7,7 +7,7 @@ const DEFAULT_STYLE = { display: 'block', }; -const IconWallet = ({ size, color, style: _style, ...rest }) => { +const IconWallet = ({ size = 18, color, style: _style, ...rest }) => { const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE; return ( @@ -20,8 +20,4 @@ const IconWallet = ({ size, color, style: _style, ...rest }) => { ); }; -IconWallet.defaultProps = { - size: 18, -}; - export default IconWallet; diff --git a/src/components/iconfont/index.d.ts b/src/components/iconfont/index.d.ts index b5dacae8..b02360f0 100644 --- a/src/components/iconfont/index.d.ts +++ b/src/components/iconfont/index.d.ts @@ -34,7 +34,7 @@ export { default as IconAbout } from './IconAbout'; export { default as IconCreate } from './IconCreate'; export { default as IconLock } from './IconLock'; export { default as IconUserSwitch } from './IconUserSwitch'; -export { default as IconAddressBook } from './IconAddressBook'; +export { default as IconAddressbook } from './IconAddressbook'; export { default as IconAVector11Stroke } from './IconAVector11Stroke'; export { default as IconMax } from './IconMax'; export { default as IconVector2 } from './IconVector2'; diff --git a/src/components/iconfont/index.js b/src/components/iconfont/index.js index ee3eab15..4036a8a7 100644 --- a/src/components/iconfont/index.js +++ b/src/components/iconfont/index.js @@ -34,7 +34,7 @@ import IconAbout from './IconAbout'; import IconCreate from './IconCreate'; import IconLock from './IconLock'; import IconUserSwitch from './IconUserSwitch'; -import IconAddressBook from './IconAddressBook'; +import IconAddressbook from './IconAddressbook'; import IconAVector11Stroke from './IconAVector11Stroke'; import IconMax from './IconMax'; import IconVector2 from './IconVector2'; @@ -85,7 +85,7 @@ export { default as IconAbout } from './IconAbout'; export { default as IconCreate } from './IconCreate'; export { default as IconLock } from './IconLock'; export { default as IconUserSwitch } from './IconUserSwitch'; -export { default as IconAddressBook } from './IconAddressBook'; +export { default as IconAddressbook } from './IconAddressbook'; export { default as IconAVector11Stroke } from './IconAVector11Stroke'; export { default as IconMax } from './IconMax'; export { default as IconVector2 } from './IconVector2'; @@ -173,7 +173,7 @@ const IconFont = ({ name, ...rest }) => { case 'User-switch': return ; case 'Address-book': - return ; + return ; case 'a-Vector11Stroke': return ; case 'max': diff --git a/src/constant/index.ts b/src/constant/index.ts index ff7bf106..08769896 100644 --- a/src/constant/index.ts +++ b/src/constant/index.ts @@ -1,6 +1,7 @@ -import IconEN from 'ui/assets/langs/en.svg'; import { CHAINS, CHAINS_ENUM, Chain } from '@debank/common'; +import IconEN from 'ui/assets/langs/en.svg'; + export { CHAINS, CHAINS_ENUM }; export const KEYRING_TYPE = { @@ -73,6 +74,7 @@ export const SAFE_RPC_METHODS = [ 'eth_signTypedData_v4', 'wallet_requestPermissions', 'wallet_getPermissions', + 'wallet_revokePermissions', 'wallet_switchEthereumChain', 'wallet_watchAsset', 'net_version', @@ -93,7 +95,7 @@ if (IS_CHROME) { } } -export const IS_AFTER_CHROME91 = IS_CHROME ? chromeVersion && chromeVersion >= 91 : false; +export const IS_AFTER_CHROME94 = IS_CHROME ? chromeVersion && chromeVersion >= 94 : false; export const GAS_LEVEL_TEXT = { slow: 'Standard', diff --git a/src/content-script/index.ts b/src/content-script/index.ts index c4c6248c..b5a5793d 100644 --- a/src/content-script/index.ts +++ b/src/content-script/index.ts @@ -1,7 +1,8 @@ import { nanoid } from 'nanoid'; -import { Message } from 'utils'; import { v4 as uuid } from 'uuid'; +import { Message } from 'utils'; + const channelName = nanoid(); const injectProviderScript = async (isDefaultWallet) => { @@ -10,8 +11,6 @@ const injectProviderScript = async (isDefaultWallet) => { await localStorage.setItem('frw:isDefaultWallet', isDefaultWallet); await localStorage.setItem('frw:uuid', uuid()); - console.log(localStorage.getItem('frw:channelName')); - const container = document.head || document.documentElement; const scriptElement = document.createElement('script'); scriptElement.id = 'injectedScript'; @@ -79,37 +78,41 @@ window.addEventListener('message', function (event) { // chrome.runtime.sendMessage({type: 'FLOW::TX', ...event.detail}) // }) -const extMessageHandler = (msg, sender) => { +const extMessageHandler = (msg, _sender) => { if (msg.type === 'FCL:VIEW:READY') { - window && window.postMessage(JSON.parse(JSON.stringify(msg || {})), '*'); + if (window) { + window.postMessage(JSON.parse(JSON.stringify(msg || {})), '*'); + } } if (msg.f_type && msg.f_type === 'PollingResponse') { - window && - window.postMessage( - JSON.parse(JSON.stringify({ ...msg, type: 'FCL:VIEW:RESPONSE' } || {})), - '*' - ); + if (window) { + window.postMessage(JSON.parse(JSON.stringify({ ...msg, type: 'FCL:VIEW:RESPONSE' })), '*'); + } } if (msg.data?.f_type && msg.data?.f_type === 'PreAuthzResponse') { - window && - window.postMessage( - JSON.parse(JSON.stringify({ ...msg, type: 'FCL:VIEW:RESPONSE' } || {})), - '*' - ); + if (window) { + window.postMessage(JSON.parse(JSON.stringify({ ...msg, type: 'FCL:VIEW:RESPONSE' })), '*'); + } } if (msg.type === 'FCL:VIEW:CLOSE') { - window && window.postMessage(JSON.parse(JSON.stringify(msg || {})), '*'); + if (window) { + window.postMessage(JSON.parse(JSON.stringify(msg || {})), '*'); + } } if (msg.type === 'FLOW::TX') { - window && window.postMessage(JSON.parse(JSON.stringify(msg || {})), '*'); + if (window) { + window.postMessage(JSON.parse(JSON.stringify(msg || {})), '*'); + } } if (msg.type === 'LILICO:NETWORK') { - window && window.postMessage(JSON.parse(JSON.stringify(msg || {})), '*'); + if (window) { + window.postMessage(JSON.parse(JSON.stringify(msg || {})), '*'); + } } return true; diff --git a/src/content-script/pageProvider/eth/utils/index.ts b/src/content-script/pageProvider/eth/utils/index.ts index 6c71ec13..3be53b1f 100644 --- a/src/content-script/pageProvider/eth/utils/index.ts +++ b/src/content-script/pageProvider/eth/utils/index.ts @@ -1,6 +1,5 @@ import { keyBy } from 'lodash'; import browser from 'webextension-polyfill'; -import { ledgerUSBVendorId } from '@ledgerhq/devices'; import BroadcastChannelMessage from './message/broadcastChannelMessage'; import PortMessage from './message/portMessage'; diff --git a/src/content-script/pageProvider/eth/utils/message/portMessage.ts b/src/content-script/pageProvider/eth/utils/message/portMessage.ts index 77a0e651..43960cd8 100644 --- a/src/content-script/pageProvider/eth/utils/message/portMessage.ts +++ b/src/content-script/pageProvider/eth/utils/message/portMessage.ts @@ -1,8 +1,8 @@ -import browser, { Runtime } from 'webextension-polyfill'; +import browser, { type Runtime } from 'webextension-polyfill'; + import Message from './index'; class PortMessage extends Message { port: Runtime.Port | null = null; - listenCallback: any; constructor(port?: Runtime.Port) { super(); diff --git a/src/ui/FRWAssets/image/crown.png b/src/ui/FRWAssets/image/crown.png new file mode 100644 index 0000000000000000000000000000000000000000..3e15d6c0f7cfa4973761d2d3579c5604a972c93e GIT binary patch literal 22759 zcmZs>1yEeUvp2f9ySoMr?yxvPg1gJ&?y|VM21$V6ZXvk);_mM5!6E3E|GoF!@4c!w zr{?r;dRnHZrcRxio@f;%88l>KWB>qwCMPSU_Kzn0ry|1tdml;fw)`WoR^p1{06=Xb z$}0%&Uz^fQR!tEA@Sy_$g2DiR=YOoAV*tQ|9RN5r0RRNk0RRH$tTt8Pzm6~q9XU%y zMF7)3j0k{(!Un+nLs0)702DFcKi2*sz*i`e|HW!h4F4ko4FE*i0AT+|M(-c}Pm%ps z|D*HY6ebVq|60t0{$J^&JedEB|D!ezXBGR8AUVtGx&Z(vIRB|ofXpnyf7WzuG<4i` z6cq%`oE%s|=1!&-tY8P{|7Zb#V8MUT!NMIx33jk|bQ1&%Q~#HQ;6MByHXAkNe~Gx; z2~+DRs!&Qgxmr;2va++XQ;Q%|Qc?n4%`FAhq@@2x{a;I%+S=XSS&)s*%gc+^i;LCC z)ryTnKtOb|felmBCATWb&D$dIU>@>s5aeAC4O?*?fXKp=&Qk=t!jPl4R`iy_W^0o?DNP zecNh-&7%EQt@_nTZ6n14rmNnT(mXrj@?}>h`FYy6Z?&IDymNK4OuEr$VQVw0ttTKfJ+Il}xXRWhvf?|v zrpoV1M8R@;CZx87oanCFmYS;y?SO43CsE8OY;?CDOkOHz74Pr8I=?g%GPnLzk>%u8 z8Flhz79+D|YNLqB$Ud7-5KrlL3KIL?$&Br8=SK>5(6`akN1icxzVcVJyDY1y(x`w~ zqU?&l;%X)PRVk^dU$65S-WWT!InYIYCdAPA#bcN1b#NHmPed4nfqSI$+zfCJn^(dP z*ZuzWLHL#L(VZX%JkY^xd2 z;&*Fwn6FV#-MtCLs?p&!*UgGde;FvFLgH_sQa<@hyYFk^H(%Pgn2*<+$G3^jsvCuE zG7uNK(BEzn&}9vyGb~cLK0nM(myAp15@@3mjSN zeIu(qr*Ck6SQcSfDwqF(LrW%IsPm?%7K2lMLn$b+JSjq0-6OvWV!?CLt=zONT zz52J)RvQa+6On&^xRR%HZ-Ku$Z(~E1Awpu9S?Gj06mFeVhO_=siY=2vR*Xni5VpyJUJz z=xqq%IfvAD4M#mzk@tvd0 z;}e=Bng{KTm92FSOS$Ovj54WJ|7Mhu;-S?X8++)l4Ev}_wiRtcWny_t8kb|kIRXpz4@qXURxaLBE7c>z|i_!>ihXtrr7^_8s)|yjlqE4Q{*bB|&Wj74c zHQ72JjFLYzG)S~T9>~O86}Fl@Ob5$Q^vJd}##Zov)o7@a=Y-J_l^^|3E%T|Up_~w} z%#E;5FhMb^(Q7w^ynCmnha7>o?0$)zwu*a+L5Gz!9{Eeb@LkP&lU^GebL56V-EtEw zRXo#Cp>0kVOb{A_TBBMRPo9b-<-VbK_CG9lLmb}F=X~h@ZY>Pn)Ue9X_AqPhl&NoXH zA`elv8ooVgOmZsn8W!N6>ipO7X(>~WtCFZ*32XVi*ruO;ip994H-f-%KxgjBLx$=h zP$b`>a@yYgnD)o>K_&`vLxRAH5iWWIy3i7GtaAdy12-NdAnL}*i}mt5*2va`8l1eg zsp7l|p8Iat-SrT7U@N(`(dxiG+PLuAYL)fN2OmVISys8eyLCWKJy`7vm1-0xiMPNT z=%PUpP}d3ciD7Ecq~gNjCafsqu!9^D^@Oe%W)ZN1ZW8k=cVPo`!|xxu=}h4fUUvTO zo}W{wqlblRnLNVaLifg6IQ0A8h{Srh%q1RhM`+D}S$j9NSQjJGs4u10` zKewOC)@Ic#q-FIbjo8Jki>m;!T$3I&W>=rmcm?0C$L{<>2yy?iP+EfBd;zxVWz>08 z)dUQ{S@XX|l71no=sgiV`LZJO#apyLWVd5zvi13r%UIzO5+Gm+*aa~U>9Rm?T|xLd z8Ie4Vv%}Pn%#6!~dTjkWRhQKgkNkdLR5gF(9*q51@;qoji7CsZEqab|@g$gtF*>ohYKim4|K zP5m7}QbXF-VW9eV0N92C(G6q06)ddwF7;0Hxje<++D7Bm81#7muUXb-`PeJ?~6 zebD(q@V39U;Xvc05JIK-D=rvKyQj&;2>wP)ml^Pf5X~ zsAiDI&OhHTJ^xhH*1E`c^2m0ZllZ~kp(4MKZd30qkAR6I6NinDUjhm4?!Dvy9Qmyi>I6BK)hVP!h!*59Tp!l>f8pjT~;af9mMQ~^4;HEikP{YdQFT>On^#> zNy6dm?^;-!k_;S4e8QXj&fA0aJ7sttfOl^m&94W*kSIzZ>1@(G?lP6{U!V`@e&>+S zjtpK~Zs^Os|L+U?vBz+6mawZcXjUoos3i;5}U$G%Sy?d1R$yRL4cEy%vz=xszE4u*pcae+nnPMvmtU@)ax?I zVLSdjJ}O+8=zp(y5&p|5m(-tEKD@PqM<SqM@y_CyrBEMnBVIe0^Tue-}&{)ld zDk~q6kWcXH1}Ew}^WBWyM-vtNAFg}WIKqEJ>=tfI_0b3EHd^Fj3-X5OOF2;SC_HZ5f{Azg0U2}_Fpd-qVg zP)?VBz@O@1fleV&&TgwU`WNJ%9~|p$XHAM*FMFaEj<8c8C#g}U1~rt8V${fUeMk}zPMGJ~ijh&U(8 z;7_rKAFRrxma+HcXXI6!?$NkmG@aA7V#~w9xOl&4f5>Pr+ox4 zBg)@j*ASzIfBCph2)|Gc@VU*z*sOn#1@LA;-tI#UZjyVFo04+n~(B>e?OlOgor=@g+)gr%;X)izZNR5p!NQWT`7C(`Ir#AI8Nk^6C8y6=B{@DKbIKRSj z?eyRPudVKfn5gr0RfI=UO@btE!J_RRpnDtGwO1AYb;@t=?ZQ7LmsvSskN>z&*4xc5 z4n<_2DdvRV1Ip;z6#ZVYVdLNIcB2{H?nkYWn>MJERTy*8_QxwaxlsISa-6Jdr}p*A+YIq!G- z^xQWmUpDB3lelv@7)g)ZO6Yk;C%@eei4o-ET6wq0713E!8kK9F97Goi^M;FTsY$Zgq0`s#H1p{Bu-LX|?g(*d>x8Wf z>pP?kS7Y+$uO!hqouPldxqTG`oC_2u%EFG1_5;gu7bM!3(Tugm1#Uxq;H*;2hDC7` z>Lm#US3^b*<=K{5S`eX?77CvoKNDU?WJ7>&?m+bD=;8P)cFI{QXmao%`Sj{XOlB#zw&rT_sEXb+X0>`g5^EJ}2u8QwaVKB8f@>Vb zCiE6Z)4Y%yv(0asgQYQB<$ptJ%*@1Mt1o6Jxa)Spq0dZEKJ$gDBZV-Qc{-M!gIKd{N$|5FOkn^BqAC9d3 zxkB;Om7@Nhw zAQc&8O2-oHnde9hVpYRR-iQkUh=x;~CFd|H`a`XIh=_<$L4rLojW37yd>sxP%7~;W zAfH7gtNW?~1F^p$m(dPeUHsLT0qUgRl~F40Nn1V=q^CKa#+?Fh7Bv%K&}%=!p|W#yfE?Q++_5IZA|R^MG2Z!cGyUkR=9G&k#h82Gc3@MeTV@M4B|M}ifdFisSC8o1F% zt4Pm2mGar(A&1aFpA&)?(cAjO-$w1cZ9AE1t#(f9J$M4qL(eBLI$)f_-&WEo~;Zg zRxY{}V8~bP`dPthB`ISZ>a@9&JN&3g#Rzs+8W&E|RS=1(iNL?X#PDm+>%<4KIs2OB zMN5In{O_{>vs^!eo!!qcl!pbB0~?P;y)(8V%lQ`F3!o=o@L@=2?nH=RZz)7NPZmXA zo?@E)o)PS-H8xKhflezLe#D3cZrW%fs9P2!?^Bh3F7>g0-3i^Q4*4OrGPuC5l(gT{& zR4v_|q=$?C%msPwXHeOp>Gvz7jn*>JcI}VZ?=!G}xSJD(GVp`N{|;5%QU*0_-&S6q zP$o=jm*&R?_HfO$QhgT#ZUwSQbl2Qul1O^6>~%Vy1}SH6Mi3&(EBn86bTnJISxsG8 zG!EvJar0oMG9A`DDn~+qzGPasW#!XcT5#AX=G$=4<#`C2U5m5}|>W z8cmskQNT_mP-*1%sb^Uuz1-B}lN)%*=oi-Dfa!b4#K%)YETdk>v{UsYGNmw!5m~(B z8&V0g(B-l9+L9{c!D;oB&GjxRdc%|79GV-*J_g^-1VgphJ#ZdR_`N&uZ)2iCKHTOB z=S&3J5^~v~Opdpcwl=U?3!>*Xe@Mq~v&4V_0|X!T5XqX$TRG4}c|-<5_AB#A#xqDB zS+eT)mOh?W?lv?&F1|PoDXIpbqEak-SKBIpYzQ6$qm!)#dt6~{Zd9`(zp&3!m1?3i0&Q)cFWKLe-%>`_0e4&AN z=+|*%N{{RuF8q#2(JhYBjy$!oC9;qHkelzO5#G>u*z?3(L>*BhypldW@-1W)TALNg z@k|t4wx{`|ij4l{)jsQ23H#kCJ=$mhYw2;4bAmn11kknGv+C^Ce-AO)5jp z?Y=RR5AMMZs*(-g>%+6}<9y8}Zouv($>v*g^HzW_0zDj3gg&&nme0?NSpUa2QG&zu zrv^Quxi2c>{E4JH?CX%Aockq8EEKXfMSE>&u7pF*xIP3q7`1)DvEU-<*AcA=O9`d0 z{3$u-X;k0EF0X-Sl7RN1$uO*^kg_*`RG+Ff-J{(6a{Fltj?Q{!Y_?L@ZG6m_#&XvO zS_!pS@?+OEQwWZGOQC%1&j43AJ0WnVQTq%RKVG7+V2#431?|*I{^H09uc3=%P_faV zJGl(s1#d)9l-OuUw6d+wAl;!m`K=b8UV!VH+s940o6~*?-B{LR%%6>h+l*1MYNP8> z-$(w1+Up`-Bh5ya4ka#-#~01P+IL(}!GXdFYsv<7j~E0PfevRd&3-qJ-dzY% z=^mD?JLbX$gG_$<`siY4^3NN0s0h@uVKhWyHq*xmO(xhlh@)*RK@IEjVl@*<2jGO^ zkO<|^QIXHJttx`XW8q1Q1H*A|wY%#QkHC{iG(MyHT-p3N^1puq0DVBMSJ%x!l-1Zq z0q`5Fscw68^R0cD^#b{~g3U`4{$wWAk4=ov1 zCG>`bej#88%QTO5}deC>(@&!Tx9vasC$yfb%UZBHg0Np0^5Ic0|KG)CRS2bHN zV-@9cPa=V)jt8s&ht{bRSV*KKbh-tLyZ(Ry_0WM46(hSZr~ueQeL-|no6$UuC09tSb0k1bCUm-l=_LD!kFqG_DV4ahtv^~(slmjcy)ST zarVq*02+N>uT77G#JNPIhrtefFB-vDQ$`ekEvz!f*k9%r75R*>pNb5Q^Zw?kbI z%d1`=!fW-f@uC$N8*if8c&;NBW{|1f`|18>F)UW8>6)B2QzDZYFSW~f0;8#g1w6My76>upU8zr9D_`)}HE46UTUa&-X> z--WB%9=kjx8x4E~OQ)fFQ@|;nG>aPKgI5iBg-c->#L56bN{oavS^G@9>qz;o=B6}F zYpD0DeNwm2TrE~&w$|q|rQNV1*BbhP7nn1={qPF}R`L=`Y}R>L+(8p(gZLBu)VizAy;vSvAPb}C?2 zpcSsPU*{Y#lF#7&Am?&BaEU1mK&G=T%-6xrSs{t}%k){l!b}epQ{m?AkBms!$RyeL%@Vo?c_>x_sZR8r>3$YYBN zdeaK5!Ki!R`biinGHsWxL+V>xjPnkPq(i}QYgh^kiFObjta zAKmc-NRybQJv+92-iDbV>!y;e>jCc^qgwWNmXCzq5$z80hMhfm%s>@OpQT=5Na>0z z1s7})gz%Y;Y!a1V7Bn2K%;P@Vcs#h4yCFZd?+kxgpZE>cyT}S|#+5EAOGc1zf!*Jv zJ)|)uAfJ)gn^v_NzfHA^5F9^Zg}r_OyOZr}n}mdfuTFq2V2Xek#@uV%f(ALE&y-AQ zB*D2D{G~QMo)Vx3A0wHC@`KgtA}>xnwptX}Kjoa?Fli67M(*qJdCAigcglDBkeRLC zpW2(OIQNYo^VUM#=faYbe3iJXtKnZx@7AZ_M_MV^l>IAlT2ID$tbD^YeDRV?ho0ZN z=>k{tF>XwN#?|inW%h!8Dlxwz8(n6$8$FPWz)cR45;hlTycpyh+YUf13Z)Q=KVQO@ z&KtA>Y8DZwPIY~wLL}d2?weR8s8Sio`9f$kG;^Z4@iDHZP6U6upf`pLO&}d)Diy|I zyf+)bi@JwrgnSjt&vUkuwqKWsWr;vYLPvD$_sutnuby5kE<8Fun~qG4N}VPsoHwt3 z0t8w_o{580>Jt|Z#~K#Rv3u0Jj>l!rPsfG-+R078NTCdoLBBDYoVp#KQ_f4Q5CkAd ztfW7?P#jojVhh2b$yYfnZ$Vnrn%?12MeA(idIhgZG5p^@gBQ^ zUI!jDdV!%9)s^@e14n7LT5j2N=6cVT)RGPh8$WM1>V+V&{%XPOsQrqH{;PEul?ag= z3tf^Jz@k`Pba7L3V^a)I1$ky&XvA%Q)`{Isi8avmla~8K^xDHJHv2D2)lsBOJETy$ zqD0jmyfL<;mUgMs;6-%000%$N%;7Gq?u}>=&OqQOIr-HQv3YWIUVpt|FaO$E3F+%l zWHbwY&f+?TSR5^0iFoV+YMyE5U(DHk7!r_$pse4P?^r0GVQt8of{feOKruiq6R@rv zg(S-O?+Cr9+t%QgU^#ewMLGK>6D130a&l-OM+EcQugX8<&F71EJGQ=g>(||ZPLyQj zs*g2If?s_^Io}XO+7O4B@j)SUE2F18nX&*EWaVAnrGPtjN&@a$m^>tM)_8XGm)>Wj zD@=TlO%L;3S#>yl?_^;jX5LDnS|(KuRU4*32X`}Nf|TP-ZL!cDhOj%mH5W-DStwT+ z*_X645pvU6Q+IxTtVr_mTt`X4q6|;#W;99tq)vEJ%gByKNJG3Np`(zbZ*HZp3Y0fu z9OL~_Vd^g~&iN3FOlHle+d1Lp50 z734#z4qThpMq!ge!8a7rpR#TsD<1ZBgNFXzpBEP#&FKVwcQE9-@x#C@@cV?q1p7cK z(M6zjgv${{i3N{p-I3cAy}1h&QCEY{}}%|XSrt1>~J<9sK<^aRVy04!eR8`|KV@! zJvWjyyY(=Th`)I98-7B;o=bq?)*GtdKx}(|-tE~UFJAM})95K304vvm0{Md}=1v!Z z9u%3voWZnrklJsTRZ|7Nd^7f0LD)!;(3e55`m7Ui$UpVQRdbPt3<&qu`Kj(X=IxA9 z4UZaC(fcJOsMc|LFB-Cu;+Yhq%!})d1~ z0k9U?RXc{Hj5^s%;f{*a{H$fM%CuSINc?%SUx>K@`3Et5hne6 zoaABgUfnAIOSkV;2;Rv%k&7g+)pi;>Ba3NBj7BG;%Wlcz1d)a(qs&XexQ zM$A60v;1+nTf&nApeyL^N+hrGE6GVK^ z3&zpxTlCMKk0xyiJj{R?QU)#I**~8T|E84_F;k|oJEqbFaw?qe;9xV~&_^vx{zdfC zh)luR6W7*0LT+Am`}lVh@Q0LKSY#e24k4aDstZ@tq{HjaaA^oXscE z!{LXpbfd}aaVIm8r;q2&``9dv1N56ujle*`2uW>qFr67efJ9$`Sy&IRP&U+S21y`c zLLhgNO}g6bSHitx7-jM_trd zrRf2mkB`vpfvewHCX|Pui42#h5MBW3cBQbaE$!h$RACe8F?O-Yy0QmiKfC>N5bc& zejYHULz=oJ#0Z9ZXd=cy-n&}Wf@a`Z_Fjs=?C}#qPfUw+#;(Z+o;!f-U`eCmkFJV) z3oqPL5Lf2`ru+@V;_TtptYKH6_1%FL0&^w>|DU5l5%m%HX#2DUjr(MOpxrpFEGI!7MKCkazC8cRI--Hr*)AcT5OREzb%T?thz zWfL1~63?v__VZFKvEkXtrq9i_+gOK|JreW2HC-0h0M&35oqb} zuxi`RWwRJrn}IOG-c4Cn4Pqv&X&R-r6$TOK(Ot(UiW*){dB_)e#^i-Cr93T7NV}5k zrN3#k&uu@KPP{ z)Y{o?==ozP3`ow=rDn>MBlP3+8lgR~mx_xcMq!(?;SBDJa2S!E4XczG2+=-OTI0_M z!A_4(E~@badW0eXQZln-t7F4wQoy#cT14gmDE($)gYj0j+*E}>6-Tj=;jFdTavuuU z%pG)XsXp<ZZ==A4Y(jgA4IfG zOZN=D#_{|acP*}ypCIy@su1LQ*VW$TD!0{}Lm@~D$p4GfwEVVEnT?1Nc@~=*T7;J= zedB&D()|ptL7N!A7z-dr6s%hiR{}(NcvjXlsK$#3g^|d)Tto3fTX0$lphY79kO-2g zn1xbpgu);4c!SrrYJ)~rmKt_qv!!%YreIZZSmPEn&ZR0j$uglwBYIOm*Ijdgh|b8aw$KJ1y;c)iEN_ppY>IrJhuEM@OYg>W1=7;&>{;{DFlLIDgsNlARoPtHXT|wj zYLsA!;?Tn&0*Rk70_ONrX=}Vs*-=qZs+0KG~gOt022 ze=X}1K0w9*IiKiSn<9p|$TkTyfh?QU587BGfMMDcV21H5L@AW{tWOTME3LP+=}{YK z4N{x~iJ0+uSXYGXNZ>2Kp8c8m=Y1~QnH7Ry*_I8IdbX0>>SO7-eJnGaa+Wgt@eG?P zQ9J1P?Gh7~#yKoRctd@&;}+h===r!5q$C5*5iW6?I$Ume6Q6p#yl*?mSrJ^H9aBum zSawHLI#P*-Gno8_i)#Q}v*HHpo(HvXHr1`#=yMn%{x2y5ed3iPlmp{lv6$;yzX{^iub#Cpe~7 zybLAM#+A|^5HA5*-U#<#`hD@WY4@+^B?gfg?o#yVU#r(mTvq#4o$*i$R~39X&R_r| zh+{0uj-*Dv8&SJC7{`0r_R5)plU+vZwE6F870QvxQWwHe2*24}MgW-ySK#{|*C z%7{91`5I+^oWg-kSFfe=tB=h`g|*VhM zxe|V%2gD`hhTNmv?{Bymah3h@Omak9$VS zN1Hg=7DVvc3}xx@NW5uh!vchjuwo{meAC&NP;zt51RYcioaP95hUoBADdXPPgzKc% z$vRRz(rZh5mL2>))4omAhffF(|Eg}Z3Vn9C82%-E9)gYjzN@6JP=73*>QeM?$GR_x za?AaGmvhJq8k997s*!bnKSv-xj9Oka{@R-O>h3M7qyG zFaRMJ7)BfPdxGMoYb@b)bo2H^6xLiS=6!b$@$ps>TuW#|YsjrWjZc1F%m*~5CX#CbL;3_ z@N<@{joyXmDZS|z*-FBMTsk93qm(hWgqZJ|%In$u@p7aQ#rH)dsxlVn0m~^wZY74Z zE-g#fPeO*Zm(ob-=QZD1W(I==a2O-xgNf2jPlh$I<*lMxKzN87eXzEbY>1YO+*!-EDL<+Ve zEP4leVekyP5gt8$7xQ#Mbdy#4-9&3}y*{QKLl=s+E^j!DztlaDToPR{s1tzUv-;Tq zCXQZ^ty(mw2Q%v!4YO*he_8_}c5r3a6VZPFy>YuAz40{W;PjQR8y|Yt^nBOs!~Rcd z+nw1Tc<|)F0|Le`m!%Etk?$58gHoSro%8mPlHv_#S6X;kCQbr&lPT6H(><+!o;ZCeUdmkYk0Pdc6oOHPZmy!`SlVwjAny=7G{X}&8DU# zS|+3(W|l|FoN$0Phr3(u8N4T@dXc?jPkq_Ha;xiiB&-=X7tdG^k^equ%Ln{GpNvU^ zqy#7JMN}KAVJuB-U6l3t?wFum&DrCh(93YsMs0tA^rGz_%e>pCot;ExHA}UoA@r~_ zEuS9uB&DhQmA-@w_}dPA`rS{i6n7ERme~$2x1FZ~?=cT4V%H#l=C$i%bbJ>EK81k0cma6t{tK1}sbZ6r^bDkt} zMyq!nW0*UKy}EUUOCT8Q19f0Nn8q@TjAZo5gt%gR1wlhQC@ct#0UHx36Omd4E#Q-7nRjf#INI4-s93)nZbiU);UOH>k;D#0`5F^M)a z$`8wTw9j+AAPnj>IqBIgjrlIYdCJ&|D z+ThV#;5Z{SADc0EZ$gkxz9#b)q7re6AlSgMQz8hAbg_Ia|0=m$iK%C~r6zwE0E~{R z$tHg-?9;J){ zqENI*lH4cM3a&$o5J1g%_QdLHw%Dg~&wQ|I`SwFi0Ffy4t4--QUs-Q%B;fajJ2OLf z14-#}g1{cteLsG2NSZP0I2QMKu0s(pqRv|m$)@)cQYK|KD~6GZjc`Aeks6#sefJ7$ zgQtjuVsWrU8{x!rZMF?=Q~ZVL2lqi9;Say7wDKA~quO0z(dDdR&8fKq%BJla+}=>Z z7PlYWR@iyqh9Ha*aa7yIXsx|jGDSY&vKh=wYZdIoU=m@tPO?JQx;~Iwn)`14{?YGN zDKRL*z9I&#A4MFQl#LH>3WGeE21Bm-HUYHmbZjs_Bj4Yjjeq*t)~kLiM*U(Mf=Pae z*`hoG=@p=O@oVK>7oA7XfZyuAx><)Q5*v?gMI;B}z9wbO!Ne2bSjdHvMMBGxm)UT0 zxz8CoQXgB#Wvpg;?hQRl*g&)b+7hLbfI5P6#q}IXbV$CGO51}ZtR0MgQ(5zMU$F!$Z}?y!qB$SB~s4B(w- zXZ3rwB|(SP2n+mdU#S$#4noLNunMW-OsBog!e0tvM4^^<(=Spqt=l8c&K=r@yvcV^ ze775#Cy}xDSAYkblZFCLr$TR*4d9#Quyc0zOL?5F$@6wXDAD#4Cm@+C z9oANvC4+=}5lK@?=YA2@Sr%UJ!vJ#;ul>VVlIva&gG%~A>A!Si_4CQBE8q<+XnBXR zhAJVPk)*_598$nW^MyWk1j zoM&A=nmI?sX!Z-4$lWFB6kLfEv zpVSYAWQ1E&!a!>o(i4a05mDPPQiTb^gcOwFh^$~cPEtBu@~!Fv6ez-!eMS9e@$LPw z80^UH!aVsej*{`tQ0S+!HD9}G>OYsGQ;=RXuay56g~8mi~#vnkgXy8^GZJEw!Mn#kGX5s-_8r2RCU`}H5gf75*?5lpes&H z0b;!H-QLqjZhe*Tllp?sqYL#OTbGT55d8^+Z5<@l4(J^$mAhJwn1sa&p>^61XOM6P z$+*|UmRJZJ+h5k@sl&1FQ&Sh!ue8__c5|Zaeny(JLYILitQr|k4%Vgm{w?Q4u}WZS zcNisvw1hua()%0}3b9X@?YS5blJZFo7$Hvb$>jv}+CLn&27dV&S7awDJpN2FCrA`M zbF#@s1{}x*HB2j}WTnl=@0miW2UXN#=IC1n#2rQf!ceI)FoI5D_@{TA9^PI*wM55Z zO8*uwb}%pAX3wr7fk8$YqKx7cOBSOu_wy`U@MA(0eyaD6lYnc7kN3P;LQhHcaLxwq z)?@&XkU}4qe~Bp?eU?W6(`XDl(c(62YHd3YY@B-|A|S$Xzm8H9ynyYPdT4@73_0^a zT)KStHWzBfA*k;}X>IYMAhwIq{tU-Pw5Bcv>$h}#&$9;Aer`C0+n*;j-ZMp(fk~_9 zUsa}t7}W@l%<|#+lt1F;zrjScO6(vFj#8HRgbu8Kv=Yq92b}BHg9)w^tUAzQR&pl+XDXY(8apG> z;HSFQXd6CJyn&@=IDX4FsS1an&}Bt*SfR;NME)Y`DrA@~59r7!lbeKS6qDE$*x6GbeYg*d?XZSS^=*pP8g;UlWw0ln#0ds=yi(s6)F zLR5lM4xJ$za=M%Znr=G&y_DM3hT%wEMRmlV@hBW40WPJV!XSt8DGglPR5rc+k~ys| zW@FtpF%wSN!#AC-K5&{5$sI zNTyK%T5B9&pNk3tS_$fsI86h^8H3#aY2_^cnsB@}JVr<}x*JAF=KztGZd6iYbc2LS zjT+q`-7rErzNB=Qz;J+szyLu&IwT)||HJd{ygV5rKitHTzG{5lC zF387hQ&6Ef<;N{8QkpZ(HJGrd3G7Dqq9%9O25-Bt(x8A(3HIjq?X*j7%H~TO;uiGLQ`W6M8-T9Ra<2RrzIf7NPHPKKCQeQK`EC@Vx}q;;ou0M z@uumJWkw7vyM$rNEGG8iLeTU@tKdy(%GBr=gKk7)GTzqGQQT#iZF8z}AG*VUBK>MM zspmqhj|n_~qvd`p>2NGaY*-t`j0u6Q0usocKkWRxo$;LV5h3Bt*A5zLW0AtM=-C44(~jYlhxwq6j+C!{&=O`2%DT;Blz zP?`VF1t4}(1?tZ@BfuCej->wr021_p$^aC>#mgD&BpX7;gQPiXs^p+;UWSpdQ6U(-k=X@_qGPpC53=<-R=J$JEV^p;Y9}tysQR z;VGzsculvdxGhG&iG$jrhq!?>r$Z9m|4kx#zf7M{<9EcaY_#v zTR?i%t<%*Zak`zu?DOsPr%2BoX*bnhzR#b$N|IG6l!YYBxv)tbAXtPAlabP%IA5() zyE6Qjs3sT}?Csv7tY_8AygVnr0^qCIN@_&E5%<;@6qW;m{Dr4xH=OtG$+C|8NsYzKd$i}Zm|dOu+4qFmAAR5RsIkY-)S&n|uL0+ypQqIQ(BLRXpCxY%ep8g>J z?Pg!ZLxYrFlY74TtCv^X{y%@Lp?mDSZ9N9hWzJ$`n8mIBd(o_=1FSZteYH)A$wd5i z)jnCrO3aT%+JxJMM}2D}AB@{ALa2W-%ohVoL6ej*$Yd*n05Dt;xEUzrTn`s4Pxy=U z&+uj}LkyiAz9-=lp|EdH<+CRfMhx;vc6mL2JdsUYgxTF3hDzIOEF#g+;s>WWjS@Tb z4}h>6ET&M}NkR5d5aYRiOOikYlM~^%cCZhWV~Tk0Hk6%_lv&qwM*FP&rP|Rdbc!VjI!G(6B+rU$6)?f=-hqd@$W8NG&6yC3k zbKTiV<|c_HfSs5FgXo6(*6D1Ld%iU>;#`(w+r(Ui;=S7o$r#pWNI zj950lT&-s;CEAnsj!~bZhP;^uYj<#X^ZpE&DJpe2E0LGZtq49`g!h_0n`w5Yni*WJ zBqoq&;a&W6QdK&XyL8styhDW71|Kz$`%5S0O4LC}$r~pmprxL*qeVy->%F%H8WHVH zcEEyci`W~Z$-#`u0(Q7;?w>30=52>La4{6DlDj}JsN_vfShV2(ne?$6dWWSH!L~@^ zimeY1`QZn+7Y+fmj`!B&DuTTG=J2^<^qhqDzocvkPW6#^1I{;Ym3g4lu|@Z#OS9G* zB|KiB2UQcN{laBbZ)kw)DKlMoJp+u@M*(5EA&;%wgHLYgk+WO+H@gDn;hD4O^6US+wLZJQhDZqauQ`q6u7!MI|1aX?D=>^#KguV2$468nS{m z@B38XQy zIT?WU7oTjz^V9{oVM&Q31QH)IkKqZT4<%}OHh49 zL^*xvliA@GJ+sONvfV=G+4?V_bX7dTYFyrVbZT!1^4kks-j}Y<>IKz^Q3XAIFAO@Y zCpGgLyIuZ<1AW)2etllW4%90&HKBD2{BqcCcy2T0*dUmsaUP>&5s3o_@xb_gv4V1l z(m!i_*-R=eqFa<8C;Mz9B#lk9T4fPliHDdH0(&Q7lTT4tQ3cWoNk7%B`EbXLJ44E) zZ=(d3v}E*05A_h_zi;A88i!4)bTSZjk4U2tC73m)wmK5Fz_&6ltj&KpLc(0v9(tUc z!7f?78>_=E*;4UtS&B61w8um8@ji)pHPOz0+P6S+y|HyH2XRresIY8To9#xwe6krJ zvoa}IzwqT|gL6ie2H?b-eMp)TGB3X=kE0fdmd|4GinrFJB65=4gp=i*C=#s^9CGi= z1EVBzwB15&?O2KE691ZnX1fF7s}tLUgN9St*djl*QUck;AT{ICGxkw!MUhe_XJldx zldcO9NVUP?l)sJpBJU*J>B{)E9h^1E)n&HyVCdl;bKrfHKi9C*Gd)2ro9|%TKsI5ZTXi(S?vD=$>HZSQQ zbbO-yrIXn>Y$(ME6qO;7h2(v#$R>~}CSIa}_GHn>s*5J(al}?JEfKZQUm4Fep$r<_ z4b76!F%`avVf+aiuG|_Jwxclj^A(jcGvDR)J1t;yU2J$PrYzHvV}3y&_;UbJJl zJ>CSW`HoLthJVNw3&v{x;$JlAN}|a>pHzpJKuB8FddS^*NnYdN2uB;wq`}i@!2Iu9 z#af0$5MUZ0KNo4*j+j*ZuGr@l5M+9@dfY233L9MO6PGFBP!(PWWTXAgFtFF(dGKN{ z)i`^1Z`}i7mxoYP#>=6tJ8xO0A#FmzND2_$`1} z2#oSm4lhJhuvYpt^cJ76Q?2}^V!*0Q(QYhYVL$U=`Y7q*-qXMa{N5752}`O3m3%t`0*y=GL$Xl5tP+uf(VY-BX7P&-~a zB;c>5?72nSUL{aLG14sdr zhE%EByQuo11UBY`_a%>b$irC6vu=bDBP5JtqKrAX9J{&L1ov6EcsY4dnjNI+V1KU4)!#Z^VQvuFMT!NsaOJIb~=FPkJ= zx}>rH?#c|xWSf0N!)K+!iYIo(k%2Sd>G&7Oge%6ANr7-k`IJ)|ZD(s8)N zKztg*a-6~?*!!Cozuy*b#8!0<&6mo#pnfGo^vt5Z~)%t>;U zKIf1%0rd1)dA_G8?`Y$cQi<$Qy*jixJqq9pb|edQ5v#uKDBF$vo+H`P_EJAvqo>zn z>~5s%$@pVMWPb_#RVb$!I$+f22j}&bUL0S2T(ZAq1KEjIwNgq5) zQLt!ZW4u&CpPDt^MCC}`!v?7B$W_|0q8C*Yvm^F{BUC7Z7)?Gw>W>}7o{m57?Ednr+;ywu$7akr}Y7GT& zs7f0*V%BaqhwO0hb~l5ePP{Ii2#U<@R2lSvJV>w=mvdM}&XLg`E^^v@wT~ETKIN>^ z{#Ly|LK=tjCtx^vqRS{D(cDi8@t4QC&mtmhoW1=3d5t(>aIToZ2Hj_|0t7W|IqXr(bd^C zpgG}->$h9N^Yz2Xy}I6`L#!q%+zLq=UsY)HqsV}lJs%{i^w-J#NVh?QZr^8Vb2cyX zQM(H#9w+7Hq$|cSWOkJV>YO9({us&bvN46)mWT%5qYn#$NZpA}G zAre_sPzS-74B0CFlX*V^EFKGc^XN$-<(7P&{m4lg_n~`(I_MU`x@&=&wu@nWnAzgu zt@`?Z#6Y#}=`#!(UJN3gyN~TF`-|b53i3c2T8@Z3_20OfWaPR@Y67JS>t2l=dYc?uT9U}V|YJgpDjhsU#mxdc2 zfmmzVWTA{6?8Y*(Lo-51bXBhPp)n@eZAJdHNNewvs5>Ud_{0wru$Uk)qaCIE>Eqx( z9}6t@;C7;qjGI=B}>q9_-l&-+G=b>QSC+cdp9Xh}&((Fs(` z>-?E%fD^!&#nCWa?LG6zG++#%jyVDg{n%gVBrcyd6cIDdLJrpz8as=>L5CB9!o7sk zex2xK3VBs%G}7g2!JlVrSCpq-Kga|pKeaJeTQt36=QkJV2^_z|kXDw< z^o6nCm3fxm)k5U5^L#uWwi)qI&adnQHB3{mr=3$=5N&@sw{n)_qF%fCP(I_{M>zyv z{7A^Rn3J+ny!~N`kHpOg6B*1B*aHk}>P%PT((H*nYPVs>61NmBf8zk)@@CzPdy*|B z^zI7mh6}_ewaNfh#C~Cq%%~eG_sOFTcimU}B%xlJR$o6?rkp~mPz#tJ`p-u1M!vfh zb{%X7oLZGP#W5BHOH^)Q#0S<~YFaQ-5-Xo9S@YrNd+f*_=Y7I7%BVjGQ{GkrGO@8d z$8fq1R<@O^a1<^+bTmC8K_^cNA!x*go1O5jHb?-*u)}9rgvKL#8N znNWB=2hQbEQor@Ur$g-wa&d`__%{bL^%~;8RFwkmWqR*t$H);QLJ}K}mH&?4m(eaK zbLnKkbu`pX6UHt*hmeA3078>uC1 zs8ypaNMT&5Wd@o|RT6oHpP#QEwcR&eFRo;~oZ}K#7JXL#mWlcX5@2d4FWT1^67?GZ z9ouYO=mDNB$o1|P?#`&!aep5KZJh25b`8a)ztR+a%Dx|C;S`Tn2xsJAgE)drS|qg_ z%}&Z-bA1gPqzaiEtZzyG3>?Sd!?r3}l?=j;GdqYpsXb`|1w=v@$i7r*G&P(0hrF(n z^8drX6MS`Hh{eHq3?kHCzLL#+sPtl?n)g6Ol)$gcoy)zj zJJm}g8QpAI72r3{6yr?UwXCNsR|_SfioFzkKioUwS5?%6${9vHsb~}9Eg)L>R178Y zX+qH_k&%$9*X;HB32Q}EU6kg|FL5^gyUVU#A26^8USEe?9&s&FA90Ab z(S?`tLNsysN;zTWCI4A#Jg8M6B;ptE?~ZA{dA!-N{usomi3!}*!R>)^eQ6)IZc3LD z>pB`x()0y2E-QRUE$xL|uO>FXle^S?ACS68y%NgTojk$m9Lz-N8XZkKQ!1r3aIj#G zL4-1&>Lj4rzhEM>qr&gVqQ%~+rpu{@tW31po-2P3`ru*W*6Gnr zaFrjWW`sScUt6#sOu+lvW!$m6+7FXY22nlpUy$Jp%3&m}b|vGf#o!!nPprfjHG$uq zZuzEWhm;N@KB6@^Q2_g$9-y&0tFe8te~6c_@KdE-yGn8AwmBuAqoF?ay7sg_O}3E^ z8gKo!EO33kj}~-6a|C8|6{R>InGv_HG_}fB$b)VN_G8$tDAakbF - - - - - - - - diff --git a/src/ui/FRWAssets/svg/planetr.svg b/src/ui/FRWAssets/svg/planetr.svg deleted file mode 100644 index bc228c64..00000000 --- a/src/ui/FRWAssets/svg/planetr.svg +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/ui/FRWComponent/Confetti.tsx b/src/ui/FRWComponent/Confetti.tsx new file mode 100644 index 00000000..6fb5cca9 --- /dev/null +++ b/src/ui/FRWComponent/Confetti.tsx @@ -0,0 +1,171 @@ +import Particles, { initParticlesEngine, type IParticlesProps } from '@tsparticles/react'; +import React, { useEffect, useRef } from 'react'; +import { loadFull } from 'tsparticles'; + +const CONFETTI_OPTIONS: IParticlesProps['options'] = { + fullScreen: { + zIndex: 1, + }, + emitters: [ + { + direction: 'bottom', + startCount: 0, + position: { x: 50, y: 0 }, + size: { + width: 20, + height: 0, + }, + rate: { + delay: 0, + quantity: 2, + }, + life: { + count: 200, + duration: 0.01, + // delay: 0.6, + }, + }, + ], + particles: { + number: { + value: 250, + }, + color: { + value: ['#9146FF', '#FFAAA8', '#8FFFD2', '#FFD37A', '#FF38DB'], + }, + shape: { + type: ['square', 'circle', 'heart'], + options: {}, + }, + opacity: { + value: { + min: 0, + max: 1, + }, + animation: { + enable: true, + speed: 0.5, + startValue: 'max', + destroy: 'min', + }, + }, + size: { + value: { + min: 2, + max: 5, + }, + }, + links: { + enable: false, + }, + life: { + duration: { + sync: true, + value: 10, + }, + count: 1, + }, + move: { + angle: { + value: 45, + offset: 0, + }, + drift: { + min: -0, + max: 0, + }, + enable: true, + gravity: { + enable: true, + acceleration: 20, + }, + speed: { + min: 20, + max: 90, + }, + decay: 0.1, + //direction: 'left', + straight: false, + outModes: { + default: 'none', + bottom: 'destroy', + }, + }, + rotate: { + value: { + min: 0, + max: 360, + }, + direction: 'random', + move: true, + animation: { + enable: true, + speed: 60, + }, + }, + tilt: { + direction: 'random', + enable: true, + move: true, + value: { + min: 0, + max: 360, + }, + animation: { + enable: true, + speed: 60, + }, + }, + roll: { + darken: { + enable: true, + value: 25, + }, + enable: true, + speed: { + min: 15, + max: 25, + }, + }, + wobble: { + distance: 20, + enable: true, + move: true, + speed: { + min: -15, + max: 15, + }, + }, + }, +}; + +export const useParticlesInit = () => { + const initRef = useRef | null | boolean>(false); + + useEffect(() => { + if (!initRef.current) { + initRef.current = initParticlesEngine(async (engine) => { + await loadFull(engine); + }).then(() => { + return true; + }); + } + }, []); + + return !!initRef.current; +}; + +// Confetti component +// This is using the particles library. +// It would be a good idea to replace it with react-confetti +const Confetti = () => { + const isInitialized = useParticlesInit(); + + if (!isInitialized) { + return null; + } + console.log('Confetti'); + return ; +}; + +export default Confetti; diff --git a/src/ui/FRWComponent/CredentialBox.tsx b/src/ui/FRWComponent/CredentialBox.tsx index bc25e722..4a9c6567 100644 --- a/src/ui/FRWComponent/CredentialBox.tsx +++ b/src/ui/FRWComponent/CredentialBox.tsx @@ -1,6 +1,7 @@ -import React from 'react'; -import { styled } from '@mui/material/styles'; import { Box, Grid, IconButton, Typography } from '@mui/material'; +import { styled } from '@mui/material/styles'; +import React from 'react'; + import IconCopy from '../../components/iconfont/IconCopy'; export const CredentialBox = ({ data }) => { diff --git a/src/ui/FRWComponent/FRWChildProfile.tsx b/src/ui/FRWComponent/FRWChildProfile.tsx index a902e425..99b8592f 100644 --- a/src/ui/FRWComponent/FRWChildProfile.tsx +++ b/src/ui/FRWComponent/FRWChildProfile.tsx @@ -1,18 +1,7 @@ -import React, { useState, useEffect } from 'react'; -import { - Box, - Typography, - Avatar, - Skeleton, - Select, - MenuItem, - FormControl, - InputLabel, -} from '@mui/material'; -import { ThemeProvider } from '@mui/material/styles'; -import theme from '../style/LLTheme'; -import { makeStyles } from '@mui/styles'; -import { useWallet, formatAddress } from 'ui/utils'; +import { Box, Typography, Avatar } from '@mui/material'; +import React, { useState, useEffect, useCallback } from 'react'; + +import { useWallet } from 'ui/utils'; import { isValidEthereumAddress } from 'ui/utils/address'; const tempEmoji = { @@ -36,21 +25,21 @@ export const FRWChildProfile = ({ contact, address, isLoading = false }) => { } }; - const getEmoji = async () => { + const getEmoji = useCallback(async () => { const emojiList = await usewallet.getEmoji(); if (isValidEthereumAddress(contact.address)) { setEmoji(emojiList[1]); } else { setEmoji(emojiList[0]); } - }; + }, [contact, usewallet]); useEffect(() => { getEmoji(); - }, [contact]); + }, [contact, getEmoji]); return ( - + <> { - + ); }; diff --git a/src/ui/FRWComponent/FRWDropdownProfileCard.tsx b/src/ui/FRWComponent/FRWDropdownProfileCard.tsx index 34908407..293667bb 100644 --- a/src/ui/FRWComponent/FRWDropdownProfileCard.tsx +++ b/src/ui/FRWComponent/FRWDropdownProfileCard.tsx @@ -1,4 +1,3 @@ -import React, { useState, useEffect } from 'react'; import { Box, Typography, @@ -9,9 +8,9 @@ import { FormControl, InputLabel, } from '@mui/material'; -import { ThemeProvider } from '@mui/material/styles'; -import theme from '../style/LLTheme'; import { makeStyles } from '@mui/styles'; +import React, { useState, useEffect, useCallback } from 'react'; + import { useWallet, formatAddress, isEmoji } from 'ui/utils'; import { isValidEthereumAddress } from 'ui/utils/address'; @@ -62,21 +61,21 @@ export const FRWDropdownProfileCard = ({ } }; - const getEmoji = async () => { + const getEmoji = useCallback(async () => { const emojiList = await usewallet.getEmoji(); if (isValidEthereumAddress(contact.address)) { setEmoji(emojiList[1]); } else { setEmoji(emojiList[0]); } - }; + }, [contact.address, usewallet]); useEffect(() => { getEmoji(); - }, [contact]); + }, [contact, getEmoji]); return ( - + <> - + ); }; diff --git a/src/ui/FRWComponent/FRWProfile.tsx b/src/ui/FRWComponent/FRWProfile.tsx index 73ecfb5a..5530731a 100644 --- a/src/ui/FRWComponent/FRWProfile.tsx +++ b/src/ui/FRWComponent/FRWProfile.tsx @@ -1,8 +1,7 @@ -import React, { useState, useEffect } from 'react'; import { Box, Typography, Avatar, Skeleton } from '@mui/material'; -import { ThemeProvider } from '@mui/material/styles'; -import theme from '../style/LLTheme'; import { makeStyles } from '@mui/styles'; +import React, { useState, useEffect, useCallback } from 'react'; + import { useWallet, formatAddress, isEmoji } from 'ui/utils'; const tempEmoji = { @@ -15,21 +14,21 @@ export const FRWProfile = ({ contact, isLoading = false, isEvm = false }) => { const usewallet = useWallet(); const [emoji, setEmoji] = useState(tempEmoji); - const getEmoji = async () => { + const getEmoji = useCallback(async () => { const emojiList = await usewallet.getEmoji(); if (isEvm) { setEmoji(emojiList[1]); } else { setEmoji(emojiList[0]); } - }; + }, [isEvm, usewallet]); useEffect(() => { getEmoji(); - }, [contact]); + }, [contact, getEmoji]); return ( - + <> { )} - + ); }; diff --git a/src/ui/FRWComponent/FRWProfileCard.tsx b/src/ui/FRWComponent/FRWProfileCard.tsx index 095f9376..1be00f3d 100644 --- a/src/ui/FRWComponent/FRWProfileCard.tsx +++ b/src/ui/FRWComponent/FRWProfileCard.tsx @@ -1,8 +1,7 @@ -import React, { useState, useEffect } from 'react'; import { Box, Typography, Avatar, Skeleton } from '@mui/material'; -import { ThemeProvider } from '@mui/material/styles'; -import theme from '../style/LLTheme'; import { makeStyles } from '@mui/styles'; +import React, { useState, useEffect, useCallback } from 'react'; + import { useWallet, formatAddress } from 'ui/utils'; import { isValidEthereumAddress } from 'ui/utils/address'; @@ -27,21 +26,21 @@ export const FRWProfileCard = ({ contact, isEvm = false, isLoading = false }) => } }; - const getEmoji = async () => { + const getEmoji = useCallback(async () => { const emojiList = await usewallet.getEmoji(); if (isValidEthereumAddress(contact.address)) { setEmoji(emojiList[1]); } else { setEmoji(emojiList[0]); } - }; + }, [contact, usewallet]); useEffect(() => { getEmoji(); - }, [contact]); + }, [contact, getEmoji]); return ( - + <> - + ); }; diff --git a/src/ui/FRWComponent/FWContactCard.tsx b/src/ui/FRWComponent/FWContactCard.tsx index 1936198e..eccc6ddd 100644 --- a/src/ui/FRWComponent/FWContactCard.tsx +++ b/src/ui/FRWComponent/FWContactCard.tsx @@ -1,13 +1,12 @@ -import React, { useState } from 'react'; -import { Box, Typography, Avatar, IconButton, CardMedia, Skeleton } from '@mui/material'; -import { useWallet, formatAddress } from 'ui/utils'; import PersonAddAltIcon from '@mui/icons-material/PersonAddAlt'; -import { ThemeProvider } from '@mui/material/styles'; -import theme from '../style/LLTheme'; +import { Box, Typography, Avatar, IconButton, CardMedia, Skeleton } from '@mui/material'; import { makeStyles } from '@mui/styles'; -import closex from 'ui/assets/closex.svg'; +import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; +import closex from 'ui/assets/closex.svg'; +import { useWallet, formatAddress } from 'ui/utils'; + const useStyles = makeStyles(() => ({ ContactCardAvatar: { mr: '13px', @@ -83,7 +82,7 @@ export const FWContactCard = ({ contact, hideCloseButton, isSend = false, isLoad }; return ( - + <> )} - + ); }; diff --git a/src/ui/FRWComponent/FWDropDownProfile.tsx b/src/ui/FRWComponent/FWDropDownProfile.tsx index b830db80..258bf4cb 100644 --- a/src/ui/FRWComponent/FWDropDownProfile.tsx +++ b/src/ui/FRWComponent/FWDropDownProfile.tsx @@ -1,4 +1,3 @@ -import React, { useState, useEffect } from 'react'; import { Box, Typography, @@ -9,9 +8,9 @@ import { FormControl, InputLabel, } from '@mui/material'; -import { ThemeProvider } from '@mui/material/styles'; -import theme from '../style/LLTheme'; import { makeStyles } from '@mui/styles'; +import React, { useState, useEffect, useCallback } from 'react'; + import { useWallet, isEmoji, formatAddress } from 'ui/utils'; import { isValidEthereumAddress } from 'ui/utils/address'; @@ -51,21 +50,21 @@ export const FWDropDownProfile = ({ setSelectedChildAccount(select); }; - const getEmoji = async () => { + const getEmoji = useCallback(async () => { const emojiList = await usewallet.getEmoji(); if (isValidEthereumAddress(contact.address)) { setEmoji(emojiList[1]); } else { setEmoji(emojiList[0]); } - }; + }, [contact, usewallet]); useEffect(() => { getEmoji(); - }, [contact]); + }, [contact, getEmoji]); return ( - + <> - + ); }; diff --git a/src/ui/FRWComponent/KeyPathInputs.tsx b/src/ui/FRWComponent/KeyPathInputs.tsx index 040cb45e..a9d8645f 100644 --- a/src/ui/FRWComponent/KeyPathInputs.tsx +++ b/src/ui/FRWComponent/KeyPathInputs.tsx @@ -1,8 +1,5 @@ -import { useEffect, useState, useContext } from 'react'; -import React from 'react'; import { Box, - Button, Typography, CardMedia, TextareaAutosize, @@ -10,13 +7,14 @@ import { AccordionSummary, AccordionDetails, } from '@mui/material'; +import { styled } from '@mui/material/styles'; import { makeStyles } from '@mui/styles'; -import { LLSpinner } from 'ui/FRWComponent'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import React, { useEffect, useState } from 'react'; + +import { storage } from 'background/webapi'; + import Expand from '../FRWAssets/svg/expand.svg'; import Hide from '../FRWAssets/svg/hide.svg'; -import { styled } from '@mui/material/styles'; -import { storage } from 'background/webapi'; const useStyles = makeStyles((theme) => ({ form: { diff --git a/src/ui/FRWComponent/LLComingSoonWarning.tsx b/src/ui/FRWComponent/LLComingSoonWarning.tsx index 7c8df38e..79a5b76a 100644 --- a/src/ui/FRWComponent/LLComingSoonWarning.tsx +++ b/src/ui/FRWComponent/LLComingSoonWarning.tsx @@ -1,6 +1,6 @@ -import React from 'react'; -import { IconButton, Alert, Collapse } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; +import { IconButton, Alert, Collapse } from '@mui/material'; +import React from 'react'; interface ComingSoonProps { alertOpen: boolean; diff --git a/src/ui/FRWComponent/LLConnectLoading.tsx b/src/ui/FRWComponent/LLConnectLoading.tsx index 443f4458..bcabcf98 100644 --- a/src/ui/FRWComponent/LLConnectLoading.tsx +++ b/src/ui/FRWComponent/LLConnectLoading.tsx @@ -1,7 +1,6 @@ -import React, { useState, useEffect } from 'react'; - import { Box, CardMedia, Typography } from '@mui/material'; import Avatar from '@mui/material/Avatar'; +import React, { useState, useEffect } from 'react'; export const LLConnectLoading = ({ logo }) => { const [count, setCount] = useState(0); diff --git a/src/ui/FRWComponent/LLContactCard.tsx b/src/ui/FRWComponent/LLContactCard.tsx index 2037af7b..f33b43d2 100644 --- a/src/ui/FRWComponent/LLContactCard.tsx +++ b/src/ui/FRWComponent/LLContactCard.tsx @@ -1,13 +1,12 @@ -import React, { useState } from 'react'; -import { Box, Typography, Avatar, IconButton, CardMedia, Skeleton } from '@mui/material'; -import { useWallet, formatAddress, isEmoji } from 'ui/utils'; import PersonAddAltIcon from '@mui/icons-material/PersonAddAlt'; -import { ThemeProvider } from '@mui/material/styles'; -import theme from '../style/LLTheme'; +import { Box, Typography, Avatar, IconButton, CardMedia, Skeleton } from '@mui/material'; import { makeStyles } from '@mui/styles'; -import closex from 'ui/assets/closex.svg'; +import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; +import closex from 'ui/assets/closex.svg'; +import { useWallet, formatAddress, isEmoji } from 'ui/utils'; + const useStyles = makeStyles(() => ({ ContactCardAvatar: { mr: '13px', @@ -70,7 +69,7 @@ export const LLContactCard = ({ contact, hideCloseButton, isSend = false, isLoad }; return ( - + <> )} - + ); }; diff --git a/src/ui/FRWComponent/LLContactEth.tsx b/src/ui/FRWComponent/LLContactEth.tsx index 16fc45db..b032b43a 100644 --- a/src/ui/FRWComponent/LLContactEth.tsx +++ b/src/ui/FRWComponent/LLContactEth.tsx @@ -1,13 +1,12 @@ -import React, { useState } from 'react'; -import { Box, Typography, Avatar, IconButton, CardMedia, Skeleton } from '@mui/material'; -import { useWallet, formatAddress } from 'ui/utils'; import PersonAddAltIcon from '@mui/icons-material/PersonAddAlt'; -import { ThemeProvider } from '@mui/material/styles'; -import theme from '../style/LLTheme'; +import { Box, Typography, Avatar, IconButton, CardMedia, Skeleton } from '@mui/material'; import { makeStyles } from '@mui/styles'; -import closex from 'ui/assets/closex.svg'; +import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; +import closex from 'ui/assets/closex.svg'; +import { useWallet, formatAddress } from 'ui/utils'; + const useStyles = makeStyles(() => ({ ContactCardAvatar: { mr: '13px', @@ -73,7 +72,7 @@ export const LLContactEth = ({ contact, hideCloseButton, isSend = false, isLoadi }; return ( - + <> )} - + ); }; diff --git a/src/ui/FRWComponent/LLDeleteBackupPopup.tsx b/src/ui/FRWComponent/LLDeleteBackupPopup.tsx index de2ebd18..7f868bf2 100644 --- a/src/ui/FRWComponent/LLDeleteBackupPopup.tsx +++ b/src/ui/FRWComponent/LLDeleteBackupPopup.tsx @@ -1,6 +1,8 @@ -import React from 'react'; import { Typography, Box, Drawer, Stack } from '@mui/material'; +import React from 'react'; + import { LLPrimaryButton, LLSecondaryButton } from 'ui/FRWComponent'; + import IconSubtract from '../../components/iconfont/IconSubtract'; interface DeleteBackupProps { diff --git a/src/ui/FRWComponent/LLFlownsPop.tsx b/src/ui/FRWComponent/LLFlownsPop.tsx index 6435bca8..3c2ad0d5 100644 --- a/src/ui/FRWComponent/LLFlownsPop.tsx +++ b/src/ui/FRWComponent/LLFlownsPop.tsx @@ -1,11 +1,12 @@ -import React, { useState, useEffect } from 'react'; -import { Typography, Box, Stack, CardMedia, IconButton } from '@mui/material'; -import { LLPrimaryButton, LLSecondaryButton } from 'ui/FRWComponent'; -import IconSubtract from '../../components/iconfont/IconSubtract'; +import CloseIcon from '@mui/icons-material/Close'; +import { Typography, Box, IconButton } from '@mui/material'; import { makeStyles } from '@mui/styles'; +import React, { useState, useEffect, useCallback } from 'react'; + +import { LLPrimaryButton } from 'ui/FRWComponent'; import { useWallet } from 'ui/utils'; + import lilicoIcon from '../../../_raw/images/icon-256.png'; -import CloseIcon from '@mui/icons-material/Close'; interface DeleteBackupProps { deleteBackupPop: boolean; @@ -58,14 +59,14 @@ export const LLFlownsPop = (props: DeleteBackupProps) => { setExpand(true); }; - const getUsername = async () => { + const getUsername = useCallback(async () => { const userInfo = await wallet.getUserInfo(false); setUsername(userInfo.username); - }; + }, [wallet]); useEffect(() => { getUsername(); - }, []); + }, [getUsername]); const renderContent = () => ( { }; return ( - - - {inputValue && ( - - - {genHelperText(isValidating, isValid, errorMsg, successMsg)} - - - )} - - + <> + + + {genHelperText(isValidating, isValid, errorMsg, successMsg)} + + + ); }; diff --git a/src/ui/FRWComponent/LLHeader.tsx b/src/ui/FRWComponent/LLHeader.tsx index f407a4e6..dad9db3c 100644 --- a/src/ui/FRWComponent/LLHeader.tsx +++ b/src/ui/FRWComponent/LLHeader.tsx @@ -1,8 +1,8 @@ -import React from 'react'; -import { Grid, IconButton, Typography, Tooltip } from '@mui/material'; -import HelpOutlineRoundedIcon from '@mui/icons-material/HelpOutlineRounded'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import HelpOutlineRoundedIcon from '@mui/icons-material/HelpOutlineRounded'; +import { Grid, IconButton, Typography, Tooltip } from '@mui/material'; import { styled } from '@mui/material/styles'; +import React from 'react'; import { useHistory } from 'react-router-dom'; interface LLHeaderProps { diff --git a/src/ui/FRWComponent/LLLinkingLoading.tsx b/src/ui/FRWComponent/LLLinkingLoading.tsx index 117b0dc1..02aaba6b 100644 --- a/src/ui/FRWComponent/LLLinkingLoading.tsx +++ b/src/ui/FRWComponent/LLLinkingLoading.tsx @@ -1,10 +1,11 @@ -import React, { useState, useEffect } from 'react'; -import { useHistory } from 'react-router-dom'; -import { makeStyles } from '@mui/styles'; import { Box, CardMedia, Typography } from '@mui/material'; import Avatar from '@mui/material/Avatar'; -import { LLPrimaryButton } from 'ui/FRWComponent'; +import { makeStyles } from '@mui/styles'; +import React, { useState, useEffect } from 'react'; +import { useHistory } from 'react-router-dom'; + import IconCheck from 'ui/assets/check.svg'; +import { LLPrimaryButton } from 'ui/FRWComponent'; const useStyles = makeStyles({ IconCheck: { diff --git a/src/ui/FRWComponent/LLNotFound.tsx b/src/ui/FRWComponent/LLNotFound.tsx index aabfd9c2..af57046f 100644 --- a/src/ui/FRWComponent/LLNotFound.tsx +++ b/src/ui/FRWComponent/LLNotFound.tsx @@ -1,16 +1,14 @@ +import { Box, Button, Typography, CardMedia, Stack } from '@mui/material'; import React from 'react'; -import { Box, Button, Typography, CardMedia, Stack, CssBaseline } from '@mui/material'; -import { ThemeProvider } from '@mui/material/styles'; -import theme from '../style/LLTheme'; -import NotFoundIcon from 'ui/FRWAssets/svg/notfound.svg'; import { useHistory } from 'react-router-dom'; +import NotFoundIcon from 'ui/FRWAssets/svg/notfound.svg'; + export const LLNotFound = ({ setShowDialog }) => { const history = useHistory(); return ( - - + <> { - + ); }; diff --git a/src/ui/FRWComponent/LLPinAlert.tsx b/src/ui/FRWComponent/LLPinAlert.tsx index 2017ef2c..0280f050 100644 --- a/src/ui/FRWComponent/LLPinAlert.tsx +++ b/src/ui/FRWComponent/LLPinAlert.tsx @@ -1,21 +1,15 @@ -import React from 'react'; -import { Box, Typography } from '@mui/material'; -import { Snackbar, SnackbarContent, Slide } from '@mui/material'; -import PushPinOutlinedIcon from '@mui/icons-material/PushPinOutlined'; import ExtensionRoundedIcon from '@mui/icons-material/ExtensionRounded'; +import PushPinOutlinedIcon from '@mui/icons-material/PushPinOutlined'; +import { Snackbar, SnackbarContent, Box, Typography } from '@mui/material'; +import React from 'react'; + import lilicoIcon from '../../../_raw/images/icon-128.png'; -const slideTransition = (props) => { - return ; -}; +import SlideLeftRight from './SlideLeftRight'; export const LLPinAlert = ({ open }) => { return ( - + { @@ -18,7 +17,7 @@ export const LLProfile = ({ contact, isLoading = false }) => { }; return ( - + <> { )} - + ); }; diff --git a/src/ui/FRWComponent/LLResetPopup.tsx b/src/ui/FRWComponent/LLResetPopup.tsx index 72be1fe2..5b766985 100644 --- a/src/ui/FRWComponent/LLResetPopup.tsx +++ b/src/ui/FRWComponent/LLResetPopup.tsx @@ -1,8 +1,10 @@ +import { Typography, Box, Drawer, Stack } from '@mui/material'; import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; -import { Typography, Box, Drawer, Stack } from '@mui/material'; + import { LLPrimaryButton, LLSecondaryButton, LLWarningButton } from 'ui/FRWComponent'; import { useWallet } from 'ui/utils'; + import IconSubtract from '../../components/iconfont/IconSubtract'; interface AddOrEditAddressProps { diff --git a/src/ui/FRWComponent/LLSecondaryButton.tsx b/src/ui/FRWComponent/LLSecondaryButton.tsx index 2037dfe8..868a1263 100644 --- a/src/ui/FRWComponent/LLSecondaryButton.tsx +++ b/src/ui/FRWComponent/LLSecondaryButton.tsx @@ -1,6 +1,6 @@ -import React from 'react'; -import Button, { ButtonProps } from '@mui/material/Button'; +import Button, { type ButtonProps } from '@mui/material/Button'; import { styled } from '@mui/material/styles'; +import React from 'react'; interface LLSecondaryButtonProps extends ButtonProps { label: string; diff --git a/src/ui/FRWComponent/LLSpinner.tsx b/src/ui/FRWComponent/LLSpinner.tsx index 84c7fe09..73768d99 100644 --- a/src/ui/FRWComponent/LLSpinner.tsx +++ b/src/ui/FRWComponent/LLSpinner.tsx @@ -1,9 +1,9 @@ -import React from 'react'; import Box from '@mui/material/Box'; import CircularProgress, { circularProgressClasses, - CircularProgressProps, + type CircularProgressProps, } from '@mui/material/CircularProgress'; +import React from 'react'; // Inspired by the former Facebook spinners. function LLCircularProgress(props: CircularProgressProps) { diff --git a/src/ui/FRWComponent/LLSwap.tsx b/src/ui/FRWComponent/LLSwap.tsx index 9550db1f..47323664 100644 --- a/src/ui/FRWComponent/LLSwap.tsx +++ b/src/ui/FRWComponent/LLSwap.tsx @@ -1,12 +1,10 @@ -import React from 'react'; import { Box, Typography, Avatar, Skeleton } from '@mui/material'; -import { ThemeProvider } from '@mui/material/styles'; -import theme from '../style/LLTheme'; import { makeStyles } from '@mui/styles'; +import React from 'react'; export const LLSwap = ({ token, amount, isLoading = false }) => { return ( - + <> { )} - + ); }; diff --git a/src/ui/FRWComponent/LLWarningButton.tsx b/src/ui/FRWComponent/LLWarningButton.tsx index c9787320..e01e8df2 100644 --- a/src/ui/FRWComponent/LLWarningButton.tsx +++ b/src/ui/FRWComponent/LLWarningButton.tsx @@ -1,6 +1,6 @@ -import React from 'react'; -import Button, { ButtonProps } from '@mui/material/Button'; +import Button, { type ButtonProps } from '@mui/material/Button'; import { styled } from '@mui/material/styles'; +import React from 'react'; interface LLWarningButtonProps extends ButtonProps { label: string | JSX.Element; diff --git a/src/ui/FRWComponent/NumberTransition.tsx b/src/ui/FRWComponent/NumberTransition.tsx new file mode 100644 index 00000000..9fb59734 --- /dev/null +++ b/src/ui/FRWComponent/NumberTransition.tsx @@ -0,0 +1,51 @@ +import { Box } from '@mui/material'; +import { keyframes } from '@mui/system'; +import React, { useEffect, useState } from 'react'; + +const slideDown = keyframes` + from { + opacity: 0; + transform: translateY(-1em); + } + to { + opacity: 1; + transform: translateY(0); + } +`; + +interface NumberTransitionProps { + number: string; + delay?: number; +} + +export const NumberTransition = ({ number, delay = 0 }: NumberTransitionProps) => { + const [show, setShow] = useState(false); + + useEffect(() => { + const timer = setTimeout(() => { + setShow(true); + }, delay); + + return () => clearTimeout(timer); + }, [delay, number]); + + return ( + span': { + display: 'inline-block', + visibility: show ? 'visible' : 'hidden', + animation: show ? `${slideDown} 300ms ease forwards` : 'none', + animationDelay: `${delay}ms`, + }, + }} + > + {number} + + ); +}; diff --git a/src/ui/FRWComponent/PopupModal/errorModel.tsx b/src/ui/FRWComponent/PopupModal/errorModel.tsx index 184459d9..a8ad324f 100644 --- a/src/ui/FRWComponent/PopupModal/errorModel.tsx +++ b/src/ui/FRWComponent/PopupModal/errorModel.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Button, Dialog, @@ -10,7 +9,9 @@ import { Select, Typography, } from '@mui/material'; +import React from 'react'; import { useHistory } from 'react-router-dom'; + import { CustomDialog } from './importAddressModal'; const ErrorModel = ({ isOpen, onOpenChange, errorName, errorMessage, isGoback = false }) => { diff --git a/src/ui/FRWComponent/PopupModal/importAddressModal.tsx b/src/ui/FRWComponent/PopupModal/importAddressModal.tsx index 72ad4ef7..cdc37dc9 100644 --- a/src/ui/FRWComponent/PopupModal/importAddressModal.tsx +++ b/src/ui/FRWComponent/PopupModal/importAddressModal.tsx @@ -1,6 +1,3 @@ -import React from 'react'; -import { styled } from '@mui/material/styles'; - import { Box, Typography, @@ -13,6 +10,8 @@ import { FormControl, Select, } from '@mui/material'; +import { styled } from '@mui/material/styles'; +import React from 'react'; export const CustomDialog = styled(Dialog)({ '& .MuiPaper-root': { diff --git a/src/ui/FRWComponent/PopupModal/resetModal.tsx b/src/ui/FRWComponent/PopupModal/resetModal.tsx index 45e4ae73..268a757a 100644 --- a/src/ui/FRWComponent/PopupModal/resetModal.tsx +++ b/src/ui/FRWComponent/PopupModal/resetModal.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Button, Dialog, @@ -10,7 +9,9 @@ import { Select, Typography, } from '@mui/material'; +import React from 'react'; import { useHistory } from 'react-router-dom'; + import { CustomDialog } from './importAddressModal'; const ResetModal = ({ diff --git a/src/ui/FRWComponent/SlideDown.tsx b/src/ui/FRWComponent/SlideDown.tsx new file mode 100644 index 00000000..b24f5cb1 --- /dev/null +++ b/src/ui/FRWComponent/SlideDown.tsx @@ -0,0 +1,23 @@ +import { Box, Slide } from '@mui/material'; +import React, { useRef } from 'react'; + +const SlideDown = ({ + show, + children, +}: { + show: boolean; + children: React.ReactElement; + container?: HTMLElement; +}) => { + const containerRef = useRef(null); + return ( + <> + + + {children} + + + ); +}; + +export default SlideDown; diff --git a/src/ui/FRWComponent/SlideLeftRight.tsx b/src/ui/FRWComponent/SlideLeftRight.tsx new file mode 100644 index 00000000..ee2b54f1 --- /dev/null +++ b/src/ui/FRWComponent/SlideLeftRight.tsx @@ -0,0 +1,26 @@ +import { Box, Slide } from '@mui/material'; +import React from 'react'; + +const SlideLeftRight = React.forwardRef( + ( + { + show, + direction = 'left', + children, + }: { + show: boolean; + direction: 'left' | 'right'; + children: React.ReactElement; + }, + ref + ) => { + const showIt = !!show; + if (showIt) { + return children; + } else { + return null; + } + } +); + +export default SlideLeftRight; diff --git a/src/ui/FRWComponent/SlideRelative.tsx b/src/ui/FRWComponent/SlideRelative.tsx new file mode 100644 index 00000000..b097b306 --- /dev/null +++ b/src/ui/FRWComponent/SlideRelative.tsx @@ -0,0 +1,31 @@ +import { Box, Slide } from '@mui/material'; +import React, { useRef } from 'react'; + +const SlideRelative = React.forwardRef( + ( + { + show, + direction, + children, + }: { show: boolean; direction: 'up' | 'down' | 'left' | 'right'; children: React.ReactElement }, + ref + ) => { + const containerRef = useRef(null); + return ( + <> + + + {children} + + + ); + } +); + +export default SlideRelative; diff --git a/src/ui/FRWComponent/StorageExceededAlert.tsx b/src/ui/FRWComponent/StorageExceededAlert.tsx index 80b6f10b..e9a60c95 100644 --- a/src/ui/FRWComponent/StorageExceededAlert.tsx +++ b/src/ui/FRWComponent/StorageExceededAlert.tsx @@ -3,6 +3,8 @@ import { Box, Typography, Button, IconButton } from '@mui/material'; import React from 'react'; import { useHistory } from 'react-router-dom'; +import eventBus from '@/eventBus'; + interface StorageExceededAlertProps { open: boolean; onClose: () => void; @@ -13,7 +15,7 @@ const StorageExceededAlert: React.FC = ({ open, onClo const handleBuyFlow = () => { onClose(); - history.push('/dashboard/wallet/buy'); + eventBus.emit('openOnRamp'); }; if (!open) return null; @@ -116,7 +118,7 @@ const StorageExceededAlert: React.FC = ({ open, onClo color="inherit" onClick={() => { onClose(); - history.push('/deposit'); + history.push('/dashboard/wallet/deposit'); }} sx={{ height: '48px', diff --git a/src/ui/FRWComponent/WarningSnackbar.tsx b/src/ui/FRWComponent/WarningSnackbar.tsx index 3a46a6a9..7ffc3d25 100644 --- a/src/ui/FRWComponent/WarningSnackbar.tsx +++ b/src/ui/FRWComponent/WarningSnackbar.tsx @@ -17,7 +17,15 @@ export default function WarningSnackbar({ message, }: WarningSnackbarProps) { return ( - + } variant="filled" @@ -30,6 +38,7 @@ export default function WarningSnackbar({ borderRadius: '24px', margin: '0 auto 80px', zIndex: '2000', + pointerEvents: 'auto', // Make the alert itself clickable }} > {message} diff --git a/src/ui/FRWComponent/WarningStorageLowSnackbar.tsx b/src/ui/FRWComponent/WarningStorageLowSnackbar.tsx index dca9ca17..eb7bc96c 100644 --- a/src/ui/FRWComponent/WarningStorageLowSnackbar.tsx +++ b/src/ui/FRWComponent/WarningStorageLowSnackbar.tsx @@ -22,11 +22,6 @@ export const WarningStorageLowSnackbar = ({ return null; } return ( - {}} - alertIcon={warningIcon} - message={chrome.i18n.getMessage('Insufficient_storage')} - /> + {}} alertIcon={warningIcon} message={message} /> ); }; diff --git a/src/ui/index.html b/src/ui/index.html index 0457e908..583cff87 100644 --- a/src/ui/index.html +++ b/src/ui/index.html @@ -124,6 +124,9 @@ margin: 0px; } + <% if (devMode && hasDevTools) { %> + + <% } %> diff --git a/src/ui/index.tsx b/src/ui/index.tsx index 84678200..38a41aac 100644 --- a/src/ui/index.tsx +++ b/src/ui/index.tsx @@ -1,10 +1,12 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Views from './views'; -import { Message } from '@/utils'; -import { getUITypeName } from 'ui/utils'; +import React, { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; + import eventBus from '@/eventBus'; +import { Message } from '@/utils'; import { EVENTS } from 'consts'; +import { getUITypeName } from 'ui/utils'; + +import Views from './views'; // import './style/index.less'; function initAppMeta() { @@ -54,7 +56,6 @@ const wallet: Record = new Proxy( }, } ); - break; default: return function (...params: any) { chrome.runtime.sendMessage( @@ -63,7 +64,7 @@ const wallet: Record = new Proxy( method: key, params, }, - function (response) { + function (_response) { // console.log('portMessageChannel 3 ->', response); } ); @@ -97,4 +98,10 @@ eventBus.addEventListener(EVENTS.broadcastToBackground, (data) => { }); }); -ReactDOM.render(, document.getElementById('root')); +const container = document.getElementById('root'); +const root = createRoot(container!); // createRoot(container!) if you use TypeScript +root.render( + + + +); diff --git a/src/ui/popup.html b/src/ui/popup.html index 3a1df1a3..b260a156 100644 --- a/src/ui/popup.html +++ b/src/ui/popup.html @@ -133,6 +133,9 @@ } + <% if (devMode && hasDevTools) { %> + + <% } %> diff --git a/src/ui/style/LLTheme.ts b/src/ui/style/LLTheme.ts index 394c6aaa..59c5034d 100644 --- a/src/ui/style/LLTheme.ts +++ b/src/ui/style/LLTheme.ts @@ -1,8 +1,7 @@ -import { createTheme } from '@mui/material/styles'; -import { Theme as SystemTheme } from '@mui/system'; +import { createTheme, type ThemeOptions } from '@mui/material/styles'; import './fonts.css'; -const theme: SystemTheme = createTheme({ +const themeOptions: ThemeOptions = { components: { MuiCssBaseline: { styleOverrides: { @@ -157,7 +156,7 @@ const theme: SystemTheme = createTheme({ fontWeight: 600, }, }, -}); +}; declare module '@mui/material/Button' { interface ButtonPropsColorOverrides { @@ -165,4 +164,4 @@ declare module '@mui/material/Button' { } } -export default theme; +export default themeOptions; diff --git a/src/ui/utils/hooks.ts b/src/ui/utils/hooks.ts index a5705f7c..ea11c059 100644 --- a/src/ui/utils/hooks.ts +++ b/src/ui/utils/hooks.ts @@ -1,6 +1,8 @@ -import { useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { useHistory } from 'react-router-dom'; + import { useWallet } from './WalletContext'; + import { getUiType } from './index'; export const useApproval = () => { @@ -10,44 +12,53 @@ export const useApproval = () => { // console.log('useApproval 2') - const getApproval = wallet.getApproval; + const getApproval = useCallback(() => wallet.getApproval(), [wallet]); - const linkningConfirm = async (data?: any, stay = false, forceReject = false) => { - const approval = await getApproval(); + const linkingConfirm = useCallback( + async (data?: any, stay = false, forceReject = false) => { + const approval = await getApproval(); - if (approval) { - await wallet.resolveApproval(data, forceReject); - return; - } - if (stay) { - return; - } - // setTimeout(() => { - // history.replace('/'); - // }); - }; + if (approval) { + await wallet.resolveApproval(data, forceReject); + return; + } + if (stay) { + return; + } + // setTimeout(() => { + // history.replace('/'); + // }); + }, + [getApproval, wallet] + ); - const resolveApproval = async (data?: any, stay = false, forceReject = false) => { - const approval = await getApproval(); + const resolveApproval = useCallback( + async (data?: any, stay = false, forceReject = false) => { + const approval = await getApproval(); - if (approval) { - wallet.resolveApproval(data, forceReject); - } - if (stay) { - return; - } - setTimeout(() => { - history.replace('/'); - }); - }; - - const rejectApproval = async (err?) => { - const approval = await getApproval(); - if (approval) { - await wallet.rejectApproval(err); - } - history.push('/'); - }; + if (approval) { + wallet.resolveApproval(data, forceReject); + } + if (stay) { + return; + } + setTimeout(() => { + history.replace('/'); + }); + }, + [getApproval, wallet, history] + ); + + const rejectApproval = useCallback( + async (err?) => { + const approval = await getApproval(); + if (approval) { + await wallet.rejectApproval(err); + } + history.push('/'); + }, + [getApproval, wallet, history] + ); useEffect(() => { console.log('useApproval', getUiType(), getUiType().isNotification); @@ -58,9 +69,9 @@ export const useApproval = () => { window.addEventListener('beforeunload', rejectApproval); return () => window.removeEventListener('beforeunload', rejectApproval); - }, []); + }, [rejectApproval]); - return [getApproval, resolveApproval, rejectApproval, linkningConfirm] as const; + return [getApproval, resolveApproval, rejectApproval, linkingConfirm] as const; }; export const useSelectOption = ({ @@ -75,7 +86,7 @@ export const useSelectOption = ({ value?: T[]; }) => { const isControlled = useRef(typeof value !== 'undefined').current; - const [idxs, setChoosedIdxs] = useState( + const [chosenIndexes, setChosenIndexes] = useState( (isControlled ? value! : defaultValue).map((x) => options.indexOf(x)) ); @@ -85,50 +96,64 @@ export const useSelectOption = ({ } // shallow compare - if (value && idxs.some((x, i) => options[x] != value[i])) { - setChoosedIdxs(value.map((x) => options.indexOf(x))); + if (value) { + setChosenIndexes(value.map((x) => options.indexOf(x))); } - }, [value]); + }, [value, options, isControlled]); - const changeValue = (idxs: number[]) => { - setChoosedIdxs([...idxs]); - onChange && onChange(idxs.map((o) => options[o])); - }; + const changeValue = useCallback( + (indexes: number[]) => { + setChosenIndexes([...indexes]); + if (onChange) { + onChange(indexes.map((o) => options[o])); + } + }, + [options, onChange] + ); - const handleRemove = (i: number) => { - idxs.splice(i, 1); - changeValue(idxs); - }; + const handleRemove = useCallback( + (i: number) => { + chosenIndexes.splice(i, 1); + changeValue(chosenIndexes); + }, + [chosenIndexes, changeValue] + ); - const handleChoose = (i: number) => { - if (idxs.includes(i)) { - return; - } + const handleChoose = useCallback( + (i: number) => { + if (chosenIndexes.includes(i)) { + return; + } - idxs.push(i); - changeValue(idxs); - }; + chosenIndexes.push(i); + changeValue(chosenIndexes); + }, + [chosenIndexes, changeValue] + ); - const handleToggle = (i: number) => { - const inIdxs = idxs.indexOf(i); - if (inIdxs !== -1) { - handleRemove(inIdxs); - } else { - handleChoose(i); - } - }; + const handleToggle = useCallback( + (i: number) => { + const inIdxs = chosenIndexes.indexOf(i); + if (inIdxs !== -1) { + handleRemove(inIdxs); + } else { + handleChoose(i); + } + }, + [chosenIndexes, handleRemove, handleChoose] + ); - const handleClear = () => { + const handleClear = useCallback(() => { changeValue([]); - }; + }, [changeValue]); return [ - idxs.map((o) => options[o]), + chosenIndexes.map((o) => options[o]), handleRemove, handleChoose, handleToggle, handleClear, - idxs, + chosenIndexes, ] as const; }; @@ -154,27 +179,34 @@ export const useWalletRequest = ( const [res, setRes] = useState(); const [err, setErr] = useState(); - const run = async (...args) => { - setLoading(true); - try { - const _res = await Promise.resolve(requestFn(...args)); - if (!mounted.current) { - return; - } - setRes(_res); - onSuccess && onSuccess(_res); - } catch (err) { - if (!mounted.current) { - return; - } - setErr(err); - onError && onError(err); - } finally { - if (mounted.current) { - setLoading(false); + const run = useCallback( + async (...args) => { + setLoading(true); + try { + const _res = await Promise.resolve(requestFn(...args)); + if (!mounted.current) { + return; + } + setRes(_res); + if (onSuccess) { + onSuccess(_res); + } + } catch (err) { + if (!mounted.current) { + return; + } + setErr(err); + if (onError) { + onError(err); + } + } finally { + if (mounted.current) { + setLoading(false); + } } - } - }; + }, + [requestFn, mounted, onSuccess, onError] + ); return [run, loading, res, err] as const; }; @@ -190,19 +222,24 @@ export const useHover = ({ mouseEnterDelayMS = 0, mouseLeaveDelayMS = 0 }: UseHo HoverProps, ] => { const [isHovering, setIsHovering] = useState(false); - let mouseEnterTimer: number | undefined; - let mouseOutTimer: number | undefined; + const mouseEnterTimer = useRef(); + const mouseOutTimer = useRef(); + + const onMouseEnter = useCallback(() => { + clearTimeout(mouseOutTimer.current); + mouseEnterTimer.current = window.setTimeout(() => setIsHovering(true), mouseEnterDelayMS); + }, [mouseEnterDelayMS, mouseOutTimer]); + + const onMouseLeave = useCallback(() => { + clearTimeout(mouseEnterTimer.current); + mouseOutTimer.current = window.setTimeout(() => setIsHovering(false), mouseLeaveDelayMS); + }, [mouseLeaveDelayMS, mouseEnterTimer]); + return [ isHovering, { - onMouseEnter: () => { - clearTimeout(mouseOutTimer); - mouseEnterTimer = window.setTimeout(() => setIsHovering(true), mouseEnterDelayMS); - }, - onMouseLeave: () => { - clearTimeout(mouseEnterTimer); - mouseOutTimer = window.setTimeout(() => setIsHovering(false), mouseLeaveDelayMS); - }, + onMouseEnter, + onMouseLeave, }, ]; }; diff --git a/src/ui/utils/index.ts b/src/ui/utils/index.ts index 8bcf85ca..8aa3e560 100644 --- a/src/ui/utils/index.ts +++ b/src/ui/utils/index.ts @@ -13,8 +13,6 @@ export * from './time'; export * from './number'; -export * from './options'; - export * from './saveStorage'; export * from './mixpanelBrowserService'; diff --git a/src/ui/utils/number.ts b/src/ui/utils/number.ts index de30d011..b3c00d74 100644 --- a/src/ui/utils/number.ts +++ b/src/ui/utils/number.ts @@ -50,14 +50,12 @@ export const formatLargeNumber = (num) => { export const addDotSeparators = (num) => { // replace with http://numeraljs.com/ if more requirements const [integerPart, decimalPart] = parseFloat(num).toFixed(8).split('.'); - const newIntegerPart = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ','); - let result = `${newIntegerPart}.${decimalPart}`; + // Format the integer part with comma separators + const newIntegerPart = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ','); - // Check if the total length of the result exceeds 13 characters - if (result.length > 13) { - result = result.slice(0, 13) + '...'; - } + const trimmedDecimal = decimalPart.replace(/0+$/, ''); + const formattedDecimal = trimmedDecimal.length > 0 ? trimmedDecimal : decimalPart.slice(-3); - return result; + return `${newIntegerPart}.${formattedDecimal}`; }; diff --git a/src/ui/utils/options.ts b/src/ui/utils/options.ts deleted file mode 100644 index 322fa2d1..00000000 --- a/src/ui/utils/options.ts +++ /dev/null @@ -1,125 +0,0 @@ -const Options = { - fullScreen: true, - fpsLimit: 120, - detectRetina: true, - emitters: { - direction: 'bottom', - startCount: 0, - position: { x: 50, y: 0 }, - size: { - width: 20, - height: 0, - }, - rate: { - delay: 0, - quantity: 2, - }, - life: { - count: 200, - duration: 0.01, - // delay:0.6, - }, - }, - particles: { - number: { - value: 250, - }, - color: { - value: ['#9146FF', '#FFAAA8', '#8FFFD2', '#FFD37A', '#FF38DB'], - }, - shape: { - type: ['square', 'circle', 'heart'], - }, - opacity: { - value: 1, - animation: { - enable: true, - minimumValue: 0, - speed: 0.5, - startValue: 'max', - destroy: 'min', - }, - }, - size: { - value: 5, - }, - links: { - enable: false, - }, - life: { - duration: { - sync: true, - value: 10, - }, - count: 1, - }, - move: { - angle: { - value: 45, - offset: 0, - }, - drift: { - min: -0, - max: 0, - }, - enable: true, - gravity: { - enable: true, - acceleration: 20, - }, - speed: 90, - decay: 1 - 0.9, - direction: -90, - random: true, - straight: false, - outModes: { - default: 'none', - bottom: 'destroy', - }, - }, - rotate: { - value: { - min: 0, - max: 360, - }, - direction: 'random', - animation: { - enable: true, - speed: 60, - }, - }, - tilt: { - direction: 'random', - enable: true, - value: { - min: 0, - max: 360, - }, - animation: { - enable: true, - speed: 60, - }, - }, - roll: { - darken: { - enable: true, - value: 25, - }, - enable: true, - speed: { - min: 15, - max: 25, - }, - }, - wobble: { - distance: 20, - enable: true, - speed: { - min: -15, - max: 15, - }, - }, - }, -}; - -export { Options }; diff --git a/src/ui/utils/useStorageCheck.ts b/src/ui/utils/useStorageCheck.ts index 348c5acd..4254bcfd 100644 --- a/src/ui/utils/useStorageCheck.ts +++ b/src/ui/utils/useStorageCheck.ts @@ -56,6 +56,8 @@ export const useStorageCheck = ({ // Initial storage check useEffect(() => { + // Add this to track when the effect is actually running + let mounted = true; if (wallet) { checkStorageStatus().then( diff --git a/src/ui/views/AddWelcome/AddRegister/AllSet.tsx b/src/ui/views/AddWelcome/AddRegister/AllSet.tsx index cb2f5d6b..b4bea0e9 100644 --- a/src/ui/views/AddWelcome/AddRegister/AllSet.tsx +++ b/src/ui/views/AddWelcome/AddRegister/AllSet.tsx @@ -1,9 +1,9 @@ +import { Button, Typography, CardMedia } from '@mui/material'; +import { Box } from '@mui/system'; import React, { useEffect } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, CssBaseline, CardMedia } from '@mui/material'; -import theme from '../../../style/LLTheme'; -import AllSetIcon from 'ui/FRWAssets/svg/allset.svg'; + import { storage } from 'background/webapi'; +import AllSetIcon from 'ui/FRWAssets/svg/allset.svg'; const AllSet = ({ handleClick }) => { const removeTempPass = () => { @@ -14,8 +14,7 @@ const AllSet = ({ handleClick }) => { removeTempPass(); }, []); return ( - - + <> { - + ); }; diff --git a/src/ui/views/AddWelcome/AddRegister/GoogleBackup.tsx b/src/ui/views/AddWelcome/AddRegister/GoogleBackup.tsx index e38e70a9..135de36c 100644 --- a/src/ui/views/AddWelcome/AddRegister/GoogleBackup.tsx +++ b/src/ui/views/AddWelcome/AddRegister/GoogleBackup.tsx @@ -1,12 +1,13 @@ +import InfoIcon from '@mui/icons-material/Info'; +import { Button, Typography } from '@mui/material'; +import { Box } from '@mui/system'; import React, { useState } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, CssBaseline } from '@mui/material'; -import theme from '../../../style/LLTheme'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { LLSpinner } from 'ui/FRWComponent'; import { useWallet } from 'ui/utils'; + import IconGoogleDrive from '../../../../components/iconfont/IconGoogleDrive'; -import { LLSpinner } from 'ui/FRWComponent'; -import InfoIcon from '@mui/icons-material/Info'; -import { Presets } from 'react-component-transition'; const GoogleBackup = ({ handleClick, mnemonic, username, password }) => { const wallets = useWallet(); @@ -33,8 +34,7 @@ const GoogleBackup = ({ handleClick, mnemonic, username, password }) => { } }; return ( - - + <> {chrome.i18n.getMessage('Create')} @@ -94,35 +94,29 @@ const GoogleBackup = ({ handleClick, mnemonic, username, password }) => { - {backupErr && ( - - - {/* */} - - - {chrome.i18n.getMessage( - 'Backup_failed_you_may_still_conduct_backup_inside_extension' - )} - - - - )} + + + {/* */} + + + {chrome.i18n.getMessage( + 'Backup_failed_you_may_still_conduct_backup_inside_extension' + )} + + + - + ); }; diff --git a/src/ui/views/AddWelcome/AddRegister/PickUsername.tsx b/src/ui/views/AddWelcome/AddRegister/PickUsername.tsx index 05e057c5..08d7049d 100644 --- a/src/ui/views/AddWelcome/AddRegister/PickUsername.tsx +++ b/src/ui/views/AddWelcome/AddRegister/PickUsername.tsx @@ -1,13 +1,21 @@ -import React, { useEffect, useState } from 'react'; +import { + CircularProgress, + IconButton, + Button, + Typography, + FormControl, + Input, + InputAdornment, +} from '@mui/material'; import { makeStyles } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, FormControl, Input, InputAdornment, CssBaseline } from '@mui/material'; -import CancelIcon from '../../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; -import theme from '../../../style/LLTheme'; -import { Presets } from 'react-component-transition'; +import { Box } from '@mui/system'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; import { useWallet } from 'ui/utils'; -import { CircularProgress, IconButton } from '@mui/material'; + +import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../../components/iconfont/IconClose'; import EmailIcon from '../../../assets/alternate-email.svg'; const useStyles = makeStyles((theme) => ({ @@ -57,19 +65,22 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { ); - const usernameCorrect = ( - - - - {chrome.i18n.getMessage('Sounds_good')} - - + const usernameCorrect = useMemo( + () => ( + + + + {chrome.i18n.getMessage('Sounds_good')} + + + ), + [] ); const usernameLoading = () => ( { ); - const [username, setUsername] = useState(savedUsername || ''); + const [username, setUsername] = useState(savedUsername || ''); const [helperText, setHelperText] = useState(
); - const regex = /^[A-Za-z0-9]{3,15}$/; - - const setErrorMessage = (message: string) => { - setLoading(false); - setUsernameValid(false); - setHelperText(usernameError(message)); - }; + const setErrorMessage = useCallback( + (message: string) => { + setLoading(false); + setUsernameValid(false); + setHelperText(usernameError(message)); + }, + [setLoading, setUsernameValid, setHelperText] + ); useEffect(() => { setUsernameValid(false); @@ -107,6 +119,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { setErrorMessage(chrome.i18n.getMessage('Too__long')); return; } + const regex = /^[A-Za-z0-9]{3,15}$/; if (!regex.test(username)) { setErrorMessage( @@ -118,7 +131,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { .checkUsername(username.toLowerCase()) .then((response) => { setLoading(false); - if (response.data.username != username.toLowerCase()) { + if (response.data.username !== username.toLowerCase()) { setLoading(false); return; } @@ -126,7 +139,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { setUsernameValid(true); setHelperText(usernameCorrect); } else { - if (response.message == 'Username is reserved') { + if (response.message === 'Username is reserved') { setErrorMessage( chrome.i18n.getMessage('This__username__is__reserved__Please__contact') ); @@ -141,7 +154,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { }, 500); return () => clearTimeout(delayDebounceFn); - }, [username]); + }, [setErrorMessage, username, usernameCorrect, wallet.openapi]); const msgBgColor = () => { if (isLoading) { @@ -151,8 +164,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { }; return ( - - + <> {chrome.i18n.getMessage('Pick__Your')} @@ -196,20 +208,18 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { } /> - - {username && ( - - {helperText} - - )} - + + + {helperText} + + @@ -233,7 +243,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { - + ); }; diff --git a/src/ui/views/AddWelcome/AddRegister/RecoveryPhrase.tsx b/src/ui/views/AddWelcome/AddRegister/RecoveryPhrase.tsx index 1c2df908..4c5cd4b1 100644 --- a/src/ui/views/AddWelcome/AddRegister/RecoveryPhrase.tsx +++ b/src/ui/views/AddWelcome/AddRegister/RecoveryPhrase.tsx @@ -1,238 +1,234 @@ -import React, { useState } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, IconButton, CssBaseline } from '@mui/material'; -import theme from '../../../style/LLTheme'; -import LockRoundedIcon from '@mui/icons-material/LockRounded'; +import InfoIcon from '@mui/icons-material/Info'; import LockOpenRoundedIcon from '@mui/icons-material/LockOpenRounded'; +import LockRoundedIcon from '@mui/icons-material/LockRounded'; +import { Button, Typography, IconButton } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useState } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; + import IconCopy from '../../../../components/iconfont/IconCopy'; -import { Presets } from 'react-component-transition'; -import InfoIcon from '@mui/icons-material/Info'; const RecoveryPhrase = ({ handleClick, mnemonic }) => { const [canGoNext, setCanGoNext] = useState(false); const [isCoverBlur, coverBlur] = useState(true); return ( - - - - - {chrome.i18n.getMessage('Recovery')} - - {chrome.i18n.getMessage('Phrase')} - - - - {chrome.i18n.getMessage( - 'Write__down__this__phrase__in__this__exact__order__and__keep__them__safe' - )} - + + + {chrome.i18n.getMessage('Recovery')} + + {chrome.i18n.getMessage('Phrase')} + + + + {chrome.i18n.getMessage( + 'Write__down__this__phrase__in__this__exact__order__and__keep__them__safe' + )} + + - - {mnemonic.split(' ').map((word, i) => { - return ( + {mnemonic.split(' ').map((word, i) => { + return ( + - - {i + 1} - - - {word} - + {i + 1} - ); - })} + + {word} + + + ); + })} + { + coverBlur(!isCoverBlur); + }} + component="span" + sx={{ + position: 'absolute', + bottom: '0', + right: '0', + height: '40px', + width: '40px', + my: '16px', + mx: '24px', + backgroundColor: 'neutral1.main', + transition: 'all .3s ease-in-out', + justifySelf: 'end', + opacity: isCoverBlur ? 0 : 1, + // visibility: isCoverBlur ? 'hidden' : 'visible', + // ':hover': { + // bgcolor: '#41CC5D', + // }, + }} + > + + + + + {isCoverBlur && ( + { coverBlur(!isCoverBlur); + setCanGoNext(true); }} component="span" sx={{ - position: 'absolute', - bottom: '0', - right: '0', - height: '40px', - width: '40px', - my: '16px', - mx: '24px', backgroundColor: 'neutral1.main', - transition: 'all .3s ease-in-out', - justifySelf: 'end', - opacity: isCoverBlur ? 0 : 1, - // visibility: isCoverBlur ? 'hidden' : 'visible', // ':hover': { // bgcolor: '#41CC5D', // }, }} > - + + + {chrome.i18n.getMessage('Click__here__to__reveal__phrase')} + + )} + + + + + - {isCoverBlur && ( - - { - coverBlur(!isCoverBlur); - setCanGoNext(true); - }} - component="span" - sx={{ - backgroundColor: 'neutral1.main', - // ':hover': { - // bgcolor: '#41CC5D', - // }, - }} - > - - - - {chrome.i18n.getMessage('Click__here__to__reveal__phrase')} - - - )} - - - - - - - - + + - + + {chrome.i18n.getMessage('Okay__I__have__saved__it__properly')} + + - + ); }; diff --git a/src/ui/views/AddWelcome/AddRegister/RegisterHeader.tsx b/src/ui/views/AddWelcome/AddRegister/RegisterHeader.tsx index 31399d20..fb499d7f 100644 --- a/src/ui/views/AddWelcome/AddRegister/RegisterHeader.tsx +++ b/src/ui/views/AddWelcome/AddRegister/RegisterHeader.tsx @@ -1,12 +1,11 @@ -import React from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography } from '@mui/material'; -import theme from '../../../style/LLTheme'; import HelpOutlineRoundedIcon from '@mui/icons-material/HelpOutlineRounded'; +import { Button, Typography } from '@mui/material'; +import { Box } from '@mui/system'; +import React from 'react'; const RegisterHeader = () => { return ( - + <> { - + ); }; diff --git a/src/ui/views/AddWelcome/AddRegister/RepeatPhrase.tsx b/src/ui/views/AddWelcome/AddRegister/RepeatPhrase.tsx index 6d40db1d..7cc83c5a 100644 --- a/src/ui/views/AddWelcome/AddRegister/RepeatPhrase.tsx +++ b/src/ui/views/AddWelcome/AddRegister/RepeatPhrase.tsx @@ -1,12 +1,9 @@ -import React, { useState, useEffect } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, CssBaseline } from '@mui/material'; -import theme from '../../../style/LLTheme'; -import LockRoundedIcon from '@mui/icons-material/LockRounded'; -import LockOpenRoundedIcon from '@mui/icons-material/LockOpenRounded'; -import IconCopy from '../../../../components/iconfont/IconCopy'; -import { Presets } from 'react-component-transition'; import InfoIcon from '@mui/icons-material/Info'; +import { Button, Typography } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useState, useEffect, useCallback, useMemo } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; const randomElement = (list: any[]) => { return list[Math.floor(Math.random() * list.length)]; @@ -26,22 +23,28 @@ const RepeatPhrase = ({ handleClick, mnemonic }) => { const [selectedPhrase, setSelect] = useState([]); const [repeatArray, setRepeat] = useState([[], [], []]); - const mnemonicArray = mnemonic.split(' '); - const fullIndex = [...Array(mnemonicArray.length).keys()]; - const positionList: number[][] = chunkArray(fullIndex, Math.floor(mnemonicArray.length / 3)); + const mnemonicArray = useMemo(() => mnemonic.split(' '), [mnemonic]); + const fullIndex = useMemo(() => [...Array(mnemonicArray.length).keys()], [mnemonicArray]); + const positionList = useMemo( + () => chunkArray(fullIndex, Math.floor(mnemonicArray.length / 3)), + [fullIndex, mnemonicArray] + ); - const setSelected = (i, v) => { - const tempArray = selectedPhrase; - tempArray[i] = v; - setSelect([...tempArray]); - }; + const setSelected = useCallback( + (i, v) => { + const tempArray = selectedPhrase; + tempArray[i] = v; + setSelect([...tempArray]); + }, + [selectedPhrase] + ); const checkMatch = () => { const correctMatch = chosenIndex.map((index) => mnemonicArray[index]); if ( - selectedPhrase[0] == correctMatch[0] && - selectedPhrase[1] == correctMatch[1] && - selectedPhrase[2] == correctMatch[2] + selectedPhrase[0] === correctMatch[0] && + selectedPhrase[1] === correctMatch[1] && + selectedPhrase[2] === correctMatch[2] ) { handleClick(); return; @@ -55,7 +58,7 @@ const RepeatPhrase = ({ handleClick, mnemonic }) => { }, 5000); }; - const handleRandom = () => { + const handleRandom = useCallback(() => { const arr: number[] = []; // [[0,1,2,3],[4,5,6,7],[8,9,10,11]] const repeatIndex: number[][] = [[], [], []]; @@ -65,7 +68,7 @@ const RepeatPhrase = ({ handleClick, mnemonic }) => { positionList.forEach((list, i) => { const picked = randomElement(list); const exclude = fullIndex - .filter((item) => item != picked) + .filter((item) => item !== picked) .sort(() => { return Math.random() - 0.5; }); @@ -78,14 +81,13 @@ const RepeatPhrase = ({ handleClick, mnemonic }) => { }); setChosen(arr); setRepeat(repeatMap); - }; + }, [mnemonicArray, positionList]); useEffect(() => { handleRandom(); - }, []); + }, [handleRandom]); return ( - - + <> {chrome.i18n.getMessage('Verify') + ' '} @@ -152,14 +154,14 @@ const RepeatPhrase = ({ handleClick, mnemonic }) => { height: '100%', width: '100%', borderRadius: '8px', - backgroundColor: `${selectedPhrase[i] == v ? '#fff' : 'none'}`, + backgroundColor: `${selectedPhrase[i] === v ? '#fff' : 'none'}`, }} > {v} @@ -184,33 +186,27 @@ const RepeatPhrase = ({ handleClick, mnemonic }) => { justifyContent: 'flex-end', }} > - {incorrect && ( - - - - - {chrome.i18n.getMessage('Incorrect_recovery_phrases_please_try_again')} - - - - )} + + + + + {chrome.i18n.getMessage('Incorrect_recovery_phrases_please_try_again')} + + + - + ); }; diff --git a/src/ui/views/AddWelcome/AddRegister/SetPassword.tsx b/src/ui/views/AddWelcome/AddRegister/SetPassword.tsx index a619aa66..fa9ab332 100644 --- a/src/ui/views/AddWelcome/AddRegister/SetPassword.tsx +++ b/src/ui/views/AddWelcome/AddRegister/SetPassword.tsx @@ -11,25 +11,23 @@ import { InputAdornment, FormGroup, LinearProgress, - CssBaseline, } from '@mui/material'; import Checkbox from '@mui/material/Checkbox'; import FormControlLabel from '@mui/material/FormControlLabel'; import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import { Box } from '@mui/system'; import HDWallet from 'ethereum-hdwallet'; import React, { useEffect, useState } from 'react'; -import { Presets } from 'react-component-transition'; import zxcvbn from 'zxcvbn'; import { storage } from '@/background/webapi'; -import type { AccountKey } from 'background/service/networkModel'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { type AccountKey } from 'background/service/networkModel'; import { LLSpinner } from 'ui/FRWComponent'; import { useWallet, saveIndex, mixpanelBrowserService } from 'ui/utils'; import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; import CancelIcon from '../../../../components/iconfont/IconClose'; -import theme from '../../../style/LLTheme'; const useStyles = makeStyles(() => ({ customInputLabel: { @@ -242,8 +240,7 @@ const SetPassword = ({ handleClick, mnemonic, username, setExPassword, tempPassw }; return ( - - + <> {chrome.i18n.getMessage('Confirm__Password')} @@ -284,9 +281,11 @@ const SetPassword = ({ handleClick, mnemonic, username, setExPassword, tempPassw } /> - - {password && helperText} - + + + {helperText} + + @@ -349,7 +348,7 @@ const SetPassword = ({ handleClick, mnemonic, username, setExPassword, tempPassw {errMessage} - + ); }; diff --git a/src/ui/views/AddWelcome/AddRegister/index.tsx b/src/ui/views/AddWelcome/AddRegister/index.tsx index ffc9ae3d..373c4f38 100644 --- a/src/ui/views/AddWelcome/AddRegister/index.tsx +++ b/src/ui/views/AddWelcome/AddRegister/index.tsx @@ -1,23 +1,24 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory } from 'react-router-dom'; -import { Box, ThemeProvider } from '@mui/system'; import { IconButton, Typography } from '@mui/material'; +import { Box } from '@mui/system'; +import * as bip39 from 'bip39'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useHistory } from 'react-router-dom'; + +import { LLPinAlert } from '@/ui/FRWComponent'; +import Confetti from '@/ui/FRWComponent/Confetti'; +import SlideLeftRight from '@/ui/FRWComponent/SlideLeftRight'; +import { storage } from 'background/webapi'; +import { useWallet } from 'ui/utils'; + import BackButtonIcon from '../../../../components/iconfont/IconBackButton'; -import theme from '../../../style/LLTheme'; -import RegisterHeader from './RegisterHeader'; + +import AllSet from './AllSet'; +import GoogleBackup from './GoogleBackup'; import PickUsername from './PickUsername'; import RecoveryPhrase from './RecoveryPhrase'; +import RegisterHeader from './RegisterHeader'; import RepeatPhrase from './RepeatPhrase'; -import GoogleBackup from './GoogleBackup'; -import AllSet from './AllSet'; import SetPassword from './SetPassword'; -import Particles from 'react-tsparticles'; -import * as bip39 from 'bip39'; -import { ComponentTransition, AnimationTypes } from 'react-component-transition'; -import { LLPinAlert } from '@/ui/FRWComponent'; -import options from '../../Import/options'; -import { useWallet } from 'ui/utils'; -import { storage } from 'background/webapi'; enum Direction { Right, @@ -31,13 +32,13 @@ const AddRegister = () => { const [direction, setDirection] = useState(Direction.Right); const [username, setUsername] = useState(''); const [password, setPassword] = useState(null); - const [mnemonic, setMnemonic] = useState(bip39.generateMnemonic()); + const [mnemonic] = useState(bip39.generateMnemonic()); const getUsername = (username: string) => { setUsername(username.toLowerCase()); }; - const loadView = async () => { + const loadView = useCallback(async () => { // console.log(wallet); wallet .getCurrentAccount() @@ -49,14 +50,14 @@ const AddRegister = () => { .catch(() => { return; }); - }; + }, [wallet, history]); - const loadTempPassword = async () => { + const loadTempPassword = useCallback(async () => { const temp = await storage.get('tempPassword'); if (temp) { setPassword(temp); } - }; + }, []); const goNext = () => { setDirection(Direction.Right); @@ -115,12 +116,10 @@ const AddRegister = () => { useEffect(() => { loadView(); loadTempPassword(); - }, []); - - const height = [480, 600, 640, 620, 480, 480]; + }, [loadView, loadTempPassword]); return ( - + <> { alignItems: 'center', }} > - {activeIndex == 5 && ( - - )} + {activeIndex === 5 && } - + {/* height why not use auto */} @@ -189,30 +183,14 @@ const AddRegister = () => { - + {page(activeIndex)} - + - + ); }; diff --git a/src/ui/views/AddWelcome/AddressImport/GoogleBackup.tsx b/src/ui/views/AddWelcome/AddressImport/GoogleBackup.tsx index e38e70a9..135de36c 100644 --- a/src/ui/views/AddWelcome/AddressImport/GoogleBackup.tsx +++ b/src/ui/views/AddWelcome/AddressImport/GoogleBackup.tsx @@ -1,12 +1,13 @@ +import InfoIcon from '@mui/icons-material/Info'; +import { Button, Typography } from '@mui/material'; +import { Box } from '@mui/system'; import React, { useState } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, CssBaseline } from '@mui/material'; -import theme from '../../../style/LLTheme'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { LLSpinner } from 'ui/FRWComponent'; import { useWallet } from 'ui/utils'; + import IconGoogleDrive from '../../../../components/iconfont/IconGoogleDrive'; -import { LLSpinner } from 'ui/FRWComponent'; -import InfoIcon from '@mui/icons-material/Info'; -import { Presets } from 'react-component-transition'; const GoogleBackup = ({ handleClick, mnemonic, username, password }) => { const wallets = useWallet(); @@ -33,8 +34,7 @@ const GoogleBackup = ({ handleClick, mnemonic, username, password }) => { } }; return ( - - + <> {chrome.i18n.getMessage('Create')} @@ -94,35 +94,29 @@ const GoogleBackup = ({ handleClick, mnemonic, username, password }) => { - {backupErr && ( - - - {/* */} - - - {chrome.i18n.getMessage( - 'Backup_failed_you_may_still_conduct_backup_inside_extension' - )} - - - - )} + + + {/* */} + + + {chrome.i18n.getMessage( + 'Backup_failed_you_may_still_conduct_backup_inside_extension' + )} + + + - + ); }; diff --git a/src/ui/views/AddWelcome/AddressImport/GoogleImport/DecryptWallet.tsx b/src/ui/views/AddWelcome/AddressImport/GoogleImport/DecryptWallet.tsx index 525d41f0..723901e5 100644 --- a/src/ui/views/AddWelcome/AddressImport/GoogleImport/DecryptWallet.tsx +++ b/src/ui/views/AddWelcome/AddressImport/GoogleImport/DecryptWallet.tsx @@ -1,22 +1,15 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; -import { - Button, - Typography, - IconButton, - Input, - InputAdornment, - FormGroup, - CssBaseline, -} from '@mui/material'; -import CancelIcon from '../../../../../components/iconfont/IconClose'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import VisibilityIcon from '@mui/icons-material/Visibility'; -import { Presets } from 'react-component-transition'; -import theme from '../../../../style/LLTheme'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; +import { Button, Typography, IconButton, Input, InputAdornment, FormGroup } from '@mui/material'; +import { makeStyles } from '@mui/styles'; +import { Box } from '@mui/system'; +import React, { useEffect, useState } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; import { useWallet } from 'ui/utils'; +import CancelIcon from '../../../../../components/iconfont/IconClose'; + // const helperTextStyles = makeStyles(() => ({ // root: { // size: '16px', @@ -123,8 +116,7 @@ const DecryptWallet = ({ handleClick, setMnemonic, username }) => { }, [password]); return ( - - + <> {chrome.i18n.getMessage('Welcome__Back')} @@ -168,7 +160,9 @@ const DecryptWallet = ({ handleClick, setMnemonic, username }) => { } /> - {password && helperText} + + {helperText} + @@ -192,7 +186,7 @@ const DecryptWallet = ({ handleClick, setMnemonic, username }) => { - + ); }; diff --git a/src/ui/views/AddWelcome/AddressImport/GoogleImport/GoogleAccounts.tsx b/src/ui/views/AddWelcome/AddressImport/GoogleImport/GoogleAccounts.tsx index f5eb1ada..bcb4b65a 100644 --- a/src/ui/views/AddWelcome/AddressImport/GoogleImport/GoogleAccounts.tsx +++ b/src/ui/views/AddWelcome/AddressImport/GoogleImport/GoogleAccounts.tsx @@ -1,5 +1,4 @@ -import React, { useState, useEffect } from 'react'; -import { ThemeProvider } from '@mui/system'; +import ArrowForwardRoundedIcon from '@mui/icons-material/ArrowForwardRounded'; import { Typography, Avatar, @@ -10,10 +9,9 @@ import { ListItemText, IconButton, ListItem, - CssBaseline, } from '@mui/material'; -import theme from '../../../../style/LLTheme'; -import ArrowForwardRoundedIcon from '@mui/icons-material/ArrowForwardRounded'; +import React, { useState, useEffect, useCallback } from 'react'; + import { useWallet } from 'ui/utils'; const FetchAvatar = ({ username }) => { @@ -22,27 +20,27 @@ const FetchAvatar = ({ username }) => { ); const wallet = useWallet(); - const fetchUserAvatar = async (username) => { - const { data } = await wallet.openapi.searchUser(username); - const users = data.users; - if (users.length > 0 && users[0].avatar) { - setAvatar(users[0].avatar); - } - }; + const fetchUserAvatar = useCallback( + async (username) => { + const { data } = await wallet.openapi.searchUser(username); + const users = data.users; + if (users.length > 0 && users[0].avatar) { + setAvatar(users[0].avatar); + } + }, + [setAvatar, wallet.openapi] + ); useEffect(() => { fetchUserAvatar(username); - }, []); + }, [fetchUserAvatar, username]); return ; }; const GoogleAccounts = ({ handleClick, accounts, setUsername }) => { - const [canGoNext, setCanGoNext] = useState(true); - return ( - - + <> {chrome.i18n.getMessage('We__ve__found') + ' '} @@ -104,7 +102,7 @@ const GoogleAccounts = ({ handleClick, accounts, setUsername }) => { - + ); }; diff --git a/src/ui/views/AddWelcome/AddressImport/GoogleImport/RecoverPassword.tsx b/src/ui/views/AddWelcome/AddressImport/GoogleImport/RecoverPassword.tsx index 8655cf9b..0276fb36 100644 --- a/src/ui/views/AddWelcome/AddressImport/GoogleImport/RecoverPassword.tsx +++ b/src/ui/views/AddWelcome/AddressImport/GoogleImport/RecoverPassword.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Button, Typography, @@ -11,22 +10,19 @@ import { LinearProgress, Alert, Snackbar, - CssBaseline, } from '@mui/material'; - -import { LLSpinner } from 'ui/FRWComponent'; -import CancelIcon from '../../../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../../../components/iconfont/IconCheckmark'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; -import VisibilityIcon from '@mui/icons-material/Visibility'; -import FormControlLabel from '@mui/material/FormControlLabel'; import Checkbox from '@mui/material/Checkbox'; -import { Presets } from 'react-component-transition'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import { makeStyles, styled } from '@mui/styles'; +import { Box } from '@mui/system'; +import React, { useEffect, useState } from 'react'; import zxcvbn from 'zxcvbn'; -import theme from '../../../../style/LLTheme'; + +import { LLSpinner, LLNotFound } from '@/ui/FRWComponent'; import { useWallet, saveIndex } from 'ui/utils'; -import { LLNotFound } from 'ui/FRWComponent'; -import { storage } from '@/background/webapi'; + +import CheckCircleIcon from '../../../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../../../components/iconfont/IconClose'; // const helperTextStyles = makeStyles(() => ({ // root: { @@ -232,11 +228,10 @@ const SetPassword = ({ handleClick, mnemonic, username, lastPassword }) => { setPassword(''); setConfirmPassword(''); } - }, [isCheck]); + }, [isCheck, lastPassword]); return ( - - + <> {!showDialog ? ( @@ -333,7 +328,7 @@ const SetPassword = ({ handleClick, mnemonic, username, lastPassword }) => { {errorMessage} - + ); }; diff --git a/src/ui/views/AddWelcome/AddressImport/GoogleImport/RecoveryPhrase.tsx b/src/ui/views/AddWelcome/AddressImport/GoogleImport/RecoveryPhrase.tsx index e95f09f5..bdf0ad9d 100644 --- a/src/ui/views/AddWelcome/AddressImport/GoogleImport/RecoveryPhrase.tsx +++ b/src/ui/views/AddWelcome/AddressImport/GoogleImport/RecoveryPhrase.tsx @@ -1,20 +1,20 @@ -import React, { useState } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, IconButton, CssBaseline } from '@mui/material'; -import theme from '../../../../style/LLTheme'; -import LockRoundedIcon from '@mui/icons-material/LockRounded'; +import InfoIcon from '@mui/icons-material/Info'; import LockOpenRoundedIcon from '@mui/icons-material/LockOpenRounded'; +import LockRoundedIcon from '@mui/icons-material/LockRounded'; +import { Button, Typography, IconButton } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useState } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; + import IconCopy from '../../../../../components/iconfont/IconCopy'; -import { Presets } from 'react-component-transition'; -import InfoIcon from '@mui/icons-material/Info'; const RecoveryPhrase = ({ handleClick, mnemonic }) => { const [canGoNext, setCanGoNext] = useState(true); const [isCoverBlur, coverBlur] = useState(false); return ( - - + <> {chrome.i18n.getMessage('Review') + ' '} @@ -191,7 +191,7 @@ const RecoveryPhrase = ({ handleClick, mnemonic }) => { marginBottom: '8px', }} > - + { )} - + - + ); }; diff --git a/src/ui/views/AddWelcome/AddressImport/GoogleImport/index.tsx b/src/ui/views/AddWelcome/AddressImport/GoogleImport/index.tsx index 4a38d0ea..eab1e689 100644 --- a/src/ui/views/AddWelcome/AddressImport/GoogleImport/index.tsx +++ b/src/ui/views/AddWelcome/AddressImport/GoogleImport/index.tsx @@ -1,20 +1,22 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory, useLocation } from 'react-router-dom'; -import { Box, ThemeProvider } from '@mui/system'; import { IconButton, Typography } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useHistory, useLocation } from 'react-router-dom'; + +import { LLPinAlert } from '@/ui/FRWComponent'; +import Confetti from '@/ui/FRWComponent/Confetti'; +import SlideLeftRight from '@/ui/FRWComponent/SlideLeftRight'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { storage } from 'background/webapi'; + import BackButtonIcon from '../../../../../components/iconfont/IconBackButton'; -import theme from '../../../../style/LLTheme'; -import RegisterHeader from '../../../Register/RegisterHeader'; import AllSet from '../../../Register/AllSet'; +import RegisterHeader from '../../../Register/RegisterHeader'; + import DecryptWallet from './DecryptWallet'; -import RecoveryPhrase from './RecoveryPhrase'; import GoogleAccounts from './GoogleAccounts'; import RecoveryPassword from './RecoverPassword'; -import Particles from 'react-tsparticles'; -import { ComponentTransition, AnimationTypes } from 'react-component-transition'; -import { LLPinAlert } from '@/ui/FRWComponent'; -import { Options } from 'ui/utils'; -import { storage } from 'background/webapi'; +import RecoveryPhrase from './RecoveryPhrase'; enum Direction { Right, @@ -53,14 +55,14 @@ const GoogleImport = () => { } }; - const loadTempPassword = async () => { + const loadTempPassword = useCallback(async () => { const temp = await storage.get('tempPassword'); if (temp) { setPassword(temp); } - }; + }, []); - const getGoogleAccounts = async () => { + const getGoogleAccounts = useCallback(async () => { // const backupFile = await storage.get('googleBackup'); // await setBackup(backupFile); const users = location.state.accounts; @@ -71,12 +73,12 @@ const GoogleImport = () => { } else { setAccounts(users); } - }; + }, [location?.state?.accounts]); useEffect(() => { getGoogleAccounts(); loadTempPassword(); - }, []); + }, [getGoogleAccounts, loadTempPassword]); const page = (index) => { switch (index) { @@ -104,10 +106,8 @@ const GoogleImport = () => { } }; - const heights = [500, 500, 600, 600, 500]; - return ( - + <> { alignItems: 'center', }} > - {activeIndex == 4 && ( - - )} - + {activeIndex === 4 && } + { - + {page(activeIndex)} - + - + ); }; diff --git a/src/ui/views/AddWelcome/AddressImport/ImportPager.tsx b/src/ui/views/AddWelcome/AddressImport/ImportPager.tsx index 668a216d..cb7888dc 100644 --- a/src/ui/views/AddWelcome/AddressImport/ImportPager.tsx +++ b/src/ui/views/AddWelcome/AddressImport/ImportPager.tsx @@ -1,18 +1,16 @@ -import { useEffect, useState, useContext } from 'react'; -import React from 'react'; import { Box, Tabs, Tab, Typography } from '@mui/material'; -import SeedPhraseImport from './importComponent/SeedPhrase'; -import KeyImport from './importComponent/KeyImport'; -import JsonImport from './importComponent/JsonImport'; -import Googledrive from './importComponent/Googledrive'; +import React, { useState } from 'react'; -import ImportAddressModel from '../../../FRWComponent/PopupModal/importAddressModal'; +import { storage } from '@/background/webapi'; +import { useWallet } from 'ui/utils'; import ErrorModel from '../../../FRWComponent/PopupModal/errorModel'; -import { useWallet } from 'ui/utils'; -import * as bip39 from 'bip39'; -import { storage } from '@/background/webapi'; -import { Presets } from 'react-component-transition'; +import ImportAddressModel from '../../../FRWComponent/PopupModal/importAddressModal'; + +import Googledrive from './importComponent/Googledrive'; +import JsonImport from './importComponent/JsonImport'; +import KeyImport from './importComponent/KeyImport'; +import SeedPhraseImport from './importComponent/SeedPhrase'; function TabPanel(props) { const { children, value, index, ...other } = props; diff --git a/src/ui/views/AddWelcome/AddressImport/PickUsername.tsx b/src/ui/views/AddWelcome/AddressImport/PickUsername.tsx index c93b36bd..ec5cb3b7 100644 --- a/src/ui/views/AddWelcome/AddressImport/PickUsername.tsx +++ b/src/ui/views/AddWelcome/AddressImport/PickUsername.tsx @@ -1,16 +1,24 @@ -import React, { useEffect, useState } from 'react'; +import { + CircularProgress, + IconButton, + Button, + Typography, + FormControl, + Input, + InputAdornment, +} from '@mui/material'; import { makeStyles } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, FormControl, Input, InputAdornment, CssBaseline } from '@mui/material'; -import CancelIcon from '../../../../components/iconfont/IconClose'; +import { Box } from '@mui/system'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { useWallet } from 'ui/utils'; + import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; -import theme from '../../../style/LLTheme'; +import CancelIcon from '../../../../components/iconfont/IconClose'; import EmailIcon from '../../../assets/alternate-email.svg'; -import { Presets } from 'react-component-transition'; -import { useWallet } from 'ui/utils'; -import { CircularProgress, IconButton } from '@mui/material'; -const useStyles = makeStyles((theme) => ({ +const useStyles = makeStyles((_theme) => ({ customInputLabel: { '& legend': { visibility: 'visible', @@ -57,41 +65,48 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { ); - const usernameCorrect = ( - - - - {chrome.i18n.getMessage('Sounds_good')} - - + const usernameCorrect = useMemo( + () => ( + + + + {chrome.i18n.getMessage('Sounds_good')} + + + ), + [] ); - const usernameLoading = () => ( - - - {chrome.i18n.getMessage('Checking')} - + const usernameLoading = useMemo( + () => ( + + + {chrome.i18n.getMessage('Checking')} + + ), + [] ); const [username, setUsername] = useState(savedUsername || ''); const [helperText, setHelperText] = useState(
); - const regex = /^[A-Za-z0-9]{3,15}$/; - - const setErrorMessage = (message: string) => { - setLoading(false); - setUsernameValid(false); - setHelperText(usernameError(message)); - }; + const setErrorMessage = useCallback( + (message: string) => { + setLoading(false); + setUsernameValid(false); + setHelperText(usernameError(message)); + }, + [setLoading, setUsernameValid, setHelperText] + ); useEffect(() => { setUsernameValid(false); @@ -107,6 +122,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { setErrorMessage(chrome.i18n.getMessage('Too__long')); return; } + const regex = /^[A-Za-z0-9]{3,15}$/; if (!regex.test(username)) { setErrorMessage( @@ -118,7 +134,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { .checkUsername(username.toLowerCase()) .then((response) => { setLoading(false); - if (response.data.username != username.toLowerCase()) { + if (response.data.username !== username.toLowerCase()) { setLoading(false); return; } @@ -126,7 +142,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { setUsernameValid(true); setHelperText(usernameCorrect); } else { - if (response.message == 'Username is reserved') { + if (response.message === 'Username is reserved') { setErrorMessage( chrome.i18n.getMessage('This__username__is__reserved__Please__contact') ); @@ -136,23 +152,23 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { } }) .catch((error) => { + console.error(error); setErrorMessage(chrome.i18n.getMessage('Oops__unexpected__error')); }); }, 500); return () => clearTimeout(delayDebounceFn); - }, [username]); + }, [username, setErrorMessage, usernameCorrect, wallet.openapi, usernameLoading]); - const msgBgColor = () => { + const msgBgColor = useCallback(() => { if (isLoading) { return 'neutral.light'; } return usernameValid ? 'success.light' : 'error.light'; - }; + }, [isLoading, usernameValid]); return ( - - + <> {chrome.i18n.getMessage('Pick__Your')} @@ -196,20 +212,18 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { } /> - - {username && ( - - {helperText} - - )} - + + + {helperText} + + @@ -233,7 +247,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { - + ); }; diff --git a/src/ui/views/AddWelcome/AddressImport/RecoverPassword.tsx b/src/ui/views/AddWelcome/AddressImport/RecoverPassword.tsx index 93583041..2d95bc0a 100644 --- a/src/ui/views/AddWelcome/AddressImport/RecoverPassword.tsx +++ b/src/ui/views/AddWelcome/AddressImport/RecoverPassword.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Button, Typography, @@ -11,13 +10,13 @@ import { LinearProgress, Alert, Snackbar, - CssBaseline, } from '@mui/material'; -import { LLSpinner } from 'ui/FRWComponent'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; -import VisibilityIcon from '@mui/icons-material/Visibility'; +import { makeStyles, styled } from '@mui/styles'; +import { Box } from '@mui/system'; +import React, { useEffect, useState } from 'react'; import zxcvbn from 'zxcvbn'; -import theme from '../../../style/LLTheme'; + +import { LLSpinner } from 'ui/FRWComponent'; import { useWallet, saveIndex } from 'ui/utils'; import ErrorModel from '../../../FRWComponent/PopupModal/errorModel'; @@ -223,8 +222,7 @@ const SetPassword = ({ handleClick, mnemonic, pk, tempPassword, goEnd, accountKe }; return ( - - + <> {chrome.i18n.getMessage('Welcome__Back__import')} @@ -315,7 +313,7 @@ const SetPassword = ({ handleClick, mnemonic, pk, tempPassword, goEnd, accountKe isGoback={true} /> )} - + ); }; diff --git a/src/ui/views/AddWelcome/AddressImport/SetPassword.tsx b/src/ui/views/AddWelcome/AddressImport/SetPassword.tsx index 9c5e1415..5b486b81 100644 --- a/src/ui/views/AddWelcome/AddressImport/SetPassword.tsx +++ b/src/ui/views/AddWelcome/AddressImport/SetPassword.tsx @@ -11,24 +11,23 @@ import { InputAdornment, FormGroup, LinearProgress, - CssBaseline, } from '@mui/material'; import Checkbox from '@mui/material/Checkbox'; import FormControlLabel from '@mui/material/FormControlLabel'; import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; -import React, { useEffect, useState } from 'react'; -import { Presets } from 'react-component-transition'; +import { Box } from '@mui/system'; +import React, { useCallback, useEffect, useState } from 'react'; import zxcvbn from 'zxcvbn'; import { storage } from '@/background/webapi'; import { getHashAlgo, getSignAlgo } from '@/shared/utils/algo'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { AccountKey } from 'background/service/networkModel'; import { LLSpinner } from 'ui/FRWComponent'; import { useWallet, saveIndex } from 'ui/utils'; import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; import CancelIcon from '../../../../components/iconfont/IconClose'; -import theme from '../../../style/LLTheme'; const useStyles = makeStyles(() => ({ customInputLabel: { @@ -153,13 +152,13 @@ const SetPassword = ({ handleClick, mnemonic, pk, username, tempPassword, accoun setShowError(false); }; - const loadTempPassword = async () => { + const loadTempPassword = useCallback(async () => { setPassword(tempPassword); - }; + }, [tempPassword]); useEffect(() => { loadTempPassword(); - }, []); + }, [loadTempPassword]); const successInfo = (message) => { return ( @@ -282,8 +281,7 @@ const SetPassword = ({ handleClick, mnemonic, pk, username, tempPassword, accoun }, [password]); return ( - - + <> {chrome.i18n.getMessage('Create')} @@ -329,9 +327,9 @@ const SetPassword = ({ handleClick, mnemonic, pk, username, tempPassword, accoun } /> - - {password && helperText} - + + {helperText} + @@ -394,7 +392,7 @@ const SetPassword = ({ handleClick, mnemonic, pk, username, tempPassword, accoun {errMessage} - + ); }; diff --git a/src/ui/views/AddWelcome/AddressImport/index.tsx b/src/ui/views/AddWelcome/AddressImport/index.tsx index 5991d6c7..73405e25 100644 --- a/src/ui/views/AddWelcome/AddressImport/index.tsx +++ b/src/ui/views/AddWelcome/AddressImport/index.tsx @@ -1,23 +1,23 @@ -import React, { useState, useEffect } from 'react'; +import { IconButton, Typography, Snackbar, Alert } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useState, useEffect, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; -import { Box, ThemeProvider } from '@mui/system'; -import { IconButton, Typography, Button, Snackbar, Alert } from '@mui/material'; + +import Confetti from '@/ui/FRWComponent/Confetti'; +import SlideLeftRight from '@/ui/FRWComponent/SlideLeftRight'; +import { storage } from 'background/webapi'; +import { LLPinAlert } from 'ui/FRWComponent'; +import { useWallet } from 'ui/utils'; + import BackButtonIcon from '../../../../components/iconfont/IconBackButton'; -import IconGoogleDrive from '../../../../components/iconfont/IconGoogleDrive'; -import theme from '../../../style/LLTheme'; -import RegisterHeader from '../../Register/RegisterHeader'; import AllSet from '../../Register/AllSet'; -import SeedPhrase from './importComponent/SeedPhrase'; -import PickUsername from './PickUsername'; -import SetPassword from './SetPassword'; +import RegisterHeader from '../../Register/RegisterHeader'; + import GoogleBackup from './GoogleBackup'; -import { storage } from 'background/webapi'; -import Particles from 'react-tsparticles'; -import { LLPinAlert, LLSpinner } from 'ui/FRWComponent'; -import { ComponentTransition, AnimationTypes } from 'react-component-transition'; -import { useWallet, Options } from 'ui/utils'; import ImportPager from './ImportPager'; +import PickUsername from './PickUsername'; import RecoverPassword from './RecoverPassword'; +import SetPassword from './SetPassword'; enum Direction { Right, @@ -34,10 +34,8 @@ const AddressImport = () => { const [errMessage, setErrorMessage] = useState(chrome.i18n.getMessage('No__backup__found')); const [showError, setShowError] = useState(false); const [direction, setDirection] = useState(Direction.Right); - const [loading, setLoading] = useState(false); const [password, setPassword] = useState(null); const [accounts, setAccounts] = useState([]); - const [isImport, setImport] = useState(false); const getUsername = (username: string) => { setUsername(username.toLowerCase()); @@ -50,7 +48,7 @@ const AddressImport = () => { } }; - const loadView = async () => { + const loadView = useCallback(async () => { // console.log(wallet); wallet .getCurrentAccount() @@ -62,7 +60,7 @@ const AddressImport = () => { .catch(() => { return; }); - }; + }, [history, wallet]); const goNext = () => { setDirection(Direction.Right); if (activeIndex < 5) { @@ -166,12 +164,11 @@ const AddressImport = () => { }; useEffect(() => { - console.log('wallet'); loadView(); - }, []); + }, [loadView]); return ( - + <> { alignItems: 'center', }} > - {activeIndex == 5 && ( - - )} + {activeIndex === 5 && } - + {/* height why not use auto */} @@ -237,26 +229,75 @@ const AddressImport = () => { {chrome.i18n.getMessage('STEP')} {activeIndex + 1}/5 + + + + + + - + + + + + + + + + - {page(activeIndex)} - + + @@ -271,7 +312,7 @@ const AddressImport = () => { - + ); }; diff --git a/src/ui/views/AddWelcome/ProxySync/ProxyQr.tsx b/src/ui/views/AddWelcome/ProxySync/ProxyQr.tsx index 13cfb93a..64f7ac4b 100644 --- a/src/ui/views/AddWelcome/ProxySync/ProxyQr.tsx +++ b/src/ui/views/AddWelcome/ProxySync/ProxyQr.tsx @@ -1,17 +1,17 @@ -import React, { useEffect, useCallback, useState } from 'react'; +import { Typography } from '@mui/material'; import { makeStyles } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, FormControl, Input, InputAdornment, CssBaseline } from '@mui/material'; -import theme from '../../../style/LLTheme'; -import { useWallet } from 'ui/utils'; +import { Box } from '@mui/system'; import { Core } from '@walletconnect/core'; -import { FCLWalletConnectMethod } from '@/ui/utils/type'; import SignClient from '@walletconnect/sign-client'; -import { PairingTypes, SessionTypes } from '@walletconnect/types'; +import { type SessionTypes } from '@walletconnect/types'; import * as bip39 from 'bip39'; -import { storage } from 'background/webapi'; +import React, { useEffect, useCallback, useState } from 'react'; import { QRCode } from 'react-qrcode-logo'; + +import { FCLWalletConnectMethod } from '@/ui/utils/type'; +import { storage } from 'background/webapi'; import lilo from 'ui/FRWAssets/image/lilo.png'; +import { useWallet } from 'ui/utils'; interface DeviceInfoRequest { deviceId: string; @@ -37,7 +37,7 @@ interface DeviceInfoRequest { device_id?: string; } -const useStyles = makeStyles((theme) => ({ +const useStyles = makeStyles((_theme) => ({ customInputLabel: { '& legend': { visibility: 'visible', @@ -78,20 +78,115 @@ const ProxyQr = ({ process.env.NODE_ENV === 'production' ? 'mainnet' : 'testnet' ); - const loadNetwork = async () => { + const loadNetwork = useCallback(async () => { const currentNetwork = await usewallet.getNetwork(); setNetwork(currentNetwork); - }; + }, [usewallet]); useEffect(() => { loadNetwork(); + }, [loadNetwork]); + + const onSessionConnected = useCallback(async (_session: SessionTypes.Struct) => { + console.log('_session ', _session); + setShowLoading(true); + setSession(_session); }, []); + const _subscribeToEvents = useCallback( + async (_client: SignClient) => { + if (typeof _client === 'undefined') { + throw new Error('WalletConnect is not initialized'); + } + + _client.on('session_update', ({ topic, params }) => { + console.log('EVENT', 'session_update', { topic, params }); + const { namespaces } = params; + const _session = _client.session.get(topic); + const updatedSession = { ..._session, namespaces }; + onSessionConnected(updatedSession); + }); + console.log('EVENT _client ', _client); + }, + [onSessionConnected] + ); + + const getDeviceInfo = useCallback(async (): Promise => { + const result = await usewallet.openapi.getLocation(); + const installationId = await usewallet.openapi.getInstallationId(); + // console.log('location ', userlocation); + const userlocation = result.data; + const deviceInfo: DeviceInfoRequest = { + city: userlocation.city, + continent: userlocation.country, + continentCode: userlocation.countryCode, + country: userlocation.country, + countryCode: userlocation.countryCode, + currency: userlocation.countryCode, + deviceId: installationId, + device_id: installationId, + district: '', + ip: userlocation.query, + isp: userlocation.as, + lat: userlocation.lat, + lon: userlocation.lon, + name: 'FRW Chrome Extension', + org: userlocation.org, + regionName: userlocation.regionName, + type: '2', + userAgent: 'Chrome', + zip: userlocation.zip, + }; + return deviceInfo; + }, [usewallet]); + + const sendRequest = useCallback( + async (wallet: SignClient, topic: string) => { + console.log(wallet); + const deviceInfo: DeviceInfoRequest = await getDeviceInfo(); + const jwtToken = await usewallet.requestProxyToken(); + wallet + .request({ + topic: topic, + chainId: `flow:${currentNetwork}`, + request: { + method: FCLWalletConnectMethod.proxyaccount, + params: { + method: FCLWalletConnectMethod.proxyaccount, + data: { + deviceInfo: deviceInfo, + jwt: jwtToken, + }, + }, + }, + }) + .then(async (result: any) => { + console.log(result); + const jsonObject = JSON.parse(result); + const accountKey = { + public_key: jsonObject.data.publicKey, + hash_algo: Number(jsonObject.data.hashAlgo), + sign_algo: Number(jsonObject.data.signAlgo), + weight: Number(jsonObject.data.weight), + }; + usewallet.openapi.loginV3(accountKey, deviceInfo, jsonObject.data.signature); + storage.set(`${jsonObject.data.userId}Topic`, topic); + confirmMnemonic(mnemonic); + confirmPk(jsonObject.data.publicKey); + console.log('jsonObject ', jsonObject); + handleClick(); + }) + .catch((error) => { + console.error('Error in first wallet request:', error); + }); + }, + [usewallet, currentNetwork, confirmMnemonic, confirmPk, mnemonic, handleClick, getDeviceInfo] + ); + useEffect(() => { const createWeb3Wallet = async () => { try { const wallet = await SignClient.init({ - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: Unreachable code error core: new Core({ projectId: process.env.WC_PROJECTID, @@ -143,104 +238,10 @@ const ProxyQr = ({ } }; createWeb3Wallet(); - }, []); - - const onSessionConnected = useCallback(async (_session: SessionTypes.Struct) => { - console.log('_session ', _session); - setShowLoading(true); - setSession(_session); - }, []); - - const _subscribeToEvents = useCallback( - async (_client: SignClient) => { - if (typeof _client === 'undefined') { - throw new Error('WalletConnect is not initialized'); - } - - _client.on('session_update', ({ topic, params }) => { - console.log('EVENT', 'session_update', { topic, params }); - const { namespaces } = params; - const _session = _client.session.get(topic); - const updatedSession = { ..._session, namespaces }; - onSessionConnected(updatedSession); - }); - console.log('EVENT _client ', _client); - }, - [onSessionConnected] - ); - - async function sendRequest(wallet: SignClient, topic: string) { - console.log(wallet); - const deviceInfo: DeviceInfoRequest = await getDeviceInfo(); - const jwtToken = await usewallet.requestProxyToken(); - wallet - .request({ - topic: topic, - chainId: `flow:${currentNetwork}`, - request: { - method: FCLWalletConnectMethod.proxyaccount, - params: { - method: FCLWalletConnectMethod.proxyaccount, - data: { - deviceInfo: deviceInfo, - jwt: jwtToken, - }, - }, - }, - }) - .then(async (result: any) => { - console.log(result); - const jsonObject = JSON.parse(result); - const accountKey = { - public_key: jsonObject.data.publicKey, - hash_algo: Number(jsonObject.data.hashAlgo), - sign_algo: Number(jsonObject.data.signAlgo), - weight: Number(jsonObject.data.weight), - }; - usewallet.openapi.loginV3(accountKey, deviceInfo, jsonObject.data.signature); - storage.set(`${jsonObject.data.userId}Topic`, topic); - confirmMnemonic(mnemonic); - confirmPk(jsonObject.data.publicKey); - console.log('jsonObject ', jsonObject); - handleClick(); - }) - .catch((error) => { - console.error('Error in first wallet request:', error); - }); - } - - const getDeviceInfo = async (): Promise => { - const result = await usewallet.openapi.getLocation(); - const installationId = await usewallet.openapi.getInstallationId(); - // console.log('location ', userlocation); - const userlocation = result.data; - const deviceInfo: DeviceInfoRequest = { - city: userlocation.city, - continent: userlocation.country, - continentCode: userlocation.countryCode, - country: userlocation.country, - countryCode: userlocation.countryCode, - currency: userlocation.countryCode, - deviceId: installationId, - device_id: installationId, - district: '', - ip: userlocation.query, - isp: userlocation.as, - lat: userlocation.lat, - lon: userlocation.lon, - name: 'FRW Chrome Extension', - org: userlocation.org, - regionName: userlocation.regionName, - type: '2', - userAgent: 'Chrome', - zip: userlocation.zip, - }; - return deviceInfo; - }; + }, [_subscribeToEvents, currentNetwork, onSessionConnected, sendRequest]); return ( - - + <> {/* */} - + ); }; diff --git a/src/ui/views/AddWelcome/ProxySync/RegisterHeader.tsx b/src/ui/views/AddWelcome/ProxySync/RegisterHeader.tsx index 31399d20..fb499d7f 100644 --- a/src/ui/views/AddWelcome/ProxySync/RegisterHeader.tsx +++ b/src/ui/views/AddWelcome/ProxySync/RegisterHeader.tsx @@ -1,12 +1,11 @@ -import React from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography } from '@mui/material'; -import theme from '../../../style/LLTheme'; import HelpOutlineRoundedIcon from '@mui/icons-material/HelpOutlineRounded'; +import { Button, Typography } from '@mui/material'; +import { Box } from '@mui/system'; +import React from 'react'; const RegisterHeader = () => { return ( - + <> { - + ); }; diff --git a/src/ui/views/AddWelcome/ProxySync/SetPassword.tsx b/src/ui/views/AddWelcome/ProxySync/SetPassword.tsx index 47839d40..6ec9a061 100644 --- a/src/ui/views/AddWelcome/ProxySync/SetPassword.tsx +++ b/src/ui/views/AddWelcome/ProxySync/SetPassword.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Button, Typography, @@ -11,18 +10,19 @@ import { LinearProgress, Alert, Snackbar, - CssBaseline, } from '@mui/material'; -import { LLSpinner } from 'ui/FRWComponent'; -import CancelIcon from '../../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; -import VisibilityIcon from '@mui/icons-material/Visibility'; -import { Presets } from 'react-component-transition'; +import { makeStyles, styled } from '@mui/styles'; +import { Box } from '@mui/system'; +import React, { useEffect, useState } from 'react'; import zxcvbn from 'zxcvbn'; -import theme from '../../../style/LLTheme'; -import { useWallet, saveIndex } from 'ui/utils'; + import { storage } from '@/background/webapi'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { LLSpinner } from 'ui/FRWComponent'; +import { useWallet, saveIndex } from 'ui/utils'; + +import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../../components/iconfont/IconClose'; // const helperTextStyles = makeStyles(() => ({ // root: { @@ -243,8 +243,7 @@ const SetPassword = ({ }, [password]); return ( - - + <> } /> - {password && helperText} + + {helperText} + @@ -372,7 +373,7 @@ const SetPassword = ({ - + ); }; diff --git a/src/ui/views/AddWelcome/ProxySync/index.tsx b/src/ui/views/AddWelcome/ProxySync/index.tsx index 1ac2f301..6e2cdf1f 100644 --- a/src/ui/views/AddWelcome/ProxySync/index.tsx +++ b/src/ui/views/AddWelcome/ProxySync/index.tsx @@ -1,18 +1,20 @@ -import React, { useState, useEffect } from 'react'; +import { IconButton } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useState, useEffect, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; -import { Box, ThemeProvider } from '@mui/system'; -import { IconButton, Typography, Button, Snackbar, Alert } from '@mui/material'; + +import Confetti from '@/ui/FRWComponent/Confetti'; +import SlideLeftRight from '@/ui/FRWComponent/SlideLeftRight'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { LLPinAlert } from 'ui/FRWComponent'; +import { useWallet } from 'ui/utils'; + import BackButtonIcon from '../../../../components/iconfont/IconBackButton'; -import IconGoogleDrive from '../../../../components/iconfont/IconGoogleDrive'; -import theme from '../../../style/LLTheme'; -import RegisterHeader from '../AddRegister/RegisterHeader'; import AllSet from '../AddRegister/AllSet'; -import SetPassword from './SetPassword'; +import RegisterHeader from '../AddRegister/RegisterHeader'; + import ProxyQr from './ProxyQr'; -import Particles from 'react-tsparticles'; -import { LLPinAlert, LLSpinner } from 'ui/FRWComponent'; -import { ComponentTransition, AnimationTypes } from 'react-component-transition'; -import { useWallet, Options } from 'ui/utils'; +import SetPassword from './SetPassword'; enum Direction { Right, @@ -26,10 +28,7 @@ const ProxySync = () => { const [publickey, setPubkey] = useState(''); const [mnemonic, setMnemonic] = useState(''); const [username, setUsername] = useState(''); - const [errMessage, setErrorMessage] = useState(chrome.i18n.getMessage('No__backup__found')); - const [showError, setShowError] = useState(false); const [direction, setDirection] = useState(Direction.Right); - const [loading, setLoading] = useState(false); const [accountKey, setAccountKey] = useState(null); const [deviceInfo, setDeviceInfo] = useState(null); @@ -37,7 +36,7 @@ const ProxySync = () => { setUsername(username.toLowerCase()); }; - const loadView = async () => { + const loadView = useCallback(async () => { wallet .getCurrentAccount() .then((res) => { @@ -48,7 +47,7 @@ const ProxySync = () => { .catch(() => { return; }); - }; + }, [history, wallet]); const goNext = () => { setDirection(Direction.Right); if (activeIndex < 2) { @@ -67,13 +66,6 @@ const ProxySync = () => { } }; - const handleErrorClose = (event?: React.SyntheticEvent | Event, reason?: string) => { - if (reason === 'clickaway') { - return; - } - setShowError(false); - }; - const page = (index) => { switch (index) { case 0: @@ -109,10 +101,10 @@ const ProxySync = () => { useEffect(() => { loadView(); - }, []); + }, [loadView]); return ( - + <> { alignItems: 'center', }} > - {activeIndex == 2 && ( - - )} + {activeIndex === 2 && } - + {/* height why not use auto */} @@ -160,31 +147,15 @@ const ProxySync = () => { )} - + {page(activeIndex)} - + - + ); }; diff --git a/src/ui/views/AddWelcome/Sync/RegisterHeader.tsx b/src/ui/views/AddWelcome/Sync/RegisterHeader.tsx index 31399d20..fb499d7f 100644 --- a/src/ui/views/AddWelcome/Sync/RegisterHeader.tsx +++ b/src/ui/views/AddWelcome/Sync/RegisterHeader.tsx @@ -1,12 +1,11 @@ -import React from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography } from '@mui/material'; -import theme from '../../../style/LLTheme'; import HelpOutlineRoundedIcon from '@mui/icons-material/HelpOutlineRounded'; +import { Button, Typography } from '@mui/material'; +import { Box } from '@mui/system'; +import React from 'react'; const RegisterHeader = () => { return ( - + <> { - + ); }; diff --git a/src/ui/views/AddWelcome/Sync/SetPassword.tsx b/src/ui/views/AddWelcome/Sync/SetPassword.tsx index 86e3d561..dbac7f00 100644 --- a/src/ui/views/AddWelcome/Sync/SetPassword.tsx +++ b/src/ui/views/AddWelcome/Sync/SetPassword.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Button, Typography, @@ -11,18 +10,19 @@ import { LinearProgress, Alert, Snackbar, - CssBaseline, } from '@mui/material'; -import { LLSpinner } from 'ui/FRWComponent'; -import CancelIcon from '../../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; -import VisibilityIcon from '@mui/icons-material/Visibility'; -import { Presets } from 'react-component-transition'; +import { makeStyles, styled } from '@mui/styles'; +import { Box } from '@mui/system'; +import React, { useEffect, useState } from 'react'; import zxcvbn from 'zxcvbn'; -import theme from '../../../style/LLTheme'; -import { useWallet, saveIndex } from 'ui/utils'; + import { storage } from '@/background/webapi'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { LLSpinner } from 'ui/FRWComponent'; +import { useWallet, saveIndex } from 'ui/utils'; + +import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../../components/iconfont/IconClose'; // const helperTextStyles = makeStyles(() => ({ // root: { @@ -237,8 +237,7 @@ const SetPassword = ({ handleClick, mnemonic, username, setUsername, accountKey, }, [password]); return ( - - + <> } /> - {password && helperText} + + {helperText} + @@ -366,7 +367,7 @@ const SetPassword = ({ handleClick, mnemonic, username, setUsername, accountKey, - + ); }; diff --git a/src/ui/views/AddWelcome/Sync/SyncQr.tsx b/src/ui/views/AddWelcome/Sync/SyncQr.tsx index 8716a58f..b85dcd1b 100644 --- a/src/ui/views/AddWelcome/Sync/SyncQr.tsx +++ b/src/ui/views/AddWelcome/Sync/SyncQr.tsx @@ -1,17 +1,17 @@ -import React, { useEffect, useCallback, useState } from 'react'; +import { Typography } from '@mui/material'; import { makeStyles } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, FormControl, Input, InputAdornment, CssBaseline } from '@mui/material'; -import theme from '../../../style/LLTheme'; -import { useWallet } from 'ui/utils'; +import { Box } from '@mui/system'; import { Core } from '@walletconnect/core'; -import { FCLWalletConnectMethod } from '@/ui/utils/type'; import SignClient from '@walletconnect/sign-client'; -import { PairingTypes, SessionTypes } from '@walletconnect/types'; +import { type SessionTypes } from '@walletconnect/types'; import * as bip39 from 'bip39'; import HDWallet from 'ethereum-hdwallet'; +import React, { useEffect, useCallback, useState } from 'react'; import { QRCode } from 'react-qrcode-logo'; + +import { FCLWalletConnectMethod } from '@/ui/utils/type'; import lilo from 'ui/FRWAssets/image/lilo.png'; +import { useWallet } from 'ui/utils'; interface AccountKey { hashAlgo: number; @@ -84,69 +84,14 @@ const SyncQr = ({ process.env.NODE_ENV === 'production' ? 'mainnet' : 'testnet' ); - const loadNetwork = async () => { + const loadNetwork = useCallback(async () => { const currentNetwork = await usewallet.getNetwork(); setNetwork(currentNetwork); - }; + }, [usewallet]); useEffect(() => { loadNetwork(); - }, []); - - useEffect(() => { - const createWeb3Wallet = async () => { - try { - const wallet = await SignClient.init({ - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore: Unreachable code error - core: new Core({ - projectId: process.env.WC_PROJECTID, - }), - metadata: { - name: 'Flow Walllet', - description: 'Digital wallet created for everyone.', - url: 'https://fcw-link.lilico.app', - icons: ['https://fcw-link.lilico.app/logo.png'], - }, - }); - await _subscribeToEvents(wallet); - - try { - const { uri, approval } = await wallet.connect({ - requiredNamespaces: { - flow: { - methods: [FCLWalletConnectMethod.accountInfo, FCLWalletConnectMethod.addDeviceInfo], - chains: [`flow:${currentNetwork}`], - events: [], - }, - }, - }); - - // Open QRCode modal if a URI was returned (i.e. we're not connecting an existing pairing). - if (uri) { - console.log('uri ', uri); - await setUri(uri); - // Await session approval from the wallet. - const session = await approval(); - await onSessionConnected(session); - - console.log('session ', session); - sendRequest(wallet, session.topic); - - // onSessionConnect(session) - // Close the QRCode modal in case it was open. - } - } catch (e) { - console.error(e); - } - await setWeb3Wallet(wallet); - console.log('web3wallet', web3wallet); - } catch (e) { - console.error(e); - } - }; - createWeb3Wallet(); - }, []); + }, [loadNetwork]); const onSessionConnected = useCallback(async (_session: SessionTypes.Struct) => { console.log('_session ', _session); @@ -171,73 +116,7 @@ const SyncQr = ({ }, [onSessionConnected] ); - - async function sendRequest(wallet: SignClient, topic: string) { - console.log(wallet); - wallet - .request({ - topic: topic, - chainId: `flow:${currentNetwork}`, - request: { - method: FCLWalletConnectMethod.accountInfo, - params: [], - }, - }) - .then(async (result: any) => { - console.log('result ', result); - const jsonObject = JSON.parse(result); - console.log('jsonObject ', jsonObject); - if (jsonObject.method === FCLWalletConnectMethod.accountInfo) { - const accountKey: AccountKey = getAccountKey(); - const deviceInfo: DeviceInfoRequest = await getDeviceInfo(); - const ak = { - public_key: accountKey.publicKey, - hash_algo: accountKey.hashAlgo, - sign_algo: accountKey.signAlgo, - weight: accountKey.weight, - }; - console.log('sent ->', accountKey); - confirmMnemonic(mnemonic); - setAccountKey(ak); - setDeviceInfo(deviceInfo); - wallet - .request({ - topic: topic, - chainId: `flow:${currentNetwork}`, - request: { - method: FCLWalletConnectMethod.addDeviceInfo, - params: { - method: '', - data: { - username: '', - accountKey: accountKey, - deviceInfo: deviceInfo, - }, - }, - }, - }) - .then(async (sent) => { - handleClick(); - // usewallet.signInV3(mnemonic, ak, deviceInfo).then(async (result) => { - - // const userInfo = await usewallet.getUserInfo(true); - // setUsername(userInfo.username); - // handleClick(); - // }).catch((error) => { - // console.error('Error in sign in wallet request:', error); - // }); - }) - .catch((error) => { - console.error('Error in second wallet request:', error); - }); - } - }) - .catch((error) => { - console.error('Error in first wallet request:', error); - }); - } - - const getAccountKey = () => { + const getAccountKey = useCallback(() => { const hdwallet = HDWallet.fromMnemonic(mnemonic); const publicKey = hdwallet.derive("m/44'/539'/0'/0/0").getPublicKey().toString('hex'); const key: AccountKey = { @@ -247,9 +126,9 @@ const SyncQr = ({ publicKey: publicKey, }; return key; - }; + }, [mnemonic]); - const getDeviceInfo = async (): Promise => { + const getDeviceInfo = useCallback(async (): Promise => { const result = await usewallet.openapi.getLocation(); const installationId = await usewallet.openapi.getInstallationId(); // console.log('location ', userlocation); @@ -276,11 +155,141 @@ const SyncQr = ({ zip: userlocation.zip, }; return deviceInfo; - }; + }, [usewallet]); + + const sendRequest = useCallback( + async (wallet: SignClient, topic: string) => { + console.log(wallet); + wallet + .request({ + topic: topic, + chainId: `flow:${currentNetwork}`, + request: { + method: FCLWalletConnectMethod.accountInfo, + params: [], + }, + }) + .then(async (result: any) => { + console.log('result ', result); + const jsonObject = JSON.parse(result); + console.log('jsonObject ', jsonObject); + if (jsonObject.method === FCLWalletConnectMethod.accountInfo) { + const accountKey: AccountKey = getAccountKey(); + const deviceInfo: DeviceInfoRequest = await getDeviceInfo(); + const ak = { + public_key: accountKey.publicKey, + hash_algo: accountKey.hashAlgo, + sign_algo: accountKey.signAlgo, + weight: accountKey.weight, + }; + console.log('sent ->', accountKey); + confirmMnemonic(mnemonic); + setAccountKey(ak); + setDeviceInfo(deviceInfo); + wallet + .request({ + topic: topic, + chainId: `flow:${currentNetwork}`, + request: { + method: FCLWalletConnectMethod.addDeviceInfo, + params: { + method: '', + data: { + username: '', + accountKey: accountKey, + deviceInfo: deviceInfo, + }, + }, + }, + }) + .then(async (sent) => { + handleClick(); + // usewallet.signInV3(mnemonic, ak, deviceInfo).then(async (result) => { + + // const userInfo = await usewallet.getUserInfo(true); + // setUsername(userInfo.username); + // handleClick(); + // }).catch((error) => { + // console.error('Error in sign in wallet request:', error); + // }); + }) + .catch((error) => { + console.error('Error in second wallet request:', error); + }); + } + }) + .catch((error) => { + console.error('Error in first wallet request:', error); + }); + }, + [ + confirmMnemonic, + currentNetwork, + getAccountKey, + getDeviceInfo, + handleClick, + mnemonic, + setAccountKey, + setDeviceInfo, + ] + ); + + useEffect(() => { + const createWeb3Wallet = async () => { + try { + const wallet = await SignClient.init({ + // @ts-ignore: Unreachable code error + core: new Core({ + projectId: process.env.WC_PROJECTID, + }), + metadata: { + name: 'Flow Walllet', + description: 'Digital wallet created for everyone.', + url: 'https://fcw-link.lilico.app', + icons: ['https://fcw-link.lilico.app/logo.png'], + }, + }); + await _subscribeToEvents(wallet); + + try { + const { uri, approval } = await wallet.connect({ + requiredNamespaces: { + flow: { + methods: [FCLWalletConnectMethod.accountInfo, FCLWalletConnectMethod.addDeviceInfo], + chains: [`flow:${currentNetwork}`], + events: [], + }, + }, + }); + + // Open QRCode modal if a URI was returned (i.e. we're not connecting an existing pairing). + if (uri) { + console.log('uri ', uri); + await setUri(uri); + // Await session approval from the wallet. + const session = await approval(); + await onSessionConnected(session); + + console.log('session ', session); + sendRequest(wallet, session.topic); + + // onSessionConnect(session) + // Close the QRCode modal in case it was open. + } + } catch (e) { + console.error(e); + } + await setWeb3Wallet(wallet); + console.log('web3wallet', web3wallet); + } catch (e) { + console.error(e); + } + }; + createWeb3Wallet(); + }, [_subscribeToEvents, currentNetwork, onSessionConnected, sendRequest, web3wallet]); return ( - - + <> {/* */} - + ); }; diff --git a/src/ui/views/AddWelcome/Sync/index.tsx b/src/ui/views/AddWelcome/Sync/index.tsx index e5a21829..79e54925 100644 --- a/src/ui/views/AddWelcome/Sync/index.tsx +++ b/src/ui/views/AddWelcome/Sync/index.tsx @@ -1,18 +1,20 @@ -import React, { useState, useEffect } from 'react'; +import { IconButton } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useState, useEffect, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; -import { Box, ThemeProvider } from '@mui/system'; -import { IconButton, Typography, Button, Snackbar, Alert } from '@mui/material'; + +import Confetti from '@/ui/FRWComponent/Confetti'; +import SlideLeftRight from '@/ui/FRWComponent/SlideLeftRight'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { LLPinAlert } from 'ui/FRWComponent'; +import { useWallet } from 'ui/utils'; + import BackButtonIcon from '../../../../components/iconfont/IconBackButton'; -import IconGoogleDrive from '../../../../components/iconfont/IconGoogleDrive'; -import theme from '../../../style/LLTheme'; -import RegisterHeader from '../AddRegister/RegisterHeader'; import AllSet from '../AddRegister/AllSet'; +import RegisterHeader from '../AddRegister/RegisterHeader'; + import SetPassword from './SetPassword'; import SyncQr from './SyncQr'; -import Particles from 'react-tsparticles'; -import { LLPinAlert, LLSpinner } from 'ui/FRWComponent'; -import { ComponentTransition, AnimationTypes } from 'react-component-transition'; -import { useWallet, Options } from 'ui/utils'; enum Direction { Right, @@ -25,10 +27,7 @@ const Sync = () => { const [activeIndex, onChange] = useState(0); const [mnemonic, setMnemonic] = useState(''); const [username, setUsername] = useState(''); - const [errMessage, setErrorMessage] = useState(chrome.i18n.getMessage('No__backup__found')); - const [showError, setShowError] = useState(false); const [direction, setDirection] = useState(Direction.Right); - const [loading, setLoading] = useState(false); const [accountKey, setAccountKey] = useState(null); const [deviceInfo, setDeviceInfo] = useState(null); @@ -36,7 +35,7 @@ const Sync = () => { setUsername(username.toLowerCase()); }; - const loadView = async () => { + const loadView = useCallback(async () => { // console.log(wallet); wallet .getCurrentAccount() @@ -48,7 +47,7 @@ const Sync = () => { .catch(() => { return; }); - }; + }, [history, wallet]); const goNext = () => { setDirection(Direction.Right); if (activeIndex < 2) { @@ -67,13 +66,6 @@ const Sync = () => { } }; - const handleErrorClose = (event?: React.SyntheticEvent | Event, reason?: string) => { - if (reason === 'clickaway') { - return; - } - setShowError(false); - }; - const page = (index) => { switch (index) { case 0: @@ -106,12 +98,11 @@ const Sync = () => { }; useEffect(() => { - console.log('wallet'); loadView(); - }, []); + }, [loadView]); return ( - + <> { alignItems: 'center', }} > - {activeIndex == 2 && ( - - )} + {activeIndex === 2 && } - + {/* height why not use auto */} @@ -159,31 +145,15 @@ const Sync = () => { )} - + {page(activeIndex)} - + - + ); }; diff --git a/src/ui/views/AddWelcome/index.tsx b/src/ui/views/AddWelcome/index.tsx index 559b595f..c7f9fa9c 100644 --- a/src/ui/views/AddWelcome/index.tsx +++ b/src/ui/views/AddWelcome/index.tsx @@ -1,20 +1,19 @@ +import { Typography, Button, CardMedia } from '@mui/material'; +import { Box } from '@mui/system'; import React from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Typography, Button, CssBaseline, CardMedia } from '@mui/material'; -import theme from '../../style/LLTheme'; -import RegisterHeader from '../Register/RegisterHeader'; +import { Link } from 'react-router-dom'; + +import IconFlow from '../../../components/iconfont/IconFlow'; import appicon from '../../FRWAssets/image/appicon.png'; import create from '../../FRWAssets/svg/create.svg'; import importPng from '../../FRWAssets/svg/import.svg'; -import qr from '../../FRWAssets/svg/scanIcon.svg'; import outside from '../../FRWAssets/svg/importoutside.svg'; -import { Link } from 'react-router-dom'; -import IconFlow from '../../../components/iconfont/IconFlow'; +import qr from '../../FRWAssets/svg/scanIcon.svg'; +import RegisterHeader from '../Register/RegisterHeader'; const AddWelcome = () => { return ( - - + <> { margin: '24px 0 44px', }} > - {/* {chrome.i18n.getMessage('appDescription')} {' '} */} - {chrome.i18n.getMessage('A_crypto_wallet_on_Flow')} - - {chrome.i18n.getMessage('Explorers_Collectors_and_Gamers')} - + {chrome.i18n.getMessage('A_crypto_wallet_on_Flow')}{' '} + + {chrome.i18n.getMessage('Explorers_Collectors_and_Gamers')} + - {/* - */} - - + ); }; diff --git a/src/ui/views/AddressImport/GoogleImport/DecryptWallet.tsx b/src/ui/views/AddressImport/GoogleImport/DecryptWallet.tsx index 85d3d3cb..53660340 100644 --- a/src/ui/views/AddressImport/GoogleImport/DecryptWallet.tsx +++ b/src/ui/views/AddressImport/GoogleImport/DecryptWallet.tsx @@ -1,22 +1,15 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; -import { - Button, - Typography, - IconButton, - Input, - InputAdornment, - FormGroup, - CssBaseline, -} from '@mui/material'; -import CancelIcon from '../../../../components/iconfont/IconClose'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import VisibilityIcon from '@mui/icons-material/Visibility'; -import { Presets } from 'react-component-transition'; -import theme from '../../../style/LLTheme'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; +import { Button, Typography, IconButton, Input, InputAdornment, FormGroup } from '@mui/material'; +import { makeStyles } from '@mui/styles'; +import { Box } from '@mui/system'; +import React, { useEffect, useState } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; import { useWallet } from 'ui/utils'; +import CancelIcon from '../../../../components/iconfont/IconClose'; + // const helperTextStyles = makeStyles(() => ({ // root: { // size: '16px', @@ -124,8 +117,7 @@ const DecryptWallet = ({ handleClick, setMnemonic, username, setNextPassword }) }, [password]); return ( - - + <> {chrome.i18n.getMessage('Welcome__Back')} @@ -169,7 +161,9 @@ const DecryptWallet = ({ handleClick, setMnemonic, username, setNextPassword }) } /> - {password && helperText} + + {helperText} + @@ -193,7 +187,7 @@ const DecryptWallet = ({ handleClick, setMnemonic, username, setNextPassword }) - + ); }; diff --git a/src/ui/views/AddressImport/GoogleImport/GoogleAccounts.tsx b/src/ui/views/AddressImport/GoogleImport/GoogleAccounts.tsx index 76cc4e6f..2b91ed3a 100644 --- a/src/ui/views/AddressImport/GoogleImport/GoogleAccounts.tsx +++ b/src/ui/views/AddressImport/GoogleImport/GoogleAccounts.tsx @@ -1,5 +1,4 @@ -import React, { useState, useEffect } from 'react'; -import { ThemeProvider } from '@mui/system'; +import ArrowForwardRoundedIcon from '@mui/icons-material/ArrowForwardRounded'; import { Typography, Avatar, @@ -10,10 +9,9 @@ import { ListItemText, IconButton, ListItem, - CssBaseline, } from '@mui/material'; -import theme from '../../../style/LLTheme'; -import ArrowForwardRoundedIcon from '@mui/icons-material/ArrowForwardRounded'; +import React, { useState, useEffect, useCallback } from 'react'; + import { useWallet } from 'ui/utils'; const FetchAvatar = ({ username }) => { @@ -22,27 +20,27 @@ const FetchAvatar = ({ username }) => { ); const wallet = useWallet(); - const fetchUserAvatar = async (username) => { - const { data } = await wallet.openapi.searchUser(username); - const users = data.users; - if (users.length > 0 && users[0].avatar) { - setAvatar(users[0].avatar); - } - }; + const fetchUserAvatar = useCallback( + async (username) => { + const { data } = await wallet.openapi.searchUser(username); + const users = data.users; + if (users.length > 0 && users[0].avatar) { + setAvatar(users[0].avatar); + } + }, + [wallet] + ); useEffect(() => { fetchUserAvatar(username); - }, []); + }, [fetchUserAvatar, username]); return ; }; const GoogleAccounts = ({ handleClick, accounts, setUsername }) => { - const [canGoNext, setCanGoNext] = useState(true); - return ( - - + <> {chrome.i18n.getMessage('We__ve__found') + ' '} @@ -104,7 +102,7 @@ const GoogleAccounts = ({ handleClick, accounts, setUsername }) => { - + ); }; diff --git a/src/ui/views/AddressImport/GoogleImport/RecoverPassword.tsx b/src/ui/views/AddressImport/GoogleImport/RecoverPassword.tsx index 36cab46f..89b8508d 100644 --- a/src/ui/views/AddressImport/GoogleImport/RecoverPassword.tsx +++ b/src/ui/views/AddressImport/GoogleImport/RecoverPassword.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Button, Typography, @@ -11,21 +10,20 @@ import { LinearProgress, Alert, Snackbar, - CssBaseline, } from '@mui/material'; - -import { LLSpinner } from 'ui/FRWComponent'; -import CancelIcon from '../../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; -import VisibilityIcon from '@mui/icons-material/Visibility'; -import FormControlLabel from '@mui/material/FormControlLabel'; import Checkbox from '@mui/material/Checkbox'; -import { Presets } from 'react-component-transition'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import { makeStyles, styled } from '@mui/styles'; +import { Box } from '@mui/system'; +import React, { useEffect, useState } from 'react'; import zxcvbn from 'zxcvbn'; -import theme from '../../../style/LLTheme'; + +import { LLSpinner, LLNotFound } from '@/ui/FRWComponent'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; import { useWallet, saveIndex } from 'ui/utils'; -import { LLNotFound } from 'ui/FRWComponent'; + +import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../../components/iconfont/IconClose'; // const helperTextStyles = makeStyles(() => ({ // root: { @@ -253,11 +251,10 @@ const SetPassword = ({ handleClick, mnemonic, username, lastPassword }) => { setPassword(''); setConfirmPassword(''); } - }, [isCheck]); + }, [isCheck, lastPassword]); return ( - - + <> {!showDialog ? ( @@ -305,7 +302,9 @@ const SetPassword = ({ handleClick, mnemonic, username, lastPassword }) => { } /> - {password && helperText} + + {helperText} + { } /> - - {confirmPassword && helperMatch} - + + {helperMatch} + @@ -383,7 +382,7 @@ const SetPassword = ({ handleClick, mnemonic, username, lastPassword }) => { {errorMessage} - + ); }; diff --git a/src/ui/views/AddressImport/GoogleImport/RecoveryPhrase.tsx b/src/ui/views/AddressImport/GoogleImport/RecoveryPhrase.tsx index c9e2a32f..456f377a 100644 --- a/src/ui/views/AddressImport/GoogleImport/RecoveryPhrase.tsx +++ b/src/ui/views/AddressImport/GoogleImport/RecoveryPhrase.tsx @@ -1,20 +1,20 @@ -import React, { useState } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, IconButton, CssBaseline } from '@mui/material'; -import theme from '../../../style/LLTheme'; -import LockRoundedIcon from '@mui/icons-material/LockRounded'; +import InfoIcon from '@mui/icons-material/Info'; import LockOpenRoundedIcon from '@mui/icons-material/LockOpenRounded'; +import LockRoundedIcon from '@mui/icons-material/LockRounded'; +import { Button, Typography, IconButton } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useState } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; + import IconCopy from '../../../../components/iconfont/IconCopy'; -import { Presets } from 'react-component-transition'; -import InfoIcon from '@mui/icons-material/Info'; const RecoveryPhrase = ({ handleClick, mnemonic }) => { const [canGoNext, setCanGoNext] = useState(true); const [isCoverBlur, coverBlur] = useState(false); return ( - - + <> {chrome.i18n.getMessage('Review') + ' '} @@ -191,7 +191,7 @@ const RecoveryPhrase = ({ handleClick, mnemonic }) => { marginBottom: '8px', }} > - + { )} - + - + ); }; diff --git a/src/ui/views/AddressImport/GoogleImport/index.tsx b/src/ui/views/AddressImport/GoogleImport/index.tsx index 27f5eb74..017f7fe2 100644 --- a/src/ui/views/AddressImport/GoogleImport/index.tsx +++ b/src/ui/views/AddressImport/GoogleImport/index.tsx @@ -1,19 +1,20 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory, useLocation } from 'react-router-dom'; -import { Box, ThemeProvider } from '@mui/system'; import { IconButton, Typography } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useHistory, useLocation } from 'react-router-dom'; + +import { LLPinAlert } from '@/ui/FRWComponent'; +import Confetti from '@/ui/FRWComponent/Confetti'; +import SlideLeftRight from '@/ui/FRWComponent/SlideLeftRight'; + import BackButtonIcon from '../../../../components/iconfont/IconBackButton'; -import theme from '../../../style/LLTheme'; -import RegisterHeader from '../../Register/RegisterHeader'; import AllSet from '../../Register/AllSet'; +import RegisterHeader from '../../Register/RegisterHeader'; + import DecryptWallet from './DecryptWallet'; -import RecoveryPhrase from './RecoveryPhrase'; import GoogleAccounts from './GoogleAccounts'; import RecoveryPassword from './RecoverPassword'; -import Particles from 'react-tsparticles'; -import { ComponentTransition, AnimationTypes } from 'react-component-transition'; -import { LLPinAlert } from '@/ui/FRWComponent'; -import options from '../../Import/options'; +import RecoveryPhrase from './RecoveryPhrase'; enum Direction { Right, @@ -52,16 +53,16 @@ const GoogleImport = () => { } }; - const getGoogleAccounts = async () => { + const getGoogleAccounts = useCallback(async () => { // const backupFile = await storage.get('googleBackup'); // await setBackup(backupFile); - const users = location.state.accounts; + const users = location?.state?.accounts; setAccounts(users); - }; + }, [location?.state?.accounts]); useEffect(() => { getGoogleAccounts(); - }, []); + }, [getGoogleAccounts]); const page = (index) => { switch (index) { @@ -96,10 +97,8 @@ const GoogleImport = () => { } }; - const heights = [500, 500, 600, 600, 500]; - return ( - + <> { alignItems: 'center', }} > - {activeIndex == 4 && ( - - )} - + {activeIndex === 4 && } + { - + {page(activeIndex)} - + - + ); }; diff --git a/src/ui/views/AddressImport/PickUsername.tsx b/src/ui/views/AddressImport/PickUsername.tsx index 3bbf384b..d5429db9 100644 --- a/src/ui/views/AddressImport/PickUsername.tsx +++ b/src/ui/views/AddressImport/PickUsername.tsx @@ -1,16 +1,24 @@ -import React, { useEffect, useState } from 'react'; +import { + CircularProgress, + IconButton, + Button, + Typography, + FormControl, + Input, + InputAdornment, +} from '@mui/material'; import { makeStyles } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, FormControl, Input, InputAdornment, CssBaseline } from '@mui/material'; -import CancelIcon from '../../../components/iconfont/IconClose'; +import { Box } from '@mui/system'; +import React, { useCallback, useEffect, useState } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { useWallet } from 'ui/utils'; + import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; -import theme from '../../style/LLTheme'; +import CancelIcon from '../../../components/iconfont/IconClose'; import EmailIcon from '../../assets/alternate-email.svg'; -import { Presets } from 'react-component-transition'; -import { useWallet } from 'ui/utils'; -import { CircularProgress, IconButton } from '@mui/material'; -const useStyles = makeStyles((theme) => ({ +const useStyles = makeStyles((_theme) => ({ customInputLabel: { '& legend': { visibility: 'visible', @@ -31,71 +39,69 @@ const useStyles = makeStyles((theme) => ({ }, })); +const UsernameError = ({ errorMsg }: { errorMsg: string }) => ( + + + + {errorMsg} + {errorMsg.startsWith('This username is reserved') && ( + + hi@lilico.app + {chrome.i18n.getMessage('for__any__inquiry')} + + )} + + +); + +const UsernameCorrect = () => ( + + + + {chrome.i18n.getMessage('Sounds_good')} + + +); +const UsernameLoading = () => ( + + + {chrome.i18n.getMessage('Checking')} + +); + const PickUsername = ({ handleClick, savedUsername, getUsername }) => { const classes = useStyles(); const wallet = useWallet(); const [isLoading, setLoading] = useState(false); const [usernameValid, setUsernameValid] = useState(false); - const usernameError = (errorMsg) => ( - - - - {errorMsg} - {errorMsg.startsWith('This username is reserved') && ( - - hi@lilico.app - {chrome.i18n.getMessage('for__any__inquiry')} - - )} - - - ); - const usernameCorrect = ( - - - - {chrome.i18n.getMessage('Sounds_good')} - - - ); - const usernameLoading = () => ( - - - {chrome.i18n.getMessage('Checking')} - - ); - const [username, setUsername] = useState(savedUsername || ''); const [helperText, setHelperText] = useState(
); - const regex = /^[A-Za-z0-9]{3,15}$/; - - const setErrorMessage = (message: string) => { - setLoading(false); - setUsernameValid(false); - setHelperText(usernameError(message)); - }; + const setErrorMessage = useCallback( + (message: string) => { + setLoading(false); + setUsernameValid(false); + setHelperText(); + }, + [setLoading, setUsernameValid, setHelperText] + ); useEffect(() => { setUsernameValid(false); - setHelperText(usernameLoading); + setHelperText(); setLoading(true); const delayDebounceFn = setTimeout(() => { if (username.length < 3) { @@ -107,6 +113,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { setErrorMessage(chrome.i18n.getMessage('Too__long')); return; } + const regex = /^[A-Za-z0-9]{3,15}$/; if (!regex.test(username)) { setErrorMessage( @@ -118,15 +125,15 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { .checkUsername(username.toLowerCase()) .then((response) => { setLoading(false); - if (response.data.username != username.toLowerCase()) { + if (response.data.username !== username.toLowerCase()) { setLoading(false); return; } if (response.data.unique) { setUsernameValid(true); - setHelperText(usernameCorrect); + setHelperText(); } else { - if (response.message == 'Username is reserved') { + if (response.message === 'Username is reserved') { setErrorMessage( chrome.i18n.getMessage('This__username__is__reserved__Please__contact') ); @@ -136,12 +143,13 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { } }) .catch((error) => { + console.error(error); setErrorMessage(chrome.i18n.getMessage('Oops__unexpected__error')); }); }, 500); return () => clearTimeout(delayDebounceFn); - }, [username]); + }, [setErrorMessage, username, wallet.openapi]); const msgBgColor = () => { if (isLoading) { @@ -151,8 +159,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { }; return ( - - + <> {chrome.i18n.getMessage('Pick__Your')} @@ -196,20 +203,18 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { } /> - - {username && ( - - {helperText} - - )} - + + + {helperText} + + @@ -233,7 +238,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { - + ); }; diff --git a/src/ui/views/AddressImport/RecoverPassword.tsx b/src/ui/views/AddressImport/RecoverPassword.tsx index daa6a9ff..e9d4a0d5 100644 --- a/src/ui/views/AddressImport/RecoverPassword.tsx +++ b/src/ui/views/AddressImport/RecoverPassword.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Button, Typography, @@ -11,18 +10,19 @@ import { LinearProgress, Alert, Snackbar, - CssBaseline, } from '@mui/material'; -import { LLSpinner } from 'ui/FRWComponent'; -import CancelIcon from '../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; -import VisibilityIcon from '@mui/icons-material/Visibility'; -import { Presets } from 'react-component-transition'; +import { makeStyles, styled } from '@mui/styles'; +import { Box } from '@mui/system'; +import React, { useEffect, useState } from 'react'; import zxcvbn from 'zxcvbn'; -import theme from '../../style/LLTheme'; -import { useWallet, saveIndex } from 'ui/utils'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; import { storage } from 'background/webapi'; +import { LLSpinner } from 'ui/FRWComponent'; +import { useWallet, saveIndex } from 'ui/utils'; + +import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../components/iconfont/IconClose'; // const helperTextStyles = makeStyles(() => ({ // root: { @@ -241,8 +241,7 @@ const SetPassword = ({ handleClick, mnemonic, pk, username, goEnd }) => { }, [confirmPassword, password]); return ( - - + <> {chrome.i18n.getMessage('Welcome__Back')} @@ -289,7 +288,9 @@ const SetPassword = ({ handleClick, mnemonic, pk, username, goEnd }) => { } /> - {password && helperText} + + {helperText} + { } /> - - {confirmPassword && helperMatch} - + + {helperMatch} + @@ -377,7 +378,7 @@ const SetPassword = ({ handleClick, mnemonic, pk, username, goEnd }) => { {errorMessage} - + ); }; diff --git a/src/ui/views/AddressImport/SetPassword.tsx b/src/ui/views/AddressImport/SetPassword.tsx index c1742704..ae326a5e 100644 --- a/src/ui/views/AddressImport/SetPassword.tsx +++ b/src/ui/views/AddressImport/SetPassword.tsx @@ -11,24 +11,23 @@ import { InputAdornment, FormGroup, LinearProgress, - CssBaseline, } from '@mui/material'; import Checkbox from '@mui/material/Checkbox'; import FormControlLabel from '@mui/material/FormControlLabel'; import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import { Box } from '@mui/system'; import React, { useEffect, useState } from 'react'; -import { Presets } from 'react-component-transition'; import zxcvbn from 'zxcvbn'; import { storage } from '@/background/webapi'; import { getHashAlgo, getSignAlgo } from '@/shared/utils/algo'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { AccountKey } from 'background/service/networkModel'; import { LLSpinner } from 'ui/FRWComponent'; import { useWallet, saveIndex } from 'ui/utils'; import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; import CancelIcon from '../../../components/iconfont/IconClose'; -import theme from '../../style/LLTheme'; const useStyles = makeStyles(() => ({ customInputLabel: { @@ -275,8 +274,7 @@ const SetPassword = ({ handleClick, mnemonic, pk, username, setExPassword, accou }, [confirmPassword, password]); return ( - - + <> {chrome.i18n.getMessage('Create')} @@ -323,9 +321,9 @@ const SetPassword = ({ handleClick, mnemonic, pk, username, setExPassword, accou } /> - - {password && helperText} - + + {helperText} + } /> - - {confirmPassword && helperMatch} - + + {helperMatch} + @@ -413,7 +411,7 @@ const SetPassword = ({ handleClick, mnemonic, pk, username, setExPassword, accou {errMessage} - + ); }; diff --git a/src/ui/views/AddressImport/index.tsx b/src/ui/views/AddressImport/index.tsx index 45ce3389..196a5968 100644 --- a/src/ui/views/AddressImport/index.tsx +++ b/src/ui/views/AddressImport/index.tsx @@ -1,22 +1,21 @@ -import React, { useState, useEffect } from 'react'; +import { IconButton, Typography } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useState, useEffect, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; -import { Box, ThemeProvider } from '@mui/system'; -import { IconButton, Typography, Button, Snackbar, Alert } from '@mui/material'; + +import Confetti from '@/ui/FRWComponent/Confetti'; +import SlideLeftRight from '@/ui/FRWComponent/SlideLeftRight'; +import { LLPinAlert } from 'ui/FRWComponent'; +import { useWallet } from 'ui/utils'; + import BackButtonIcon from '../../../components/iconfont/IconBackButton'; -import IconGoogleDrive from '../../../components/iconfont/IconGoogleDrive'; -import theme from '../../style/LLTheme'; -import RegisterHeader from '../Register/RegisterHeader'; import AllSet from '../Register/AllSet'; -import SeedPhrase from './importComponent/SeedPhrase'; +import RegisterHeader from '../Register/RegisterHeader'; + +import ImportPager from './ImportPager'; import PickUsername from './PickUsername'; -import SetPassword from './SetPassword'; -import GoogleBackup from './GoogleBackup'; import RecoverPassword from './RecoverPassword'; -import Particles from 'react-tsparticles'; -import { LLPinAlert, LLSpinner } from 'ui/FRWComponent'; -import { ComponentTransition, AnimationTypes } from 'react-component-transition'; -import { useWallet, Options } from 'ui/utils'; -import ImportPager from './ImportPager'; +import SetPassword from './SetPassword'; enum Direction { Right, @@ -30,19 +29,17 @@ const AddressImport = () => { const [mnemonic, setMnemonic] = useState(''); const [pk, setPk] = useState(null); const [username, setUsername] = useState(''); - const [errMessage, setErrorMessage] = useState(chrome.i18n.getMessage('No__backup__found')); - const [showError, setShowError] = useState(false); + const [, setErrorMessage] = useState(chrome.i18n.getMessage('No__backup__found')); + const [, setShowError] = useState(false); const [direction, setDirection] = useState(Direction.Right); - const [loading, setLoading] = useState(false); - const [password, setPassword] = useState(null); + const [, setPassword] = useState(null); const [accounts, setAccounts] = useState([]); - const [isImport, setImport] = useState(false); const getUsername = (username: string) => { setUsername(username.toLowerCase()); }; - const loadView = async () => { + const loadView = useCallback(async () => { // console.log(wallet); wallet .getCurrentAccount() @@ -54,7 +51,8 @@ const AddressImport = () => { .catch(() => { return; }); - }; + }, [wallet, history]); + const goNext = () => { setDirection(Direction.Right); if (activeIndex < 4) { @@ -138,12 +136,11 @@ const AddressImport = () => { }; useEffect(() => { - console.log('wallet'); loadView(); - }, []); + }, [loadView]); return ( - + <> { alignItems: 'center', }} > - {activeIndex == 4 && ( - - )} + {activeIndex === 4 && } - + {/* height why not use auto */} @@ -210,30 +202,67 @@ const AddressImport = () => { - + + + + + + + + + + + + - {page(activeIndex)} - + + - + ); }; diff --git a/src/ui/views/Approval/components/Confimation.tsx b/src/ui/views/Approval/components/Confimation.tsx index e957c487..bee1241b 100644 --- a/src/ui/views/Approval/components/Confimation.tsx +++ b/src/ui/views/Approval/components/Confimation.tsx @@ -1,33 +1,23 @@ -import React, { useEffect, useState } from 'react'; +import { Stack, Box } from '@mui/material'; +import * as fcl from '@onflow/fcl'; +import dedent from 'dedent'; +import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; + +import { + LLPrimaryButton, + LLSecondaryButton, + LLConnectLoading, + LLLinkingLoading, +} from '@/ui/FRWComponent'; +import { type UserInfoResponse } from 'background/service/networkModel'; import { useApproval, useWallet } from 'ui/utils'; // import { CHAINS_ENUM } from 'consts'; -import { ThemeProvider } from '@mui/system'; -import { - Stack, - Box, - Typography, - Divider, - Accordion, - AccordionSummary, - AccordionDetails, -} from '@mui/material'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import theme from 'ui/style/LLTheme'; -import * as fcl from '@onflow/fcl'; -import { LLPrimaryButton, LLSecondaryButton } from 'ui/FRWComponent'; -import Highlight from 'react-highlight'; + import './github-dark-dimmed.css'; -import * as secp from '@noble/secp256k1'; -import { SHA3 } from 'sha3'; -import IconFlow from '../../../../components/iconfont/IconFlow'; + import { DefaultBlock } from './DefaultBlock'; import { LinkingBlock } from './LinkingBlock'; -import { LLConnectLoading, LLLinkingLoading } from '@/ui/FRWComponent'; -import { UserInfoResponse } from 'background/service/networkModel'; -import dedent from 'dedent'; -import GppGoodRoundedIcon from '@mui/icons-material/GppGoodRounded'; -import { Presets } from 'react-component-transition'; interface ConnectProps { params: any; @@ -35,7 +25,7 @@ interface ConnectProps { // defaultChain: CHAINS_ENUM; } -const Confimation = ({ params: { icon, origin, tabId, type } }: ConnectProps) => { +const Confirmation = ({ params: { icon, origin, tabId, type } }: ConnectProps) => { const [, resolveApproval, rejectApproval, linkningConfirm] = useApproval(); const { t } = useTranslation(); const wallet = useWallet(); @@ -85,10 +75,10 @@ const Confimation = ({ params: { icon, origin, tabId, type } }: ConnectProps) => sig: string | null; } - const getUserInfo = async () => { + const getUserInfo = useCallback(async () => { const userResult = await wallet.getUserInfo(false); await setUserInfo(userResult); - }; + }, [wallet]); // useEffect(() => { // getUserInfo(); @@ -100,17 +90,20 @@ const Confimation = ({ params: { icon, origin, tabId, type } }: ConnectProps) => // } // }, [accountArgs]) - const fetchTxInfo = async (cadence: string) => { - // const account = await wallet.getCurrentAccount(); - const network = await wallet.getNetwork(); - const result = await wallet.openapi.getTransactionTemplate(cadence, network); - if (result != null) { - setAuditor(result); - setExpanded(false); - } - }; + const fetchTxInfo = useCallback( + async (cadence: string) => { + // const account = await wallet.getCurrentAccount(); + const network = await wallet.getNetwork(); + const result = await wallet.openapi.getTransactionTemplate(cadence, network); + if (result !== null) { + setAuditor(result); + setExpanded(false); + } + }, + [wallet] + ); - const handleCancel = () => { + const handleCancel = useCallback(() => { if (opener) { if (windowId) { chrome.windows.update(windowId, { focused: true }); @@ -126,28 +119,33 @@ const Confimation = ({ params: { icon, origin, tabId, type } }: ConnectProps) => setApproval(false); rejectApproval('User rejected the request.'); } - }; - - const fclCallback = (data) => { - if (typeof data != 'object') return; - if (data.type !== 'FCL:VIEW:READY:RESPONSE') return; - const newSignable: Signable = data.body; - const hostname = data.config?.client?.hostname; - hostname && setHost(hostname); - setImage(data.config.app.icon); - setAccountTitle(data.config.app.title); - const firstLine = newSignable.cadence.trim().split('\n')[0]; - - const isAccountLinking = firstLine.includes('#allowAccountLinking'); - setAccountLinking(isAccountLinking); - if (isAccountLinking) { - setAccountArgs(newSignable['args']); - } - setSignable(newSignable); - getUserInfo(); + }, [opener, windowId, rejectApproval]); + + const fclCallback = useCallback( + (data) => { + if (typeof data !== 'object') return; + if (data.type !== 'FCL:VIEW:READY:RESPONSE') return; + const newSignable: Signable = data.body; + const hostname = data.config?.client?.hostname; + if (hostname) { + setHost(hostname); + } + setImage(data.config.app.icon); + setAccountTitle(data.config.app.title); + const firstLine = newSignable.cadence.trim().split('\n')[0]; + + const isAccountLinking = firstLine.includes('#allowAccountLinking'); + setAccountLinking(isAccountLinking); + if (isAccountLinking) { + setAccountArgs(newSignable['args']); + } + setSignable(newSignable); + getUserInfo(); - fetchTxInfo(newSignable.cadence); - }; + fetchTxInfo(newSignable.cadence); + }, + [fetchTxInfo, getUserInfo] + ); const sendAuthzToFCL = async () => { console.log('sendAuthzToFCL ==>', signable); @@ -188,82 +186,92 @@ const Confimation = ({ params: { icon, origin, tabId, type } }: ConnectProps) => } }; - const sendSignature = (signable, signedMessage) => { - if (opener) { - chrome.tabs.sendMessage(opener, { - f_type: 'PollingResponse', - f_vsn: '1.0.0', - status: 'APPROVED', - reason: null, - data: new fcl.WalletUtils.CompositeSignature(signable.addr, signable.keyId, signedMessage), - }); - } - }; + const sendSignature = useCallback( + (signable, signedMessage) => { + if (opener) { + chrome.tabs.sendMessage(opener, { + f_type: 'PollingResponse', + f_vsn: '1.0.0', + status: 'APPROVED', + reason: null, + data: new fcl.WalletUtils.CompositeSignature( + signable.addr, + signable.keyId, + signedMessage + ), + }); + } + }, + [opener] + ); - const signPayer = async (signable) => { - setIsLoading(true); - const value = await sessionStorage.getItem('pendingRefBlockId'); + const signPayer = useCallback( + async (signable) => { + setIsLoading(true); + const value = await sessionStorage.getItem('pendingRefBlockId'); - console.log('signPayer ->', signable.voucher.refBlock, value, signable.roles.payer); + console.log('signPayer ->', signable.voucher.refBlock, value, signable.roles.payer); - if (signable.roles.payer !== true) { - return; - } + if (signable.roles.payer !== true) { + return; + } - if (signable.voucher.refBlock !== value) { - return; - } + if (signable.voucher.refBlock !== value) { + return; + } - try { - const signedMessage = await wallet.signPayer(signable); - sendSignature(signable, signedMessage); - setApproval(true); - // if (accountLinking) { - // await linkningConfirm(); - // } else { - // resolveApproval(); - // setIsLoading(false); - // } - resolveApproval(); - setIsLoading(false); - } catch (err) { - setIsLoading(false); - handleCancel(); - } - }; + try { + const signedMessage = await wallet.signPayer(signable); + sendSignature(signable, signedMessage); + setApproval(true); + // if (accountLinking) { + // await linkningConfirm(); + // } else { + // resolveApproval(); + // setIsLoading(false); + // } + resolveApproval(); + setIsLoading(false); + } catch (err) { + setIsLoading(false); + handleCancel(); + } + }, + [wallet, sendSignature, resolveApproval, handleCancel, setIsLoading] + ); - const loadPayer = async () => { + const loadPayer = useCallback(async () => { const isEnabled = await wallet.allowLilicoPay(); setLilicoEnabled(isEnabled); - }; + }, [wallet]); useEffect(() => { loadPayer(); return () => { sessionStorage.removeItem('pendingRefBlockId'); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore chrome.storage.session?.remove('pendingRefBlockId'); }; - }, []); + }, [loadPayer]); useEffect(() => { console.log('pendingRefBlockId ->', lilicoEnabled, signable, approval); if (lilicoEnabled && signable && signable.message && approval) { signPayer(signable); } - }, [signable]); + }, [approval, lilicoEnabled, signPayer, signable]); useEffect(() => { - chrome.tabs && + if (chrome.tabs) { chrome.tabs .query({ active: true, currentWindow: false, }) .then((tabs) => { - const targetTab = tabs.filter((item) => item.id == tabId); + const targetTab = tabs.filter((item) => item.id === tabId); let host = ''; if (targetTab[0].url) { @@ -276,6 +284,7 @@ const Confimation = ({ params: { icon, origin, tabId, type } }: ConnectProps) => setOpener(targetTab[0].id); chrome.tabs.sendMessage(targetTab[0].id || 0, { type: 'FCL:VIEW:READY' }); }); + } const extMessageHandler = (msg, sender, sendResponse) => { // console.log('extMessageHandler -->', msg); @@ -283,14 +292,18 @@ const Confimation = ({ params: { icon, origin, tabId, type } }: ConnectProps) => if (msg.type === 'FCL:VIEW:READY:RESPONSE') { console.log('extMessageHandler -->', msg.type, msg); - msg.host && setHost(msg.host); + if (msg.host) { + setHost(msg.host); + } if (msg.config?.app?.title) { setTitle(msg.config.app.title); } if (msg.config?.app?.icon) { setLogo(msg.config.app.icon); } - setCadenceScript(msg.body.cadence); + if (msg.body?.cadence) { + setCadenceScript(msg.body.cadence); + } if (msg.body?.args?.length > 0) { setCadenceArguments(msg.body.args); } @@ -318,7 +331,7 @@ const Confimation = ({ params: { icon, origin, tabId, type } }: ConnectProps) => console.log('removeListener'); }); }; - }, []); + }, [fclCallback, tabId]); window.onbeforeunload = () => { if (!approval) { @@ -327,7 +340,7 @@ const Confimation = ({ params: { icon, origin, tabId, type } }: ConnectProps) => }; return ( - + <> {isLoading ? ( {accountLinking ? ( @@ -387,8 +400,8 @@ const Confimation = ({ params: { icon, origin, tabId, type } }: ConnectProps) => )} - + ); }; -export default Confimation; +export default Confirmation; diff --git a/src/ui/views/Approval/components/Connect.tsx b/src/ui/views/Approval/components/Connect.tsx index 6eae4ff5..c8baa075 100644 --- a/src/ui/views/Approval/components/Connect.tsx +++ b/src/ui/views/Approval/components/Connect.tsx @@ -1,5 +1,4 @@ import { Stack, Box, Typography, Divider, CardMedia } from '@mui/material'; -import { ThemeProvider } from '@mui/system'; import { WalletUtils } from '@onflow/fcl'; import React, { useCallback, useEffect, useState } from 'react'; @@ -12,7 +11,6 @@ import linkGlobe from 'ui/FRWAssets/svg/linkGlobe.svg'; import mainnetsvg from 'ui/FRWAssets/svg/mainnet.svg'; import testnetsvg from 'ui/FRWAssets/svg/testnet.svg'; import { LLPrimaryButton, LLSecondaryButton, LLConnectLoading } from 'ui/FRWComponent'; -import theme from 'ui/style/LLTheme'; import { useApproval, useWallet } from 'ui/utils'; // import { CHAINS_ENUM } from 'consts'; @@ -401,7 +399,7 @@ const Connect = ({ params: { /*icon, origin,*/ tabId } }: ConnectProps) => { ); return ( - + <> {showSwitch ? ( { ) : ( {renderContent()} )} - + ); }; diff --git a/src/ui/views/Approval/components/DefaultBlock.tsx b/src/ui/views/Approval/components/DefaultBlock.tsx index a07b7e69..8db7a2da 100644 --- a/src/ui/views/Approval/components/DefaultBlock.tsx +++ b/src/ui/views/Approval/components/DefaultBlock.tsx @@ -1,4 +1,5 @@ -import React from 'react'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import GppGoodRoundedIcon from '@mui/icons-material/GppGoodRounded'; import { Stack, Box, @@ -8,13 +9,12 @@ import { AccordionSummary, AccordionDetails, } from '@mui/material'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import GppGoodRoundedIcon from '@mui/icons-material/GppGoodRounded'; -import { useHistory } from 'react-router-dom'; -import { Presets } from 'react-component-transition'; -import IconFlow from '../../../../components/iconfont/IconFlow'; +import Fade from '@mui/material/Fade'; +import React from 'react'; import Highlight from 'react-highlight'; +import IconFlow from '../../../../components/iconfont/IconFlow'; + export const DefaultBlock = ({ title, host, @@ -27,7 +27,29 @@ export const DefaultBlock = ({ setExpanded, dedent, }) => { - const history = useHistory(); + const processItem = (item) => { + if (Array.isArray(item)) { + return `[ ${item.map((value) => processItem(value)).join(', ')} ]`; + } else if (typeof item === 'object' && item !== null) { + if (item.type && item.value !== undefined) { + return `${processItem(item.value)}`; + } else { + return `${Object.entries(item) + .map(([_, value]) => processItem(value)) + .join('\n')}`; + } + } else { + return JSON.stringify(item); + } + }; + + const displayCadenceArguments = (cadenceArguments) => { + if (!Array.isArray(cadenceArguments)) { + console.error('cadenceArguments is not an array:', cadenceArguments); + return; + } + return cadenceArguments.map((item) => processItem(item)).join('\n\n'); + }; return ( @@ -50,7 +72,7 @@ export const DefaultBlock = ({ - + {auditor && ( )} - + - {`[\n${cadenceArguments.map((item) => `\t${item.value}: ${item.type}`).join(',\n')}\n]`} + {`\n${displayCadenceArguments(cadenceArguments)}\n`} diff --git a/src/ui/views/Approval/components/EthApproval/EthConfirm/DefaultBlock.tsx b/src/ui/views/Approval/components/EthApproval/EthConfirm/DefaultBlock.tsx index 49275ac7..bd34ba9f 100644 --- a/src/ui/views/Approval/components/EthApproval/EthConfirm/DefaultBlock.tsx +++ b/src/ui/views/Approval/components/EthApproval/EthConfirm/DefaultBlock.tsx @@ -1,4 +1,5 @@ -import React, { useState, useEffect } from 'react'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import GppGoodRoundedIcon from '@mui/icons-material/GppGoodRounded'; import { Stack, Box, @@ -8,15 +9,15 @@ import { AccordionSummary, AccordionDetails, } from '@mui/material'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import GppGoodRoundedIcon from '@mui/icons-material/GppGoodRounded'; -import { useHistory } from 'react-router-dom'; -import { Presets } from 'react-component-transition'; -import IconFlow from '../../../../../../components/iconfont/IconFlow'; +import React, { useState, useEffect } from 'react'; import Highlight from 'react-highlight'; +import { useHistory } from 'react-router-dom'; + import { getScripts } from 'background/utils'; import placeholder from 'ui/FRWAssets/image/placeholder.png'; +import IconFlow from '../../../../../../components/iconfont/IconFlow'; + export const DefaultBlock = ({ title, host, diff --git a/src/ui/views/Approval/components/EthApproval/EthConfirm/index.tsx b/src/ui/views/Approval/components/EthApproval/EthConfirm/index.tsx index 524bf07a..5530099d 100644 --- a/src/ui/views/Approval/components/EthApproval/EthConfirm/index.tsx +++ b/src/ui/views/Approval/components/EthApproval/EthConfirm/index.tsx @@ -1,35 +1,18 @@ -import React, { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useApproval, useWallet } from 'ui/utils'; -// import { CHAINS_ENUM } from 'consts'; -import { ThemeProvider } from '@mui/system'; -import { - Stack, - Box, - Typography, - Divider, - Accordion, - AccordionSummary, - AccordionDetails, -} from '@mui/material'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import theme from 'ui/style/LLTheme'; +import { Stack, Box } from '@mui/material'; import * as fcl from '@onflow/fcl'; +import dedent from 'dedent'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { LLConnectLoading, LLLinkingLoading } from '@/ui/FRWComponent'; +import { type UserInfoResponse } from 'background/service/networkModel'; import { LLPrimaryButton, LLSecondaryButton } from 'ui/FRWComponent'; -import Highlight from 'react-highlight'; -import * as secp from '@noble/secp256k1'; -import { SHA3 } from 'sha3'; +import { useApproval, useWallet } from 'ui/utils'; + import { DefaultBlock } from './DefaultBlock'; -import { LLConnectLoading, LLLinkingLoading } from '@/ui/FRWComponent'; -import { UserInfoResponse } from 'background/service/networkModel'; -import dedent from 'dedent'; -import GppGoodRoundedIcon from '@mui/icons-material/GppGoodRounded'; -import { Presets } from 'react-component-transition'; interface ConnectProps { params: any; - // onChainChange(chain: CHAINS_ENUM): void; - // defaultChain: CHAINS_ENUM; } const EthConfirm = ({ params }: ConnectProps) => { @@ -108,15 +91,15 @@ const EthConfirm = ({ params }: ConnectProps) => { const handleAllow = async () => { await checkCoa(); resolveApproval({ - defaultChain: 646, + defaultChain: 747, signPermission: 'MAINNET_AND_TESTNET', }); }; - const loadPayer = async () => { + const loadPayer = useCallback(async () => { const isEnabled = await usewallet.allowLilicoPay(); setLilicoEnabled(isEnabled); - }; + }, [usewallet]); const checkCoa = async () => { setLoading(true); @@ -140,10 +123,10 @@ const EthConfirm = ({ params }: ConnectProps) => { loadPayer(); extractData(params); } - }, []); + }, [loadPayer, params]); return ( - + <> {isLoading ? ( {accountLinking ? ( @@ -217,7 +200,7 @@ const EthConfirm = ({ params }: ConnectProps) => { )} - + ); }; diff --git a/src/ui/views/Approval/components/EthApproval/EthConnect/index.tsx b/src/ui/views/Approval/components/EthApproval/EthConnect/index.tsx index 731db874..7f1f7662 100644 --- a/src/ui/views/Approval/components/EthApproval/EthConnect/index.tsx +++ b/src/ui/views/Approval/components/EthApproval/EthConnect/index.tsx @@ -1,22 +1,21 @@ -import React, { useEffect, useState } from 'react'; -import { useLocation, useHistory } from 'react-router-dom'; +import { Stack, Box, Typography, Divider, CardMedia, Card } from '@mui/material'; +import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useApproval, useWallet, formatAddress } from 'ui/utils'; -import { isValidEthereumAddress } from 'ui/utils/address'; +import { useLocation, useHistory } from 'react-router-dom'; + import enableBg from 'ui/FRWAssets/image/enableBg.png'; -import { ThemeProvider } from '@mui/system'; -import { Stack, Box, Typography, Divider, CardMedia, Card } from '@mui/material'; -import linkGlobe from 'ui/FRWAssets/svg/linkGlobe.svg'; import flowgrey from 'ui/FRWAssets/svg/flow-grey.svg'; +import linkGlobe from 'ui/FRWAssets/svg/linkGlobe.svg'; +import { LLPrimaryButton, LLSecondaryButton, LLSpinner, LLConnectLoading } from 'ui/FRWComponent'; +import { useApproval, useWallet, formatAddress } from 'ui/utils'; +import { isValidEthereumAddress } from 'ui/utils/address'; + import CheckCircleIcon from '../../../../../../components/iconfont/IconCheckmark'; -import theme from 'ui/style/LLTheme'; + // import EthMove from '../EthMove'; -import { LLPrimaryButton, LLSecondaryButton, LLSpinner, LLConnectLoading } from 'ui/FRWComponent'; interface ConnectProps { params: any; - // onChainChange(chain: CHAINS_ENUM): void; - // defaultChain: CHAINS_ENUM; } const EthConnect = ({ params: { icon, name, origin } }: ConnectProps) => { @@ -24,7 +23,6 @@ const EthConnect = ({ params: { icon, name, origin } }: ConnectProps) => { showChainsModal?: boolean; }>(); const { showChainsModal = false } = state ?? {}; - const history = useHistory(); const [, resolveApproval, rejectApproval] = useApproval(); const { t } = useTranslation(); const usewallet = useWallet(); @@ -33,7 +31,7 @@ const EthConnect = ({ params: { icon, name, origin } }: ConnectProps) => { const [appIdentifier, setAppIdentifier] = useState(undefined); const [nonce, setNonce] = useState(undefined); const [opener, setOpener] = useState(undefined); - const [defaultChain, setDefaultChain] = useState('FLOW'); + const [defaultChain, setDefaultChain] = useState(747); const [host, setHost] = useState(''); const [showMoveBoard, setMoveBoard] = useState(true); const [msgNetwork, setMsgNetwork] = useState('testnet'); @@ -45,7 +43,7 @@ const EthConnect = ({ params: { icon, name, origin } }: ConnectProps) => { // TODO: replace default logo const [logo, setLogo] = useState(''); const [evmAddress, setEvmAddress] = useState(''); - const init = async () => { + const init = useCallback(async () => { const network = await usewallet.getNetwork(); setCurrent(network); let currentWallet; @@ -70,15 +68,12 @@ const EthConnect = ({ params: { icon, name, origin } }: ConnectProps) => { }; await usewallet.setActiveWallet(walletInfo, 'evm'); } - const site = await usewallet.getSite(origin); - const collectList: { name: string; logo_url: string }[] = []; - const defaultChain = 'FLOW'; - const isShowTestnet = false; + const defaultChain = network === 'testnet' ? 545 : 747; setDefaultChain(defaultChain); setIsLoading(false); - }; + }, [usewallet, icon, currentNetwork]); const createCoa = async () => { setIsLoading(true); @@ -101,23 +96,26 @@ const EthConnect = ({ params: { icon, name, origin } }: ConnectProps) => { }); }; - const transactionDoneHanlder = async (request) => { - if (request.msg === 'transactionDone') { - const currentWallet = await usewallet.getCurrentWallet(); - const res = await usewallet.queryEvmAddress(currentWallet.address); - setEvmAddress(res!); - setIsEvm(isValidEthereumAddress(res)); - } - return true; - }; + const transactionDoneHandler = useCallback( + async (request) => { + if (request.msg === 'transactionDone') { + const currentWallet = await usewallet.getCurrentWallet(); + const res = await usewallet.queryEvmAddress(currentWallet.address); + setEvmAddress(res!); + setIsEvm(isValidEthereumAddress(res)); + } + return true; + }, + [usewallet] + ); useEffect(() => { - chrome.runtime.onMessage.addListener(transactionDoneHanlder); + chrome.runtime.onMessage.addListener(transactionDoneHandler); return () => { - chrome.runtime.onMessage.removeListener(transactionDoneHanlder); + chrome.runtime.onMessage.removeListener(transactionDoneHandler); }; - }, []); + }, [transactionDoneHandler]); const handleCancel = () => { rejectApproval('User rejected the request.'); @@ -132,7 +130,7 @@ const EthConnect = ({ params: { icon, name, origin } }: ConnectProps) => { useEffect(() => { init(); - }, []); + }, [init]); const renderContent = () => ( @@ -362,9 +360,9 @@ const EthConnect = ({ params: { icon, name, origin } }: ConnectProps) => { ); return ( - + <> {renderContent()} - + ); }; diff --git a/src/ui/views/Approval/components/EthApproval/EthEnable/index.tsx b/src/ui/views/Approval/components/EthApproval/EthEnable/index.tsx index 70a30162..a6481b1d 100644 --- a/src/ui/views/Approval/components/EthApproval/EthEnable/index.tsx +++ b/src/ui/views/Approval/components/EthApproval/EthEnable/index.tsx @@ -1,21 +1,18 @@ -import React, { useEffect, useState } from 'react'; -import { useLocation, useHistory } from 'react-router-dom'; +import { Stack, Box, Typography, Divider, CardMedia, Card } from '@mui/material'; +import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useLocation, useHistory } from 'react-router-dom'; + +import enableBg from 'ui/FRWAssets/image/enableBg.png'; +import flowgrey from 'ui/FRWAssets/svg/flow-grey.svg'; +import linkGlobe from 'ui/FRWAssets/svg/linkGlobe.svg'; +import { LLPrimaryButton, LLSecondaryButton, LLSpinner, LLConnectLoading } from 'ui/FRWComponent'; import { useApproval, useWallet, formatAddress } from 'ui/utils'; import { isValidEthereumAddress } from 'ui/utils/address'; // import { CHAINS_ENUM } from 'consts'; -import { ThemeProvider } from '@mui/system'; -import { Stack, Box, Typography, Divider, CardMedia, Card } from '@mui/material'; -import linkGlobe from 'ui/FRWAssets/svg/linkGlobe.svg'; -import flowgrey from 'ui/FRWAssets/svg/flow-grey.svg'; -import enableBg from 'ui/FRWAssets/image/enableBg.png'; -import theme from 'ui/style/LLTheme'; -import { LLPrimaryButton, LLSecondaryButton, LLSpinner, LLConnectLoading } from 'ui/FRWComponent'; interface ConnectProps { params: any; - // onChainChange(chain: CHAINS_ENUM): void; - // defaultChain: CHAINS_ENUM; } const EthEnable = ({ params: { icon, name, origin } }: ConnectProps) => { @@ -32,7 +29,7 @@ const EthEnable = ({ params: { icon, name, origin } }: ConnectProps) => { const [appIdentifier, setAppIdentifier] = useState(undefined); const [nonce, setNonce] = useState(undefined); const [opener, setOpener] = useState(undefined); - const [defaultChain, setDefaultChain] = useState('FLOW'); + const [defaultChain, setDefaultChain] = useState(747); const [host, setHost] = useState(''); const [title, setTitle] = useState(''); const [msgNetwork, setMsgNetwork] = useState('testnet'); @@ -44,17 +41,18 @@ const EthEnable = ({ params: { icon, name, origin } }: ConnectProps) => { // TODO: replace default logo const [logo, setLogo] = useState(''); const [evmAddress, setEvmAddress] = useState(''); - const init = async () => { + const init = useCallback(async () => { setLogo(icon); const site = await wallet.getSite(origin); const collectList: { name: string; logo_url: string }[] = []; - const defaultChain = 'FLOW'; + const network = await wallet.getNetwork(); + const defaultChain = network === 'testnet' ? 545 : 747; const isShowTestnet = false; setDefaultChain(defaultChain); setIsLoading(false); - }; + }, [wallet, origin, icon]); const handleCancel = () => { rejectApproval('User rejected the request.'); @@ -69,7 +67,7 @@ const EthEnable = ({ params: { icon, name, origin } }: ConnectProps) => { useEffect(() => { init(); - }, []); + }, [init]); const renderContent = () => ( @@ -133,9 +131,9 @@ const EthEnable = ({ params: { icon, name, origin } }: ConnectProps) => { ); return ( - + <> {renderContent()} - + ); }; diff --git a/src/ui/views/Approval/components/EthApproval/EthMove/AccountBox.tsx b/src/ui/views/Approval/components/EthApproval/EthMove/AccountBox.tsx deleted file mode 100644 index 8ee75f23..00000000 --- a/src/ui/views/Approval/components/EthApproval/EthMove/AccountBox.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles } from '@mui/styles'; -import { useWallet } from 'ui/utils'; -import { formatString } from 'ui/utils/address'; -import { Typography, Box, CardMedia } from '@mui/material'; -import accountMove from 'ui/FRWAssets/svg/accountMove.svg'; -import emoji from 'background/utils/emoji.json'; -import { storage } from '@/background/webapi'; - -function AccountBox({ isEvm }) { - const usewallet = useWallet(); - - const [first, setFirst] = useState(''); - const [second, setSecond] = useState(''); - const [userInfo, setUser] = useState(null); - const [firstEmoji, setFirstEmoji] = useState(null); - const [secondEmoji, setSecondEmoji] = useState(null); - - const requestAddress = async () => { - const userContact = { - contact_name: '', - avatar: '', - }; - const info = await usewallet.getUserInfo(false); - userContact.avatar = info.avatar; - userContact.contact_name = info.username; - setUser(userContact); - - let evmAddress = await usewallet.getEvmAddress(); - evmAddress = formatString(evmAddress); - const address = await usewallet.getCurrentAddress(); - - const emojires = await usewallet.getEmoji(); - - if (isEvm) { - setFirst(evmAddress); - setSecond(address!); - setFirstEmoji(emojires[1]); - setSecondEmoji(emojires[0]); - } else { - setFirst(address!); - setSecond(evmAddress); - setFirstEmoji(emojires[0]); - setSecondEmoji(emojires[1]); - } - }; - - useEffect(() => { - requestAddress(); - }, []); - - return ( - - {chrome.i18n.getMessage('Account')} - - - - {firstEmoji && ( - - - - {firstEmoji.emoji} - - - - {firstEmoji.name} - - {isEvm && ( - - EVM - - )} - - )} - {first} - - - - - - {secondEmoji && ( - - - - {secondEmoji.emoji} - - - - {secondEmoji.name} - - {!isEvm && ( - - EVM - - )} - - )} - {second} - - - - ); -} - -export default AccountBox; diff --git a/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/MoveFromEvm/MoveToken.tsx b/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/MoveFromEvm/MoveToken.tsx deleted file mode 100644 index 12e9d059..00000000 --- a/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/MoveFromEvm/MoveToken.tsx +++ /dev/null @@ -1,294 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { - Box, - Typography, - IconButton, - ListItemText, - Select, - MenuItem, - ListItemIcon, - FormControl, - InputAdornment, - Input, - Chip, - Tooltip, -} from '@mui/material'; -import { makeStyles } from '@mui/styles'; -import CancelIcon from '../../../../../../../../components/iconfont/IconClose'; -import AttachMoneyRoundedIcon from '@mui/icons-material/AttachMoneyRounded'; -import { StyledEngineProvider } from '@mui/material/styles'; -import BN from 'bignumber.js'; -import { Presets } from 'react-component-transition'; - -const useStyles = makeStyles(() => ({ - customInputLabel: { - '& legend': { - visibility: 'visible', - }, - }, - inputBox: { - minHeight: '64px', - paddingRight: '12px', - paddingLeft: '0', - py: '14px', - zIndex: '999', - fontSize: '24px', - backgroundColor: '#282828', - borderRadius: '12px', - boxSizing: 'border-box', - }, - selectRoot: { - fontFamily: 'IBM Plex Sans, sans-serif', - fontSize: '0.875rem', - boxSizing: 'border-box', - borderRadius: '0.75em', - marginRight: '0', - textAlign: 'left', - lineHeight: '1.5', - display: 'flex', - gap: '8px', - color: '#CDD2D7', - border: '1px solid #282828', - - // &.${selectUnstyledClasses.expanded} { - // &::after { - // content: '▴'; - // } - // } - - // &::after { - // content: '▾'; - // float: right; - // } - '&ul': { - fontFamily: 'IBM Plex Sans, sans-serif', - fontSize: '0.875rem', - boxSizing: 'border-box', - padding: '5px', - margin: '10px 0', - maxHeight: '400px', - backgroundColor: '#282828', - border: 'none', - borderRadius: '0.75em', - color: '#CDD2D7', - overflow: 'auto', - outline: '0px', - }, - '& .MuiOutlinedInput-notchedOutline': { - border: 'none !important', - borderWidth: '0px !important', - outline: 'none !important', - }, - }, - selectList: { - fontFamily: 'IBM Plex Sans, sans-serif', - fontSize: '0.875rem', - boxSizing: 'border-box', - padding: '5px', - margin: '10px 0', - maxHeight: '400px', - backgroundColor: '#282828', - border: '1px solid #787878', - borderRadius: '0.75em', - color: '#CDD2D7', - overflow: 'auto', - outline: '0px', - }, - exceedBox: { - background: 'rgba(196,69,54,0.08)', - display: 'flex', - height: '25px', - }, -})); - -const MoveToken = ({ - amount, - setAmount, - secondAmount, - setSecondAmount, - exceed, - setExceed, - coinInfo, - setCurrentCoin, - coinList, -}) => { - const classes = useStyles(); - const [coin, setCoin] = useState('flow'); - const [coinType, setCoinType] = useState(0); - const handleMaxClick = () => { - if (coinInfo) { - if (coin === 'flow') { - setAmount(coinInfo.balance - 0.001); - } else { - setAmount(coinInfo.balance); - } - } - }; - - const renderValue = (option) => { - setCurrentCoin(option); - setCoin(option); - const selectCoin = coinList.find((coin) => coin.unit === option); - return selectCoin && ; - }; - - const swap = () => { - setCoinType(!coinType); - }; - - const currentCoinType = () => { - setCoin(coinInfo.unit); - }; - - useEffect(() => { - currentCoinType(); - }, []); - - useEffect(() => { - if (coinType) { - const secondInt = parseInt(secondAmount); - const value = new BN(secondInt).dividedBy(new BN(coinInfo.price)).toNumber(); - if (coinInfo.balance - value < 0) { - setExceed(true); - } else { - setExceed(false); - } - if (isNaN(value)) { - setAmount(0); - } else { - setAmount(parseFloat(value.toFixed(3))); - } - } - }, [secondAmount]); - - useEffect(() => { - if (!coinType) { - if (coinInfo && amount) { - const result = parseFloat((coinInfo.amountbalance - amount).toPrecision()); - if (coinInfo.balance - amount < 0) { - setExceed(true); - } else if (coin === 'flow' && result < 0.001) { - setExceed(true); - } else { - setExceed(false); - } - const value = new BN(amount).times(new BN(coinInfo.price)).toFixed(3); - setSecondAmount(value); - } - } - }, [amount, coin]); - - return ( - - - - - - - { - // let value = event.target.value; - // value = (Math.round(value * 100) / 100).toFixed(2) - setExceed(false); - setAmount(event.target.value); - }} - inputProps={{ sx: { fontSize: '24px' } }} - endAdornment={ - - - - } - /> - - - - {chrome.i18n.getMessage('Balance')} - {coinInfo.balance} - - - - {exceed && ( - - - - {chrome.i18n.getMessage('Insufficient_balance') + - (coin === 'flow' - ? chrome.i18n.getMessage('on_Flow_the_balance_cant_less_than_0001_FLOW') - : '')} - - - )} - - - - ); -}; - -export default MoveToken; diff --git a/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/MoveFromEvm/index.tsx b/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/MoveFromEvm/index.tsx deleted file mode 100644 index 4936c3d8..00000000 --- a/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/MoveFromEvm/index.tsx +++ /dev/null @@ -1,301 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useHistory } from 'react-router-dom'; -import { Box, Button, Typography, Drawer, IconButton, Grid } from '@mui/material'; -import CloseIcon from '@mui/icons-material/Close'; -import { CoinItem } from 'background/service/coinList'; -import { ThemeProvider } from '@mui/material/styles'; -import TransferFrom from '../TransferFrom'; -import TransferTo from '../TransferTo'; -import MoveToken from './MoveToken'; -import { useWallet } from 'ui/utils'; -import { withPrefix } from 'ui/utils/address'; -import IconSwitch from '../../../../../../../../components/iconfont/IconSwitch'; -import { LLSpinner } from 'ui/FRWComponent'; -import { Contact } from 'background/service/networkModel'; -import wallet from '@/background/controller/wallet'; - -interface TransferConfirmationProps { - isConfirmationOpen: boolean; - data: any; - handleCloseIconClicked: () => void; - handleCancelBtnClicked: () => void; - handleAddBtnClicked: () => void; -} - -const MoveFromEvm = (props: TransferConfirmationProps) => { - const userContact = { - address: '', - id: 0, - contact_name: '', - avatar: '', - domain: { - domain_type: 999, - value: '', - }, - } as unknown as Contact; - - const evmContact = { - address: '', - id: 0, - contact_name: '', - avatar: '', - domain: { - domain_type: 999, - value: '', - }, - } as unknown as Contact; - - const empty: CoinItem = { - coin: '', - unit: '', - balance: 0, - price: 0, - change24h: 0, - total: 0, - icon: '', - }; - - const usewallet = useWallet(); - const history = useHistory(); - const [userWallet, setWallet] = useState(null); - const [currentCoin, setCurrentCoin] = useState('flow'); - const [coinList, setCoinList] = useState([]); - // const [exceed, setExceed] = useState(false); - const [amount, setAmount] = useState(''); - // const [validated, setValidated] = useState(null); - const [userInfo, setUser] = useState(userContact); - const [evmUserInfo, setEvmUser] = useState(evmContact); - const [network, setNetwork] = useState('mainnet'); - const [evmAddress, setEvmAddress] = useState(''); - const [coinInfo, setCoinInfo] = useState(empty); - const [secondAmount, setSecondAmount] = useState('0.0'); - const [isLoading, setLoading] = useState(false); - const [exceed, setExceed] = useState(false); - - const setUserWallet = async () => { - // const walletList = await storage.get('userWallet'); - setLoading(true); - const wallet = await usewallet.getMainWallet(); - const network = await usewallet.getNetwork(); - const token = await usewallet.getCurrentCoin(); - setNetwork(network); - setCurrentCoin(token); - // userWallet - await setWallet(wallet); - const evmWallet = await usewallet.getEvmWallet(); - setEvmAddress(evmWallet.address); - const coinList = await usewallet.getCoinList(); - setCoinList(coinList); - const tokenResult = await usewallet.openapi.getTokenInfo(token, network); - const coinInfo = coinList.find( - (coin) => coin && coin.unit.toLowerCase() === tokenResult!.symbol.toLowerCase() - ); - setCoinInfo(coinInfo!); - - const info = await usewallet.getUserInfo(false); - userContact.address = withPrefix(wallet) || ''; - userContact.avatar = info.avatar; - userContact.contact_name = info.username; - setUser(userContact); - - evmContact.address = withPrefix(evmWallet.address) || ''; - evmContact.avatar = evmWallet.icon; - evmContact.contact_name = evmWallet.name; - setEvmUser(evmContact); - - // const result = await usewallet.openapi.fetchTokenList(network); - setLoading(false); - return; - }; - - const moveToken = async () => { - setLoading(true); - usewallet - .withdrawFlowEvm(amount, userInfo.address) - .then(async (createRes) => { - usewallet.listenTransaction( - createRes, - true, - 'Transfer to EVM complete', - `Your have moved ${amount} Flow to your EVM address ${evmAddress}. \nClick to view this transaction.` - ); - await usewallet.setDashIndex(0); - - setLoading(false); - props.handleCloseIconClicked(); - }) - .catch((err) => { - console.log(err); - setLoading(false); - }); - }; - - const bridgeToken = async () => { - setLoading(true); - const tokenResult = await wallet.openapi.getTokenInfo(currentCoin, network); - - usewallet - .bridgeToFlow(tokenResult!['flowIdentifier'], amount, tokenResult) - .then(async (createRes) => { - usewallet.listenTransaction( - createRes, - true, - 'Transfer to EVM complete', - `Your have moved ${amount} Flow to your EVM address ${evmAddress}. \nClick to view this transaction.` - ); - await usewallet.setDashIndex(0); - - setLoading(false); - props.handleCloseIconClicked(); - }) - .catch((err) => { - console.log(err); - setLoading(false); - }); - }; - - const handleCoinInfo = async () => { - if (coinList.length > 0) { - const coinInfo = coinList.find( - (coin) => coin.unit.toLowerCase() === currentCoin.toLowerCase() - ); - setCoinInfo(coinInfo!); - } - }; - - const handleMove = async () => { - if (currentCoin.toLowerCase() === 'flow') { - moveToken(); - } else { - bridgeToken(); - } - }; - - useEffect(() => { - setUserWallet(); - }, []); - - useEffect(() => { - handleCoinInfo(); - }, [currentCoin]); - - return ( - - - - - - - {chrome.i18n.getMessage('move_tokens')} - - - - - - - - - {userWallet && } - - {isLoading ? ( - - - - ) : ( - - - - )} - - {evmAddress && } - - - - - {coinInfo.unit && coinInfo && ( - - )} - - - - - - - ); -}; - -export default MoveFromEvm; diff --git a/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/MoveFromFlow/MoveToken.tsx b/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/MoveFromFlow/MoveToken.tsx deleted file mode 100644 index 12e9d059..00000000 --- a/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/MoveFromFlow/MoveToken.tsx +++ /dev/null @@ -1,294 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { - Box, - Typography, - IconButton, - ListItemText, - Select, - MenuItem, - ListItemIcon, - FormControl, - InputAdornment, - Input, - Chip, - Tooltip, -} from '@mui/material'; -import { makeStyles } from '@mui/styles'; -import CancelIcon from '../../../../../../../../components/iconfont/IconClose'; -import AttachMoneyRoundedIcon from '@mui/icons-material/AttachMoneyRounded'; -import { StyledEngineProvider } from '@mui/material/styles'; -import BN from 'bignumber.js'; -import { Presets } from 'react-component-transition'; - -const useStyles = makeStyles(() => ({ - customInputLabel: { - '& legend': { - visibility: 'visible', - }, - }, - inputBox: { - minHeight: '64px', - paddingRight: '12px', - paddingLeft: '0', - py: '14px', - zIndex: '999', - fontSize: '24px', - backgroundColor: '#282828', - borderRadius: '12px', - boxSizing: 'border-box', - }, - selectRoot: { - fontFamily: 'IBM Plex Sans, sans-serif', - fontSize: '0.875rem', - boxSizing: 'border-box', - borderRadius: '0.75em', - marginRight: '0', - textAlign: 'left', - lineHeight: '1.5', - display: 'flex', - gap: '8px', - color: '#CDD2D7', - border: '1px solid #282828', - - // &.${selectUnstyledClasses.expanded} { - // &::after { - // content: '▴'; - // } - // } - - // &::after { - // content: '▾'; - // float: right; - // } - '&ul': { - fontFamily: 'IBM Plex Sans, sans-serif', - fontSize: '0.875rem', - boxSizing: 'border-box', - padding: '5px', - margin: '10px 0', - maxHeight: '400px', - backgroundColor: '#282828', - border: 'none', - borderRadius: '0.75em', - color: '#CDD2D7', - overflow: 'auto', - outline: '0px', - }, - '& .MuiOutlinedInput-notchedOutline': { - border: 'none !important', - borderWidth: '0px !important', - outline: 'none !important', - }, - }, - selectList: { - fontFamily: 'IBM Plex Sans, sans-serif', - fontSize: '0.875rem', - boxSizing: 'border-box', - padding: '5px', - margin: '10px 0', - maxHeight: '400px', - backgroundColor: '#282828', - border: '1px solid #787878', - borderRadius: '0.75em', - color: '#CDD2D7', - overflow: 'auto', - outline: '0px', - }, - exceedBox: { - background: 'rgba(196,69,54,0.08)', - display: 'flex', - height: '25px', - }, -})); - -const MoveToken = ({ - amount, - setAmount, - secondAmount, - setSecondAmount, - exceed, - setExceed, - coinInfo, - setCurrentCoin, - coinList, -}) => { - const classes = useStyles(); - const [coin, setCoin] = useState('flow'); - const [coinType, setCoinType] = useState(0); - const handleMaxClick = () => { - if (coinInfo) { - if (coin === 'flow') { - setAmount(coinInfo.balance - 0.001); - } else { - setAmount(coinInfo.balance); - } - } - }; - - const renderValue = (option) => { - setCurrentCoin(option); - setCoin(option); - const selectCoin = coinList.find((coin) => coin.unit === option); - return selectCoin && ; - }; - - const swap = () => { - setCoinType(!coinType); - }; - - const currentCoinType = () => { - setCoin(coinInfo.unit); - }; - - useEffect(() => { - currentCoinType(); - }, []); - - useEffect(() => { - if (coinType) { - const secondInt = parseInt(secondAmount); - const value = new BN(secondInt).dividedBy(new BN(coinInfo.price)).toNumber(); - if (coinInfo.balance - value < 0) { - setExceed(true); - } else { - setExceed(false); - } - if (isNaN(value)) { - setAmount(0); - } else { - setAmount(parseFloat(value.toFixed(3))); - } - } - }, [secondAmount]); - - useEffect(() => { - if (!coinType) { - if (coinInfo && amount) { - const result = parseFloat((coinInfo.amountbalance - amount).toPrecision()); - if (coinInfo.balance - amount < 0) { - setExceed(true); - } else if (coin === 'flow' && result < 0.001) { - setExceed(true); - } else { - setExceed(false); - } - const value = new BN(amount).times(new BN(coinInfo.price)).toFixed(3); - setSecondAmount(value); - } - } - }, [amount, coin]); - - return ( - - - - - - - { - // let value = event.target.value; - // value = (Math.round(value * 100) / 100).toFixed(2) - setExceed(false); - setAmount(event.target.value); - }} - inputProps={{ sx: { fontSize: '24px' } }} - endAdornment={ - - - - } - /> - - - - {chrome.i18n.getMessage('Balance')} - {coinInfo.balance} - - - - {exceed && ( - - - - {chrome.i18n.getMessage('Insufficient_balance') + - (coin === 'flow' - ? chrome.i18n.getMessage('on_Flow_the_balance_cant_less_than_0001_FLOW') - : '')} - - - )} - - - - ); -}; - -export default MoveToken; diff --git a/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/MoveFromFlow/index.tsx b/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/MoveFromFlow/index.tsx deleted file mode 100644 index b6381536..00000000 --- a/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/MoveFromFlow/index.tsx +++ /dev/null @@ -1,319 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useHistory } from 'react-router-dom'; -import { Box, Button, Typography, Drawer, IconButton, Grid } from '@mui/material'; -import CloseIcon from '@mui/icons-material/Close'; -import { CoinItem } from 'background/service/coinList'; -import { ThemeProvider } from '@mui/material/styles'; -import TransferFrom from '../TransferFrom'; -import TransferTo from '../TransferTo'; -import MoveToken from './MoveToken'; -import { useWallet } from 'ui/utils'; -import { withPrefix } from 'ui/utils/address'; -import IconSwitch from '../../../../../../../../components/iconfont/IconSwitch'; -import { LLSpinner } from 'ui/FRWComponent'; -import { Contact } from 'background/service/networkModel'; -import wallet from '@/background/controller/wallet'; - -interface TransferConfirmationProps { - isConfirmationOpen: boolean; - data: any; - handleCloseIconClicked: () => void; - handleCancelBtnClicked: () => void; - handleAddBtnClicked: () => void; -} - -const MoveFromFlow = (props: TransferConfirmationProps) => { - enum ENV { - Mainnet = 'mainnet', - Testnet = 'testnet', - } - enum Error { - Exceed = 'Insufficient balance', - Fail = 'Cannot find swap pair', - } - - // declare enum Strategy { - // GitHub = 'GitHub', - // Static = 'Static', - // CDN = 'CDN' - // } - const userContact = { - address: '', - id: 0, - contact_name: '', - avatar: '', - domain: { - domain_type: 999, - value: '', - }, - } as unknown as Contact; - - const evmContact = { - address: '', - id: 0, - contact_name: '', - avatar: '', - domain: { - domain_type: 999, - value: '', - }, - } as unknown as Contact; - - const empty: CoinItem = { - coin: '', - unit: '', - balance: 0, - price: 0, - change24h: 0, - total: 0, - icon: '', - }; - - const usewallet = useWallet(); - const history = useHistory(); - const [userWallet, setWallet] = useState(null); - const [currentCoin, setCurrentCoin] = useState('flow'); - const [coinList, setCoinList] = useState([]); - // const [exceed, setExceed] = useState(false); - const [amount, setAmount] = useState(''); - // const [validated, setValidated] = useState(null); - const [userInfo, setUser] = useState(userContact); - const [evmUserInfo, setEvmUser] = useState(evmContact); - const [network, setNetwork] = useState('mainnet'); - const [evmAddress, setEvmAddress] = useState(''); - const [coinInfo, setCoinInfo] = useState(empty); - const [secondAmount, setSecondAmount] = useState('0.0'); - const [isLoading, setLoading] = useState(false); - const [errorType, setErrorType] = useState(null); - const [exceed, setExceed] = useState(false); - - const setUserWallet = async () => { - // const walletList = await storage.get('userWallet'); - setLoading(true); - const token = await usewallet.getCurrentCoin(); - const wallet = await usewallet.getMainWallet(); - const network = await usewallet.getNetwork(); - setNetwork(network); - setCurrentCoin(token); - // userWallet - await setWallet(wallet); - const coinList = await usewallet.getCoinList(); - setCoinList(coinList); - const evmWallet = await usewallet.getEvmWallet(); - setEvmAddress(evmWallet.address); - const coinInfo = coinList.find((coin) => coin.unit.toLowerCase() === token.toLowerCase()); - setCoinInfo(coinInfo!); - - const info = await usewallet.getUserInfo(false); - userContact.address = withPrefix(wallet) || ''; - userContact.avatar = info.avatar; - userContact.contact_name = info.username; - setUser(userContact); - - evmContact.address = withPrefix(evmWallet.address) || ''; - evmContact.avatar = evmWallet.icon; - evmContact.contact_name = evmWallet.name; - setEvmUser(evmContact); - // const result = await usewallet.openapi.fetchTokenList(network); - setLoading(false); - return; - }; - - const moveToken = async () => { - setLoading(true); - usewallet - .fundFlowEvm(amount) - .then(async (createRes) => { - usewallet.listenTransaction( - createRes, - true, - 'Transfer to EVM complete', - `Your have moved ${amount} Flow to your EVM address ${evmAddress}. \nClick to view this transaction.` - ); - await usewallet.setDashIndex(0); - - setLoading(false); - props.handleCloseIconClicked(); - }) - .catch((err) => { - console.log(err); - setLoading(false); - }); - }; - - const bridgeToken = async () => { - setLoading(true); - const tokenResult = await wallet.openapi.getTokenInfo(currentCoin, network); - const address = tokenResult!.address.startsWith('0x') - ? tokenResult!.address.slice(2) - : tokenResult!.address; - - usewallet - .bridgeToEvm(`A.${address}.${tokenResult!.contractName}.Vault`, amount) - .then(async (createRes) => { - usewallet.listenTransaction( - createRes, - true, - 'Transfer to EVM complete', - `Your have moved ${amount} Flow to your EVM address ${evmAddress}. \nClick to view this transaction.` - ); - await usewallet.setDashIndex(0); - - setLoading(false); - props.handleCloseIconClicked(); - }) - .catch((err) => { - console.log(err); - setLoading(false); - }); - }; - - const handleMove = async () => { - if (currentCoin.toLowerCase() === 'flow') { - moveToken(); - } else { - bridgeToken(); - } - }; - - const handleCoinInfo = async () => { - if (coinList.length > 0) { - const coinInfo = coinList.find( - (coin) => coin.unit.toLowerCase() === currentCoin.toLowerCase() - ); - setCoinInfo(coinInfo!); - } - }; - - useEffect(() => { - setUserWallet(); - }, []); - - useEffect(() => { - handleCoinInfo(); - }, [currentCoin]); - - return ( - - - - - - - {chrome.i18n.getMessage('move_tokens')} - - - - - - - - - {userWallet && } - - {isLoading ? ( - - - - ) : ( - - - - )} - - {evmAddress && } - - - - - {coinInfo.unit && ( - - )} - - - - - - - ); -}; - -export default MoveFromFlow; diff --git a/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/TransferFrom.tsx b/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/TransferFrom.tsx deleted file mode 100644 index d3a665c8..00000000 --- a/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/TransferFrom.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { Box, Typography, CardMedia } from '@mui/material'; -import { StyledEngineProvider } from '@mui/material/styles'; -import { useWallet } from 'ui/utils'; - -const tempEmoji = { - emoji: '🥥', - name: 'Coconut', - bgcolor: '#FFE4C4', -}; - -const TransferFrom = ({ wallet, userInfo }) => { - const usewallet = useWallet(); - const [emoji, setEmoji] = useState(tempEmoji); - - const getEmoji = async () => { - const emojiList = await usewallet.getEmoji(); - setEmoji(emojiList[0]); - }; - - useEffect(() => { - getEmoji(); - }, [userInfo]); - - return ( - - - - - - {emoji.emoji} - - - {emoji.name} - - {userInfo.address} - - - - - - - ); -}; - -export default TransferFrom; diff --git a/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/TransferTo.tsx b/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/TransferTo.tsx deleted file mode 100644 index f76419d3..00000000 --- a/src/ui/views/Approval/components/EthApproval/EthMove/EvmMove/TransferTo.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { Box, Typography, CardMedia } from '@mui/material'; -import { StyledEngineProvider } from '@mui/material/styles'; -import { formatAddress } from 'ui/utils'; -import { useWallet } from 'ui/utils'; - -const tempEmoji = { - emoji: '🥥', - name: 'Coconut', - bgcolor: '#FFE4C4', -}; - -const TransferTo = ({ wallet, userInfo }) => { - const usewallet = useWallet(); - const [emoji, setEmoji] = useState(tempEmoji); - - const getEmoji = async () => { - const emojiList = await usewallet.getEmoji(); - setEmoji(emojiList[1]); - }; - - useEffect(() => { - getEmoji(); - }, [userInfo]); - - return ( - - - - - - {emoji.emoji} - - - - {emoji.name} - - EVM - - - - - {formatAddress(wallet)} - - - - - - - ); -}; - -export default TransferTo; diff --git a/src/ui/views/Approval/components/EthApproval/EthMove/MoveCollectionSelect.tsx b/src/ui/views/Approval/components/EthApproval/EthMove/MoveCollectionSelect.tsx deleted file mode 100644 index b1339fdc..00000000 --- a/src/ui/views/Approval/components/EthApproval/EthMove/MoveCollectionSelect.tsx +++ /dev/null @@ -1,208 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { - Box, - Button, - InputAdornment, - Typography, - Drawer, - IconButton, - TextField, - ListItem, - ListItemButton, - Avatar, - CardMedia, -} from '@mui/material'; -import CloseIcon from '@mui/icons-material/Close'; -import { useWallet } from 'ui/utils'; -import { useHistory } from 'react-router-dom'; -import popLock from 'ui/FRWAssets/svg/popLock.svg'; -import popAdd from 'ui/FRWAssets/svg/popAdd.svg'; -import SearchIcon from '@mui/icons-material/Search'; -import selected from 'ui/FRWAssets/svg/selected.svg'; - -const MoveCollectionSelect = ({ - showMoveBoard, - handleCloseIconClicked, - handleCancelBtnClicked, - handleAddBtnClicked, - selectedCollection, - setSelected, - collectionList, -}) => { - const usewallet = useWallet(); - const history = useHistory(); - const [filter, setFilter] = useState(''); - - const handleFilterChange = (event) => { - setFilter(event.target.value); - }; - - const filteredCollectionList = collectionList.filter((obj) => - obj.CollectionName.toLowerCase().includes(filter.toLowerCase()) - ); - - return ( - - - - - - {chrome.i18n.getMessage('select_collection')} - - - - - - - - {collectionList.length > 0 && ( - - - - - ), - }} - /> - - {filteredCollectionList.map((obj, index) => ( - - setSelected(obj.id)} - sx={{ - width: '100%', - padding: '0', - borderRadius: '12px', - '&:hover': { backgroundColor: 'rgba(44, 44, 44, 0.1)' }, - }} - > - - - - - {obj.CollectionName} - - - {obj.NftCount} NFTs - - - - {selectedCollection === obj.id && ( - - )} - - - - - ))} - - )} - - - ); -}; - -export default MoveCollectionSelect; diff --git a/src/ui/views/Approval/components/EthApproval/EthMove/MoveEvm/index.tsx b/src/ui/views/Approval/components/EthApproval/EthMove/MoveEvm/index.tsx deleted file mode 100644 index 990e8c73..00000000 --- a/src/ui/views/Approval/components/EthApproval/EthMove/MoveEvm/index.tsx +++ /dev/null @@ -1,438 +0,0 @@ -import CloseIcon from '@mui/icons-material/Close'; -import { Box, Button, Skeleton, Typography, Drawer, IconButton, CardMedia } from '@mui/material'; -import React, { useState, useEffect } from 'react'; - -import WarningSnackbar from '@/ui/FRWComponent/WarningSnackbar'; -import alertMark from 'ui/FRWAssets/svg/alertMark.svg'; -import moveSelectDrop from 'ui/FRWAssets/svg/moveSelectDrop.svg'; -import selected from 'ui/FRWAssets/svg/selected.svg'; -import { LLSpinner } from 'ui/FRWComponent'; -import { useWallet } from 'ui/utils'; - -import AccountBox from '../AccountBox'; -import MoveCollectionSelect from '../MoveCollectionSelect'; - -interface MoveBoardProps { - showMoveBoard: boolean; - handleCloseIconClicked: () => void; - handleCancelBtnClicked: () => void; - handleAddBtnClicked: () => void; - handleReturnHome: () => void; -} - -const MoveEvm = (props: MoveBoardProps) => { - const usewallet = useWallet(); - const [cadenceNft, setCadenceNft] = useState(null); - const [collectionList, setCollectionList] = useState(null); - const [selectedCollection, setSelected] = useState(''); - const [collectionDetail, setCollectionDetail] = useState(null); - const [collectInfo, setCollectionInfo] = useState(null); - const [nftIdArray, setNftIdArray] = useState([]); - const [sending, setSending] = useState(false); - const [failed, setFailed] = useState(false); - const [loading, setLoading] = useState(true); - const [errorOpen, setShowError] = useState(false); - const [selectCollection, setSelectCollection] = useState(false); - // console.log('props.loggedInAccounts', props.current) - - const handleErrorClose = (event?: React.SyntheticEvent | Event, reason?: string) => { - if (reason === 'clickaway') { - return; - } - setShowError(false); - }; - - const updateCurrentCollection = async () => { - if (collectionList && cadenceNft) { - const collection = collectionList.find((collection) => collection.id === selectedCollection); - const selectedShow = cadenceNft.find((collection) => collection.name === selectedCollection); - setCollectionDetail(collection); - setCollectionInfo(selectedShow); - } - }; - - const requestCadenceNft = async () => { - const cadenceResult = await usewallet.reqeustEvmNft(); - const tokensWithNfts = cadenceResult.filter((token) => token.nftIds && token.nftIds.length > 0); - const filteredData = tokensWithNfts.filter((item) => item.flowIdentifier); - if (filteredData.length > 0) { - setSelected(filteredData[0].name); - const extractedObjects = filteredData.map((obj) => { - const flowIdentifierParts = obj.flowIdentifier.split('.'); - return { - CollectionName: flowIdentifierParts[2], - NftCount: obj.nfts.length, - id: obj.name, - address: flowIdentifierParts[1], - logo: obj.logoURI, - }; - }); - setCadenceNft(filteredData); - setCollectionList(extractedObjects); - } else { - setCollectionInfo({ nfts: [] }); - } - setLoading(false); - }; - - const toggleSelectNft = async (nftId) => { - const tempIdArray = [...nftIdArray]; - const index = tempIdArray.indexOf(nftId); - - if (index === -1) { - // If nftId is not in the array, add it - if (tempIdArray.length < 9) { - tempIdArray.push(nftId); - } else { - // Display an error or warning message that no more than 3 IDs are allowed - setShowError(true); - } - } else { - // If nftId is in the array, remove it - tempIdArray.splice(index, 1); - } - - setNftIdArray(tempIdArray); - }; - - const moveNFT = async () => { - setSending(true); - const collection = collectionList.find((collection) => collection.id === selectedCollection); - - usewallet - .batchBridgeNftFromEvm(collection.flowIdentifier, nftIdArray) - .then(async (txID) => { - usewallet.listenTransaction( - txID, - true, - `Move complete`, - `You have moved ${nftIdArray.length} ${collection.CollectionName} from evm to your flow address. \nClick to view this transaction.` - ); - props.handleReturnHome(); - props.handleCloseIconClicked(); - await usewallet.setDashIndex(0); - setSending(false); - }) - .catch(() => { - setSending(false); - setFailed(true); - }); - }; - - useEffect(() => { - requestCadenceNft(); - }, []); - - useEffect(() => { - updateCurrentCollection(); - }, [collectionList, cadenceNft, selectedCollection]); - - return ( - - - - - - {chrome.i18n.getMessage('select')} NFTs - - - - - - - - - - - - - - {chrome.i18n.getMessage('collection')} - - - {collectionDetail && ( - - )} - - {!loading ? ( - - {collectInfo.nfts.length > 0 ? ( - collectInfo.nfts.map((nfts) => ( - - - - )) - ) : ( - - - 0 NFTs - - - )} - - ) : ( - - - - - - - - - - - - - - - )} - - - {selectCollection && ( - setSelectCollection(false)} - handleCancelBtnClicked={() => setSelectCollection(false)} - handleAddBtnClicked={() => setSelectCollection(false)} - selectedCollection={selectedCollection} - setSelected={setSelected} - collectionList={collectionList} - /> - )} - - - ); -}; - -export default MoveEvm; diff --git a/src/ui/views/Approval/components/EthApproval/EthMove/MoveNfts/index.tsx b/src/ui/views/Approval/components/EthApproval/EthMove/MoveNfts/index.tsx deleted file mode 100644 index f929df1e..00000000 --- a/src/ui/views/Approval/components/EthApproval/EthMove/MoveNfts/index.tsx +++ /dev/null @@ -1,464 +0,0 @@ -import CloseIcon from '@mui/icons-material/Close'; -import { Box, Button, Skeleton, Typography, Drawer, IconButton, CardMedia } from '@mui/material'; -import React, { useState, useEffect, useCallback } from 'react'; - -import WarningSnackbar from '@/ui/FRWComponent/WarningSnackbar'; -import alertMark from 'ui/FRWAssets/svg/alertMark.svg'; -import moveSelectDrop from 'ui/FRWAssets/svg/moveSelectDrop.svg'; -import selected from 'ui/FRWAssets/svg/selected.svg'; -import { LLSpinner } from 'ui/FRWComponent'; -import { useWallet } from 'ui/utils'; - -import AccountBox from '../AccountBox'; -import MoveCollectionSelect from '../MoveCollectionSelect'; - -interface MoveBoardProps { - showMoveBoard: boolean; - handleCloseIconClicked: () => void; - handleCancelBtnClicked: () => void; - handleAddBtnClicked: () => void; - handleReturnHome: () => void; -} - -const MoveNfts = (props: MoveBoardProps) => { - const usewallet = useWallet(); - const [isLoading, setIsLoading] = useState(true); - const [collectionList, setCollectionList] = useState(null); - const [selectedCollection, setSelected] = useState(''); - const [collectionDetail, setCollectionDetail] = useState(null); - const [nftIdArray, setNftIdArray] = useState([]); - const [sending, setSending] = useState(false); - const [failed, setFailed] = useState(false); - const [errorOpen, setShowError] = useState(false); - const [selectCollection, setSelectCollection] = useState(false); - const [currentCollection, setCurrentCollection] = useState({ - CollectionName: '', - NftCount: 0, - id: '', - address: '', - logo: '', - }); - // console.log('props.loggedInAccounts', props.current) - - const handleErrorClose = (event?: React.SyntheticEvent | Event, reason?: string) => { - if (reason === 'clickaway') { - return; - } - setShowError(false); - }; - - const findCollectionByContractName = useCallback(() => { - if (collectionList) { - const collection = collectionList.find((collection) => collection.id === selectedCollection); - setCurrentCollection(collection); - } - }, [collectionList, selectedCollection]); - - const requestCadenceNft = useCallback(async () => { - setIsLoading(true); - try { - const cadenceResult = await usewallet.requestCadenceNft(); - setSelected(cadenceResult[0].collection.id); - - const extractedObjects = cadenceResult.map((obj) => { - return { - CollectionName: obj.collection.contract_name, - NftCount: obj.count, - id: obj.collection.id, - address: obj.collection.address, - logo: obj.collection.logo, - }; - }); - - setCollectionList(extractedObjects); - } catch (error) { - console.error('Error fetching NFT data:', error); - setSelected(''); - setCollectionList(null); - setIsLoading(false); - } - }, [usewallet]); - - const requestCollectionInfo = useCallback(async () => { - if (selectedCollection) { - try { - const cadenceResult = await usewallet.requestCollectionInfo(selectedCollection); - setCollectionDetail(cadenceResult); - } catch (error) { - console.error('Error requesting collection info:', error); - setCollectionDetail(null); - } finally { - setIsLoading(false); - } - } - }, [usewallet, selectedCollection]); - - const toggleSelectNft = async (nftId) => { - const tempIdArray = [...nftIdArray]; - const index = tempIdArray.indexOf(nftId); - - if (index === -1) { - // If nftId is not in the array, add it - if (tempIdArray.length < 9) { - tempIdArray.push(nftId); - } else { - // Display an error or warning message that no more than 3 IDs are allowed - setShowError(true); - } - } else { - // If nftId is in the array, remove it - tempIdArray.splice(index, 1); - } - - setNftIdArray(tempIdArray); - }; - - const moveNFT = async () => { - setSending(true); - usewallet - .batchBridgeNftToEvm(collectionDetail.collection.flowIdentifier, nftIdArray) - .then(async (txID) => { - usewallet.listenTransaction( - txID, - true, - `Move complete`, - `You have moved ${nftIdArray.length} ${collectionDetail.collection.contract_name} to your evm address. \nClick to view this transaction.` - ); - props.handleReturnHome(); - props.handleCloseIconClicked(); - await usewallet.setDashIndex(0); - setSending(false); - }) - .catch(() => { - setSending(false); - setFailed(true); - }); - }; - - useEffect(() => { - setIsLoading(true); - requestCadenceNft(); - }, [requestCadenceNft]); - - useEffect(() => { - setIsLoading(true); - requestCollectionInfo(); - }, [requestCollectionInfo]); - - useEffect(() => { - setIsLoading(true); - findCollectionByContractName(); - }, [collectionList, selectedCollection, findCollectionByContractName]); - - return ( - - - - - - {chrome.i18n.getMessage('select')} NFTs - - - - - - - - - - - - - {chrome.i18n.getMessage('collection')} - - - {currentCollection && collectionDetail && ( - - )} - - {!isLoading ? ( - - {collectionDetail && collectionDetail.nfts.length > 0 ? ( - collectionDetail.nfts.map((nft) => ( - - - - )) - ) : ( - - - 0 NFTs - - - )} - - ) : ( - - - - - - - - - - - - - - - )} - - - - {selectCollection && ( - setSelectCollection(false)} - handleCancelBtnClicked={() => setSelectCollection(false)} - handleAddBtnClicked={() => setSelectCollection(false)} - selectedCollection={selectedCollection} - setSelected={setSelected} - collectionList={collectionList} - /> - )} - - - ); -}; - -export default MoveNfts; diff --git a/src/ui/views/Approval/components/EthApproval/EthMove/index.tsx b/src/ui/views/Approval/components/EthApproval/EthMove/index.tsx deleted file mode 100644 index a772a7c7..00000000 --- a/src/ui/views/Approval/components/EthApproval/EthMove/index.tsx +++ /dev/null @@ -1,255 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { - Box, - Button, - ListItemButton, - Typography, - Drawer, - IconButton, - ListItem, - ListItemIcon, - ListItemText, - Avatar, - CardMedia, -} from '@mui/material'; -import CloseIcon from '@mui/icons-material/Close'; -import { useWallet } from 'ui/utils'; -import { useHistory } from 'react-router-dom'; -import popLock from 'ui/FRWAssets/svg/popLock.svg'; -import moveftbg from 'ui/FRWAssets/svg/moveftbg.svg'; -import movenftbg from 'ui/FRWAssets/svg/movenftbg.svg'; -import moveft from 'ui/FRWAssets/image/moveft.png'; -import movenft from 'ui/FRWAssets/image/movenft.png'; -import MoveNfts from './MoveNfts'; -import MoveEvm from './MoveEvm'; -import MoveFromFlow from './EvmMove/MoveFromFlow'; -import MoveFromEvm from './EvmMove/MoveFromEvm'; -import { add } from 'lodash'; - -interface MoveBoardProps { - showMoveBoard: boolean; - handleCloseIconClicked: () => void; - handleCancelBtnClicked: () => void; - handleAddBtnClicked: () => void; -} - -const MoveBoard = (props: MoveBoardProps) => { - const usewallet = useWallet(); - const history = useHistory(); - const [showSelectNft, setSelectBoard] = useState(false); - const [moveFtOpen, setMoveFt] = useState(false); - const [childType, setChildType] = useState(''); - - // console.log('props.loggedInAccounts', props.current) - - const requestChildType = async () => { - const result = await usewallet.getActiveWallet(); - setChildType(result); - }; - - const closeFullPage = () => { - setMoveFt(false); - props.handleCancelBtnClicked(); - }; - - useEffect(() => { - requestChildType(); - }, []); - - return ( - - - - - - {chrome.i18n.getMessage('move_assets')} - - - - - - - - - - - {chrome.i18n.getMessage('Would_you_like_to_move')} - {`${childType === 'evm' ? 'FLOW' : 'EVM on FLOW'}`}{' '} - {chrome.i18n.getMessage('lowercaseaccount')}? - - - - - - - - - - {showSelectNft && ( - <> - {childType === 'evm' ? ( - setSelectBoard(false)} - handleCancelBtnClicked={() => setSelectBoard(false)} - handleAddBtnClicked={() => setSelectBoard(false)} - handleReturnHome={() => props.handleCancelBtnClicked()} - /> - ) : ( - setSelectBoard(false)} - handleCancelBtnClicked={() => setSelectBoard(false)} - handleAddBtnClicked={() => setSelectBoard(false)} - handleReturnHome={() => props.handleCancelBtnClicked()} - /> - )} - - )} - - {moveFtOpen && - (childType === 'evm' ? ( - closeFullPage()} - handleCancelBtnClicked={() => setMoveFt(false)} - handleAddBtnClicked={() => { - setMoveFt(false); - }} - /> - ) : ( - closeFullPage()} - handleCancelBtnClicked={() => setMoveFt(false)} - handleAddBtnClicked={() => { - setMoveFt(false); - }} - /> - ))} - - ); -}; - -export default MoveBoard; diff --git a/src/ui/views/Approval/components/EthApproval/EthSignType/DefaultBlock.tsx b/src/ui/views/Approval/components/EthApproval/EthSignType/DefaultBlock.tsx index 49275ac7..bd34ba9f 100644 --- a/src/ui/views/Approval/components/EthApproval/EthSignType/DefaultBlock.tsx +++ b/src/ui/views/Approval/components/EthApproval/EthSignType/DefaultBlock.tsx @@ -1,4 +1,5 @@ -import React, { useState, useEffect } from 'react'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import GppGoodRoundedIcon from '@mui/icons-material/GppGoodRounded'; import { Stack, Box, @@ -8,15 +9,15 @@ import { AccordionSummary, AccordionDetails, } from '@mui/material'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import GppGoodRoundedIcon from '@mui/icons-material/GppGoodRounded'; -import { useHistory } from 'react-router-dom'; -import { Presets } from 'react-component-transition'; -import IconFlow from '../../../../../../components/iconfont/IconFlow'; +import React, { useState, useEffect } from 'react'; import Highlight from 'react-highlight'; +import { useHistory } from 'react-router-dom'; + import { getScripts } from 'background/utils'; import placeholder from 'ui/FRWAssets/image/placeholder.png'; +import IconFlow from '../../../../../../components/iconfont/IconFlow'; + export const DefaultBlock = ({ title, host, diff --git a/src/ui/views/Approval/components/EthApproval/EthSignType/index.tsx b/src/ui/views/Approval/components/EthApproval/EthSignType/index.tsx index 4f17d701..a6a9c4d9 100644 --- a/src/ui/views/Approval/components/EthApproval/EthSignType/index.tsx +++ b/src/ui/views/Approval/components/EthApproval/EthSignType/index.tsx @@ -1,21 +1,17 @@ -import React, { useEffect, useState } from 'react'; +import { Stack, Box, Typography, CardMedia } from '@mui/material'; +import * as fcl from '@onflow/fcl'; +import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useApproval, useWallet } from 'ui/utils'; + // import { CHAINS_ENUM } from 'consts'; -import { ThemeProvider } from '@mui/system'; -import { Stack, Box, Typography, CardMedia } from '@mui/material'; -import theme from 'ui/style/LLTheme'; -import { LLPrimaryButton, LLSecondaryButton } from 'ui/FRWComponent'; import { LLConnectLoading, LLLinkingLoading } from '@/ui/FRWComponent'; -import { UserInfoResponse } from 'background/service/networkModel'; +import { type UserInfoResponse } from 'background/service/networkModel'; +import { LLPrimaryButton, LLSecondaryButton } from 'ui/FRWComponent'; +import { useApproval, useWallet, formatAddress } from 'ui/utils'; import { isValidEthereumAddress } from 'ui/utils/address'; -import { formatAddress } from 'ui/utils'; -import * as fcl from '@onflow/fcl'; interface ConnectProps { params: any; - // onChainChange(chain: CHAINS_ENUM): void; - // defaultChain: CHAINS_ENUM; } const EthSignType = ({ params }: ConnectProps) => { @@ -64,7 +60,7 @@ const EthSignType = ({ params }: ConnectProps) => { sig: string | null; } - const extractData = () => { + const extractData = useCallback(() => { console.log('obj ', params); let data = ''; let address = ''; @@ -79,7 +75,7 @@ const EthSignType = ({ params }: ConnectProps) => { const jsonObject = JSON.parse(data); setMessages(jsonObject); console.log('data, ', data); - }; + }, [params]); const handleCancel = () => { rejectApproval('User rejected the request.'); @@ -87,8 +83,9 @@ const EthSignType = ({ params }: ConnectProps) => { const handleAllow = async () => { await checkCoa(); + const network = await wallet.getNetwork(); resolveApproval({ - defaultChain: 646, + defaultChain: network === 'testnet' ? 545 : 747, signPermission: 'MAINNET_AND_TESTNET', }); }; @@ -114,7 +111,7 @@ const EthSignType = ({ params }: ConnectProps) => { if (params) { extractData(); } - }, []); + }, [extractData, params]); const JsonRenderer = ({ data }) => { // Recursive function to render objects, including arrays and nested objects @@ -246,7 +243,7 @@ const EthSignType = ({ params }: ConnectProps) => { }; return ( - + <> {isLoading ? ( {accountLinking ? ( @@ -324,7 +321,7 @@ const EthSignType = ({ params }: ConnectProps) => { )} - + ); }; diff --git a/src/ui/views/Approval/components/EthApproval/EthSignV1/DefaultBlock.tsx b/src/ui/views/Approval/components/EthApproval/EthSignV1/DefaultBlock.tsx index 49275ac7..bd34ba9f 100644 --- a/src/ui/views/Approval/components/EthApproval/EthSignV1/DefaultBlock.tsx +++ b/src/ui/views/Approval/components/EthApproval/EthSignV1/DefaultBlock.tsx @@ -1,4 +1,5 @@ -import React, { useState, useEffect } from 'react'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import GppGoodRoundedIcon from '@mui/icons-material/GppGoodRounded'; import { Stack, Box, @@ -8,15 +9,15 @@ import { AccordionSummary, AccordionDetails, } from '@mui/material'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import GppGoodRoundedIcon from '@mui/icons-material/GppGoodRounded'; -import { useHistory } from 'react-router-dom'; -import { Presets } from 'react-component-transition'; -import IconFlow from '../../../../../../components/iconfont/IconFlow'; +import React, { useState, useEffect } from 'react'; import Highlight from 'react-highlight'; +import { useHistory } from 'react-router-dom'; + import { getScripts } from 'background/utils'; import placeholder from 'ui/FRWAssets/image/placeholder.png'; +import IconFlow from '../../../../../../components/iconfont/IconFlow'; + export const DefaultBlock = ({ title, host, diff --git a/src/ui/views/Approval/components/EthApproval/EthSignV1/index.tsx b/src/ui/views/Approval/components/EthApproval/EthSignV1/index.tsx index b59f9cb4..be81817d 100644 --- a/src/ui/views/Approval/components/EthApproval/EthSignV1/index.tsx +++ b/src/ui/views/Approval/components/EthApproval/EthSignV1/index.tsx @@ -1,21 +1,17 @@ -import React, { useEffect, useState } from 'react'; +import { Stack, Box, Typography, CardMedia } from '@mui/material'; +import * as fcl from '@onflow/fcl'; +import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useApproval, useWallet } from 'ui/utils'; + // import { CHAINS_ENUM } from 'consts'; -import { ThemeProvider } from '@mui/system'; -import { Stack, Box, Typography, CardMedia } from '@mui/material'; -import theme from 'ui/style/LLTheme'; -import { LLPrimaryButton, LLSecondaryButton } from 'ui/FRWComponent'; import { LLConnectLoading, LLLinkingLoading } from '@/ui/FRWComponent'; -import { UserInfoResponse } from 'background/service/networkModel'; +import { type UserInfoResponse } from 'background/service/networkModel'; +import { LLPrimaryButton, LLSecondaryButton } from 'ui/FRWComponent'; +import { useApproval, useWallet } from 'ui/utils'; import { isValidEthereumAddress } from 'ui/utils/address'; -import { formatAddress } from 'ui/utils'; -import * as fcl from '@onflow/fcl'; interface ConnectProps { params: any; - // onChainChange(chain: CHAINS_ENUM): void; - // defaultChain: CHAINS_ENUM; } const EthSignV1 = ({ params }: ConnectProps) => { @@ -64,7 +60,7 @@ const EthSignV1 = ({ params }: ConnectProps) => { sig: string | null; } - const extractData = () => { + const extractData = useCallback(() => { console.log('obj ', params); let data = ''; let address = ''; @@ -78,7 +74,7 @@ const EthSignV1 = ({ params }: ConnectProps) => { } setMessages(data); console.log('data, ', data); - }; + }, [params]); const handleCancel = () => { rejectApproval('User rejected the request.'); @@ -86,8 +82,9 @@ const EthSignV1 = ({ params }: ConnectProps) => { const handleAllow = async () => { await checkCoa(); + const network = await wallet.getNetwork(); resolveApproval({ - defaultChain: 646, + defaultChain: network === 'testnet' ? 545 : 747, signPermission: 'MAINNET_AND_TESTNET', }); }; @@ -113,7 +110,7 @@ const EthSignV1 = ({ params }: ConnectProps) => { if (params) { extractData(); } - }, []); + }, [extractData, params]); const JsonRenderer = ({ data }) => { return ( @@ -147,7 +144,7 @@ const EthSignV1 = ({ params }: ConnectProps) => { }; return ( - + <> {isLoading ? ( {accountLinking ? ( @@ -225,7 +222,7 @@ const EthSignV1 = ({ params }: ConnectProps) => { )} - + ); }; diff --git a/src/ui/views/Approval/components/EthApproval/EthSuggest/index.tsx b/src/ui/views/Approval/components/EthApproval/EthSuggest/index.tsx index 221452d8..99e94e30 100644 --- a/src/ui/views/Approval/components/EthApproval/EthSuggest/index.tsx +++ b/src/ui/views/Approval/components/EthApproval/EthSuggest/index.tsx @@ -1,16 +1,15 @@ -import React, { useEffect, useState } from 'react'; -import { useLocation } from 'react-router-dom'; -import { useTranslation } from 'react-i18next'; -import { useApproval, useWallet } from 'ui/utils'; -import { withPrefix } from 'ui/utils/address'; -import { ThemeProvider } from '@mui/system'; import { Stack, Box, Typography, Divider, CardMedia } from '@mui/material'; -import theme from 'ui/style/LLTheme'; -// import EthMove from '../EthMove'; -import { LLPrimaryButton, LLSecondaryButton, LLConnectLoading } from 'ui/FRWComponent'; import { Contract, ethers } from 'ethers'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useLocation } from 'react-router-dom'; + import { storage } from '@/background/webapi'; import { EVM_ENDPOINT } from 'consts'; +import { LLPrimaryButton, LLSecondaryButton, LLConnectLoading } from 'ui/FRWComponent'; +import { useApproval, useWallet } from 'ui/utils'; +import { withPrefix } from 'ui/utils/address'; +// import EthMove from '../EthMove'; const EthSuggest = (data) => { const { state } = useLocation<{ @@ -20,83 +19,83 @@ const EthSuggest = (data) => { const { t } = useTranslation(); const usewallet = useWallet(); const [isLoading, setLoading] = useState(false); - const [defaultChain, setDefaultChain] = useState('FLOW'); - const [isEvm, setIsEvm] = useState(false); - // TODO: replace default logo const [logo, setLogo] = useState(''); const [evmAddress, setEvmAddress] = useState(''); const [isValidatingAddress, setIsValidatingAddress] = useState(false); const [validationError, setValidationError] = useState(false); const [coinInfo, setCoinInfo] = useState({}); - const init = async () => { - console.log('suggest data ', data); - const contractAddress = data.params.data.params.options.address; - addCustom(contractAddress); - }; - const addCustom = async (address) => { - setLoading(true); - const contractAddress = withPrefix(address)!.toLowerCase(); - const network = await usewallet.getNetwork(); - const provider = new ethers.JsonRpcProvider(EVM_ENDPOINT[network]); - const evmAddress = await usewallet.getEvmAddress(); - const ftContract = new Contract( - contractAddress!, - [ - 'function name() view returns (string)', - 'function symbol() view returns (string)', - 'function totalSupply() view returns (uint256)', - 'function decimals() view returns (uint8)', - 'function balanceOf(address) view returns (uint)', - ], - provider - ); + const addCustom = useCallback( + async (address) => { + setLoading(true); + const contractAddress = withPrefix(address)!.toLowerCase(); + const network = await usewallet.getNetwork(); + const provider = new ethers.JsonRpcProvider(EVM_ENDPOINT[network]); + const evmAddress = await usewallet.getEvmAddress(); + const ftContract = new Contract( + contractAddress!, + [ + 'function name() view returns (string)', + 'function symbol() view returns (string)', + 'function totalSupply() view returns (uint256)', + 'function decimals() view returns (uint8)', + 'function balanceOf(address) view returns (uint)', + ], + provider + ); - // Helper function to handle contract calls - async function getContractData(contract, method, ...args) { - try { - const result = await contract[method](...args); - if (!result || result === '0x') { - console.error(`No data returned for method: ${method}`); + // Helper function to handle contract calls + async function getContractData(contract, method, ...args) { + try { + const result = await contract[method](...args); + if (!result || result === '0x') { + console.error(`No data returned for method: ${method}`); + return null; + } + return result; + } catch (error) { + console.error(`Error calling ${method}:`, error); return null; } - return result; - } catch (error) { - console.error(`Error calling ${method}:`, error); - return null; } - } - const decimals = await getContractData(ftContract, 'decimals'); - const name = await getContractData(ftContract, 'name'); - const symbol = await getContractData(ftContract, 'symbol'); - const balance = await ftContract.balanceOf(evmAddress); - console.log('balance ', evmAddress, balance); + const decimals = await getContractData(ftContract, 'decimals'); + const name = await getContractData(ftContract, 'name'); + const symbol = await getContractData(ftContract, 'symbol'); + const balance = await ftContract.balanceOf(evmAddress); + console.log('balance ', evmAddress, balance); - if (decimals !== null && name !== null && symbol !== null) { - const info = { - coin: name, - unit: symbol, - icon: '', - price: 0, - change24h: 0, - total: Number(balance) / Math.pow(10, Number(decimals)), - address: contractAddress?.toLowerCase(), - decimals: Number(decimals), - }; + if (decimals !== null && name !== null && symbol !== null) { + const info = { + coin: name, + unit: symbol, + icon: '', + price: 0, + change24h: 0, + total: Number(balance) / Math.pow(10, Number(decimals)), + address: contractAddress?.toLowerCase(), + decimals: Number(decimals), + }; - const flowId = await usewallet.getAssociatedFlowIdentifier(contractAddress); - info['flowIdentifier'] = flowId; - setCoinInfo(info); - setLoading(false); - } else { - console.error('Failed to retrieve all required data for the token.'); - setIsValidatingAddress(false); - setValidationError(true); - setLoading(false); - } - }; + const flowId = await usewallet.getAssociatedFlowIdentifier(contractAddress); + info['flowIdentifier'] = flowId; + setCoinInfo(info); + setLoading(false); + } else { + console.error('Failed to retrieve all required data for the token.'); + setIsValidatingAddress(false); + setValidationError(true); + setLoading(false); + } + }, + [usewallet] + ); + const init = useCallback(async () => { + console.log('suggest data ', data); + const contractAddress = data.params.data.params.options.address; + addCustom(contractAddress); + }, [addCustom, data]); const importCustom = async () => { setLoading(true); @@ -133,7 +132,7 @@ const EthSuggest = (data) => { useEffect(() => { init(); - }, []); + }, [init]); const renderContent = () => ( @@ -253,9 +252,9 @@ const EthSuggest = (data) => { ); return ( - + <> {renderContent()} - + ); }; diff --git a/src/ui/views/Approval/components/EthApproval/EthSwitch/index.tsx b/src/ui/views/Approval/components/EthApproval/EthSwitch/index.tsx index fa44cdae..4df14d72 100644 --- a/src/ui/views/Approval/components/EthApproval/EthSwitch/index.tsx +++ b/src/ui/views/Approval/components/EthApproval/EthSwitch/index.tsx @@ -1,26 +1,22 @@ -import React, { useEffect, useState } from 'react'; -import { useLocation, useHistory } from 'react-router-dom'; -import { useTranslation } from 'react-i18next'; -import { useApproval, useWallet, formatAddress } from 'ui/utils'; -// import { CHAINS_ENUM } from 'consts'; -import { ThemeProvider } from '@mui/system'; import { Stack, Box, Typography, Divider, CardMedia } from '@mui/material'; -import { authnServiceDefinition, serviceDefinition } from 'background/controller/serviceDefinition'; -import theme from 'ui/style/LLTheme'; -import { LLPrimaryButton, LLSecondaryButton, LLConnectLoading } from 'ui/FRWComponent'; import { WalletUtils } from '@onflow/fcl'; -import Link from 'ui/FRWAssets/svg/link.svg'; -import testnetsvg from 'ui/FRWAssets/svg/testnet.svg'; -import mainnetsvg from 'ui/FRWAssets/svg/mainnet.svg'; -import linkGlobe from 'ui/FRWAssets/svg/linkGlobe.svg'; -import flowgrey from 'ui/FRWAssets/svg/flow-grey.svg'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useLocation, useHistory } from 'react-router-dom'; import { storage } from '@/background/webapi'; +import { authnServiceDefinition, serviceDefinition } from 'background/controller/serviceDefinition'; +import flowgrey from 'ui/FRWAssets/svg/flow-grey.svg'; +import Link from 'ui/FRWAssets/svg/link.svg'; +import linkGlobe from 'ui/FRWAssets/svg/linkGlobe.svg'; +import mainnetsvg from 'ui/FRWAssets/svg/mainnet.svg'; +import testnetsvg from 'ui/FRWAssets/svg/testnet.svg'; +import { LLPrimaryButton, LLSecondaryButton, LLConnectLoading } from 'ui/FRWComponent'; +import { useApproval, useWallet, formatAddress } from 'ui/utils'; +// import { CHAINS_ENUM } from 'consts'; interface ConnectProps { params: any; - // onChainChange(chain: CHAINS_ENUM): void; - // defaultChain: CHAINS_ENUM; } const EthSwitch = ({ params: { origin, target } }: ConnectProps) => { @@ -63,12 +59,12 @@ const EthSwitch = ({ params: { origin, target } }: ConnectProps) => { setMsgNetwork(target); } resolveApproval({ - defaultChain: 'FLOW', + defaultChain: target === 'testnet' ? 545 : 747, signPermission: 'MAINNET_AND_TESTNET', }); }; - const checkNetwork = async () => { + const checkNetwork = useCallback(async () => { console.log('target ', target); const network = await wallet.getNetwork(); @@ -80,11 +76,11 @@ const EthSwitch = ({ params: { origin, target } }: ConnectProps) => { } const address = await wallet.getCurrentAddress(); setCurrentAddress(address!); - }; + }, [wallet, target]); useEffect(() => { checkNetwork(); - }, [currentNetwork]); + }, [checkNetwork, currentNetwork]); const networkColor = (network: string) => { switch (network) { @@ -98,7 +94,7 @@ const EthSwitch = ({ params: { origin, target } }: ConnectProps) => { }; return ( - + <> { /> - + ); }; diff --git a/src/ui/views/Approval/components/SignMessage.tsx b/src/ui/views/Approval/components/SignMessage.tsx index c65a71b0..0981da35 100644 --- a/src/ui/views/Approval/components/SignMessage.tsx +++ b/src/ui/views/Approval/components/SignMessage.tsx @@ -1,8 +1,4 @@ -import React, { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useApproval, useWallet } from 'ui/utils'; -// import { CHAINS_ENUM } from 'consts'; -import { ThemeProvider } from '@mui/system'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import { Stack, Box, @@ -12,13 +8,15 @@ import { AccordionSummary, AccordionDetails, } from '@mui/material'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import theme from 'ui/style/LLTheme'; import * as fcl from '@onflow/fcl'; -import { LLPrimaryButton, LLSecondaryButton } from 'ui/FRWComponent'; -import './github-dark-dimmed.css'; +import React, { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { storage } from '@/background/webapi'; +import { LLPrimaryButton, LLSecondaryButton } from 'ui/FRWComponent'; +import { useApproval, useWallet } from 'ui/utils'; + +import './github-dark-dimmed.css'; interface ConnectProps { params: any; @@ -103,7 +101,9 @@ const SignMessage = ({ params: { icon, origin, tabId, type } }: ConnectProps) => const extMessageHandler = (msg, sender, sendResponse) => { if (msg.type === 'FCL:VIEW:READY:RESPONSE') { - msg.host && setHost(msg.host); + if (msg.host) { + setHost(msg.host); + } if (msg.config.app.title) { setTitle(msg.config.app.title); } @@ -119,14 +119,14 @@ const SignMessage = ({ params: { icon, origin, tabId, type } }: ConnectProps) => }; useEffect(() => { - chrome.tabs && + if (chrome.tabs) { chrome.tabs .query({ active: true, currentWindow: false, }) .then((tabs) => { - const targetTab = tabs.filter((item) => item.id == tabId); + const targetTab = tabs.filter((item) => item.id === tabId); let host = ''; if (targetTab[0].url) { @@ -139,6 +139,7 @@ const SignMessage = ({ params: { icon, origin, tabId, type } }: ConnectProps) => setOpener(targetTab[0].id); chrome.tabs.sendMessage(targetTab[0].id || 0, { type: 'FCL:VIEW:READY' }); }); + } chrome.runtime?.onMessage.addListener(extMessageHandler); @@ -147,7 +148,7 @@ const SignMessage = ({ params: { icon, origin, tabId, type } }: ConnectProps) => console.log('removeListener'); }); }; - }, []); + }, [tabId]); window.onbeforeunload = () => { if (!approval) { @@ -167,7 +168,7 @@ const SignMessage = ({ params: { icon, origin, tabId, type } }: ConnectProps) => // } return ( - + <> /> - + ); }; diff --git a/src/ui/views/Approval/components/index.ts b/src/ui/views/Approval/components/index.ts index ef940fb3..b382b5b2 100644 --- a/src/ui/views/Approval/components/index.ts +++ b/src/ui/views/Approval/components/index.ts @@ -1,12 +1,11 @@ export { default as Connect } from './Connect'; -export { default as Confimation } from './Confimation'; +export { default as Confirmation } from './Confimation'; export { default as SignMessage } from './SignMessage'; export { default as EthConnect } from './EthApproval/EthConnect'; export { default as EthSuggest } from './EthApproval/EthSuggest'; export { default as EthConfirm } from './EthApproval/EthConfirm'; export { default as EthEnable } from './EthApproval/EthEnable'; -export { default as EthMove } from './EthApproval/EthMove'; export { default as EthSwitch } from './EthApproval/EthSwitch'; export { default as EthSignType } from './EthApproval/EthSignType'; export { default as EthSignV1 } from './EthApproval/EthSignV1'; diff --git a/src/ui/views/Dashboard/Header.tsx b/src/ui/views/Dashboard/Header.tsx index 1ee323c4..7d5b9e97 100644 --- a/src/ui/views/Dashboard/Header.tsx +++ b/src/ui/views/Dashboard/Header.tsx @@ -25,6 +25,7 @@ import { useHistory } from 'react-router-dom'; import { storage } from '@/background/webapi'; import eventBus from '@/eventBus'; +import StorageExceededAlert from '@/ui/FRWComponent/StorageExceededAlert'; import { withPrefix, ensureEvmAddressPrefix } from '@/ui/utils/address'; import { useNews } from '@/ui/utils/NewsContext'; import type { UserInfoResponse, WalletResponse } from 'background/service/networkModel'; @@ -74,7 +75,7 @@ const tempEmoji = [ }, ]; -const Header = ({ loading }) => { +const Header = ({ loading = false }) => { const usewallet = useWallet(); const classes = useStyles(); const history = useHistory(); @@ -116,6 +117,8 @@ const Header = ({ loading }) => { const [switchLoading, setSwitchLoading] = useState(false); + const [, setErrorMessage] = useState(''); + const [errorCode, setErrorCode] = useState(null); // const { unreadCount } = useNotificationStore(); // TODO: add notification count const { unreadCount } = useNews(); @@ -191,6 +194,10 @@ const Header = ({ loading }) => { evmWallet.address = evmAddress; await setCurrent(evmWallet); setMainLoading(false); + } else if (isChild) { + const currentWallet = await usewallet.getCurrentWallet(); + await setCurrent(currentWallet); + setMainLoading(false); } else { const mainwallet = await usewallet.returnMainWallet(); await setCurrent(mainwallet); @@ -251,30 +258,32 @@ const Header = ({ loading }) => { usewallet.setChildWallet(childresp); }, [freshUserInfo, freshUserWallet, usewallet]); - const switchAccount = async (account) => { - setSwitchLoading(true); - try { - const switchingTo = process.env.NODE_ENV === 'production' ? 'mainnet' : 'testnet'; - await storage.set('currentAccountIndex', account.indexInLoggedInAccounts); - if (account.id) { - await storage.set('currentId', account.id); - } else { - await storage.set('currentId', ''); + const switchAccount = useCallback( + async (account) => { + setSwitchLoading(true); + try { + const switchingTo = process.env.NODE_ENV === 'production' ? 'mainnet' : 'testnet'; + await storage.set('currentAccountIndex', account.indexInLoggedInAccounts); + if (account.id) { + await storage.set('currentId', account.id); + } else { + await storage.set('currentId', ''); + } + + await usewallet.lockWallet(); + await usewallet.clearWallet(); + await usewallet.switchNetwork(switchingTo); + + history.push('/switchunlock'); + } catch (error) { + console.error('Error during account switch:', error); + // Handle any additional error reporting or user feedback here if needed + } finally { + setSwitchLoading(false); } - - await usewallet.lockWallet(); - await usewallet.clearWallet(); - await usewallet.refreshAll(); - await usewallet.switchNetwork(switchingTo); - - history.push('/switchunlock'); - } catch (error) { - console.error('Error during account switch:', error); - // Handle any additional error reporting or user feedback here if needed - } finally { - setSwitchLoading(false); - } - }; + }, + [usewallet, history] + ); const loadNetwork = useCallback(async () => { const network = await usewallet.getNetwork(); @@ -346,7 +355,6 @@ const Header = ({ loading }) => { // Navigate if needed history.push('/dashboard'); - //eslint-disable-next-line no-restricted-globals window.location.reload(); }; @@ -354,13 +362,19 @@ const Header = ({ loading }) => { // This is just to handle pending transactions // The header will listen to the transactionPending event // It shows spinner on the header when there is a pending transaction - // It doesn't need to handle transactionError events if (request.msg === 'transactionPending') { setIsPending(true); } if (request.msg === 'transactionDone') { setIsPending(false); } + // The header should handle transactionError events + if (request.msg === 'transactionError') { + console.warn('transactionError', request.errorMessage, request.errorCode); + // The error message is not used anywhere else for now + setErrorMessage(request.errorMessage); + setErrorCode(request.errorCode); + } return true; }; @@ -998,6 +1012,7 @@ const Header = ({ loading }) => { )} + setErrorCode(null)} /> ); }; diff --git a/src/ui/views/Dashboard/index.tsx b/src/ui/views/Dashboard/index.tsx index 3119d494..eb5a4ffa 100644 --- a/src/ui/views/Dashboard/index.tsx +++ b/src/ui/views/Dashboard/index.tsx @@ -1,20 +1,22 @@ -import React, { useEffect, useState } from 'react'; import Box from '@mui/material/Box'; -import PropTypes from 'prop-types'; import { useTheme } from '@mui/material/styles'; +import { getApp, initializeApp } from 'firebase/app'; +import { fetchAndActivate, getRemoteConfig } from 'firebase/remote-config'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useLocation, useHistory } from 'react-router-dom'; import SwipeableViews from 'react-swipeable-views'; -import WalletTab from '../Wallet'; + +import { getFirbaseConfig } from 'background/utils/firebaseConfig'; +import { LLTestnetIndicator } from 'ui/FRWComponent'; +import { useWallet } from 'ui/utils'; + import NFTTab from '../NFT'; import NftEvm from '../NftEvm'; -import Staking from '../Staking'; import SettingTab from '../Setting'; -import { useLocation, useHistory } from 'react-router-dom'; +import Staking from '../Staking'; +import WalletTab from '../Wallet'; + import NavBar from './NavBar'; -import { useWallet } from 'ui/utils'; -import { LLTestnetIndicator } from 'ui/FRWComponent'; -import { fetchAndActivate, getRemoteConfig } from 'firebase/remote-config'; -import { getApp, initializeApp } from 'firebase/app'; -import { getFirbaseConfig } from 'background/utils/firebaseConfig'; function TabPanel(props) { const { children, value, index, ...other } = props; @@ -32,12 +34,6 @@ function TabPanel(props) { ); } -TabPanel.propTypes = { - children: PropTypes.node, - index: PropTypes.number.isRequired, - value: PropTypes.number.isRequired, -}; - const Dashboard = ({ value, setValue }) => { // const [value, setValue] = React.useState('wallet'); const history = useHistory(); @@ -53,7 +49,7 @@ const Dashboard = ({ value, setValue }) => { setValue(index); }; - const fetchAll = async () => { + const fetchAll = useCallback(async () => { setLoading(true); //todo fix cadence loading await wallet.getCadenceScripts(); @@ -87,11 +83,11 @@ const Dashboard = ({ value, setValue }) => { setNetwork(network); setDomain(userDomain); setLoading(false); - }; + }, [wallet]); useEffect(() => { fetchAll(); - }, [wallet]); + }, [fetchAll, wallet]); return (
@@ -105,7 +101,6 @@ const Dashboard = ({ value, setValue }) => { > {currentNetwork === 'testnet' && value === 0 && } {/*
*/} - ({ customInputLabel: { @@ -142,13 +144,13 @@ const MoveToken = ({ setCoinType(!coinType); }; - const currentCoinType = () => { + const currentCoinType = useCallback(() => { setCoin(coinInfo.unit); - }; + }, [coinInfo.unit]); useEffect(() => { currentCoinType(); - }, []); + }, [currentCoinType]); useEffect(() => { if (coinType) { @@ -165,7 +167,7 @@ const MoveToken = ({ setAmount(parseFloat(value.toFixed(3))); } } - }, [secondAmount]); + }, [coinInfo.balance, coinInfo.price, coinType, secondAmount, setAmount, setExceed]); useEffect(() => { if (!coinType) { @@ -182,7 +184,7 @@ const MoveToken = ({ setSecondAmount(value); } } - }, [amount, coin]); + }, [amount, coin, coinInfo, coinInfo.price, coinInfo.unit, coinType, setExceed, setSecondAmount]); return ( @@ -265,33 +267,31 @@ const MoveToken = ({ {coinInfo.balance} - - {exceed && ( - + + + - - - {chrome.i18n.getMessage('Insufficient_balance') + - (coin === 'flow' - ? chrome.i18n.getMessage('on_Flow_the_balance_cant_less_than_0001_FLOW') - : '')} - - - )} - + {chrome.i18n.getMessage('Insufficient_balance') + + (coin === 'flow' + ? chrome.i18n.getMessage('on_Flow_the_balance_cant_less_than_0001_FLOW') + : '')} + + + ); diff --git a/src/ui/views/EvmMove/MoveFromChild/index.tsx b/src/ui/views/EvmMove/MoveFromChild/index.tsx index 8ddf7e4e..821a24b5 100644 --- a/src/ui/views/EvmMove/MoveFromChild/index.tsx +++ b/src/ui/views/EvmMove/MoveFromChild/index.tsx @@ -1,19 +1,23 @@ -import React, { useState, useEffect } from 'react'; -import { useHistory } from 'react-router-dom'; -import { Box, Button, Typography, Drawer, IconButton, Grid } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; -import { CoinItem } from 'background/service/coinList'; +import { Box, Button, Typography, Drawer, IconButton, Grid } from '@mui/material'; +import React, { useState, useEffect, useCallback } from 'react'; +import { useHistory } from 'react-router-dom'; + +import wallet from '@/background/controller/wallet'; +import { WarningStorageLowSnackbar } from '@/ui/FRWComponent/WarningStorageLowSnackbar'; +import { useStorageCheck } from '@/ui/utils/useStorageCheck'; +import type { CoinItem } from 'background/service/coinList'; +import type { Contact } from 'background/service/networkModel'; +import { LLSpinner } from 'ui/FRWComponent'; +import { useWallet } from 'ui/utils'; +import { withPrefix } from 'ui/utils/address'; + +import IconSwitch from '../../../../components/iconfont/IconSwitch'; import theme from '../../../style/LLTheme'; -import { ThemeProvider } from '@mui/material/styles'; import TransferFrom from '../TransferFrom'; import TransferTo from '../TransferTo'; + import MoveToken from './MoveToken'; -import { useWallet } from 'ui/utils'; -import { withPrefix } from 'ui/utils/address'; -import IconSwitch from '../../../../components/iconfont/IconSwitch'; -import { LLSpinner } from 'ui/FRWComponent'; -import { Contact } from 'background/service/networkModel'; -import wallet from '@/background/controller/wallet'; interface TransferConfirmationProps { isConfirmationOpen: boolean; @@ -22,6 +26,27 @@ interface TransferConfirmationProps { handleCancelBtnClicked: () => void; handleAddBtnClicked: () => void; } +const USER_CONTACT = { + address: '', + id: 0, + contact_name: '', + avatar: '', + domain: { + domain_type: 999, + value: '', + }, +} as unknown as Contact; + +const CHILD_CONTACT = { + address: '', + id: 0, + contact_name: '', + avatar: '', + domain: { + domain_type: 999, + value: '', + }, +} as unknown as Contact; const MoveFromChild = (props: TransferConfirmationProps) => { enum ENV { @@ -38,27 +63,6 @@ const MoveFromChild = (props: TransferConfirmationProps) => { // Static = 'Static', // CDN = 'CDN' // } - const userContact = { - address: '', - id: 0, - contact_name: '', - avatar: '', - domain: { - domain_type: 999, - value: '', - }, - } as unknown as Contact; - - const childContact = { - address: '', - id: 0, - contact_name: '', - avatar: '', - domain: { - domain_type: 999, - value: '', - }, - } as unknown as Contact; const empty: CoinItem = { coin: '', @@ -78,8 +82,8 @@ const MoveFromChild = (props: TransferConfirmationProps) => { // const [exceed, setExceed] = useState(false); const [amount, setAmount] = useState(''); // const [validated, setValidated] = useState(null); - const [userInfo, setUser] = useState(userContact); - const [childUserInfo, setChildUser] = useState(childContact); + const [userInfo, setUser] = useState(USER_CONTACT); + const [childUserInfo, setChildUser] = useState(CHILD_CONTACT); const [network, setNetwork] = useState('mainnet'); const [childAddress, setChildAddress] = useState(''); const [coinInfo, setCoinInfo] = useState(empty); @@ -88,8 +92,15 @@ const MoveFromChild = (props: TransferConfirmationProps) => { const [errorType, setErrorType] = useState(null); const [exceed, setExceed] = useState(false); const [minAmount, setMinAmount] = useState(0.001); + const { sufficient: isSufficient, sufficientAfterAction } = useStorageCheck({ + transferAmount: Number(amount) || 0, + movingBetweenEVMAndFlow: true, + }); + + const isLowStorage = isSufficient !== undefined && !isSufficient; // isSufficient is undefined when the storage check is not yet completed + const isLowStorageAfterAction = sufficientAfterAction !== undefined && !sufficientAfterAction; - const setUserWallet = async () => { + const setUserWallet = useCallback(async () => { // const walletList = await storage.get('userWallet'); setLoading(true); const token = await usewallet.getCurrentCoin(); @@ -107,38 +118,40 @@ const MoveFromChild = (props: TransferConfirmationProps) => { setCoinInfo(coinInfo!); const info = await usewallet.getUserInfo(false); - userContact.address = withPrefix(wallet) || ''; - userContact.avatar = info.avatar; - userContact.contact_name = info.username; - setUser(userContact); + + const walletAddress = withPrefix(wallet) || ''; + setUser({ + ...USER_CONTACT, + address: walletAddress, + avatar: info.avatar, + contact_name: info.username, + }); const childResp = await usewallet.checkUserChildAccount(); const cwallet = childResp[currentAddress!]; - childContact.address = withPrefix(currentAddress!) || ''; - childContact.avatar = cwallet.thumbnail.url; - childContact.contact_name = cwallet.name; - - setUserMinAmount(); - setChildUser(childContact); - // const result = await usewallet.openapi.fetchTokenList(network); - setLoading(false); - return; - }; - - const setUserMinAmount = async () => { try { // Try fetching the min amount from the API - const minAmount = await usewallet.openapi.getAccountMinFlow(userContact.address); + const minAmount = await usewallet.openapi.getAccountMinFlow(walletAddress); setMinAmount(minAmount); } catch (error) { // If there's an error, set the min amount to 0.001 console.error('Error fetching min amount:', error); setMinAmount(0.001); } - }; - const moveToken = async () => { + setChildUser({ + ...CHILD_CONTACT, + address: withPrefix(currentAddress!) || '', + avatar: cwallet.thumbnail.url, + contact_name: cwallet.name, + }); + // const result = await usewallet.openapi.fetchTokenList(network); + setLoading(false); + return; + }, [usewallet]); + + const moveToken = useCallback(async () => { setLoading(true); const tokenResult = await wallet.openapi.getTokenInfo(currentCoin, network); usewallet @@ -147,8 +160,8 @@ const MoveFromChild = (props: TransferConfirmationProps) => { usewallet.listenTransaction( createRes, true, - 'Transfer to EVM complete', - `Your have moved ${amount} Flow to your EVM address ${childAddress}. \nClick to view this transaction.` + 'Transfer complete', + `Your have moved ${amount} ${tokenResult!.name} to your address ${userWallet}. \nClick to view this transaction.` ); await usewallet.setDashIndex(0); history.push('/dashboard?activity=1'); @@ -159,28 +172,28 @@ const MoveFromChild = (props: TransferConfirmationProps) => { console.log(err); setLoading(false); }); - }; + }, [currentCoin, network, usewallet, childUserInfo, amount, userWallet, history, props]); const handleMove = async () => { moveToken(); }; - const handleCoinInfo = async () => { + const handleCoinInfo = useCallback(async () => { if (coinList.length > 0) { const coinInfo = coinList.find( (coin) => coin.unit.toLowerCase() === currentCoin.toLowerCase() ); setCoinInfo(coinInfo!); } - }; + }, [coinList, currentCoin]); useEffect(() => { setUserWallet(); - }, []); + }, [setUserWallet]); useEffect(() => { handleCoinInfo(); - }, [currentCoin]); + }, [currentCoin, handleCoinInfo]); return ( { + - + ); }; diff --git a/src/ui/views/Import/GoogleImport/GoogleAccounts.tsx b/src/ui/views/Import/GoogleImport/GoogleAccounts.tsx index 76cc4e6f..ae3c991f 100644 --- a/src/ui/views/Import/GoogleImport/GoogleAccounts.tsx +++ b/src/ui/views/Import/GoogleImport/GoogleAccounts.tsx @@ -1,5 +1,4 @@ -import React, { useState, useEffect } from 'react'; -import { ThemeProvider } from '@mui/system'; +import ArrowForwardRoundedIcon from '@mui/icons-material/ArrowForwardRounded'; import { Typography, Avatar, @@ -10,10 +9,9 @@ import { ListItemText, IconButton, ListItem, - CssBaseline, } from '@mui/material'; -import theme from '../../../style/LLTheme'; -import ArrowForwardRoundedIcon from '@mui/icons-material/ArrowForwardRounded'; +import React, { useState, useEffect, useCallback } from 'react'; + import { useWallet } from 'ui/utils'; const FetchAvatar = ({ username }) => { @@ -22,17 +20,20 @@ const FetchAvatar = ({ username }) => { ); const wallet = useWallet(); - const fetchUserAvatar = async (username) => { - const { data } = await wallet.openapi.searchUser(username); - const users = data.users; - if (users.length > 0 && users[0].avatar) { - setAvatar(users[0].avatar); - } - }; + const fetchUserAvatar = useCallback( + async (username) => { + const { data } = await wallet.openapi.searchUser(username); + const users = data.users; + if (users.length > 0 && users[0].avatar) { + setAvatar(users[0].avatar); + } + }, + [wallet] + ); useEffect(() => { fetchUserAvatar(username); - }, []); + }, [fetchUserAvatar, username]); return ; }; @@ -41,8 +42,7 @@ const GoogleAccounts = ({ handleClick, accounts, setUsername }) => { const [canGoNext, setCanGoNext] = useState(true); return ( - - + <> {chrome.i18n.getMessage('We__ve__found') + ' '} @@ -104,7 +104,7 @@ const GoogleAccounts = ({ handleClick, accounts, setUsername }) => { - + ); }; diff --git a/src/ui/views/Import/GoogleImport/RecoverPassword.tsx b/src/ui/views/Import/GoogleImport/RecoverPassword.tsx index 9beb9f08..c935ee3c 100644 --- a/src/ui/views/Import/GoogleImport/RecoverPassword.tsx +++ b/src/ui/views/Import/GoogleImport/RecoverPassword.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Button, Typography, @@ -11,22 +10,21 @@ import { LinearProgress, Alert, Snackbar, - CssBaseline, } from '@mui/material'; - -import { LLSpinner } from 'ui/FRWComponent'; -import CancelIcon from '../../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; -import VisibilityIcon from '@mui/icons-material/Visibility'; -import FormControlLabel from '@mui/material/FormControlLabel'; import Checkbox from '@mui/material/Checkbox'; -import { Presets } from 'react-component-transition'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import { makeStyles, styled } from '@mui/styles'; +import { Box } from '@mui/system'; +import React, { useEffect, useState } from 'react'; import zxcvbn from 'zxcvbn'; -import theme from '../../../style/LLTheme'; + +import { LLSpinner, LLNotFound } from '@/ui/FRWComponent'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; import { useWallet, saveIndex } from 'ui/utils'; -import { LLNotFound } from 'ui/FRWComponent'; -import { storage } from '@/background/webapi'; + +import CheckCircleIcon from '../../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../../components/iconfont/IconClose'; +import theme from '../../../style/LLTheme'; // const helperTextStyles = makeStyles(() => ({ // root: { @@ -254,11 +252,10 @@ const SetPassword = ({ handleClick, mnemonic, username, lastPassword }) => { setPassword(''); setConfirmPassword(''); } - }, [isCheck]); + }, [isCheck, lastPassword]); return ( - - + <> {!showDialog ? ( @@ -306,7 +303,9 @@ const SetPassword = ({ handleClick, mnemonic, username, lastPassword }) => { } /> - {password && helperText} + + {helperText} + { } /> - - {confirmPassword && helperMatch} - + + {helperMatch} + @@ -384,7 +383,7 @@ const SetPassword = ({ handleClick, mnemonic, username, lastPassword }) => { {errorMessage} - + ); }; diff --git a/src/ui/views/Import/GoogleImport/RecoveryPhrase.tsx b/src/ui/views/Import/GoogleImport/RecoveryPhrase.tsx index c9e2a32f..55ec2c12 100644 --- a/src/ui/views/Import/GoogleImport/RecoveryPhrase.tsx +++ b/src/ui/views/Import/GoogleImport/RecoveryPhrase.tsx @@ -1,20 +1,20 @@ -import React, { useState } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, IconButton, CssBaseline } from '@mui/material'; -import theme from '../../../style/LLTheme'; -import LockRoundedIcon from '@mui/icons-material/LockRounded'; +import InfoIcon from '@mui/icons-material/Info'; import LockOpenRoundedIcon from '@mui/icons-material/LockOpenRounded'; +import LockRoundedIcon from '@mui/icons-material/LockRounded'; +import { Button, Typography, IconButton } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useState } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; + import IconCopy from '../../../../components/iconfont/IconCopy'; -import { Presets } from 'react-component-transition'; -import InfoIcon from '@mui/icons-material/Info'; const RecoveryPhrase = ({ handleClick, mnemonic }) => { const [canGoNext, setCanGoNext] = useState(true); const [isCoverBlur, coverBlur] = useState(false); return ( - - + <> {chrome.i18n.getMessage('Review') + ' '} @@ -191,7 +191,7 @@ const RecoveryPhrase = ({ handleClick, mnemonic }) => { marginBottom: '8px', }} > - + { )} - + - + ); }; diff --git a/src/ui/views/Import/GoogleImport/index.tsx b/src/ui/views/Import/GoogleImport/index.tsx index c06add63..7cd96471 100644 --- a/src/ui/views/Import/GoogleImport/index.tsx +++ b/src/ui/views/Import/GoogleImport/index.tsx @@ -1,19 +1,21 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory, useLocation } from 'react-router-dom'; -import { Box, ThemeProvider } from '@mui/system'; import { IconButton, Typography } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useHistory, useLocation } from 'react-router-dom'; + +import { LLPinAlert } from '@/ui/FRWComponent'; +import Confetti from '@/ui/FRWComponent/Confetti'; +import SlideLeftRight from '@/ui/FRWComponent/SlideLeftRight'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; + import BackButtonIcon from '../../../../components/iconfont/IconBackButton'; -import theme from '../../../style/LLTheme'; -import RegisterHeader from '../../Register/RegisterHeader'; import AllSet from '../../Register/AllSet'; +import RegisterHeader from '../../Register/RegisterHeader'; + import DecryptWallet from './DecryptWallet'; -import RecoveryPhrase from './RecoveryPhrase'; import GoogleAccounts from './GoogleAccounts'; import RecoveryPassword from './RecoverPassword'; -import Particles from 'react-tsparticles'; -import { ComponentTransition, AnimationTypes } from 'react-component-transition'; -import { LLPinAlert } from '@/ui/FRWComponent'; -import options from '../options'; +import RecoveryPhrase from './RecoveryPhrase'; enum Direction { Right, @@ -52,16 +54,16 @@ const GoogleImport = () => { } }; - const getGoogleAccounts = async () => { + const getGoogleAccounts = useCallback(async () => { // const backupFile = await storage.get('googleBackup'); // await setBackup(backupFile); const users = location.state.accounts; setAccounts(users); - }; + }, [location.state.accounts]); useEffect(() => { getGoogleAccounts(); - }, []); + }, [getGoogleAccounts]); const page = (index) => { switch (index) { @@ -99,7 +101,7 @@ const GoogleImport = () => { const heights = [500, 500, 600, 600, 500]; return ( - + <> { alignItems: 'center', }} > - {activeIndex == 4 && ( - - )} - + {activeIndex === 4 && } + { - + {page(activeIndex)} - + - + ); }; diff --git a/src/ui/views/Import/ImportComponent/PrivateKey.tsx b/src/ui/views/Import/ImportComponent/PrivateKey.tsx index e3a5fab3..57c3f213 100644 --- a/src/ui/views/Import/ImportComponent/PrivateKey.tsx +++ b/src/ui/views/Import/ImportComponent/PrivateKey.tsx @@ -1,7 +1,7 @@ -import { useEffect, useState, useContext } from 'react'; -import React from 'react'; import { Typography, FormControl, Input, Box } from '@mui/material'; -import { Presets } from 'react-component-transition'; +import React from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; const PrivateKey = ({ helperText, msgBgColor, pk, setpk }) => { return ( @@ -39,20 +39,18 @@ const PrivateKey = ({ helperText, msgBgColor, pk, setpk }) => { }, }} /> - - {pk && ( - - {helperText} - - )} - + + + {helperText} + + diff --git a/src/ui/views/Import/ImportComponent/SeedPhrase.tsx b/src/ui/views/Import/ImportComponent/SeedPhrase.tsx index cfa2383e..ac2adc82 100644 --- a/src/ui/views/Import/ImportComponent/SeedPhrase.tsx +++ b/src/ui/views/Import/ImportComponent/SeedPhrase.tsx @@ -1,7 +1,7 @@ -import { useEffect, useState, useContext } from 'react'; -import React from 'react'; import { Typography, FormControl, Input, Box } from '@mui/material'; -import { Presets } from 'react-component-transition'; +import React from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; const SeedPhrase = ({ helperText, msgBgColor, mnemonic, setmnemonic }) => { return ( @@ -42,20 +42,18 @@ const SeedPhrase = ({ helperText, msgBgColor, mnemonic, setmnemonic }) => { }, }} /> - - {mnemonic && ( - - {helperText} - - )} - + + + {helperText} + + diff --git a/src/ui/views/Import/ImportPager.tsx b/src/ui/views/Import/ImportPager.tsx index 10073583..04cd73f4 100644 --- a/src/ui/views/Import/ImportPager.tsx +++ b/src/ui/views/Import/ImportPager.tsx @@ -1,19 +1,21 @@ +import { IconButton, Typography, Button, Snackbar, Alert } from '@mui/material'; +import { Box } from '@mui/system'; import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; -import { Box, ThemeProvider } from '@mui/system'; -import { IconButton, Typography, Button, Snackbar, Alert } from '@mui/material'; + +import Confetti from '@/ui/FRWComponent/Confetti'; +import SlideLeftRight from '@/ui/FRWComponent/SlideLeftRight'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { LLPinAlert, LLSpinner } from 'ui/FRWComponent'; +import { useWallet } from 'ui/utils'; + import BackButtonIcon from '../../../components/iconfont/IconBackButton'; import IconGoogleDrive from '../../../components/iconfont/IconGoogleDrive'; -import theme from '../../style/LLTheme'; +import AllSet from '../Register/AllSet'; import RegisterHeader from '../Register/RegisterHeader'; + import ImportRecoveryPhrase from './ImportRecoveryPhrase'; -import AllSet from '../Register/AllSet'; import RecoverPassword from './RecoverPassword'; -import Particles from 'react-tsparticles'; -import { LLPinAlert, LLSpinner } from 'ui/FRWComponent'; -import { ComponentTransition, AnimationTypes } from 'react-component-transition'; -import { useWallet } from 'ui/utils'; -import options from './options'; enum Direction { Right, @@ -68,7 +70,7 @@ const ImportPager = () => { } setLoading(false); } catch (e) { - console.log(e); + console.error(e); setShowError(true); setErrorMessage(chrome.i18n.getMessage('Something__is__wrong')); setLoading(false); @@ -105,7 +107,7 @@ const ImportPager = () => { }; return ( - + <> { alignItems: 'center', }} > - {activeIndex == 2 && ( - - )} + {activeIndex === 2 && } - + @@ -169,25 +166,9 @@ const ImportPager = () => { - + {page(activeIndex)} - + {activeIndex === 0 && ( @@ -225,7 +206,7 @@ const ImportPager = () => { - + ); }; diff --git a/src/ui/views/Import/ImportRecoveryPhrase.tsx b/src/ui/views/Import/ImportRecoveryPhrase.tsx index 32f383c1..e1e77104 100644 --- a/src/ui/views/Import/ImportRecoveryPhrase.tsx +++ b/src/ui/views/Import/ImportRecoveryPhrase.tsx @@ -1,26 +1,18 @@ -import React, { useEffect, useState } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; +import { Typography, Tabs, Tab, CircularProgress, Button, Snackbar, Alert } from '@mui/material'; import { makeStyles } from '@mui/styles'; -import { - Typography, - Tabs, - Tab, - CircularProgress, - Button, - Snackbar, - Alert, - CssBaseline, -} from '@mui/material'; -import theme from '../../style/LLTheme'; -import { Presets } from 'react-component-transition'; -import { useWallet } from 'ui/utils'; -import CancelIcon from '../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; +import { Box } from '@mui/system'; import * as bip39 from 'bip39'; -import { LLNotFound, LLSpinner } from 'ui/FRWComponent'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; + import { storage } from '@/background/webapi'; -import SeedPhrase from './ImportComponent/SeedPhrase'; +import { LLNotFound, LLSpinner } from 'ui/FRWComponent'; +import { useWallet } from 'ui/utils'; + +import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../components/iconfont/IconClose'; + import PrivateKey from './ImportComponent/PrivateKey'; +import SeedPhrase from './ImportComponent/SeedPhrase'; function TabPanel(props) { const { children, value, index, ...other } = props; @@ -70,6 +62,58 @@ const useStyles = makeStyles((theme) => ({ }, })); +const mnemonicError = (errorMsg) => ( + + + + {errorMsg} + + +); + +const MnemonicCorrect: React.FC = () => ( + + + + {chrome.i18n.getMessage('Recovery__phrase__valid')} + + +); + +const PrivateCorrect: React.FC = () => ( + + + + {chrome.i18n.getMessage('Private__key_valid')} + + +); + +const MnemonicLoading: React.FC = () => ( + + + {chrome.i18n.getMessage('Checking')} + +); + const ImportRecoveryPhrase = ({ handleClick, confirmMnemonic, confirmPk, setUsername }) => { const classes = useStyles(); const wallet = useWallet(); @@ -85,7 +129,7 @@ const ImportRecoveryPhrase = ({ handleClick, confirmMnemonic, confirmPk, setUser const [showDialog, setShowDialog] = useState(false); const [showError, setShowError] = useState(false); - const [helperText, setHelperText] = useState(
); + const [helperText, setHelperText] = useState(
); const [selectedTab, setSelectedTab] = useState(0); const signIn = async () => { @@ -134,91 +178,22 @@ const ImportRecoveryPhrase = ({ handleClick, confirmMnemonic, confirmPk, setUser } }; - const mnemonicError = (errorMsg) => ( - - - - {errorMsg} - - - ); - - const mnemonicCorrect = ( - - - - {chrome.i18n.getMessage('Recovery__phrase__valid')} - - - ); - - const privateCorrect = ( - - - - {chrome.i18n.getMessage('Private__key_valid')} - - - ); - - const mnemonicLoading = () => ( - - - {chrome.i18n.getMessage('Checking')} - + const setErrorMessage = useCallback( + (message: string) => { + setLoading(false); + setMnemonicValid(false); + setHelperText(mnemonicError(message)); + }, + [setLoading, setMnemonicValid, setHelperText] ); - - const renderSnackBar = () => { - return ( - setShowError(false)} - > - { - setShowError(false); - }} - > - Something went wrong, please try again later - - - ); - }; - useEffect(() => { setMnemonicValid(false); - setHelperText(mnemonicLoading); + setHelperText(); setLoading(true); const delayDebounceFn = setTimeout(() => { setLoading(false); const length = mnemonic.trim().split(/\s+/g).length; - if (!(length == 12 || length == 24)) { + if (!(length === 12 || length === 24)) { setErrorMessage( chrome.i18n.getMessage('Recovery__phrases__word__count__must__be__12__or__24__words') ); @@ -232,17 +207,17 @@ const ImportRecoveryPhrase = ({ handleClick, confirmMnemonic, confirmPk, setUser } setMnemonicValid(true); - setHelperText(mnemonicCorrect); + setHelperText(); storage.set('premnemonic', formatted); setMnemonic(formatted); }, 500); return () => clearTimeout(delayDebounceFn); - }, [mnemonic]); + }, [mnemonic, setErrorMessage]); useEffect(() => { setMnemonicValid(false); - setHelperText(mnemonicLoading); + setHelperText(); setLoading(true); const delayDebounceFn = setTimeout(() => { setLoading(false); @@ -250,7 +225,7 @@ const ImportRecoveryPhrase = ({ handleClick, confirmMnemonic, confirmPk, setUser const isvalid = hexRegex.test(pk); if (isvalid) { setMnemonicValid(true); - setHelperText(privateCorrect); + setHelperText(); return; } else { setErrorMessage(chrome.i18n.getMessage('Private__is__invalid')); @@ -259,13 +234,7 @@ const ImportRecoveryPhrase = ({ handleClick, confirmMnemonic, confirmPk, setUser }, 500); return () => clearTimeout(delayDebounceFn); - }, [pk]); - - const setErrorMessage = (message: string) => { - setLoading(false); - setMnemonicValid(false); - setHelperText(mnemonicError(message)); - }; + }, [pk, setErrorMessage]); const msgBgColor = () => { if (isLoading) { @@ -279,8 +248,7 @@ const ImportRecoveryPhrase = ({ handleClick, confirmMnemonic, confirmPk, setUser }; return ( - - + <> {!showDialog ? ( @@ -333,12 +301,27 @@ const ImportRecoveryPhrase = ({ handleClick, confirmMnemonic, confirmPk, setUser - {renderSnackBar()} + setShowError(false)} + > + { + setShowError(false); + }} + > + Something went wrong, please try again later + + ) : ( )} - + ); }; diff --git a/src/ui/views/Import/RecoverPassword.tsx b/src/ui/views/Import/RecoverPassword.tsx index 05936d56..c1764991 100644 --- a/src/ui/views/Import/RecoverPassword.tsx +++ b/src/ui/views/Import/RecoverPassword.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Button, Typography, @@ -11,18 +10,19 @@ import { LinearProgress, Alert, Snackbar, - CssBaseline, } from '@mui/material'; -import { LLSpinner } from 'ui/FRWComponent'; -import CancelIcon from '../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; -import VisibilityIcon from '@mui/icons-material/Visibility'; -import { Presets } from 'react-component-transition'; +import { makeStyles, styled } from '@mui/styles'; +import { Box } from '@mui/system'; +import React, { useEffect, useState } from 'react'; import zxcvbn from 'zxcvbn'; -import theme from '../../style/LLTheme'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { LLSpinner } from 'ui/FRWComponent'; import { useWallet } from 'ui/utils'; +import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../components/iconfont/IconClose'; + // const helperTextStyles = makeStyles(() => ({ // root: { // size: '16px', @@ -239,8 +239,7 @@ const SetPassword = ({ handleClick, mnemonic, pk, username }) => { }, [confirmPassword, password]); return ( - - + <> {chrome.i18n.getMessage('Welcome__Back')} @@ -287,7 +286,9 @@ const SetPassword = ({ handleClick, mnemonic, pk, username }) => { } /> - {password && helperText} + + {helperText} + { } /> - - {confirmPassword && helperMatch} - + + {helperMatch} + @@ -375,7 +376,7 @@ const SetPassword = ({ handleClick, mnemonic, pk, username }) => { {errorMessage} - + ); }; diff --git a/src/ui/views/Import/options.ts b/src/ui/views/Import/options.ts deleted file mode 100644 index 9f7a97b7..00000000 --- a/src/ui/views/Import/options.ts +++ /dev/null @@ -1,125 +0,0 @@ -const Options = { - fullScreen: true, - fpsLimit: 120, - detectRetina: true, - emitters: { - direction: 'bottom', - startCount: 0, - position: { x: 50, y: 0 }, - size: { - width: 20, - height: 0, - }, - rate: { - delay: 0, - quantity: 2, - }, - life: { - count: 200, - duration: 0.01, - // delay:0.6, - }, - }, - particles: { - number: { - value: 250, - }, - color: { - value: ['#9146FF', '#FFAAA8', '#8FFFD2', '#FFD37A', '#FF38DB'], - }, - shape: { - type: ['square', 'circle', 'heart'], - }, - opacity: { - value: 1, - animation: { - enable: true, - minimumValue: 0, - speed: 0.5, - startValue: 'max', - destroy: 'min', - }, - }, - size: { - value: 5, - }, - links: { - enable: false, - }, - life: { - duration: { - sync: true, - value: 10, - }, - count: 1, - }, - move: { - angle: { - value: 45, - offset: 0, - }, - drift: { - min: -0, - max: 0, - }, - enable: true, - gravity: { - enable: true, - acceleration: 20, - }, - speed: 90, - decay: 1 - 0.9, - direction: -90, - random: true, - straight: false, - outModes: { - default: 'none', - bottom: 'destroy', - }, - }, - rotate: { - value: { - min: 0, - max: 360, - }, - direction: 'random', - animation: { - enable: true, - speed: 60, - }, - }, - tilt: { - direction: 'random', - enable: true, - value: { - min: 0, - max: 360, - }, - animation: { - enable: true, - speed: 60, - }, - }, - roll: { - darken: { - enable: true, - value: 25, - }, - enable: true, - speed: { - min: 15, - max: 25, - }, - }, - wobble: { - distance: 20, - enable: true, - speed: { - min: -15, - max: 15, - }, - }, - }, -}; - -export default Options; diff --git a/src/ui/views/Inbox/Nft.tsx b/src/ui/views/Inbox/Nft.tsx index 1d1e60ae..f0555e0d 100644 --- a/src/ui/views/Inbox/Nft.tsx +++ b/src/ui/views/Inbox/Nft.tsx @@ -1,8 +1,3 @@ -import React, { useEffect, useState } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { useHistory } from 'react-router-dom'; -import theme from '../../style/LLTheme'; -import { useWallet } from 'ui/utils'; import { Typography, ListItem, @@ -14,9 +9,14 @@ import { List, CardMedia, } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useEffect, useState } from 'react'; +import { useHistory } from 'react-router-dom'; + +import fallback from 'ui/FRWAssets/image/errorImage.png'; import activity from 'ui/FRWAssets/svg/activity.svg'; import { LLPrimaryButton, LLSpinner } from 'ui/FRWComponent'; -import fallback from 'ui/FRWAssets/image/errorImage.png'; +import { useWallet } from 'ui/utils'; const Nft = ({ data }) => { const wallet = useWallet(); @@ -70,7 +70,7 @@ const Nft = ({ data }) => { }, [data]); return ( - + <> {!isLoading ? ( @@ -156,7 +156,7 @@ const Nft = ({ data }) => { alignItems: 'center', }} > - {ids == ibx ? ( + {ids === ibx ? ( { }) )} - + ); }; diff --git a/src/ui/views/Inbox/Token.tsx b/src/ui/views/Inbox/Token.tsx index bce01e4a..7fb8ed96 100644 --- a/src/ui/views/Inbox/Token.tsx +++ b/src/ui/views/Inbox/Token.tsx @@ -1,8 +1,3 @@ -import React, { useEffect, useState } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { useHistory } from 'react-router-dom'; -import theme from '../../style/LLTheme'; -import { useWallet } from 'ui/utils'; import { Typography, ListItem, @@ -14,11 +9,16 @@ import { List, CardMedia, } from '@mui/material'; -import activity from 'ui/FRWAssets/svg/activity.svg'; -import { LLPrimaryButton, LLSpinner } from 'ui/FRWComponent'; +import { Box } from '@mui/system'; import { setDefaultWordlist } from 'bip39'; import { reject } from 'lodash'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useHistory } from 'react-router-dom'; + import { TokenModel } from 'background/service/networkModel'; +import activity from 'ui/FRWAssets/svg/activity.svg'; +import { LLPrimaryButton, LLSpinner } from 'ui/FRWComponent'; +import { useWallet } from 'ui/utils'; const Token = ({ data }) => { const wallet = useWallet(); @@ -61,11 +61,11 @@ const Token = ({ data }) => { }); }; - const getToken = () => { + const getToken = useCallback(() => { wallet.openapi.getAllToken().then((res) => { setTokens(res); }); - }; + }, [setTokens, wallet.openapi]); useEffect(() => { setLoading(data === null); @@ -75,10 +75,10 @@ const Token = ({ data }) => { checkEmpty(data); setLoading(false); } - }, [data]); + }, [data, getToken]); return ( - + <> {!isLoading ? ( @@ -87,7 +87,7 @@ const Token = ({ data }) => { {(Object.keys(inbox['vaultBalances']) || []).map((ibx: any, v: any) => { const contractName = ibx.split('.')[2]; const token = tokens.find( - (item) => item.contract_name.toLowerCase() == contractName.toLowerCase() + (item) => item.contract_name.toLowerCase() === contractName.toLowerCase() ); let tokenIcon = 'https://lilico.app/placeholder-2.0.png'; if (token) { @@ -270,7 +270,7 @@ const Token = ({ data }) => { }) )} - + ); }; diff --git a/src/ui/views/InnerRoute.tsx b/src/ui/views/InnerRoute.tsx index fc0c7002..9605a13d 100644 --- a/src/ui/views/InnerRoute.tsx +++ b/src/ui/views/InnerRoute.tsx @@ -1,57 +1,57 @@ -import React, { useState, useEffect } from 'react'; import { makeStyles } from '@mui/styles'; -import Settingone from './Setting/Settingone'; -import Switchaccount from './Setting/Switchaccount'; -import Security from './Setting/Security'; -import PrivateKeyPassword from './Setting/privatekey/Privatekeypassword'; -import Recoveryphrasepassword from './Setting/recoveryphase/Recoveryphrasepassword'; -import KeyList from './Setting/KeyList/KeyList'; -import Keydetail from './Setting/privatekey/Keydetail'; -import RecoveryPhasesDetail from './Setting/recoveryphase/Recoveryphasedetail'; -import Resetpwd from './Setting/Resetpwd'; -import './MainRoute.css'; -import { withRouter } from 'react-router-dom'; -import Header from './Dashboard/Header'; -import Dashboard from './Dashboard'; -import CollectionDetail from './NFT/CollectionDetail'; -import EvmCollectionDetail from './NftEvm/CollectionDetail'; -import Detail from './NFT/Detail'; -import NftEvmDetail from './NftEvm/Detail'; +import React, { useState, useEffect, useCallback } from 'react'; +import { Switch, withRouter, type RouteComponentProps } from 'react-router-dom'; + import { PrivateRoute } from 'ui/component'; import { useWallet } from 'ui/utils'; + +import Deposit from '../views/Deposit'; import Enable from '../views/Enable'; import Send from '../views/Send'; import Swap from '../views/Swap'; -import Deposit from '../views/Deposit'; -import AddressBook from './Setting/AddressBook'; -import SendAmount from './Send/SendAmount'; -import SendEth from './Send/SendEth'; -import TokenDetail from './TokenDetail'; -import TokenList from './TokenList'; -import AddCustomEvmToken from './Wallet/AddCustom/AddCustomEvmToken'; -import Inbox from './Inbox'; -import StakingPage from './Staking/StakingPage'; -import UnstakePage from './Staking/UnstakePage'; -import NodeDetail from './Staking/NodeDetail'; + +import Dashboard from './Dashboard'; +import Header from './Dashboard/Header'; import Flowns from './Flowns'; +import Inbox from './Inbox'; +import CollectionDetail from './NFT/CollectionDetail'; +import Detail from './NFT/Detail'; import AddList from './NFT/NFTList/AddList'; +import SendToAddress from './NFT/SendNFT/SendToAddress'; +import EvmCollectionDetail from './NftEvm/CollectionDetail'; +import NftEvmDetail from './NftEvm/Detail'; +import SendNftEvm from './NftEvm/SendNFT/SendToAddress'; +import SendAmount from './Send/SendAmount'; +import SendEth from './Send/SendEth'; import About from './Setting/About/About'; -import Linked from './Setting/Linked'; -import LinkedDetail from './Setting/Linked/LinkedDetail'; -import LinkedCollection from './Setting/Linked/LinkedCollection'; -import LinkedNftDetail from './Setting/Linked/LinkedNftDetail'; import Account from './Setting/Account'; +import AddressBook from './Setting/AddressBook'; +import ManageBackups from './Setting/Backups'; import DeveloperMode from './Setting/DeveloperMode/DeveloperMode'; -import Devices from './Setting/Devices/Devices'; import DeviceInfo from './Setting/Devices/DeviceInfo'; +import KeyList from './Setting/KeyList/KeyList'; +import Linked from './Setting/Linked'; +import LinkedCollection from './Setting/Linked/LinkedCollection'; +import LinkedDetail from './Setting/Linked/LinkedDetail'; +import LinkedNftDetail from './Setting/Linked/LinkedNftDetail'; +import Keydetail from './Setting/privatekey/Keydetail'; +import PrivateKeyPassword from './Setting/privatekey/Privatekeypassword'; +import RecoveryPhasesDetail from './Setting/recoveryphase/Recoveryphasedetail'; +import Recoveryphrasepassword from './Setting/recoveryphase/Recoveryphrasepassword'; +import Resetpwd from './Setting/Resetpwd'; +import Security from './Setting/Security'; +import Settingone from './Setting/Settingone'; +import Switchaccount from './Setting/Switchaccount'; +import './MainRoute.css'; import WalletList from './Setting/Wallet'; -import WalletDetail from './Setting/Wallet/WalletDetail'; import RemoveWallet from './Setting/Wallet/RemoveWallet'; -import ManageBackups from './Setting/Backups'; -import SendToAddress from './NFT/SendNFT/SendToAddress'; -import SendNftEvm from './NftEvm/SendNFT/SendToAddress'; -import { spring, AnimatedSwitch } from 'react-router-transition'; -// import OnRampList from './Wallet/OnRampList'; +import WalletDetail from './Setting/Wallet/WalletDetail'; +import NodeDetail from './Staking/NodeDetail'; +import StakingPage from './Staking/StakingPage'; +import UnstakePage from './Staking/UnstakePage'; +import TokenDetail from './TokenDetail'; +import TokenList from './TokenList'; +import AddCustomEvmToken from './Wallet/AddCustom/AddCustomEvmToken'; const useStyles = makeStyles(() => ({ innerWrapper: { @@ -67,45 +67,13 @@ const useStyles = makeStyles(() => ({ }, })); -function zoom(val) { - return spring(val, { - stiffness: 174, - damping: 24, - }); -} - -const switchConfig = { - atEnter: { - opacity: 0, - offset: 50, - }, - atLeave: { - opacity: 0, - offset: zoom(-50), - }, - atActive: { - opacity: 1, - offset: zoom(0), - }, -}; - -function mapStyles(styles) { - return { - opacity: styles.opacity, - transform: `translateX(${styles.offset}px)`, - }; -} - -const Inner = (props) => { +const InnerRoute = (props: RouteComponentProps) => { const classes = useStyles(); - // const location = useLocation(); - // const history = useHistory(); - const [loading, setLoading] = useState(false); const [value, setValue] = useState(0); const wallet = useWallet(); - const fetch = async () => { + const fetch = useCallback(async () => { const dashIndex = await wallet.getDashIndex(); if (dashIndex) { setValue(dashIndex); @@ -113,216 +81,167 @@ const Inner = (props) => { setValue(0); await wallet.setDashIndex(0); } - }; + }, [wallet]); useEffect(() => { fetch(); - }, []); + }, [fetch]); useEffect(() => { wallet.setDashIndex(value); - }, [value]); + }, [value, wallet]); return (
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {/* - - */} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {/* ======= - - - - - */} - +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
); }; -export default withRouter(Inner); +export default withRouter(InnerRoute); diff --git a/src/ui/views/MainRoute.tsx b/src/ui/views/MainRoute.tsx index 75a3e908..61cf72d4 100644 --- a/src/ui/views/MainRoute.tsx +++ b/src/ui/views/MainRoute.tsx @@ -1,99 +1,51 @@ import React from 'react'; import { Switch, Route } from 'react-router-dom'; -import Synce from './Sync'; -import RegisterPager from './Register/RegisterPager'; -import RecoverRegister from './RecoverRegister'; + import AddressImport from './AddressImport'; +import GoogleImport from './AddressImport/GoogleImport'; +import AddWelcome from './AddWelcome'; import AddRegister from './AddWelcome/AddRegister'; import AddImport from './AddWelcome/AddressImport'; +import AddGoogle from './AddWelcome/AddressImport/GoogleImport'; import AddSync from './AddWelcome/Sync'; // import ProxySync from './AddWelcome/ProxySync'; -import AddWelcome from './AddWelcome'; -import GoogleImport from './AddressImport/GoogleImport'; -import AddGoogle from './AddWelcome/AddressImport/GoogleImport'; import Forgot from './Forgot'; -import Reset from './Forgot/Reset'; import Recover from './Forgot/Recover'; +import Reset from './Forgot/Reset'; +import RecoverRegister from './RecoverRegister'; +import RegisterPager from './Register/RegisterPager'; +import Synce from './Sync'; import WelcomePage from './WelcomePage'; -import './MainRoute.css'; -import { spring, AnimatedSwitch } from 'react-router-transition'; -// eslint-disable-next-line @typescript-eslint/no-empty-function +import './MainRoute.css'; const LogPageView = () => { return null; }; -function mapStyles(styles) { - return { - opacity: styles.opacity, - transform: `scale(${styles.scale})`, - }; -} - -// wrap the `spring` helper to use a bouncy config -function bounce(val) { - return spring(val, { - stiffness: 33, - damping: 22, - }); -} - -// child matches will... -const bounceTransition = { - // start in a transparent, upscaled state - atEnter: { - opacity: 0.5, - // offset: 10, - scale: 1.05, - }, - // leave in a transparent, downscaled state - atLeave: { - opacity: bounce(0.5), - // offset: 10, - scale: bounce(0.95), - }, - // and rest at an opaque, normally-scaled state - atActive: { - opacity: 1, - // offset: 0, - scale: bounce(1), - }, -}; - -const Main = () => { +export const MainRoute: React.FC = () => { return (
- - - - - - - - - {/* */} - - - - - - - - - - - - + + + + + + + + {/* */} + + + + + + + + + + +
); }; - -export default Main; diff --git a/src/ui/views/MoveBoard/MoveEvm/index.tsx b/src/ui/views/MoveBoard/MoveEvm/index.tsx index f1e586fb..2ca29e1b 100644 --- a/src/ui/views/MoveBoard/MoveEvm/index.tsx +++ b/src/ui/views/MoveBoard/MoveEvm/index.tsx @@ -1,9 +1,11 @@ import CloseIcon from '@mui/icons-material/Close'; import { Box, Button, Skeleton, Typography, Drawer, IconButton, CardMedia } from '@mui/material'; -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; import WarningSnackbar from '@/ui/FRWComponent/WarningSnackbar'; +import { WarningStorageLowSnackbar } from '@/ui/FRWComponent/WarningStorageLowSnackbar'; +import { useStorageCheck } from '@/ui/utils/useStorageCheck'; import alertMark from 'ui/FRWAssets/svg/alertMark.svg'; import moveSelectDrop from 'ui/FRWAssets/svg/moveSelectDrop.svg'; import selected from 'ui/FRWAssets/svg/selected.svg'; @@ -36,6 +38,13 @@ const MoveEvm = (props: MoveBoardProps) => { const [errorOpen, setShowError] = useState(false); const [selectCollection, setSelectCollection] = useState(false); const [selectedAccount, setSelectedChildAccount] = useState(null); + const { sufficient: isSufficient, sufficientAfterAction } = useStorageCheck({ + transferAmount: 0, + movingBetweenEVMAndFlow: true, + }); + + const isLowStorage = isSufficient !== undefined && !isSufficient; // isSufficient is undefined when the storage check is not yet completed + const isLowStorageAfterAction = sufficientAfterAction !== undefined && !sufficientAfterAction; const handleErrorClose = (event?: React.SyntheticEvent | Event, reason?: string) => { if (reason === 'clickaway') { @@ -44,7 +53,7 @@ const MoveEvm = (props: MoveBoardProps) => { setShowError(false); }; - const updateCurrentCollection = async () => { + const updateCurrentCollection = useCallback(async () => { if (collectionList && cadenceNft) { const collection = collectionList.find((collection) => collection.id === selectedCollection); @@ -52,9 +61,9 @@ const MoveEvm = (props: MoveBoardProps) => { setCollectionDetail(collection); setCollectionInfo(cadenceResult); } - }; + }, [collectionList, cadenceNft, selectedCollection, usewallet]); - const requestCadenceNft = async () => { + const requestCadenceNft = useCallback(async () => { const cadenceResult = await usewallet.reqeustEvmNft(); const tokensWithNfts = cadenceResult.filter((token) => token.ids && token.ids.length > 0); const filteredData = tokensWithNfts.filter((item) => item.collection.flowIdentifier); @@ -77,7 +86,7 @@ const MoveEvm = (props: MoveBoardProps) => { setCollectionInfo({ nfts: [] }); } setLoading(false); - }; + }, [usewallet]); const toggleSelectNft = async (nftId) => { const tempIdArray = [...nftIdArray]; @@ -166,11 +175,11 @@ const MoveEvm = (props: MoveBoardProps) => { useEffect(() => { requestCadenceNft(); - }, []); + }, [requestCadenceNft]); useEffect(() => { updateCurrentCollection(); - }, [collectionList, cadenceNft, selectedCollection]); + }, [collectionList, cadenceNft, selectedCollection, updateCurrentCollection]); return ( { )} + - + ); }; diff --git a/src/ui/views/RecoverRegister/PickUsername.tsx b/src/ui/views/RecoverRegister/PickUsername.tsx index 1f88f7a0..571500dd 100644 --- a/src/ui/views/RecoverRegister/PickUsername.tsx +++ b/src/ui/views/RecoverRegister/PickUsername.tsx @@ -1,14 +1,22 @@ -import React, { useEffect, useState } from 'react'; +import { + CircularProgress, + IconButton, + Button, + Typography, + FormControl, + Input, + InputAdornment, +} from '@mui/material'; import { makeStyles } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, FormControl, Input, InputAdornment, CssBaseline } from '@mui/material'; -import CancelIcon from '../../../components/iconfont/IconClose'; +import { Box } from '@mui/system'; +import React, { useCallback, useEffect, useState } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { useWallet } from 'ui/utils'; + import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; -import theme from '../../style/LLTheme'; +import CancelIcon from '../../../components/iconfont/IconClose'; import EmailIcon from '../../assets/alternate-email.svg'; -import { Presets } from 'react-component-transition'; -import { useWallet } from 'ui/utils'; -import { CircularProgress, IconButton } from '@mui/material'; const useStyles = makeStyles(() => ({ customInputLabel: { @@ -30,6 +38,41 @@ const useStyles = makeStyles(() => ({ }, }, })); +const UsernameError = (errorMsg) => ( + + + + {errorMsg} + + +); +const UsernameCorrect = ( + + + + {chrome.i18n.getMessage('Sounds_good')} + + +); +const UsernameLoading = () => ( + + + Checking + {chrome.i18n.getMessage('Flow_Core')} + +); const PickUsername = ({ handleClick, savedUsername, getUsername }) => { const classes = useStyles(); @@ -37,60 +80,21 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { const [isLoading, setLoading] = useState(false); const [usernameValid, setUsernameValid] = useState(false); - const usernameError = (errorMsg) => ( - - - - {errorMsg} - - - ); - const usernameCorrect = ( - - - - {chrome.i18n.getMessage('Sounds_good')} - - - ); - const usernameLoading = () => ( - - - Checking - {chrome.i18n.getMessage('Flow_Core')} - - ); - const [username, setUsername] = useState(savedUsername || ''); const [helperText, setHelperText] = useState(
); - const regex = /^[A-Za-z0-9]{3,15}$/; - - const setErrorMessage = (message: string) => { - setLoading(false); - setUsernameValid(false); - setHelperText(usernameError(message)); - }; + const setErrorMessage = useCallback( + (message: string) => { + setLoading(false); + setUsernameValid(false); + setHelperText(UsernameError(message)); + }, + [setLoading, setUsernameValid, setHelperText] + ); useEffect(() => { setUsernameValid(false); - setHelperText(usernameLoading); + setHelperText(UsernameLoading); setLoading(true); const delayDebounceFn = setTimeout(() => { if (username.length < 3) { @@ -102,6 +106,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { setErrorMessage(chrome.i18n.getMessage('Too__long')); return; } + const regex = /^[A-Za-z0-9]{3,15}$/; if (!regex.test(username)) { setErrorMessage( @@ -114,13 +119,13 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { .checkUsername(username.toLowerCase()) .then((response) => { setLoading(false); - if (response.data.username != username.toLowerCase()) { + if (response.data.username !== username.toLowerCase()) { setLoading(false); return; } if (response.data.unique) { setUsernameValid(true); - setHelperText(usernameCorrect); + setHelperText(UsernameCorrect); } else { setErrorMessage(chrome.i18n.getMessage('This__name__has__been__taken')); } @@ -132,7 +137,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { }, 500); return () => clearTimeout(delayDebounceFn); - }, [username]); + }, [setErrorMessage, username, wallet.openapi]); const msgBgColor = () => { if (isLoading) { @@ -142,8 +147,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { }; return ( - - + <> {chrome.i18n.getMessage('Pick__Your') + ' '} @@ -187,20 +191,18 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { } /> - - {username && ( - - {helperText} - - )} - + + + {helperText} + + @@ -224,7 +226,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { - + ); }; diff --git a/src/ui/views/RecoverRegister/RecoveryPhrase.tsx b/src/ui/views/RecoverRegister/RecoveryPhrase.tsx index d548b60b..26fabf20 100644 --- a/src/ui/views/RecoverRegister/RecoveryPhrase.tsx +++ b/src/ui/views/RecoverRegister/RecoveryPhrase.tsx @@ -1,9 +1,9 @@ -import React, { useState } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, IconButton, CssBaseline } from '@mui/material'; -import theme from '../../style/LLTheme'; -import LockRoundedIcon from '@mui/icons-material/LockRounded'; import LockOpenRoundedIcon from '@mui/icons-material/LockOpenRounded'; +import LockRoundedIcon from '@mui/icons-material/LockRounded'; +import { Button, Typography, IconButton } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useState } from 'react'; + import IconCopy from '../../../components/iconfont/IconCopy'; const RecoveryPhrase = ({ handleClick, mnemonic }) => { @@ -11,8 +11,7 @@ const RecoveryPhrase = ({ handleClick, mnemonic }) => { const [isCoverBlur, coverBlur] = useState(false); return ( - - + <> {chrome.i18n.getMessage('Recovery') + ' '} @@ -192,7 +191,7 @@ const RecoveryPhrase = ({ handleClick, mnemonic }) => { - + ); }; diff --git a/src/ui/views/RecoverRegister/RegisterHeader.tsx b/src/ui/views/RecoverRegister/RegisterHeader.tsx index e6ad55e5..ebb9631a 100644 --- a/src/ui/views/RecoverRegister/RegisterHeader.tsx +++ b/src/ui/views/RecoverRegister/RegisterHeader.tsx @@ -1,13 +1,11 @@ -import React from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button } from '@mui/material'; import HelpOutlineRoundedIcon from '@mui/icons-material/HelpOutlineRounded'; import PhoneAndroidRoundedIcon from '@mui/icons-material/PhoneAndroidRounded'; -import theme from '../../style/LLTheme'; - +import { Button } from '@mui/material'; +import { Box } from '@mui/system'; +import React from 'react'; const RegisterHeader = () => { return ( - + <> { {chrome.i18n.getMessage('Need__Help')} - + ); }; diff --git a/src/ui/views/RecoverRegister/SetPassword.tsx b/src/ui/views/RecoverRegister/SetPassword.tsx index bc2d5a47..30d6e950 100644 --- a/src/ui/views/RecoverRegister/SetPassword.tsx +++ b/src/ui/views/RecoverRegister/SetPassword.tsx @@ -7,7 +7,6 @@ import { Alert, Snackbar, Link, - CssBaseline, Input, InputAdornment, FormGroup, @@ -16,20 +15,19 @@ import { import Checkbox from '@mui/material/Checkbox'; import FormControlLabel from '@mui/material/FormControlLabel'; import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import { Box } from '@mui/system'; import HDWallet from 'ethereum-hdwallet'; import React, { useEffect, useState } from 'react'; -import { Presets } from 'react-component-transition'; import zxcvbn from 'zxcvbn'; import { storage } from '@/background/webapi'; import { LLSpinner } from '@/ui/FRWComponent'; -import type { AccountKey } from 'background/service/networkModel'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { type AccountKey } from 'background/service/networkModel'; import { useWallet, saveIndex, mixpanelBrowserService } from 'ui/utils'; import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; import CancelIcon from '../../../components/iconfont/IconClose'; -import theme from '../../style/LLTheme'; const useStyles = makeStyles(() => ({ customInputLabel: { @@ -265,8 +263,7 @@ const SetPassword = ({ handleClick, mnemonic, username }) => { }, [confirmPassword, password]); return ( - - + <> {chrome.i18n.getMessage('Create') + ' '} @@ -313,7 +310,9 @@ const SetPassword = ({ handleClick, mnemonic, username }) => { } /> - {password && helperText} + + {helperText} + { } /> - - {confirmPassword && helperMatch} - + + {helperMatch} + @@ -406,7 +405,7 @@ const SetPassword = ({ handleClick, mnemonic, username }) => { - + ); }; diff --git a/src/ui/views/RecoverRegister/index.tsx b/src/ui/views/RecoverRegister/index.tsx index cd11941d..0cdf98fe 100644 --- a/src/ui/views/RecoverRegister/index.tsx +++ b/src/ui/views/RecoverRegister/index.tsx @@ -1,25 +1,26 @@ -import React, { useState, useEffect } from 'react'; +import ExtensionRoundedIcon from '@mui/icons-material/ExtensionRounded'; +import PushPinOutlinedIcon from '@mui/icons-material/PushPinOutlined'; +import { IconButton, Typography, Snackbar, SnackbarContent } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useState, useEffect, useRef } from 'react'; import { useHistory } from 'react-router-dom'; -import { Box, ThemeProvider } from '@mui/system'; -import { IconButton, Typography, Snackbar, SnackbarContent, Slide } from '@mui/material'; + +import { storage } from '@/background/webapi'; +import Confetti from '@/ui/FRWComponent/Confetti'; +import SlideLeftRight from '@/ui/FRWComponent/SlideLeftRight'; + +import lilicoIcon from '../../..//..//_raw/images/icon-48.png'; import BackButtonIcon from '../../../components/iconfont/IconBackButton'; -import theme from '../../style/LLTheme'; + +import AllSet from './AllSet'; import PickUsername from './PickUsername'; import RecoveryPhrase from './RecoveryPhrase'; -import AllSet from './AllSet'; import SetPassword from './SetPassword'; -import Particles from 'react-tsparticles'; -import { storage } from '@/background/webapi'; -import { ComponentTransition, AnimationTypes } from 'react-component-transition'; -import PushPinOutlinedIcon from '@mui/icons-material/PushPinOutlined'; -import ExtensionRoundedIcon from '@mui/icons-material/ExtensionRounded'; -import lilicoIcon from '../../..//..//_raw/images/icon-48.png'; + enum Direction { Right, Left, } -import options from '../Import/options'; - const RecoverRegister = () => { const history = useHistory(); const [activeIndex, onChange] = useState(0); @@ -27,6 +28,7 @@ const RecoverRegister = () => { const [username, setUsername] = useState(''); const [mnemonic, setMnemonic] = useState(''); + const pageRef = useRef(null); const getUsername = (username: string) => { setUsername(username.toLowerCase()); }; @@ -61,7 +63,7 @@ const RecoverRegister = () => { } }; - const page = (index) => { + const Page = React.forwardRef(({ index }: { index: number }, pageRef) => { switch (index) { case 0: return ( @@ -74,18 +76,12 @@ const RecoverRegister = () => { case 3: return ; default: - return
; + return ; } - }; - - const slideTransition = (props) => { - return ; - }; - - const height = [480, 520, 580, 480, 480]; + }); return ( - + <> { alignItems: 'center', }} > - {activeIndex == 3 && ( - - )} - {/* */} - - + {activeIndex === 3 && } + + { - - {page(activeIndex)} - + + + - + ); }; diff --git a/src/ui/views/Register/AllSet.tsx b/src/ui/views/Register/AllSet.tsx index 80a68f0b..d04a6f72 100644 --- a/src/ui/views/Register/AllSet.tsx +++ b/src/ui/views/Register/AllSet.tsx @@ -1,11 +1,9 @@ -import { Button, Typography, CssBaseline, CardMedia } from '@mui/material'; -import { Box, ThemeProvider } from '@mui/system'; +import { Button, Typography, CardMedia } from '@mui/material'; +import { Box } from '@mui/system'; import React, { useCallback, useEffect } from 'react'; import AllSetIcon from 'ui/FRWAssets/svg/allset.svg'; -import { mixpanelBrowserService, useWallet } from 'ui/utils'; - -import theme from '../../style/LLTheme'; +import { useWallet, mixpanelBrowserService } from 'ui/utils'; const AllSet = ({ handleClick }) => { const wallet = useWallet(); @@ -29,9 +27,9 @@ const AllSet = ({ handleClick }) => { trackAccountRecovered(); }); }, [loadScript, trackAccountRecovered]); + return ( - - + <> { - + ); }; diff --git a/src/ui/views/Register/GoogleBackup.tsx b/src/ui/views/Register/GoogleBackup.tsx index de6cc46f..26911a04 100644 --- a/src/ui/views/Register/GoogleBackup.tsx +++ b/src/ui/views/Register/GoogleBackup.tsx @@ -1,12 +1,13 @@ +import InfoIcon from '@mui/icons-material/Info'; +import { Button, Typography } from '@mui/material'; +import { Box } from '@mui/system'; import React, { useState } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, CssBaseline } from '@mui/material'; -import theme from '../../style/LLTheme'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { LLSpinner } from 'ui/FRWComponent'; import { useWallet } from 'ui/utils'; + import IconGoogleDrive from '../../../components/iconfont/IconGoogleDrive'; -import { LLSpinner } from 'ui/FRWComponent'; -import InfoIcon from '@mui/icons-material/Info'; -import { Presets } from 'react-component-transition'; const GoogleBackup = ({ handleClick, mnemonic, username, password }) => { const wallets = useWallet(); @@ -33,8 +34,7 @@ const GoogleBackup = ({ handleClick, mnemonic, username, password }) => { } }; return ( - - + <> {chrome.i18n.getMessage('Create')} @@ -94,35 +94,29 @@ const GoogleBackup = ({ handleClick, mnemonic, username, password }) => { - {backupErr && ( - - - {/* */} - - - {chrome.i18n.getMessage( - 'Backup_failed_you_may_still_conduct_backup_inside_extension' - )} - - - - )} + + + {/* */} + + + {chrome.i18n.getMessage( + 'Backup_failed_you_may_still_conduct_backup_inside_extension' + )} + + + - + ); }; diff --git a/src/ui/views/Register/PickUsername.tsx b/src/ui/views/Register/PickUsername.tsx index 3bbf384b..49a460bf 100644 --- a/src/ui/views/Register/PickUsername.tsx +++ b/src/ui/views/Register/PickUsername.tsx @@ -1,16 +1,24 @@ -import React, { useEffect, useState } from 'react'; +import { + CircularProgress, + IconButton, + Button, + Typography, + FormControl, + Input, + InputAdornment, +} from '@mui/material'; import { makeStyles } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, FormControl, Input, InputAdornment, CssBaseline } from '@mui/material'; -import CancelIcon from '../../../components/iconfont/IconClose'; +import { Box } from '@mui/system'; +import React, { useCallback, useEffect, useState } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { useWallet } from 'ui/utils'; + import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; -import theme from '../../style/LLTheme'; +import CancelIcon from '../../../components/iconfont/IconClose'; import EmailIcon from '../../assets/alternate-email.svg'; -import { Presets } from 'react-component-transition'; -import { useWallet } from 'ui/utils'; -import { CircularProgress, IconButton } from '@mui/material'; -const useStyles = makeStyles((theme) => ({ +const useStyles = makeStyles((_theme) => ({ customInputLabel: { '& legend': { visibility: 'visible', @@ -31,71 +39,68 @@ const useStyles = makeStyles((theme) => ({ }, })); +const UsernameError = (errorMsg) => ( + + + + {errorMsg} + {errorMsg.startsWith('This username is reserved') && ( + + hi@lilico.app + {chrome.i18n.getMessage('for__any__inquiry')} + + )} + + +); +const UsernameCorrect = ( + + + + {chrome.i18n.getMessage('Sounds_good')} + + +); +const UsernameLoading = () => ( + + + {chrome.i18n.getMessage('Checking')} + +); + const PickUsername = ({ handleClick, savedUsername, getUsername }) => { const classes = useStyles(); const wallet = useWallet(); const [isLoading, setLoading] = useState(false); const [usernameValid, setUsernameValid] = useState(false); - const usernameError = (errorMsg) => ( - - - - {errorMsg} - {errorMsg.startsWith('This username is reserved') && ( - - hi@lilico.app - {chrome.i18n.getMessage('for__any__inquiry')} - - )} - - - ); - const usernameCorrect = ( - - - - {chrome.i18n.getMessage('Sounds_good')} - - - ); - const usernameLoading = () => ( - - - {chrome.i18n.getMessage('Checking')} - - ); - const [username, setUsername] = useState(savedUsername || ''); const [helperText, setHelperText] = useState(
); - const regex = /^[A-Za-z0-9]{3,15}$/; - - const setErrorMessage = (message: string) => { - setLoading(false); - setUsernameValid(false); - setHelperText(usernameError(message)); - }; + const setErrorMessage = useCallback( + (message: string) => { + setLoading(false); + setUsernameValid(false); + setHelperText(UsernameError(message)); + }, + [setLoading, setUsernameValid, setHelperText] + ); useEffect(() => { setUsernameValid(false); - setHelperText(usernameLoading); + setHelperText(UsernameLoading); setLoading(true); const delayDebounceFn = setTimeout(() => { if (username.length < 3) { @@ -107,6 +112,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { setErrorMessage(chrome.i18n.getMessage('Too__long')); return; } + const regex = /^[A-Za-z0-9]{3,15}$/; if (!regex.test(username)) { setErrorMessage( @@ -118,15 +124,15 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { .checkUsername(username.toLowerCase()) .then((response) => { setLoading(false); - if (response.data.username != username.toLowerCase()) { + if (response.data.username !== username.toLowerCase()) { setLoading(false); return; } if (response.data.unique) { setUsernameValid(true); - setHelperText(usernameCorrect); + setHelperText(UsernameCorrect); } else { - if (response.message == 'Username is reserved') { + if (response.message === 'Username is reserved') { setErrorMessage( chrome.i18n.getMessage('This__username__is__reserved__Please__contact') ); @@ -141,7 +147,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { }, 500); return () => clearTimeout(delayDebounceFn); - }, [username]); + }, [setErrorMessage, username, wallet.openapi]); const msgBgColor = () => { if (isLoading) { @@ -151,8 +157,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { }; return ( - - + <> {chrome.i18n.getMessage('Pick__Your')} @@ -196,20 +201,18 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { } /> - - {username && ( - - {helperText} - - )} - + + + {helperText} + + @@ -233,7 +236,7 @@ const PickUsername = ({ handleClick, savedUsername, getUsername }) => { - + ); }; diff --git a/src/ui/views/Register/RecoveryPhrase.tsx b/src/ui/views/Register/RecoveryPhrase.tsx index 228afeb1..ecf80ad8 100644 --- a/src/ui/views/Register/RecoveryPhrase.tsx +++ b/src/ui/views/Register/RecoveryPhrase.tsx @@ -1,20 +1,20 @@ -import React, { useState } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, IconButton, CssBaseline } from '@mui/material'; -import theme from '../../style/LLTheme'; -import LockRoundedIcon from '@mui/icons-material/LockRounded'; +import InfoIcon from '@mui/icons-material/Info'; import LockOpenRoundedIcon from '@mui/icons-material/LockOpenRounded'; +import LockRoundedIcon from '@mui/icons-material/LockRounded'; +import { Button, Typography, IconButton } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useState } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; + import IconCopy from '../../../components/iconfont/IconCopy'; -import { Presets } from 'react-component-transition'; -import InfoIcon from '@mui/icons-material/Info'; const RecoveryPhrase = ({ handleClick, mnemonic }) => { const [canGoNext, setCanGoNext] = useState(false); const [isCoverBlur, coverBlur] = useState(true); return ( - - + <> {chrome.i18n.getMessage('Recovery')} @@ -189,7 +189,7 @@ const RecoveryPhrase = ({ handleClick, mnemonic }) => { justifyContent: 'flex-end', }} > - + { )} - + - + ); }; diff --git a/src/ui/views/Register/RegisterHeader.tsx b/src/ui/views/Register/RegisterHeader.tsx index 234219ff..fb499d7f 100644 --- a/src/ui/views/Register/RegisterHeader.tsx +++ b/src/ui/views/Register/RegisterHeader.tsx @@ -1,12 +1,11 @@ -import React from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography } from '@mui/material'; -import theme from '../../style/LLTheme'; import HelpOutlineRoundedIcon from '@mui/icons-material/HelpOutlineRounded'; +import { Button, Typography } from '@mui/material'; +import { Box } from '@mui/system'; +import React from 'react'; const RegisterHeader = () => { return ( - + <> { - + ); }; diff --git a/src/ui/views/Register/RegisterPager.tsx b/src/ui/views/Register/RegisterPager.tsx index 9df50f69..42fc829d 100644 --- a/src/ui/views/Register/RegisterPager.tsx +++ b/src/ui/views/Register/RegisterPager.tsx @@ -1,22 +1,23 @@ -import React, { useEffect, useState } from 'react'; -import { useHistory } from 'react-router-dom'; -import { Box, ThemeProvider } from '@mui/system'; import { IconButton, Typography } from '@mui/material'; +import { Box } from '@mui/system'; +import * as bip39 from 'bip39'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useHistory } from 'react-router-dom'; + +import { LLPinAlert } from '@/ui/FRWComponent'; +import Confetti from '@/ui/FRWComponent/Confetti'; +import SlideLeftRight from '@/ui/FRWComponent/SlideLeftRight'; +import { useWallet } from 'ui/utils'; + import BackButtonIcon from '../../../components/iconfont/IconBackButton'; -import theme from '../../style/LLTheme'; -import RegisterHeader from './RegisterHeader'; + +import AllSet from './AllSet'; +import GoogleBackup from './GoogleBackup'; import PickUsername from './PickUsername'; import RecoveryPhrase from './RecoveryPhrase'; +import RegisterHeader from './RegisterHeader'; import RepeatPhrase from './RepeatPhrase'; -import GoogleBackup from './GoogleBackup'; -import AllSet from './AllSet'; import SetPassword from './SetPassword'; -import Particles from 'react-tsparticles'; -import * as bip39 from 'bip39'; -import { ComponentTransition, AnimationTypes } from 'react-component-transition'; -import { LLPinAlert } from '@/ui/FRWComponent'; -import options from '../Import/options'; -import { useWallet } from 'ui/utils'; enum Direction { Right, @@ -30,13 +31,13 @@ const RegisterPager = () => { const [direction, setDirection] = useState(Direction.Right); const [username, setUsername] = useState(''); const [password, setPassword] = useState(null); - const [mnemonic, setMnemonic] = useState(bip39.generateMnemonic()); + const [mnemonic] = useState(bip39.generateMnemonic()); const getUsername = (username: string) => { setUsername(username.toLowerCase()); }; - const loadView = async () => { + const loadView = useCallback(async () => { // console.log(wallet); wallet .getCurrentAccount() @@ -48,7 +49,7 @@ const RegisterPager = () => { .catch(() => { return; }); - }; + }, [wallet, history]); const goNext = () => { setDirection(Direction.Right); @@ -104,14 +105,11 @@ const RegisterPager = () => { }; useEffect(() => { - console.log('wallet'); loadView(); - }, []); - - const height = [480, 600, 640, 620, 480, 480]; + }, [loadView]); return ( - + <> { alignItems: 'center', }} > - {activeIndex == 5 && ( - - )} + {activeIndex === 5 && } - + {/* height why not use auto */} @@ -180,30 +173,14 @@ const RegisterPager = () => { - + {page(activeIndex)} - + - + ); }; diff --git a/src/ui/views/Register/RepeatPhrase.tsx b/src/ui/views/Register/RepeatPhrase.tsx index 90ae7afd..eaf852d7 100644 --- a/src/ui/views/Register/RepeatPhrase.tsx +++ b/src/ui/views/Register/RepeatPhrase.tsx @@ -1,12 +1,13 @@ -import React, { useState, useEffect } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, CssBaseline } from '@mui/material'; -import theme from '../../style/LLTheme'; -import LockRoundedIcon from '@mui/icons-material/LockRounded'; +import InfoIcon from '@mui/icons-material/Info'; import LockOpenRoundedIcon from '@mui/icons-material/LockOpenRounded'; +import LockRoundedIcon from '@mui/icons-material/LockRounded'; +import { Button, Typography } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useState, useEffect, useCallback } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; + import IconCopy from '../../../components/iconfont/IconCopy'; -import { Presets } from 'react-component-transition'; -import InfoIcon from '@mui/icons-material/Info'; const randomElement = (list: any[]) => { return list[Math.floor(Math.random() * list.length)]; @@ -40,9 +41,9 @@ const RepeatPhrase = ({ handleClick, mnemonic }) => { const checkMatch = () => { const correctMatch = chosenIndex.map((index) => mnemonicArray[index]); if ( - selectedPhrase[0] == correctMatch[0] && - selectedPhrase[1] == correctMatch[1] && - selectedPhrase[2] == correctMatch[2] + selectedPhrase[0] === correctMatch[0] && + selectedPhrase[1] === correctMatch[1] && + selectedPhrase[2] === correctMatch[2] ) { handleClick(); return; @@ -56,7 +57,7 @@ const RepeatPhrase = ({ handleClick, mnemonic }) => { }, 5000); }; - const handleRandom = () => { + const handleRandom = useCallback(() => { const arr: number[] = []; // [[0,1,2,3],[4,5,6,7],[8,9,10,11]] const repeatIndex: number[][] = [[], [], []]; @@ -66,7 +67,7 @@ const RepeatPhrase = ({ handleClick, mnemonic }) => { positionList.forEach((list, i) => { const picked = randomElement(list); const exclude = fullIndex - .filter((item) => item != picked) + .filter((item) => item !== picked) .sort(() => { return Math.random() - 0.5; }); @@ -79,14 +80,14 @@ const RepeatPhrase = ({ handleClick, mnemonic }) => { }); setChosen(arr); setRepeat(repeatMap); - }; + }, [mnemonicArray, positionList]); + useEffect(() => { handleRandom(); - }, []); + }, [handleRandom]); return ( - - + <> {chrome.i18n.getMessage('Verify') + ' '} @@ -153,14 +154,14 @@ const RepeatPhrase = ({ handleClick, mnemonic }) => { height: '100%', width: '100%', borderRadius: '8px', - backgroundColor: `${selectedPhrase[i] == v ? '#fff' : 'none'}`, + backgroundColor: `${selectedPhrase[i] === v ? '#fff' : 'none'}`, }} > {v} @@ -185,33 +186,27 @@ const RepeatPhrase = ({ handleClick, mnemonic }) => { justifyContent: 'flex-end', }} > - {incorrect && ( - - - - - {chrome.i18n.getMessage('Incorrect_recovery_phrases_please_try_again')} - - - - )} + + + + + {chrome.i18n.getMessage('Incorrect_recovery_phrases_please_try_again')} + + + - + ); }; diff --git a/src/ui/views/Register/SetPassword.tsx b/src/ui/views/Register/SetPassword.tsx index bf7730c8..1f0da904 100644 --- a/src/ui/views/Register/SetPassword.tsx +++ b/src/ui/views/Register/SetPassword.tsx @@ -11,25 +11,23 @@ import { InputAdornment, FormGroup, LinearProgress, - CssBaseline, } from '@mui/material'; import Checkbox from '@mui/material/Checkbox'; import FormControlLabel from '@mui/material/FormControlLabel'; import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import { Box } from '@mui/system'; import HDWallet from 'ethereum-hdwallet'; import React, { useEffect, useState } from 'react'; -import { Presets } from 'react-component-transition'; import zxcvbn from 'zxcvbn'; import { storage } from '@/background/webapi'; -import type { AccountKey } from 'background/service/networkModel'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { type AccountKey } from 'background/service/networkModel'; import { LLSpinner } from 'ui/FRWComponent'; import { useWallet, saveIndex, mixpanelBrowserService } from 'ui/utils'; import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; import CancelIcon from '../../../components/iconfont/IconClose'; -import theme from '../../style/LLTheme'; const useStyles = makeStyles(() => ({ customInputLabel: { @@ -266,8 +264,7 @@ const SetPassword = ({ handleClick, mnemonic, username, setExPassword }) => { }, [confirmPassword, password]); return ( - - + <> {chrome.i18n.getMessage('Create')} @@ -314,9 +311,9 @@ const SetPassword = ({ handleClick, mnemonic, username, setExPassword }) => { } /> - - {password && helperText} - + + {helperText} + { } /> - - {confirmPassword && helperMatch} - + + {helperMatch} + @@ -404,7 +401,7 @@ const SetPassword = ({ handleClick, mnemonic, username, setExPassword }) => { {errMessage} - + ); }; diff --git a/src/ui/views/Reset/RecoverPassword.tsx b/src/ui/views/Reset/RecoverPassword.tsx index 8a32bd18..4e068406 100644 --- a/src/ui/views/Reset/RecoverPassword.tsx +++ b/src/ui/views/Reset/RecoverPassword.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Button, Typography, @@ -12,20 +11,21 @@ import { InputAdornment, FormGroup, LinearProgress, - CssBaseline, } from '@mui/material'; -import CancelIcon from '../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; -import VisibilityIcon from '@mui/icons-material/Visibility'; -import FormControlLabel from '@mui/material/FormControlLabel'; import Checkbox from '@mui/material/Checkbox'; -import { Presets } from 'react-component-transition'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import { makeStyles, styled } from '@mui/styles'; +import { Box } from '@mui/system'; +import React, { useEffect, useState } from 'react'; import zxcvbn from 'zxcvbn'; -import theme from '../../style/LLTheme'; -import { useWallet, saveIndex } from 'ui/utils'; -import { LLSpinner } from 'ui/FRWComponent'; + import { storage } from '@/background/webapi'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { LLSpinner } from 'ui/FRWComponent'; +import { useWallet, saveIndex } from 'ui/utils'; + +import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../components/iconfont/IconClose'; // const helperTextStyles = makeStyles(() => ({ // root: { @@ -188,7 +188,7 @@ const SetPassword = ({ handleClick, mnemonic, username }) => { ); }; - const [helperText, setHelperText] = useState(
); + const [helperText, setHelperText] = useState(
); const [helperMatch, setHelperMatch] = useState(
); const signIn = async () => { @@ -249,8 +249,7 @@ const SetPassword = ({ handleClick, mnemonic, username }) => { }; return ( - - + <> {chrome.i18n.getMessage('Create')} @@ -298,7 +297,9 @@ const SetPassword = ({ handleClick, mnemonic, username }) => { } /> - {password && helperText} + + {helperText} + { } /> - - {confirmPassword && helperMatch} - + + {helperMatch} + @@ -382,7 +383,7 @@ const SetPassword = ({ handleClick, mnemonic, username }) => { {renderSnackBar()} - + ); }; diff --git a/src/ui/views/Reset/ResetRecoveryPhrase.tsx b/src/ui/views/Reset/ResetRecoveryPhrase.tsx index 3359309b..447efa6c 100644 --- a/src/ui/views/Reset/ResetRecoveryPhrase.tsx +++ b/src/ui/views/Reset/ResetRecoveryPhrase.tsx @@ -1,6 +1,3 @@ -import React, { useEffect, useState } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { makeStyles } from '@mui/styles'; import { Typography, FormControl, @@ -9,16 +6,19 @@ import { Snackbar, Alert, Button, - CssBaseline, } from '@mui/material'; -import theme from '../../style/LLTheme'; -import { Presets } from 'react-component-transition'; -import { useWallet } from 'ui/utils'; -import CancelIcon from '../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; +import { makeStyles } from '@mui/styles'; +import { Box } from '@mui/system'; import * as bip39 from 'bip39'; -import { LLNotFound, LLSpinner } from 'ui/FRWComponent'; +import React, { useCallback, useEffect, useState } from 'react'; + import { storage } from '@/background/webapi'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { LLNotFound, LLSpinner } from 'ui/FRWComponent'; +import { useWallet } from 'ui/utils'; + +import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../components/iconfont/IconClose'; const useStyles = makeStyles((theme) => ({ customInputLabel: { @@ -43,6 +43,43 @@ const useStyles = makeStyles((theme) => ({ }, })); +const MnemonicError = (errorMsg) => ( + + + + {errorMsg} + + +); + +const MnemonicCorrect = ( + + + + {chrome.i18n.getMessage('Recovery__phrase__valid')} + + +); + +const MnemonicLoading = () => ( + + + {chrome.i18n.getMessage('Checking')} + +); + const ResetRecoveryPhrase = ({ handleClick, confirmMnemonic, setUsername }) => { const classes = useStyles(); const wallet = useWallet(); @@ -77,47 +114,6 @@ const ResetRecoveryPhrase = ({ handleClick, confirmMnemonic, setUsername }) => { } }; - const mnemonicError = (errorMsg) => ( - - - - {errorMsg} - - - ); - - const mnemonicCorrect = ( - - - - {chrome.i18n.getMessage('Recovery__phrase__valid')} - - - ); - - const mnemonicLoading = () => ( - - - {chrome.i18n.getMessage('Checking')} - - ); - const renderSnackBar = () => { return ( { ); }; + const setErrorMessage = useCallback( + (message: string) => { + setLoading(false); + setMnemonicValid(false); + setHelperText(MnemonicError(message)); + }, + [setLoading, setMnemonicValid, setHelperText] + ); useEffect(() => { setMnemonicValid(false); - setHelperText(mnemonicLoading); + setHelperText(MnemonicLoading); setLoading(true); const delayDebounceFn = setTimeout(() => { setLoading(false); const length = mnemonic.trim().split(/\s+/g).length; - if (!(length == 12 || length == 24)) { + if (!(length === 12 || length === 24)) { setErrorMessage( chrome.i18n.getMessage('Recovery_phrases_word_count_must_be_12_or_24_words') ); @@ -160,19 +164,13 @@ const ResetRecoveryPhrase = ({ handleClick, confirmMnemonic, setUsername }) => { } setMnemonicValid(true); - setHelperText(mnemonicCorrect); + setHelperText(MnemonicCorrect); storage.set('premnemonic', formatted); setMnemonic(formatted); }, 500); return () => clearTimeout(delayDebounceFn); - }, [mnemonic]); - - const setErrorMessage = (message: string) => { - setLoading(false); - setMnemonicValid(false); - setHelperText(mnemonicError(message)); - }; + }, [mnemonic, setErrorMessage]); const msgBgColor = () => { if (isLoading) { @@ -182,8 +180,7 @@ const ResetRecoveryPhrase = ({ handleClick, confirmMnemonic, setUsername }) => { }; return ( - - + <> {!showDialog ? ( @@ -216,20 +213,18 @@ const ResetRecoveryPhrase = ({ handleClick, confirmMnemonic, setUsername }) => { setMnemonic(event.target.value); }} /> - - {mnemonic && ( - - {helperText} - - )} - + + + {helperText} + + @@ -262,7 +257,7 @@ const ResetRecoveryPhrase = ({ handleClick, confirmMnemonic, setUsername }) => { ) : ( )} - + ); }; diff --git a/src/ui/views/Reset/index.tsx b/src/ui/views/Reset/index.tsx index 99430ed8..5872e984 100644 --- a/src/ui/views/Reset/index.tsx +++ b/src/ui/views/Reset/index.tsx @@ -1,19 +1,21 @@ +import { IconButton, Typography, Button, Snackbar, Alert } from '@mui/material'; +import { Box } from '@mui/system'; import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; -import { Box, ThemeProvider } from '@mui/system'; -import { IconButton, Typography, Button, Snackbar, Alert } from '@mui/material'; + +import Confetti from '@/ui/FRWComponent/Confetti'; +import SlideLeftRight from '@/ui/FRWComponent/SlideLeftRight'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { LLSpinner } from 'ui/FRWComponent'; +import { useWallet } from 'ui/utils'; + import BackButtonIcon from '../../../components/iconfont/IconBackButton'; -import theme from '../../style/LLTheme'; -import RegisterHeader from '../Register/RegisterHeader'; -import ResetRecoveryPhrase from './ResetRecoveryPhrase'; +import IconGoogleDrive from '../../../components/iconfont/IconGoogleDrive'; import AllSet from '../Register/AllSet'; +import RegisterHeader from '../Register/RegisterHeader'; + import RecoverPassword from './RecoverPassword'; -import Particles from 'react-tsparticles'; -import { LLSpinner } from 'ui/FRWComponent'; -import IconGoogleDrive from '../../../components/iconfont/IconGoogleDrive'; -import { useWallet } from 'ui/utils'; -import { ComponentTransition, AnimationTypes } from 'react-component-transition'; -import options from '../Import/options'; +import ResetRecoveryPhrase from './ResetRecoveryPhrase'; enum Direction { Right, @@ -92,7 +94,7 @@ const Reset = () => { }; return ( - + <> { alignItems: 'center', }} > - {activeIndex == 2 && ( - - )} + {activeIndex === 2 && } + { - + {page(activeIndex)} - + {activeIndex === 0 && ( @@ -212,7 +194,7 @@ const Reset = () => { - + ); }; diff --git a/src/ui/views/RetrievePK/index.tsx b/src/ui/views/RetrievePK/index.tsx index a45dedd1..49b90d24 100644 --- a/src/ui/views/RetrievePK/index.tsx +++ b/src/ui/views/RetrievePK/index.tsx @@ -1,12 +1,13 @@ -import React, { useEffect, useRef, useState } from 'react'; // import { useTranslation } from 'react-i18next'; -import { useWallet, useApproval, useWalletRequest } from 'ui/utils'; -import { Typography, Box, FormControl, List, ListItem, ListItemText } from '@mui/material'; +import { Input, Typography, Box, FormControl, List, ListItem, ListItemText } from '@mui/material'; +import { makeStyles } from '@mui/styles'; +import React, { useEffect, useRef, useState } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; import { LLPrimaryButton, CredentialBox, LLSecondaryButton } from 'ui/FRWComponent'; -import { Input } from '@mui/material'; -import { Presets } from 'react-component-transition'; +import { useWallet, useApproval, useWalletRequest } from 'ui/utils'; + import CancelIcon from '../../../components/iconfont/IconClose'; -import { makeStyles } from '@mui/styles'; const useStyles = makeStyles(() => ({ customInputLabel: { @@ -155,20 +156,18 @@ const RetrievePK = () => { onKeyDown={handleKeyDown} /> - - {showError && ( - - {usernameError()} - - )} - + + + {usernameError()} + + )} diff --git a/src/ui/views/Send/AccountsList.tsx b/src/ui/views/Send/AccountsList.tsx index b32f2290..622674e5 100644 --- a/src/ui/views/Send/AccountsList.tsx +++ b/src/ui/views/Send/AccountsList.tsx @@ -1,11 +1,12 @@ -import React, { useEffect, useState } from 'react'; -import { List, ListSubheader, CardMedia, Typography, ButtonBase, Box } from '@mui/material'; +import { List, ListSubheader, ButtonBase, Box } from '@mui/material'; import { groupBy, isEmpty } from 'lodash'; -import { LLContactCard, LLContactEth, FWContactCard } from '../../FRWComponent'; +import React, { useEffect, useState, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; -import { useWallet } from 'ui/utils'; + import { withPrefix, isValidEthereumAddress } from '@/ui/utils/address'; -import EmptyAddress from 'ui/assets/EmptyAddress.svg'; +import { useWallet } from 'ui/utils'; + +import { LLContactCard, LLContactEth, FWContactCard } from '../../FRWComponent'; type ChildAccount = { [key: string]: { @@ -20,13 +21,13 @@ type ChildAccount = { const AccountsList = ({ filteredContacts, isLoading, handleClick, isSend = true }) => { const usewallet = useWallet(); - const [grouped, setGrouped] = useState([]); + const [, setGrouped] = useState([]); const [childAccounts, setChildAccount] = useState([]); const [walletList, setWalletList] = useState([]); const [evmAddress, setEvmAddress] = useState([]); - const getWallet = async () => { + const getWallet = useCallback(async () => { const wallet = await usewallet.getUserWallets(); const fData = wallet.filter((item) => item.blockchain !== null); const currentNetwork = await usewallet.getNetwork(); @@ -35,8 +36,8 @@ const AccountsList = ({ filteredContacts, isLoading, handleClick, isSend = true if (!Array.isArray(sortData)) { sortData = []; } - const filteredData = (sortData || []).filter((wallet, index) => { - return wallet.chain_id == currentNetwork; + const filteredData = (sortData || []).filter((wallet, _index) => { + return wallet.chain_id === currentNetwork; }); const walletData = (filteredData || []).map((wallet, index) => { return { @@ -52,7 +53,6 @@ const AccountsList = ({ filteredContacts, isLoading, handleClick, isSend = true const cAccountArray = convertObjectToContactArray(childresp); setChildAccount(cAccountArray); } - console.log('childresp ', wdArray); // putDeviceInfo(fData); await setWalletList(wdArray); @@ -69,7 +69,7 @@ const AccountsList = ({ filteredContacts, isLoading, handleClick, isSend = true setEvmAddress([evmData]); } } - }; + }, [usewallet]); function convertObjectToContactArray(data) { return Object.keys(data).map((address, index) => ({ @@ -89,7 +89,7 @@ const AccountsList = ({ filteredContacts, isLoading, handleClick, isSend = true async function convertArrayToContactArray(array, emojiList) { // Fetch emoji list - return array.map((item, index) => { + return array.map((item, _index) => { return { id: item.id, contact_name: emojiList[0].name, // Use the corresponding emoji name @@ -119,7 +119,7 @@ const AccountsList = ({ filteredContacts, isLoading, handleClick, isSend = true const group = groupBy(filteredContacts, (contact) => contact.contact_name[0]); setGrouped(group); getWallet(); - }, [filteredContacts]); + }, [filteredContacts, getWallet]); const history = useHistory(); diff --git a/src/ui/views/Send/AddressBookList.tsx b/src/ui/views/Send/AddressBookList.tsx index 49c74260..dd18ad3f 100644 --- a/src/ui/views/Send/AddressBookList.tsx +++ b/src/ui/views/Send/AddressBookList.tsx @@ -1,9 +1,11 @@ -import React, { useEffect, useState } from 'react'; import { List, ListSubheader, CardMedia, Typography, ButtonBase, Box } from '@mui/material'; import { groupBy, isEmpty } from 'lodash'; -import { LLContactCard } from '../../FRWComponent'; +import React, { useEffect, useState } from 'react'; import { useHistory } from 'react-router-dom'; -import EmptyAddress from 'ui/assets/EmptyAddress.svg'; + +import emptyAddress from 'ui/assets/emptyAddress.svg'; + +import { LLContactCard } from '../../FRWComponent'; const AddressBookList = ({ filteredContacts, isLoading, handleClick }) => { const [grouped, setGrouped] = useState([]); @@ -62,7 +64,7 @@ const AddressBookList = ({ filteredContacts, isLoading, handleClick }) => { > { return ( @@ -39,7 +40,7 @@ const RecentList = ({ filteredContacts, isLoading, handleClick }) => { > { const [grouped, setGrouped] = useState([]); @@ -15,9 +16,7 @@ const SearchList = ({ searchContacts, isLoading, handleClick }) => { const group = groupBy(filterContacts, (contact) => contact.group); setGrouped(group); - }, []); - - const history = useHistory(); + }, [searchContacts]); return ( @@ -65,7 +64,7 @@ const SearchList = ({ searchContacts, isLoading, handleClick }) => { > { - const userContact = { - address: '', - id: 0, - contact_name: '', - avatar: '', - domain: { - domain_type: 999, - value: '', - }, - } as unknown as Contact; - - const empty: CoinItem = { - coin: '', - unit: '', - balance: 0, - price: 0, - change24h: 0, - total: 0, - icon: '', - }; +const EMPTY_COIN: CoinItem = { + coin: '', + unit: '', + balance: 0, + price: 0, + change24h: 0, + total: 0, + icon: '', +}; +const SendAmount = () => { const history = useHistory(); const location = useLocation(); const usewallet = useWallet(); @@ -52,14 +52,29 @@ const SendAmount = () => { const [amount, setAmount] = useState(undefined); const [secondAmount, setSecondAmount] = useState('0.0'); const [validated, setValidated] = useState(null); - const [userInfo, setUser] = useState(userContact); + const [userInfo, setUser] = useState(USER_CONTACT); const [network, setNetwork] = useState('mainnet'); - const [coinInfo, setCoinInfo] = useState(empty); + const [coinInfo, setCoinInfo] = useState(EMPTY_COIN); const [isLoading, setLoading] = useState(false); const [childType, setChildType] = useState(''); const [minAmount, setMinAmount] = useState(0); - const setUserWallet = async () => { + const setUserMinAmount = useCallback( + async (address: string) => { + try { + // Try fetching the min amount from the API + const minAmount = await usewallet.openapi.getAccountMinFlow(address); + setMinAmount(minAmount); + } catch (error) { + // If there's an error, set the min amount to 0.001 + console.error('Error fetching min amount:', error); + setMinAmount(0.001); + } + }, + [usewallet] + ); + + const setUserWallet = useCallback(async () => { // const walletList = await storage.get('userWallet'); setLoading(true); const token = await usewallet.getCurrentCoin(); @@ -84,6 +99,8 @@ const SendAmount = () => { const info = await usewallet.getUserInfo(false); const isChild = await usewallet.getActiveWallet(); console.log('isChild ', info, isChild); + const userContact = { ...USER_CONTACT }; + if (isChild) { if (isChild !== 'evm') { const childResp = await usewallet.checkUserChildAccount(); @@ -101,23 +118,11 @@ const SendAmount = () => { userContact.avatar = info.avatar; userContact.contact_name = info.username; } - setUserMinAmount(); + setUserMinAmount(userContact.address); setUser(userContact); - }; + }, [childType, setWallet, setCoinList, setCoinInfo, setUser, setUserMinAmount, usewallet]); - const setUserMinAmount = async () => { - try { - // Try fetching the min amount from the API - const minAmount = await usewallet.openapi.getAccountMinFlow(userContact.address); - setMinAmount(minAmount); - } catch (error) { - // If there's an error, set the min amount to 0.001 - console.error('Error fetching min amount:', error); - setMinAmount(0.001); - } - }; - - const checkAddress = async () => { + const checkAddress = useCallback(async () => { const child = await usewallet.getActiveWallet(); setChildType(child); @@ -131,34 +136,34 @@ const SendAmount = () => { setValidated(false); } setLoading(false); - }; + }, [setLoading, setValidated, location?.state?.contact?.address, usewallet]); const numberWithCommas = (x) => { return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); }; - const updateCoinInfo = () => { + const updateCoinInfo = useCallback(() => { const coin = coinList.find((coin) => coin.unit.toLowerCase() === currentCoin.toLowerCase()); if (coin) { setCoinInfo(coin); } - }; + }, [coinList, currentCoin, setCoinInfo]); useEffect(() => { checkAddress(); - }, []); + }, [checkAddress]); useEffect(() => { setUserWallet(); - }, [childType]); + }, [childType, setUserWallet]); useEffect(() => { updateCoinInfo(); - }, [currentCoin]); + }, [currentCoin, updateCoinInfo]); return (
- + <> @@ -170,11 +175,11 @@ const SendAmount = () => { isSend={true} /> - - {validated !== null && - (validated ? ( - <> - ) : ( + {validated !== null && + (validated ? ( + <> + ) : ( + { - ))} - + + ))} { /> )} - +
); }; diff --git a/src/ui/views/Send/SendEth/EvmConfirmation.tsx b/src/ui/views/Send/SendEth/EvmConfirmation.tsx index 3b83a433..a70a1e5f 100644 --- a/src/ui/views/Send/SendEth/EvmConfirmation.tsx +++ b/src/ui/views/Send/SendEth/EvmConfirmation.tsx @@ -3,9 +3,9 @@ import InfoIcon from '@mui/icons-material/Info'; import { Box, Typography, Drawer, Stack, Grid, CardMedia, IconButton, Button } from '@mui/material'; import BN from 'bignumber.js'; import React, { useState, useEffect, useCallback } from 'react'; -import { Presets } from 'react-component-transition'; import { useHistory } from 'react-router-dom'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; import StorageExceededAlert from '@/ui/FRWComponent/StorageExceededAlert'; import { WarningStorageLowSnackbar } from '@/ui/FRWComponent/WarningStorageLowSnackbar'; import { useStorageCheck } from '@/ui/utils/useStorageCheck'; @@ -286,28 +286,26 @@ const ToEthConfirmation = (props: ToEthConfirmationProps) => {
- {occupied && ( - - - {/* */} - - - {chrome.i18n.getMessage('Your_address_is_currently_processing_another_transaction')} - - - - )} + + + {/* */} + + + {chrome.i18n.getMessage('Your_address_is_currently_processing_another_transaction')} + + + { size="large" sx={{ height: '50px', + width: '100%', borderRadius: '12px', textTransform: 'capitalize', display: 'flex', diff --git a/src/ui/views/Send/SendEth/ToEthConfirmation.tsx b/src/ui/views/Send/SendEth/ToEthConfirmation.tsx index 37708d5c..d29cd64b 100644 --- a/src/ui/views/Send/SendEth/ToEthConfirmation.tsx +++ b/src/ui/views/Send/SendEth/ToEthConfirmation.tsx @@ -2,10 +2,11 @@ import CloseIcon from '@mui/icons-material/Close'; import InfoIcon from '@mui/icons-material/Info'; import { Box, Typography, Drawer, Stack, Grid, CardMedia, IconButton, Button } from '@mui/material'; import React, { useState, useEffect, useCallback } from 'react'; -import { Presets } from 'react-component-transition'; import { useHistory } from 'react-router-dom'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; import StorageExceededAlert from '@/ui/FRWComponent/StorageExceededAlert'; +import { WarningStorageLowSnackbar } from '@/ui/FRWComponent/WarningStorageLowSnackbar'; import { useStorageCheck } from '@/ui/utils/useStorageCheck'; import IconNext from 'ui/FRWAssets/svg/next.svg'; import { LLSpinner, LLProfile, FRWProfile } from 'ui/FRWComponent'; @@ -30,9 +31,13 @@ const ToEthConfirmation = (props: ToEthConfirmationProps) => { const [occupied, setOccupied] = useState(false); const [tid, setTid] = useState(''); const [count, setCount] = useState(0); - const { sufficient: isSufficient } = useStorageCheck(); + const { sufficient: isSufficient, sufficientAfterAction } = useStorageCheck({ + transferAmount: 0, + movingBetweenEVMAndFlow: true, + }); const isLowStorage = isSufficient !== undefined && !isSufficient; // isSufficient is undefined when the storage check is not yet completed + const isLowStorageAfterAction = sufficientAfterAction !== undefined && !sufficientAfterAction; const colorArray = [ '#32E35529', @@ -296,29 +301,30 @@ const ToEthConfirmation = (props: ToEthConfirmationProps) => { - {occupied && ( - - - {/* */} - - - {chrome.i18n.getMessage('Your_address_is_currently_processing_another_transaction')} - - - - )} - + + + {/* */} + + + {chrome.i18n.getMessage('Your_address_is_currently_processing_another_transaction')} + + + +
- + ); }; diff --git a/src/ui/views/Staking/StakingPage.tsx b/src/ui/views/Staking/StakingPage.tsx index 885433c0..e7a55589 100644 --- a/src/ui/views/Staking/StakingPage.tsx +++ b/src/ui/views/Staking/StakingPage.tsx @@ -1,23 +1,41 @@ -import React, { useState, useEffect } from 'react'; -import { Box, Button, Typography, CardMedia } from '@mui/material'; -import { useHistory, useParams, useLocation } from 'react-router-dom'; -import ArrowBackIcon from '@mui/icons-material/ArrowBack'; -import { CoinItem } from 'background/service/coinList'; -import theme from '../../style/LLTheme'; -import { ThemeProvider } from '@mui/material/styles'; -import StakeAmount from './components/StakeAmount'; -import { useWallet } from 'ui/utils'; -import { withPrefix } from 'ui/utils/address'; -import IconSwitch from '../../../components/iconfont/IconSwitch'; -import { LLSpinner } from 'ui/FRWComponent'; -import { Contact } from 'background/service/networkModel'; -import { Presets } from 'react-component-transition'; -import CancelIcon from '../../../components/iconfont/IconClose'; +import { Box, Button, Typography } from '@mui/material'; +import React, { useState, useEffect, useCallback } from 'react'; +import { useParams } from 'react-router-dom'; + import { LLHeader } from '@/ui/FRWComponent'; -import { TokenListProvider } from 'flow-native-token-registry'; +import { type CoinItem } from 'background/service/coinList'; +import { useWallet } from 'ui/utils'; + +import StakeAmount from './components/StakeAmount'; import StakeConfirm from './components/StakeConfirm'; -import Increment from '../../FRWAssets/svg/increment.svg'; +const FLOW_TOKEN = { + name: 'Flow', + address: { + mainnet: '0x1654653399040a61', + testnet: '0x7e60df042a9c0868', + crescendo: '0x7e60df042a9c0868', + }, + contract_name: 'FlowToken', + storage_path: { + balance: '/public/flowTokenBalance', + vault: '/storage/flowTokenVault', + receiver: '/public/flowTokenReceiver', + }, + decimal: 8, + icon: 'https://raw.githubusercontent.com/Outblock/Assets/main/ft/flow/logo.png', + symbol: 'flow', + website: 'https://www.onflow.org', +}; +const EMPTY_COIN: CoinItem = { + coin: '', + unit: '', + balance: 0, + price: 0, + change24h: 0, + total: 0, + icon: '', +}; const StakingPage = () => { enum ENV { @@ -36,34 +54,6 @@ const StakingPage = () => { // CDN = 'CDN' // } - const flowToken = { - name: 'Flow', - address: { - mainnet: '0x1654653399040a61', - testnet: '0x7e60df042a9c0868', - crescendo: '0x7e60df042a9c0868', - }, - contract_name: 'FlowToken', - storage_path: { - balance: '/public/flowTokenBalance', - vault: '/storage/flowTokenVault', - receiver: '/public/flowTokenReceiver', - }, - decimal: 8, - icon: 'https://raw.githubusercontent.com/Outblock/Assets/main/ft/flow/logo.png', - symbol: 'flow', - website: 'https://www.onflow.org', - }; - const empty: CoinItem = { - coin: '', - unit: '', - balance: 0, - price: 0, - change24h: 0, - total: 0, - icon: '', - }; - const usewallet = useWallet(); const location = useParams(); const [userWallet, setWallet] = useState(null); @@ -73,7 +63,7 @@ const StakingPage = () => { const [amount, setAmount] = useState('0'); const [outAmount, setOutAmount] = useState(0); const [network, setNetwork] = useState('mainnet'); - const [coinInfo, setCoinInfo] = useState(empty); + const [coinInfo, setCoinInfo] = useState(EMPTY_COIN); const [nodeid, setNodeid] = useState(null); const [delegateid, setDelegate] = useState(null); const [token1, setToken1] = useState(null); @@ -87,7 +77,7 @@ const StakingPage = () => { const inputAmount = (coinInfo.balance * value).toString(); setAmount(inputAmount); }; - const setUserWallet = async () => { + const setUserWallet = useCallback(async () => { const nodeid = location['nodeid']; const delegateid = location['delegateid']; setNodeid(nodeid); @@ -108,22 +98,22 @@ const StakingPage = () => { setCoinInfo(coinInfo!); setLoading(false); return; - }; + }, [location, usewallet]); - const getApy = async () => { + const getApy = useCallback(async () => { const result = await usewallet.getApr(); setApr(result); - }; + }, [usewallet]); useEffect(() => { getApy(); setUserWallet(); - setToken0(flowToken); - }, []); + setToken0(FLOW_TOKEN); + }, [getApy, setUserWallet]); return (
- + <> @@ -366,7 +356,7 @@ const StakingPage = () => { }} /> - +
); }; diff --git a/src/ui/views/Staking/TransferList.tsx b/src/ui/views/Staking/TransferList.tsx index 3636969d..4e1356e8 100644 --- a/src/ui/views/Staking/TransferList.tsx +++ b/src/ui/views/Staking/TransferList.tsx @@ -1,8 +1,7 @@ -import React, { useEffect, useState } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { useWallet } from 'ui/utils'; -import { formatString } from 'ui/utils/address'; -import theme from '../../style/LLTheme'; +import CallMadeRoundedIcon from '@mui/icons-material/CallMadeRounded'; +import CallReceivedRoundedIcon from '@mui/icons-material/CallReceivedRounded'; +import ChevronRightRoundedIcon from '@mui/icons-material/ChevronRightRounded'; +import ExpandLessRoundedIcon from '@mui/icons-material/ExpandLessRounded'; import { Typography, ListItem, @@ -14,14 +13,17 @@ import { CardMedia, Button, } from '@mui/material'; -import activity from 'ui/FRWAssets/svg/activity.svg'; +import { Box } from '@mui/system'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; +import React, { useCallback, useEffect, useState } from 'react'; + +import activity from 'ui/FRWAssets/svg/activity.svg'; +import { useWallet } from 'ui/utils'; +import { formatString } from 'ui/utils/address'; + import IconExec from '../../../components/iconfont/IconExec'; -import ChevronRightRoundedIcon from '@mui/icons-material/ChevronRightRounded'; -import ExpandLessRoundedIcon from '@mui/icons-material/ExpandLessRounded'; -import CallMadeRoundedIcon from '@mui/icons-material/CallMadeRounded'; -import CallReceivedRoundedIcon from '@mui/icons-material/CallReceivedRounded'; + dayjs.extend(relativeTime); const TransferList = ({ setCount }) => { @@ -34,7 +36,7 @@ const TransferList = ({ setCount }) => { const [address, setAddress] = useState('0x'); const [showButton, setShowButton] = useState(false); - const fetchTransaction = async () => { + const fetchTransaction = useCallback(async () => { setLoading(true); const monitor = await wallet.getMonitor(); setMonitor(monitor); @@ -55,14 +57,17 @@ const TransferList = ({ setCount }) => { } catch (e) { setLoading(false); } - }; + }, [wallet, setCount]); - const extMessageHandler = (req) => { - if (req.msg === 'transferListReceived') { - fetchTransaction(); - } - return true; - }; + const extMessageHandler = useCallback( + (req) => { + if (req.msg === 'transferListReceived') { + fetchTransaction(); + } + return true; + }, + [fetchTransaction] + ); useEffect(() => { fetchTransaction(); @@ -71,7 +76,7 @@ const TransferList = ({ setCount }) => { return () => { chrome.runtime.onMessage.removeListener(extMessageHandler); }; - }, []); + }, [extMessageHandler, fetchTransaction]); const timeConverter = (timeStamp: number) => { let time = dayjs.unix(timeStamp); @@ -98,7 +103,7 @@ const TransferList = ({ setCount }) => { color: isReceive && isFT ? 'success.main' : 'text.primary', }} > - {props.type == 1 + {props.type === 1 ? (isReceive ? '+' : '-') + `${props.amount}` : `${props.token.split('.')[2]}`}
@@ -143,7 +148,7 @@ const TransferList = ({ setCount }) => { variant="body1" sx={{ fontSize: 14, fontWeight: '500', textAlign: 'start' }} > - {props.type == 1 ? `${props.token}` : `${props.token.split('.')[2]}`} + {props.type === 1 ? `${props.token}` : `${props.token.split('.')[2]}`} ) : ( @@ -182,7 +187,7 @@ const TransferList = ({ setCount }) => { }; return ( - + <> {!isLoading ? ( {transaction.length ? ( @@ -208,10 +213,10 @@ const TransferList = ({ setCount }) => { sx={{ paddingRight: '0px' }} dense={true} onClick={() => { - { - monitor === 'flowscan' - ? window.open(`${flowscanURL}/tx/${tx.hash}`) - : window.open(`${viewSource}/${tx.hash}`); + if (monitor === 'flowscan') { + window.open(`${flowscanURL}/tx/${tx.hash}`); + } else { + window.open(`${viewSource}/${tx.hash}`); } }} > @@ -300,7 +305,7 @@ const TransferList = ({ setCount }) => { ); }) )} - + ); }; diff --git a/src/ui/views/Staking/UnstakePage.tsx b/src/ui/views/Staking/UnstakePage.tsx index 5743e923..16bc12f9 100644 --- a/src/ui/views/Staking/UnstakePage.tsx +++ b/src/ui/views/Staking/UnstakePage.tsx @@ -1,21 +1,42 @@ -import React, { useState, useEffect } from 'react'; -import { Box, Button, Typography, CardMedia } from '@mui/material'; -import { useHistory, useParams, useLocation } from 'react-router-dom'; -import ArrowBackIcon from '@mui/icons-material/ArrowBack'; -import { CoinItem } from 'background/service/coinList'; -import theme from '../../style/LLTheme'; -import { ThemeProvider } from '@mui/material/styles'; -import UnstakeAmount from './components/UnstakeAmount'; -import { useWallet } from 'ui/utils'; -import { withPrefix } from 'ui/utils/address'; -import IconSwitch from '../../../components/iconfont/IconSwitch'; -import { LLSpinner } from 'ui/FRWComponent'; -import { Contact } from 'background/service/networkModel'; -import { Presets } from 'react-component-transition'; -import CancelIcon from '../../../components/iconfont/IconClose'; +import { Box, Button, Typography } from '@mui/material'; +import React, { useState, useEffect, useCallback } from 'react'; +import { useParams } from 'react-router-dom'; + import { LLHeader } from '@/ui/FRWComponent'; +import { type CoinItem } from 'background/service/coinList'; +import { useWallet } from 'ui/utils'; + +import UnstakeAmount from './components/UnstakeAmount'; import UnstakeConfirm from './components/UnstakeConfirm'; +const FLOW_TOKEN = { + name: 'Flow', + address: { + mainnet: '0x1654653399040a61', + testnet: '0x7e60df042a9c0868', + crescendo: '0x7e60df042a9c0868', + }, + contract_name: 'FlowToken', + storage_path: { + balance: '/public/flowTokenBalance', + vault: '/storage/flowTokenVault', + receiver: '/public/flowTokenReceiver', + }, + decimal: 8, + icon: 'https://raw.githubusercontent.com/Outblock/Assets/main/ft/flow/logo.png', + symbol: 'flow', + website: 'https://www.onflow.org', +}; +const EMPTY_COIN: CoinItem = { + coin: '', + unit: '', + balance: 0, + price: 0, + change24h: 0, + total: 0, + icon: '', +}; + const UnstakePage = () => { enum ENV { Mainnet = 'mainnet', @@ -32,34 +53,6 @@ const UnstakePage = () => { // CDN = 'CDN' // } - const flowToken = { - name: 'Flow', - address: { - mainnet: '0x1654653399040a61', - testnet: '0x7e60df042a9c0868', - crescendo: '0x7e60df042a9c0868', - }, - contract_name: 'FlowToken', - storage_path: { - balance: '/public/flowTokenBalance', - vault: '/storage/flowTokenVault', - receiver: '/public/flowTokenReceiver', - }, - decimal: 8, - icon: 'https://raw.githubusercontent.com/Outblock/Assets/main/ft/flow/logo.png', - symbol: 'flow', - website: 'https://www.onflow.org', - }; - const empty: CoinItem = { - coin: '', - unit: '', - balance: 0, - price: 0, - change24h: 0, - total: 0, - icon: '', - }; - const usewallet = useWallet(); const location = useParams(); const [userWallet, setWallet] = useState(null); @@ -68,7 +61,7 @@ const UnstakePage = () => { const [exceed, setExceed] = useState(false); const [amount, setAmount] = useState('0'); const [network, setNetwork] = useState('mainnet'); - const [coinInfo, setCoinInfo] = useState(empty); + const [coinInfo, setCoinInfo] = useState(EMPTY_COIN); const [nodeid, setNodeid] = useState(null); const [delegateid, setDelegate] = useState(null); const [token0, setToken0] = useState(null); @@ -83,7 +76,7 @@ const UnstakePage = () => { const inputAmount = (nodeInfo.tokensStaked * value).toString(); setAmount(inputAmount); }; - const setUserWallet = async () => { + const setUserWallet = useCallback(async () => { const nodeid = location['nodeid']; const delegateid = location['delegateid']; setNodeid(nodeid); @@ -114,22 +107,22 @@ const UnstakePage = () => { setCoinInfo(coinInfo!); setLoading(false); return; - }; + }, [usewallet, location]); - const getApy = async () => { + const getApy = useCallback(async () => { const result = await usewallet.getApr(); setApr(result); - }; + }, [usewallet]); useEffect(() => { getApy(); setUserWallet(); - setToken0(flowToken); - }, []); + setToken0(FLOW_TOKEN); + }, [getApy, setUserWallet]); return (
- + <> @@ -371,7 +364,7 @@ const UnstakePage = () => { }} /> - +
); }; diff --git a/src/ui/views/Staking/components/StakeAmount.tsx b/src/ui/views/Staking/components/StakeAmount.tsx index 5c384580..60a0752c 100644 --- a/src/ui/views/Staking/components/StakeAmount.tsx +++ b/src/ui/views/Staking/components/StakeAmount.tsx @@ -1,27 +1,7 @@ -import React, { useState, useEffect } from 'react'; -import { - Box, - Typography, - Button, - ListItemText, - Select, - MenuItem, - ListItemIcon, - FormControl, - InputAdornment, - Input, - Chip, - Tooltip, - Stack, - Avatar, -} from '@mui/material'; -import { makeStyles } from '@mui/styles'; -import StakeConfirm from './StakeConfirm'; -import AttachMoneyRoundedIcon from '@mui/icons-material/AttachMoneyRounded'; +import { Box, Typography, FormControl, InputAdornment, Input, Stack } from '@mui/material'; import { StyledEngineProvider } from '@mui/material/styles'; -import BN from 'bignumber.js'; -import { Presets } from 'react-component-transition'; -import SelectIcon from '@mui/icons-material/ArrowDropDown'; +import { makeStyles } from '@mui/styles'; +import React, { useState, useEffect, useCallback } from 'react'; const useStyles = makeStyles(() => ({ customInputLabel: { @@ -129,15 +109,15 @@ const StakeAmount = ({ removeError(); } } - }, [amount]); + }, [amount, coinInfo, removeError, setError, setLess]); - const currentCoinType = () => { + const currentCoinType = useCallback(() => { setCoin(coinInfo.unit); - }; + }, [coinInfo.unit]); useEffect(() => { currentCoinType(); - }, []); + }, [currentCoinType]); return ( diff --git a/src/ui/views/Staking/components/UnstakeAmount.tsx b/src/ui/views/Staking/components/UnstakeAmount.tsx index e7ebd3ca..eece2d3b 100644 --- a/src/ui/views/Staking/components/UnstakeAmount.tsx +++ b/src/ui/views/Staking/components/UnstakeAmount.tsx @@ -1,27 +1,7 @@ -import React, { useState, useEffect } from 'react'; -import { - Box, - Typography, - Button, - ListItemText, - Select, - MenuItem, - ListItemIcon, - FormControl, - InputAdornment, - Input, - Chip, - Tooltip, - Stack, - Avatar, -} from '@mui/material'; -import { makeStyles } from '@mui/styles'; -import StakeConfirm from './StakeConfirm'; -import AttachMoneyRoundedIcon from '@mui/icons-material/AttachMoneyRounded'; +import { Box, Typography, FormControl, InputAdornment, Input, Stack } from '@mui/material'; import { StyledEngineProvider } from '@mui/material/styles'; -import BN from 'bignumber.js'; -import { Presets } from 'react-component-transition'; -import SelectIcon from '@mui/icons-material/ArrowDropDown'; +import { makeStyles } from '@mui/styles'; +import React, { useState, useEffect, useCallback } from 'react'; const useStyles = makeStyles(() => ({ customInputLabel: { @@ -128,15 +108,15 @@ const UnstakeAmount = ({ removeError(); } } - }, [amount]); + }, [amount, coinInfo, nodeInfo.tokensStaked, removeError, setError]); - const currentCoinType = () => { + const currentCoinType = useCallback(() => { setCoin(coinInfo.unit); - }; + }, [coinInfo.unit]); useEffect(() => { currentCoinType(); - }, []); + }, [currentCoinType]); return ( diff --git a/src/ui/views/Staking/index.tsx b/src/ui/views/Staking/index.tsx index eb2c7751..e3e97960 100644 --- a/src/ui/views/Staking/index.tsx +++ b/src/ui/views/Staking/index.tsx @@ -1,11 +1,12 @@ -import React, { useState, useEffect } from 'react'; -import { useHistory } from 'react-router-dom'; -import { Typography, Box, CardMedia, Snackbar, Alert, SnackbarContent, Slide } from '@mui/material'; +import { Box, Snackbar, Alert } from '@mui/material'; +import React, { useState, useEffect, useCallback } from 'react'; + import { notification } from 'background/webapi'; // import '../../Unlock/style.css'; import { useWallet } from 'ui/utils'; -import NoStake from './NoStake'; + import HaveStake from './HaveStake'; +import NoStake from './NoStake'; const Staking = () => { const wallet = useWallet(); @@ -34,7 +35,7 @@ const Staking = () => { // const result = await wallet.createStake('true'); }; - const loadNetwork = async () => { + const loadNetwork = useCallback(async () => { const result = await wallet.getNetwork(); setNetwork(result); @@ -45,7 +46,7 @@ const Staking = () => { const storageData = await wallet.getCoinList(); const flowObject = storageData.find((coin) => coin.unit.toLowerCase() === 'flow'); setAmount(flowObject!.balance); - }; + }, [wallet]); useEffect(() => { console.log('Updated amount: ', amount); @@ -58,7 +59,7 @@ const Staking = () => { setLoading(false); }; - const getNodeInfo = async () => { + const getNodeInfo = useCallback(async () => { const address = await wallet.getCurrentAddress(); wallet .delegateInfo(address) @@ -77,12 +78,12 @@ const Staking = () => { .catch((err) => { console.log(err); }); - }; + }, [wallet]); useEffect(() => { getNodeInfo(); loadNetwork(); - }, []); + }, [getNodeInfo, loadNetwork]); return ( diff --git a/src/ui/views/Swap/SelectToken.tsx b/src/ui/views/Swap/SelectToken.tsx index 236b2f60..1a5b0ebd 100644 --- a/src/ui/views/Swap/SelectToken.tsx +++ b/src/ui/views/Swap/SelectToken.tsx @@ -1,6 +1,6 @@ -import React, { useState, useEffect } from 'react'; -import { useHistory } from 'react-router-dom'; - +import CloseIcon from '@mui/icons-material/Close'; +import InfoIcon from '@mui/icons-material/Info'; +import SearchIcon from '@mui/icons-material/Search'; import { Box, Typography, @@ -19,18 +19,14 @@ import { ListItemButton, ListItemText, } from '@mui/material'; -import CloseIcon from '@mui/icons-material/Close'; -import { LLSpinner } from 'ui/FRWComponent'; +import { makeStyles } from '@mui/styles'; +import React, { useState, useEffect, useCallback } from 'react'; +import { useHistory } from 'react-router-dom'; + import { useWallet } from 'ui/utils'; -import { LLProfile } from 'ui/FRWComponent'; -import IconNext from 'ui/FRWAssets/svg/next.svg'; + +import { IconCheckmark } from '../../../components/iconfont'; import IconSwitch from '../../../components/iconfont/IconSwitch'; -import eventBus from '@/eventBus'; -import InfoIcon from '@mui/icons-material/Info'; -import { Presets } from 'react-component-transition'; -import SearchIcon from '@mui/icons-material/Search'; -import { makeStyles } from '@mui/styles'; -import { IconCheckmark, IconPlus } from '../../../components/iconfont'; interface TransferConfirmationProps { isConfirmationOpen: boolean; @@ -77,14 +73,14 @@ const SelectToken = (props: TransferConfirmationProps) => { const [token0, setToken0] = useState(null); const [token1, setToken1] = useState(null); - const setToken = async () => { + const setToken = useCallback(async () => { if (props.data.token0) { setToken0(props.data.token0.contract_name); } if (props.data.token1) { setToken1(props.data.token1.contract_name); } - }; + }, [props.data.token0, props.data.token1]); const setSelectToken = (token) => { props.updateCoinInfo(token); @@ -95,12 +91,12 @@ const SelectToken = (props: TransferConfirmationProps) => { useEffect(() => { // startCount(); setToken(); - }, [props.data.token0]); + }, [props.data.token0, setToken]); useEffect(() => { // startCount(); setToken(); - }, [props.data.token1]); + }, [props.data.token1, setToken]); const renderContent = () => ( { backgroundColor: '#1f1f1f', borderRadius: '16px', }} - disabled={coin.contract_name == token0 || coin.contract_name == token1} + disabled={coin.contract_name === token0 || coin.contract_name === token1} > setSelectToken(coin)} secondaryAction={ - coin.contract_name == token0 || coin.contract_name == token1 ? ( + coin.contract_name === token0 || coin.contract_name === token1 ? ( diff --git a/src/ui/views/Swap/SwapTarget.tsx b/src/ui/views/Swap/SwapTarget.tsx index 4f32cddd..c15083cb 100644 --- a/src/ui/views/Swap/SwapTarget.tsx +++ b/src/ui/views/Swap/SwapTarget.tsx @@ -1,29 +1,8 @@ -import React, { useState, useEffect } from 'react'; -import { - Box, - Typography, - Button, - ListItemText, - Select, - MenuItem, - ListItemIcon, - FormControl, - InputAdornment, - Input, - Chip, - Tooltip, - Avatar, -} from '@mui/material'; -import { makeStyles } from '@mui/styles'; -import CloseIcon from '@mui/icons-material/Close'; -import IconFlow from '../../../components/iconfont/IconFlow'; -import IconSwitch from '../../../components/iconfont/IconSwitch'; -import CancelIcon from '../../../components/iconfont/IconClose'; -import AttachMoneyRoundedIcon from '@mui/icons-material/AttachMoneyRounded'; -import { StyledEngineProvider } from '@mui/material/styles'; -import BN from 'bignumber.js'; -import { Presets } from 'react-component-transition'; import SelectIcon from '@mui/icons-material/ArrowDropDown'; +import { Box, Typography, Button, FormControl, InputAdornment, Input, Avatar } from '@mui/material'; +import { StyledEngineProvider } from '@mui/material/styles'; +import { makeStyles } from '@mui/styles'; +import React from 'react'; const useStyles = makeStyles(() => ({ customInputLabel: { diff --git a/src/ui/views/Swap/TransferAmount.tsx b/src/ui/views/Swap/TransferAmount.tsx index fbd46bb3..a01f35d7 100644 --- a/src/ui/views/Swap/TransferAmount.tsx +++ b/src/ui/views/Swap/TransferAmount.tsx @@ -1,28 +1,18 @@ -import React, { useState, useEffect } from 'react'; +import SelectIcon from '@mui/icons-material/ArrowDropDown'; import { Box, Typography, Button, - ListItemText, - Select, - MenuItem, - ListItemIcon, FormControl, InputAdornment, Input, Chip, - Tooltip, Stack, Avatar, } from '@mui/material'; -import { makeStyles } from '@mui/styles'; -import IconFlow from '../../../components/iconfont/IconFlow'; -import CancelIcon from '../../../components/iconfont/IconClose'; -import AttachMoneyRoundedIcon from '@mui/icons-material/AttachMoneyRounded'; import { StyledEngineProvider } from '@mui/material/styles'; -import BN from 'bignumber.js'; -import { Presets } from 'react-component-transition'; -import SelectIcon from '@mui/icons-material/ArrowDropDown'; +import { makeStyles } from '@mui/styles'; +import React, { useEffect } from 'react'; const useStyles = makeStyles(() => ({ customInputLabel: { @@ -134,7 +124,7 @@ const TransferAmount = ({ removeError(); } } - }, [amount]); + }, [amount, coinInfo, removeError, setError]); return ( diff --git a/src/ui/views/Swap/TransferConfirmation.tsx b/src/ui/views/Swap/TransferConfirmation.tsx index 65d357c2..ca52c9a5 100644 --- a/src/ui/views/Swap/TransferConfirmation.tsx +++ b/src/ui/views/Swap/TransferConfirmation.tsx @@ -2,9 +2,9 @@ import CloseIcon from '@mui/icons-material/Close'; import InfoIcon from '@mui/icons-material/Info'; import { Box, Typography, Drawer, Grid, CardMedia, IconButton, Button } from '@mui/material'; import React, { useState, useEffect, useCallback } from 'react'; -import { Presets } from 'react-component-transition'; import { useHistory } from 'react-router-dom'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; import StorageExceededAlert from '@/ui/FRWComponent/StorageExceededAlert'; import { WarningStorageLowSnackbar } from '@/ui/FRWComponent/WarningStorageLowSnackbar'; import { useStorageCheck } from '@/ui/utils/useStorageCheck'; @@ -259,28 +259,26 @@ const TransferConfirmation = (props: TransferConfirmationProps) => { - {occupied && ( - - - {/* */} - - - {chrome.i18n.getMessage('Your_address_is_currently_processing_another_transaction')} - - - - )} + + + {/* */} + + + {chrome.i18n.getMessage('Your_address_is_currently_processing_another_transaction')} + + + { size="large" sx={{ height: '50px', + width: '100%', borderRadius: '12px', textTransform: 'capitalize', display: 'flex', diff --git a/src/ui/views/Swap/index.tsx b/src/ui/views/Swap/index.tsx index 821ecbe5..1a5f4581 100644 --- a/src/ui/views/Swap/index.tsx +++ b/src/ui/views/Swap/index.tsx @@ -1,25 +1,64 @@ -import React, { useState, useEffect } from 'react'; import { Box, Button, Typography, IconButton, CardMedia } from '@mui/material'; +import React, { useState, useEffect, useCallback } from 'react'; + // import { useHistory, useLocation } from 'react-router-dom'; // import ArrowBackIcon from '@mui/icons-material/ArrowBack'; -import { CoinItem } from 'background/service/coinList'; -import theme from '../../style/LLTheme'; -import { ThemeProvider } from '@mui/material/styles'; -import TransferAmount from './TransferAmount'; -import SwapTarget from './SwapTarget'; +import { LLHeader } from '@/ui/FRWComponent'; +import { type CoinItem } from 'background/service/coinList'; +import { type Contact } from 'background/service/networkModel'; +import { LLSpinner } from 'ui/FRWComponent'; import { useWallet } from 'ui/utils'; import { withPrefix } from 'ui/utils/address'; + import IconSwitch from '../../../components/iconfont/IconSwitch'; -import TransferConfirmation from './TransferConfirmation'; +import Increment from '../../FRWAssets/svg/increment.svg'; + import SelectToken from './SelectToken'; -import { LLSpinner } from 'ui/FRWComponent'; -import { Contact } from 'background/service/networkModel'; -// import { Presets } from 'react-component-transition'; +import SwapTarget from './SwapTarget'; +import TransferAmount from './TransferAmount'; +import TransferConfirmation from './TransferConfirmation'; + +// // import CancelIcon from '../../../components/iconfont/IconClose'; -import { LLHeader } from '@/ui/FRWComponent'; // import { TokenListProvider } from 'flow-native-token-registry'; +const USER_CONTACT = { + address: '', + id: 0, + contact_name: '', + avatar: '', + domain: { + domain_type: 999, + value: '', + }, +} as unknown as Contact; -import Increment from '../../FRWAssets/svg/increment.svg'; +const FLOW_TOKEN = { + name: 'Flow', + address: { + mainnet: '0x1654653399040a61', + testnet: '0x7e60df042a9c0868', + crescendo: '0x7e60df042a9c0868', + }, + contract_name: 'FlowToken', + storage_path: { + balance: '/public/flowTokenBalance', + vault: '/storage/flowTokenVault', + receiver: '/public/flowTokenReceiver', + }, + decimal: 8, + icon: 'https://raw.githubusercontent.com/Outblock/Assets/main/ft/flow/logo.png', + symbol: 'flow', + website: 'https://www.onflow.org', +}; +const EMPTY_COIN: CoinItem = { + coin: '', + unit: '', + balance: 0, + price: 0, + change24h: 0, + total: 0, + icon: '', +}; const Swap = () => { enum ENV { @@ -36,44 +75,6 @@ const Swap = () => { // Static = 'Static', // CDN = 'CDN' // } - const userContact = { - address: '', - id: 0, - contact_name: '', - avatar: '', - domain: { - domain_type: 999, - value: '', - }, - } as unknown as Contact; - - const flowToken = { - name: 'Flow', - address: { - mainnet: '0x1654653399040a61', - testnet: '0x7e60df042a9c0868', - crescendo: '0x7e60df042a9c0868', - }, - contract_name: 'FlowToken', - storage_path: { - balance: '/public/flowTokenBalance', - vault: '/storage/flowTokenVault', - receiver: '/public/flowTokenReceiver', - }, - decimal: 8, - icon: 'https://raw.githubusercontent.com/Outblock/Assets/main/ft/flow/logo.png', - symbol: 'flow', - website: 'https://www.onflow.org', - }; - const empty: CoinItem = { - coin: '', - unit: '', - balance: 0, - price: 0, - change24h: 0, - total: 0, - icon: '', - }; const usewallet = useWallet(); const [userWallet, setWallet] = useState(null); @@ -85,10 +86,10 @@ const Swap = () => { const [amount, setAmount] = useState('0'); const [outAmount, setOutAmount] = useState(0); // const [validated, setValidated] = useState(null); - const [userInfo, setUser] = useState(userContact); + const [userInfo, setUser] = useState(USER_CONTACT); const [selectTarget, setSelectTarget] = useState(0); const [network, setNetwork] = useState('mainnet'); - const [coinInfo, setCoinInfo] = useState(empty); + const [coinInfo, setCoinInfo] = useState(EMPTY_COIN); const [estimateInfo, setEstimateInfo] = useState(null); const [token1, setToken1] = useState(null); const [token0, setToken0] = useState(null); @@ -98,7 +99,7 @@ const Swap = () => { const [swapPrice, setPrice] = useState(0); const [errorType, setErrorType] = useState(null); - const setUserWallet = async () => { + const setUserWallet = useCallback(async () => { // const walletList = await storage.get('userWallet'); setLoading(true); const token = await usewallet.getCurrentCoin(); @@ -114,9 +115,12 @@ const Swap = () => { setCoinInfo(coinInfo!); const info = await usewallet.getUserInfo(false); - userContact.address = withPrefix(wallet.address) || ''; - userContact.avatar = info.avatar; - userContact.contact_name = info.username; + const userContact = { + ...USER_CONTACT, + address: withPrefix(wallet.address) || '', + avatar: info.avatar, + contact_name: info.username, + }; setUser(userContact); // const result = await usewallet.openapi.fetchTokenList(network); usewallet.openapi.getAllToken().then((res) => { @@ -124,7 +128,7 @@ const Swap = () => { }); setLoading(false); return; - }; + }, [usewallet]); const updateCoinInfo = (token) => { if (selectTarget) { @@ -165,7 +169,7 @@ const Swap = () => { setSwapTypes(1); }; - const estimateOut = async () => { + const estimateOut = useCallback(async () => { setLoading(true); const network = await usewallet.getNetwork(); if (token0 && token1) { @@ -185,7 +189,7 @@ const Swap = () => { let price: any = result.data.tokenOutAmount / parseFloat(result.data.tokenInAmount); price = (Math.round(price * 1000) / 1000).toFixed(3); setPrice(price); - if (errorType == Error.Fail) { + if (errorType === Error.Fail) { setErrorType(null); } setEstimateInfo(result.data); @@ -197,9 +201,9 @@ const Swap = () => { } setLoading(false); return; - }; + }, [outAmount, usewallet, token0, token1, errorType, Error.Fail]); - const estimate = async () => { + const estimate = useCallback(async () => { setLoading(true); if (Number(amount) <= 0) { setLoading(false); @@ -219,7 +223,7 @@ const Swap = () => { let price: any = result.data.tokenOutAmount / parseFloat(result.data.tokenInAmount); price = (Math.round(price * 1000) / 1000).toFixed(3); setPrice(price); - if (errorType == Error.Fail) { + if (errorType === Error.Fail) { setErrorType(null); } setEstimateInfo(result.data); @@ -231,12 +235,12 @@ const Swap = () => { } } setLoading(false); - }; + }, [amount, usewallet, token0, token1, errorType, Error.Fail]); useEffect(() => { setUserWallet(); - setToken0(flowToken); - }, []); + setToken0(FLOW_TOKEN); + }, [setUserWallet]); useEffect(() => { if (swapTypes) { @@ -247,21 +251,21 @@ const Swap = () => { }, 500); return () => clearTimeout(delayDebounceFn); - }, [amount]); + }, [amount, estimate, swapTypes]); useEffect(() => { const delayDebounceFn = setTimeout(async () => { estimate(); }, 500); return () => clearTimeout(delayDebounceFn); - }, [token1]); + }, [token1, estimate]); useEffect(() => { const delayDebounceFn = setTimeout(async () => { estimate(); }, 500); return () => clearTimeout(delayDebounceFn); - }, [token0]); + }, [token0, estimate]); useEffect(() => { if (!swapTypes) { @@ -271,11 +275,11 @@ const Swap = () => { estimateOut(); }, 500); return () => clearTimeout(delayDebounceFn); - }, [outAmount]); + }, [outAmount, estimateOut, swapTypes]); return (
- + <> @@ -443,7 +447,7 @@ const Swap = () => { {/* - { }}> Estimated Fees - { textTransform: 'capitalize', }} disabled={ - outAmount <= 0 || Number(amount) <= 0 || errorType || isLoading || token1 == null + outAmount <= 0 || Number(amount) <= 0 || errorType || isLoading || token1 === null } > @@ -511,7 +515,7 @@ const Swap = () => { updateCoinInfo={updateCoinInfo} /> - +
); }; diff --git a/src/ui/views/SwitchUnlock/index.tsx b/src/ui/views/SwitchUnlock/index.tsx index cbadadb8..80497e4d 100644 --- a/src/ui/views/SwitchUnlock/index.tsx +++ b/src/ui/views/SwitchUnlock/index.tsx @@ -1,14 +1,16 @@ -import React, { useEffect, useRef, useState } from 'react'; // import { useTranslation } from 'react-i18next'; -import { useWallet, useApproval, useWalletRequest } from 'ui/utils'; -import { Typography, Box, FormControl } from '@mui/material'; -import { LLPrimaryButton, LLResetPopup } from 'ui/FRWComponent'; -import { Input } from '@mui/material'; -import { Presets } from 'react-component-transition'; -import CancelIcon from '../../../components/iconfont/IconClose'; +import { Input, Typography, Box, FormControl } from '@mui/material'; import { makeStyles } from '@mui/styles'; +import React, { useEffect, useRef, useState } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; import lilo from 'ui/FRWAssets/image/lilo.png'; +import { LLPrimaryButton, LLResetPopup } from 'ui/FRWComponent'; +import { useWallet, useApproval, useWalletRequest } from 'ui/utils'; import { openInternalPageInTab } from 'ui/utils/webapi'; + +import CancelIcon from '../../../components/iconfont/IconClose'; + import './style.css'; const useStyles = makeStyles(() => ({ @@ -143,20 +145,18 @@ const SwitchUnlock = () => { onKeyDown={handleKeyDown} /> - - {showError && ( - - {usernameError()} - - )} - + + + {usernameError()} + + {/* */} diff --git a/src/ui/views/Sync/AllSet.tsx b/src/ui/views/Sync/AllSet.tsx index 1252f73c..cca17c81 100644 --- a/src/ui/views/Sync/AllSet.tsx +++ b/src/ui/views/Sync/AllSet.tsx @@ -1,13 +1,12 @@ +import { Button, Typography, CardMedia } from '@mui/material'; +import { Box } from '@mui/system'; import React from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, CssBaseline, CardMedia } from '@mui/material'; -import theme from '../../style/LLTheme'; + import AllSetIcon from 'ui/FRWAssets/svg/allset.svg'; const AllSet = ({ handleClick }) => { return ( - - + <> { - + ); }; diff --git a/src/ui/views/Sync/RegisterHeader.tsx b/src/ui/views/Sync/RegisterHeader.tsx index 234219ff..fb499d7f 100644 --- a/src/ui/views/Sync/RegisterHeader.tsx +++ b/src/ui/views/Sync/RegisterHeader.tsx @@ -1,12 +1,11 @@ -import React from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography } from '@mui/material'; -import theme from '../../style/LLTheme'; import HelpOutlineRoundedIcon from '@mui/icons-material/HelpOutlineRounded'; +import { Button, Typography } from '@mui/material'; +import { Box } from '@mui/system'; +import React from 'react'; const RegisterHeader = () => { return ( - + <> { - + ); }; diff --git a/src/ui/views/Sync/SetPassword.tsx b/src/ui/views/Sync/SetPassword.tsx index 267d2d9d..95f9317e 100644 --- a/src/ui/views/Sync/SetPassword.tsx +++ b/src/ui/views/Sync/SetPassword.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState } from 'react'; -import { makeStyles, styled } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Button, Typography, @@ -11,18 +10,19 @@ import { LinearProgress, Alert, Snackbar, - CssBaseline, } from '@mui/material'; -import { LLSpinner } from 'ui/FRWComponent'; -import CancelIcon from '../../../components/iconfont/IconClose'; -import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; -import VisibilityIcon from '@mui/icons-material/Visibility'; -import { Presets } from 'react-component-transition'; +import { makeStyles, styled } from '@mui/styles'; +import { Box } from '@mui/system'; +import React, { useEffect, useState } from 'react'; import zxcvbn from 'zxcvbn'; -import theme from '../../style/LLTheme'; -import { useWallet, saveIndex } from 'ui/utils'; + import { storage } from '@/background/webapi'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { LLSpinner } from 'ui/FRWComponent'; +import { useWallet, saveIndex } from 'ui/utils'; + +import CheckCircleIcon from '../../../components/iconfont/IconCheckmark'; +import CancelIcon from '../../../components/iconfont/IconClose'; // const helperTextStyles = makeStyles(() => ({ // root: { @@ -238,8 +238,7 @@ const SetPassword = ({ handleClick, mnemonic, username }) => { }, [confirmPassword, password]); return ( - - + <> { } /> - {password && helperText} + + {helperText} + { } /> - - {confirmPassword && helperMatch} - + + {helperMatch} + @@ -394,7 +395,7 @@ const SetPassword = ({ handleClick, mnemonic, username }) => { - + ); }; diff --git a/src/ui/views/Sync/SyncQr.tsx b/src/ui/views/Sync/SyncQr.tsx index 9c070274..496bd2f4 100644 --- a/src/ui/views/Sync/SyncQr.tsx +++ b/src/ui/views/Sync/SyncQr.tsx @@ -1,17 +1,17 @@ -import React, { useEffect, useCallback, useState } from 'react'; +import { Button, Typography, FormControl, Input, InputAdornment } from '@mui/material'; import { makeStyles } from '@mui/styles'; -import { Box, ThemeProvider } from '@mui/system'; -import { Button, Typography, FormControl, Input, InputAdornment, CssBaseline } from '@mui/material'; -import theme from '../../style/LLTheme'; -import { useWallet } from 'ui/utils'; +import { Box } from '@mui/system'; import { Core } from '@walletconnect/core'; -import { FCLWalletConnectMethod } from '@/ui/utils/type'; import SignClient from '@walletconnect/sign-client'; -import { PairingTypes, SessionTypes } from '@walletconnect/types'; +import { PairingTypes, type SessionTypes } from '@walletconnect/types'; import * as bip39 from 'bip39'; import HDWallet from 'ethereum-hdwallet'; +import React, { useEffect, useCallback, useState } from 'react'; import { QRCode } from 'react-qrcode-logo'; + +import { FCLWalletConnectMethod } from '@/ui/utils/type'; import lilo from 'ui/FRWAssets/image/lilo.png'; +import { useWallet } from 'ui/utils'; interface AccountKey { hashAlgo: number; @@ -77,69 +77,14 @@ const SyncQr = ({ handleClick, savedUsername, confirmMnemonic, setUsername }) => process.env.NODE_ENV === 'production' ? 'mainnet' : 'testnet' ); - const loadNetwork = async () => { + const loadNetwork = useCallback(async () => { const currentNetwork = await usewallet.getNetwork(); setNetwork(currentNetwork); - }; + }, [usewallet]); useEffect(() => { loadNetwork(); - }, []); - - useEffect(() => { - const createWeb3Wallet = async () => { - try { - const wallet = await SignClient.init({ - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore: Unreachable code error - core: new Core({ - projectId: process.env.WC_PROJECTID, - }), - metadata: { - name: 'Flow Walllet', - description: 'Digital wallet created for everyone.', - url: 'https://fcw-link.lilico.app', - icons: ['https://fcw-link.lilico.app/logo.png'], - }, - }); - await _subscribeToEvents(wallet); - - try { - const { uri, approval } = await wallet.connect({ - requiredNamespaces: { - flow: { - methods: [FCLWalletConnectMethod.accountInfo, FCLWalletConnectMethod.addDeviceInfo], - chains: [`flow:${currentNetwork}`], - events: [], - }, - }, - }); - - // Open QRCode modal if a URI was returned (i.e. we're not connecting an existing pairing). - if (uri) { - console.log('uri ', uri); - await setUri(uri); - // Await session approval from the wallet. - const session = await approval(); - await onSessionConnected(session); - - console.log('session ', session); - sendRequest(wallet, session.topic); - - // onSessionConnect(session) - // Close the QRCode modal in case it was open. - } - } catch (e) { - console.error(e); - } - await setWeb3Wallet(wallet); - console.log('web3wallet', web3wallet); - } catch (e) { - console.error(e); - } - }; - createWeb3Wallet(); - }, []); + }, [loadNetwork]); const onSessionConnected = useCallback(async (_session: SessionTypes.Struct) => { console.log('_session ', _session); @@ -165,68 +110,7 @@ const SyncQr = ({ handleClick, savedUsername, confirmMnemonic, setUsername }) => [onSessionConnected] ); - async function sendRequest(wallet: SignClient, topic: string) { - wallet - .request({ - topic: topic, - chainId: `flow:${currentNetwork}`, - request: { - method: FCLWalletConnectMethod.accountInfo, - params: [], - }, - }) - .then(async (result: any) => { - const jsonObject = JSON.parse(result); - if (jsonObject.method === FCLWalletConnectMethod.accountInfo) { - const accountKey: AccountKey = getAccountKey(); - const deviceInfo: DeviceInfoRequest = await getDeviceInfo(); - - wallet - .request({ - topic: topic, - chainId: `flow:${currentNetwork}`, - request: { - method: FCLWalletConnectMethod.addDeviceInfo, - params: { - method: '', - data: { - username: '', - accountKey: accountKey, - deviceInfo: deviceInfo, - }, - }, - }, - }) - .then(async (sent) => { - const ak = { - public_key: accountKey.publicKey, - hash_algo: accountKey.hashAlgo, - sign_algo: accountKey.signAlgo, - weight: accountKey.weight, - }; - usewallet - .signInV3(mnemonic, ak, deviceInfo) - .then(async (result) => { - confirmMnemonic(mnemonic); - const userInfo = await usewallet.getUserInfo(true); - setUsername(userInfo.username); - handleClick(); - }) - .catch((error) => { - console.error('Error in sign in wallet request:', error); - }); - }) - .catch((error) => { - console.error('Error in second wallet request:', error); - }); - } - }) - .catch((error) => { - console.error('Error in first wallet request:', error); - }); - } - - const getAccountKey = () => { + const getAccountKey = useCallback(() => { const hdwallet = HDWallet.fromMnemonic(mnemonic); const publicKey = hdwallet.derive("m/44'/539'/0'/0/0").getPublicKey().toString('hex'); const key: AccountKey = { @@ -236,9 +120,9 @@ const SyncQr = ({ handleClick, savedUsername, confirmMnemonic, setUsername }) => publicKey: publicKey, }; return key; - }; + }, [mnemonic]); - const getDeviceInfo = async (): Promise => { + const getDeviceInfo = useCallback(async (): Promise => { const result = await usewallet.openapi.getLocation(); const installationId = await usewallet.openapi.getInstallationId(); const userlocation = result.data; @@ -264,11 +148,137 @@ const SyncQr = ({ handleClick, savedUsername, confirmMnemonic, setUsername }) => zip: userlocation.zip, }; return deviceInfo; - }; + }, [usewallet]); + + const sendRequest = useCallback( + async (wallet: SignClient, topic: string) => { + wallet + .request({ + topic: topic, + chainId: `flow:${currentNetwork}`, + request: { + method: FCLWalletConnectMethod.accountInfo, + params: [], + }, + }) + .then(async (result: any) => { + const jsonObject = JSON.parse(result); + if (jsonObject.method === FCLWalletConnectMethod.accountInfo) { + const accountKey: AccountKey = getAccountKey(); + const deviceInfo: DeviceInfoRequest = await getDeviceInfo(); + + wallet + .request({ + topic: topic, + chainId: `flow:${currentNetwork}`, + request: { + method: FCLWalletConnectMethod.addDeviceInfo, + params: { + method: '', + data: { + username: '', + accountKey: accountKey, + deviceInfo: deviceInfo, + }, + }, + }, + }) + .then(async (sent) => { + const ak = { + public_key: accountKey.publicKey, + hash_algo: accountKey.hashAlgo, + sign_algo: accountKey.signAlgo, + weight: accountKey.weight, + }; + usewallet + .signInV3(mnemonic, ak, deviceInfo) + .then(async (result) => { + confirmMnemonic(mnemonic); + const userInfo = await usewallet.getUserInfo(true); + setUsername(userInfo.username); + handleClick(); + }) + .catch((error) => { + console.error('Error in sign in wallet request:', error); + }); + }) + .catch((error) => { + console.error('Error in second wallet request:', error); + }); + } + }) + .catch((error) => { + console.error('Error in first wallet request:', error); + }); + }, + [ + confirmMnemonic, + currentNetwork, + getAccountKey, + getDeviceInfo, + handleClick, + mnemonic, + setUsername, + usewallet, + ] + ); + + useEffect(() => { + const createWeb3Wallet = async () => { + try { + const wallet = await SignClient.init({ + // @ts-ignore: Unreachable code error + core: new Core({ + projectId: process.env.WC_PROJECTID, + }), + metadata: { + name: 'Flow Walllet', + description: 'Digital wallet created for everyone.', + url: 'https://fcw-link.lilico.app', + icons: ['https://fcw-link.lilico.app/logo.png'], + }, + }); + await _subscribeToEvents(wallet); + + try { + const { uri, approval } = await wallet.connect({ + requiredNamespaces: { + flow: { + methods: [FCLWalletConnectMethod.accountInfo, FCLWalletConnectMethod.addDeviceInfo], + chains: [`flow:${currentNetwork}`], + events: [], + }, + }, + }); + + // Open QRCode modal if a URI was returned (i.e. we're not connecting an existing pairing). + if (uri) { + console.log('uri ', uri); + await setUri(uri); + // Await session approval from the wallet. + const session = await approval(); + await onSessionConnected(session); + + console.log('session ', session); + sendRequest(wallet, session.topic); + + // onSessionConnect(session) + // Close the QRCode modal in case it was open. + } + } catch (e) { + console.error(e); + } + await setWeb3Wallet(wallet); + console.log('web3wallet', web3wallet); + } catch (e) { + console.error(e); + } + }; + createWeb3Wallet(); + }, [_subscribeToEvents, currentNetwork, onSessionConnected, sendRequest, web3wallet]); return ( - - + <> {/* */} - + ); }; diff --git a/src/ui/views/Sync/index.tsx b/src/ui/views/Sync/index.tsx index cde10911..2ee8ebb8 100644 --- a/src/ui/views/Sync/index.tsx +++ b/src/ui/views/Sync/index.tsx @@ -1,18 +1,20 @@ -import React, { useState, useEffect } from 'react'; +import { IconButton } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useState, useEffect, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; -import { Box, ThemeProvider } from '@mui/system'; -import { IconButton, Typography, Button, Snackbar, Alert } from '@mui/material'; + +import Confetti from '@/ui/FRWComponent/Confetti'; +import SlideLeftRight from '@/ui/FRWComponent/SlideLeftRight'; +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; +import { LLPinAlert } from 'ui/FRWComponent'; +import { useWallet } from 'ui/utils'; + import BackButtonIcon from '../../../components/iconfont/IconBackButton'; -import IconGoogleDrive from '../../../components/iconfont/IconGoogleDrive'; -import theme from '../../style/LLTheme'; -import RegisterHeader from '../Register/RegisterHeader'; import AllSet from '../Register/AllSet'; +import RegisterHeader from '../Register/RegisterHeader'; + import SetPassword from './SetPassword'; import SyncQr from './SyncQr'; -import Particles from 'react-tsparticles'; -import { LLPinAlert, LLSpinner } from 'ui/FRWComponent'; -import { ComponentTransition, AnimationTypes } from 'react-component-transition'; -import { useWallet, Options } from 'ui/utils'; enum Direction { Right, @@ -25,16 +27,13 @@ const Sync = () => { const [activeIndex, onChange] = useState(0); const [mnemonic, setMnemonic] = useState(''); const [username, setUsername] = useState(''); - const [errMessage, setErrorMessage] = useState(chrome.i18n.getMessage('No__backup__found')); - const [showError, setShowError] = useState(false); const [direction, setDirection] = useState(Direction.Right); - const [loading, setLoading] = useState(false); const getUsername = (username: string) => { setUsername(username.toLowerCase()); }; - const loadView = async () => { + const loadView = useCallback(async () => { wallet .getCurrentAccount() .then((res) => { @@ -45,7 +44,7 @@ const Sync = () => { .catch(() => { return; }); - }; + }, [wallet, history]); const goNext = () => { setDirection(Direction.Right); if (activeIndex < 2) { @@ -64,13 +63,6 @@ const Sync = () => { } }; - const handleErrorClose = (event?: React.SyntheticEvent | Event, reason?: string) => { - if (reason === 'clickaway') { - return; - } - setShowError(false); - }; - const page = (index) => { switch (index) { case 0: @@ -93,10 +85,10 @@ const Sync = () => { useEffect(() => { loadView(); - }, []); + }, [loadView]); return ( - + <> { alignItems: 'center', }} > - {activeIndex == 2 && ( - - )} + {activeIndex === 2 && } - + {/* height why not use auto */} @@ -144,31 +131,15 @@ const Sync = () => { )} - + {page(activeIndex)} - + - + ); }; diff --git a/src/ui/views/TokenDetail/TokenInfoCard.tsx b/src/ui/views/TokenDetail/TokenInfoCard.tsx index dd3f6215..7e18fc4e 100644 --- a/src/ui/views/TokenDetail/TokenInfoCard.tsx +++ b/src/ui/views/TokenDetail/TokenInfoCard.tsx @@ -1,13 +1,16 @@ -import React, { useEffect, useState, useRef } from 'react'; -import { useWallet } from 'ui/utils'; -import { addDotSeparators } from 'ui/utils/number'; import { Typography, Box, ButtonBase, CardMedia } from '@mui/material'; -import IconChevronRight from '../../../components/iconfont/IconChevronRight'; +import { type TokenInfo } from 'flow-native-token-registry'; +import React, { useEffect, useState, useRef, useCallback } from 'react'; +import { useHistory } from 'react-router-dom'; + import { LLPrimaryButton } from '@/ui/FRWComponent'; -import { TokenInfo } from 'flow-native-token-registry'; +import { isValidEthereumAddress } from '@/ui/utils/address'; import iconMove from 'ui/FRWAssets/svg/moveIcon.svg'; +import { useWallet } from 'ui/utils'; +import { addDotSeparators } from 'ui/utils/number'; + +import IconChevronRight from '../../../components/iconfont/IconChevronRight'; -import { useHistory } from 'react-router-dom'; // import tips from 'ui/FRWAssets/svg/tips.svg'; const TokenInfoCard = ({ @@ -43,14 +46,14 @@ const TokenInfoCard = ({ }; checkPermission(); - }, [balance]); + }, [balance, tokenInfo.custom, wallet]); const toSend = async () => { await wallet.setCurrentCoin(token); history.push('/dashboard/wallet/send'); }; - const getActive = async () => { + const getActive = useCallback(async () => { const evmEnabled = await wallet.getEvmEnabled(); setEvmEnabled(evmEnabled); const isChild = await wallet.getActiveWallet(); @@ -71,7 +74,7 @@ const TokenInfoCard = ({ .getWalletTokenBalance(token) .then((response) => { if (isMounted.current) { - setBalance(parseFloat(parseFloat(response).toFixed(3))); + setBalance(parseFloat(response)); } }) .catch((err) => { @@ -84,7 +87,7 @@ const TokenInfoCard = ({ isMounted.current = false; // Mark component as unmounted clearTimeout(timerId); // Clear the timer }; - }; + }, [setAccessible, token, tokenInfo, wallet]); const moveToken = () => { if (childType && childType !== 'evm') { @@ -95,6 +98,18 @@ const TokenInfoCard = ({ } }; + const getUrl = (data) => { + if (data.extensions?.website?.trim()) { + return data.extensions.website; + } + if (isValidEthereumAddress(data.address)) { + return `https://evm.flowscan.io/token/${data.address}`; + } else if (data.symbol.toLowerCase() === 'flow') { + return 'https://flow.com/'; + } + return `https://flowscan.io/account/${data.address}/tokens`; + }; + useEffect(() => { isMounted.current = true; getActive(); @@ -102,7 +117,7 @@ const TokenInfoCard = ({ return () => { isMounted.current = false; }; - }, [token]); + }, [token, getActive]); return ( - data.extensions && window.open(data.extensions.website, '_blank')} - > + window.open(getUrl(data), '_blank')}> {data.name} - {data.extensions && - data.extensions.website && - data.extensions.website.trim() !== '' && } + @@ -209,7 +220,7 @@ const TokenInfoCard = ({ - ${addDotSeparators((balance * price).toFixed(3))} {chrome.i18n.getMessage('USD')} + ${addDotSeparators(balance * price)} {chrome.i18n.getMessage('USD')} {(!childType || childType === 'evm') && ( diff --git a/src/ui/views/TokenDetail/index.tsx b/src/ui/views/TokenDetail/index.tsx index 270ceef8..c704c7ff 100644 --- a/src/ui/views/TokenDetail/index.tsx +++ b/src/ui/views/TokenDetail/index.tsx @@ -129,7 +129,7 @@ const TokenDetail = () => { } setProviders(result); if (result.length === 0) { - const data = await usewallet.openapi.getTokenPrices(); + const data = await usewallet.openapi.getTokenPrices('pricesMap'); const price = await usewallet.openapi.getPricesByAddress(tokenResult!.address, data); if (price) { setPrice(price); @@ -240,8 +240,7 @@ const TokenDetail = () => { /> )} {token === 'flow' && } - {/* {network === 'testnet' || network === 'crescendo' && token === 'flow' && } */} - + {network === 'testnet' && token === 'flow' && } {providers?.length > 0 && ( )} diff --git a/src/ui/views/TokenList/AddTokenConfirmation.tsx b/src/ui/views/TokenList/AddTokenConfirmation.tsx index 952cc292..22fc9aeb 100644 --- a/src/ui/views/TokenList/AddTokenConfirmation.tsx +++ b/src/ui/views/TokenList/AddTokenConfirmation.tsx @@ -1,11 +1,11 @@ +import CloseIcon from '@mui/icons-material/Close'; +import { Box, Typography, Drawer, Grid, Button, IconButton } from '@mui/material'; +import { type TokenInfo } from 'flow-native-token-registry'; import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; -import { Box, Typography, Drawer, Grid, Button, IconButton } from '@mui/material'; -import CloseIcon from '@mui/icons-material/Close'; import { LLSpinner } from 'ui/FRWComponent'; import { useWallet } from 'ui/utils'; -import { TokenInfo } from 'flow-native-token-registry'; interface AddTokenConfirmationProps { isConfirmationOpen: boolean; @@ -136,6 +136,7 @@ const AddTokenConfirmation = (props: AddTokenConfirmationProps) => { size="large" sx={{ height: '50px', + width: '100%', borderRadius: '12px', textTransform: 'capitalize', display: 'flex', diff --git a/src/ui/views/Unlock/index.tsx b/src/ui/views/Unlock/index.tsx index 43ebf524..9e266ea3 100644 --- a/src/ui/views/Unlock/index.tsx +++ b/src/ui/views/Unlock/index.tsx @@ -1,14 +1,15 @@ -import React, { useEffect, useRef, useState } from 'react'; // import { useTranslation } from 'react-i18next'; -import { useWallet, useApproval, useWalletRequest } from 'ui/utils'; -import { Typography, Box, FormControl } from '@mui/material'; -import { LLPrimaryButton, LLResetPopup } from 'ui/FRWComponent'; -import { Input } from '@mui/material'; -import { Presets } from 'react-component-transition'; -import CancelIcon from '../../../components/iconfont/IconClose'; +import { Input, Typography, Box, FormControl } from '@mui/material'; import { makeStyles } from '@mui/styles'; -import { openInternalPageInTab } from 'ui/utils/webapi'; +import React, { useEffect, useRef, useState } from 'react'; + +import SlideRelative from '@/ui/FRWComponent/SlideRelative'; import lilo from 'ui/FRWAssets/image/lilo.png'; +import { LLPrimaryButton, LLResetPopup } from 'ui/FRWComponent'; +import { useWallet, useApproval, useWalletRequest } from 'ui/utils'; +import { openInternalPageInTab } from 'ui/utils/webapi'; + +import CancelIcon from '../../../components/iconfont/IconClose'; import './style.css'; const useStyles = makeStyles(() => ({ @@ -33,6 +34,15 @@ const useStyles = makeStyles(() => ({ }, })); +const UsernameError: React.FC = () => ( + + + + {chrome.i18n.getMessage('Incorrect__Password')} + + +); + const Unlock = () => { const wallet = useWallet(); const classes = useStyles(); @@ -69,21 +79,6 @@ const Unlock = () => { } }; - const usernameError = () => ( - - - - {chrome.i18n.getMessage('Incorrect__Password')} - - - ); - return ( { - + { onKeyDown={handleKeyDown} /> - - {showError && ( - - {usernameError()} + + + + - )} - - - {/* */} + + diff --git a/src/ui/views/Wallet/Coinlist.tsx b/src/ui/views/Wallet/Coinlist.tsx index aba4cead..a09153a8 100644 --- a/src/ui/views/Wallet/Coinlist.tsx +++ b/src/ui/views/Wallet/Coinlist.tsx @@ -1,8 +1,3 @@ -import React, { useEffect, useState } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { useHistory } from 'react-router-dom'; -import theme from '../../style/LLTheme'; -import { formatLargeNumber } from 'ui/utils/number'; import { Typography, ListItem, @@ -14,7 +9,14 @@ import { List, IconButton, } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useEffect, useState } from 'react'; +import { useHistory } from 'react-router-dom'; + +import { formatLargeNumber } from 'ui/utils/number'; + import IconCreate from '../../../components/iconfont/IconCreate'; + import TokenDropdown from './TokenDropdown'; const CoinList = ({ data, ableFt, isActive, childType, coinLoading }) => { @@ -156,7 +158,7 @@ const CoinList = ({ data, ableFt, isActive, childType, coinLoading }) => { }; return ( - + <> {!childType && ( @@ -217,7 +219,11 @@ const CoinList = ({ data, ableFt, isActive, childType, coinLoading }) => { @@ -238,7 +244,7 @@ const CoinList = ({ data, ableFt, isActive, childType, coinLoading }) => { ); })} - + ); }; diff --git a/src/ui/views/Wallet/TransferList.tsx b/src/ui/views/Wallet/TransferList.tsx index 9cc2c8fd..f28ce82a 100644 --- a/src/ui/views/Wallet/TransferList.tsx +++ b/src/ui/views/Wallet/TransferList.tsx @@ -1,8 +1,6 @@ -import React, { useEffect, useState } from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { useWallet } from 'ui/utils'; -import { formatString } from 'ui/utils/address'; -import theme from '../../style/LLTheme'; +import CallMadeRoundedIcon from '@mui/icons-material/CallMadeRounded'; +import CallReceivedRoundedIcon from '@mui/icons-material/CallReceivedRounded'; +import ChevronRightRoundedIcon from '@mui/icons-material/ChevronRightRounded'; import { Typography, ListItem, @@ -13,15 +11,17 @@ import { ListItemButton, CardMedia, Button, + Box, } from '@mui/material'; -import activity from 'ui/FRWAssets/svg/activity.svg'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; +import React, { useCallback, useEffect, useState } from 'react'; + +import activity from 'ui/FRWAssets/svg/activity.svg'; +import { useWallet } from 'ui/utils'; +import { formatString } from 'ui/utils/address'; // import IconExec from '../../../components/iconfont/IconExec'; -import ChevronRightRoundedIcon from '@mui/icons-material/ChevronRightRounded'; // import ExpandLessRoundedIcon from '@mui/icons-material/ExpandLessRounded'; -import CallMadeRoundedIcon from '@mui/icons-material/CallMadeRounded'; -import CallReceivedRoundedIcon from '@mui/icons-material/CallReceivedRounded'; dayjs.extend(relativeTime); const TransferList = ({ setCount }) => { @@ -34,7 +34,7 @@ const TransferList = ({ setCount }) => { const [address, setAddress] = useState('0x'); const [showButton, setShowButton] = useState(false); - const fetchTransaction = async () => { + const fetchTransaction = useCallback(async () => { setLoading(true); const monitor = await wallet.getMonitor(); setMonitor(monitor); @@ -52,17 +52,20 @@ const TransferList = ({ setCount }) => { setShowButton(data['count'] > 15); } setTx(data['list']); - } catch (e) { + } catch { setLoading(false); } - }; + }, [wallet, setCount]); - const extMessageHandler = (req) => { - if (req.msg === 'transferListReceived') { - fetchTransaction(); - } - return true; - }; + const extMessageHandler = useCallback( + (req) => { + if (req.msg === 'transferListReceived') { + fetchTransaction(); + } + return true; + }, + [fetchTransaction] + ); useEffect(() => { fetchTransaction(); @@ -71,7 +74,7 @@ const TransferList = ({ setCount }) => { return () => { chrome.runtime.onMessage.removeListener(extMessageHandler); }; - }, []); + }, [extMessageHandler, fetchTransaction]); const timeConverter = (timeStamp: number) => { let time = dayjs.unix(timeStamp); @@ -84,6 +87,14 @@ const TransferList = ({ setCount }) => { const EndListItemText = (props) => { const isReceive = props.txType === 2; const isFT = props.type === 1; + + const calculateMaxWidth = () => { + const textLength = + props.type === 1 ? `${props.amount}`.replace(/^-/, '').length : `${props.token}`.length; + const baseWidth = 30; + const additionalWidth = textLength * 8; + return `${Math.min(baseWidth + additionalWidth, 70)}px`; + }; return ( { - {props.type == 1 + {props.type === 1 ? (isReceive ? '+' : '-') + `${props.amount}`.replace(/^-/, '') : `${props.token}`} @@ -133,7 +148,7 @@ const TransferList = ({ setCount }) => { disableTypography={true} primary={ !isLoading ? ( - + {props.txType === 1 ? ( ) : ( @@ -141,7 +156,13 @@ const TransferList = ({ setCount }) => { )} {props.title} @@ -182,7 +203,7 @@ const TransferList = ({ setCount }) => { }; return ( - + <> {!isLoading ? ( {transaction && transaction.length ? ( @@ -209,9 +230,11 @@ const TransferList = ({ setCount }) => { dense={true} onClick={() => { { - monitor === 'flowscan' - ? window.open(`${flowscanURL}/tx/${tx.hash}`) - : window.open(`${viewSource}/${tx.hash}`); + const url = + monitor === 'flowscan' + ? `${flowscanURL}/tx/${tx.hash}` + : `${viewSource}/${tx.hash}`; + window.open(url); } }} > @@ -301,7 +324,7 @@ const TransferList = ({ setCount }) => { ); }) )} - + ); }; diff --git a/src/ui/views/Wallet/index.tsx b/src/ui/views/Wallet/index.tsx index 0e805450..6a586047 100644 --- a/src/ui/views/Wallet/index.tsx +++ b/src/ui/views/Wallet/index.tsx @@ -1,36 +1,29 @@ -import React, { useEffect, useState } from 'react'; -import { useWallet } from 'ui/utils'; -import { formatLargeNumber } from 'ui/utils/number'; -import { Box } from '@mui/system'; -import { - Typography, - Button, - Tab, - Tabs, - Skeleton, - Drawer, - ButtonBase, - CardMedia, -} from '@mui/material'; -import theme from '../../style/LLTheme'; +import FlashOnRoundedIcon from '@mui/icons-material/FlashOnRounded'; import SavingsRoundedIcon from '@mui/icons-material/SavingsRounded'; +import { Typography, Button, Tab, Tabs, Skeleton, Drawer, CardMedia } from '@mui/material'; +import { Box } from '@mui/system'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useHistory, useLocation } from 'react-router-dom'; import SwipeableViews from 'react-swipeable-views'; -import FlashOnRoundedIcon from '@mui/icons-material/FlashOnRounded'; -import CoinList from './Coinlist'; -import MoveBoard from '../MoveBoard'; -import { withPrefix } from '../../utils/address'; -import { useHistory } from 'react-router-dom'; -import TransferList from './TransferList'; -import { useLocation } from 'react-router-dom'; + import eventBus from '@/eventBus'; import LLComingSoon from '@/ui/FRWComponent/LLComingSoonWarning'; -import ReactTextTransition from 'react-text-transition'; -import OnRampList from './OnRampList'; +import { NumberTransition } from '@/ui/FRWComponent/NumberTransition'; +import buyIcon from 'ui/FRWAssets/svg/buyIcon.svg'; import iconMove from 'ui/FRWAssets/svg/homeMove.svg'; +import receiveIcon from 'ui/FRWAssets/svg/receiveIcon.svg'; import sendIcon from 'ui/FRWAssets/svg/sendIcon.svg'; import swapIcon from 'ui/FRWAssets/svg/swapIcon.svg'; -import receiveIcon from 'ui/FRWAssets/svg/receiveIcon.svg'; -import buyIcon from 'ui/FRWAssets/svg/buyIcon.svg'; +import { useWallet } from 'ui/utils'; +import { formatLargeNumber } from 'ui/utils/number'; + +import theme from '../../style/LLTheme'; +import { withPrefix } from '../../utils/address'; +import MoveBoard from '../MoveBoard'; + +import CoinList from './Coinlist'; +import OnRampList from './OnRampList'; +import TransferList from './TransferList'; function TabPanel(props) { const { children, value, index, ...other } = props; @@ -67,11 +60,11 @@ const WalletTab = ({ network }) => { const [balance, setBalance] = useState('$0.00'); const [childType, setChildType] = useState(''); const [alertOpen, setAlertOpen] = useState(false); - const [childAccount, setChildAccount] = useState({}); + const [, setChildAccount] = useState({}); const [txCount, setTxCount] = useState(''); const [isOnRamp, setOnRamp] = useState(false); const [isActive, setIsActive] = useState(true); - const [swapConfig, setSwapConfig] = useState(false); + const [, setSwapConfig] = useState(false); const [showMoveBoard, setMoveBoard] = useState(false); const [buyHover, setBuyHover] = useState(false); const [sendHover, setSendHover] = useState(false); @@ -81,9 +74,8 @@ const WalletTab = ({ network }) => { const [childStateLoading, setChildStateLoading] = useState(false); const [lastManualAddressCallTime, setlastManualAddressCallTime] = useState(0); - const [incLink, _] = useState( - network === 'mainnet' ? 'https://app.increment.fi/swap' : 'https://demo.increment.fi/swap' - ); + const incLink = + network === 'mainnet' ? 'https://app.increment.fi/swap' : 'https://demo.increment.fi/swap'; const expiry_time = 60000; @@ -95,7 +87,7 @@ const WalletTab = ({ network }) => { setValue(index); }; - const setUserAddress = async () => { + const setUserAddress = useCallback(async () => { let data = ''; try { if (childType === 'evm') { @@ -119,99 +111,130 @@ const WalletTab = ({ network }) => { console.error('Error getting manual address:', error); } } else { + // eslint-disable-next-line no-console console.log('Skipped calling getManualAddress to prevent frequent calls'); } } return data; - }; + }, [childType, lastManualAddressCallTime, wallet]); //todo: move to util const pollingFunction = (func, time = 1000, endTime, immediate = false) => { - immediate && func(); + if (immediate) { + func(); + } const startTime = new Date().getTime(); const pollTimer = setInterval(async () => { const nowTime = new Date().getTime(); const data = await func(); if (data.length > 2 || nowTime - startTime >= endTime) { - pollTimer && clearInterval(pollTimer); + if (pollTimer) { + clearInterval(pollTimer); + } eventBus.emit('addressDone'); } }, time); return pollTimer; }; - const refreshWithRetry = async (expiry_time, retryCount = 0, delay = 2000, maxRetries = 5) => { - if (childStateLoading) { - console.log('childStateLoading.'); - return; - } - try { - const refreshedCoinlist = await wallet.refreshCoinList(expiry_time); - console.log(`refreshedCoinlist fetching price for token:`, refreshedCoinlist); - - if ( - Array.isArray(refreshedCoinlist) && - refreshedCoinlist.length === 0 && - retryCount < maxRetries - ) { - console.log( - `No data found, retrying in 5 seconds... (Attempt ${retryCount + 1} of ${maxRetries})` + const handleStorageData = useCallback( + async (storageData) => { + if (storageData) { + const uniqueTokens = storageData.filter( + (token, index, self) => + index === self.findIndex((t) => t.unit.toLowerCase() === token.unit.toLowerCase()) ); - setTimeout(() => { - refreshWithRetry(expiry_time, retryCount + 1); - }, delay); - } else { - console.log(`refreshedCoinlist found, refreshedCoinlist`, refreshedCoinlist); - sortWallet(refreshedCoinlist); - } - } catch (error) { - console.error(`Error fetching price for token:`, error); - if (retryCount < maxRetries) { - console.log( - `Retrying refreshCoinList in ${delay / 1000} seconds... (Attempt ${ - retryCount + 1 - } of ${maxRetries})` - ); - setTimeout(() => { - refreshWithRetry(expiry_time, retryCount + 1); - }, delay); - } else { - wallet - .refreshCoinList(expiry_time) - .then((res) => { - if (Array.isArray(res) && res.length === 0 && retryCount < maxRetries) { - console.log( - `No data found in storage, retrying in 5 seconds... (Attempt ${ - retryCount + 1 - } of ${maxRetries})` - ); - setTimeout(() => { - refreshWithRetry(expiry_time, retryCount + 1); - }, delay); - } else { - console.log(`res found, refreshedCoinlist`, res); - sortWallet(res); - } - }) - .catch((err) => { - console.error(`Error getting CoinList price for token:`, err); + await setCoinData(uniqueTokens); + let sum = 0; + storageData + .filter((item) => item.total !== null) + .forEach((coin) => { + sum = sum + parseFloat(coin.total); }); + setBalance('$ ' + sum.toFixed(2)); } - } - }; + }, + [setCoinData, setBalance] + ); - const sortWallet = (data) => { - const sorted = data.sort((a, b) => { - if (b.total === a.total) { - return b.balance - a.balance; - } else { - return b.total - a.total; + const sortWallet = useCallback( + (data) => { + const sorted = data.sort((a, b) => { + if (b.total === a.total) { + return b.balance - a.balance; + } else { + return b.total - a.total; + } + }); + handleStorageData(sorted); + }, + [handleStorageData] + ); + + const refreshWithRetry = useCallback( + async (expiry_time, retryCount = 0, delay = 2000, maxRetries = 5) => { + if (childStateLoading) { + console.log('childStateLoading.'); + return; } - }); - handleStorageData(sorted); - }; + try { + const refreshedCoinlist = await wallet.refreshCoinList(expiry_time); + console.log(`refreshedCoinlist fetching price for token:`, refreshedCoinlist); + + if ( + Array.isArray(refreshedCoinlist) && + refreshedCoinlist.length === 0 && + retryCount < maxRetries + ) { + console.log( + `No data found, retrying in 5 seconds... (Attempt ${retryCount + 1} of ${maxRetries})` + ); + setTimeout(() => { + refreshWithRetry(expiry_time, retryCount + 1); + }, delay); + } else { + console.log(`refreshedCoinlist found, refreshedCoinlist`, refreshedCoinlist); + sortWallet(refreshedCoinlist); + } + } catch (error) { + console.error(`Error fetching price for token:`, error); + if (retryCount < maxRetries) { + console.log( + `Retrying refreshCoinList in ${delay / 1000} seconds... (Attempt ${ + retryCount + 1 + } of ${maxRetries})` + ); + setTimeout(() => { + refreshWithRetry(expiry_time, retryCount + 1); + }, delay); + } else { + wallet + .refreshCoinList(expiry_time) + .then((res) => { + if (Array.isArray(res) && res.length === 0 && retryCount < maxRetries) { + console.log( + `No data found in storage, retrying in 5 seconds... (Attempt ${ + retryCount + 1 + } of ${maxRetries})` + ); + setTimeout(() => { + refreshWithRetry(expiry_time, retryCount + 1); + }, delay); + } else { + console.log(`res found, refreshedCoinlist`, res); + sortWallet(res); + } + }) + .catch((err) => { + console.error(`Error getting CoinList price for token:`, err); + }); + } + } + }, + [childStateLoading, sortWallet, wallet] + ); - const fetchWallet = async () => { + const fetchWallet = useCallback(async () => { // If childType is 'evm', handle it first const isChild = await wallet.getActiveWallet(); if (isChild === 'evm') { @@ -237,49 +260,32 @@ const WalletTab = ({ network }) => { } else { sortWallet(refreshedCoinlist); } - } catch (error) { + } catch { refreshWithRetry(expiry_time); } - }; + }, [address, isActive, refreshWithRetry, sortWallet, wallet]); - const loadCache = async () => { + const loadCache = useCallback(async () => { const storageSwap = await wallet.getSwapConfig(); setSwapConfig(storageSwap); const storageData = await wallet.getCoinList(expiry_time); sortWallet(storageData); - }; - - const handleStorageData = async (storageData) => { - if (storageData) { - const uniqueTokens = storageData.filter( - (token, index, self) => - index === self.findIndex((t) => t.unit.toLowerCase() === token.unit.toLowerCase()) - ); - await setCoinData(uniqueTokens); - let sum = 0; - storageData - .filter((item) => item.total !== null) - .forEach((coin) => { - sum = sum + parseFloat(coin.total); - }); - setBalance('$ ' + sum.toFixed(2)); - } - }; + }, [expiry_time, sortWallet, wallet]); - const fetchChildState = async () => { + const fetchChildState = useCallback(async () => { setChildStateLoading(true); const isChild = await wallet.getActiveWallet(); const childresp = await wallet.checkUserChildAccount(); setChildAccount(childresp); setChildType(isChild); if (isChild && isChild !== 'evm') { - await setIsActive(false); + setIsActive(false); } else { setIsActive(true); } setChildStateLoading(false); return isChild; - }; + }, [wallet]); useEffect(() => { fetchChildState(); @@ -292,7 +298,7 @@ const WalletTab = ({ network }) => { return function cleanup() { clearInterval(pollTimer); }; - }, []); + }, [fetchChildState, location.search, setUserAddress]); const goMoveBoard = async () => { setMoveBoard(true); @@ -318,11 +324,11 @@ const WalletTab = ({ network }) => { setCoinLoading(false); fetchWallet(); } - }, [address]); + }, [address, fetchWallet, loadCache]); useEffect(() => { setUserAddress(); - }, [childType]); + }, [childType, setUserAddress]); useEffect(() => { const checkPermission = async () => { @@ -331,6 +337,17 @@ const WalletTab = ({ network }) => { }; checkPermission(); + }, [wallet]); + + useEffect(() => { + // Add event listener for opening onramp + const onRampHandler = () => setOnRamp(true); + eventBus.addEventListener('openOnRamp', onRampHandler); + + // Clean up listener + return () => { + eventBus.removeEventListener('openOnRamp', onRampHandler); + }; }, []); return ( @@ -384,15 +401,7 @@ const WalletTab = ({ network }) => { /> */} {`$${formatLargeNumber(balance)}`.split('').map((n, i) => ( - + ))} )} diff --git a/src/ui/views/WelcomePage/index.tsx b/src/ui/views/WelcomePage/index.tsx index 819aa813..1dc94972 100644 --- a/src/ui/views/WelcomePage/index.tsx +++ b/src/ui/views/WelcomePage/index.tsx @@ -1,19 +1,18 @@ +import { Typography, Button, CardMedia } from '@mui/material'; +import { Box } from '@mui/system'; import React from 'react'; -import { Box, ThemeProvider } from '@mui/system'; -import { Typography, Button, CssBaseline, CardMedia } from '@mui/material'; -import theme from '../../style/LLTheme'; -import RegisterHeader from '../Register/RegisterHeader'; +import { Link } from 'react-router-dom'; + +import IconFlow from '../../../components/iconfont/IconFlow'; import appicon from '../../FRWAssets/image/appicon.png'; import create from '../../FRWAssets/svg/create.svg'; import importPng from '../../FRWAssets/svg/import.svg'; import qr from '../../FRWAssets/svg/scanIcon.svg'; -import { Link } from 'react-router-dom'; -import IconFlow from '../../../components/iconfont/IconFlow'; +import RegisterHeader from '../Register/RegisterHeader'; const WelcomePage = () => { return ( - - + <> { - + ); }; diff --git a/src/ui/views/index.tsx b/src/ui/views/index.tsx index f322c456..75c1d21e 100644 --- a/src/ui/views/index.tsx +++ b/src/ui/views/index.tsx @@ -1,25 +1,23 @@ +import { CssBaseline } from '@mui/material'; import GlobalStyles from '@mui/material/GlobalStyles'; -import { ThemeProvider } from '@mui/material/styles'; -import { createMemoryHistory } from 'history'; -import React, { lazy, Suspense } from 'react'; +import { createTheme, ThemeProvider } from '@mui/material/styles'; +import React from 'react'; import { HashRouter as Router, Route } from 'react-router-dom'; +import themeOptions from '@/ui/style/LLTheme'; import { NewsProvider } from '@/ui/utils/NewsContext'; import { PrivateRoute } from 'ui/component'; import { WalletProvider, mixpanelBrowserService } from 'ui/utils'; -import theme from '../style/LLTheme'; - import Approval from './Approval'; import InnerRoute from './InnerRoute'; +import { MainRoute } from './MainRoute'; import RetrievePK from './RetrievePK'; import SortHat from './SortHat'; import SwitchUnlock from './SwitchUnlock'; import Unlock from './Unlock'; -const AsyncMainRoute = lazy(() => import('./MainRoute')); - -// import Reset from './Reset'; +const theme = createTheme(themeOptions); function Main() { React.useEffect(() => { @@ -30,7 +28,7 @@ function Main() { return ( //@ts-ignore - + @@ -38,9 +36,7 @@ function Main() { - - - + @@ -52,8 +48,11 @@ function Main() { } const App = ({ wallet }: { wallet: any }) => { + console.log('Theme:', theme); // Add this to debug + return ( +