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

feat: improve sensitive data exposure page #672

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
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import React, { CSSProperties, useCallback, useMemo, useState } from 'react';
import React, { CSSProperties, useCallback, useEffect, useMemo, useState } from 'react';
import styled, { css, FlattenSimpleInterpolation, useTheme } from 'styled-components';

import { WebText } from '../web-text';
import { Row, View } from '../base';
import IconCopy from '@assets/web/icon-copy';
import { Row, View } from '../base';
import { WebText } from '../web-text';

interface WebCopyButtonProps {
width?: CSSProperties['width'];
height?: CSSProperties['height'];
copyText: string;
clearClipboardTimeout?: number;
onCopy?: () => void;
}

const StyledContainer = styled(Row) <{ clicked: boolean }>`
const StyledContainer = styled(Row)<{ clicked: boolean }>`
display: flex;
padding: 0 14px 0 14px;
gap: 4px;
Expand All @@ -21,7 +23,7 @@ const StyledContainer = styled(Row) <{ clicked: boolean }>`
border: 1px solid #212429;
background: transparent;
cursor: pointer;
user-select:none;
user-select: none;

svg {
width: 16px;
Expand All @@ -39,15 +41,23 @@ const StyledContainer = styled(Row) <{ clicked: boolean }>`
}
}

