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

Set gRPC endpoint during onboarding, part 2 #877

Merged
merged 9 commits into from
Apr 5, 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
2 changes: 2 additions & 0 deletions apps/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"exponential-backoff": "^3.1.1",
"framer-motion": "^11.0.22",
"immer": "^10.0.4",
"lodash": "^4.17.21",
jessepinho marked this conversation as resolved.
Show resolved Hide resolved
"lucide-react": "^0.363.0",
"node-fetch": "^3.3.2",
"react": "^18.2.0",
Expand All @@ -45,6 +46,7 @@
"@penumbra-zone/polyfills": "workspace:*",
"@radix-ui/react-icons": "^1.3.0",
"@types/firefox-webext-browser": "^120.0.3",
"@types/lodash": "^4.17.0",
"@types/react": "^18.2.72",
"@types/react-dom": "^18.2.22",
"autoprefixer": "^10.4.19",
Expand Down
6 changes: 3 additions & 3 deletions apps/extension/src/routes/page/onboarding/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ImportSeedPhrase } from './import';
import { OnboardingSuccess } from './success';
import { SetPassword } from './set-password';
import { pageIndexLoader } from '..';
import { SetRpcEndpoint } from './set-rpc-endpoint';
import { SetGrpcEndpoint } from './set-grpc-endpoint';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Consolidating the naming

Copy link
Contributor

Choose a reason for hiding this comment

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

would prefer to align on "rpc", it's a more generic term

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ugh, same. I'll merge this and then discuss in Discord. The issue was, the local storage key is grpcEndpoint, so I figured we should standardize on that since it's already in a bunch of users' local storage.


export const onboardingRoutes = [
{
Expand All @@ -30,8 +30,8 @@ export const onboardingRoutes = [
element: <SetPassword />,
},
{
path: PagePath.SET_RPC_ENDPOINT,
element: <SetRpcEndpoint />,
path: PagePath.SET_GRPC_ENDPOINT,
element: <SetGrpcEndpoint />,
},
{
path: PagePath.ONBOARDING_SUCCESS,
Expand Down
34 changes: 34 additions & 0 deletions apps/extension/src/routes/page/onboarding/set-grpc-endpoint.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Card, CardDescription, CardHeader, CardTitle } from '@penumbra-zone/ui/components/ui/card';
import { FadeTransition } from '@penumbra-zone/ui/components/ui/fade-transition';
import { usePageNav } from '../../../utils/navigate';
import { PagePath } from '../paths';
import { ServicesMessage } from '@penumbra-zone/types/src/services';
import { GrpcEndpointForm } from '../../../shared/components/grpc-endpoint-form';

export const SetGrpcEndpoint = () => {
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 file used to be set-rpc-endpoint.tsx. When I renamed it to set-grpc-endpoint.tsx, and extracted the GrpcEndpointForm component, the file was so different that Git thought of it as an entirely different file.

const navigate = usePageNav();

const onSuccess = (): void => {
void chrome.runtime.sendMessage(ServicesMessage.OnboardComplete);
navigate(PagePath.ONBOARDING_SUCCESS);
};

return (
<FadeTransition>
<Card className='w-[400px]' gradient>
<CardHeader>
<CardTitle>Select your preferred RPC endpoint</CardTitle>
<CardDescription>
The requests you make may reveal your intentions about transactions you wish to make, so
select an RPC node that you trust. If you&apos;re unsure which one to choose, leave this
option set to the default.
</CardDescription>
</CardHeader>

<div className='mt-6'>
<GrpcEndpointForm submitButtonLabel='Next' onSuccess={onSuccess} />
</div>
</Card>
</FadeTransition>
);
};
2 changes: 1 addition & 1 deletion apps/extension/src/routes/page/onboarding/set-password.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const SetPassword = () => {

void (async () => {
await onboardingSave(password);
navigate(PagePath.SET_RPC_ENDPOINT);
navigate(PagePath.SET_GRPC_ENDPOINT);
})();
};

Expand Down
96 changes: 0 additions & 96 deletions apps/extension/src/routes/page/onboarding/set-rpc-endpoint.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion apps/extension/src/routes/page/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export enum PagePath {
IMPORT_SEED_PHRASE = '/welcome/import',
ONBOARDING_SUCCESS = '/welcome/success',
SET_PASSWORD = '/welcome/set-password',
SET_RPC_ENDPOINT = '/welcome/set-rpc-endpoint',
SET_GRPC_ENDPOINT = '/welcome/set-grpc-endpoint',
RESTORE_PASSWORD = '/restore-password',
RESTORE_PASSWORD_INDEX = '/restore-password/',
RESTORE_PASSWORD_SET_PASSWORD = '/restore-password/set-password',
Expand Down
9 changes: 9 additions & 0 deletions apps/extension/src/routes/popup/padding-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ReactNode } from 'react';

/**
* Wrap this around any top-level components that should have horizontal and
* bottom padding in the extension popup.
*/
export const PaddingWrapper = ({ children }: { children: ReactNode }) => {
turbocrime marked this conversation as resolved.
Show resolved Hide resolved
return <div className='px-[30px] pb-[30px]'>{children}</div>;
};
112 changes: 14 additions & 98 deletions apps/extension/src/routes/popup/settings/settings-rpc.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,15 @@
import { QueryService } from '@buf/penumbra-zone_penumbra.connectrpc_es/penumbra/core/app/v1/app_connect';
import { createGrpcWebTransport } from '@connectrpc/connect-web';
import { createPromiseClient } from '@connectrpc/connect';
import { FormEvent, useState } from 'react';
import { Button } from '@penumbra-zone/ui/components/ui/button';
import { useState } from 'react';
import { FadeTransition } from '@penumbra-zone/ui/components/ui/fade-transition';
import { Input } from '@penumbra-zone/ui/components/ui/input';
import { useChainIdQuery } from '../../../hooks/chain-id';
import { ShareGradientIcon } from '../../../icons/share-gradient';
import { SettingsHeader } from '../../../shared/components/settings-header';
import { useStore } from '../../../state';
import { networkSelector } from '../../../state/network';
import '@penumbra-zone/polyfills/src/Promise.withResolvers';
import { TrashIcon } from '@radix-ui/react-icons';
import { ServicesMessage } from '@penumbra-zone/types/src/services';
import { GrpcEndpointForm } from '../../../shared/components/grpc-endpoint-form';
import { PaddingWrapper } from '../padding-wrapper';

export const SettingsRPC = () => {
const { chainId: currentChainId } = useChainIdQuery();
const [newChainId, setNewChainId] = useState<string>();
const { grpcEndpoint, setGRPCEndpoint } = useStore(networkSelector);

const [rpcInput, setRpcInput] = useState<string>(grpcEndpoint ?? '');
const [rpcError, setRpcError] = useState<string>();
const [countdownTime, setCountdownTime] = useState<number>();
const submitButtonLabel =
Number(countdownTime) > 0 ? `Saved! Restarting in ${countdownTime}...` : 'Save';

const countdown = (seconds: number) => {
const { promise, resolve } = Promise.withResolvers();
Expand All @@ -33,32 +21,11 @@ export const SettingsRPC = () => {
return promise;
};

const onSubmit = (evt: FormEvent<HTMLFormElement>) => {
evt.preventDefault();
void (async () => {
try {
const trialClient = createPromiseClient(
QueryService,
createGrpcWebTransport({ baseUrl: rpcInput }),
);
const { appParameters } = await trialClient.appParameters({});
if (!appParameters?.chainId) throw new Error('Endpoint did not provide a valid chainId');

setRpcError(undefined);
setNewChainId(appParameters.chainId);
await setGRPCEndpoint(rpcInput);
// If the chain id has changed, our cache is invalid
if (appParameters.chainId !== currentChainId)
void chrome.runtime.sendMessage(ServicesMessage.ClearCache);
// Visually confirm success for a few seconds
await countdown(5);
// Reload the extension to ensure all scopes holding the old config are killed
chrome.runtime.reload();
} catch (e: unknown) {
console.warn('Could not use new RPC endpoint', e);
setRpcError(String(e) || 'Unknown RPC failure');
}
})();
const onSuccess = async () => {
// Visually confirm success for a few seconds, then reload the extension to
// ensure all scopes holding the old config are killed
await countdown(5);
chrome.runtime.reload();
};

return (
Expand All @@ -68,61 +35,10 @@ export const SettingsRPC = () => {
<div className='mx-auto size-20'>
<ShareGradientIcon />
</div>
<form
className='flex flex-1 flex-col items-start justify-between px-[30px] pb-[30px]'
onSubmit={onSubmit}
>
<div className='flex w-full flex-col gap-4'>
<div className='flex flex-col items-center justify-center gap-2'>
<div className='flex items-center gap-2 self-start'>
<div className='text-base font-bold'>RPC URL</div>
{rpcError ? <div className='italic text-red-400'>{rpcError}</div> : null}
</div>
<div className='relative w-full'>
<div className='absolute inset-y-0 right-4 flex cursor-pointer items-center'>
{rpcInput !== grpcEndpoint ? (
<Button
type='reset'
variant='outline'
onClick={() => setRpcInput(grpcEndpoint ?? DEFAULT_GRPC_URL)}
>
<TrashIcon />
</Button>
) : null}
</div>
<Input
variant={rpcError ? 'error' : rpcInput !== grpcEndpoint ? 'warn' : 'default'}
value={rpcInput}
onChange={evt => {
setRpcError(undefined);
setRpcInput(evt.target.value);
}}
className='text-muted-foreground'
/>
</div>
</div>
<div className='flex flex-col gap-2'>
<p className='font-headline text-base font-semibold'>Chain id</p>
<div className='flex h-11 w-full items-center rounded-lg border bg-background px-3 py-2 text-muted-foreground'>
{newChainId ?? currentChainId}
</div>
</div>
</div>
{countdownTime !== undefined ? (
<Button disabled variant='outline' size='lg' className='w-full'>
Saved! Restarting in {countdownTime}...
</Button>
) : (
<Button
variant={rpcInput !== grpcEndpoint ? 'gradient' : 'outline'}
size='lg'
className='w-full'
type='submit'
>
Save
</Button>
)}
</form>

<PaddingWrapper>
<GrpcEndpointForm submitButtonLabel={submitButtonLabel} onSuccess={onSuccess} />
</PaddingWrapper>
</div>
</FadeTransition>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { cn } from '@penumbra-zone/ui/lib/utils';

export const ChainIdOrError = ({
error,
chainId,
chainIdChanged,
}: {
error?: string;
chainId?: string;
chainIdChanged: boolean;
}) => {
if (!error && !chainId) return null;

return (
<div
className={cn(
'flex justify-center font-mono text-xs text-muted-foreground',
!!error && 'text-red-400',
!error && chainIdChanged && 'text-rust',
)}
>
{error ? error : chainId ? `Chain ID: ${chainId}` : null}
</div>
);
};
Loading
Loading