Skip to content

Commit

Permalink
DTRA-1424 / Kate / Enable purchase functionality (binary-com#145)
Browse files Browse the repository at this point in the history
* refactor: customize amount of buttons and it name

* feat: add basis text and amount for button based on contract type

* refactor: purchase button

* feat: eanble purchase functionality

* feat: add sell functionality for acc button

* chore: add caption text from quill

* fix: add store init to bottomnav
  • Loading branch information
kate-deriv committed Jul 15, 2024
1 parent 7930387 commit 00fff44
Show file tree
Hide file tree
Showing 5 changed files with 285 additions and 40 deletions.
2 changes: 2 additions & 0 deletions packages/shared/src/utils/constants/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import { LocalStore } from '../storage';
export const getLocalizedBasis = () =>
({
accumulator: localize('Accumulators'),
current_stake: localize('Current stake'),
multiplier: localize('Multiplier'),
max_payout: localize('Max payout'),
payout_per_pip: localize('Payout per pip'),
payout_per_point: localize('Payout per point'),
payout: localize('Payout'),
Expand Down
8 changes: 6 additions & 2 deletions packages/trader/src/AppV2/Components/BottomNav/bottom-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type BottomNavProps = {
const BottomNav = observer(({ children, className }: BottomNavProps) => {
const history = useHistory();
const location = useLocation();
const { active_positions_count } = useStore().portfolio;
const { active_positions_count, onMount, onUnmount } = useStore().portfolio;

const bottomNavItems = [
{
Expand Down Expand Up @@ -85,7 +85,11 @@ const BottomNav = observer(({ children, className }: BottomNavProps) => {
setSelectedIndex(index);
history.push(bottomNavItems[index].path);
};

React.useEffect(() => {
onMount();
return onUnmount;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div className={classNames('bottom-nav', className)}>
<div className='bottom-nav-selection'>{children}</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React from 'react';
import clsx from 'clsx';
import { CaptionText } from '@deriv-com/quill-ui';
import { useTraderStore } from 'Stores/useTraderStores';
import { getLocalizedBasis } from '@deriv/shared';
import { Money } from '@deriv/components';

type TPurchaseButtonContent = {
current_stake?: number | null;
info: ReturnType<typeof useTraderStore>['proposal_info'][0];
is_reverse?: boolean;
} & Pick<
ReturnType<typeof useTraderStore>,
| 'currency'
| 'has_open_accu_contract'
| 'is_accumulator'
| 'is_multiplier'
| 'is_vanilla_fx'
| 'is_vanilla'
| 'is_turbos'
>;

const PurchaseButtonContent = ({
currency,
current_stake,
has_open_accu_contract,
info,
is_accumulator,
is_multiplier,
is_turbos,
is_vanilla,
is_vanilla_fx,
is_reverse,
}: TPurchaseButtonContent) => {
const localized_basis = getLocalizedBasis();

const getAmount = () => {
if (is_multiplier) return info.stake;
if (is_accumulator) return has_open_accu_contract ? Number(current_stake) : info.maximum_payout;
return info?.obj_contract_basis?.value;
};
const getTextBasis = () => {
if (is_turbos || (is_vanilla && !is_vanilla_fx)) return localized_basis.payout_per_point;
if (is_vanilla_fx) return localized_basis.payout_per_pip;
if (is_multiplier) return localized_basis.stake;
if (is_accumulator) return has_open_accu_contract ? localized_basis.current_stake : localized_basis.max_payout;
return localized_basis.payout;
};

return (
<CaptionText
size='sm'
className={clsx(
'purchase-button__information__wrapper',
is_reverse && 'purchase-button__information__wrapper--reverse'
)}
>
<CaptionText
as='span'
size='sm'
className={clsx(!has_open_accu_contract && 'purchase-button__information__item')}
>
{getTextBasis()}
</CaptionText>
<CaptionText
as='span'
size='sm'
className={clsx(!has_open_accu_contract && 'purchase-button__information__item')}
>
<Money
amount={getAmount()}
currency={currency}
should_format={!is_turbos && !is_vanilla}
show_currency
/>
</CaptionText>
</CaptionText>
);
};

export default PurchaseButtonContent;
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,41 @@
padding-block: var(--core-spacing-400);
align-items: flex-end;
height: var(--core-size-2800);
gap: 0;

& + & {
&:nth-child(2):not(.purchase-button--loading) {
align-items: flex-start;
}

&__payout {
width: 100%;
display: inline-flex;
justify-content: space-between;
&--single {
align-items: center;
justify-content: center;

.purchase-button__information__wrapper {
justify-content: center;
gap: var(--core-spacing-400);
}
}

&--loading {
align-items: center;
font-size: var(--core-fontSize-50);
line-height: var(--core-lineHeight-100);
}

&__information {
&__wrapper {
width: 100%;
display: inline-flex;
justify-content: space-between;
align-items: center;

&--reverse {
flex-direction: row-reverse;
}
}

&__item {
color: var(--core-color-solid-slate-50);
}
}

&__wrapper {
Expand Down
197 changes: 166 additions & 31 deletions packages/trader/src/AppV2/Components/PurchaseButton/purchase-button.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,170 @@
import React from 'react';
import clsx from 'clsx';
import { observer } from 'mobx-react';
import { useStore } from '@deriv/stores';
import { useTraderStore } from 'Stores/useTraderStores';
import { Button } from '@deriv-com/quill-ui';
import { Localize, localize } from '@deriv/translations';

// TODO: add Text component from Quill UI when it will support sm size with font-size 12px
const PurchaseButton = () => (
<div className='purchase-button__wrapper'>
<Button
color='purchase'
size='lg'
label={<Localize i18n_default_text='Rise' />}
fullWidth
className='purchase-button'
>
<p className='purchase-button__payout'>
<span>{localize('Payout')}</span>
<span>19.55 USD</span>
</p>
</Button>
<Button
color='sell'
size='lg'
label={<Localize i18n_default_text='Fall' />}
fullWidth
className='purchase-button'
>
<p className='purchase-button__payout'>
<span>19.55 USD</span>
<span>{localize('Payout')}</span>
</p>
</Button>
</div>
);
import { useDevice } from '@deriv-com/ui';
import {
getContractTypeDisplay,
getIndicativePrice,
hasContractEntered,
isAccumulatorContract,
isEmptyObject,
isOpen,
isValidToSell,
} from '@deriv/shared';
import { Localize } from '@deriv/translations';
import { TTradeStore } from 'Types';
import PurchaseButtonContent from './purchase-button-content';

const PurchaseButton = observer(() => {
const {
portfolio: { all_positions, onClickSell },
ui: { purchase_states: purchased_states_arr, setPurchaseState },
} = useStore();
const {
contract_type,
currency,
has_open_accu_contract,
is_accumulator,
is_multiplier,
is_purchase_enabled,
is_trade_enabled,
is_turbos,
is_vanilla_fx,
is_vanilla,
onPurchase,
proposal_info,
symbol,
trade_types,
validation_errors,
} = useTraderStore();
const { isMobile } = useDevice();
//TODO: add error handling when design will be ready
const is_high_low = /^high_low$/.test(contract_type.toLowerCase());
const is_proposal_empty = isEmptyObject(proposal_info);
const is_disabled = !is_trade_enabled || !is_purchase_enabled || is_proposal_empty;
const purchase_button_content_props = {
currency,
has_open_accu_contract,
is_accumulator,
is_multiplier,
is_turbos,
is_vanilla_fx,
is_vanilla,
};
const trade_types_array = Object.keys(trade_types);

const isLoading = (info: TTradeStore['proposal_info'][string]) => {
const has_validation_error = Object.values(validation_errors).some(e => e.length);
return !has_validation_error && !info?.has_error && !info.id;
};

const active_accu_contract = is_accumulator
? all_positions.find(
({ contract_info, type }) =>
isAccumulatorContract(type) && contract_info.underlying === symbol && !contract_info.is_sold
)
: undefined;
const is_valid_to_sell = active_accu_contract?.contract_info
? hasContractEntered(active_accu_contract.contract_info) &&
isOpen(active_accu_contract.contract_info) &&
isValidToSell(active_accu_contract.contract_info)
: false;
const current_stake =
(is_valid_to_sell && active_accu_contract && getIndicativePrice(active_accu_contract.contract_info)) || null;

if (is_accumulator && has_open_accu_contract) {
const info = proposal_info?.[trade_types_array[0]] || {};
return (
<div className='purchase-button__wrapper'>
<Button
color='black'
variant='secondary'
size='lg'
label={<Localize i18n_default_text='Sell' />}
fullWidth
className='purchase-button purchase-button--single'
disabled={!is_valid_to_sell || active_accu_contract?.is_sell_requested}
onClick={() => onClickSell(active_accu_contract?.contract_info.contract_id)}
>
<PurchaseButtonContent
{...purchase_button_content_props}
info={info}
current_stake={current_stake}
/>
</Button>
</div>
);
}

if (trade_types_array.length === 1) {
const info = proposal_info?.[trade_types_array[0]] || {};
const is_loading = isLoading(info) || !is_purchase_enabled;

return (
<div className='purchase-button__wrapper'>
<Button
color='purchase'
size='lg'
label={getContractTypeDisplay(trade_types_array[0], {
isHighLow: is_high_low,
showButtonName: true,
})}
fullWidth
className='purchase-button purchase-button--single'
isLoading={is_loading}
disabled={(is_disabled || !info.id) && !is_loading}
onClick={() => onPurchase(info.id, info.stake, trade_types_array[0], isMobile)}
>
{!is_loading && <PurchaseButtonContent {...purchase_button_content_props} info={info} />}
</Button>
</div>
);
}

return (
<div className='purchase-button__wrapper'>
{trade_types_array.map((trade_type, index) => {
const info = proposal_info?.[trade_type] || {};
const is_loading = isLoading(info);
const is_another_button_loading =
is_loading &&
purchased_states_arr.includes(true) &&
!purchased_states_arr[index] &&
!is_purchase_enabled;

return (
<Button
key={trade_type}
color={index ? 'purchase' : 'sell'}
size='lg'
label={getContractTypeDisplay(trade_type, { isHighLow: is_high_low, showButtonName: true })}
fullWidth
className={clsx(
'purchase-button',
is_loading && !is_another_button_loading && 'purchase-button--loading'
)}
isLoading={is_loading && !is_another_button_loading}
disabled={((is_disabled || !info.id) && !is_loading) || is_another_button_loading}
onClick={() => {
setPurchaseState(index);
onPurchase(info.id, info.stake, trade_type, isMobile);
}}
>
{(!is_loading || is_another_button_loading) && (
<PurchaseButtonContent
{...purchase_button_content_props}
info={info}
is_reverse={!!index}
/>
)}
</Button>
);
})}
</div>
);
});

export default PurchaseButton;

0 comments on commit 00fff44

Please sign in to comment.