mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-11-17 02:05:50 +00:00
feat: migrate rpc-tool to redocly
Differences between original: - react18-json-view used for responses - cleaned up markup for expand and collapse Original work on this was done by @khancode Co-authored-by: Omar Khan <khancodegt@gmail.com> Fix RPC sidebar and width
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import { useTranslate } from '@portal/hooks'
|
||||
import { ReactElement, useState } from 'react';
|
||||
import JsonView from 'react18-json-view'
|
||||
|
||||
interface RPCResponseGroupProps {
|
||||
response: any
|
||||
anchor: ReactElement
|
||||
customExpanded?: number,
|
||||
customExpandedText?: string
|
||||
}
|
||||
|
||||
export const RPCResponseGroup = ({ response, anchor, customExpanded, customExpandedText }: RPCResponseGroupProps) => {
|
||||
const [expanded, setExpanded] = useState<number | false>(1)
|
||||
|
||||
return <div className="group group-tx">
|
||||
<h3>{anchor}</h3>
|
||||
<RPCResponseGroupExpanders customExpanded={customExpanded} customExpandedText={customExpandedText} setExpanded={setExpanded} />
|
||||
<JsonView
|
||||
src={response}
|
||||
collapsed={expanded}
|
||||
collapseStringsAfterLength={100}
|
||||
enableClipboard={false}
|
||||
/>
|
||||
<RPCResponseGroupExpanders customExpanded={customExpanded} customExpandedText={customExpandedText} setExpanded={setExpanded} />
|
||||
</div>
|
||||
}
|
||||
|
||||
const RPCResponseGroupExpanders = ({ customExpanded, customExpandedText, setExpanded }) => {
|
||||
const { translate } = useTranslate();
|
||||
|
||||
return <ul className="nav nav-pills">
|
||||
{customExpanded && customExpandedText && (
|
||||
<li className="nav-item">
|
||||
<a className="nav-link" onClick={() => setExpanded(customExpanded)}>
|
||||
{customExpandedText}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
<li className="nav-item">
|
||||
<a className="nav-link" onClick={() => setExpanded(false)}>{translate("expand all")}</a>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<a className="nav-link" onClick={() => setExpanded(1)}>
|
||||
{translate("collapse all")}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
320
content/resources/dev-tools/rpc-tool.page.tsx
Normal file
320
content/resources/dev-tools/rpc-tool.page.tsx
Normal file
@@ -0,0 +1,320 @@
|
||||
import { useTranslate } from "@portal/hooks";
|
||||
import { Link } from '@portal/Link';
|
||||
import { useState, useEffect } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import JsonView from 'react18-json-view'
|
||||
import { Client, isValidAddress } from 'xrpl'
|
||||
|
||||
import { RPCResponseGroup } from './components/rpc-tool/rpc-response-group';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
export default function RpcTool() {
|
||||
const { hash: slug } = useLocation();
|
||||
const [accountInfoResponse, setAccountInfoResponse] = useState(null);
|
||||
const [accountLinesResponse, setAccountLinesResponse] = useState(null);
|
||||
const [accountTxResponse, setAccountTxResponse] = useState(null);
|
||||
const [accountObjectsResponse, setAccountObjectsResponse] = useState(null);
|
||||
const [txResponse, setTxResponse] = useState(null);
|
||||
const [ledgerResponse, setLedgerResponse] = useState(null);
|
||||
const [inputText, setInputText] = useState(slug ? slug.slice(1) : "");
|
||||
const [errorText, setErrorText] = useState(null);
|
||||
const { translate } = useTranslate();
|
||||
const [inputType, setInputType] = useState("");
|
||||
const [showResult, setShowResult] = useState(false);
|
||||
const [progressBarWidth, setProgressBarWidth] = useState("0%");
|
||||
|
||||
const FULL_HISTORY_SERVER = "wss://s2.ripple.com";
|
||||
const reTxId = /^[0-9A-Fa-f]{64}$/;
|
||||
const reLedgerSeq = /^[0-9]+$/;
|
||||
|
||||
/*
|
||||
XRPL requests
|
||||
* account_info
|
||||
* account_lines
|
||||
* account_tx
|
||||
* account_objects
|
||||
* tx
|
||||
* ledger
|
||||
*/
|
||||
|
||||
const accountInfo = async (
|
||||
client: Client,
|
||||
address: string
|
||||
): Promise<void> => {
|
||||
const response = await client.request({
|
||||
command: "account_info",
|
||||
account: address,
|
||||
});
|
||||
setProgressBarWidth("20%");
|
||||
setAccountInfoResponse(response);
|
||||
};
|
||||
|
||||
const accountLines = async (
|
||||
client: Client,
|
||||
address: string
|
||||
): Promise<void> => {
|
||||
const response = await client.request({
|
||||
command: "account_lines",
|
||||
account: address,
|
||||
});
|
||||
setProgressBarWidth("40%");
|
||||
setAccountLinesResponse(response);
|
||||
};
|
||||
|
||||
const accountTx = async (
|
||||
client: Client,
|
||||
address: string
|
||||
): Promise<void> => {
|
||||
const response = await client.request({
|
||||
command: "account_tx",
|
||||
account: address,
|
||||
ledger_index_min: -1,
|
||||
ledger_index_max: -1,
|
||||
binary: false,
|
||||
limit: 20,
|
||||
forward: false,
|
||||
});
|
||||
setProgressBarWidth("60%");
|
||||
setAccountTxResponse(response);
|
||||
};
|
||||
|
||||
const accountObjects = async (
|
||||
client: Client,
|
||||
address: string
|
||||
): Promise<void> => {
|
||||
const response = await client.request({
|
||||
command: "account_objects",
|
||||
account: address,
|
||||
});
|
||||
setProgressBarWidth("80%");
|
||||
setAccountObjectsResponse(response);
|
||||
};
|
||||
|
||||
const tx = async (
|
||||
client: Client,
|
||||
transactionId: string
|
||||
): Promise<void> => {
|
||||
const response = await client.request({
|
||||
command: "tx",
|
||||
transaction: transactionId,
|
||||
binary: false,
|
||||
});
|
||||
setProgressBarWidth("100%");
|
||||
setTxResponse(response);
|
||||
};
|
||||
|
||||
const ledger = async (
|
||||
client: Client,
|
||||
ledgerSequence: string
|
||||
): Promise<void> => {
|
||||
const response = await client.request({
|
||||
command: "ledger",
|
||||
ledger_index: parseInt(ledgerSequence),
|
||||
transactions: true,
|
||||
expand: true,
|
||||
});
|
||||
setProgressBarWidth("100%");
|
||||
setLedgerResponse(response);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (slug && slug !== "") {
|
||||
getInfo();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const getInfo = async (): Promise<void> => {
|
||||
setAccountInfoResponse(null);
|
||||
setAccountLinesResponse(null);
|
||||
setAccountTxResponse(null);
|
||||
setAccountObjectsResponse(null);
|
||||
setTxResponse(null);
|
||||
setLedgerResponse(null);
|
||||
setErrorText(null);
|
||||
setShowResult(true);
|
||||
setProgressBarWidth("0%");
|
||||
|
||||
const client = new Client(FULL_HISTORY_SERVER);
|
||||
await client.connect();
|
||||
|
||||
if (isValidAddress(inputText)) {
|
||||
// Example input: rh3VLyj1GbQjX7eA15BwUagEhSrPHmLkSR
|
||||
setInputType("accounts");
|
||||
setErrorText("");
|
||||
|
||||
setProgressBarWidth("10%");
|
||||
|
||||
// account_info
|
||||
await accountInfo(client, inputText);
|
||||
|
||||
// account_lines
|
||||
await accountLines(client, inputText);
|
||||
|
||||
// account_tx
|
||||
await accountTx(client, inputText);
|
||||
|
||||
// account_objects
|
||||
await accountObjects(client, inputText);
|
||||
setProgressBarWidth("100%");
|
||||
} else if (reTxId.test(inputText)) {
|
||||
// Example input: A25795C88E176FFF85B8D595D1960229F4ACC825BAE634ADF38F6AE38E0D24D8
|
||||
setInputType("transactions");
|
||||
setErrorText("");
|
||||
setProgressBarWidth("10%");
|
||||
|
||||
// tx
|
||||
await tx(client, inputText);
|
||||
} else if (reLedgerSeq.test(inputText)) {
|
||||
// Example input: 131524184
|
||||
setInputType("ledgers");
|
||||
setErrorText("");
|
||||
setProgressBarWidth("10%");
|
||||
|
||||
// ledger
|
||||
await ledger(client, inputText);
|
||||
} else {
|
||||
setProgressBarWidth("100%");
|
||||
|
||||
setErrorText("Input is not a valid address or transaction hash");
|
||||
}
|
||||
|
||||
await client.disconnect();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container-fluid rpc-tool" role="document" id="main_content_wrapper">
|
||||
<div className="row">
|
||||
<main
|
||||
className="main order-md-3"
|
||||
role="main"
|
||||
id="main_content_body"
|
||||
>
|
||||
<section className="container-fluid pt-3 p-md-3">
|
||||
<h1>RPC Tool</h1>
|
||||
<div className="content">
|
||||
<p>
|
||||
{translate(
|
||||
"This is a debug tool for printing raw information about an account (by classic address), a transaction (by hash) or a ledger (by sequence number)."
|
||||
)}
|
||||
</p>
|
||||
<input
|
||||
onChange={(event) => setInputText(event.target.value)}
|
||||
value={inputText}
|
||||
id="target"
|
||||
className="form-control"
|
||||
required
|
||||
type="text"
|
||||
placeholder={translate(
|
||||
"XRP Ledger address, transaction ID, or ledger index"
|
||||
)}
|
||||
/>
|
||||
<span className="help-block">
|
||||
<small>
|
||||
{translate("Try an account like ")}
|
||||
<em>rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn</em>.
|
||||
</small>
|
||||
</span>
|
||||
<button className="btn btn-primary" onClick={getInfo}>
|
||||
{translate("Get info")}
|
||||
</button>
|
||||
{showResult && (
|
||||
<div id="result">
|
||||
<h2>{translate("Result")}</h2>
|
||||
<div
|
||||
id="progress"
|
||||
className="progress"
|
||||
style={
|
||||
progressBarWidth === "100%" ?
|
||||
{ transition: 'opacity 0.5s ease-out', opacity: 0, } :
|
||||
{ opacity: 1 }
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="progress-bar progress-bar-striped progress-bar-animated"
|
||||
role="progressbar"
|
||||
style={{
|
||||
width: progressBarWidth,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{errorText && (
|
||||
<div id="error" className="devportal-callout warning">
|
||||
{translate(errorText)}
|
||||
</div>
|
||||
)}
|
||||
{errorText === "" && (
|
||||
<ul id="links" className="nav nav-pills">
|
||||
<li>
|
||||
<Link
|
||||
id="permalink"
|
||||
to={`/resources/dev-tools/rpc-tool#${inputText}`}
|
||||
target="_blank"
|
||||
>
|
||||
{translate("Permalink")}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
id="explorerlink"
|
||||
to={`https://livenet.xrpl.org/${inputType}/${inputText}`}
|
||||
target="_blank"
|
||||
>
|
||||
{translate("Explorer")}
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
{txResponse && (
|
||||
<RPCResponseGroup response={txResponse}
|
||||
anchor={<a href="tx.html">tx</a>}
|
||||
customExpanded={3}
|
||||
customExpandedText={translate("expand tx")}
|
||||
/>
|
||||
)}
|
||||
<div className="group group-account">
|
||||
{accountInfoResponse && (
|
||||
<>
|
||||
<h3>
|
||||
<a href="account_info.html">account_info</a>
|
||||
</h3>
|
||||
<JsonView
|
||||
src={accountInfoResponse}
|
||||
collapsed={1}
|
||||
collapseStringsAfterLength={100}
|
||||
enableClipboard={false}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{accountLinesResponse && (
|
||||
<RPCResponseGroup response={accountLinesResponse}
|
||||
anchor={<a href="account_lines.html">account_lines</a>}
|
||||
/>)
|
||||
}
|
||||
{accountTxResponse && (
|
||||
<RPCResponseGroup response={accountTxResponse}
|
||||
anchor={<><a href="account_tx.html">account_tx</a>{" "} {translate("(last 20)")}</>}
|
||||
customExpanded={3}
|
||||
customExpandedText={translate("expand tx")}
|
||||
/>)
|
||||
}
|
||||
{accountObjectsResponse && (
|
||||
<RPCResponseGroup response={accountObjectsResponse}
|
||||
anchor={<a href="account_objects.html">account_objects</a>}
|
||||
/>)
|
||||
}
|
||||
</div>
|
||||
{ledgerResponse && (
|
||||
<RPCResponseGroup response={ledgerResponse}
|
||||
anchor={<a href="ledger.html">ledger</a>}
|
||||
/>)
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -631,16 +631,15 @@
|
||||
expanded: false
|
||||
items:
|
||||
- label: RPC Tool
|
||||
page: resources/dev-tools/rpc-tool.page.tsx
|
||||
- label: WebSocket API Tool
|
||||
- label: ripple.txt Validator
|
||||
- label: xrp-ledger.toml Checker
|
||||
- label: Domain Verification Checker
|
||||
- label: XRP Faucets
|
||||
href: /dev-tools/xrp-faucets
|
||||
page: dev-tools/xrp-faucets.page.tsx
|
||||
page: resources/dev-tools/xrp-faucets.page.tsx
|
||||
- label: Transaction Sender
|
||||
href: /dev-tools/tx-sender
|
||||
page: dev-tools/tx-sender.page.tsx
|
||||
page: resources/dev-tools/tx-sender.page.tsx
|
||||
- label: XRPL Learning Portal
|
||||
href: https://learn.xrpl.org/
|
||||
external: true
|
||||
|
||||
File diff suppressed because one or more lines are too long
9
package-lock.json
generated
9
package-lock.json
generated
@@ -20,6 +20,7 @@
|
||||
"moment": "^2.29.4",
|
||||
"react": "^18.2.0",
|
||||
"react-alert": "^7.0.3",
|
||||
"react18-json-view": "^0.2.6",
|
||||
"xrpl": "^3.0.0-beta.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -9563,6 +9564,14 @@
|
||||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react18-json-view": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/react18-json-view/-/react18-json-view-0.2.6.tgz",
|
||||
"integrity": "sha512-RHAY880UwC7SClyQBoij50q2InpSrj5zmP2DCL73vEaaVTyj/QbMPBk4FRKMQ7LF8FSxhh+VI6mK3AhlBaCBxw==",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/reactjs-popup": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.6.tgz",
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
"@codemirror/view": "^6.22.2",
|
||||
"@lezer/highlight": "^1.2.0",
|
||||
"@redocly/realm": "0.69.4",
|
||||
"@uiw/react-codemirror": "^4.21.21",
|
||||
"@uiw/codemirror-themes": "4.21.21",
|
||||
"@uiw/react-codemirror": "^4.21.21",
|
||||
"clsx": "^2.0.0",
|
||||
"lottie-react": "^2.4.0",
|
||||
"moment": "^2.29.4",
|
||||
"react": "^18.2.0",
|
||||
"react-alert": "^7.0.3",
|
||||
"react18-json-view": "^0.2.6",
|
||||
"xrpl": "^3.0.0-beta.1"
|
||||
},
|
||||
"overrides": {
|
||||
@@ -30,8 +31,8 @@
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bootstrap": "^4.6.2",
|
||||
"htmltojsx": "^0.3.0",
|
||||
"sass": "1.26.10",
|
||||
"bootstrap": "^4.6.2"
|
||||
"sass": "1.26.10"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +1,66 @@
|
||||
// Styles for the legacy XRP Ledger RPC Tool. Minimally maintained.
|
||||
@import '../node_modules/react18-json-view/src/style';
|
||||
|
||||
.json-view {
|
||||
font-family: $font-family-monospace;
|
||||
padding: 1em;
|
||||
background: $gray-800;
|
||||
overflow: hidden;
|
||||
color: $gray-100 !important;
|
||||
font-size: 14px;
|
||||
letter-spacing: 0;
|
||||
|
||||
svg {
|
||||
height: 11px !important;
|
||||
color: $gray-100;
|
||||
}
|
||||
}
|
||||
|
||||
.jv-button {
|
||||
color: $orange-500 !important;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.jv-indent {
|
||||
border-left: 1px solid $gray-600;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
/* stylelint-disable selector-class-pattern -- react18-json-view uses these */
|
||||
.json-view--boolean {
|
||||
color: $magenta-600 !important;
|
||||
}
|
||||
|
||||
.json-view--pair {
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
.json-view--property {
|
||||
color: $gray-100 !important;
|
||||
}
|
||||
|
||||
.json-view--null,
|
||||
.json-view--undefined {
|
||||
display: inline-block;
|
||||
padding: 1px 2px;
|
||||
border-radius: 3px;
|
||||
background-color: $gray-600;
|
||||
color: $gray-100 !important;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.json-view--number {
|
||||
color: $green-300 !important;
|
||||
}
|
||||
|
||||
.json-view--string {
|
||||
color: $orange-500 !important;
|
||||
}
|
||||
|
||||
/* stylelint-enable selector-class-pattern */
|
||||
|
||||
.rpc-tool {
|
||||
|
||||
#result {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#log {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.content .json li {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.json .name {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.json ul {
|
||||
margin: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.json li {
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.json .type-string .val {
|
||||
color: $green-700;
|
||||
}
|
||||
|
||||
.json .type-number .val {
|
||||
color: $blue-500;
|
||||
}
|
||||
|
||||
.json a.toggle:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.json > a.toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.json a.toggle:after {
|
||||
content: " +\0000a0";
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.json > ul,
|
||||
.json .expanded > ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.json .ellipsis {
|
||||
color: $gray-500;
|
||||
}
|
||||
|
||||
.json > .ellipsis,
|
||||
.json .expanded > .ellipsis {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.json .expanded > a.toggle:after {
|
||||
content: " \2212\0000a0";
|
||||
}
|
||||
|
||||
.json .indentafter {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.json .expanded > .indentafter {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.tools {
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
font-size: 0.8em;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
.tools>li>a {
|
||||
.nav-link {
|
||||
cursor: pointer;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
|
||||
Reference in New Issue
Block a user