diff --git a/src/components/dapp/dapp-approval-drawer.tsx b/src/components/dapp/dapp-approval-drawer.tsx index 9b6b816..c4c697c 100644 --- a/src/components/dapp/dapp-approval-drawer.tsx +++ b/src/components/dapp/dapp-approval-drawer.tsx @@ -35,6 +35,7 @@ import { import AddressLabel from '@/components/address-label' import { useTxTypeDescription } from '@/hooks/use-tx-type-description' import { isWalletLocked } from '@/lib/lock' +import IdnWarningBadge from '@/components/idn-warning-badge' import { validateVaultPassphrase } from '@/lib/vault' import { toast } from 'sonner' @@ -72,7 +73,9 @@ const DappApprovalDrawer = () => { useEffect(() => { const syncLockState = () => { - setLocked(isWalletLocked()) + const nowLocked = isWalletLocked() + if (nowLocked) setPassphrase('') + setLocked(nowLocked) } syncLockState() @@ -286,6 +289,7 @@ const DappApprovalDrawer = () => { {t('dapp.approval.origin')}

{current.origin}

+ {current.method === 'connect' && connectSummary && (
diff --git a/src/components/idn-warning-badge.tsx b/src/components/idn-warning-badge.tsx new file mode 100644 index 0000000..c737320 --- /dev/null +++ b/src/components/idn-warning-badge.tsx @@ -0,0 +1,18 @@ +import { TriangleAlertIcon } from 'lucide-react' +import { useTranslation } from 'react-i18next' +import { hasIdnHostname } from '@/lib/utils' + +type Props = { origin: string } + +const IdnWarningBadge = ({ origin }: Props) => { + const { t } = useTranslation() + if (!hasIdnHostname(origin)) return null + return ( + + + {t('dapp.idnWarning')} + + ) +} + +export default IdnWarningBadge diff --git a/src/lib/utils.ts b/src/lib/utils.ts index f4f9db5..cc3f273 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -149,6 +149,15 @@ export const formatAddressLabel = ( return `${name} (${truncated})` } +export const hasIdnHostname = (origin: string): boolean => { + try { + const { hostname } = new URL(origin) + return hostname.includes('xn--') || /[^a-z0-9.-]/i.test(hostname) + } catch { + return false + } +} + export type ExplorerObject = 'tx' export const compareBigIntDesc = (a: string, b: string): number => { diff --git a/src/locales/en.json b/src/locales/en.json index be160e0..154aa88 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -471,6 +471,7 @@ "importVault": "Import vault backup" }, "dapp": { + "idnWarning": "Internationalized domain", "approval": { "title": "Connection request", "connectTitle": "Connection request", diff --git a/src/pages/manage-accounts.tsx b/src/pages/manage-accounts.tsx index 823c6ba..2ed267b 100644 --- a/src/pages/manage-accounts.tsx +++ b/src/pages/manage-accounts.tsx @@ -22,6 +22,7 @@ import { repairDuplicateVaultEntries, setOnboarded, } from '@/lib/vault' +import { isWalletLocked } from '@/lib/lock' import AccountListItem from '@/components/pages/manage-accounts/account-list-item' import AddAccountDrawer from '@/components/pages/manage-accounts/add-account-drawer' import RenameAccountDrawer from '@/components/pages/manage-accounts/rename-account-drawer' @@ -126,6 +127,19 @@ const ManageAccounts = () => { } }, [refreshFromCache]) + useEffect(() => { + const onLock = () => { + if (!isWalletLocked()) return + setRevealedSeed('') + setSeedTarget(null) + setPassphraseInput('') + setPassphrasePromptOpen(false) + setPendingAction(null) + } + window.addEventListener('wallet-lock-updated', onLock) + return () => window.removeEventListener('wallet-lock-updated', onLock) + }, []) + const balanceQueries = useQueries({ queries: orderedAccounts.map((account) => ({ queryKey: ['qubic', 'balance', account.identity], diff --git a/src/pages/onboarding/create-wallet.tsx b/src/pages/onboarding/create-wallet.tsx index e19f06a..71f881b 100644 --- a/src/pages/onboarding/create-wallet.tsx +++ b/src/pages/onboarding/create-wallet.tsx @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next' import { Button } from '@/components/ui/button' import { generateSeed, isSeedLike } from '@/lib/seed' import { validatePassphraseStrength } from '@/lib/passphrase' -import { setUnlocked } from '@/lib/lock' +import { isWalletLocked, setUnlocked } from '@/lib/lock' import { openBrowserVault, setOnboarded, @@ -63,6 +63,18 @@ const CreateWallet = ({ setHasConfirmedSeedBackup(false) } + useEffect(() => { + const onLock = () => { + if (!isWalletLocked()) return + setSeed('') + setPassphrase('') + setConfirmPassphrase('') + setHasConfirmedSeedBackup(false) + } + window.addEventListener('wallet-lock-updated', onLock) + return () => window.removeEventListener('wallet-lock-updated', onLock) + }, []) + useEffect(() => { let isActive = true diff --git a/src/pages/onboarding/import-seed.tsx b/src/pages/onboarding/import-seed.tsx index 5da7e35..607346e 100644 --- a/src/pages/onboarding/import-seed.tsx +++ b/src/pages/onboarding/import-seed.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import { identityFromSeed } from '@qubic-labs/core' import { ArrowLeftIcon, ArrowRightIcon, KeyRoundIcon } from 'lucide-react' import { useNavigate } from 'react-router-dom' @@ -16,7 +16,7 @@ import { isAccountNameTaken, saveCachedAccounts, } from '@/lib/accounts' -import { setUnlocked } from '@/lib/lock' +import { isWalletLocked, setUnlocked } from '@/lib/lock' import { openBrowserVault, setOnboarded, @@ -73,6 +73,17 @@ const ImportSeed = ({ setDerivedIdentity(null) } + useEffect(() => { + const onLock = () => { + if (!isWalletLocked()) return + setSeed('') + setPassphrase('') + setDerivedIdentity(null) + } + window.addEventListener('wallet-lock-updated', onLock) + return () => window.removeEventListener('wallet-lock-updated', onLock) + }, []) + const handleSeedChange = (value: string) => { setSeed(normalizeSeedInput(value)) if (step === 1 && status) { diff --git a/src/pages/settings/connected-sites.tsx b/src/pages/settings/connected-sites.tsx index 630d275..382d6b1 100644 --- a/src/pages/settings/connected-sites.tsx +++ b/src/pages/settings/connected-sites.tsx @@ -18,6 +18,7 @@ import { } from '@/lib/dapp/storage' import { useAccountNames } from '@/hooks/use-account-names' import { truncateString } from '@/lib/utils' +import IdnWarningBadge from '@/components/idn-warning-badge' const ConnectedSites = () => { const { t } = useTranslation() @@ -87,6 +88,7 @@ const ConnectedSites = () => {

{site.origin}

+

{t('settings.connectedSites.connectedAt', { date: new Date(site.connectedAt).toLocaleString(),