Browse Source

Fix lint issues

develop
Apostolos Fanakis 4 years ago
parent
commit
40925e2a7a
  1. 2
      packages/concordia-app/package.json
  2. 21
      packages/concordia-app/src/components/App.jsx
  3. 72
      packages/concordia-app/src/components/AppContext.js
  4. 81
      packages/concordia-app/src/components/AppContext.jsx
  5. 20
      packages/concordia-app/src/components/CoreLayoutContainer.jsx
  6. 8
      packages/concordia-app/src/components/HomeContainer.jsx
  7. 78
      packages/concordia-app/src/components/LoadingComponent.jsx
  8. 121
      packages/concordia-app/src/components/LoadingContainer.jsx
  9. 35
      packages/concordia-app/src/components/MenuComponent.jsx
  10. 69
      packages/concordia-app/src/components/SignUpForm.jsx
  11. 13
      packages/concordia-app/src/index.jsx
  12. 22
      packages/concordia-app/src/options/breezeOptions.js
  13. 6
      packages/concordia-app/src/options/drizzleOptions.js
  14. 2
      packages/concordia-app/src/options/web3Options.js
  15. 6
      packages/concordia-app/src/orbit/orbitUtils.js
  16. 72
      packages/concordia-app/src/orbit/ΕthereumIdentityProvider.js
  17. 8
      packages/concordia-app/src/redux/reducers/userReducer.js
  18. 21
      packages/concordia-app/src/redux/sagas/orbitSaga.js
  19. 8
      packages/concordia-app/src/redux/sagas/rootSaga.js
  20. 23
      packages/concordia-app/src/redux/sagas/userSaga.js
  21. 13
      packages/concordia-app/src/redux/store.js
  22. 38
      packages/concordia-app/src/utils/serviceWorker.js

2
packages/concordia-app/package.json

