Merge pull request #18 from polkadot-api/vo-links

feat: persist chain through hash params
This commit is contained in:
Victor Oliva
2024-12-06 13:41:03 +01:00
committed by GitHub
14 changed files with 3762 additions and 4603 deletions
+1 -1
View File
@@ -85,7 +85,7 @@
"pnpm": {
"patchedDependencies": {
"react18-json-view@0.2.8": "patches/react18-json-view@0.2.8.patch",
"@polkadot-api/observable-client": "patches/@polkadot-api__observable-client.patch"
"@polkadot-api/observable-client@0.6.3": "patches/@polkadot-api__observable-client@0.6.3.patch"
}
}
}
@@ -1,8 +1,8 @@
diff --git a/dist/esm/chainHead/chainHead.mjs b/dist/esm/chainHead/chainHead.mjs
index 694f29035186d89e0db3d4171d05a939f026570e..bff0a740c625f3bfb56938d2b8859d00d4a53b52 100644
index 43d1b02d7e3346d854971b92c102a61378c4e150..557ca7f05fe8effbfc3e448c7417f5e9ef715c04 100644
--- a/dist/esm/chainHead/chainHead.mjs
+++ b/dist/esm/chainHead/chainHead.mjs
@@ -157,7 +157,7 @@ const getChainHead$ = (chainHead) => {
@@ -154,7 +154,7 @@ const getChainHead$ = (chainHead) => {
best$.pipe(map((b) => b.hash))
);
const _body$ = withOptionalHash$(commonEnhancer(lazyFollower("body")));
@@ -11,7 +11,7 @@ index 694f29035186d89e0db3d4171d05a939f026570e..bff0a740c625f3bfb56938d2b8859d00
const _storage$ = commonEnhancer(lazyFollower("storage"));
const storage$ = withOptionalHash$(
(hash, withCanonicalChain2, type, keyMapper, childTrie = null, mapper) => pinnedBlocks$.pipe(
@@ -244,11 +244,11 @@ const getChainHead$ = (chainHead) => {
@@ -243,11 +243,11 @@ const getChainHead$ = (chainHead) => {
runtime$,
metadata$,
header$,
+3622 -4574
View File
File diff suppressed because it is too large Load Diff
+63
View File
@@ -0,0 +1,63 @@
import { FC } from "react"
import {
LinkProps,
Path,
Link as RouterLink,
Location as RouterLocation,
useLocation,
} from "react-router-dom"
export const getHashParams = (location?: Location | RouterLocation) => {
location = location ?? globalThis.location
if (location.hash) return new URLSearchParams(location.hash.slice(1))
return new URLSearchParams()
}
export const setHashParams = (
newParams: Record<string, string | null>,
location?: Location | RouterLocation,
) => {
const params = getHashParams(location)
Object.entries(newParams).forEach(([param, value]) => {
if (value === null) params.delete(param)
else params.set(param, value)
})
location = location ?? globalThis.location
location.hash = "#" + params.toString()
}
const persistingKeys = ["networkId", "endpoint"]
export const Link: FC<LinkProps & React.RefAttributes<HTMLAnchorElement>> = (
props,
) => {
const location = useLocation()
const originalParams = getHashParams(location)
const appendParams = (hash: string) => {
const parsedParams = new URLSearchParams(hash.slice(1))
persistingKeys.forEach(
(key) =>
originalParams.has(key) &&
!parsedParams.has(key) &&
parsedParams.set(key, originalParams.get(key)!),
)
return "#" + parsedParams.toString()
}
const persistKeys = (to: Partial<Path> | string): Partial<Path> | string => {
if (typeof to === "string") {
const idx = to.indexOf("#")
return (idx < 0 ? to : to.slice(0, idx)) + appendParams(to.slice(idx))
}
return to.hash
? {
...to,
hash: appendParams(to.hash),
}
: to
}
return <RouterLink {...props} to={persistKeys(props.to)} />
}
+3 -3
View File
@@ -1,14 +1,14 @@
import { runtimeCtx$ } from "@/state/chains/chain.state"
import { CircularProgress } from "@/components/CircularProgress"
import { CopyText } from "@/components/Copy"
import { Link } from "@/hashParams"
import { groupBy } from "@/lib/groupBy"
import { runtimeCtx$ } from "@/state/chains/chain.state"
import { state, useStateObservable } from "@react-rxjs/core"
import { FC } from "react"
import { Link } from "react-router-dom"
import { map } from "rxjs"
import { blockInfoState$ } from "./block.state"
import { BlockStatusIcon, statusText } from "./Detail/BlockState"
import { filterEvt } from "./events.state"
import { CopyText } from "@/components/Copy"
const maxBlockWeight$ = state(
runtimeCtx$.pipe(
+2 -2
View File
@@ -1,6 +1,7 @@
import { chainHead$ } from "@/state/chains/chain.state"
import { CopyText } from "@/components/Copy"
import { Popover } from "@/components/Popover"
import { Link } from "@/hashParams"
import { chainHead$ } from "@/state/chains/chain.state"
import { state, useStateObservable } from "@react-rxjs/core"
import { FC } from "react"
import { combineLatest, debounceTime, map, switchMap } from "rxjs"
@@ -8,7 +9,6 @@ import { twMerge } from "tailwind-merge"
import { BlockInfo, blocksByHeight$, finalized$ } from "./block.state"
import { BlockPopover } from "./BlockPopover"
import * as Finalizing from "./FinalizingTable"
import { Link } from "react-router-dom"
const best$ = chainHead$.pipeState(switchMap((chainHead) => chainHead.best$))
+6 -5
View File
@@ -1,18 +1,21 @@
import { runtimeCtx$ } from "@/state/chains/chain.state"
import { CopyBinary } from "@/codec-components/ViewCodec/CopyBinary"
import { AccountIdDisplay } from "@/components/AccountIdDisplay"
import { CopyText } from "@/components/Copy"
import { ExpandBtn } from "@/components/Expand"
import { JsonDisplay } from "@/components/JsonDisplay"
import { Loading } from "@/components/Loading"
import { Link } from "@/hashParams"
import { groupBy } from "@/lib/groupBy"
import { runtimeCtx$ } from "@/state/chains/chain.state"
import { SystemEvent } from "@polkadot-api/observable-client"
import { toHex } from "@polkadot-api/utils"
import * as Tabs from "@radix-ui/react-tabs"
import { state, useStateObservable } from "@react-rxjs/core"
import { combineKeys } from "@react-rxjs/utils"
import { Edit } from "lucide-react"
import { Enum, HexString, SS58String } from "polkadot-api"
import { FC, useEffect, useRef, useState } from "react"
import { Link, useLocation, useParams } from "react-router-dom"
import { useLocation, useParams } from "react-router-dom"
import {
combineLatest,
distinctUntilChanged,
@@ -26,8 +29,6 @@ import { twMerge } from "tailwind-merge"
import { blockInfoState$, blocksByHeight$, BlockState } from "../block.state"
import { BlockStatusIcon, statusText } from "./BlockState"
import { createExtrinsicCodec, DecodedExtrinsic } from "./extrinsicDecoder"
import { CopyText } from "@/components/Copy"
import { Enum, HexString, SS58String } from "polkadot-api"
const Sender: React.FC<{
sender: Enum<{ Id: SS58String }> | SS58String | HexString
@@ -321,7 +322,7 @@ const Extrinsic: FC<{
<Edit size={14} />
</span>
) : (
<Link to={"/extrinsics#" + toHex(extrinsic.callData)}>
<Link to={"/extrinsics#data=" + toHex(extrinsic.callData)}>
<Edit size={14} />
</Link>
)}
+1 -1
View File
@@ -1,7 +1,7 @@
import { CopyText } from "@/components/Copy"
import { JsonDisplay } from "@/components/JsonDisplay"
import { Link } from "@/hashParams"
import { FC } from "react"
import { Link } from "react-router-dom"
import { BlockStatusIcon, statusText } from "./Detail/BlockState"
import { EventInfo, eventKey } from "./events.state"
+1 -1
View File
@@ -1,11 +1,11 @@
import { Popover } from "@/components/Popover"
import { Link } from "@/hashParams"
import { useStateObservable } from "@react-rxjs/core"
import { twMerge } from "tailwind-merge"
import { finalized$ } from "./block.state"
import { EventPopover } from "./EventPopover"
import { eventKey, recentEvents$ } from "./events.state"
import * as Finalizing from "./FinalizingTable"
import { Link } from "react-router-dom"
export const Events = () => {
const events = useStateObservable(recentEvents$)
+19 -4
View File
@@ -1,21 +1,23 @@
import { runtimeCtx$ } from "@/state/chains/chain.state"
import { BinaryDisplay } from "@/codec-components/LookupTypeEdit"
import { ButtonGroup } from "@/components/ButtonGroup"
import { LoadingMetadata } from "@/components/Loading"
import { withSubscribe } from "@/components/withSuspense"
import { getHashParams, setHashParams } from "@/hashParams"
import { runtimeCtx$ } from "@/state/chains/chain.state"
import {
CodecComponentType,
CodecComponentValue,
} from "@polkadot-api/react-builder"
import { Binary } from "@polkadot-api/substrate-bindings"
import { toHex } from "@polkadot-api/utils"
import { state, useStateObservable } from "@react-rxjs/core"
import { useState } from "react"
import { useEffect, useState } from "react"
import { useLocation } from "react-router-dom"
import { map } from "rxjs"
import { twMerge } from "tailwind-merge"
import { EditMode } from "./EditMode"
import { JsonMode } from "./JsonMode"
import { ExtrinsicModal } from "./SubmitTx/SubmitTx"
import { twMerge } from "tailwind-merge"
const extrinsicProps$ = state(
runtimeCtx$.pipe(
@@ -42,7 +44,7 @@ export const Extrinsics = withSubscribe(
const [componentValue, setComponentValue] = useState<CodecComponentValue>({
type: CodecComponentType.Initial,
value: location.hash.slice(1),
value: getHashParams(location).get("data") ?? "",
})
const binaryValue =
(componentValue.type === CodecComponentType.Initial
@@ -51,6 +53,19 @@ export const Extrinsics = withSubscribe(
? null
: componentValue.value.encoded) ?? null
useEffect(() => {
if (binaryValue && binaryValue.length < 1024 * 1024) {
setHashParams({
data:
typeof binaryValue === "string" ? binaryValue : toHex(binaryValue),
})
} else {
setHashParams({
data: null,
})
}
}, [binaryValue])
return (
<div
className={twMerge(
+3 -2
View File
@@ -1,7 +1,8 @@
import { Link, useLocation } from "react-router-dom"
import { NetworkSwitcher } from "./Network/Network"
import { Link } from "@/hashParams"
import { FC, PropsWithChildren } from "react"
import { useLocation } from "react-router-dom"
import { twMerge } from "tailwind-merge"
import { NetworkSwitcher } from "./Network/Network"
export const Header = () => (
<div className="flex-shrink-0 border-b">
+1 -1
View File
@@ -1,4 +1,5 @@
import { ExpandBtn } from "@/components/Expand"
import { Link } from "@/hashParams"
import { V14Lookup } from "@polkadot-api/substrate-bindings"
import { Edit } from "lucide-react"
import {
@@ -8,7 +9,6 @@ import {
useContext,
useState,
} from "react"
import { Link } from "react-router-dom"
import { twMerge } from "tailwind-merge"
export const Lookup: FC = () => {
+1 -1
View File
@@ -1,8 +1,8 @@
import { CopyText } from "@/components/Copy"
import { Link } from "@/hashParams"
import { shortStr } from "@/utils"
import { TxBroadcastEvent } from "polkadot-api"
import * as React from "react"
import { Link } from "react-router-dom"
import { onGoingEvents } from "./transactions.state"
export const Transaction: React.FC<{
+36 -5
View File
@@ -18,7 +18,7 @@ import {
switchMap,
tap,
} from "rxjs"
import { defaultNetwork, Network } from "./networks"
import { defaultNetwork, Network, networkCategories } from "./networks"
import {
createSmoldotSource,
getSmoldotProvider,
@@ -29,6 +29,7 @@ import {
getWebsocketProvider,
WebsocketSource,
} from "./websocket"
import { getHashParams, setHashParams } from "@/hashParams"
export type ChainSource = WebsocketSource | SmoldotSource
@@ -50,10 +51,40 @@ export const getProvider = (source: ChainSource) =>
export const [selectedChainChanged$, onChangeChain] =
createSignal<SelectedChain>()
export const selectedChain$ = state<SelectedChain>(selectedChainChanged$, {
network: defaultNetwork,
endpoint: "light-client",
})
selectedChainChanged$.subscribe(({ network, endpoint }) =>
setHashParams({
networkId: network.id,
endpoint,
}),
)
const getDefaultChain = (): SelectedChain => {
const findNetwork = (networkId: string) => {
for (const { networks } of networkCategories) {
for (const network of networks) {
if (network.id === networkId) {
return network
}
}
}
return null
}
const hashParams = getHashParams()
if (hashParams.has("networkId") && hashParams.has("endpoint")) {
const network = findNetwork(hashParams.get("networkId")!)
if (network) {
return { network, endpoint: hashParams.get("endpoint")! }
}
}
return {
network: defaultNetwork,
endpoint: "light-client",
}
}
export const selectedChain$ = state<SelectedChain>(
selectedChainChanged$,
getDefaultChain(),
)
const selectedSource$ = selectedChain$.pipe(switchMap(getChainSource))