style: add network

This commit is contained in:
Cody Bender 2024-08-09 15:34:21 -04:00
parent 17810801dd
commit 4ff1c10699
4 changed files with 296 additions and 263 deletions

View File

@ -229,19 +229,7 @@ const App = (): React.JSX.Element => {
component={HomeScreen}
options={{
// eslint-disable-next-line react/no-unstable-nested-components
header: () => (
<Text
variant="titleLarge"
style={{
paddingLeft: 24,
paddingVertical: 12,
width: "auto",
backgroundColor: "#18181A",
}}
>
Laconic Wallet
</Text>
),
header: () => <Header title="Wallet" />,
}}
/>
<Stack.Screen
@ -249,7 +237,7 @@ const App = (): React.JSX.Element => {
component={SignMessage}
options={{
// eslint-disable-next-line react/no-unstable-nested-components
headerTitle: () => <Text variant="titleLarge">Sign Message</Text>,
header: () => <Header title="Wallet" />,
}}
/>
<Stack.Screen
@ -257,7 +245,7 @@ const App = (): React.JSX.Element => {
component={SignRequest}
options={{
// eslint-disable-next-line react/no-unstable-nested-components
headerTitle: () => <Text variant="titleLarge">Sign Request</Text>,
header: () => <Header title="Wallet" />,
}}
/>
<Stack.Screen
@ -272,7 +260,7 @@ const App = (): React.JSX.Element => {
name="AddSession"
component={AddSession}
options={{
title: "New session",
header: () => <Header title="Wallet" />,
}}
/>
<Stack.Screen
@ -297,14 +285,14 @@ const App = (): React.JSX.Element => {
name="ApproveTransfer"
component={ApproveTransfer}
options={{
title: "Approve transfer",
header: () => <Header title="Wallet" />,
}}
/>
<Stack.Screen
name="AddNetwork"
component={AddNetwork}
options={{
title: "Add Network",
header: () => <Header title="Wallet" />,
}}
/>
<Stack.Screen
@ -318,7 +306,7 @@ const App = (): React.JSX.Element => {
name="ApproveTransaction"
component={ApproveTransaction}
options={{
title: "Approve Transaction",
header: () => <Header title="Wallet" />,
}}
/>
</Stack.Navigator>

View File

@ -1,6 +1,5 @@
import { Divider, Stack, SvgIcon, Typography } from "@mui/material";
import React from "react";
import { Text } from "react-native-paper";
export const Header: React.FC<{ title: string }> = ({ title }) => (
<Stack

View File

@ -1,21 +1,20 @@
import React, { useCallback, useEffect, useState } from 'react';
import { View } from 'react-native';
import { useForm, Controller, useWatch, FieldErrors } from 'react-hook-form';
import { TextInput, Button, HelperText } from 'react-native-paper';
import React, { useCallback, useEffect, useState } from "react";
import { useForm, Controller, useWatch, FieldErrors } from "react-hook-form";
import { TextInput, HelperText } from "react-native-paper";
import { HDNode } from 'ethers/lib/utils';
import { chains } from 'chain-registry';
import { useDebouncedCallback } from 'use-debounce';
import { z } from 'zod';
import { HDNode } from "ethers/lib/utils";
import { chains } from "chain-registry";
import { useDebouncedCallback } from "use-debounce";
import { z } from "zod";
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { useNavigation } from '@react-navigation/native';
import { zodResolver } from '@hookform/resolvers/zod';
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
import { useNavigation } from "@react-navigation/native";
import { zodResolver } from "@hookform/resolvers/zod";
import { StackParamsList } from '../types';
import { SelectNetworkType } from '../components/SelectNetworkType';
import { storeNetworkData } from '../utils/accounts';
import { useNetworks } from '../context/NetworksContext';
import { StackParamsList } from "../types";
import { SelectNetworkType } from "../components/SelectNetworkType";
import { storeNetworkData } from "../utils/accounts";
import { useNetworks } from "../context/NetworksContext";
import {
COSMOS,
EIP155,
@ -23,14 +22,17 @@ import {
EMPTY_FIELD_ERROR,
INVALID_URL_ERROR,
IS_NUMBER_REGEX,
} from '../utils/constants';
import { getCosmosAccounts } from '../utils/accounts';
import ETH_CHAINS from '../assets/ethereum-chains.json';
} from "../utils/constants";
import { getCosmosAccounts } from "../utils/accounts";
import ETH_CHAINS from "../assets/ethereum-chains.json";
import {
getInternetCredentials,
setInternetCredentials,
} from '../utils/key-store';
import styles from '../styles/stylesheet';
} from "../utils/key-store";
import { Button, Divider, Grid, Typography } from "@mui/material";
import { Container } from "../components/Container";
import { ArrowBack } from "@mui/icons-material";
import { LoadingButton } from "@mui/lab";
const ethNetworkDataSchema = z.object({
chainId: z.string().nonempty({ message: EMPTY_FIELD_ERROR }),
@ -39,7 +41,7 @@ const ethNetworkDataSchema = z.object({
blockExplorerUrl: z
.string()
.url({ message: INVALID_URL_ERROR })
.or(z.literal('')),
.or(z.literal("")),
coinType: z
.string()
.nonempty({ message: EMPTY_FIELD_ERROR })
@ -54,7 +56,7 @@ const cosmosNetworkDataSchema = z.object({
blockExplorerUrl: z
.string()
.url({ message: INVALID_URL_ERROR })
.or(z.literal('')),
.or(z.literal("")),
coinType: z
.string()
.nonempty({ message: EMPTY_FIELD_ERROR })
@ -85,13 +87,13 @@ const AddNetwork = () => {
setValue,
reset,
} = useForm<z.infer<typeof networksFormDataSchema>>({
mode: 'onChange',
mode: "onChange",
resolver: zodResolver(networksFormDataSchema),
});
const watchChainId = useWatch({
control,
name: 'chainId',
name: "chainId",
});
const updateNetworkType = (newNetworkType: string) => {
@ -101,16 +103,16 @@ const AddNetwork = () => {
const fetchChainDetails = useDebouncedCallback((chainId: string) => {
if (namespace === EIP155) {
const ethChainDetails = ETH_CHAINS.find(
chain => chain.chainId === Number(chainId),
(chain) => chain.chainId === Number(chainId),
);
if (!ethChainDetails) {
return;
}
setValue('networkName', ethChainDetails.name);
setValue('rpcUrl', ethChainDetails.rpc[0]);
setValue('blockExplorerUrl', ethChainDetails.explorers?.[0].url || '');
setValue('coinType', String(ethChainDetails.slip44 ?? '60'));
setValue('currencySymbol', ethChainDetails.nativeCurrency.symbol);
setValue("networkName", ethChainDetails.name);
setValue("rpcUrl", ethChainDetails.rpc[0]);
setValue("blockExplorerUrl", ethChainDetails.explorers?.[0].url || "");
setValue("coinType", String(ethChainDetails.slip44 ?? "60"));
setValue("currencySymbol", ethChainDetails.nativeCurrency.symbol);
return;
}
const cosmosChainDetails = chains.find(
@ -119,14 +121,14 @@ const AddNetwork = () => {
if (!cosmosChainDetails) {
return;
}
setValue('networkName', cosmosChainDetails.pretty_name);
setValue('rpcUrl', cosmosChainDetails.apis?.rpc?.[0]?.address || '');
setValue('blockExplorerUrl', cosmosChainDetails.explorers?.[0].url || '');
setValue('addressPrefix', cosmosChainDetails.bech32_prefix);
setValue('coinType', String(cosmosChainDetails.slip44 ?? '118'));
setValue('nativeDenom', cosmosChainDetails.fees?.fee_tokens[0].denom || '');
setValue("networkName", cosmosChainDetails.pretty_name);
setValue("rpcUrl", cosmosChainDetails.apis?.rpc?.[0]?.address || "");
setValue("blockExplorerUrl", cosmosChainDetails.explorers?.[0].url || "");
setValue("addressPrefix", cosmosChainDetails.bech32_prefix);
setValue("coinType", String(cosmosChainDetails.slip44 ?? "118"));
setValue("nativeDenom", cosmosChainDetails.fees?.fee_tokens[0].denom || "");
setValue(
'gasPrice',
"gasPrice",
String(
cosmosChainDetails.fees?.fee_tokens[0].average_gas_price ||
String(process.env.DEFAULT_GAS_PRICE),
@ -142,11 +144,11 @@ const AddNetwork = () => {
isDefault: false,
};
const mnemonicServer = await getInternetCredentials('mnemonicServer');
const mnemonicServer = await getInternetCredentials("mnemonicServer");
const mnemonic = mnemonicServer;
if (!mnemonic) {
throw new Error('Mnemonic not found');
throw new Error("Mnemonic not found");
}
const hdNode = HDNode.fromMnemonic(mnemonic);
@ -172,7 +174,7 @@ const AddNetwork = () => {
break;
default:
throw new Error('Unsupported namespace');
throw new Error("Unsupported namespace");
}
const accountInfo = `${hdPath},${node.privateKey},${node.publicKey},${address}`;
@ -183,22 +185,22 @@ const AddNetwork = () => {
await Promise.all([
setInternetCredentials(
`accounts/${newNetworkData.namespace}:${newNetworkData.chainId}/0`,
'_',
"_",
accountInfo,
),
setInternetCredentials(
`addAccountCounter/${newNetworkData.namespace}:${newNetworkData.chainId}`,
'_',
'1',
"_",
"1",
),
setInternetCredentials(
`accountIndices/${newNetworkData.namespace}:${newNetworkData.chainId}`,
'_',
'0',
"_",
"0",
),
]);
navigation.navigate('Home');
navigation.navigate("Home");
},
[navigation, namespace, setNetworksData],
);
@ -212,208 +214,254 @@ const AddNetwork = () => {
}, [namespace, reset]);
return (
<View style={styles.appContainer}>
<SelectNetworkType updateNetworkType={updateNetworkType} />
<Container boxProps={{ sx: { backgroundColor: "inherit" } }}>
<Button
startIcon={<ArrowBack />}
color="info"
sx={{ mb: 4 }}
onClick={() => navigation.navigate("Home")}
>
Home
</Button>
<Typography variant="h4" sx={{ mb: 4 }}>
Add Network
</Typography>
<Container>
<SelectNetworkType updateNetworkType={updateNetworkType} />
<Divider flexItem sx={{ my: 4 }} />
<Controller
control={control}
name="chainId"
defaultValue=""
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
value={value}
label="Chain ID"
onBlur={onBlur}
onChangeText={textValue => onChange(textValue)}
<Grid container spacing={2} sx={{ px: 1 }}>
<Grid item xs={6}>
<Controller
control={control}
name="chainId"
defaultValue=""
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
value={value}
label="Chain ID"
onBlur={onBlur}
onChangeText={(textValue) => onChange(textValue)}
/>
<HelperText type="error">
{errors.chainId?.message}
</HelperText>
</>
)}
/>
<HelperText type="error">{errors.chainId?.message}</HelperText>
</>
)}
/>
<Controller
control={control}
defaultValue=""
name="networkName"
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
label="Network Name"
value={value}
onBlur={onBlur}
onChangeText={textValue => onChange(textValue)}
/>
<HelperText type="error">{errors.networkName?.message}</HelperText>
</>
)}
/>
<Controller
control={control}
name="rpcUrl"
defaultValue=""
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
label="New RPC URL"
onBlur={onBlur}
value={value}
onChangeText={textValue => onChange(textValue)}
/>
<HelperText type="error">{errors.rpcUrl?.message}</HelperText>
</>
)}
/>
</Grid>
<Controller
control={control}
defaultValue=""
name="blockExplorerUrl"
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
value={value}
label="Block Explorer URL (Optional)"
onBlur={onBlur}
onChangeText={textValue => onChange(textValue)}
<Grid item xs={6}>
<Controller
control={control}
defaultValue=""
name="networkName"
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
label="Network Name"
value={value}
onBlur={onBlur}
onChangeText={(textValue) => onChange(textValue)}
/>
<HelperText type="error">
{errors.networkName?.message}
</HelperText>
</>
)}
/>
<HelperText type="error">
{errors.blockExplorerUrl?.message}
</HelperText>
</>
)}
/>
<Controller
control={control}
name="coinType"
defaultValue=""
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
value={value}
label="Coin Type"
onBlur={onBlur}
onChangeText={onChange}
</Grid>
<Grid item xs={6}>
<Controller
control={control}
name="rpcUrl"
defaultValue=""
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
label="New RPC URL"
onBlur={onBlur}
value={value}
onChangeText={(textValue) => onChange(textValue)}
/>
<HelperText type="error">{errors.rpcUrl?.message}</HelperText>
</>
)}
/>
<HelperText type="error">{errors.coinType?.message}</HelperText>
</>
)}
/>
{namespace === EIP155 ? (
<Controller
control={control}
name="currencySymbol"
defaultValue=""
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
value={value}
label="Currency Symbol"
onBlur={onBlur}
onChangeText={textValue => onChange(textValue)}
</Grid>
<Grid item xs={6}>
<Controller
control={control}
defaultValue=""
name="blockExplorerUrl"
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
value={value}
label="Block Explorer URL (Optional)"
onBlur={onBlur}
onChangeText={(textValue) => onChange(textValue)}
/>
<HelperText type="error">
{errors.blockExplorerUrl?.message}
</HelperText>
</>
)}
/>
</Grid>
<Grid item xs={namespace === EIP155 ? 12 : 6}>
<Controller
control={control}
name="coinType"
defaultValue=""
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
value={value}
label="Coin Type"
onBlur={onBlur}
onChangeText={onChange}
/>
<HelperText type="error">
{errors.coinType?.message}
</HelperText>
</>
)}
/>
</Grid>
{namespace === EIP155 ? (
<Grid item xs={12}>
<Controller
control={control}
name="currencySymbol"
defaultValue=""
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
value={value}
label="Currency Symbol"
onBlur={onBlur}
onChangeText={(textValue) => onChange(textValue)}
/>
<HelperText type="error">
{
(
errors as FieldErrors<
z.infer<typeof ethNetworkDataSchema>
>
).currencySymbol?.message
}
</HelperText>
</>
)}
/>
<HelperText type="error">
{
(errors as FieldErrors<z.infer<typeof ethNetworkDataSchema>>)
.currencySymbol?.message
}
</HelperText>
</Grid>
) : (
<>
<Grid item xs={6}>
<Controller
control={control}
name="nativeDenom"
defaultValue=""
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
value={value}
label="Native Denom"
onBlur={onBlur}
onChangeText={(textValue) => onChange(textValue)}
/>
<HelperText type="error">
{
(
errors as FieldErrors<
z.infer<typeof cosmosNetworkDataSchema>
>
).nativeDenom?.message
}
</HelperText>
</>
)}
/>
</Grid>
<Grid item xs={6}>
<Controller
control={control}
name="addressPrefix"
defaultValue=""
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
value={value}
label="Address Prefix"
onBlur={onBlur}
onChangeText={(textValue) => onChange(textValue)}
/>
<HelperText type="error">
{
(
errors as FieldErrors<
z.infer<typeof cosmosNetworkDataSchema>
>
).addressPrefix?.message
}
</HelperText>
</>
)}
/>
</Grid>
<Grid item xs={6}>
<Controller
control={control}
name="gasPrice"
defaultValue=""
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
value={value}
label="Gas Price"
onBlur={onBlur}
onChangeText={onChange}
/>
<HelperText type="error">
{
(
errors as FieldErrors<
z.infer<typeof cosmosNetworkDataSchema>
>
).gasPrice?.message
}
</HelperText>
</>
)}
/>
</Grid>
</>
)}
/>
) : (
<>
<Controller
control={control}
name="nativeDenom"
defaultValue=""
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
value={value}
label="Native Denom"
onBlur={onBlur}
onChangeText={textValue => onChange(textValue)}
/>
<HelperText type="error">
{
(
errors as FieldErrors<
z.infer<typeof cosmosNetworkDataSchema>
>
).nativeDenom?.message
}
</HelperText>
</>
)}
/>
<Controller
control={control}
name="addressPrefix"
defaultValue=""
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
value={value}
label="Address Prefix"
onBlur={onBlur}
onChangeText={textValue => onChange(textValue)}
/>
<HelperText type="error">
{
(
errors as FieldErrors<
z.infer<typeof cosmosNetworkDataSchema>
>
).addressPrefix?.message
}
</HelperText>
</>
)}
/>
<Controller
control={control}
name="gasPrice"
defaultValue=""
render={({ field: { onChange, onBlur, value } }) => (
<>
<TextInput
mode="outlined"
value={value}
label="Gas Price"
onBlur={onBlur}
onChangeText={onChange}
/>
<HelperText type="error">
{
(
errors as FieldErrors<
z.infer<typeof cosmosNetworkDataSchema>
>
).gasPrice?.message
}
</HelperText>
</>
)}
/>
</>
)}
<Button
mode="contained"
loading={isSubmitting}
disabled={isSubmitting}
style={styles.networksButton}
onPress={handleSubmit(submit)}>
{isSubmitting ? 'Adding' : 'Submit'}
</Button>
</View>
</Grid>
<LoadingButton
variant="contained"
loading={isSubmitting}
disabled={isSubmitting}
onClick={handleSubmit(submit)}
sx={{ minWidth: "200px", px: 4, py: 1, mt: 2 }}
>
{isSubmitting ? "Adding" : "Submit"}
</LoadingButton>
</Container>
</Container>
);
};

View File

@ -12,7 +12,6 @@ import { useNavigation } from "@react-navigation/native";
import { setInternetCredentials } from "../utils/key-store";
import { StackParamsList } from "../types";
import styles from "../styles/stylesheet";
import { retrieveNetworksData } from "../utils/accounts";
import { useNetworks } from "../context/NetworksContext";
import {
@ -214,9 +213,8 @@ const EditNetwork = ({ route }: EditNetworkProps) => {
variant="contained"
loading={isSubmitting}
disabled={isSubmitting}
style={styles.networksButton}
onClick={handleSubmit(submit)}
sx={{ minWidth: "200px", px: 4, py: 1 }}
sx={{ minWidth: "200px", px: 4, py: 1, mt: 2 }}
>
{isSubmitting ? "Adding" : "Submit"}
</LoadingButton>