diff --git a/src/app/common/utils.ts b/src/app/common/utils.ts
index b5e67bd371..e20c1f6cf3 100644
--- a/src/app/common/utils.ts
+++ b/src/app/common/utils.ts
@@ -1,5 +1,3 @@
-import type { ClipboardEvent } from 'react';
-
import { hexToBytes } from '@stacks/common';
import {
BytesReader,
@@ -42,11 +40,6 @@ export function extractPhraseFromString(value: string) {
}
}
-export function extractPhraseFromPasteEvent(event: ClipboardEvent) {
- const pasted = event.clipboardData.getData('Text');
- return extractPhraseFromString(pasted);
-}
-
interface MakeTxExplorerLinkArgs {
blockchain: Blockchains;
mode: BitcoinNetworkModes;
diff --git a/src/app/pages/onboarding/sign-in/hooks/use-sign-in.ts b/src/app/pages/onboarding/sign-in/hooks/use-sign-in.ts
index 6c77056dba..eb08fb3207 100644
--- a/src/app/pages/onboarding/sign-in/hooks/use-sign-in.ts
+++ b/src/app/pages/onboarding/sign-in/hooks/use-sign-in.ts
@@ -7,7 +7,7 @@ import { RouteUrls } from '@shared/route-urls';
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { useLoading } from '@app/common/hooks/use-loading';
-import { delay, extractPhraseFromPasteEvent } from '@app/common/utils';
+import { delay } from '@app/common/utils';
import { useAppDispatch } from '@app/store';
import { inMemoryKeyActions } from '@app/store/in-memory-key/in-memory-key.actions';
import { onboardingActions } from '@app/store/onboarding/onboarding.actions';
@@ -67,14 +67,6 @@ export function useSignIn() {
[setIsLoading, dispatch, analytics, navigate, setIsIdle, handleSetError]
);
- const onPaste = useCallback(
- async (event: React.ClipboardEvent) => {
- const value = extractPhraseFromPasteEvent(event);
- await submitMnemonicForm(value);
- },
- [submitMnemonicForm]
- );
-
const toggleKeyMask = useCallback(() => {
setIsKeyMasked(prev => !prev);
}, []);
@@ -90,7 +82,6 @@ export function useSignIn() {
);
return {
- onPaste,
submitMnemonicForm,
ref: textAreaRef,
error,
diff --git a/src/app/pages/onboarding/sign-in/sign-in.tsx b/src/app/pages/onboarding/sign-in/sign-in.tsx
index 65c861f89f..195276a829 100644
--- a/src/app/pages/onboarding/sign-in/sign-in.tsx
+++ b/src/app/pages/onboarding/sign-in/sign-in.tsx
@@ -1,124 +1,187 @@
-import { FiEye, FiEyeOff } from 'react-icons/fi';
+import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import YourSecretKey from '@assets/images/onboarding/your-secret-key.png';
-import { css } from '@emotion/react';
-import { Box, Input, Stack, Text, color, useMediaQuery } from '@stacks/ui';
+import { Box, Button, Flex, Grid, Input, Stack, Text, color, useMediaQuery } from '@stacks/ui';
import { OnboardingSelectors } from '@tests/selectors/onboarding.selectors';
-import { Form, Formik } from 'formik';
+import { useFocus } from 'use-events';
import { RouteUrls } from '@shared/route-urls';
import { useRouteHeader } from '@app/common/hooks/use-route-header';
+import { createNullArrayOfLength, extractPhraseFromString } from '@app/common/utils';
import { CenteredPageContainer } from '@app/components/centered-page-container';
import { ErrorLabel } from '@app/components/error-label';
-import {
- CENTERED_FULL_PAGE_MAX_WIDTH,
- DESKTOP_VIEWPORT_MIN_WIDTH,
-} from '@app/components/global-styles/full-page-styles';
+import { DESKTOP_VIEWPORT_MIN_WIDTH } from '@app/components/global-styles/full-page-styles';
import { Header } from '@app/components/header';
-import { Link } from '@app/components/link';
import { PageTitle } from '@app/components/page-title';
import { PrimaryButton } from '@app/components/primary-button';
-import { Title } from '@app/components/typography';
+import { Caption, Title } from '@app/components/typography';
import { useSignIn } from '@app/pages/onboarding/sign-in/hooks/use-sign-in';
+interface MnemonicWordInputProps {
+ index: number;
+ value: string;
+ onUpdateWord(word: string): void;
+ onPasteEntireKey(word: string): void;
+}
+function MnemonicWordInput({
+ index,
+ value,
+ onUpdateWord,
+ onPasteEntireKey,
+}: MnemonicWordInputProps) {
+ const [isFocused, bind] = useFocus();
+
+ return (
+
+ {
+ const pasteValue = extractPhraseFromString(e.clipboardData.getData('text'));
+ if (pasteValue.includes(' ')) {
+ e.preventDefault();
+ //assume its a full key
+ onPasteEntireKey(pasteValue);
+ }
+ }}
+ onChange={(e: any) => {
+ e.preventDefault();
+ onUpdateWord(e.target.value);
+ }}
+ {...bind}
+ />
+
+ );
+}
+
export function SignIn() {
- const { onPaste, submitMnemonicForm, error, isLoading, ref, toggleKeyMask, isKeyMasked } =
- useSignIn();
+ const { submitMnemonicForm, error, isLoading } = useSignIn();
const navigate = useNavigate();
+ const [twentyFourWordMode, setTwentyFourWordMode] = useState(true);
+
const [desktopViewport] = useMediaQuery(`(min-width: ${DESKTOP_VIEWPORT_MIN_WIDTH})`);
useRouteHeader( navigate(RouteUrls.Onboarding)} hideActions />);
+ const [mnemonic, setMnemonic] = useState<(string | null)[]>(() => createNullArrayOfLength(24));
+
+ function mnemonicWordUpdate(index: number, word: string) {
+ const newMnemonic = [...mnemonic];
+ newMnemonic[index] = word;
+ setMnemonic(newMnemonic);
+ }
+
+ function updateEntireKey(key: string) {
+ const newKey = key.split(' ');
+ setMnemonic(newKey);
+ void submitMnemonicForm(key);
+ }
+
return (
- submitMnemonicForm(values.secretKey)}
+ {
+ e.preventDefault();
+ void submitMnemonicForm(mnemonic.join(' '));
+ }}
+ px={['loose', 'base-loose']}
+ spacing={['loose', 'extra-loose']}
+ textAlign={['left', 'center']}
>
- {form => (
-
+
+
+
+ {desktopViewport ? (
+ Sign in with your Secret Key
+ ) : (
+ <>
+ Sign in with Secret Key
+ >
)}
-
+
+
+ Enter your Secret Key to sign in with an existing wallet
+
+ Tip: You can paste in your entire Secret Key at once
+
+
+
+ {createNullArrayOfLength(twentyFourWordMode ? 24 : 12).map((_, i) => (
+ {
+ (document.activeElement as any)?.blur();
+ updateEntireKey(key);
+ }}
+ onUpdateWord={w => mnemonicWordUpdate(i, w)}
+ />
+ ))}
+
+
+
+ {error && (
+
+
+ {error}
+
+
+ )}
+
+ Continue
+
+
+
+
);
}
diff --git a/src/shared/route-urls.ts b/src/shared/route-urls.ts
index da61af372c..e751d83155 100644
--- a/src/shared/route-urls.ts
+++ b/src/shared/route-urls.ts
@@ -6,7 +6,6 @@ export enum RouteUrls {
BackUpSecretKey = '/back-up-secret-key',
SetPassword = '/set-password',
SignIn = '/sign-in',
- MagicRecoveryCode = '/recovery-code',
RequestDiagnostics = '/request-diagnostics',
// Ledger routes
diff --git a/tests-legacy/page-objects/wallet.page.ts b/tests-legacy/page-objects/wallet.page.ts
index 4900850424..b24fa9578b 100644
--- a/tests-legacy/page-objects/wallet.page.ts
+++ b/tests-legacy/page-objects/wallet.page.ts
@@ -51,7 +51,6 @@ export class WalletPage {
$signOutDeleteWalletBtn = createTestSelector(SettingsSelectors.BtnSignOutActuallyDeleteWallet);
$enterPasswordInput = createTestSelector(SettingsSelectors.EnterPasswordInput);
$unlockWalletBtn = createTestSelector(SettingsSelectors.UnlockWalletBtn);
- $magicRecoveryMessage = createTestSelector(WalletPageSelectors.MagicRecoveryMessage);
$hideStepsBtn = createTestSelector(OnboardingSelectors.HideStepsBtn);
$suggestedStepsList = createTestSelector(OnboardingSelectors.StepsList);
$suggestedStepStartBtn = createTestSelector(OnboardingSelectors.StepItemStart);
@@ -149,8 +148,10 @@ export class WalletPage {
}
async enterSecretKey(secretKey: string) {
- await this.page.waitForSelector('textarea');
- await this.page.fill('textarea', secretKey);
+ const key = secretKey.split(' ');
+ for (let i = 0; i < key.length; i++) {
+ await this.page.getByTestId(`mnemonic-input-${i}`).fill(key[i]);
+ }
await this.page.click(this.$buttonSignInKeyContinue);
}
@@ -205,10 +206,6 @@ export class WalletPage {
await this.page.click(this.$fundAccountBtn);
}
- async waitForMagicRecoveryMessage() {
- await this.page.waitForSelector(this.$magicRecoveryMessage, { timeout: 30000 });
- }
-
async waitForSendButton() {
await this.page.waitForSelector(this.$sendTokenBtn, { timeout: 30000 });
}
diff --git a/tests-legacy/page-objects/wallet.selectors.ts b/tests-legacy/page-objects/wallet.selectors.ts
index 220069ad2f..03fa378cbc 100644
--- a/tests-legacy/page-objects/wallet.selectors.ts
+++ b/tests-legacy/page-objects/wallet.selectors.ts
@@ -1,4 +1,3 @@
export enum WalletPageSelectors {
- MagicRecoveryMessage = 'magic-recovery-message',
StatusMessage = 'status-message',
}
diff --git a/tests/page-object-models/onboarding.page.ts b/tests/page-object-models/onboarding.page.ts
index d8a0fe0161..a4b7849218 100644
--- a/tests/page-object-models/onboarding.page.ts
+++ b/tests/page-object-models/onboarding.page.ts
@@ -127,7 +127,12 @@ export class OnboardingPage {
async signInExistingUser() {
await this.denyAnalytics();
await this.page.getByTestId(OnboardingSelectors.SignInLink).click();
- await this.page.getByTestId(OnboardingSelectors.SecretKeyInput).fill(TEST_SECRET_KEY);
+
+ const key = TEST_SECRET_KEY.split(' ');
+ for (let i = 0; i < key.length; i++) {
+ await this.page.getByTestId(`mnemonic-input-${i}`).fill(key[i]);
+ }
+
await this.page.getByTestId(OnboardingSelectors.SignInBtn).click();
await this.setPassword();
await this.page.waitForURL('**' + RouteUrls.Home);