Web3 Only Auth + MetaMask future proofing (#187)
* Setup ethereum.enable() flow, change login to only use web3. * Fix sign in icon to be white again.
This commit is contained in:
parent
79773ea4da
commit
0670eaa054
|
@ -48,9 +48,12 @@ class SignIn extends React.Component<Props> {
|
|||
</Button>
|
||||
</div>
|
||||
|
||||
<p className="SignIn-back">
|
||||
Want to use a different identity? <a onClick={this.props.reset}>Click here</a>.
|
||||
</p>
|
||||
{/*
|
||||
Temporarily only supporting web3, so there are no other identites
|
||||
<p className="SignIn-back">
|
||||
Want to use a different identity? <a onClick={this.props.reset}>Click here</a>.
|
||||
</p>
|
||||
*/}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -101,9 +101,12 @@ class SignUp extends React.Component<Props, State> {
|
|||
</Form>
|
||||
</div>
|
||||
|
||||
<p className="SignUp-back">
|
||||
Want to use a different identity? <a onClick={this.props.reset}>Click here</a>.
|
||||
</p>
|
||||
{/*
|
||||
Temporarily only supporting web3, so there are no other identites
|
||||
<p className="SignUp-back">
|
||||
Want to use a different identity? <a onClick={this.props.reset}>Click here</a>.
|
||||
</p>
|
||||
*/}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -28,7 +28,8 @@ interface State {
|
|||
}
|
||||
|
||||
const DEFAULT_STATE: State = {
|
||||
provider: null,
|
||||
// Temporarily hardcode to web3, change to null when others are supported
|
||||
provider: AUTH_PROVIDER.WEB3,
|
||||
address: null,
|
||||
};
|
||||
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Button, Alert } from 'antd';
|
||||
import { web3Actions } from 'modules/web3';
|
||||
import { Button, Alert, Spin } from 'antd';
|
||||
import { enableWeb3 } from 'modules/web3/actions';
|
||||
import { AppState } from 'store/reducers';
|
||||
import MetamaskIcon from 'static/images/metamask.png';
|
||||
import './Web3.less';
|
||||
|
||||
interface StateProps {
|
||||
accounts: AppState['web3']['accounts'];
|
||||
isWeb3Locked: AppState['web3']['isWeb3Locked'];
|
||||
isEnablingWeb3: AppState['web3']['isEnablingWeb3'];
|
||||
accountsLoading: AppState['web3']['accountsLoading'];
|
||||
web3EnableError: AppState['web3']['web3EnableError'];
|
||||
accountsError: AppState['web3']['accountsError'];
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
setWeb3: typeof web3Actions['setWeb3'];
|
||||
setAccounts: typeof web3Actions['setAccounts'];
|
||||
enableWeb3: typeof enableWeb3;
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
|
@ -23,6 +25,12 @@ interface OwnProps {
|
|||
type Props = StateProps & DispatchProps & OwnProps;
|
||||
|
||||
class Web3Provider extends React.Component<Props> {
|
||||
componentWillMount() {
|
||||
if (!this.props.accounts || !this.props.accounts[0]) {
|
||||
this.props.enableWeb3();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { accounts } = this.props;
|
||||
if (accounts && accounts[0]) {
|
||||
|
@ -31,42 +39,52 @@ class Web3Provider extends React.Component<Props> {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { isWeb3Locked } = this.props;
|
||||
const {
|
||||
isEnablingWeb3,
|
||||
accountsLoading,
|
||||
web3EnableError,
|
||||
accountsError,
|
||||
} = this.props;
|
||||
const isLoading = isEnablingWeb3 || accountsLoading;
|
||||
const error = web3EnableError || accountsError;
|
||||
return (
|
||||
<div className="Web3Provider">
|
||||
<img className="Web3Provider-logo" src={MetamaskIcon} />
|
||||
<p className="Web3Provider-description">
|
||||
Make sure you have MetaMask or another web3 provider installed and unlocked,
|
||||
then click below.
|
||||
</p>
|
||||
{isWeb3Locked && (
|
||||
<Alert
|
||||
showIcon
|
||||
type="error"
|
||||
message="It looks like MetaMask is locked"
|
||||
style={{ margin: '1rem auto' }}
|
||||
/>
|
||||
{isLoading ? (
|
||||
<Spin tip="Connecting..." />
|
||||
) : (
|
||||
<>
|
||||
<img className="Web3Provider-logo" src={MetamaskIcon} />
|
||||
<p className="Web3Provider-description">
|
||||
Make sure you have MetaMask or another web3 provider installed and unlocked,
|
||||
then click below.
|
||||
</p>
|
||||
{error && (
|
||||
<Alert
|
||||
showIcon
|
||||
type="error"
|
||||
message={error}
|
||||
style={{ margin: '1rem auto' }}
|
||||
/>
|
||||
)}
|
||||
<Button type="primary" size="large" onClick={this.props.enableWeb3}>
|
||||
Connect to Web3
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button type="primary" size="large" onClick={this.connect}>
|
||||
Connect to Web3
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private connect = () => {
|
||||
this.props.setWeb3();
|
||||
this.props.setAccounts();
|
||||
};
|
||||
}
|
||||
|
||||
export default connect<StateProps, DispatchProps, OwnProps, AppState>(
|
||||
state => ({
|
||||
accounts: state.web3.accounts,
|
||||
isWeb3Locked: state.web3.isWeb3Locked,
|
||||
isEnablingWeb3: state.web3.isEnablingWeb3,
|
||||
accountsLoading: state.web3.accountsLoading,
|
||||
web3EnableError: state.web3.web3EnableError,
|
||||
accountsError: state.web3.accountsError,
|
||||
}),
|
||||
{
|
||||
setWeb3: web3Actions.setWeb3,
|
||||
setAccounts: web3Actions.setAccounts,
|
||||
enableWeb3,
|
||||
},
|
||||
)(Web3Provider);
|
||||
|
|
|
@ -14,10 +14,6 @@
|
|||
text-shadow: none;
|
||||
box-shadow: 0 1px rgba(0, 0, 0, 0.1);
|
||||
|
||||
svg {
|
||||
fill: #333;
|
||||
}
|
||||
|
||||
&.is-transparent {
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
|
@ -58,6 +54,7 @@
|
|||
height: 2rem;
|
||||
width: auto;
|
||||
transform: translateY(10%);
|
||||
fill: #333;
|
||||
|
||||
.is-transparent & {
|
||||
transform: scale(1.4) translateY(20%);
|
||||
|
|
|
@ -33,6 +33,10 @@ export function setWeb3() {
|
|||
};
|
||||
}
|
||||
|
||||
export function enableWeb3() {
|
||||
return { type: types.ENABLE_WEB3_PENDING };
|
||||
}
|
||||
|
||||
export type TSetContract = typeof setContract;
|
||||
export function setContract(json: any, deployedAddress?: string) {
|
||||
return (dispatch: Dispatch<any>, getState: GetState) => {
|
||||
|
|
|
@ -11,6 +11,9 @@ export interface Web3State {
|
|||
isWrongNetwork: boolean;
|
||||
isWeb3Locked: boolean;
|
||||
|
||||
isEnablingWeb3: boolean;
|
||||
web3EnableError: null | string;
|
||||
|
||||
contracts: Contract[];
|
||||
contractsLoading: boolean;
|
||||
contractsError: null | string;
|
||||
|
@ -39,6 +42,9 @@ export const INITIAL_STATE: Web3State = {
|
|||
isWrongNetwork: false,
|
||||
isWeb3Locked: false,
|
||||
|
||||
isEnablingWeb3: false,
|
||||
web3EnableError: null,
|
||||
|
||||
contracts: [],
|
||||
contractsLoading: false,
|
||||
contractsError: null,
|
||||
|
@ -96,6 +102,23 @@ export default (state = INITIAL_STATE, action: any): Web3State => {
|
|||
isMissingWeb3: true,
|
||||
};
|
||||
|
||||
case types.ENABLE_WEB3_PENDING:
|
||||
return {
|
||||
...state,
|
||||
isEnablingWeb3: true,
|
||||
};
|
||||
case types.ENABLE_WEB3_FULFILLED:
|
||||
return {
|
||||
...state,
|
||||
isEnablingWeb3: false,
|
||||
};
|
||||
case types.ENABLE_WEB3_REJECTED:
|
||||
return {
|
||||
...state,
|
||||
isEnablingWeb3: false,
|
||||
web3EnableError: action.payload,
|
||||
};
|
||||
|
||||
case types.CROWD_FUND_PENDING:
|
||||
return {
|
||||
...state,
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { SagaIterator } from 'redux-saga';
|
||||
import { put, all, fork, take } from 'redux-saga/effects';
|
||||
import { put, all, fork, take, takeLatest, select, call } from 'redux-saga/effects';
|
||||
import { setWeb3, setAccounts, setContract } from './actions';
|
||||
import { selectWeb3 } from './selectors';
|
||||
import { safeEnable } from 'utils/web3';
|
||||
import types from './types';
|
||||
|
||||
/* tslint:disable no-var-requires --- TODO: find a better way to import contract */
|
||||
|
@ -18,6 +20,30 @@ export function* bootstrapWeb3(): SagaIterator {
|
|||
yield all([put<any>(setAccounts()), put<any>(setContract(CrowdFundFactory))]);
|
||||
}
|
||||
|
||||
export function* handleEnableWeb3(): SagaIterator {
|
||||
const web3 = yield select(selectWeb3);
|
||||
|
||||
try {
|
||||
if (!web3) {
|
||||
const web3Action = yield take([types.WEB3_FULFILLED, types.WEB3_REJECTED]);
|
||||
if (web3Action.type === types.WEB3_REJECTED) {
|
||||
throw new Error('No web3 instance available');
|
||||
}
|
||||
}
|
||||
|
||||
yield call(safeEnable);
|
||||
yield put<any>(setAccounts());
|
||||
yield put({ type: types.ENABLE_WEB3_FULFILLED });
|
||||
} catch (err) {
|
||||
yield put({
|
||||
type: types.ENABLE_WEB3_REJECTED,
|
||||
payload: err.message || err.toString(),
|
||||
error: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default function* authSaga(): SagaIterator {
|
||||
yield all([fork(bootstrapWeb3)]);
|
||||
yield takeLatest(types.ENABLE_WEB3_PENDING, handleEnableWeb3);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import { AppState } from 'store/reducers';
|
||||
|
||||
export function selectWeb3(state: AppState) {
|
||||
return state.web3.web3;
|
||||
}
|
||||
|
||||
export function findContract(state: AppState, contractAddress: string) {
|
||||
const { contracts } = state.web3;
|
||||
return contracts.find(contract => contract._address === contractAddress);
|
||||
|
|
|
@ -4,6 +4,11 @@ enum web3Types {
|
|||
WEB3_REJECTED = 'WEB3_REJECTED',
|
||||
WEB3_PENDING = 'WEB3_PENDING',
|
||||
|
||||
ENABLE_WEB3 = 'ENABLE_WEB3',
|
||||
ENABLE_WEB3_PENDING = 'ENABLE_WEB3_PENDING',
|
||||
ENABLE_WEB3_REJECTED = 'ENABLE_WEB3_REJECTED',
|
||||
ENABLE_WEB3_FULFILLED = 'ENABLE_WEB3_FULFILLED',
|
||||
|
||||
CONTRACT = 'CONTRACT',
|
||||
CONTRACT_FULFILLED = 'CONTRACT_FULFILLED',
|
||||
CONTRACT_REJECTED = 'CONTRACT_REJECTED',
|
||||
|
|
|
@ -32,3 +32,12 @@ export function web3ErrorToString(err: Web3ErrorResponse): string {
|
|||
.slice(-1)[0]
|
||||
.trim();
|
||||
}
|
||||
|
||||
export function safeEnable(): Promise<void> {
|
||||
const w = typeof window === 'undefined' ? {} : (window as any);
|
||||
if (!w.ethereum || !w.ethereum.enable) {
|
||||
return Promise.reject('No web3 client installed');
|
||||
}
|
||||
|
||||
return w.ethereum.enable();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue