Skip to content

Commit c6176b5

Browse files
aapelivclaude
andcommitted
mobile: move Sentry init to service/sentry.ts; trace on triple-tap
Extract the Sentry.init block out of _layout.tsx into a dedicated side-effect module, and add the build's API and web base URLs to the event scope. Drop the login test button and instead send a debug event to Sentry on the triple-tap Home gesture that already shows the debug toast. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent f4b874c commit c6176b5

4 files changed

Lines changed: 98 additions & 90 deletions

File tree

app/mobile/app/(tabs)/_layout.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as Sentry from "@sentry/react-native";
12
import * as Clipboard from "expo-clipboard";
23
import Constants from "expo-constants";
34
import { Tabs } from "expo-router";
@@ -64,7 +65,13 @@ export default function TabLayout() {
6465
tapTimestamps.current = [];
6566
const info = getDebugInfo();
6667
void Clipboard.setStringAsync(info);
67-
setDebugToast(`${info}\n\nCopied to clipboard`);
68+
// Confirms the device can reach Sentry; the event also carries the
69+
// version/OTA/gitHash tags set in the global scope at init.
70+
Sentry.captureMessage("debug.triple-tap", {
71+
level: "info",
72+
contexts: { debug: { info } },
73+
});
74+
setDebugToast(`${info}\n\nCopied to clipboard & sent to Sentry`);
6875
if (toastTimer.current) clearTimeout(toastTimer.current);
6976
toastTimer.current = setTimeout(() => setDebugToast(null), 6000);
7077
}, []);

app/mobile/app/_layout.tsx

Lines changed: 1 addition & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import "react-native-reanimated";
22
import "@/i18n";
3+
import "@/service/sentry";
34

45
import {
56
Ubuntu_300Light,
@@ -18,13 +19,10 @@ import {
1819
ThemeProvider,
1920
} from "@react-navigation/native";
2021
import * as Sentry from "@sentry/react-native";
21-
import * as Application from "expo-application";
22-
import Constants from "expo-constants";
2322
import * as Notifications from "expo-notifications";
2423
import { Href, router, Stack } from "expo-router";
2524
import * as SplashScreen from "expo-splash-screen";
2625
import { StatusBar } from "expo-status-bar";
27-
import * as Updates from "expo-updates";
2826
import { useEffect, useState } from "react";
2927
import { useColorScheme } from "react-native";
3028
import { SafeAreaProvider } from "react-native-safe-area-context";
@@ -36,78 +34,6 @@ import { useRegisterPushNotifications } from "@/features/notifications/useRegist
3634
import { reconfigureApiClient } from "@/service/client";
3735
import { getNotificationPath } from "@/utils/getNotificationPath";
3836

39-
const extra = Constants.expoConfig?.extra as
40-
| { gitHash?: string; appVariant?: string }
41-
| undefined;
42-
const gitHash = extra?.gitHash ?? "unknown";
43-
const appVariant = extra?.appVariant ?? "unknown";
44-
// The native binary downloaded from the App Store / Play Store. This stays
45-
// fixed across OTA updates, unlike Constants.expoConfig.version which is read
46-
// from the (possibly OTA-updated) JS bundle.
47-
const nativeAppVersion = Application.nativeApplicationVersion ?? "unknown";
48-
const nativeBuildVersion = Application.nativeBuildVersion ?? "unknown";
49-
50-
// Only report from the store-distributed staging and production apps. The dev
51-
// tool (a dev client) and local dev builds would otherwise pollute the project
52-
// with noise from in-progress work.
53-
const sentryEnabled = appVariant === "production" || appVariant === "staging";
54-
55-
if (sentryEnabled) {
56-
Sentry.init({
57-
dsn: "https://7de06aa8cca6dacc9620667dd84a0d01@o782870.ingest.us.sentry.io/4507718344704000",
58-
59-
environment:
60-
process.env.EXPO_PUBLIC_COUCHERS_ENV ??
61-
process.env.NEXT_PUBLIC_COUCHERS_ENV ??
62-
"dev",
63-
// release/dist are intentionally left unset so the @sentry/react-native
64-
// Expo integration derives them from the native build and OTA update,
65-
// matching the values its source-map upload uses. The store-binary and OTA
66-
// identities below are surfaced as tags/contexts (which don't affect
67-
// symbolication) for searching and filtering.
68-
initialScope: {
69-
tags: {
70-
appVariant,
71-
gitHash,
72-
nativeAppVersion,
73-
nativeBuildVersion,
74-
runtimeVersion: Updates.runtimeVersion ?? "unknown",
75-
updateChannel: Updates.channel ?? "none",
76-
launchSource: Updates.isEmbeddedLaunch ? "embedded" : "ota",
77-
},
78-
contexts: {
79-
store_build: {
80-
nativeApplicationVersion: nativeAppVersion,
81-
nativeBuildVersion,
82-
gitHash,
83-
},
84-
ota: {
85-
updateId: Updates.updateId ?? "none",
86-
channel: Updates.channel ?? "none",
87-
runtimeVersion: Updates.runtimeVersion ?? "unknown",
88-
isEmbeddedLaunch: Updates.isEmbeddedLaunch,
89-
createdAt: Updates.createdAt?.toISOString() ?? "unknown",
90-
},
91-
},
92-
},
93-
94-
// Adds more context data to events (IP address, cookies, user, etc.)
95-
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
96-
sendDefaultPii: true,
97-
98-
// Enable Logs
99-
enableLogs: true,
100-
101-
// Configure Session Replay
102-
replaysSessionSampleRate: 0.1,
103-
replaysOnErrorSampleRate: 1,
104-
integrations: [
105-
Sentry.mobileReplayIntegration(),
106-
Sentry.feedbackIntegration(),
107-
],
108-
});
109-
}
110-
11137
// Module-level Set to track handled notification IDs (persists across component remounts)
11238
const handledNotificationIds = new Set<string>();
11339

app/mobile/app/login.tsx

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
11
import { useFocusEffect } from "@react-navigation/native";
2-
import * as Sentry from "@sentry/react-native";
32
import { Href, useRouter } from "expo-router";
43
import { Empty } from "google-protobuf/google/protobuf/empty_pb";
54
import { useCallback, useState } from "react";
6-
import {
7-
Appearance,
8-
BackHandler,
9-
Button,
10-
Linking,
11-
useColorScheme,
12-
} from "react-native";
5+
import { Appearance, BackHandler, Linking, useColorScheme } from "react-native";
136
import { SafeAreaView } from "react-native-safe-area-context";
147
import { WebView } from "react-native-webview";
158

@@ -123,12 +116,6 @@ export default function LoginScreen() {
123116
}
124117
}}
125118
/>
126-
<Button
127-
title="Try!"
128-
onPress={() => {
129-
Sentry.captureException(new Error("First error"));
130-
}}
131-
/>
132119
</SafeAreaView>
133120
);
134121
}

