use lbryio/lbryinc

This commit is contained in:
Sean Yesmunt 2018-09-23 23:44:42 -04:00
parent b972064f1c
commit 9b700e48bf
45 changed files with 200 additions and 1347 deletions

View file

@ -17,10 +17,10 @@ module.name_mapper='^types\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/types\1'
module.name_mapper='^component\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/component\1'
module.name_mapper='^page\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/page\1'
module.name_mapper='^lbry\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/lbry\1'
module.name_mapper='^rewards\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/rewards\1'
module.name_mapper='^modal\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/modal\1'
module.name_mapper='^app\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/app\1'
module.name_mapper='^native\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/native\1'
module.name_mapper='^analytics\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/analytics\1'
[strict]

View file

@ -51,6 +51,7 @@
"hast-util-sanitize": "^1.1.2",
"keytar": "^4.2.1",
"lbry-redux": "lbryio/lbry-redux#d1cee82af119c0c5f98ec27f94b2e7f61e34b54c",
"lbryinc": "lbryio/lbryinc#c09aa2645ecccb33c83e9a9545ff119232910f6f",
"localforage": "^1.7.1",
"mammoth": "^1.4.6",
"mime": "^2.3.1",

View file

@ -1,6 +1,6 @@
// @flow
import mixpanel from 'mixpanel-browser';
import Lbryio from 'lbryio';
import { Lbryio } from 'lbryinc';
import isDev from 'electron-is-dev';
if (isDev) {

View file

@ -38,4 +38,7 @@ global.__ = i18n.__;
global.__n = i18n.__n;
global.app = app;
// Lbryinc needs access to the redux store for dispatching auth-releated actions
global.store = app.store;
export default app;

View file

@ -1,7 +1,7 @@
import { connect } from 'react-redux';
import { selectPageTitle, selectHistoryIndex, selectActiveHistoryEntry } from 'lbry-redux';
import { doRecordScroll } from 'redux/actions/navigation';
import { selectUser } from 'redux/selectors/user';
import { selectUser } from 'lbryinc';
import { doAlertError } from 'redux/actions/app';
import App from './view';
@ -17,4 +17,7 @@ const perform = dispatch => ({
recordScroll: scrollPosition => dispatch(doRecordScroll(scrollPosition)),
});
export default connect(select, perform)(App);
export default connect(
select,
perform
)(App);

View file

@ -1,12 +1,14 @@
import React from 'react';
import { connect } from 'react-redux';
import { selectUserEmail } from 'redux/selectors/user';
import { selectUserEmail } from 'lbryinc';
import CardVerify from './view';
const select = state => ({
email: selectUserEmail(state),
});
const perform = dispatch => ({});
const perform = () => ({});
export default connect(select, perform)(CardVerify);
export default connect(
select,
perform
)(CardVerify);

View file

@ -3,6 +3,7 @@ import * as settings from 'constants/settings';
import { doChangeVolume } from 'redux/actions/app';
import { selectVolume } from 'redux/selectors/app';
import { doPlayUri, doSetPlayingUri, savePosition } from 'redux/actions/content';
import { doClaimEligiblePurchaseRewards } from 'lbryinc';
import {
makeSelectMetadataForUri,
makeSelectContentTypeForUri,
@ -13,7 +14,6 @@ import {
makeSelectDownloadingForUri,
selectSearchBarFocused,
} from 'lbry-redux';
import { doClaimEligiblePurchaseRewards } from 'redux/actions/rewards';
import { makeSelectClientSetting, selectShowNsfw } from 'redux/selectors/settings';
import { selectPlayingUri, makeSelectContentPositionForUri } from 'redux/selectors/content';
import { selectFileInfoErrors } from 'redux/selectors/file_info';

View file

@ -1,6 +1,5 @@
import React from 'react';
import { connect } from 'react-redux';
import { selectUserInvitees, selectUserInviteStatusIsPending } from 'redux/selectors/user';
import { selectUserInvitees, selectUserInviteStatusIsPending } from 'lbryinc';
import InviteList from './view';
const select = state => ({
@ -8,6 +7,9 @@ const select = state => ({
isPending: selectUserInviteStatusIsPending(state),
});
const perform = dispatch => ({});
const perform = () => ({});
export default connect(select, perform)(InviteList);
export default connect(
select,
perform
)(InviteList);

View file

@ -1,10 +1,20 @@
// @flow
import React from 'react';
import Icon from 'component/common/icon';
import RewardLink from 'component/rewardLink';
import rewards from 'rewards.js';
import { rewards } from 'lbryinc';
import * as icons from 'constants/icons';
class InviteList extends React.PureComponent {
type Props = {
invitees: ?Array<{
email: string,
invite_accepted: boolean,
invite_reward_claimed: boolean,
invite_reward_claimable: boolean,
}>,
};
class InviteList extends React.PureComponent<Props> {
render() {
const { invitees } = this.props;
@ -31,8 +41,8 @@ class InviteList extends React.PureComponent {
</tr>
</thead>
<tbody>
{invitees.map((invitee, index) => (
<tr key={index}>
{invitees.map(invitee => (
<tr key={invitee.email}>
<td>{invitee.email}</td>
<td className="text-center">
{invitee.invite_accepted ? (

View file

@ -3,8 +3,8 @@ import {
selectUserInvitesRemaining,
selectUserInviteNewIsPending,
selectUserInviteNewErrorMessage,
} from 'redux/selectors/user';
import { doUserInviteNew } from 'redux/actions/user';
doUserInviteNew,
} from 'lbryinc';
import InviteNew from './view';
const select = state => ({
@ -17,4 +17,7 @@ const perform = dispatch => ({
inviteNew: email => dispatch(doUserInviteNew(email)),
});
export default connect(select, perform)(InviteNew);
export default connect(
select,
perform
)(InviteNew);

View file

@ -3,9 +3,10 @@ import {
makeSelectClaimRewardError,
makeSelectRewardByType,
makeSelectIsRewardClaimPending,
} from 'redux/selectors/rewards';
doClaimRewardType,
doClaimRewardClearError,
} from 'lbryinc';
import { doNavigate } from 'redux/actions/navigation';
import { doClaimRewardType, doClaimRewardClearError } from 'redux/actions/rewards';
import RewardLink from './view';
const makeSelect = () => {
@ -28,4 +29,7 @@ const perform = dispatch => ({
navigate: path => dispatch(doNavigate(path)),
});
export default connect(makeSelect, perform)(RewardLink);
export default connect(
makeSelect,
perform
)(RewardLink);

View file

@ -1,10 +1,12 @@
import React from 'react';
import { connect } from 'react-redux';
import { selectClaimedRewards } from 'redux/selectors/rewards';
import { selectClaimedRewards } from 'lbryinc';
import RewardListClaimed from './view';
const select = state => ({
rewards: selectClaimedRewards(state),
});
export default connect(select, null)(RewardListClaimed);
export default connect(
select,
null
)(RewardListClaimed);

View file

@ -1,6 +1,5 @@
import { connect } from 'react-redux';
import { selectUnclaimedRewardValue, selectFetchingRewards } from 'redux/selectors/rewards';
import { doRewardList } from 'redux/actions/rewards';
import { selectUnclaimedRewardValue, selectFetchingRewards, doRewardList } from 'lbryinc';
import { doFetchRewardedContent } from 'redux/actions/content';
import RewardSummary from './view';

View file

@ -3,7 +3,7 @@ import React from 'react';
import Icon from 'component/common/icon';
import RewardLink from 'component/rewardLink';
import Button from 'component/button';
import rewards from 'rewards';
import { rewards } from 'lbryinc';
import * as icons from 'constants/icons';
type Props = {

View file

@ -1,8 +1,7 @@
import { connect } from 'react-redux';
import { selectClaimedRewardsByTransactionId } from 'redux/selectors/rewards';
import { selectClaimedRewardsByTransactionId } from 'lbryinc';
import { doNavigate } from 'redux/actions/navigation';
import { doNotify } from 'lbry-redux';
import { selectAllMyClaimsByOutpoint } from 'lbry-redux';
import { selectAllMyClaimsByOutpoint, doNotify } from 'lbry-redux';
import TransactionList from './view';
const select = state => ({
@ -15,4 +14,7 @@ const perform = dispatch => ({
openModal: (modal, props) => dispatch(doNotify(modal, props)),
});
export default connect(select, perform)(TransactionList);
export default connect(
select,
perform
)(TransactionList);

View file

@ -1,7 +1,5 @@
import React from 'react';
import { connect } from 'react-redux';
import { doUserEmailNew, doUserInviteNew } from 'redux/actions/user';
import { selectEmailNewIsPending, selectEmailNewErrorMessage } from 'redux/selectors/user';
import { selectEmailNewIsPending, selectEmailNewErrorMessage, doUserEmailNew } from 'lbryinc';
import UserEmailNew from './view';
const select = state => ({
@ -13,4 +11,7 @@ const perform = dispatch => ({
addUserEmail: email => dispatch(doUserEmailNew(email)),
});
export default connect(select, perform)(UserEmailNew);
export default connect(
select,
perform
)(UserEmailNew);

View file

@ -1,14 +1,12 @@
import { connect } from 'react-redux';
import {
doUserEmailVerify,
doUserEmailVerifyFailure,
doUserResendVerificationEmail,
} from 'redux/actions/user';
import {
selectEmailVerifyIsPending,
selectEmailToVerify,
selectEmailVerifyErrorMessage,
} from 'redux/selectors/user';
doUserEmailVerify,
doUserEmailVerifyFailure,
doUserResendVerificationEmail,
} from 'lbryinc';
import UserEmailVerify from './view';
const select = state => ({
@ -23,4 +21,7 @@ const perform = dispatch => ({
resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email)),
});
export default connect(select, perform)(UserEmailVerify);
export default connect(
select,
perform
)(UserEmailVerify);

View file

@ -1,7 +1,5 @@
import React from 'react';
import { connect } from 'react-redux';
import { doUserPhoneNew } from 'redux/actions/user';
import { selectPhoneNewErrorMessage } from 'redux/selectors/user';
import { selectPhoneNewErrorMessage, doUserPhoneNew } from 'lbryinc';
import UserPhoneNew from './view';
const select = state => ({
@ -9,7 +7,10 @@ const select = state => ({
});
const perform = dispatch => ({
addUserPhone: (phone, country_code) => dispatch(doUserPhoneNew(phone, country_code)),
addUserPhone: (phone, countryCode) => dispatch(doUserPhoneNew(phone, countryCode)),
});
export default connect(select, perform)(UserPhoneNew);
export default connect(
select,
perform
)(UserPhoneNew);

View file

@ -1,11 +1,11 @@
import React from 'react';
import { connect } from 'react-redux';
import { doUserPhoneVerify, doUserPhoneReset } from 'redux/actions/user';
import {
doUserPhoneVerify,
doUserPhoneReset,
selectPhoneToVerify,
selectPhoneVerifyErrorMessage,
selectUserCountryCode,
} from 'redux/selectors/user';
} from 'lbryinc';
import UserPhoneVerify from './view';
const select = state => ({
@ -19,4 +19,7 @@ const perform = dispatch => ({
verifyUserPhone: code => dispatch(doUserPhoneVerify(code)),
});
export default connect(select, perform)(UserPhoneVerify);
export default connect(
select,
perform
)(UserPhoneVerify);

View file

@ -1,13 +1,13 @@
import { connect } from 'react-redux';
import { doNotify, MODALS } from 'lbry-redux';
import { doNavigate } from 'redux/actions/navigation';
import { doUserIdentityVerify } from 'redux/actions/user';
import rewards from 'rewards';
import { makeSelectRewardByType } from 'redux/selectors/rewards';
import {
doUserIdentityVerify,
rewards,
makeSelectRewardByType,
selectIdentityVerifyIsPending,
selectIdentityVerifyErrorMessage,
} from 'redux/selectors/user';
} from 'lbryinc';
import UserVerify from './view';
const select = state => {
@ -26,4 +26,7 @@ const perform = dispatch => ({
verifyPhone: () => dispatch(doNotify({ id: MODALS.PHONE_COLLECTION })),
});
export default connect(select, perform)(UserVerify);
export default connect(
select,
perform
)(UserVerify);

View file

@ -2,7 +2,7 @@
import * as React from 'react';
import Button from 'component/button';
import CardVerify from 'component/cardVerify';
import lbryio from 'lbryio';
import Lbryio from 'lbryinc';
import * as icons from 'constants/icons';
type Props = {
@ -51,7 +51,7 @@ class UserVerify extends React.PureComponent<Props> {
label={__('Perform Card Verification')}
disabled={isPending}
token={this.onToken}
stripeKey={lbryio.getStripeToken()}
stripeKey={Lbryio.getStripeToken()}
/>
</div>
<div className="card__content">

View file

@ -11,9 +11,10 @@ import { doConditionalAuthNavigate, doDaemonReady, doAutoUpdate } from 'redux/ac
import { doNotify, doBlackListedOutpointsSubscribe, isURIValid } from 'lbry-redux';
import { doNavigate } from 'redux/actions/navigation';
import { doDownloadLanguages, doUpdateIsNightAsync } from 'redux/actions/settings';
import { doUserEmailVerify, doAuthenticate } from 'redux/actions/user';
import { doUserEmailVerify, doAuthenticate, Lbryio } from 'lbryinc';
import 'scss/all.scss';
import store from 'store';
import pjson from 'package.json';
import app from './app';
import analytics from './analytics';
import doLogWarningConsoleMessage from './logWarningConsoleMessage';
@ -23,6 +24,40 @@ const APPPAGEURL = 'lbry://?';
autoUpdater.logger = remote.require('electron-log');
// We need to override Lbryio for getting/setting the authToken
// We interect with ipcRenderer to get the auth key from a users keyring
Lbryio.setOverride('setAuthToken', status => {
Lbryio.call(
'user',
'new',
{
auth_token: '',
language: 'en',
app_id: status.installation_id,
},
'post'
).then(response => {
if (!response.auth_token) {
throw new Error(__('auth_token is missing from response'));
}
ipcRenderer.send('set-auth-token', response.auth_token);
});
});
Lbryio.setOverride(
'getAuthToken',
() =>
new Promise(resolve => {
ipcRenderer.once('auth-token-response', (event, token) => {
Lbryio.authToken = token;
resolve(token);
});
ipcRenderer.send('get-auth-token');
})
);
ipcRenderer.on('open-uri-requested', (event, uri, newSession) => {
if (uri && uri.startsWith('lbry://')) {
if (uri.startsWith('lbry://?verify=')) {
@ -156,7 +191,7 @@ const init = () => {
ReactDOM.render(
<Provider store={store}>
<SplashScreen
authenticate={() => app.store.dispatch(doAuthenticate())}
authenticate={() => app.store.dispatch(doAuthenticate(pjson.version))}
onReadyToLaunch={onDaemonReady}
/>
</Provider>,

View file

@ -1,155 +0,0 @@
import { ipcRenderer } from 'electron';
import { Lbry } from 'lbry-redux';
import querystring from 'querystring';
const Lbryio = {
enabled: true,
authenticationPromise: null,
};
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/';
Lbryio.call = (resource, action, params = {}, method = 'get') => {
if (!Lbryio.enabled) {
console.log(__('Internal API disabled'));
return Promise.reject(new Error(__('LBRY internal API is disabled')));
}
if (!(method === 'get' || method === 'post')) {
return Promise.reject(new Error(__('Invalid method')));
}
function checkAndParse(response) {
if (response.status >= 200 && response.status < 300) {
return response.json();
}
return response.json().then(json => {
let error;
if (json.error) {
error = new Error(json.error);
} else {
error = new Error('Unknown API error signature');
}
error.response = response; // This is primarily a hack used in actions/user.js
return Promise.reject(error);
});
}
function makeRequest(url, options) {
return fetch(url, options).then(checkAndParse);
}
return Lbryio.getAuthToken().then(token => {
const fullParams = { auth_token: token, ...params };
const qs = querystring.stringify(fullParams);
let url = `${CONNECTION_STRING}${resource}/${action}?${qs}`;
let options = {
method: 'GET',
};
if (method === 'post') {
options = {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: qs,
};
url = `${CONNECTION_STRING}${resource}/${action}`;
}
return makeRequest(url, options).then(response => response.data);
});
};
Lbryio.authToken = null;
Lbryio.getAuthToken = () =>
new Promise(resolve => {
if (Lbryio.authToken) {
resolve(Lbryio.authToken);
} else {
ipcRenderer.once('auth-token-response', (event, token) => {
Lbryio.authToken = token;
return resolve(token);
});
ipcRenderer.send('get-auth-token');
}
});
Lbryio.setAuthToken = token => {
Lbryio.authToken = token ? token.toString().trim() : null;
ipcRenderer.send('set-auth-token', token);
};
Lbryio.getCurrentUser = () => Lbryio.call('user', 'me');
Lbryio.authenticate = () => {
if (!Lbryio.enabled) {
return new Promise(resolve => {
resolve({
id: 1,
language: 'en',
primary_email: 'disabled@lbry.io',
has_verified_email: true,
is_identity_verified: true,
is_reward_approved: false,
});
});
}
if (Lbryio.authenticationPromise === null) {
Lbryio.authenticationPromise = new Promise((resolve, reject) => {
Lbryio.getAuthToken()
.then(token => {
if (!token || token.length > 60) {
return false;
}
// check that token works
return Lbryio.getCurrentUser()
.then(() => true)
.catch(() => false);
})
.then(isTokenValid => {
if (isTokenValid) {
return reject;
}
return Lbry.status()
.then(status =>
Lbryio.call(
'user',
'new',
{
auth_token: '',
language: 'en',
app_id: status.installation_id,
},
'post'
)
)
.then(response => {
if (!response.auth_token) {
throw new Error(__('auth_token is missing from response'));
}
return Lbryio.setAuthToken(response.auth_token);
});
})
.then(Lbryio.getCurrentUser)
.then(resolve, reject);
});
}
return Lbryio.authenticationPromise;
};
Lbryio.getStripeToken = () =>
CONNECTION_STRING.startsWith('http://localhost:')
? 'pk_test_NoL1JWL7i1ipfhVId5KfDZgo'
: 'pk_live_e8M4dRNnCCbmpZzduEUZBgJO';
export default Lbryio;

View file

@ -1,9 +1,8 @@
import { connect } from 'react-redux';
import { doNavigate } from 'redux/actions/navigation';
import { doSetClientSetting } from 'redux/actions/settings';
import { selectUserIsRewardApproved } from 'redux/selectors/user';
import { selectUserIsRewardApproved, selectUnclaimedRewardValue } from 'lbryinc';
import { selectBalance, doHideNotification } from 'lbry-redux';
import { selectUnclaimedRewardValue } from 'redux/selectors/rewards';
import * as settings from 'constants/settings';
import ModalCreditIntro from './view';
@ -25,4 +24,7 @@ const perform = dispatch => () => ({
},
});
export default connect(select, perform)(ModalCreditIntro);
export default connect(
select,
perform
)(ModalCreditIntro);

View file

@ -2,7 +2,7 @@ import * as settings from 'constants/settings';
import { connect } from 'react-redux';
import { doHideNotification } from 'lbry-redux';
import { doSetClientSetting } from 'redux/actions/settings';
import { selectEmailToVerify, selectUser } from 'redux/selectors/user';
import { selectEmailToVerify, selectUser } from 'lbryinc';
import ModalEmailCollection from './view';
const select = state => ({
@ -17,4 +17,7 @@ const perform = dispatch => () => ({
},
});
export default connect(select, perform)(ModalEmailCollection);
export default connect(
select,
perform
)(ModalEmailCollection);

View file

@ -1,7 +1,6 @@
import rewards from 'rewards';
import { rewards, makeSelectRewardByType } from 'lbryinc';
import { connect } from 'react-redux';
import { doHideNotification } from 'lbry-redux';
import { makeSelectRewardByType } from 'redux/selectors/rewards';
import ModalFirstReward from './view';
const select = state => {
@ -16,4 +15,7 @@ const perform = dispatch => ({
closeModal: () => dispatch(doHideNotification()),
});
export default connect(select, perform)(ModalFirstReward);
export default connect(
select,
perform
)(ModalFirstReward);

View file

@ -1,9 +1,6 @@
import React from 'react';
import * as settings from 'constants/settings';
import { connect } from 'react-redux';
import { doHideNotification } from 'lbry-redux';
import { doSetClientSetting } from 'redux/actions/settings';
import { selectPhoneToVerify, selectUser } from 'redux/selectors/user';
import { selectPhoneToVerify, selectUser } from 'lbryinc';
import { doNavigate } from 'redux/actions/navigation';
import ModalPhoneCollection from './view';
@ -19,4 +16,7 @@ const perform = dispatch => () => ({
},
});
export default connect(select, perform)(ModalPhoneCollection);
export default connect(
select,
perform
)(ModalPhoneCollection);

View file

@ -9,7 +9,7 @@ import {
selectNotificationProps,
} from 'lbry-redux';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import { selectUser, selectUserIsVerificationCandidate } from 'redux/selectors/user';
import { selectUser, selectUserIsVerificationCandidate } from 'lbryinc';
import ModalRouter from './view';
@ -32,4 +32,7 @@ const perform = dispatch => ({
openModal: notification => dispatch(doNotify(notification)),
});
export default connect(select, perform)(ModalRouter);
export default connect(
select,
perform
)(ModalRouter);

View file

@ -8,7 +8,7 @@ import {
selectUser,
selectUserIsPending,
selectIdentityVerifyIsPending,
} from 'redux/selectors/user';
} from 'lbryinc';
import AuthPage from './view';
const select = state => ({
@ -26,4 +26,7 @@ const perform = dispatch => ({
navigate: path => dispatch(doNavigate(path)),
});
export default connect(select, perform)(AuthPage);
export default connect(
select,
perform
)(AuthPage);

View file

@ -21,7 +21,7 @@ type Props = {
navigate: (string, ?{}) => void,
};
export class AuthPage extends React.PureComponent<Props> {
class AuthPage extends React.PureComponent<Props> {
componentWillMount() {
this.navigateIfAuthenticated(this.props);
}

View file

@ -1,8 +1,7 @@
import { connect } from 'react-redux';
import { doAuthNavigate } from 'redux/actions/navigation';
import { doFetchAccessToken } from 'redux/actions/user';
import { doFetchAccessToken, selectAccessToken, selectUser } from 'lbryinc';
import { selectDaemonSettings } from 'redux/selectors/settings';
import { selectAccessToken, selectUser } from 'redux/selectors/user';
import HelpPage from './view';
const select = state => ({

View file

@ -1,11 +1,10 @@
import React from 'react';
import { connect } from 'react-redux';
import InvitePage from './view';
import { doFetchInviteStatus } from 'redux/actions/user';
import {
doFetchInviteStatus,
selectUserInviteStatusFailed,
selectUserInviteStatusIsPending,
} from 'redux/selectors/user';
} from 'lbryinc';
import InvitePage from './view';
const select = state => ({
isFailed: selectUserInviteStatusFailed(state),
@ -16,4 +15,7 @@ const perform = dispatch => ({
fetchInviteStatus: () => dispatch(doFetchInviteStatus()),
});
export default connect(select, perform)(InvitePage);
export default connect(
select,
perform
)(InvitePage);

View file

@ -3,10 +3,10 @@ import {
selectFetchingRewards,
selectUnclaimedRewards,
selectClaimedRewards,
} from 'redux/selectors/rewards';
import { selectUser } from 'redux/selectors/user';
selectUser,
doRewardList,
} from 'lbryinc';
import { doAuthNavigate, doNavigate } from 'redux/actions/navigation';
import { doRewardList } from 'redux/actions/rewards';
import { selectDaemonSettings } from 'redux/selectors/settings';
import RewardsPage from './view';

View file

@ -16,7 +16,6 @@ import Native from 'native';
import { doFetchRewardedContent } from 'redux/actions/content';
import { doFetchDaemonSettings } from 'redux/actions/settings';
import { doAuthNavigate } from 'redux/actions/navigation';
import { doAuthenticate } from 'redux/actions/user';
import { doCheckSubscriptionsInit } from 'redux/actions/subscriptions';
import {
selectIsUpgradeSkipped,
@ -28,7 +27,8 @@ import {
selectRemoteVersion,
selectUpgradeTimer,
} from 'redux/selectors/app';
import { lbrySettings as config } from 'package.json';
import { doAuthenticate } from 'lbryinc';
import { lbrySettings as config, version as appVersion } from 'package.json';
const { autoUpdater } = remote.require('electron-updater');
const { download } = remote.require('electron-dl');
@ -333,7 +333,7 @@ export function doDaemonReady() {
return (dispatch, getState) => {
const state = getState();
dispatch(doAuthenticate());
dispatch(doAuthenticate(appVersion));
dispatch({ type: ACTIONS.DAEMON_READY });
dispatch(doFetchDaemonSettings());
dispatch(doBalanceSubscribe());

View file

@ -1,7 +1,6 @@
// @flow
import * as NOTIFICATION_TYPES from 'constants/notification_types';
import { ipcRenderer } from 'electron';
import Lbryio from 'lbryio';
import { doAlertError } from 'redux/actions/app';
import { doNavigate } from 'redux/actions/navigation';
import {
@ -32,6 +31,7 @@ import { makeSelectClientSetting, selectosNotificationsEnabled } from 'redux/sel
import setBadge from 'util/setBadge';
import setProgressBar from 'util/setProgressBar';
import analytics from 'analytics';
import { Lbryio } from 'lbryinc';
const DOWNLOAD_POLL_INTERVAL = 250;

View file

@ -1,117 +0,0 @@
import * as ACTIONS from 'constants/action_types';
import Lbryio from 'lbryio';
import { doNotify, MODALS } from 'lbry-redux';
import { selectUnclaimedRewards } from 'redux/selectors/rewards';
import { selectUserIsRewardApproved } from 'redux/selectors/user';
import rewards from 'rewards';
export function doRewardList() {
return dispatch => {
dispatch({
type: ACTIONS.FETCH_REWARDS_STARTED,
});
Lbryio.call('reward', 'list', { multiple_rewards_per_type: true })
.then(userRewards => {
dispatch({
type: ACTIONS.FETCH_REWARDS_COMPLETED,
data: { userRewards },
});
})
.catch(() => {
dispatch({
type: ACTIONS.FETCH_REWARDS_COMPLETED,
data: { userRewards: [] },
});
});
};
}
export function doClaimRewardType(rewardType, options) {
return (dispatch, getState) => {
const state = getState();
const unclaimedRewards = selectUnclaimedRewards(state);
const reward = unclaimedRewards.find(ur => ur.reward_type === rewardType);
const userIsRewardApproved = selectUserIsRewardApproved(state);
if (!reward || reward.transaction_id) {
// already claimed or doesn't exist, do nothing
return;
}
if (!userIsRewardApproved && rewardType !== rewards.TYPE_CONFIRM_EMAIL) {
if (!options || !options.failSilently) {
const action = doNotify({
id: MODALS.REWARD_APPROVAL_REQUIRED,
isError: false,
});
dispatch(action);
}
return;
}
dispatch({
type: ACTIONS.CLAIM_REWARD_STARTED,
data: { reward },
});
const success = successReward => {
dispatch(doRewardList());
dispatch({
type: ACTIONS.CLAIM_REWARD_SUCCESS,
data: {
reward: successReward,
},
});
if (successReward.reward_type === rewards.TYPE_NEW_USER) {
const action = doNotify({
id: MODALS.FIRST_REWARD,
isError: false,
});
dispatch(action);
}
};
const failure = error => {
dispatch({
type: ACTIONS.CLAIM_REWARD_FAILURE,
data: {
reward,
error: !options || !options.failSilently ? error : undefined,
},
});
};
rewards.claimReward(rewardType).then(success, failure);
};
}
export function doClaimEligiblePurchaseRewards() {
return (dispatch, getState) => {
const state = getState();
const unclaimedRewards = selectUnclaimedRewards(state);
const userIsRewardApproved = selectUserIsRewardApproved(state);
if (!userIsRewardApproved || !Lbryio.enabled) {
return;
}
if (unclaimedRewards.find(ur => ur.reward_type === rewards.TYPE_FIRST_STREAM)) {
dispatch(doClaimRewardType(rewards.TYPE_FIRST_STREAM));
} else {
[rewards.TYPE_MANY_DOWNLOADS, rewards.TYPE_FEATURED_DOWNLOAD].forEach(type => {
dispatch(doClaimRewardType(type, { failSilently: true }));
});
}
};
}
export function doClaimRewardClearError(reward) {
return dispatch => {
dispatch({
type: ACTIONS.CLAIM_REWARD_CLEAR_ERROR,
data: { reward },
});
};
}

View file

@ -2,16 +2,14 @@
import * as ACTIONS from 'constants/action_types';
import * as NOTIFICATION_TYPES from 'constants/notification_types';
import * as SETTINGS from 'constants/settings';
import rewards from 'rewards';
import { Lbryio, rewards, doClaimRewardType } from 'lbryinc';
import type { Dispatch, SubscriptionNotifications } from 'redux/reducers/subscriptions';
import type { Subscription } from 'types/subscription';
import { selectSubscriptions } from 'redux/selectors/subscriptions';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import { Lbry, buildURI, parseURI, selectCurrentPage } from 'lbry-redux';
import { doPurchaseUri, doFetchClaimsByChannel } from 'redux/actions/content';
import { doClaimRewardType } from 'redux/actions/rewards';
import Promise from 'bluebird';
import Lbryio from 'lbryio';
const CHECK_SUBSCRIPTIONS_INTERVAL = 15 * 60 * 1000;
const SUBSCRIPTION_DOWNLOAD_LIMIT = 1;
@ -246,7 +244,7 @@ export const doChannelSubscribe = (subscription: Subscription) => (
claim_id: claimId,
});
dispatch(doClaimRewardType(rewards.SUBSCRIPTION, { failSilently: true }));
dispatch(doClaimRewardType(rewards.TYPE_SUBSCRIPTION, { failSilently: true }));
}
dispatch(doCheckSubscription(subscription.uri, true));

View file

@ -1,360 +0,0 @@
import * as ACTIONS from 'constants/action_types';
import Lbryio from 'lbryio';
import { Lbry, doNotify, MODALS, doHideNotification } from 'lbry-redux';
import { doClaimRewardType, doRewardList } from 'redux/actions/rewards';
import {
selectEmailToVerify,
selectPhoneToVerify,
selectUserCountryCode,
} from 'redux/selectors/user';
import rewards from 'rewards';
import analytics from 'analytics';
import pjson from 'package.json';
export function doFetchInviteStatus() {
return dispatch => {
dispatch({
type: ACTIONS.USER_INVITE_STATUS_FETCH_STARTED,
});
Lbryio.call('user', 'invite_status')
.then(status => {
dispatch({
type: ACTIONS.USER_INVITE_STATUS_FETCH_SUCCESS,
data: {
invitesRemaining: status.invites_remaining ? status.invites_remaining : 0,
invitees: status.invitees,
},
});
})
.catch(error => {
dispatch({
type: ACTIONS.USER_INVITE_STATUS_FETCH_FAILURE,
data: { error },
});
});
};
}
export function doInstallNew() {
const payload = { app_version: pjson.version };
Lbry.status().then(status => {
payload.app_id = status.installation_id;
if (status.dht) payload.node_id = status.dht.node_id;
Lbry.version().then(version => {
payload.daemon_version = version.lbrynet_version;
payload.operating_system = version.os_system;
payload.platform = version.platform;
Lbryio.call('install', 'new', payload);
});
});
}
export function doAuthenticate() {
return dispatch => {
dispatch({
type: ACTIONS.AUTHENTICATION_STARTED,
});
Lbryio.authenticate()
.then(user => {
analytics.setUser(user);
dispatch({
type: ACTIONS.AUTHENTICATION_SUCCESS,
data: { user },
});
dispatch(doRewardList());
dispatch(doFetchInviteStatus());
doInstallNew();
})
.catch(error => {
dispatch(doNotify({ id: MODALS.AUTHENTICATION_FAILURE }));
dispatch({
type: ACTIONS.AUTHENTICATION_FAILURE,
data: { error },
});
});
};
}
export function doUserFetch() {
return dispatch => {
dispatch({
type: ACTIONS.USER_FETCH_STARTED,
});
Lbryio.getCurrentUser()
.then(user => {
analytics.setUser(user);
dispatch(doRewardList());
dispatch({
type: ACTIONS.USER_FETCH_SUCCESS,
data: { user },
});
})
.catch(error => {
dispatch({
type: ACTIONS.USER_FETCH_FAILURE,
data: { error },
});
});
};
}
export function doUserPhoneReset() {
return {
type: ACTIONS.USER_PHONE_RESET,
};
}
export function doUserPhoneNew(phone, countryCode) {
return dispatch => {
dispatch({
type: ACTIONS.USER_PHONE_NEW_STARTED,
data: { phone, country_code: countryCode },
});
const success = () => {
dispatch({
type: ACTIONS.USER_PHONE_NEW_SUCCESS,
data: { phone },
});
};
const failure = error => {
dispatch({
type: ACTIONS.USER_PHONE_NEW_FAILURE,
data: { error },
});
};
Lbryio.call(
'user',
'phone_number_new',
{ phone_number: phone, country_code: countryCode },
'post'
).then(success, failure);
};
}
export function doUserPhoneVerifyFailure(error) {
return {
type: ACTIONS.USER_PHONE_VERIFY_FAILURE,
data: { error },
};
}
export function doUserPhoneVerify(verificationCode) {
return (dispatch, getState) => {
const phoneNumber = selectPhoneToVerify(getState());
const countryCode = selectUserCountryCode(getState());
dispatch({
type: ACTIONS.USER_PHONE_VERIFY_STARTED,
code: verificationCode,
});
Lbryio.call(
'user',
'phone_number_confirm',
{
verification_code: verificationCode,
phone_number: phoneNumber,
country_code: countryCode,
},
'post'
)
.then(user => {
if (user.is_identity_verified) {
dispatch({
type: ACTIONS.USER_PHONE_VERIFY_SUCCESS,
data: { user },
});
dispatch(doHideNotification());
dispatch(doClaimRewardType(rewards.TYPE_NEW_USER));
}
})
.catch(error => dispatch(doUserPhoneVerifyFailure(error)));
};
}
export function doUserEmailNew(email) {
return dispatch => {
dispatch({
type: ACTIONS.USER_EMAIL_NEW_STARTED,
email,
});
const success = () => {
dispatch({
type: ACTIONS.USER_EMAIL_NEW_SUCCESS,
data: { email },
});
dispatch(doUserFetch());
};
const failure = error => {
dispatch({
type: ACTIONS.USER_EMAIL_NEW_FAILURE,
data: { error },
});
};
Lbryio.call('user_email', 'new', { email, send_verification_email: true }, 'post')
.catch(error => {
if (error.response && error.response.status === 409) {
return Lbryio.call(
'user_email',
'resend_token',
{ email, only_if_expired: true },
'post'
).then(success, failure);
}
throw error;
})
.then(success, failure);
};
}
export function doUserResendVerificationEmail(email) {
return dispatch => {
dispatch({
type: ACTIONS.USER_EMAIL_VERIFY_RETRY,
email,
});
const success = () => {
dispatch({
type: ACTIONS.USER_EMAIL_NEW_SUCCESS,
data: { email },
});
dispatch(doUserFetch());
};
const failure = error => {
dispatch({
type: ACTIONS.USER_EMAIL_NEW_FAILURE,
data: { error },
});
};
Lbryio.call('user_email', 'resend_token', { email }, 'post')
.catch(error => {
if (error.response && error.response.status === 409) {
throw error;
}
})
.then(success, failure);
};
}
export function doUserEmailVerifyFailure(error) {
return {
type: ACTIONS.USER_EMAIL_VERIFY_FAILURE,
data: { error },
};
}
export function doUserEmailVerify(verificationToken, recaptcha) {
return (dispatch, getState) => {
const email = selectEmailToVerify(getState());
dispatch({
type: ACTIONS.USER_EMAIL_VERIFY_STARTED,
code: verificationToken,
recaptcha,
});
Lbryio.call(
'user_email',
'confirm',
{
verification_token: verificationToken,
email,
recaptcha,
},
'post'
)
.then(userEmail => {
if (userEmail.is_verified) {
dispatch({
type: ACTIONS.USER_EMAIL_VERIFY_SUCCESS,
data: { email },
});
dispatch(doUserFetch());
} else {
throw new Error('Your email is still not verified.'); // shouldn't happen
}
})
.catch(error => dispatch(doUserEmailVerifyFailure(error)));
};
}
export function doUserIdentityVerify(stripeToken) {
return dispatch => {
dispatch({
type: ACTIONS.USER_IDENTITY_VERIFY_STARTED,
token: stripeToken,
});
Lbryio.call('user', 'verify_identity', { stripe_token: stripeToken }, 'post')
.then(user => {
if (user.is_identity_verified) {
dispatch({
type: ACTIONS.USER_IDENTITY_VERIFY_SUCCESS,
data: { user },
});
dispatch(doClaimRewardType(rewards.TYPE_NEW_USER));
} else {
throw new Error('Your identity is still not verified. This should not happen.'); // shouldn't happen
}
})
.catch(error => {
dispatch({
type: ACTIONS.USER_IDENTITY_VERIFY_FAILURE,
data: { error: error.toString() },
});
});
};
}
export function doFetchAccessToken() {
return dispatch => {
const success = token =>
dispatch({
type: ACTIONS.FETCH_ACCESS_TOKEN_SUCCESS,
data: { token },
});
Lbryio.getAuthToken().then(success);
};
}
export function doUserInviteNew(email) {
return dispatch => {
dispatch({
type: ACTIONS.USER_INVITE_NEW_STARTED,
});
Lbryio.call('user', 'invite', { email }, 'post')
.then(() => {
dispatch({
type: ACTIONS.USER_INVITE_NEW_SUCCESS,
data: { email },
});
dispatch(
doNotify({
displayType: ['snackbar'],
message: __('Invite sent to %s', email),
})
);
dispatch(doFetchInviteStatus());
})
.catch(error => {
dispatch({
type: ACTIONS.USER_INVITE_NEW_FAILURE,
data: { error },
});
});
};
}

View file

@ -1,98 +0,0 @@
import * as ACTIONS from 'constants/action_types';
const reducers = {};
const defaultState = {
fetching: false,
claimedRewardsById: {}, // id => reward
unclaimedRewards: [],
claimPendingByType: {},
claimErrorsByType: {},
};
reducers[ACTIONS.FETCH_REWARDS_STARTED] = state =>
Object.assign({}, state, {
fetching: true,
});
reducers[ACTIONS.FETCH_REWARDS_COMPLETED] = (state, action) => {
const { userRewards } = action.data;
const unclaimedRewards = [];
const claimedRewards = {};
userRewards.forEach(reward => {
if (reward.transaction_id) {
claimedRewards[reward.id] = reward;
} else {
unclaimedRewards.push(reward);
}
});
return Object.assign({}, state, {
claimedRewardsById: claimedRewards,
unclaimedRewards,
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[ACTIONS.CLAIM_REWARD_STARTED] = (state, action) => {
const { reward } = action.data;
return setClaimRewardState(state, reward, true, '');
};
reducers[ACTIONS.CLAIM_REWARD_SUCCESS] = (state, action) => {
const { reward } = action.data;
const { unclaimedRewards } = state;
const index = unclaimedRewards.findIndex(ur => ur.reward_type === reward.reward_type);
unclaimedRewards.splice(index, 1);
const { claimedRewardsById } = state;
claimedRewardsById[reward.id] = reward;
const newState = {
...state,
unclaimedRewards: [...unclaimedRewards],
claimedRewardsById: { ...claimedRewardsById },
};
return setClaimRewardState(newState, reward, false, '');
};
reducers[ACTIONS.CLAIM_REWARD_FAILURE] = (state, action) => {
const { reward, error } = action.data;
return setClaimRewardState(state, reward, false, error ? error.message : '');
};
reducers[ACTIONS.CLAIM_REWARD_CLEAR_ERROR] = (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];
if (handler) return handler(state, action);
return state;
}

View file

@ -1,222 +0,0 @@
import * as ACTIONS from 'constants/action_types';
const reducers = {};
const defaultState = {
authenticationIsPending: false,
userIsPending: false,
emailNewIsPending: false,
emailNewErrorMessage: '',
emailToVerify: '',
inviteNewErrorMessage: '',
inviteNewIsPending: false,
inviteStatusIsPending: false,
invitesRemaining: undefined,
invitees: undefined,
user: undefined,
};
reducers[ACTIONS.AUTHENTICATION_STARTED] = state =>
Object.assign({}, state, {
authenticationIsPending: true,
userIsPending: true,
user: defaultState.user,
});
reducers[ACTIONS.AUTHENTICATION_SUCCESS] = (state, action) =>
Object.assign({}, state, {
authenticationIsPending: false,
userIsPending: false,
user: action.data.user,
});
reducers[ACTIONS.AUTHENTICATION_FAILURE] = state =>
Object.assign({}, state, {
authenticationIsPending: false,
userIsPending: false,
user: null,
});
reducers[ACTIONS.USER_FETCH_STARTED] = state =>
Object.assign({}, state, {
userIsPending: true,
user: defaultState.user,
});
reducers[ACTIONS.USER_FETCH_SUCCESS] = (state, action) =>
Object.assign({}, state, {
userIsPending: false,
user: action.data.user,
});
reducers[ACTIONS.USER_FETCH_FAILURE] = state =>
Object.assign({}, state, {
userIsPending: true,
user: null,
});
reducers[ACTIONS.USER_PHONE_NEW_STARTED] = (state, action) => {
const user = Object.assign({}, state.user);
user.country_code = action.data.country_code;
return Object.assign({}, state, {
phoneNewIsPending: true,
phoneNewErrorMessage: '',
user,
});
};
reducers[ACTIONS.USER_PHONE_NEW_SUCCESS] = (state, action) =>
Object.assign({}, state, {
phoneToVerify: action.data.phone,
phoneNewIsPending: false,
});
reducers[ACTIONS.USER_PHONE_RESET] = state =>
Object.assign({}, state, {
phoneToVerify: null,
});
reducers[ACTIONS.USER_PHONE_NEW_FAILURE] = (state, action) =>
Object.assign({}, state, {
phoneNewIsPending: false,
phoneNewErrorMessage: action.data.error,
});
reducers[ACTIONS.USER_PHONE_VERIFY_STARTED] = state =>
Object.assign({}, state, {
phoneVerifyIsPending: true,
phoneVerifyErrorMessage: '',
});
reducers[ACTIONS.USER_PHONE_VERIFY_SUCCESS] = (state, action) =>
Object.assign({}, state, {
phoneToVerify: '',
phoneVerifyIsPending: false,
user: action.data.user,
});
reducers[ACTIONS.USER_PHONE_VERIFY_FAILURE] = (state, action) =>
Object.assign({}, state, {
phoneVerifyIsPending: false,
phoneVerifyErrorMessage: action.data.error,
});
reducers[ACTIONS.USER_EMAIL_NEW_STARTED] = state =>
Object.assign({}, state, {
emailNewIsPending: true,
emailNewErrorMessage: '',
});
reducers[ACTIONS.USER_EMAIL_NEW_SUCCESS] = (state, action) => {
const user = Object.assign({}, state.user);
user.primary_email = action.data.email;
return Object.assign({}, state, {
emailToVerify: action.data.email,
emailNewIsPending: false,
user,
});
};
reducers[ACTIONS.USER_EMAIL_NEW_EXISTS] = (state, action) =>
Object.assign({}, state, {
emailToVerify: action.data.email,
emailNewIsPending: false,
});
reducers[ACTIONS.USER_EMAIL_NEW_FAILURE] = (state, action) =>
Object.assign({}, state, {
emailNewIsPending: false,
emailNewErrorMessage: action.data.error,
});
reducers[ACTIONS.USER_EMAIL_VERIFY_STARTED] = state =>
Object.assign({}, state, {
emailVerifyIsPending: true,
emailVerifyErrorMessage: '',
});
reducers[ACTIONS.USER_EMAIL_VERIFY_SUCCESS] = (state, action) => {
const user = Object.assign({}, state.user);
user.primary_email = action.data.email;
return Object.assign({}, state, {
emailToVerify: '',
emailVerifyIsPending: false,
user,
});
};
reducers[ACTIONS.USER_EMAIL_VERIFY_FAILURE] = (state, action) =>
Object.assign({}, state, {
emailVerifyIsPending: false,
emailVerifyErrorMessage: action.data.error,
});
reducers[ACTIONS.USER_IDENTITY_VERIFY_STARTED] = state =>
Object.assign({}, state, {
identityVerifyIsPending: true,
identityVerifyErrorMessage: '',
});
reducers[ACTIONS.USER_IDENTITY_VERIFY_SUCCESS] = (state, action) =>
Object.assign({}, state, {
identityVerifyIsPending: false,
identityVerifyErrorMessage: '',
user: action.data.user,
});
reducers[ACTIONS.USER_IDENTITY_VERIFY_FAILURE] = (state, action) =>
Object.assign({}, state, {
identityVerifyIsPending: false,
identityVerifyErrorMessage: action.data.error,
});
reducers[ACTIONS.FETCH_ACCESS_TOKEN_SUCCESS] = (state, action) => {
const { token } = action.data;
return Object.assign({}, state, {
accessToken: token,
});
};
reducers[ACTIONS.USER_INVITE_STATUS_FETCH_STARTED] = state =>
Object.assign({}, state, {
inviteStatusIsPending: true,
});
reducers[ACTIONS.USER_INVITE_STATUS_FETCH_SUCCESS] = (state, action) =>
Object.assign({}, state, {
inviteStatusIsPending: false,
invitesRemaining: action.data.invitesRemaining,
invitees: action.data.invitees,
});
reducers[ACTIONS.USER_INVITE_NEW_STARTED] = state =>
Object.assign({}, state, {
inviteNewIsPending: true,
inviteNewErrorMessage: '',
});
reducers[ACTIONS.USER_INVITE_NEW_SUCCESS] = state =>
Object.assign({}, state, {
inviteNewIsPending: false,
inviteNewErrorMessage: '',
});
reducers[ACTIONS.USER_INVITE_NEW_FAILURE] = (state, action) =>
Object.assign({}, state, {
inviteNewIsPending: false,
inviteNewErrorMessage: action.data.error.message,
});
reducers[ACTIONS.USER_INVITE_STATUS_FETCH_FAILURE] = state =>
Object.assign({}, state, {
inviteStatusIsPending: false,
invitesRemaining: null,
invitees: null,
});
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);
return state;
}

View file

@ -1,64 +0,0 @@
import { createSelector } from 'reselect';
const selectState = state => state.rewards || {};
export const selectUnclaimedRewardsByType = createSelector(
selectState,
state => state.unclaimedRewardsByType
);
export const selectClaimedRewardsById = createSelector(
selectState,
state => state.claimedRewardsById
);
export const selectClaimedRewards = createSelector(
selectClaimedRewardsById,
byId => Object.values(byId) || []
);
export const selectClaimedRewardsByTransactionId = createSelector(selectClaimedRewards, rewards =>
rewards.reduce((mapParam, reward) => {
const map = mapParam;
map[reward.transaction_id] = reward;
return map;
}, {})
);
export const selectUnclaimedRewards = createSelector(selectState, state => state.unclaimedRewards);
export const selectFetchingRewards = createSelector(selectState, state => !!state.fetching);
export const selectUnclaimedRewardValue = createSelector(selectUnclaimedRewards, rewards =>
rewards.reduce((sum, reward) => sum + reward.reward_amount, 0)
);
export const selectClaimsPendingByType = createSelector(
selectState,
state => state.claimPendingByType
);
const selectIsClaimRewardPending = (state, props) =>
selectClaimsPendingByType(state, props)[props.reward_type];
export const makeSelectIsRewardClaimPending = () =>
createSelector(selectIsClaimRewardPending, isClaiming => isClaiming);
export const selectClaimErrorsByType = createSelector(
selectState,
state => state.claimErrorsByType
);
const selectClaimRewardError = (state, props) =>
selectClaimErrorsByType(state, props)[props.reward_type];
export const makeSelectClaimRewardError = () =>
createSelector(selectClaimRewardError, errorMessage => errorMessage);
const selectRewardByType = (state, rewardType) =>
selectUnclaimedRewards(state).find(reward => reward.reward_type === rewardType);
export const makeSelectRewardByType = () => createSelector(selectRewardByType, reward => reward);
export const makeSelectRewardAmountByType = () =>
createSelector(selectRewardByType, reward => (reward ? reward.reward_amount : 0));

View file

@ -1,118 +0,0 @@
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 selectUserEmail = createSelector(
selectUser,
user => (user ? user.primary_email : null)
);
export const selectUserPhone = createSelector(
selectUser,
user => (user ? user.phone_number : null)
);
export const selectUserCountryCode = createSelector(
selectUser,
user => (user ? user.country_code : null)
);
export const selectEmailToVerify = createSelector(
selectState,
selectUserEmail,
(state, userEmail) => state.emailToVerify || userEmail
);
export const selectPhoneToVerify = createSelector(
selectState,
selectUserPhone,
(state, userPhone) => state.phoneToVerify || userPhone
);
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 selectPhoneNewErrorMessage = createSelector(
selectState,
state => state.phoneNewErrorMessage
);
export const selectEmailVerifyIsPending = createSelector(
selectState,
state => state.emailVerifyIsPending
);
export const selectEmailVerifyErrorMessage = createSelector(
selectState,
state => state.emailVerifyErrorMessage
);
export const selectPhoneVerifyErrorMessage = createSelector(
selectState,
state => state.phoneVerifyErrorMessage
);
export const selectIdentityVerifyIsPending = createSelector(
selectState,
state => state.identityVerifyIsPending
);
export const selectIdentityVerifyErrorMessage = createSelector(
selectState,
state => state.identityVerifyErrorMessage
);
export const selectUserIsVerificationCandidate = createSelector(
selectUser,
user => user && (!user.has_verified_email || !user.is_identity_verified)
);
export const selectAccessToken = createSelector(selectState, state => state.accessToken);
export const selectUserInviteStatusIsPending = createSelector(
selectState,
state => state.inviteStatusIsPending
);
export const selectUserInvitesRemaining = createSelector(
selectState,
state => state.invitesRemaining
);
export const selectUserInvitees = createSelector(selectState, state => state.invitees);
export const selectUserInviteStatusFailed = createSelector(
selectUserInvitesRemaining,
() => selectUserInvitesRemaining === null
);
export const selectUserInviteNewIsPending = createSelector(
selectState,
state => state.inviteNewIsPending
);
export const selectUserInviteNewErrorMessage = createSelector(
selectState,
state => state.inviteNewErrorMessage
);

View file

@ -1,113 +0,0 @@
import { Lbry, doNotify } from 'lbry-redux';
import Lbryio from 'lbryio';
const rewards = {};
rewards.TYPE_NEW_DEVELOPER = 'new_developer';
rewards.TYPE_NEW_USER = 'new_user';
rewards.TYPE_CONFIRM_EMAIL = 'verified_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_REFERRAL = 'referral';
rewards.YOUTUBE_CREATOR = 'youtube_creator';
rewards.SUBSCRIPTION = 'subscription';
rewards.claimReward = 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 => {
const message =
reward.reward_notification || `You have claimed a ${reward.reward_amount} LBC reward.`;
// Display global notice
const action = doNotify({
message,
linkText: __('Show All'),
linkTarget: '/rewards',
isError: false,
displayType: ['snackbar'],
});
window.app.store.dispatch(action);
// Add more events here to display other places
resolve(reward);
}, reject);
}
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(claims => {
const claim = claims
.reverse()
.find(
foundClaim =>
foundClaim.name.length &&
foundClaim.name[0] === '@' &&
foundClaim.txid.length &&
foundClaim.category === '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 => {
const claim = claims
.reverse()
.find(
foundClaim =>
foundClaim.name.length &&
foundClaim.name[0] !== '@' &&
foundClaim.txid.length &&
foundClaim.category === '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);
}
});
});
};
export default rewards;

View file

@ -12,9 +12,8 @@ import {
blacklistReducer,
} from 'lbry-redux';
import navigationReducer from 'redux/reducers/navigation';
import rewardsReducer from 'redux/reducers/rewards';
import settingsReducer from 'redux/reducers/settings';
import userReducer from 'redux/reducers/user';
import { userReducer, rewardsReducer } from 'lbryinc';
import shapeShiftReducer from 'redux/reducers/shape_shift';
import subscriptionsReducer from 'redux/reducers/subscriptions';
import publishReducer from 'redux/reducers/publish';

View file

@ -5655,6 +5655,13 @@ lazy-val@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.3.tgz#bb97b200ef00801d94c317e29dc6ed39e31c5edc"
lbry-redux@lbryio/lbry-redux:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/a8e81949837171e94e649fce6f7c7a8b7faadd51"
dependencies:
proxy-polyfill "0.1.6"
reselect "^3.0.0"
lbry-redux@lbryio/lbry-redux#d1cee82af119c0c5f98ec27f94b2e7f61e34b54c:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/d1cee82af119c0c5f98ec27f94b2e7f61e34b54c"
@ -5662,6 +5669,13 @@ lbry-redux@lbryio/lbry-redux#d1cee82af119c0c5f98ec27f94b2e7f61e34b54c:
proxy-polyfill "0.1.6"
reselect "^3.0.0"
lbryinc@lbryio/lbryinc#c09aa2645ecccb33c83e9a9545ff119232910f6f:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/c09aa2645ecccb33c83e9a9545ff119232910f6f"
dependencies:
lbry-redux lbryio/lbry-redux
reselect "^3.0.0"
lcid@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"