Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

saved browsers settings #865

Merged
merged 22 commits into from
Apr 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 40 additions & 19 deletions packages/common-components/src/ExpansionPanel/ExpansionPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { ReactNode, useState } from "react"
import React, { ReactNode, useCallback, useState } from "react"
import { ITheme, makeStyles, createStyles } from "@chainsafe/common-theme"
import { Typography } from "../Typography"
import clsx from "clsx"
Expand Down Expand Up @@ -35,6 +35,7 @@ const useStyles = makeStyles(
}px`,
color: palette.additional["gray"][9],
cursor: "pointer",
display: "flex",
"&.basic": {
backgroundColor: palette.additional["gray"][2],
...overrides?.ExpansionPanel?.heading?.basic?.root,
Expand All @@ -56,6 +57,9 @@ const useStyles = makeStyles(
},
...overrides?.ExpansionPanel?.heading?.root
},
flexGrow: {
flex: 1
},
content: {
overflow: "hidden",
color: palette.additional["gray"][8],
Expand Down Expand Up @@ -90,37 +94,54 @@ export interface IExpansionPanelProps {
children?: ReactNode | ReactNode[] | null
active?: boolean
variant?: "basic" | "borderless"
iconPosition?: "left" | "right"
toggle?: (state: boolean) => void
injectedClasses?: {
root?: string
heading?: string
content?: string
}
}

const ExpansionPanel: React.FC<IExpansionPanelProps> = ({
children,
header,
variant = "basic",
toggle,
active
}: IExpansionPanelProps) => {
const ExpansionPanel = ({ children, header, iconPosition, variant = "basic", toggle, active, injectedClasses }: IExpansionPanelProps) => {
const classes = useStyles()
const [activeInternal, setActive] = useState(!!active)
const handleToggle = () => {
const handleToggle = useCallback(() => {
toggle && toggle(!activeInternal)
setActive(!activeInternal)
}
}, [activeInternal, toggle])

return (
<div className={clsx(classes.root, variant)}>
<div className={clsx(classes.root, variant, injectedClasses?.root)}>
<section
onClick={() => handleToggle()}
className={clsx(classes.heading, variant, {
["active"]: active != undefined ? active : activeInternal
})}
onClick={handleToggle}
className={clsx(
classes.heading,
variant,
injectedClasses?.heading,
{
["active"]: active !== undefined ? active : activeInternal
}
)}
>
<DirectionalRightIcon className={classes.icon} />
{iconPosition === "left" && <DirectionalRightIcon className={classes.icon} />}
<Typography>{header}</Typography>
{iconPosition === "right" && (
<>
<div className={classes.flexGrow} />
<DirectionalRightIcon className={classes.icon} />
</>
)}
</section>
<section
className={clsx(classes.content, variant, {
["active"]: active != undefined ? active : activeInternal
})}
className={clsx(
classes.content,
variant,
injectedClasses?.content,
{
["active"]: active !== undefined ? active : activeInternal
}
)}
>
{children}
</section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { CheckCircleSvg, CopySvg, KeySvg, Typography } from "@chainsafe/common-c
import { Trans } from "@lingui/macro"
import { useThresholdKey } from "../../../Contexts/ThresholdKeyContext"
import { CSFTheme } from "../../../Themes/types"
import bowser from "bowser"
import clsx from "clsx"
import { ROUTE_LINKS } from "../../FilesRoutes"

Expand Down Expand Up @@ -196,7 +195,7 @@ const SignInMethods = ({ goToComplete, goToMnemonic, goToPassword, goToSkip, cla
desktop && (
<Typography variant="h5">
<Trans>Saved</Trans>{" "}
{`${bowser.parse(browserShares[0].userAgent).browser.name} ${bowser.parse(browserShares[0].userAgent).browser.version}`}
{`${browserShares[0].browser.name} ${browserShares[0].browser.version}`}
</Typography>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import React, { useCallback, useState } from "react"
import {
makeStyles,
createStyles
} from "@chainsafe/common-theme"
import { CSFTheme } from "../../../../Themes/types"
import { Button, ExpansionPanel, Typography } from "@chainsafe/common-components"
import clsx from "clsx"
import { Trans } from "@lingui/macro"
import dayjs from "dayjs"
import { BrowserShare, useThresholdKey } from "../../../../Contexts/ThresholdKeyContext"

const useStyles = makeStyles(({ palette, constants, animation, breakpoints }: CSFTheme) =>
createStyles({
panelHeading: {
backgroundColor: palette.additional["gray"][4],
borderRadius: "10px",
padding: `${constants.generalUnit}px 0 ${constants.generalUnit}px ${constants.generalUnit * 2}px`,
transition: `border-radius ${animation.transform}ms`,
"&.active": {
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0
}
},
panelBody: {
backgroundColor: palette.additional["gray"][4],
padding: 0,
borderBottomLeftRadius: "10px",
borderBottomRightRadius: "10px",
marginTop: `-${constants.generalUnit}px`
},
panelContent: {
marginTop: constants.generalUnit,
marginBottom: constants.generalUnit,
color: palette.additional["gray"][9],
display: "flex",
flexDirection: "column"
},
subtitle: {
paddingBottom: constants.generalUnit,
lineHeight: "24px",
[breakpoints.down("md")]: {
fontSize: "14px",
lineHeight: "22px"
}
},
subtitleLast: {
lineHeight: "24px",
[breakpoints.down("md")]: {
fontSize: "14px",
lineHeight: "22px"
}
},
lightSubtitle: {
color: palette.additional["gray"][8],
paddingBottom: constants.generalUnit * 0.5,
[breakpoints.down("md")]: {
fontSize: "14px",
lineHeight: "22px"
}
},
actionBox: {
marginTop: constants.generalUnit * 2
},
spanMarginRight: {
marginRight: "0.5rem"
}
})
)

function download(filename: string, text: string) {
const element = document.createElement("a")
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text))
element.setAttribute("download", filename)
element.style.display = "none"
document.body.appendChild(element)
element.click()
document.body.removeChild(element)
}

const BrowserPanel = ({ dateAdded, shareIndex, browser, os }: BrowserShare) => {
const { deleteShare, getSerializedDeviceShare } = useThresholdKey()
const classes = useStyles()
const [showPanel, setShowPanel] = useState(false)
const [loadingDeleteShare, setLoadingDeleteShare] = useState(false)
const [loadingDownloadKey, setLoadingDownloadKey] = useState(false)

const onDeleteShare = useCallback(() => {
setLoadingDeleteShare(true)
deleteShare(shareIndex)
.then(() => {
setLoadingDeleteShare(false)
setShowPanel(false)
}).catch((e) => {
console.error(e)
setLoadingDeleteShare(false)
})
},
[deleteShare, shareIndex])

const onDownloadKey = useCallback(() => {
setLoadingDownloadKey(true)
getSerializedDeviceShare(shareIndex)
.then((mnemonicKey) => {
if (mnemonicKey) {
download(`Chainsafe Files - ${browser.name || ""} key.txt`, mnemonicKey)
}
setLoadingDownloadKey(false)
})
.catch((e) => {
console.error(e)
setLoadingDownloadKey(false)
})
}, [browser.name, getSerializedDeviceShare, shareIndex])

return (
<ExpansionPanel
header={browser.name || ""}
variant="borderless"
injectedClasses={{ heading: clsx(classes.panelHeading, showPanel && "active"), content: classes.panelBody }}
iconPosition={"right"}
active={showPanel}
toggle={() => setShowPanel(!showPanel)}
>
<div className={classes.panelBody}>
<div className={classes.panelContent}>
<Typography
variant="body1"
component="p"
className={classes.subtitle}
>
<span className={classes.spanMarginRight}><Trans>Operating system:</Trans></span>{os.name}
</Typography>
<Typography
variant="body1"
component="p"
className={classes.subtitle}
>
<span className={classes.spanMarginRight}>
<Trans>Browser:</Trans>
</span>
<span className={classes.spanMarginRight}>{browser.name}</span>{browser.version}
</Typography>
<Typography
variant="body1"
component="p"
className={classes.subtitleLast}
>
<span className={classes.spanMarginRight}><Trans>Saved on:</Trans></span>{dayjs(dateAdded).format("DD MMM YYYY - HH:mm")}
</Typography>
<div className={classes.actionBox}>
<Typography
variant="body1"
component="p"
className={classes.lightSubtitle}
>
<Trans>Your recovery key can be used to restore your account in place of your backup phrase.</Trans>
</Typography>
<Button
size="small"
loading={loadingDownloadKey}
disabled={loadingDownloadKey}
onClick={onDownloadKey}
>
<Trans>Download recovery key</Trans>
</Button>
</div>
<div className={classes.actionBox}>
<Typography
variant="body1"
component="p"
className={classes.lightSubtitle}
>
<Trans>Forgetting this browser deletes this from your list of sign-in methods.
You will not be able to forget a browser if you only have two methods set up.</Trans>
</Typography>
<Button
size="small"
loading={loadingDeleteShare}
onClick={onDeleteShare}
disabled={loadingDeleteShare}
>
<Trans>Forget this browser</Trans>
</Button>
</div>
</div>
</div>
</ExpansionPanel>
)
}

export default BrowserPanel
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from "react"
import {
makeStyles,
createStyles
} from "@chainsafe/common-theme"
import { CSFTheme } from "../../../../Themes/types"
import { Typography } from "@chainsafe/common-components"
import BrowserPanel from "./BrowserPanel"
import { useThresholdKey } from "../../../../Contexts/ThresholdKeyContext"

const useStyles = makeStyles(({ constants, breakpoints }: CSFTheme) =>
createStyles({
root: {
paddingBottom: constants.generalUnit,
[breakpoints.down("md")]: {
padding: `0 ${constants.generalUnit * 2}px`
}
},
title: {
fontSize: "16px",
lineHeight: "24px",
paddingBottom: constants.generalUnit * 2
},
expansionContainer: {
marginBottom: constants.generalUnit * 3
}
})
)

const SavedBrowsers: React.FC = () => {
const classes = useStyles()
const { browserShares } = useThresholdKey()

return (
<div className={classes.root}>
<Typography component="p" variant="body1" className={classes.title}>
Saved Browsers
</Typography>
{browserShares.map((bs, i) => (
<div key={i} className={classes.expansionContainer}>
<BrowserPanel {...bs}/>
</div>
))}
</div>
)
}

export default SavedBrowsers
Loading