diff --git a/src/components/auth/AuthMasterPasswordPopover.tsx b/src/components/auth/AuthMasterPasswordPopover.tsx index 66e05b8..2b6ebe0 100644 --- a/src/components/auth/AuthMasterPasswordPopover.tsx +++ b/src/components/auth/AuthMasterPasswordPopover.tsx @@ -5,15 +5,13 @@ import { Popover, Button, Input, Form } from "antd"; import { TooltipPlacement } from "antd/lib/tooltip"; -import { useSelector, shallowEqual } from "react-redux"; -import { RootState } from "@store"; import { useTranslation } from "react-i18next"; import { translateError } from "@utils/i18n"; import { FakeUsernameInput } from "./FakeUsernameInput"; import { getMasterPasswordInput } from "./MasterPasswordInput"; -import { authMasterPassword } from "@wallets"; +import { authMasterPassword, useMasterPassword } from "@wallets"; interface FormValues { masterPassword: string; @@ -26,7 +24,7 @@ } export const AuthMasterPasswordPopover: FC = ({ encrypt, onSubmit, placement, children }) => { - const { salt, tester } = useSelector((s: RootState) => s.masterPassword, shallowEqual); + const { salt, tester } = useMasterPassword(); const { t } = useTranslation(); const [form] = Form.useForm(); diff --git a/src/components/auth/AuthorisedAction.tsx b/src/components/auth/AuthorisedAction.tsx index 85db53f..545c8df 100644 --- a/src/components/auth/AuthorisedAction.tsx +++ b/src/components/auth/AuthorisedAction.tsx @@ -4,8 +4,7 @@ import React, { FC, useState } from "react"; import { TooltipPlacement } from "antd/lib/tooltip"; -import { useSelector, shallowEqual } from "react-redux"; -import { RootState } from "@store"; +import { useMasterPassword } from "@wallets"; import { AuthMasterPasswordPopover } from "./AuthMasterPasswordPopover"; import { SetMasterPasswordModal } from "./SetMasterPasswordModal"; @@ -23,8 +22,7 @@ } export const AuthorisedAction: FC = ({ encrypt, onAuthed, popoverPlacement, children }) => { - const { isAuthed, hasMasterPassword } - = useSelector((s: RootState) => s.masterPassword, shallowEqual); + const { isAuthed, hasMasterPassword } = useMasterPassword(); // Don't render the modal and popover unless we absolutely have to const [clicked, setClicked] = useState(false); diff --git a/src/global/ForcedAuth.tsx b/src/global/ForcedAuth.tsx index 645f05f..aa775f1 100644 --- a/src/global/ForcedAuth.tsx +++ b/src/global/ForcedAuth.tsx @@ -4,10 +4,7 @@ import { message } from "antd"; import { useTranslation, TFunction } from "react-i18next"; -import { useSelector, shallowEqual } from "react-redux"; -import { RootState } from "@store"; - -import { authMasterPassword } from "@wallets"; +import { authMasterPassword, useMasterPassword } from "@wallets"; import { useMountEffect } from "@utils"; @@ -27,7 +24,7 @@ * containing the master password, and automatically authenticate with it. */ export function ForcedAuth(): JSX.Element | null { const { isAuthed, hasMasterPassword, salt, tester } - = useSelector((s: RootState) => s.masterPassword, shallowEqual); + = useMasterPassword(); const { t } = useTranslation(); diff --git a/src/global/ws/SyncMOTD.tsx b/src/global/ws/SyncMOTD.tsx index 0564008..c897d28 100644 --- a/src/global/ws/SyncMOTD.tsx +++ b/src/global/ws/SyncMOTD.tsx @@ -12,7 +12,9 @@ import * as api from "@api"; import { KristMOTD, KristMOTDBase } from "@api/types"; -import { recalculateWallets, useWallets } from "@wallets"; +import { + recalculateWallets, useWallets, useMasterPasswordOnly +} from "@wallets"; import { useAddressPrefix } from "@utils/currency"; import Debug from "debug"; @@ -47,7 +49,7 @@ // All these are used to determine if we need to recalculate the addresses const addressPrefix = useAddressPrefix(); - const masterPassword = useSelector((s: RootState) => s.masterPassword.masterPassword); + const masterPassword = useMasterPasswordOnly(); const { wallets } = useWallets(); // Update the MOTD when the sync node changes, and on startup diff --git a/src/krist/wallets/masterPassword.ts b/src/krist/wallets/masterPassword.ts index 7407b96..0e82bb0 100644 --- a/src/krist/wallets/masterPassword.ts +++ b/src/krist/wallets/masterPassword.ts @@ -1,6 +1,9 @@ // Copyright (c) 2020-2021 Drew Lemmy // This file is part of KristWeb 2 under GPL-3.0. // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt +import { useSelector, shallowEqual } from "react-redux"; +import { RootState } from "@store"; + import { toHex } from "@utils"; import { aesGcmEncrypt, aesGcmDecrypt } from "@utils/crypto"; @@ -60,3 +63,22 @@ // Dispatch the auth state changes to the Redux store store.dispatch(actions.setMasterPassword(saltHex, tester, password)); } + +interface MasterPasswordHookResponse { + isAuthed?: boolean; + masterPassword?: string; + salt?: string; + tester?: string; + hasMasterPassword?: boolean; +} + +/** Hook to return the master password state. */ +export const useMasterPassword = (): MasterPasswordHookResponse => + useSelector((s: RootState) => s.masterPassword, shallowEqual); + +/** + * Hook to return just the master password. Used when the authed state is + * already known (e.g. the component was rendered within an AuthorisedAction). + */ +export const useMasterPasswordOnly = (): string | undefined => + useSelector((s: RootState) => s.masterPassword.masterPassword); diff --git a/src/pages/wallets/AddWalletModal.tsx b/src/pages/wallets/AddWalletModal.tsx index 1a5cae6..3abbc4d 100644 --- a/src/pages/wallets/AddWalletModal.tsx +++ b/src/pages/wallets/AddWalletModal.tsx @@ -5,8 +5,6 @@ import { Modal, Form, Input, Checkbox, Collapse, Button, Tooltip, Typography, Row, Col, message, notification, Grid } from "antd"; import { ReloadOutlined } from "@ant-design/icons"; -import { useSelector } from "react-redux"; -import { RootState } from "@store"; import { useTranslation, Trans } from "react-i18next"; import { generatePassword } from "@utils"; @@ -19,7 +17,8 @@ import { SelectWalletFormat } from "@comp/wallets/SelectWalletFormat"; import { Wallet, WalletFormatName, calculateAddress, formatNeedsUsername, - useWallets, addWallet, decryptWallet, editWallet, ADDRESS_LIST_LIMIT + useWallets, addWallet, decryptWallet, editWallet, ADDRESS_LIST_LIMIT, + useMasterPasswordOnly } from "@wallets"; const { Text } = Typography; @@ -52,7 +51,7 @@ const initialFormat = editing?.format || "kristwallet"; // Required to encrypt new wallets - const masterPassword = useSelector((s: RootState) => s.masterPassword.masterPassword); + const masterPassword = useMasterPasswordOnly(); // Required to check for existing wallets const { wallets } = useWallets(); const addressPrefix = useAddressPrefix(); diff --git a/src/pages/wallets/info/DecryptReveal.tsx b/src/pages/wallets/info/DecryptReveal.tsx index d204948..68b4b4a 100644 --- a/src/pages/wallets/info/DecryptReveal.tsx +++ b/src/pages/wallets/info/DecryptReveal.tsx @@ -8,8 +8,7 @@ import { useTranslation } from "react-i18next"; -import { useSelector, shallowEqual } from "react-redux"; -import { RootState } from "@store"; +import { useMasterPassword } from "@wallets"; import { aesGcmDecrypt } from "@utils/crypto"; import { AuthorisedAction } from "@comp/auth/AuthorisedAction"; @@ -33,8 +32,7 @@ }: Props): JSX.Element { const { t } = useTranslation(); - const { isAuthed, masterPassword } - = useSelector((s: RootState) => s.masterPassword, shallowEqual); + const { isAuthed, masterPassword } = useMasterPassword(); const [revealed, setRevealed] = useState(false); const [decrypted, setDecrypted] = useState();