diff --git a/App.tsx b/App.tsx
index cd62d5da..e66b9d2c 100644
--- a/App.tsx
+++ b/App.tsx
@@ -1,5 +1,10 @@
import React from 'react';
+// Imported first for its side effect: Sentry.init must run before other modules
+// (store creation, firebase) so their errors are captured.
+// eslint-disable-next-line import/order
+import Sentry from './src/sentry';
+
import analytics from '@react-native-firebase/analytics';
import { StatusBar } from 'expo-status-bar';
import { View } from 'react-native';
@@ -24,7 +29,7 @@ if (process.env.EXPO_PUBLIC_FIREBASE_ANALYTICS == 'false') {
analytics().setAnalyticsCollectionEnabled(false);
}
-export default function App() {
+function App() {
return (
@@ -47,4 +52,6 @@ export default function App() {
);
-};
+}
+
+export default Sentry.wrap(App);
diff --git a/app.config.js b/app.config.js
index 46aa2df1..48902247 100644
--- a/app.config.js
+++ b/app.config.js
@@ -142,5 +142,14 @@ export default {
skipOnboarding: true,
},
],
+ [
+ '@sentry/react-native/expo',
+ {
+ // Source map upload during EAS builds; skipped with a warning unless
+ // SENTRY_AUTH_TOKEN is set in the build environment.
+ organization: process.env.SENTRY_ORG || 'justin-wyne',
+ project: process.env.SENTRY_PROJECT || 'scorepad-react-native',
+ },
+ ],
],
};
diff --git a/eas.json b/eas.json
index 188f1211..efdc9a2b 100644
--- a/eas.json
+++ b/eas.json
@@ -10,7 +10,8 @@
"distribution": "internal",
"env": {
"APP_VARIANT": "development",
- "EXPO_PUBLIC_FIREBASE_ANALYTICS": "false"
+ "EXPO_PUBLIC_FIREBASE_ANALYTICS": "false",
+ "EXPO_PUBLIC_SENTRY_DSN": "https://88dd6d7c83b64ed8870ff21a2a9f1ba7@o1326242.ingest.us.sentry.io/4504710808076288"
}
},
"preview": {
@@ -18,7 +19,8 @@
"channel": "preview",
"env": {
"APP_VARIANT": "preview",
- "EXPO_PUBLIC_FIREBASE_ANALYTICS": "false"
+ "EXPO_PUBLIC_FIREBASE_ANALYTICS": "false",
+ "EXPO_PUBLIC_SENTRY_DSN": "https://88dd6d7c83b64ed8870ff21a2a9f1ba7@o1326242.ingest.us.sentry.io/4504710808076288"
}
},
"production": {
diff --git a/jest.config.ts b/jest.config.ts
index 560b148a..fe480a2e 100644
--- a/jest.config.ts
+++ b/jest.config.ts
@@ -6,7 +6,7 @@ const config: Config = {
'^.+\\.tsx?$': 'babel-jest',
},
transformIgnorePatterns: [
- 'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg|immer|react-redux|@reduxjs/toolkit)'
+ 'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|@sentry/.*|native-base|react-native-svg|immer|react-redux|@reduxjs/toolkit)'
],
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',
testPathIgnorePatterns: ['/e2e/', '/.claude/'],
diff --git a/metro.config.js b/metro.config.js
index e0656ade..7ab08b4d 100644
--- a/metro.config.js
+++ b/metro.config.js
@@ -1,7 +1,9 @@
/* eslint-disable @typescript-eslint/no-require-imports */
-const { getDefaultConfig } = require('@expo/metro-config');
+const { getSentryExpoConfig } = require('@sentry/react-native/metro');
-const defaultConfig = getDefaultConfig(__dirname);
+// Sentry's wrapper of Expo's default config: adds debug ID injection and
+// source map options so stack traces symbolicate in Sentry.
+const defaultConfig = getSentryExpoConfig(__dirname);
// NOTE: do NOT add 'cjs' to assetExts — it prevents Metro from executing
// .cjs files as JavaScript (e.g. superstruct/dist/index.cjs gets treated as
// a binary asset instead of a module).
diff --git a/package-lock.json b/package-lock.json
index 20a04638..2b875e7a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,6 +26,7 @@
"@react-navigation/native": "^7.2.4",
"@react-navigation/native-stack": "^7.15.1",
"@reduxjs/toolkit": "^2.12.0",
+ "@sentry/react-native": "~7.11.0",
"colorsheet": "^1.0.5",
"expo": "^56.0.0",
"expo-application": "~56.0.3",
@@ -5888,6 +5889,310 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@sentry-internal/browser-utils": {
+ "version": "10.37.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.37.0.tgz",
+ "integrity": "sha512-rqdESYaVio9Ktz55lhUhtBsBUCF3wvvJuWia5YqoHDd+egyIfwWxITTAa0TSEyZl7283A4WNHNl0hyeEMblmfA==",
+ "license": "MIT",
+ "dependencies": {
+ "@sentry/core": "10.37.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@sentry-internal/feedback": {
+ "version": "10.37.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.37.0.tgz",
+ "integrity": "sha512-P0PVlfrDvfvCYg2KPIS7YUG/4i6ZPf8z1MicXx09C9Cz9W9UhSBh/nii13eBdDtLav2BFMKhvaFMcghXHX03Hw==",
+ "license": "MIT",
+ "dependencies": {
+ "@sentry/core": "10.37.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@sentry-internal/replay": {
+ "version": "10.37.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.37.0.tgz",
+ "integrity": "sha512-snuk12ZaDerxesSnetNIwKoth/51R0y/h3eXD/bGtXp+hnSkeXN5HanI/RJl297llRjn4zJYRShW9Nx86Ay0Dw==",
+ "license": "MIT",
+ "dependencies": {
+ "@sentry-internal/browser-utils": "10.37.0",
+ "@sentry/core": "10.37.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@sentry-internal/replay-canvas": {
+ "version": "10.37.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.37.0.tgz",
+ "integrity": "sha512-PyIYSbjLs+L5essYV0MyIsh4n5xfv2eV7l0nhUoPJv9Bak3kattQY3tholOj0EP3SgKgb+8HSZnmazgF++Hbog==",
+ "license": "MIT",
+ "dependencies": {
+ "@sentry-internal/replay": "10.37.0",
+ "@sentry/core": "10.37.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@sentry/babel-plugin-component-annotate": {
+ "version": "4.8.0",
+ "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-4.8.0.tgz",
+ "integrity": "sha512-cy/9Eipkv23MsEJ4IuB4dNlVwS9UqOzI3Eu+QPake5BVFgPYCX0uP0Tr3Z43Ime6Rb+BiDnWC51AJK9i9afHYw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/@sentry/browser": {
+ "version": "10.37.0",
+ "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.37.0.tgz",
+ "integrity": "sha512-kheqJNqGZP5TSBCPv4Vienv1sfZwXKHQDYR+xrdHHYdZqwWuZMJJW/cLO9XjYAe+B9NnJ4UwJOoY4fPvU+HQ1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@sentry-internal/browser-utils": "10.37.0",
+ "@sentry-internal/feedback": "10.37.0",
+ "@sentry-internal/replay": "10.37.0",
+ "@sentry-internal/replay-canvas": "10.37.0",
+ "@sentry/core": "10.37.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@sentry/cli": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.58.4.tgz",
+ "integrity": "sha512-ArDrpuS8JtDYEvwGleVE+FgR+qHaOp77IgdGSacz6SZy6Lv90uX0Nu4UrHCQJz8/xwIcNxSqnN22lq0dH4IqTg==",
+ "hasInstallScript": true,
+ "license": "FSL-1.1-MIT",
+ "dependencies": {
+ "https-proxy-agent": "^5.0.0",
+ "node-fetch": "^2.6.7",
+ "progress": "^2.0.3",
+ "proxy-from-env": "^1.1.0",
+ "which": "^2.0.2"
+ },
+ "bin": {
+ "sentry-cli": "bin/sentry-cli"
+ },
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@sentry/cli-darwin": "2.58.4",
+ "@sentry/cli-linux-arm": "2.58.4",
+ "@sentry/cli-linux-arm64": "2.58.4",
+ "@sentry/cli-linux-i686": "2.58.4",
+ "@sentry/cli-linux-x64": "2.58.4",
+ "@sentry/cli-win32-arm64": "2.58.4",
+ "@sentry/cli-win32-i686": "2.58.4",
+ "@sentry/cli-win32-x64": "2.58.4"
+ }
+ },
+ "node_modules/@sentry/cli-darwin": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.58.4.tgz",
+ "integrity": "sha512-kbTD+P4X8O+nsNwPxCywtj3q22ecyRHWff98rdcmtRrvwz8CKi/T4Jxn/fnn2i4VEchy08OWBuZAqaA5Kh2hRQ==",
+ "license": "FSL-1.1-MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@sentry/cli-linux-arm": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.58.4.tgz",
+ "integrity": "sha512-rdQ8beTwnN48hv7iV7e7ZKucPec5NJkRdrrycMJMZlzGBPi56LqnclgsHySJ6Kfq506A2MNuQnKGaf/sBC9REA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "FSL-1.1-MIT",
+ "optional": true,
+ "os": [
+ "linux",
+ "freebsd",
+ "android"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@sentry/cli-linux-arm64": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.58.4.tgz",
+ "integrity": "sha512-0g0KwsOozkLtzN8/0+oMZoOuQ0o7W6O+hx+ydVU1bktaMGKEJLMAWxOQNjsh1TcBbNIXVOKM/I8l0ROhaAb8Ig==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "FSL-1.1-MIT",
+ "optional": true,
+ "os": [
+ "linux",
+ "freebsd",
+ "android"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@sentry/cli-linux-i686": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.58.4.tgz",
+ "integrity": "sha512-NseoIQAFtkziHyjZNPTu1Gm1opeQHt7Wm1LbLrGWVIRvUOzlslO9/8i6wETUZ6TjlQxBVRgd3Q0lRBG2A8rFYA==",
+ "cpu": [
+ "x86",
+ "ia32"
+ ],
+ "license": "FSL-1.1-MIT",
+ "optional": true,
+ "os": [
+ "linux",
+ "freebsd",
+ "android"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@sentry/cli-linux-x64": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.58.4.tgz",
+ "integrity": "sha512-d3Arz+OO/wJYTqCYlSN3Ktm+W8rynQ/IMtSZLK8nu0ryh5mJOh+9XlXY6oDXw4YlsM8qCRrNquR8iEI1Y/IH+Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "FSL-1.1-MIT",
+ "optional": true,
+ "os": [
+ "linux",
+ "freebsd",
+ "android"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@sentry/cli-win32-arm64": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.58.4.tgz",
+ "integrity": "sha512-bqYrF43+jXdDBh0f8HIJU3tbvlOFtGyRjHB8AoRuMQv9TEDUfENZyCelhdjA+KwDKYl48R1Yasb4EHNzsoO83w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "FSL-1.1-MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@sentry/cli-win32-i686": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.58.4.tgz",
+ "integrity": "sha512-3triFD6jyvhVcXOmGyttf+deKZcC1tURdhnmDUIBkiDPJKGT/N5xa4qAtHJlAB/h8L9jgYih9bvJnvvFVM7yug==",
+ "cpu": [
+ "x86",
+ "ia32"
+ ],
+ "license": "FSL-1.1-MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@sentry/cli-win32-x64": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.58.4.tgz",
+ "integrity": "sha512-cSzN4PjM1RsCZ4pxMjI0VI7yNCkxiJ5jmWncyiwHXGiXrV1eXYdQ3n1LhUYLZ91CafyprR0OhDcE+RVZ26Qb5w==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "FSL-1.1-MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@sentry/core": {
+ "version": "10.37.0",
+ "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.37.0.tgz",
+ "integrity": "sha512-hkRz7S4gkKLgPf+p3XgVjVm7tAfvcEPZxeACCC6jmoeKhGkzN44nXwLiqqshJ25RMcSrhfFvJa/FlBg6zupz7g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@sentry/react": {
+ "version": "10.37.0",
+ "resolved": "https://registry.npmjs.org/@sentry/react/-/react-10.37.0.tgz",
+ "integrity": "sha512-XLnXJOHgsCeVAVBbO+9AuGlZWnCxLQHLOmKxpIr8wjE3g7dHibtug6cv8JLx78O4dd7aoCqv2TTyyKY9FLJ2EQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@sentry/browser": "10.37.0",
+ "@sentry/core": "10.37.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "react": "^16.14.0 || 17.x || 18.x || 19.x"
+ }
+ },
+ "node_modules/@sentry/react-native": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/@sentry/react-native/-/react-native-7.11.0.tgz",
+ "integrity": "sha512-OiDaLCAGpRN18YG/o7IIwLhU0Xpb0tYKQ5QxkGHiwb+L3VHn+MqGCGfITYNdhqr06HHMvu9Lysm+UJxaNmGaJg==",
+ "license": "MIT",
+ "dependencies": {
+ "@sentry/babel-plugin-component-annotate": "4.8.0",
+ "@sentry/browser": "10.37.0",
+ "@sentry/cli": "2.58.4",
+ "@sentry/core": "10.37.0",
+ "@sentry/react": "10.37.0",
+ "@sentry/types": "10.37.0"
+ },
+ "bin": {
+ "sentry-expo-upload-sourcemaps": "scripts/expo-upload-sourcemaps.js"
+ },
+ "peerDependencies": {
+ "expo": ">=49.0.0",
+ "react": ">=17.0.0",
+ "react-native": ">=0.65.0"
+ },
+ "peerDependenciesMeta": {
+ "expo": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@sentry/types": {
+ "version": "10.37.0",
+ "resolved": "https://registry.npmjs.org/@sentry/types/-/types-10.37.0.tgz",
+ "integrity": "sha512-umpnUKRC0AAbJrADg6SlFtqN2yzf7NHciCF9lkHau+ax2PIZ/NDmoG4RQujFVflVaVoD60Ly2t+CcPnYIWMPlw==",
+ "license": "MIT",
+ "dependencies": {
+ "@sentry/core": "10.37.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@sinclair/typebox": {
"version": "0.27.8",
"license": "MIT"
@@ -7234,7 +7539,6 @@
},
"node_modules/agent-base": {
"version": "6.0.2",
- "dev": true,
"license": "MIT",
"dependencies": {
"debug": "4"
@@ -12720,7 +13024,6 @@
},
"node_modules/https-proxy-agent": {
"version": "5.0.1",
- "dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "6",
@@ -17411,7 +17714,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
- "dev": true,
"license": "MIT"
},
"node_modules/psl": {
@@ -24918,6 +25220,160 @@
"integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==",
"dev": true
},
+ "@sentry-internal/browser-utils": {
+ "version": "10.37.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.37.0.tgz",
+ "integrity": "sha512-rqdESYaVio9Ktz55lhUhtBsBUCF3wvvJuWia5YqoHDd+egyIfwWxITTAa0TSEyZl7283A4WNHNl0hyeEMblmfA==",
+ "requires": {
+ "@sentry/core": "10.37.0"
+ }
+ },
+ "@sentry-internal/feedback": {
+ "version": "10.37.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.37.0.tgz",
+ "integrity": "sha512-P0PVlfrDvfvCYg2KPIS7YUG/4i6ZPf8z1MicXx09C9Cz9W9UhSBh/nii13eBdDtLav2BFMKhvaFMcghXHX03Hw==",
+ "requires": {
+ "@sentry/core": "10.37.0"
+ }
+ },
+ "@sentry-internal/replay": {
+ "version": "10.37.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.37.0.tgz",
+ "integrity": "sha512-snuk12ZaDerxesSnetNIwKoth/51R0y/h3eXD/bGtXp+hnSkeXN5HanI/RJl297llRjn4zJYRShW9Nx86Ay0Dw==",
+ "requires": {
+ "@sentry-internal/browser-utils": "10.37.0",
+ "@sentry/core": "10.37.0"
+ }
+ },
+ "@sentry-internal/replay-canvas": {
+ "version": "10.37.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.37.0.tgz",
+ "integrity": "sha512-PyIYSbjLs+L5essYV0MyIsh4n5xfv2eV7l0nhUoPJv9Bak3kattQY3tholOj0EP3SgKgb+8HSZnmazgF++Hbog==",
+ "requires": {
+ "@sentry-internal/replay": "10.37.0",
+ "@sentry/core": "10.37.0"
+ }
+ },
+ "@sentry/babel-plugin-component-annotate": {
+ "version": "4.8.0",
+ "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-4.8.0.tgz",
+ "integrity": "sha512-cy/9Eipkv23MsEJ4IuB4dNlVwS9UqOzI3Eu+QPake5BVFgPYCX0uP0Tr3Z43Ime6Rb+BiDnWC51AJK9i9afHYw=="
+ },
+ "@sentry/browser": {
+ "version": "10.37.0",
+ "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.37.0.tgz",
+ "integrity": "sha512-kheqJNqGZP5TSBCPv4Vienv1sfZwXKHQDYR+xrdHHYdZqwWuZMJJW/cLO9XjYAe+B9NnJ4UwJOoY4fPvU+HQ1Q==",
+ "requires": {
+ "@sentry-internal/browser-utils": "10.37.0",
+ "@sentry-internal/feedback": "10.37.0",
+ "@sentry-internal/replay": "10.37.0",
+ "@sentry-internal/replay-canvas": "10.37.0",
+ "@sentry/core": "10.37.0"
+ }
+ },
+ "@sentry/cli": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.58.4.tgz",
+ "integrity": "sha512-ArDrpuS8JtDYEvwGleVE+FgR+qHaOp77IgdGSacz6SZy6Lv90uX0Nu4UrHCQJz8/xwIcNxSqnN22lq0dH4IqTg==",
+ "requires": {
+ "@sentry/cli-darwin": "2.58.4",
+ "@sentry/cli-linux-arm": "2.58.4",
+ "@sentry/cli-linux-arm64": "2.58.4",
+ "@sentry/cli-linux-i686": "2.58.4",
+ "@sentry/cli-linux-x64": "2.58.4",
+ "@sentry/cli-win32-arm64": "2.58.4",
+ "@sentry/cli-win32-i686": "2.58.4",
+ "@sentry/cli-win32-x64": "2.58.4",
+ "https-proxy-agent": "^5.0.0",
+ "node-fetch": "^2.6.7",
+ "progress": "^2.0.3",
+ "proxy-from-env": "^1.1.0",
+ "which": "^2.0.2"
+ }
+ },
+ "@sentry/cli-darwin": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.58.4.tgz",
+ "integrity": "sha512-kbTD+P4X8O+nsNwPxCywtj3q22ecyRHWff98rdcmtRrvwz8CKi/T4Jxn/fnn2i4VEchy08OWBuZAqaA5Kh2hRQ==",
+ "optional": true
+ },
+ "@sentry/cli-linux-arm": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.58.4.tgz",
+ "integrity": "sha512-rdQ8beTwnN48hv7iV7e7ZKucPec5NJkRdrrycMJMZlzGBPi56LqnclgsHySJ6Kfq506A2MNuQnKGaf/sBC9REA==",
+ "optional": true
+ },
+ "@sentry/cli-linux-arm64": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.58.4.tgz",
+ "integrity": "sha512-0g0KwsOozkLtzN8/0+oMZoOuQ0o7W6O+hx+ydVU1bktaMGKEJLMAWxOQNjsh1TcBbNIXVOKM/I8l0ROhaAb8Ig==",
+ "optional": true
+ },
+ "@sentry/cli-linux-i686": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.58.4.tgz",
+ "integrity": "sha512-NseoIQAFtkziHyjZNPTu1Gm1opeQHt7Wm1LbLrGWVIRvUOzlslO9/8i6wETUZ6TjlQxBVRgd3Q0lRBG2A8rFYA==",
+ "optional": true
+ },
+ "@sentry/cli-linux-x64": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.58.4.tgz",
+ "integrity": "sha512-d3Arz+OO/wJYTqCYlSN3Ktm+W8rynQ/IMtSZLK8nu0ryh5mJOh+9XlXY6oDXw4YlsM8qCRrNquR8iEI1Y/IH+Q==",
+ "optional": true
+ },
+ "@sentry/cli-win32-arm64": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.58.4.tgz",
+ "integrity": "sha512-bqYrF43+jXdDBh0f8HIJU3tbvlOFtGyRjHB8AoRuMQv9TEDUfENZyCelhdjA+KwDKYl48R1Yasb4EHNzsoO83w==",
+ "optional": true
+ },
+ "@sentry/cli-win32-i686": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.58.4.tgz",
+ "integrity": "sha512-3triFD6jyvhVcXOmGyttf+deKZcC1tURdhnmDUIBkiDPJKGT/N5xa4qAtHJlAB/h8L9jgYih9bvJnvvFVM7yug==",
+ "optional": true
+ },
+ "@sentry/cli-win32-x64": {
+ "version": "2.58.4",
+ "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.58.4.tgz",
+ "integrity": "sha512-cSzN4PjM1RsCZ4pxMjI0VI7yNCkxiJ5jmWncyiwHXGiXrV1eXYdQ3n1LhUYLZ91CafyprR0OhDcE+RVZ26Qb5w==",
+ "optional": true
+ },
+ "@sentry/core": {
+ "version": "10.37.0",
+ "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.37.0.tgz",
+ "integrity": "sha512-hkRz7S4gkKLgPf+p3XgVjVm7tAfvcEPZxeACCC6jmoeKhGkzN44nXwLiqqshJ25RMcSrhfFvJa/FlBg6zupz7g=="
+ },
+ "@sentry/react": {
+ "version": "10.37.0",
+ "resolved": "https://registry.npmjs.org/@sentry/react/-/react-10.37.0.tgz",
+ "integrity": "sha512-XLnXJOHgsCeVAVBbO+9AuGlZWnCxLQHLOmKxpIr8wjE3g7dHibtug6cv8JLx78O4dd7aoCqv2TTyyKY9FLJ2EQ==",
+ "requires": {
+ "@sentry/browser": "10.37.0",
+ "@sentry/core": "10.37.0"
+ }
+ },
+ "@sentry/react-native": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/@sentry/react-native/-/react-native-7.11.0.tgz",
+ "integrity": "sha512-OiDaLCAGpRN18YG/o7IIwLhU0Xpb0tYKQ5QxkGHiwb+L3VHn+MqGCGfITYNdhqr06HHMvu9Lysm+UJxaNmGaJg==",
+ "requires": {
+ "@sentry/babel-plugin-component-annotate": "4.8.0",
+ "@sentry/browser": "10.37.0",
+ "@sentry/cli": "2.58.4",
+ "@sentry/core": "10.37.0",
+ "@sentry/react": "10.37.0",
+ "@sentry/types": "10.37.0"
+ }
+ },
+ "@sentry/types": {
+ "version": "10.37.0",
+ "resolved": "https://registry.npmjs.org/@sentry/types/-/types-10.37.0.tgz",
+ "integrity": "sha512-umpnUKRC0AAbJrADg6SlFtqN2yzf7NHciCF9lkHau+ax2PIZ/NDmoG4RQujFVflVaVoD60Ly2t+CcPnYIWMPlw==",
+ "requires": {
+ "@sentry/core": "10.37.0"
+ }
+ },
"@sinclair/typebox": {
"version": "0.27.8"
},
@@ -25847,7 +26303,6 @@
},
"agent-base": {
"version": "6.0.2",
- "dev": true,
"requires": {
"debug": "4"
}
@@ -29338,7 +29793,6 @@
},
"https-proxy-agent": {
"version": "5.0.1",
- "dev": true,
"requires": {
"agent-base": "6",
"debug": "4"
@@ -32314,8 +32768,7 @@
"proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
- "dev": true
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"psl": {
"version": "1.9.0",
diff --git a/package.json b/package.json
index 4c05db47..e259ad93 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,7 @@
"@react-navigation/native": "^7.2.4",
"@react-navigation/native-stack": "^7.15.1",
"@reduxjs/toolkit": "^2.12.0",
+ "@sentry/react-native": "~7.11.0",
"colorsheet": "^1.0.5",
"expo": "^56.0.0",
"expo-application": "~56.0.3",
diff --git a/src/Navigation.tsx b/src/Navigation.tsx
index 4957c7d3..16dc8243 100644
--- a/src/Navigation.tsx
+++ b/src/Navigation.tsx
@@ -1,6 +1,6 @@
import React, { useState } from 'react';
-import { DarkTheme, DefaultTheme, NavigationContainer } from '@react-navigation/native';
+import { DarkTheme, DefaultTheme, NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { Platform, View } from 'react-native';
@@ -19,6 +19,7 @@ import GameSheet from './components/Sheets/GameSheet';
import { GestureInfoSheetContextProvider } from './components/Sheets/GestureInfoSheetContext';
import EditPlayerScreen from './screens/EditPlayerScreen';
import ShareScreen from './screens/ShareScreen';
+import { navigationIntegration } from './sentry';
import { useTheme } from './theme';
export type RootStackParamList = {
@@ -52,11 +53,14 @@ export const Navigation = () => {
const fullscreen = useAppSelector(state => state.settings.home_fullscreen);
const [showGameSheet, setShowGameSheet] = useState(false);
+ const navigationRef = useNavigationContainerRef();
return (
navigationIntegration.registerNavigationContainer(navigationRef)}
>
diff --git a/src/sentry.ts b/src/sentry.ts
new file mode 100644
index 00000000..43a6c3ee
--- /dev/null
+++ b/src/sentry.ts
@@ -0,0 +1,37 @@
+import * as Sentry from '@sentry/react-native';
+import * as Application from 'expo-application';
+
+// DSN is not a secret; it identifies the Sentry project for event ingestion.
+// Presence of the DSN is the on/off switch: set EXPO_PUBLIC_SENTRY_DSN in EAS
+// environment variables for builds, or in a local .env (gitignored) to test
+// from a dev client. With no DSN, the SDK stays fully disabled.
+const SENTRY_DSN = process.env.EXPO_PUBLIC_SENTRY_DSN;
+
+// One Sentry project for all variants; the environment tag separates
+// dev/preview/production events. Derived from the bundle id suffix
+// (com.wyne.scorepad[.dev|.preview]) so no extra env plumbing is needed.
+const appId = Application.applicationId ?? '';
+const environment = __DEV__ || appId.endsWith('.dev')
+ ? 'development'
+ : appId.endsWith('.preview')
+ ? 'preview'
+ : 'production';
+
+export const navigationIntegration = Sentry.reactNavigationIntegration({
+ enableTimeToInitialDisplay: true,
+});
+
+Sentry.init({
+ dsn: SENTRY_DSN,
+ enabled: Boolean(SENTRY_DSN),
+ environment,
+ // Low event volume app: trace every session so slow/frozen frame and
+ // screen-load regressions are visible per release.
+ tracesSampleRate: 1.0,
+ integrations: [navigationIntegration],
+ enableNativeFramesTracking: true,
+ // Release health: track session adoption/crash-free rate per version.
+ enableAutoSessionTracking: true,
+});
+
+export default Sentry;