hooks#useAccount TypeScript Examples

The following examples show how to use hooks#useAccount. 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: Accounts.tsx    From gear-js with GNU General Public License v3.0 6 votes vote down vote up
function Accounts({ list, onChange }: Props) {
  const { switchAccount } = useAccount();
  const isAnyAccount = list.length > 0;

  const handleAccountButtonClick = (account: InjectedAccountWithMeta) => {
    switchAccount(account);
    // TODO: 'account' to consts
    localStorage.setItem('account', account.address);
    onChange();
  };

  const getAccounts = () =>
    list.map((account) => (
      <li key={account.address}>
        <AccountButton
          address={account.address}
          name={account.meta.name}
          isActive={isLoggedIn(account)}
          onClick={() => handleAccountButtonClick(account)}
          block
        />
      </li>
    ));

  return isAnyAccount ? (
    <ul className={styles.list}>{getAccounts()}</ul>
  ) : (
    <p>
      No accounts found. Please open Polkadot extension, create a new account or import existing one and reload the
      page.
    </p>
  );
}
Example #2
Source File: Message.tsx    From gear-js with GNU General Public License v3.0 6 votes vote down vote up
Message = ({ message }: Props) => {
  const { id } = message;

  const { api } = useApi();
  const { account } = useAccount();
  const alert = useAlert();

  const showErrorAlert = (error: string) => {
    alert.error(error);
  };

  const showSuccessAlert = (data: ISubmittableResult) => {
    if (!data.status.isBroadcast) {
      alert.success(`Status: ${data.status}`);
    }
  };

  const handleClaimButtonClick = () => {
    if (account) {
      const { address, meta } = account;

      api.claimValueFromMailbox.submit(id);
      web3FromSource(meta.source)
        .then(({ signer }) => api.claimValueFromMailbox.signAndSend(address, { signer }, showSuccessAlert))
        .catch((error: Error) => showErrorAlert(error.message));
    }
  };

  return (
    <div className={styles.message}>
      <pre className={styles.pre}>{getPreformattedText(message)}</pre>
      <div>
        <ReplyLink to={id} />
        <Button text="Claim value" icon={claimIcon} color="secondary" size="small" onClick={handleClaimButtonClick} />
      </div>
    </div>
  );
}
Example #3
Source File: Mailbox.tsx    From gear-js with GNU General Public License v3.0 6 votes vote down vote up
Mailbox = () => {
  const { api } = useApi();
  const { account } = useAccount();
  const [mailbox, setMailbox] = useState<MailboxType>([]);
  const isAnyMessage = mailbox.length > 0;

  useEffect(() => {
    if (account) {
      api.mailbox.read(account.address).then(setMailbox);
    } else {
      setMailbox([]);
    }
  }, [account, api.mailbox]);

  const getMessages = () => mailbox.map(([, message], index) => <Message key={index} message={message} />);

  return (
    <div className="wrapper">
      <Box className={styles.box}>
        <h2 className={styles.heading}>Mailbox:</h2>
        <div className={styles.messages}>{isAnyMessage ? getMessages() : <p>No messages</p>}</div>
      </Box>
    </div>
  );
}
Example #4
Source File: Account.tsx    From gear-js with GNU General Public License v3.0 6 votes vote down vote up
function Account() {
  const { accounts, account } = useAccount();
  const [isModalOpen, setIsModalOpen] = useState(false);

  const openModal = () => {
    setIsModalOpen(true);
  };

  const closeModal = () => {
    setIsModalOpen(false);
  };

  return (
    <>
      {account ? (
        <Wallet balance={account.balance} address={account.address} name={account.meta.name} onClick={openModal} />
      ) : (
        <Button icon={userSVG} text="Sign in" onClick={openModal} />
      )}
      {isModalOpen && <AccountsModal accounts={accounts} close={closeModal} />}
    </>
  );
}
Example #5
Source File: Accounts.tsx    From gear-js with GNU General Public License v3.0 6 votes vote down vote up
function Accounts({ list, onChange }: Props) {
  const { switchAccount } = useAccount();
  const isAnyAccount = list.length > 0;

  const handleAccountButtonClick = (account: InjectedAccountWithMeta) => {
    switchAccount(account);
    // TODO: 'account' to consts
    localStorage.setItem('account', account.address);
    onChange();
  };

  const getAccounts = () =>
    list.map((account) => (
      <li key={account.address}>
        <AccountButton
          address={account.address}
          name={account.meta.name}
          isActive={isLoggedIn(account)}
          onClick={() => handleAccountButtonClick(account)}
          block
        />
      </li>
    ));

  return isAnyAccount ? (
    <ul className={styles.list}>{getAccounts()}</ul>
  ) : (
    <p>
      No accounts found. Please open Polkadot extension, create a new account or import existing one and reload the
      page.
    </p>
  );
}
Example #6
Source File: Account.tsx    From gear-js with GNU General Public License v3.0 6 votes vote down vote up
function Account() {
  const { account } = useAccount();
  const accounts = useAccounts();
  const [isModalOpen, setIsModalOpen] = useState(false);

  const openModal = () => {
    setIsModalOpen(true);
  };

  const closeModal = () => {
    setIsModalOpen(false);
  };

  return (
    <>
      {account ? (
        <Wallet balance={account.balance} address={account.address} name={account.meta.name} onClick={openModal} />
      ) : (
        <Button icon={userSVG} text="Sign in" onClick={openModal} />
      )}
      {isModalOpen && <AccountsModal accounts={accounts} close={closeModal} />}
    </>
  );
}
Example #7
Source File: hooks.ts    From gear-js with GNU General Public License v3.0 5 votes vote down vote up
function useListing() {
  const { id } = useParams() as Params;
  const { account } = useAccount();

  const nft = useNft(id);
  const { name, description, ownerId, media, reference } = nft || {};

  const [details, setDetails] = useState<NFTDetails>();
  const { royalty, rarity, attributes } = details || {};

  const marketNft = useMarketNft(id);
  const { price, auction, offers } = marketNft || {};
  const { currentPrice, bids } = auction || {};

  const isSale = !!price;
  const isAuction = !!auction;
  const isListed = isSale || isAuction;
  const isOwner = account ? GearKeyring.decodeAddress(account.address) === ownerId : false;

  useEffect(() => {
    if (reference) {
      fetch(reference)
        .then((response) => response.json())
        .then(setDetails);
    }
  }, [reference]);

  const heading = `${name} #${id}`;

  return {
    heading,
    description,
    ownerId,
    media,
    reference,
    royalty,
    rarity,
    attributes,
    price,
    offers,
    currentPrice,
    bids,
    isSale,
    isAuction,
    isListed,
    isOwner,
  };
}
Example #8
Source File: Listing.tsx    From gear-js with GNU General Public License v3.0 5 votes vote down vote up
function Listing() {
  const { id } = useParams() as Params;
  const { account } = useAccount();
  const nft = useNft(id);

  const [details, setDetails] = useState<NFTDetails>();
  const { royalty, rarity, attributes } = details || {};

  const marketNft = useMarketNft(id);
  const { price, auction, offers } = marketNft || {};
  const { startedAt, endedAt, currentPrice, bids } = auction || {};

  const isSale = !!price;
  const isAuction = !!auction;
  const isOwner = account?.decodedAddress === nft?.ownerId;

  useEffect(() => {
    const { reference } = nft || {};

    if (reference) {
      const path = `${IPFS_GATEWAY_ADDRESS}/${reference}`;

      fetch(path)
        .then((response) => response.json())
        .then(setDetails);
    }
  }, [nft]);

  // eslint-disable-next-line no-nested-ternary
  return nft ? (
    // eslint-disable-next-line no-nested-ternary
    isSale ? (
      <SaleListing
        id={id}
        isOwner={isOwner}
        heading={`${nft.name} #${id}`}
        description={nft.description}
        image={nft.media}
        owner={nft.ownerId}
        price={price}
        offers={offers || []}
        rarity={rarity}
        royalty={royalty}
        attrs={attributes}
      />
    ) : isAuction ? (
      <AuctionListing
        id={id}
        isOwner={isOwner}
        heading={`${nft.name} #${id}`}
        description={nft.description}
        image={nft.media}
        owner={nft.ownerId}
        price={currentPrice}
        offers={bids || []}
        rarity={rarity}
        royalty={royalty}
        attrs={attributes}
        startedAt={startedAt || ''}
        endedAt={endedAt || ''}
      />
    ) : (
      <OwnerListing
        id={id}
        isOwner={isOwner}
        heading={`${nft.name} #${id}`}
        description={nft.description}
        image={nft.media}
        owner={nft.ownerId}
        offers={offers || []}
        rarity={rarity}
        royalty={royalty}
        attrs={attributes}
      />
    )
  ) : null;
}
Example #9
Source File: useMessage.ts    From gear-js with GNU General Public License v3.0 5 votes vote down vote up
function useMessage(destination: Hex, metadata: Metadata | undefined) {
  const alert = useAlert();
  const { api } = useApi();
  const { account } = useAccount();
  const { enableLoading, disableLoading } = useLoading();

  const handleEventsStatus = (events: EventRecord[]) => {
    events.forEach(({ event: { method } }) => {
      if (method === 'DispatchMessageEnqueued') {
        alert.success('Send message: Finalized');
        // onSucessCallback();
      } else if (method === 'ExtrinsicFailed') {
        alert.error('Extrinsic failed');
      }
    });
  };

  const handleStatus = (result: ISubmittableResult) => {
    const { status, events } = result;
    const { isInBlock, isInvalid, isFinalized } = status;

    if (isInvalid) {
      alert.error('Transaction error. Status: isInvalid');
      disableLoading();
    } else if (isInBlock) {
      alert.success('Send message: In block');
    } else if (isFinalized) {
      handleEventsStatus(events);
      disableLoading();
    }
  };

  // TODO: eslint
  // eslint-disable-next-line consistent-return
  const sendMessage = async (payload: AnyJson, value: string | number = 0) => {
    if (account && metadata) {
      enableLoading();

      const { address, decodedAddress, meta } = account;
      const gasLimit = await api.program.gasSpent.handle(decodedAddress, destination, payload, value, metadata);

      const message = { destination, payload, gasLimit, value };
      api.message.submit(message, metadata);

      const { source } = meta;
      const { signer } = await web3FromSource(source);
      return api.message.signAndSend(address, { signer }, handleStatus);
    }
  };

  return sendMessage;
}
Example #10
Source File: OnLogin.tsx    From gear-js with GNU General Public License v3.0 5 votes vote down vote up
function OnLogin({ children, fallback }: Props) {
  const { account } = useAccount();

  // eslint-disable-next-line react/jsx-no-useless-fragment
  return <>{account ? children : fallback}</>;
}
Example #11
Source File: SelectAccountModal.tsx    From gear-js with GNU General Public License v3.0 5 votes vote down vote up
SelectAccountModal = (props: Props) => {
  const alert = useAlert();
  const { logout, switchAccount } = useAccount();

  const { isOpen, accounts, onClose } = props;

  const selectAccount = (account: InjectedAccountWithMeta) => {
    switchAccount(account);

    localStorage.setItem(LOCAL_STORAGE.ACCOUNT, account.address);
    localStorage.setItem(LOCAL_STORAGE.PUBLIC_KEY_RAW, GearKeyring.decodeAddress(account.address));

    onClose();

    alert.success('Account successfully changed');
  };

  const handleLogout = () => {
    logout()

    localStorage.removeItem(LOCAL_STORAGE.ACCOUNT);
    localStorage.removeItem(LOCAL_STORAGE.PUBLIC_KEY_RAW);

    onClose();
  };

  return (
    <Modal
      open={isOpen}
      title="Connect"
      content={
        accounts ? (
          <>
            <AccountList list={accounts} toggleAccount={selectAccount} />
            <Button
              aria-label="Logout"
              icon={logoutSVG}
              color="transparent"
              className={styles.logoutButton}
              onClick={handleLogout}
            />
          </>
        ) : (
          <p className={styles.message}>
            Polkadot extension was not found or disabled. Please{' '}
            <a href="https://polkadot.js.org/extension/" target="_blank" rel="noreferrer">
              install it
            </a>
          </p>
        )
      }
      handleClose={onClose}
    />
  );
}
Example #12
Source File: Wallet.tsx    From gear-js with GNU General Public License v3.0 5 votes vote down vote up
Wallet = () => {
  const { api } = useApi();
  const accounts = useAccounts();
  const { account } = useAccount();

  const [isModalOpen, setIsModalOpen] = useState(false);
  const [accountBalance, setAccountBalance] = useState('');

  useEffect(() => {
    if (account && api) {
      api.balance.findOut(account.address).then((result) => setAccountBalance(result.toHuman()));
    }
  }, [account, api]);

  useEffect(() => {
    // TODO: think how to wrap it hook
    let unsub: UnsubscribePromise | undefined;

    if (account && api) {
      unsub = api.gearEvents.subscribeToBalanceChange(account.address, (balance) => {
        setAccountBalance(balance.toHuman());
      });
    }

    return () => {
      if (unsub) {
        unsub.then((callback) => callback());
      }
    };
  }, [api, account]);

  const openModal = () => {
    setIsModalOpen(true);
  };

  const closeModal = () => {
    setIsModalOpen(false);
  };

  const balanceSectionClassName = clsx(styles.section, styles.balance);
  const accButtonClassName = clsx(
    buttonStyles.button,
    buttonStyles.normal,
    buttonStyles.secondary,
    styles.accountButton
  );

  return (
    <>
      <div className={styles.wallet}>
        {account ? (
          <>
            <div className={balanceSectionClassName}>
              <p>
                Balance: <span className={styles.balanceAmount}>{accountBalance}</span>
              </p>
            </div>
            <div className={styles.section}>
              <button type="button" className={accButtonClassName} onClick={openModal}>
                <Identicon value={account.address} size={28} theme="polkadot" className={styles.avatar} />
                {account.meta.name}
              </button>
            </div>
          </>
        ) : (
          <div>
            <Button text="Connect" color="secondary" className={styles.accountButton} onClick={openModal} />
          </div>
        )}
      </div>
      <SelectAccountModal isOpen={isModalOpen} accounts={accounts} onClose={closeModal} />
    </>
  );
}
Example #13
Source File: Offer.tsx    From gear-js with GNU General Public License v3.0 5 votes vote down vote up
function Offer({ bid, bidder, listingOwner, hash }: Props) {
  const { id } = useParams() as Params;
  const sendMessage = useMarketplaceMessage();
  const { account } = useAccount();

  const [isModalOpen, setIsModalOpen] = useState(false);

  const isOwner = account?.decodedAddress === listingOwner;
  const isSale = !!hash;

  const openModal = () => {
    setIsModalOpen(true);
  };

  const closeModal = () => {
    setIsModalOpen(false);
  };

  const accept = () => {
    const payload = {
      AcceptOffer: { nftContractId: NFT_CONTRACT_ADDRESS, tokenId: id, offerHash: hash },
    };

    sendMessage(payload).then(closeModal);
  };

  return (
    <>
      <div className={styles.offer}>
        <div className={styles.info}>
          <p className={styles.bid}>{bid}</p>
          <p className={styles.bidder}>{bidder}</p>
        </div>
        {isOwner && isSale && <Button text="Accept" size="small" onClick={openModal} />}
      </div>
      {isModalOpen && (
        <ConfirmationModal heading={`Do you agree to sell the item for ${bid}?`} close={closeModal} onSubmit={accept} />
      )}
    </>
  );
}
Example #14
Source File: Messages.tsx    From gear-js with GNU General Public License v3.0 5 votes vote down vote up
Messages = () => {
  const [searchParams, setSearchParams] = useSearchParams();
  const { account } = useAccount();

  const page = searchParams.has(URL_PARAMS.PAGE) ? Number(searchParams.get(URL_PARAMS.PAGE)) : 1;
  const query = searchParams.has(URL_PARAMS.QUERY) ? String(searchParams.get(URL_PARAMS.QUERY)) : '';

  const [messages, setMessages] = useState<MessageModel[]>([]);
  const [messagesCount, setMessagesCount] = useState(0);

  useChangeEffect(() => {
    searchParams.set(URL_PARAMS.PAGE, String(1));
    searchParams.set(URL_PARAMS.QUERY, '');
    setSearchParams(searchParams);
  }, [account]);

  useEffect(() => {
    if (account) {
      const messageParams = {
        destination: GearKeyring.decodeAddress(account.address),
        limit: INITIAL_LIMIT_BY_PAGE,
        offset: (page - 1) * INITIAL_LIMIT_BY_PAGE,
        query,
      };

      getMessages(messageParams).then(({ result }) => {
        setMessages(result.messages);
        setMessagesCount(result.count);
      });
    }
  }, [page, query, account]);

  return (
    <div className="messages">
      <div className="pagination__wrapper">
        <span className="pagination__wrapper-caption">Total results: {messagesCount || 0}</span>
        <Pagination page={page} pagesAmount={messagesCount || 1} />
      </div>
      <SearchForm placeholder="Find message by ID" />
      <MessagesList messages={messages} />
      {messagesCount > 0 && (
        <div className="pagination_bottom">
          <Pagination page={page} pagesAmount={messagesCount || 1} />
        </div>
      )}
    </div>
  );
}
Example #15
Source File: All.tsx    From gear-js with GNU General Public License v3.0 5 votes vote down vote up
All = () => {
  const [searchParams] = useSearchParams();
  const { account } = useAccount();
  const accountDecodedAddress = GearKeyring.decodeAddress(account?.address || '0x00');

  const page = searchParams.has(URL_PARAMS.PAGE) ? Number(searchParams.get(URL_PARAMS.PAGE)) : 1;
  const query = searchParams.has(URL_PARAMS.QUERY) ? String(searchParams.get(URL_PARAMS.QUERY)) : '';

  const [programs, setPrograms] = useState<ProgramModel[]>([]);
  const [programsCount, setProgramsCount] = useState(0);

  useEffect(() => {
    const programParams = { limit: INITIAL_LIMIT_BY_PAGE, offset: (page - 1) * INITIAL_LIMIT_BY_PAGE, query };

    getPrograms(programParams).then(({ result }) => {
      setPrograms(result.programs);
      setProgramsCount(result.count);
    });
  }, [page, query]);

  return (
    <div className="all-programs">
      <div className={styles.paginationWrapper}>
        <span>Total results: {programsCount || 0}</span>
        <Pagination page={page} pagesAmount={programsCount || 1} />
      </div>
      <SearchForm placeholder="Find program" />
      <ProgramsLegend />
      <div className={styles.allProgramsList}>
        {programs.map((program: ProgramModel) => (
          <UserProgram key={program.id} program={program} isMetaLinkActive={accountDecodedAddress === program.owner} />
        ))}
      </div>
      {programsCount > 0 && (
        <div className={styles.paginationBottom}>
          <Pagination page={page} pagesAmount={programsCount || 1} />
        </div>
      )}
    </div>
  );
}
Example #16
Source File: Recent.tsx    From gear-js with GNU General Public License v3.0 5 votes vote down vote up
Recent = () => {
  const [searchParams, setSearchParams] = useSearchParams();
  const { account } = useAccount();

  const page = searchParams.has(URL_PARAMS.PAGE) ? Number(searchParams.get(URL_PARAMS.PAGE)) : 1;
  const query = searchParams.has(URL_PARAMS.QUERY) ? String(searchParams.get(URL_PARAMS.QUERY)) : '';

  const [programs, setPrograms] = useState<ProgramModel[]>([]);
  const [programsCount, setProgramsCount] = useState(0);

  useChangeEffect(() => {
    searchParams.set(URL_PARAMS.PAGE, String(1));
    searchParams.set(URL_PARAMS.QUERY, '');
    setSearchParams(searchParams);
  }, [account]);

  useEffect(() => {
    if (account) {
      const params = {
        query,
        owner: GearKeyring.decodeAddress(account.address),
        limit: INITIAL_LIMIT_BY_PAGE,
        offset: (page - 1) * INITIAL_LIMIT_BY_PAGE,
      };

      getUserPrograms(params).then(({ result }) => {
        setPrograms(result.programs);
        setProgramsCount(result.count);
      });
    }
  }, [page, query, account]);

  return (
    <div className={styles.blockList}>
      <div className={styles.paginationWrapper}>
        <span>Total results: {programsCount || 0}</span>
        <Pagination page={page} pagesAmount={programsCount || 1} />
      </div>
      <SearchForm placeholder="Find program" />
      <ProgramsLegend />
      <div>
        {programs.map((program: ProgramModel) => (
          <UserProgram key={program.id} program={program} />
        ))}
      </div>
      {programsCount > 0 && (
        <div className={styles.paginationBottom}>
          <Pagination page={page} pagesAmount={programsCount || 1} />
        </div>
      )}
    </div>
  );
}
Example #17
Source File: MessageForm.tsx    From gear-js with GNU General Public License v3.0 4 votes vote down vote up
MessageForm: VFC<Props> = ({ id, metadata, replyErrorCode }) => {
  const { api } = useApi();
  const alert = useAlert();
  const { account: currentAccount } = useAccount();

  const initialValues = useRef<FormValues>({
    value: 0,
    payload: '',
    gasLimit: 20000000,
    payloadType: 'Bytes',
    destination: id,
  });

  const isReply = !!replyErrorCode;
  const isMeta = useMemo(() => metadata && Object.keys(metadata).length > 0, [metadata]);

  const handleSubmit = (values: FormValues, { resetForm }: FormikHelpers<FormValues>) => {
    if (!currentAccount) {
      alert.error(`WALLET NOT CONNECTED`);
      return;
    }

    const payload = getSubmitPayload(values.payload);
    const apiMethod = isReply ? api.reply : api.message;
    const payloadType = isMeta ? void 0 : values.payloadType;

    const message = {
      value: values.value.toString(),
      payload,
      gasLimit: values.gasLimit.toString(),
      replyToId: values.destination,
      destination: values.destination,
    };

    sendMessage(apiMethod, currentAccount, message, alert, resetForm, metadata, payloadType);
  };

  const handleCalculateGas = (values: FormValues, setFieldValue: SetFieldValue) => () => {
    const method = isReply ? 'reply' : 'handle';

    calculateGas(method, api, values, alert, metadata, null, id, replyErrorCode).then((gasLimit) =>
      setFieldValue('gasLimit', gasLimit)
    );
  };

  const payloadFormValues = useMemo(() => getPayloadFormValues(metadata?.types, metadata?.handle_input), [metadata]);

  return (
    <Formik initialValues={initialValues.current} validateOnBlur validationSchema={Schema} onSubmit={handleSubmit}>
      {({ errors, touched, values, setFieldValue }) => (
        <Form id="message-form">
          <div className="message-form--wrapper">
            <div className="message-form--col">
              <div className="message-form--info">
                <label htmlFor="destination" className="message-form__field">
                  {isReply ? 'Message Id:' : 'Destination:'}
                </label>
                <div className="message-form__field-wrapper">
                  <Field
                    id="destination"
                    name="destination"
                    type="text"
                    className={clsx(
                      'inputField',
                      errors.destination && touched.destination && 'message-form__input-error'
                    )}
                  />
                  {errors.destination && touched.destination && (
                    <div className="message-form__error">{errors.destination}</div>
                  )}
                </div>
              </div>

              <div className="message-form--info">
                <label htmlFor="payload" className="message-form__field">
                  Payload:
                </label>
                <FormPayload name="payload" values={payloadFormValues} />
              </div>

              {!isMeta && (
                <div className="message-form--info">
                  <label htmlFor="payloadType" className="message-form__field">
                    Payload type:
                  </label>
                  <PayloadType />
                </div>
              )}

              <div className="message-form--info">
                <label htmlFor="gasLimit" className="message-form__field">
                  Gas limit:
                </label>
                <div className="message-form__field-wrapper">
                  <NumberFormat
                    name="gasLimit"
                    placeholder="20,000,000"
                    value={values.gasLimit}
                    thousandSeparator
                    allowNegative={false}
                    className={clsx('inputField', errors.gasLimit && touched.gasLimit && 'message-form__input-error')}
                    onValueChange={(val) => {
                      const { floatValue } = val;
                      setFieldValue('gasLimit', floatValue);
                    }}
                  />
                  {errors.gasLimit && touched.gasLimit ? (
                    <div className="message-form__error">{errors.gasLimit}</div>
                  ) : null}
                </div>
              </div>

              <div className="message-form--info">
                <label htmlFor="value" className="message-form__field">
                  Value:
                </label>
                <div className="message-form__field-wrapper">
                  <Field
                    id="value"
                    name="value"
                    placeholder="20000"
                    type="number"
                    className={clsx('inputField', errors.value && touched.value && 'message-form__input-error')}
                  />
                  {errors.value && touched.value ? <div className="message-form__error">{errors.value}</div> : null}
                </div>
              </div>
              <div className="message-form--btns">
                <button
                  className="message-form__button"
                  type="button"
                  onClick={handleCalculateGas(values, setFieldValue)}
                >
                  Calculate Gas
                </button>
                <button className="message-form__button" type="submit">
                  <img src={MessageIllustration} alt="message" />
                  {isReply ? 'Send reply' : 'Send message'}
                </button>
              </div>
            </div>
          </div>
        </Form>
      )}
    </Formik>
  );
}
Example #18
Source File: useCodeUpload.tsx    From gear-js with GNU General Public License v3.0 4 votes vote down vote up
useCodeUpload = () => {
  const { api } = useApi();
  const alert = useAlert();
  const { account } = useAccount();

  const submit = async (file: File) => {
    const arrayBuffer = (await readFileAsync(file)) as ArrayBuffer;
    const buffer = Buffer.from(arrayBuffer);

    return api.code.submit(buffer);
  };

  const getErrorMessage = (event: Event) => {
    const { docs, method: errorMethod } = api.getExtrinsicFailedError(event);
    const formattedDocs = docs.filter(Boolean).join('. ');

    return `${errorMethod}: ${formattedDocs}`;
  };

  const uploadCode = async (file: File) => {
    if (!account) {
      alert.error('Wallet not connected');

      return;
    }

    const { address, meta } = account;

    const alertTitle = 'gear.submitCode';
    const alertId = alert.loading('SignIn', { title: alertTitle });

    try {
      const { signer } = await web3FromSource(meta.source);
      const { codeHash } = await submit(file);

      await api.code.signAndSend(address, { signer }, ({ events, status }) => {
        if (status.isReady) {
          alert.update(alertId, 'Ready');

          return;
        }

        if (status.isInBlock) {
          alert.update(alertId, 'InBlock');

          events.forEach(({ event }) => {
            const { method, section } = event;

            if (method === 'CodeSaved') {
              alert.success(<CopiedInfo title="Code hash" info={codeHash} />, {
                title: `${section}.CodeSaved`,
                timeout: 0,
              });

              return;
            }

            if (method === 'ExtrinsicFailed') {
              alert.error(getErrorMessage(event), { title: `${section}.ExtrinsicFailed` });

              return;
            }
          });

          return;
        }

        if (status.isFinalized) {
          alert.update(alertId, 'Finalized', DEFAULT_SUCCESS_OPTIONS);

          return;
        }

        if (status.isInvalid) {
          alert.update(alertId, PROGRAM_ERRORS.INVALID_TRANSACTION, DEFAULT_ERROR_OPTIONS);
        }
      });
    } catch (error) {
      alert.update(alertId, `${error}`, DEFAULT_ERROR_OPTIONS);
      console.error(error);
    }
  };

  return uploadCode;
}
Example #19
Source File: UploadForm.tsx    From gear-js with GNU General Public License v3.0 4 votes vote down vote up
UploadForm: VFC<Props> = ({ setDroppedFile, droppedFile }) => {
  const { api } = useApi();
  const alert = useAlert();
  const { account } = useAccount();

  const [fieldFromFile, setFieldFromFile] = useState<string[] | null>(null);
  const [meta, setMeta] = useState<Metadata | null>(null);
  const [metaFile, setMetaFile] = useState<string | null>(null);
  const [droppedMetaFile, setDroppedMetaFile] = useState<File>();
  const [isMetaFromFile, setIsMetaFromFile] = useState<boolean>(true);

  const handleResetForm = () => {
    setDroppedFile(null);
  };

  const handleResetMetaForm = (setValues: SetValues) => {
    setMeta(null);
    setMetaFile(null);
    setDroppedMetaFile(void 0);
    setFieldFromFile(null);
    setValues(INITIAL_VALUES, false);
  };

  const handleUploadMetaFile = (setValues: SetValues) => async (event: ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];

    if (!file) {
      return handleResetMetaForm(setValues);
    }

    try {
      if (!checkFileFormat(file)) {
        throw new Error('Wrong file format');
      }

      setDroppedMetaFile(file);

      const readedFile = (await readFileAsync(file)) as Buffer;
      const metadata: Metadata = await getWasmMetadata(readedFile);

      if (!metadata) {
        throw new Error('Failed to load metadata');
      }

      const metaBufferString = Buffer.from(new Uint8Array(readedFile)).toString('base64');
      const valuesFromFile = getMetaValues(metadata);

      setMeta(metadata);
      setMetaFile(metaBufferString);
      setFieldFromFile(Object.keys(valuesFromFile));
      setValues(
        ({ programValues }) => ({
          metaValues: valuesFromFile,
          programValues: {
            ...programValues,
            programName: metadata?.title || '',
          },
        }),
        false
      );
    } catch (error) {
      alert.error(`${error}`);
    }
  };

  const handleSubmitForm = (values: FormValues) => {
    if (!account) {
      alert.error(`Wallet not connected`);
      return;
    }

    const { value, payload, gasLimit, programName } = values.programValues;

    const programOptions: UploadProgramModel = {
      meta: void 0,
      value,
      title: '',
      gasLimit,
      programName,
      initPayload: meta ? getSubmitPayload(payload) : payload,
    };

    if (meta) {
      programOptions.meta = isMetaFromFile ? meta : values.metaValues;
    }

    UploadProgram(api, account, droppedFile, programOptions, metaFile, alert, handleResetForm).catch(() => {
      alert.error(`Invalid JSON format`);
    });
  };

  const handleCalculateGas = async (values: ProgramValues, setFieldValue: SetFieldValue) => {
    const fileBuffer = (await readFileAsync(droppedFile)) as ArrayBuffer;
    const code = Buffer.from(new Uint8Array(fileBuffer));

    calculateGas('init', api, values, alert, meta, code).then((gasLimit) =>
      setFieldValue('programValues.gasLimit', gasLimit)
    );
  };

  const payloadFormValues = useMemo(() => getPayloadFormValues(meta?.types, meta?.init_input), [meta]);

  const metaFields = isMetaFromFile ? fieldFromFile : META_FIELDS;
  const isUploadAvailable = !(account && parseInt(account.balance.value, 10) > 0);

  return (
    <Box className={styles.uploadFormWrapper}>
      <h3 className={styles.heading}>UPLOAD NEW PROGRAM</h3>
      <Formik initialValues={INITIAL_VALUES} validateOnBlur validationSchema={Schema} onSubmit={handleSubmitForm}>
        {({ values, setFieldValue, setValues }) => (
          <Form className={styles.uploadForm}>
            <div className={styles.formContent}>
              <div className={styles.program}>
                <div className={styles.fieldWrapper}>
                  <span className={styles.caption}>File:</span>
                  <span className={styles.fileName}>{droppedFile.name}</span>
                </div>
                <FormInput
                  name="programValues.programName"
                  label="Name:"
                  placeholder="Name"
                  className={styles.formField}
                />
                <FormNumberFormat
                  name="programValues.gasLimit"
                  label="Gas limit:"
                  placeholder="20,000,000"
                  thousandSeparator
                  allowNegative={false}
                  className={styles.formField}
                />
                <FormInput
                  type="number"
                  name="programValues.value"
                  label="Initial value:"
                  placeholder="0"
                  className={styles.formField}
                />
                <div className={styles.fieldWrapper}>
                  <label htmlFor="programValues.payload" className={clsx(styles.caption, styles.top)}>
                    Initial payload:
                  </label>
                  <FormPayload name="programValues.payload" values={payloadFormValues} />
                </div>
              </div>

              <fieldset className={styles.meta}>
                <legend className={styles.metaLegend}>Metadata:</legend>
                <MetaSwitch isMetaFromFile={isMetaFromFile} onChange={setIsMetaFromFile} className={styles.formField} />
                {isMetaFromFile && (
                  <div className={styles.fieldWrapper}>
                    <FileInput
                      label="Metadata file:"
                      value={droppedMetaFile}
                      className={clsx(styles.formField, styles.fileInput)}
                      onChange={handleUploadMetaFile(setValues)}
                    />
                  </div>
                )}
                {metaFields?.map((field) => {
                  const FormField = field === 'types' ? FormTextarea : FormInput;

                  return (
                    <FormField
                      key={field}
                      name={`metaValues.${field}`}
                      label={`${field}:`}
                      disabled={isMetaFromFile}
                      className={styles.formField}
                    />
                  );
                })}
              </fieldset>
            </div>

            <div className={styles.buttons}>
              <Button type="submit" text="Upload program" disabled={isUploadAvailable} />
              <Button
                text="Calculate Gas"
                onClick={() => {
                  handleCalculateGas(values.programValues, setFieldValue);
                }}
              />
              <Button
                type="submit"
                text="Cancel upload"
                color="transparent"
                aria-label="closeUploadForm"
                onClick={handleResetForm}
              />
            </div>
          </Form>
        )}
      </Formik>
    </Box>
  );
}
Example #20
Source File: UploadMetaForm.tsx    From gear-js with GNU General Public License v3.0 4 votes vote down vote up
UploadMetaForm = ({ programId, programName }: Props) => {
  const alert = useAlert();
  const { account } = useAccount();

  const [isFileUpload, setFileUpload] = useState(true);

  const [meta, setMeta] = useState<Metadata | null>(null);
  const [metaFile, setMetaFile] = useState<File | null>(null);
  const [metaBuffer, setMetaBuffer] = useState<string | null>(null);
  const [fieldsFromFile, setFieldFromFile] = useState<string[] | null>(null);
  const [initialValues, setInitialValues] = useState<FormValues>({
    name: programName,
    ...INITIAL_VALUES,
  });

  const handleUploadMetaFile = async (file: File) => {
    try {
      const fileBuffer = (await readFileAsync(file)) as Buffer;
      const currentMetaWasm = await getWasmMetadata(fileBuffer);

      if (!currentMetaWasm) {
        return;
      }

      const valuesFromFile = getMetaValues(currentMetaWasm);
      const currentMetaBuffer = Buffer.from(new Uint8Array(fileBuffer)).toString('base64');

      setMeta(currentMetaWasm);
      setMetaBuffer(currentMetaBuffer);
      setFieldFromFile(Object.keys(valuesFromFile));
      setInitialValues({
        ...INITIAL_VALUES,
        ...valuesFromFile,
        name: currentMetaWasm.title ?? programName,
      });
    } catch (error) {
      alert.error(`${error}`);
    } finally {
      setMetaFile(file);
    }
  };

  const resetForm = () => {
    setMetaFile(null);
    setMeta(null);
    setMetaBuffer(null);
    setFieldFromFile(null);
    setInitialValues({
      name: programName,
      ...INITIAL_VALUES,
    });
  };

  const handleSubmit = (values: FormValues, actions: FormikHelpers<FormValues>) => {
    if (!account) {
      alert.error(`WALLET NOT CONNECTED`);
      return;
    }

    const { name, ...formMeta } = values;

    if (isFileUpload) {
      if (meta) {
        addMetadata(meta, metaBuffer, account, programId, name, alert);
      } else {
        alert.error(`ERROR: metadata not loaded`);
      }
    } else {
      addMetadata(formMeta, null, account, programId, name, alert);
    }

    actions.setSubmitting(false);
    resetForm();
  };

  const fields = isFileUpload ? fieldsFromFile : META_FIELDS;

  return (
    <Formik
      initialValues={initialValues}
      validateOnBlur
      validationSchema={Schema}
      enableReinitialize
      onSubmit={handleSubmit}
    >
      {({ isValid, isSubmitting }: FormikProps<FormValues>) => {
        const emptyFile = isFileUpload && !meta;
        const disabledBtn = emptyFile || !isValid || isSubmitting;

        return (
          <Form className={styles.uploadMetaForm}>
            <MetaSwitch isMetaFromFile={isFileUpload} onChange={setFileUpload} className={styles.formField} />
            <FormInput name="name" label="Program name:" className={styles.formField} />
            {fields?.map((field) => {
              const MetaField = field === 'types' ? FormTextarea : FormInput;

              return (
                <MetaField
                  key={field}
                  name={field}
                  label={`${field}:`}
                  disabled={isFileUpload}
                  className={styles.formField}
                />
              );
            })}
            {isFileUpload && (
              <MetaFile
                file={metaFile}
                className={styles.formField}
                onUpload={handleUploadMetaFile}
                onDelete={resetForm}
              />
            )}
            <div className={styles.formBtnWrapper}>
              <Button type="submit" text="Upload metadata" className={styles.formSubmitBtn} disabled={disabledBtn} />
            </div>
          </Form>
        );
      }}
    </Formik>
  );
}
Example #21
Source File: ProgramSwitch.tsx    From gear-js with GNU General Public License v3.0 4 votes vote down vote up
ProgramSwitch: VFC<Props> = ({ pageType }) => {
  const { api } = useApi();
  const alert = useAlert();
  const { account: currentAccount } = useAccount();

  const [captchaToken, setCaptchaToken] = useState('');
  const captchaRef = useRef<HCaptcha>(null);

  const handleTransferBalance = async () => {
    try {
      if (!currentAccount) {
        throw new Error(`WALLET NOT CONNECTED`);
      }

      const apiRequest = new ServerRPCRequestService();
      const response = await apiRequest.callRPC(RPC_METHODS.GET_TEST_BALANCE, {
        address: `${currentAccount.address}`,
        token: captchaToken,
      });

      if (response.error) {
        alert.error(`${response.error.message}`);
      }
    } catch (error) {
      alert.error(`${error}`);
    }
  };

  useEffect(() => {
    if (captchaToken) {
      handleTransferBalance();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [captchaToken]);

  const handleTestBalanceClick = () => {
    if (captchaToken) {
      handleTransferBalance();
    } else {
      captchaRef.current?.execute();
    }
  };

  const handleTransferBalanceFromAlice = () => {
    try {
      if (!currentAccount) {
        throw new Error(`WALLET NOT CONNECTED`);
      }

      if (api) {
        api.balance.transferFromAlice(currentAccount.address, GEAR_BALANCE_TRANSFER_VALUE);
      }
    } catch (error) {
      alert.error(`${error}`);
    }
  };

  return (
    <div className="switch-block">
      <div className="switch-block--wrapper">
        <div className="switch-buttons">
          <Link
            to={routes.main}
            className={clsx(
              'switch-buttons__item',
              pageType === SWITCH_PAGE_TYPES.UPLOAD_PROGRAM && 'switch-buttons__item--active'
            )}
          >
            Upload program
          </Link>
          <Link
            to={routes.uploadedPrograms}
            className={clsx(
              'switch-buttons__item',
              pageType === SWITCH_PAGE_TYPES.UPLOADED_PROGRAMS && 'switch-buttons__item--active'
            )}
          >
            My programs
          </Link>
          <Link
            to={routes.allPrograms}
            className={clsx(
              'switch-buttons__item',
              pageType === SWITCH_PAGE_TYPES.ALL_PROGRAMS && 'switch-buttons__item--active'
            )}
          >
            All programs
          </Link>
          <Link
            to={routes.messages}
            className={clsx(
              'switch-buttons__item',
              pageType === SWITCH_PAGE_TYPES.ALL_MESSAGES && 'switch-buttons__item--active'
            )}
          >
            Messages
          </Link>
        </div>
        {currentAccount && (
          <>
            <Button
              className="test-balance-button"
              text="Get test balance"
              onClick={isDevChain() ? handleTransferBalanceFromAlice : handleTestBalanceClick}
            />
            <HCaptcha
              sitekey={HCAPTCHA_SITE_KEY}
              onVerify={setCaptchaToken}
              onExpire={() => setCaptchaToken('')}
              ref={captchaRef}
              theme="dark"
              size="invisible"
            />
          </>
        )}
      </div>
      <BlocksSummary />
    </div>
  );
}