Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance the management of 'confirm' button state in components that use TokenSelector & update copy #3151

Merged
merged 6 commits into from
Feb 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/modules/core/components/TokenEditDialog/TokenEditDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,11 @@ const TokenEditDialog = ({
const [tokenSelectorHasError, setTokenSelectorHasError] = useState<boolean>(
false,
);
const [isLoadingAddress, setisLoadingAddress] = useState<boolean>(false);

const handleTokenSelect = (token: OneToken) => {
const handleTokenSelect = (checkingAddress: boolean, token: OneToken) => {
setTokenData(token);
setisLoadingAddress(checkingAddress);
};

const handleTokenSelectError = (hasError: boolean) => {
Expand Down Expand Up @@ -184,8 +186,10 @@ const TokenEditDialog = ({
</Paragraph>
<TokenSelector
tokenAddress={values.tokenAddress as string}
onTokenSelect={(token: OneToken) => handleTokenSelect(token)}
onTokenSelect={handleTokenSelect}
onTokenSelectError={handleTokenSelectError}
tokenSelectorHasError={tokenSelectorHasError}
isLoadingAddress={isLoadingAddress}
tokenData={tokenData}
label={MSG.fieldLabel}
appearance={{ colorSchema: 'grey', theme: 'fat' }}
Expand Down Expand Up @@ -238,7 +242,8 @@ const TokenEditDialog = ({
tokenSelectorHasError ||
!isValid ||
inputDisabled ||
!hasTokensListChanged(values)
!hasTokensListChanged(values) ||
isLoadingAddress
}
type="submit"
style={{ width: styles.wideButton }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,25 @@ const StepSelectToken = ({
}: Props) => {
const [tokenData, setTokenData] = useState<OneToken | undefined>();
const { formatMessage } = useIntl();
const [isLoadingAddress, setisLoadingAddress] = useState<boolean>(false);
const [tokenSelectorHasError, setTokenSelectorHasError] = useState<boolean>(
false,
);

const handleTokenSelect = (token: OneToken, setFieldValue: SetFieldValue) => {
const handleTokenSelect = (
checkingAddress: boolean,
token: OneToken,
setFieldValue: SetFieldValue,
) => {
setTokenData(token);
if (token) {
setFieldValue('tokenName', token.name);
setFieldValue('tokenSymbol', token.symbol);
}
setisLoadingAddress(checkingAddress);

setFieldValue('tokenName', token?.name || '');
setFieldValue('tokenSymbol', token?.symbol || '');
};

const handleTokenSelectError = (hasError: boolean) => {
setTokenSelectorHasError(hasError);
};

const goToTokenCreate = useCallback(() => {
Expand Down Expand Up @@ -158,9 +170,12 @@ const StepSelectToken = ({
<div>
<TokenSelector
tokenAddress={values.tokenAddress}
onTokenSelect={(token: OneToken) =>
handleTokenSelect(token, setFieldValue)
}
onTokenSelect={(checkingAddress: boolean, token: OneToken) => {
handleTokenSelect(checkingAddress, token, setFieldValue);
}}
onTokenSelectError={handleTokenSelectError}
tokenSelectorHasError={tokenSelectorHasError}
isLoadingAddress={isLoadingAddress}
tokenData={tokenData}
extra={
<button
Expand Down Expand Up @@ -198,8 +213,13 @@ const StepSelectToken = ({
<Button
appearance={{ theme: 'primary', size: 'large' }}
type="submit"
disabled={!isValid || (!dirty && !stepCompleted)}
text={MSG.continue}
disabled={
tokenSelectorHasError ||
!isValid ||
(!dirty && !stepCompleted) ||
isLoadingAddress
}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useState, ReactNode } from 'react';
import React, { useCallback, useEffect, ReactNode } from 'react';
import { defineMessages, MessageDescriptor, useIntl } from 'react-intl';
import { useApolloClient } from '@apollo/client';

Expand Down Expand Up @@ -36,14 +36,17 @@ const MSG = defineMessages({
},
statusNotFound: {
id: 'dashboard.CreateColonyWizard.TokenSelector.statusNotFound',
defaultMessage: 'Token data not found. Please type in token details',
defaultMessage:
'Token data not found. Please check the token contract address.',
},
});

interface Props {
tokenAddress: string;
onTokenSelect: (arg0: OneToken | null | void) => any;
onTokenSelectError?: (arg: boolean) => any;
onTokenSelect: (checkingAddress: boolean, token?: OneToken | null) => void;
onTokenSelectError: (arg: boolean) => void;
danbr marked this conversation as resolved.
Show resolved Hide resolved
tokenSelectorHasError: boolean;
isLoadingAddress: boolean;
tokenData?: OneToken;
label?: string | MessageDescriptor;
appearance?: Appearance;
Expand All @@ -53,8 +56,15 @@ interface Props {
disabled?: boolean;
}

const getStatusText = (isLoading: boolean, tokenData?: OneToken) => {
if (isLoading) {
const getStatusText = (
hasError: boolean,
isLoadingAddress: boolean,
tokenData?: OneToken,
) => {
if (hasError) {
return {};
}
if (isLoadingAddress) {
return { status: MSG.statusLoading };
}
if (tokenData === null) {
Expand All @@ -76,6 +86,8 @@ const TokenSelector = ({
tokenAddress,
onTokenSelect,
onTokenSelectError,
tokenSelectorHasError,
isLoadingAddress,
tokenData,
extra,
label,
Expand All @@ -92,31 +104,23 @@ const TokenSelector = ({
return data && data.token;
}, [apolloClient, tokenAddress]);

const [isLoading, setLoading] = useState(false);

const handleGetTokenSuccess = useCallback(
(token: OneToken) => {
const { name, symbol } = token;
setLoading(false);
const { name, symbol } = token || {};
if (!name || !symbol) {
onTokenSelect(null);
onTokenSelect(false, null);
onTokenSelectError(true);
danbr marked this conversation as resolved.
Show resolved Hide resolved
return;
}
onTokenSelect(token);
if (onTokenSelectError) {
onTokenSelectError(false);
}
onTokenSelect(false, token);
},
[onTokenSelect, onTokenSelectError],
);

const handleGetTokenError = useCallback(
(error: Error) => {
setLoading(false);
onTokenSelect(null);
if (onTokenSelectError) {
onTokenSelectError(true);
}
onTokenSelect(false, null);
onTokenSelectError(true);
log.error(error);
},
[onTokenSelect, onTokenSelectError],
Expand All @@ -127,27 +131,27 @@ const TokenSelector = ({
useEffect(() => {
// Guard against updates that don't include a new, valid `tokenAddress`,
// or if the form is submitting or loading.
if (tokenAddress === prevTokenAddress || isLoading) return;
if (tokenAddress === prevTokenAddress || isLoadingAddress) return;
if (!tokenAddress || !tokenAddress.length || !isAddress(tokenAddress)) {
onTokenSelect();
onTokenSelect(false);
return;
}
// For a valid address, attempt to load token info.
// This is setting state during `componentDidUpdate`, which is
// generally a bad idea, but we are guarding against it by checking the
// state first.
setLoading(true);
onTokenSelect();

onTokenSelectError(false);
onTokenSelect(true);
// Get the token address and handle success/error
getToken()
.then((token: OneToken) => handleGetTokenSuccess(token))
.catch((error) => handleGetTokenError(error));
}, [
tokenAddress,
getToken,
isLoading,
isLoadingAddress,
onTokenSelect,
onTokenSelectError,
prevTokenAddress,
handleGetTokenSuccess,
handleGetTokenError,
Expand All @@ -165,9 +169,12 @@ const TokenSelector = ({
name="tokenAddress"
label={labelText || MSG.inputLabel}
extra={extra}
{...getStatusText(isLoading, tokenData)}
{...getStatusText(tokenSelectorHasError, isLoadingAddress, tokenData)}
appearance={appearance}
disabled={disabled}
forcedFieldError={
tokenSelectorHasError ? MSG.statusNotFound : undefined
}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,17 @@ const UserTokenEditDialogForm = ({
const [tokenSelectorHasError, setTokenSelectorHasError] = useState<boolean>(
false,
);
const [isLoadingAddress, setisLoadingAddress] = useState<boolean>(false);
const { formatMessage } = useIntl();

const sortedTokenIds = useMemo(
() => tokensList.map((token) => token.id).sort(),
[tokensList],
);

const handleTokenSelect = (token: OneToken) => {
const handleTokenSelect = (checkingAddress: boolean, token: OneToken) => {
setTokenData(token);
setisLoadingAddress(checkingAddress);
};

const handleTokenSelectError = (hasError: boolean) => {
Expand Down Expand Up @@ -177,8 +179,10 @@ const UserTokenEditDialogForm = ({
</Paragraph>
<TokenSelector
tokenAddress={values.tokenAddress as string}
onTokenSelect={(token: OneToken) => handleTokenSelect(token)}
onTokenSelect={handleTokenSelect}
onTokenSelectError={handleTokenSelectError}
tokenSelectorHasError={tokenSelectorHasError}
isLoadingAddress={isLoadingAddress}
tokenData={tokenData}
label={MSG.fieldLabel}
appearance={{ colorSchema: 'grey', theme: 'fat' }}
Expand Down Expand Up @@ -207,7 +211,8 @@ const UserTokenEditDialogForm = ({
!isValid ||
!hasRegisteredProfile ||
!hasTokensListChanged(values) ||
isSubmitting
isSubmitting ||
isLoadingAddress
}
type="submit"
style={{ width: styles.wideButton }}
Expand Down