Files
dydx-v4-web/src/views/dialogs/MobileSignInDialog.tsx
T
moo-onthelawn 1c1d602bf3 TRCL-3491 Add "System" setting to themeing options, and match cmd+k dialog to new display preferences (#265)
* clean up

* clean up UI

* add functionality, without user exposure yet

* fix commit history

* remove unnecessary change

* lint

* implemented functionality

* clean up, add into cmd+k

* fix imports

* TRCL-3537 DisplaySettings + Themeing Polish (#267)

* wip

* make system panel reflect user prefs

* clean up PnlChart

* testing changes removal

* clean up logo styling

* fix export of dark dots background

* remove comment

* fix system panel color background
2024-02-01 20:03:10 -05:00

218 lines
5.7 KiB
TypeScript

import { useMemo, useState } from 'react';
import styled, { AnyStyledComponent, css } from 'styled-components';
import { AES } from 'crypto-js';
import { AlertType } from '@/constants/alerts';
import { ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { useAccounts, useStringGetter } from '@/hooks';
import { layoutMixins } from '@/styles/layoutMixins';
import { AlertMessage } from '@/components/AlertMessage';
import { Dialog } from '@/components/Dialog';
import { Icon, IconName } from '@/components/Icon';
import { TimeoutButton } from '@/components/TimeoutButton';
import { ToggleButton } from '@/components/ToggleButton';
import { QrCode } from '@/components/QrCode';
import { log } from '@/lib/telemetry';
type ElementProps = {
setIsOpen: (open: boolean) => void;
};
enum MobileSignInState {
Waiting = 'Waiting',
Scanning = 'Scanning',
Connected = 'Connected',
Failed = 'Failed',
}
const MobileQrCode = ({
encryptionKey,
isShowing,
onClick,
}: {
encryptionKey: string;
isShowing: boolean;
onClick: () => void;
}) => {
const stringGetter = useStringGetter();
const { dydxAddress, hdKey } = useAccounts();
const { mnemonic, privateKey, publicKey } = hdKey ?? {};
if (!mnemonic || !privateKey || !publicKey) {
log('MobileQrCode', new Error('missing hdKey'));
return null;
}
const data = {
mnemonic,
cosmosAddress: dydxAddress,
pubkeyHex: Buffer.from(publicKey).toString('hex'),
privkeyHex: Buffer.from(privateKey).toString('hex'),
};
const encryptedData = AES.encrypt(JSON.stringify(data), encryptionKey).toString();
return (
<Styled.QrCodeContainer isShowing={isShowing} onClick={onClick}>
<QrCode hasLogo size={432} value={encryptedData} />
<span>{stringGetter({ key: STRING_KEYS.CLICK_TO_SHOW })}</span>
</Styled.QrCodeContainer>
);
};
export const MobileSignInDialog = ({ setIsOpen }: ElementProps) => {
const [currentState, setCurrentState] = useState(MobileSignInState.Waiting);
const [isScanning, setIsScanning] = useState(false);
const stringGetter = useStringGetter();
// Generate a random 6 digit encryptionKey
const encryptionKey = useMemo(() => String(Math.floor(100000 + Math.random() * 900000)), []);
const title = {
[MobileSignInState.Waiting]: stringGetter({ key: STRING_KEYS.TITLE_SIGN_INTO_MOBILE }),
[MobileSignInState.Scanning]: stringGetter({ key: STRING_KEYS.TITLE_SCAN_FROM_APP }),
[MobileSignInState.Connected]: stringGetter({ key: STRING_KEYS.TITLE_CONNECTED }),
[MobileSignInState.Failed]: stringGetter({ key: STRING_KEYS.TITLE_FAILED_TO_CONNECT }),
}[currentState];
const content = {
[MobileSignInState.Waiting]: (
<>
<Styled.WaitingSpan>
<p>{stringGetter({ key: STRING_KEYS.DESCRIPTION_ABOUT_TO_TRANSFER })}</p>
<p>
<strong>{stringGetter({ key: STRING_KEYS.DESCRIPTION_NEVER_SHARE })} </strong>
</p>
</Styled.WaitingSpan>
<TimeoutButton
onClick={() => setCurrentState(MobileSignInState.Scanning)}
timeoutInSeconds={8}
>
{stringGetter({ key: STRING_KEYS.CONTINUE })}
</TimeoutButton>
</>
),
[MobileSignInState.Scanning]: (
<>
<p>
{stringGetter({
key: STRING_KEYS.WHILE_ONBOARDING,
params: {
SYNC_WITH_DESKTOP: (
<strong>{stringGetter({ key: STRING_KEYS.SYNC_WITH_DESKTOP })}</strong>
),
},
})}
</p>
<span>
Encryption Key: <strong>{encryptionKey}</strong>
</span>
<MobileQrCode
encryptionKey={encryptionKey}
isShowing={isScanning}
onClick={() => setIsScanning(!isScanning)}
/>
<AlertMessage type={AlertType.Warning}>
{stringGetter({ key: STRING_KEYS.NEVER_SHARE })}
</AlertMessage>
<footer>
<span>
{stringGetter({ key: !isScanning ? STRING_KEYS.READY : STRING_KEYS.NOT_READY })}
</span>
<ToggleButton
size={ButtonSize.Small}
isPressed={isScanning}
onPressedChange={setIsScanning}
slotLeft={<Icon iconName={!isScanning ? IconName.Show : IconName.Hide} />}
>
{stringGetter({ key: !isScanning ? STRING_KEYS.SHOW_CODE : STRING_KEYS.HIDE_CODE })}
</ToggleButton>
</footer>
</>
),
[MobileSignInState.Connected]: null,
[MobileSignInState.Failed]: null,
}[currentState];
return (
<Dialog isOpen setIsOpen={setIsOpen} title={title}>
<Styled.Content>{content}</Styled.Content>
</Dialog>
);
};
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Content = styled.div`
${layoutMixins.column}
gap: 1rem;
strong {
font-weight: 900;
color: var(--color-text-2);
}
footer {
${layoutMixins.row}
justify-content: space-between;
svg {
width: auto;
}
}
`;
Styled.WaitingSpan = styled.span`
strong {
color: var(--color-warning);
}
`;
Styled.QrCodeContainer = styled.figure<{ isShowing: boolean }>`
${layoutMixins.stack}
overflow: hidden;
border-radius: 0.75em;
cursor: pointer;
transition: 0.2s;
&:hover {
filter: brightness(var(--hover-filter-base));
}
> * {
position: relative;
transition: 0.16s;
}
> :first-child {
pointer-events: none;
${({ isShowing }) =>
!isShowing &&
css`
filter: blur(1rem) brightness(1.4);
will-change: filter;
`}
}
> span {
place-self: center;
font-size: 1.4em;
color: var(--color-text-2);
${({ isShowing }) =>
isShowing &&
css`
opacity: 0;
`}
}
`;