feat: improve custom endpoint network selection
This commit is contained in:
@@ -30,6 +30,7 @@ import {
|
||||
selectedChain$,
|
||||
} from "@/state/chains/chain.state"
|
||||
import { addCustomNetwork, getCustomNetwork } from "@/state/chains/networks"
|
||||
import { Input } from "@polkahub/ui-components"
|
||||
import { useStateObservable } from "@react-rxjs/core"
|
||||
import { Check, ChevronDown } from "lucide-react"
|
||||
import { FC, useState } from "react"
|
||||
@@ -43,6 +44,47 @@ export function NetworkSwitcher({
|
||||
const [open, setOpen] = useState(false)
|
||||
const selectedChain = useStateObservable(selectedChain$)
|
||||
|
||||
const getChainName = () => {
|
||||
if (selectedChain.network.id === "custom") {
|
||||
try {
|
||||
const url = new URL(selectedChain.endpoint)
|
||||
if (["127.0.0.1", "localhost"].includes(url.hostname)) {
|
||||
return "Localhost"
|
||||
}
|
||||
|
||||
return url.hostname
|
||||
} catch {
|
||||
return selectedChain.endpoint
|
||||
}
|
||||
}
|
||||
return selectedChain.network.display
|
||||
}
|
||||
const getNodeName = () => {
|
||||
if (selectedChain.withChopsticks) {
|
||||
return <Chopsticks className="inline-block align-text-top" />
|
||||
}
|
||||
|
||||
if (selectedChain.network.id === "localhost") {
|
||||
try {
|
||||
const url = new URL(selectedChain.endpoint)
|
||||
|
||||
return url.port
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedChain.endpoint === "light-client") {
|
||||
return "Smoldot"
|
||||
}
|
||||
|
||||
return (
|
||||
Object.entries(selectedChain.network.endpoints).find(
|
||||
([, e]) => selectedChain.endpoint === e,
|
||||
)?.[0] ?? selectedChain.endpoint
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
@@ -54,7 +96,10 @@ export function NetworkSwitcher({
|
||||
)}
|
||||
>
|
||||
<span className="overflow-hidden text-ellipsis">
|
||||
{selectedChain.network.display}
|
||||
{getChainName()}
|
||||
<span className="text-sm text-muted-foreground ml-1">
|
||||
{getNodeName()}
|
||||
</span>
|
||||
</span>
|
||||
<ChevronDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
@@ -100,7 +145,7 @@ const NetworkSwitchDialogContent: FC<{
|
||||
|
||||
const handleConfirm = () => {
|
||||
const chopsticksEnabled = selectedRpc !== "light-client" && withChopsticks
|
||||
if (selectedNetwork.id === "custom-network") {
|
||||
if (selectedNetwork.id === "custom") {
|
||||
addCustomNetwork(selectedRpc)
|
||||
onChangeChain({
|
||||
network: getCustomNetwork(),
|
||||
@@ -147,59 +192,72 @@ const NetworkSwitchDialogContent: FC<{
|
||||
<div className="text-foreground/50">No networks found.</div>
|
||||
</CommandEmpty>
|
||||
<ScrollArea className="h-[260px]">
|
||||
{networkCategories.map((category) => (
|
||||
<CommandGroup key={category.name} heading={category.name}>
|
||||
{category.networks.map((network) => (
|
||||
<CommandItem
|
||||
key={network.id}
|
||||
onSelect={() => handleNetworkSelect(network)}
|
||||
value={
|
||||
network.display.includes(category.name)
|
||||
? network.display
|
||||
: `${category.name} ${network.display}`
|
||||
}
|
||||
>
|
||||
<Check
|
||||
className={`mr-2 h-4 w-4 ${
|
||||
selectedNetwork.id === network.id
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
}`}
|
||||
/>
|
||||
{network.display}
|
||||
</CommandItem>
|
||||
))}
|
||||
{category.name === "Custom" && isValidUri(enteredText) ? (
|
||||
<CommandItem
|
||||
value={enteredText}
|
||||
onSelect={() => {
|
||||
handleNetworkSelect({
|
||||
id: "custom-network",
|
||||
lightclient: false,
|
||||
endpoints: { custom: enteredText },
|
||||
display: enteredText,
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={`mr-2 h-4 w-4 ${
|
||||
selectedNetwork.id === "custom-network"
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
}`}
|
||||
/>
|
||||
{enteredText}
|
||||
</CommandItem>
|
||||
) : null}
|
||||
</CommandGroup>
|
||||
))}
|
||||
{networkCategories.map((category) => {
|
||||
if (category.name === "Custom") {
|
||||
if (!isValidUri(enteredText)) return null
|
||||
return (
|
||||
<CommandGroup key={category.name} heading={category.name}>
|
||||
<CommandItem
|
||||
value={enteredText}
|
||||
onSelect={() => {
|
||||
handleNetworkSelect({
|
||||
id: "custom",
|
||||
lightclient: false,
|
||||
endpoints: { custom: enteredText },
|
||||
display: enteredText,
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={`mr-2 h-4 w-4 ${
|
||||
selectedNetwork.id === "custom"
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
}`}
|
||||
/>
|
||||
{enteredText}
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<CommandGroup key={category.name} heading={category.name}>
|
||||
{category.networks.map((network) => (
|
||||
<CommandItem
|
||||
key={network.id}
|
||||
onSelect={() => handleNetworkSelect(network)}
|
||||
value={
|
||||
network.display.includes(category.name)
|
||||
? network.display
|
||||
: `${category.name} ${network.display}`
|
||||
}
|
||||
>
|
||||
<Check
|
||||
className={`mr-2 h-4 w-4 ${
|
||||
selectedNetwork.id === network.id
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
}`}
|
||||
/>
|
||||
{network.display}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
)
|
||||
})}
|
||||
</ScrollArea>
|
||||
</CommandList>
|
||||
</CommandPopover>
|
||||
<div className="h-[50vh] flex flex-col gap-2">
|
||||
{selectedNetwork ? (
|
||||
<div className="grow overflow-hidden flex flex-col">
|
||||
<p className="py-2">Network: {selectedNetwork.display}</p>
|
||||
<p className="py-2">
|
||||
Network:{" "}
|
||||
{selectedNetwork.id === "custom"
|
||||
? "Custom"
|
||||
: selectedNetwork.display}
|
||||
</p>
|
||||
<div className="overflow-auto">
|
||||
<RadioGroup
|
||||
value={selectedRpc}
|
||||
@@ -225,6 +283,13 @@ const NetworkSwitchDialogContent: FC<{
|
||||
/>
|
||||
),
|
||||
)}
|
||||
{selectedNetwork.id === "localhost" ? (
|
||||
<CustomPort
|
||||
selectedRpc={selectedRpc}
|
||||
setSelectedRpc={setSelectedRpc}
|
||||
initialPort=""
|
||||
/>
|
||||
) : null}
|
||||
</RadioGroup>
|
||||
</div>
|
||||
</div>
|
||||
@@ -259,7 +324,7 @@ const NetworkSwitchDialogContent: FC<{
|
||||
disabled={
|
||||
!selectedNetwork ||
|
||||
!hasChanged ||
|
||||
(selectedNetwork.id === "custom-network" && !selectedRpc)
|
||||
(selectedNetwork.id === "custom" && !selectedRpc)
|
||||
}
|
||||
>
|
||||
Confirm Selection
|
||||
@@ -319,3 +384,48 @@ const ConnectionOption: FC<{
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
|
||||
const CustomPort: FC<{
|
||||
selectedRpc: string
|
||||
setSelectedRpc: (rpc: string) => void
|
||||
initialPort: string
|
||||
}> = ({ selectedRpc, setSelectedRpc, initialPort }) => {
|
||||
const [port, setPort] = useState(initialPort)
|
||||
const value = `ws://localhost:${port}`
|
||||
const isSelected = selectedRpc === value
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`overflow-hidden p-3 border rounded-md ${isSelected ? "border-polkadot bg-polkadot/5" : "border-border"}`}
|
||||
>
|
||||
<div className="flex items-start space-x-2">
|
||||
<RadioGroupItem
|
||||
value={value}
|
||||
id="chain-localhost-other"
|
||||
className="mt-1"
|
||||
/>
|
||||
<div className="grid gap-0.5 grow">
|
||||
<Label htmlFor="chain-localhost-other" className="font-medium">
|
||||
Other ports
|
||||
<Badge variant="outline" className="ml-2 text-xs">
|
||||
RPC
|
||||
</Badge>
|
||||
<p className="text-xs text-muted-foreground">Local RPC node</p>
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-2 pt-2 border-t">
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="Port"
|
||||
value={port}
|
||||
onChange={(evt) => {
|
||||
setPort(evt.target.value)
|
||||
setSelectedRpc(`ws://localhost:${evt.target.value}`)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ const networks = {
|
||||
Kusama,
|
||||
Paseo,
|
||||
Westend,
|
||||
Custom: [
|
||||
Localhost: [
|
||||
{
|
||||
id: "localhost",
|
||||
display: "Localhost",
|
||||
@@ -50,6 +50,14 @@ const networks = {
|
||||
},
|
||||
} as Network,
|
||||
],
|
||||
Custom: [
|
||||
{
|
||||
id: "custom",
|
||||
display: "Custom",
|
||||
lightclient: false,
|
||||
endpoints: {},
|
||||
} as Network,
|
||||
],
|
||||
}
|
||||
|
||||
export const networkCategories: NetworkCategory[] = Object.entries(
|
||||
|
||||
Reference in New Issue
Block a user