@ -9,7 +9,7 @@
"eject": "react-scripts eject", "eject": "react-scripts eject",
"postinstall": "patch-package", "postinstall": "patch-package",
"analyze": "source-map-explorer 'build/static/js/*.js'", "analyze": "source-map-explorer 'build/static/js/*.js'",
"lint": "yarn run eslint . --format table" "lint": "yarn run eslint --ext js,jsx . --format table"
}, },
"browserslist": { "browserslist": {
"production": [ "production": [

21
packages/concordia-app/src/components/App.jsx

@ -1,16 +1,15 @@
import React from 'react' import React from 'react';
import { Provider } from 'react-redux' import { Provider } from 'react-redux';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom' import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import LoadingContainer from './LoadingContainer' import PropTypes from 'prop-types';
import PropTypes from 'prop-types' import LoadingContainer from './LoadingContainer';
// CSS // CSS
import '../assets/css/app.css'; import '../assets/css/app.css';
import CoreLayoutContainer from './CoreLayoutContainer'; import CoreLayoutContainer from './CoreLayoutContainer';
import HomeContainer from './HomeContainer'; import HomeContainer from './HomeContainer';
import NotFound from '../components/NotFound'; import NotFound from './NotFound';
const App = ({ store }) => ( const App = ({ store }) => (
<Provider store={store}> <Provider store={store}>
@ -25,10 +24,10 @@ const App = ({ store }) => (
</Router> </Router>
</LoadingContainer> </LoadingContainer>
</Provider> </Provider>
) );
App.propTypes = { App.propTypes = {
store: PropTypes.object.isRequired store: PropTypes.object.isRequired,
} };
export default App export default App;

72
packages/concordia-app/src/components/AppContext.js

@ -1,72 +0,0 @@
// Modified version of https://github.com/trufflesuite/drizzle/blob/develop/packages/react-plugin/src/DrizzleContext.js
import React from "react";
const Context = React.createContext();
class Provider extends React.Component {
state = {
drizzleState: null,
drizzleInitialized: false,
breezeState: null,
breezeInitialized: false
};
componentDidMount() {
const { drizzle, breeze } = this.props;
// subscribe to changes in the store, keep state up-to-date
this.unsubscribe = drizzle.store.subscribe(() => {
const drizzleState = drizzle.store.getState();
const breezeState = breeze.store.getState();
if (drizzleState.drizzleStatus.initialized) {
this.setState({
drizzleState,
drizzleInitialized: true
});
}
if (breezeState.breezeStatus.initialized) {
this.setState({
breezeState: breezeState,
breezeInitialized: true
});
}
});
this.unsubscribe = breeze.store.subscribe(() => {
const breezeState = breeze.store.getState();
if (breezeState.breezeStatus.initialized) {
this.setState({
breezeState: breezeState,
breezeInitialized: true
});
}
});
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
return (
<Context.Provider
value={{
drizzle: this.props.drizzle,
drizzleState: this.state.drizzleState,
drizzleInitialized: this.state.drizzleInitialized,
breeze: this.props.breeze,
breezeState: this.state.breezeState,
breezeInitialized: this.state.breezeInitialized
}}
>
{this.props.children}
</Context.Provider>
);
}
}
export default {
Context: Context,
Consumer: Context.Consumer,
Provider
};

81
packages/concordia-app/src/components/AppContext.jsx

@ -0,0 +1,81 @@
// Modified version of https://github.com/trufflesuite/drizzle/blob/develop/packages/react-plugin/src/DrizzleContext.js
import React from 'react';
const Context = React.createContext();
class Provider extends React.Component {
constructor(props) {
super(props);
this.state = {
drizzleState: null,
drizzleInitialized: false,
breezeState: null,
breezeInitialized: false,
};
}
componentDidMount() {
const { drizzle, breeze } = this.props;
// subscribe to changes in the store, keep state up-to-date
this.unsubscribe = drizzle.store.subscribe(() => {
const drizzleState = drizzle.store.getState();
const breezeState = breeze.store.getState();
if (drizzleState.drizzleStatus.initialized) {
this.setState({
drizzleState,
drizzleInitialized: true,
});
}
if (breezeState.breezeStatus.initialized) {
this.setState({
breezeState,
breezeInitialized: true,
});
}
});
this.unsubscribe = breeze.store.subscribe(() => {
const breezeState = breeze.store.getState();
if (breezeState.breezeStatus.initialized) {
this.setState({
breezeState,
breezeInitialized: true,
});
}
});
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
const {
drizzleState, drizzleInitialized, breezeState, breezeInitialized,
} = this.state;
const { drizzle, breeze, children } = this.props;
return (
<Context.Provider
value={{
drizzle,
drizzleState,
drizzleInitialized,
breeze,
breezeState,
breezeInitialized,
}}
>
{children}
</Context.Provider>
);
}
}
export default {
Context,
Consumer: Context.Consumer,
Provider,
};

20
packages/concordia-app/src/components/CoreLayoutContainer.jsx

@ -1,19 +1,21 @@
import React, { Component } from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import MenuComponent from './MenuComponent'; import MenuComponent from './MenuComponent';
export default class CoreLayout extends Component { const CoreLayout = (props) => {
render() { const { children } = props;
return ( return (
<div> <div>
<MenuComponent/> <MenuComponent />
{this.props.children} {children}
</div> </div>
) );
} };
}
CoreLayout.propTypes = { CoreLayout.propTypes = {
children: PropTypes.element.isRequired children: PropTypes.element.isRequired,
}; };
export default CoreLayout;

8
packages/concordia-app/src/components/HomeContainer.jsx

@ -1,9 +1,5 @@
import React, { Component } from 'react'; import React from 'react';
class HomeContainer extends Component { const HomeContainer = () => (<p>TODO: Home Container</p>);
render() {
return(<p>TODO: Home Container</p>);
}
}
export default HomeContainer; export default HomeContainer;

78
packages/concordia-app/src/components/LoadingComponent.jsx

@ -1,4 +1,4 @@
import React, { Component } from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Container, Progress } from 'semantic-ui-react'; import { Container, Progress } from 'semantic-ui-react';
@ -7,67 +7,61 @@ import { Container, Progress } from 'semantic-ui-react';
import '../assets/css/loading-component.css'; import '../assets/css/loading-component.css';
// Images // Images
import ethereum_logo from '../assets/images/ethereum_logo.svg'; import ethereumLogo from '../assets/images/ethereum_logo.svg';
import ipfs_logo from '../assets/images/ipfs_logo.svg'; import ipfsLogo from '../assets/images/ipfs_logo.svg';
import orbitdb_logo from '../assets/images/orbitdb_logo.png'; import orbitdbLogo from '../assets/images/orbitdb_logo.png';
import app_logo from '../assets/images/app_logo.png'; import appLogo from '../assets/images/app_logo.png';
class LoadingComponent extends Component { const LoadingComponent = (props) => {
render(){ const {
const { image_type, message_list, progress_type } = this.props ; imageType, messageList, progressType, title, message, progress,
let imageSrc, imageAlt, listItems, indicating, error; } = props;
let imageSrc; let imageAlt; let listItems; let indicating; let
error;
if (image_type === "ethereum"){ if (imageType === 'ethereum') {
imageSrc = ethereum_logo; imageSrc = ethereumLogo;
imageAlt = "ethereum_logo"; imageAlt = 'ethereum_logo';
} } else if (imageType === 'ipfs') {
else if (image_type === "ipfs"){ imageSrc = ipfsLogo;
imageSrc = ipfs_logo; imageAlt = 'ipfs_logo';
imageAlt = "ipfs_logo"; } else if (imageType === 'orbit') {
} imageSrc = orbitdbLogo;
else if (image_type === "orbit"){ imageAlt = 'orbitdb_logo';
imageSrc = orbitdb_logo; } else if (imageType === 'app') {
imageAlt = "orbitdb_logo"; imageSrc = appLogo;
} imageAlt = 'app_logo';
else if (image_type === "app"){
imageSrc = app_logo;
imageAlt = "app_logo";
} }
if(progress_type === "indicating") if (progressType === 'indicating') indicating = true;
indicating = true; else if (progressType === 'error') error = true;
else if(progress_type === "error")
error = true;
if(message_list){ if (messageList) {
listItems = message_list.map((listItem) => listItems = messageList.map((listItem) => <li>{listItem}</li>);
<li>{listItem}</li>
);
} }
const list = message_list ? <ul>{listItems}</ul> : ''; const list = messageList ? <ul>{listItems}</ul> : '';
return( return (
<main className="loading-screen"> <main className="loading-screen">
<Container> <Container>
<img src={imageSrc} alt={imageAlt} className="loading-img" /> <img src={imageSrc} alt={imageAlt} className="loading-img" />
<p><strong>{this.props.title}</strong></p> <p><strong>{title}</strong></p>
<p>{this.props.message}</p> <p>{message}</p>
{list} {list}
</Container> </Container>
<Progress percent={this.props.progress} size='small' indicating={indicating} error={error}/> <Progress percent={progress} size="small" indicating={indicating} error={error} />
</main> </main>
); );
} };
}
LoadingComponent.propTypes = { LoadingComponent.propTypes = {
title: PropTypes.string.isRequired, title: PropTypes.string.isRequired,
message: PropTypes.string.isRequired, message: PropTypes.string.isRequired,
message_list: PropTypes.arrayOf(PropTypes.string), messageList: PropTypes.arrayOf(PropTypes.string),
image_type: PropTypes.string.isRequired, imageType: PropTypes.string.isRequired,
progress: PropTypes.number.isRequired, progress: PropTypes.number.isRequired,
progress_type: PropTypes.string.isRequired, progressType: PropTypes.string.isRequired,
}; };
export default LoadingComponent; export default LoadingComponent;

121
packages/concordia-app/src/components/LoadingContainer.jsx

@ -1,7 +1,7 @@
import React, { Children, Component } from 'react'; import React, { Children, Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { breezeConstants } from '@ezerous/breeze' import { breezeConstants } from '@ezerous/breeze';
import LoadingComponent from './LoadingComponent'; import LoadingComponent from './LoadingComponent';
@ -10,114 +10,145 @@ import '../assets/css/loading-component.css';
class LoadingContainer extends Component { class LoadingContainer extends Component {
render() { render() {
if ((this.props.web3.status === 'initializing' || !this.props.web3.networkId) const {
&& !this.props.web3.networkFailed) { web3: {
return <LoadingComponent status, networkId, networkFailed, accountsFailed,
},
drizzleStatus: {
initializing,
failed,
},
contractInitialized, contractDeployed, ipfsStatus, orbitStatus, userFetched, children,
} = this.props;
if ((status === 'initializing' || !networkId)
&& !networkFailed) {
return (
<LoadingComponent
title="Connecting to the Ethereum network..." title="Connecting to the Ethereum network..."
message="Please make sure to unlock MetaMask and grant the app the right to connect to your account." message="Please make sure to unlock MetaMask and grant the app the right to connect to your account."
image_type="ethereum" imageType="ethereum"
progress={20} progress={20}
progress_type="indicating" progressType="indicating"
/> />
);
} }
if (this.props.web3.status === 'failed' || this.props.web3.networkFailed) { if (status === 'failed' || networkFailed) {
return <LoadingComponent return (
<LoadingComponent
title="No connection to the Ethereum network!" title="No connection to the Ethereum network!"
message="Please make sure that:" message="Please make sure that:"
message_list={['MetaMask is unlocked and pointed to the correct, available network', message_list={['MetaMask is unlocked and pointed to the correct, available network',
'The app has been granted the right to connect to your account']} 'The app has been granted the right to connect to your account']}
image_type="ethereum" imageType="ethereum"
progress={20} progress={20}
progress_type="error" progressType="error"
/> />
);
} }
if (this.props.web3.status === 'initialized' && this.props.web3.accountsFailed) { if (status === 'initialized' && accountsFailed) {
return <LoadingComponent return (
<LoadingComponent
title="We can't find any Ethereum accounts!" title="We can't find any Ethereum accounts!"
message="Please make sure that MetaMask is unlocked." message="Please make sure that MetaMask is unlocked."
image_type="ethereum" imageType="ethereum"
progress={20} progress={20}
progress_type="error" progressType="error"
/> />
);
} }
if (this.props.drizzleStatus.initializing if (initializing
|| (!this.props.drizzleStatus.failed && !this.props.contractInitialized && this.props.contractDeployed )){ || (!failed && !contractInitialized && contractDeployed)) {
return <LoadingComponent return (
<LoadingComponent
title="Initializing contracts..." title="Initializing contracts..."
message="" message=""
image_type="ethereum" imageType="ethereum"
progress={40} progress={40}
progress_type="indicating" progressType="indicating"
/> />
);
} }
if (!this.props.contractDeployed) { if (!contractDeployed) {
return <LoadingComponent return (
<LoadingComponent
title="No contracts found on the current network!" title="No contracts found on the current network!"
message="Please make sure that you are connected to the correct network and the contracts are deployed." message="Please make sure that you are connected to the correct network and the contracts are deployed."
image_type="ethereum" imageType="ethereum"
progress={40} progress={40}
progress_type="error" progressType="error"
/> />
);
} }
if (this.props.ipfsStatus === breezeConstants.STATUS_INITIALIZING) { if (ipfsStatus === breezeConstants.STATUS_INITIALIZING) {
return <LoadingComponent return (
<LoadingComponent
title="Initializing IPFS..." title="Initializing IPFS..."
message="" message=""
image_type="ipfs" imageType="ipfs"
progress={60} progress={60}
progress_type="indicating" progressType="indicating"
/> />
);
} }
if (this.props.ipfsStatus === breezeConstants.STATUS_FAILED) { if (ipfsStatus === breezeConstants.STATUS_FAILED) {
return <LoadingComponent return (
<LoadingComponent
title="IPFS initialization failed!" title="IPFS initialization failed!"
message="" message=""
image_type="ipfs" imageType="ipfs"
progress={60} progress={60}
progress_type="error" progressType="error"
/> />
);
} }
if (this.props.orbitStatus === breezeConstants.STATUS_INITIALIZING) { if (orbitStatus === breezeConstants.STATUS_INITIALIZING) {
const message = process.env.NODE_ENV === 'development' const message = process.env.NODE_ENV === 'development'
? 'If needed, please sign the transaction in MetaMask to create the databases.' ? 'If needed, please sign the transaction in MetaMask to create the databases.'
: 'Please sign the transaction in MetaMask to create the databases.'; : 'Please sign the transaction in MetaMask to create the databases.';
return <LoadingComponent return (
<LoadingComponent
title="Preparing OrbitDB..." title="Preparing OrbitDB..."
message={message} message={message}
image_type="orbit" imageType="orbit"
progress={80} progress={80}
progress_type="indicating" progressType="indicating"
/> />
);
} }
if (this.props.orbitStatus === breezeConstants.STATUS_FAILED) { if (orbitStatus === breezeConstants.STATUS_FAILED) {
return <LoadingComponent return (
<LoadingComponent
title="OrbitDB initialization failed!" title="OrbitDB initialization failed!"
message="" message=""
image_type="orbit" imageType="orbit"
progress={80} progress={80}
progress_type="error" progressType="error"
/> />
);
} }
if (!this.props.userFetched){ if (!userFetched) {
return <LoadingComponent return (
<LoadingComponent
title="Loading dapp..." title="Loading dapp..."
message="" message=""
image_type="app" imageType="app"
progress={90} progress={90}
progress_type="indicating" progressType="indicating"
/> />
);
} }
return Children.only(this.props.children); return Children.only(children);
} }
} }
@ -130,7 +161,7 @@ const mapStateToProps = (state) => ({
accounts: state.accounts, accounts: state.accounts,
contractInitialized: state.contracts.Forum.initialized, contractInitialized: state.contracts.Forum.initialized,
contractDeployed: state.contracts.Forum.deployed, contractDeployed: state.contracts.Forum.deployed,
userFetched: state.user.address userFetched: state.user.address,
}); });
export default connect(mapStateToProps)(LoadingContainer); export default connect(mapStateToProps)(LoadingContainer);

35
packages/concordia-app/src/components/MenuComponent.jsx

@ -1,38 +1,35 @@
import React, { Component } from 'react'; import React from 'react';
import { withRouter } from "react-router"; import { withRouter } from 'react-router';
import { Menu } from 'semantic-ui-react'; import { Menu } from 'semantic-ui-react';
import AppContext from "./AppContext"; import AppContext from './AppContext';
import app_logo from '../assets/images/app_logo.png'; import appLogo from '../assets/images/app_logo.png';
import SignUpForm from './SignUpForm'; import SignUpForm from './SignUpForm';
class MenuComponent extends Component { const MenuComponent = (props) => {
render() { const { history: { push } } = props;
return ( return (
<AppContext.Consumer> <AppContext.Consumer>
{context => { {() => (
return(
<div> <div>
<Menu color='black' inverted> <Menu color="black" inverted>
<Menu.Item <Menu.Item
link link
name='home' name="home"
onClick={() => { this.props.history.push("/"); }} onClick={() => { push('/'); }}
> >
<img src={app_logo} alt="app_logo"/> <img src={appLogo} alt="app_logo" />
</Menu.Item> </Menu.Item>
<SignUpForm/> <SignUpForm />
</Menu> </Menu>
</div> </div>
) )}
}
}
</AppContext.Consumer> </AppContext.Consumer>
) );
} };
}
export default withRouter(MenuComponent); export default withRouter(MenuComponent);

69
packages/concordia-app/src/components/SignUpForm.jsx

@ -1,8 +1,10 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Button, Form, Menu, Message, Modal } from 'semantic-ui-react'; import {
Button, Form, Menu, Message, Modal,
} from 'semantic-ui-react';
import AppContext from "./AppContext";
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import AppContext from './AppContext';
const contractName = 'Forum'; const contractName = 'Forum';
const checkUsernameTakenMethod = 'isUserNameTaken'; const checkUsernameTakenMethod = 'isUserNameTaken';
@ -30,22 +32,8 @@ class SignUpForm extends Component {
}; };
} }
handleInputChange(e, { name, value }) { componentDidUpdate() {
this.setState({ // TODO
[name]: value,
error: false,
});
if (value !== '') {
if (this.checkedUsernames.length > 0) {
if (this.checkedUsernames.some((e) => e.usernameChecked === value)) {
return;
}
}
this.contract.methods[checkUsernameTakenMethod].cacheCall(
value,
);
}
} }
handleSubmit() { handleSubmit() {
@ -67,8 +55,22 @@ class SignUpForm extends Component {
} }
} }
componentDidUpdate() { handleInputChange(e, { name, value }) {
// TODO this.setState({
[name]: value,
error: false,
});
if (value !== '') {
if (this.checkedUsernames.length > 0) {
if (this.checkedUsernames.some((e) => e.usernameChecked === value)) {
return;
}
}
this.contract.methods[checkUsernameTakenMethod].cacheCall(
value,
);
}
} }
completeAction() { completeAction() {
@ -76,8 +78,8 @@ class SignUpForm extends Component {
const { user, account } = this.props; const { user, account } = this.props;
if (user.hasSignedUp) { if (user.hasSignedUp) {
console.log('Signing up..') console.log('Signing up..');
this.contract.methods['signUp'].cacheSend(usernameInput); this.contract.methods.signUp.cacheSend(usernameInput);
} else { } else {
this.setState({ this.setState({
signingUp: true, signingUp: true,
@ -96,21 +98,25 @@ class SignUpForm extends Component {
error, usernameInput, errorHeader, errorMessage, signingUp, error, usernameInput, errorHeader, errorMessage, signingUp,
} = this.state; } = this.state;
return( return (
<Modal as={Form} onSubmit={e => this.handleSubmit(e)} trigger={ <Modal
as={Form}
onSubmit={(e) => this.handleSubmit(e)}
trigger={(
<Menu.Item <Menu.Item
name='signup' name="signup"
position='right' position="right"
content='Sign Up' content="Sign Up"
/> />
}> )}
>
<Modal.Header>Sign Up</Modal.Header> <Modal.Header>Sign Up</Modal.Header>
<Modal.Content> <Modal.Content>
<Form.Field required> <Form.Field required>
<label>Username</label> <label>Username</label>
<Form.Input <Form.Input
placeholder='Username' placeholder="Username"
name="usernameInput" name="usernameInput"
value={usernameInput} value={usernameInput}
onChange={this.handleInputChange} onChange={this.handleInputChange}
@ -125,15 +131,14 @@ class SignUpForm extends Component {
</Modal.Content> </Modal.Content>
</Modal> </Modal>
) );
} }
} }
SignUpForm.contextType = AppContext.Context; SignUpForm.contextType = AppContext.Context;
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
user: state.user user: state.user,
}); });
export default connect(mapStateToProps)(SignUpForm); export default connect(mapStateToProps)(SignUpForm);

13
packages/concordia-app/src/index.js → packages/concordia-app/src/index.jsx

@ -1,11 +1,11 @@
import React from 'react'; import React from 'react';
import { render } from 'react-dom'; import { render } from 'react-dom';
import App from './components/App' import { Drizzle } from '@ezerous/drizzle';
import { Breeze } from '@ezerous/breeze';
import App from './components/App';
import store from './redux/store'; import store from './redux/store';
import { Drizzle } from '@ezerous/drizzle'
import { Breeze } from '@ezerous/breeze'
import AppContext from "./components/AppContext"; import AppContext from './components/AppContext';
import drizzleOptions from './options/drizzleOptions'; import drizzleOptions from './options/drizzleOptions';
import * as serviceWorker from './utils/serviceWorker'; import * as serviceWorker from './utils/serviceWorker';
@ -20,10 +20,7 @@ render(
<AppContext.Provider drizzle={drizzle} breeze={breeze}> <AppContext.Provider drizzle={drizzle} breeze={breeze}>
<App store={store} /> <App store={store} />
</AppContext.Provider>, </AppContext.Provider>,
document.getElementById('root') document.getElementById('root'),
); );
serviceWorker.unregister(); // See also: http://bit.ly/CRA-PWA serviceWorker.unregister(); // See also: http://bit.ly/CRA-PWA

22
packages/concordia-app/src/options/breezeOptions.js

@ -1,6 +1,6 @@
import { orbitConstants } from '@ezerous/breeze';
import web3Options from './web3Options'; import web3Options from './web3Options';
import EthereumIdentityProvider from '../orbit/ΕthereumIdentityProvider'; import EthereumIdentityProvider from '../orbit/ΕthereumIdentityProvider';
import { orbitConstants } from '@ezerous/breeze'
const { web3 } = web3Options; const { web3 } = web3Options;
EthereumIdentityProvider.setWeb3(web3); EthereumIdentityProvider.setWeb3(web3);
@ -12,34 +12,34 @@ const breezeOptions = {
Swarm: [ Swarm: [
// Use local signaling server (see also rendezvous script in package.json) // Use local signaling server (see also rendezvous script in package.json)
// For more information: https://github.com/libp2p/js-libp2p-webrtc-star // For more information: https://github.com/libp2p/js-libp2p-webrtc-star
'/ip4/127.0.0.1/tcp/9090/wss/p2p-webrtc-star' '/ip4/127.0.0.1/tcp/9090/wss/p2p-webrtc-star',
// Use the following public servers if needed // Use the following public servers if needed
// '/dns4/wrtc-star1.par.dwebops.pub/tcp/443/wss/p2p-webrtc-star', // '/dns4/wrtc-star1.par.dwebops.pub/tcp/443/wss/p2p-webrtc-star',
// '/dns4/ wrtc-star2.sjc.dwebops.pub/tcp/443/wss/p2p-webrtc-star' // '/dns4/ wrtc-star2.sjc.dwebops.pub/tcp/443/wss/p2p-webrtc-star'
] ],
}, },
}, },
preload: { preload: {
enabled: false enabled: false,
}, },
init: { init: {
emptyRepo: true emptyRepo: true,
} },
}, },
orbit: { orbit: {
identityProvider: EthereumIdentityProvider, identityProvider: EthereumIdentityProvider,
databases: [ databases: [
{ {
name: 'topics', name: 'topics',
type: orbitConstants.ORBIT_TYPE_KEYVALUE type: orbitConstants.ORBIT_TYPE_KEYVALUE,
}, },
{ {
name: 'posts', name: 'posts',
type: orbitConstants.ORBIT_TYPE_KEYVALUE type: orbitConstants.ORBIT_TYPE_KEYVALUE,
} },
] ],
} },
}; };
export default breezeOptions; export default breezeOptions;

