{renderText(lang(message))}
diff --git a/src/components/auth/Auth.module.scss b/src/components/auth/Auth.module.scss
index d006fddd..8ee66e58 100644
--- a/src/components/auth/Auth.module.scss
+++ b/src/components/auth/Auth.module.scss
@@ -17,6 +17,10 @@
.transitionSlide {
background: var(--color-background-second);
+
+ @supports (padding-top: env(safe-area-inset-top)) {
+ padding-top: env(safe-area-inset-top);
+ }
}
.container {
@@ -49,7 +53,7 @@
}
@supports (padding-bottom: env(safe-area-inset-bottom)) {
- padding-bottom: calc(1rem + env(safe-area-inset-bottom));
+ padding-bottom: max(1rem, env(safe-area-inset-bottom));
}
@include respond-above(sm) {
@@ -57,6 +61,12 @@
}
}
+.containerFullSize {
+ overflow: hidden;
+
+ padding: 0 !important;
+}
+
.logo {
display: block !important;
@@ -296,6 +306,10 @@
column-gap: 1rem;
margin: 1.5rem 1rem 1rem;
+
+ @supports (margin-bottom: max(env(safe-area-inset-bottom), 1rem)) {
+ margin-bottom: max(env(safe-area-inset-bottom), 1rem);
+ }
}
.modalSticker {
@@ -303,19 +317,27 @@
}
.form {
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
+
+ width: 100%;
+}
+
+.formWidgets {
width: 100%;
margin-top: 1.75rem;
}
.checkMnemonicInput {
- margin-bottom: 1rem;
+ margin-bottom: 1.25rem;
}
.errors {
width: 100%;
padding: 0 0.5rem;
- font-size: 0.9375rem;
+ font-size: 0.8125rem;
font-weight: 600;
color: var(--color-gray-1);
}
@@ -324,7 +346,7 @@
width: 100%;
padding: 0 0.5rem;
- font-size: 0.9375rem;
+ font-size: 0.8125rem;
color: var(--color-gray-1);
}
@@ -343,8 +365,6 @@
}
.error {
- margin-top: 0.5rem;
-
font-size: 0.9375rem;
font-weight: 600;
color: var(--color-red);
@@ -500,6 +520,121 @@
margin: 0.75rem -1rem 0;
}
+.biometricsStep {
+ align-self: center;
+
+ margin-bottom: 1rem;
+ padding: 0.75rem;
+
+ font-size: 0.9375rem;
+ font-weight: 600;
+ color: var(--light-gray-1);
+ white-space: nowrap;
+
+ background-color: var(--color-gray-button-background-light);
+ border-radius: var(--border-radius-buttons);
+}
+
+.biometricsError {
+ align-self: center;
+
+ margin-top: 0.25rem;
+ padding: 0.375rem 0.5rem;
+
+ font-size: 1.0625rem;
+ font-weight: 700;
+ line-height: 1;
+ color: var(--color-transaction-red-text);
+
+ background-color: var(--color-transaction-red-background);
+ border-radius: var(--border-radius-tiny);
+}
+
+.stepTransition {
+ width: auto !important;
+ height: auto !important;
+
+ white-space: nowrap;
+}
+
+.passwordFormContainer {
+ overflow-y: scroll;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ max-width: 27rem;
+ height: 32.5rem;
+ max-height: 100%;
+ margin: 0 auto;
+ padding: 0 1rem 1rem;
+
+ @include adapt-padding-to-scrollbar(1rem);
+
+ @include respond-above(xs) {
+ max-width: 31.4375rem;
+ }
+
+ @supports (padding-bottom: env(safe-area-inset-bottom)) {
+ padding-bottom: calc(1rem + env(safe-area-inset-bottom));
+ }
+}
+
+.pinPadHeader {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ margin-top: auto;
+}
+
+.headerBack {
+ cursor: var(--custom-cursor, pointer);
+
+ position: absolute;
+ top: 1.5rem;
+ left: 0.5rem;
+
+ display: flex;
+ align-items: center;
+
+ padding: 0;
+
+ font-size: 1.0625rem;
+ color: var(--color-blue);
+
+ @supports (top: max(calc(env(safe-area-inset-top) + 0.375rem), 1.5rem)) {
+ top: max(calc(env(safe-area-inset-top) + 0.375rem), 1.5rem);
+ }
+}
+
+.iconChevron {
+ font-size: 1.5rem;
+}
+
+.biometricsIcon {
+ width: 7rem;
+ height: 7rem;
+ margin: 5rem auto 2rem;
+}
+
+.biometricsTitle {
+ margin-bottom: 2rem;
+
+ font-size: 1.6875rem;
+ font-weight: 800;
+ text-align: center;
+}
+
+.biometricsSubtitle {
+ margin: 0 2rem;
+ padding-bottom: 2rem;
+
+ font-size: 1.0625rem;
+ color: var(--color-gray-1);
+ text-align: center;
+}
+
@supports (padding-bottom: env(safe-area-inset-bottom)) {
@include respond-below(xs) {
.disclaimerBackupDialog {
diff --git a/src/components/auth/Auth.tsx b/src/components/auth/Auth.tsx
index 5c38c6e9..46546367 100644
--- a/src/components/auth/Auth.tsx
+++ b/src/components/auth/Auth.tsx
@@ -14,8 +14,13 @@ import useLastCallback from '../../hooks/useLastCallback';
import SettingsAbout from '../settings/SettingsAbout';
import Transition from '../ui/Transition';
+import AuthBackupWalletModal from './AuthBackupWalletModal';
+import AuthConfirmPin from './AuthConfirmPin';
import AuthCreateBackup from './AuthCreateBackup';
+import AuthCreateBiometrics from './AuthCreateBiometrics';
+import AuthCreateNativeBiometrics from './AuthCreateNativeBiometrics';
import AuthCreatePassword from './AuthCreatePassword';
+import AuthCreatePin from './AuthCreatePin';
import AuthCreatingWallet from './AuthCreatingWallet';
import AuthDisclaimer from './AuthDisclaimer';
import AuthImportMnemonic from './AuthImportMnemonic';
@@ -24,16 +29,20 @@ import AuthStart from './AuthStart';
import styles from './Auth.module.scss';
type StateProps = Pick
;
const RENDER_COUNT = Object.keys(AuthState).length / 2;
const Auth = ({
state,
+ biometricsStep,
+ error,
isLoading,
mnemonic,
mnemonicCheckIndexes,
+ isBackupModalOpen,
method,
}: StateProps) => {
const {
@@ -48,9 +57,11 @@ const Auth = ({
true,
) ?? -1;
+ const [prevKey, setPrevKey] = useState(undefined);
const [nextKey, setNextKey] = useState(renderingAuthState + 1);
- const updateNextKey = useLastCallback(() => {
+ const updateRenderingKeys = useLastCallback(() => {
setNextKey(renderingAuthState + 1);
+ setPrevKey(renderingAuthState === AuthState.confirmPin ? AuthState.createPin : undefined);
});
// eslint-disable-next-line consistent-return
@@ -60,57 +71,95 @@ const Auth = ({
return ;
case AuthState.creatingWallet:
return ;
+ case AuthState.createPin:
+ return ;
+ case AuthState.confirmPin:
+ return ;
+ case AuthState.createBiometrics:
+ return (
+
+ );
+ case AuthState.createNativeBiometrics:
+ return (
+
+ );
case AuthState.createPassword:
return ;
case AuthState.createBackup:
- return ;
+ return ;
case AuthState.disclaimerAndBackup:
return (
-
+
);
case AuthState.importWallet:
return ;
+ case AuthState.importWalletCreatePin:
+ return ;
+ case AuthState.importWalletConfirmPin:
+ return ;
case AuthState.disclaimer:
return (
);
+ case AuthState.importWalletCreateNativeBiometrics:
+ return (
+
+ );
case AuthState.importWalletCreatePassword:
return ;
+ case AuthState.importWalletCreateBiometrics:
+ return (
+
+ );
case AuthState.about:
return ;
}
}
return (
-
- {renderAuthScreen}
-
+ <>
+
+ {renderAuthScreen}
+
+
+ >
);
};
export default memo(withGlobal((global): StateProps => {
return pick(global.auth, [
- 'state', 'mnemonic', 'mnemonicCheckIndexes', 'isLoading', 'method',
+ 'state', 'biometricsStep', 'error', 'mnemonic', 'mnemonicCheckIndexes', 'isLoading', 'method',
+ 'isBackupModalOpen',
]);
})(Auth));
diff --git a/src/components/auth/AuthBackupWalletModal.tsx b/src/components/auth/AuthBackupWalletModal.tsx
new file mode 100644
index 00000000..dd599d22
--- /dev/null
+++ b/src/components/auth/AuthBackupWalletModal.tsx
@@ -0,0 +1,127 @@
+import React, { memo, useState } from '../../lib/teact/teact';
+import { getActions } from '../../global';
+
+import resolveModalTransitionName from '../../util/resolveModalTransitionName';
+
+import useLang from '../../hooks/useLang';
+import useLastCallback from '../../hooks/useLastCallback';
+
+import Modal from '../ui/Modal';
+import Transition from '../ui/Transition';
+import MnemonicCheck from './MnemonicCheck';
+import MnemonicList from './MnemonicList';
+import SafetyRules from './SafetyRules';
+
+import modalStyles from '../ui/Modal.module.scss';
+import styles from './Auth.module.scss';
+
+interface OwnProps {
+ isOpen?: boolean;
+ mnemonic?: string[];
+ checkIndexes?: number[];
+}
+
+const SLIDE_ANIMATION_DURATION_MS = 250;
+
+enum BackupState {
+ Accept,
+ View,
+ Confirm,
+}
+function AuthBackupWalletModal({
+ isOpen, mnemonic, checkIndexes,
+}: OwnProps) {
+ const {
+ restartCheckMnemonicIndexes,
+ closeAuthBackupWalletModal,
+ } = getActions();
+
+ const lang = useLang();
+ const [renderingKey, setRenderingKey] = useState(BackupState.Accept);
+ const [nextKey, setNextKey] = useState(BackupState.View);
+
+ const handleModalClose = useLastCallback(() => {
+ setRenderingKey(BackupState.Accept);
+ setNextKey(BackupState.View);
+ });
+
+ const handleMnemonicView = useLastCallback(() => {
+ setRenderingKey(BackupState.View);
+ setNextKey(BackupState.Confirm);
+ });
+ const handleRestartCheckMnemonic = useLastCallback(() => {
+ handleMnemonicView();
+
+ setTimeout(() => {
+ restartCheckMnemonicIndexes();
+ }, SLIDE_ANIMATION_DURATION_MS);
+ });
+
+ const handleShowMnemonicCheck = useLastCallback(() => {
+ setRenderingKey(BackupState.Confirm);
+ setNextKey(undefined);
+ });
+
+ const handleMnemonicCheckSubmit = useLastCallback(() => {
+ closeAuthBackupWalletModal({ isBackupCreated: true });
+ });
+
+ // eslint-disable-next-line consistent-return
+ function renderModalContent(isScreenActive: boolean, isFrom: boolean, currentScreenKey: number) {
+ switch (currentScreenKey) {
+ case BackupState.Accept:
+ return (
+
+ );
+
+ case BackupState.View:
+ return (
+
+ );
+
+ case BackupState.Confirm:
+ return (
+
+ );
+ }
+ }
+ return (
+
+
+ {renderModalContent}
+
+
+ );
+}
+
+export default memo(AuthBackupWalletModal);
diff --git a/src/components/auth/AuthBackupWarning.tsx b/src/components/auth/AuthBackupWarning.tsx
new file mode 100644
index 00000000..46007d50
--- /dev/null
+++ b/src/components/auth/AuthBackupWarning.tsx
@@ -0,0 +1,50 @@
+import React, { memo } from '../../lib/teact/teact';
+import { getActions } from '../../global';
+
+import renderText from '../../global/helpers/renderText';
+import buildClassName from '../../util/buildClassName';
+
+import useLang from '../../hooks/useLang';
+
+import Button from '../ui/Button';
+import Modal from '../ui/Modal';
+
+import styles from './Auth.module.scss';
+
+interface OwnProps {
+ isOpen: boolean;
+ onSkip: NoneToVoidFunction;
+ onClose: NoneToVoidFunction;
+}
+
+function AuthBackupWarning({ isOpen, onSkip, onClose }: OwnProps) {
+ const { openAuthBackupWalletModal } = getActions();
+
+ const lang = useLang();
+
+ return (
+
+ {renderText(lang('$auth_backup_warning_notice'))}
+
+
+
+
+
+ );
+}
+
+export default memo(AuthBackupWarning);
diff --git a/src/components/auth/AuthConfirmPin.tsx b/src/components/auth/AuthConfirmPin.tsx
new file mode 100644
index 00000000..bcc0c9fb
--- /dev/null
+++ b/src/components/auth/AuthConfirmPin.tsx
@@ -0,0 +1,111 @@
+import React, { memo, useEffect, useState } from '../../lib/teact/teact';
+import { getActions, withGlobal } from '../../global';
+
+import type { AuthMethod } from '../../global/types';
+
+import { PIN_LENGTH } from '../../config';
+import buildClassName from '../../util/buildClassName';
+import { pause } from '../../util/schedulers';
+import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets';
+
+import useHistoryBack from '../../hooks/useHistoryBack';
+import useLang from '../../hooks/useLang';
+import useLastCallback from '../../hooks/useLastCallback';
+
+import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview';
+import Button from '../ui/Button';
+import PinPad from '../ui/PinPad';
+
+import styles from './Auth.module.scss';
+
+interface OwnProps {
+ isActive?: boolean;
+ method?: AuthMethod;
+}
+
+interface StateProps {
+ pin: string;
+}
+
+const SUBMIT_PAUSE_MS = 1500;
+
+const AuthConfirmPin = ({
+ isActive,
+ method,
+ pin,
+}: OwnProps & StateProps) => {
+ const { confirmPin, cancelConfirmPin } = getActions();
+
+ const lang = useLang();
+ const [pinConfirm, setPinConfirm] = useState('');
+ const [error, setError] = useState('');
+ const [isConfirmed, setIsConfirmed] = useState(false);
+ const isImporting = method !== 'createAccount';
+
+ const handleBackClick = useLastCallback(() => {
+ cancelConfirmPin({ isImporting });
+ });
+
+ useHistoryBack({
+ isActive,
+ onBack: handleBackClick,
+ });
+
+ useEffect(() => {
+ if (isActive) {
+ setPinConfirm('');
+ setError('');
+ setIsConfirmed(false);
+ }
+ }, [isActive]);
+
+ const handleChange = useLastCallback((value: string) => {
+ setPinConfirm(value);
+ setError('');
+ });
+
+ const handleSubmit = useLastCallback(async (value: string) => {
+ if (value === pin) {
+ setIsConfirmed(true);
+ await pause(SUBMIT_PAUSE_MS);
+ confirmPin({ isImporting });
+ } else {
+ setError(lang('Codes don\'t match'));
+ }
+ });
+
+ return (
+
+
+
+
+
+
{lang(isImporting ? 'Wallet is imported!' : 'Wallet is ready!')}
+
+
+
+
+ );
+};
+
+export default memo(withGlobal((global) => {
+ return {
+ pin: global.auth.password,
+ };
+})(AuthConfirmPin));
diff --git a/src/components/auth/AuthCreateBackup.tsx b/src/components/auth/AuthCreateBackup.tsx
index 53892093..a3b0d097 100644
--- a/src/components/auth/AuthCreateBackup.tsx
+++ b/src/components/auth/AuthCreateBackup.tsx
@@ -1,111 +1,31 @@
-import React, { memo, useState } from '../../lib/teact/teact';
+import React, { memo } from '../../lib/teact/teact';
import { getActions } from '../../global';
import renderText from '../../global/helpers/renderText';
import buildClassName from '../../util/buildClassName';
import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets';
-import useFlag from '../../hooks/useFlag';
import useLang from '../../hooks/useLang';
-import useLastCallback from '../../hooks/useLastCallback';
import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview';
import Button from '../ui/Button';
-import Modal from '../ui/Modal';
-import Transition from '../ui/Transition';
-import MnemonicCheck from './MnemonicCheck';
-import MnemonicList from './MnemonicList';
-import SafetyRules from './SafetyRules';
-import modalStyles from '../ui/Modal.module.scss';
import styles from './Auth.module.scss';
interface OwnProps {
isActive?: boolean;
- mnemonic?: string[];
- checkIndexes?: number[];
}
-enum BackupState {
- Accept,
- View,
- Confirm,
-}
-
-const SLIDE_ANIMATION_DURATION_MS = 250;
-
-const AuthCreateBackup = ({ isActive, mnemonic, checkIndexes }: OwnProps) => {
- const { afterCheckMnemonic, skipCheckMnemonic, restartCheckMnemonicIndexes } = getActions();
+const AuthCreateBackup = ({ isActive }: OwnProps) => {
+ const { skipCheckMnemonic, openAuthBackupWalletModal } = getActions();
const lang = useLang();
- const [isModalOpen, openModal, closeModal] = useFlag();
-
- const [renderingKey, setRenderingKey] = useState(BackupState.Accept);
- const [nextKey, setNextKey] = useState(BackupState.View);
-
- const handleModalClose = useLastCallback(() => {
- setRenderingKey(BackupState.Accept);
- setNextKey(BackupState.View);
- });
-
- const handleMnemonicView = useLastCallback(() => {
- setRenderingKey(BackupState.View);
- setNextKey(BackupState.Confirm);
- });
-
- const handleRestartCheckMnemonic = useLastCallback(() => {
- handleMnemonicView();
-
- setTimeout(() => {
- restartCheckMnemonicIndexes();
- }, SLIDE_ANIMATION_DURATION_MS);
- });
-
- const handleShowMnemonicCheck = useLastCallback(() => {
- setRenderingKey(BackupState.Confirm);
- setNextKey(undefined);
- });
-
- const handleMnemonicCheckSubmit = useLastCallback(() => {
- closeModal();
- afterCheckMnemonic();
- });
-
- // eslint-disable-next-line consistent-return
- function renderModalContent(isScreenActive: boolean, isFrom: boolean, currentScreenKey: number) {
- switch (currentScreenKey) {
- case BackupState.Accept:
- return ;
-
- case BackupState.View:
- return (
-
- );
-
- case BackupState.Confirm:
- return (
-
- );
- }
- }
return (