app/mobile/service/sentry.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import * as Sentry from "@sentry/react-native";
2+
import * as Application from "expo-application";
3+
import Constants from "expo-constants";
4+
import * as Updates from "expo-updates";
5+
6+
import { getDefaultApiBaseUrl, getDefaultWebBaseUrl } from "@/config/urls";
7+
8+
const extra = Constants.expoConfig?.extra as
9+
| { gitHash?: string; appVariant?: string }
10+
| undefined;
11+
const gitHash = extra?.gitHash ?? "unknown";
12+
const appVariant = extra?.appVariant ?? "unknown";
13+
// The backend/web the build is wired to. Read the build defaults rather than
14+
// the runtime getters, since Sentry inits before any dev URL override hydrates.
15+
const apiBaseUrl = getDefaultApiBaseUrl();
16+
const webBaseUrl = getDefaultWebBaseUrl();
17+
// The native binary downloaded from the App Store / Play Store. This stays
18+
// fixed across OTA updates, unlike Constants.expoConfig.version which is read
19+
// from the (possibly OTA-updated) JS bundle.
20+
const nativeAppVersion = Application.nativeApplicationVersion ?? "unknown";
21+
const nativeBuildVersion = Application.nativeBuildVersion ?? "unknown";
22+
23+
// Only report from the store-distributed staging and production apps. The dev
24+
// tool (a dev client) and local dev builds would otherwise pollute the project
25+
// with noise from in-progress work.
26+
const sentryEnabled = appVariant === "production" || appVariant === "staging";
27+
28+
if (sentryEnabled) {
29+
Sentry.init({
30+
dsn: "https://7de06aa8cca6dacc9620667dd84a0d01@o782870.ingest.us.sentry.io/4507718344704000",
31+
32+
environment:
33+
process.env.EXPO_PUBLIC_COUCHERS_ENV ??
34+
process.env.NEXT_PUBLIC_COUCHERS_ENV ??
35+
"dev",
36+
// release/dist are intentionally left unset so the @sentry/react-native
37+
// Expo integration derives them from the native build and OTA update,
38+
// matching the values its source-map upload uses. The store-binary and OTA
39+
// identities below are surfaced as tags/contexts (which don't affect
40+
// symbolication) for searching and filtering.
41+
initialScope: {
42+
tags: {
43+
appVariant,
44+
gitHash,
45+
nativeAppVersion,
46+
nativeBuildVersion,
47+
apiBaseUrl,
48+
webBaseUrl,
49+
runtimeVersion: Updates.runtimeVersion ?? "unknown",
50+
updateChannel: Updates.channel ?? "none",
51+
launchSource: Updates.isEmbeddedLaunch ? "embedded" : "ota",
52+
},
53+
contexts: {
54+
config: {
55+
apiBaseUrl,
56+
webBaseUrl,
57+
},
58+
store_build: {
59+
nativeApplicationVersion: nativeAppVersion,
60+
nativeBuildVersion,
61+
gitHash,
62+
},
63+
ota: {
64+
updateId: Updates.updateId ?? "none",
65+
channel: Updates.channel ?? "none",
66+
runtimeVersion: Updates.runtimeVersion ?? "unknown",
67+
isEmbeddedLaunch: Updates.isEmbeddedLaunch,
68+
createdAt: Updates.createdAt?.toISOString() ?? "unknown",
69+
},
70+
},
71+
},
72+
73+
// Adds more context data to events (IP address, cookies, user, etc.)
74+
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
75+
sendDefaultPii: true,
76+
77+
// Enable Logs
78+
enableLogs: true,
79+
80+
// Configure Session Replay
81+
replaysSessionSampleRate: 0.1,
82+
replaysOnErrorSampleRate: 1,
83+
integrations: [
84+
Sentry.mobileReplayIntegration(),
85+
Sentry.feedbackIntegration(),
86+
],
87+
});
88+
}

0 commit comments

Comments
 (0)