cyb/src/features/studio/ActionBar.tsx

import { useCallback, useEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { ActionBar } from 'src/components';
import { PATTERN_IPFS_HASH } from 'src/constants/patterns';
import { useBackend } from 'src/contexts/backend/backend';
import { useQueryClient } from 'src/contexts/queryClient';
import { useSigningClient } from 'src/contexts/signerClient';
import useWaitForTransaction from 'src/hooks/useWaitForTransaction';
import { InputMemo } from 'src/pages/teleport/components/Inputs';
import { routes } from 'src/routes';
import { sendCyberlinkArray } from 'src/services/neuron/neuronApi';
import { addIfpsMessageOrCid } from 'src/utils/ipfs/helpers';
import useAdviserTexts from '../adviser/useAdviserTexts';
import { KeywordsItem, useStudioContext } from './studio.context';
import { friendlyErrorMessage } from 'src/utils/errorMessages';
import { checkLoopLinks, mapLinks, reduceLoopKeywords } from './utils/utils';

// function execute (content: any[]) {

//   // const addToIPFS = useAddToIPFS([content])
//   const {isLoading, error} = useExecutionWithWaitAndAdviser(execute);

//   // src/features/cyberlinks/hooks/useCyberlink.ts
//   // useCyberlinks(cyberlinks[])

//   const {setAdviser} = useAdviserTexts({});

//   function execute2 () {

//     // check existing links
//     // setAdviser()

//     await addToIPFS.execute();

//     // useCyberlinks

//   }

//   return {
//     isReady,
//     iaLoading,
//     error
//   }

// }

function ActionBarContainer() {
  const { signer, signingClient } = useSigningClient();
  const { isIpfsInitialized, ipfsApi, senseApi, isDbInitialized } = useBackend();
  const queryClient = useQueryClient();
  const navigate = useNavigate();

  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string>();

  const [newKeywords, setNewKeywords] = useState<
    | {
        value: string;
        fileName?: string;
      }
    | undefined
  >();

  const {
    currentMarkdown,
    keywordsFrom,
    keywordsTo,
    setStateActionBar,
    stateActionBar,
    addKeywords,
  } = useStudioContext();

  const [tx, setTx] = useState({
    hash: '',
    onSuccess: () => {},
  });

  useWaitForTransaction({
    hash: tx.hash,
    onSuccess: tx.onSuccess,
  });

  const { setAdviser } = useAdviserTexts({
    isLoading: loading,
    loadingText: 'transaction pending...',
    error,
  });

  const addNewKeywords = useCallback(async () => {
    if (!newKeywords?.value || !ipfsApi) {
      return;
    }

    const { value, fileName } = newKeywords;

    const newItem: KeywordsItem[] = [];

    if (value.match(PATTERN_IPFS_HASH)) {
      newItem.push({ text: fileName || value, cid: value });
    } else {
      const arrValue = value.split(',');

      for (let index = 0; index < arrValue.length; index++) {
        const item = arrValue[index];
        // eslint-disable-next-line no-await-in-loop
        const itemCid = await addIfpsMessageOrCid(item, { ipfsApi });
        newItem.push({ text: item, cid: itemCid });
      }
    }

    addKeywords(stateActionBar === 'keywords-from' ? 'from' : 'to', newItem);

    setNewKeywords(undefined);
    setStateActionBar('link');
  }, [newKeywords, ipfsApi, addKeywords, stateActionBar, setStateActionBar]);

  useEffect(() => {
    setError(undefined);
  }, []);

  const createCyberlinkTx = async () => {
    if (!signer || !signingClient || !senseApi || !isDbInitialized) {
      return;
    }

    const [{ address }] = await signer.getAccounts();

    // Check V (millivolt) balance โ€” required for cyberlinks
    if (queryClient) {
      try {
        const balance = await queryClient.getBalance(address, 'millivolt');
        if (!balance || BigInt(balance.amount) === 0n) {
          setError('You need V (volts) to create cyberlinks. Go to Energy to get some.');
          return;
        }
      } catch {
        // If balance check fails, proceed anyway โ€” tx will fail with a clear error
      }
    }

    setAdviser('preparing content...');
    setLoading(true);

    const currentMarkdownCid = await addIfpsMessageOrCid(currentMarkdown, {
      ipfsApi,
    });

    const links = mapLinks(currentMarkdownCid, {
      from: keywordsFrom,
      to: keywordsTo,
    });

    const { uniqueLinks, loopLink } = await checkLoopLinks(links);

    if (loopLink.length) {
      setAdviser(
        <>
          Links with these keywords have already been created: <br />
          {reduceLoopKeywords(loopLink, [...keywordsFrom, ...keywordsTo]).join(', ')}
        </>,
        'yellow'
      );
    }

    if (!uniqueLinks.length) {
      setTimeout(() => {
        setError('try adding more unique keywords');
      }, 5000);
      return;
    }

    setLoading(true);
    setAdviser('signing and broadcasting transaction...');

    await sendCyberlinkArray(address, uniqueLinks, { signingClient, senseApi })
      .then((txHash) => {
        setAdviser('waiting for transaction confirmation...');
        setTx({
          hash: txHash,
          onSuccess: () => {
            setLoading(false);
            setAdviser('link created successfully!', 'green');
            setTimeout(() => navigate(routes.ipfs.getLink(currentMarkdownCid)), 2000);
          },
        });
      })
      .catch((e) => {
        setError(friendlyErrorMessage(e?.message || e));
        console.error(e);
        setLoading(false);
      });
  };

  const isDisabledLink = useMemo(() => {
    const isKeywords = ![...keywordsFrom, ...keywordsTo].length;

    return isKeywords || loading || !currentMarkdown.length;
  }, [currentMarkdown, keywordsFrom, keywordsTo, loading]);

  if (!isIpfsInitialized) {
    return <ActionBar>node is loading...</ActionBar>;
  }

  if (stateActionBar === 'link') {
    return (
      <ActionBar
        button={{
          text: 'publish',
          disabled: isDisabledLink,
          onClick: createCyberlinkTx,
          pending: loading,
        }}
      />
    );
  }

  if (stateActionBar === 'keywords-from' || stateActionBar === 'keywords-to') {
    const textBtn = `add ${stateActionBar === 'keywords-from' ? 'incoming' : 'outcoming'} link(s)`;
    return (
      <ActionBar
        button={{
          text: textBtn,
          onClick: addNewKeywords,
          disabled: !newKeywords?.value.length,
        }}
        onClickBack={() => setStateActionBar('link')}
      >
        <InputMemo
          title="type keywords"
          value={newKeywords?.value || ''}
          onChangeValue={(value, fileName) => setNewKeywords({ value, fileName })}
        />
      </ActionBar>
    );
  }

  return null;
}

export default ActionBarContainer;

Synonyms

cyb/src/containers/mint/actionBar.tsx
cyb/src/layouts/ui/ActionBar.tsx
cyb/src/containers/warp/ActionBar.tsx
pussy-ts/src/containers/warp/ActionBar.tsx
pussy-ts/src/containers/governance/actionBar.tsx
pussy-ts/src/containers/portal/citizenship/ActionBar.tsx
cyb/src/pages/teleport/relayer/ActionBar.tsx
cyb/src/containers/portal/citizenship/ActionBar.tsx
pussy-ts/src/containers/energy/component/actionBar.tsx
cyb/src/containers/energy/component/actionBar.tsx
cyb/src/pages/Keys/ActionBar/actionBar.tsx
pussy-ts/src/pages/Keys/ActionBar/actionBar.tsx
pussy-ts/src/pages/teleport/relayer/ActionBar.tsx
pussy-ts/src/pages/robot/_refactor/account/actionBar.tsx
cyb/src/features/sense/ui/ActionBar/ActionBar.tsx
cyb/src/pages/robot/_refactor/account/actionBar.tsx
pussy-ts/src/features/sense/ui/ActionBar/ActionBar.tsx

Neighbours