All files / app/pages/onboarding/08-set-password set-password.tsx

0% Statements 0/66
0% Branches 0/25
0% Functions 0/7
0% Lines 0/63

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150                                                                                                                                                                                                                                                                                                           
import {
  Onboarding,
  OnboardingTitle,
  OnboardingText,
  OnboardingButton,
} from '@components/onboarding';
import { ExplainerTooltip } from '@components/tooltip';
import { FULL_ENTITY_NAME } from '@constants/index';
import {
  validatePassword,
  blankPasswordValidation,
  ValidatedPassword,
} from '@crypto/validate-password';
import { useAnalytics } from '@hooks/use-analytics';
import { useBackButton } from '@hooks/use-back-url';
import { Text, Input, Flex, color } from '@stacks/ui';
import { setSoftwareWallet } from '@store/keys';
import { blastUndoStackToRemovePasswordFromMemory } from '@utils/blast-undo-stack';
import { OnboardingSelector } from 'app/tests/features/onboarding.selectors';
import React, { useCallback, useRef, useState } from 'react';
import { useDispatch } from 'react-redux';
import { useHistory } from 'react-router';
 
const weakPasswordWarningMessage = (result: ValidatedPassword) => {
  Iif (result.isMnemonicPhrase) {
    return `Don't use your mnemonic Secret Key as your wallet password. This password is used to encrypt your Secret Key.`;
  }
  Iif (result.feedback.suggestions.length > 0) {
    return `${result.feedback.suggestions.join(' ')}`;
  }
  Iif (result.feedback.warning) {
    return `${result.feedback.warning}`;
  }
  Iif (!result.meetsScoreRequirement) {
    return 'This password is vulnerable to brute force attacks. Try adding more non-alphanumeric characters.';
  }
  Iif (!result.meetsLengthRequirement) {
    return 'Your password must be at least 12 characters long';
  }
  return 'Consider using a password generator to ensure your funds are sufficiently secure.';
};
 
export const SetPassword: React.FC = () => {
  const history = useHistory();
 
  const dispatch = useDispatch();
  const passwordInputRef = useRef<HTMLInputElement>(null);
  const [password, setPassword] = useState<string | null>(null);
  const [strengthResult, setStrengthResult] = useState(blankPasswordValidation);
  const [hasSubmitted, setHasSubmitted] = useState(false);
  const [btnDisabled, setBtnDisabled] = useState(false);
  const [successfullyChosenStrongPass, setSuccessfullyChosenStrongPass] = useState(false);
  const analytics = useAnalytics();
 
  const handleBack = useCallback(() => {
    history.goBack();
  }, [history]);
 
  useBackButton(btnDisabled ? null : handleBack);
 
  const handlePasswordInput = (e: React.FormEvent<HTMLInputElement>) => {
    e.preventDefault();
    const pass = e.currentTarget.value;
    setPassword(pass);
    const result = validatePassword(pass);
    Iif (result.isMnemonicPhrase) setHasSubmitted(true);
    setStrengthResult(result);
  };
 
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    Iif (password === null) return;
    setHasSubmitted(true);
    const result = validatePassword(password);
    setStrengthResult(result);
    if (result.meetsAllStrengthRequirements) {
      setBtnDisabled(true);
      setSuccessfullyChosenStrongPass(true);
      void analytics.track('submit_valid_password');
      blastUndoStackToRemovePasswordFromMemory(passwordInputRef.current);
      dispatch(setSoftwareWallet({ password, history }));
    } else {
      void analytics.track('submit_invalid_password');
    }
  };
 
  const strengthText = (result: ValidatedPassword) =>
    result.meetsAllStrengthRequirements ? 'Strong' : 'Not strong enough';
 
  const strengthColor = (result: ValidatedPassword) =>
    result.meetsAllStrengthRequirements ? color('feedback-success') : color('feedback-error');
 
  return (
    <Onboarding as="form" onSubmit={handleSubmit}>
      <OnboardingTitle>Set a password</OnboardingTitle>
      <OnboardingText>
        You’ll use your password when sending transactions. <br />
        If you forget it, you can restore your wallet from your Secret Key.
      </OnboardingText>
      <Flex mt="base-loose">
        <ExplainerTooltip textStyle="caption">
          Your password encrypts your Secret Key on your computer. This way, you can keep your
          Secret Key safe, and only ever have to access it when restoring your wallet.
          <br />
          <br />
          No one from {FULL_ENTITY_NAME} will ever ask for your password or Secret Key.
        </ExplainerTooltip>
        <Text textStyle="caption" color={color('text-caption')} ml="tight">
          Why do I need to set a password?
        </Text>
      </Flex>
      <Input
        type="password"
        mt="base-tight"
        onChange={handlePasswordInput}
        data-test={OnboardingSelector.InputPassword}
        ref={passwordInputRef}
      />
      {!successfullyChosenStrongPass && (
        <Text display="block" textStyle="body.small" color={color('text-caption')} mt="base">
          Password strength:
          <Text
            textStyle="body.small.medium"
            color={password === null ? undefined : strengthColor(strengthResult)}
            ml="tight"
          >
            {password === null ? '—' : strengthText(strengthResult)}
          </Text>
        </Text>
      )}
      {!strengthResult.meetsAllStrengthRequirements &&
        hasSubmitted &&
        !successfullyChosenStrongPass && (
          <Text display="block" textStyle="body.small" color="ink.600" mt="tight">
            {weakPasswordWarningMessage(strengthResult)}
          </Text>
        )}
      <OnboardingButton
        type="submit"
        mt="loose"
        isLoading={btnDisabled}
        isDisabled={btnDisabled}
        data-test={OnboardingSelector.BtnContinueFromPassword}
      >
        Continue
      </OnboardingButton>
    </Onboarding>
  );
};