Untitled

mail@pastecode.io avatarunknown
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,
  },
});