Skip to content

Commit

Permalink
feat: action box mobile
Browse files Browse the repository at this point in the history
  • Loading branch information
k0beLeenders committed Dec 9, 2023
1 parent c3109e7 commit c4f3f2c
Show file tree
Hide file tree
Showing 16 changed files with 465 additions and 609 deletions.
101 changes: 81 additions & 20 deletions apps/marginfi-v2-ui/src/components/common/ActionBox/ActionBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ActionType } from "@mrgnlabs/marginfi-v2-ui-state";
import { WSOL_MINT, numeralFormatter } from "@mrgnlabs/mrgn-common";
import { ChevronDownIcon } from "@radix-ui/react-icons";
import { useMrgnlendStore, useUiStore } from "~/store";
import { MarginfiActionParams, closeBalance, executeLendingAction } from "~/utils";
import { MarginfiActionParams, closeBalance, cn, executeLendingAction } from "~/utils";
import { LendingModes } from "~/types";
import { useWalletContext } from "~/hooks/useWalletContext";

Expand All @@ -20,15 +20,15 @@ import { IconWallet } from "~/components/ui/icons";
import { ActionBoxActions } from "./ActionBoxActions";

export const ActionBox = () => {
const [mfiClient, nativeSolBalance, setIsRefreshingStore, fetchMrgnlendState, selectedAccount] = useMrgnlendStore(
(state) => [
const [mfiClient, nativeSolBalance, setIsRefreshingStore, fetchMrgnlendState, selectedAccount, accountSummary] =
useMrgnlendStore((state) => [
state.marginfiClient,
state.nativeSolBalance,
state.setIsRefreshingStore,
state.fetchMrgnlendState,
state.selectedAccount,
]
);
state.accountSummary,
]);
const [lendingMode, setLendingMode, actionMode, setActionMode, selectedToken, setSelectedToken] = useUiStore(
(state) => [
state.lendingMode,
Expand Down Expand Up @@ -92,31 +92,84 @@ export const ActionBox = () => {
}
}, [lendingMode, selectedToken, setAmount, setActionMode]);

const liquidationPrice = React.useMemo(() => {
const isActive = selectedToken?.isActive;
const isLending = lendingMode === LendingModes.LEND;
let liquidationPrice = 0;

if (isActive) {
if (!amount || amount === 0 || isLending || !selectedAccount || !selectedToken) {
liquidationPrice = selectedToken?.position.liquidationPrice ?? 0;
} else {
const borrowed = selectedToken?.position.amount ?? 0;

liquidationPrice =
selectedAccount.computeLiquidationPriceForBankAmount(selectedToken?.address, isLending, amount + borrowed) ??
0;
}
}

return liquidationPrice;
}, [selectedToken, amount, lendingMode]);

const healthColorLiquidation = React.useMemo(() => {
const isActive = selectedToken?.isActive;

if (isActive) {
const price = selectedToken.info.oraclePrice.price.toNumber();
const safety = liquidationPrice / price;
let color: string;
if (safety >= 0.5) {
color = "#75BA80"; // green color " : "#",
} else if (safety >= 0.25) {
color = "#B8B45F"; // yellow color
} else {
color = "#CF6F6F"; // red color
}

return color;
} else {
return "#fff";
}
}, [selectedToken, liquidationPrice]);

React.useEffect(() => {
if (!selectedToken || !amount) {
if (!selectedToken) {
setPreview([]);
return;
}

const isActive = selectedToken?.isActive;
let supplied = 0;
let borrowed = 0;

if (isActive) {
const isLending = selectedToken?.position?.isLending;
if (isLending) supplied = selectedToken?.position.amount ?? 0;
else borrowed = selectedToken?.position.amount ?? 0;
}
const healthFactor = accountSummary.healthFactor;

setPreview([
{
key: "Your deposited amount",
value: `${amount} ${selectedToken.meta.tokenSymbol}`,
key: "Supplied amount",
value: `${numeralFormatter(supplied)}`,
},
{
key: "Liquidation price",
value: usdFormatter.format(amount),
key: "Borrowed amount",
value: `${numeralFormatter(borrowed)}`,
},
{
key: "Some propertya",
value: "--",
key: "Liquidation price",
value:
liquidationPrice > 0.01 ? usdFormatter.format(liquidationPrice) : `$${liquidationPrice.toExponential(2)}`,
},
{
key: "Some propertyb",
value: "--",
key: "Health factor",
value: `${numeralFormatter(healthFactor * 100)}%`,
},
]);
}, [selectedToken, amount]);
}, [selectedToken, amount, liquidationPrice]);

