Skip to content

Commit

Permalink
SDS v1.0.0 + GH Action update + receiver wallet balance
Browse files Browse the repository at this point in the history
  • Loading branch information
quietbits committed Oct 23, 2023
1 parent d15d262 commit cb5d48e
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 41 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v2
- uses: actions/setup-node@v3
with:
node-version: 18
- run: yarn install
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"private": true,
"dependencies": {
"@reduxjs/toolkit": "^1.9.5",
"@stellar/design-system": "^1.0.0-beta.14",
"@stellar/design-system": "^1.0.0",
"@stellar/tsconfig": "^1.0.2",
"@svgr/webpack": "8.0.1",
"@tanstack/react-query": "^4.29.25",
Expand Down
29 changes: 29 additions & 0 deletions src/apiQueries/useStellarAccountInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useQuery } from "@tanstack/react-query";
import { HORIZON_URL } from "constants/settings";
import { fetchStellarApi } from "helpers/fetchStellarApi";
import { shortenAccountKey } from "helpers/shortenAccountKey";
import { ApiStellarAccount, AppError } from "types";

export const useStellarAccountInfo = (stellarAddress: string | undefined) => {
const query = useQuery<ApiStellarAccount, AppError>({
queryKey: ["stellar", "accounts", stellarAddress],
queryFn: async () => {
if (!stellarAddress) {
return {};
}

return await fetchStellarApi(
`${HORIZON_URL}/accounts/${stellarAddress}`,
undefined,
{
notFoundMessage: `${shortenAccountKey(
stellarAddress,
)} address was not found.`,
},
);
},
enabled: Boolean(stellarAddress),
});

return query;
};
42 changes: 14 additions & 28 deletions src/components/ReceiverWalletBalance.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Fragment, useEffect } from "react";
import { Notification } from "@stellar/design-system";
import { useQuery } from "@tanstack/react-query";
import { getStellarAccountInfo } from "api/getStellarAccountInfo";
import { Fragment } from "react";
import { Loader, Notification } from "@stellar/design-system";
import { useStellarAccountInfo } from "apiQueries/useStellarAccountInfo";
import { AssetAmount } from "components/AssetAmount";

