diff --git a/app/App.tsx b/app/App.tsx
index 12fe27516..98bd2d896 100644
--- a/app/App.tsx
+++ b/app/App.tsx
@@ -4,6 +4,7 @@ import { Buffer } from 'buffer';
import React from 'react';
import { YStack } from 'tamagui';
+import ErrorBoundary from './src/components/ErrorBoundary';
import AppNavigation from './src/navigation';
import { initSentry, wrapWithSentry } from './src/Sentry';
import { AuthProvider } from './src/stores/authProvider';
@@ -16,15 +17,17 @@ global.Buffer = Buffer;
function App(): React.JSX.Element {
return (
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/app/ios/OpenPassport/AppDelegate.mm b/app/ios/OpenPassport/AppDelegate.mm
index fd3d91a5c..ef53149e0 100644
--- a/app/ios/OpenPassport/AppDelegate.mm
+++ b/app/ios/OpenPassport/AppDelegate.mm
@@ -5,6 +5,7 @@
#import
#import
#import
+#import
@implementation AppDelegate
@@ -82,4 +83,13 @@ - (NSString *)stringFromDeviceToken:(NSData *)deviceToken
return [tokenString copy];
}
+// for segment deep link tracking
+- (BOOL)application:(UIApplication *)application
+ openURL: (NSURL *)url
+ options:(nonnull NSDictionary *)options {
+
+ [AnalyticsReactNative trackDeepLink:url withOptions:options];
+ return YES;
+}
+
@end
diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock
index 1845ddb38..dd1f38650 100644
--- a/app/ios/Podfile.lock
+++ b/app/ios/Podfile.lock
@@ -1804,7 +1804,7 @@ PODS:
- Yoga
- RNSVG (15.11.1):
- React-Core
- - segment-analytics-react-native (2.20.3):
+ - segment-analytics-react-native (2.21.0):
- React-Core
- sovran-react-native
- Sentry (8.50.2):
@@ -2231,7 +2231,7 @@ SPEC CHECKSUMS:
RNScreens: b7e8d29c6be98f478bc3fb4a97cc770aa9ba7509
RNSentry: c462461c0a5aaba206265f1f3db01b237cd33239
RNSVG: 46769c92d1609e617dbf9326ad8a0cff912d0982
- segment-analytics-react-native: 6f98edf18246782ee7428c5380c6519a3d2acf5e
+ segment-analytics-react-native: dab2ae61d917629b8d040b572f9f7a11b2cc5940
Sentry: 1ca8405451040482877dcd344dfa3ef80b646631
SentryPrivate: d651efb234cf385ec9a1cdd3eff94b5e78a0e0fe
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
diff --git a/app/package.json b/app/package.json
index 512fb0d6a..fd4fa06bd 100644
--- a/app/package.json
+++ b/app/package.json
@@ -55,7 +55,7 @@
"@react-native-google-signin/google-signin": "^13.1.0",
"@react-navigation/native": "^7.0.14",
"@react-navigation/native-stack": "^7.2.0",
- "@segment/analytics-react-native": "^2.20.3",
+ "@segment/analytics-react-native": "^2.21.0",
"@segment/sovran-react-native": "^1.1.3",
"@sentry/react-native": "^6.10.0",
"@stablelib/cbor": "^2.0.1",
diff --git a/app/src/Segment.ts b/app/src/Segment.ts
index 21fe64b31..2f20e6714 100644
--- a/app/src/Segment.ts
+++ b/app/src/Segment.ts
@@ -2,10 +2,12 @@ import '@ethersproject/shims';
import { SEGMENT_KEY } from '@env';
import {
+ BackgroundFlushPolicy,
createClient,
EventPlugin,
PluginType,
SegmentEvent,
+ StartupFlushPolicy,
} from '@segment/analytics-react-native';
let segmentClient: ReturnType | null = null;
@@ -43,10 +45,13 @@ export const createSegmentClient = () => {
return segmentClient;
}
+ const flushPolicies = [new StartupFlushPolicy(), new BackgroundFlushPolicy()];
+
const client = createClient({
writeKey: SEGMENT_KEY,
trackAppLifecycleEvents: true,
- debug: true,
+ trackDeepLinks: true,
+ debug: __DEV__,
collectDeviceId: false,
defaultSettings: {
integrations: {
@@ -55,6 +60,7 @@ export const createSegmentClient = () => {
},
},
},
+ flushPolicies,
});
client.add({ plugin: new DisableTrackingPlugin() });
diff --git a/app/src/components/ErrorBoundary.tsx b/app/src/components/ErrorBoundary.tsx
new file mode 100644
index 000000000..c8b502923
--- /dev/null
+++ b/app/src/components/ErrorBoundary.tsx
@@ -0,0 +1,46 @@
+import React from 'react';
+import { Text, View } from 'react-native';
+
+import analytics from '../utils/analytics';
+
+const { flush: flushAnalytics } = analytics();
+
+interface Props {
+ children: React.ReactNode;
+}
+
+interface State {
+ hasError: boolean;
+}
+
+class ErrorBoundary extends React.Component {
+ constructor(props: Props) {
+ super(props);
+ this.state = { hasError: false };
+ }
+
+ static getDerivedStateFromError(_: Error): State {
+ return { hasError: true };
+ }
+
+ componentDidCatch() {
+ // Flush analytics before the app crashes
+ flushAnalytics();
+ }
+
+ render() {
+ if (this.state.hasError) {
+ return (
+
+ Something went wrong. Please restart the app.
+
+ );
+ }
+
+ return this.props.children;
+ }
+}
+
+export default ErrorBoundary;
diff --git a/app/src/components/buttons/AbstractButton.tsx b/app/src/components/buttons/AbstractButton.tsx
index 49c158f85..d5bd4d5db 100644
--- a/app/src/components/buttons/AbstractButton.tsx
+++ b/app/src/components/buttons/AbstractButton.tsx
@@ -3,20 +3,25 @@ import { StyleSheet, ViewStyle } from 'react-native';
import { Button, Text, ViewProps } from 'tamagui';
import { shouldShowAesopRedesign } from '../../hooks/useAesopRedesign';
+import analytics from '../../utils/analytics';
import { dinot } from '../../utils/fonts';
import { pressedStyle } from './pressedStyle';
export interface ButtonProps extends ViewProps {
children: React.ReactNode;
animatedComponent?: React.ReactNode;
+ trackEvent?: string;
}
interface AbstractButtonProps extends ButtonProps {
bgColor: string;
borderColor?: string;
color: string;
+ onPress?: ((e: any) => void) | null | undefined;
}
+const { trackEvent: analyticsTrackEvent } = analytics();
+
/*
Base Button component that can be used to create different types of buttons
use PrimaryButton and SecondaryButton instead of this component or create a new button component
@@ -30,13 +35,28 @@ export default function AbstractButton({
borderColor,
style,
animatedComponent,
+ trackEvent,
+ onPress,
...props
}: AbstractButtonProps) {
const hasBorder = borderColor ? true : false;
+
+ const handlePress = (e: any) => {
+ if (trackEvent) {
+ // attempt to hide the event category
+ trackEvent = trackEvent.split(':')[1].trim() ?? trackEvent;
+ analyticsTrackEvent(`Click: ${trackEvent}`);
+ }
+ if (onPress) {
+ onPress(e);
+ }
+ };
+
return (