fix: error message page

This commit is contained in:
fbwoolf
2022-04-19 16:47:27 -05:00
committed by kyranjamie
parent 14f2045e7c
commit e5799617b9
11 changed files with 148 additions and 57 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1,67 @@
import { ReactNode } from 'react';
import { Box, Flex, Text, color, Button } from '@stacks/ui';
import { Title } from '@app/components/typography';
import GenericError from '@assets/images/generic-error.png';
interface ErrorProps {
body: string;
helpTextList: ReactNode[];
onClose(): void;
title: string;
}
export function GenericErrorLayout(props: ErrorProps) {
const { body, helpTextList, onClose, title } = props;
return (
<Flex alignItems="center" flexDirection="column" px={['loose', 'unset']} width="100%">
<Box mt="loose">
<img src={GenericError} width="106px" />
</Box>
<Title fontSize={4} mt="loose">
{title}
</Title>
<Text
color={color('text-caption')}
fontSize="16px"
lineHeight="1.6"
mt="base"
textAlign="center"
>
{body}
</Text>
<Box
as="ul"
border="2px solid #EFEFF2"
borderRadius="12px"
color={color('text-caption')}
fontSize="14px"
lineHeight="1.6"
mt="extra-loose"
pb="loose"
pl="40px"
pr="loose"
pt="tight"
width="100%"
>
{helpTextList}
<Box as="li" mt="base">
Still stuck? Reach out to{' '}
<Text
as="button"
color={color('accent')}
onClick={() => {
window.open('mailto:support@hiro.so');
window.close();
}}
>
support@hiro.so
</Text>
</Box>
</Box>
<Button fontSize="14px" mt="base-tight" onClick={onClose} p="base" variant="link">
Close window
</Button>
</Flex>
);
}

View File

@@ -0,0 +1,22 @@
import { ReactNode } from 'react';
import { Header } from '@app/components/header';
import { useRouteHeader } from '@app/common/hooks/use-route-header';
import { GenericErrorLayout } from './generic-error.layout';
interface ErrorProps {
body: string;
helpTextList: ReactNode[];
onClose?(): void;
title: string;
}
export function GenericError(props: ErrorProps) {
const { body, helpTextList, onClose = () => window.close(), title } = props;
useRouteHeader(<Header hideActions />);
return (
<GenericErrorLayout body={body} helpTextList={helpTextList} onClose={onClose} title={title} />
);
}

View File

