utils#formatNumber JavaScript Examples

The following examples show how to use utils#formatNumber. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.jsx    From Artion-Client with GNU General Public License v3.0 6 votes vote down vote up
OwnersModal = ({ visible, onClose, holders }) => {
  return (
    <Modal visible={visible} title="Owned by" onClose={onClose}>
      {holders.map((holder, idx) => (
        <Holder key={idx} holder={holder}>
          <div className={styles.holderInfo}>
            <div className={styles.avatarWrapper}>
              {holder.imageHash ? (
                <img
                  src={`https://cloudflare-ipfs.com/ipfs/${holder.imageHash}`}
                  width={40}
                  height={40}
                />
              ) : (
                <Identicon
                  account={holder.address}
                  size={40}
                  className={styles.avatar}
                />
              )}
            </div>
            <div className={styles.info}>
              <div className={styles.alias}>{holder.alias || 'Unnamed'}</div>
              <div className={styles.address}>
                {shortenAddress(holder.address)}
              </div>
            </div>
          </div>
          <div className={styles.holdCount}>
            {`${formatNumber(holder.supply)} item${
              holder.supply !== 1 ? 's' : ''
            }`}
          </div>
        </Holder>
      ))}
    </Modal>
  );
}
Example #2
Source File: index.jsx    From Artion-Client with GNU General Public License v3.0 4 votes vote down vote up
AuctionModal = ({
  visible,
  onClose,
  onStartAuction,
  auction,
  auctionStarted,
  confirming,
  approveContract,
  contractApproving,
  contractApproved,
}) => {
  const { tokens } = useTokens();
  const { getSalesContract } = useSalesContract();

  const [now, setNow] = useState(new Date());
  const [reservePrice, setReservePrice] = useState('');
  const [startTime, setStartTime] = useState(
    new Date(new Date().getTime() + 2 * 60 * 1000)
  );
  const [endTime, setEndTime] = useState(
    new Date(new Date().getTime() + 24 * 60 * 60 * 1000)
  );
  const [focused, setFocused] = useState(false);
  const [minBidReserve, setMinBidReserve] = useState(false);
  const [options, setOptions] = useState([]);
  const [selected, setSelected] = useState([]);
  const [tokenPrice, setTokenPrice] = useState();
  const [tokenPriceInterval, setTokenPriceInterval] = useState();
  const [inputError, setInputError] = useState(null);

  useEffect(() => {
    setInterval(() => setNow(new Date()), 1000);
  }, []);

  useEffect(() => {
    if (tokens?.length) {
      setOptions(tokens);
    }
  }, [tokens]);

  useEffect(() => {
    setReservePrice(auction?.reservePrice || '');
    setStartTime(
      auction?.startTime
        ? new Date(auction.startTime * 1000)
        : new Date(new Date().getTime() + 2 * 60 * 1000)
    );
    setEndTime(
      auction?.endTime
        ? new Date(auction.endTime * 1000)
        : new Date(new Date().getTime() + 24 * 60 * 60 * 1000)
    );
  }, [visible, auction]);

  useEffect(() => {
    if (visible && tokens?.length) {
      setSelected([auction ? auction.token : tokens[0]]);
    }
  }, [visible, auction]);

  const CustomCheckbox = withStyles({
    root: {
      '&$checked': {
        color: '#1969FF',
      },
    },
    checked: {},
  })(props => <Checkbox color="default" {...props} />);

  const getTokenPrice = () => {
    if (tokenPriceInterval) clearInterval(tokenPriceInterval);
    const func = async () => {
      const tk = selected[0].address || ethers.constants.AddressZero;
      try {
        const salesContract = await getSalesContract();
        const price = await salesContract.getPrice(tk);
        setTokenPrice(parseFloat(ethers.utils.formatUnits(price, 18)));
      } catch {
        setTokenPrice(null);
      }
    };
    func();
    setTokenPriceInterval(setInterval(func, 60 * 1000));
  };

  useEffect(() => {
    if (selected.length === 0) return;

    getTokenPrice();
  }, [selected]);

  const validateInput = (() => {
    if (reservePrice.length === 0 || parseFloat(reservePrice) == 0)
      return false;
    if (!auctionStarted && startTime.getTime() < now.getTime()) return false;
    return (
      endTime.getTime() >= now.getTime() + 1000 * 60 * 5 &&
      endTime.getTime() >= startTime.getTime() + 1000 * 60 * 5
    );
  })();

  return (
    <Modal
      visible={visible}
      title={auction ? 'Update Auction' : 'Start Auction'}
      onClose={onClose}
      submitDisabled={
        contractApproving ||
        confirming ||
        (contractApproved && !validateInput) ||
        inputError
      }
      submitLabel={
        contractApproved ? (
          confirming ? (
            <ClipLoader color="#FFF" size={16} />
          ) : auction ? (
            'Update Auction'
          ) : (
            'Start Auction'
          )
        ) : contractApproving ? (
          'Approving Item'
        ) : (
          'Approve Item'
        )
      }
      onSubmit={() =>
        contractApproved
          ? !confirming && validateInput
            ? onStartAuction(
                selected[0],
                reservePrice,
                startTime,
                endTime,
                minBidReserve
              )
            : null
          : approveContract()
      }
    >
      <div className={styles.formGroup}>
        <div className={styles.formLabel}>
          Reserve Price&nbsp;
          <BootstrapTooltip
            title="Reserve price is your desired one you want to get from this auction."
            placement="top"
          >
            <HelpOutlineIcon />
          </BootstrapTooltip>
        </div>
        <div className={cx(styles.formInputCont, focused && styles.focused)}>
          <Select
            options={options}
            disabled={auction || confirming}
            values={selected}
            onChange={tk => {
              setSelected(tk);
            }}
            className={styles.select}
            placeholder=""
            itemRenderer={({ item, itemIndex, methods }) => (
              <div
                key={itemIndex}
                className={styles.token}
                onClick={() => {
                  methods.clearAll();
                  methods.addItem(item);
                }}
              >
                <img src={item?.icon} className={styles.tokenIcon} />
                <div className={styles.tokenSymbol}>{item.symbol}</div>
              </div>
            )}
            contentRenderer={({ props: { values } }) =>
              values.length > 0 ? (
                <div className={styles.selectedToken}>
                  <img src={values[0]?.icon} className={styles.tokenIcon} />
                  <div className={styles.tokenSymbol}>{values[0].symbol}</div>
                </div>
              ) : (
                <div className={styles.selectedToken} />
              )
            }
          />
          <PriceInput
            className={styles.formInput}
            placeholder="0.00"
            value={'' + reservePrice}
            decimals={selected[0]?.decimals || 0}
            onChange={setReservePrice}
            onFocus={() => setFocused(true)}
            onBlur={() => setFocused(false)}
            disabled={contractApproving || confirming}
            onInputError={err => setInputError(err)}
          />
          <div className={styles.usdPrice}>
            {!isNaN(tokenPrice) && tokenPrice !== null ? (
              `$${formatNumber(
                ((parseFloat(reservePrice) || 0) * tokenPrice).toFixed(2)
              )}`
            ) : (
              <Skeleton width={100} height={24} />
            )}
          </div>
        </div>
        <InputError text={inputError} />
      </div>
      <div className={styles.formGroupDates}>
        <div className={styles.formGroup}>
          <div className={styles.formLabel}>Start Time</div>
          <div className={styles.formInputCont}>
            <Datetime
              value={startTime}
              className={'calendarAboveInput'}
              onChange={val => setStartTime(val.toDate())}
              inputProps={{
                className: styles.formInput,
                onKeyDown: e => e.preventDefault(),
                disabled: auctionStarted || contractApproving || confirming,
              }}
              closeOnSelect
              isValidDate={cur =>
                cur.valueOf() > now.getTime() - 1000 * 60 * 60 * 24
              }
            />
          </div>
        </div>
        <div className={styles.formGroup}>
          <div className={styles.formLabel}>Auction Expiration</div>
          <div className={styles.formInputCont}>
            <Datetime
              value={endTime}
              className={'calendarAboveInput'}
              onChange={val => setEndTime(val.toDate())}
              inputProps={{
                className: styles.formInput,
                onKeyDown: e => e.preventDefault(),
                disabled: contractApproving || confirming,
              }}
              closeOnSelect
              isValidDate={cur =>
                cur.valueOf() > startTime.getTime() - 1000 * 60 * 60 * 23
              }
            />
          </div>
        </div>
      </div>
      <FormControlLabel
        className={cx(styles.formControl, styles.selected)}
        classes={{ label: styles.groupTitle }}
        control={
          <CustomCheckbox
            checked={minBidReserve}
            onChange={() => setMinBidReserve(prevState => !prevState)}
          />
        }
        label="Minimum bid should be equal or greater than reserve price"
      />
    </Modal>
  );
}
Example #3
Source File: index.jsx    From Artion-Client with GNU General Public License v3.0 4 votes vote down vote up
BidModal = ({
  visible,
  onClose,
  onPlaceBid,
  minBidAmount,
  confirming,
  token,
  firstBid,
}) => {
  const { tokens } = useTokens();
  const { getSalesContract } = useSalesContract();
  const [currentBid, setCurrentBid] = useState(0);
  const [price, setPrice] = useState('');
  const [focused, setFocused] = useState(false);
  const [options, setOptions] = useState([]);
  const [tokenPrice, setTokenPrice] = useState();
  const [tokenPriceInterval, setTokenPriceInterval] = useState();
  const [inputError, setInputError] = useState(null);

  useEffect(() => {
    setPrice(minBidAmount);
    setCurrentBid(parseFloat(minBidAmount));
  }, [visible]);

  useEffect(() => {
    if (tokens?.length) {
      setOptions(tokens);
    }
  }, [tokens]);

  const getTokenPrice = () => {
    if (tokenPriceInterval) clearInterval(tokenPriceInterval);
    const func = async () => {
      const tk = token.address || ethers.constants.AddressZero;
      try {
        const salesContract = await getSalesContract();
        const price = await salesContract.getPrice(tk);
        setTokenPrice(parseFloat(ethers.utils.formatUnits(price, 18)));
      } catch {
        setTokenPrice(null);
      }
    };
    func();
    setTokenPriceInterval(setInterval(func, 60 * 1000));
  };

  useEffect(() => {
    if (token) {
      getTokenPrice();
    }
  }, [token]);

  const validateInput = () => {
    return (
      price.length > 0 &&
      parseFloat(price) > 0 &&
      (firstBid
        ? parseFloat(price) >= currentBid
        : parseFloat(price) > currentBid)
    );
  };

  return (
    <Modal
      visible={visible}
      title="Place Bid"
      onClose={onClose}
      submitDisabled={confirming || !validateInput() || inputError}
      submitLabel={confirming ? <ClipLoader color="#FFF" size={16} /> : 'Place'}
      onSubmit={() =>
        !confirming && validateInput() ? onPlaceBid(price) : null
      }
    >
      <div className={styles.formGroup}>
        <div className={styles.formLabel}>Price</div>
        <div className={cx(styles.formInputCont, focused && styles.focused)}>
          <Select
            options={options}
            disabled
            values={token ? [token] : []}
            className={styles.select}
            placeholder=""
            itemRenderer={({ item, itemIndex, methods }) => (
              <div
                key={itemIndex}
                className={styles.token}
                onClick={() => {
                  methods.clearAll();
                  methods.addItem(item);
                }}
              >
                <img src={item?.icon} className={styles.tokenIcon} />
                <div className={styles.tokenSymbol}>{item.symbol}</div>
              </div>
            )}
            contentRenderer={({ props: { values } }) =>
              values.length > 0 ? (
                <div className={styles.selectedToken}>
                  <img src={values[0]?.icon} className={styles.tokenIcon} />
                  <div className={styles.tokenSymbol}>{values[0].symbol}</div>
                </div>
              ) : (
                <div className={styles.selectedToken} />
              )
            }
          />
          <PriceInput
            className={styles.formInput}
            placeholder="0.00"
            decimals={token?.decimals || 0}
            value={'' + price}
            onChange={setPrice}
            onFocus={() => setFocused(true)}
            onBlur={() => setFocused(false)}
            disabled={confirming}
            onInputError={err => setInputError(err)}
          />
          <div className={styles.usdPrice}>
            {!isNaN(tokenPrice) && tokenPrice !== null ? (
              `$${formatNumber(
                ((parseFloat(price) || 0) * tokenPrice).toFixed(2)
              )}`
            ) : (
              <Skeleton width={100} height={24} />
            )}
          </div>
        </div>
        <InputError text={inputError} />
      </div>
    </Modal>
  );
}
Example #4
Source File: index.js    From Artion-Client with GNU General Public License v3.0 4 votes vote down vote up
BaseCard = ({ item, loading, style, create, onCreate, onLike }) => {
  const { storageUrl, likeItem, likeBundle } = useApi();
  const { getAuction } = useAuctionContract();
  const { getTokenByAddress } = useTokens();

  const [now, setNow] = useState(new Date());
  const [fetching, setFetching] = useState(false);
  const [isLiking, setIsLiking] = useState(false);
  const [info, setInfo] = useState(null);
  const [index, setIndex] = useState(0);
  const [isLike, setIsLike] = useState(false);
  const [liked, setLiked] = useState(0);
  const [auction, setAuction] = useState(null);

  const { collections } = useSelector(state => state.Collections);
  const { authToken } = useSelector(state => state.ConnectWallet);

  const collection = collections.find(
    col => col.address === item?.contractAddress
  );

  const getTokenURI = async tokenURI => {
    setFetching(true);
    try {
      tokenURI = getRandomIPFS(tokenURI);

      const { data } = await axios.get(tokenURI);

      if (data[Object.keys(data)[0]].image) {
        data.image = getRandomIPFS(data[Object.keys(data)[0]].image);
        data.name = data[Object.keys(data)[0]].name;
      }

      if (data.properties && data.properties.image) {
        data.image = getRandomIPFS(data.properties.image.description);
      }

      setInfo(data);
    } catch {
      setInfo(null);
    }
    setFetching(false);
  };

  const getCurrentAuction = async () => {
    try {
      const _auction = await getAuction(item.contractAddress, item.tokenID);
      if (_auction.endTime !== 0) {
        const token = getTokenByAddress(_auction.payToken);
        _auction.reservePrice = parseFloat(
          ethers.utils.formatUnits(_auction.reservePrice, token.decimals)
        );
        _auction.token = token;
        setAuction(_auction);
      }
    } catch (e) {
      console.log(e);
    }
  };

  useEffect(() => {
    async function fetchMyAPI() {
      if (item && !item.name) {
        await getTokenURI(item.tokenURI);
      }
      if (item) {
        if (item.imageURL) {
          // eslint-disable-next-line require-atomic-updates
          item.imageURL = getRandomIPFS(item.imageURL);
        }

        setLiked(item.liked);
        if (item.items) {
          setAuction(null);
        } else {
          getCurrentAuction();
        }
      }
    }
    fetchMyAPI();
  }, [item]);

  useEffect(() => {
    if (item?.isLiked !== undefined) {
      setIsLike(item.isLiked);
    }
  }, [item?.isLiked]);

  useEffect(() => {
    setInterval(() => {
      setNow(new Date());
    }, 1000);
  }, []);

  const auctionStarted = now.getTime() / 1000 >= auction?.startTime;

  const auctionEnded = auction?.endTime <= now.getTime() / 1000;

  const auctionActive = auctionStarted && !auctionEnded;

  const toggleFavorite = async e => {
    e.preventDefault();
    if (isLiking) return;

    setIsLiking(true);
    try {
      if (item.items) {
        const { data } = await likeBundle(item._id, authToken);
        setLiked(data);
      } else {
        const { data } = await likeItem(
          item.contractAddress,
          item.tokenID,
          authToken
        );
        setLiked(data);
      }
    } catch (err) {
      console.log(err);
    }
    setIsLike(!isLike);
    setIsLiking(false);

    onLike && onLike();
  };

  const formatDiff = diff => {
    if (diff >= ONE_MONTH) {
      const m = Math.ceil(diff / ONE_MONTH);
      return `${m} Month${m > 1 ? 's' : ''}`;
    }
    if (diff >= ONE_DAY) {
      const d = Math.ceil(diff / ONE_DAY);
      return `${d} Day${d > 1 ? 's' : ''}`;
    }
    if (diff >= ONE_HOUR) {
      const h = Math.ceil(diff / ONE_HOUR);
      return `${h} Hour${h > 1 ? 's' : ''}`;
    }
    if (diff >= ONE_MIN) {
      const h = Math.ceil(diff / ONE_MIN);
      return `${h} Min${h > 1 ? 's' : ''}`;
    }
    return `${diff} Second${diff > 1 ? 's' : ''}`;
  };

  const formatDuration = endTime => {
    const diff = endTime - Math.floor(now.getTime() / 1000);
    return formatDiff(diff);
  };

  const renderSlides = () => {
    return item.items.map((v, idx) => (
      <div className={styles.imageBox} key={idx}>
        {(v.imageURL || v.thumbnailPath?.length > 10) &&
          (v.imageURL?.includes('youtube') ? (
            <ReactPlayer
              className={styles.media}
              url={v.imageURL}
              controls={true}
              width="100%"
              height="100%"
            />
          ) : (
            <Suspense
              fallback={
                <Loader
                  type="Oval"
                  color="#007BFF"
                  height={32}
                  width={32}
                  className={styles.loader}
                />
              }
            >
              <SuspenseImg
                src={
                  v.thumbnailPath?.length > 10
                    ? `${storageUrl}/image/${v.thumbnailPath}`
                    : v.imageURL
                }
                className={styles.media}
                alt={v.name}
              />
            </Suspense>
          ))}
      </div>
    ));
  };

  const renderDots = () => {
    return item.items.map((v, idx) => (
      <div className={cx(styles.dot)} key={idx} />
    ));
  };

  const renderContent = () => {
    return (
      <>
        <div className={cx(styles.cardHeader, isLike && styles.liking)}>
          {!item ? (
            <Skeleton width={80} height={20} />
          ) : (
            <>
              {isLike ? (
                <FavoriteIcon
                  className={styles.favIcon}
                  onClick={toggleFavorite}
                />
              ) : (
                <FavoriteBorderIcon
                  className={styles.favIcon}
                  onClick={toggleFavorite}
                />
              )}
              <span className={styles.favLabel}>{liked || 0}</span>
            </>
          )}
        </div>
        <div className={styles.mediaBox}>
          <div className={styles.mediaPanel}>
            {loading || fetching ? (
              <Skeleton
                width="100%"
                height="100%"
                className={styles.mediaLoading}
              />
            ) : item.items ? (
              <>
                <Carousel
                  className={styles.carousel}
                  plugins={['fastSwipe']}
                  value={index}
                  onChange={_index =>
                    setIndex(Math.min(Math.max(_index, 0), 2))
                  }
                  slides={renderSlides()}
                  numberOfInfiniteClones={1}
                />
                <Dots
                  className={styles.dots}
                  value={index}
                  onChange={setIndex}
                  number={Math.min(item.items.length, 18)}
                  thumbnails={renderDots()}
                />
              </>
            ) : (
              <div className={styles.imageBox}>
                {(item?.imageURL ||
                  info?.image ||
                  item?.thumbnailPath?.length > 10 ||
                  item?.thumbnailPath === 'embed') &&
                  (item?.thumbnailPath === 'embed' ? (
                    <iframe src={item?.imageURL} className={styles.media} />
                  ) : (item?.imageURL || info?.image)?.includes('youtube') ? (
                    <ReactPlayer
                      className={styles.media}
                      url={item?.imageURL || info?.image}
                      controls={true}
                      width="100%"
                      height="100%"
                    />
                  ) : (
                    <Suspense
                      fallback={
                        <Loader
                          type="Oval"
                          color="#007BFF"
                          height={32}
                          width={32}
                          className={styles.loader}
                        />
                      }
                    >
                      <SuspenseImg
                        src={
                          item.thumbnailPath?.length > 10
                            ? `${storageUrl}/image/${item.thumbnailPath}`
                            : item?.imageURL || info?.image
                        }
                        className={styles.media}
                        alt={item.name}
                      />
                    </Suspense>
                  ))}
              </div>
            )}
          </div>
        </div>
        <div className={styles.content}>
          <div className={styles.topLine}>
            <div className={styles.itemName}>
              {loading || fetching ? (
                <Skeleton width={100} height={20} />
              ) : (
                <div className={styles.label}>
                  {collection?.collectionName || collection?.name}
                  {collection?.isVerified && (
                    <BootstrapTooltip
                      title="Verified Collection"
                      placement="top"
                    >
                      <CheckCircleIcon className={styles.checkIcon} />
                    </BootstrapTooltip>
                  )}
                </div>
              )}
              {loading || fetching ? (
                <Skeleton width={100} height={20} />
              ) : (
                <div className={styles.name}>{item?.name || info?.name}</div>
              )}
            </div>
            {auction?.reservePrice || item?.price ? (
              <div className={styles.alignRight}>
                {!loading && (
                  <div className={styles.label}>
                    {auctionActive ? 'Auction' : 'Price'}
                  </div>
                )}
                {loading || fetching ? (
                  <Skeleton width={80} height={20} />
                ) : (
                  <div className={cx(styles.label, styles.price)}>
                    <img
                      src={
                        auctionActive
                          ? auction?.token?.icon
                          : getTokenByAddress(item?.paymentToken)?.icon ||
                            wFTMLogo
                      }
                    />
                    {formatNumber(
                      auctionActive
                        ? auction.reservePrice
                        : item.price.toFixed(2)
                    )}
                  </div>
                )}
              </div>
            ) : (
              ''
            )}
          </div>
          <div className={styles.alignBottom}>
            <div>
              {auctionActive && (
                <>
                  {!loading && <div className={styles.label2}>Time left</div>}
                  <div className={styles.name2}>
                    {formatDuration(auction.endTime)}
                  </div>
                </>
              )}
            </div>
            {item?.lastSalePrice > 0 && (
              <div className={styles.alignRight}>
                {!loading && <div className={styles.label2}>Last Price</div>}
                {loading || fetching ? (
                  <Skeleton width={80} height={20} />
                ) : (
                  <div className={cx(styles.label2, styles.price2)}>
                    <img
                      src={
                        getTokenByAddress(item?.lastSalePricePaymentToken)?.icon
                      }
                    />
                    {formatNumber(item.lastSalePrice)}
                  </div>
                )}
              </div>
            )}
            {/* {loading || fetching ? (
              <Skeleton width={80} height={20} />
            ) : (
              <div className={styles.label}>
                {item.items
                  ? `${item.items.length} item${
                      item.items.length !== 1 ? 's' : ''
                    }`
                  : `${formatNumber(
                      item?.holderSupply || item?.supply || 1
                    )} of ${formatNumber(item?.supply || 1)}`}
              </div>
            )} */}
          </div>
        </div>
      </>
    );
  };

  return (
    <div style={style} className={styles.root} onClick={onCreate}>
      <div className={styles.card}>
        {create ? (
          <div className={styles.createBtn}>
            <div className={styles.createIcon}>
              <img src={iconPlus} />
            </div>
            <div className={styles.createLabel}>Create Bundle</div>
          </div>
        ) : item ? (
          <Link
            to={
              item.items
                ? `/bundle/${item._id}`
                : `/explore/${item.contractAddress}/${item.tokenID}`
            }
            className={styles.link}
          >
            {renderContent()}
          </Link>
        ) : (
          renderContent()
        )}
      </div>
      {item?.tokenType === 1155 && (
        <>
          <div className={styles.card} />
          <div className={styles.card} />
        </>
      )}
    </div>
  );
}
Example #5
Source File: index.jsx    From Artion-Client with GNU General Public License v3.0 4 votes vote down vote up
OfferModal = ({
  visible,
  onClose,
  onMakeOffer,
  confirming,
  totalSupply,
}) => {
  const { tokens } = useTokens();
  const { getSalesContract } = useSalesContract();

  const [price, setPrice] = useState('');
  const [quantity, setQuantity] = useState('1');
  const [endTime, setEndTime] = useState(
    new Date(new Date().getTime() + 24 * 60 * 60 * 1000)
  );
  const [options, setOptions] = useState([]);
  const [selected, setSelected] = useState([]);
  const [tokenPrice, setTokenPrice] = useState();
  const [tokenPriceInterval, setTokenPriceInterval] = useState();
  const [inputError, setInputError] = useState(null);

  useEffect(() => {
    if (tokens?.length > 1) {
      setOptions(tokens);
    }
  }, [tokens]);

  useEffect(() => {
    if (visible) {
      setPrice('');
      setQuantity('1');
      setEndTime(new Date(new Date().getTime() + 24 * 60 * 60 * 1000));
      if (tokens?.length > 1) {
        setSelected([tokens[0]]);
      }
    }
  }, [visible]);

  const getTokenPrice = () => {
    if (tokenPriceInterval) clearInterval(tokenPriceInterval);
    const func = async () => {
      const tk = selected[0].address || ethers.constants.AddressZero;
      try {
        const salesContract = await getSalesContract();
        const price = await salesContract.getPrice(tk);
        setTokenPrice(parseFloat(ethers.utils.formatUnits(price, 18)));
      } catch {
        setTokenPrice(null);
      }
    };
    func();
    setTokenPriceInterval(setInterval(func, 60 * 1000));
  };

  useEffect(() => {
    if (selected.length === 0) return;

    getTokenPrice();
  }, [selected]);

  const handleQuantityChange = e => {
    const val = e.target.value;
    if (!val) {
      setQuantity('');
      return;
    }

    if (isNaN(val)) return;

    const _quantity = parseInt(val);
    setQuantity(Math.min(_quantity, totalSupply));
  };

  const handleMakeOffer = () => {
    let quant = 1;
    if (totalSupply > 1) {
      quant = parseInt(quantity);
    }
    onMakeOffer(selected[0], price, quant, endTime);
  };

  const validateInput = () => {
    if (price.length === 0 || parseFloat(price) == 0) return false;
    if (totalSupply > 1 && quantity.length === 0) return false;
    if (endTime.getTime() < new Date().getTime()) return false;
    return true;
  };

  return (
    <Modal
      visible={visible}
      title="Place your offer"
      onClose={onClose}
      submitDisabled={confirming || !validateInput() || inputError}
      submitLabel={
        confirming ? <ClipLoader color="#FFF" size={16} /> : 'Place Offer'
      }
      onSubmit={() =>
        !confirming && validateInput() ? handleMakeOffer() : null
      }
    >
      <div className={styles.formGroup}>
        <div className={styles.formLabel}>Price</div>
        <div className={styles.formInputCont}>
          <Select
            options={options}
            disabled={confirming}
            values={selected}
            onChange={tk => {
              setSelected(tk);
            }}
            className={styles.select}
            placeholder=""
            itemRenderer={({ item, itemIndex, methods }) => (
              <div
                key={itemIndex}
                className={styles.token}
                onClick={() => {
                  methods.clearAll();
                  methods.addItem(item);
                }}
              >
                <img src={item?.icon} className={styles.tokenIcon} />
                <div className={styles.tokenSymbol}>{item.symbol}</div>
              </div>
            )}
            contentRenderer={({ props: { values } }) =>
              values.length > 0 ? (
                <div className={styles.selectedToken}>
                  <img src={values[0]?.icon} className={styles.tokenIcon} />
                  <div className={styles.tokenSymbol}>{values[0].symbol}</div>
                </div>
              ) : (
                <div className={styles.selectedToken} />
              )
            }
          />
          <PriceInput
            className={styles.formInput}
            placeholder="0.00"
            decimals={selected[0]?.decimals || 0}
            value={'' + price}
            onChange={setPrice}
            disabled={confirming}
            onInputError={err => setInputError(err)}
          />
          <div className={styles.usdPrice}>
            {!isNaN(tokenPrice) && tokenPrice !== null ? (
              `$${formatNumber(
                ((parseFloat(price) || 0) * tokenPrice).toFixed(2)
              )}`
            ) : (
              <Skeleton width={100} height={24} />
            )}
          </div>
        </div>
        <InputError text={inputError} />
      </div>
      {totalSupply !== null && (
        <div className={styles.formGroup}>
          <div className={styles.formLabel}>Quantity</div>
          <div className={styles.formInputCont}>
            <input
              className={styles.formInput}
              placeholder={totalSupply}
              value={quantity}
              onChange={handleQuantityChange}
              disabled={confirming || totalSupply === 1}
            />
          </div>
        </div>
      )}
      <div className={styles.formGroup}>
        <div className={styles.formLabel}>Offer Expiration</div>
        <div className={styles.formInputCont}>
          <Datetime
            value={endTime}
            onChange={val => setEndTime(val.toDate())}
            inputProps={{
              className: styles.formInput,
              onKeyDown: e => e.preventDefault(),
              disabled: confirming,
            }}
            closeOnSelect
            isValidDate={cur =>
              cur.valueOf() > new Date().getTime() - 1000 * 60 * 60 * 24
            }
          />
        </div>
      </div>
    </Modal>
  );
}
Example #6
Source File: index.jsx    From Artion-Client with GNU General Public License v3.0 4 votes vote down vote up
SellModal = ({
  visible,
  onClose,
  onSell,
  startPrice,
  confirming,
  approveContract,
  contractApproving,
  contractApproved,
  totalSupply,
}) => {
  const { tokens } = useTokens();
  const { getSalesContract } = useSalesContract();

  const [price, setPrice] = useState('');
  const [quantity, setQuantity] = useState('1');
  const [focused, setFocused] = useState(false);
  const [options, setOptions] = useState([]);
  const [selected, setSelected] = useState([]);
  const [tokenPrice, setTokenPrice] = useState();
  const [tokenPriceInterval, setTokenPriceInterval] = useState();
  const [inputError, setInputError] = useState(null);

  useEffect(() => {
    setPrice(startPrice > 0 ? startPrice.toString() : '');
    setQuantity('1');
    if (visible && tokens?.length) {
      setSelected([tokens[0]]);
    }
  }, [visible]);

  useEffect(() => {
    if (tokens?.length) {
      setOptions(tokens);
    }
  }, [tokens]);

  const getTokenPrice = () => {
    if (tokenPriceInterval) clearInterval(tokenPriceInterval);
    const func = async () => {
      const tk = selected[0].address || ethers.constants.AddressZero;
      try {
        const salesContract = await getSalesContract();
        const price = await salesContract.getPrice(tk);
        setTokenPrice(parseFloat(ethers.utils.formatUnits(price, 18)));
      } catch {
        setTokenPrice(null);
      }
    };
    func();
    setTokenPriceInterval(setInterval(func, 60 * 1000));
  };

  useEffect(() => {
    if (selected.length === 0) return;

    getTokenPrice();
  }, [selected]);

  const handleQuantityChange = e => {
    const val = e.target.value;
    if (!val) {
      setQuantity('');
      return;
    }

    if (isNaN(val)) return;

    const _quantity = parseInt(val);
    setQuantity(Math.min(_quantity, totalSupply));
  };

  const handleSellItem = () => {
    let quant = 1;
    if (totalSupply > 1) {
      quant = parseInt(quantity);
    }
    onSell(selected[0], price, quant);
  };

  const validateInput = () => {
    if (price.length === 0 || parseFloat(price) == 0) return false;
    if (totalSupply > 1 && quantity.length === 0) return false;
    if (selected.length === 0) return false;
    return true;
  };

  return (
    <Modal
      visible={visible}
      title={startPrice > 0 ? 'Update Your Listing' : 'Sell Your Item'}
      onClose={onClose}
      submitDisabled={
        contractApproving ||
        confirming ||
        (contractApproved && !validateInput()) ||
        inputError
      }
      submitLabel={
        contractApproved ? (
          confirming ? (
            <ClipLoader color="#FFF" size={16} />
          ) : startPrice > 0 ? (
            'Update Price'
          ) : (
            'List Item'
          )
        ) : contractApproving ? (
          'Approving Item'
        ) : (
          'Approve Item'
        )
      }
      onSubmit={() =>
        contractApproved
          ? !confirming && validateInput()
            ? handleSellItem()
            : null
          : approveContract()
      }
    >
      <div className={styles.formGroup}>
        <div className={styles.formLabel}>Price</div>
        <div className={cx(styles.formInputCont, focused && styles.focused)}>
          <Select
            options={options}
            disabled={confirming}
            values={selected}
            onChange={tk => {
              setSelected(tk);
            }}
            className={styles.select}
            placeholder=""
            itemRenderer={({ item, itemIndex, methods }) => (
              <div
                key={itemIndex}
                className={styles.token}
                onClick={() => {
                  methods.clearAll();
                  methods.addItem(item);
                }}
              >
                <img src={item?.icon} className={styles.tokenIcon} />
                <div className={styles.tokenSymbol}>{item.symbol}</div>
              </div>
            )}
            contentRenderer={({ props: { values } }) =>
              values.length > 0 ? (
                <div className={styles.selectedToken}>
                  <img src={values[0]?.icon} className={styles.tokenIcon} />
                  <div className={styles.tokenSymbol}>{values[0].symbol}</div>
                </div>
              ) : (
                <div className={styles.selectedToken} />
              )
            }
          />
          <PriceInput
            className={styles.formInput}
            placeholder="0.00"
            decimals={selected[0]?.decimals || 0}
            value={'' + price}
            onChange={setPrice}
            onFocus={() => setFocused(true)}
            onBlur={() => setFocused(false)}
            disabled={contractApproving || confirming}
            onInputError={setInputError}
          />
          <div className={styles.usdPrice}>
            {!isNaN(tokenPrice) && tokenPrice !== null ? (
              `$${formatNumber(
                ((parseFloat(price) || 0) * tokenPrice).toFixed(2)
              )}`
            ) : (
              <Skeleton width={100} height={24} />
            )}
          </div>
        </div>
        <InputError text={inputError} />
      </div>
      {totalSupply !== null && (
        <div className={styles.formGroup}>
          <div className={styles.formLabel}>Quantity</div>
          <div className={styles.formInputCont}>
            <input
              className={styles.formInput}
              placeholder={totalSupply}
              value={quantity}
              onChange={handleQuantityChange}
              disabled={contractApproving || confirming || totalSupply === 1}
            />
          </div>
        </div>
      )}
    </Modal>
  );
}
Example #7
Source File: index.jsx    From Artion-Client with GNU General Public License v3.0 4 votes vote down vote up
WFTMModal = ({ visible, onClose }) => {
  const { account, chainId } = useWeb3React();
  const { explorerUrl } = useApi();
  const { getWFTMBalance, wrapFTM, unwrapFTM } = useWFTMContract();

  const [loading, setLoading] = useState(false);
  const [balance, setBalance] = useState(0);
  const [wrappedBalance, setWrappedBalance] = useState(0);
  const [confirming, setConfirming] = useState(false);
  const [wrap, setWrap] = useState(true);
  const [amount, setAmount] = useState('');
  const [inputError, setInputError] = useState(null);

  const { price } = useSelector(state => state.Price);

  const getBalances = async (overrideLoading = false) => {
    if (!overrideLoading) {
      setLoading(true);
    }

    await window.ethereum.enable();
    const provider = new ethers.providers.Web3Provider(window.ethereum);

    let [ftmBal, wftmBal] = await Promise.all([
      await provider.getBalance(account),
      await getWFTMBalance(account),
    ]);

    setBalance(parseFloat(ftmBal.toString()) / 10 ** 18);
    setWrappedBalance(parseFloat(wftmBal.toString()) / 10 ** 18);

    if (!overrideLoading) {
      setLoading(false);
    }

    return [
      parseFloat(ftmBal.toString()) / 10 ** 18,
      parseFloat(wftmBal.toString()) / 10 ** 18,
    ];
  };

  const pollBalanceChange = async (initialFtmBal, initialWftmBal) => {
    setLoading(true);
    let timeout;
    let updated = false;

    await new Promise(
      resolve =>
        (timeout = setTimeout(
          () =>
            getBalances(true).then(([ftmBal, wftmBal]) => {
              if (ftmBal !== initialFtmBal || wftmBal !== initialWftmBal) {
                updated = true;
              }
              resolve();
            }),
          200
        ))
    );

    if (!updated) {
      await pollBalanceChange(initialFtmBal, initialWftmBal);
    }

    clearTimeout(timeout);
    return setLoading(false);
  };

  useEffect(() => {
    if (visible) {
      setLoading(false);
      setConfirming(false);
      setWrap(true);
      setAmount('');
      getBalances();
    }
  }, [visible, chainId]);

  const parseBalance = bal => {
    return bal.toFixed(4);
  };

  const isMax = () => {
    if (wrap) {
      return amount === (balance - 0.01).toString();
    }
    return amount === wrappedBalance.toString();
  };

  const onMax = () => {
    if (wrap) {
      setAmount((balance - 0.01).toString());
    } else {
      setAmount(wrappedBalance.toString());
    }
  };

  const handleWrapFTM = async () => {
    if (confirming || loading) return;

    setConfirming(true);
    try {
      const price = ethers.utils.parseEther(amount);
      if (wrap) {
        const tx = await wrapFTM(price, account);
        await tx.wait();
        await pollBalanceChange(balance, wrappedBalance);
        const toastId = showToast(
          'success',
          'Wrapped FTM successfully!',
          '',
          () => {
            toast.dismiss(toastId);
            window.open(`${explorerUrl}/tx/${tx.hash}`, '_blank');
          }
        );
      } else {
        const tx = await unwrapFTM(price);
        await tx.wait();
        await pollBalanceChange(balance, wrappedBalance);
        const toastId = showToast(
          'success',
          'Unwrap W-FTM successfully!',
          '',
          () => {
            toast.dismiss(toastId);
            window.open(`${explorerUrl}/tx/${tx.hash}`, '_blank');
          }
        );
      }
      setAmount('');
    } catch (err) {
      showToast('error', formatError(err));
      console.log(err);
    } finally {
      setConfirming(false);
    }
    getBalances();
  };

  return (
    <Modal
      visible={visible}
      title="FTM / WFTM Station"
      onClose={onClose}
      submitDisabled={
        confirming ||
        loading ||
        inputError ||
        amount.length === 0 ||
        parseFloat(amount) === 0 ||
        parseFloat(amount) > (wrap ? balance - 0.01 : wrappedBalance)
      }
      submitLabel={
        confirming || loading ? (
          <ClipLoader color="#FFF" size={16} />
        ) : wrap ? (
          'Wrap'
        ) : (
          'Unwrap'
        )
      }
      onSubmit={() =>
        amount.length &&
        parseFloat(amount) > 0 &&
        parseFloat(amount) <= (wrap ? balance - 0.01 : wrappedBalance) &&
        handleWrapFTM()
      }
    >
      <div className={cx(styles.swapContainer, !wrap && styles.reverse)}>
        <div className={styles.swapBox}>
          <div className={styles.symbol}>FTM</div>
          <div className={styles.swapBoxInner}>
            <div className={styles.balance}>
              Balance:{' '}
              {loading ? (
                <Skeleton width={60} height={20} />
              ) : (
                parseBalance(balance)
              )}
              {wrap && !isMax() && !loading && balance > 0 && (
                <div className={styles.max} onClick={onMax}>
                  (Max)
                </div>
              )}
            </div>
            <div className={styles.rightBox}>
              <PriceInput
                className={styles.input}
                placeholder="0.0"
                decimals={18}
                value={'' + amount}
                onChange={setAmount}
                onInputError={setInputError}
              />
              <div className={styles.usdVal}>
                ${formatNumber(((parseFloat(amount) || 0) * price).toFixed(2))}
              </div>
            </div>
          </div>
        </div>
        <div className={styles.swapbtn} onClick={() => setWrap(!wrap)}>
          <SwapVertIcon className={styles.icon} />
        </div>
        <div className={styles.swapBox}>
          <div className={styles.symbol}>WFTM</div>
          <div className={styles.swapBoxInner}>
            <div className={styles.balance}>
              Balance:{' '}
              {loading ? (
                <Skeleton width={60} height={20} />
              ) : (
                parseBalance(wrappedBalance)
              )}
              {!wrap && !isMax() && !loading && balance > 0 && (
                <div className={styles.max} onClick={onMax}>
                  (Max)
                </div>
              )}
            </div>
            <div className={styles.rightBox}>
              <PriceInput
                className={styles.input}
                placeholder="0.0"
                decimals={18}
                value={'' + amount}
                onChange={setAmount}
                onInputError={setInputError}
              />
              <div className={styles.usdVal}>
                ${formatNumber(((parseFloat(amount) || 0) * price).toFixed(2))}
              </div>
            </div>
          </div>
        </div>
      </div>
      <InputError text={inputError} />
    </Modal>
  );
}
Example #8
Source File: index.js    From Artion-Client with GNU General Public License v3.0 4 votes vote down vote up
ExploreFilterHeader = ({ loading, category }) => {
  const dispatch = useDispatch();

  const { collections: collectionItems } = useSelector(
    state => state.Collections
  );
  const { count } = useSelector(state => state.Tokens);
  const { groupType, sortBy, collections } = useSelector(state => state.Filter);

  const selectedCollections = () => {
    const res = new Array(collections.length).fill(null);
    collectionItems.map(item => {
      const index = collections.findIndex(_item => _item === item.address);
      if (index > -1) {
        res[index] = item;
      }
    });
    return res.filter(item => !!item);
  };

  const handleGroupTypeChange = e => {
    const newGroupType = e.target.value;
    dispatch(FilterActions.updateGroupTypeFilter(newGroupType));
  };

  const handleSortByChange = e => {
    const newSortBy = e.target.value;
    dispatch(FilterActions.updateSortByFilter(newSortBy));
  };

  const handleDeselectCollection = addr => {
    let newCollections = [];
    newCollections = collections.filter(item => item !== addr);
    dispatch(FilterActions.updateCollectionsFilter(newCollections));
  };

  return (
    <div className="filterHeaderRoot">
      <div className="filterHeaderLeft">
        <label className="filterResultLabel">
          {loading ? (
            <Skeleton width={100} height={24} />
          ) : (
            `${formatNumber(count)} Result${count !== 1 ? 's' : ''}
            ${
              category === null ? '' : `- Category: ${formatCategory(category)}`
            }`
          )}
        </label>
        {selectedCollections().map((item, idx) => (
          <div key={idx} className="filterCollectionItem">
            <img
              className="filterCollectionItemLogo"
              src={
                item.isVerified
                  ? `${getRandomIPFS('', true)}${item.logoImageHash}`
                  : nftActiveIcon
              }
            />
            <span className="filterCollectionItemName">
              {item.name || item.collectionName}
            </span>
            <CloseIcon
              className="filterCollectionRemoveItem"
              onClick={() => handleDeselectCollection(item.address)}
            />
          </div>
        ))}
      </div>
      <div className="filterSelectGroup">
        <FormControl className="filterHeaderFormControl">
          <Select
            value={groupType}
            onChange={handleGroupTypeChange}
            className="selectBox"
            classes={{
              select: 'selectInner',
              selectMenu: 'selectMenu',
              icon: 'selectIcon',
            }}
            MenuProps={{
              classes: {
                paper: 'menuPropsPaper',
                list: 'menuItemList',
              },
            }}
            IconComponent={ExpandMoreIcon}
          >
            {GroupFilters.map((filter, idx) => {
              return (
                <MenuItem
                  value={filter.value}
                  key={idx}
                  className="menuItem"
                  classes={{ selected: 'menuItemSelected ' }}
                >
                  {filter.label}
                </MenuItem>
              );
            })}
          </Select>
        </FormControl>
        <FormControl className="filterHeaderFormControl">
          <Select
            value={sortBy}
            onChange={handleSortByChange}
            className="selectBox"
            classes={{
              select: 'selectInner',
              selectMenu: 'selectMenu',
              icon: 'selectIcon',
            }}
            MenuProps={{
              classes: {
                paper: 'menuPropsPaper',
                list: 'menuItemList',
              },
            }}
            IconComponent={ExpandMoreIcon}
          >
            {SortByOptions.map((option, idx) => {
              return (
                <MenuItem
                  value={option.id}
                  key={idx}
                  className="menuItem"
                  classes={{ selected: 'menuItemSelected ' }}
                >
                  {option.label}
                </MenuItem>
              );
            })}
          </Select>
        </FormControl>
      </div>
    </div>
  );
}