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

Maryia/positions-redesign/Contract cards improvements + fetching Open positions + formatProfitTableTransactions TS migration #58

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

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { formatMoney, toMoment, getSymbolDisplayName, getMarketInformation } from '@deriv/shared';
import { ActiveSymbols, ProfitTable } from '@deriv/api-types';

export const formatProfitTableTransactions = (
transaction: NonNullable<ProfitTable['transactions']>[number],
currency: string,
active_symbols: ActiveSymbols = []
) => {
const format_string = 'DD MMM YYYY HH:mm:ss';
const purchase_time = `${toMoment(Number(transaction.purchase_time)).format(format_string)}`;
const purchase_time_unix = transaction.purchase_time;
const sell_time = `${toMoment(Number(transaction.sell_time)).format(format_string)}`;
const payout = transaction.payout ?? NaN;
const sell_price = transaction.sell_price ?? NaN;
const buy_price = transaction.buy_price ?? NaN;
const profit_loss = formatMoney(currency, Number(sell_price - buy_price), true);
const display_name = getSymbolDisplayName(
active_symbols,
getMarketInformation(transaction.shortcode ?? '').underlying
);

return {
...transaction,
...{
payout,
sell_price,
buy_price,
profit_loss,
sell_time,
purchase_time,
display_name,
purchase_time_unix,
},
};
};
5 changes: 3 additions & 2 deletions packages/reports/src/Stores/useReportsStores.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import React from 'react';
import { useStore } from '@deriv/stores';
import ProfitStores from './Modules/Profit/profit-store';
import StatementStores from './Modules/Statement/statement-store';
import { formatProfitTableTransactions } from './Modules/Profit/Helpers/format-response';

type TOverrideProfitStore = Omit<ProfitStores, 'data' | 'date_from' | 'totals'> & {
date_from: number;
data: { [key: string]: string }[];
data: ReturnType<typeof formatProfitTableTransactions>[];
totals: { [key: string]: unknown };
};

Expand Down Expand Up @@ -39,7 +40,7 @@ type TOverrideStatementStore = Omit<
suffix_icon: string;
};

