Merge branch 'master' into fix-publish-my-claims-display
This commit is contained in:
commit
cfa3aa3bc4
54 changed files with 3191 additions and 2237 deletions
|
@ -1,5 +1,5 @@
|
|||
[bumpversion]
|
||||
current_version = 0.12.0rc6
|
||||
current_version = 0.12.0rc7
|
||||
commit = True
|
||||
tag = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)((?P<release>[a-z]+)(?P<candidate>\d+))?
|
||||
|
|
|
@ -9,6 +9,7 @@ Web UI version numbers should always match the corresponding version of LBRY App
|
|||
## [Unreleased]
|
||||
### Added
|
||||
* More file types, like audio and documents, can be streamed and/or served from the app
|
||||
* App is no longer gated. Reward authorization re-written. Added basic flows for new users.
|
||||
* Videos now have a classy loading spinner
|
||||
|
||||
### Changed
|
||||
|
@ -20,6 +21,7 @@ Web UI version numbers should always match the corresponding version of LBRY App
|
|||
* Updated deprecated LBRY API call signatures
|
||||
* App scrolls to the top of the page on navigation
|
||||
* Download progress works properly for purchased but deleted files
|
||||
* Publish channels for less than 1 LBC
|
||||
|
||||
### Deprecated
|
||||
*
|
||||
|
|
1290
app/package-lock.json
generated
1290
app/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "LBRY",
|
||||
"version": "0.12.0rc6",
|
||||
"version": "0.12.0rc7",
|
||||
"main": "main.js",
|
||||
"description": "LBRY is a fully decentralized, open-source protocol facilitating the discovery, access, and (sometimes) purchase of data.",
|
||||
"author": {
|
||||
|
|
|
@ -1 +1 @@
|
|||
https://github.com/lbryio/lbry/releases/download/v0.11.0rc1/lbrynet-daemon-v0.11.0rc1-OSNAME.zip
|
||||
https://github.com/lbryio/lbry/releases/download/v0.11.0rc3/lbrynet-daemon-v0.11.0rc3-OSNAME.zip
|
||||
|
|
958
package-lock.json
generated
958
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -10,6 +10,10 @@ import {
|
|||
selectCurrentParams,
|
||||
} from "selectors/app";
|
||||
import { doSearch } from "actions/search";
|
||||
import { doFetchDaemonSettings } from "actions/settings";
|
||||
import { doAuthenticate } from "actions/user";
|
||||
import { doRewardList } from "actions/rewards";
|
||||
import { doFileList } from "actions/file_info";
|
||||
|
||||
const { remote, ipcRenderer, shell } = require("electron");
|
||||
const path = require("path");
|
||||
|
@ -216,8 +220,14 @@ export function doAlertError(errorList) {
|
|||
}
|
||||
|
||||
export function doDaemonReady() {
|
||||
return {
|
||||
type: types.DAEMON_READY,
|
||||
return function(dispatch, getState) {
|
||||
dispatch(doAuthenticate());
|
||||
dispatch({
|
||||
type: types.DAEMON_READY,
|
||||
});
|
||||
dispatch(doChangePath("/discover"));
|
||||
dispatch(doFetchDaemonSettings());
|
||||
dispatch(doFileList());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ import * as types from "constants/action_types";
|
|||
import lbry from "lbry";
|
||||
import lbryio from "lbryio";
|
||||
import lbryuri from "lbryuri";
|
||||
import rewards from "rewards";
|
||||
import { selectBalance } from "selectors/wallet";
|
||||
import {
|
||||
selectFileInfoForUri,
|
||||
|
@ -10,8 +9,8 @@ import {
|
|||
} from "selectors/file_info";
|
||||
import { selectResolvingUris } from "selectors/content";
|
||||
import { selectCostInfoForUri } from "selectors/cost_info";
|
||||
import { selectClaimsByUri } from "selectors/claims";
|
||||
import { doOpenModal } from "actions/app";
|
||||
import { doClaimEligiblePurchaseRewards } from "actions/rewards";
|
||||
|
||||
export function doResolveUri(uri) {
|
||||
return function(dispatch, getState) {
|
||||
|
@ -171,7 +170,7 @@ export function doDownloadFile(uri, streamInfo) {
|
|||
})
|
||||
.catch(() => {});
|
||||
|
||||
rewards.claimEligiblePurchaseRewards();
|
||||
dispatch(doClaimEligiblePurchaseRewards());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,9 @@ import * as types from "constants/action_types";
|
|||
import lbry from "lbry";
|
||||
import lbryio from "lbryio";
|
||||
import rewards from "rewards";
|
||||
import { selectRewards, selectRewardsByType } from "selectors/rewards";
|
||||
|
||||
export function doFetchRewards() {
|
||||
export function doRewardList() {
|
||||
return function(dispatch, getState) {
|
||||
const state = getState();
|
||||
|
||||
|
@ -11,25 +12,105 @@ export function doFetchRewards() {
|
|||
type: types.FETCH_REWARDS_STARTED,
|
||||
});
|
||||
|
||||
lbryio.call("reward", "list", {}).then(function(userRewards) {
|
||||
dispatch({
|
||||
type: types.FETCH_REWARDS_COMPLETED,
|
||||
data: { userRewards },
|
||||
lbryio
|
||||
.call("reward", "list", {})
|
||||
.then(userRewards => {
|
||||
dispatch({
|
||||
type: types.FETCH_REWARDS_COMPLETED,
|
||||
data: { userRewards },
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch({
|
||||
type: types.FETCH_REWARDS_COMPLETED,
|
||||
data: { userRewards: [] },
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doClaimReward(rewardType) {
|
||||
export function doClaimRewardType(rewardType) {
|
||||
return function(dispatch, getState) {
|
||||
try {
|
||||
rewards.claimReward(rewards[rewardType]);
|
||||
dispatch({
|
||||
type: types.REWARD_CLAIMED,
|
||||
data: {
|
||||
reward: rewards[rewardType],
|
||||
},
|
||||
});
|
||||
} catch (err) {}
|
||||
const rewardsByType = selectRewardsByType(getState()),
|
||||
reward = rewardsByType[rewardType];
|
||||
|
||||
if (reward) {
|
||||
dispatch(doClaimReward(reward));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function doClaimReward(reward, saveError = false) {
|
||||
return function(dispatch, getState) {
|
||||
if (reward.transaction_id) {
|
||||
//already claimed, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: types.CLAIM_REWARD_STARTED,
|
||||
data: { reward },
|
||||
});
|
||||
|
||||
const success = reward => {
|
||||
dispatch({
|
||||
type: types.CLAIM_REWARD_SUCCESS,
|
||||
data: {
|
||||
reward,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const failure = error => {
|
||||
dispatch({
|
||||
type: types.CLAIM_REWARD_FAILURE,
|
||||
data: {
|
||||
reward,
|
||||
error: saveError ? error : null,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
rewards.claimReward(reward.reward_type).then(success, failure);
|
||||
};
|
||||
}
|
||||
|
||||
export function doClaimEligiblePurchaseRewards() {
|
||||
return function(dispatch, getState) {
|
||||
if (!lbryio.enabled || !lbryio.getAccessToken()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rewardsByType = selectRewardsByType(getState());
|
||||
|
||||
let types = {};
|
||||
|
||||
types[rewards.TYPE_FIRST_STREAM] = false;
|
||||
types[rewards.TYPE_FEATURED_DOWNLOAD] = false;
|
||||
types[rewards.TYPE_MANY_DOWNLOADS] = false;
|
||||
Object.values(rewardsByType).forEach(reward => {
|
||||
if (types[reward.reward_type] === false && reward.transaction_id) {
|
||||
types[reward.reward_type] = true;
|
||||
}
|
||||
});
|
||||
|
||||
let unclaimedType = Object.keys(types).find(type => {
|
||||
return types[type] === false && type !== rewards.TYPE_FEATURED_DOWNLOAD; //handled below
|
||||
});
|
||||
if (unclaimedType) {
|
||||
dispatch(doClaimRewardType(unclaimedType));
|
||||
}
|
||||
if (types[rewards.TYPE_FEATURED_DOWNLOAD] === false) {
|
||||
dispatch(doClaimRewardType(rewards.TYPE_FEATURED_DOWNLOAD));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function doClaimRewardClearError(reward) {
|
||||
return function(dispatch, getState) {
|
||||
dispatch({
|
||||
type: types.CLAIM_REWARD_CLEAR_ERROR,
|
||||
data: { reward },
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
132
ui/js/actions/user.js
Normal file
132
ui/js/actions/user.js
Normal file
|
@ -0,0 +1,132 @@
|
|||
import * as types from "constants/action_types";
|
||||
import lbryio from "lbryio";
|
||||
import { setLocal } from "utils";
|
||||
import { doRewardList } from "actions/rewards";
|
||||
import { selectEmailToVerify } from "selectors/user";
|
||||
|
||||
export function doAuthenticate() {
|
||||
return function(dispatch, getState) {
|
||||
dispatch({
|
||||
type: types.AUTHENTICATION_STARTED,
|
||||
});
|
||||
lbryio
|
||||
.authenticate()
|
||||
.then(user => {
|
||||
dispatch({
|
||||
type: types.AUTHENTICATION_SUCCESS,
|
||||
data: { user },
|
||||
});
|
||||
|
||||
dispatch(doRewardList()); //FIXME - where should this happen?
|
||||
})
|
||||
.catch(error => {
|
||||
dispatch({
|
||||
type: types.AUTHENTICATION_FAILURE,
|
||||
data: { error },
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doUserFetch() {
|
||||
return function(dispatch, getState) {
|
||||
dispatch({
|
||||
type: types.USER_FETCH_STARTED,
|
||||
});
|
||||
lbryio.setCurrentUser(
|
||||
user => {
|
||||
dispatch({
|
||||
type: types.USER_FETCH_SUCCESS,
|
||||
data: { user },
|
||||
});
|
||||
},
|
||||
error => {
|
||||
dispatch({
|
||||
type: types.USER_FETCH_FAILURE,
|
||||
data: { error },
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function doUserEmailNew(email) {
|
||||
return function(dispatch, getState) {
|
||||
dispatch({
|
||||
type: types.USER_EMAIL_NEW_STARTED,
|
||||
email: email,
|
||||
});
|
||||
lbryio.call("user_email", "new", { email }, "post").then(
|
||||
() => {
|
||||
dispatch({
|
||||
type: types.USER_EMAIL_NEW_SUCCESS,
|
||||
data: { email },
|
||||
});
|
||||
dispatch(doUserFetch());
|
||||
},
|
||||
error => {
|
||||
if (
|
||||
error.xhr &&
|
||||
(error.xhr.status == 409 ||
|
||||
error.message == "This email is already in use")
|
||||
) {
|
||||
dispatch({
|
||||
type: types.USER_EMAIL_NEW_EXISTS,
|
||||
data: { email },
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: types.USER_EMAIL_NEW_FAILURE,
|
||||
data: { error: error.message },
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function doUserEmailDecline() {
|
||||
return function(dispatch, getState) {
|
||||
setLocal("user_email_declined", true);
|
||||
dispatch({
|
||||
type: types.USER_EMAIL_DECLINE,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doUserEmailVerify(verificationToken) {
|
||||
return function(dispatch, getState) {
|
||||
const email = selectEmailToVerify(getState());
|
||||
|
||||
dispatch({
|
||||
type: types.USER_EMAIL_VERIFY_STARTED,
|
||||
code: verificationToken,
|
||||
});
|
||||
|
||||
const failure = error => {
|
||||
dispatch({
|
||||
type: types.USER_EMAIL_VERIFY_FAILURE,
|
||||
data: { error: error.message },
|
||||
});
|
||||
};
|
||||
|
||||
lbryio
|
||||
.call(
|
||||
"user_email",
|
||||
"confirm",
|
||||
{ verification_token: verificationToken, email: email },
|
||||
"post"
|
||||
)
|
||||
.then(userEmail => {
|
||||
if (userEmail.is_verified) {
|
||||
dispatch({
|
||||
type: types.USER_EMAIL_VERIFY_SUCCESS,
|
||||
data: { email },
|
||||
});
|
||||
dispatch(doUserFetch());
|
||||
} else {
|
||||
failure(new Error("Your email is still not verified.")); //shouldn't happen?
|
||||
}
|
||||
}, failure);
|
||||
};
|
||||
}
|
|
@ -4,6 +4,7 @@ import Header from "component/header";
|
|||
import ErrorModal from "component/errorModal";
|
||||
import DownloadingModal from "component/downloadingModal";
|
||||
import UpgradeModal from "component/upgradeModal";
|
||||
import WelcomeModal from "component/welcomeModal";
|
||||
import lbry from "lbry";
|
||||
import { Line } from "rc-progress";
|
||||
|
||||
|
@ -34,6 +35,7 @@ class App extends React.PureComponent {
|
|||
{modal == "upgrade" && <UpgradeModal />}
|
||||
{modal == "downloading" && <DownloadingModal />}
|
||||
{modal == "error" && <ErrorModal />}
|
||||
{modal == "welcome" && <WelcomeModal />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,515 +0,0 @@
|
|||
import React from "react";
|
||||
import lbry from "../lbry.js";
|
||||
import lbryio from "../lbryio.js";
|
||||
import Modal from "./modal.js";
|
||||
import ModalPage from "./modal-page.js";
|
||||
import Link from "component/link";
|
||||
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.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
rewardType: null,
|
||||
email: "",
|
||||
submitting: false,
|
||||
};
|
||||
}
|
||||
|
||||
handleEmailChanged(event) {
|
||||
this.setState({
|
||||
email: event.target.value,
|
||||
});
|
||||
}
|
||||
|
||||
onEmailSaved(email) {
|
||||
this.props.setStage("confirm", { email: email });
|
||||
}
|
||||
|
||||
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">
|
||||
<Link
|
||||
button="primary"
|
||||
label={__("Next")}
|
||||
disabled={this.state.submitting}
|
||||
onClick={event => {
|
||||
this.handleSubmit(event);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ConfirmEmailStage extends React.PureComponent {
|
||||
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.PureComponent {
|
||||
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 way different 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 "LBC".')}
|
||||
</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>
|
||||
<p>
|
||||
{__("Preparing for first access")} <span className="busy-indicator" />
|
||||
</p>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
class CodeRequiredStage extends React.PureComponent {
|
||||
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>
|
||||
{__(
|
||||
"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.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._stages = {
|
||||
pending: PendingStage,
|
||||
error: ErrorStage,
|
||||
nocode: CodeRequiredStage,
|
||||
email: SubmitEmailStage,
|
||||
confirm: ConfirmEmailStage,
|
||||
welcome: WelcomeStage,
|
||||
};
|
||||
|
||||
this.state = {
|
||||
stage: "pending",
|
||||
stageProps: {},
|
||||
};
|
||||
}
|
||||
|
||||
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() {
|
||||
if (!this.state.stage) {
|
||||
return null;
|
||||
}
|
||||
const StageContent = this._stages[this.state.stage];
|
||||
|
||||
if (!StageContent) {
|
||||
return (
|
||||
<span className="empty">{__("Unknown authentication step.")}</span>
|
||||
);
|
||||
}
|
||||
|
||||
return this.state.stage != "welcome"
|
||||
? <ModalPage
|
||||
className="modal-page--full"
|
||||
isOpen={true}
|
||||
contentLabel={__("Authentication")}
|
||||
>
|
||||
<h1>{__("LBRY Early Access")}</h1>
|
||||
<StageContent
|
||||
{...this.state.stageProps}
|
||||
setStage={(stage, stageProps) => {
|
||||
this.setStage(stage, stageProps);
|
||||
}}
|
||||
/>
|
||||
</ModalPage>
|
||||
: <StageContent
|
||||
setStage={(stage, stageProps) => {
|
||||
this.setStage(stage, stageProps);
|
||||
}}
|
||||
{...this.state.stageProps}
|
||||
/>;
|
||||
}
|
||||
}
|
16
ui/js/component/auth/index.js
Normal file
16
ui/js/component/auth/index.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
selectAuthenticationIsPending,
|
||||
selectEmailToVerify,
|
||||
selectUserIsVerificationCandidate,
|
||||
} from "selectors/user";
|
||||
import Auth from "./view";
|
||||
|
||||
const select = state => ({
|
||||
isPending: selectAuthenticationIsPending(state),
|
||||
email: selectEmailToVerify(state),
|
||||
isVerificationCandidate: selectUserIsVerificationCandidate(state),
|
||||
});
|
||||
|
||||
export default connect(select, null)(Auth);
|
22
ui/js/component/auth/view.jsx
Normal file
22
ui/js/component/auth/view.jsx
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React from "react";
|
||||
import { BusyMessage } from "component/common";
|
||||
import UserEmailNew from "component/userEmailNew";
|
||||
import UserEmailVerify from "component/userEmailVerify";
|
||||
|
||||
export class Auth extends React.PureComponent {
|
||||
render() {
|
||||
const { isPending, email, isVerificationCandidate } = this.props;
|
||||
|
||||
if (isPending) {
|
||||
return <BusyMessage message={__("Authenticating")} />;
|
||||
} else if (!email) {
|
||||
return <UserEmailNew />;
|
||||
} else if (isVerificationCandidate) {
|
||||
return <UserEmailVerify />;
|
||||
} else {
|
||||
return <span className="empty">{__("No further steps.")}</span>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Auth;
|
24
ui/js/component/authOverlay/index.jsx
Normal file
24
ui/js/component/authOverlay/index.jsx
Normal file
|
@ -0,0 +1,24 @@
|
|||
import React from "react";
|
||||
import * as modal from "constants/modal_types";
|
||||
import { connect } from "react-redux";
|
||||
import { doUserEmailDecline } from "actions/user";
|
||||
import { doOpenModal } from "actions/app";
|
||||
import {
|
||||
selectAuthenticationIsPending,
|
||||
selectUserHasEmail,
|
||||
selectUserIsAuthRequested,
|
||||
} from "selectors/user";
|
||||
import AuthOverlay from "./view";
|
||||
|
||||
const select = state => ({
|
||||
hasEmail: selectUserHasEmail(state),
|
||||
isPending: selectAuthenticationIsPending(state),
|
||||
isShowing: selectUserIsAuthRequested(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
userEmailDecline: () => dispatch(doUserEmailDecline()),
|
||||
openWelcomeModal: () => dispatch(doOpenModal(modal.WELCOME)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(AuthOverlay);
|
83
ui/js/component/authOverlay/view.jsx
Normal file
83
ui/js/component/authOverlay/view.jsx
Normal file
|
@ -0,0 +1,83 @@
|
|||
import React from "react";
|
||||
import lbryio from "lbryio.js";
|
||||
import ModalPage from "component/modal-page.js";
|
||||
import Auth from "component/auth";
|
||||
import Link from "component/link";
|
||||
|
||||
export class AuthOverlay extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
showNoEmailConfirm: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (this.props.isShowing && !this.props.isPending && !nextProps.isShowing) {
|
||||
setTimeout(() => this.props.openWelcomeModal(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
onEmailSkipClick() {
|
||||
this.setState({ showNoEmailConfirm: true });
|
||||
}
|
||||
|
||||
onEmailSkipConfirm() {
|
||||
this.props.userEmailDecline();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!lbryio.enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { isPending, isShowing, hasEmail } = this.props;
|
||||
|
||||
if (isShowing) {
|
||||
return (
|
||||
<ModalPage
|
||||
className="modal-page--full"
|
||||
isOpen={true}
|
||||
contentLabel="Authentication"
|
||||
>
|
||||
<h1>LBRY Early Access</h1>
|
||||
<Auth />
|
||||
{isPending
|
||||
? ""
|
||||
: <div className="form-row-submit">
|
||||
{!hasEmail && this.state.showNoEmailConfirm
|
||||
? <div className="help form-input-width">
|
||||
<p>
|
||||
{__(
|
||||
"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
|
||||
onClick={() => {
|
||||
this.onEmailSkipConfirm();
|
||||
}}
|
||||
label={__("Continue without email")}
|
||||
/>
|
||||
</div>
|
||||
: <Link
|
||||
className={"button-text-help"}
|
||||
onClick={() => {
|
||||
hasEmail
|
||||
? this.onEmailSkipConfirm()
|
||||
: this.onEmailSkipClick();
|
||||
}}
|
||||
label={
|
||||
hasEmail ? __("Skip for now") : __("Do I have to?")
|
||||
}
|
||||
/>}
|
||||
</div>}
|
||||
</ModalPage>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default AuthOverlay;
|
|
@ -178,9 +178,19 @@ export class FormRow extends React.PureComponent {
|
|||
|
||||
this._fieldRequiredText = __("This field is required");
|
||||
|
||||
this.state = {
|
||||
isError: false,
|
||||
errorMessage: null,
|
||||
this.state = this.getStateFromProps(props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.setState(this.getStateFromProps(nextProps));
|
||||
}
|
||||
|
||||
getStateFromProps(props) {
|
||||
return {
|
||||
isError: !!props.errorMessage,
|
||||
errorMessage: typeof props.errorMessage === "string"
|
||||
? props.errorMessage
|
||||
: "",
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -225,6 +235,7 @@ export class FormRow extends React.PureComponent {
|
|||
delete fieldProps.label;
|
||||
}
|
||||
delete fieldProps.helper;
|
||||
delete fieldProps.errorMessage;
|
||||
|
||||
return (
|
||||
<div className="form-row">
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
import React from "react";
|
||||
import lbry from "lbry";
|
||||
import { Icon } from "component/common";
|
||||
import Modal from "component/modal";
|
||||
import rewards from "rewards";
|
||||
import Link from "component/link";
|
||||
|
||||
export class RewardLink extends React.PureComponent {
|
||||
static propTypes = {
|
||||
type: React.PropTypes.string.isRequired,
|
||||
claimed: React.PropTypes.bool,
|
||||
onRewardClaim: React.PropTypes.func,
|
||||
onRewardFailure: React.PropTypes.func,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
claimable: true,
|
||||
pending: false,
|
||||
errorMessage: null,
|
||||
};
|
||||
}
|
||||
|
||||
refreshClaimable() {
|
||||
switch (this.props.type) {
|
||||
case "new_user":
|
||||
this.setState({ claimable: true });
|
||||
return;
|
||||
|
||||
case "first_publish":
|
||||
lbry.claim_list_mine().then(list => {
|
||||
this.setState({
|
||||
claimable: list.length > 0,
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.refreshClaimable();
|
||||
}
|
||||
|
||||
claimReward() {
|
||||
this.setState({
|
||||
pending: true,
|
||||
});
|
||||
|
||||
rewards
|
||||
.claimReward(this.props.type)
|
||||
.then(reward => {
|
||||
this.setState({
|
||||
pending: false,
|
||||
errorMessage: null,
|
||||
});
|
||||
if (this.props.onRewardClaim) {
|
||||
this.props.onRewardClaim(reward);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
this.setState({
|
||||
errorMessage: error.message,
|
||||
pending: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
clearError() {
|
||||
if (this.props.onRewardFailure) {
|
||||
this.props.onRewardFailure();
|
||||
}
|
||||
this.setState({
|
||||
errorMessage: null,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="reward-link">
|
||||
{this.props.claimed
|
||||
? <span><Icon icon="icon-check" /> {__("Reward claimed.")}</span>
|
||||
: <Link
|
||||
button={this.props.button ? this.props.button : "alt"}
|
||||
disabled={this.state.pending || !this.state.claimable}
|
||||
label={
|
||||
this.state.pending ? __("Claiming...") : __("Claim Reward")
|
||||
}
|
||||
onClick={() => {
|
||||
this.claimReward();
|
||||
}}
|
||||
/>}
|
||||
{this.state.errorMessage
|
||||
? <Modal
|
||||
isOpen={true}
|
||||
contentLabel={__("Reward Claim Error")}
|
||||
className="error-modal"
|
||||
onConfirmed={() => {
|
||||
this.clearError();
|
||||
}}
|
||||
>
|
||||
{this.state.errorMessage}
|
||||
</Modal>
|
||||
: ""}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
35
ui/js/component/rewardLink/index.js
Normal file
35
ui/js/component/rewardLink/index.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
makeSelectHasClaimedReward,
|
||||
makeSelectClaimRewardError,
|
||||
makeSelectRewardByType,
|
||||
makeSelectIsRewardClaimPending,
|
||||
} from "selectors/rewards";
|
||||
import { doNavigate } from "actions/app";
|
||||
import { doClaimReward, doClaimRewardClearError } from "actions/rewards";
|
||||
import RewardLink from "./view";
|
||||
|
||||
const makeSelect = () => {
|
||||
const selectHasClaimedReward = makeSelectHasClaimedReward();
|
||||
const selectIsPending = makeSelectIsRewardClaimPending();
|
||||
const selectReward = makeSelectRewardByType();
|
||||
const selectError = makeSelectClaimRewardError();
|
||||
|
||||
const select = (state, props) => ({
|
||||
isClaimed: selectHasClaimedReward(state, props),
|
||||
errorMessage: selectError(state, props),
|
||||
isPending: selectIsPending(state, props),
|
||||
reward: selectReward(state, props),
|
||||
});
|
||||
|
||||
return select;
|
||||
};
|
||||
|
||||
const perform = dispatch => ({
|
||||
claimReward: reward => dispatch(doClaimReward(reward, true)),
|
||||
clearError: reward => dispatch(doClaimRewardClearError(reward)),
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
});
|
||||
|
||||
export default connect(makeSelect, perform)(RewardLink);
|
44
ui/js/component/rewardLink/view.jsx
Normal file
44
ui/js/component/rewardLink/view.jsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
import React from "react";
|
||||
import { Icon } from "component/common";
|
||||
import Modal from "component/modal";
|
||||
import Link from "component/link";
|
||||
|
||||
const RewardLink = props => {
|
||||
const {
|
||||
reward,
|
||||
button,
|
||||
claimReward,
|
||||
clearError,
|
||||
errorMessage,
|
||||
isClaimed,
|
||||
isPending,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div className="reward-link">
|
||||
{isClaimed
|
||||
? <span><Icon icon="icon-check" /> Reward claimed.</span>
|
||||
: <Link
|
||||
button={button ? button : "alt"}
|
||||
disabled={isPending}
|
||||
label={isPending ? "Claiming..." : "Claim Reward"}
|
||||
onClick={() => {
|
||||
claimReward(reward);
|
||||
}}
|
||||
/>}
|
||||
{errorMessage
|
||||
? <Modal
|
||||
isOpen={true}
|
||||
contentLabel="Reward Claim Error"
|
||||
className="error-modal"
|
||||
onConfirmed={() => {
|
||||
clearError(reward);
|
||||
}}
|
||||
>
|
||||
{errorMessage}
|
||||
</Modal>
|
||||
: ""}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default RewardLink;
|
|
@ -7,9 +7,8 @@ import WalletPage from "page/wallet";
|
|||
import ShowPage from "page/showPage";
|
||||
import PublishPage from "page/publish";
|
||||
import DiscoverPage from "page/discover";
|
||||
import SplashScreen from "component/splash.js";
|
||||
import DeveloperPage from "page/developer.js";
|
||||
import RewardsPage from "page/rewards.js";
|
||||
import RewardsPage from "page/rewards";
|
||||
import FileListDownloaded from "page/fileListDownloaded";
|
||||
import FileListPublished from "page/fileListPublished";
|
||||
import ChannelPage from "page/channel";
|
||||
|
|
23
ui/js/component/userEmailNew/index.jsx
Normal file
23
ui/js/component/userEmailNew/index.jsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import React from 'react'
|
||||
import {
|
||||
connect
|
||||
} from 'react-redux'
|
||||
import {
|
||||
doUserEmailNew
|
||||
} from 'actions/user'
|
||||
import {
|
||||
selectEmailNewIsPending,
|
||||
selectEmailNewErrorMessage,
|
||||
} from 'selectors/user'
|
||||
import UserEmailNew from './view'
|
||||
|
||||
const select = (state) => ({
|
||||
isPending: selectEmailNewIsPending(state),
|
||||
errorMessage: selectEmailNewErrorMessage(state),
|
||||
})
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
addUserEmail: (email) => dispatch(doUserEmailNew(email))
|
||||
})
|
||||
|
||||
export default connect(select, perform)(UserEmailNew)
|
61
ui/js/component/userEmailNew/view.jsx
Normal file
61
ui/js/component/userEmailNew/view.jsx
Normal file
|
@ -0,0 +1,61 @@
|
|||
import React from "react";
|
||||
import Link from "component/link";
|
||||
import { FormRow } from "component/form.js";
|
||||
|
||||
class UserEmailNew extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
email: "",
|
||||
};
|
||||
}
|
||||
|
||||
handleEmailChanged(event) {
|
||||
this.setState({
|
||||
email: event.target.value,
|
||||
});
|
||||
}
|
||||
|
||||
handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
this.props.addUserEmail(this.state.email);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { errorMessage, isPending } = this.props;
|
||||
|
||||
return (
|
||||
<form
|
||||
className="form-input-width"
|
||||
onSubmit={event => {
|
||||
this.handleSubmit(event);
|
||||
}}
|
||||
>
|
||||
<FormRow
|
||||
type="text"
|
||||
label="Email"
|
||||
placeholder="scrwvwls@lbry.io"
|
||||
name="email"
|
||||
value={this.state.email}
|
||||
errorMessage={errorMessage}
|
||||
onChange={event => {
|
||||
this.handleEmailChanged(event);
|
||||
}}
|
||||
/>
|
||||
<div className="form-row-submit">
|
||||
<Link
|
||||
button="primary"
|
||||
label="Next"
|
||||
disabled={isPending}
|
||||
onClick={event => {
|
||||
this.handleSubmit(event);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default UserEmailNew;
|
21
ui/js/component/userEmailVerify/index.jsx
Normal file
21
ui/js/component/userEmailVerify/index.jsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { doUserEmailVerify } from "actions/user";
|
||||
import {
|
||||
selectEmailVerifyIsPending,
|
||||
selectEmailToVerify,
|
||||
selectEmailVerifyErrorMessage,
|
||||
} from "selectors/user";
|
||||
import UserEmailVerify from "./view";
|
||||
|
||||
const select = state => ({
|
||||
isPending: selectEmailVerifyIsPending(state),
|
||||
email: selectEmailToVerify(state),
|
||||
errorMessage: selectEmailVerifyErrorMessage(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
verifyUserEmail: code => dispatch(doUserEmailVerify(code)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(UserEmailVerify);
|
68
ui/js/component/userEmailVerify/view.jsx
Normal file
68
ui/js/component/userEmailVerify/view.jsx
Normal file
|
@ -0,0 +1,68 @@
|
|||
import React from "react";
|
||||
import Link from "component/link";
|
||||
import { FormRow } from "component/form.js";
|
||||
|
||||
class UserEmailVerify extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
code: "",
|
||||
};
|
||||
}
|
||||
|
||||
handleCodeChanged(event) {
|
||||
this.setState({
|
||||
code: event.target.value,
|
||||
});
|
||||
}
|
||||
|
||||
handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
this.props.verifyUserEmail(this.state.code);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { errorMessage, isPending } = this.props;
|
||||
|
||||
return (
|
||||
<form
|
||||
className="form-input-width"
|
||||
onSubmit={event => {
|
||||
this.handleSubmit(event);
|
||||
}}
|
||||
>
|
||||
<FormRow
|
||||
type="text"
|
||||
label="Verification Code"
|
||||
placeholder="a94bXXXXXXXXXXXXXX"
|
||||
name="code"
|
||||
value={this.state.code}
|
||||
onChange={event => {
|
||||
this.handleCodeChanged(event);
|
||||
}}
|
||||
errorMessage={errorMessage}
|
||||
/>
|
||||
{/* render help separately so it always shows */}
|
||||
<div className="form-field__helper">
|
||||
<p>
|
||||
Email <Link href="mailto:help@lbry.io" label="help@lbry.io" /> if
|
||||
you did not receive or are having trouble with your code.
|
||||
</p>
|
||||
</div>
|
||||
<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>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default UserEmailVerify;
|
28
ui/js/component/welcomeModal/index.jsx
Normal file
28
ui/js/component/welcomeModal/index.jsx
Normal file
|
@ -0,0 +1,28 @@
|
|||
import React from "react";
|
||||
import rewards from "rewards";
|
||||
import { connect } from "react-redux";
|
||||
import { doCloseModal } from "actions/app";
|
||||
import { selectUserIsRewardApproved } from "selectors/user";
|
||||
import {
|
||||
makeSelectHasClaimedReward,
|
||||
makeSelectClaimRewardError,
|
||||
makeSelectRewardByType,
|
||||
} from "selectors/rewards";
|
||||
import WelcomeModal from "./view";
|
||||
|
||||
const select = (state, props) => {
|
||||
const selectHasClaimed = makeSelectHasClaimedReward(),
|
||||
selectReward = makeSelectRewardByType();
|
||||
|
||||
return {
|
||||
hasClaimed: selectHasClaimed(state, { reward_type: rewards.TYPE_NEW_USER }),
|
||||
isRewardApproved: selectUserIsRewardApproved(state),
|
||||
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
|
||||
};
|
||||
};
|
||||
|
||||
const perform = dispatch => ({
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(WelcomeModal);
|
75
ui/js/component/welcomeModal/view.jsx
Normal file
75
ui/js/component/welcomeModal/view.jsx
Normal file
|
@ -0,0 +1,75 @@
|
|||
import React from "react";
|
||||
import { Modal } from "component/modal";
|
||||
import { CreditAmount } from "component/common";
|
||||
import Link from "component/link";
|
||||
import RewardLink from "component/rewardLink";
|
||||
|
||||
class WelcomeModal extends React.PureComponent {
|
||||
render() {
|
||||
const { closeModal, hasClaimed, isRewardApproved, reward } = this.props;
|
||||
|
||||
return !hasClaimed
|
||||
? <Modal type="custom" isOpen={true} contentLabel="Welcome to LBRY">
|
||||
<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!
|
||||
{" "}{isRewardApproved ? __("Here's a nickel, kid.") : ""}
|
||||
</p>
|
||||
<div className="text-center">
|
||||
{isRewardApproved
|
||||
? <RewardLink reward_type="new_user" button="primary" />
|
||||
: <Link
|
||||
button="primary"
|
||||
onClick={closeModal}
|
||||
label="Continue"
|
||||
/>}
|
||||
</div>
|
||||
</section>
|
||||
</Modal>
|
||||
: <Modal
|
||||
type="alert"
|
||||
overlayClassName="modal-overlay modal-overlay--clear"
|
||||
isOpen={true}
|
||||
contentLabel="Welcome to LBRY"
|
||||
onConfirmed={closeModal}
|
||||
>
|
||||
<section>
|
||||
<h3 className="modal__header">About Your Reward</h3>
|
||||
<p>
|
||||
You earned a reward of
|
||||
{" "}<CreditAmount amount={reward.reward_amount} 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>;
|
||||
}
|
||||
}
|
||||
|
||||
export default WelcomeModal;
|
|
@ -69,3 +69,27 @@ export const SEARCH_CANCELLED = "SEARCH_CANCELLED";
|
|||
|
||||
// Settings
|
||||
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";
|
||||
export const USER_EMAIL_DECLINE = "USER_EMAIL_DECLINE";
|
||||
export const USER_EMAIL_NEW_STARTED = "USER_EMAIL_NEW_STARTED";
|
||||
export const USER_EMAIL_NEW_SUCCESS = "USER_EMAIL_NEW_SUCCESS";
|
||||
export const USER_EMAIL_NEW_EXISTS = "USER_EMAIL_NEW_EXISTS";
|
||||
export const USER_EMAIL_NEW_FAILURE = "USER_EMAIL_NEW_FAILURE";
|
||||
export const USER_EMAIL_VERIFY_STARTED = "USER_EMAIL_VERIFY_STARTED";
|
||||
export const USER_EMAIL_VERIFY_SUCCESS = "USER_EMAIL_VERIFY_SUCCESS";
|
||||
export const USER_EMAIL_VERIFY_FAILURE = "USER_EMAIL_VERIFY_FAILURE";
|
||||
export const USER_FETCH_STARTED = "USER_FETCH_STARTED";
|
||||
export const USER_FETCH_SUCCESS = "USER_FETCH_SUCCESS";
|
||||
export const USER_FETCH_FAILURE = "USER_FETCH_FAILURE";
|
||||
|
||||
// Rewards
|
||||
export const FETCH_REWARDS_STARTED = "FETCH_REWARDS_STARTED";
|
||||
export const FETCH_REWARDS_COMPLETED = "FETCH_REWARDS_COMPLETED";
|
||||
export const CLAIM_REWARD_STARTED = "CLAIM_REWARD_STARTED";
|
||||
export const CLAIM_REWARD_SUCCESS = "CLAIM_REWARD_SUCCESS";
|
||||
export const CLAIM_REWARD_FAILURE = "CLAIM_REWARD_FAILURE";
|
||||
export const CLAIM_REWARD_CLEAR_ERROR = "CLAIM_REWARD_CLEAR_ERROR";
|
||||
|
|
1
ui/js/constants/modal_types.js
Normal file
1
ui/js/constants/modal_types.js
Normal file
|
@ -0,0 +1 @@
|
|||
export const WELCOME = "welcome";
|
330
ui/js/lbryio.js
330
ui/js/lbryio.js
|
@ -1,206 +1,192 @@
|
|||
import { getSession, setSession } from './utils.js';
|
||||
import lbry from './lbry.js';
|
||||
import { getSession, setSession, setLocal } from "./utils.js";
|
||||
import lbry from "./lbry.js";
|
||||
|
||||
const querystring = require('querystring');
|
||||
const querystring = require("querystring");
|
||||
|
||||
const lbryio = {
|
||||
_accessToken: getSession('accessToken'),
|
||||
_authenticationPromise: null,
|
||||
_user: null,
|
||||
enabled: true
|
||||
_accessToken: getSession("accessToken"),
|
||||
_authenticationPromise: null,
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
const CONNECTION_STRING = process.env.LBRY_APP_API_URL
|
||||
? process.env.LBRY_APP_API_URL.replace(/\/*$/, '/') // exactly one slash at the end
|
||||
: 'https://api.lbry.io/';
|
||||
? process.env.LBRY_APP_API_URL.replace(/\/*$/, "/") // exactly one slash at the end
|
||||
: "https://api.lbry.io/";
|
||||
const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000;
|
||||
|
||||
lbryio._exchangePromise = null;
|
||||
lbryio._exchangeLastFetched = null;
|
||||
lbryio.getExchangeRates = function() {
|
||||
if (
|
||||
!lbryio._exchangeLastFetched ||
|
||||
Date.now() - lbryio._exchangeLastFetched > EXCHANGE_RATE_TIMEOUT
|
||||
) {
|
||||
lbryio._exchangePromise = new Promise((resolve, reject) => {
|
||||
lbryio
|
||||
.call('lbc', 'exchange_rate', {}, 'get', true)
|
||||
.then(({ lbc_usd, lbc_btc, btc_usd }) => {
|
||||
const rates = { lbc_usd, lbc_btc, btc_usd };
|
||||
resolve(rates);
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
lbryio._exchangeLastFetched = Date.now();
|
||||
}
|
||||
return lbryio._exchangePromise;
|
||||
if (
|
||||
!lbryio._exchangeLastFetched ||
|
||||
Date.now() - lbryio._exchangeLastFetched > EXCHANGE_RATE_TIMEOUT
|
||||
) {
|
||||
lbryio._exchangePromise = new Promise((resolve, reject) => {
|
||||
lbryio
|
||||
.call("lbc", "exchange_rate", {}, "get", true)
|
||||
.then(({ lbc_usd, lbc_btc, btc_usd }) => {
|
||||
const rates = { lbc_usd, lbc_btc, btc_usd };
|
||||
resolve(rates);
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
lbryio._exchangeLastFetched = Date.now();
|
||||
}
|
||||
return lbryio._exchangePromise;
|
||||
};
|
||||
|
||||
lbryio.call = function(
|
||||
resource,
|
||||
action,
|
||||
params = {},
|
||||
method = 'get',
|
||||
evenIfDisabled = false
|
||||
) {
|
||||
// evenIfDisabled is just for development, when we may have some calls working and some not
|
||||
return new Promise((resolve, reject) => {
|
||||
if (
|
||||
!lbryio.enabled &&
|
||||
!evenIfDisabled &&
|
||||
(resource != 'discover' || action != 'list')
|
||||
) {
|
||||
console.log(__('Internal API disabled'));
|
||||
reject(new Error(__('LBRY internal API is disabled')));
|
||||
return;
|
||||
}
|
||||
lbryio.call = function(resource, action, params = {}, method = "get") {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!lbryio.enabled && (resource != "discover" || action != "list")) {
|
||||
console.log(__("Internal API disabled"));
|
||||
reject(new Error(__("LBRY internal API is disabled")));
|
||||
return;
|
||||
}
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.addEventListener('error', function(event) {
|
||||
reject(
|
||||
new Error(__('Something went wrong making an internal API call.'))
|
||||
);
|
||||
});
|
||||
xhr.addEventListener("error", function(event) {
|
||||
reject(
|
||||
new Error(__("Something went wrong making an internal API call."))
|
||||
);
|
||||
});
|
||||
|
||||
xhr.addEventListener('timeout', function() {
|
||||
reject(new Error(__('XMLHttpRequest connection timed out')));
|
||||
});
|
||||
xhr.addEventListener("timeout", function() {
|
||||
reject(new Error(__("XMLHttpRequest connection timed out")));
|
||||
});
|
||||
|
||||
xhr.addEventListener('load', function() {
|
||||
const response = JSON.parse(xhr.responseText);
|
||||
xhr.addEventListener("load", function() {
|
||||
const response = JSON.parse(xhr.responseText);
|
||||
|
||||
if (!response.success) {
|
||||
if (reject) {
|
||||
let error = new Error(response.error);
|
||||
error.xhr = xhr;
|
||||
reject(error);
|
||||
} else {
|
||||
document.dispatchEvent(
|
||||
new CustomEvent('unhandledError', {
|
||||
detail: {
|
||||
connectionString: connectionString,
|
||||
method: action,
|
||||
params: params,
|
||||
message: response.error.message,
|
||||
...(response.error.data ? { data: response.error.data } : {})
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
} else {
|
||||
resolve(response.data);
|
||||
}
|
||||
});
|
||||
if (!response.success) {
|
||||
if (reject) {
|
||||
let error = new Error(response.error);
|
||||
error.xhr = xhr;
|
||||
reject(error);
|
||||
} else {
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("unhandledError", {
|
||||
detail: {
|
||||
connectionString: connectionString,
|
||||
method: action,
|
||||
params: params,
|
||||
message: response.error.message,
|
||||
...(response.error.data ? { data: response.error.data } : {}),
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
} else {
|
||||
resolve(response.data);
|
||||
}
|
||||
});
|
||||
|
||||
// For social media auth:
|
||||
//const accessToken = localStorage.getItem('accessToken');
|
||||
//const fullParams = {...params, ... accessToken ? {access_token: accessToken} : {}};
|
||||
// For social media auth:
|
||||
//const accessToken = localStorage.getItem('accessToken');
|
||||
//const fullParams = {...params, ... accessToken ? {access_token: accessToken} : {}};
|
||||
|
||||
// Temp app ID based auth:
|
||||
const fullParams = { app_id: lbryio.getAccessToken(), ...params };
|
||||
// Temp app ID based auth:
|
||||
const fullParams = { app_id: lbryio.getAccessToken(), ...params };
|
||||
|
||||
if (method == 'get') {
|
||||
xhr.open(
|
||||
'get',
|
||||
CONNECTION_STRING +
|
||||
resource +
|
||||
'/' +
|
||||
action +
|
||||
'?' +
|
||||
querystring.stringify(fullParams),
|
||||
true
|
||||
);
|
||||
xhr.send();
|
||||
} else if (method == 'post') {
|
||||
xhr.open('post', CONNECTION_STRING + resource + '/' + action, true);
|
||||
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||
xhr.send(querystring.stringify(fullParams));
|
||||
} else {
|
||||
reject(new Error(__('Invalid method')));
|
||||
}
|
||||
});
|
||||
if (method == "get") {
|
||||
xhr.open(
|
||||
"get",
|
||||
CONNECTION_STRING +
|
||||
resource +
|
||||
"/" +
|
||||
action +
|
||||
"?" +
|
||||
querystring.stringify(fullParams),
|
||||
true
|
||||
);
|
||||
xhr.send();
|
||||
} else if (method == "post") {
|
||||
xhr.open("post", CONNECTION_STRING + resource + "/" + action, true);
|
||||
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
xhr.send(querystring.stringify(fullParams));
|
||||
} else {
|
||||
reject(new Error(__("Invalid method")));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
lbryio.getAccessToken = () => {
|
||||
const token = getSession('accessToken');
|
||||
return token ? token.toString().trim() : token;
|
||||
const token = getSession("accessToken");
|
||||
return token ? token.toString().trim() : token;
|
||||
};
|
||||
|
||||
lbryio.setAccessToken = token => {
|
||||
setSession('accessToken', token ? token.toString().trim() : token);
|
||||
setSession("accessToken", token ? token.toString().trim() : token);
|
||||
};
|
||||
|
||||
lbryio.setCurrentUser = (resolve, reject) => {
|
||||
lbryio
|
||||
.call("user", "me")
|
||||
.then(data => {
|
||||
resolve(data);
|
||||
})
|
||||
.catch(function(err) {
|
||||
lbryio.setAccessToken(null);
|
||||
reject(err);
|
||||
});
|
||||
};
|
||||
|
||||
lbryio.authenticate = function() {
|
||||
if (!lbryio.enabled) {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve({
|
||||
id: 1,
|
||||
has_verified_email: true
|
||||
});
|
||||
});
|
||||
}
|
||||
if (lbryio._authenticationPromise === null) {
|
||||
lbryio._authenticationPromise = new Promise((resolve, reject) => {
|
||||
lbry
|
||||
.status()
|
||||
.then(response => {
|
||||
let installation_id = response.installation_id;
|
||||
if (!lbryio.enabled) {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve({
|
||||
id: 1,
|
||||
language: "en",
|
||||
has_email: true,
|
||||
has_verified_email: true,
|
||||
is_reward_approved: false,
|
||||
is_reward_eligible: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
if (lbryio._authenticationPromise === null) {
|
||||
lbryio._authenticationPromise = new Promise((resolve, reject) => {
|
||||
lbry
|
||||
.status()
|
||||
.then(response => {
|
||||
let installation_id = response.installation_id;
|
||||
|
||||
function setCurrentUser() {
|
||||
lbryio
|
||||
.call('user', 'me')
|
||||
.then(data => {
|
||||
lbryio.user = data;
|
||||
resolve(data);
|
||||
})
|
||||
.catch(function(err) {
|
||||
lbryio.setAccessToken(null);
|
||||
if (!getSession('reloadedOnFailedAuth')) {
|
||||
setSession('reloadedOnFailedAuth', true);
|
||||
window.location.reload();
|
||||
} else {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!lbryio.getAccessToken()) {
|
||||
lbryio
|
||||
.call(
|
||||
'user',
|
||||
'new',
|
||||
{
|
||||
language: 'en',
|
||||
app_id: installation_id
|
||||
},
|
||||
'post'
|
||||
)
|
||||
.then(function(responseData) {
|
||||
if (!responseData.id) {
|
||||
reject(
|
||||
new Error(__('Received invalid authentication response.'))
|
||||
);
|
||||
}
|
||||
lbryio.setAccessToken(installation_id);
|
||||
setCurrentUser();
|
||||
})
|
||||
.catch(function(error) {
|
||||
/*
|
||||
until we have better error code format, assume all errors are duplicate application id
|
||||
if we're wrong, this will be caught by later attempts to make a valid call
|
||||
*/
|
||||
lbryio.setAccessToken(installation_id);
|
||||
setCurrentUser();
|
||||
});
|
||||
} else {
|
||||
setCurrentUser();
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
return lbryio._authenticationPromise;
|
||||
if (!lbryio.getAccessToken()) {
|
||||
lbryio
|
||||
.call(
|
||||
"user",
|
||||
"new",
|
||||
{
|
||||
language: "en",
|
||||
app_id: installation_id,
|
||||
},
|
||||
"post"
|
||||
)
|
||||
.then(function(responseData) {
|
||||
if (!responseData.id) {
|
||||
reject(
|
||||
new Error("Received invalid authentication response.")
|
||||
);
|
||||
}
|
||||
lbryio.setAccessToken(installation_id);
|
||||
lbryio.setCurrentUser(resolve, reject);
|
||||
})
|
||||
.catch(function(error) {
|
||||
/*
|
||||
until we have better error code format, assume all errors are duplicate application id
|
||||
if we're wrong, this will be caught by later attempts to make a valid call
|
||||
*/
|
||||
lbryio.setAccessToken(installation_id);
|
||||
lbryio.setCurrentUser(resolve, reject);
|
||||
});
|
||||
} else {
|
||||
lbryio.setCurrentUser(resolve, reject);
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
return lbryio._authenticationPromise;
|
||||
};
|
||||
|
||||
export default lbryio;
|
||||
|
|
|
@ -1,18 +1,13 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import whyDidYouUpdate from "why-did-you-update";
|
||||
import lbry from "./lbry.js";
|
||||
import lbryio from "./lbryio.js";
|
||||
import lighthouse from "./lighthouse.js";
|
||||
import App from "component/app/index.js";
|
||||
import SnackBar from "component/snackBar";
|
||||
import { Provider } from "react-redux";
|
||||
import store from "store.js";
|
||||
import SplashScreen from "component/splash.js";
|
||||
import { AuthOverlay } from "component/auth.js";
|
||||
import AuthOverlay from "component/authOverlay";
|
||||
import { doChangePath, doNavigate, doDaemonReady } from "actions/app";
|
||||
import { doFetchDaemonSettings } from "actions/settings";
|
||||
import { doFileList } from "actions/file_info";
|
||||
import { toQueryString } from "util/query_params";
|
||||
|
||||
const env = ENV;
|
||||
|
@ -57,7 +52,10 @@ ipcRenderer.on("open-uri-requested", (event, uri) => {
|
|||
document.addEventListener("click", event => {
|
||||
var target = event.target;
|
||||
while (target && target !== document) {
|
||||
if (target.matches('a[href^="http"]')) {
|
||||
if (
|
||||
target.matches('a[href^="http"]') ||
|
||||
target.matches('a[href^="mailto"]')
|
||||
) {
|
||||
event.preventDefault();
|
||||
shell.openExternal(target.href);
|
||||
return;
|
||||
|
@ -68,31 +66,27 @@ document.addEventListener("click", event => {
|
|||
|
||||
const initialState = app.store.getState();
|
||||
|
||||
if (env === "development") {
|
||||
/*
|
||||
https://github.com/garbles/why-did-you-update
|
||||
"A function that monkey patches React and notifies you in the console when
|
||||
potentially unnecessary re-renders occur."
|
||||
|
||||
Just checks if props change between updates. Can be fixed by manually
|
||||
adding a check in shouldComponentUpdate or using React.PureComponent
|
||||
*/
|
||||
whyDidYouUpdate(React);
|
||||
}
|
||||
// import whyDidYouUpdate from "why-did-you-update";
|
||||
// if (env === "development") {
|
||||
// /*
|
||||
// https://github.com/garbles/why-did-you-update
|
||||
// "A function that monkey patches React and notifies you in the console when
|
||||
// potentially unnecessary re-renders occur."
|
||||
//
|
||||
// Just checks if props change between updates. Can be fixed by manually
|
||||
// adding a check in shouldComponentUpdate or using React.PureComponent
|
||||
// */
|
||||
// whyDidYouUpdate(React);
|
||||
// }
|
||||
|
||||
var init = function() {
|
||||
function onDaemonReady() {
|
||||
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(doChangePath("/discover"));
|
||||
app.store.dispatch(doFetchDaemonSettings());
|
||||
app.store.dispatch(doFileList());
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<div>{lbryio.enabled ? <AuthOverlay /> : ""}<App /><SnackBar /></div>
|
||||
<div><AuthOverlay /><App /><SnackBar /></div>
|
||||
</Provider>,
|
||||
canvas
|
||||
);
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import React from "react";
|
||||
import rewards from "rewards";
|
||||
import { connect } from "react-redux";
|
||||
import { doFetchFileInfosAndPublishedClaims } from "actions/file_info";
|
||||
import {
|
||||
selectFileInfosPublished,
|
||||
selectFileListDownloadedOrPublishedIsPending,
|
||||
} from "selectors/file_info";
|
||||
import { doClaimRewardType } from "actions/rewards";
|
||||
import { doNavigate } from "actions/app";
|
||||
import FileListPublished from "./view";
|
||||
|
||||
|
@ -16,6 +18,8 @@ const select = state => ({
|
|||
const perform = dispatch => ({
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
fetchFileListPublished: () => dispatch(doFetchFileInfosAndPublishedClaims()),
|
||||
claimFirstPublishReward: () =>
|
||||
dispatch(doClaimRewardType(rewards.TYPE_FIRST_PUBLISH)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(FileListPublished);
|
||||
|
|
|
@ -16,24 +16,7 @@ class FileListPublished extends React.PureComponent {
|
|||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.props.fileInfos.length > 0) this._requestPublishReward();
|
||||
}
|
||||
|
||||
_requestPublishReward() {
|
||||
// TODO this is throwing an error now
|
||||
// Error: LBRY internal API is disabled
|
||||
//
|
||||
// lbryio.call('reward', 'list', {}).then(function(userRewards) {
|
||||
// //already rewarded
|
||||
// if (userRewards.filter(function (reward) {
|
||||
// return reward.reward_type == rewards.TYPE_FIRST_PUBLISH && reward.transaction_id
|
||||
// }).length) {
|
||||
// return
|
||||
// }
|
||||
// else {
|
||||
// rewards.claimReward(rewards.TYPE_FIRST_PUBLISH).catch(() => {})
|
||||
// }
|
||||
// })
|
||||
if (this.props.fileInfos.length > 0) this.props.claimFirstPublishReward();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { doNavigate, doHistoryBack } from "actions/app";
|
||||
import { doClaimRewardType } from "actions/rewards";
|
||||
import { selectMyClaims } from "selectors/claims";
|
||||
import { doFetchClaimListMine } from "actions/content";
|
||||
import rewards from "rewards";
|
||||
import PublishPage from "./view";
|
||||
|
||||
const select = state => ({
|
||||
|
@ -13,6 +15,7 @@ const perform = dispatch => ({
|
|||
back: () => dispatch(doHistoryBack()),
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
fetchClaimListMine: () => dispatch(doFetchClaimListMine()),
|
||||
claimFirstChannelReward: () => dispatch(doClaimRewardType(rewards.TYPE_FIRST_CHANNEL)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(PublishPage);
|
||||
|
|
|
@ -44,7 +44,7 @@ class PublishPage extends React.PureComponent {
|
|||
// Calls API to update displayed list of channels. If a channel name is provided, will select
|
||||
// that channel at the same time (used immediately after creating a channel)
|
||||
lbry.channel_list_mine().then(channels => {
|
||||
rewards.claimReward(rewards.TYPE_FIRST_CHANNEL).then(() => {}, () => {});
|
||||
this.props.claimFirstChannelReward();
|
||||
this.setState({
|
||||
channels: channels,
|
||||
...(channel ? { channel } : {}),
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
import React from "react";
|
||||
import lbryio from "lbryio";
|
||||
import { CreditAmount, Icon } from "component/common.js";
|
||||
import SubHeader from "component/subHeader";
|
||||
import { RewardLink } from "component/reward-link";
|
||||
|
||||
export class RewardTile extends React.PureComponent {
|
||||
static propTypes = {
|
||||
type: React.PropTypes.string.isRequired,
|
||||
title: React.PropTypes.string.isRequired,
|
||||
description: React.PropTypes.string.isRequired,
|
||||
claimed: React.PropTypes.bool.isRequired,
|
||||
value: React.PropTypes.number.isRequired,
|
||||
onRewardClaim: React.PropTypes.func,
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<section className="card">
|
||||
<div className="card__inner">
|
||||
<div className="card__title-primary">
|
||||
<CreditAmount amount={this.props.value} />
|
||||
<h3>{this.props.title}</h3>
|
||||
</div>
|
||||
<div className="card__actions">
|
||||
{this.props.claimed
|
||||
? <span><Icon icon="icon-check" /> {__("Reward claimed.")}</span>
|
||||
: <RewardLink {...this.props} />}
|
||||
</div>
|
||||
<div className="card__content">{this.props.description}</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class RewardsPage extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
userRewards: null,
|
||||
failed: null,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.loadRewards();
|
||||
}
|
||||
|
||||
loadRewards() {
|
||||
lbryio.call("reward", "list", {}).then(
|
||||
userRewards => {
|
||||
this.setState({
|
||||
userRewards: userRewards,
|
||||
});
|
||||
},
|
||||
() => {
|
||||
this.setState({ failed: true });
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
<SubHeader />
|
||||
<div>
|
||||
{!this.state.userRewards
|
||||
? this.state.failed
|
||||
? <div className="empty">{__("Failed to load rewards.")}</div>
|
||||
: ""
|
||||
: this.state.userRewards.map(
|
||||
({
|
||||
reward_type,
|
||||
reward_title,
|
||||
reward_description,
|
||||
transaction_id,
|
||||
reward_amount,
|
||||
}) => {
|
||||
return (
|
||||
<RewardTile
|
||||
key={reward_type}
|
||||
onRewardClaim={this.loadRewards}
|
||||
type={reward_type}
|
||||
title={__(reward_title)}
|
||||
description={__(reward_description)}
|
||||
claimed={!!transaction_id}
|
||||
value={reward_amount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RewardsPage;
|
20
ui/js/page/rewards/index.js
Normal file
20
ui/js/page/rewards/index.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { doNavigate } from "actions/app";
|
||||
import { selectFetchingRewards, selectRewards } from "selectors/rewards";
|
||||
import {
|
||||
selectUserIsRewardEligible,
|
||||
selectUserHasEmail,
|
||||
selectUserIsVerificationCandidate,
|
||||
} from "selectors/user";
|
||||
import RewardsPage from "./view";
|
||||
|
||||
const select = state => ({
|
||||
fetching: selectFetchingRewards(state),
|
||||
rewards: selectRewards(state),
|
||||
hasEmail: selectUserHasEmail(state),
|
||||
isEligible: selectUserIsRewardEligible(state),
|
||||
isVerificationCandidate: selectUserIsVerificationCandidate(state),
|
||||
});
|
||||
|
||||
export default connect(select, null)(RewardsPage);
|
92
ui/js/page/rewards/view.jsx
Normal file
92
ui/js/page/rewards/view.jsx
Normal file
|
@ -0,0 +1,92 @@
|
|||
import React from "react";
|
||||
import lbryio from "lbryio";
|
||||
import { BusyMessage, CreditAmount, Icon } from "component/common";
|
||||
import SubHeader from "component/subHeader";
|
||||
import Auth from "component/auth";
|
||||
import Link from "component/link";
|
||||
import RewardLink from "component/rewardLink";
|
||||
|
||||
const RewardTile = props => {
|
||||
const { reward } = props;
|
||||
|
||||
const claimed = !!reward.transaction_id;
|
||||
|
||||
return (
|
||||
<section className="card">
|
||||
<div className="card__inner">
|
||||
<div className="card__title-primary">
|
||||
<CreditAmount amount={reward.reward_amount} />
|
||||
<h3>{reward.reward_title}</h3>
|
||||
</div>
|
||||
<div className="card__actions">
|
||||
{claimed
|
||||
? <span><Icon icon="icon-check" /> Reward claimed.</span>
|
||||
: <RewardLink reward_type={reward.reward_type} />}
|
||||
</div>
|
||||
<div className="card__content">{reward.reward_description}</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
const RewardsPage = props => {
|
||||
const {
|
||||
fetching,
|
||||
isEligible,
|
||||
isVerificationCandidate,
|
||||
hasEmail,
|
||||
rewards,
|
||||
} = props;
|
||||
|
||||
let content,
|
||||
isCard = false;
|
||||
|
||||
if (!hasEmail || isVerificationCandidate) {
|
||||
content = (
|
||||
<div>
|
||||
<p>
|
||||
{__(
|
||||
"Additional information is required to be eligible for the rewards program."
|
||||
)}
|
||||
</p>
|
||||
<Auth />
|
||||
</div>
|
||||
);
|
||||
isCard = true;
|
||||
} else if (!isEligible) {
|
||||
isCard = true;
|
||||
content = (
|
||||
<div className="empty">
|
||||
<p>{__("You are not eligible to claim rewards.")}</p>
|
||||
<p>
|
||||
To become eligible, email
|
||||
{" "}<Link href="mailto:help@lbry.io" label="help@lbry.io" /> with a
|
||||
link to a public social media profile.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
} else if (fetching) {
|
||||
content = <BusyMessage message="Fetching rewards" />;
|
||||
} else if (rewards.length > 0) {
|
||||
content = rewards.map(reward =>
|
||||
<RewardTile key={reward.reward_type} reward={reward} />
|
||||
);
|
||||
} else {
|
||||
content = <div className="empty">{__("Failed to load rewards.")}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
<SubHeader />
|
||||
{isCard
|
||||
? <section className="card">
|
||||
<div className="card__content">
|
||||
{content}
|
||||
</div>
|
||||
</section>
|
||||
: content}
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
export default RewardsPage;
|
|
@ -229,25 +229,32 @@ class SettingsPage extends React.PureComponent {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
{/*}
|
||||
<section className="card">
|
||||
<div className="card__content">
|
||||
<h3>{__("Language")}</h3>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
<div className="form-row">
|
||||
<FormField type="radio"
|
||||
name="language"
|
||||
label={__("English")}
|
||||
onChange={() => { this.onLanguageChange('en') }}
|
||||
defaultChecked={this.state.language=='en'} />
|
||||
<FormField
|
||||
type="radio"
|
||||
name="language"
|
||||
label={__("English")}
|
||||
onChange={() => {
|
||||
this.onLanguageChange("en");
|
||||
}}
|
||||
defaultChecked={this.state.language == "en"}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-row">
|
||||
<FormField type="radio"
|
||||
name="language"
|
||||
label="Serbian"
|
||||
onChange={() => { this.onLanguageChange('rs') }}
|
||||
defaultChecked={this.state.language=='rs'} />
|
||||
<FormField
|
||||
type="radio"
|
||||
name="language"
|
||||
label="Serbian"
|
||||
onChange={() => {
|
||||
this.onLanguageChange("rs");
|
||||
}}
|
||||
defaultChecked={this.state.language == "rs"}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>*/}
|
||||
|
|
|
@ -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) {
|
||||
const { message, linkText, linkTarget, isError } = action.data;
|
||||
const snackBar = Object.assign({}, state.snackBar);
|
||||
|
|
|
@ -1,7 +1,90 @@
|
|||
import * as types from "constants/action_types";
|
||||
|
||||
const reducers = {};
|
||||
const defaultState = {};
|
||||
const defaultState = {
|
||||
fetching: false,
|
||||
rewardsByType: {},
|
||||
claimPendingByType: {},
|
||||
claimErrorsByType: {},
|
||||
};
|
||||
|
||||
reducers[types.FETCH_REWARDS_STARTED] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
fetching: true,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.FETCH_REWARDS_COMPLETED] = function(state, action) {
|
||||
const { userRewards } = action.data;
|
||||
|
||||
const rewardsByType = {};
|
||||
userRewards.forEach(reward => (rewardsByType[reward.reward_type] = reward));
|
||||
|
||||
return Object.assign({}, state, {
|
||||
rewardsByType: rewardsByType,
|
||||
fetching: false,
|
||||
});
|
||||
};
|
||||
|
||||
function setClaimRewardState(state, reward, isClaiming, errorMessage = "") {
|
||||
const newClaimPendingByType = Object.assign({}, state.claimPendingByType);
|
||||
const newClaimErrorsByType = Object.assign({}, state.claimErrorsByType);
|
||||
if (isClaiming) {
|
||||
newClaimPendingByType[reward.reward_type] = isClaiming;
|
||||
} else {
|
||||
delete newClaimPendingByType[reward.reward_type];
|
||||
}
|
||||
if (errorMessage) {
|
||||
newClaimErrorsByType[reward.reward_type] = errorMessage;
|
||||
} else {
|
||||
delete newClaimErrorsByType[reward.reward_type];
|
||||
}
|
||||
|
||||
return Object.assign({}, state, {
|
||||
claimPendingByType: newClaimPendingByType,
|
||||
claimErrorsByType: newClaimErrorsByType,
|
||||
});
|
||||
}
|
||||
|
||||
reducers[types.CLAIM_REWARD_STARTED] = function(state, action) {
|
||||
const { reward } = action.data;
|
||||
|
||||
return setClaimRewardState(state, reward, true, "");
|
||||
};
|
||||
|
||||
reducers[types.CLAIM_REWARD_SUCCESS] = function(state, action) {
|
||||
const { reward } = action.data;
|
||||
|
||||
const existingReward = state.rewardsByType[reward.reward_type];
|
||||
const newReward = Object.assign({}, reward, {
|
||||
reward_title: existingReward.reward_title,
|
||||
reward_description: existingReward.reward_description,
|
||||
});
|
||||
const rewardsByType = Object.assign({}, state.rewardsByType);
|
||||
|
||||
rewardsByType[reward.reward_type] = newReward;
|
||||
|
||||
const newState = Object.assign({}, state, { rewardsByType });
|
||||
|
||||
return setClaimRewardState(newState, newReward, false, "");
|
||||
};
|
||||
|
||||
reducers[types.CLAIM_REWARD_FAILURE] = function(state, action) {
|
||||
const { reward, error } = action.data;
|
||||
|
||||
return setClaimRewardState(state, reward, false, error ? error.message : "");
|
||||
};
|
||||
|
||||
reducers[types.CLAIM_REWARD_CLEAR_ERROR] = function(state, action) {
|
||||
const { reward } = action.data;
|
||||
|
||||
return setClaimRewardState(
|
||||
state,
|
||||
reward,
|
||||
state.claimPendingByType[reward.reward_type],
|
||||
""
|
||||
);
|
||||
};
|
||||
|
||||
export default function reducer(state = defaultState, action) {
|
||||
const handler = reducers[action.type];
|
||||
|
|
127
ui/js/reducers/user.js
Normal file
127
ui/js/reducers/user.js
Normal file
|
@ -0,0 +1,127 @@
|
|||
import * as types from "constants/action_types";
|
||||
import { getLocal } from "utils";
|
||||
|
||||
const reducers = {};
|
||||
|
||||
const defaultState = {
|
||||
authenticationIsPending: false,
|
||||
userIsPending: false,
|
||||
emailNewIsPending: false,
|
||||
emailNewErrorMessage: "",
|
||||
emailNewDeclined: getLocal("user_email_declined", false),
|
||||
emailToVerify: "",
|
||||
user: undefined,
|
||||
};
|
||||
|
||||
reducers[types.AUTHENTICATION_STARTED] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
authenticationIsPending: true,
|
||||
userIsPending: true,
|
||||
user: defaultState.user,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.AUTHENTICATION_SUCCESS] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
authenticationIsPending: false,
|
||||
userIsPending: false,
|
||||
user: action.data.user,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.AUTHENTICATION_FAILURE] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
authenticationIsPending: false,
|
||||
userIsPending: false,
|
||||
user: null,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.USER_FETCH_STARTED] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
userIsPending: true,
|
||||
user: defaultState.user,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.USER_FETCH_SUCCESS] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
userIsPending: false,
|
||||
user: action.data.user,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.USER_FETCH_FAILURE] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
userIsPending: true,
|
||||
user: null,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.USER_EMAIL_DECLINE] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
emailNewDeclined: true,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.USER_EMAIL_NEW_STARTED] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
emailNewIsPending: true,
|
||||
emailNewErrorMessage: "",
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.USER_EMAIL_NEW_SUCCESS] = function(state, action) {
|
||||
let user = Object.assign({}, state.user);
|
||||
user.has_email = true;
|
||||
return Object.assign({}, state, {
|
||||
emailToVerify: action.data.email,
|
||||
emailNewIsPending: false,
|
||||
user: user,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.USER_EMAIL_NEW_EXISTS] = function(state, action) {
|
||||
let user = Object.assign({}, state.user);
|
||||
return Object.assign({}, state, {
|
||||
emailToVerify: action.data.email,
|
||||
emailNewIsPending: false,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.USER_EMAIL_NEW_FAILURE] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
emailNewIsPending: false,
|
||||
emailNewErrorMessage: action.data.error,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.USER_EMAIL_VERIFY_STARTED] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
emailVerifyIsPending: true,
|
||||
emailVerifyErrorMessage: "",
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.USER_EMAIL_VERIFY_SUCCESS] = function(state, action) {
|
||||
let user = Object.assign({}, state.user);
|
||||
user.has_email = true;
|
||||
return Object.assign({}, state, {
|
||||
emailToVerify: "",
|
||||
emailVerifyIsPending: false,
|
||||
user: user,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[types.USER_EMAIL_VERIFY_FAILURE] = function(state, action) {
|
||||
return Object.assign({}, state, {
|
||||
emailVerifyIsPending: false,
|
||||
emailVerifyErrorMessage: action.data.error,
|
||||
});
|
||||
};
|
||||
|
||||
export default function reducer(state = defaultState, action) {
|
||||
const handler = reducers[action.type];
|
||||
if (handler) return handler(state, action);
|
||||
return state;
|
||||
}
|
345
ui/js/rewards.js
345
ui/js/rewards.js
|
@ -1,222 +1,191 @@
|
|||
const hashes = require('jshashes');
|
||||
import lbry from 'lbry';
|
||||
import lbryio from 'lbryio';
|
||||
import { doShowSnackBar } from 'actions/app';
|
||||
const hashes = require("jshashes");
|
||||
import lbry from "lbry";
|
||||
import lbryio from "lbryio";
|
||||
import { doShowSnackBar } from "actions/app";
|
||||
|
||||
function rewardMessage(type, amount) {
|
||||
return {
|
||||
new_developer: __(
|
||||
'You earned %s for registering as a new developer.',
|
||||
amount
|
||||
),
|
||||
new_user: __('You earned %s LBC new user reward.', amount),
|
||||
confirm_email: __(
|
||||
'You earned %s LBC for verifying your email address.',
|
||||
amount
|
||||
),
|
||||
new_channel: __(
|
||||
'You earned %s LBC for creating a publisher identity.',
|
||||
amount
|
||||
),
|
||||
first_stream: __(
|
||||
'You earned %s LBC for streaming your first video.',
|
||||
amount
|
||||
),
|
||||
many_downloads: __(
|
||||
'You earned %s LBC for downloading some of the things.',
|
||||
amount
|
||||
),
|
||||
first_publish: __(
|
||||
'You earned %s LBC for making your first publication.',
|
||||
amount
|
||||
)
|
||||
}[type];
|
||||
return {
|
||||
new_developer: __(
|
||||
"You earned %s for registering as a new developer.",
|
||||
amount
|
||||
),
|
||||
new_user: __("You earned %s LBC new user reward.", amount),
|
||||
confirm_email: __(
|
||||
"You earned %s LBC for verifying your email address.",
|
||||
amount
|
||||
),
|
||||
new_channel: __(
|
||||
"You earned %s LBC for creating a publisher identity.",
|
||||
amount
|
||||
),
|
||||
first_stream: __(
|
||||
"You earned %s LBC for streaming your first video.",
|
||||
amount
|
||||
),
|
||||
many_downloads: __(
|
||||
"You earned %s LBC for downloading some of the things.",
|
||||
amount
|
||||
),
|
||||
first_publish: __(
|
||||
"You earned %s LBC for making your first publication.",
|
||||
amount
|
||||
),
|
||||
}[type];
|
||||
}
|
||||
|
||||
function toHex(s) {
|
||||
let h = '';
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
let c = s.charCodeAt(i).toString(16);
|
||||
if (c.length < 2) {
|
||||
c = '0'.concat(c);
|
||||
}
|
||||
h += c;
|
||||
}
|
||||
return h;
|
||||
let h = "";
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
let c = s.charCodeAt(i).toString(16);
|
||||
if (c.length < 2) {
|
||||
c = "0".concat(c);
|
||||
}
|
||||
h += c;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
function fromHex(h) {
|
||||
let s = '';
|
||||
for (let i = 0; i < h.length; i += 2) {
|
||||
s += String.fromCharCode(parseInt(h.substr(i, 2), 16));
|
||||
}
|
||||
return s;
|
||||
let s = "";
|
||||
for (let i = 0; i < h.length; i += 2) {
|
||||
s += String.fromCharCode(parseInt(h.substr(i, 2), 16));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function reverseString(s) {
|
||||
let o = '';
|
||||
for (let i = s.length - 1; i >= 0; i--) {
|
||||
o += s[i];
|
||||
}
|
||||
return o;
|
||||
let o = "";
|
||||
for (let i = s.length - 1; i >= 0; i--) {
|
||||
o += s[i];
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
function pack(num) {
|
||||
return (
|
||||
'' +
|
||||
String.fromCharCode(num & 0xff) +
|
||||
String.fromCharCode((num >> 8) & 0xff) +
|
||||
String.fromCharCode((num >> 16) & 0xff) +
|
||||
String.fromCharCode((num >> 24) & 0xff)
|
||||
);
|
||||
return (
|
||||
"" +
|
||||
String.fromCharCode(num & 0xff) +
|
||||
String.fromCharCode((num >> 8) & 0xff) +
|
||||
String.fromCharCode((num >> 16) & 0xff) +
|
||||
String.fromCharCode((num >> 24) & 0xff)
|
||||
);
|
||||
}
|
||||
|
||||
// Returns true if claim is an initial claim, false if it's an update to an existing claim
|
||||
function isInitialClaim(claim) {
|
||||
const reversed = reverseString(fromHex(claim.txid));
|
||||
const concat = reversed.concat(pack(claim.nout));
|
||||
const sha256 = new hashes.SHA256({ utf8: false }).raw(concat);
|
||||
const ripemd160 = new hashes.RMD160({ utf8: false }).raw(sha256);
|
||||
const hash = toHex(reverseString(ripemd160));
|
||||
return hash == claim.claim_id;
|
||||
const reversed = reverseString(fromHex(claim.txid));
|
||||
const concat = reversed.concat(pack(claim.nout));
|
||||
const sha256 = new hashes.SHA256({ utf8: false }).raw(concat);
|
||||
const ripemd160 = new hashes.RMD160({ utf8: false }).raw(sha256);
|
||||
const hash = toHex(reverseString(ripemd160));
|
||||
return hash == claim.claim_id;
|
||||
}
|
||||
|
||||
const rewards = {};
|
||||
|
||||
(rewards.TYPE_NEW_DEVELOPER = 'new_developer'), (rewards.TYPE_NEW_USER =
|
||||
'new_user'), (rewards.TYPE_CONFIRM_EMAIL =
|
||||
'confirm_email'), (rewards.TYPE_FIRST_CHANNEL =
|
||||
'new_channel'), (rewards.TYPE_FIRST_STREAM =
|
||||
'first_stream'), (rewards.TYPE_MANY_DOWNLOADS =
|
||||
'many_downloads'), (rewards.TYPE_FIRST_PUBLISH = 'first_publish');
|
||||
rewards.TYPE_FEATURED_DOWNLOAD = 'featured_download';
|
||||
(rewards.TYPE_NEW_DEVELOPER = "new_developer"), (rewards.TYPE_NEW_USER =
|
||||
"new_user"), (rewards.TYPE_CONFIRM_EMAIL =
|
||||
"confirm_email"), (rewards.TYPE_FIRST_CHANNEL =
|
||||
"new_channel"), (rewards.TYPE_FIRST_STREAM =
|
||||
"first_stream"), (rewards.TYPE_MANY_DOWNLOADS =
|
||||
"many_downloads"), (rewards.TYPE_FIRST_PUBLISH = "first_publish");
|
||||
rewards.TYPE_FEATURED_DOWNLOAD = "featured_download";
|
||||
|
||||
rewards.claimReward = function(type) {
|
||||
function requestReward(resolve, reject, params) {
|
||||
if (!lbryio.enabled) {
|
||||
reject(new Error(__('Rewards are not enabled.')));
|
||||
return;
|
||||
}
|
||||
lbryio.call('reward', 'new', params, 'post').then(({ reward_amount }) => {
|
||||
const message = rewardMessage(type, reward_amount),
|
||||
result = {
|
||||
type: type,
|
||||
amount: reward_amount,
|
||||
message: message
|
||||
};
|
||||
function requestReward(resolve, reject, params) {
|
||||
if (!lbryio.enabled || !lbryio.getAccessToken()) {
|
||||
reject(new Error(__("Rewards are not enabled.")));
|
||||
return;
|
||||
}
|
||||
lbryio.call("reward", "new", params, "post").then(reward => {
|
||||
const message = rewardMessage(type, reward.reward_amount);
|
||||
|
||||
// Display global notice
|
||||
const action = doShowSnackBar({
|
||||
message,
|
||||
linkText: __('Show All'),
|
||||
linkTarget: '/rewards',
|
||||
isError: false
|
||||
});
|
||||
window.app.store.dispatch(action);
|
||||
// Display global notice
|
||||
const action = doShowSnackBar({
|
||||
message,
|
||||
linkText: __("Show All"),
|
||||
linkTarget: "/rewards",
|
||||
isError: false,
|
||||
});
|
||||
window.app.store.dispatch(action);
|
||||
|
||||
// Add more events here to display other places
|
||||
// Add more events here to display other places
|
||||
|
||||
resolve(result);
|
||||
}, reject);
|
||||
}
|
||||
resolve(reward);
|
||||
}, reject);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
lbry.wallet_unused_address().then(address => {
|
||||
const params = {
|
||||
reward_type: type,
|
||||
wallet_address: address
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
lbry.wallet_unused_address().then(address => {
|
||||
const params = {
|
||||
reward_type: type,
|
||||
wallet_address: address,
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case rewards.TYPE_FIRST_CHANNEL:
|
||||
lbry
|
||||
.claim_list_mine()
|
||||
.then(function(claims) {
|
||||
let claim = claims.find(function(claim) {
|
||||
return (
|
||||
claim.name.length &&
|
||||
claim.name[0] == '@' &&
|
||||
claim.txid.length &&
|
||||
isInitialClaim(claim)
|
||||
);
|
||||
});
|
||||
if (claim) {
|
||||
params.transaction_id = claim.txid;
|
||||
requestReward(resolve, reject, params);
|
||||
} else {
|
||||
reject(
|
||||
new Error(__('Please create a channel identity first.'))
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
break;
|
||||
switch (type) {
|
||||
case rewards.TYPE_FIRST_CHANNEL:
|
||||
lbry
|
||||
.claim_list_mine()
|
||||
.then(function(claims) {
|
||||
let claim = claims.reverse().find(function(claim) {
|
||||
return (
|
||||
claim.name.length &&
|
||||
claim.name[0] == "@" &&
|
||||
claim.txid.length &&
|
||||
isInitialClaim(claim)
|
||||
);
|
||||
});
|
||||
if (claim) {
|
||||
params.transaction_id = claim.txid;
|
||||
requestReward(resolve, reject, params);
|
||||
} else {
|
||||
reject(
|
||||
new Error(__("Please create a channel identity first."))
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
break;
|
||||
|
||||
case rewards.TYPE_FIRST_PUBLISH:
|
||||
lbry
|
||||
.claim_list_mine()
|
||||
.then(claims => {
|
||||
let claim = claims.find(function(claim) {
|
||||
return (
|
||||
claim.name.length &&
|
||||
claim.name[0] != '@' &&
|
||||
claim.txid.length &&
|
||||
isInitialClaim(claim)
|
||||
);
|
||||
});
|
||||
if (claim) {
|
||||
params.transaction_id = claim.txid;
|
||||
requestReward(resolve, reject, params);
|
||||
} else {
|
||||
reject(
|
||||
claims.length
|
||||
? new Error(
|
||||
__(
|
||||
'Please publish something and wait for confirmation by the network to claim this reward.'
|
||||
)
|
||||
)
|
||||
: new Error(
|
||||
__('Please publish something to claim this reward.')
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
break;
|
||||
case rewards.TYPE_FIRST_PUBLISH:
|
||||
lbry
|
||||
.claim_list_mine()
|
||||
.then(claims => {
|
||||
let claim = claims.reverse().find(function(claim) {
|
||||
return (
|
||||
claim.name.length &&
|
||||
claim.name[0] != "@" &&
|
||||
claim.txid.length &&
|
||||
isInitialClaim(claim)
|
||||
);
|
||||
});
|
||||
if (claim) {
|
||||
params.transaction_id = claim.txid;
|
||||
requestReward(resolve, reject, params);
|
||||
} else {
|
||||
reject(
|
||||
claims.length
|
||||
? new Error(
|
||||
__(
|
||||
"Please publish something and wait for confirmation by the network to claim this reward."
|
||||
)
|
||||
)
|
||||
: new Error(
|
||||
__("Please publish something to claim this reward.")
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
break;
|
||||
|
||||
case rewards.TYPE_FIRST_STREAM:
|
||||
case rewards.TYPE_NEW_USER:
|
||||
default:
|
||||
requestReward(resolve, reject, params);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
rewards.claimEligiblePurchaseRewards = function() {
|
||||
let types = {};
|
||||
types[rewards.TYPE_FIRST_STREAM] = false;
|
||||
types[rewards.TYPE_FEATURED_DOWNLOAD] = false;
|
||||
types[rewards.TYPE_MANY_DOWNLOADS] = false;
|
||||
lbryio.call('reward', 'list', {}).then(
|
||||
userRewards => {
|
||||
userRewards.forEach(reward => {
|
||||
if (types[reward.reward_type] === false && reward.transaction_id) {
|
||||
types[reward.reward_type] = true;
|
||||
}
|
||||
});
|
||||
let unclaimedType = Object.keys(types).find(type => {
|
||||
return types[type] === false && type !== rewards.TYPE_FEATURED_DOWNLOAD; //handled below
|
||||
});
|
||||
if (unclaimedType) {
|
||||
rewards.claimReward(unclaimedType);
|
||||
}
|
||||
if (types[rewards.TYPE_FEATURED_DOWNLOAD] === false) {
|
||||
rewards.claimReward(rewards.TYPE_FEATURED_DOWNLOAD);
|
||||
}
|
||||
},
|
||||
() => {}
|
||||
);
|
||||
case rewards.TYPE_FIRST_STREAM:
|
||||
case rewards.TYPE_NEW_USER:
|
||||
default:
|
||||
requestReward(resolve, reject, params);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export default rewards;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { createSelector } from "reselect";
|
||||
import { selectDaemonReady, selectCurrentPage } from "selectors/app";
|
||||
|
||||
const _selectState = state => state.availability;
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { createSelector } from "reselect";
|
||||
import { selectDaemonReady, selectCurrentPage } from "selectors/app";
|
||||
|
||||
export const _selectState = state => state.content || {};
|
||||
|
||||
|
|
|
@ -1,3 +1,67 @@
|
|||
import { createSelector } from "reselect";
|
||||
import { selectUser } from "selectors/user";
|
||||
|
||||
export const _selectState = state => state.rewards || {};
|
||||
const _selectState = state => state.rewards || {};
|
||||
|
||||
export const selectRewardsByType = createSelector(
|
||||
_selectState,
|
||||
state => state.rewardsByType || {}
|
||||
);
|
||||
|
||||
export const selectRewards = createSelector(
|
||||
selectRewardsByType,
|
||||
byType => Object.values(byType) || []
|
||||
);
|
||||
|
||||
export const selectIsRewardEligible = createSelector(
|
||||
selectUser,
|
||||
user => user.can_claim_rewards
|
||||
);
|
||||
|
||||
export const selectFetchingRewards = createSelector(
|
||||
_selectState,
|
||||
state => !!state.fetching
|
||||
);
|
||||
|
||||
export const selectHasClaimedReward = (state, props) => {
|
||||
const reward = selectRewardsByType(state)[props.reward_type];
|
||||
return reward && reward.transaction_id !== "";
|
||||
};
|
||||
|
||||
export const makeSelectHasClaimedReward = () => {
|
||||
return createSelector(selectHasClaimedReward, claimed => claimed);
|
||||
};
|
||||
|
||||
export const selectClaimsPendingByType = createSelector(
|
||||
_selectState,
|
||||
state => state.claimPendingByType
|
||||
);
|
||||
|
||||
const selectIsClaimRewardPending = (state, props) => {
|
||||
return selectClaimsPendingByType(state, props)[props.reward_type];
|
||||
};
|
||||
|
||||
export const makeSelectIsRewardClaimPending = () => {
|
||||
return createSelector(selectIsClaimRewardPending, isClaiming => isClaiming);
|
||||
};
|
||||
|
||||
export const selectClaimErrorsByType = createSelector(
|
||||
_selectState,
|
||||
state => state.claimErrorsByType
|
||||
);
|
||||
|
||||
const selectClaimRewardError = (state, props) => {
|
||||
return selectClaimErrorsByType(state, props)[props.reward_type];
|
||||
};
|
||||
|
||||
export const makeSelectClaimRewardError = () => {
|
||||
return createSelector(selectClaimRewardError, errorMessage => errorMessage);
|
||||
};
|
||||
|
||||
const selectRewardByType = (state, props) => {
|
||||
return selectRewardsByType(state)[props.reward_type];
|
||||
};
|
||||
|
||||
export const makeSelectRewardByType = () => {
|
||||
return createSelector(selectRewardByType, reward => reward);
|
||||
};
|
||||
|
|
82
ui/js/selectors/user.js
Normal file
82
ui/js/selectors/user.js
Normal file
|
@ -0,0 +1,82 @@
|
|||
import { createSelector } from "reselect";
|
||||
|
||||
export const _selectState = state => state.user || {};
|
||||
|
||||
export const selectAuthenticationIsPending = createSelector(
|
||||
_selectState,
|
||||
state => state.authenticationIsPending
|
||||
);
|
||||
|
||||
export const selectUserIsPending = createSelector(
|
||||
_selectState,
|
||||
state => state.userIsPending
|
||||
);
|
||||
|
||||
export const selectUser = createSelector(
|
||||
_selectState,
|
||||
state => state.user || {}
|
||||
);
|
||||
|
||||
export const selectEmailToVerify = createSelector(
|
||||
_selectState,
|
||||
state => state.emailToVerify
|
||||
);
|
||||
|
||||
export const selectUserHasEmail = createSelector(
|
||||
selectUser,
|
||||
selectEmailToVerify,
|
||||
(user, email) => (user && user.has_email) || email
|
||||
);
|
||||
|
||||
export const selectUserIsRewardEligible = createSelector(
|
||||
selectUser,
|
||||
user => user && user.is_reward_eligible
|
||||
);
|
||||
|
||||
export const selectUserIsRewardApproved = createSelector(
|
||||
selectUser,
|
||||
user => user && user.is_reward_approved
|
||||
);
|
||||
|
||||
export const selectEmailNewIsPending = createSelector(
|
||||
_selectState,
|
||||
state => state.emailNewIsPending
|
||||
);
|
||||
|
||||
export const selectEmailNewErrorMessage = createSelector(
|
||||
_selectState,
|
||||
state => state.emailNewErrorMessage
|
||||
);
|
||||
|
||||
export const selectEmailNewDeclined = createSelector(
|
||||
_selectState,
|
||||
state => state.emailNewDeclined
|
||||
);
|
||||
|
||||
export const selectEmailVerifyIsPending = createSelector(
|
||||
_selectState,
|
||||
state => state.emailVerifyIsPending
|
||||
);
|
||||
|
||||
export const selectEmailVerifyErrorMessage = createSelector(
|
||||
_selectState,
|
||||
state => state.emailVerifyErrorMessage
|
||||
);
|
||||
|
||||
export const selectUserIsVerificationCandidate = createSelector(
|
||||
selectUserIsRewardEligible,
|
||||
selectUserIsRewardApproved,
|
||||
selectEmailToVerify,
|
||||
selectUser,
|
||||
(isEligible, isApproved, emailToVerify, user) =>
|
||||
(isEligible && !isApproved) || (emailToVerify && user && !user.has_email)
|
||||
);
|
||||
|
||||
export const selectUserIsAuthRequested = createSelector(
|
||||
selectEmailNewDeclined,
|
||||
selectAuthenticationIsPending,
|
||||
selectUserIsVerificationCandidate,
|
||||
selectUserHasEmail,
|
||||
(isEmailDeclined, isPending, isVerificationCandidate, hasEmail) =>
|
||||
!isEmailDeclined && (isPending || !hasEmail || isVerificationCandidate)
|
||||
);
|
|
@ -50,20 +50,6 @@ export const selectGettingNewAddress = createSelector(
|
|||
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(
|
||||
_selectState,
|
||||
state => state.draftTransaction || {}
|
||||
|
|
|
@ -13,6 +13,7 @@ import rewardsReducer from 'reducers/rewards';
|
|||
import searchReducer from 'reducers/search';
|
||||
import settingsReducer from 'reducers/settings';
|
||||
import walletReducer from 'reducers/wallet';
|
||||
import userReducer from 'reducers/user';
|
||||
|
||||
function isFunction(object) {
|
||||
return typeof object === 'function';
|
||||
|
@ -47,16 +48,17 @@ function enableBatching(reducer) {
|
|||
}
|
||||
|
||||
const reducers = redux.combineReducers({
|
||||
app: appReducer,
|
||||
availability: availabilityReducer,
|
||||
claims: claimsReducer,
|
||||
fileInfo: fileInfoReducer,
|
||||
content: contentReducer,
|
||||
costInfo: costInfoReducer,
|
||||
rewards: rewardsReducer,
|
||||
search: searchReducer,
|
||||
settings: settingsReducer,
|
||||
wallet: walletReducer
|
||||
app: appReducer,
|
||||
availability: availabilityReducer,
|
||||
claims: claimsReducer,
|
||||
fileInfo: fileInfoReducer,
|
||||
content: contentReducer,
|
||||
costInfo: costInfoReducer,
|
||||
rewards: rewardsReducer,
|
||||
search: searchReducer,
|
||||
settings: settingsReducer,
|
||||
wallet: walletReducer,
|
||||
user: userReducer,
|
||||
});
|
||||
|
||||
const bulkThunk = createBulkThunkMiddleware();
|
||||
|
|
334
ui/package-lock.json
generated
334
ui/package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "lbry-web-ui",
|
||||
"version": "0.12.0rc4",
|
||||
"version": "0.12.0rc6",
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"abbrev": {
|
||||
|
@ -89,6 +89,12 @@
|
|||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz",
|
||||
"integrity": "sha1-o+Uvo5FoyCX/V7AkgSbOWo/5VQc="
|
||||
},
|
||||
"app-root-path": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.0.1.tgz",
|
||||
"integrity": "sha1-zWLc+OT9WkF+/GZNLlsQZTxlG0Y=",
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.2.tgz",
|
||||
|
@ -223,14 +229,14 @@
|
|||
"integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ="
|
||||
},
|
||||
"babel-core": {
|
||||
"version": "6.24.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.24.1.tgz",
|
||||
"integrity": "sha1-jEKFZNzh4fQfszfsNPTDsCK1rYM="
|
||||
"version": "6.25.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.25.0.tgz",
|
||||
"integrity": "sha1-fdQrBGPHQunVKW3rPsZ6kyLa1yk="
|
||||
},
|
||||
"babel-generator": {
|
||||
"version": "6.24.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.24.1.tgz",
|
||||
"integrity": "sha1-5xX0hsWN7SVknYiJRNUqoHxdlJc="
|
||||
"version": "6.25.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.25.0.tgz",
|
||||
"integrity": "sha1-M6GvcNXyiQrrRlpKd5PB32qeqfw="
|
||||
},
|
||||
"babel-helper-bindify-decorators": {
|
||||
"version": "6.24.1",
|
||||
|
@ -544,9 +550,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"babel-plugin-transform-react-display-name": {
|
||||
"version": "6.23.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.23.0.tgz",
|
||||
"integrity": "sha1-Q5iRDDWEQdxM7xh4cmTQQS7Tazc="
|
||||
"version": "6.25.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz",
|
||||
"integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE="
|
||||
},
|
||||
"babel-plugin-transform-react-jsx": {
|
||||
"version": "6.24.1",
|
||||
|
@ -616,24 +622,24 @@
|
|||
"integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs="
|
||||
},
|
||||
"babel-template": {
|
||||
"version": "6.24.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.24.1.tgz",
|
||||
"integrity": "sha1-BK5RTx+Ts6JTfyoPYKWkX7gwgzM="
|
||||
"version": "6.25.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz",
|
||||
"integrity": "sha1-ZlJBFmt8KqTGGdceGSlpVSsQwHE="
|
||||
},
|
||||
"babel-traverse": {
|
||||
"version": "6.24.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.24.1.tgz",
|
||||
"integrity": "sha1-qzZnP9NW+aCUhlnnszjV/q2zFpU="
|
||||
"version": "6.25.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz",
|
||||
"integrity": "sha1-IldJfi/NGbie3BPEyROB+VEklvE="
|
||||
},
|
||||
"babel-types": {
|
||||
"version": "6.24.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.24.1.tgz",
|
||||
"integrity": "sha1-oTaHncFbNga9oNkMH8dDBML/CXU="
|
||||
"version": "6.25.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz",
|
||||
"integrity": "sha1-cK+ySNVmDl0Y+BHZHIMDtUE0oY4="
|
||||
},
|
||||
"babylon": {
|
||||
"version": "6.17.2",
|
||||
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.2.tgz",
|
||||
"integrity": "sha1-IB0l71+JLEG65JSIsI2w3Udun1w="
|
||||
"version": "6.17.3",
|
||||
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.3.tgz",
|
||||
"integrity": "sha512-mq0x3HCAGGmQyZXviOVe5TRsw37Ijy3D43jCqt/9WVf+onx2dUgW3PosnqCbScAFhRO9DGs8nxoMzU0iiosMqQ=="
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "0.4.2",
|
||||
|
@ -778,6 +784,12 @@
|
|||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
|
||||
"integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg="
|
||||
},
|
||||
"ci-info": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.0.0.tgz",
|
||||
"integrity": "sha1-3FKF8rTiUYIWg2gcOBwziPRuxTQ=",
|
||||
"dev": true
|
||||
},
|
||||
"circular-json": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz",
|
||||
|
@ -790,12 +802,24 @@
|
|||
"integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=",
|
||||
"dev": true
|
||||
},
|
||||
"cli-spinners": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz",
|
||||
"integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw=",
|
||||
"dev": true
|
||||
},
|
||||
"cli-table": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz",
|
||||
"integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=",
|
||||
"dev": true
|
||||
},
|
||||
"cli-truncate": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz",
|
||||
"integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=",
|
||||
"dev": true
|
||||
},
|
||||
"cli-usage": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/cli-usage/-/cli-usage-0.1.4.tgz",
|
||||
|
@ -956,6 +980,20 @@
|
|||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||
},
|
||||
"cosmiconfig": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-1.1.0.tgz",
|
||||
"integrity": "sha1-DeoPmATv37kp+7GxiOJVU+oFPTc=",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"create-react-class": {
|
||||
"version": "15.5.3",
|
||||
"resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.5.3.tgz",
|
||||
|
@ -1006,6 +1044,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"date-fns": {
|
||||
"version": "1.28.5",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.28.5.tgz",
|
||||
"integrity": "sha1-JXz8RdMi30XvVlhmWWfuhBzXP68=",
|
||||
"dev": true
|
||||
},
|
||||
"date-now": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
|
||||
|
@ -1102,6 +1146,12 @@
|
|||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
|
||||
"dev": true
|
||||
},
|
||||
"elegant-spinner": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz",
|
||||
"integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=",
|
||||
"dev": true
|
||||
},
|
||||
"element-class": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/element-class/-/element-class-0.2.2.tgz",
|
||||
|
@ -1434,6 +1484,20 @@
|
|||
"integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=",
|
||||
"dev": true
|
||||
},
|
||||
"execa": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-0.6.3.tgz",
|
||||
"integrity": "sha1-V7aaWU8IF1nGnlNw8NF7nLEWWP4=",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cross-spawn": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"exenv": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.0.tgz",
|
||||
|
@ -1550,6 +1614,12 @@
|
|||
"integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=",
|
||||
"dev": true
|
||||
},
|
||||
"find-parent-dir": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz",
|
||||
"integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=",
|
||||
"dev": true
|
||||
},
|
||||
"find-up": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
|
||||
|
@ -2232,6 +2302,12 @@
|
|||
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
|
||||
"integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4="
|
||||
},
|
||||
"get-stream": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
|
||||
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
|
||||
"dev": true
|
||||
},
|
||||
"getpass": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||
|
@ -2260,9 +2336,9 @@
|
|||
"integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg="
|
||||
},
|
||||
"globals": {
|
||||
"version": "9.17.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-9.17.0.tgz",
|
||||
"integrity": "sha1-DAymltm5u2lNLlRwvTd3fKrVAoY="
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
|
||||
"integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ=="
|
||||
},
|
||||
"globby": {
|
||||
"version": "5.0.0",
|
||||
|
@ -2422,6 +2498,20 @@
|
|||
"integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=",
|
||||
"dev": true
|
||||
},
|
||||
"husky": {
|
||||
"version": "0.13.4",
|
||||
"resolved": "https://registry.npmjs.org/husky/-/husky-0.13.4.tgz",
|
||||
"integrity": "sha1-SHhcUCjeNFKlHEjBLE+UshJKFAc=",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"normalize-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-1.0.0.tgz",
|
||||
"integrity": "sha1-MtDkcvkf80VwHBWoMRAY07CpA3k=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.17",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.17.tgz",
|
||||
|
@ -2530,6 +2620,12 @@
|
|||
"integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=",
|
||||
"dev": true
|
||||
},
|
||||
"is-ci": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.0.10.tgz",
|
||||
"integrity": "sha1-9zkzayYyNlBhqdSCcM1WrjNpMY4=",
|
||||
"dev": true
|
||||
},
|
||||
"is-date-object": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
|
||||
|
@ -2610,6 +2706,12 @@
|
|||
"resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz",
|
||||
"integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU="
|
||||
},
|
||||
"is-promise": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
|
||||
"integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
|
||||
"dev": true
|
||||
},
|
||||
"is-property": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
|
||||
|
@ -2674,12 +2776,6 @@
|
|||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
|
||||
},
|
||||
"jodid25519": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz",
|
||||
"integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=",
|
||||
"optional": true
|
||||
},
|
||||
"js-base64": {
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz",
|
||||
|
@ -2795,6 +2891,44 @@
|
|||
"integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
|
||||
"dev": true
|
||||
},
|
||||
"lint-staged": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-3.6.0.tgz",
|
||||
"integrity": "sha1-zajwvvFueSjMFLc1GGrhLNZiWZw=",
|
||||
"dev": true
|
||||
},
|
||||
"listr": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/listr/-/listr-0.12.0.tgz",
|
||||
"integrity": "sha1-a84sD1YD+klYDqF81qAMwOX6RRo=",
|
||||
"dev": true
|
||||
},
|
||||
"listr-silent-renderer": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz",
|
||||
"integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=",
|
||||
"dev": true
|
||||
},
|
||||
"listr-update-renderer": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz",
|
||||
"integrity": "sha1-yoDhd5tOcCZoB+ju0a1qvjmFUPk=",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"indent-string": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.1.0.tgz",
|
||||
"integrity": "sha1-CP9DNGAziDmbMp5rlTjcejz13n0=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"listr-verbose-renderer": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.4.0.tgz",
|
||||
"integrity": "sha1-RNwBuww0oDxXIVTU0Izemx3FYg8=",
|
||||
"dev": true
|
||||
},
|
||||
"load-json-file": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
|
||||
|
@ -2889,6 +3023,12 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
|
||||
"integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc="
|
||||
},
|
||||
"lodash.chunk": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz",
|
||||
"integrity": "sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.clonedeep": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||
|
@ -2918,6 +3058,18 @@
|
|||
"integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
|
||||
"dev": true
|
||||
},
|
||||
"log-symbols": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz",
|
||||
"integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=",
|
||||
"dev": true
|
||||
},
|
||||
"log-update": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz",
|
||||
"integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=",
|
||||
"dev": true
|
||||
},
|
||||
"longest": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
|
||||
|
@ -2935,9 +3087,9 @@
|
|||
"integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8="
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz",
|
||||
"integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4="
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.0.tgz",
|
||||
"integrity": "sha512-aHGs865JXz6bkB4AHL+3AhyvTFKL3iZamKVWjIUKnXOXyasJvqPK8WAjOnAQKQZVpeXDVz19u1DD0r/12bWAdQ=="
|
||||
},
|
||||
"map-obj": {
|
||||
"version": "1.0.1",
|
||||
|
@ -3157,6 +3309,24 @@
|
|||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
|
||||
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk="
|
||||
},
|
||||
"npm-path": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/npm-path/-/npm-path-2.0.3.tgz",
|
||||
"integrity": "sha1-Fc/04ciaONp39W9gVbJPl137K74=",
|
||||
"dev": true
|
||||
},
|
||||
"npm-run-path": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
|
||||
"integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
|
||||
"dev": true
|
||||
},
|
||||
"npm-which": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npm-which/-/npm-which-3.0.1.tgz",
|
||||
"integrity": "sha1-kiXybsOihcIJyuZ8OxGmtKtxQKo=",
|
||||
"dev": true
|
||||
},
|
||||
"npmlog": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.0.tgz",
|
||||
|
@ -3255,6 +3425,12 @@
|
|||
"integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
|
||||
"dev": true
|
||||
},
|
||||
"ora": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz",
|
||||
"integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=",
|
||||
"dev": true
|
||||
},
|
||||
"original": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/original/-/original-1.0.0.tgz",
|
||||
|
@ -3300,6 +3476,12 @@
|
|||
"resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.2.tgz",
|
||||
"integrity": "sha1-0KM+7+YaIF+suQCS6CZZjVJFznY="
|
||||
},
|
||||
"p-finally": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
|
||||
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
|
||||
"dev": true
|
||||
},
|
||||
"p-limit": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz",
|
||||
|
@ -3312,6 +3494,12 @@
|
|||
"integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
|
||||
"dev": true
|
||||
},
|
||||
"p-map": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-1.1.1.tgz",
|
||||
"integrity": "sha1-BfXkrpegaDcbwqXMhr+9vBnErno=",
|
||||
"dev": true
|
||||
},
|
||||
"pako": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
|
||||
|
@ -3356,6 +3544,12 @@
|
|||
"integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
|
||||
"dev": true
|
||||
},
|
||||
"path-key": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
|
||||
"integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
|
||||
"dev": true
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
|
||||
|
@ -3428,6 +3622,12 @@
|
|||
"resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz",
|
||||
"integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks="
|
||||
},
|
||||
"prettier": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.4.4.tgz",
|
||||
"integrity": "sha512-GuuPazIvjW1DG26yLQgO+nagmRF/h9M4RaCtZWqu/eFW7csdZkQEwPJUeXX10d+LzmCnR9DuIZndqIOn3p2YoA==",
|
||||
"dev": true
|
||||
},
|
||||
"private": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/private/-/private-0.1.7.tgz",
|
||||
|
@ -3542,9 +3742,9 @@
|
|||
"integrity": "sha1-ugwoeG/VLtfk8hNf4CiNRirvk9o="
|
||||
},
|
||||
"react-modal": {
|
||||
"version": "1.7.7",
|
||||
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-1.7.7.tgz",
|
||||
"integrity": "sha1-cCBfUcWHCMSHr/aBuj/teUbjkdk="
|
||||
"version": "1.7.11",
|
||||
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-1.7.11.tgz",
|
||||
"integrity": "sha512-mKKJlDp7mIWAuBpg0ZJLoGEzyVME63vs5jBy+D53R9V+hVJ775y8DXFYRGLQYSZW9IFE0qkmNSLm7HdgDGqzsg=="
|
||||
},
|
||||
"react-redux": {
|
||||
"version": "5.0.5",
|
||||
|
@ -3660,9 +3860,9 @@
|
|||
}
|
||||
},
|
||||
"remove-trailing-separator": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz",
|
||||
"integrity": "sha1-YV67lq9VlVLUv0BXyENtSGq2PMQ="
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz",
|
||||
"integrity": "sha1-abBi2XhyetFNxrVrpKt3L9jXBRE="
|
||||
},
|
||||
"render-media": {
|
||||
"version": "2.10.0",
|
||||
|
@ -3694,6 +3894,12 @@
|
|||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
|
||||
},
|
||||
"require-from-string": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz",
|
||||
"integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=",
|
||||
"dev": true
|
||||
},
|
||||
"require-main-filename": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
|
||||
|
@ -3763,6 +3969,12 @@
|
|||
"integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=",
|
||||
"dev": true
|
||||
},
|
||||
"rxjs": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.4.0.tgz",
|
||||
"integrity": "sha1-p9sUqxV/nXqsalbmVeejhg05vyY=",
|
||||
"dev": true
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
|
||||
|
@ -3849,10 +4061,22 @@
|
|||
"integrity": "sha1-F93t3F9yL7ZlAWWIlUYZd4ZzFbo=",
|
||||
"dev": true
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
|
||||
"integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
|
||||
"dev": true
|
||||
},
|
||||
"shebang-regex": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
|
||||
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
|
||||
"dev": true
|
||||
},
|
||||
"shelljs": {
|
||||
"version": "0.7.7",
|
||||
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.7.tgz",
|
||||
"integrity": "sha1-svXHfvlxSPS09uImguELuoZnz/E=",
|
||||
"version": "0.7.8",
|
||||
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz",
|
||||
"integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=",
|
||||
"dev": true
|
||||
},
|
||||
"shellwords": {
|
||||
|
@ -3960,9 +4184,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.0.tgz",
|
||||
"integrity": "sha1-/yo+T9BEl1Vf7Zezmg/YL6+zozw=",
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz",
|
||||
"integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=",
|
||||
"dependencies": {
|
||||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
|
@ -3971,6 +4195,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"staged-git-files": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/staged-git-files/-/staged-git-files-0.0.4.tgz",
|
||||
"integrity": "sha1-15fhtVHKemOd7AI33G60u5vhfTU=",
|
||||
"dev": true
|
||||
},
|
||||
"statuses": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
|
||||
|
@ -3999,6 +4229,12 @@
|
|||
"resolved": "https://registry.npmjs.org/stream-to-blob-url/-/stream-to-blob-url-2.1.0.tgz",
|
||||
"integrity": "sha1-w0HRBQLsUSUGBzJyWOwvWGsH1iY="
|
||||
},
|
||||
"stream-to-observable": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/stream-to-observable/-/stream-to-observable-0.1.0.tgz",
|
||||
"integrity": "sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4=",
|
||||
"dev": true
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.2.tgz",
|
||||
|
@ -4030,6 +4266,12 @@
|
|||
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
|
||||
"integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4="
|
||||
},
|
||||
"strip-eof": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
|
||||
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
|
||||
"dev": true
|
||||
},
|
||||
"strip-indent": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "lbry-web-ui",
|
||||
"version": "0.12.0rc6",
|
||||
"version": "0.12.0rc7",
|
||||
"description": "LBRY UI",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
|
|
|
@ -165,3 +165,8 @@ p
|
|||
section.section-spaced {
|
||||
margin-bottom: $spacing-vertical;
|
||||
}
|
||||
|
||||
.text-center
|
||||
{
|
||||
text-align: center;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ $padding-card-horizontal: $spacing-vertical * 2/3;
|
|||
}
|
||||
.card__title-primary {
|
||||
padding: 0 $padding-card-horizontal;
|
||||
margin-top: $spacing-vertical;
|
||||
margin-top: $spacing-vertical * 2/3;
|
||||
}
|
||||
.card__title-identity {
|
||||
padding: 0 $padding-card-horizontal;
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
$width-input-border: 2px;
|
||||
$width-input-text: 330px;
|
||||
|
||||
.form-input-width {
|
||||
width: $width-input-text
|
||||
}
|
||||
|
||||
.form-row-submit
|
||||
{
|
||||
margin-top: $spacing-vertical;
|
||||
|
|
Loading…
Reference in a new issue