-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add RPC selection to onboarding flow (#868)
* Create initial component for setting RPC endpoint * Create RPC_ENDPOINTS const * Create SelectList component * Build out RPC UI * Add submit button and link to edit list * Use the RPC endpoint in state * Send the OnboardComplete message after selecting an RPC endpoint * Add a custom RPC endpoint option * Show RPC node images * Fix complain re: uncontrolled input * Fix bug with how endpoint is stored in state * Fix issue with using default grpc url * Fix issue with trying to get the RPC endpoint too early * Fix impl definition * Clean up SelectList a bit * Account for images * Add example * Refactor per PR suggestion * Move RPC endpoints to constants package * Tweak message
- Loading branch information
1 parent
725be77
commit 71b71e8
Showing
18 changed files
with
453 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
96 changes: 96 additions & 0 deletions
96
apps/extension/src/routes/page/onboarding/set-rpc-endpoint.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import { Card, CardDescription, CardHeader, CardTitle } from '@penumbra-zone/ui/components/ui/card'; | ||
import { FadeTransition } from '@penumbra-zone/ui/components/ui/fade-transition'; | ||
import { RPC_ENDPOINTS } from '@penumbra-zone/constants/src/rpc-endpoints'; | ||
import { FormEvent, useMemo, useRef, useState } from 'react'; | ||
import { SelectList } from '@penumbra-zone/ui/components/ui/select-list'; | ||
import { Button } from '@penumbra-zone/ui/components/ui/button'; | ||
import { useStore } from '../../../state'; | ||
import { usePageNav } from '../../../utils/navigate'; | ||
import { PagePath } from '../paths'; | ||
import { ServicesMessage } from '@penumbra-zone/types/src/services'; | ||
import { Network } from 'lucide-react'; | ||
|
||
const randomSort = () => (Math.random() >= 0.5 ? 1 : -1); | ||
|
||
export const SetRpcEndpoint = () => { | ||
const navigate = usePageNav(); | ||
const randomlySortedEndpoints = useMemo(() => [...RPC_ENDPOINTS].sort(randomSort), []); | ||
const [grpcEndpoint, setGrpcEndpoint] = useState(randomlySortedEndpoints[0]!.url); | ||
const setGrpcEndpointInZustand = useStore(state => state.network.setGRPCEndpoint); | ||
const customRpcEndpointInput = useRef<HTMLInputElement | null>(null); | ||
const isCustomRpcEndpoint = !RPC_ENDPOINTS.some(({ url }) => url === grpcEndpoint); | ||
|
||
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => { | ||
e.preventDefault(); | ||
await setGrpcEndpointInZustand(grpcEndpoint); | ||
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're unsure which one to choose, leave this | ||
option set to the default. | ||
</CardDescription> | ||
</CardHeader> | ||
|
||
<form className='mt-6 flex flex-col gap-4' onSubmit={e => void handleSubmit(e)}> | ||
<SelectList> | ||
{randomlySortedEndpoints.map(rpcEndpoint => ( | ||
<SelectList.Option | ||
key={rpcEndpoint.url} | ||
label={rpcEndpoint.name} | ||
secondary={rpcEndpoint.url} | ||
onSelect={setGrpcEndpoint} | ||
value={rpcEndpoint.url} | ||
isSelected={rpcEndpoint.url === grpcEndpoint} | ||
image={ | ||
!!rpcEndpoint.imageUrl && ( | ||
<img src={rpcEndpoint.imageUrl} className='size-full object-contain' /> | ||
) | ||
} | ||
/> | ||
))} | ||
|
||
<SelectList.Option | ||
label='Custom RPC' | ||
secondary={ | ||
<input | ||
type='url' | ||
ref={customRpcEndpointInput} | ||
value={isCustomRpcEndpoint && !!grpcEndpoint ? grpcEndpoint : ''} | ||
onChange={e => setGrpcEndpoint(e.target.value)} | ||
className='w-full bg-transparent' | ||
/> | ||
} | ||
onSelect={() => { | ||
if (!isCustomRpcEndpoint) setGrpcEndpoint(''); | ||
customRpcEndpointInput.current?.focus(); | ||
}} | ||
isSelected={isCustomRpcEndpoint} | ||
image={<Network className='size-full' />} | ||
/> | ||
</SelectList> | ||
|
||
<Button variant='gradient' className='mt-2' type='submit'> | ||
Next | ||
</Button> | ||
</form> | ||
|
||
<a | ||
href='https://github.com/penumbra-zone/web/blob/main/apps/extension/src/shared/rpc-endpoints.ts' | ||
target='_blank' | ||
rel='noreferrer' | ||
className='mt-6 block text-right text-xs text-muted-foreground' | ||
> | ||
Add to this list | ||
</a> | ||
</Card> | ||
</FadeTransition> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { useShallow } from 'zustand/react/shallow'; | ||
import { AllSlices, useStore } from '../state'; | ||
|
||
/** | ||
* Like `useStore()`, but checks for shallow equality to prevent unnecessary | ||
* re-renders if none of the properties returned by `selector` have changed. | ||
* | ||
* Calling `useStoreShallow(selector)` is the same as calling | ||
* `useStore(useShallow(selector))`. But it's so common to use those two | ||
* together that this function combines both for ease of use. | ||
* | ||
* @example | ||
* ```tsx | ||
* import { useStoreShallow } from '../utils/use-store-shallow'; | ||
* | ||
* const myComponentSelector = (state: AllSlices) => ({ | ||
* prop1: state.mySlice.prop1, | ||
* prop2: state.mySlice.prop2, | ||
* }); | ||
* | ||
* const MyComponent = () => { | ||
* const state = useStoreShallow(myComponentSelector); | ||
* }; | ||
* ``` | ||
*/ | ||
export const useStoreShallow = <U>(selector: (state: AllSlices) => U) => | ||
useStore(useShallow<AllSlices, U>(selector)); |
Oops, something went wrong.