set up tauri app with React frontend

This commit is contained in:
fmtabbara
2021-08-20 09:03:21 +01:00
parent d740e8b8a9
commit b405d2e4af
51 changed files with 9326 additions and 311 deletions
Generated
+874 -311
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -49,6 +49,7 @@ members = [
"mixnode",
"service-providers/network-requester",
"validator-api",
"tauri-wallet/src-tauri",
]
default-members = [
+15
View File
@@ -0,0 +1,15 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"esmodules": true
}
}
],
"@babel/preset-react",
"@babel/preset-typescript"
],
"plugins": ["@babel/plugin-transform-async-to-generator"]
}
+39
View File
@@ -0,0 +1,39 @@
{
"name": "tauri-app",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"webpack:dev": "yarn webpack serve",
"tauri:dev": "yarn tauri dev",
"dev": "yarn run webpack:dev & yarn run tauri:dev "
},
"dependencies": {
"@babel/preset-typescript": "^7.15.0",
"@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2",
"@material-ui/styles": "^4.11.4",
"@types/react-dom": "^17.0.9",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.0"
},
"devDependencies": {
"@babel/core": "^7.15.0",
"@babel/plugin-transform-async-to-generator": "^7.14.5",
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@tauri-apps/api": "^1.0.0-beta.6",
"@tauri-apps/cli": "^1.0.0-beta.9",
"@types/react-router-dom": "^5.1.8",
"babel-loader": "^8.2.2",
"css-loader": "^6.2.0",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.3.2",
"style-loader": "^3.2.1",
"url-loader": "^4.1.1",
"webpack": "^5.50.0",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^3.11.2"
}
}
+9
View File
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>Tauri Web Wallet</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
+4
View File
@@ -0,0 +1,4 @@
# Generated by Cargo
# will have compiled files and executables
/target/
WixTools
+24
View File
@@ -0,0 +1,24 @@
[package]
name = "Nym-Wallet"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
license = ""
repository = ""
default-run = "Nym-Wallet"
edition = "2018"
build = "src/build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "1.0.0-beta.4" }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.0.0-beta.6", features = ["api-all"] }
[features]
default = [ "custom-protocol" ]
custom-protocol = [ "tauri/custom-protocol" ]
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+14
View File
@@ -0,0 +1,14 @@
max_width = 100
hard_tabs = false
tab_spaces = 2
newline_style = "Auto"
use_small_heuristics = "Default"
reorder_imports = true
reorder_modules = true
remove_nested_parens = true
edition = "2018"
merge_derives = true
use_try_shorthand = false
use_field_init_shorthand = false
force_explicit_abi = true
imports_granularity = "Crate"
+3
View File
@@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}
+10
View File
@@ -0,0 +1,10 @@
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
fn main() {
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
+67
View File
@@ -0,0 +1,67 @@
{
"package": {
"productName": "tauri-wallet",
"version": "0.1.0"
},
"build": {
"distDir": "../dist",
"devPath": "http://localhost:9000",
"beforeDevCommand": "",
"beforeBuildCommand": ""
},
"tauri": {
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.tauri.dev",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"resources": [],
"externalBin": [],
"copyright": "",
"category": "DeveloperTool",
"shortDescription": "",
"longDescription": "",
"deb": {
"depends": [],
"useBootstrapper": false
},
"macOS": {
"frameworks": [],
"minimumSystemVersion": "",
"useBootstrapper": false,
"exceptionDomain": "",
"signingIdentity": null,
"entitlements": null
},
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
}
},
"updater": {
"active": false
},
"allowlist": {
"all": true
},
"windows": [
{
"title": "nym-wallet",
"width": 800,
"height": 600,
"resizable": true,
"fullscreen": false
}
],
"security": {
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'"
}
}
}
@@ -0,0 +1,41 @@
import React, { useState } from 'react'
import { Button } from '@material-ui/core'
import { Check } from '@material-ui/icons'
import { green } from '@material-ui/core/colors'
import { clipboard } from '@tauri-apps/api'
const copy = (text: string): Promise<{ success: boolean; value: string }> => {
return new Promise((resolve, reject) => {
clipboard
.writeText(text)
.then(() => resolve({ success: true, value: text }))
.catch((e) => reject({ success: false, value: 'Failed to copy: ' + e }))
})
}
export const CopyToClipboard = ({ text }: { text: string }) => {
const [copied, setCopied] = useState(false)
const handleCopy = async () => {
setCopied(false)
const res = await copy(text)
console.log(res)
if (res.success) {
setCopied(true)
} else {
console.log(res.value)
}
}
return (
<Button
size="small"
variant="outlined"
aria-label="save"
onClick={handleCopy}
endIcon={copied && <Check style={{ color: green[500] }} />}
>
{copied ? 'Copied' : 'Copy'}
</Button>
)
}
+20
View File
@@ -0,0 +1,20 @@
import React from 'react'
import { Grid, Theme, useTheme } from '@material-ui/core'
export const Layout = ({ children }: { children: React.ReactElement }) => {
const theme: Theme = useTheme()
return (
<div
style={{
padding: theme.spacing(5),
}}
>
<Grid container justifyContent="center">
<Grid item xs={12} md={8} xl={6}>
{children}
</Grid>
</Grid>
</div>
)
}
+43
View File
@@ -0,0 +1,43 @@
import { List, ListItem, ListItemIcon, ListItemText } from '@material-ui/core'
import {
AccountBalanceWalletRounded,
ArrowBack,
ArrowForward,
ExitToApp,
} from '@material-ui/icons'
import React from 'react'
import { Link } from 'react-router-dom'
const routesSchema = [
{
label: 'Balance',
route: '/balance',
Icon: <AccountBalanceWalletRounded />,
},
{
label: 'Send',
route: '/send',
Icon: <ArrowForward />,
},
{
label: 'Receive',
route: '/receive',
Icon: <ArrowBack />,
},
{
label: 'Logout',
route: '/signin',
Icon: <ExitToApp />,
},
]
export const Nav = () => (
<List>
{routesSchema.map((r, i) => (
<ListItem button component={Link} to={r.route} key={i} color="red">
<ListItemIcon>{r.Icon}</ListItemIcon>
<ListItemText primary={r.label} />
</ListItem>
))}
</List>
)
+36
View File
@@ -0,0 +1,36 @@
import React from 'react'
import { Card, CardContent, CardHeader, useTheme } from '@material-ui/core'
export const NymCard = ({
title,
subheader,
children,
}: {
title: string
subheader?: string
children: React.ReactElement
}) => {
const theme = useTheme()
return (
<Card variant="outlined">
<CardHeader
title={title}
subheader={subheader}
titleTypographyProps={{ variant: 'h5' }}
subheaderTypographyProps={{ variant: 'subtitle1' }}
style={{
padding: theme.spacing(2.5),
borderBottom: `1px solid ${theme.palette.grey[200]}`,
}}
/>
<CardContent
style={{
background: theme.palette.grey[50],
padding: theme.spacing(2, 5),
}}
>
{children}
</CardContent>
</Card>
)
}
+42
View File
@@ -0,0 +1,42 @@
import React from 'react'
import { Nav } from './Nav'
export const Page = ({ children }: { children: React.ReactElement }) => {
return (
<div
style={{
height: '100vh',
width: '100vw',
display: 'grid',
gridTemplateColumns: '400px auto',
gridTemplateRows: '100%',
gridColumnGap: '8px',
gridRowGap: '0px',
}}
>
<div
style={{
gridArea: '1 / 1 / 2 / 2',
background: '#121726',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderTopRightRadius: 10,
borderBottomRightRadius: 10,
}}
>
<Nav />
</div>
<div
style={{
gridArea: '1 / 2 / 2 / 3',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{children}
</div>
</div>
)
}
+5
View File
@@ -0,0 +1,5 @@
export * from './Layout'
export * from './NymCard'
export * from './CopyToClipboard'
export * from './Page'
export * from './Nav'
Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

+16
View File
@@ -0,0 +1,16 @@
import React from 'react'
import ReactDOM from 'react-dom'
import { CssBaseline, ThemeProvider } from '@material-ui/core'
import { Routes } from './routes'
import { theme } from './theme'
const App = () => (
<ThemeProvider theme={theme}>
<CssBaseline />
<Routes />
</ThemeProvider>
)
const root = document.getElementById('root')
ReactDOM.render(<App />, root)
+14
View File
@@ -0,0 +1,14 @@
import React, { useState } from 'react'
import { Layout, Page } from '../components'
export const NotFound = () => {
return (
<Page>
<Layout>
<>
<h1>404</h1>
</>
</Layout>
</Page>
)
}
View File
View File
View File
+29
View File
@@ -0,0 +1,29 @@
import React from 'react'
import { Switch, Route } from 'react-router-dom'
import { BrowserRouter as Router } from 'react-router-dom'
import { NotFound } from './404'
import { Receive } from './receive'
import { Send } from './send'
import { SignIn } from './sign-in'
export const Routes = () => (
<Router>
<Switch>
<Route path="/" exact>
<SignIn />
</Route>
<Route path="/signin">
<SignIn />
</Route>
<Route path="/receive">
<Receive />
</Route>
<Route path="/send">
<Send />
</Route>
<Route path="*">
<NotFound />
</Route>
</Switch>
</Router>
)
+47
View File
@@ -0,0 +1,47 @@
import React from 'react'
import { Card, CardContent, Grid, Typography } from '@material-ui/core'
import { CopyToClipboard, Layout, NymCard, Page } from '../components'
import { useMediaQuery } from '@material-ui/core'
export const Receive = () => {
const matches = useMediaQuery('(min-width:769px)')
const address = 'Example address here'
return (
<Page>
<Layout>
<>
<NymCard title="Receive Nym">
<Grid container direction="column" spacing={1}>
<Grid item>
<Typography variant="subtitle1" noWrap={false}>
You can receive tokens by providing this address to the sender
</Typography>
</Grid>
<Grid item>
<Card>
<CardContent>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
flexWrap: 'wrap',
}}
>
<Typography
variant={matches ? 'h5' : 'subtitle1'}
style={{ wordBreak: 'break-word' }}
>
{address}
</Typography>
<CopyToClipboard text={address} />
</div>
</CardContent>
</Card>
</Grid>
</Grid>
</NymCard>
</>
</Layout>
</Page>
)
}
+14
View File
@@ -0,0 +1,14 @@
import React from 'react'
import { Layout, Page } from '../components'
export const Send = () => {
return (
<Page>
<Layout>
<>
<h1>Send</h1>
</>
</Layout>
</Page>
)
}
+203
View File
@@ -0,0 +1,203 @@
import React, { useState } from 'react'
import {
TextField,
LinearProgress,
Button,
Typography,
Grid,
Link,
Container,
Card,
CardHeader,
Theme,
} from '@material-ui/core'
import { Layout, NymCard } from '../components'
import { useTheme } from '@material-ui/styles'
import logo from '../images/logo.png'
import { useHistory } from 'react-router-dom'
export const SignIn = () => {
const [loading, setLoading] = useState(false)
const theme: Theme = useTheme()
const history = useHistory()
return (
<div
style={{
height: '100vh',
width: '100vw',
display: 'grid',
gridTemplateColumns: '400px auto',
gridTemplateRows: '100%',
gridColumnGap: '0px',
gridRowGap: '0px',
}}
>
<div
style={{
gridArea: '1 / 1 / 2 / 2',
background: '#121726',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderTopRightRadius: 10,
borderBottomRightRadius: 10,
}}
>
<img src={logo} style={{ width: 100 }} />
</div>
<div
style={{
gridArea: '1 / 2 / 2 / 3',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<div style={{ width: 400 }}>
<Typography variant="h4">Sign in</Typography>
<form noValidate onSubmit={() => history.push('/send')}>
<Grid container direction="column" spacing={1}>
<Grid item>
<TextField
size="medium"
variant="outlined"
margin="normal"
required
fullWidth
id="mnemonic"
label="BIP-39 Mnemonic"
name="mnemonic"
autoComplete="mnemonic"
autoFocus
style={{ background: 'white' }}
/>
</Grid>
<Grid item>
<Button
fullWidth
variant="contained"
color="primary"
type="submit"
disabled={loading}
size="large"
>
Sign In
</Button>
</Grid>
<Grid item style={{ marginTop: theme.spacing(1) }}>
<Typography variant="body2" component="span">
Don't have an account?
</Typography>{' '}
<Link>Create one</Link>
</Grid>
</Grid>
</form>
</div>
</div>
</div>
// <Layout>
// <div
// style={{ maxWidth: 500, margin: 'auto', marginTop: 'calc(100vh/4)' }}
// >
// <img src={logo} style={{ width: 300 }} />
// <NymCard title="Sign in">
// <>
// <form noValidate onSubmit={() => {}}>
// <Grid container direction="column" spacing={1}>
// <Grid item>
// <TextField
// variant="outlined"
// margin="normal"
// required
// fullWidth
// id="mnemonic"
// label="BIP-39 Mnemonic"
// name="mnemonic"
// autoComplete="mnemonic"
// autoFocus
// style={{ background: 'white' }}
// />
// </Grid>
// <Grid item>
// <Button
// fullWidth
// variant="contained"
// color="primary"
// type="submit"
// disabled={loading}
// >
// Sign In
// </Button>
// </Grid>
// <Grid item style={{ marginTop: theme.spacing(1) }}>
// <Typography variant="body2" component="span">
// Don't have an account?
// </Typography>{' '}
// <Link>Create one</Link>
// </Grid>
// </Grid>
// </form>
// </>
// </NymCard>
// </div>
// </Layout>
)
}
{
/* <Grid container style={{ height: '100%' }}>
<Grid
item
style={{
width: 400,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: '#121726',
}}
>
<img src={logo} style={{ width: 100 }} />
</Grid>
<Grid item xs={11}>
<NymCard title="Sign in">
<>
<form noValidate onSubmit={() => {}}>
<Grid container direction="column" spacing={1}>
<Grid item>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="mnemonic"
label="BIP-39 Mnemonic"
name="mnemonic"
autoComplete="mnemonic"
autoFocus
style={{ background: 'white' }}
/>
</Grid>
<Grid item>
<Button
fullWidth
variant="contained"
color="primary"
type="submit"
disabled={loading}
>
Sign In
</Button>
</Grid>
<Grid item style={{ marginTop: theme.spacing(1) }}>
<Typography variant="body2" component="span">
Don't have an account?
</Typography>{' '}
<Link>Create one</Link>
</Grid>
</Grid>
</form>
</>
</NymCard>
</Grid>
</Grid> */
}
+16
View File
@@ -0,0 +1,16 @@
import { createTheme } from '@material-ui/core'
export const theme = createTheme({
palette: {
primary: {
main: '#F4731B',
},
},
overrides: {
MuiButton: {
containedPrimary: {
color: 'white',
},
},
},
})
+2
View File
@@ -0,0 +1,2 @@
declare module '*.jpg'
declare module '*.png'
+73
View File
@@ -0,0 +1,73 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
"jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true /* Enable all strict type-checking options. */,
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
"typeRoots": [
"node_modules/@types",
"src/types"
] /* List of folders to include type definitions from. */,
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true /* Skip type checking of declaration files. */,
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}
+43
View File
@@ -0,0 +1,43 @@
const HtmlWebpackPlugin = require('html-webpack-plugin')
var path = require('path')
module.exports = {
mode: 'development',
entry: path.resolve(__dirname, '/src/index'),
devServer: {
port: 9000,
compress: true,
historyApiFallback: true,
hot: true,
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/index.html'),
filename: 'index.html',
}),
],
module: {
rules: [
{
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
},
],
},
],
},
resolve: {
extensions: ['*', '.js', '.jsx', '.ts', '.tsx'],
},
}
File diff suppressed because it is too large Load Diff