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

feat: UI Improvements #46

Merged
merged 17 commits into from
Jan 23, 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
1 change: 1 addition & 0 deletions app/.env
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
NEXT_PUBLIC_PROGRAM_ADDRESS=
NEXT_PUBLIC_CLUSTER_API_URL=
NEXT_PUBLIC_LICENSE_KEY_MUI=
NEXT_PUBLIC_ENABLE_TX_SIMUL=
ANCHOR_WALLET=wallet.json
3 changes: 2 additions & 1 deletion app/src/atoms/account-order-token-pair-cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Maybe from "easy-maybe/lib";
import PairCardSymbols from "./pair-card-symbols";
import useTokenPairByPool from "../hooks/use-token-pair-by-pool";
import useTokensByMint from "../hooks/use-tokens-by-mint";
import { keepPrevious } from "../swr-options";

export interface Props
extends GridCellParams<
Expand All @@ -16,7 +17,7 @@ export interface Props
> {}

export default ({ row }: Pick<Props, "row">) => {
const tokenPair = useTokenPairByPool(row.pool);
const tokenPair = useTokenPairByPool(row.pool, keepPrevious());

const mints = Maybe.withDefault(
undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ export const TokenAmountTextField = styled(NumericFormat)`
background: transparent;
padding: 5px 12px;
font-size: 32px;
font-weight: 600px;
color: #fff;
font-weight: 400;
color: ${(p) => p.theme.palette.text.primary};
&:disabled {
color: ${(p) => p.theme.palette.text.secondary};
}
`;

export const TokenAmountMaxButton = styled(Button)`
Expand Down
64 changes: 64 additions & 0 deletions app/src/atoms/amount-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import M from "easy-maybe/lib";
import type { ChangeEvent } from "react";
import { useCallback } from "react";
import * as Styled from "./amount-field.styled";
import { formatPrice } from "../domain/index";

export default ({
amount,
disabled,
maxAmount,
onChange: handleChange = () => {},
price,
}: {
amount: number;
disabled: boolean;
maxAmount?: number;
onChange?: (arg0: number) => void;
price?: number;
}) => {
const onMaxClick = useCallback(() => {
M.andMap(handleChange, M.of(maxAmount));
}, [handleChange, maxAmount]);

const onChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
const next = Number(e.target.value);

handleChange(next);
},
[handleChange]
);

const totalAmount = M.andThen<number, number>(
(p) => (Number.isNaN(amount) ? M.of(undefined) : M.of(p * amount)),
M.of(price)
);

const displayAmount = M.withDefault(
"",
M.andMap(
(a) => (a === 0 ? formatPrice(0) : `~${formatPrice(a)}`),
totalAmount
)
);

return (
<Styled.TokenField>
<Styled.TokenAmountTextField
allowNegative={false}
disabled={disabled}
value={amount}
onChange={onChange}
/>
<Styled.SecondaryControls direction="row" spacing={1}>
<Styled.TokenAmountInUSD>{displayAmount}</Styled.TokenAmountInUSD>
{maxAmount && (
<Styled.TokenAmountMaxButton onClick={onMaxClick} size="small">
max
</Styled.TokenAmountMaxButton>
)}
</Styled.SecondaryControls>
</Styled.TokenField>
);
};
6 changes: 4 additions & 2 deletions app/src/atoms/time-interval.styled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ export const Interval = styled(Box)`
`;

export const Label = styled(Box)`
font-size: 13px;
font-weight: 600;
${(p) => `
font-size: ${p.theme.typography.overline.fontSize};
font-weight: ${p.theme.typography.overline.fontWeight};
`}
display: flex;
flex-direction: row;
gap: 8px;
Expand Down
6 changes: 4 additions & 2 deletions app/src/atoms/time-interval.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Box from "@mui/material/Box";
import ButtonGroup from "@mui/material/ButtonGroup";
import InfoIcon from "@mui/icons-material/Info";
import Popover from "@mui/material/Popover";
import { useCallback, useState } from "react";
import { useCallback, useMemo, useState } from "react";

import * as Styled from "./time-interval.styled";
import Intervals from "../molecules/interval-button-group";
Expand All @@ -29,6 +29,8 @@ export default ({
setAnchorEl(event.currentTarget);
};

const intervalValues = useMemo(() => values, [values]);

const handlePopoverClose = () => {
setAnchorEl(null);
};
Expand Down Expand Up @@ -81,7 +83,7 @@ export default ({
disabled={disabled}
onClick={onIntervalSelect}
value={value}
values={values}
values={intervalValues}
/>
</ButtonGroup>
</Styled.Interval>
Expand Down
71 changes: 20 additions & 51 deletions app/src/atoms/token-field.tsx
Original file line number Diff line number Diff line change
@@ -1,70 +1,39 @@
import type { ChangeEvent } from "react";
import M from "easy-maybe/lib";
import { useCallback, useState } from "react";

import * as Styled from "./token-field.styled";
import AmountField from "./amount-field";
import usePrice from "../hooks/use-price";
import { isFloat } from "../utils/index";

export interface Props {
export default ({
defaultValue = 0,
disabled = false,
maxAmount,
name,
onChange: handleChange,
}: {
defaultValue?: number;
disabled?: boolean;
maxAmount?: number;
name?: string;
onChange: (arg0: number) => void;
}

const possibleAmount = (amount: string) =>
isFloat(amount) ? Number(amount).toFixed(2) : amount ?? 0;

export default ({ maxAmount, name, onChange: handleChange }: Props) => {
const [amount, setAmount] = useState<number>(0);
}) => {
const [amount, setAmount] = useState<number>(defaultValue);

const price = usePrice(name ? { id: name } : undefined);

const onMaxClick = useCallback(() => {
if (!maxAmount) return;
setAmount(maxAmount);
handleChange(maxAmount);
}, [handleChange, maxAmount, setAmount]);

const onChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
const next = Number(e.target.value);

(next: number) => {
setAmount(next);
handleChange(next);
},
[handleChange, setAmount]
);

const amountUsd = M.withDefault(
undefined,
M.andMap(
(p) => (Number.isNaN(amount) ? "0" : String(p * amount)),
M.of(price.data)
)
);

return (
<Styled.TokenField>
<Styled.TokenAmountTextField
allowNegative={false}
value={amount}
onChange={onChange}
/>
<Styled.SecondaryControls direction="row" spacing={1}>
<Styled.TokenAmountInUSD>
{!amountUsd || amountUsd === "0"
? `$0`
: `~$${possibleAmount(amountUsd)}`}
</Styled.TokenAmountInUSD>
<Styled.TokenAmountMaxButton
disabled={!maxAmount}
onClick={onMaxClick}
size="small"
>
max
</Styled.TokenAmountMaxButton>
</Styled.SecondaryControls>
</Styled.TokenField>
<AmountField
amount={amount}
disabled={disabled}
maxAmount={maxAmount}
onChange={onChange}
price={price.data}
/>
);
};
12 changes: 8 additions & 4 deletions app/src/atoms/token-select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ import { useState } from "react";
import * as Styled from "./token-select.styled";
import useBreakpoints from "../hooks/use-breakpoints";

export interface Props {
export default ({
alt,
disabled = false,
image,
label,
onClick,
}: {
alt?: string;
disabled?: boolean;
image?: string;
label?: string;
onClick: (e: MouseEvent) => void;
}

export default ({ alt, disabled = false, image, label, onClick }: Props) => {
}) => {
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
const [open, setOpen] = useState<boolean>(false);
const { isMobile } = useBreakpoints();
Expand Down
13 changes: 13 additions & 0 deletions app/src/atoms/transaction-runner.styled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@ export const Container = styled(Box)`
justify-content: center;
`;

export const DesktopContainer = styled(Box)`
display: flex;
flex-direction: column;
justify-content: center;
width: 90vw;
`;

export const MobileContainer = styled(Box)`
display: flex;
flex-direction: column;
justify-content: center;
`;

export const RunnerTitle = styled(Typography)`
text-align: center;
`;
Expand Down
20 changes: 18 additions & 2 deletions app/src/atoms/transaction-runner.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable react/jsx-props-no-spreading */
import Box from "@mui/material/Box";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import ErrorIcon from "@mui/icons-material/Error";
Expand All @@ -8,6 +9,7 @@ import Typography from "@mui/material/Typography";

import * as Styled from "./transaction-runner.styled";
import LogViewer from "../organisms/log-viewer";
import useBreakpoints from "../hooks/use-breakpoints";

const extractErrorMessage = (message: string) => {
const msgAnchor = "Error Message:";
Expand All @@ -25,6 +27,20 @@ const extractErrorMessage = (message: string) => {
return message;
};

const ResponsiveContainer = ({ children, omitDesktop, ...props }: any) => {
const { isMobile } = useBreakpoints();

if (isMobile || omitDesktop) {
return (
<Styled.MobileContainer {...props}>{children}</Styled.MobileContainer>
);
}

return (
<Styled.DesktopContainer {...props}>{children}</Styled.DesktopContainer>
);
};

export const Empty = () => (
<Styled.Container p={2}>
<Box pb={2}>
Expand Down Expand Up @@ -85,7 +101,7 @@ export const Success = ({
);

export const Error = ({ error, logs }: { error: Error; logs?: string[] }) => (
<Styled.Container p={2}>
<ResponsiveContainer p={2} omitDesktop={!logs}>
<Box pb={1}>
<Styled.ErrorIcon>
<ErrorIcon />
Expand All @@ -99,5 +115,5 @@ export const Error = ({ error, logs }: { error: Error; logs?: string[] }) => (
</Box>
)}
<LogViewer logs={logs} />
</Styled.Container>
</ResponsiveContainer>
);
6 changes: 3 additions & 3 deletions app/src/domain/order.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ export const validate = (
) => {
const result: ValidationErrors = {};

if (!tokenA) result.a = new Error("Required");
if (!tokenB) result.b = new Error("Required");
if (!tokenA) result.a = new Error("Should select the token");
if (!tokenB) result.b = new Error("Should select the token");
if (!amount) result.amount = new Error("Specify the amount of token");
if (Number.isNaN(Number(amount)))
result.amount = new Error("Should be the number");
result.amount = new Error("Amount should be the number");

if (tif) {
const [timeInForce, modes] = tif;
Expand Down
3 changes: 3 additions & 0 deletions app/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export const JUPITER_CONFIG_URI = "https://quote-api.jup.ag";

export const JUPITER_PRICE_ENDPOINT_V1 = "https://price.jup.ag/v1/price";

export const NEXT_PUBLIC_ENABLE_TX_SIMUL =
process.env.NEXT_PUBLIC_ENABLE_TX_SIMUL || "1";

export const AnkrClusterApiUrl = "https://rpc.ankr.com/solana";

// TODO: cover absent env value
Expand Down
3 changes: 2 additions & 1 deletion app/src/hooks/use-balance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ export default (mint: Voidable<string>, options = {}) => {
const { publicKey: address } = useWallet();
const { provider } = useProgram();

const accountTokens = useAccountTokens();
const accountTokens = useAccountTokens(undefined, options);
// bypass hook options

return useSWR(
M.withDefault(
Expand Down
13 changes: 8 additions & 5 deletions app/src/hooks/use-cancel-order.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { isNil } from "ramda";

import useProgram from "./use-program";
import useTxRunner from "../contexts/transaction-runner-context";
import { NEXT_PUBLIC_ENABLE_TX_SIMUL } from "../env";

export default () => {
const { provider, program } = useProgram();
Expand Down Expand Up @@ -115,13 +116,15 @@ export default () => {
.preInstructions(pre)
.postInstructions(post);

setInfo("Simulating transaction...");
if (NEXT_PUBLIC_ENABLE_TX_SIMUL === "1") {
setInfo("Simulating transaction...");

const simResult = await tx.simulate().catch((e) => {
console.error("Failed to simulate", e); // eslint-disable-line no-console
});
const simResult = await tx.simulate().catch((e) => {
console.error("Failed to simulate", e); // eslint-disable-line no-console
});

if (simResult) console.debug(simResult.raw, simResult.events); // eslint-disable-line no-console, max-len
if (simResult) console.debug(simResult.raw, simResult.events); // eslint-disable-line no-console, max-len
}

setInfo("Executing the transaction...");

Expand Down
Loading