Implemented Mixpanel analytics
This commit is contained in:
parent
173920d9eb
commit
bb7fa61c33
11 changed files with 98 additions and 11 deletions
|
@ -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>
|
||||||
|
|
|
@ -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 });
|
||||||
}
|
}
|
||||||
}>
|
}>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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 }); }}/>}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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(() => {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue