Skip to content

Commit

Permalink
Explore Endpoints: pages part 1 (#805)
Browse files Browse the repository at this point in the history
* All Assets

* Claimable Balances

* Effects + refactor endpoint URL template

* Fee Stats

* Ledgers

* Offers

* Operations

* Fix positiveInt validation when changing routes
  • Loading branch information
quietbits authored Apr 3, 2024
1 parent 083d0cb commit 9cdbed6
Show file tree
Hide file tree
Showing 9 changed files with 607 additions and 55 deletions.
109 changes: 87 additions & 22 deletions src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,56 @@ export default function ExploreEndpoints() {
resetParams,
} = exploreEndpoints;

const REGEX_TEMPLATE_SEARCH_PARAMS = /\{\?.+?\}/;
const REGEX_TEMPLATE_SEARCH_PARAMS_VALUE = /(?<=\{\?).+?(?=\})/;
const REGEX_TEMPLATE_PATH_PARAM_VALUE = /(?<=\{).+?(?=\})/;

// Parse page URL from the template to get path and search params
const parseTemplate = useCallback((templateString: string | undefined) => {
let template = templateString;
let templateParams = "";

if (template) {
const matchSearchParams = template.match(
REGEX_TEMPLATE_SEARCH_PARAMS,
)?.[0];

if (matchSearchParams) {
template = template.replace(matchSearchParams, "");
templateParams =
matchSearchParams.match(REGEX_TEMPLATE_SEARCH_PARAMS_VALUE)?.[0] ??
"";
}
}

// Getting path params
if (template) {
const urlPathParamArr: string[] = [];

template.split("/").forEach((p) => {
const param = p.match(REGEX_TEMPLATE_PATH_PARAM_VALUE)?.[0];

if (param) {
return urlPathParamArr.push(param);
}
});

setUrlPathparams(urlPathParamArr.join(","));
}

return {
templatePath: template ?? "",
templateParams: templateParams ?? "",
};
// Not including RegEx const
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const [formError, setFormError] = useState<AnyObject>({});
const [requestUrl, setRequestUrl] = useState<string>("");
const [urlPath, setUrlPath] = useState("");
const [urlPathParams, setUrlPathparams] = useState("");
const [urlParams, setUrlParams] = useState("");

const queryClient = useQueryClient();
const {
Expand Down Expand Up @@ -156,6 +204,14 @@ export default function ExploreEndpoints() {
}
}, [currentEndpoint, currentPage, resetStates, updateCurrentEndpoint]);

useEffect(() => {
const { templatePath, templateParams } = parseTemplate(
pageData?.endpointUrlTemplate,
);
setUrlPath(templatePath);
setUrlParams(templateParams);
}, [pageData?.endpointUrlTemplate, parseTemplate]);

useEffect(() => {
// Save network for endpoints if we don't have it yet.
if (network.id && !endpointNetwork.id) {
Expand All @@ -175,33 +231,42 @@ export default function ExploreEndpoints() {
}, [isSuccess, isError]);

const buildUrl = useCallback(() => {
const mapPathParamToValue = (pathParams: string[]) => {
return pathParams.map((pp) => params[pp] ?? pp).join("/");
};
const parseUrlPath = (path: string) => {
const pathArr: string[] = [];

path.split("/").forEach((p) => {
const param = p.match(REGEX_TEMPLATE_PATH_PARAM_VALUE)?.[0];

if (param) {
return pathArr.push(params[param] ?? "");
}

const endpointPath = `/accounts${pageData?.endpointPathParams ? `/${mapPathParamToValue(pageData.endpointPathParams.split(","))}` : ""}`;
const endpointParams = pageData?.endpointParams;
return pathArr.push(p);
});

const baseUrl = `${endpointNetwork.horizonUrl}${endpointPath}`;
return pathArr.join("/");
};

const baseUrl = `${endpointNetwork.horizonUrl}${parseUrlPath(urlPath)}`;
const searchParams = new URLSearchParams();
const templateParams = endpointParams?.split(",");
const templateParams = urlParams?.split(",");

const getParamRequestValue = (param: string) => {
const value = parseJsonString(params[param]);

if (!value) {
if (!value && typeof value !== "boolean") {
return false;
}

if (param === "asset") {
if (["asset", "selling", "buying"].includes(param)) {
if (value.type === "native") {
return "native";
}

return `${value.code}:${value.issuer}`;
}

return value;
return `${value}`;
};

// Build search params keeping the same params order
Expand All @@ -216,12 +281,9 @@ export default function ExploreEndpoints() {
const searchParamString = searchParams.toString();

return `${baseUrl}${searchParamString ? `?${searchParamString}` : ""}`;
}, [
endpointNetwork.horizonUrl,
pageData?.endpointParams,
pageData?.endpointPathParams,
params,
]);
// Not including RegEx const
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [endpointNetwork.horizonUrl, params, urlParams, urlPath]);

useEffect(() => {
setRequestUrl(buildUrl());
Expand Down Expand Up @@ -279,15 +341,15 @@ export default function ExploreEndpoints() {
};

const renderFields = () => {
if (!pageData) {
return null;
}

const allFields = sanitizeArray([
...pageData.endpointPathParams.split(","),
...pageData.endpointParams.split(","),
...urlPathParams.split(","),
...urlParams.split(","),
]);

if (!pageData || allFields.length === 0) {
return null;
}

return (
<div className="Endpoints__content">
<div className="PageBody__content">
Expand Down Expand Up @@ -320,6 +382,8 @@ export default function ExploreEndpoints() {

switch (f) {
case "asset":
case "selling":
case "buying":
return component.render({
value: params[f],
error: formError[f],
Expand All @@ -334,6 +398,7 @@ export default function ExploreEndpoints() {
},
});
case "order":
case "include_failed":
return component.render({
value: params[f],
error: formError[f],
Expand Down
2 changes: 1 addition & 1 deletion src/components/FormElements/LimitPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ interface LimitPickerProps extends Omit<InputProps, "fieldSize"> {
id: string;
fieldSize?: "sm" | "md" | "lg";
labelSuffix?: string | React.ReactNode;
placeholder?: string;
value: string;
placeholder?: string;
error: string | undefined;
// eslint-disable-next-line no-unused-vars
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
Expand Down
38 changes: 38 additions & 0 deletions src/components/FormElements/PositiveIntPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from "react";
import { Input, InputProps } from "@stellar/design-system";

interface PositiveIntPickerProps extends Omit<InputProps, "fieldSize"> {
id: string;
fieldSize?: "sm" | "md" | "lg";
labelSuffix?: string | React.ReactNode;
label: string;
value: string;
placeholder?: string;
error: string | undefined;
// eslint-disable-next-line no-unused-vars
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}

export const PositiveIntPicker = ({
id,
fieldSize = "md",
labelSuffix,
label,
value,
error,
onChange,
...props
}: PositiveIntPickerProps) => {
return (
<Input
id={id}
fieldSize={fieldSize}
label={label}
labelSuffix={labelSuffix}
value={value}
error={error}
onChange={onChange}
{...props}
/>
);
};
38 changes: 38 additions & 0 deletions src/components/FormElements/TextPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from "react";
import { Input, InputProps } from "@stellar/design-system";

interface TextPickerProps extends Omit<InputProps, "fieldSize"> {
id: string;
fieldSize?: "sm" | "md" | "lg";
labelSuffix?: string | React.ReactNode;
label: string;
value: string;
placeholder?: string;
error: string | undefined;
// eslint-disable-next-line no-unused-vars
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}

export const TextPicker = ({
id,
fieldSize = "md",
labelSuffix,
label,
value,
error,
onChange,
...props
}: TextPickerProps) => {
return (
<Input
id={id}
fieldSize={fieldSize}
label={label}
labelSuffix={labelSuffix}
value={value}
error={error}
onChange={onChange}
{...props}
/>
);
};
Loading

0 comments on commit 9cdbed6

Please sign in to comment.