-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: chain selector initial state (#161)
- Loading branch information
Showing
13 changed files
with
222 additions
and
180 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,155 +1,138 @@ | ||
import { useEffect, useState } from "react"; | ||
import { useEffect, useMemo, useState } from "react"; | ||
import { type GasCardProps } from "@/utils/types/molecules.types"; | ||
import { useGoldRush } from "@/utils/store"; | ||
import type { Option } from "@/utils/option"; | ||
import { None, Some } from "@/utils/option"; | ||
import { type GasPricesResponse, type PriceItem } from "@covalenthq/client-sdk"; | ||
import { type GasPricesResponse } from "@covalenthq/client-sdk"; | ||
import { Skeleton } from "@/components/ui/skeleton"; | ||
import { GRK_SIZES } from "@/utils/constants/shared.constants"; | ||
|
||
export const GasCard: React.FC<GasCardProps> = ({ chain_name, event_type }) => { | ||
const [maybeResult, setResult] = useState<Option<GasPricesResponse>>(None); | ||
const [error, setError] = useState({ error: false, error_message: "" }); | ||
const [errorMessage, setErrorMessage] = useState<string | null>(null); | ||
const { covalentClient } = useGoldRush(); | ||
|
||
const handleGas = async () => { | ||
setResult(None); | ||
let response; | ||
try { | ||
response = await covalentClient.BaseService.getGasPrices( | ||
chain_name, | ||
event_type | ||
); | ||
if (response.error) { | ||
setError({ | ||
error: response ? response.error : false, | ||
error_message: response ? response.error_message : "", | ||
}); | ||
setResult(new Some(response.data)); | ||
return; | ||
} | ||
console.log(response); | ||
setResult(new Some(response.data)); | ||
setError({ error: false, error_message: "" }); | ||
} catch (error) { | ||
setError({ | ||
error: response ? response.error : false, | ||
error_message: response ? response.error_message : "", | ||
}); | ||
console.error(`Error fetching Gas for ${chain_name}:`, error); | ||
} | ||
}; | ||
|
||
useEffect(() => { | ||
handleGas(); | ||
(async () => { | ||
setResult(None); | ||
setErrorMessage(null); | ||
try { | ||
const { data, ...error } = | ||
await covalentClient.BaseService.getGasPrices( | ||
chain_name, | ||
event_type | ||
); | ||
if (error.error) { | ||
setErrorMessage(error.error_message); | ||
throw error; | ||
} | ||
setResult(new Some(data)); | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
})(); | ||
}, [chain_name, event_type]); | ||
|
||
const copy = useMemo< | ||
{ | ||
logo: string; | ||
content: string; | ||
}[] | ||
>( | ||
() => [ | ||
{ | ||
logo: "🚴", | ||
content: "Low", | ||
}, | ||
{ | ||
logo: "🚗", | ||
content: "Normal", | ||
}, | ||
{ | ||
logo: "🚄", | ||
content: "High", | ||
}, | ||
], | ||
[] | ||
); | ||
|
||
return ( | ||
<> | ||
<div className="flex w-full flex-col gap-4 rounded border border-secondary-light dark:border-secondary-dark"> | ||
<div className="bg-accent-light h-12 rounded-t" /> | ||
<div className="flex flex-col items-center justify-center gap-2 p-4"> | ||
<span className="text-7xl">⛽</span> | ||
<h2 className="text-xl">Current Network Fee</h2> | ||
<label className="flex items-center gap-x-2 text-base text-secondary-light dark:text-secondary-dark"> | ||
Base Fee:{" "} | ||
{maybeResult.match({ | ||
None: () => <Skeleton size={GRK_SIZES.MEDIUM} />, | ||
Some: (result) => { | ||
const baseFeeGwei = Math.round( | ||
(parseInt(String(result?.base_fee)) ?? 0) / | ||
Math.pow(10, 9) | ||
); | ||
return <span> {baseFeeGwei}</span>; | ||
}, | ||
})} | ||
</label> | ||
</div> | ||
<div className="flex w-full flex-col gap-6 p-6"> | ||
{maybeResult.match({ | ||
None: () => { | ||
return ( | ||
<div className="flex flex-col gap-3"> | ||
{[1, 2, 3].map((o, i) => { | ||
return ( | ||
<div | ||
className="flex justify-between" | ||
key={i} | ||
> | ||
<Skeleton | ||
size={GRK_SIZES.LARGE} | ||
/> | ||
<Skeleton | ||
size={GRK_SIZES.LARGE} | ||
/> | ||
</div> | ||
); | ||
})} | ||
<div className="flex w-full flex-col gap-4 rounded border border-secondary-light p-2 dark:border-secondary-dark"> | ||
<div className="flex flex-col items-center justify-center gap-2"> | ||
{maybeResult.match({ | ||
None: () => <Skeleton size={GRK_SIZES.LARGE} />, | ||
Some: ({ base_fee }) => ( | ||
<p className="text-xl"> | ||
⛽ Base Fee:{" "} | ||
{Math.round( | ||
(Number(base_fee) ?? 0) / Math.pow(10, 9) | ||
)}{" "} | ||
Gwei | ||
</p> | ||
), | ||
})} | ||
</div> | ||
|
||
<div className="grid grid-cols-3 items-center"> | ||
{maybeResult.match({ | ||
None: () => | ||
Array(3) | ||
.fill(null) | ||
.map(() => ( | ||
<div key={Math.random()} className="mx-auto"> | ||
<Skeleton size={GRK_SIZES.LARGE} /> | ||
</div> | ||
); | ||
}, | ||
Some: (result) => { | ||
if (error.error) { | ||
return ( | ||
<div className="flex items-center justify-center"> | ||
{error.error_message} | ||
</div> | ||
); | ||
} | ||
return result.items | ||
)), | ||
Some: ({ items }) => | ||
errorMessage ? ( | ||
<p className="mt-4">{errorMessage}</p> | ||
) : ( | ||
items | ||
.sort( | ||
( | ||
a: { gas_price: string }, | ||
b: { gas_price: string } | ||
) => | ||
(a, b) => | ||
parseInt(a.gas_price) - | ||
parseInt(b.gas_price) | ||
) | ||
.map((o: PriceItem, i: number) => { | ||
const gwei = Math.round( | ||
parseInt(o.gas_price) / Math.pow(10, 9) | ||
); | ||
return ( | ||
.map( | ||
( | ||
{ | ||
interval, | ||
gas_price, | ||
pretty_total_gas_quote, | ||
}, | ||
i | ||
) => ( | ||
<div | ||
className="flex justify-between" | ||
key={i} | ||
key={Math.random()} | ||
className="text-center" | ||
> | ||
<div className="flex items-center gap-4"> | ||
<div className="text-3xl"> | ||
{i === 0 | ||
? "🚴" | ||
: i === 1 | ||
? "🚗" | ||
: "🚄"} | ||
</div> | ||
<div className="flex flex-col"> | ||
<label> | ||
{i === 0 | ||
? "Low" | ||
: i === 1 | ||
? "Normal" | ||
: "High"} | ||
</label> | ||
<label className="text-sm text-secondary-light dark:text-secondary-dark"> | ||
{o.interval} | ||
</label> | ||
</div> | ||
</div> | ||
<div className="flex flex-col"> | ||
<label> | ||
{gwei.toFixed(0)} Gwei | ||
</label> | ||
<label className="text-sm text-secondary-light dark:text-secondary-dark"> | ||
{o.pretty_total_gas_quote} | ||
</label> | ||
</div> | ||
<p className="text-3xl"> | ||
{copy[i].logo} | ||
</p> | ||
<p className="mt-2"> | ||
{copy[i].content} | ||
</p> | ||
|
||
<p> | ||
{Math.round( | ||
parseInt(gas_price) / | ||
Math.pow(10, 9) | ||
).toFixed(0)}{" "} | ||
Gwei | ||
<span className="ml-1 text-sm text-secondary-light dark:text-secondary-dark"> | ||
({pretty_total_gas_quote}) | ||
</span> | ||
</p> | ||
|
||
<p className="text-sm text-secondary-light dark:text-secondary-dark"> | ||
{interval} | ||
</p> | ||
</div> | ||
); | ||
}); | ||
}, | ||
})} | ||
</div> | ||
) | ||
) | ||
), | ||
})} | ||
</div> | ||
</> | ||
</div> | ||
); | ||
}; |
17 changes: 17 additions & 0 deletions
17
src/components/Molecules/LatestPrice/LatestPrice.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { type Meta, type StoryObj } from "@storybook/react"; | ||
import { LatestPrice as LatestPriceComponent } from "./LatestPrice"; | ||
|
||
type Story = StoryObj<typeof LatestPriceComponent>; | ||
|
||
const meta: Meta<typeof LatestPriceComponent> = { | ||
title: "Molecules/Latest Price", | ||
component: LatestPriceComponent, | ||
}; | ||
|
||
export default meta; | ||
|
||
export const LatestPrice: Story = { | ||
args: { | ||
chain_name: "eth-mainnet", | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { Card, CardDescription } from "@/components/ui/card"; | ||
import { Skeleton } from "@/components/ui/skeleton"; | ||
import { GRK_SIZES } from "@/utils/constants/shared.constants"; | ||
import { None, Some, type Option } from "@/utils/option"; | ||
import { useGoldRush } from "@/utils/store"; | ||
import { type LatestPriceProps } from "@/utils/types/molecules.types"; | ||
import { type Price } from "@covalenthq/client-sdk"; | ||
import { useEffect, useState } from "react"; | ||
|
||
export const LatestPrice: React.FC<LatestPriceProps> = ({ chain_name }) => { | ||
const { covalentClient } = useGoldRush(); | ||
const [errorMessage, setErrorMessage] = useState<string | null>(null); | ||
const [maybeResult, setResult] = useState<Option<Price>>(None); | ||
|
||
useEffect(() => { | ||
(async () => { | ||
setResult(None); | ||
setErrorMessage(null); | ||
try { | ||
const { data, ...error } = | ||
await covalentClient.PricingService.getTokenPrices( | ||
chain_name, | ||
"USD", | ||
"0x0000000000000000000000000000000000000000" | ||
); | ||
if (error.error) { | ||
setErrorMessage(error.error_message); | ||
throw error; | ||
} | ||
setResult(new Some(data[0].items[0])); | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
})(); | ||
}, [chain_name]); | ||
|
||
return ( | ||
<Card className="flex w-full flex-col items-start gap-x-4 rounded border border-secondary-light p-2 dark:border-secondary-dark dark:bg-background-dark dark:text-white"> | ||
{maybeResult.match({ | ||
None: () => <Skeleton size={GRK_SIZES.LARGE} />, | ||
Some: ({ | ||
pretty_price, | ||
contract_metadata: { contract_ticker_symbol }, | ||
}) => | ||
errorMessage ? ( | ||
<p className="mt-4">{errorMessage}</p> | ||
) : ( | ||
<div className="w-full"> | ||
<div> | ||
<CardDescription> | ||
{contract_ticker_symbol} PRICE | ||
</CardDescription> | ||
|
||
<p className="mt-1 flex items-center gap-x-1.5"> | ||
{pretty_price} | ||
</p> | ||
</div> | ||
</div> | ||
), | ||
})} | ||
</Card> | ||
); | ||
}; |
Oops, something went wrong.