diff --git a/.changeset/hip-beds-do.md b/.changeset/hip-beds-do.md new file mode 100644 index 000000000..e3b5f68a1 --- /dev/null +++ b/.changeset/hip-beds-do.md @@ -0,0 +1,5 @@ +--- +"fuels-wallet": patch +--- + +Allow editing network name when adding. diff --git a/packages/app/src/systems/Network/components/NetworkForm/NetworkForm.tsx b/packages/app/src/systems/Network/components/NetworkForm/NetworkForm.tsx index 0eebab47d..b8615028a 100644 --- a/packages/app/src/systems/Network/components/NetworkForm/NetworkForm.tsx +++ b/packages/app/src/systems/Network/components/NetworkForm/NetworkForm.tsx @@ -40,10 +40,17 @@ export function NetworkForm({ }: NetworkFormProps) { const [isFirstShownTestConnectionBtn, setIsFirstShownTestConnectionBtn] = useState(false); - const { control, formState } = form; + const { control, formState, setValue } = form; const url = useWatch({ control, name: 'url' }); const chainId = useWatch({ control, name: 'chainId' }); + const customName = useWatch({ control, name: 'name' }); + + useEffect(() => { + if (isReviewing && chainName) { + setValue('name', chainName); + } + }, [isReviewing, chainName, setValue]); useEffect(() => { if (isValid && chainId) { @@ -54,12 +61,40 @@ export function NetworkForm({ return ( {isReviewing && ( - + <> + + + Network Name + + } + isRequired + isInvalid={Boolean(formState.errors?.name)} + render={({ field }) => ( + + + + )} + /> + {formState.errors?.name && ( + + {formState.errors.name.message} + + )} + )} {!isReviewing && ( <> diff --git a/packages/app/src/systems/Network/hooks/useNetworkForm.ts b/packages/app/src/systems/Network/hooks/useNetworkForm.ts index f380393b7..f4ebbed47 100644 --- a/packages/app/src/systems/Network/hooks/useNetworkForm.ts +++ b/packages/app/src/systems/Network/hooks/useNetworkForm.ts @@ -1,3 +1,4 @@ +import type { NetworkData } from '@fuel-wallet/types'; import { yupResolver } from '@hookform/resolvers/yup'; import { useEffect } from 'react'; import { useForm } from 'react-hook-form'; @@ -12,11 +13,13 @@ const schema = yup .object({ name: yup .string() + .default('') .test('is-required', 'Name is required', function (value) { return !this.options?.context?.isEditing || !!value; }), url: yup .string() + .default('') .test('is-url-valid', 'URL is not valid', isValidNetworkUrl) .test('is-network-valid', 'Network is not valid', function (url) { return ( @@ -26,6 +29,7 @@ const schema = yup .required('URL is required'), explorerUrl: yup .string() + .default('') .test( 'is-url-valid', 'Explorer URL is not valid', @@ -33,10 +37,8 @@ const schema = yup ) .optional(), chainId: yup - .mixed() - .transform((value) => - value != null && value !== '' ? Number(value) : undefined - ) + .string() + .default('') .required('Chain ID is required') .test( 'chainId-match', @@ -51,22 +53,26 @@ const schema = yup .test( 'is-numbers-only', 'Chain ID must contain only numbers', - (value) => value == null || Number.isInteger(value) + (value) => { + if (!value) return true; + const num = Number(value); + return !Number.isNaN(num) && Number.isInteger(num); + } ), }) .required(); -const DEFAULT_VALUES = { +const DEFAULT_VALUES: NetworkFormValues = { name: '', url: '', explorerUrl: '', - chainId: undefined, + chainId: '', }; export type UseNetworkFormReturn = ReturnType; export type UseAddNetworkOpts = { - defaultValues?: Maybe; + defaultValues?: Maybe>; context?: { providerChainId?: number; isEditing?: boolean; @@ -82,13 +88,22 @@ export function useNetworkForm({ defaultValues, context }: UseAddNetworkOpts) { resetOptions: { keepValues: true, }, - defaultValues: defaultValues || DEFAULT_VALUES, + defaultValues: defaultValues + ? { + ...DEFAULT_VALUES, + ...defaultValues, + chainId: defaultValues.chainId?.toString() || '', + } + : DEFAULT_VALUES, context, }); useEffect(() => { if (defaultValues) { - form.reset(defaultValues); + form.reset({ + ...defaultValues, + chainId: defaultValues.chainId?.toString() || '', + }); } }, [defaultValues, form]); diff --git a/packages/app/src/systems/Network/machines/networksMachine.ts b/packages/app/src/systems/Network/machines/networksMachine.ts index d35706922..6611f52d6 100644 --- a/packages/app/src/systems/Network/machines/networksMachine.ts +++ b/packages/app/src/systems/Network/machines/networksMachine.ts @@ -146,8 +146,11 @@ export const networksMachine = createMachine( }, onDone: [ { - target: 'idle', + target: 'waitingAddNetwork', cond: FetchMachine.hasError, + actions: assign({ + error: (_, ev) => ev.data, + }), }, { actions: [ diff --git a/packages/app/src/systems/Network/pages/AddNetwork/AddNetwork.tsx b/packages/app/src/systems/Network/pages/AddNetwork/AddNetwork.tsx index b31643cf8..ca140f538 100644 --- a/packages/app/src/systems/Network/pages/AddNetwork/AddNetwork.tsx +++ b/packages/app/src/systems/Network/pages/AddNetwork/AddNetwork.tsx @@ -31,6 +31,7 @@ export function AddNetwork() { const form = useNetworkForm({ context }); const url = useWatch({ control: form.control, name: 'url' }); const chainId = useWatch({ control: form.control, name: 'chainId' }); + const name = useWatch({ control: form.control, name: 'name' }); const isValid = form.formState.isDirty && form.formState.isValid && @@ -61,15 +62,24 @@ export function AddNetwork() { }); } - function onAddNetwork() { - const name = chainInfoToAdd?.name || ''; - handlers.addNetwork({ - data: { - chainId: Number(chainId), - name, - url, - }, - }); + async function onAddNetwork() { + if (!name) return; + try { + await handlers.addNetwork({ + data: { + chainId: Number(chainId), + name, + url, + }, + }); + } catch (error) { + if (error instanceof Error && error.message.includes('already exists')) { + form.setError('name', { + type: 'manual', + message: 'A network with this name already exists', + }); + } + } } return ( @@ -101,7 +111,7 @@ export function AddNetwork() {