Skip to content

Commit

Permalink
[launcher] speed up the setup wallet process (#3846)
Browse files Browse the repository at this point in the history
+ remove gettingVSPInfo step from the setup view  process
+ fix loading status on process manages/unmanaged views
 + cleanup process managed and unmanaged tickets
+ add tests
  • Loading branch information
bgptr authored and alexlyp committed Apr 6, 2023
1 parent 23a2bb3 commit a84bd29
Show file tree
Hide file tree
Showing 13 changed files with 913 additions and 209 deletions.
2 changes: 1 addition & 1 deletion app/actions/ClientActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const startWalletServicesTrigger = () => (dispatch, getState) =>
if (privacyEnabled) {
dispatch(getAccountMixerServiceAttempt());
}
dispatch(discoverAvailableVSPs());
await dispatch(discoverAvailableVSPs());
await dispatch(getNextAddressAttempt(0));
await dispatch(getPeerInfo());
await dispatch(getTicketPriceAttempt());
Expand Down
86 changes: 41 additions & 45 deletions app/actions/VSPActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,43 +294,46 @@ export const GETVSPSPUBKEYS_SUCCESS = "GETVSPSPUBKEYS_SUCCESS";
export const GETVSPSPUBKEYS_FAILED = "GETVSPSPUBKEYS_FAILED";

// getVSPsPubkeys gets all available vsps and its pubkeys, so we can store it.
export const getVSPsPubkeys = () => async (dispatch) => {
try {
export const getVSPsPubkeys = () => (dispatch) =>
new Promise((resolve, reject) => {
dispatch({ type: GETVSPSPUBKEYS_ATTEMPT });
const vsps = await dispatch(discoverAvailableVSPs());
if (!isArray(vsps)) {
throw new Error("INVALID_VSPS");
}
await Promise.all(
vsps.map((vsp) => {
return new Promise((resolve) =>
dispatch(getVSPInfo(vsp.host))
.then(({ pubkey }) => {
if (pubkey) {
vsp.pubkey = pubkey;
resolve(vsp);
} else {
resolve(null);
}
})
.catch(() => {
resolve(null);
// Skip to the next vsp.
})
);
})
).then((result) => {
const availableVSPsPubkeys = result.filter((vsp) => !!vsp?.pubkey);
dispatch({
type: GETVSPSPUBKEYS_SUCCESS,
availableVSPsPubkeys
dispatch(discoverAvailableVSPs()).then((vsps) => {
if (!isArray(vsps)) {
dispatch({
type: GETVSPSPUBKEYS_FAILED,
error: new Error("INVALID_VSPS")
});
return reject("INVALID_VSPS");
}

Promise.all(
vsps.map((vsp) => {
return new Promise((resolveVspInfo) =>
dispatch(getVSPInfo(vsp.host))
.then(({ pubkey }) => {
if (pubkey) {
vsp.pubkey = pubkey;
resolveVspInfo(vsp);
} else {
resolveVspInfo(null);
}
})
.catch(() => {
resolveVspInfo(null);
// Skip to the next vsp.
})
);
})
).then((result) => {
const availableVSPsPubkeys = result.filter((vsp) => !!vsp?.pubkey);
dispatch({
type: GETVSPSPUBKEYS_SUCCESS,
availableVSPsPubkeys
});
resolve(availableVSPsPubkeys);
});
return availableVSPsPubkeys;
});
} catch (error) {
dispatch({ type: GETVSPSPUBKEYS_FAILED, error });
}
};
});

export const PROCESSMANAGEDTICKETS_ATTEMPT = "PROCESSMANAGEDTICKETS_ATTEMPT";
export const PROCESSMANAGEDTICKETS_SUCCESS = "PROCESSMANAGEDTICKETS_SUCCESS";
Expand All @@ -340,14 +343,14 @@ export const PROCESSMANAGEDTICKETS_FAILED = "PROCESSMANAGEDTICKETS_FAILED";
// synced, and sync them.
export const processManagedTickets =
(passphrase) => async (dispatch, getState) => {
dispatch({ type: PROCESSMANAGEDTICKETS_ATTEMPT });
const walletService = sel.walletService(getState());
let availableVSPsPubkeys = sel.getAvailableVSPsPubkeys(getState());

if (!availableVSPsPubkeys) {
availableVSPsPubkeys = await dispatch(getVSPsPubkeys());
}
try {
dispatch({ type: PROCESSMANAGEDTICKETS_ATTEMPT });
if (!availableVSPsPubkeys) {
availableVSPsPubkeys = await dispatch(getVSPsPubkeys());
}
let feeAccount, changeAccount;
const mixedAccount = sel.getMixedAccount(getState());
if (mixedAccount) {
Expand Down Expand Up @@ -386,13 +389,6 @@ export const processManagedTickets =
dispatch({ type: PROCESSMANAGEDTICKETS_SUCCESS });
} catch (error) {
dispatch({ type: PROCESSMANAGEDTICKETS_FAILED, error });
if (
String(error).indexOf(
"wallet.Unlock: invalid passphrase:: secretkey.DeriveKey"
) > 0
) {
throw "Invalid private passphrase, please try again.";
}
throw error;
}
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,65 +1,60 @@
import { useState, useEffect } from "react";
import { Subtitle } from "shared";
import { FormattedMessage as T } from "react-intl";
import { PassphraseModalButton, InvisibleButton } from "buttons";
import styles from "./ProcessUnmanagedTickets.module.css";
import { VSPSelect } from "inputs";
import { useDaemonStartup } from "hooks";

export default ({
cancel,
send,
onProcessTickets,
title,
description,
noVspSelection,
error,
isProcessingManaged
}) => {
const [isValid, setIsValid] = useState(false);
const [vsp, setVSP] = useState(null);
export default ({ cancel, send, error }) => {
const { onProcessManagedTickets, isProcessingManaged } = useDaemonStartup();

const onSubmitContinue = (passphrase) => {
// send a continue so we can go to the loading state
onProcessTickets(passphrase)
onProcessManagedTickets(passphrase)
.then(() => send({ type: "CONTINUE" }))
.catch((error) => {
send({ type: "ERROR", error });
});
return;
};

useEffect(() => {
if (noVspSelection) {
setIsValid(true);
return;
}
if (vsp) {
setIsValid(true);
}
}, [vsp, noVspSelection]);

return (
<div className={styles.content}>
<Subtitle className={styles.subtitle} title={title} />
<div className={styles.description}>{description}</div>
{!noVspSelection && (
<VSPSelect className={styles.vspSelect} {...{ onChange: setVSP }} />
)}
<Subtitle
className={styles.subtitle}
title={
<T
id="getstarted.processManagedTickets.title"
m="Process Managed Tickets"
/>
}
/>
<div className={styles.description}>
{
<T
id="getstarted.processManagedTickets.description"
m={`Your wallet appears to have live tickets. Processing managed
tickets confirms with the VSPs that all of your submitted tickets
are currently known and paid for by the VSPs. If you've already
confirmed your tickets then you may skip this step.`}
/>
}
</div>
{error && <div className="error">{error}</div>}
<div className={styles.buttonWrapper}>
<PassphraseModalButton
modalTitle={<T id="process.mangedTickets.title" m="Passphrase" />}
modalClassName={styles.passphraseModal}
onSubmit={onSubmitContinue}
buttonLabel={<T id="process.mangedTickets.button" m="Continue" />}
disabled={!isValid || isProcessingManaged}
disabled={isProcessingManaged}
loading={isProcessingManaged}
/>
{!isProcessingManaged && (
<InvisibleButton className={styles.skipButton} onClick={cancel}>
<T id="process.mangedTickets.button.skip" m="Skip" />
</InvisibleButton>
)}
<InvisibleButton
className={styles.skipButton}
onClick={cancel}
disabled={isProcessingManaged}>
<T id="process.mangedTickets.button.skip" m="Skip" />
</InvisibleButton>
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./ProcessManagedTickets";
Original file line number Diff line number Diff line change
@@ -1,67 +1,60 @@
import { useState, useEffect } from "react";
import { Subtitle } from "shared";
import { FormattedMessage as T } from "react-intl";
import { PassphraseModalButton, InvisibleButton } from "buttons";
import styles from "./ProcessUnmanagedTickets.module.css";
import { VSPSelect } from "inputs";
import { useProcessUnmanagedTickets } from "./hooks";

export default ({
cancel,
send,
onProcessTickets,
title,
description,
noVspSelection,
isProcessingUnmanaged,
error,
availableVSPs
}) => {
const [isValid, setIsValid] = useState(false);
const [vsp, setVSP] = useState(null);

const onSubmitContinue = (passphrase) => {
onProcessTickets(passphrase, vsp.host, vsp.pubkey)
.then(() => send({ type: "CONTINUE" }))
.catch((error) => {
send({ type: "ERROR", error });
});
};

useEffect(() => {
if (noVspSelection) {
setIsValid(true);
return;
}
if (vsp) {
setIsValid(true);
}
}, [vsp, noVspSelection]);
export default ({ cancel, send, error }) => {
const {
isProcessingUnmanaged,
availableVSPs,
vsp,
setVSP,
onSubmitContinue
} = useProcessUnmanagedTickets({ send });

return (
<div className={styles.content}>
<Subtitle className={styles.subtitle} title={title} />
<div className={styles.description}>{description}</div>
{!noVspSelection && (
<VSPSelect
className={styles.vspSelect}
{...{ onChange: setVSP, options: availableVSPs }}
/>
)}
<Subtitle
className={styles.subtitle}
title={
<T
id="getstarted.processUnmangedTickets.title"
m="Process Unmanaged Tickets"
/>
}
/>
<div className={styles.description}>
{
<T
id="getstarted.processUnmangedTickets.description"
m={`Looks like you have vsp ticket with unprocessed fee.
If they are picked to vote and they are not linked with a vsp,
they may miss, if you are not properly dealing with solo vote.`}
/>
}
</div>
<VSPSelect
className={styles.vspSelect}
{...{ onChange: setVSP, options: availableVSPs }}
/>
{error && <div className="error">{error}</div>}
<div className={styles.buttonWrapper}>
<PassphraseModalButton
modalTitle={<T id="process.unmangedTickets.title" m="Passphrase" />}
modalClassName={styles.passphraseModal}
onSubmit={onSubmitContinue}
buttonLabel={<T id="process.unmangedTickets.button" m="Continue" />}
disabled={!isValid || isProcessingUnmanaged}
disabled={!vsp || isProcessingUnmanaged}
loading={isProcessingUnmanaged}
/>
{!isProcessingUnmanaged && (
<InvisibleButton className={styles.skipButton} onClick={cancel}>
<T id="process.unmanagedTickets.button.skip" m="Skip" />
</InvisibleButton>
)}
<InvisibleButton
className={styles.skipButton}
onClick={cancel}
disabled={isProcessingUnmanaged}>
<T id="process.unmanagedTickets.button.skip" m="Skip" />
</InvisibleButton>
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,13 @@
}

.buttonWrapper {
margin-top: 10px;
margin-top: 2rem;
}

.buttonWrapper .skipButton {
margin-left: 1rem;
}

.description {
margin-bottom: 1rem;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useState } from "react";
import { useSettings } from "hooks";
import { useSelector } from "react-redux";
import { useDaemonStartup } from "hooks";
import { getAvailableVSPs } from "selectors";

export const useProcessUnmanagedTickets = ({ send }) => {
const { onProcessUnmanagedTickets, isProcessingUnmanaged } =
useDaemonStartup();
const { isVSPListingEnabled } = useSettings();
const availableVSPs = isVSPListingEnabled
? useSelector(getAvailableVSPs)
: [];
const [vsp, setVSP] = useState(null);

const onSubmitContinue = (passphrase) => {
onProcessUnmanagedTickets(passphrase, vsp.host, vsp.pubkey)
.then(() => send({ type: "CONTINUE" }))
.catch((error) => {
send({ type: "ERROR", error });
});
};

return {
isProcessingUnmanaged,
availableVSPs,
vsp,
setVSP,
onSubmitContinue
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./ProcessUnmanagedTickets";
Loading

0 comments on commit a84bd29

Please sign in to comment.