${({ clicked }): FlattenSimpleInterpolation | string => clicked ? css`
background: rgba(255, 255, 255, 0.08);
`: ''}
${({ clicked }): FlattenSimpleInterpolation | string =>
clicked
? css`
background: rgba(255, 255, 255, 0.08);
`
: ''}
`;

const CLEAR_CLIPBOARD_TIMEOUT = 30_000; // 30 seconds
const COPY_TOOLTIP_DISPLAY_TIMEOUT = 2_000; // 2 seconds

export const WebCopyButton: React.FC<WebCopyButtonProps> = ({
width = 'fit-content',
height = 32,
copyText,
clearClipboardTimeout = CLEAR_CLIPBOARD_TIMEOUT,
onCopy,
}) => {
const theme = useTheme();
const [clicked, setClicked] = useState(false);
Expand All @@ -58,7 +68,7 @@ export const WebCopyButton: React.FC<WebCopyButtonProps> = ({
return 'Copied!';
}
return 'Copy';
}, [clicked])
}, [clicked]);

const activated = useMemo(() => {
return mouseover || clicked;
Expand All @@ -69,11 +79,14 @@ export const WebCopyButton: React.FC<WebCopyButtonProps> = ({
return;
}
setClicked(true);

navigator.clipboard.writeText(copyText);
onCopy && onCopy();

setTimeout(() => {
setClicked(false);
}, 2000);
}, [clicked, copyText]);
}, COPY_TOOLTIP_DISPLAY_TIMEOUT);
}, [clicked, copyText, onCopy]);

const onMouseOver = useCallback(() => {
setMouseover(true);
Expand All @@ -83,6 +96,20 @@ export const WebCopyButton: React.FC<WebCopyButtonProps> = ({
setMouseover(false);
}, []);

useEffect(() => {
if (!clicked) {
return;
}

const timeout = setTimeout(() => {
navigator?.clipboard?.writeText('');
}, clearClipboardTimeout);

return () => {
clearTimeout(timeout);
};
}, [clicked, clearClipboardTimeout]);

return (
<StyledContainer
style={{
Expand All @@ -96,25 +123,19 @@ export const WebCopyButton: React.FC<WebCopyButtonProps> = ({
onMouseOut={onMouseLeave}
>
{clicked ? (
<WebText
color={activated ? theme.webNeutral._100 : theme.webNeutral._500}
type='body6'
>
<WebText color={activated ? theme.webNeutral._100 : theme.webNeutral._500} type='body6'>
{buttonStr}
</WebText>
) : (
<React.Fragment>
<View>
<IconCopy />
</View>
<WebText
color={activated ? theme.webNeutral._100 : theme.webNeutral._500}
type='title6'
>
<WebText color={activated ? theme.webNeutral._100 : theme.webNeutral._500} type='title6'>
{buttonStr}
</WebText>
</React.Fragment>
)}
</StyledContainer>
);
}
};
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { ReactElement, useState } from 'react';
import { ReactElement, useMemo, useState } from 'react';
import styled, { useTheme } from 'styled-components';

import IconWarning from '@assets/web/warning.svg';

import { Row, View, WebButton, WebCheckBox, WebImg, WebText } from '@components/atoms';
import { WebCopyButton } from '@components/atoms/web-copy-button';
import { WebHoldButton } from '@components/atoms/web-hold-button';
import { WebSeedBox } from '@components/molecules';
import { UseWalletCreateReturn } from '@hooks/web/use-wallet-create-screen';
import { WebHoldButton } from '@components/atoms/web-hold-button';
import { WebCopyButton } from '@components/atoms/web-copy-button';

const StyledContainer = styled(View)`
width: 100%;
Expand Down Expand Up @@ -36,6 +36,18 @@ const GetMnemonicStep = ({
const [ableToReveal, setAbleToReveal] = useState(false);
const [agreeAbleToReveals, setAgreeAbleToReveals] = useState(false);
const [checkSavedMnemonic, setCheckSavedMnemonic] = useState(false);
const [copied, setCopied] = useState(false);

const warningCopiedMessage = useMemo(() => {
if (!copied) {
return '';
}
return 'You have copied sensitive info. Make sure you do not paste it in public or shared environments, and clear your clipboard as soon as you’ve used it.';
}, [copied]);

const onCopy = (): void => {
setCopied(true);
};

return (
<StyledContainer>
Expand All @@ -47,6 +59,15 @@ const GetMnemonicStep = ({
This phrase is the only way to recover this wallet. DO NOT share it with anyone.
</WebText>
</StyledWarnBox>

{warningCopiedMessage && (
<StyledWarnBox>
<WebImg src={IconWarning} size={20} />
<WebText type='body6' color={theme.webWarning._100}>
{warningCopiedMessage}
</WebText>
</StyledWarnBox>
)}
</StyledMessageBox>

<View style={{ width: '100%', gap: 16 }}>
Expand All @@ -56,7 +77,7 @@ const GetMnemonicStep = ({
<>
<Row style={{ justifyContent: 'center', columnGap: 12 }}>
<WebHoldButton onFinishHold={(response): void => setShowBlur(!response)} />
<WebCopyButton width={80} copyText={seeds} />
<WebCopyButton width={80} copyText={seeds} onCopy={onCopy} />
</Row>
<Row style={{ columnGap: 8, alignItems: 'center', marginTop: 8 }}>
<WebCheckBox
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const WalletExportResult: React.FC<WalletExportResultProps> = ({ exportType, exp
const theme = useTheme();
const [blur, setBlur] = useState(true);
const [initializedDone, setInitializedDone] = useState(false);
const [copied, setCopied] = useState(false);

const title = useMemo(() => {
if (exportType === 'PRIVATE_KEY') {
Expand All @@ -63,6 +64,13 @@ const WalletExportResult: React.FC<WalletExportResultProps> = ({ exportType, exp
return 'Your seed phrase is the only way to recover your wallet. Keep it somewhere safe and secret.';
}, [exportType]);

const warningCopiedMessage = useMemo(() => {
if (!copied) {
return '';
}
return 'You have copied sensitive info. Make sure you do not paste it in public or shared environments, and clear your clipboard as soon as you’ve used it.';
}, [copied]);

const seeds = useMemo((): string[] => {
if (exportType !== 'SEED_PHRASE' || !exportData) {
return [];
Expand Down Expand Up @@ -97,6 +105,10 @@ const WalletExportResult: React.FC<WalletExportResultProps> = ({ exportType, exp
});
};

const onCopy = (): void => {
setCopied(true);
};

return (
<StyledContainer>
<StyledMessageBox>
Expand All @@ -107,6 +119,15 @@ const WalletExportResult: React.FC<WalletExportResultProps> = ({ exportType, exp
{warningMessage}
</WebText>
</StyledWarnBox>

{warningCopiedMessage && (
<StyledWarnBox center={exportType === 'SEED_PHRASE'}>
<WebImg src={IconWarning} size={20} />
<WebText type='body6' color={theme.webWarning._100}>
{warningCopiedMessage}
</WebText>
</StyledWarnBox>
)}
</StyledMessageBox>

<StyledInputBox>
Expand All @@ -116,7 +137,7 @@ const WalletExportResult: React.FC<WalletExportResultProps> = ({ exportType, exp
)}
<Row style={{ gap: 16, justifyContent: 'center' }}>
<WebHoldButton onFinishHold={onFinishHold} />
<WebCopyButton width={80} copyText={exportData || ''} />
<WebCopyButton width={80} copyText={exportData || ''} onCopy={onCopy} />
</Row>
</StyledInputBox>

Expand Down
Loading