Merge pull request #18 from polkadot-api/vo-links
feat: persist chain through hash params
This commit is contained in:
+1
-1
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -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$,
|
||||
Generated
+3622
-4574
File diff suppressed because it is too large
Load Diff
@@ -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)} />
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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$))
|
||||
|
||||
|
||||
@@ -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,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,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$)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,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,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<{
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user