Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explore Endpoints: pages part 1 #805

Merged
merged 9 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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("/");
Copy link
Contributor

@jeesunikim jeesunikim Apr 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

double-slash

it creates double slashes, /effects as an example because of an empty string in between.
['', 'liquidity_pools', '', 'effects']

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is intentional because a required param is missing. We do the same thing in the current Lab, and I couldn't come up with a better solution. Open to suggestions and improvements!

endpoints-lp-example.webm

Copy link
Contributor

@jeesunikim jeesunikim Apr 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for the details! I think this is fine :)

};

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
Loading