pussy-ts/src/containers/portal/release/ActionBarRelease.tsx

/* eslint-disable jsx-a11y/control-has-associated-label */
import { useMemo, useCallback, useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useSigningClient } from 'src/contexts/signerClient';
import { Nullable } from 'src/types';
import { AccountValue } from 'src/types/defaultAccount';
import { useQueryClient } from 'src/contexts/queryClient';
import BigNumber from 'bignumber.js';
import Soft3MessageFactory from 'src/soft.js/api/msgs';
import { GIFT_ICON } from '../utils';
import { Dots, BtnGrd, ActionBar, Account } from '../../../components';
import { CYBER } from '../../../utils/config';
import { PATTERN_CYBER } from 'src/constants/app';
import { trimString } from '../../../utils/utils';
import { TxHash } from '../hook/usePingTxs';
import { CurrentRelease } from './type';
import mssgsClaim from '../utilsMsgs';
import { useAdviser } from 'src/features/adviser/context';

const releaseMsg = (giftAddress: string) => {
  return {
    release: {
      gift_address: giftAddress,
    },
  };
};

const STEP_INIT = 0;
const STEP_CHECK_ACC = 1;
const STATE_CHANGE_ACCOUNT = 1.1;
const STEP_RELEASE = 2;

type Props = {
  txHash: Nullable<TxHash>;
  updateTxHash: (data: TxHash) => void;
  selectedAddress: Nullable<string>;
  isRelease: boolean;
  loadingRelease: boolean;
  addressActive: Nullable<AccountValue>;
  currentRelease: Nullable<CurrentRelease[]>;
  redirectFunc: (steps: 'claim' | 'prove') => void;
  callback?: (error: string) => void;
  availableRelease: (isLedger: boolean) => number;
};