@@ -62,29 +62,31 @@ export const Header: React.FC<HeaderProps> = memo(props => {
</Box>
) : null}
{!title && (!onClose || desktopViewport) ? (
<Stack
alignItems="flex-end"
<Flex
alignItems="center"
flexBasis="60%"
height="36px"
isInline
justifyContent={onClose ? 'center' : 'unset'}
>
<HiroWalletLogo
data-testid={OnboardingSelectors.HiroWalletLogoRouteToHome}
isClickable={hiroWalletLogoIsClickable}
onClick={hiroWalletLogoIsClickable ? () => navigate(RouteUrls.Home) : undefined}
/>
<Caption
color={color('text-caption')}
display={!version ? 'none' : 'unset'}
fontFamily="mono"
marginRight="10px"
mb="2px"
variant="c3"
>
{version}
</Caption>
</Stack>
<Flex alignItems="flex-end">
<HiroWalletLogo
data-testid={OnboardingSelectors.HiroWalletLogoRouteToHome}
isClickable={hiroWalletLogoIsClickable}
onClick={hiroWalletLogoIsClickable ? () => navigate(RouteUrls.Home) : undefined}
/>
<Caption
color={color('text-caption')}
display={!version ? 'none' : 'unset'}
fontFamily="mono"
marginRight="10px"
mb="2px"
ml="tight"
variant="c3"
>
{version}
</Caption>
</Flex>
</Flex>
) : (
<Title
alignSelf="center"

View File

@@ -1,4 +1,5 @@
import { memo } from 'react';
import { Navigate } from 'react-router-dom';
import { STXTransferPayload, TransactionTypes } from '@stacks/connect';
import { color, Stack, useClipboard, Fade, Flex } from '@stacks/ui';
import { truncateMiddle } from '@stacks/ui-utils';
@@ -16,6 +17,7 @@ import {
} from '@app/store/transactions/requests.hooks';
import { useCurrentAccountAvailableStxBalance } from '@app/store/accounts/account.hooks';
import { useCurrentAccount } from '@app/store/accounts/account.hooks';
import { RouteUrls } from '@shared/route-urls';
export const FeeInsufficientFundsErrorMessage = memo(props => {
const currentAccount = useCurrentAccount();
@@ -116,44 +118,11 @@ export const IncorrectContractAddressMessage = memo(props => {
);
});
export const UnauthorizedErrorMessage = memo(props => {
useScrollLock(true);
return (
<Fade in>
{styles => (
<Flex
position="absolute"
width="100%"
height="100vh"
zIndex={99}
left={0}
top={0}
alignItems="center"
justifyContent="center"
p="loose"
bg="rgba(0,0,0,0.35)"
backdropFilter="blur(10px)"
style={styles}
>
<ErrorMessage
title="Unauthorized request"
body="The transaction request was not properly authorized by any of your accounts. If you've logged in to this app before, then you might need to re-authenticate into this application before attempting to sign a transaction with the Hiro Wallet."
border={'1px solid'}
borderColor={color('border')}
boxShadow="high"
css={{
'& > *': {
pointerEvents: 'all',
},
}}
{...props}
/>
</Flex>
)}
</Fade>
);
export const UnauthorizedRequestRedirect = memo(() => {
return <Navigate to={RouteUrls.UnauthorizedRequest} />;
});
// TODO: Change this to new Error component?
export const ExpiredRequestErrorMessage = memo(props => {
useScrollLock(true);
return (

View File

@@ -10,7 +10,7 @@ import {
IncorrectContractAddressMessage,
NoContractErrorMessage,
StxTransferInsufficientFundsErrorMessage,
UnauthorizedErrorMessage,
UnauthorizedRequestRedirect,
} from './error-messages';
export enum TransactionErrorReason {
@@ -49,7 +49,7 @@ const TransactionErrorSuspense = memo(() => {
case TransactionErrorReason.FeeInsufficientFunds:
return <FeeInsufficientFundsErrorMessage />;
case TransactionErrorReason.Unauthorized:
return <UnauthorizedErrorMessage />;
return <UnauthorizedRequestRedirect />;
case TransactionErrorReason.ExpiredRequest:
return <ExpiredRequestErrorMessage />;
default:

View File

@@ -0,0 +1,16 @@
import { Box } from '@stacks/ui';
import { GenericError } from '@app/components/generic-error/generic-error';
const body = `The transaction request was not properly authorized by any of your Hiro Wallet accounts. This typically happens if you've logged into this app before using another account.`;
const helpTextList = [
<Box as="li" mt="base">
Please sign out of the app and sign back in to re-authenticate into the application. This should
help you successfully sign your transaction with the Hiro Wallet.
</Box>,
];
const title = 'Unauthorized request';
export function UnauthorizedRequest() {
return <GenericError body={body} helpTextList={helpTextList} title={title} />;
}

View File

@@ -23,6 +23,7 @@ import { BuyPage } from '@app/pages/buy/buy';
import { BackUpSecretKeyPage } from '@app/pages/onboarding/back-up-secret-key/back-up-secret-key';
import { WelcomePage } from '@app/pages/onboarding/welcome/welcome';
import { useHasStateRehydrated } from '@app/store';
import { UnauthorizedRequest } from '@app/pages/unauthorized-request/unauthorized-request';
import { RouteUrls } from '@shared/route-urls';
import { useOnWalletLock } from './hooks/use-on-wallet-lock';
@@ -145,6 +146,7 @@ export function AppRoutes(): JSX.Element | null {
</AccountGate>
}
/>
<Route path={RouteUrls.UnauthorizedRequest} element={<UnauthorizedRequest />} />
<Route
path={RouteUrls.ViewSecretKey}
element={

View File

@@ -16,6 +16,7 @@ export enum RouteUrls {
Send = '/send',
SignOutConfirm = '/sign-out',
TransactionRequest = '/transaction',
UnauthorizedRequest = '/unauthorized-request',
ViewSecretKey = '/view-secret-key',
// Locked wallet route
Unlock = '/unlock',

View File

@@ -18,6 +18,7 @@ describe(`Settings integration tests`, () => {
await wallet.signUp();
await wallet.waitForHideOnboardingsStepsButton();
await wallet.clickHideSteps();
await delay(500);
});
afterAll(async () => {
@@ -27,6 +28,7 @@ describe(`Settings integration tests`, () => {
});
it('should be able to create a new account', async () => {
await wallet.waitForSettingsButton();
await wallet.clickSettingsButton();
await wallet.page.click(createTestSelector(SettingsSelectors.CreateAccountBtn));
await delay(500);
@@ -39,6 +41,7 @@ describe(`Settings integration tests`, () => {
it(`should be able to create ${numOfAccountsToTest} new accounts then switch between them`, async () => {
for (let i = 0; i < numOfAccountsToTest - 1; i++) {
await wallet.waitForSettingsButton();
await wallet.clickSettingsButton();
await wallet.page.click(createTestSelector(SettingsSelectors.CreateAccountBtn));
await delay(500);
@@ -46,6 +49,7 @@ describe(`Settings integration tests`, () => {
}
for (let i = 0; i < numOfAccountsToTest; i++) {
await wallet.waitForSettingsButton();
await wallet.clickSettingsButton();
await wallet.page.click(createTestSelector(SettingsSelectors.SwitchAccount));
await delay(500);
@@ -64,6 +68,7 @@ describe(`Settings integration tests`, () => {
});
it('should be able to view and save secret key to clipboard', async () => {
await wallet.waitForSettingsButton();
await wallet.clickSettingsButton();
await wallet.page.click(createTestSelector(SettingsSelectors.ViewSecretKeyListItem));
await wallet.page.click(createTestSelector(SettingsSelectors.CopyKeyToClipboardBtn));
@@ -75,6 +80,7 @@ describe(`Settings integration tests`, () => {
it('should be able to sign out, lock and unlock the extension', async () => {
const secretKey = await wallet.getSecretKey();
await wallet.waitForSettingsButton();
await wallet.clickSettingsButton();
await wallet.page.click(createTestSelector(SettingsSelectors.SignOutListItem));
await wallet.page.click(wallet.$signOutConfirmHasBackupCheckbox);
@@ -84,6 +90,7 @@ describe(`Settings integration tests`, () => {
const password = randomString(15);
await wallet.enterNewPassword(password);
await wallet.enterConfirmPasswordAndClickDone(password);
await wallet.waitForSettingsButton();
await wallet.clickSettingsButton();
await wallet.page.click(createTestSelector(SettingsSelectors.LockListItem));
await wallet.enterPasswordAndUnlockWallet(password);
@@ -94,6 +101,7 @@ describe(`Settings integration tests`, () => {
});
it('should be able to change network', async () => {
await wallet.waitForSettingsButton();
await wallet.clickSettingsButton();
const currentNetwork = await wallet.page.textContent(
createTestSelector(SettingsSelectors.CurrentNetwork)

View File

@@ -104,6 +104,10 @@ export class WalletPage {
await this.page.click(this.$hideStepsBtn);
}
async waitForSettingsButton() {
await this.page.waitForSelector(this.$settingsButton, { timeout: 30000 });
}
async waitForHomePage() {
await this.page.waitForSelector(this.$homePageBalancesList, { timeout: 30000 });
}