mirror of
https://github.com/zhigang1992/wallet.git
synced 2026-04-29 05:05:32 +08:00
fix: error message page
This commit is contained in:
BIN
public/assets/images/generic-error.png
Normal file
BIN
public/assets/images/generic-error.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
67
src/app/components/generic-error/generic-error.layout.tsx
Normal file
67
src/app/components/generic-error/generic-error.layout.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
22
src/app/components/generic-error/generic-error.tsx
Normal file
22
src/app/components/generic-error/generic-error.tsx
Normal 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} />
|
||||
);
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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:
|
||||
|
||||
16
src/app/pages/unauthorized-request/unauthorized-request.tsx
Normal file
16
src/app/pages/unauthorized-request/unauthorized-request.tsx
Normal 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} />;
|
||||
}
|
||||
@@ -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={
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user