6
packages/concordia-app/src/options/drizzleOptions.js

@ -4,14 +4,14 @@ import web3Options from './web3Options';
const drizzleOptions = { const drizzleOptions = {
web3: { web3: {
customProvider: web3Options.web3 customProvider: web3Options.web3,
}, },
contracts, contracts,
events: { events: {
Forum: ['UserSignedUp', 'UsernameUpdated', 'TopicCreated', 'PostCreated'] Forum: ['UserSignedUp', 'UsernameUpdated', 'TopicCreated', 'PostCreated'],
}, },
reloadWindowOnNetworkChange: true, reloadWindowOnNetworkChange: true,
reloadWindowOnAccountChange: true // We need it to reinitialize breeze and create new Orbit databases reloadWindowOnAccountChange: true, // We need it to reinitialize breeze and create new Orbit databases
}; };
export default drizzleOptions; export default drizzleOptions;

2
packages/concordia-app/src/options/web3Options.js

@ -8,7 +8,7 @@ const web3 = new Web3(Web3.givenProvider || `ws://${WEB3_URL}:${WEB3_PORT}`);
EthereumIdentityProvider.setWeb3(web3); EthereumIdentityProvider.setWeb3(web3);
const web3Options = { const web3Options = {
web3 web3,
}; };
export default web3Options; export default web3Options;