type TReportsStore = {
export type TReportsStore = {
profit_table: TOverrideProfitStore;
statement: TOverrideStatementStore;
};
Expand Down
1 change: 1 addition & 0 deletions packages/stores/src/mockStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,7 @@ const mock = (): TStores & { is_mock: boolean } => {
barriers: [],
error: '',
getPositionById: jest.fn(),
is_active_empty: false,
is_loading: false,
is_accumulator: false,
is_multiplier: false,
Expand Down
1 change: 1 addition & 0 deletions packages/stores/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,7 @@ type TPortfolioStore = {
barriers: TBarriers;
error: string;
getPositionById: (id: number) => TPortfolioPosition;
is_active_empty: boolean;
is_loading: boolean;
is_multiplier: boolean;
is_accumulator: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,33 @@ import { CaptionText } from '@deriv-com/quill-ui';
import { LabelPairedStopwatchCaptionRegularIcon } from '@deriv/quill-icons';
import { useStore } from '@deriv/stores';
import { observer } from 'mobx-react';
import { getCardLabels } from '@deriv/shared';
import { RemainingTime } from '@deriv/components';

export type TContractCardDurationProps = Pick<
TPortfolioPosition['contract_info'],
'expiry_time' | 'purchase_time' | 'tick_count'
> & {
currentTick: number | null;
isMultiplier?: boolean;
export type TContractCardDurationProps = Pick<TPortfolioPosition['contract_info'], 'expiry_time' | 'tick_count'> & {
currentTick?: number | null;
hasNoAutoExpiry?: boolean;
};

export const ContractCardDuration = observer(
({ currentTick, expiry_time, isMultiplier, purchase_time, tick_count }: TContractCardDurationProps) => {
({ currentTick, expiry_time, hasNoAutoExpiry, tick_count }: TContractCardDurationProps) => {
const { server_time } = useStore().common;

const getDisplayedDuration = () => {
if (hasNoAutoExpiry) return <Localize i18n_default_text='Ongoing' />;
if (tick_count && currentTick) {
return `${currentTick}/${tick_count} ${getCardLabels().TICKS.toLowerCase()}`;
}
return <RemainingTime end_time={expiry_time} getCardLabels={getCardLabels} start_time={server_time} />;
};

if (!expiry_time) return null;
return (
// TODO: when Tag is exported from quill-ui, use <Tag
// className='contract-card-duration'
// icon={<LabelPairedStopwatchCaptionRegularIcon />}
// label={isMultiplier ? <Localize i18n_default_text='Ongoing' /> : 'Duration'}
// />
// TODO: when <Tag /> is exported from quill-ui, use it instead
<div className='contract-card-duration'>
<LabelPairedStopwatchCaptionRegularIcon />
<CaptionText className='duration'>
{/* in progress */}
{isMultiplier ? <Localize i18n_default_text='Ongoing' /> : 'Duration'}
<CaptionText as='div' className='duration'>
{getDisplayedDuration()}
</CaptionText>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,76 +1,67 @@
import {
getContractPath,
getCurrentTick,
getTotalProfit,
getTradeTypeName,
isHighLow,
isMultiplierContract,
isValidToCancel,
isValidToSell,
} from '@deriv/shared';
import { getContractPath } from '@deriv/shared';
import { TPortfolioPosition } from '@deriv/stores/types';
import React from 'react';
import ContractCard from './contract-card';
import classNames from 'classnames';
import { TClosedPosition } from 'AppV2/Containers/Positions/positions-content';

export type TContractCardListProps = {
currency?: string;
hasButtonsDemo?: boolean;
onClickCancel?: (contractId: number) => void;
onClickSell?: (contractId: number) => void;
positions?: TPortfolioPosition[];
positions?: (TPortfolioPosition | TClosedPosition)[];
setHasButtonsDemo?: React.Dispatch<React.SetStateAction<boolean>>;
};

const ContractCardList = ({ onClickCancel, onClickSell, positions = [], ...rest }: TContractCardListProps) => {
// TODO: make it work not only with an open position data but also with a profit_table transaction data
const timeoutIds = React.useRef<Array<ReturnType<typeof setTimeout>>>([]);
const ContractCardList = ({
currency,
hasButtonsDemo,
onClickCancel,
onClickSell,
positions = [],
setHasButtonsDemo,
}: TContractCardListProps) => {
const closedCardsTimeouts = React.useRef<Array<ReturnType<typeof setTimeout>>>([]);

React.useEffect(() => {
const timers = timeoutIds.current;
const timers = closedCardsTimeouts.current;
const demoTimeout = setTimeout(() => setHasButtonsDemo?.(false), 720);
return () => {
if (timers.length) {
timers.forEach(id => clearTimeout(id));
}
if (demoTimeout) clearTimeout(demoTimeout);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const handleClose = (id: number, shouldCancel?: boolean) => {
const timeoutId = setTimeout(() => {
shouldCancel ? onClickCancel?.(id) : onClickSell?.(id);
}, 160);
timeoutIds.current.push(timeoutId);
closedCardsTimeouts.current.push(timeoutId);
};

if (!positions.length) return null;
return (
<div className='contract-card-list'>
{positions.map(({ id, is_sell_requested, contract_info }) => {
const { contract_type, display_name, profit, shortcode } = contract_info;
const contract_main_title = getTradeTypeName(contract_type ?? '', {
isHighLow: isHighLow({ shortcode }),
showMainTitle: true,
});
const currentTick = contract_info.tick_count ? getCurrentTick(contract_info) : null;
const tradeTypeName = `${contract_main_title} ${getTradeTypeName(contract_type ?? '', {
isHighLow: isHighLow({ shortcode }),
})}`.trim();
const isMultiplier = isMultiplierContract(contract_type);
const validToCancel = isValidToCancel(contract_info);
const validToSell = isValidToSell(contract_info) && !is_sell_requested;
const totalProfit = isMultiplierContract(contract_type) ? getTotalProfit(contract_info) : profit;
<div
className={classNames('contract-card-list', {
'contract-card-list--has-buttons-demo': hasButtonsDemo && !positions[0].contract_info.sell_time,
})}
>
{positions.map(position => {
const { contract_id: id } = position.contract_info;
return (
<ContractCard
key={id ?? contract_info.contract_id}
{...contract_info}
{...rest}
currentTick={currentTick}
isMultiplier={isMultiplier}
isValidToCancel={validToCancel}
isValidToSell={validToSell}
key={id}
contractInfo={position.contract_info}
currency={currency}
id={id}
isSellRequested={(position as TPortfolioPosition).is_sell_requested}
onCancel={() => id && handleClose?.(id, true)}
onClose={() => id && handleClose?.(id)}
redirectTo={getContractPath(id)}
symbolName={display_name}
totalProfit={totalProfit}
tradeTypeName={tradeTypeName}
redirectTo={id ? getContractPath(id) : undefined}
/>
);
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
justify-content: space-between;
height: 10.4rem;
transform: translateX(0);
transition: transform var(--motion-duration-snappy) var(--motion-easing-inandout);
transition: transform var(--motion-duration-snappy) var(--motion-easing-linear);

.icon,
.dc-icon {
Expand All @@ -34,16 +34,22 @@
}
.details,
.status-and-profit,
.status,
&-duration {
display: flex;
gap: 0.8rem;
align-items: center;
}
.status,
&-duration {
background-color: color-mix(in sRGB, var(--component-textIcon-normal-default) 4%, transparent);
background-color: var(--core-color-opacity-black-75);
height: 2.4rem;
padding: 0 0.8rem;
border-radius: var(--semantic-borderRadius-sm);

.dc-remaining-time {
font-size: unset;
}
}
.status-and-profit {
justify-content: space-between;
Expand Down Expand Up @@ -106,6 +112,10 @@
button:not(:disabled) {
background-color: var(--core-color-solid-cherry-700);
}
.status {
color: var(--core-color-solid-red-900);
background-color: var(--core-color-opacity-red-100);
}
}
&.won {
.total-profit {
Expand All @@ -114,11 +124,15 @@
button:not(:disabled) {
background-color: var(--core-color-solid-emerald-700);
}
.status {
color: var(--core-color-solid-green-900);
background-color: var(--core-color-opacity-green-100);
}
}
&.show-buttons {
transform: translateX(calc(var(--core-size-3600) * -1));

&--has-cancel-button {
&.has-cancel-button {
transform: translateX(calc(var(--core-size-3600) * -2));
}
}
Expand All @@ -133,16 +147,51 @@
&.deleted {
opacity: var(--core-opacity-50);
max-height: 0;
transition: max-height var(--core-motion-duration-200), opacity var(--core-motion-duration-200);
transition: max-height var(--motion-duration-snappy), opacity var(--motion-duration-snappy);
transition-timing-function: var(--motion-easing-inandout);
}
}
&-list {
display: flex;
flex-grow: 1;
flex-direction: column;
gap: 0.8rem;
width: inherit;
padding: 0.8rem;
overflow: hidden;

&--has-buttons-demo {
.contract-card-wrapper:first-child {
.contract-card {
animation: var(--motion-duration-relax) var(--motion-easing-inandout) bounce-one-button;

&.has-cancel-button {
animation: var(--motion-duration-relax) var(--motion-easing-inandout) bounce-two-buttons;
}
}
}
}
}
}

@keyframes bounce-one-button {
0%,
100% {
transform: translateX(0);
}
30%,
70% {
transform: translateX(calc(var(--core-size-3600) * -1));
}
}

@keyframes bounce-two-buttons {
0%,
100% {
transform: translateX(0);
}
30%,
70% {
transform: translateX(calc(var(--core-size-3600) * -2));
}
}
Loading
Loading