11import * as React from 'react' ;
22import { Logger , setConnected } from '@maplibre/maplibre-react-native' ;
3- import { getLocales } from 'expo-localization' ;
43
54// Maplibre logs when tile requests are cancelled, which is often.
65// this turns off the unneccessary noise in the console logs
@@ -39,17 +38,19 @@ import * as SplashScreen from 'expo-splash-screen';
3938import * as Sentry from '@sentry/react-native' ;
4039import * as TaskManager from 'expo-task-manager' ;
4140import { LOCATION_TASK_NAME , LocationCallbackInfo } from './sharedTypes/location' ;
42- import { initSentry } from '@comapeo/core-react-native/sentry' ;
43- import { AppDiagnosticMetrics } from './metrics/AppDiagnosticMetrics' ;
44- import { DeviceDiagnosticMetrics } from './metrics/DeviceDiagnosticMetrics' ;
41+ import {
42+ initSentry ,
43+ setApplicationUsageData ,
44+ getDiagnosticsEnabled ,
45+ getApplicationUsageData ,
46+ } from '@comapeo/core-react-native/sentry' ;
4547import { createDraftObservationStore } from './contexts/PersistedStores/DraftObservationStore' ;
4648import { createTrackStore } from './contexts/TrackStoreContext' ;
4749import { createSecurityStore } from './contexts/SecurityStoreContext' ;
4850import { createCoordinateFormatStore } from './contexts/CoordinateFormatStoreContext' ;
4951import { createUnitSystemStore } from './contexts/UnitSystemStoreContext' ;
5052import { createManualEntryCoordinateFormatStore } from './contexts/ManualEntryCoordinateFormatStoreContext' ;
5153import { createActiveProjectIdStore } from './contexts/ActiveProjectIdStoreContext' ;
52- import { createMetricsDiagnosticsStore } from './contexts/MetricsDiagnosticsStoreContext' ;
5354import { createLocaleStore , LocaleContext } from './contexts/LocaleStoreContext' ;
5455import { IntlProvider } from './contexts/IntlContext' ;
5556import { ServerLoading } from './ServerLoading' ;
@@ -63,47 +64,32 @@ import {createQADeviceNameStore} from './contexts/QADeviceNameStoreContext.tsx';
6364import { FatalError } from './screens/FatalError.tsx' ;
6465import { FatalErrorUntranslated } from './screens/FatalErrorUntranslated.tsx' ;
6566import { postHog } from './lib/posthog.ts' ;
66- import { APP_VARIANT } from './lib/appVariant.ts' ;
67+ import { getLocales } from 'expo-localization' ;
68+ import { AppUsageData } from './metrics/AppUsageData.ts' ;
69+ import { DeviceDiagnostics } from './metrics/DeviceDiagnosticMetrics.ts' ;
6770
68- // DSN / environment / tracesSampleRate are baked into the native config by the
69- // @comapeo /core-react-native plugin (app.config.js) and locked by initSentry,
70- // which owns the Sentry.init call across the RN, Node, and Android-FGS hubs —
71- // we pass only the allowlisted extensions. Tracing stays env-gated via the
72- // plugin's tracesSampleRate; the full runtime consent model (and a stable user
73- // id) migrates later with an updated core-react-native.
74- const appMetricsOptIn = APP_VARIANT !== 'production' ;
7571let navigationIntegration :
7672 ReturnType < ( typeof Sentry ) [ 'reactNavigationIntegration' ] > | undefined =
7773 undefined ;
7874
75+ const backendAppUsageDataEnabled = getApplicationUsageData ( ) ;
76+
7977initSentry ( {
8078 integrations : defaults => {
81- if ( ! appMetricsOptIn ) return defaults ;
79+ if ( ! backendAppUsageDataEnabled ) return defaults ;
8280 navigationIntegration = Sentry . reactNavigationIntegration ( {
8381 enableTimeToInitialDisplay : true ,
8482 ignoreEmptyBackNavigationTransactions : false ,
8583 } ) ;
8684 return [ ...defaults , navigationIntegration ] ;
8785 } ,
88- tags : appMetricsOptIn ? { appMetricsOptIn : 'true' } : undefined ,
86+ tags : backendAppUsageDataEnabled ? { appMetricsOptIn : 'true' } : undefined ,
8987} ) ;
9088
9189const persistedLocaleStore = createLocaleStore ( {
9290 persist : true ,
9391} ) ;
9492
95- const appDiagnosticMetrics = new AppDiagnosticMetrics ( {
96- getLocaleInfo : ( ) => {
97- const systemLocales = getLocales ( ) ;
98- const { languageTag} = persistedLocaleStore . instance . getState ( ) ;
99-
100- return {
101- appLanguageTag : languageTag ,
102- deviceLanguageTag : systemLocales [ 0 ] ! . languageTag ,
103- } ;
104- } ,
105- } ) ;
106- const deviceDiagnosticMetrics = new DeviceDiagnosticMetrics ( ) ;
10793const mapServerApi = {
10894 async getBaseUrl ( ) {
10995 return new URL ( await comapeoServicesClient . mapServer . getBaseUrl ( ) ) ;
@@ -142,31 +128,13 @@ const persistedActiveProjectIdStore = createActiveProjectIdStore({
142128 persist : true ,
143129} ) ;
144130
145- const persistedMetricsDiagnosticsStore = createMetricsDiagnosticsStore ( {
146- persist : true ,
147- } ) ;
148-
149131const qaDeviceNameStore = createQADeviceNameStore ( { persist : true } ) ;
150132
151133const savedLocationStore = createSavedLocationStore ( { persist : true } ) ;
152134const lowStorageBannerStore = createLowStorageBannerStore ( ) ;
153135const earlyAccessStore = createEarlyAccessStore ( { persist : true } ) ;
154136const persistedUnitSystemStore = createUnitSystemStore ( { persist : true } ) ;
155137
156- // Ensure that these metrics instances are initially in sync with initial state of relevant store
157- const metricsIsEnabled =
158- persistedMetricsDiagnosticsStore . instance . getState ( ) . isEnabled ;
159- appDiagnosticMetrics . setEnabled ( metricsIsEnabled ) ;
160- deviceDiagnosticMetrics . setEnabled ( metricsIsEnabled ) ;
161-
162- // Sync metrics instances with subsequent changes in relevant store state
163- persistedMetricsDiagnosticsStore . instance . subscribe ( ( current , previous ) => {
164- if ( previous . isEnabled !== current . isEnabled ) {
165- appDiagnosticMetrics . setEnabled ( current . isEnabled ) ;
166- deviceDiagnosticMetrics . setEnabled ( current . isEnabled ) ;
167- }
168- } ) ;
169-
170138// Defines task that handles background location updates for tracks feature
171139TaskManager . defineTask (
172140 LOCATION_TASK_NAME ,
@@ -187,16 +155,45 @@ TaskManager.defineTask(
187155 } ,
188156) ;
189157
158+ const appUsageData = new AppUsageData ( {
159+ getLocaleInfo : ( ) => {
160+ const systemLocales = getLocales ( ) ;
161+ const { languageTag} = persistedLocaleStore . instance . getState ( ) ;
162+
163+ return {
164+ appLanguageTag : languageTag ,
165+ deviceLanguageTag : systemLocales [ 0 ] ! . languageTag ,
166+ } ;
167+ } ,
168+ } ) ;
169+
170+ // App must be restart for the diagnostics to be turned on/off in the backend so this keeps it in sync
171+ appUsageData . setEnabled ( backendAppUsageDataEnabled ) ;
172+
190173const appUsagePromptStore = createAppUsageStatsStore ( {
191174 persist : true ,
192175 appUsageMetricsOptIn : ( ) => {
193176 postHog . optIn ( ) ;
177+ appUsageData . setEnabled ( true ) ;
178+ // Restart-to-activate: takes effect next launch, not this session.
179+ setApplicationUsageData ( true ) . catch ( err => {
180+ Sentry . captureException ( err ) ;
181+ } ) ;
194182 } ,
195183 appUsageMetricsOptOut : ( ) => {
196184 postHog . optOut ( ) ;
185+ appUsageData . setEnabled ( false ) ;
186+ setApplicationUsageData ( false ) . catch ( err => {
187+ Sentry . captureException ( err ) ;
188+ } ) ;
197189 } ,
198190} ) ;
199191
192+ const deviceDiagnostics = new DeviceDiagnostics ( ) ;
193+ const backendDiagnosticsEnabled = getDiagnosticsEnabled ( ) ;
194+ // App must be restart for the diagnostics to be turned on/off in the backend so this keeps it in sync
195+ deviceDiagnostics . setEnabled ( backendDiagnosticsEnabled ) ;
196+
200197const queryClient = new QueryClient ( ) ;
201198
202199AppState . addEventListener ( 'change' , status => {
@@ -255,7 +252,6 @@ const App = () => {
255252 }
256253 savedLocationStore = { savedLocationStore }
257254 activeProjectIdStore = { persistedActiveProjectIdStore }
258- metricsDiagnosticsStore = { persistedMetricsDiagnosticsStore }
259255 appUsageStatsStore = { appUsagePromptStore }
260256 lowStorageBannerStore = { lowStorageBannerStore }
261257 earlyAccessStore = { earlyAccessStore }
0 commit comments