6
packages/concordia-app/src/orbit/orbitUtils.js

@ -1,7 +1,11 @@
// https://github.com/orbitdb/orbit-db/blob/master/GUIDE.md#address // https://github.com/orbitdb/orbit-db/blob/master/GUIDE.md#address
export async function determineDBAddress({orbit, dbName, type, identityId}) { async function determineDBAddress({
orbit, dbName, type, identityId,
}) {
const ipfsMultihash = (await orbit.determineAddress(dbName, type, { const ipfsMultihash = (await orbit.determineAddress(dbName, type, {
accessController: { write: [identityId] }, accessController: { write: [identityId] },
})).root; })).root;
return `/orbitdb/${ipfsMultihash}/${dbName}`; return `/orbitdb/${ipfsMultihash}/${dbName}`;
} }
export default determineDBAddress;

72
packages/concordia-app/src/orbit/ΕthereumIdentityProvider.js

@ -1,23 +1,24 @@
/* eslint-disable no-console */
/* eslint-disable no-return-await */
import IdentityProvider from 'orbit-db-identity-provider';
import { getIdentitySignaturePubKey, storeIdentitySignaturePubKey } from './levelUtils'; import { getIdentitySignaturePubKey, storeIdentitySignaturePubKey } from './levelUtils';
import IdentityProvider from "orbit-db-identity-provider";
const LOGGING_PREFIX = 'EthereumIdentityProvider: '; const LOGGING_PREFIX = 'EthereumIdentityProvider: ';
class EthereumIdentityProvider extends IdentityProvider{ class EthereumIdentityProvider extends IdentityProvider {
constructor(options = {}) { constructor(options = {}) {
if(!EthereumIdentityProvider.web3) if (!EthereumIdentityProvider.web3) {
throw new Error(LOGGING_PREFIX + "Couldn't create identity, because web3 wasn't set. " + throw new Error(`${LOGGING_PREFIX}Couldn't create identity, because web3 wasn't set. `
"Please use setWeb3(web3) first!"); + 'Please use setWeb3(web3) first!');
}
super(options); super(options);
// Orbit's Identity Id (user's Ethereum address) - Optional (will be grabbed later if omitted) // Orbit's Identity Id (user's Ethereum address) - Optional (will be grabbed later if omitted)
const id = options.id; const { id } = options;
if(id){ if (id) {
if(EthereumIdentityProvider.web3.utils.isAddress(id)) if (EthereumIdentityProvider.web3.utils.isAddress(id)) this.id = options.id;
this.id = options.id; else throw new Error(`${LOGGING_PREFIX}Couldn't create identity, because an invalid id was supplied.`);
else
throw new Error(LOGGING_PREFIX + "Couldn't create identity, because an invalid id was supplied.");
} }
} }
@ -25,19 +26,21 @@ class EthereumIdentityProvider extends IdentityProvider{
async getId() { async getId() {
// Id wasn't in the constructor, grab it now // Id wasn't in the constructor, grab it now
if(!this.id) { if (!this.id) {
const accounts = await EthereumIdentityProvider.web3.eth.getAccounts(); const accounts = await EthereumIdentityProvider.web3.eth.getAccounts();
if(!accounts[0]) if (!accounts[0]) {
throw new Error(LOGGING_PREFIX + "Couldn't create identity, because no web3 accounts were found (locked Metamask?)."); throw new Error(`${LOGGING_PREFIX}Couldn't create identity, because no web3 accounts were found (
locked Metamask?).`);
}
this.id = accounts[0]; [this.id] = accounts;
} }
return this.id; return this.id;
} }
async signIdentity(data) { async signIdentity(data) {
if (process.env.NODE_ENV === 'development') { //Don't sign repeatedly while in development if (process.env.NODE_ENV === 'development') { // Don't sign repeatedly while in development
console.debug(LOGGING_PREFIX + 'Attempting to find stored Orbit identity data...'); console.debug(`${LOGGING_PREFIX}Attempting to find stored Orbit identity data...`);
const signaturePubKey = await getIdentitySignaturePubKey(data); const signaturePubKey = await getIdentitySignaturePubKey(data);
if (signaturePubKey) { if (signaturePubKey) {
const identityInfo = { const identityInfo = {
@ -46,59 +49,58 @@ class EthereumIdentityProvider extends IdentityProvider{
signaturePubKey, signaturePubKey,
}; };
if (await EthereumIdentityProvider.verifyIdentityInfo(identityInfo)) { if (await EthereumIdentityProvider.verifyIdentityInfo(identityInfo)) {
console.debug(LOGGING_PREFIX + 'Found and verified stored Orbit identity data!'); console.debug(`${LOGGING_PREFIX}Found and verified stored Orbit identity data!`);
return signaturePubKey; return signaturePubKey;
} }
console.debug(LOGGING_PREFIX + "Stored Orbit identity data couldn't be verified."); console.debug(`${LOGGING_PREFIX}Stored Orbit identity data couldn't be verified.`);
} else } else console.debug(`${LOGGING_PREFIX}No stored Orbit identity data were found.`);
console.debug(LOGGING_PREFIX + 'No stored Orbit identity data were found.');
} }
return await this.doSignIdentity(data); return await this.doSignIdentity(data);
} }
// eslint-disable-next-line consistent-return
async doSignIdentity(data) { async doSignIdentity(data) {
try { try {
const signaturePubKey = await EthereumIdentityProvider.web3.eth.personal.sign(data, this.id, ''); const signaturePubKey = await EthereumIdentityProvider.web3.eth.personal.sign(data, this.id, '');
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
storeIdentitySignaturePubKey(data, signaturePubKey) storeIdentitySignaturePubKey(data, signaturePubKey)
.then(() => { .then(() => {
console.debug(LOGGING_PREFIX + 'Successfully stored current Orbit identity data.'); console.debug(`${LOGGING_PREFIX}Successfully stored current Orbit identity data.`);
}) })
.catch(() => { .catch(() => {
console.warn(LOGGING_PREFIX + "Couldn't store current Orbit identity data..."); console.warn(`${LOGGING_PREFIX}Couldn't store current Orbit identity data...`);
}); });
} }
return signaturePubKey; // Password not required for MetaMask return signaturePubKey; // Password not required for MetaMask
} catch (error) { } catch (error) {
if(error.code && error.code === 4001){ if (error.code && error.code === 4001) {
console.debug(LOGGING_PREFIX + 'User denied message signature.'); console.debug(`${LOGGING_PREFIX}User denied message signature.`);
return await this.doSignIdentity(data); return await this.doSignIdentity(data);
} }
else{
console.error(LOGGING_PREFIX + 'Failed to sign data.'); console.error(`${LOGGING_PREFIX}Failed to sign data.`);
console.error(error); console.error(error);
} }
} }
}
static async verifyIdentity(identity) { static async verifyIdentity(identity) {
// Verify that identity was signed by the ID // Verify that identity was signed by the ID
return new Promise(resolve => { return new Promise((resolve) => {
resolve(EthereumIdentityProvider.web3.eth.accounts.recover(identity.publicKey + identity.signatures.id, resolve(EthereumIdentityProvider.web3.eth.accounts.recover(identity.publicKey + identity.signatures.id,
identity.signatures.publicKey) === identity.id) identity.signatures.publicKey) === identity.id);
}) });
} }
static async verifyIdentityInfo(identityInfo) { static async verifyIdentityInfo(identityInfo) {
// Verify that identity was signed by the ID // Verify that identity was signed by the ID
return new Promise(resolve => { return new Promise((resolve) => {
resolve(EthereumIdentityProvider.web3.eth.accounts.recover(identityInfo.pubKeySignId, resolve(EthereumIdentityProvider.web3.eth.accounts.recover(identityInfo.pubKeySignId,
identityInfo.signaturePubKey) === identityInfo.id) identityInfo.signaturePubKey) === identityInfo.id);
}) });
} }
// Initialize by supplying a web3 object // Initialize by supplying a web3 object
static setWeb3(web3){ static setWeb3(web3) {
EthereumIdentityProvider.web3 = web3; EthereumIdentityProvider.web3 = web3;
} }
} }

