midway through auth rewrite
This commit is contained in:
parent
ddd01855b0
commit
e0b8afe028
14 changed files with 480 additions and 52 deletions
|
@ -9,7 +9,18 @@ import {
|
||||||
selectCurrentPage,
|
selectCurrentPage,
|
||||||
selectCurrentParams,
|
selectCurrentParams,
|
||||||
} from "selectors/app";
|
} from "selectors/app";
|
||||||
import { doSearch } from "actions/search";
|
import {
|
||||||
|
doSearch,
|
||||||
|
} from 'actions/search'
|
||||||
|
import {
|
||||||
|
doFetchDaemonSettings
|
||||||
|
} from 'actions/settings'
|
||||||
|
import {
|
||||||
|
doAuthenticate
|
||||||
|
} from 'actions/user'
|
||||||
|
import {
|
||||||
|
doFileList
|
||||||
|
} from 'actions/file_info'
|
||||||
|
|
||||||
const { remote, ipcRenderer, shell } = require("electron");
|
const { remote, ipcRenderer, shell } = require("electron");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
@ -216,9 +227,15 @@ export function doAlertError(errorList) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doDaemonReady() {
|
export function doDaemonReady() {
|
||||||
return {
|
return function(dispatch, getState) {
|
||||||
type: types.DAEMON_READY,
|
dispatch({
|
||||||
};
|
type: types.DAEMON_READY
|
||||||
|
})
|
||||||
|
dispatch(doAuthenticate())
|
||||||
|
dispatch(doChangePath('/discover'))
|
||||||
|
dispatch(doFetchDaemonSettings())
|
||||||
|
dispatch(doFileList())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doShowSnackBar(data) {
|
export function doShowSnackBar(data) {
|
||||||
|
|
21
ui/js/actions/user.js
Normal file
21
ui/js/actions/user.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import * as types from 'constants/action_types'
|
||||||
|
import lbryio from 'lbryio'
|
||||||
|
|
||||||
|
export function doAuthenticate() {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
dispatch({
|
||||||
|
type: types.AUTHENTICATION_STARTED,
|
||||||
|
})
|
||||||
|
lbryio.authenticate().then((user) => {
|
||||||
|
dispatch({
|
||||||
|
type: types.AUTHENTICATION_SUCCESS,
|
||||||
|
data: { user }
|
||||||
|
})
|
||||||
|
}).catch((error) => {
|
||||||
|
dispatch({
|
||||||
|
type: types.AUTHENTICATION_FAILURE,
|
||||||
|
data: { error }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
21
ui/js/component/authOverlay/index.jsx
Normal file
21
ui/js/component/authOverlay/index.jsx
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
connect
|
||||||
|
} from 'react-redux'
|
||||||
|
import {
|
||||||
|
doStartUpgrade,
|
||||||
|
doCancelUpgrade,
|
||||||
|
} from 'actions/app'
|
||||||
|
import {
|
||||||
|
selectAuthenticationIsPending,
|
||||||
|
} from 'selectors/user'
|
||||||
|
import AuthOverlay from './view'
|
||||||
|
|
||||||
|
const select = (state) => ({
|
||||||
|
isPending: selectAuthenticationIsPending(state)
|
||||||
|
})
|
||||||
|
|
||||||
|
const perform = (dispatch) => ({
|
||||||
|
})
|
||||||
|
|
||||||
|
export default connect(select, perform)(AuthOverlay)
|
350
ui/js/component/authOverlay/view.jsx
Normal file
350
ui/js/component/authOverlay/view.jsx
Normal file
|
@ -0,0 +1,350 @@
|
||||||
|
import React from "react";
|
||||||
|
import lbry from "lbry.js";
|
||||||
|
import lbryio from "lbryio.js";
|
||||||
|
import Modal from "component/modal.js";
|
||||||
|
import ModalPage from "component/modal-page.js";
|
||||||
|
import Link from "component/link"
|
||||||
|
import {BusyMessage} from "component/common"
|
||||||
|
import {RewardLink} from 'component/reward-link';
|
||||||
|
import {FormRow} from "component/form.js";
|
||||||
|
import {CreditAmount, Address} from "component/common.js";
|
||||||
|
import {getLocal, setLocal} from 'utils.js';
|
||||||
|
import rewards from 'rewards'
|
||||||
|
|
||||||
|
|
||||||
|
class SubmitEmailStage extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
rewardType: null,
|
||||||
|
email: '',
|
||||||
|
showNoEmailConfirm: false,
|
||||||
|
submitting: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEmailChanged(event) {
|
||||||
|
this.setState({
|
||||||
|
email: event.target.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onEmailSaved(email) {
|
||||||
|
this.props.setStage("confirm", { email: email })
|
||||||
|
}
|
||||||
|
|
||||||
|
onEmailSkipClick() {
|
||||||
|
this.setState({ showNoEmailConfirm: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
onEmailSkipConfirm() {
|
||||||
|
setLocal('auth_bypassed', true);
|
||||||
|
this.props.setStage(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
submitting: true,
|
||||||
|
});
|
||||||
|
lbryio.call('user_email', 'new', {email: this.state.email}, 'post').then(() => {
|
||||||
|
this.onEmailSaved(this.state.email);
|
||||||
|
}, (error) => {
|
||||||
|
if (error.xhr && (error.xhr.status == 409 || error.message == "This email is already in use")) {
|
||||||
|
this.onEmailSaved(this.state.email);
|
||||||
|
return;
|
||||||
|
} else if (this._emailRow) {
|
||||||
|
this._emailRow.showError(error.message)
|
||||||
|
}
|
||||||
|
this.setState({ submitting: false });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<form onSubmit={(event) => { this.handleSubmit(event) }}>
|
||||||
|
<FormRow ref={(ref) => { this._emailRow = ref }} type="text" label="Email" placeholder="scrwvwls@lbry.io"
|
||||||
|
name="email" value={this.state.email}
|
||||||
|
onChange={(event) => { this.handleEmailChanged(event) }} />
|
||||||
|
<div className="form-row-submit form-row-submit--with-footer">
|
||||||
|
<Link button="primary" label="Next" disabled={this.state.submitting} onClick={(event) => { this.handleSubmit(event) }} />
|
||||||
|
</div>
|
||||||
|
{ this.state.showNoEmailConfirm ?
|
||||||
|
<div>
|
||||||
|
<p className="help form-input-width">If you continue without an email, you will be ineligible to earn free LBC rewards, as well as unable to receive security related communications.</p>
|
||||||
|
<Link className="button-text-help" onClick={ () => { this.onEmailSkipConfirm() }} label="Continue without email" />
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<Link className="button-text-help" onClick={ () => { this.onEmailSkipClick() }} label="Do I have to?" />
|
||||||
|
}
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfirmEmailStage extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
rewardType: null,
|
||||||
|
code: '',
|
||||||
|
submitting: false,
|
||||||
|
errorMessage: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCodeChanged(event) {
|
||||||
|
this.setState({
|
||||||
|
code: event.target.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.setState({
|
||||||
|
submitting: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmitError = (error) => {
|
||||||
|
if (this._codeRow) {
|
||||||
|
this._codeRow.showError(error.message)
|
||||||
|
}
|
||||||
|
this.setState({ submitting: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
lbryio.call('user_email', 'confirm', {verification_token: this.state.code, email: this.props.email}, 'post').then((userEmail) => {
|
||||||
|
if (userEmail.is_verified) {
|
||||||
|
this.props.setStage("welcome")
|
||||||
|
} else {
|
||||||
|
onSubmitError(new Error("Your email is still not verified.")) //shouldn't happen?
|
||||||
|
}
|
||||||
|
}, onSubmitError);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<form onSubmit={(event) => { this.handleSubmit(event) }}>
|
||||||
|
<FormRow label="Verification Code" ref={(ref) => { this._codeRow = ref }} type="text"
|
||||||
|
name="code" placeholder="a94bXXXXXXXXXXXXXX" value={this.state.code} onChange={(event) => { this.handleCodeChanged(event) }}
|
||||||
|
helper="A verification code is required to access this version."/>
|
||||||
|
<div className="form-row-submit form-row-submit--with-footer">
|
||||||
|
<Link button="primary" label="Verify" disabled={this.state.submitting} onClick={(event) => { this.handleSubmit(event)}} />
|
||||||
|
</div>
|
||||||
|
<div className="form-field__helper">
|
||||||
|
No code? <Link onClick={() => { this.props.setStage("nocode")}} label="Click here" />.
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WelcomeStage extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
endAuth: React.PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
hasReward: false,
|
||||||
|
rewardAmount: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onRewardClaim(reward) {
|
||||||
|
this.setState({
|
||||||
|
hasReward: true,
|
||||||
|
rewardAmount: reward.amount
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
!this.state.hasReward ?
|
||||||
|
<Modal type="custom" isOpen={true} contentLabel="Welcome to LBRY" {...this.props}>
|
||||||
|
<section>
|
||||||
|
<h3 className="modal__header">Welcome to LBRY.</h3>
|
||||||
|
<p>Using LBRY is like dating a centaur. Totally normal up top, and <em>way different</em> underneath.</p>
|
||||||
|
<p>Up top, LBRY is similar to popular media sites.</p>
|
||||||
|
<p>Below, LBRY is controlled by users -- you -- via blockchain and decentralization.</p>
|
||||||
|
<p>Thank you for making content freedom possible! Here's a nickel, kid.</p>
|
||||||
|
<div style={{textAlign: "center", marginBottom: "12px"}}>
|
||||||
|
<RewardLink type="new_user" button="primary" onRewardClaim={(event) => { this.onRewardClaim(event) }} onRewardFailure={() => this.props.setStage(null)} onConfirmed={() => { this.props.setStage(null) }} />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</Modal> :
|
||||||
|
<Modal type="alert" overlayClassName="modal-overlay modal-overlay--clear" isOpen={true} contentLabel="Welcome to LBRY" {...this.props} onConfirmed={() => { this.props.setStage(null) }}>
|
||||||
|
<section>
|
||||||
|
<h3 className="modal__header">About Your Reward</h3>
|
||||||
|
<p>You earned a reward of <CreditAmount amount={this.state.rewardAmount} label={false} /> LBRY credits, or <em>LBC</em>.</p>
|
||||||
|
<p>This reward will show in your Wallet momentarily, probably while you are reading this message.</p>
|
||||||
|
<p>LBC is used to compensate creators, to publish, and to have say in how the network works.</p>
|
||||||
|
<p>No need to understand it all just yet! Try watching or downloading something next.</p>
|
||||||
|
<p>Finally, know that LBRY is an early beta and that it earns the name.</p>
|
||||||
|
</section>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ErrorStage = (props) => {
|
||||||
|
return <section>
|
||||||
|
<p>An error was encountered that we cannot continue from.</p>
|
||||||
|
<p>At least we're earning the name beta.</p>
|
||||||
|
{ props.errorText ? <p>Message: {props.errorText}</p> : '' }
|
||||||
|
<Link button="alt" label="Try Reload" onClick={() => { window.location.reload() } } />
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
|
||||||
|
const PendingStage = (props) => {
|
||||||
|
return <section>
|
||||||
|
<BusyMessage message="Authenticating" />
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CodeRequiredStage extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this._balanceSubscribeId = null
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
balance: 0,
|
||||||
|
address: getLocal('wallet_address')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this._balanceSubscribeId = lbry.balanceSubscribe((balance) => {
|
||||||
|
this.setState({
|
||||||
|
balance: balance
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!this.state.address) {
|
||||||
|
lbry.wallet_unused_address().then((address) => {
|
||||||
|
setLocal('wallet_address', address);
|
||||||
|
this.setState({ address: address });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this._balanceSubscribeId) {
|
||||||
|
lbry.balanceUnsubscribe(this._balanceSubscribeId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const disabled = this.state.balance < 1;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<section className="section-spaced">
|
||||||
|
<p>Early access to LBRY is restricted as we build and scale the network.</p>
|
||||||
|
<p>There are two ways in.</p>
|
||||||
|
<h3>Own LBRY Credits</h3>
|
||||||
|
<p>If you own at least 1 LBC, you can get in right now.</p>
|
||||||
|
<p style={{ textAlign: "center"}}><Link onClick={() => { setLocal('auth_bypassed', true); this.props.setStage(null); }}
|
||||||
|
disabled={disabled} label="Let Me In" button={ disabled ? "alt" : "primary" } /></p>
|
||||||
|
<p>Your balance is <CreditAmount amount={this.state.balance} />. To increase your balance, send credits to this address:</p>
|
||||||
|
<p><Address address={ this.state.address ? this.state.address : "Generating Address..." } /></p>
|
||||||
|
<p>If you don't understand how to send credits, then...</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h3>Wait For A Code</h3>
|
||||||
|
<p>If you provide your email, you'll automatically receive a notification when the system is open.</p>
|
||||||
|
<p><Link onClick={() => { this.props.setStage("email"); }} label="Return" /></p>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class AuthOverlay extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this._stages = {
|
||||||
|
pending: PendingStage,
|
||||||
|
error: ErrorStage,
|
||||||
|
nocode: CodeRequiredStage,
|
||||||
|
email: SubmitEmailStage,
|
||||||
|
confirm: ConfirmEmailStage,
|
||||||
|
welcome: WelcomeStage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setStage(stage, stageProps = {}) {
|
||||||
|
this.setState({
|
||||||
|
stage: stage,
|
||||||
|
stageProps: stageProps
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
// lbryio.authenticate().then((user) => {
|
||||||
|
// if (!user.has_verified_email) {
|
||||||
|
// if (getLocal('auth_bypassed')) {
|
||||||
|
// this.setStage(null)
|
||||||
|
// } else {
|
||||||
|
// this.setStage("email", {})
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// lbryio.call('reward', 'list', {}).then((userRewards) => {
|
||||||
|
// userRewards.filter(function(reward) {
|
||||||
|
// return reward.reward_type == rewards.TYPE_NEW_USER && reward.transaction_id;
|
||||||
|
// }).length ?
|
||||||
|
// this.setStage(null) :
|
||||||
|
// this.setStage("welcome")
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }).catch((err) => {
|
||||||
|
// this.setStage("error", { errorText: err.message })
|
||||||
|
// document.dispatchEvent(new CustomEvent('unhandledError', {
|
||||||
|
// detail: {
|
||||||
|
// message: err.message,
|
||||||
|
// data: err.stack
|
||||||
|
// }
|
||||||
|
// }));
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let StageContent
|
||||||
|
const {
|
||||||
|
isPending,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
if (isPending) {
|
||||||
|
StageContent = PendingStage;
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
StageContent = this._stages[this.state.stage];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StageContent) {
|
||||||
|
return <span className="empty">Unknown authentication step.</span>
|
||||||
|
}
|
||||||
|
//setStage={(stage, stageProps) => { this.setStage(stage, stageProps) }} {...this.state.stageProps}
|
||||||
|
return (
|
||||||
|
true || this.state.stage != "welcome" ?
|
||||||
|
<ModalPage className="modal-page--full" isOpen={true} contentLabel="Authentication">
|
||||||
|
<h1>LBRY Early Access</h1>
|
||||||
|
<StageContent />
|
||||||
|
</ModalPage> :
|
||||||
|
<StageContent />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AuthOverlay
|
|
@ -69,3 +69,8 @@ export const SEARCH_CANCELLED = "SEARCH_CANCELLED";
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
export const DAEMON_SETTINGS_RECEIVED = "DAEMON_SETTINGS_RECEIVED";
|
export const DAEMON_SETTINGS_RECEIVED = "DAEMON_SETTINGS_RECEIVED";
|
||||||
|
|
||||||
|
// User
|
||||||
|
export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED'
|
||||||
|
export const AUTHENTICATION_SUCCESS = 'AUTHENTICATION_SUCCESS'
|
||||||
|
export const AUTHENTICATION_FAILURE = 'AUTHENTICATION_FAILURE'
|
||||||
|
|
|
@ -144,7 +144,7 @@ lbryio.authenticate = function() {
|
||||||
lbryio._authenticationPromise = new Promise((resolve, reject) => {
|
lbryio._authenticationPromise = new Promise((resolve, reject) => {
|
||||||
lbry.status().then((response) => {
|
lbry.status().then((response) => {
|
||||||
|
|
||||||
let installation_id = response.installation_id.substring(0, response.installation_id.length - 2) + "C";
|
let installation_id = response.installation_id.substring(0, response.installation_id.length - 6) + "C";
|
||||||
|
|
||||||
function setCurrentUser() {
|
function setCurrentUser() {
|
||||||
lbryio.call('user', 'me').then((data) => {
|
lbryio.call('user', 'me').then((data) => {
|
||||||
|
@ -152,12 +152,7 @@ lbryio.authenticate = function() {
|
||||||
resolve(data)
|
resolve(data)
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
lbryio.setAccessToken(null);
|
lbryio.setAccessToken(null);
|
||||||
if (!getSession('reloadedOnFailedAuth')) {
|
|
||||||
setSession('reloadedOnFailedAuth', true)
|
|
||||||
window.location.reload();
|
|
||||||
} else {
|
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import lbry from './lbry.js';
|
import lbry from './lbry.js';
|
||||||
import lbryio from './lbryio.js';
|
import lbryio from './lbryio.js';
|
||||||
import lighthouse from './lighthouse.js';
|
|
||||||
import App from 'component/app/index.js';
|
import App from 'component/app/index.js';
|
||||||
import SnackBar from 'component/snackBar';
|
import SnackBar from 'component/snackBar';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
|
@ -10,8 +9,6 @@ import store from 'store.js';
|
||||||
import SplashScreen from 'component/splash.js';
|
import SplashScreen from 'component/splash.js';
|
||||||
import { AuthOverlay } from 'component/auth.js';
|
import { AuthOverlay } from 'component/auth.js';
|
||||||
import { doChangePath, doNavigate, doDaemonReady } from 'actions/app';
|
import { doChangePath, doNavigate, doDaemonReady } from 'actions/app';
|
||||||
import { doFetchDaemonSettings } from 'actions/settings';
|
|
||||||
import { doFileList } from 'actions/file_info';
|
|
||||||
import { toQueryString } from 'util/query_params';
|
import { toQueryString } from 'util/query_params';
|
||||||
|
|
||||||
const { remote, ipcRenderer, shell } = require('electron');
|
const { remote, ipcRenderer, shell } = require('electron');
|
||||||
|
@ -69,12 +66,7 @@ const initialState = app.store.getState();
|
||||||
var init = function() {
|
var init = function() {
|
||||||
function onDaemonReady() {
|
function onDaemonReady() {
|
||||||
window.sessionStorage.setItem('loaded', 'y'); //once we've made it here once per session, we don't need to show splash again
|
window.sessionStorage.setItem('loaded', 'y'); //once we've made it here once per session, we don't need to show splash again
|
||||||
const actions = [];
|
app.store.dispatch(doDaemonReady())
|
||||||
|
|
||||||
app.store.dispatch(doDaemonReady());
|
|
||||||
app.store.dispatch(doChangePath('/discover'));
|
|
||||||
app.store.dispatch(doFetchDaemonSettings());
|
|
||||||
app.store.dispatch(doFileList());
|
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
|
|
|
@ -81,12 +81,6 @@ reducers[types.UPGRADE_DOWNLOAD_PROGRESSED] = function(state, action) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[types.DAEMON_READY] = function(state, action) {
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
daemonReady: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
reducers[types.SHOW_SNACKBAR] = function(state, action) {
|
reducers[types.SHOW_SNACKBAR] = function(state, action) {
|
||||||
const { message, linkText, linkTarget, isError } = action.data;
|
const { message, linkText, linkTarget, isError } = action.data;
|
||||||
const snackBar = Object.assign({}, state.snackBar);
|
const snackBar = Object.assign({}, state.snackBar);
|
||||||
|
|
34
ui/js/reducers/user.js
Normal file
34
ui/js/reducers/user.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import * as types from 'constants/action_types'
|
||||||
|
|
||||||
|
const reducers = {}
|
||||||
|
|
||||||
|
const defaultState = {
|
||||||
|
authenticationIsPending: false,
|
||||||
|
user: undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.AUTHENTICATION_STARTED] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
authenticationIsPending: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.AUTHENTICATION_SUCCESS] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
authenticationIsPending: false,
|
||||||
|
user: action.data.user,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.AUTHENTICATION_FAILURE] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
authenticationIsPending: false,
|
||||||
|
user: null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function reducer(state = defaultState, action) {
|
||||||
|
const handler = reducers[action.type];
|
||||||
|
if (handler) return handler(state, action);
|
||||||
|
return state;
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
import { createSelector } from "reselect";
|
import { createSelector } from "reselect";
|
||||||
import { selectDaemonReady, selectCurrentPage } from "selectors/app";
|
|
||||||
|
|
||||||
const _selectState = state => state.availability;
|
const _selectState = state => state.availability;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { createSelector } from "reselect";
|
import { createSelector } from "reselect";
|
||||||
import { selectDaemonReady, selectCurrentPage } from "selectors/app";
|
|
||||||
|
|
||||||
export const _selectState = state => state.content || {};
|
export const _selectState = state => state.content || {};
|
||||||
|
|
||||||
|
|
13
ui/js/selectors/user.js
Normal file
13
ui/js/selectors/user.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { createSelector } from 'reselect'
|
||||||
|
|
||||||
|
export const _selectState = state => state.user || {}
|
||||||
|
|
||||||
|
export const selectAuthenticationIsPending = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => state.authenticationIsPending
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectAuthenticationIsFailed = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => state.user === null
|
||||||
|
)
|
|
@ -50,20 +50,6 @@ export const selectGettingNewAddress = createSelector(
|
||||||
state => state.gettingNewAddress
|
state => state.gettingNewAddress
|
||||||
);
|
);
|
||||||
|
|
||||||
export const shouldCheckAddressIsMine = createSelector(
|
|
||||||
_selectState,
|
|
||||||
selectCurrentPage,
|
|
||||||
selectReceiveAddress,
|
|
||||||
selectDaemonReady,
|
|
||||||
(state, page, address, daemonReady) => {
|
|
||||||
if (!daemonReady) return false;
|
|
||||||
if (address === undefined) return false;
|
|
||||||
if (state.addressOwnershipChecked) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const selectDraftTransaction = createSelector(
|
export const selectDraftTransaction = createSelector(
|
||||||
_selectState,
|
_selectState,
|
||||||
state => state.draftTransaction || {}
|
state => state.draftTransaction || {}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import rewardsReducer from 'reducers/rewards';
|
||||||
import searchReducer from 'reducers/search';
|
import searchReducer from 'reducers/search';
|
||||||
import settingsReducer from 'reducers/settings';
|
import settingsReducer from 'reducers/settings';
|
||||||
import walletReducer from 'reducers/wallet';
|
import walletReducer from 'reducers/wallet';
|
||||||
|
import userReducer from 'reducers/user';
|
||||||
|
|
||||||
function isFunction(object) {
|
function isFunction(object) {
|
||||||
return typeof object === 'function';
|
return typeof object === 'function';
|
||||||
|
@ -56,7 +57,8 @@ const reducers = redux.combineReducers({
|
||||||
rewards: rewardsReducer,
|
rewards: rewardsReducer,
|
||||||
search: searchReducer,
|
search: searchReducer,
|
||||||
settings: settingsReducer,
|
settings: settingsReducer,
|
||||||
wallet: walletReducer
|
wallet: walletReducer,
|
||||||
|
user: userReducer,
|
||||||
});
|
});
|
||||||
|
|
||||||
const bulkThunk = createBulkThunkMiddleware();
|
const bulkThunk = createBulkThunkMiddleware();
|
||||||
|
|
Loading…
Reference in a new issue