From c6a207b54242d73b824855626a41c1e56164e407 Mon Sep 17 00:00:00 2001 From: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Date: Wed, 24 Mar 2021 13:36:02 +0100 Subject: [PATCH] Manage share reconstruction (#830) * password mnemonic reset shareTransfer * styling * error handling and button styling * lint * cleanup logs * disable buttons when thre is an error * Apply suggestions from code review Co-authored-by: Ryan Noble * remove checkbox and save json to filestorage * Apply suggestions from code review * add some error handling with try/catch * lint * prevent double screen Co-authored-by: Ryan Noble --- .../Modules/LoginModule/InitialScreen.tsx | 41 +-- .../Modules/LoginModule/MissingShares.tsx | 317 +++++++++++++++--- .../Modules/LoginModule/SaveNewDevice.tsx | 132 +++++++- .../src/Components/Pages/LoginPage.tsx | 66 +++- .../src/Contexts/ThresholdKeyContext.tsx | 274 ++++++--------- 5 files changed, 560 insertions(+), 270 deletions(-) diff --git a/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx b/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx index 9ffd8f7bae..4926145275 100644 --- a/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx +++ b/packages/files-ui/src/Components/Modules/LoginModule/InitialScreen.tsx @@ -10,33 +10,8 @@ import { LOGIN_TYPE } from "@toruslabs/torus-direct-web-sdk" import { ROUTE_LINKS } from "../../FilesRoutes" const useStyles = makeStyles( - ({ constants, palette, breakpoints, zIndex }: CSFTheme) => + ({ constants, palette, breakpoints }: CSFTheme) => createStyles({ - root: { - display: "flex", - flexDirection: "column", - alignItems: "center", - flex: "1 1 0", - position: "absolute", - top: "50%", - left: "50%", - transform: "translate(-50%, -50%)", - zIndex: zIndex?.layer1, - backgroundColor: constants.landing.background, - border: `1px solid ${constants.landing.border}`, - boxShadow: constants.landing.boxShadow, - borderRadius: 6, - [breakpoints.up("md")]:{ - minHeight: "64vh", - justifyContent: "space-between", - minWidth: 440 - }, - [breakpoints.down("md")]: { - padding: `${constants.generalUnit * 4}px ${constants.generalUnit * 6.5}px`, - justifyContent: "center", - width: `calc(100vw - ${constants.generalUnit * 2}px)` - } - }, buttonSection: { [breakpoints.up("md")]: { position: "absolute", @@ -117,15 +92,11 @@ const useStyles = makeStyles( display: "none" } } - } - ) + }) ) const InitialScreen: React.FC = () => { - const { - selectWallet, - resetAndSelectWallet - } = useImployApi() + const { selectWallet, resetAndSelectWallet } = useImployApi() const { desktop } = useThemeSwitcher() const { wallet } = useWeb3() const { login } = useThresholdKey() @@ -192,7 +163,7 @@ const InitialScreen: React.FC = () => { } return ( -
+ <> { desktop && !isConnecting && !error && ( {
Connection failed - {error} + {error}
+ ) } diff --git a/packages/files-ui/src/Components/Modules/LoginModule/MissingShares.tsx b/packages/files-ui/src/Components/Modules/LoginModule/MissingShares.tsx index 1a42cfbc3b..970eaaae80 100644 --- a/packages/files-ui/src/Components/Modules/LoginModule/MissingShares.tsx +++ b/packages/files-ui/src/Components/Modules/LoginModule/MissingShares.tsx @@ -1,61 +1,298 @@ -import React, { useState } from "react" +import React, { ChangeEvent, useCallback, useState } from "react" import { useThresholdKey } from "../../../Contexts/ThresholdKeyContext" -import { Button, TextInput } from "@chainsafe/common-components" +import { Button, TextInput, Typography } from "@chainsafe/common-components" import { SECURITY_QUESTIONS_MODULE_NAME } from "@tkey/security-questions" +import { t, Trans } from "@lingui/macro" +import { createStyles, makeStyles } from "@chainsafe/common-theme" +import { CSFTheme } from "../../../Themes/types" +import clsx from "clsx" -const MissingShares: React.FC = () => { - const { - keyDetails, - inputPasswordShare, - inputMnemonicShare - } = useThresholdKey() - const [password, setPassword] = useState("") - const [mnemonic, setMnemonic] = useState("") +const useStyles = makeStyles(({ breakpoints, constants, palette }: CSFTheme) => + createStyles({ + content:{ + width: "100%" + }, + buttonSection: { + [breakpoints.up("md")]: { + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)" + }, + [breakpoints.down("md")]: { + display: "flex", + flexDirection: "column", + justifyContent: "space-evenly" + } + }, + button: { + width: `calc(100% - ${constants.generalUnit * 8}px)`, + marginLeft: constants.generalUnit * 4, + marginRight: constants.generalUnit * 4, + marginBottom: constants.generalUnit * 2, + [breakpoints.up("md")]: { + backgroundColor: palette.common.black.main, + color: palette.common.white.main + }, + [breakpoints.down("md")]: { + backgroundColor: palette.common.black.main, + color: palette.common.white.main + } + }, + buttonWrapper: { + display: "flex", + flexDirection: "column", + alignItems: "center", + marginTop: constants.generalUnit * 4, + marginBottom: constants.generalUnit * 4 + }, + headerText: { + textAlign: "center", + [breakpoints.up("md")]: { + paddingTop: constants.generalUnit * 4, + paddingBottom: constants.generalUnit * 8 + }, + [breakpoints.down("md")]: { + paddingTop: constants.generalUnit * 3, + paddingBottom: constants.generalUnit * 3, + textAlign: "center" + } + }, + text: { + display: "inline-block", + paddingLeft: constants.generalUnit * 4, + paddingRight: constants.generalUnit * 4, + textAlign: "center", + [breakpoints.down("md")]: { + paddingLeft: 0, + paddingRight: 0, + "&.label": { + paddingLeft: constants.generalUnit * 4 + } + } + }, + footer: { + textAlign: "center", + marginTop: constants.generalUnit * 2, + padding: `${constants.generalUnit * 2.5}px ${constants.generalUnit * 1.5}px`, + width: "100%" + }, + buttonLink: { + color: palette.additional["gray"][10], + outline: "none", + textDecoration: "underline", + cursor: "pointer" + }, + textInput:{ + width: "100%", + margin: 0, + paddingLeft: constants.generalUnit*4, + paddingRight: constants.generalUnit*4 + }, + belowInput: { + margin: "auto", + marginTop: constants.generalUnit * 4 + }, + textAreaContainer: { + marginTop: constants.generalUnit, + paddingLeft: constants.generalUnit * 4, + paddingRight: constants.generalUnit * 4, + "& > textarea" : { + width: "100%", + height: constants.generalUnit * 10, + padding: constants.generalUnit + } + }, + error: { + display: "inline-block", + padding: constants.generalUnit * 4 + } + })) +const MissingShares: React.FC = () => { + const { keyDetails, inputPasswordShare, inputMnemonicShare } = useThresholdKey() + const [password, setPassword] = useState("") + const [mnemonic, setMnemonic] = useState("") + const [withMnemonic, setWithMnemonic] = useState(false) + const [withPassword, setWithPassword] = useState(false) + const classes = useStyles() + const { logout } = useThresholdKey() + const [isLoading, setIsLoading] = useState(false) + const [error, setError] = useState("") + const shares = keyDetails ? Object.values(keyDetails.shareDescriptions).map((share) => { return JSON.parse(share[0]) }) : [] - const hasPasswordShare = - shares.filter((s) => s.module === SECURITY_QUESTIONS_MODULE_NAME).length > 0 + const hasPasswordShare = shares.filter((s) => s.module === SECURITY_QUESTIONS_MODULE_NAME).length > 0 - const handleSubmitPassword = async () => { + const handleSubmitPassword = () => { if (!password) return - await inputPasswordShare(password) + setIsLoading(true) + + inputPasswordShare(password) + .catch((e) => { + setIsLoading(false) + setError(t`Password does not match user account, please double-check and try again.`) + console.error("error upon password input", e) + }) } - const handleSubmitMnemonicShare = async () => { + const handleSubmitMnemonicShare = () => { if (!mnemonic) return - await inputMnemonicShare(mnemonic) + setIsLoading(true) + + inputMnemonicShare(mnemonic) + .catch(() => { + setIsLoading(false) + setError(t`Backup phrase does not match user account, please double-check and try again.`) + }) } + const onPasswordChange = useCallback((password: string | number | undefined) => { + setError("") + setPassword(password?.toString() || "") + }, []) + + const onMnemonicChange = useCallback((event: ChangeEvent) => { + setError("") + setMnemonic(event.currentTarget.value) + }, []) + + const onResetMethod = useCallback(() => { + setError("") + setMnemonic("") + setPassword("") + setWithMnemonic(false) + setWithPassword(false) + }, []) + return ( -
- {hasPasswordShare && ( - <> - - - val ? setPassword(val.toString()) : setPassword("") - } - /> - - - )} - <> - - - val ? setMnemonic(val.toString()) : setMnemonic("") - } - /> - - -
+ <> +
+ + Sign in + + { !withMnemonic && !withPassword && ( + <> + + + Looks like you’re signing in from a new browser. + Please choose one of the following to continue: + + +
+ {hasPasswordShare && ( + + )} + +
+ + + Or confirm by signing into your Files on any + browser you’ve used before. + + + + )} + {withPassword && ( +
+ + Enter password: + + + + + {error} + +
+ )} + {withMnemonic && ( +
+ + Enter backup phrase: + +
+