mirror of
https://github.com/status-im/safe-react.git
synced 2025-01-10 18:15:37 +00:00
Merge pull request #2 from gnosis/react-router
React router v4 + Aliases modules import [JEST, Webpack, VsCode]
This commit is contained in:
commit
bb4906cef5
1
.babelrc
1
.babelrc
@ -10,6 +10,7 @@
|
||||
"@babel/stage-0"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-syntax-dynamic-import",
|
||||
"transform-es3-member-expression-literals",
|
||||
"transform-es3-property-literals"
|
||||
]
|
||||
|
@ -38,6 +38,7 @@ module.exports = {
|
||||
appIndexJs: resolveApp('src/index.js'),
|
||||
appPackageJson: resolveApp('package.json'),
|
||||
appSrc: resolveApp('src'),
|
||||
appContracts: resolveApp('gnosis-safe-contracts/build/contracts'),
|
||||
yarnLockFile: resolveApp('yarn.lock'),
|
||||
testsSetup: resolveApp('src/setupTests.js'),
|
||||
appNodeModules: resolveApp('node_modules'),
|
||||
|
@ -58,12 +58,21 @@ module.exports = {
|
||||
// initialization, it doesn't blow up the WebpackDevServer client, and
|
||||
// changing JS code would still trigger a refresh.
|
||||
],
|
||||
resolve: {
|
||||
resolve: {
|
||||
modules: [
|
||||
paths.appSrc,
|
||||
'node_modules',
|
||||
paths.appContracts,
|
||||
],
|
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
// https://github.com/facebookincubator/create-react-app/issues/290
|
||||
extensions: ['.js', '.json', '.jsx'],
|
||||
alias: {
|
||||
'~': paths.appSrc,
|
||||
'#': paths.appContracts,
|
||||
}
|
||||
},
|
||||
output: {
|
||||
// Next line is not used in dev but WebpackDevServer crashes without it:
|
||||
|
@ -101,11 +101,20 @@ module.exports = {
|
||||
publicPath: publicPath
|
||||
},
|
||||
resolve: {
|
||||
modules: [
|
||||
paths.appSrc,
|
||||
'node_modules',
|
||||
paths.appContracts,
|
||||
],
|
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
// https://github.com/facebookincubator/create-react-app/issues/290
|
||||
extensions: ['.js', '.json', '.jsx'],
|
||||
alias: {
|
||||
'~': paths.appSrc,
|
||||
'#': paths.appContracts,
|
||||
}
|
||||
},
|
||||
|
||||
module: {
|
||||
|
18
jsconfig.json
Normal file
18
jsconfig.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2017",
|
||||
"allowSyntheticDefaultImports": false,
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"~/*":["src/*"],
|
||||
"@/*":["gnosis-safe-contracts/build/contracts"]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"build_webpack",
|
||||
"config",
|
||||
"public",
|
||||
"scripts"
|
||||
]
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
var Migrations = artifacts.require("./Migrations.sol");
|
||||
|
||||
module.exports = function(deployer) {
|
||||
deployer.deploy(Migrations);
|
||||
};
|
@ -1,5 +0,0 @@
|
||||
var SimpleStorage = artifacts.require("./SimpleStorage.sol");
|
||||
|
||||
module.exports = function(deployer) {
|
||||
deployer.deploy(SimpleStorage);
|
||||
};
|
71
package-lock.json
generated
71
package-lock.json
generated
@ -8298,6 +8298,18 @@
|
||||
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
|
||||
"dev": true
|
||||
},
|
||||
"history": {
|
||||
"version": "4.7.2",
|
||||
"resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz",
|
||||
"integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==",
|
||||
"requires": {
|
||||
"invariant": "2.2.3",
|
||||
"loose-envify": "1.3.1",
|
||||
"resolve-pathname": "2.2.0",
|
||||
"value-equal": "0.4.0",
|
||||
"warning": "3.0.0"
|
||||
}
|
||||
},
|
||||
"hmac-drbg": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||
@ -9030,7 +9042,6 @@
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.3.tgz",
|
||||
"integrity": "sha512-7Z5PPegwDTyjbaeCnV0efcyS6vdKAU51kpEmS7QFib3P4822l8ICYyMn7qvJnc+WzLoDsuI9gPMKbJ8pCu8XtA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "1.3.1"
|
||||
}
|
||||
@ -9411,8 +9422,7 @@
|
||||
"isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
||||
},
|
||||
"isexe": {
|
||||
"version": "2.0.0",
|
||||
@ -15148,6 +15158,14 @@
|
||||
"theming": "1.3.0"
|
||||
}
|
||||
},
|
||||
"react-loadable": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-loadable/-/react-loadable-5.3.1.tgz",
|
||||
"integrity": "sha1-lpnpoI/tSbrNacqqKCA0tip2vN0=",
|
||||
"requires": {
|
||||
"prop-types": "15.6.1"
|
||||
}
|
||||
},
|
||||
"react-popper": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-0.8.2.tgz",
|
||||
@ -15157,6 +15175,43 @@
|
||||
"prop-types": "15.6.1"
|
||||
}
|
||||
},
|
||||
"react-router": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-4.2.0.tgz",
|
||||
"integrity": "sha512-DY6pjwRhdARE4TDw7XjxjZsbx9lKmIcyZoZ+SDO7SBJ1KUeWNxT22Kara2AC7u6/c2SYEHlEDLnzBCcNhLE8Vg==",
|
||||
"requires": {
|
||||
"history": "4.7.2",
|
||||
"hoist-non-react-statics": "2.5.0",
|
||||
"invariant": "2.2.3",
|
||||
"loose-envify": "1.3.1",
|
||||
"path-to-regexp": "1.7.0",
|
||||
"prop-types": "15.6.1",
|
||||
"warning": "3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"path-to-regexp": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
|
||||
"integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
|
||||
"requires": {
|
||||
"isarray": "0.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-router-dom": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.2.2.tgz",
|
||||
"integrity": "sha512-cHMFC1ZoLDfEaMFoKTjN7fry/oczMgRt5BKfMAkTu5zEuJvUiPp1J8d0eXSVTnBh6pxlbdqDhozunOOLtmKfPA==",
|
||||
"requires": {
|
||||
"history": "4.7.2",
|
||||
"invariant": "2.2.3",
|
||||
"loose-envify": "1.3.1",
|
||||
"prop-types": "15.6.1",
|
||||
"react-router": "4.2.0",
|
||||
"warning": "3.0.0"
|
||||
}
|
||||
},
|
||||
"react-scrollbar-size": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-scrollbar-size/-/react-scrollbar-size-2.1.0.tgz",
|
||||
@ -15675,6 +15730,11 @@
|
||||
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
|
||||
"dev": true
|
||||
},
|
||||
"resolve-pathname": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz",
|
||||
"integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg=="
|
||||
},
|
||||
"resolve-url": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
|
||||
@ -19484,6 +19544,11 @@
|
||||
"spdx-expression-parse": "3.0.0"
|
||||
}
|
||||
},
|
||||
"value-equal": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz",
|
||||
"integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw=="
|
||||
},
|
||||
"vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
|
@ -23,6 +23,7 @@
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.0.0-beta.40",
|
||||
"@babel/core": "^7.0.0-beta.40",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0-beta.40",
|
||||
"@babel/polyfill": "^7.0.0-beta.40",
|
||||
"@babel/preset-env": "^7.0.0-beta.40",
|
||||
"@babel/preset-react": "^7.0.0-beta.40",
|
||||
@ -62,7 +63,9 @@
|
||||
"final-form": "^4.2.1",
|
||||
"material-ui": "^1.0.0-beta.35",
|
||||
"material-ui-icons": "^1.0.0-beta.35",
|
||||
"react-final-form": "^3.1.2"
|
||||
"react-final-form": "^3.1.2",
|
||||
"react-loadable": "^5.3.1",
|
||||
"react-router-dom": "^4.2.2"
|
||||
},
|
||||
"jest": {
|
||||
"collectCoverageFrom": [
|
||||
@ -87,6 +90,8 @@
|
||||
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"~(.*)$": "<rootDir>/src/$1",
|
||||
"#(.*)$": "<rootDir>/gnosis-safe-contracts/build/contracts/$1",
|
||||
"^react-native$": "react-native-web"
|
||||
},
|
||||
"moduleFileExtensions": [
|
||||
|
120
src/App.js
120
src/App.js
@ -1,113 +1,11 @@
|
||||
import Button from 'material-ui/Button';
|
||||
import React, { Component } from 'react'
|
||||
import { Form, Field } from 'react-final-form'
|
||||
import Safe from '../gnosis-safe-contracts/build/contracts/GnosisSafe.json'
|
||||
import getWeb3, { promisify } from './utils/getWeb3'
|
||||
import contract from 'truffle-contract'
|
||||
import TextField from './components/forms/TextField'
|
||||
import Page from './components/layout/Page'
|
||||
import PageFrame from './components/layout/PageFrame'
|
||||
import './App.scss'
|
||||
import React from 'react'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import AppRoutes from './routes'
|
||||
|
||||
class App extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
const ReactRouterApp = () => (
|
||||
<BrowserRouter>
|
||||
<AppRoutes />
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
this.state = {
|
||||
storageValue: 0,
|
||||
web3: undefined,
|
||||
safeAddress: undefined,
|
||||
funds: undefined,
|
||||
}
|
||||
|
||||
this.safe = contract(Safe)
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
getWeb3.then(results => {
|
||||
const web3 = results.web3
|
||||
this.safe.setProvider(web3.currentProvider)
|
||||
this.setState({web3})
|
||||
})
|
||||
.catch(() => {
|
||||
console.log('Error finding web3.')
|
||||
})
|
||||
}
|
||||
|
||||
onCallSafeContractSubmit = async () => {
|
||||
try {
|
||||
const web3 = this.state.web3
|
||||
const accounts = await promisify(cb => web3.eth.getAccounts(cb))
|
||||
const safeInstance = await this.safe.new([accounts[0]], 1, 0, 0, { from: accounts[0], gas: '5000000' })
|
||||
const transactionHash = safeInstance.transactionHash
|
||||
// const transaction = await promisify(cb => web3.eth.getTransaction(transactionHash, cb))
|
||||
// console.log("Transaction" + JSON.stringify(transaction, 2, 0))
|
||||
const transactionReceipt = await promisify(cb => web3.eth.getTransactionReceipt(transactionHash, cb))
|
||||
console.log("Transaction Receipt" + JSON.stringify(transactionReceipt, 2, 0))
|
||||
this.setState({ safeAddress: safeInstance.address})
|
||||
} catch (error) {
|
||||
console.log("Error while creating the Safe")
|
||||
}
|
||||
}
|
||||
|
||||
onAddFunds = async (values) => {
|
||||
const fundsToAdd = values.funds
|
||||
try {
|
||||
const { web3, safeAddress } = this.state
|
||||
const accounts = await promisify(cb => web3.eth.getAccounts(cb))
|
||||
const txData = {from: accounts[0], to: safeAddress, value: web3.toWei(fundsToAdd, 'ether')}
|
||||
await promisify(cb => web3.eth.sendTransaction(txData, cb))
|
||||
const funds = await promisify( cb => web3.eth.getBalance(safeAddress, cb))
|
||||
const fundsInEther = funds ? web3.fromWei(funds.toNumber(), 'ether') : 0
|
||||
this.setState({funds: fundsInEther})
|
||||
} catch (error) {
|
||||
console.log("Errog adding funds to safe" + error)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { safeAddress, funds } = this.state
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<PageFrame>
|
||||
<Form
|
||||
onSubmit={this.onCallSafeContractSubmit}
|
||||
render={({ handleSubmit, pristine, invalid }) => (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<h2>Create a new Safe instance for testing purposes</h2>
|
||||
<div>
|
||||
<Button variant="raised" color="primary" type="submit">
|
||||
Create Safe
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
/>
|
||||
<Form
|
||||
onSubmit={this.onAddFunds}
|
||||
render={({ handleSubmit, pristine, invalid }) => (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<h2>Add Funds to the safe</h2>
|
||||
<div style={{ margin: '10px 0px'}}>
|
||||
<label style={{ marginRight: '10px' }}>{safeAddress ? safeAddress : 'Not safe detected'}</label>
|
||||
</div>
|
||||
{ safeAddress && <div>
|
||||
<Field name="funds" component={TextField} type="text" placeholder="ETH to add" />
|
||||
<Button type="submit" disabled={ !safeAddress || pristine || invalid}>
|
||||
Add funds
|
||||
</Button>
|
||||
</div> }
|
||||
{ safeAddress && <div style={{ margin: '15px 0px'}}>
|
||||
Total funds in this safe: { funds ? funds : 0 } ETH
|
||||
</div> }
|
||||
</form>
|
||||
)}
|
||||
/>
|
||||
</PageFrame>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App
|
||||
export default ReactRouterApp
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { MuiThemeProvider } from 'material-ui/styles'
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import theme from '~/theme/mui'
|
||||
import App from './App'
|
||||
import { MuiThemeProvider } from 'material-ui/styles';
|
||||
import theme from './theme/mui'
|
||||
|
||||
const Root = () => (
|
||||
<MuiThemeProvider theme={theme}>
|
||||
|
23
src/routes/index.js
Normal file
23
src/routes/index.js
Normal file
@ -0,0 +1,23 @@
|
||||
import { CircularProgress } from 'material-ui/Progress';
|
||||
import React from 'react'
|
||||
import Loadable from 'react-loadable';
|
||||
import { Switch, Redirect, Route } from 'react-router-dom'
|
||||
import Welcome from './welcome/components/Layout'
|
||||
|
||||
const Loading = () => <CircularProgress size={50} />
|
||||
|
||||
const Transactions = Loadable({
|
||||
loader: () => import('./transactions/components/Layout'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
const Routes = () => (
|
||||
<Switch>
|
||||
<Redirect exact from="/" to="/welcome" />
|
||||
<Route exact path='/welcome' component={Welcome} />
|
||||
<Route exact path='/transactions' component={Transactions} />
|
||||
</Switch>
|
||||
)
|
||||
|
||||
export default Routes
|
||||
|
11
src/routes/transactions/components/Layout.jsx
Normal file
11
src/routes/transactions/components/Layout.jsx
Normal file
@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
class Layout extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>I am transactions Layout</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Layout
|
119
src/routes/welcome/components/Layout.jsx
Normal file
119
src/routes/welcome/components/Layout.jsx
Normal file
@ -0,0 +1,119 @@
|
||||
import Button from 'material-ui/Button';
|
||||
import React, { Component } from 'react'
|
||||
import { Form, Field } from 'react-final-form'
|
||||
import { Link } from 'react-router-dom'
|
||||
import contract from 'truffle-contract'
|
||||
import TextField from '~/components/forms/TextField'
|
||||
import Page from '~/components/layout/Page'
|
||||
import PageFrame from '~/components/layout/PageFrame'
|
||||
import getWeb3, { promisify } from '~/utils/getWeb3'
|
||||
import Safe from '#/GnosisSafe.json'
|
||||
import './App.scss'
|
||||
|
||||
class App extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
storageValue: 0,
|
||||
web3: undefined,
|
||||
safeAddress: undefined,
|
||||
funds: undefined,
|
||||
}
|
||||
|
||||
this.safe = contract(Safe)
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
getWeb3.then(results => {
|
||||
const web3 = results.web3
|
||||
this.safe.setProvider(web3.currentProvider)
|
||||
this.setState({web3})
|
||||
})
|
||||
.catch(() => {
|
||||
console.log('Error finding web3.')
|
||||
})
|
||||
}
|
||||
|
||||
onCallSafeContractSubmit = async () => {
|
||||
try {
|
||||
const web3 = this.state.web3
|
||||
const accounts = await promisify(cb => web3.eth.getAccounts(cb))
|
||||
const safeInstance = await this.safe.new([accounts[0]], 1, 0, 0, { from: accounts[0], gas: '5000000' })
|
||||
const transactionHash = safeInstance.transactionHash
|
||||
// const transaction = await promisify(cb => web3.eth.getTransaction(transactionHash, cb))
|
||||
// console.log("Transaction" + JSON.stringify(transaction, 2, 0))
|
||||
const transactionReceipt = await promisify(cb => web3.eth.getTransactionReceipt(transactionHash, cb))
|
||||
console.log("Transaction Receipt" + JSON.stringify(transactionReceipt, 2, 0))
|
||||
this.setState({ safeAddress: safeInstance.address})
|
||||
} catch (error) {
|
||||
console.log("Error while creating the Safe")
|
||||
}
|
||||
}
|
||||
|
||||
onAddFunds = async (values) => {
|
||||
const fundsToAdd = values.funds
|
||||
try {
|
||||
const { web3, safeAddress } = this.state
|
||||
const accounts = await promisify(cb => web3.eth.getAccounts(cb))
|
||||
const txData = {from: accounts[0], to: safeAddress, value: web3.toWei(fundsToAdd, 'ether')}
|
||||
await promisify(cb => web3.eth.sendTransaction(txData, cb))
|
||||
const funds = await promisify( cb => web3.eth.getBalance(safeAddress, cb))
|
||||
const fundsInEther = funds ? web3.fromWei(funds.toNumber(), 'ether') : 0
|
||||
this.setState({funds: fundsInEther})
|
||||
} catch (error) {
|
||||
console.log("Errog adding funds to safe" + error)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { safeAddress, funds } = this.state
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<PageFrame>
|
||||
<Form
|
||||
onSubmit={this.onCallSafeContractSubmit}
|
||||
render={({ handleSubmit, pristine, invalid }) => (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<h2>Create a new Safe instance for testing purposes</h2>
|
||||
<div>
|
||||
<Button variant="raised" color="primary" type="submit">
|
||||
Create Safe
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
/>
|
||||
<Form
|
||||
onSubmit={this.onAddFunds}
|
||||
render={({ handleSubmit, pristine, invalid }) => (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<h2>Add Funds to the safe</h2>
|
||||
<div style={{ margin: '10px 0px'}}>
|
||||
<label style={{ marginRight: '10px' }}>{safeAddress ? safeAddress : 'Not safe detected'}</label>
|
||||
</div>
|
||||
{ safeAddress && <div>
|
||||
<Field name="funds" component={TextField} type="text" placeholder="ETH to add" />
|
||||
<Button type="submit" disabled={ !safeAddress || pristine || invalid}>
|
||||
Add funds
|
||||
</Button>
|
||||
</div> }
|
||||
{ safeAddress && <div style={{ margin: '15px 0px'}}>
|
||||
Total funds in this safe: { funds ? funds : 0 } ETH
|
||||
</div> }
|
||||
</form>
|
||||
)}
|
||||
/>
|
||||
<Link to="/transactions">
|
||||
<Button variant="raised" color="primary">
|
||||
Go to transactions
|
||||
</Button>
|
||||
</Link>
|
||||
</PageFrame>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App
|
Loading…
x
Reference in New Issue
Block a user