Merge branch 'master' into issue/1119
This commit is contained in:
commit
39072b07b5
18 changed files with 147 additions and 26 deletions
|
@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|||
* Do not kill an existing daemon, instead check if one exists ([#973](https://github.com/lbryio/lbry-app/pull/973))
|
||||
* Enable play button immediately after user clicks download ([#987](https://github.com/lbryio/lbry-app/pull/987))
|
||||
* Significantly improved search performance ([#1032](https://github.com/lbryio/lbry-app/pull/1032))
|
||||
* Allow editing of claims when bid is greater than current balance ([1105](https://github.com/lbryio/lbry-app/pull/1105))
|
||||
|
||||
### Fixed
|
||||
* Fixed sort by date of published content ([#986](https://github.com/lbryio/lbry-app/issues/986))
|
||||
|
@ -35,6 +36,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|||
* App will no longer reset when minimizing to tray ([#1042](https://github.com/lbryio/lbry-app/pull/1042))
|
||||
* Error when clicking LBRY URLs when app is closed on macOS ([#1119](https://github.com/lbryio/lbry-app/issues/1119))
|
||||
* LBRY URLs not working on Linux ([#1120](https://github.com/lbryio/lbry-app/issues/1120))
|
||||
* Fix Windows notifications not showing ([1145](https://github.com/lbryio/lbry-app/pull/1145))
|
||||
|
||||
### Deprecated
|
||||
*
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
"keywords": [
|
||||
"lbry"
|
||||
],
|
||||
"build": {
|
||||
"appId": "io.lbry.LBRY"
|
||||
},
|
||||
"license": "MIT",
|
||||
"homepage": "https://lbry.io/",
|
||||
"bugs": {
|
||||
|
|
|
@ -50,6 +50,7 @@ const installExtensions = async () => {
|
|||
|
||||
app.setAsDefaultProtocolClient('lbry');
|
||||
app.setName('LBRY');
|
||||
app.setAppUserModelId('io.lbry.LBRY');
|
||||
|
||||
app.on('ready', async () => {
|
||||
const processList = await findProcess('name', 'lbrynet-daemon');
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import mixpanel from 'mixpanel-browser';
|
||||
import Lbryio from 'lbryio';
|
||||
import isDev from 'electron-is-dev';
|
||||
import type { Subscription } from 'redux/reducers/subscriptions';
|
||||
|
||||
if (isDev) {
|
||||
mixpanel.init('691723e855cabb9d27a7a79002216967');
|
||||
|
@ -13,7 +14,9 @@ type Analytics = {
|
|||
track: (string, ?Object) => void,
|
||||
setUser: Object => void,
|
||||
toggle: (boolean, ?boolean) => void,
|
||||
apiLog: (string, string, string) => void,
|
||||
apiLogView: (string, string, string) => void,
|
||||
apiLogSubscribe: Subscription => void,
|
||||
apiLogUnsubscribe: Subscription => void,
|
||||
};
|
||||
|
||||
let analyticsEnabled: boolean = false;
|
||||
|
@ -44,7 +47,7 @@ const analytics: Analytics = {
|
|||
}
|
||||
analyticsEnabled = enabled;
|
||||
},
|
||||
apiLog: (uri: string, outpoint: string, claimId: string): void => {
|
||||
apiLogView: (uri: string, outpoint: string, claimId: string): void => {
|
||||
if (analyticsEnabled) {
|
||||
Lbryio.call('file', 'view', {
|
||||
uri,
|
||||
|
@ -53,6 +56,20 @@ const analytics: Analytics = {
|
|||
}).catch(() => {});
|
||||
}
|
||||
},
|
||||
apiLogSubscribe: (subscription: Subscription): void => {
|
||||
if (analyticsEnabled) {
|
||||
Lbryio.call('subscription', 'new', {
|
||||
subscription,
|
||||
}).catch(() => {});
|
||||
}
|
||||
},
|
||||
apiLogUnsubscribe: (subscription: Subscription): void => {
|
||||
if (analyticsEnabled) {
|
||||
Lbryio.call('subscription', 'delete', {
|
||||
subscription,
|
||||
}).catch(() => {});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default analytics;
|
||||
|
|
|
@ -57,6 +57,16 @@ class ChannelSection extends React.PureComponent {
|
|||
|
||||
return;
|
||||
}
|
||||
if (newChannelBid === 0) {
|
||||
this.refs.newChannelName.showError(__('Bid value must be greater than 0.'));
|
||||
|
||||
return;
|
||||
}
|
||||
if (newChannelBid === balance) {
|
||||
this.refs.newChannelName.showError(__('Please decrease your bid to account for transaction fees.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
creatingChannel: true,
|
||||
|
|
|
@ -61,15 +61,6 @@ class PublishForm extends React.PureComponent {
|
|||
}
|
||||
|
||||
handleSubmit() {
|
||||
const { balance } = this.props;
|
||||
const { bid } = this.state;
|
||||
|
||||
if (bid > balance) {
|
||||
this.handlePublishError({ message: 'insufficient funds' });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
submitting: true,
|
||||
});
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doChannelSubscribe, doChannelUnsubscribe } from 'redux/actions/subscriptions';
|
||||
import { doOpenModal } from 'redux/actions/app';
|
||||
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
||||
|
||||
import SubscribeButton from './view';
|
||||
|
@ -11,4 +12,5 @@ const select = (state, props) => ({
|
|||
export default connect(select, {
|
||||
doChannelSubscribe,
|
||||
doChannelUnsubscribe,
|
||||
doOpenModal,
|
||||
})(SubscribeButton);
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
import React from 'react';
|
||||
import Link from 'component/link';
|
||||
import * as modals from 'constants/modal_types';
|
||||
|
||||
export default ({ channelName, uri, subscriptions, doChannelSubscribe, doChannelUnsubscribe }) => {
|
||||
export default ({
|
||||
channelName,
|
||||
uri,
|
||||
subscriptions,
|
||||
doChannelSubscribe,
|
||||
doChannelUnsubscribe,
|
||||
doOpenModal,
|
||||
}) => {
|
||||
const isSubscribed =
|
||||
subscriptions.map(subscription => subscription.channelName).indexOf(channelName) !== -1;
|
||||
|
||||
|
@ -15,12 +23,15 @@ export default ({ channelName, uri, subscriptions, doChannelSubscribe, doChannel
|
|||
iconRight={isSubscribed ? '' : 'at'}
|
||||
button={isSubscribed ? 'alt' : 'primary'}
|
||||
label={subscriptionLabel}
|
||||
onClick={() =>
|
||||
onClick={() => {
|
||||
if (!subscriptions.length) {
|
||||
doOpenModal(modals.FIRST_SUBSCRIPTION);
|
||||
}
|
||||
subscriptionHandler({
|
||||
channelName,
|
||||
uri,
|
||||
})
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : null;
|
||||
|
|
|
@ -16,3 +16,4 @@ export const TRANSACTION_FAILED = 'transaction_failed';
|
|||
export const REWARD_APPROVAL_REQUIRED = 'reward_approval_required';
|
||||
export const AFFIRM_PURCHASE = 'affirm_purchase';
|
||||
export const CONFIRM_CLAIM_REVOKE = 'confirmClaimRevoke';
|
||||
export const FIRST_SUBSCRIPTION = 'firstSubscription';
|
||||
|
|
11
src/renderer/modal/modalFirstSubscription/index.js
Normal file
11
src/renderer/modal/modalFirstSubscription/index.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doCloseModal } from 'redux/actions/app';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import ModalFirstSubscription from './view';
|
||||
|
||||
const perform = dispatch => () => ({
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
});
|
||||
|
||||
export default connect(null, perform)(ModalFirstSubscription);
|
43
src/renderer/modal/modalFirstSubscription/view.jsx
Normal file
43
src/renderer/modal/modalFirstSubscription/view.jsx
Normal file
|
@ -0,0 +1,43 @@
|
|||
import React from 'react';
|
||||
import { Modal } from 'modal/modal';
|
||||
import Link from 'component/link';
|
||||
|
||||
const ModalFirstSubscription = props => {
|
||||
const { closeModal, navigate } = props;
|
||||
|
||||
return (
|
||||
<Modal type="custom" isOpen contentLabel="Subscriptions 101">
|
||||
<section>
|
||||
<h3 className="modal__header">{__('Subscriptions 101')}</h3>
|
||||
<p>{__('You just subscribed to your first channel. Awesome!')}</p>
|
||||
<p>{__('A few quick things to know:')}</p>
|
||||
<p>
|
||||
{__('1) You can use the')}{' '}
|
||||
<Link
|
||||
label={__('Subscriptions Page')}
|
||||
onClick={() => {
|
||||
navigate('/subscriptions');
|
||||
closeModal();
|
||||
}}
|
||||
/>{' '}
|
||||
{__('to view content across all of your subscribed channels.')}
|
||||
</p>
|
||||
<p>
|
||||
{__(
|
||||
'2) This app will automatically download new free content from channels you are subscribed to.'
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{__(
|
||||
'3) If we have your email address, we may send you notifications and rewards related to new content.'
|
||||
)}
|
||||
</p>
|
||||
<div className="modal__buttons">
|
||||
<Link button="primary" onClick={closeModal} label={__('Got it')} />
|
||||
</div>
|
||||
</section>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModalFirstSubscription;
|
|
@ -14,8 +14,9 @@ import ModalTransactionFailed from 'modal/modalTransactionFailed';
|
|||
import ModalFileTimeout from 'modal/modalFileTimeout';
|
||||
import ModalAffirmPurchase from 'modal/modalAffirmPurchase';
|
||||
import ModalRevokeClaim from 'modal/modalRevokeClaim';
|
||||
import ModalEmailCollection from '../modalEmailCollection';
|
||||
import ModalPhoneCollection from '../modalPhoneCollection';
|
||||
import ModalEmailCollection from 'modal/modalEmailCollection';
|
||||
import ModalPhoneCollection from 'modal/modalPhoneCollection';
|
||||
import ModalFirstSubscription from 'modal/modalFirstSubscription';
|
||||
import * as modals from 'constants/modal_types';
|
||||
|
||||
class ModalRouter extends React.PureComponent {
|
||||
|
@ -135,6 +136,8 @@ class ModalRouter extends React.PureComponent {
|
|||
return <ModalPhoneCollection {...modalProps} />;
|
||||
case modals.EMAIL_COLLECTION:
|
||||
return <ModalEmailCollection {...modalProps} />;
|
||||
case modals.FIRST_SUBSCRIPTION:
|
||||
return <ModalFirstSubscription {...modalProps} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -4,9 +4,11 @@ import { selectFetchingRewards, selectUnclaimedRewards } from 'redux/selectors/r
|
|||
import { selectUser } from 'redux/selectors/user';
|
||||
import { doAuthNavigate, doNavigate } from 'redux/actions/navigation';
|
||||
import { doRewardList } from 'redux/actions/rewards';
|
||||
import { selectDaemonSettings } from 'redux/selectors/settings';
|
||||
import RewardsPage from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
daemonSettings: selectDaemonSettings(state),
|
||||
fetching: selectFetchingRewards(state),
|
||||
rewards: selectUnclaimedRewards(state),
|
||||
user: selectUser(state),
|
||||
|
|
|
@ -29,9 +29,9 @@ class RewardsPage extends React.PureComponent {
|
|||
// }
|
||||
|
||||
renderPageHeader() {
|
||||
const { doAuth, navigate, user } = this.props;
|
||||
const { doAuth, navigate, user, daemonSettings } = this.props;
|
||||
|
||||
if (user && !user.is_reward_approved) {
|
||||
if (user && !user.is_reward_approved && daemonSettings.share_usage_data) {
|
||||
if (!user.primary_email || !user.has_verified_email || !user.is_identity_verified) {
|
||||
return (
|
||||
<section className="card">
|
||||
|
@ -78,9 +78,21 @@ class RewardsPage extends React.PureComponent {
|
|||
}
|
||||
|
||||
renderUnclaimedRewards() {
|
||||
const { fetching, rewards, user } = this.props;
|
||||
const { fetching, rewards, user, daemonSettings, navigate } = this.props;
|
||||
|
||||
if (fetching) {
|
||||
if (!daemonSettings.share_usage_data) {
|
||||
return (
|
||||
<div className="card__content empty">
|
||||
<p>
|
||||
{__(
|
||||
'Rewards are currently disabled for your account. Turn on diagnostic data sharing, in'
|
||||
)}{' '}
|
||||
<Link onClick={() => navigate('/settings')} label="Settings" />
|
||||
{__(', in order to re-enable them.')}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
} else if (fetching) {
|
||||
return (
|
||||
<div className="card__content">
|
||||
<BusyMessage message={__('Fetching rewards')} />
|
||||
|
|
|
@ -309,7 +309,10 @@ class SettingsPage extends React.PureComponent {
|
|||
onChange={this.onShareDataChange.bind(this)}
|
||||
defaultChecked={daemonSettings.share_usage_data}
|
||||
label={__(
|
||||
'Help make LBRY better by contributing analytics and diagnostic data and about my usage'
|
||||
'Help make LBRY better by contributing analytics and diagnostic data about my usage.'
|
||||
)}
|
||||
helper={__(
|
||||
'You will be ineligible to earn rewards while diagnostics are not being shared.'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -228,7 +228,7 @@ export function doDownloadFile(uri, streamInfo) {
|
|||
return dispatch => {
|
||||
dispatch(doStartDownload(uri, streamInfo.outpoint));
|
||||
|
||||
analytics.apiLog(uri, streamInfo.output, streamInfo.claim_id);
|
||||
analytics.apiLogView(uri, streamInfo.output, streamInfo.claim_id);
|
||||
|
||||
dispatch(doClaimEligiblePurchaseRewards());
|
||||
};
|
||||
|
|
|
@ -6,21 +6,30 @@ import Lbry from 'lbry';
|
|||
import { doPurchaseUri } from 'redux/actions/content';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { buildURI } from 'lbryURI';
|
||||
import analytics from 'analytics';
|
||||
|
||||
const CHECK_SUBSCRIPTIONS_INTERVAL = 60 * 60 * 1000;
|
||||
|
||||
export const doChannelSubscribe = (subscription: Subscription) => (dispatch: Dispatch) =>
|
||||
export const doChannelSubscribe = (subscription: Subscription) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: ACTIONS.CHANNEL_SUBSCRIBE,
|
||||
data: subscription,
|
||||
});
|
||||
|
||||
export const doChannelUnsubscribe = (subscription: Subscription) => (dispatch: Dispatch) =>
|
||||
analytics.apiLogSubscribe(subscription);
|
||||
|
||||
dispatch(doCheckSubscription(subscription, true));
|
||||
};
|
||||
|
||||
export const doChannelUnsubscribe = (subscription: Subscription) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: ACTIONS.CHANNEL_UNSUBSCRIBE,
|
||||
data: subscription,
|
||||
});
|
||||
|
||||
analytics.apiLogUnsubscribe(subscription);
|
||||
};
|
||||
|
||||
export const doCheckSubscriptions = () => (
|
||||
dispatch: Dispatch,
|
||||
getState: () => SubscriptionState
|
||||
|
|
|
@ -43,7 +43,7 @@ reducers[ACTIONS.FETCH_CLAIM_LIST_MINE_COMPLETED] = (state, action) => {
|
|||
const byId = Object.assign({}, state.byId);
|
||||
const pendingById = Object.assign({}, state.pendingById);
|
||||
|
||||
claims.filter(claim => claim.category && claim.category.match(/claim/)).forEach(claim => {
|
||||
claims.filter(claim => claim.category && (claim.category.match(/claim/) || claim.category.match(/update/))).forEach(claim => {
|
||||
byId[claim.claim_id] = claim;
|
||||
|
||||
const pending = Object.values(pendingById).find(
|
||||
|
|
Loading…
Reference in a new issue