//NOTE repurposed from /views/Activate/PassportTransfer,
// with a lot of logic from /lib/useInviter
//TODO display "you may now _close this window_" cta after calling a flow cb
//TODO make sure we also call failure cb if we get closed prematurely
import React, { useState, useCallback } from 'react';
import { Grid, Text, ErrorText, H4 } from 'indigo-react';
import cn from 'classnames';
import ob from 'urbit-ob';
import { toBN } from 'web3-utils';
import { useNetwork } from 'store/network';
import { useWallet } from 'store/wallet';
import * as wg from 'lib/walletgen';
import * as tank from 'lib/tank';
import { GAS_LIMITS } from 'lib/constants';
import {
signTransaction,
sendSignedTransaction,
waitForTransactionConfirm,
hexify,
} from 'lib/txn';
import useGasPrice from 'lib/useGasPrice';
import useLifecycle from 'lib/useLifecycle';
import useBlockWindowClose from 'lib/useBlockWindowClose';
import { useFlowCommand } from 'lib/flowCommand';
import { hasReceived, sendMail } from 'lib/inviteMail';
import { safeToWei } from 'lib/lib';
import LoadingBar from 'components/LoadingBar';
import { RestartButton } from 'components/Buttons';
import NeedFundsNotice from 'components/NeedFundsNotice';
import * as az from 'azimuth-js';
const labelForProgress = progress => {
if (progress <= 0) {
return 'Checking sanity...';
} else if (progress <= 0.2) {
return 'Generating invite...';
} else if (progress <= 0.3) {
return 'Building transaction...';
} else if (progress <= 0.4) {
return 'Signing transaction...';
} else if (progress <= 0.5) {
return 'Funding transaction...';
} else if (progress <= 0.7) {
return 'Sending transaction...';
} else if (progress <= 0.9) {
return 'Sending invite email...';
} else {
return 'Done';
}
};
export default function JustInvite({ className, resetActivateRouter }) {
const { wallet, walletType, walletHdPath, authToken } = useWallet();
const flow = useFlowCommand();
const { web3, contracts, networkType } = useNetwork();
const { gasPrice } = useGasPrice();
const [generalError, setGeneralError] = useState();
const [progress, setProgress] = useState(0);
const [needFunds, setNeedFunds] = useState(null);
useBlockWindowClose();
// useEffect(() => {
// switch (type) {
// case 'progress':
// return setProcesss(state);
// case 'askFunding':
// return setNeedFunds(value);
// case 'gotFunding':
// return setNeedFunds(false);
// default:
// console.error(`Unknown update: ${type}`);
// }
// }, [progress]);
const sendInvite = useCallback(async () => {
setGeneralError(false);
try {
const _contracts = contracts.getOrElse(null);
const _web3 = web3.getOrElse(null);
const _wallet = wallet.getOrElse(null);
const _authToken = authToken.getOrElse(null);
if (!_contracts || !_web3 || !_wallet || !_authToken) {
// not using need because we want a custom error
throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
}
const _as = ob.patp2dec(flow.as);
const _ship = ob.patp2dec(flow.ship);
const _email = flow.email;
console.log('xx checking email');
// check if email not invited before
if (await hasReceived(_email)) {
throw new Error(
`This email (${_email}) has already received an invite.`
);
}
setProgress(0.1);
console.log('xx checking owner', _wallet.address, _as);
// check if logged in with flow.as's owner
//TODO we could move this check to the login screen
if (!(await az.azimuth.isOwner(_contracts, _as, _wallet.address))) {
throw new Error(`You are not logged in as ${flow.as}'s owner.'`);
}
setProgress(0.2);
// generate invite code + address
const { ticket, owner } = await wg.generateTemporaryDeterministicWallet(
_ship,
_authToken
);
setProgress(0.3);
// generate invite tx
const inviteTx = az.delegatedSending.sendPoint(
_contracts,
_as,
_ship,
owner.keys.address
);
setProgress(0.4);
// sign invite tx
const signedTx = await signTransaction({
wallet: _wallet,
walletType,
walletHdPath,
networkType,
chainId: await _web3.eth.net.getId(),
nonce: await _web3.eth.getTransactionCount(_wallet.address),
txn: inviteTx,
gasPrice: gasPrice.toFixed(), // expects string gwei
gasLimit: GAS_LIMITS.GIFT_PLANET.toFixed(),
});
const rawTx = hexify(signedTx.serialize());
setProgress(0.5);
// ensure funds
const totalCost = toBN(GAS_LIMITS.GIFT_PLANET).mul(toBN(gasPrice));
const tankWasUsed = await tank.ensureFundsFor(
_web3,
_as,
_wallet.address,
walletType,
safeToWei(totalCost, 'gwei'),
[rawTx],
(address, minBalance, balance) =>
setNeedFunds({ address, minBalance, balance }),
() => setNeedFunds(undefined)
);
setProgress(0.7);
// send and await transaction
const txHash = await sendSignedTransaction(_web3, signedTx, tankWasUsed);
await waitForTransactionConfirm(_web3, txHash);
setProgress(0.9);
// send invite email
//NOTE we could chuck a "pls join group-name" message in here
await sendMail(_email, ticket, flow.as, '', rawTx);
setProgress(1);
} catch (error) {
// some generic error
setGeneralError(error);
}
}, [
wallet,
web3,
contracts,
networkType,
authToken,
flow,
gasPrice,
walletHdPath,
walletType,
]);
useLifecycle(() => {
sendInvite();
});
const goToRestart = useCallback(() => {
sendInvite();
}, [sendInvite]);
const renderAdditionalInfo = () => {
if (generalError) {
console.log(generalError);
let msg =
typeof generalError === 'string'
? generalError
: generalError.message || 'Something went wrong!';
return (
<>
{msg}
Restart
>
);
}
if (needFunds) {
return (
);
}
// if (progress < 1.0) {
// return (
//
// Never give your Master Ticket to anyone
//
// );
// }
return null;
};
return (
{labelForProgress(progress)}
This process can take up to 5 minutes to complete. Don't close
this page until the process is complete.
{renderAdditionalInfo()}
);
}