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

home summary #20

Merged
merged 9 commits into from
Aug 11, 2023
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"react-dropzone": "^14.2.3",
"react-loading-skeleton": "^3.3.1",
"sass": "^1.63.6",
"useink": "^1.8.0"
"useink": "^1.13.0"
},
"devDependencies": {
"@storybook/addon-essentials": "^7.2.1",
Expand Down
17 changes: 17 additions & 0 deletions src/components/SummaryCard/XsignerBalanceText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Box, BoxProps, styled, Typography } from "@mui/material";

const BoxWrapper = styled(Box)<BoxProps>(() => ({}));

interface Props {
freeBalance: string | undefined;
reservedBalance: string | undefined;
}

export function XsignerBalanceText({ freeBalance, reservedBalance }: Props) {
return (
<BoxWrapper>
<Typography>{freeBalance}</Typography>
<Typography>{reservedBalance}</Typography>
</BoxWrapper>
);
}
48 changes: 48 additions & 0 deletions src/components/SummaryCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Box, CardHeader, Typography } from "@mui/material";

import { LoadingSkeleton } from "../common/LoadingSkeleton";
import { SummaryCardStyled, TextSummary } from "./styled";

export interface SummaryCardProps {
captionTitle: string;
caption?: string;
captionComponent?: React.ReactNode;
isLoading?: boolean;
widthSkeleton?: string;
}

export const SummaryCard = ({
captionTitle,
caption,
captionComponent,
isLoading,
widthSkeleton,
}: SummaryCardProps) => {
const _captionComponent = isLoading ? (
<LoadingSkeleton count={2} width={widthSkeleton} />
) : (
captionComponent
);

return (
<SummaryCardStyled border={false}>
{_captionComponent && !caption ? (
_captionComponent
) : (
<Box justifyContent="center">
<TextSummary>{caption}</TextSummary>
</Box>
)}
{captionTitle && (
<CardHeader
sx={{ paddingBottom: 0 }}
title={
<Typography variant="h4" color="white">
{captionTitle}
</Typography>
}
/>
)}
</SummaryCardStyled>
);
};
16 changes: 16 additions & 0 deletions src/components/SummaryCard/styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { styled, Typography, TypographyProps } from "@mui/material";

import { WidgetCard, WidgetCardProps } from "../common/muiExtended/WidgetCard";

export const SummaryCardStyled = styled(WidgetCard)<WidgetCardProps>(() => ({
minHeight: "9rem",
display: "flex",
alignItems: "center",
justifyContent: "center",
flexDirection: "column",
minWidth: "180px",
}));

export const TextSummary = styled(Typography)<TypographyProps>(() => ({
fontSize: "2em",
}));
20 changes: 15 additions & 5 deletions src/components/common/LoadingSkeleton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import { useTheme } from "@mui/material";
import { Box, BoxProps, styled, useTheme } from "@mui/material";
import Skeleton, { SkeletonTheme } from "react-loading-skeleton";

export interface LoadingSkeletonProps {
count?: number;
width?: string;
}

export const LoadingSkeleton = ({ count = 1 }: LoadingSkeletonProps) => {
const BoxWrapper = styled(Box)<BoxProps>(() => {
return {
justifyContent: "center",
};
});

export const LoadingSkeleton = ({
count = 1,
width = "80%",
}: LoadingSkeletonProps) => {
const theme = useTheme();

return (
<SkeletonTheme
baseColor={theme.palette.background.default}
baseColor={theme.palette.background.paper}
highlightColor={theme.palette.primary.main}
>
<p>
<BoxWrapper width={width}>
<Skeleton count={count} />
</p>
</BoxWrapper>
</SkeletonTheme>
);
};
26 changes: 26 additions & 0 deletions src/components/common/muiExtended/WidgetCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Card, CardProps, styled } from "@mui/material";
import { forwardRef, PropsWithChildren } from "react";

export interface WidgetCardProps
extends PropsWithChildren<Pick<CardProps, "sx" | "elevation">> {
border: boolean;
}

const CardStyled = styled(Card)<CardProps & { hasborder: string }>(
({ hasborder }) => ({
minHeight: "5rem",
border: hasborder === "true" ? "1px solid" : "none",
borderRadius: 8,
})
);

export const WidgetCard: React.FC<WidgetCardProps> = forwardRef<
HTMLDivElement,
WidgetCardProps
>(function RefMainCard({ children, border, ...props }, ref) {
return (
<CardStyled hasborder={border.toString()} ref={ref} {...props}>
{children}
</CardStyled>
);
});
50 changes: 50 additions & 0 deletions src/hooks/useGetBalance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useEffect, useMemo, useState } from "react";
import { useBalance } from "useink";

import { planckToDecimalFormatted } from "@/services/useink/substrate/parseUnit";

import { useNetworkApi } from "./useNetworkApi";

interface XsignerBalance {
freeBalance?: string;
reservedBalance?: string;
}

export function useGetBalance(address: string | undefined) {
const [isLoading, setIsLoading] = useState(true);
const [balance, setBalance] = useState<XsignerBalance>();
const _balance = useBalance({ address });
const balanceWithoutFormat = useMemo(
() => ({
freeBalance: _balance?.freeBalance,
reservedBalance: _balance?.reservedBalance,
}),
[_balance?.freeBalance, _balance?.reservedBalance]
);
const api = useNetworkApi();

useEffect(() => {
if (!api?.api) return;

setIsLoading(true);
const freeBalance = planckToDecimalFormatted(
balanceWithoutFormat?.freeBalance,
{
significantFigures: 4,
api: api?.api,
}
);
const reservedBalance = planckToDecimalFormatted(
balanceWithoutFormat?.reservedBalance,
{
significantFigures: 4,
api: api?.api,
}
);

setBalance({ freeBalance, reservedBalance });
setIsLoading(false);
}, [api?.api, balanceWithoutFormat]);

return { balance, isLoading };
}
10 changes: 10 additions & 0 deletions src/hooks/useNetworkApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useApi } from "useink";

