feat: optout sentry reporting
This commit is contained in:
parent
754e14946c
commit
c1b220f007
6 changed files with 276 additions and 53 deletions
|
@ -6,7 +6,7 @@ import { ErrorUtils } from "react-native";
|
||||||
import { createLogger } from "~/lib/logger";
|
import { createLogger } from "~/lib/logger";
|
||||||
import { SYSTEM_SCOPES } from "~/lib/logger/scopes";
|
import { SYSTEM_SCOPES } from "~/lib/logger/scopes";
|
||||||
|
|
||||||
import { authActions, permissionWizardActions } from "~/stores";
|
import { authActions, permissionWizardActions, paramsActions } from "~/stores";
|
||||||
import { secureStore } from "~/storage/memorySecureStore";
|
import { secureStore } from "~/storage/memorySecureStore";
|
||||||
import memoryAsyncStorage from "~/storage/memoryAsyncStorage";
|
import memoryAsyncStorage from "~/storage/memoryAsyncStorage";
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@ const initializeStores = () => {
|
||||||
// Then initialize other stores sequentially
|
// Then initialize other stores sequentially
|
||||||
initializeStore("authActions", authActions.init);
|
initializeStore("authActions", authActions.init);
|
||||||
initializeStore("permissionWizard", permissionWizardActions.init);
|
initializeStore("permissionWizard", permissionWizardActions.init);
|
||||||
|
initializeStore("paramsActions", paramsActions.init);
|
||||||
initializeStore("storeSubscriptions", storeSubscriptions.init);
|
initializeStore("storeSubscriptions", storeSubscriptions.init);
|
||||||
|
|
||||||
appLogger.info("Core initialization complete");
|
appLogger.info("Core initialization complete");
|
||||||
|
|
78
src/scenes/Params/SentryOptOut.js
Normal file
78
src/scenes/Params/SentryOptOut.js
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import React, { useCallback } from "react";
|
||||||
|
import { View } from "react-native";
|
||||||
|
import { Title, Switch } from "react-native-paper";
|
||||||
|
import { createStyles } from "~/theme";
|
||||||
|
import { useParamsState, paramsActions } from "~/stores";
|
||||||
|
import Text from "~/components/Text";
|
||||||
|
import { setSentryEnabled } from "~/sentry";
|
||||||
|
|
||||||
|
function SentryOptOut() {
|
||||||
|
const styles = useStyles();
|
||||||
|
const { sentryEnabled } = useParamsState(["sentryEnabled"]);
|
||||||
|
|
||||||
|
const handleToggle = useCallback(async () => {
|
||||||
|
const newValue = !sentryEnabled;
|
||||||
|
await paramsActions.setSentryEnabled(newValue);
|
||||||
|
|
||||||
|
// Dynamically enable/disable Sentry
|
||||||
|
setSentryEnabled(newValue);
|
||||||
|
}, [sentryEnabled]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Title style={styles.title}>Rapport d'erreurs</Title>
|
||||||
|
<View style={styles.content}>
|
||||||
|
<View style={styles.switchContainer}>
|
||||||
|
<Text style={styles.label}>Envoyer les rapports d'erreurs</Text>
|
||||||
|
<Switch
|
||||||
|
value={sentryEnabled}
|
||||||
|
onValueChange={handleToggle}
|
||||||
|
style={styles.switch}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<Text style={styles.description}>
|
||||||
|
Les rapports d'erreurs nous aident à améliorer l'application en nous
|
||||||
|
permettant de mieux identifier et corriger les problèmes techniques.
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = createStyles(({ theme: { colors } }) => ({
|
||||||
|
container: {
|
||||||
|
width: "100%",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: "bold",
|
||||||
|
marginVertical: 15,
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
switchContainer: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
marginBottom: 5,
|
||||||
|
paddingVertical: 5,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
fontSize: 16,
|
||||||
|
flex: 1,
|
||||||
|
marginRight: 15,
|
||||||
|
},
|
||||||
|
switch: {
|
||||||
|
flexShrink: 0,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: colors.onSurfaceVariant,
|
||||||
|
textAlign: "left",
|
||||||
|
lineHeight: 20,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default SentryOptOut;
|
|
@ -6,6 +6,7 @@ import ParamsRadius from "./Radius";
|
||||||
import ParamsEmergencyCall from "./EmergencyCall";
|
import ParamsEmergencyCall from "./EmergencyCall";
|
||||||
import ThemeSwitcher from "./ThemeSwitcher";
|
import ThemeSwitcher from "./ThemeSwitcher";
|
||||||
import Permissions from "./Permissions";
|
import Permissions from "./Permissions";
|
||||||
|
import SentryOptOut from "./SentryOptOut";
|
||||||
|
|
||||||
export default function ParamsView({ data }) {
|
export default function ParamsView({ data }) {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
@ -25,6 +26,9 @@ export default function ParamsView({ data }) {
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<ParamsRadius data={data} />
|
<ParamsRadius data={data} />
|
||||||
</View>
|
</View>
|
||||||
|
<View style={styles.section}>
|
||||||
|
<SentryOptOut />
|
||||||
|
</View>
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<Permissions />
|
<Permissions />
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -3,6 +3,15 @@ import { Platform } from "react-native";
|
||||||
|
|
||||||
import env from "~/env";
|
import env from "~/env";
|
||||||
import packageJson from "../../package.json";
|
import packageJson from "../../package.json";
|
||||||
|
import memoryAsyncStorage from "~/storage/memoryAsyncStorage";
|
||||||
|
import { STORAGE_KEYS } from "~/storage/storageKeys";
|
||||||
|
import { createLogger } from "~/lib/logger";
|
||||||
|
import { SYSTEM_SCOPES } from "~/lib/logger/scopes";
|
||||||
|
|
||||||
|
const sentryLogger = createLogger({
|
||||||
|
module: SYSTEM_SCOPES.APP,
|
||||||
|
feature: "sentry",
|
||||||
|
});
|
||||||
|
|
||||||
// Get the build number from native code
|
// Get the build number from native code
|
||||||
const getBuildNumber = () => {
|
const getBuildNumber = () => {
|
||||||
|
@ -23,8 +32,54 @@ const getReleaseVersion = () => {
|
||||||
return `com.alertesecours@${version}+${buildNumber}`;
|
return `com.alertesecours@${version}+${buildNumber}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
Sentry.init({
|
// Check if Sentry is enabled by user preference
|
||||||
|
const checkSentryEnabled = async () => {
|
||||||
|
try {
|
||||||
|
// Wait for memory storage to be initialized
|
||||||
|
let retries = 0;
|
||||||
|
const maxRetries = 10;
|
||||||
|
|
||||||
|
while (retries < maxRetries) {
|
||||||
|
try {
|
||||||
|
const stored = await memoryAsyncStorage.getItem(
|
||||||
|
STORAGE_KEYS.SENTRY_ENABLED,
|
||||||
|
);
|
||||||
|
if (stored !== null) {
|
||||||
|
return JSON.parse(stored);
|
||||||
|
}
|
||||||
|
break; // Storage is ready, no preference stored
|
||||||
|
} catch (error) {
|
||||||
|
if (
|
||||||
|
error.message?.includes("not initialized") &&
|
||||||
|
retries < maxRetries - 1
|
||||||
|
) {
|
||||||
|
// Wait a bit and retry if storage not initialized
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||||
|
retries++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sentryLogger.warn("Failed to check Sentry preference", {
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
sentryLogger.warn("Failed to check Sentry preference", {
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Default to enabled if no preference stored or error occurred
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize Sentry with user preference check
|
||||||
|
const initializeSentry = async () => {
|
||||||
|
const isEnabled = await checkSentryEnabled();
|
||||||
|
|
||||||
|
Sentry.init({
|
||||||
dsn: env.SENTRY_DSN,
|
dsn: env.SENTRY_DSN,
|
||||||
|
enabled: isEnabled,
|
||||||
tracesSampleRate: 0.1,
|
tracesSampleRate: 0.1,
|
||||||
debug: __DEV__,
|
debug: __DEV__,
|
||||||
// Configure release to match ios-archive.sh format
|
// Configure release to match ios-archive.sh format
|
||||||
|
@ -79,4 +134,37 @@ Sentry.init({
|
||||||
// maskAllVectors: false,
|
// maskAllVectors: false,
|
||||||
// }),
|
// }),
|
||||||
],
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize Sentry asynchronously
|
||||||
|
initializeSentry().catch((error) => {
|
||||||
|
sentryLogger.warn("Failed to initialize Sentry", {
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Export function to dynamically control Sentry
|
||||||
|
export const setSentryEnabled = (enabled) => {
|
||||||
|
try {
|
||||||
|
// Use the newer Sentry API
|
||||||
|
const client = Sentry.getClient();
|
||||||
|
if (client) {
|
||||||
|
const options = client.getOptions();
|
||||||
|
options.enabled = enabled;
|
||||||
|
if (!enabled) {
|
||||||
|
// Clear any pending events when disabling
|
||||||
|
Sentry.withScope((scope) => {
|
||||||
|
scope.clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
sentryLogger.info("Sentry state toggled", { enabled });
|
||||||
|
} else {
|
||||||
|
sentryLogger.warn("Sentry client not available for toggling");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
sentryLogger.warn("Failed to toggle Sentry state", {
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -80,4 +80,5 @@ export const STORAGE_KEYS = {
|
||||||
LAST_KNOWN_LOCATION: registerAsyncStorageKey("@last_known_location"),
|
LAST_KNOWN_LOCATION: registerAsyncStorageKey("@last_known_location"),
|
||||||
EULA_ACCEPTED_SIMPLE: registerAsyncStorageKey("eula_accepted"),
|
EULA_ACCEPTED_SIMPLE: registerAsyncStorageKey("eula_accepted"),
|
||||||
EMULATOR_MODE_ENABLED: registerAsyncStorageKey("emulator_mode_enabled"),
|
EMULATOR_MODE_ENABLED: registerAsyncStorageKey("emulator_mode_enabled"),
|
||||||
|
SENTRY_ENABLED: registerAsyncStorageKey("@sentry_enabled"),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,13 @@
|
||||||
import { createAtom } from "~/lib/atomic-zustand";
|
import { createAtom } from "~/lib/atomic-zustand";
|
||||||
|
import memoryAsyncStorage from "~/storage/memoryAsyncStorage";
|
||||||
|
import { STORAGE_KEYS } from "~/storage/storageKeys";
|
||||||
|
import { createLogger } from "~/lib/logger";
|
||||||
|
import { SYSTEM_SCOPES } from "~/lib/logger/scopes";
|
||||||
|
|
||||||
|
const paramsLogger = createLogger({
|
||||||
|
module: SYSTEM_SCOPES.APP,
|
||||||
|
feature: "params",
|
||||||
|
});
|
||||||
|
|
||||||
export default createAtom(({ merge, reset }) => {
|
export default createAtom(({ merge, reset }) => {
|
||||||
const setDevModeEnabled = (b) => {
|
const setDevModeEnabled = (b) => {
|
||||||
|
@ -37,6 +46,45 @@ export default createAtom(({ merge, reset }) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setSentryEnabled = async (sentryEnabled) => {
|
||||||
|
merge({
|
||||||
|
sentryEnabled,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Persist to storage
|
||||||
|
try {
|
||||||
|
await memoryAsyncStorage.setItem(
|
||||||
|
STORAGE_KEYS.SENTRY_ENABLED,
|
||||||
|
JSON.stringify(sentryEnabled),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
paramsLogger.warn("Failed to persist Sentry preference", {
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const initSentryEnabled = async () => {
|
||||||
|
try {
|
||||||
|
const stored = await memoryAsyncStorage.getItem(
|
||||||
|
STORAGE_KEYS.SENTRY_ENABLED,
|
||||||
|
);
|
||||||
|
if (stored !== null) {
|
||||||
|
const sentryEnabled = JSON.parse(stored);
|
||||||
|
merge({ sentryEnabled });
|
||||||
|
return sentryEnabled;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
paramsLogger.warn("Failed to load Sentry preference", {
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const init = async () => {
|
||||||
|
await initSentryEnabled();
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
default: {
|
default: {
|
||||||
// devModeEnabled: false,
|
// devModeEnabled: false,
|
||||||
|
@ -46,6 +94,7 @@ export default createAtom(({ merge, reset }) => {
|
||||||
mapColorScheme: "auto",
|
mapColorScheme: "auto",
|
||||||
hasRegisteredRelatives: null,
|
hasRegisteredRelatives: null,
|
||||||
alertListSortBy: "location",
|
alertListSortBy: "location",
|
||||||
|
sentryEnabled: true,
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
reset,
|
reset,
|
||||||
|
@ -55,6 +104,8 @@ export default createAtom(({ merge, reset }) => {
|
||||||
setMapColorScheme,
|
setMapColorScheme,
|
||||||
setHasRegisteredRelatives,
|
setHasRegisteredRelatives,
|
||||||
setAlertListSortBy,
|
setAlertListSortBy,
|
||||||
|
setSentryEnabled,
|
||||||
|
init,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue