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(bonsai-ui): Migrate UnopenedIsolatedPosition cards #1437

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
4 changes: 1 addition & 3 deletions src/components/PortfolioCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { AssetIcon } from './AssetIcon';

type PortfolioCardProps = {
assetName: string;
assetId?: string;
assetIcon?: React.ReactNode;
assetImgUrl?: Nullable<string>;
actionSlot: React.ReactNode;
Expand All @@ -20,7 +19,6 @@ type PortfolioCardProps = {
};

export const PortfolioCard = ({
assetId,
assetIcon,
assetImgUrl,
assetName,
Expand All @@ -31,7 +29,7 @@ export const PortfolioCard = ({
return (
<$PortfolioCard>
<$MarketRow>
{assetIcon ?? <AssetIcon logoUrl={assetImgUrl} symbol={assetId} />}
{assetIcon ?? <AssetIcon logoUrl={assetImgUrl} />}
{assetName}
</$MarketRow>
<div tw="spacedRow mt-0.5 px-0.625 py-0">
Expand Down
24 changes: 13 additions & 11 deletions src/components/PotentialPositionCard.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
import { useCallback } from 'react';

import { SubaccountPendingPosition } from '@/constants/abacus';
import { BonsaiCore } from '@/abacus-ts/ontology';
import { PendingIsolatedPosition } from '@/abacus-ts/types/summaryTypes';

import { DialogTypes } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';

import { useStringGetter } from '@/hooks/useStringGetter';

import { useAppDispatch, useAppSelector } from '@/state/appTypes';
import { getAssetImageUrl } from '@/state/assetsSelectors';
import { openDialog } from '@/state/dialogs';

import { orEmptyObj } from '@/lib/typeUtils';

import { Icon, IconName } from './Icon';
import { Link } from './Link';
import { Output, OutputType } from './Output';
import { PortfolioCard } from './PortfolioCard';

type PotentialPositionCardProps = {
marketName: string;
onViewOrders: (marketId: string) => void;
pendingPosition: SubaccountPendingPosition;
pendingPosition: PendingIsolatedPosition;
};

export const PotentialPositionCard = ({
marketName,
onViewOrders,
pendingPosition,
}: PotentialPositionCardProps) => {
Expand All @@ -35,16 +36,17 @@ export const PotentialPositionCard = ({
);

const stringGetter = useStringGetter();
const { assetId, freeCollateral, marketId, orderCount } = pendingPosition;
const assetImgUrl = useAppSelector((s) => getAssetImageUrl(s, assetId));
const { assetId, displayableAsset, equity, marketId, orders } = pendingPosition;
const orderCount = orders.length;
const assets = orEmptyObj(useAppSelector(BonsaiCore.markets.assets.data));
const { name, logo } = orEmptyObj(assets[assetId]);

return (
<PortfolioCard
assetName={marketName}
assetId={assetId}
assetImgUrl={assetImgUrl}
assetName={name ?? displayableAsset}
assetImgUrl={logo}
detailLabel={stringGetter({ key: STRING_KEYS.MARGIN })}
detailValue={<Output type={OutputType.Fiat} value={freeCollateral?.current} />}
detailValue={<Output type={OutputType.Fiat} value={equity} />}
actionSlot={
<>
<Link onClick={() => onViewOrders(marketId)} isAccent tw="font-small-book">
Expand Down
65 changes: 35 additions & 30 deletions src/pages/trade/UnopenedIsolatedPositions.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ReactNode, useEffect, useState } from 'react';

import { shallowEqual } from 'react-redux';
import { BonsaiCore, BonsaiHelpers } from '@/abacus-ts/ontology';
import type { PendingIsolatedPosition } from '@/abacus-ts/types/summaryTypes';
import styled, { css } from 'styled-components';

import { SubaccountPendingPosition } from '@/constants/abacus';
import { STRING_KEYS } from '@/constants/localization';
import { EMPTY_ARR } from '@/constants/objects';

import { useStringGetter } from '@/hooks/useStringGetter';

Expand All @@ -15,9 +16,7 @@ import { DropdownIcon } from '@/components/DropdownIcon';
import { IconName } from '@/components/Icon';
import { PotentialPositionCard } from '@/components/PotentialPositionCard';

import { getExistingOpenPositions, getNonZeroPendingPositions } from '@/state/accountSelectors';
import { useAppSelector } from '@/state/appTypes';
import { getAssets } from '@/state/assetsSelectors';

type UnopenedIsolatedPositionsProps = {
className?: string;
Expand All @@ -28,19 +27,26 @@ export const MaybeUnopenedIsolatedPositionsDrawer = ({
className,
onViewOrders,
}: UnopenedIsolatedPositionsProps) => {
const numNormalPositions = useAppSelector(getExistingOpenPositions, shallowEqual)?.length;
const parentSubaccountPositionsLoading =
useAppSelector(BonsaiCore.account.parentSubaccountPositions.loading) === 'pending';

const numNormalPositions = (
useAppSelector(BonsaiCore.account.parentSubaccountPositions.data) ?? EMPTY_ARR
).length;

const pendingIsolatedPositions =
useAppSelector(BonsaiHelpers.unopenedIsolatedPositions) ?? EMPTY_ARR;

const [isOpen, setIsOpen] = useState(numNormalPositions === 0);
useEffect(() => {
if (numNormalPositions === 0) {
if (!parentSubaccountPositionsLoading && numNormalPositions === 0) {
setIsOpen(true);
}
}, [numNormalPositions]);

const pendingPositions = useAppSelector(getNonZeroPendingPositions, shallowEqual);
}, [parentSubaccountPositionsLoading, numNormalPositions]);

const stringGetter = useStringGetter();

if (!pendingPositions?.length) return null;
if (!pendingIsolatedPositions.length) return null;

return (
<$UnopenedIsolatedPositionsDrawerContainer className={className} isOpen={isOpen}>
Expand All @@ -53,7 +59,7 @@ export const MaybeUnopenedIsolatedPositionsDrawer = ({
<div tw="px-1 pb-1 pt-0">
<UnopenedIsolatedPositionsCards
onViewOrders={onViewOrders}
pendingPositions={pendingPositions}
pendingPositions={pendingIsolatedPositions}
/>
</div>
)}
Expand All @@ -66,48 +72,47 @@ type UnopenedIsolatedPositionsPanelProps = {
className?: string;
header: ReactNode;
};

export const MaybeUnopenedIsolatedPositionsPanel = ({
onViewOrders,
header,
className,
}: UnopenedIsolatedPositionsPanelProps) => {
const pendingPositions = useAppSelector(getNonZeroPendingPositions, shallowEqual);
if (!pendingPositions?.length) return null;
const pendingIsolatedPositions =
useAppSelector(BonsaiHelpers.unopenedIsolatedPositions) ?? EMPTY_ARR;

if (!pendingIsolatedPositions.length) return null;

return (
<div className={className}>
{header}
<UnopenedIsolatedPositionsCards
onViewOrders={onViewOrders}
pendingPositions={pendingPositions}
pendingPositions={pendingIsolatedPositions}
/>
</div>
);
};

type UnopenedIsolatedPositionsCardsProps = {
onViewOrders: (marketId: string) => void;
pendingPositions: SubaccountPendingPosition[];
pendingPositions: PendingIsolatedPosition[];
};

const UnopenedIsolatedPositionsCards = ({
onViewOrders,
pendingPositions,
}: UnopenedIsolatedPositionsCardsProps) => {
const assetsData = useAppSelector(getAssets, shallowEqual);
return (
<$Cards>
{pendingPositions.map((pendingPosition) => (
<PotentialPositionCard
key={pendingPosition.assetId}
marketName={assetsData?.[pendingPosition.assetId]?.name ?? pendingPosition.assetId ?? ''}
pendingPosition={pendingPosition}
onViewOrders={onViewOrders}
/>
))}
</$Cards>
);
};
}: UnopenedIsolatedPositionsCardsProps) => (
<$Cards>
{pendingPositions.map((pendingPosition) => (
<PotentialPositionCard
key={pendingPosition.marketId}
pendingPosition={pendingPosition}
onViewOrders={onViewOrders}
/>
))}
</$Cards>
);

const $UnopenedIsolatedPositionsDrawerContainer = styled.div<{ isOpen?: boolean }>`
overflow: auto;
Expand Down
Loading