interface ReceiverWalletBalanceProps {
Expand All @@ -11,47 +10,34 @@ interface ReceiverWalletBalanceProps {
export const ReceiverWalletBalance = ({
stellarAddress,
}: ReceiverWalletBalanceProps) => {
const getBalance = async () => {
if (!stellarAddress) {
return [];
}
const { isLoading, isFetching, data, error } =
useStellarAccountInfo(stellarAddress);

const response = await getStellarAccountInfo(stellarAddress);
// We don't want to show XLM (native) balance
return response.balances.filter((b) => b.asset_issuer && b.asset_code);
};
const balances =
data?.balances.filter((b) => b.asset_issuer && b.asset_code) || [];

const { isLoading, data, isError, error, refetch } = useQuery({
queryKey: ["ReceiverWalletBalance"],
queryFn: getBalance,
});

useEffect(() => {
refetch();
}, [stellarAddress, refetch]);

if (isLoading) {
return <div className="Note">Loading…</div>;
if (isLoading || isFetching) {
return <Loader />;
}

if (isError) {
if (error) {
return (
<Notification variant="error" title="Error">
{error as string}
{error.message}
</Notification>
);
}

if (data?.length === 0) {
if (balances?.length === 0) {
return <>{"-"}</>;
}

return (
<>
{data?.map((b, index) => (
{balances?.map((b, index) => (
<Fragment key={`${b.asset_code}-${b.asset_issuer}`}>
<AssetAmount assetCode={b.asset_code ?? ""} amount={b.balance} />
{index < data.length - 1 ? ", " : null}
{index < balances.length - 1 ? ", " : null}
</Fragment>
))}
</>
Expand Down
9 changes: 2 additions & 7 deletions src/components/ReceiverWalletHistory.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useEffect } from "react";
import { Card, Link, Profile, Notification } from "@stellar/design-system";
import { useQuery } from "@tanstack/react-query";

Expand Down Expand Up @@ -60,15 +59,11 @@ export const ReceiverWalletHistory = ({
return payments;
};

const { isLoading, isError, data, error, refetch } = useQuery({
queryKey: ["ReceiverWalletHistory"],
const { isLoading, isError, data, error } = useQuery({
queryKey: ["stellar", "accounts", stellarAddress],
queryFn: getPayments,
});

useEffect(() => {
refetch();
}, [stellarAddress, refetch]);

if (isLoading) {
return <div className="Note">Loading…</div>;
}
Expand Down
25 changes: 25 additions & 0 deletions src/helpers/fetchStellarApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { normalizeStellarApiError } from "helpers/normalizeStellarApiError";

type FetchStellarApiOptions = {
notFoundMessage?: string;
};

export const fetchStellarApi = async (
fetchUrl: string,
fetchOptions?: RequestInit,
options?: FetchStellarApiOptions,
) => {
try {
const request = await fetch(fetchUrl, fetchOptions);

if (request.status === 404) {
throw new Error(options?.notFoundMessage || "Not found");
}

const response = await request.json();

return response;
} catch (e) {
throw normalizeStellarApiError(e as Error);
}
};
109 changes: 109 additions & 0 deletions src/helpers/normalizeStellarApiError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { GENERIC_ERROR_MESSAGE } from "constants/settings";
import { AnyObject, AppError } from "types";

export const normalizeStellarApiError = (
error: Error,
defaultMessage = GENERIC_ERROR_MESSAGE,
): AppError => {
let message = "";

// Make sure error is not an empty object
if (JSON.stringify(error) === "{}") {
message = defaultMessage;
} else {
message = getErrorString(error);
}

return {
message: message,
};
};

// User friendly error messages
const TX_ERROR_TEXT: AnyObject = {
buy_not_authorized:
"The issuer must authorize you to trade this token. Visit the issuer’s site more info.",
op_malformed: "The input is incorrect and would result in an invalid offer.",
op_sell_no_trust: "You are not authorized to sell this asset.",
op_line_full: "You have reached the limit allowed for buying that asset.",
op_no_destination: "The destination account doesn't exist.",
op_no_trust:
"One or more accounts in this transaction doesn't have a trustline with the desired asset.",
op_underfunded: "You don’t have enough to cover that transaction.",
op_under_dest_min:
"We couldn’t complete your transaction at this time because the exchange rate offered is no longer available. Please try again.",
op_over_source_max:
"We couldn’t complete your transaction at this time because the exchange rate offered is no longer available. Please try again.",
op_cross_self:
"You already have an offer out that would immediately cross this one.",
op_sell_no_issuer: "The issuer of that token doesn’t exist.",
buy_no_issuer: "The issuer of that token doesn’t exist.",
op_offer_not_found: "We couldn’t find that offer.",
op_low_reserve: "That offer would take you below the minimum XLM reserve.",
op_not_authorized:
"This operation was not authorized, please make sure the asset you used complies with the Regulated Assets protocol (SEP-8).",
tx_bad_auth: "Something went wrong while signing a transaction.",
tx_bad_seq:
"The app has gotten out of sync with the network. Please try again later.",
tx_too_late: "This transaction has expired.",
};

// Given a Horizon error object, return a human-readable string that summarizes it.
export function getErrorString(err: any): string {
const e = err && err.response ? err.response : err;

// timeout errors return timeout
if (e && e.status === 504) {
return "Sorry, the request timed out! Please try again later.";
}

// first, try to parse the errors in extras
if (e && e.data && e.data.extras && e.data.extras.result_codes) {
const resultCodes = e.data.extras.result_codes;

if (resultCodes.operations) {
// Map all errors into a single message string.
const codes = resultCodes.operations;
// Transactions with multiple operations might have mixed successes and
// errors. Ignore some codes to only handle codes we have messages for.
const ignoredCodes = ["op_success"];
const message = codes
.filter((code: string) => !ignoredCodes.includes(code))
.map((code: string) => TX_ERROR_TEXT[code] || `Error code '${code}'.`)
.join(" ");

if (message) {
return message;
}
}

if (resultCodes.transaction) {
return (
TX_ERROR_TEXT[resultCodes.transaction] ||
`Error code '${resultCodes.transaction}'`
);
}
}

if (e && e.data && e.data.detail) {
return e.data.detail;
}

if (e && e.detail) {
return e.detail;
}

if (e && e.message) {
return e.message;
}

if (e && e.errors) {
return e.errors[0].message;
}

if (e && e.error) {
return e.error;
}

return e.toString();
}
9 changes: 5 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2151,14 +2151,15 @@
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718"
integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==

"@stellar/design-system@^1.0.0-beta.14":
version "1.0.0-beta.14"
resolved "https://registry.yarnpkg.com/@stellar/design-system/-/design-system-1.0.0-beta.14.tgz#56914f4d575eb93b65129ec280f077e845fb6cce"
integrity sha512-51MpT/bg1ShJmbYnB17bphd8jSHCgqfTqudNInXGEM2BcXwH8PQWFDIgQ07jD6wIvOGhRAktyYNE7wQv1X2O2w==
"@stellar/design-system@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@stellar/design-system/-/design-system-1.0.0.tgz#8e1d3be5b20baa475ae373bef5cb7bb92bec9087"
integrity sha512-EKEIY2Ze80DgN6N0TuRxnag6FN9UWkKrM6Uo0Hows/3oSX13c+4DUJdVpuuBsd0J7rgGXZUJ1HE841kLI8dv5Q==
dependencies:
"@floating-ui/dom" "^1.2.5"
bignumber.js "^9.1.1"
configurable-date-input-polyfill "^3.1.5"
lodash "^4.17.21"
react-copy-to-clipboard "^5.1.0"
tslib "^2.5.0"

Expand Down

0 comments on commit cb5d48e

Please sign in to comment.