8
packages/concordia-app/src/redux/reducers/userReducer.js

@ -9,12 +9,12 @@ const initialState = {
const userReducer = (state = initialState, action) => { const userReducer = (state = initialState, action) => {
const { type } = action; const { type } = action;
if(type === USER_DATA_UPDATED) { if (type === USER_DATA_UPDATED) {
const { address, username } = action; const { address, username } = action;
if(username){ if (username) {
return { return {
username: username, username,
address: address, address,
hasSignedUp: true, hasSignedUp: true,
}; };
} }

21
packages/concordia-app/src/redux/sagas/orbitSaga.js

@ -1,22 +1,21 @@
import { put, all, take } from 'redux-saga/effects' import { put, all, take } from 'redux-saga/effects';
import { breezeActions } from '@ezerous/breeze' import { breezeActions } from '@ezerous/breeze';
import { drizzleActions } from '@ezerous/drizzle' import { drizzleActions } from '@ezerous/drizzle';
function * initOrbitDatabases (action) { function* initOrbitDatabases(action) {
const { account, breeze} = action; const { account, breeze } = action;
yield put(breezeActions.orbit.orbitInit(breeze, account)); //same as breeze.initOrbit(account); yield put(breezeActions.orbit.orbitInit(breeze, account)); // same as breeze.initOrbit(account);
} }
function * orbitSaga () { function* orbitSaga() {
const res = yield all([ const res = yield all([
take(drizzleActions.drizzle.DRIZZLE_INITIALIZED), take(drizzleActions.drizzle.DRIZZLE_INITIALIZED),
take(breezeActions.breeze.BREEZE_INITIALIZED), take(breezeActions.breeze.BREEZE_INITIALIZED),
take(drizzleActions.account.ACCOUNTS_FETCHED) take(drizzleActions.account.ACCOUNTS_FETCHED),
]); ]);
yield initOrbitDatabases({breeze:res[1].breeze, account: res[2].accounts[0]}); yield initOrbitDatabases({ breeze: res[1].breeze, account: res[2].accounts[0] });
} }
export default orbitSaga export default orbitSaga;

8
packages/concordia-app/src/redux/sagas/rootSaga.js

@ -1,15 +1,15 @@
import { all, fork } from 'redux-saga/effects'; import { all, fork } from 'redux-saga/effects';
import { drizzleSagas } from '@ezerous/drizzle'; import { drizzleSagas } from '@ezerous/drizzle';
import { breezeSagas } from '@ezerous/breeze' import { breezeSagas } from '@ezerous/breeze';
import orbitSaga from './orbitSaga' import orbitSaga from './orbitSaga';
import userSaga from './userSaga' import userSaga from './userSaga';
export default function* root() { export default function* root() {
const sagas = [ const sagas = [
...drizzleSagas, ...drizzleSagas,
...breezeSagas, ...breezeSagas,
orbitSaga, orbitSaga,
userSaga userSaga,
]; ];
yield all( yield all(
sagas.map((saga) => fork(saga)), sagas.map((saga) => fork(saga)),

23
packages/concordia-app/src/redux/sagas/userSaga.js

@ -1,10 +1,13 @@
import { all, call, put, take } from 'redux-saga/effects'; /* eslint-disable no-console */
import {
all, call, put, take,
} from 'redux-saga/effects';
import { drizzleActions } from '@ezerous/drizzle'; import { drizzleActions } from '@ezerous/drizzle';
import { USER_DATA_UPDATED, USER_DATA_ERROR } from '../actions/userActions'; import { USER_DATA_UPDATED, USER_DATA_ERROR } from '../actions/userActions';
function * fetchUserData ({drizzle, account}) { function* fetchUserData({ drizzle, account }) {
const contract = drizzle.contracts['Forum']; const contract = drizzle.contracts.Forum;
const transaction = yield call(contract.methods.hasUserSignedUp, account); const transaction = yield call(contract.methods.hasUserSignedUp, account);
try { try {
@ -15,28 +18,26 @@ function * fetchUserData ({drizzle, account}) {
if (callResult) { if (callResult) {
const txObj2 = yield call(contract.methods.getUsername, account); const txObj2 = yield call(contract.methods.getUsername, account);
dispatchArgs.username = yield call(txObj2.call, { dispatchArgs.username = yield call(txObj2.call, {
address: account address: account,
}); });
} }
yield put({ yield put({
type: USER_DATA_UPDATED, ...dispatchArgs type: USER_DATA_UPDATED, ...dispatchArgs,
}); });
} catch (error) { } catch (error) {
console.error(error); console.error(error);
yield put({ type: USER_DATA_ERROR }); yield put({ type: USER_DATA_ERROR });
} }
} }
function* userSaga() {
function * userSaga () {
const res = yield all([ const res = yield all([
take(drizzleActions.drizzle.DRIZZLE_INITIALIZED), take(drizzleActions.drizzle.DRIZZLE_INITIALIZED),
take(drizzleActions.account.ACCOUNTS_FETCHED) take(drizzleActions.account.ACCOUNTS_FETCHED),
]); ]);
yield fetchUserData({drizzle:res[0].drizzle, account: res[1].accounts[0]}); yield fetchUserData({ drizzle: res[0].drizzle, account: res[1].accounts[0] });
} }
export default userSaga export default userSaga;

13
packages/concordia-app/src/redux/store.js

@ -1,8 +1,8 @@
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'; import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';
import { drizzleReducers, drizzleMiddlewares, generateContractsInitialState } from '@ezerous/drizzle'; import { drizzleReducers, drizzleMiddlewares, generateContractsInitialState } from '@ezerous/drizzle';
import { breezeReducers } from '@ezerous/breeze' import { breezeReducers } from '@ezerous/breeze';
import userReducer from './reducers/userReducer'
import createSagaMiddleware from 'redux-saga'; import createSagaMiddleware from 'redux-saga';
import userReducer from './reducers/userReducer';
import rootSaga from './sagas/rootSaga'; import rootSaga from './sagas/rootSaga';
import drizzleOptions from '../options/drizzleOptions'; import drizzleOptions from '../options/drizzleOptions';
@ -13,12 +13,13 @@ const initialState = {
const sagaMiddleware = createSagaMiddleware(); const sagaMiddleware = createSagaMiddleware();
const store = configureStore({ const store = configureStore({
reducer: {...drizzleReducers, ...breezeReducers, user: userReducer }, reducer: { ...drizzleReducers, ...breezeReducers, user: userReducer },
middleware: getDefaultMiddleware({ middleware: getDefaultMiddleware({
serializableCheck: false, //https://redux.js.org/style-guide/style-guide/#do-not-put-non-serializable-values-in-state-or-actions // https://redux.js.org/style-guide/style-guide/#do-not-put-non-serializable-values-in-state-or-actions
serializableCheck: false,
}).concat(drizzleMiddlewares).concat(sagaMiddleware), }).concat(drizzleMiddlewares).concat(sagaMiddleware),
preloadedState: initialState preloadedState: initialState,
}) });
sagaMiddleware.run(rootSaga); sagaMiddleware.run(rootSaga);
export default store; export default store;

38
packages/concordia-app/src/utils/serviceWorker.js

@ -1,3 +1,6 @@
/* eslint-disable no-console */
/* eslint-disable no-use-before-define */
// This optional code is used to register a service worker. // This optional code is used to register a service worker.
// register() is not called by default. // register() is not called by default.
@ -11,13 +14,13 @@
// opt-in, read https://bit.ly/CRA-PWA // opt-in, read https://bit.ly/CRA-PWA
const isLocalhost = Boolean( const isLocalhost = Boolean(
window.location.hostname === 'localhost' || window.location.hostname === 'localhost'
// [::1] is the IPv6 localhost address. // [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' || || window.location.hostname === '[::1]'
// 127.0.0.1/8 is considered localhost for IPv4. // 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match( || window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/,
) ),
); );
export function register(config) { export function register(config) {
@ -42,8 +45,8 @@ export function register(config) {
// service worker/PWA documentation. // service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => { navigator.serviceWorker.ready.then(() => {
console.log( console.log(
'This web app is being served cache-first by a service ' + 'This web app is being served cache-first by a service '
'worker. To learn more, visit https://bit.ly/CRA-PWA' + 'worker. To learn more, visit https://bit.ly/CRA-PWA',
); );
}); });
} else { } else {
@ -57,7 +60,8 @@ export function register(config) {
function registerValidSW(swUrl, config) { function registerValidSW(swUrl, config) {
navigator.serviceWorker navigator.serviceWorker
.register(swUrl) .register(swUrl)
.then(registration => { .then((registration) => {
// eslint-disable-next-line no-param-reassign
registration.onupdatefound = () => { registration.onupdatefound = () => {
const installingWorker = registration.installing; const installingWorker = registration.installing;
if (installingWorker == null) { if (installingWorker == null) {
@ -70,8 +74,8 @@ function registerValidSW(swUrl, config) {
// but the previous service worker will still serve the older // but the previous service worker will still serve the older
// content until all client tabs are closed. // content until all client tabs are closed.
console.log( console.log(
'New content is available and will be used when all ' + 'New content is available and will be used when all '
'tabs for this page are closed. See https://bit.ly/CRA-PWA.' + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.',
); );
// Execute callback // Execute callback
@ -93,7 +97,7 @@ function registerValidSW(swUrl, config) {
}; };
}; };
}) })
.catch(error => { .catch((error) => {
console.error('Error during service worker registration:', error); console.error('Error during service worker registration:', error);
}); });
} }
@ -101,15 +105,15 @@ function registerValidSW(swUrl, config) {
function checkValidServiceWorker(swUrl, config) { function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page. // Check if the service worker can be found. If it can't reload the page.
fetch(swUrl) fetch(swUrl)
.then(response => { .then((response) => {
// Ensure service worker exists, and that we really are getting a JS file. // Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type'); const contentType = response.headers.get('content-type');
if ( if (
response.status === 404 || response.status === 404
(contentType != null && contentType.indexOf('javascript') === -1) || (contentType != null && contentType.indexOf('javascript') === -1)
) { ) {
// No service worker found. Probably a different app. Reload the page. // No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => { navigator.serviceWorker.ready.then((registration) => {
registration.unregister().then(() => { registration.unregister().then(() => {
window.location.reload(); window.location.reload();
}); });
@ -121,14 +125,14 @@ function checkValidServiceWorker(swUrl, config) {
}) })
.catch(() => { .catch(() => {
console.log( console.log(
'No internet connection found. App is running in offline mode.' 'No internet connection found. App is running in offline mode.',
); );
}); });
} }
export function unregister() { export function unregister() {
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => { navigator.serviceWorker.ready.then((registration) => {
registration.unregister(); registration.unregister();
}); });
} }

Loading…
Cancel
Save