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 12 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',
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) {
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 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: 'ClerkLoginScreen',
wangannie marked this conversation as resolved.
Show resolved Hide resolved
action: 'confirmTransaction',
error: err,
});
console.log('Error creating transaction in Airtable', err);
}
};
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[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],
Copy link
Contributor

Choose a reason for hiding this comment

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

Out of curiosity why did this fix it? Do lookup type records always return arrays from Airtable :o I was not aware

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 apparently they do return arrays. Not sure if there's a better way to do this, since clerks should only ever be connected to a single store?

});
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',
component: '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',
component: 'StoreLookupScreen',
});
await AsyncStorage.setItem('trainingMode', JSON.stringify(true));
} else {
await AsyncStorage.setItem('trainingMode', JSON.stringify(false));
Expand Down
Loading