diff --git a/packages/adena-extension/src/assets/web/confirm-check.svg b/packages/adena-extension/src/assets/web/confirm-check.svg
new file mode 100644
index 00000000..da9b1a9f
--- /dev/null
+++ b/packages/adena-extension/src/assets/web/confirm-check.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/adena-extension/src/common/constants/resource.constant.ts b/packages/adena-extension/src/common/constants/resource.constant.ts
index 329db438..2bd6a921 100644
--- a/packages/adena-extension/src/common/constants/resource.constant.ts
+++ b/packages/adena-extension/src/common/constants/resource.constant.ts
@@ -1,2 +1,3 @@
export const ADENA_TERMS_PAGE = 'https://adena.app/terms' as const;
export const ADENA_DOCS_PAGE = 'https://docs.adena.app' as const;
+export const GNO_CLI_HELP_PAGE = 'https://docs.onbloc.xyz/docs/cli' as const;
diff --git a/packages/adena-extension/src/components/atoms/rolling-number/index.tsx b/packages/adena-extension/src/components/atoms/rolling-number/index.tsx
new file mode 100644
index 00000000..07e6a3a8
--- /dev/null
+++ b/packages/adena-extension/src/components/atoms/rolling-number/index.tsx
@@ -0,0 +1,88 @@
+import { WebFontType } from '@styles/theme';
+import React, { useEffect, useState } from 'react';
+import styled, { css, FlattenSimpleInterpolation } from 'styled-components';
+import { View } from '../base';
+import { WebText } from '../web-text';
+
+export const StyledWrapper = styled(View) <{ active: boolean, height?: number }>`
+ width: fit-content;
+ height: ${({ height }): string => height ? `${height}px` : '1em'};
+ flex-direction: column;
+ overflow: hidden;
+
+ @keyframes rolling-animation {
+ from {
+ transform: translate(0, 0);
+ }
+
+ to {
+ transform: translate(0, -100%);
+ }
+ }
+
+ ${({ active }): FlattenSimpleInterpolation | string => active ? css`
+ & > * {
+ animation: rolling-animation 0.2s linear forwards;
+ }
+ ` : ''}
+`;
+
+
+export interface RollingNumberProps {
+ value: number;
+ height?: number;
+ type: WebFontType;
+ color?: string;
+ style?: React.CSSProperties;
+ textCenter?: boolean;
+}
+
+const RollingNumber: React.FC = ({
+ height,
+ value,
+ type,
+ color,
+ style,
+ textCenter,
+}) => {
+ const [currentValue, setCurrentValue] = useState(value);
+ const [animated, setAnimated] = useState(false);
+
+ useEffect(() => {
+ if (currentValue !== value) {
+ setAnimated(true);
+ }
+ }, [value]);
+
+ useEffect(() => {
+ if (animated) {
+ setTimeout(() => {
+ setAnimated(false);
+ setCurrentValue(value);
+ }, 200);
+ }
+ }, [animated]);
+
+ return (
+
+
+ {currentValue}
+
+
+ {value}
+
+
+ );
+};
+
+export default RollingNumber;
\ No newline at end of file
diff --git a/packages/adena-extension/src/components/atoms/rolling-number/rolling-number.spec.tsx b/packages/adena-extension/src/components/atoms/rolling-number/rolling-number.spec.tsx
new file mode 100644
index 00000000..6f70a878
--- /dev/null
+++ b/packages/adena-extension/src/components/atoms/rolling-number/rolling-number.spec.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import { RecoilRoot } from 'recoil';
+import { ThemeProvider } from 'styled-components';
+import { render } from '@testing-library/react';
+import theme from '@styles/theme';
+import { GlobalWebStyle } from '@styles/global-style';
+import RollingNumber from '.';
+
+describe('RollingNumber Component', () => {
+ it('RollingNumber render', () => {
+
+ render(
+
+
+
+
+
+ ,
+ );
+ });
+});
\ No newline at end of file
diff --git a/packages/adena-extension/src/components/atoms/rolling-number/rolling-number.stories.tsx b/packages/adena-extension/src/components/atoms/rolling-number/rolling-number.stories.tsx
new file mode 100644
index 00000000..d94e9eae
--- /dev/null
+++ b/packages/adena-extension/src/components/atoms/rolling-number/rolling-number.stories.tsx
@@ -0,0 +1,15 @@
+import { Meta, StoryObj } from '@storybook/react';
+import RollingNumber from '.';
+
+export default {
+ title: 'components/atoms/RollingNumber',
+ component: RollingNumber,
+} as Meta;
+
+export const Default: StoryObj = {
+ args: {
+ value: 3,
+ type: 'body6',
+ color: '#FBC224',
+ },
+};
\ No newline at end of file
diff --git a/packages/adena-extension/src/components/atoms/web-button/index.tsx b/packages/adena-extension/src/components/atoms/web-button/index.tsx
index f2d797d4..ffba7c79 100644
--- a/packages/adena-extension/src/components/atoms/web-button/index.tsx
+++ b/packages/adena-extension/src/components/atoms/web-button/index.tsx
@@ -1,7 +1,7 @@
import React, { ButtonHTMLAttributes, ReactElement, ReactNode } from 'react';
import styled from 'styled-components';
-import { WebFontType } from '@styles/theme';
+import { getTheme, WebFontType } from '@styles/theme';
import IconRight from '@assets/web/chevron-right.svg';
import { WebImg } from '../web-img';
@@ -13,18 +13,18 @@ type WebButtonProps = {
textType?: WebFontType;
figure: 'primary' | 'secondary' | 'tertiary' | 'quaternary';
} & (
- | { text: string; rightIcon?: 'chevronRight' }
- | {
+ | { text: string; rightIcon?: 'chevronRight' }
+ | {
children: ReactNode;
}
-) &
+ ) &
ButtonHTMLAttributes;
const StyledButtonBase = styled.button<{ size: 'full' | 'large' | 'small' }>`
cursor: pointer;
border: transparent;
border-radius: 14px;
- padding: ${({ size }): string => (size === 'large' ? '12px 16px 16px' : '8px 24px')};
+ padding: ${({ size }): string => (size === 'large' ? '12px 16px 16px' : '8px 16px')};
display: flex;
flex-direction: row;
width: ${({ size }): string => (size === 'full' ? '100%' : 'auto')};
@@ -37,10 +37,12 @@ const StyledButtonBase = styled.button<{ size: 'full' | 'large' | 'small' }>`
`;
const StyledButtonPrimary = styled(StyledButtonBase)`
+ color ${getTheme('webNeutral', '_100')};
outline: 1px solid rgba(255, 255, 255, 0.4);
background: linear-gradient(180deg, #0059ff 0%, #004bd6 100%);
:hover {
+ color ${getTheme('webNeutral', '_100')};
outline: 2px solid rgba(255, 255, 255, 0.4);
background: linear-gradient(180deg, #0059ff 0%, #004bd6 100%);
box-shadow: 0px 0px 24px 0px rgba(0, 89, 255, 0.32), 0px 1px 3px 0px rgba(0, 0, 0, 0.1),
@@ -48,6 +50,7 @@ const StyledButtonPrimary = styled(StyledButtonBase)`
}
:active {
+ color ${getTheme('webNeutral', '_100')};
outline: 2px solid rgba(255, 255, 255, 0.4);
background: linear-gradient(180deg, #0059ff 0%, #004bd6 100%);
box-shadow: 0px 0px 24px 0px rgba(0, 89, 255, 0.32), 0px 0px 0px 3px rgba(0, 89, 255, 0.16),
@@ -56,31 +59,37 @@ const StyledButtonPrimary = styled(StyledButtonBase)`
`;
const StyledButtonSecondary = styled(StyledButtonBase)`
+ color: #ADCAFF;
outline: 1px solid rgba(122, 169, 255, 0.24);
background: rgba(0, 89, 255, 0.16);
:hover {
+ color: #ADCAFF;
outline: 2px solid rgba(122, 169, 255, 0.24);
background: rgba(0, 89, 255, 0.2);
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.1), 0px 1px 2px 0px rgba(0, 0, 0, 0.06);
}
:active {
+ color: #7AA9FF;
outline: 2px solid rgba(122, 169, 255, 0.24);
background: rgba(0, 89, 255, 0.2);
}
`;
const StyledButtonTertiary = styled(StyledButtonBase)`
+ color ${getTheme('webNeutral', '_300')};
outline: 1px solid rgba(188, 197, 214, 0.16);
background: rgba(188, 197, 214, 0.04);
:hover {
+ color ${getTheme('webNeutral', '_300')};
outline: 2px solid rgba(188, 197, 214, 0.16);
background: rgba(188, 197, 214, 0.06);
}
:active {
+ color ${getTheme('webNeutral', '_100')};
outline: 1px solid rgba(188, 197, 214, 0.24);
background: rgba(188, 197, 214, 0.04);
box-shadow: 0px 0px 16px 0px rgba(255, 255, 255, 0.04),
diff --git a/packages/adena-extension/src/components/atoms/web-input/index.tsx b/packages/adena-extension/src/components/atoms/web-input/index.tsx
index acc852bb..0a92fd4c 100644
--- a/packages/adena-extension/src/components/atoms/web-input/index.tsx
+++ b/packages/adena-extension/src/components/atoms/web-input/index.tsx
@@ -15,12 +15,13 @@ export const WebInput = styled.input`
border: 1px solid;
padding: 12px 16px;
border-color: ${({ error, theme }): string => (error ? theme.webError._200 : theme.webNeutral._800)};
- background-color: ${({ error, theme }): string => (error ? theme.webError._300 : 'transparent')};
+ background-color: ${({ error, theme }): string => (error ? theme.webError._300 : theme.webNeutral._900)};
::placeholder {
color: ${getTheme('webNeutral', '_700')};
}
:focus {
+ background-color: ${({ error, theme }): string => (error ? theme.webError._300 : theme.webInput._100)};
box-shadow: 0px 0px 0px 3px rgba(255, 255, 255, 0.04), 0px 1px 3px 0px rgba(0, 0, 0, 0.1),
0px 1px 2px 0px rgba(0, 0, 0, 0.06);
}
diff --git a/packages/adena-extension/src/components/atoms/web-text/index.tsx b/packages/adena-extension/src/components/atoms/web-text/index.tsx
index 3ed3a022..95c73fde 100644
--- a/packages/adena-extension/src/components/atoms/web-text/index.tsx
+++ b/packages/adena-extension/src/components/atoms/web-text/index.tsx
@@ -1,18 +1,18 @@
-import React, { ReactElement } from 'react';
+import React, { CSSProperties, ReactElement } from 'react';
import styled, { FlattenSimpleInterpolation } from 'styled-components';
-import { WebFontType, getTheme, webFonts } from '@styles/theme';
+import { WebFontType, webFonts } from '@styles/theme';
type FormTextProps = {
type: WebFontType;
children: string | number;
- color?: string;
+ color?: CSSProperties['color'];
style?: React.CSSProperties;
textCenter?: boolean;
};
-const StyledContainer = styled.div<{ type: WebFontType }>`
- color: ${getTheme('webNeutral', '_100')};
+const StyledContainer = styled.div<{ type: WebFontType, color?: CSSProperties['color']; }>`
+ color: ${({ color }): CSSProperties['color'] => color ? color : '#FAFCFF'};
${({ type }): FlattenSimpleInterpolation => webFonts[type]}
white-space: pre-wrap;
`;
diff --git a/packages/adena-extension/src/components/molecules/terms-checkbox/index.tsx b/packages/adena-extension/src/components/molecules/terms-checkbox/index.tsx
index 39d33fd4..905f470a 100644
--- a/packages/adena-extension/src/components/molecules/terms-checkbox/index.tsx
+++ b/packages/adena-extension/src/components/molecules/terms-checkbox/index.tsx
@@ -29,7 +29,7 @@ const Wrapper = styled.div<{ margin?: string }>`
const Label = styled.label<{ checkboxPos: CheckboxPos }>`
${mixins.flex({ direction: 'row', justify: 'flex-start' })};
position: relative;
- padding-left: 32px;
+ padding-left: 28px;
cursor: pointer;
&:before {
${({ checkboxPos }): CSSProp =>
@@ -54,6 +54,7 @@ const Label = styled.label<{ checkboxPos: CheckboxPos }>`
`;
const Input = styled.input`
+ display: none;
&[type='checkbox'] {
width: 0px;
height: 0px;
diff --git a/packages/adena-extension/src/components/pages/web/loading-accounts/index.tsx b/packages/adena-extension/src/components/pages/web/loading-accounts/index.tsx
new file mode 100644
index 00000000..e41e5bff
--- /dev/null
+++ b/packages/adena-extension/src/components/pages/web/loading-accounts/index.tsx
@@ -0,0 +1,35 @@
+import { ReactElement } from 'react';
+import styled, { useTheme } from 'styled-components';
+
+import AnimationLoadingAccount from '@assets/web/loading-account-idle.gif';
+
+import { View, WebImg, WebText } from '@components/atoms';
+
+const StyledContainer = styled(View)`
+ row-gap: 24px;
+ align-items: center;
+`;
+
+const StyledMessageBox = styled(View)`
+ row-gap: 16px;
+`;
+
+const WebLoadingAccounts: React.FC = (): ReactElement => {
+ const theme = useTheme();
+
+ return (
+
+
+
+
+ Loading Accounts
+
+
+ We’re loading accounts. This will take a few seconds...
+
+
+
+ );
+};
+
+export default WebLoadingAccounts;
diff --git a/packages/adena-extension/src/components/pages/web/main-header/index.tsx b/packages/adena-extension/src/components/pages/web/main-header/index.tsx
index e7ea28dd..3fd65377 100644
--- a/packages/adena-extension/src/components/pages/web/main-header/index.tsx
+++ b/packages/adena-extension/src/components/pages/web/main-header/index.tsx
@@ -19,6 +19,10 @@ const StyledDot = styled(View) <{ selected: boolean }>`
selected ? theme.webPrimary._100 : 'rgba(0, 89, 255, 0.32)'};
`;
+const StyledEmpty = styled(View)`
+ width: 24px;
+`;
+
export type WebMainHeaderProps = {
stepLength: number;
onClickGoBack: () => void;
@@ -47,7 +51,7 @@ export const WebMainHeader = ({
))}
)}
-
+
);
};
diff --git a/packages/adena-extension/src/hooks/web/common/use-create-password-screen.ts b/packages/adena-extension/src/hooks/web/common/use-create-password-screen.ts
index 156e4621..4740783a 100644
--- a/packages/adena-extension/src/hooks/web/common/use-create-password-screen.ts
+++ b/packages/adena-extension/src/hooks/web/common/use-create-password-screen.ts
@@ -14,12 +14,15 @@ import { useAdenaContext } from '@hooks/use-context';
export type UseCreatePasswordScreenReturn = {
passwordState: {
value: string;
+ confirm: boolean;
onChange: (e: React.ChangeEvent) => void;
+ errorMessage: string;
error: boolean;
ref: React.RefObject;
};
confirmPasswordState: {
value: string;
+ confirm: boolean;
onChange: (e: React.ChangeEvent) => void;
error: boolean;
};
@@ -48,11 +51,19 @@ export const useCreatePasswordScreen = (): UseCreatePasswordScreenReturn => {
const [isConfirmPasswordError, setIsConfirmPasswordError] = useState(false);
const { password, confirmPassword } = inputs;
const [errorMessage, setErrorMessage] = useState('');
+ const [passwordErrorMessage, setPasswordErrorMessage] = useState('');
const disabledCreateButton = useMemo(() => {
return !terms || !password || !confirmPassword;
}, [terms, password, confirmPassword]);
+ const confirmPasswordLength = (password: string): boolean => {
+ if (password.length < 8 || password.length > 256) {
+ return false;
+ }
+ return true;
+ };
+
const _validateConfirmPassword = (validationPassword?: boolean): boolean => {
try {
if (validateNotMatchConfirmPassword(password, confirmPassword)) return true;
@@ -82,7 +93,7 @@ export const useCreatePasswordScreen = (): UseCreatePasswordScreenReturn => {
console.log(error);
setIsPasswordError(true);
if (error instanceof PasswordValidationError) {
- setErrorMessage(error.message);
+ setPasswordErrorMessage(error.message);
}
}
return false;
@@ -148,6 +159,7 @@ export const useCreatePasswordScreen = (): UseCreatePasswordScreenReturn => {
setIsPasswordError(false);
setIsConfirmPasswordError(false);
setErrorMessage('');
+ setPasswordErrorMessage('');
}, [password, confirmPassword]);
useEffect(() => {
@@ -159,12 +171,15 @@ export const useCreatePasswordScreen = (): UseCreatePasswordScreenReturn => {
return {
passwordState: {
value: password,
+ confirm: confirmPasswordLength(password),
onChange: onChangePassword,
error: isPasswordError,
+ errorMessage: passwordErrorMessage,
ref: inputRef,
},
confirmPasswordState: {
value: confirmPassword,
+ confirm: confirmPasswordLength(confirmPassword),
onChange: onChangePassword,
error: isConfirmPasswordError,
},
diff --git a/packages/adena-extension/src/hooks/web/questionnaire/use-questionnaire-screen.ts b/packages/adena-extension/src/hooks/web/questionnaire/use-questionnaire-screen.ts
index c82ae437..2747efe0 100644
--- a/packages/adena-extension/src/hooks/web/questionnaire/use-questionnaire-screen.ts
+++ b/packages/adena-extension/src/hooks/web/questionnaire/use-questionnaire-screen.ts
@@ -38,7 +38,7 @@ export const questionnaireStep: Record<
};
const useQuestionnaireScreen = (): UseQuestionnaireScreenReturn => {
- const { navigate, params } = useAppNavigate();
+ const { navigate, goBack, params } = useAppNavigate();
const { doneQuestionnaire } = useQuestionnaire();
const [questionnaireState, setQuestionnaireState] = useState('INIT');
const [questionIndex, setQuestionIndex] = useState(1);
@@ -78,8 +78,12 @@ const useQuestionnaireScreen = (): UseQuestionnaireScreenReturn => {
navigate(callbackPath, { state: { doneQuestionnaire: false } });
return;
}
- if (questionnaireState === 'QUESTION' && questionIndex > 1) {
- setQuestionIndex(questionIndex - 1);
+ if (questionnaireState === 'QUESTION') {
+ if (questionIndex > 1) {
+ setQuestionIndex(questionIndex - 1);
+ } else {
+ goBack();
+ }
return;
}
setQuestionIndex(1);
diff --git a/packages/adena-extension/src/hooks/web/setup-airgap/use-setup-airgap-screen.ts b/packages/adena-extension/src/hooks/web/setup-airgap/use-setup-airgap-screen.ts
index 16164be1..6ef138b1 100644
--- a/packages/adena-extension/src/hooks/web/setup-airgap/use-setup-airgap-screen.ts
+++ b/packages/adena-extension/src/hooks/web/setup-airgap/use-setup-airgap-screen.ts
@@ -6,6 +6,7 @@ import useAppNavigate from '@hooks/use-app-navigate';
import { useAdenaContext, useWalletContext } from '@hooks/use-context';
import { useCurrentAccount } from '@hooks/use-current-account';
import { RoutePath } from '@types';
+import { useLoadAccounts } from '@hooks/use-load-accounts';
export type UseSetupAirgapScreenReturn = {
address: string;
@@ -45,6 +46,7 @@ const useSetupAirgapScreen = (): UseSetupAirgapScreenReturn => {
const { navigate } = useAppNavigate();
const { updateWallet } = useWalletContext();
const { walletService } = useAdenaContext();
+ const { accounts } = useLoadAccounts();
const [setupAirgapState, setSetupAirgapState] = useState('INIT');
const [address, setAddress] = useState('');
const [errorMessage, setErrorMessage] = useState(null);
@@ -72,12 +74,24 @@ const useSetupAirgapScreen = (): UseSetupAirgapScreenReturn => {
return false;
}, [address]);
- const confirmAddress = useCallback(() => {
+ const _existsAddress = useCallback(async () => {
+ return Promise.all(accounts.map((account) => account.getAddress('g'))).then((addresses) =>
+ addresses.includes(address),
+ );
+ }, [accounts, address]);
+
+ const confirmAddress = useCallback(async () => {
if (!_validateAddress()) {
const errorMessage = 'Invalid address';
setErrorMessage(errorMessage);
return;
}
+ const existAddress = await _existsAddress();
+ if (existAddress) {
+ const errorMessage = 'Address already synced';
+ setErrorMessage(errorMessage);
+ return;
+ }
setSetupAirgapState('COMPLETE');
}, [_validateAddress]);
diff --git a/packages/adena-extension/src/hooks/web/use-account-import-screen.ts b/packages/adena-extension/src/hooks/web/use-account-import-screen.ts
index d1823867..54cdfd45 100644
--- a/packages/adena-extension/src/hooks/web/use-account-import-screen.ts
+++ b/packages/adena-extension/src/hooks/web/use-account-import-screen.ts
@@ -1,9 +1,8 @@
-import { useCallback, useState } from 'react';
-import { Wallet, PrivateKeyKeyring, SingleAccount } from 'adena-module';
+import { useCallback, useMemo, useState } from 'react';
+import { Wallet, PrivateKeyKeyring, SingleAccount, Account, Keyring } from 'adena-module';
import { RoutePath } from '@types';
import useAppNavigate from '@hooks/use-app-navigate';
-import { useQuery } from '@tanstack/react-query';
import { useWalletContext } from '@hooks/use-context';
import { useCurrentAccount } from '@hooks/use-current-account';
import useQuestionnaire from './use-questionnaire';
@@ -12,19 +11,20 @@ export type UseAccountImportReturn = {
isValidForm: boolean;
errMsg: string;
privateKey: string;
- setPrivateKey: React.Dispatch>;
+ setPrivateKey: (privateKey: string) => void;
step: AccountImportStateType;
setStep: React.Dispatch>;
accountImportStepNo: {
INIT: number;
SET_PRIVATE_KEY: number;
+ LOADING: number;
};
stepLength: number;
onClickGoBack: () => void;
onClickNext: () => void;
};
-export type AccountImportStateType = 'INIT' | 'SET_PRIVATE_KEY';
+export type AccountImportStateType = 'INIT' | 'SET_PRIVATE_KEY' | 'LOADING';
const useAccountImportScreen = ({ wallet }: { wallet: Wallet }): UseAccountImportReturn => {
const { navigate, params } = useAppNavigate();
@@ -39,43 +39,50 @@ const useAccountImportScreen = ({ wallet }: { wallet: Wallet }): UseAccountImpor
const [privateKey, setPrivateKey] = useState('');
const [errMsg, setErrMsg] = useState('');
- const { data: keyring } = useQuery(
- ['keyring', privateKey],
- async () => {
- setErrMsg('');
- if (privateKey) {
- const _privateKey = privateKey.replace('0x', '');
- const regExp = /[0-9A-Fa-f]{64}/g;
- if (_privateKey.length !== 64 || !_privateKey.match(regExp)) {
- setErrMsg('Invalid private key');
- return;
- }
- const _keyring = await PrivateKeyKeyring.fromPrivateKeyStr(privateKey);
- const account = await SingleAccount.createBy(_keyring, wallet.nextAccountName);
- const storedAccount = wallet.accounts.find(
- (_account) => JSON.stringify(_account.publicKey) === JSON.stringify(account.publicKey),
- );
- if (storedAccount) {
- setErrMsg('Private key already registered');
- return;
- }
-
- return _keyring;
- }
- },
- {
- enabled: !!privateKey,
- },
- );
-
- const isValidForm = !!privateKey && !!keyring && !errMsg;
-
const stepLength = ableToSkipQuestionnaire ? 3 : 4;
const accountImportStepNo = {
INIT: 0,
SET_PRIVATE_KEY: ableToSkipQuestionnaire ? 1 : 2,
+ LOADING: ableToSkipQuestionnaire ? 1 : 2,
};
+ const isValidForm = useMemo(() => {
+ return !!privateKey || !errMsg;
+ }, [privateKey]);
+
+ const changePrivateKey = useCallback((privateKey: string) => {
+ setErrMsg('');
+ setPrivateKey(privateKey);
+ }, []);
+
+ const makePrivateKeyAccountAndKeyring = useCallback(async (): Promise<{
+ account: Account;
+ keyring: Keyring;
+ } | null> => {
+ setErrMsg('');
+ if (!privateKey) {
+ return null;
+ }
+
+ const keyring = await PrivateKeyKeyring.fromPrivateKeyStr(privateKey).catch(() => null);
+ if (keyring === null) {
+ setErrMsg('Invalid private key');
+ return null;
+ }
+
+ const account = await SingleAccount.createBy(keyring, wallet.nextAccountName);
+ const address = await account.getAddress('g');
+ const storedAddresses = await Promise.all(
+ wallet.accounts.map((account) => account.getAddress('g')),
+ );
+ const existAddress = storedAddresses.includes(address);
+ if (existAddress) {
+ setErrMsg('Private key already registered');
+ return null;
+ }
+ return { account, keyring };
+ }, [wallet, privateKey]);
+
const onClickGoBack = useCallback(() => {
if (step === 'INIT') {
navigate(RoutePath.WebAdvancedOption);
@@ -95,9 +102,13 @@ const useAccountImportScreen = ({ wallet }: { wallet: Wallet }): UseAccountImpor
},
});
}
- } else if (step === 'SET_PRIVATE_KEY' && keyring) {
- const account = await SingleAccount.createBy(keyring, wallet.nextAccountName);
-
+ } else if (step === 'SET_PRIVATE_KEY') {
+ const result = await makePrivateKeyAccountAndKeyring();
+ if (!result) {
+ return;
+ }
+ setStep('LOADING');
+ const { account, keyring } = result;
account.index = wallet.lastAccountIndex + 1;
account.name = `Account ${account.index}`;
const clone = wallet.clone();
@@ -108,13 +119,13 @@ const useAccountImportScreen = ({ wallet }: { wallet: Wallet }): UseAccountImpor
await changeCurrentAccount(account);
navigate(RoutePath.WebAccountAddedComplete);
}
- }, [step, privateKey, ableToSkipQuestionnaire, keyring]);
+ }, [step, privateKey, ableToSkipQuestionnaire, makePrivateKeyAccountAndKeyring]);
return {
isValidForm,
errMsg,
privateKey,
- setPrivateKey,
+ setPrivateKey: changePrivateKey,
step,
setStep,
accountImportStepNo,
diff --git a/packages/adena-extension/src/pages/web/account-import-screen/index.tsx b/packages/adena-extension/src/pages/web/account-import-screen/index.tsx
index f17d028e..cb1bb4c4 100644
--- a/packages/adena-extension/src/pages/web/account-import-screen/index.tsx
+++ b/packages/adena-extension/src/pages/web/account-import-screen/index.tsx
@@ -11,12 +11,17 @@ import useAppNavigate from '@hooks/use-app-navigate';
import { RoutePath } from '@types';
import SensitiveInfoStep from '@components/pages/web/sensitive-info-step';
import { ADENA_DOCS_PAGE } from '@common/constants/resource.constant';
+import WebLoadingAccounts from '@components/pages/web/loading-accounts';
const HasWallet = ({ wallet }: { wallet: Wallet }): ReactElement => {
const useAccountImportScreenReturn = useAccountImportScreen({ wallet });
const { step, onClickGoBack, stepLength, accountImportStepNo, onClickNext } =
useAccountImportScreenReturn;
+ if (step === 'LOADING') {
+ return ;
+ }
+
return (
<>
`
+const StyledItem = styled(Row) <{ error: boolean }>`
position: relative;
overflow: hidden;
height: 40px;
@@ -66,7 +66,7 @@ const SetPrivateKeyStep = ({
- Enter a seed phrase or your private key to import your existing wallet.
+ Enter a private key to import your existing wallet.
@@ -75,6 +75,7 @@ const SetPrivateKeyStep = ({
{
setPrivateKey(value);
}}
diff --git a/packages/adena-extension/src/pages/web/advanced-option-screen/index.tsx b/packages/adena-extension/src/pages/web/advanced-option-screen/index.tsx
index 2cc3d88c..28135160 100644
--- a/packages/adena-extension/src/pages/web/advanced-option-screen/index.tsx
+++ b/packages/adena-extension/src/pages/web/advanced-option-screen/index.tsx
@@ -58,9 +58,9 @@ const AdvancedOptionScreen = (): ReactElement => {
}}
style={{ width: 176 }}
>
-
+
- Create New Wallet
+ Create New Wallet
{
}
}}
>
-
+
- Import Existing Wallet
+ Import Existing Wallet
{
navigate(RoutePath.WebGoogleLogin);
}}
>
-
+
- Sign In With Google
+ Sign In With Google
diff --git a/packages/adena-extension/src/pages/web/create-password-screen/index.tsx b/packages/adena-extension/src/pages/web/create-password-screen/index.tsx
index 33b1139c..363bac5d 100644
--- a/packages/adena-extension/src/pages/web/create-password-screen/index.tsx
+++ b/packages/adena-extension/src/pages/web/create-password-screen/index.tsx
@@ -3,12 +3,14 @@ import styled, { useTheme } from 'styled-components';
import {
WebInput,
- ErrorText,
WebMain,
View,
WebText,
Pressable,
WebButton,
+ Row,
+ WebImg,
+ WebErrorText,
} from '@components/atoms';
import { TermsCheckbox } from '@components/molecules';
import { WebMainHeader } from '@components/pages/web/main-header';
@@ -18,13 +20,32 @@ import useLink from '@hooks/use-link';
import useAppNavigate from '@hooks/use-app-navigate';
import { RoutePath } from '@types';
+import IconConfirm from '@assets/web/confirm-check.svg';
+
+const StyledContainer = styled(View)`
+ height: 330px;
+ row-gap: 24px;
+ align-items: flex-start;
+`;
+
const StyledMessageBox = styled(View)`
+ width: 100%;
row-gap: 16px;
`;
-const StyledInputBox = styled(View)`
+const StyledInputContainer = styled(View)`
row-gap: 16px;
- width: 384px;
+ width: 100%;
+`;
+
+const StyledInputBox = styled(View)`
+ row-gap: 12px;
+ width: 100%;
+`;
+
+const StyledInputWrapper = styled(Row)`
+ width: 416px;
+ gap: 12px;
`;
const CreatePasswordScreen = (): JSX.Element => {
@@ -39,61 +60,77 @@ const CreatePasswordScreen = (): JSX.Element => {
}, [openLink]);
return (
-
+
-
- Create a password
-
- This will be used to unlock your wallet.
-
-
-
-
-
-
+
+ Create a password
+
+ This will be used to unlock your wallet.
+
+
+
+
+
+
+
+ {passwordState.confirm && ()}
+
+ {passwordState.errorMessage && }
+
+
+
+
+
+ {confirmPasswordState.confirm && ()}
+
+ {errorMessage && }
+
+
+
+
+
+ Terms of Use.
+
+
+
+
- {errorMessage && }
-
-
-
-
- Terms of Use.
-
-
-
-
+
);
};
diff --git a/packages/adena-extension/src/pages/web/google-login-screen/request-fail.tsx b/packages/adena-extension/src/pages/web/google-login-screen/request-fail.tsx
index a17d01b9..60e23c5a 100644
--- a/packages/adena-extension/src/pages/web/google-login-screen/request-fail.tsx
+++ b/packages/adena-extension/src/pages/web/google-login-screen/request-fail.tsx
@@ -39,7 +39,7 @@ const GoogleLoginRequestFail: React.FC = ({
Login Failed
@@ -49,10 +49,11 @@ const GoogleLoginRequestFail: React.FC = ({
);
diff --git a/packages/adena-extension/src/pages/web/google-login-screen/request.tsx b/packages/adena-extension/src/pages/web/google-login-screen/request.tsx
index 215c563f..202f23c7 100644
--- a/packages/adena-extension/src/pages/web/google-login-screen/request.tsx
+++ b/packages/adena-extension/src/pages/web/google-login-screen/request.tsx
@@ -45,7 +45,7 @@ const GoogleLoginRequest: React.FC = ({
Waiting for Google Login
@@ -55,7 +55,7 @@ const GoogleLoginRequest: React.FC = ({
{
}}
style={{ width: 204 }}
>
-
+
- Connect Hardware Wallet
+ Connect Hardware Wallet
{
style={{ width: 204 }}
onClick={moveSetupAirgapScreen}
>
-
+
- Set Up Airgap Account
+ Set Up Airgap Account
{
}}
style={{ width: 204 }}
>
-
+
- Advanced Options
+ Advanced Options
diff --git a/packages/adena-extension/src/pages/web/questionnaire-screen/question.tsx b/packages/adena-extension/src/pages/web/questionnaire-screen/question.tsx
index d05e2823..41d7c6a3 100644
--- a/packages/adena-extension/src/pages/web/questionnaire-screen/question.tsx
+++ b/packages/adena-extension/src/pages/web/questionnaire-screen/question.tsx
@@ -6,6 +6,7 @@ import { Question } from '@types';
import { WebMainHeader } from '@components/pages/web/main-header';
import WebAnswerButton from '@components/molecules/web-answer-button/web-answer-button';
import IconInfo from '@assets/web/info.svg';
+import RollingNumber from '@components/atoms/rolling-number';
const StyledContainer = styled(View)`
width: 416px;
@@ -37,6 +38,12 @@ const StyledWarningTextWrapper = styled(Row)`
align-items: center;
`;
+const StyledWarningDescriptionWrapper = styled(Row)`
+ margin-left: 1px;
+ justify-content: flex-start;
+ align-items: center;
+`;
+
interface QuestionnaireQuestionProps {
question: Question | null;
nextQuestion: () => void;
@@ -49,15 +56,26 @@ const QuestionnaireQuestion: React.FC = ({
backStep,
}) => {
const theme = useTheme();
- const [selectedAnswers, setSelectedAnswers] = useState([]);
+ const [selectedAnswerIndex, setSelectedAnswerIndex] = useState(null);
const [retryTime, setRetryTime] = useState(0);
- const isWarning = useMemo(() => {
+ const selectedAnswer = useMemo(() => {
if (!question) {
+ return null;
+ }
+ const selectedAnswer = question.answers.find((_, index) => selectedAnswerIndex === index);
+ if (!selectedAnswer) {
+ return null;
+ }
+ return selectedAnswer;
+ }, [question, selectedAnswerIndex])
+
+ const isWarning = useMemo(() => {
+ if (!selectedAnswer) {
return false;
}
- return question.answers.findIndex((answer, index) => answer.correct === false && selectedAnswers.includes(index)) > 0;
- }, [question, selectedAnswers])
+ return selectedAnswer.correct === false;
+ }, [selectedAnswer])
const isRetry = useMemo(() => {
return retryTime > 0;
@@ -67,9 +85,8 @@ const QuestionnaireQuestion: React.FC = ({
if (!question) {
return false;
}
- const correctAnswer = question.answers.find((answer, index) => answer.correct && selectedAnswers.includes(index));
- return correctAnswer ? true : false;
- }, [question, selectedAnswers]);
+ return selectedAnswer?.correct === true;
+ }, [question, selectedAnswer]);
const questionTitle = useMemo(() => {
if (!question) {
@@ -89,22 +106,20 @@ const QuestionnaireQuestion: React.FC = ({
if (retryTime > 0) {
return;
}
- if (!selectedAnswers.includes(index)) {
- setSelectedAnswers([...selectedAnswers, index]);
- setRetryTime(3);
- }
- }, [retryTime, selectedAnswers]);
+ setSelectedAnswerIndex(index);
+ setRetryTime(3);
+ }, [retryTime]);
const onClickNextButton = useCallback(() => {
if (availableNext) {
nextQuestion();
}
- setSelectedAnswers([]);
+ setSelectedAnswerIndex(null);
setRetryTime(0);
}, [availableNext]);
const onClickBack = useCallback(() => {
- setSelectedAnswers([]);
+ setSelectedAnswerIndex(null);
setRetryTime(0);
backStep()
}, [backStep]);
@@ -142,7 +157,7 @@ const QuestionnaireQuestion: React.FC = ({
key={index}
correct={answer.correct}
answer={answer.answer}
- selected={selectedAnswers.includes(index)}
+ selected={selectedAnswerIndex === index}
onClick={(): void => selectAnswer(index)}
/>
))}
@@ -155,7 +170,12 @@ const QuestionnaireQuestion: React.FC = ({
{isRetry ? 'Incorrect answer! Please try again in ' : 'Incorrect answer! Please try again.'}
- {isRetry ? `${retryTime} seconds.` : ''}
+ {isRetry && (
+
+
+ seconds.
+
+ )}
)}
diff --git a/packages/adena-extension/src/pages/web/setup-airgap-screen/complete.tsx b/packages/adena-extension/src/pages/web/setup-airgap-screen/complete.tsx
index 30e26016..119424c5 100644
--- a/packages/adena-extension/src/pages/web/setup-airgap-screen/complete.tsx
+++ b/packages/adena-extension/src/pages/web/setup-airgap-screen/complete.tsx
@@ -8,6 +8,7 @@ import IconCheck from '@assets/web/web-check-circle.svg';
const StyledContainer = styled(View)`
row-gap: 24px;
+ height: 350px;
`;
const StyledMessageBox = styled(View)`
diff --git a/packages/adena-extension/src/pages/web/setup-airgap-screen/enter-address.tsx b/packages/adena-extension/src/pages/web/setup-airgap-screen/enter-address.tsx
index 80b7650f..1b9f6189 100644
--- a/packages/adena-extension/src/pages/web/setup-airgap-screen/enter-address.tsx
+++ b/packages/adena-extension/src/pages/web/setup-airgap-screen/enter-address.tsx
@@ -7,6 +7,7 @@ import IconAirgap from '@assets/web/airgap-green.svg';
const StyledContainer = styled(View)`
width: 416px;
+ height: 350px;
row-gap: 24px;
`;
@@ -26,7 +27,7 @@ interface StyledInputProps {
placeholder: string;
onChange: (event: React.ChangeEvent) => void;
}
-const StyledInput = styled(WebInput)`
+const StyledInput = styled(WebInput) `
border: ${({ theme, error }): string => (error ? `1px solid ${theme.webError._200}` : '')};
background-color: ${({ theme, error }): string => (error ? theme.webError._300 : '')};
@@ -79,6 +80,7 @@ const SetupAirgapEnterAddress: React.FC = ({
type='text'
name='address'
placeholder='Account Address'
+ autoComplete='off'
onChange={onChangeAddressInput}
/>
{errorMessage && }