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

Show a nicer UI when minifront can't connect to the extension, and for other errors #752

Merged
merged 8 commits into from
Mar 14, 2024
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
29 changes: 29 additions & 0 deletions apps/minifront/src/components/extension-unavailable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { SplashPage } from '@penumbra-zone/ui';
import { HeadTag } from './metadata/head-tag';

const NODE_STATUS_PAGE_URL =
window.location.hostname === 'localhost' ? 'http://localhost:5174' : '/';
Comment on lines +4 to +5
Copy link
Contributor

@turbocrime turbocrime Mar 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this probably needs a .env and webpack define

edit: i guess this is vite

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, it seems to be the same as the grpc endpoint uri

Copy link
Contributor Author

@jessepinho jessepinho Mar 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is tricky... we'd want to define the port number in .env, you mean, right? that would mean putting 5174 in node-status's .env file, and then importing it from minifront? (Cause I assume we'd also want to use the .env value in node-status's package.json dev script.)


export const ExtensionUnavailable = () => {
return (
<>
<HeadTag />

<SplashPage title='Penumbra extension unavailble'>
<p className='mb-2'>We can&apos;t currently connect to the Penumbra extension.</p>
<p className='mb-2'>
This page may have been left open for too long, causing a timeout. Please reload this page
and see if that fixes the issue.
</p>
<p>
If it doesn&apos;t, the RPC node that you&apos;re connected to could be down. Check{' '}
<a href={NODE_STATUS_PAGE_URL} className='underline'>
the node&apos;s status page
</a>{' '}
and, if it is down, consider switching to a different RPC URL in the Penumbra
extension&apos;s settings.
</p>
</SplashPage>
Comment on lines +12 to +26
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these cases should be readily distinguishable by the error message

</>
);
};
5 changes: 5 additions & 0 deletions apps/minifront/src/components/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { SplashPage } from '@penumbra-zone/ui';

export const NotFound = () => {
return <SplashPage title='404'>That page could not be found. </SplashPage>;
};
18 changes: 13 additions & 5 deletions apps/minifront/src/components/shared/error-boundary.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import { useRouteError } from 'react-router-dom';
import { isRouteErrorResponse, useRouteError } from 'react-router-dom';
import { PraxNotConnectedError } from '@penumbra-zone/client';
import { ExtensionNotConnected } from '../extension-not-connected';
import { NotFound } from '../not-found';
import { ExtensionUnavailable } from '../extension-unavailable';
import { Code, ConnectError } from '@connectrpc/connect';
import { SplashPage } from '@penumbra-zone/ui';

export const ErrorBoundary = () => {
const error = useRouteError();

if (error instanceof ConnectError && error.code === Code.Unavailable) {
return <ExtensionUnavailable />;
}
if (error instanceof PraxNotConnectedError) return <ExtensionNotConnected />;
if (isRouteErrorResponse(error) && error.status === 404) return <NotFound />;

console.error(error);
console.error('ErrorBoundary caught error:', error);

return (
<div className='text-red'>
<h1 className='text-xl'>{String(error)}</h1>
</div>
<SplashPage title='Error' description='Something went wrong while loading this page.'>
{String(error)}
</SplashPage>
);
};
6 changes: 2 additions & 4 deletions apps/minifront/src/fetchers/chain-id.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { viewClient } from '../clients';

export const getChainId = async (): Promise<string> => {
export const getChainId = async (): Promise<string | undefined> => {
const { parameters } = await viewClient.appParameters({});
if (!parameters?.chainId) throw new Error('No chainId in response');

return parameters.chainId;
return parameters?.chainId;
Comment on lines +3 to +5
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a little change @turbocrime suggested. We don't need to throw if the chain ID doesn't come back from the server.

};
9 changes: 5 additions & 4 deletions apps/node-status/src/components/error-boundary.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { SplashPage } from '@penumbra-zone/ui';
import { useRouteError } from 'react-router-dom';

export const ErrorBoundary = () => {
const error = useRouteError();

console.error(error);
console.error('ErrorBoundary caught error:', error);

return (
<div className='text-red'>
<h1 className='text-xl'>{String(error)}</h1>
</div>
<SplashPage title='Error' description='Something went wrong while loading this page.'>
{String(error)}
</SplashPage>
);
};
Loading