DeFiCh/wallet

View on GitHub
mobile-app/app/components/BottomSheetTokenList.tsx

Summary

Maintainability
A
0 mins
Test Coverage
import { memo } from "react";
import * as React from "react";
import { tailwind } from "@tailwind";
import { Platform, View } from "react-native";
import { NumericFormat as NumberFormat } from "react-number-format";
import BigNumber from "bignumber.js";
import { NavigationProp, useNavigation } from "@react-navigation/native";
import { BottomSheetFlatList } from "@gorhom/bottom-sheet";
import { useThemeContext } from "@waveshq/walletkit-ui";
import { AddOrRemoveCollateralResponse } from "@screens/AppNavigator/screens/Loans/components/AddOrRemoveCollateralForm";
import { CollateralItem } from "@screens/AppNavigator/screens/Loans/screens/EditCollateralScreen";
import { LoanVaultActive } from "@defichain/whale-api-client/dist/api/loan";
import { useTokenPrice } from "@screens/AppNavigator/screens/Portfolio/hooks/TokenPrice";
import { getActivePrice } from "@screens/AppNavigator/screens/Auctions/helpers/ActivePrice";
import { ActiveUSDValueV2 } from "@screens/AppNavigator/screens/Loans/VaultDetail/components/ActiveUSDValueV2";
import { useCollateralTokenList } from "@screens/AppNavigator/screens/Loans/hooks/CollateralTokenList";
import { BottomSheetWithNavRouteParam } from "./BottomSheetWithNav";
import {
  ThemedFlatListV2,
  ThemedTextV2,
  ThemedTouchableOpacityV2,
} from "./themed";
import { CollateralFactorTag } from "./CollateralFactorTag";
import { SymbolIcon } from "./SymbolIcon";

interface BottomSheetTokenListProps {
  onTokenPress?: (token: CollateralItem | BottomSheetToken) => void;
  navigateToScreen?: {
    screenName: string;
    onButtonPress?: (item: AddOrRemoveCollateralResponse) => void;
  };
  vault?: LoanVaultActive;
  tokenType: TokenType;
  isOraclePrice?: boolean;
}

export interface BottomSheetToken {
  tokenId: string;
  available: BigNumber;
  token: {
    name: string;
    displaySymbol: string;
    symbol: string;
    isLPS?: boolean;
  };
  factor?: string;
  reserve?: string;
}

export enum TokenType {
  BottomSheetToken = "BottomSheetToken",
  CollateralItem = "CollateralItem",
}

