Newer
Older
CrypticOreWallet / src / components / auth / AuthMasterPasswordPopover.tsx
// Copyright (c) 2020-2021 Drew Lemmy
// This file is part of KristWeb 2 under AGPL-3.0.
// Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt
import { useState, useRef, useCallback, FC, Ref } from "react";
import { Popover, Button, Input, Form } from "antd";
import { TooltipPlacement } from "antd/lib/tooltip";

import { useTFns, translateError } from "@utils/i18n";

import { FakeUsernameInput } from "./FakeUsernameInput";
import { getMasterPasswordInput } from "./MasterPasswordInput";

import { authMasterPassword, useMasterPassword } from "@wallets";

interface FormValues {
  masterPassword: string;
}

interface Props {
  encrypt?: boolean;
  onSubmit: () => void;
  placement?: TooltipPlacement;
}

export const AuthMasterPasswordPopover: FC<Props> = ({
  encrypt,
  onSubmit,
  placement,
  children
}) => {
  const { tStr } = useTFns("masterPassword.");

  const inputRef = useRef<Input>(null);

  const onVisibleChange = useCallback(() =>
    setTimeout(() => inputRef.current?.focus(), 20), [inputRef]);

  return <Popover
    trigger="click"
    overlayClassName="authorised-action-popover"
    title={tStr(encrypt ? "popoverTitleEncrypt" : "popoverTitle")}
    placement={placement}
    onVisibleChange={onVisibleChange}
    content={() => <AuthForm
      encrypt={encrypt}
      onSubmit={onSubmit}
      inputRef={inputRef}
    />}
  >
    {children}
  </Popover>;
};

interface AuthFormProps {
  encrypt?: boolean;
  onSubmit: () => void;
  inputRef: Ref<Input>;
}

function AuthForm({
  encrypt,
  onSubmit,
  inputRef
}: AuthFormProps): JSX.Element {
  const { t, tStr, tKey } = useTFns("masterPassword.");

  const { salt, tester } = useMasterPassword();

  const [form] = Form.useForm();
  const [passwordError, setPasswordError] = useState<string | undefined>();

  const onFinish = useCallback(async function(values: FormValues) {
    try {
      await authMasterPassword(salt, tester, values.masterPassword);
      onSubmit();
    } catch (err) {
      setPasswordError(translateError(t, err, tKey("errorUnknown")));
    }
  }, [t, tKey, onSubmit, salt, tester]);

  return <>
    <p>{tStr(encrypt ? "popoverDescriptionEncrypt" : "popoverDescription")}</p>

    <Form
      form={form}
      name="authMasterPassword"
      layout="vertical"
      onFinish={onFinish}
    >
      <FakeUsernameInput />

      {/* Password input */}
      <Form.Item
        name="masterPassword"

        hasFeedback={passwordError !== undefined}
        validateStatus={passwordError !== undefined ? "error" : undefined}
        help={passwordError}

        rules={[
          { required: true, message: t("masterPassword.errorPasswordRequired") },
          { min: 0, message: t("masterPassword.errorPasswordLength") },
        ]}

        style={{ marginBottom: 8 }}
      >
        {getMasterPasswordInput({
          inputRef,
          placeholder: t("masterPassword.passwordPlaceholder"),
          autoFocus: true
        })}
      </Form.Item>

      {/* Submit button */}
      <Button type="primary" htmlType="submit" size="small">
        {t("masterPassword.popoverAuthoriseButton")}
      </Button>
    </Form>
  </>;
}