exclude entries that didn't actually change.
This commit is contained in:
@@ -9,7 +9,7 @@ import { useTheme } from "@/ThemeProvider"
|
||||
|
||||
export const JsonDisplay: FC<{
|
||||
src: unknown
|
||||
collapsed?: boolean
|
||||
collapsed?: boolean | number
|
||||
}> = ({ src, ...props }) => {
|
||||
const theme = useTheme()
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { chopsticksInstance$ } from "@/chopsticks/chopsticks"
|
||||
import { Chopsticks } from "@/components/Icons"
|
||||
import { Loading } from "@/components/Loading"
|
||||
import { groupBy } from "@/lib/groupBy"
|
||||
import { runtimeCtx$ } from "@/state/chains/chain.state"
|
||||
@@ -6,21 +6,13 @@ import * as Tabs from "@radix-ui/react-tabs"
|
||||
import { state, useStateObservable } from "@react-rxjs/core"
|
||||
import { FC, useState } from "react"
|
||||
import { useLocation, useParams } from "react-router-dom"
|
||||
import {
|
||||
combineLatest,
|
||||
distinctUntilChanged,
|
||||
filter,
|
||||
map,
|
||||
switchMap,
|
||||
take,
|
||||
} from "rxjs"
|
||||
import { combineLatest, distinctUntilChanged, filter, map, take } from "rxjs"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import { BlockInfo, blockInfoState$ } from "../block.state"
|
||||
import { BlockEvents } from "./BlockEvents"
|
||||
import { BlockStorageDiff } from "./BlockStorageDiff"
|
||||
import { ApplyExtrinsicEvent, Extrinsic } from "./Extrinsic"
|
||||
import { createExtrinsicCodec, DecodedExtrinsic } from "./extrinsicDecoder"
|
||||
import { Chopsticks } from "@/components/Icons"
|
||||
|
||||
const blockExtrinsics$ = state((hash: string) => {
|
||||
const decoder$ = runtimeCtx$.pipe(
|
||||
@@ -42,23 +34,6 @@ const blockExtrinsics$ = state((hash: string) => {
|
||||
)
|
||||
}, [])
|
||||
|
||||
const blockHasDiff$ = state(
|
||||
(hash: string) =>
|
||||
chopsticksInstance$.pipe(
|
||||
switchMap((chain) => {
|
||||
if (!chain) return [null]
|
||||
|
||||
return chain.getBlock(hash as any)
|
||||
}),
|
||||
switchMap((block) => {
|
||||
if (!block) return [null]
|
||||
return block.storageDiff()
|
||||
}),
|
||||
map((v) => Boolean(v && Object.keys(v).length > 0)),
|
||||
),
|
||||
false,
|
||||
)
|
||||
|
||||
type Tab = "signed" | "unsigned" | "events" | "diff"
|
||||
export const BlockBody: FC<{
|
||||
block: BlockInfo
|
||||
@@ -66,7 +41,6 @@ export const BlockBody: FC<{
|
||||
const { hash } = useParams()
|
||||
const [selectedTab, setSelectedTab] = useState<Tab | null>(null)
|
||||
const extrinsics = useStateObservable(blockExtrinsics$(hash ?? ""))
|
||||
const diff = useStateObservable(blockHasDiff$(hash ?? ""))
|
||||
|
||||
const location = useLocation()
|
||||
const hashParams = new URLSearchParams(location.hash.slice(1))
|
||||
@@ -150,7 +124,7 @@ export const BlockBody: FC<{
|
||||
>
|
||||
Events
|
||||
</Tabs.Trigger>
|
||||
{diff && (
|
||||
{block.diff && (
|
||||
<Tabs.Trigger
|
||||
className={twMerge(
|
||||
"bg-secondary text-secondary-foreground/80 px-4 py-2 hover:text-polkadot-500 border-t border-r rounded-tr border-polkadot-200",
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { FC } from "react"
|
||||
import { BlockInfo } from "../block.state"
|
||||
import { Binary, Codec } from "polkadot-api"
|
||||
import { bytesToString } from "@/components/BinaryInput"
|
||||
import { CopyText } from "@/components/Copy"
|
||||
import { JsonDisplay } from "@/components/JsonDisplay"
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion"
|
||||
import { groupBy } from "@/lib/groupBy"
|
||||
import { dynamicBuilder$, lookup$ } from "@/state/chains/chain.state"
|
||||
import {
|
||||
HexString,
|
||||
Struct,
|
||||
@@ -11,49 +19,21 @@ import {
|
||||
u8,
|
||||
Vector,
|
||||
} from "@polkadot-api/substrate-bindings"
|
||||
import { state, useStateObservable } from "@react-rxjs/core"
|
||||
import { dynamicBuilder$, lookup$ } from "@/state/chains/chain.state"
|
||||
import { chopsticksInstance$ } from "@/chopsticks/chopsticks"
|
||||
import { switchMap, map, combineLatest } from "rxjs"
|
||||
import { toHex } from "@polkadot-api/utils"
|
||||
import { groupBy } from "@/lib/groupBy"
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion"
|
||||
import { bytesToString } from "@/components/BinaryInput"
|
||||
import { CopyText } from "@/components/Copy"
|
||||
import { JsonDisplay } from "@/components/JsonDisplay"
|
||||
|
||||
const storageDiff$ = (hash: string) =>
|
||||
chopsticksInstance$.pipe(
|
||||
switchMap((chain) => {
|
||||
if (!chain) return [null]
|
||||
|
||||
return chain.getBlock(hash as any)
|
||||
}),
|
||||
switchMap((block) => {
|
||||
if (!block) return [null]
|
||||
return block.storageDiff()
|
||||
}),
|
||||
map((v) =>
|
||||
v && Object.keys(v).length > 0
|
||||
? (v as Record<string, string | null>)
|
||||
: null,
|
||||
),
|
||||
// map((v) => v ?? testDiff),
|
||||
)
|
||||
import { state, useStateObservable } from "@react-rxjs/core"
|
||||
import { Binary, Codec } from "polkadot-api"
|
||||
import { FC } from "react"
|
||||
import { combineLatest, map } from "rxjs"
|
||||
import { BlockInfo, blockInfo$ } from "../block.state"
|
||||
|
||||
const TWOX128_LEN = 32
|
||||
|
||||
const blockDiff$ = state(
|
||||
(hash: string) =>
|
||||
// TODO for current block
|
||||
combineLatest([lookup$, dynamicBuilder$, storageDiff$(hash)]).pipe(
|
||||
map(([lookup, dynamicBuilder, diff]) => {
|
||||
if (!diff) return null
|
||||
combineLatest([lookup$, dynamicBuilder$, blockInfo$(hash)]).pipe(
|
||||
map(([lookup, dynamicBuilder, block]) => {
|
||||
if (!block.diff) return null
|
||||
|
||||
const palletKeys = Object.fromEntries(
|
||||
lookup.metadata.pallets.map((pallet) => [
|
||||
@@ -70,22 +50,29 @@ const blockDiff$ = state(
|
||||
]),
|
||||
)
|
||||
|
||||
return Object.entries(diff)
|
||||
.filter(([, newValue]) => newValue !== null)
|
||||
return Object.entries(block.diff)
|
||||
.map(
|
||||
([key, newValue]): {
|
||||
([key, [prevValue, newValue]]): {
|
||||
key: HexString
|
||||
decodedKey: [string, ...unknown[]]
|
||||
newValue: HexString
|
||||
prevValue: HexString | null
|
||||
decodedPrevValue: unknown
|
||||
newValue: HexString | null
|
||||
decodedNewValue: unknown
|
||||
} | null => {
|
||||
try {
|
||||
if (wellKnownKeys[key]) {
|
||||
return {
|
||||
key,
|
||||
newValue: newValue!,
|
||||
prevValue,
|
||||
newValue,
|
||||
decodedKey: [wellKnownKeys[key].name],
|
||||
decodedNewValue: wellKnownKeys[key].codec.dec(newValue!),
|
||||
decodedNewValue: newValue
|
||||
? wellKnownKeys[key].codec.dec(newValue)
|
||||
: null,
|
||||
decodedPrevValue: prevValue
|
||||
? wellKnownKeys[key].codec.dec(prevValue)
|
||||
: null,
|
||||
}
|
||||
}
|
||||
const pallet = palletKeys[key.slice(2, 2 + TWOX128_LEN)]
|
||||
@@ -99,13 +86,19 @@ const blockDiff$ = state(
|
||||
if (storageCodec) {
|
||||
return {
|
||||
key,
|
||||
newValue: newValue!,
|
||||
prevValue,
|
||||
newValue,
|
||||
decodedKey: [
|
||||
pallet.name,
|
||||
entry,
|
||||
...storageCodec.keys.dec(key),
|
||||
],
|
||||
decodedNewValue: storageCodec.value.dec(newValue!),
|
||||
decodedNewValue: newValue
|
||||
? storageCodec.value.dec(newValue)
|
||||
: null,
|
||||
decodedPrevValue: prevValue
|
||||
? storageCodec.value.dec(prevValue)
|
||||
: null,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,7 +157,16 @@ export const BlockStorageDiff: FC<{
|
||||
)
|
||||
.join(".")}
|
||||
</div>
|
||||
<JsonDisplay collapsed src={change.decodedNewValue} />
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div className="overflow-auto">
|
||||
<div className="text-muted-foreground">Previous Value</div>
|
||||
<JsonDisplay collapsed={1} src={change.decodedPrevValue} />
|
||||
</div>
|
||||
<div className="overflow-auto">
|
||||
<div className="text-muted-foreground">New Value</div>
|
||||
<JsonDisplay collapsed={1} src={change.decodedNewValue} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</AccordionContent>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { chopsticksInstance$ } from "@/chopsticks/chopsticks"
|
||||
import { chainClient$, client$ } from "@/state/chains/chain.state"
|
||||
import { SystemEvent } from "@polkadot-api/observable-client"
|
||||
import { state } from "@react-rxjs/core"
|
||||
@@ -30,6 +31,7 @@ import {
|
||||
takeWhile,
|
||||
tap,
|
||||
timer,
|
||||
toArray,
|
||||
withLatestFrom,
|
||||
} from "rxjs"
|
||||
|
||||
@@ -57,6 +59,7 @@ export interface BlockInfo {
|
||||
digests: unknown[]
|
||||
} | null
|
||||
status: BlockState
|
||||
diff: Record<string, [string | null, string | null]> | null
|
||||
}
|
||||
export const [blockInfo$, recordedBlocks$] = partitionByKey(
|
||||
client$.pipe(switchMap((client) => client.blocks$)),
|
||||
@@ -98,6 +101,7 @@ export const [blockInfo$, recordedBlocks$] = partitionByKey(
|
||||
}),
|
||||
),
|
||||
status: getBlockStatus$(client, hash, number),
|
||||
diff: getBlockDiff$(parent, hash),
|
||||
}),
|
||||
NEVER,
|
||||
),
|
||||
@@ -159,6 +163,7 @@ const getUnpinnedBlockInfo$ = (hash: string): Observable<BlockInfo> => {
|
||||
},
|
||||
number: Number(header.number),
|
||||
status: BlockState.Finalized,
|
||||
diff: null,
|
||||
}),
|
||||
),
|
||||
tap((v) => disconnectedBlocks$.next(v)),
|
||||
@@ -265,3 +270,49 @@ const getBlockStatus$ = (
|
||||
true,
|
||||
),
|
||||
)
|
||||
|
||||
const getBlockDiff$ = (
|
||||
parent: string,
|
||||
hash: string,
|
||||
): Observable<Record<string, [string | null, string | null]> | null> =>
|
||||
chopsticksInstance$.pipe(
|
||||
take(1),
|
||||
switchMap((chain) => (chain ? chain.getBlock(hash as any) : [null])),
|
||||
switchMap((block) => (block ? block.storageDiff() : [null])),
|
||||
map((v) =>
|
||||
v && Object.keys(v).length > 0
|
||||
? (v as Record<string, string | null>)
|
||||
: null,
|
||||
),
|
||||
withLatestFrom(chainClient$),
|
||||
switchMap(([diff, { chainHead }]) => {
|
||||
if (!diff) return [null]
|
||||
|
||||
return chainHead
|
||||
.storageQueries$(
|
||||
parent,
|
||||
Object.keys(diff).map((key) => ({
|
||||
key,
|
||||
type: "value",
|
||||
})),
|
||||
)
|
||||
.pipe(
|
||||
toArray(),
|
||||
map(
|
||||
(previousResults): Record<string, [string | null, string | null]> =>
|
||||
Object.fromEntries(
|
||||
previousResults
|
||||
.map((result) => [
|
||||
result.key,
|
||||
[result.value ?? null, diff[result.key]],
|
||||
])
|
||||
.filter(([, [prevVal, newVal]]) => prevVal !== newVal),
|
||||
),
|
||||
),
|
||||
catchError((ex) => {
|
||||
console.error(ex)
|
||||
return [null]
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user