Use lbryio/lbryinc #1937

Merged
neb-b merged 1 commit from lbryinc into master 2018-09-24 15:32:44 +02:00
45 changed files with 200 additions and 1347 deletions
Showing only changes of commit 9b700e48bf - Show all commits
.flowconfigpackage.json
src/renderer
analytics.jsapp.js
component
app
cardVerify
fileViewer
inviteList
inviteNew
rewardLink
rewardListClaimed
rewardSummary
rewardTile
transactionList
userEmailNew
userEmailVerify
userPhoneNew
userPhoneVerify
userVerify
index.jslbryio.js
modal
modalCreditIntro
modalEmailCollection
modalFirstReward
modalPhoneCollection
modalRouter
page
redux
rewards.jsstore.js
yarn.lock

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;
skhameneh commented 2018-09-19 17:19:05 +02:00 (Migrated from github.com)
Review

Is this still needed?

Is this still needed?
neb-b commented 2018-09-19 17:53:12 +02:00 (Migrated from github.com)
Review

Yeah, there are a few places where it looks for window.store

Yeah, there are a few places where it looks for `window.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"