Untitled
plain_text
a month ago
18 kB
1
Indexable
Never
import { RefreshControl, ScrollView, StyleSheet, View } from 'react-native'; import React from 'react'; import Left_button from 'app/components/left_button'; import { Icons } from 'app/constant'; import vndcTheme from 'app/assets/themes/vndc_theme'; import AppText from 'app/components/app_text'; import { getBottomSpace } from 'app/libs/react-native-iphone-x-helper'; import Item, { ItemIdTransaction } from './components/item'; import I18n from 'react-native-i18n'; import { DIC_WORKING_TYPE, getCondition, ORDER_STATUS, TYPE_ORDER, TYPE_SIDE, } from '../futures_onus/constants/order'; import { useBaseQuoteToken } from 'app/store/actions/futures/market_info'; import { useConfigSymbol, useSelectTickSize, } from 'app/store/actions/futures/exchange_info'; import { formatBalance, formatPriceWithTickSize, formatTime, formatVNDFee, formatVNDPnl, } from 'app/screens/futures_onus/helper'; import { getPositionsClose } from 'app/store/api/futures/private'; import ItemTrades from './components/item_trades'; import { BASE_TOKEN } from 'app/screens/futures_onus/constants/marketConfigs'; import Token_logo from 'app/components/token_logo'; import { onShowModalTooltips } from 'app/components/modal_tooltips'; import { forEach, isEmpty } from 'lodash'; import Big from 'big.js'; import Right_button from 'app/components/right_button'; import ModalShare from './components/modal_share'; import { Portal } from 'react-native-portalize'; // item // id:1010186 // clientOrderId:"" // sequenceId:1880185 // symbol:BTCUSDT // side:BUY // positionSide:BOTH // reduceOnly:false // type:MARKET // size:0.592 // filledSize:0.592 // price:0 // stopPrice:0 // workingType:MARK_PRICE // priceProtect:false // postOnly:false // status:FILLED // timeInForce:GTC // eventTimestamp:1672804755866 // entryTimestamp:1672804755825 // userId:2bbf820e-da51-4f0b-a2bb-68b5cb437f45 // originType:"" // closePosition:false // closeReason:"" // strategyId:"" const FuturesOnusHistoryDetailTransaction = React.memo(props => { const { navigation } = props; const item = navigation.getParam('item', {}); const tickSize = useSelectTickSize(item.symbol); const { baseAsset, quoteAsset } = useBaseQuoteToken(item.symbol); const dataConfig = useConfigSymbol(item.symbol); const quotePrecision = quoteAsset === BASE_TOKEN.VNDC ? 0 : +dataConfig.quotePrecision; const modalShareRef = React.useRef(null); const [trades, setTrades] = React.useState([]); const leverage = React.useMemo( () => trades?.[trades?.length - 1]?.leverage || 0, [trades], ); const [loading, setLoading] = React.useState(true); const [refreshing, setRefreshing] = React.useState(false); React.useLayoutEffect(() => { getPositionsClose({ symbol: item?.symbol, orderId: item?.id }).then(res => { if (res?.data && res?.data?.length > 0) { setTrades(res.data); } setLoading(false); }); }, []); const isLiq = React.useMemo(() => { return ( item?.autoCloseType === 'LIQUIDATION' && item?.filledSize > 0 && item?.type === TYPE_ORDER.MARKET ); }, [item]); const totalFee = React.useMemo(() => { return +item.accumFee; }, [item]); const totalRealizedProfit = React.useMemo(() => { return item.accumPnl; }, [item]); // giá đóng const avgPrice = React.useMemo(() => { return +item.avgPrice; }, [item]); //gia mở const entryPrice = React.useMemo(() => { const closePrice = avgPrice; const sizeClose = item.filledSize; const side = item?.side === TYPE_SIDE.BUY ? -1 : 1; if (sizeClose === 0 || sizeClose === '0') { return 0; } return new Big(closePrice * sizeClose * side - totalRealizedProfit) .div(sizeClose * side) .toNumber(); }, [trades, avgPrice, item]); const liqPrice = React.useMemo(() => { if (!isLiq) return 0; if (+item.price) return +item.price; return 0; }, [item.price, isLiq]); const prefixPnl = React.useMemo(() => { return +totalRealizedProfit > 0 ? '+' : +totalRealizedProfit < 0 ? '-' : ''; }, [totalRealizedProfit]); const strTypeBuySell = React.useMemo(() => { //item?.reduceOnly true thì là lệnh đóng, còn false là lệnh mở // lệnh đóng sẽ là lệnh mở của lệnh mở trước đó if (item?.side === TYPE_SIDE.BUY) { return I18n.t('futures_onus.long'); } return I18n.t('futures_onus.short'); }, [item]); const isOrderOpen = React.useMemo(() => { return !(item?.reduceOnly || item?.closePosition); }, [item]); const strType = React.useMemo(() => { //item?.reduceOnly true thì là lệnh đóng, còn false là lệnh mở // lệnh đóng sẽ là lệnh mở của lệnh mở trước đó if (item?.reduceOnly || item?.closePosition) { return `${I18n.t('futures_onus.close_order')} ${ item?.side === TYPE_SIDE.BUY ? I18n.t('futures_onus.short') : I18n.t('futures_onus.long') }`; } return I18n.t('futures_onus.open_order'); }, [item]); const dataInfoCode = React.useMemo( () => [ { title: I18n.t( 'futures_onus_history_detail_transaction.transaction_code', ), value: item.id, }, { title: I18n.t('futures_onus_history_detail_transaction.create_time'), value: formatTime(item?.entryTimestamp, 'HH:mm:ss yyyy-MM-dd', true), }, ], [], ); const dataInfoType = React.useMemo( () => [ { title: I18n.t('futures_onus_history_detail_transaction.type'), renderValue: () => ( <View style={styles.row}> <AppText style={styles.txt145}> {isLiq ? I18n.t('futures_onus.type_liquidation') : I18n.t( `futures_onus.type_${item?.originType?.toLowerCase() || item?.type.toLowerCase()}`, )} </AppText> {leverage > 0 ? ( <> <View style={styles.dot} /> <AppText style={styles.txt145}>{leverage}x</AppText> </> ) : null} <View style={styles.dot} /> <AppText style={styles.txt145}>{strType}</AppText> </View> ), }, { title: `${I18n.t('futures_onus.volume')} ${baseAsset}`, value: +item?.filledSize !== 0 ? `${formatBalance( item?.filledSize || item?.size, dataConfig && +dataConfig.sizePrecision, )} ${baseAsset}` : '-', }, { title: `${I18n.t('futures_onus.volume')} ${quoteAsset}`, value: item?.filledSize && avgPrice ? `${formatVNDPnl( quoteAsset === BASE_TOKEN.VNDC, formatBalance(+item?.filledSize * +avgPrice, quotePrecision), )} ${quoteAsset}` : '-', }, { title: I18n.t('nami.price_open'), value: +entryPrice && leverage > 0 ? `${formatPriceWithTickSize(entryPrice, tickSize)} ${quoteAsset}` : '-', isHide: !(+entryPrice && leverage > 0) || isOrderOpen, }, { title: isOrderOpen ? I18n.t('nami.price_open') : I18n.t('nami.price_close'), value: +avgPrice ? `${formatPriceWithTickSize(avgPrice, tickSize)} ${quoteAsset}` : '-', isHide: !isOrderOpen && isLiq, }, { title: I18n.t('futures_onus.liquidation_price'), value: +liqPrice ? `${formatPriceWithTickSize(liqPrice, tickSize)} ${quoteAsset}` : '-', isHide: +(liqPrice || '0') === 0, onPressInfo: () => { onShowModalTooltips({ title: I18n.t('futures_onus.liquidation_price'), message: I18n.t('futures_onus.description_liquid_price'), }); }, }, { title: I18n.t('futures_onus.condition'), value: +(item?.stopPrice || '0') !== 0 ? `${I18n.t( `futures_onus.${DIC_WORKING_TYPE[item.workingType]}`, )} ${getCondition( item.side, item.originType || item.type, )} ${formatPriceWithTickSize( item?.stopPrice, tickSize, )} ${quoteAsset}` : '-', isHide: +(item?.stopPrice || '0') === 0, }, { title: I18n.t('futures_onus_history_detail_transaction.reduce_only'), value: item?.reduceOnly ? I18n.t('futures_onus.yes') : I18n.t('futures_onus.no'), onPressInfo: () => { onShowModalTooltips({ title: I18n.t('futures_onus.description_title_reduce_only'), message: I18n.t('futures_onus.description_desc_reduce_only'), }); }, isHide: !item?.reduceOnly, }, ], [ avgPrice, dataConfig, prefixPnl, isLiq, strType, isOrderOpen, entryPrice, leverage, liqPrice, ], ); const onRefresh = React.useCallback(() => { setRefreshing(true); getPositionsClose({ symbol: item?.symbol, orderId: item?.id }).then(res => { if (res?.data && res?.data?.length > 0) { setTrades(res.data); } setRefreshing(false); }); }, []); const dataInfoFee = React.useMemo( () => [ { title: I18n.t('futures_onus_history_detail_transaction.reason'), value: (item.status === ORDER_STATUS.CANCELED || item.status === ORDER_STATUS.REJECTED) && item.closeReason ? `${I18n.t(`futures_onus.${item.closeReason}`, { defaultValue: item.closeReason, })}` : '', isHide: !(item.status === ORDER_STATUS.REJECTED && item.closeReason), }, { title: I18n.t('futures_onus_history_detail_transaction.fee'), value: !totalFee ? '-' : quoteAsset === BASE_TOKEN.VNDC ? `${formatBalance(formatVNDFee(totalFee))} ${quoteAsset}` : `${formatBalance(totalFee, quotePrecision)} ${quoteAsset}`, }, { title: I18n.t( 'futures_onus_history_detail_transaction.real_profit_loss', ), value: prefixPnl ? `${prefixPnl}${formatBalance( Math.abs(totalRealizedProfit), quotePrecision, )} ${quoteAsset}` : '-', styleValue: { color: totalRealizedProfit > 0 ? vndcTheme.priceUp : totalRealizedProfit < 0 ? vndcTheme.priceDown : vndcTheme.textColor, }, }, ], [totalFee, totalRealizedProfit, prefixPnl, item, dataConfig], ); React.useLayoutEffect(() => { navigation.setParams({ onShare: () => { let closePrice = avgPrice; let side = item?.side === TYPE_SIDE.BUY ? -1 : 1; let tpsl = (closePrice / entryPrice - 1) * leverage * side * 100; let _item = { ...item, entryPrice, //giá vào closePrice, //giá đóng leverage, side: item.side === TYPE_SIDE.BUY ? TYPE_SIDE.SELL : TYPE_SIDE.BUY, tpsl, }; modalShareRef.current?.open(_item); }, isShowShare: leverage > 0 && totalRealizedProfit !== 0 && !isLiq, }); }, [ entryPrice, avgPrice, item, trades, totalRealizedProfit, leverage, isLiq, ]); return ( <View style={styles.container}> <View style={styles.mask} /> <ScrollView contentContainerStyle={styles.contentContainerStyle} showsVerticalScrollIndicator={false} refreshControl={ <RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor={vndcTheme.textColor_1} /> }> <View style={styles.containerTop}> <View style={styles.row}> <View style={styles.containerLogo}> <Token_logo token={quoteAsset} style={styles.logoBaseToken} /> <Token_logo token={baseAsset} style={styles.logoQuoteToken} /> </View> <View> <AppText style={styles.txtTitle}>{item.symbol}</AppText> <AppText style={styles.txt166} color={ item.side === TYPE_SIDE.BUY ? vndcTheme.priceUp : vndcTheme.priceDown }> {I18n.t('futures_onus_history_detail_transaction.order')}{' '} {strTypeBuySell} </AppText> </View> <View style={styles.space} /> <View style={[ styles.tag, { backgroundColor: item.status === ORDER_STATUS.FILLED ? '#0FA47C10' : '#F6F6F610', }, ]}> <AppText style={styles.txt146} color={ item.status === ORDER_STATUS.FILLED ? vndcTheme.priceUp : vndcTheme.textColor }> {I18n.t( `futures_onus.order_status_${ item?.status === ORDER_STATUS.CANCELED && Math.abs(+item.filledSize) > 0 ? 'partiall_filled' : item?.status?.toLowerCase() }`, { defaultValue: '-', }, )} </AppText> </View> </View> <View height={12} /> <View style={styles.divider} /> <View style={styles.rowId}> <ItemIdTransaction item={dataInfoCode[0]} isCopy /> <View width={32} /> <ItemIdTransaction item={dataInfoCode[1]} /> </View> <View style={styles.divider} /> {dataInfoType.map((i, index) => { if (i.isHide) return null; return <Item key={index} item={i} styleValue={i.styleValue} />; })} <View style={styles.divider} /> {dataInfoFee.map((i, index) => { if (i.isHide) return null; return <Item key={index} item={i} styleValue={i.styleValue} />; })} </View> <View style={styles.containerDetailTransaction}> <View height={22} /> <AppText style={styles.txtTitleDetail}> {I18n.t('futures_onus_history_detail_transaction.match_history')} </AppText> {trades.map((e, i) => { const isLast = i === trades.length - 1; return ( <ItemTrades key={i} e={e} baseAsset={baseAsset} quoteAsset={quoteAsset} dataConfig={dataConfig} isLast={isLast} isLiq={isLiq} /> ); })} {trades.length === 0 && !loading ? ( <AppText style={styles.txtNoData}> {I18n.t('futures_onus_history_detail_transaction.no_more')} </AppText> ) : null} </View> </ScrollView> <Portal> <ModalShare ref={modalShareRef} /> </Portal> </View> ); }); FuturesOnusHistoryDetailTransaction.navigationOptions = props => { const { navigation } = props; const onShare = navigation.getParam('onShare', () => {}); const isShowShare = navigation.getParam('isShowShare', false); return { headerLeft: () => ( <Left_button onPress={() => navigation.goBack()} imageUrl={Icons.arrow_left} /> ), headerRight: () => isShowShare ? ( <Right_button onPress={() => { onShare && onShare(); }} imageUrl={Icons.icon_share_16} /> ) : null, title: I18n.t('futures_onus_history_detail_transaction.order_details'), headerStyle: { backgroundColor: vndcTheme.backgroundColor_2, elevation: 0, shadowColor: 'transparent', }, }; }; export default FuturesOnusHistoryDetailTransaction; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: vndcTheme.backgroundColor_1, }, divider: { height: 1, backgroundColor: vndcTheme.line_2, marginVertical: 5, }, txtTitle: { fontSize: 24, fontWeight: '600', }, contentContainerStyle: { paddingBottom: getBottomSpace() + 16, backgroundColor: vndcTheme.backgroundColor_1, }, txtTitleDetail: { fontSize: 18, fontWeight: '600', }, txtNoData: { fontSize: 14, fontWeight: '400', paddingVertical: 24, textAlign: 'center', color: vndcTheme.textColor_1, }, containerTop: { backgroundColor: vndcTheme.backgroundColor_2, paddingHorizontal: 15, borderBottomLeftRadius: 16, borderBottomRightRadius: 16, overflow: 'hidden', paddingVertical: 15, }, mask: { position: 'absolute', width: '100%', height: 300, backgroundColor: vndcTheme.backgroundColor_2, }, rowId: { flexDirection: 'row', paddingVertical: 10, }, containerDetailTransaction: { paddingHorizontal: 15, }, containerLogo: { width: 38, height: 38, marginRight: 10, }, logoBaseToken: { position: 'absolute', right: 0, bottom: 0, width: 24, height: 24, }, logoQuoteToken: { borderWidth: 2, width: 28, height: 28, borderColor: vndcTheme.backgroundColor_1, }, row: { flexDirection: 'row', alignItems: 'center', }, txt166: { fontSize: 16, fontWeight: '600', }, tag: { paddingVertical: 3, paddingHorizontal: 6, borderRadius: 100, }, txt146: { fontSize: 14, fontWeight: '600', }, space: { flex: 1, }, txt145: { fontSize: 14, fontWeight: '500', }, dot: { width: 3, height: 3, borderRadius: 1.5, backgroundColor: vndcTheme.line_1, marginHorizontal: 6, }, });