export const BottomSheetTokenList = ({
  onTokenPress,
  navigateToScreen,
  vault,
  tokenType,
  isOraclePrice,
}: BottomSheetTokenListProps): React.MemoExoticComponent<() => JSX.Element> =>
  memo(() => {
    const { collateralTokens } = useCollateralTokenList();
    const { isLight } = useThemeContext();
    const navigation =
      useNavigation<NavigationProp<BottomSheetWithNavRouteParam>>();
    const flatListComponents = {
      mobile: BottomSheetFlatList,
      web: ThemedFlatListV2,
    };
    const FlatList =
      Platform.OS === "web"
        ? flatListComponents.web
        : flatListComponents.mobile;
    const { getTokenPrice } = useTokenPrice();

    function isCollateralItem(
      item: CollateralItem | BottomSheetToken
    ): item is CollateralItem {
      return (item as CollateralItem).activateAfterBlock !== undefined;
    }

    return (
      <FlatList
        testID="bottom_sheet_token_list"
        data={collateralTokens}
        renderItem={({
          item,
        }: {
          item: CollateralItem | BottomSheetToken;
        }): JSX.Element => {
          const activePrice =
            tokenType === TokenType.CollateralItem
              ? new BigNumber(
                  getActivePrice(
                    item.token.symbol,
                    (item as CollateralItem)?.activePrice,
                    (item as CollateralItem).factor,
                    "ACTIVE",
                    "COLLATERAL"
                  )
                )
              : getTokenPrice(
                  item.token.symbol,
                  new BigNumber("1"),
                  item.token.isLPS
                );
          return (
            <ThemedTouchableOpacityV2
              disabled={new BigNumber(item.available).lte(0)}
              onPress={() => {
                if (onTokenPress !== undefined) {
                  onTokenPress(item);
                }
                if (navigateToScreen !== undefined) {
                  navigation.navigate({
                    name: navigateToScreen.screenName,
                    params: {
                      token: item.token,
                      activePrice,
                      available: item.available.toFixed(8),
                      onButtonPress: navigateToScreen.onButtonPress,
                      collateralFactor: new BigNumber(item.factor ?? 0).times(
                        100
                      ),
                      isAdd: true,
                      vault,
                      collateralTokens,
                      ...(isCollateralItem(item) && { collateralItem: item }),
                    },
                    merge: true,
                  });
                }
              }}
              style={tailwind(
                "px-5 py-4.5 flex flex-row items-start justify-between mt-2 rounded-lg-v2",
                {
                  "opacity-30": new BigNumber(item.available).lte(0),
                }
              )}
              light={tailwind("bg-mono-light-v2-00")}
              dark={tailwind("bg-mono-dark-v2-00")}
              testID={`select_${item.token.displaySymbol}`}
            >
              <View style={tailwind("w-7/12 flex flex-row items-center")}>
                <SymbolIcon
                  symbol={item.token.displaySymbol}
                  styleProps={tailwind("w-9 h-9")}
                />
                <View style={tailwind("ml-2 flex-auto")}>
                  <View style={tailwind("flex flex-row")}>
                    <ThemedTextV2
                      testID={`token_symbol_${item.token.displaySymbol}`}
                      style={tailwind("text-sm font-semibold-v2")}
                      light={tailwind("text-mono-light-v2-900")}
                      dark={tailwind("text-mono-dark-v2-900")}
                    >
                      {item.token.displaySymbol}
                    </ThemedTextV2>
                    <View style={tailwind("ml-1")}>
                      <CollateralFactorTag factor={item.factor} />
                    </View>
                  </View>
                  <ThemedTextV2
                    light={tailwind("text-mono-light-v2-700")}
                    dark={tailwind("text-mono-dark-v2-700")}
                    style={tailwind([
                      "text-xs font-normal-v2 mt-1",
                      { hidden: item.token.name === "" },
                    ])}
                    numberOfLines={1}
                    ellipsizeMode="tail"
                  >
                    {item.token.name}
                  </ThemedTextV2>
                </View>
              </View>
              <View style={tailwind("w-5/12 flex flex-row items-center")}>
                <View style={tailwind("flex flex-1")}>
                  <NumberFormat
                    value={item.available.toFixed(8)}
                    thousandSeparator
                    displayType="text"
                    renderText={(value) => (
                      <ThemedTextV2
                        style={tailwind("text-sm font-semibold-v2 text-right")}
                        light={tailwind("text-mono-light-v2-900")}
                        dark={tailwind("text-mono-dark-v2-900")}
                        testID={`select_${item.token.displaySymbol}_value`}
                      >
                        {value}
                      </ThemedTextV2>
                    )}
                  />
                  <ActiveUSDValueV2
                    price={new BigNumber(item.available).multipliedBy(
                      activePrice
                    )}
                    containerStyle={tailwind("justify-end mt-1")}
                    isOraclePrice={isOraclePrice}
                  />
                </View>
              </View>
            </ThemedTouchableOpacityV2>
          );
        }}
        keyExtractor={(item) => item.tokenId}
        style={tailwind({
          "bg-mono-dark-v2-100": !isLight,
          "bg-mono-light-v2-100": isLight,
          "pt-1 -mt-1": Platform.OS === "android", // Word-around fix for line showing on android
        })}
        contentContainerStyle={tailwind("px-5 pb-20")}
      />
    );
  });