feat: persist chain on 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@0.6.3.patch"
|
||||
"@polkadot-api/observable-client@0.6.3": "patches/@polkadot-api__observable-client@0.6.3.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<{
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
getWebsocketProvider,
|
||||
WebsocketSource,
|
||||
} from "./websocket"
|
||||
import { getHashParams, setHashParams } from "@/hashParams"
|
||||
|
||||
export type ChainSource = WebsocketSource | SmoldotSource
|
||||
|
||||
@@ -50,15 +51,11 @@ export const getProvider = (source: ChainSource) =>
|
||||
|
||||
export const [selectedChainChanged$, onChangeChain] =
|
||||
createSignal<SelectedChain>()
|
||||
const SELECTED_CHAIN_KEY = "selected-chain"
|
||||
selectedChainChanged$.subscribe((chain) =>
|
||||
localStorage.setItem(
|
||||
SELECTED_CHAIN_KEY,
|
||||
JSON.stringify({
|
||||
networkId: chain.network.id,
|
||||
endpoint: chain.endpoint,
|
||||
}),
|
||||
),
|
||||
selectedChainChanged$.subscribe(({ network, endpoint }) =>
|
||||
setHashParams({
|
||||
networkId: network.id,
|
||||
endpoint,
|
||||
}),
|
||||
)
|
||||
const getDefaultChain = (): SelectedChain => {
|
||||
const findNetwork = (networkId: string) => {
|
||||
@@ -71,29 +68,11 @@ const getDefaultChain = (): SelectedChain => {
|
||||
}
|
||||
return null
|
||||
}
|
||||
if (globalThis.location?.hash) {
|
||||
const hashParams = new URLSearchParams(location.hash.slice(1))
|
||||
if (hashParams.has("networkId") && hashParams.has("endpoint")) {
|
||||
const network = findNetwork(hashParams.get("networkId")!)
|
||||
if (network) {
|
||||
return { network, endpoint: hashParams.get("endpoint")! }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const lsValue = localStorage.getItem(SELECTED_CHAIN_KEY)
|
||||
if (lsValue) {
|
||||
try {
|
||||
const { networkId, endpoint } = JSON.parse(lsValue)
|
||||
const network = findNetwork(networkId)
|
||||
if (network) {
|
||||
return {
|
||||
network,
|
||||
endpoint,
|
||||
}
|
||||
}
|
||||
} catch (ex) {
|
||||
console.error(ex)
|
||||
const hashParams = getHashParams()
|
||||
if (hashParams.has("networkId") && hashParams.has("endpoint")) {
|
||||
const network = findNetwork(hashParams.get("networkId")!)
|
||||
if (network) {
|
||||
return { network, endpoint: hashParams.get("endpoint")! }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user