style: add network

This commit is contained in:
Cody Bender 2024-08-09 15:34:21 -04:00
parent 17e96d0e52
commit 43305a8d2f
5 changed files with 303 additions and 270 deletions

View File

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

View File

@ -1,9 +1,8 @@
import { View } from 'react-native'; import { View } from "react-native";
import React from 'react'; import React from "react";
import { LoadingButton } from '@mui/lab'; import { LoadingButton } from "@mui/lab";
import { CreateWalletProps } from '../types'; import { CreateWalletProps } from "../types";
import styles from '../styles/stylesheet';
const CreateWallet = ({ const CreateWallet = ({
isWalletCreating, isWalletCreating,
@ -16,8 +15,9 @@ const CreateWallet = ({
variant="contained" variant="contained"
loading={isWalletCreating} loading={isWalletCreating}
disabled={isWalletCreating} disabled={isWalletCreating}
onClick={createWalletHandler}> onClick={createWalletHandler}
{isWalletCreating ? 'Creating' : 'CREATE WALLET'} >
{isWalletCreating ? "Creating" : "CREATE WALLET"}
</LoadingButton> </LoadingButton>
</View> </View>
</View> </View>

View File

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

View File

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

View File

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