Skip to content

Commit

Permalink
Firebase Analytics + Sentry error logging (#54)
Browse files Browse the repository at this point in the history
* Base analytics configuration

* Log screen changes in navigation

* Added analytics logging in Rewards

* Added analytics to checkout, auth, drawer

* Disable analytics in development mode

* Update env template to fallback to process.NODE_ENV

* Install missing packages

* Removed disable firebase in dev

* Added transaction error log to Sentry

* Changed 'screen' event param to 'component'

* Fixed invalid user property promise rejection

* Add Sentry logging to all .catch blocks, fix typo

* Removed await

Co-authored-by: annie.ro98 <[email protected]>
  • Loading branch information
wangannie and annieyro authored May 11, 2020
1 parent 1d456c2 commit c0c9862
Show file tree
Hide file tree
Showing 16 changed files with 701 additions and 409 deletions.
3 changes: 2 additions & 1 deletion App.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as ScreenOrientation from 'expo-screen-orientation';
import React, { useState } from 'react';
import { Platform, StatusBar, StyleSheet } from 'react-native';
import * as Sentry from 'sentry-expo';
import { env } from './environment';
import AppNavigator from './navigation/AppNavigator';
import { Container } from './styled/shared';

Expand All @@ -14,7 +15,7 @@ Sentry.init({
enableInExpoDevelopment: false,
release: 'v1.1.1',
debug: true,
environment: process.env.NODE_ENV,
environment: env,
});

export default function App(props) {
Expand Down
36 changes: 36 additions & 0 deletions GoogleService-Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CLIENT_ID</key>
<string>556962069588-t4fm267m8jriipmhi3g51t032uu058t8.apps.googleusercontent.com</string>
<key>REVERSED_CLIENT_ID</key>
<string>com.googleusercontent.apps.556962069588-t4fm267m8jriipmhi3g51t032uu058t8</string>
<key>API_KEY</key>
<string>AIzaSyAhZpUiPpXSik2gJEY318xD-mAyMKkD8nY</string>
<key>GCM_SENDER_ID</key>
<string>556962069588</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>org.calblueprint.HealthyCornersClerk</string>
<key>PROJECT_ID</key>
<string>healthy-corners-clerk</string>
<key>STORAGE_BUCKET</key>
<string>healthy-corners-clerk.appspot.com</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:556962069588:ios:9f636ecd24fafe876401c1</string>
<key>DATABASE_URL</key>
<string>https://healthy-corners-clerk.firebaseio.com</string>
</dict>
</plist>
16 changes: 16 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"platforms": ["ios", "android", "web"],
"android": {
"package": "org.calblueprint.HealthyCornersClerk",
"googleServicesFile": "./google-services.json",
"versionCode": 5,
"permissions": []
},
Expand All @@ -24,10 +25,25 @@
"assetBundlePatterns": ["**/*"],
"ios": {
"bundleIdentifier": "org.calblueprint.HealthyCornersClerk",
"googleServicesFile": "./GoogleService-Info.plist",
"buildNumber": "1.1.2",
"supportsTablet": true,
"requireFullScreen": true
},
"web": {
"config": {
"firebase": {
"apiKey": "AIzaSyCiFxSYjtUVN8EZknbuIAOcDxGGdQIVMb4",
"authDomain": "healthy-corners-clerk.firebaseapp.com",
"databaseURL": "https://healthy-corners-clerk.firebaseio.com",
"projectId": "healthy-corners-clerk",
"storageBucket": "healthy-corners-clerk.appspot.com",
"messagingSenderId": "556962069588",
"appId": "1:556962069588:web:0e63f6035f4d435d6401c1",
"measurementId": "G-L6MBR8JPLE"
}
}
},
"hooks": {
"postPublish": [
{
Expand Down
11 changes: 8 additions & 3 deletions environment.example
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,20 @@ const ENV_VARIABLES = {
},
};

const env = Constants.manifest.releaseChannel || 'dev';
// If releaseChannel not set, use process.env.NODE_ENV as substitute
const env =
Constants.manifest.releaseChannel || process.env.NODE_ENV === 'production'
? 'prod'
: 'dev';

const getEnvVars = () => {
if (__DEV__) {
return ENV_VARIABLES.dev;
}
if (env === 'prod') {
return ENV_VARIABLES.prod;
}
console.log('[environment.js] UNREACHED');
return {};
// Fall through to dev
return ENV_VARIABLES.dev;
};
export { getEnvVars as default, staticEnvVars, env };
40 changes: 40 additions & 0 deletions google-services.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"project_info": {
"project_number": "556962069588",
"firebase_url": "https://healthy-corners-clerk.firebaseio.com",
"project_id": "healthy-corners-clerk",
"storage_bucket": "healthy-corners-clerk.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:556962069588:android:39ede0a684457a0d6401c1",
"android_client_info": {
"package_name": "org.calblueprint.HealthyCornersClerk"
}
},
"oauth_client": [
{
"client_id": "556962069588-dvojual5o8o7tm4dk2ki4anr4vai37fk.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyCaQkohTIyN6mhWiqmLURx0vdyEzfAw9nE"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "556962069588-dvojual5o8o7tm4dk2ki4anr4vai37fk.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}
32 changes: 31 additions & 1 deletion navigation/AppNavigator.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createDrawerNavigator } from '@react-navigation/drawer';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import * as Analytics from 'expo-firebase-analytics';
import React from 'react';
import Colors from '../constants/Colors';
import DrawerContent from './DrawerContent';
Expand All @@ -9,6 +10,17 @@ import AuthStackNavigator from './stack_navigators/AuthStackNavigator';

const Drawer = createDrawerNavigator();

const getActiveRouteName = (state) => {
const route = state.routes[state.index];

if (route.state) {
// Dive into nested navigators
return getActiveRouteName(route.state);
}

return route.name;
};

function DrawerNavigator() {
return (
// eslint-disable-next-line react/jsx-props-no-spreading
Expand All @@ -21,8 +33,26 @@ function DrawerNavigator() {
const AppContainerStack = createStackNavigator();

export default function createAppContainer() {
const routeNameRef = React.useRef();
const navigationRef = React.useRef();

React.useEffect(() => {
const state = navigationRef.current.getRootState();

// Save the initial route name
routeNameRef.current = getActiveRouteName(state);
}, []);
return (
<NavigationContainer>
<NavigationContainer
ref={navigationRef}
onStateChange={(state) => {
const previousRouteName = routeNameRef.current;
const currentRouteName = getActiveRouteName(state);
if (previousRouteName !== currentRouteName) {
Analytics.setCurrentScreen(currentRouteName);
}
routeNameRef.current = currentRouteName;
}}>
<AppContainerStack.Navigator
initialRouteName="Auth"
screenOptions={{ headerShown: false, cardStyle: { backgroundColor: Colors.lightest }, gestureEnabled: false }}>
Expand Down
7 changes: 7 additions & 0 deletions navigation/DrawerContent.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as Analytics from 'expo-firebase-analytics';
import PropTypes from 'prop-types';
import React from 'react';
import { AsyncStorage, Linking, TouchableOpacity, View } from 'react-native';
Expand All @@ -19,6 +20,12 @@ class DrawerContent extends React.Component {

_logout = async () => {
AsyncStorage.clear();
Analytics.logEvent('ClerkLogOut', {
name: 'Log out',
function: '_logout',
component: 'DrawerContent',
});
Analytics.setUserId(null);
this.props.navigation.navigate('Auth');
};

Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
"expo": "^37.0.0",
"expo-asset": "~8.1.4",
"expo-constants": "~9.0.0",
"expo-firebase-analytics": "~2.1.1",
"expo-firebase-core": "^1.0.0",
"expo-font": "~8.1.0",
"expo-screen-orientation": "~1.0.0",
"expo-web-browser": "~8.1.0",
Expand Down Expand Up @@ -97,5 +99,8 @@
"prettier": "^2.0.4",
"yarnhook": "^0.4.3"
},
"private": true
"private": true,
"peerDependencies": {
"@unimodules/core": "^5.1.0"
}
}
20 changes: 20 additions & 0 deletions screens/CheckoutScreen.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as Analytics from 'expo-firebase-analytics';
import PropTypes from 'prop-types';
import React from 'react';
import update from 'react-addons-update';
Expand All @@ -20,6 +21,7 @@ import {
updateCustomerPoints,
} from '../lib/checkoutUtils';
import { checkoutNumCols, productCardPxHeight } from '../lib/constants';
import { logErrorToSentry } from '../lib/logUtils';
import { BottomBar, ProductsContainer, SaleContainer, TabContainer, TopBar } from '../styled/checkout';
import QuantityModal from './modals/QuantityModal';
import RewardModal from './modals/RewardModal';
Expand Down Expand Up @@ -158,6 +160,12 @@ export default class CheckoutScreen extends React.Component {
if (eligibleRewards && transaction.totalSale >= rewardDollarValue) {
const continueWithoutRewards = await this.confirmNoRewards(eligibleRewards);
if (!continueWithoutRewards) {
Analytics.logEvent('GoBackApplyRewards', {
name: 'Selected "Go back to apply rewards"',
function: 'displayConfirmation',
component: 'CheckoutScreen',
eligible_rewards: eligibleRewards,
});
return;
}
}
Expand Down Expand Up @@ -221,6 +229,13 @@ export default class CheckoutScreen extends React.Component {
try {
const transactionId = await addTransaction(this.state.customer, this.state.cart, transaction);
await updateCustomerPoints(this.state.customer, transaction.pointsEarned, transaction.rewardsApplied);
Analytics.logEvent('ConfirmTransaction', {
name: 'Complete sale',
function: 'confirmTransaction',
component: 'CheckoutScreen',
purpose: 'Transaction completed and confirmed.',
transaction: transactionId,
});
this.props.navigation.navigate('Confirmation', { transactionId });
} catch (err) {
// TODO better handling - should prompt the user to try again, or at least say something is wrong with the service
Expand All @@ -230,6 +245,11 @@ export default class CheckoutScreen extends React.Component {
'We were unable to create a transaction in Airtable. Please let an administrator know ASAP so they can fix this issue.',
[{ text: 'OK' }]
);
logErrorToSentry({
screen: 'CheckoutScreen',
action: 'confirmTransaction',
error: err,
});
console.log('Error creating transaction in Airtable', err);
}
};
Expand Down
21 changes: 20 additions & 1 deletion screens/ClerkLoginScreen.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FontAwesome5 } from '@expo/vector-icons';
import * as Analytics from 'expo-firebase-analytics';
import PropTypes from 'prop-types';
import React from 'react';
import { AsyncStorage, View } from 'react-native';
Expand All @@ -9,7 +10,7 @@ import DismissKeyboard from '../components/DismissKeyboard';
import Colors from '../constants/Colors';
import { status } from '../lib/constants';
import { lookupClerk } from '../lib/loginUtils';
import { logAuthErrorToSentry } from '../lib/logUtils';
import { logAuthErrorToSentry, logErrorToSentry } from '../lib/logUtils';
import { CheckInContainer, CheckInContentContainer, TextField } from '../styled/checkin';
import { RowContainer } from '../styled/shared';

Expand Down Expand Up @@ -69,6 +70,19 @@ export default class ClerkLoginScreen extends React.Component {
});
console.log(lookupResult.errorMsg);
} else {
Analytics.setUserId(clerkRecord.id);
Analytics.setUserProperties({
clerk_name: clerkRecord.clerkName,
store: clerkRecord.storeName[0],
});
Analytics.logEvent('ClerkLogin', {
name: 'Successful Clerk login',
function: 'handleSubmit',
component: 'ClerkLoginScreen',
clerk_id: clerkRecord.id,
clerk_name: clerkRecord.clerkName,
store_name: clerkRecord.storeName[0],
});
Sentry.configureScope((scope) => {
scope.setUser({
id: clerkRecord.id,
Expand All @@ -80,6 +94,11 @@ export default class ClerkLoginScreen extends React.Component {
// TODO reset state using onFocusEffect; this can cause memory leaks
this.setState({ errorMsg: lookupResult.errorMsg, password: '', errorShown: clerkNotFound });
} catch (err) {
logErrorToSentry({
screen: 'ClerkLoginScreen',
action: 'handleSubmit',
error: err,
});
console.error('Clerk Login Screen:', err);
}
};
Expand Down
6 changes: 6 additions & 0 deletions screens/ConfirmationScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Body, ButtonLabel, FilledButtonContainer, Subhead, Title } from '../com
import DrawerButton from '../components/DrawerButton';
import { getTransactionsById } from '../lib/airtable/request';
import { displayDollarValue } from '../lib/checkoutUtils';
import { logErrorToSentry } from '../lib/logUtils';
import { ColumnContainer, RowContainer, SpaceBetweenRowContainer } from '../styled/shared';

export default class ConfirmationScreen extends React.Component {
Expand All @@ -30,6 +31,11 @@ export default class ConfirmationScreen extends React.Component {
}
this.setState({ transaction, isLoading: false });
} catch (err) {
logErrorToSentry({
screen: 'Confirmation Screen',
action: 'loadTransactions',
error: err,
});
console.error('Confirmation screen: loading transaction ', err);
}
}
Expand Down
Loading

0 comments on commit c0c9862

Please sign in to comment.