import { usePolkadotContext } from "@/context/usePolkadotContext";

export function useNetworkApi() {
const { network } = usePolkadotContext();
const api = useApi(network);

return api;
}
45 changes: 38 additions & 7 deletions src/pages/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,45 @@
import { Card, Grid } from "@mui/material";
import { Grid, Typography } from "@mui/material";

import { SummaryCard } from "@/components/SummaryCard";
import { XsignerBalanceText } from "@/components/SummaryCard/XsignerBalanceText";
import { useGetBalance } from "@/hooks/useGetBalance";
import { useGetXsignerSelected } from "@/hooks/xsignerSelected/useGetXsignerSelected";

export default function AppDashboard() {
const { xSignerSelected } = useGetXsignerSelected();

const { balance, isLoading: isLoadingBalance } = useGetBalance(
xSignerSelected?.address
);

return (
<Grid container>
<Grid item xs={12} component="section">
<Card></Card>
<Card></Card>
<Card></Card>
<Grid container spacing={3}>
<Grid item xs={12}>
<Typography variant="h5">Summary</Typography>
</Grid>

<Grid item xs={12} sm={6} md={3}>
<SummaryCard
captionTitle="Balance"
widthSkeleton="60%"
captionComponent={
<XsignerBalanceText
freeBalance={balance?.freeBalance}
reservedBalance={balance?.reservedBalance}
/>
}
isLoading={isLoadingBalance}
/>
</Grid>
<Grid item xs={12} sm={6} md={3}>
<SummaryCard captionTitle="Tracked Tokens" caption="0" />
</Grid>
<Grid item xs={12} sm={6} md={3}>
<SummaryCard captionTitle="Tracked NFTs" caption="3" />
</Grid>
<Grid item xs={12} sm={6} md={3}>
<SummaryCard captionTitle="Transactions queued" caption="0" />
</Grid>
<Grid item>Widgets</Grid>
</Grid>
);
}
78 changes: 78 additions & 0 deletions src/services/useink/substrate/parseUnit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import BN from "bn.js";

import {
chainTokenSymbol,
IRegistryInfo,
} from "@/services/useink/substrate/tokenTypes";

// convert string with commas to a BN e.g. 1,000,000 to new BN('1_000_000')
export const stringNumberToBN = (valWithCommas: string): BN => {
const v = valWithCommas.split(",").join("");
return new BN(v);
};

interface DecimalOptions {
api?: IRegistryInfo;
decimals?: number;
}

// convert decimal to planck unit e.g. 1.0000 to 1000000000000
export const planckToDecimal = (
amount: undefined | string | number | number[] | BN | Uint8Array | Buffer,
options: DecimalOptions
): number | undefined => {
const decimals =
options.decimals !== undefined
? options.decimals
: options.api?.registry.chainDecimals[0];

if (!decimals || !amount) return;
if (decimals === undefined || amount === undefined) return;

const base = new BN(10).pow(new BN(decimals));
const { div, mod } = new BN(amount).divmod(base);

return parseFloat(
`${div.toString()}.${mod.toString().padStart(decimals, "0")}`
);
};

interface PlanckToDecimalOptions {
significantFigures?: number;
symbol?: string;
}

// convert planck unit to decimal with token name (ROC,DOT,KSM) e.g. 100000000000 to 1.0000 ROC
export const planckToDecimalFormatted = (
amount: undefined | string | number | number[] | BN | Uint8Array | Buffer,
options: PlanckToDecimalOptions & DecimalOptions
): string | undefined => {
const decimalAmount = planckToDecimal(amount, options);
if (decimalAmount === undefined) return;

const formattedVal =
options?.significantFigures === undefined
? decimalAmount.toString()
: decimalAmount.toFixed(options?.significantFigures).toString();

const symbol = options?.symbol
? options.symbol
: options.api
? chainTokenSymbol(options.api)
: "";

return `${formattedVal} ${symbol}`;
};

// convert decimal to planck unit e.g. 1.0000 to 1000000000000
export const decimalToPlanck = (
amount: number,
options: DecimalOptions | undefined
): bigint | undefined => {
const decimals = options?.decimals || options?.api?.registry.chainDecimals[0];
if (!decimals) return;

const convertedValue = BigInt(amount * 10 ** decimals);

return convertedValue;
};
12 changes: 12 additions & 0 deletions src/services/useink/substrate/tokenTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export interface IRegistryInfo {
registry: {
chainDecimals: (number | undefined)[];
chainTokens: (string | undefined)[];
};
}

export const chainTokenSymbol = (api: IRegistryInfo): string | undefined =>
api.registry.chainTokens[0];

export const chainDecimals = (api: IRegistryInfo): number | undefined =>
api.registry.chainDecimals[0];
2 changes: 1 addition & 1 deletion src/stories/LoadingButton.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {

// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
const meta = {
title: "Example/LoadingButton",
title: "Components/LoadingButton",
component: LoadingButton,
parameters: {
layout: "centered",
Expand Down
Loading