First run timing (#83)

* first run time tracking implementation
* Fix values in AsyncStorage calls. Some tweaks to Mixpanel event properties.
* remove first launch related items in AsyncStorage after final retrieval
This commit is contained in:
akinwale 2018-04-24 20:32:17 +01:00 committed by GitHub
parent 77a86da984
commit 90d17604fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 66 additions and 18 deletions

View file

@ -8,6 +8,7 @@
}, },
"dependencies": { "dependencies": {
"lbry-redux": "lbryio/lbry-redux", "lbry-redux": "lbryio/lbry-redux",
"moment": "^2.22.1",
"react": "16.2.0", "react": "16.2.0",
"react-native": "0.52.0", "react-native": "0.52.0",
"react-native-vector-icons": "^4.5.0", "react-native-vector-icons": "^4.5.0",

View file

@ -14,7 +14,13 @@ import {
} from 'react-navigation'; } from 'react-navigation';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { addListener } from '../utils/redux'; import { addListener } from '../utils/redux';
import { AppState, BackHandler, NativeModules, TextInput } from 'react-native'; import {
AppState,
AsyncStorage,
BackHandler,
NativeModules,
TextInput
} from 'react-native';
import { SETTINGS } from 'lbry-redux'; import { SETTINGS } from 'lbry-redux';
import { makeSelectClientSetting } from '../redux/selectors/settings'; import { makeSelectClientSetting } from '../redux/selectors/settings';
import Feather from 'react-native-vector-icons/Feather'; import Feather from 'react-native-vector-icons/Feather';
@ -102,16 +108,16 @@ class AppWithNavigationState extends React.Component {
} }
_handleAppStateChange = (nextAppState) => { _handleAppStateChange = (nextAppState) => {
// this is properly handled in native code at the moment // Check if the app was suspended
/*const { keepDaemonRunning } = this.props; if (AppState.currentState && AppState.currentState.match(/inactive|background/)) {
if (AppState.currentState && AsyncStorage.getItem('firstLaunchTime').then(start => {
AppState.currentState.match(/inactive|background/) && if (start !== null && !isNaN(parseInt(start, 10))) {
NativeModules.DaemonServiceControl) { // App suspended during first launch?
if (!keepDaemonRunning) { // If so, this needs to be included as a property when tracking
// terminate the daemon background service when is suspended / inactive AsyncStorage.setItem('firstLaunchSuspended', 'true');
//NativeModules.DaemonServiceControl.stopService(); }
} });
}*/ }
} }
render() { render() {

View file

@ -61,7 +61,7 @@ class FileDownloadButton extends React.PureComponent {
return ( return (
<TouchableOpacity style={[style, fileDownloadButtonStyle.container]} onPress={() => { <TouchableOpacity style={[style, fileDownloadButtonStyle.container]} onPress={() => {
if (NativeModules.Mixpanel) { if (NativeModules.Mixpanel) {
NativeModules.Mixpanel.track('Purchase Uri', { uri }); NativeModules.Mixpanel.track('Purchase Uri', { Uri: uri });
} }
purchaseUri(uri); purchaseUri(uri);
}}> }}>

View file

@ -59,7 +59,7 @@ class FileItem extends React.PureComponent {
<View style={style}> <View style={style}>
<TouchableOpacity style={discoverStyle.container} onPress={() => { <TouchableOpacity style={discoverStyle.container} onPress={() => {
if (NativeModules.Mixpanel) { if (NativeModules.Mixpanel) {
NativeModules.Mixpanel.track('Tap', { uri }); NativeModules.Mixpanel.track('Discover Tap', { Uri: uri });
} }
navigation.navigate('File', { uri: uri }); navigation.navigate('File', { uri: uri });
} }

View file

@ -85,7 +85,7 @@ class MediaPlayer extends React.PureComponent {
if (this.state.firstPlay) { if (this.state.firstPlay) {
if (NativeModules.Mixpanel) { if (NativeModules.Mixpanel) {
const { uri } = this.props; const { uri } = this.props;
NativeModules.Mixpanel.track('Play', { uri }); NativeModules.Mixpanel.track('Play', { Uri: uri });
} }
this.setState({ firstPlay: false }); this.setState({ firstPlay: false });
this.hidePlayerControls(); this.hidePlayerControls();

View file

@ -6,7 +6,7 @@ import SearchInput from './view';
const perform = dispatch => ({ const perform = dispatch => ({
search: search => { search: search => {
if (NativeModules.Mixpanel) { if (NativeModules.Mixpanel) {
NativeModules.Mixpanel.track('Search', { query: search }); NativeModules.Mixpanel.track('Search', { Query: search });
} }
return dispatch(doSearch(search)); return dispatch(doSearch(search));
}, },

View file

@ -1,7 +1,14 @@
import React from 'react'; import React from 'react';
import { Provider, connect } from 'react-redux'; import { Provider, connect } from 'react-redux';
import DiscoverPage from './page/discover'; import DiscoverPage from './page/discover';
import { AppRegistry, AppState, StyleSheet, Text, View, AsyncStorage, NativeModules } from 'react-native'; import {
AppRegistry,
AppState,
AsyncStorage,
Text,
View,
NativeModules
} from 'react-native';
import { createStore, applyMiddleware, compose, combineReducers } from 'redux'; import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
import { import {
StackNavigator, addNavigationHelpers StackNavigator, addNavigationHelpers
@ -21,6 +28,7 @@ import {
walletReducer walletReducer
} from 'lbry-redux'; } from 'lbry-redux';
import settingsReducer from './redux/reducers/settings'; import settingsReducer from './redux/reducers/settings';
import moment from 'moment';
import { reactNavigationMiddleware } from './utils/redux'; import { reactNavigationMiddleware } from './utils/redux';
function isFunction(object) { function isFunction(object) {
@ -105,6 +113,16 @@ persistStore(store, persistOptions, err => {
}); });
class LBRYApp extends React.Component { class LBRYApp extends React.Component {
componentDidMount() {
AsyncStorage.getItem('hasLaunched').then(value => {
if (value == null || value !== 'true') {
AsyncStorage.setItem('hasLaunched', 'true');
// only set firstLaunchTime since we've determined that this is the first app launch ever
AsyncStorage.setItem('firstLaunchTime', String(moment().unix()));
}
});
}
render() { render() {
return ( return (
<Provider store={store}> <Provider store={store}>

View file

@ -1,12 +1,35 @@
import React from 'react'; import React from 'react';
import FeaturedCategory from '../../component/featuredCategory'; import FeaturedCategory from '../../component/featuredCategory';
import NavigationActions from 'react-navigation'; import NavigationActions from 'react-navigation';
import { Text, View, ScrollView } from 'react-native'; import { AsyncStorage, NativeModules, ScrollView, Text, View } from 'react-native';
import moment from 'moment';
import discoverStyle from '../../styles/discover'; import discoverStyle from '../../styles/discover';
import Feather from 'react-native-vector-icons/Feather'; import Feather from 'react-native-vector-icons/Feather';
class DiscoverPage extends React.PureComponent { class DiscoverPage extends React.PureComponent {
componentWillMount() { componentWillMount() {
// Track the total time taken if this is the first launch
AsyncStorage.getItem('firstLaunchTime').then(startTime => {
if (startTime !== null && !isNaN(parseInt(startTime, 10))) {
// We don't need this value anymore once we've retrieved it
AsyncStorage.removeItem('firstLaunchTime');
// We know this is the first app launch because firstLaunchTime is set and it's a valid number
const start = parseInt(startTime, 10);
const now = moment().unix();
const delta = now - start;
AsyncStorage.getItem('firstLaunchSuspended').then(suspended => {
AsyncStorage.removeItem('firstLaunchSuspended');
const appSuspended = (suspended === 'true');
if (NativeModules.Mixpanel) {
NativeModules.Mixpanel.track('First Run Time', {
'Total Seconds': delta, 'App Suspended': appSuspended
});
}
});
}
});
this.props.fetchFeaturedUris(); this.props.fetchFeaturedUris();
} }

View file

@ -33,7 +33,7 @@ class FilePage extends React.PureComponent {
this.fetchFileInfo(this.props); this.fetchFileInfo(this.props);
this.fetchCostInfo(this.props); this.fetchCostInfo(this.props);
if (NativeModules.Mixpanel) { if (NativeModules.Mixpanel) {
NativeModules.Mixpanel.track('Open File Page', { uri: this.props.navigation.state.params.uri }); NativeModules.Mixpanel.track('Open File Page', { Uri: this.props.navigation.state.params.uri });
} }
} }