style: initial dark mode

This commit is contained in:
Cody Bender 2024-08-09 00:59:27 -04:00
parent ba05a82406
commit e0632d1a50
5 changed files with 213 additions and 163 deletions

View File

@ -1,21 +1,19 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<meta charset="utf-8" /> <head>
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="theme-color" content="#000000" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta <meta name="theme-color" content="#000000" />
name="description" <meta name="description" content="Laconic Wallet Web App" />
content="Laconic Wallet Web App" <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
/> <!--
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
--> -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!-- <!--
Notice the use of %PUBLIC_URL% in the tags above. Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build. It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML. Only files inside the `public` folder can be referenced from the HTML.
@ -24,52 +22,58 @@
work correctly both with client-side routing and a non-root public URL. work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<title>Laconic Wallet</title> <title>Laconic Wallet</title>
<style> <style>
body { #app {
margin: 0; background-color: #0f0f0f;
padding: 0; }
height: 100%;
body {
margin: 0;
padding: 0;
height: 100%;
background-color: #0f0f0f;
}
.loader-wrapper {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
display: grid;
place-items: center;
}
.loader {
border: 16px solid #e3e3e3;
border-top: 16px solid #6750a4;
border-radius: 50%;
width: 140px;
height: 140px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
} }
.loader-wrapper { 100% {
width: 100%; transform: rotate(360deg);
height: 100%;
position: fixed;
top: 0;
left: 0;
display: grid;
place-items: center;
} }
}
</style>
</head>
.loader { <body>
border: 16px solid #e3e3e3; <noscript>You need to enable JavaScript to run this app.</noscript>
border-top: 16px solid #6750a4; <div id="root">
border-radius: 50%; <div class="loader-wrapper">
width: 140px; <div class="loader"></div>
height: 140px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root">
<div class="loader-wrapper">
<div class="loader"></div>
</div>
</div> </div>
<!-- </div>
<!--
This HTML file is a template. This HTML file is a template.
If you open it directly in the browser, you will see an empty page. If you open it directly in the browser, you will see an empty page.
@ -79,5 +83,6 @@
To begin the development, run `npm start` or `yarn start`. To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`. To create a production bundle, use `npm run build` or `yarn build`.
--> -->
</body> </body>
</html> </html>

View File

@ -21,5 +21,5 @@
"start_url": ".", "start_url": ".",
"display": "standalone", "display": "standalone",
"theme_color": "#000000", "theme_color": "#000000",
"background_color": "#ffffff" "background_color": "#0f0f0f"
} }

View File

@ -1,43 +1,43 @@
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from "react";
import { Button, Snackbar, Text } from 'react-native-paper'; import { Button, Snackbar, Surface, Text } from "react-native-paper";
import { TxBody, AuthInfo } from 'cosmjs-types/cosmos/tx/v1beta1/tx'; import { TxBody, AuthInfo } from "cosmjs-types/cosmos/tx/v1beta1/tx";
import { SignClientTypes } from '@walletconnect/types'; import { SignClientTypes } from "@walletconnect/types";
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from "@react-navigation/native";
import { import {
createStackNavigator, createStackNavigator,
StackNavigationProp, StackNavigationProp,
} from '@react-navigation/stack'; } from "@react-navigation/stack";
import { getSdkError } from '@walletconnect/utils'; import { getSdkError } from "@walletconnect/utils";
import { Web3WalletTypes } from '@walletconnect/web3wallet'; import { Web3WalletTypes } from "@walletconnect/web3wallet";
import { formatJsonRpcResult } from '@json-rpc-tools/utils'; import { formatJsonRpcResult } from "@json-rpc-tools/utils";
import PairingModal from './components/PairingModal'; import PairingModal from "./components/PairingModal";
import { useWalletConnect } from './context/WalletConnectContext'; import { useWalletConnect } from "./context/WalletConnectContext";
import { useAccounts } from './context/AccountsContext'; import { useAccounts } from "./context/AccountsContext";
import InvalidPath from './screens/InvalidPath'; import InvalidPath from "./screens/InvalidPath";
import SignMessage from './screens/SignMessage'; import SignMessage from "./screens/SignMessage";
import HomeScreen from './screens/HomeScreen'; import HomeScreen from "./screens/HomeScreen";
import SignRequest from './screens/SignRequest'; import SignRequest from "./screens/SignRequest";
import AddSession from './screens/AddSession'; import AddSession from "./screens/AddSession";
import WalletConnect from './screens/WalletConnect'; import WalletConnect from "./screens/WalletConnect";
import ApproveTransaction from './screens/ApproveTransaction'; import ApproveTransaction from "./screens/ApproveTransaction";
import { StackParamsList } from './types'; import { StackParamsList } from "./types";
import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Data'; import { EIP155_SIGNING_METHODS } from "./utils/wallet-connect/EIP155Data";
import { getSignParamsMessage } from './utils/wallet-connect/helpers'; import { getSignParamsMessage } from "./utils/wallet-connect/helpers";
import ApproveTransfer from './screens/ApproveTransfer'; import ApproveTransfer from "./screens/ApproveTransfer";
import AddNetwork from './screens/AddNetwork'; import AddNetwork from "./screens/AddNetwork";
import EditNetwork from './screens/EditNetwork'; import EditNetwork from "./screens/EditNetwork";
import { COSMOS, EIP155 } from './utils/constants'; import { COSMOS, EIP155 } from "./utils/constants";
import { useNetworks } from './context/NetworksContext'; import { useNetworks } from "./context/NetworksContext";
import { NETWORK_METHODS } from './utils/wallet-connect/common-data'; import { NETWORK_METHODS } from "./utils/wallet-connect/common-data";
import { COSMOS_METHODS } from './utils/wallet-connect/COSMOSData'; import { COSMOS_METHODS } from "./utils/wallet-connect/COSMOSData";
import styles from "./styles/stylesheet";
const Stack = createStackNavigator<StackParamsList>(); const Stack = createStackNavigator<StackParamsList>();
const App = (): React.JSX.Element => { const App = (): React.JSX.Element => {
const navigation = const navigation = useNavigation<StackNavigationProp<StackParamsList>>();
useNavigation<StackNavigationProp<StackParamsList>>();
const { web3wallet, setActiveSessions } = useWalletConnect(); const { web3wallet, setActiveSessions } = useWalletConnect();
const { accounts, setCurrentIndex } = useAccounts(); const { accounts, setCurrentIndex } = useAccounts();
@ -45,16 +45,16 @@ const App = (): React.JSX.Element => {
const [modalVisible, setModalVisible] = useState(false); const [modalVisible, setModalVisible] = useState(false);
const [toastVisible, setToastVisible] = useState(false); const [toastVisible, setToastVisible] = useState(false);
const [currentProposal, setCurrentProposal] = useState< const [currentProposal, setCurrentProposal] = useState<
SignClientTypes.EventArguments['session_proposal'] | undefined SignClientTypes.EventArguments["session_proposal"] | undefined
>(); >();
const onSessionProposal = useCallback( const onSessionProposal = useCallback(
async (proposal: SignClientTypes.EventArguments['session_proposal']) => { async (proposal: SignClientTypes.EventArguments["session_proposal"]) => {
if (!accounts.length || !accounts.length) { if (!accounts.length || !accounts.length) {
const { id } = proposal; const { id } = proposal;
await web3wallet!.rejectSession({ await web3wallet!.rejectSession({
id, id,
reason: getSdkError('UNSUPPORTED_ACCOUNTS'), reason: getSdkError("UNSUPPORTED_ACCOUNTS"),
}); });
return; return;
} }
@ -74,10 +74,11 @@ const App = (): React.JSX.Element => {
switch (request.method) { switch (request.method) {
case NETWORK_METHODS.GET_NETWORKS: case NETWORK_METHODS.GET_NETWORKS:
const currentNetworkId = networksData.find( const currentNetworkId = networksData.find(
networkData => networkData.networkId === selectedNetwork!.networkId, (networkData) =>
networkData.networkId === selectedNetwork!.networkId,
)?.networkId; )?.networkId;
const networkNamesData = networksData.map(networkData => { const networkNamesData = networksData.map((networkData) => {
return { return {
id: networkData.networkId, id: networkData.networkId,
name: networkData.networkName, name: networkData.networkName,
@ -98,13 +99,13 @@ const App = (): React.JSX.Element => {
case NETWORK_METHODS.CHANGE_NETWORK: case NETWORK_METHODS.CHANGE_NETWORK:
const networkNameData = request.params[0]; const networkNameData = request.params[0];
const network = networksData.find( const network = networksData.find(
networkData => networkData.networkId === networkNameData.id, (networkData) => networkData.networkId === networkNameData.id,
); );
setCurrentIndex(0); setCurrentIndex(0);
setSelectedNetwork(network); setSelectedNetwork(network);
const response = formatJsonRpcResult(id, { const response = formatJsonRpcResult(id, {
response: 'true', response: "true",
}); });
await web3wallet!.respondSessionRequest({ await web3wallet!.respondSessionRequest({
@ -114,7 +115,7 @@ const App = (): React.JSX.Element => {
break; break;
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION: case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
navigation.navigate('ApproveTransfer', { navigation.navigate("ApproveTransfer", {
transaction: request.params[0], transaction: request.params[0],
requestEvent, requestEvent,
requestSessionData, requestSessionData,
@ -122,7 +123,7 @@ const App = (): React.JSX.Element => {
break; break;
case EIP155_SIGNING_METHODS.PERSONAL_SIGN: case EIP155_SIGNING_METHODS.PERSONAL_SIGN:
navigation.navigate('SignRequest', { navigation.navigate("SignRequest", {
namespace: EIP155, namespace: EIP155,
address: request.params[1], address: request.params[1],
message: getSignParamsMessage(request.params), message: getSignParamsMessage(request.params),
@ -136,19 +137,19 @@ const App = (): React.JSX.Element => {
txbody: TxBody.toJSON( txbody: TxBody.toJSON(
TxBody.decode( TxBody.decode(
Uint8Array.from( Uint8Array.from(
Buffer.from(request.params.signDoc.bodyBytes, 'hex'), Buffer.from(request.params.signDoc.bodyBytes, "hex"),
), ),
), ),
), ),
authInfo: AuthInfo.toJSON( authInfo: AuthInfo.toJSON(
AuthInfo.decode( AuthInfo.decode(
Uint8Array.from( Uint8Array.from(
Buffer.from(request.params.signDoc.authInfoBytes, 'hex'), Buffer.from(request.params.signDoc.authInfoBytes, "hex"),
), ),
), ),
), ),
}; };
navigation.navigate('SignRequest', { navigation.navigate("SignRequest", {
namespace: COSMOS, namespace: COSMOS,
address: request.params.signerAddress, address: request.params.signerAddress,
message: JSON.stringify(message, undefined, 2), message: JSON.stringify(message, undefined, 2),
@ -158,7 +159,7 @@ const App = (): React.JSX.Element => {
break; break;
case COSMOS_METHODS.COSMOS_SIGN_AMINO: case COSMOS_METHODS.COSMOS_SIGN_AMINO:
navigation.navigate('SignRequest', { navigation.navigate("SignRequest", {
namespace: COSMOS, namespace: COSMOS,
address: request.params.signerAddress, address: request.params.signerAddress,
message: request.params.signDoc.memo, message: request.params.signDoc.memo,
@ -168,7 +169,7 @@ const App = (): React.JSX.Element => {
break; break;
case COSMOS_METHODS.COSMOS_SEND_TOKENS: case COSMOS_METHODS.COSMOS_SEND_TOKENS:
navigation.navigate('ApproveTransfer', { navigation.navigate("ApproveTransfer", {
transaction: request.params[0], transaction: request.params[0],
requestEvent, requestEvent,
requestSessionData, requestSessionData,
@ -177,7 +178,7 @@ const App = (): React.JSX.Element => {
case COSMOS_METHODS.COSMOS_SEND_TRANSACTION: case COSMOS_METHODS.COSMOS_SEND_TRANSACTION:
const { transactionMessage, signer } = request.params; const { transactionMessage, signer } = request.params;
navigation.navigate('ApproveTransaction', { navigation.navigate("ApproveTransaction", {
transactionMessage, transactionMessage,
signer, signer,
requestEvent, requestEvent,
@ -186,7 +187,7 @@ const App = (): React.JSX.Element => {
break; break;
default: default:
throw new Error('Invalid method'); throw new Error("Invalid method");
} }
}, },
[ [
@ -195,7 +196,7 @@ const App = (): React.JSX.Element => {
setSelectedNetwork, setSelectedNetwork,
setCurrentIndex, setCurrentIndex,
selectedNetwork, selectedNetwork,
web3wallet web3wallet,
], ],
); );
@ -205,18 +206,18 @@ const App = (): React.JSX.Element => {
}, [setActiveSessions, web3wallet]); }, [setActiveSessions, web3wallet]);
useEffect(() => { useEffect(() => {
web3wallet?.on('session_proposal', onSessionProposal); web3wallet?.on("session_proposal", onSessionProposal);
web3wallet?.on('session_request', onSessionRequest); web3wallet?.on("session_request", onSessionRequest);
web3wallet?.on('session_delete', onSessionDelete); web3wallet?.on("session_delete", onSessionDelete);
return () => { return () => {
web3wallet?.off('session_proposal', onSessionProposal); web3wallet?.off("session_proposal", onSessionProposal);
web3wallet?.off('session_request', onSessionRequest); web3wallet?.off("session_request", onSessionRequest);
web3wallet?.off('session_delete', onSessionDelete); web3wallet?.off("session_delete", onSessionDelete);
}; };
}); });
return ( return (
<> <Surface id="surfface" style={styles.appSurface}>
<Stack.Navigator <Stack.Navigator
screenOptions={{ screenOptions={{
headerBackTitleVisible: true, headerBackTitleVisible: true,
@ -227,7 +228,19 @@ 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
headerTitle: () => <Text variant="titleLarge">Laconic Wallet</Text>, header: () => (
<Text
variant="titleLarge"
style={{
paddingLeft: 24,
paddingVertical: 12,
width: "auto",
backgroundColor: "#18181A",
}}
>
Laconic Wallet
</Text>
),
}} }}
/> />
<Stack.Screen <Stack.Screen
@ -258,7 +271,7 @@ const App = (): React.JSX.Element => {
name="AddSession" name="AddSession"
component={AddSession} component={AddSession}
options={{ options={{
title: 'New session', title: "New session",
}} }}
/> />
<Stack.Screen <Stack.Screen
@ -271,8 +284,9 @@ const App = (): React.JSX.Element => {
headerRight: () => ( headerRight: () => (
<Button <Button
onPress={() => { onPress={() => {
navigation.navigate('AddSession'); navigation.navigate("AddSession");
}}> }}
>
{<Text>PAIR</Text>} {<Text>PAIR</Text>}
</Button> </Button>
), ),
@ -282,28 +296,28 @@ const App = (): React.JSX.Element => {
name="ApproveTransfer" name="ApproveTransfer"
component={ApproveTransfer} component={ApproveTransfer}
options={{ options={{
title: 'Approve transfer', title: "Approve transfer",
}} }}
/> />
<Stack.Screen <Stack.Screen
name="AddNetwork" name="AddNetwork"
component={AddNetwork} component={AddNetwork}
options={{ options={{
title: 'Add Network', title: "Add Network",
}} }}
/> />
<Stack.Screen <Stack.Screen
name="EditNetwork" name="EditNetwork"
component={EditNetwork} component={EditNetwork}
options={{ options={{
title: 'Edit Network', title: "Edit Network",
}} }}
/> />
<Stack.Screen <Stack.Screen
name="ApproveTransaction" name="ApproveTransaction"
component={ApproveTransaction} component={ApproveTransaction}
options={{ options={{
title: 'Approve Transaction', title: "Approve Transaction",
}} }}
/> />
</Stack.Navigator> </Stack.Navigator>
@ -317,10 +331,11 @@ const App = (): React.JSX.Element => {
<Snackbar <Snackbar
visible={toastVisible} visible={toastVisible}
onDismiss={() => setToastVisible(false)} onDismiss={() => setToastVisible(false)}
duration={3000}> duration={3000}
>
Session approved Session approved
</Snackbar> </Snackbar>
</> </Surface>
); );
}; };

View File

@ -1,59 +1,85 @@
import React from 'react'; import React from "react";
import ReactDOM from 'react-dom/client'; import ReactDOM from "react-dom/client";
import { PaperProvider, MD3LightTheme as DefaultTheme, } from 'react-native-paper'; import {
import { NavigationContainer } from '@react-navigation/native'; PaperProvider,
import { Platform } from 'react-native'; MD3DarkTheme as DefaultTheme,
import { Buffer } from 'buffer'; } from "react-native-paper";
import { NavigationContainer, DarkTheme } from "@react-navigation/native";
import { Platform } from "react-native";
import { Buffer } from "buffer";
import './index.css'; import "./index.css";
import App from './App'; import App from "./App";
import { AccountsProvider } from './context/AccountsContext'; import { AccountsProvider } from "./context/AccountsContext";
import { NetworksProvider } from './context/NetworksContext'; import { NetworksProvider } from "./context/NetworksContext";
import reportWebVitals from './reportWebVitals'; import reportWebVitals from "./reportWebVitals";
import { WalletConnectProvider } from './context/WalletConnectContext'; import { WalletConnectProvider } from "./context/WalletConnectContext";
import { createTheme, ThemeProvider } from "@mui/material";
globalThis.Buffer = Buffer; globalThis.Buffer = Buffer;
const linking = { const linking = {
prefixes: ['https://wallet.laconic.com'] prefixes: ["https://wallet.laconic.com"],
}; };
const theme = { const theme = {
...DefaultTheme, ...DefaultTheme,
dark: false, dark: true,
}; };
const navigationTheme: typeof DarkTheme = {
...DarkTheme,
colors: {
...DarkTheme.colors,
primary: "#0000F4",
background: "#0F0F0F",
card: "#18181A",
},
};
const muiTheme = createTheme({
palette: {
mode: "dark",
primary: {
main: "#0000F4",
},
},
});
const root = ReactDOM.createRoot( const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement document.getElementById("root") as HTMLElement,
); );
root.render( root.render(
<PaperProvider theme={theme}> <div id="app">
<NetworksProvider> <PaperProvider theme={theme}>
<AccountsProvider> <NetworksProvider>
<WalletConnectProvider> <AccountsProvider>
<NavigationContainer <WalletConnectProvider>
linking={linking} <NavigationContainer
documentTitle={{ linking={linking}
formatter: () => documentTitle={{
`Laconic Wallet`, formatter: () => `Laconic Wallet`,
}} }}
> theme={navigationTheme}
<React.Fragment> >
{Platform.OS === 'web' ? ( <React.Fragment>
<style type="text/css">{` {Platform.OS === "web" ? (
<style type="text/css">{`
@font-face { @font-face {
font-family: 'MaterialCommunityIcons'; font-family: 'MaterialCommunityIcons';
src: url(${require('react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf')}) format('truetype'); src: url(${require("react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf")}) format('truetype');
} }
`}</style> `}</style>
) : null} ) : null}
<App /> <ThemeProvider theme={muiTheme}>
</React.Fragment> <App />
</NavigationContainer> </ThemeProvider>
</WalletConnectProvider> </React.Fragment>
</AccountsProvider> </NavigationContainer>
</NetworksProvider> </WalletConnectProvider>
</PaperProvider> </AccountsProvider>
</NetworksProvider>
</PaperProvider>
</div>,
); );
// If you want to start measuring performance in your app, pass a function // If you want to start measuring performance in your app, pass a function

View File

@ -29,10 +29,14 @@ const styles = StyleSheet.create({
accountComponent: { accountComponent: {
flex: 4, flex: 4,
}, },
appSurface: {
backgroundColor: '#0f0f0f',
},
appContainer: { appContainer: {
flexGrow: 1, flexGrow: 1,
marginTop: 24, marginTop: 24,
paddingHorizontal: 24, paddingHorizontal: 24,
backgroundColor: '#0f0f0f',
}, },
resetContainer: { resetContainer: {
flex: 1, flex: 1,
@ -178,7 +182,7 @@ const styles = StyleSheet.create({
width: '100%', width: '100%',
height: '50%', height: '50%',
position: 'absolute', position: 'absolute',
backgroundColor: 'white', backgroundColor: '#0f0f0f',
bottom: 0, bottom: 0,
}, },
modalOuterContainer: { flex: 1 }, modalOuterContainer: { flex: 1 },