From d6f4c046a09a36bd47abd37c104499ef75baeaae Mon Sep 17 00:00:00 2001 From: Iveta Date: Wed, 3 Apr 2024 16:56:47 -0400 Subject: [PATCH] Explore Endpoints: pages part 2 (#807) * All Assets * Claimable Balances * Effects + refactor endpoint URL template * Fee Stats * Ledgers * Offers * Operations * Payments * Transactions * Update SDS * Order book + asset object params * Trade aggregations * Fix PrettyJson records rendering * Trades * Cleanup * Cleanup * Fix linting --- package.json | 2 +- .../explore-endpoints/[[...pages]]/page.tsx | 188 +++++++++++--- .../FormElements/PositiveIntPicker.tsx | 1 - src/components/FormElements/TextPicker.tsx | 1 - src/components/FormElements/XdrPicker.tsx | 39 +++ src/components/NetworkIndicator/styles.scss | 2 +- src/components/PrettyJson/index.tsx | 24 +- src/components/formComponentTemplate.tsx | 136 +++++++++- src/constants/exploreEndpointsPages.ts | 232 ++++++++++++++++-- src/query/useExploreEndpoint.ts | 26 +- src/styles/globals.scss | 6 + src/validate/index.ts | 2 + src/validate/methods/xdr.ts | 7 + yarn.lock | 101 ++++---- 14 files changed, 652 insertions(+), 115 deletions(-) create mode 100644 src/components/FormElements/XdrPicker.tsx create mode 100644 src/validate/methods/xdr.ts diff --git a/package.json b/package.json index 8490d405..ba10109c 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "git-info": "rm -rf src/generated/ && mkdir src/generated/ && echo export default \"{\\\"commitHash\\\": \\\"$(git rev-parse --short HEAD)\\\", \\\"version\\\": \\\"$(git describe --tags --always)\\\"};\" > src/generated/gitInfo.ts" }, "dependencies": { - "@stellar/design-system": "^2.0.0-beta.8", + "@stellar/design-system": "^2.0.0-beta.9", "@stellar/stellar-sdk": "^11.3.0", "@tanstack/react-query": "^5.28.8", "@tanstack/react-query-devtools": "^5.28.8", diff --git a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx index dac2e54c..7bce3c73 100644 --- a/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx +++ b/src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx @@ -11,6 +11,7 @@ import { Input, Link, Text, + Textarea, } from "@stellar/design-system"; import { useQueryClient } from "@tanstack/react-query"; @@ -85,7 +86,7 @@ export default function ExploreEndpoints() { const param = p.match(REGEX_TEMPLATE_PATH_PARAM_VALUE)?.[0]; if (param) { - return urlPathParamArr.push(param); + urlPathParamArr.push(param); } }); @@ -115,7 +116,12 @@ export default function ExploreEndpoints() { refetch, isSuccess, isError, - } = useExploreEndpoint(requestUrl); + } = useExploreEndpoint( + requestUrl, + // There is only one endpoint request for POST, using params directly for + // simplicity. + pageData?.requestMethod === "POST" ? { tx: params.tx ?? "" } : undefined, + ); const responseEl = useRef(null); @@ -138,18 +144,34 @@ export default function ExploreEndpoints() { // Checking if there are any errors isValid = isEmptyObject(formError); - // When non-native asset is selected, code and issuer fields are required - if (params.asset) { - const assetObj = parseJsonString(params.asset); + // Asset components + const assetParams = [ + params.asset, + params.selling_asset, + params.buying_asset, + params.base_asset, + params.counter_asset, + ]; + + assetParams.forEach((aParam) => { + // No need to keep checking if one field is invalid + if (!isValidReqAssetFields) { + return; + } + + // When non-native asset is selected, code and issuer fields are required + if (aParam) { + const assetObj = parseJsonString(aParam); - if ( - ["issued", "credit_alphanum4", "credit_alphanum12"].includes( - assetObj.type, - ) - ) { - isValidReqAssetFields = Boolean(assetObj.code && assetObj.issuer); + if ( + ["issued", "credit_alphanum4", "credit_alphanum12"].includes( + assetObj.type, + ) + ) { + isValidReqAssetFields = Boolean(assetObj.code && assetObj.issuer); + } } - } + }); return isValidReqAssetFields && isValidReqFields && isValid; }; @@ -158,7 +180,6 @@ export default function ExploreEndpoints() { () => queryClient.resetQueries({ queryKey: ["exploreEndpoint", "response"], - exact: true, }), [queryClient], ); @@ -192,6 +213,35 @@ export default function ExploreEndpoints() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const mapParamToValue = (key: string, value: string) => { + const [param, prop] = (value as string)?.split(".") || []; + const mappedValue = parseJsonString(params?.[param])?.[prop]; + + return mappedValue ? { [key]: mappedValue } : {}; + }; + + // Persist mapped custom template props to params + useEffect(() => { + const paramMapping = pageData?.custom?.paramMapping; + + if (paramMapping) { + const mappedParams = Object.entries(paramMapping).reduce( + (res, [key, value]) => { + const mappedVal = mapParamToValue(key, value as string); + + return { ...res, ...mappedVal }; + }, + {} as AnyObject, + ); + + if (!isEmptyObject(mappedParams)) { + updateParams(mappedParams); + } + } + // Run this only once when page loads + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { if (currentPage) { updateCurrentEndpoint(currentPage); @@ -248,6 +298,11 @@ export default function ExploreEndpoints() { }; const baseUrl = `${endpointNetwork.horizonUrl}${parseUrlPath(urlPath)}`; + + if (pageData?.requestMethod === "POST") { + return baseUrl; + } + const searchParams = new URLSearchParams(); const templateParams = urlParams?.split(","); @@ -305,44 +360,72 @@ export default function ExploreEndpoints() { }, delay); }; + const renderPostPayload = () => { + if (pageData?.requestMethod === "POST") { + return ( +
+