Merge pull request #68 from lbryio/mixpanel

Implemented Mixpanel analytics
This commit is contained in:
akinwale 2018-04-23 05:00:38 +01:00 committed by GitHub
commit a4798c3cf9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 98 additions and 11 deletions

View file

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Text, View, TouchableOpacity } from 'react-native'; import { NativeModules, Text, View, TouchableOpacity } from 'react-native';
import fileDownloadButtonStyle from '../../styles/fileDownloadButton'; import fileDownloadButtonStyle from '../../styles/fileDownloadButton';
class FileDownloadButton extends React.PureComponent { class FileDownloadButton extends React.PureComponent {
@ -60,6 +60,9 @@ class FileDownloadButton extends React.PureComponent {
} }
return ( return (
<TouchableOpacity style={[style, fileDownloadButtonStyle.container]} onPress={() => { <TouchableOpacity style={[style, fileDownloadButtonStyle.container]} onPress={() => {
if (NativeModules.Mixpanel) {
NativeModules.Mixpanel.track('Purchase Uri', { uri });
}
purchaseUri(uri); purchaseUri(uri);
}}> }}>
<Text style={fileDownloadButtonStyle.text}>Download</Text> <Text style={fileDownloadButtonStyle.text}>Download</Text>

View file

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { normalizeURI } from 'lbry-redux'; import { normalizeURI } from 'lbry-redux';
import { NavigationActions } from 'react-navigation'; import { NavigationActions } from 'react-navigation';
import { Text, View, TouchableOpacity } from 'react-native'; import { NativeModules, Text, View, TouchableOpacity } from 'react-native';
import FileItemMedia from '../fileItemMedia'; import FileItemMedia from '../fileItemMedia';
import FilePrice from '../filePrice'; import FilePrice from '../filePrice';
import NsfwOverlay from '../nsfwOverlay'; import NsfwOverlay from '../nsfwOverlay';
@ -58,6 +58,9 @@ class FileItem extends React.PureComponent {
return ( return (
<View style={style}> <View style={style}>
<TouchableOpacity style={discoverStyle.container} onPress={() => { <TouchableOpacity style={discoverStyle.container} onPress={() => {
if (NativeModules.Mixpanel) {
NativeModules.Mixpanel.track('Tap', { uri });
}
navigation.navigate('File', { uri: uri }); navigation.navigate('File', { uri: uri });
} }
}> }>

View file

@ -1,6 +1,13 @@
import React from 'react'; import React from 'react';
import { Lbry } from 'lbry-redux'; import { Lbry } from 'lbry-redux';
import { PanResponder, Text, View, ScrollView, TouchableOpacity } from 'react-native'; import {
NativeModules,
PanResponder,
Text,
View,
ScrollView,
TouchableOpacity
} from 'react-native';
import Video from 'react-native-video'; import Video from 'react-native-video';
import Icon from 'react-native-vector-icons/FontAwesome'; import Icon from 'react-native-vector-icons/FontAwesome';
import FileItemMedia from '../fileItemMedia'; import FileItemMedia from '../fileItemMedia';
@ -76,6 +83,10 @@ class MediaPlayer extends React.PureComponent {
} }
if (this.state.firstPlay) { if (this.state.firstPlay) {
if (NativeModules.Mixpanel) {
const { uri } = this.props;
NativeModules.Mixpanel.track('Play', { uri });
}
this.setState({ firstPlay: false }); this.setState({ firstPlay: false });
this.hidePlayerControls(); this.hidePlayerControls();
} }
@ -232,7 +243,7 @@ class MediaPlayer extends React.PureComponent {
} }
render() { render() {
const { fileInfo, title, thumbnail, style, fullScreenStyle } = this.props; const { fileInfo, thumbnail, style, fullScreenStyle } = this.props;
const flexCompleted = this.getCurrentTimePercentage() * 100; const flexCompleted = this.getCurrentTimePercentage() * 100;
const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100; const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100;

View file

@ -1,9 +1,15 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { NativeModules } from 'react-native';
import { doSearch, doUpdateSearchQuery } from 'lbry-redux'; import { doSearch, doUpdateSearchQuery } from 'lbry-redux';
import SearchInput from './view'; import SearchInput from './view';
const perform = dispatch => ({ const perform = dispatch => ({
search: search => dispatch(doSearch(search)), search: search => {
if (NativeModules.Mixpanel) {
NativeModules.Mixpanel.track('Search', { query: search });
}
return dispatch(doSearch(search));
},
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query, false)) updateSearchQuery: query => dispatch(doUpdateSearchQuery(query, false))
}); });

View file

@ -32,6 +32,9 @@ class FilePage extends React.PureComponent {
StatusBar.setHidden(false); StatusBar.setHidden(false);
this.fetchFileInfo(this.props); this.fetchFileInfo(this.props);
this.fetchCostInfo(this.props); this.fetchCostInfo(this.props);
if (NativeModules.Mixpanel) {
NativeModules.Mixpanel.track('Open File Page', { uri: this.props.navigation.state.params.uri });
}
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
@ -136,6 +139,7 @@ class FilePage extends React.PureComponent {
{isPlayable && !this.state.mediaLoaded && <ActivityIndicator size="large" color={Colors.LbryGreen} style={filePageStyle.loading} />} {isPlayable && !this.state.mediaLoaded && <ActivityIndicator size="large" color={Colors.LbryGreen} style={filePageStyle.loading} />}
{!completed && <FileDownloadButton uri={navigation.state.params.uri} style={filePageStyle.downloadButton} />} {!completed && <FileDownloadButton uri={navigation.state.params.uri} style={filePageStyle.downloadButton} />}
{fileInfo && isPlayable && <MediaPlayer fileInfo={fileInfo} {fileInfo && isPlayable && <MediaPlayer fileInfo={fileInfo}
uri={navigation.state.params.uri}
style={filePageStyle.player} style={filePageStyle.player}
onFullscreenToggled={this.handleFullscreenToggle} onFullscreenToggled={this.handleFullscreenToggle}
onMediaLoaded={() => { this.setState({ mediaLoaded: true }); }}/>} onMediaLoaded={() => { this.setState({ mediaLoaded: true }); }}/>}

View file

@ -14,7 +14,7 @@ const select = (state) => ({
}); });
const perform = dispatch => ({ const perform = dispatch => ({
search: (query) => dispatch(doSearch(query)), search: (query) => dispatch(doSearch(query))
}); });
export default connect(select, perform)(SearchPage); export default connect(select, perform)(SearchPage);

View file

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { Lbry } from 'lbry-redux'; import { Lbry } from 'lbry-redux';
import { View, Text } from 'react-native'; import { View, Text, NativeModules } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import splashStyle from '../../styles/splash'; import splashStyle from '../../styles/splash';
@ -66,6 +66,10 @@ class SplashScreen extends React.PureComponent {
} }
componentDidMount() { componentDidMount() {
if (NativeModules.Mixpanel) {
NativeModules.Mixpanel.track('App Launch', null);
}
Lbry Lbry
.connect() .connect()
.then(() => { .then(() => {

View file

@ -86,7 +86,7 @@ fullscreen = 0
#android.presplash_color = #FFFFFF #android.presplash_color = #FFFFFF
# (list) Permissions # (list) Permissions
android.permissions = INTERNET,READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE android.permissions = ACCESS_NETWORK_STATE,BLUETOOTH,INTERNET,READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE
# (int) Android API to use # (int) Android API to use
android.api = 23 android.api = 23
@ -148,7 +148,7 @@ android.react_src = ./app
# (list) Gradle dependencies to add (currently works only with sdl2_gradle # (list) Gradle dependencies to add (currently works only with sdl2_gradle
# bootstrap) # bootstrap)
android.gradle_dependencies = com.android.support:appcompat-v7:23.4.0, com.facebook.react:react-native:+ android.gradle_dependencies = com.android.support:appcompat-v7:23.4.0, com.facebook.react:react-native:+, com.mixpanel.android:mixpanel-android:5+, com.google.android.gms:play-services-gcm:11.0.4+
# (str) python-for-android branch to use, defaults to master # (str) python-for-android branch to use, defaults to master
#p4a.branch = stable #p4a.branch = stable

View file

@ -86,7 +86,7 @@ fullscreen = 0
#android.presplash_color = #FFFFFF #android.presplash_color = #FFFFFF
# (list) Permissions # (list) Permissions
android.permissions = INTERNET,READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE android.permissions = ACCESS_NETWORK_STATE,BLUETOOTH,INTERNET,READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE
# (int) Android API to use # (int) Android API to use
android.api = 23 android.api = 23
@ -148,7 +148,7 @@ android.react_src = ./app
# (list) Gradle dependencies to add (currently works only with sdl2_gradle # (list) Gradle dependencies to add (currently works only with sdl2_gradle
# bootstrap) # bootstrap)
android.gradle_dependencies = com.android.support:appcompat-v7:23.4.0, com.facebook.react:react-native:+ android.gradle_dependencies = com.android.support:appcompat-v7:23.4.0, com.facebook.react:react-native:+, com.mixpanel.android:mixpanel-android:5+, com.google.android.gms:play-services-gcm:11.0.4+
# (str) python-for-android branch to use, defaults to master # (str) python-for-android branch to use, defaults to master
#p4a.branch = stable #p4a.branch = stable

View file

@ -0,0 +1,54 @@
package io.lbry.browser.reactmodules;
import android.content.Context;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.mixpanel.android.mpmetrics.MixpanelAPI;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONObject;
import org.json.JSONException;
public class MixpanelModule extends ReactContextBaseJavaModule {
private static final String MIXPANEL_TOKEN = "93b81fb957cb0ddcd3198c10853a6a95";
private Context context;
private MixpanelAPI mixpanel;
public MixpanelModule(ReactApplicationContext reactContext) {
super(reactContext);
this.context = reactContext;
this.mixpanel = MixpanelAPI.getInstance(this.context, MIXPANEL_TOKEN);
}
@Override
public String getName() {
return "Mixpanel";
}
@ReactMethod
public void track(String name, ReadableMap payload) {
JSONObject props = new JSONObject();
try {
if (payload != null) {
HashMap<String, Object> payloadMap = payload.toHashMap();
for (Map.Entry<String, Object> entry : payloadMap.entrySet()) {
props.put(entry.getKey(), entry.getValue());
}
}
} catch (JSONException e) {
// Cannot use props. Stick with empty props.
}
if (mixpanel != null) {
mixpanel.track(name, props);
}
}
}

View file

@ -7,6 +7,7 @@ import com.facebook.react.uimanager.ViewManager;
import io.lbry.browser.reactmodules.DaemonServiceControlModule; import io.lbry.browser.reactmodules.DaemonServiceControlModule;
import io.lbry.browser.reactmodules.DownloadManagerModule; import io.lbry.browser.reactmodules.DownloadManagerModule;
import io.lbry.browser.reactmodules.MixpanelModule;
import io.lbry.browser.reactmodules.ScreenOrientationModule; import io.lbry.browser.reactmodules.ScreenOrientationModule;
import java.util.ArrayList; import java.util.ArrayList;
@ -25,6 +26,7 @@ public class LbryReactPackage implements ReactPackage {
modules.add(new DaemonServiceControlModule(reactContext)); modules.add(new DaemonServiceControlModule(reactContext));
modules.add(new DownloadManagerModule(reactContext)); modules.add(new DownloadManagerModule(reactContext));
modules.add(new MixpanelModule(reactContext));
modules.add(new ScreenOrientationModule(reactContext)); modules.add(new ScreenOrientationModule(reactContext));
return modules; return modules;