React.useEffect(() => {
if (!selectedToken || !amountInputRef.current) return;
Expand Down Expand Up @@ -204,7 +257,7 @@ export const ActionBox = () => {
!hasLSTDialogShown.includes(selectedToken.meta.tokenSymbol as LSTDialogVariants)
) {
setHasLSTDialogShown((prev) => [...prev, selectedToken.meta.tokenSymbol as LSTDialogVariants]);
setLSTDialogVariant(selectedToken.meta.tokenSymbol);
setLSTDialogVariant(selectedToken.meta.tokenSymbol as LSTDialogVariants);
setIsLSTDialogOpen(true);
setLSTDialogCallback(() => action);

Expand All @@ -219,7 +272,7 @@ export const ActionBox = () => {
!hasLSTDialogShown.includes(selectedToken.meta.tokenSymbol as LSTDialogVariants)
) {
setHasLSTDialogShown((prev) => [...prev, selectedToken.meta.tokenSymbol as LSTDialogVariants]);
setLSTDialogVariant(selectedToken.meta.tokenSymbol);
setLSTDialogVariant(selectedToken.meta.tokenSymbol as LSTDialogVariants);
return;
}
}, [
Expand Down Expand Up @@ -321,12 +374,20 @@ export const ActionBox = () => {
handleAction={() => (showCloseBalance ? handleCloseBalance() : handleLendingAction())}
isLoading={isLoading}
/>
{selectedToken !== null && amount !== null && preview.length > 0 && (
{selectedToken !== null && preview.length > 0 && (
<dl className="grid grid-cols-2 text-muted-foreground gap-y-2 mt-4 text-sm">
{preview.map((item) => (
{preview.map((item, idx) => (
<React.Fragment key={item.key}>
<dt>{item.key}</dt>
<dd className="text-white font-medium text-right">{item.value}</dd>
<dd
className={cn(
`text-[${
item.key === "Liquidation price" ? healthColorLiquidation : "white"
}] font-medium text-right`
)}
>
{item.value}
</dd>
</React.Fragment>
))}
</dl>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useUiStore } from "~/store";

import { Button } from "~/components/ui/button";
import { IconLoader } from "~/components/ui/icons";
import { useWalletContext } from "~/hooks/useWalletContext";

type ActionBoxActionsProps = {
amount: number;
Expand All @@ -23,21 +24,24 @@ export const ActionBoxActions = ({
handleAction,
}: ActionBoxActionsProps) => {
const [actionMode, selectedToken] = useUiStore((state) => [state.actionMode, state.selectedToken]);
const { connected } = useWalletContext();

const isActionDisabled = React.useMemo(() => {
const isValidInput = amount > 0;
return ((maxAmount === 0 || !isValidInput) && !showCloseBalance) || isLoading;
return ((maxAmount === 0 || !isValidInput) && !showCloseBalance) || isLoading || !connected;
}, [amount, showCloseBalance, maxAmount, isLoading]);

const actionText = React.useMemo(() => {
if (!connected) {
return "Connect your wallet";
}
if (!selectedToken) {
return "Select token and amount";
}

if (showCloseBalance) {
return "Close account";
}
console.log({ actionMode });

if (maxAmount === 0) {
switch (actionMode) {
Expand All @@ -59,7 +63,7 @@ export const ActionBoxActions = ({
}

return actionMode;
}, [actionMode, amount, selectedToken, maxAmount, showCloseBalance]);
}, [actionMode, amount, selectedToken, connected, maxAmount, showCloseBalance]);

return (
<Button disabled={isActionDisabled} className="w-full py-6" onClick={handleAction}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const ActionBoxDialog = ({ children }: ActionBoxDialogProps) => {
return (
<Dialog open={isDialogOpen} onOpenChange={(open) => setIsDialogOpen(open)}>
<DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent className="md:flex md:max-w-[520px] md:p-4">
<DialogContent className="md:flex md:max-w-[520px] md:p-4 p-0">
<ActionBox />
</DialogContent>
</Dialog>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useMrgnlendStore, useUiStore } from "~/store";

import { cn } from "~/utils";

import { useWalletContext } from "~/hooks/useWalletContext";
import { Popover, PopoverContent, PopoverTrigger } from "~/components/ui/popover";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from "~/components/ui/command";
import { Button } from "~/components/ui/button";
Expand All @@ -30,6 +31,7 @@ export const ActionBoxTokens = ({ currentToken, setCurrentToken }: ActionBoxToke
const [lendingMode] = useUiStore((state) => [state.lendingMode]);
const [searchQuery, setSearchQuery] = React.useState("");
const [isTokenPopoverOpen, setIsTokenPopoverOpen] = React.useState(false);
const { connected } = useWalletContext();

const calculateRate = React.useCallback(
(bank: ExtendedBankInfo) =>
Expand Down Expand Up @@ -160,7 +162,7 @@ export const ActionBoxTokens = ({ currentToken, setCurrentToken }: ActionBoxToke
<IconX size={18} className="text-white/50" />
</button>
<CommandEmpty>No tokens found.</CommandEmpty>
{lendingMode === LendingModes.LEND && filteredBanksUserOwns.length > 0 && (
{lendingMode === LendingModes.LEND && connected && filteredBanksUserOwns.length > 0 && (
<CommandGroup heading="Available in your wallet">
{filteredBanksUserOwns.slice(0, searchQuery.length === 0 ? 5 : 3).map((bank, index) => (
<CommandItem
Expand Down Expand Up @@ -247,37 +249,38 @@ export const ActionBoxTokens = ({ currentToken, setCurrentToken }: ActionBoxToke
))}
</CommandGroup>
)}
{(searchQuery.length > 0 || lendingMode === LendingModes.BORROW) && filteredBanks.length > 0 && (
<CommandGroup heading="All tokens">
{filteredBanks.slice(0, searchQuery.length === 0 ? 5 : 3).map((bank, index) => (
<CommandItem
key={index}
value={bank.address?.toString().toLowerCase()}
onSelect={(currentValue) => {
setCurrentToken(
extendedBankInfos.find(
(bankInfo) => bankInfo.address.toString().toLowerCase() === currentValue
) ?? null
);
setIsTokenPopoverOpen(false);
}}
className={cn(
"cursor-pointer font-medium flex items-center justify-between gap-2 data-[selected=true]:bg-background-gray-light data-[selected=true]:text-white",
lendingMode === LendingModes.LEND && "py-2",
lendingMode === LendingModes.BORROW && "h-[60px]"
)}
>
<ActionBoxItem
rate={calculateRate(bank)}
lendingMode={lendingMode}
bank={bank}
showBalanceOverride={false}
nativeSolBalance={nativeSolBalance}
/>
</CommandItem>
))}
</CommandGroup>
)}
{(searchQuery.length > 0 || lendingMode === LendingModes.BORROW || !connected) &&
filteredBanks.length > 0 && (
<CommandGroup heading="All tokens">
{filteredBanks.slice(0, searchQuery.length === 0 ? 5 : 3).map((bank, index) => (
<CommandItem
key={index}
value={bank.address?.toString().toLowerCase()}
onSelect={(currentValue) => {
setCurrentToken(
extendedBankInfos.find(
(bankInfo) => bankInfo.address.toString().toLowerCase() === currentValue
) ?? null
);
setIsTokenPopoverOpen(false);
}}
className={cn(
"cursor-pointer font-medium flex items-center justify-between gap-2 data-[selected=true]:bg-background-gray-light data-[selected=true]:text-white",
lendingMode === LendingModes.LEND && "py-2",
lendingMode === LendingModes.BORROW && "h-[60px]"
)}
>
<ActionBoxItem
rate={calculateRate(bank)}
lendingMode={lendingMode}
bank={bank}
showBalanceOverride={false}
nativeSolBalance={nativeSolBalance}
/>
</CommandItem>
))}
</CommandGroup>
)}
</Command>
</PopoverContent>
</Popover>
Expand Down
Loading

0 comments on commit c4f3f2c

Please sign in to comment.