Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Firebase Analytics + Sentry error logging #54

Merged
merged 16 commits into from
May 11, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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',
screen: '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"
}
}
14 changes: 14 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 Down Expand Up @@ -158,6 +159,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',
screen: 'CheckoutScreen',
eligible_rewards: eligibleRewards,
});
return;
}
}
Expand Down Expand Up @@ -221,6 +228,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',
screen: 'CheckoutScreen',
purpose: 'Transaction completed and confirmed.',
transaction: transactionId,
});
this.props.navigation.navigate('Confirmation', { transactionId });
} catch (err) {
wangannie marked this conversation as resolved.
Show resolved Hide resolved
// TODO better handling - should prompt the user to try again, or at least say something is wrong with the service
Expand Down
14 changes: 14 additions & 0 deletions 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 Down Expand Up @@ -82,6 +83,19 @@ export default class ClerkLoginScreen extends React.Component {
});
console.log(errMsg);
} else {
Analytics.setUserId(clerkRecord.id);
Analytics.setUserProperties({
clerk_name: clerkRecord.clerkName,
store: clerkRecord.storeName,
});
Analytics.logEvent('ClerkLogin', {
name: 'Successful Clerk login',
function: 'handleSubmit',
screen: 'ClerkLoginScreen',
clerk_id: clerkRecord.id,
clerk_name: clerkRecord.clerkName,
store_name: clerkRecord.storeName,
});
Sentry.configureScope((scope) => {
scope.setUser({
id: clerkRecord.id,
Expand Down
8 changes: 8 additions & 0 deletions screens/CustomerLookupScreen.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 Down Expand Up @@ -41,6 +42,13 @@ export default class CustomerLookupScreen extends React.Component {

_asyncCustomerFound = async (customerRecord) => {
await AsyncStorage.setItem('customerId', customerRecord.id);
await Analytics.logEvent('CustomerFound', {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

await used here

name: 'Customer lookup successful',
function: '_asyncCustomerFound',
screen: 'CustomerLookupScreen',
customer_id: customerRecord.id,
customer_name: customerRecord.name,
});
this.props.navigation.navigate('Checkout');
};

Expand Down
6 changes: 6 additions & 0 deletions screens/StoreLookupScreen.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, Button, Keyboard } from 'react-native';
Expand Down Expand Up @@ -69,6 +70,11 @@ export default class StoreLookupScreen extends React.Component {
handleNavigate = async () => {
// Clerk training: set `trainingMode` to "true" in AsyncStorage
if (this.state.store.storeName === 'CLERK TRAINING') {
await Analytics.logEvent('SelectTrainingMode', {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Analytics.logEvent returns a promise? Googled a bit and it doesn't seem like it. If it does, we'll have to add try/catch blocks because that means there are unhandled promise rejections. Otherwise, using await here is a bit misleading

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah you're right. I removed the awaits

name: 'Select Clerk Training mode',
function: 'handleNavigate',
screen: 'StoreLookupScreen',
});
await AsyncStorage.setItem('trainingMode', JSON.stringify(true));
} else {
await AsyncStorage.setItem('trainingMode', JSON.stringify(false));
Expand Down
26 changes: 26 additions & 0 deletions screens/modals/QuantityModal.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 { Modal, TouchableOpacity, View } from 'react-native';
Expand Down Expand Up @@ -68,6 +69,13 @@ export default class QuantityModal extends React.Component {
const currentQuantityInt = parseInt(this.state.currentQuantity, 10);
const priceDifference = (currentQuantityInt - initialQuantity) * this.state.product.customerCost;
this.props.callback(this.state.product, currentQuantityInt, priceDifference);
Analytics.logEvent('ConfirmQuantity', {
name: 'Apply product quantity',
function: 'handleUpdateCart',
screen: 'QuantityModal',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be confusing to have screen as an event property since screen_name is captured with the Analytics too. Perhaps component?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! Thanks Tommy

product: this.state.product.fullName,
quantity: currentQuantityInt,
});
this.setModalVisible(!this.state.modalVisible);
};

Expand Down Expand Up @@ -140,6 +148,15 @@ export default class QuantityModal extends React.Component {
<TouchableOpacity
onPress={() => {
this.setModalVisible(true);
Analytics.logEvent('OpenQuantityModal', {
name: 'Open quantity modal',
function: 'onPress',
screen: 'QuantityModal',
source: 'LineItemCard',
action: product.quantity > 0 ? 'update' : 'add_new',
product: product.fullName,
product_id: product.id,
});
}}>
<LineItemCard product={product} />
</TouchableOpacity>
Expand All @@ -148,6 +165,15 @@ export default class QuantityModal extends React.Component {
<TouchableOpacity
onPress={() => {
this.setModalVisible(true);
Analytics.logEvent('OpenQuantityModal', {
name: 'Open quantity modal',
function: 'onPress',
screen: 'QuantityModal',
source: 'ProductDisplayCard',
action: product.quantity > 0 ? 'update' : 'add_new',
product: product.fullName,
product_id: product.id,
});
}}>
<ProductDisplayCard product={product} />
</TouchableOpacity>
Expand Down
Loading