function ActionBarRelease({
  txHash,
  updateTxHash,
  selectedAddress,
  isRelease,
  currentRelease,
  totalGift,
  totalRelease,
  loadingRelease,
  callback,
  availableRelease,
  addressActive,
  redirectFunc,
}: Props) {
  const navigate = useNavigate();
  const [step, setStep] = useState(STEP_INIT);
  const { signer, signingClient } = useSigningClient();
  const queryClient = useQueryClient();
  const { setAdviser } = useAdviser();

  const getRelease = useCallback(async () => {
    try {
      if (signer && signingClient && currentRelease) {
        const { isNanoLedger, bech32Address: addressKeplr } =
          await signer.keplr.getKey(CYBER.CHAIN_ID);

        const msgs = [];

        if (currentRelease.length > 0) {
          currentRelease
            .slice(0, isNanoLedger ? 1 : currentRelease.length)
            .forEach((item) => {
              const { address } = item;
              const msgObject = releaseMsg(address);
              msgs.push(msgObject);
            });
        }

        if (msgs.length === 0) {
          return;
        }

        const msgsBroadcast = await mssgsClaim(
          {
            sender: addressKeplr,
            isNanoLedger,
          },
          msgs,
          availableRelease(isNanoLedger),
          queryClient
        );

        if (isNanoLedger) {
          setAdviser(
            "Ledger Nano-S is temporarily not supported, but don't worry, you can release your gift later",
            'red'
          );
        }

        if (!msgsBroadcast.length) {
          return;
        }

        const multiplier = new BigNumber(2).multipliedBy(msgsBroadcast.length);

        const executeResponseResult = await signingClient.signAndBroadcast(
          addressKeplr,
          [...msgsBroadcast],
          Soft3MessageFactory.fee(multiplier.toNumber()),
          'cyber'
        );

        if (executeResponseResult.code === 0) {
          updateTxHash({
            status: 'pending',
            txHash: executeResponseResult.transactionHash,
          });
        }

        if (executeResponseResult.code) {
          updateTxHash({
            txHash: executeResponseResult?.transactionHash,
            status: 'error',
            rawLog: executeResponseResult?.rawLog.toString(),
          });
        }

        setStep(STEP_INIT);
      }
    } catch (error) {
      console.error(error);
      // FIXME: not clear how to handle error codes
      callback?.('contract call error');
      setStep(STEP_INIT);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [
    signer,
    signingClient,
    currentRelease,
    queryClient,
    availableRelease,
    setAdviser,
  ]);

  useEffect(() => {
    const checkAddress = async () => {
      if (step === STEP_CHECK_ACC || step === STATE_CHANGE_ACCOUNT) {
        if (signer && addressActive) {
          const [{ address }] = await signer.getAccounts();
          const { bech32 } = addressActive;
          if (address === bech32) {
            setStep(STEP_RELEASE);
            getRelease();
          } else {
            setStep(STATE_CHANGE_ACCOUNT);
          }
        }
      }
    };
    checkAddress();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [step, signer, addressActive]);

  const useAddressOwner = useMemo(() => {
    if (addressActive) {
      const { name, bech32 } = addressActive;
      if (name !== undefined && name !== null) {
        return (
          <>
            account
            <span style={{ color: '#36d6ae', padding: '0 5px' }}>{name}</span>
          </>
        );
      }
      return (
        <>
          address
          <span style={{ color: '#36d6ae', padding: '0 5px' }}>
            {trimString(bech32, 10, 4)}
          </span>
        </>
      );
    }
    return '';
  }, [addressActive]);

  const useValidOwner = useMemo(() => {
    if (
      selectedAddress &&
      !selectedAddress.match(PATTERN_CYBER) &&
      currentRelease &&
      addressActive &&
      currentRelease.length > 0 &&
      currentRelease[0].addressOwner !== addressActive.bech32
    ) {
      return true;
    }

    return false;
  }, [addressActive, currentRelease, selectedAddress]);

  const isValidClaime = useMemo(() => {
    if (
      selectedAddress &&
      selectedAddress.match(PATTERN_CYBER) &&
      totalGift !== null &&
      totalRelease === null
    ) {
      return true;
    }

    const validFields =
      selectedAddress &&
      totalGift !== null &&
      totalRelease !== null &&
      !selectedAddress.match(PATTERN_CYBER);

    if (
      validFields &&
      Object.prototype.hasOwnProperty.call(totalGift, selectedAddress) &&
      !Object.prototype.hasOwnProperty.call(totalRelease, selectedAddress)
    ) {
      return true;
    }

    return false;
  }, [selectedAddress, totalGift, totalRelease]);

  const isValidProve = useMemo(() => {
    if (
      selectedAddress &&
      selectedAddress.match(PATTERN_CYBER) &&
      totalGift === null
    ) {
      return true;
    }

    if (
      selectedAddress &&
      totalGift !== null &&
      !selectedAddress.match(PATTERN_CYBER) &&
      !Object.prototype.hasOwnProperty.call(totalGift, selectedAddress)
    ) {
      return true;
    }

    return false;
  }, [selectedAddress, totalGift]);

  const useSelectCyber = useMemo(() => {
    return selectedAddress && selectedAddress.match(PATTERN_CYBER);
  }, [selectedAddress]);

  const redirectToGift = useCallback(
    (key: 'claim' | 'prove') => {
      if (redirectFunc) {
        redirectFunc(key);
      }

      navigate('/gift');
    },
    [navigate, redirectFunc]
  );

  if (currentRelease && useValidOwner) {
    return (
      <ActionBar>
        <div
          style={{
            display: 'flex',
          }}
        >
          already claimed by
          <Account
            styleUser={{ marginLeft: '5px' }}
            address={currentRelease[0].addressOwner}
          />
          . switch account to release
        </div>
      </ActionBar>
    );
  }

  if (step === STATE_CHANGE_ACCOUNT) {
    return (
      <ActionBar onClickBack={() => setStep(STEP_INIT)}>
        choose {useAddressOwner} in keplr
      </ActionBar>
    );
  }

  if (loadingRelease) {
    return (
      <ActionBar>
        <BtnGrd disabled text={<Dots />} />
      </ActionBar>
    );
  }

  if (isValidProve) {
    return (
      <ActionBar>
        <BtnGrd
          onClick={() => redirectToGift('prove')}
          text="go to prove address"
        />
      </ActionBar>
    );
  }

  if (isValidClaime) {
    return (
      <ActionBar>
        <BtnGrd onClick={() => redirectToGift('claim')} text="go to claim" />
      </ActionBar>
    );
  }

  if (totalRelease !== null) {
    return (
      <ActionBar>
        <BtnGrd
          disabled={isRelease === false || isRelease === null}
          onClick={() => setStep(STEP_CHECK_ACC)}
          text={
            useSelectCyber ? `release all ${GIFT_ICON}` : `release ${GIFT_ICON}`
          }
          pending={
            step === STEP_RELEASE ||
            step === STEP_CHECK_ACC ||
            txHash?.status === 'pending'
          }
        />
      </ActionBar>
    );
  }

  return null;
}

export default ActionBarRelease;

Synonyms

cyb/src/containers/portal/release/ActionBarRelease.tsx

Neighbours