diff --git a/src/renderer/analytics.js b/src/renderer/analytics.js index 57d053fc9..2a03b7428 100644 --- a/src/renderer/analytics.js +++ b/src/renderer/analytics.js @@ -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; diff --git a/src/renderer/component/subscribeButton/index.js b/src/renderer/component/subscribeButton/index.js index 2f5dceb30..6c93bbb65 100644 --- a/src/renderer/component/subscribeButton/index.js +++ b/src/renderer/component/subscribeButton/index.js @@ -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); diff --git a/src/renderer/component/subscribeButton/view.jsx b/src/renderer/component/subscribeButton/view.jsx index 216b0bf72..bb0d0c146 100644 --- a/src/renderer/component/subscribeButton/view.jsx +++ b/src/renderer/component/subscribeButton/view.jsx @@ -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, - }) - } + }); + }} /> ) : null; diff --git a/src/renderer/constants/modal_types.js b/src/renderer/constants/modal_types.js index a0272b708..0c2e03da1 100644 --- a/src/renderer/constants/modal_types.js +++ b/src/renderer/constants/modal_types.js @@ -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'; diff --git a/src/renderer/modal/modalFirstSubscription/index.js b/src/renderer/modal/modalFirstSubscription/index.js new file mode 100644 index 000000000..6c669f79b --- /dev/null +++ b/src/renderer/modal/modalFirstSubscription/index.js @@ -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); diff --git a/src/renderer/modal/modalFirstSubscription/view.jsx b/src/renderer/modal/modalFirstSubscription/view.jsx new file mode 100644 index 000000000..546819fbc --- /dev/null +++ b/src/renderer/modal/modalFirstSubscription/view.jsx @@ -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 ( + +
+

{__('Subscriptions 101')}

+

{__('You just subscribed to your first channel. Awesome!')}

+

{__('A few quick things to know:')}

+

+ {__('1) You can use the')}{' '} + { + navigate('/subscriptions'); + closeModal(); + }} + />{' '} + {__('to view content across all of your subscribed channels.')} +

+

+ {__( + '2) This app will automatically download new free content from channels you are subscribed to.' + )} +

+

+ {__( + '3) If we have your email address, we may send you notifications and rewards related to new content.' + )} +

+
+ +
+
+
+ ); +}; + +export default ModalFirstSubscription; diff --git a/src/renderer/modal/modalRouter/view.jsx b/src/renderer/modal/modalRouter/view.jsx index 341883171..510d49b9d 100644 --- a/src/renderer/modal/modalRouter/view.jsx +++ b/src/renderer/modal/modalRouter/view.jsx @@ -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 ; case modals.EMAIL_COLLECTION: return ; + case modals.FIRST_SUBSCRIPTION: + return ; default: return null; } diff --git a/src/renderer/redux/actions/content.js b/src/renderer/redux/actions/content.js index 5fd99c23e..146b9b6d5 100644 --- a/src/renderer/redux/actions/content.js +++ b/src/renderer/redux/actions/content.js @@ -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()); }; diff --git a/src/renderer/redux/actions/subscriptions.js b/src/renderer/redux/actions/subscriptions.js index 8a390987b..58269ab40 100644 --- a/src/renderer/redux/actions/subscriptions.js +++ b/src/renderer/redux/actions/subscriptions.js @@ -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