diff --git a/static/app/components/events/contexts/knownContext/accessibility.spec.tsx b/static/app/components/events/contexts/knownContext/accessibility.spec.tsx
new file mode 100644
index 000000000000..49e9835858f7
--- /dev/null
+++ b/static/app/components/events/contexts/knownContext/accessibility.spec.tsx
@@ -0,0 +1,79 @@
+import {EventFixture} from 'sentry-fixture/event';
+
+import {render, screen} from 'sentry-test/reactTestingLibrary';
+
+import {ContextCard} from 'sentry/components/events/contexts/contextCard';
+import {
+ getAccessibilityContextData,
+ type AccessibilityContext,
+} from 'sentry/components/events/contexts/knownContext/accessibility';
+
+const MOCK_ACCESSIBILITY_CONTEXT: AccessibilityContext = {
+ accessible_navigation: false,
+ bold_text: false,
+ disable_animations: true,
+ high_contrast: false,
+ invert_colors: false,
+ reduce_motion: false,
+ // Extra data is still valid and preserved
+ extra_data: 'something',
+ unknown_key: 123,
+};
+
+const MOCK_REDACTION = {
+ reduce_motion: {
+ '': {
+ rem: [['organization:0', 's', 0, 0]],
+ len: 5,
+ },
+ },
+};
+
+describe('AccessibilityContext', () => {
+ it('returns values according to the parameters', () => {
+ expect(getAccessibilityContextData({data: MOCK_ACCESSIBILITY_CONTEXT})).toEqual([
+ {
+ key: 'accessible_navigation',
+ subject: 'Accessible Navigation',
+ value: false,
+ },
+ {key: 'bold_text', subject: 'Bold Text', value: false},
+ {key: 'disable_animations', subject: 'Disable Animations', value: true},
+ {key: 'high_contrast', subject: 'High Contrast', value: false},
+ {key: 'invert_colors', subject: 'Invert Colors', value: false},
+ {key: 'reduce_motion', subject: 'Reduce Motion', value: false},
+ {
+ key: 'extra_data',
+ subject: 'extra_data',
+ value: 'something',
+ meta: undefined,
+ },
+ {
+ key: 'unknown_key',
+ subject: 'unknown_key',
+ value: 123,
+ meta: undefined,
+ },
+ ]);
+ });
+
+ it('renders with meta annotations correctly', () => {
+ const event = EventFixture({
+ _meta: {contexts: {accessibility: MOCK_REDACTION}},
+ });
+
+ render(
+
+ );
+
+ expect(screen.getByText('Accessibility')).toBeInTheDocument();
+ expect(screen.getByText('Disable Animations')).toBeInTheDocument();
+ expect(screen.getByText('Reduce Motion')).toBeInTheDocument();
+ expect(screen.getByText(/redacted/)).toBeInTheDocument();
+ });
+});
diff --git a/static/app/components/events/contexts/knownContext/accessibility.tsx b/static/app/components/events/contexts/knownContext/accessibility.tsx
new file mode 100644
index 000000000000..f3c8819e6671
--- /dev/null
+++ b/static/app/components/events/contexts/knownContext/accessibility.tsx
@@ -0,0 +1,78 @@
+import {getContextKeys} from 'sentry/components/events/contexts/utils';
+import {t} from 'sentry/locale';
+import type {KeyValueListData} from 'sentry/types/group';
+
+enum AccessibilityContextKeys {
+ ACCESSIBLE_NAVIGATION = 'accessible_navigation',
+ BOLD_TEXT = 'bold_text',
+ DISABLE_ANIMATIONS = 'disable_animations',
+ HIGH_CONTRAST = 'high_contrast',
+ INVERT_COLORS = 'invert_colors',
+ REDUCE_MOTION = 'reduce_motion',
+}
+
+export interface AccessibilityContext {
+ [key: string]: any;
+ [AccessibilityContextKeys.ACCESSIBLE_NAVIGATION]?: boolean;
+ [AccessibilityContextKeys.BOLD_TEXT]?: boolean;
+ [AccessibilityContextKeys.DISABLE_ANIMATIONS]?: boolean;
+ [AccessibilityContextKeys.HIGH_CONTRAST]?: boolean;
+ [AccessibilityContextKeys.INVERT_COLORS]?: boolean;
+ [AccessibilityContextKeys.REDUCE_MOTION]?: boolean;
+}
+
+export function getAccessibilityContextData({
+ data,
+ meta,
+}: {
+ data: AccessibilityContext;
+ meta?: Record;
+}): KeyValueListData {
+ return getContextKeys({data}).map(ctxKey => {
+ switch (ctxKey) {
+ case AccessibilityContextKeys.ACCESSIBLE_NAVIGATION:
+ return {
+ key: ctxKey,
+ subject: t('Accessible Navigation'),
+ value: data.accessible_navigation,
+ };
+ case AccessibilityContextKeys.BOLD_TEXT:
+ return {
+ key: ctxKey,
+ subject: t('Bold Text'),
+ value: data.bold_text,
+ };
+ case AccessibilityContextKeys.DISABLE_ANIMATIONS:
+ return {
+ key: ctxKey,
+ subject: t('Disable Animations'),
+ value: data.disable_animations,
+ };
+ case AccessibilityContextKeys.HIGH_CONTRAST:
+ return {
+ key: ctxKey,
+ subject: t('High Contrast'),
+ value: data.high_contrast,
+ };
+ case AccessibilityContextKeys.INVERT_COLORS:
+ return {
+ key: ctxKey,
+ subject: t('Invert Colors'),
+ value: data.invert_colors,
+ };
+ case AccessibilityContextKeys.REDUCE_MOTION:
+ return {
+ key: ctxKey,
+ subject: t('Reduce Motion'),
+ value: data.reduce_motion,
+ };
+ default:
+ return {
+ key: ctxKey,
+ subject: ctxKey,
+ value: data[ctxKey],
+ meta: meta?.[ctxKey]?.[''],
+ };
+ }
+ });
+}
diff --git a/static/app/components/events/contexts/knownContext/app.spec.tsx b/static/app/components/events/contexts/knownContext/app.spec.tsx
index 8860ba7849da..3b4aa44b4e3e 100644
--- a/static/app/components/events/contexts/knownContext/app.spec.tsx
+++ b/static/app/components/events/contexts/knownContext/app.spec.tsx
@@ -21,6 +21,12 @@ const MOCK_APP_CONTEXT: AppContext = {
is_active: false,
app_memory: 1048576 * 12,
view_names: ['app.view1', 'app.view2'],
+ is_split_apks: false,
+ permissions: {
+ ACCESS_NETWORK_STATE: 'granted',
+ CAMERA: 'not_granted',
+ INTERNET: 'granted',
+ },
// Extra data is still valid and preserved
extra_data: 'something',
unknown_key: 123,
@@ -73,6 +79,16 @@ describe('AppContext', () => {
subject: 'View Names',
value: ['app.view1', 'app.view2'],
},
+ {key: 'is_split_apks', subject: 'Split APKs', value: false},
+ {
+ key: 'permissions',
+ subject: 'Permissions',
+ value: {
+ ACCESS_NETWORK_STATE: 'granted',
+ CAMERA: 'not_granted',
+ INTERNET: 'granted',
+ },
+ },
{
key: 'extra_data',
subject: 'extra_data',
diff --git a/static/app/components/events/contexts/knownContext/app.tsx b/static/app/components/events/contexts/knownContext/app.tsx
index 4f851e599c24..73bdb5a23fcf 100644
--- a/static/app/components/events/contexts/knownContext/app.tsx
+++ b/static/app/components/events/contexts/knownContext/app.tsx
@@ -24,6 +24,8 @@ enum AppContextKeys {
// XXX: From https://github.com/getsentry/sentry/issues/87238, not in the schema yet.
FREE_MEMORY = 'free_memory',
ARCHITECTURE = 'app_arch',
+ IS_SPLIT_APKS = 'is_split_apks',
+ PERMISSIONS = 'permissions',
}
export interface AppContext {
@@ -43,6 +45,8 @@ export interface AppContext {
[AppContextKeys.VIEW_NAMES]?: string[];
[AppContextKeys.FREE_MEMORY]?: number;
[AppContextKeys.ARCHITECTURE]?: string;
+ [AppContextKeys.IS_SPLIT_APKS]?: boolean;
+ [AppContextKeys.PERMISSIONS]?: Record;
}
// https://github.com/getsentry/relay/blob/24.10.0/relay-event-schema/src/protocol/contexts/app.rs#L37
@@ -151,6 +155,18 @@ export function getAppContextData({
subject: t('Architecture'),
value: data.app_arch,
};
+ case AppContextKeys.IS_SPLIT_APKS:
+ return {
+ key: ctxKey,
+ subject: t('Split APKs'),
+ value: data.is_split_apks,
+ };
+ case AppContextKeys.PERMISSIONS:
+ return {
+ key: ctxKey,
+ subject: t('Permissions'),
+ value: data.permissions,
+ };
default:
return {
key: ctxKey,
diff --git a/static/app/components/events/contexts/knownContext/dartContext.spec.tsx b/static/app/components/events/contexts/knownContext/dartContext.spec.tsx
new file mode 100644
index 000000000000..e142d04fe042
--- /dev/null
+++ b/static/app/components/events/contexts/knownContext/dartContext.spec.tsx
@@ -0,0 +1,76 @@
+import {EventFixture} from 'sentry-fixture/event';
+
+import {render, screen} from 'sentry-test/reactTestingLibrary';
+
+import {ContextCard} from 'sentry/components/events/contexts/contextCard';
+import {
+ getDartContextData,
+ type DartContext,
+} from 'sentry/components/events/contexts/knownContext/dartContext';
+
+const MOCK_DART_CONTEXT: DartContext = {
+ compile_mode: 'debug',
+ executable: 'flutter',
+ resolved_executable: '/system/bin/app_process64',
+ script: 'file:///main.dart',
+ // Extra data is still valid and preserved
+ extra_data: 'something',
+ unknown_key: 123,
+};
+
+const MOCK_REDACTION = {
+ script: {
+ '': {
+ rem: [['organization:0', 's', 0, 0]],
+ len: 20,
+ },
+ },
+};
+
+describe('DartContext', () => {
+ it('returns values according to the parameters', () => {
+ expect(getDartContextData({data: MOCK_DART_CONTEXT})).toEqual([
+ {key: 'compile_mode', subject: 'Compile Mode', value: 'debug'},
+ {key: 'executable', subject: 'Executable', value: 'flutter'},
+ {
+ key: 'resolved_executable',
+ subject: 'Resolved Executable',
+ value: '/system/bin/app_process64',
+ },
+ {key: 'script', subject: 'Script', value: 'file:///main.dart'},
+ {
+ key: 'extra_data',
+ subject: 'extra_data',
+ value: 'something',
+ meta: undefined,
+ },
+ {
+ key: 'unknown_key',
+ subject: 'unknown_key',
+ value: 123,
+ meta: undefined,
+ },
+ ]);
+ });
+
+ it('renders with meta annotations correctly', () => {
+ const event = EventFixture({
+ _meta: {contexts: {dart_context: MOCK_REDACTION}},
+ });
+
+ render(
+
+ );
+
+ expect(screen.getByText('Dart')).toBeInTheDocument();
+ expect(screen.getByText('Compile Mode')).toBeInTheDocument();
+ expect(screen.getByText('debug')).toBeInTheDocument();
+ expect(screen.getByText('Script')).toBeInTheDocument();
+ expect(screen.getByText(/redacted/)).toBeInTheDocument();
+ });
+});
diff --git a/static/app/components/events/contexts/knownContext/dartContext.tsx b/static/app/components/events/contexts/knownContext/dartContext.tsx
new file mode 100644
index 000000000000..fc80c0f4220a
--- /dev/null
+++ b/static/app/components/events/contexts/knownContext/dartContext.tsx
@@ -0,0 +1,62 @@
+import {getContextKeys} from 'sentry/components/events/contexts/utils';
+import {t} from 'sentry/locale';
+import type {KeyValueListData} from 'sentry/types/group';
+
+enum DartContextKeys {
+ COMPILE_MODE = 'compile_mode',
+ EXECUTABLE = 'executable',
+ RESOLVED_EXECUTABLE = 'resolved_executable',
+ SCRIPT = 'script',
+}
+
+export interface DartContext {
+ [key: string]: any;
+ [DartContextKeys.COMPILE_MODE]?: string;
+ [DartContextKeys.EXECUTABLE]?: string;
+ [DartContextKeys.RESOLVED_EXECUTABLE]?: string;
+ [DartContextKeys.SCRIPT]?: string;
+}
+
+export function getDartContextData({
+ data,
+ meta,
+}: {
+ data: DartContext;
+ meta?: Record;
+}): KeyValueListData {
+ return getContextKeys({data}).map(ctxKey => {
+ switch (ctxKey) {
+ case DartContextKeys.COMPILE_MODE:
+ return {
+ key: ctxKey,
+ subject: t('Compile Mode'),
+ value: data.compile_mode,
+ };
+ case DartContextKeys.EXECUTABLE:
+ return {
+ key: ctxKey,
+ subject: t('Executable'),
+ value: data.executable,
+ };
+ case DartContextKeys.RESOLVED_EXECUTABLE:
+ return {
+ key: ctxKey,
+ subject: t('Resolved Executable'),
+ value: data.resolved_executable,
+ };
+ case DartContextKeys.SCRIPT:
+ return {
+ key: ctxKey,
+ subject: t('Script'),
+ value: data.script,
+ };
+ default:
+ return {
+ key: ctxKey,
+ subject: ctxKey,
+ value: data[ctxKey],
+ meta: meta?.[ctxKey]?.[''],
+ };
+ }
+ });
+}
diff --git a/static/app/components/events/contexts/knownContext/device.spec.tsx b/static/app/components/events/contexts/knownContext/device.spec.tsx
index fb85bb2da436..86a38d2e0029 100644
--- a/static/app/components/events/contexts/knownContext/device.spec.tsx
+++ b/static/app/components/events/contexts/knownContext/device.spec.tsx
@@ -35,6 +35,12 @@ const MOCK_DEVICE_CONTEXT: DeviceContext = {
manufacturer: 'Google',
free_storage: 508784640,
model: 'Android SDK built for x86',
+ locale: 'en_US',
+ archs: ['arm64-v8a'],
+ chipset: 'AOSP ranchu',
+ connection_type: 'cellular',
+ low_power_mode: false,
+ thermal_state: 'nominal',
};
const MOCK_REDACTION = {
@@ -74,9 +80,8 @@ describe('DeviceContext', () => {
},
{
key: 'timezone',
- subject: 'timezone',
+ subject: 'Timezone',
value: 'America/Los_Angeles',
- meta: undefined,
},
{
key: 'external_storage_size',
@@ -132,6 +137,12 @@ describe('DeviceContext', () => {
subject: 'Model',
value: expect.anything(),
},
+ {key: 'locale', subject: 'Locale', value: 'en_US'},
+ {key: 'archs', subject: 'Architectures', value: ['arm64-v8a']},
+ {key: 'chipset', subject: 'Chipset', value: 'AOSP ranchu'},
+ {key: 'connection_type', subject: 'Connection Type', value: 'cellular'},
+ {key: 'low_power_mode', subject: 'Low Power Mode', value: false},
+ {key: 'thermal_state', subject: 'Thermal State', value: 'nominal'},
]);
});
diff --git a/static/app/components/events/contexts/knownContext/device.tsx b/static/app/components/events/contexts/knownContext/device.tsx
index cd367bd05e73..ac6dd0b9069c 100644
--- a/static/app/components/events/contexts/knownContext/device.tsx
+++ b/static/app/components/events/contexts/knownContext/device.tsx
@@ -421,6 +421,48 @@ export function getDeviceContextData({
subject: t('Supports Vibration'),
value: data.supports_vibration,
};
+ case DeviceContextKey.TIMEZONE:
+ return {
+ key: ctxKey,
+ subject: t('Timezone'),
+ value: data.timezone,
+ };
+ case DeviceContextKey.LOCALE:
+ return {
+ key: ctxKey,
+ subject: t('Locale'),
+ value: data.locale,
+ };
+ case DeviceContextKey.ARCHS:
+ return {
+ key: ctxKey,
+ subject: t('Architectures'),
+ value: data.archs,
+ };
+ case DeviceContextKey.CHIPSET:
+ return {
+ key: ctxKey,
+ subject: t('Chipset'),
+ value: data.chipset,
+ };
+ case DeviceContextKey.CONNECTION_TYPE:
+ return {
+ key: ctxKey,
+ subject: t('Connection Type'),
+ value: data.connection_type,
+ };
+ case DeviceContextKey.LOW_POWER_MODE:
+ return {
+ key: ctxKey,
+ subject: t('Low Power Mode'),
+ value: data.low_power_mode,
+ };
+ case DeviceContextKey.THERMAL_STATE:
+ return {
+ key: ctxKey,
+ subject: t('Thermal State'),
+ value: data.thermal_state,
+ };
default:
return {
key: ctxKey,
diff --git a/static/app/components/events/contexts/knownContext/flutterContext.spec.tsx b/static/app/components/events/contexts/knownContext/flutterContext.spec.tsx
new file mode 100644
index 000000000000..0e159c8da474
--- /dev/null
+++ b/static/app/components/events/contexts/knownContext/flutterContext.spec.tsx
@@ -0,0 +1,68 @@
+import {EventFixture} from 'sentry-fixture/event';
+
+import {render, screen} from 'sentry-test/reactTestingLibrary';
+
+import {ContextCard} from 'sentry/components/events/contexts/contextCard';
+import {
+ getFlutterContextData,
+ type FlutterContext,
+} from 'sentry/components/events/contexts/knownContext/flutterContext';
+
+const MOCK_FLUTTER_CONTEXT: FlutterContext = {
+ default_route_name: '/',
+ has_render_view: 'true',
+ // Extra data is still valid and preserved
+ extra_data: 'something',
+ unknown_key: 123,
+};
+
+const MOCK_REDACTION = {
+ default_route_name: {
+ '': {
+ rem: [['organization:0', 's', 0, 0]],
+ len: 1,
+ },
+ },
+};
+
+describe('FlutterContext', () => {
+ it('returns values according to the parameters', () => {
+ expect(getFlutterContextData({data: MOCK_FLUTTER_CONTEXT})).toEqual([
+ {key: 'default_route_name', subject: 'Default Route Name', value: '/'},
+ {key: 'has_render_view', subject: 'Has Render View', value: 'true'},
+ {
+ key: 'extra_data',
+ subject: 'extra_data',
+ value: 'something',
+ meta: undefined,
+ },
+ {
+ key: 'unknown_key',
+ subject: 'unknown_key',
+ value: 123,
+ meta: undefined,
+ },
+ ]);
+ });
+
+ it('renders with meta annotations correctly', () => {
+ const event = EventFixture({
+ _meta: {contexts: {flutter_context: MOCK_REDACTION}},
+ });
+
+ render(
+
+ );
+
+ expect(screen.getByText('Flutter')).toBeInTheDocument();
+ expect(screen.getByText('Has Render View')).toBeInTheDocument();
+ expect(screen.getByText('true')).toBeInTheDocument();
+ expect(screen.getByText('Default Route Name')).toBeInTheDocument();
+ expect(screen.getByText(/redacted/)).toBeInTheDocument();
+ });
+});
diff --git a/static/app/components/events/contexts/knownContext/flutterContext.tsx b/static/app/components/events/contexts/knownContext/flutterContext.tsx
new file mode 100644
index 000000000000..a396e4bfb0eb
--- /dev/null
+++ b/static/app/components/events/contexts/knownContext/flutterContext.tsx
@@ -0,0 +1,46 @@
+import {getContextKeys} from 'sentry/components/events/contexts/utils';
+import {t} from 'sentry/locale';
+import type {KeyValueListData} from 'sentry/types/group';
+
+enum FlutterContextKeys {
+ DEFAULT_ROUTE_NAME = 'default_route_name',
+ HAS_RENDER_VIEW = 'has_render_view',
+}
+
+export interface FlutterContext {
+ [key: string]: any;
+ [FlutterContextKeys.DEFAULT_ROUTE_NAME]?: string;
+ [FlutterContextKeys.HAS_RENDER_VIEW]?: string;
+}
+
+export function getFlutterContextData({
+ data,
+ meta,
+}: {
+ data: FlutterContext;
+ meta?: Record;
+}): KeyValueListData {
+ return getContextKeys({data}).map(ctxKey => {
+ switch (ctxKey) {
+ case FlutterContextKeys.DEFAULT_ROUTE_NAME:
+ return {
+ key: ctxKey,
+ subject: t('Default Route Name'),
+ value: data.default_route_name,
+ };
+ case FlutterContextKeys.HAS_RENDER_VIEW:
+ return {
+ key: ctxKey,
+ subject: t('Has Render View'),
+ value: data.has_render_view,
+ };
+ default:
+ return {
+ key: ctxKey,
+ subject: ctxKey,
+ value: data[ctxKey],
+ meta: meta?.[ctxKey]?.[''],
+ };
+ }
+ });
+}
diff --git a/static/app/components/events/contexts/knownContext/reactNativeContext.spec.tsx b/static/app/components/events/contexts/knownContext/reactNativeContext.spec.tsx
new file mode 100644
index 000000000000..536bd10ebd26
--- /dev/null
+++ b/static/app/components/events/contexts/knownContext/reactNativeContext.spec.tsx
@@ -0,0 +1,86 @@
+import {EventFixture} from 'sentry-fixture/event';
+
+import {render, screen} from 'sentry-test/reactTestingLibrary';
+
+import {ContextCard} from 'sentry/components/events/contexts/contextCard';
+import {
+ getReactNativeContextData,
+ type ReactNativeContext,
+} from 'sentry/components/events/contexts/knownContext/reactNativeContext';
+
+const MOCK_REACT_NATIVE_CONTEXT: ReactNativeContext = {
+ expo: false,
+ fabric: true,
+ hermes_debug_info: false,
+ hermes_version: '250829098.0.10',
+ js_engine: 'hermes',
+ react_native_version: '0.85.1',
+ turbo_module: true,
+ // Extra data is still valid and preserved
+ extra_data: 'something',
+ unknown_key: 123,
+};
+
+const MOCK_REDACTION = {
+ hermes_version: {
+ '': {
+ rem: [['organization:0', 's', 0, 0]],
+ len: 15,
+ },
+ },
+};
+
+describe('ReactNativeContext', () => {
+ it('returns values according to the parameters', () => {
+ expect(getReactNativeContextData({data: MOCK_REACT_NATIVE_CONTEXT})).toEqual([
+ {key: 'expo', subject: 'Expo', value: false},
+ {key: 'fabric', subject: 'Fabric', value: true},
+ {key: 'hermes_debug_info', subject: 'Hermes Debug Info', value: false},
+ {
+ key: 'hermes_version',
+ subject: 'Hermes Version',
+ value: '250829098.0.10',
+ },
+ {key: 'js_engine', subject: 'JS Engine', value: 'hermes'},
+ {
+ key: 'react_native_version',
+ subject: 'React Native Version',
+ value: '0.85.1',
+ },
+ {key: 'turbo_module', subject: 'Turbo Module', value: true},
+ {
+ key: 'extra_data',
+ subject: 'extra_data',
+ value: 'something',
+ meta: undefined,
+ },
+ {
+ key: 'unknown_key',
+ subject: 'unknown_key',
+ value: 123,
+ meta: undefined,
+ },
+ ]);
+ });
+
+ it('renders with meta annotations correctly', () => {
+ const event = EventFixture({
+ _meta: {contexts: {react_native_context: MOCK_REDACTION}},
+ });
+
+ render(
+
+ );
+
+ expect(screen.getByText('React Native')).toBeInTheDocument();
+ expect(screen.getByText('JS Engine')).toBeInTheDocument();
+ expect(screen.getByText('hermes')).toBeInTheDocument();
+ expect(screen.getByText('Hermes Version')).toBeInTheDocument();
+ expect(screen.getByText(/redacted/)).toBeInTheDocument();
+ });
+});
diff --git a/static/app/components/events/contexts/knownContext/reactNativeContext.tsx b/static/app/components/events/contexts/knownContext/reactNativeContext.tsx
new file mode 100644
index 000000000000..258a5d11c306
--- /dev/null
+++ b/static/app/components/events/contexts/knownContext/reactNativeContext.tsx
@@ -0,0 +1,86 @@
+import {getContextKeys} from 'sentry/components/events/contexts/utils';
+import {t} from 'sentry/locale';
+import type {KeyValueListData} from 'sentry/types/group';
+
+enum ReactNativeContextKeys {
+ EXPO = 'expo',
+ FABRIC = 'fabric',
+ HERMES_DEBUG_INFO = 'hermes_debug_info',
+ HERMES_VERSION = 'hermes_version',
+ JS_ENGINE = 'js_engine',
+ REACT_NATIVE_VERSION = 'react_native_version',
+ TURBO_MODULE = 'turbo_module',
+}
+
+export interface ReactNativeContext {
+ [key: string]: any;
+ [ReactNativeContextKeys.EXPO]?: boolean;
+ [ReactNativeContextKeys.FABRIC]?: boolean;
+ [ReactNativeContextKeys.HERMES_DEBUG_INFO]?: boolean;
+ [ReactNativeContextKeys.HERMES_VERSION]?: string;
+ [ReactNativeContextKeys.JS_ENGINE]?: string;
+ [ReactNativeContextKeys.REACT_NATIVE_VERSION]?: string;
+ [ReactNativeContextKeys.TURBO_MODULE]?: boolean;
+}
+
+export function getReactNativeContextData({
+ data,
+ meta,
+}: {
+ data: ReactNativeContext;
+ meta?: Record;
+}): KeyValueListData {
+ return getContextKeys({data}).map(ctxKey => {
+ switch (ctxKey) {
+ case ReactNativeContextKeys.EXPO:
+ return {
+ key: ctxKey,
+ subject: t('Expo'),
+ value: data.expo,
+ };
+ case ReactNativeContextKeys.FABRIC:
+ return {
+ key: ctxKey,
+ subject: t('Fabric'),
+ value: data.fabric,
+ };
+ case ReactNativeContextKeys.HERMES_DEBUG_INFO:
+ return {
+ key: ctxKey,
+ subject: t('Hermes Debug Info'),
+ value: data.hermes_debug_info,
+ };
+ case ReactNativeContextKeys.HERMES_VERSION:
+ return {
+ key: ctxKey,
+ subject: t('Hermes Version'),
+ value: data.hermes_version,
+ };
+ case ReactNativeContextKeys.JS_ENGINE:
+ return {
+ key: ctxKey,
+ subject: t('JS Engine'),
+ value: data.js_engine,
+ };
+ case ReactNativeContextKeys.REACT_NATIVE_VERSION:
+ return {
+ key: ctxKey,
+ subject: t('React Native Version'),
+ value: data.react_native_version,
+ };
+ case ReactNativeContextKeys.TURBO_MODULE:
+ return {
+ key: ctxKey,
+ subject: t('Turbo Module'),
+ value: data.turbo_module,
+ };
+ default:
+ return {
+ key: ctxKey,
+ subject: ctxKey,
+ value: data[ctxKey],
+ meta: meta?.[ctxKey]?.[''],
+ };
+ }
+ });
+}
diff --git a/static/app/components/events/contexts/utils.tsx b/static/app/components/events/contexts/utils.tsx
index d57ba74602a8..7b41bfea8a3c 100644
--- a/static/app/components/events/contexts/utils.tsx
+++ b/static/app/components/events/contexts/utils.tsx
@@ -12,16 +12,20 @@ import {
getLogoImage,
type ContextIconProps,
} from 'sentry/components/events/contexts/contextIcon';
+import {getAccessibilityContextData} from 'sentry/components/events/contexts/knownContext/accessibility';
import {getAppContextData} from 'sentry/components/events/contexts/knownContext/app';
import {getBrowserContextData} from 'sentry/components/events/contexts/knownContext/browser';
import {getCloudResourceContextData} from 'sentry/components/events/contexts/knownContext/cloudResource';
import {getCultureContextData} from 'sentry/components/events/contexts/knownContext/culture';
+import {getDartContextData} from 'sentry/components/events/contexts/knownContext/dartContext';
import {getDeviceContextData} from 'sentry/components/events/contexts/knownContext/device';
+import {getFlutterContextData} from 'sentry/components/events/contexts/knownContext/flutterContext';
import {getGPUContextData} from 'sentry/components/events/contexts/knownContext/gpu';
import {getMemoryInfoContext} from 'sentry/components/events/contexts/knownContext/memoryInfo';
import {getMissingInstrumentationContextData} from 'sentry/components/events/contexts/knownContext/missingInstrumentation';
import {getOperatingSystemContextData} from 'sentry/components/events/contexts/knownContext/os';
import {getProfileContextData} from 'sentry/components/events/contexts/knownContext/profile';
+import {getReactNativeContextData} from 'sentry/components/events/contexts/knownContext/reactNativeContext';
import {getReplayContextData} from 'sentry/components/events/contexts/knownContext/replay';
import {getRuntimeContextData} from 'sentry/components/events/contexts/knownContext/runtime';
import {getStateContextData} from 'sentry/components/events/contexts/knownContext/state';
@@ -293,6 +297,12 @@ export function getContextTitle({
return t('OTA Updates');
case 'react_native_context':
return t('React Native');
+ case 'accessibility':
+ return t('Accessibility');
+ case 'flutter_context':
+ return t('Flutter');
+ case 'dart_context':
+ return t('Dart');
default:
return contextType;
}
@@ -435,6 +445,14 @@ export function getFormattedContextData({
return getCultureContextData({data: contextValue, meta});
case 'missing_instrumentation':
return getMissingInstrumentationContextData({data: contextValue, meta});
+ case 'accessibility':
+ return getAccessibilityContextData({data: contextValue, meta});
+ case 'react_native_context':
+ return getReactNativeContextData({data: contextValue, meta});
+ case 'flutter_context':
+ return getFlutterContextData({data: contextValue, meta});
+ case 'dart_context':
+ return getDartContextData({data: contextValue, meta});
default:
return getContextKeys({data: contextValue}).map(ctxKey => ({
key: ctxKey,
diff --git a/static/app/types/event.tsx b/static/app/types/event.tsx
index e159a6de9b1b..ad772db2ae15 100644
--- a/static/app/types/event.tsx
+++ b/static/app/types/event.tsx
@@ -433,6 +433,13 @@ export enum DeviceContextKey {
SUPPORTS_LOCATION_SERVICE = 'supports_location_service',
SUPPORTS_VIBRATION = 'supports_vibration',
USABLE_MEMORY = 'usable_memory',
+ TIMEZONE = 'timezone',
+ LOCALE = 'locale',
+ ARCHS = 'archs',
+ CHIPSET = 'chipset',
+ CONNECTION_TYPE = 'connection_type',
+ LOW_POWER_MODE = 'low_power_mode',
+ THERMAL_STATE = 'thermal_state',
}
// https://develop.sentry.dev/sdk/event-payloads/contexts/#device-context
@@ -478,10 +485,16 @@ export interface DeviceContext
[DeviceContextKey.SUPPORTS_LOCATION_SERVICE]?: boolean;
[DeviceContextKey.SUPPORTS_VIBRATION]?: boolean;
[DeviceContextKey.USABLE_MEMORY]?: number;
+ [DeviceContextKey.LOCALE]?: string;
+ [DeviceContextKey.ARCHS]?: string[];
+ [DeviceContextKey.CHIPSET]?: string;
+ [DeviceContextKey.CONNECTION_TYPE]?: string;
+ [DeviceContextKey.LOW_POWER_MODE]?: boolean;
+ // This field is deprecated in favour of timezone field in culture context
+ [DeviceContextKey.TIMEZONE]?: string;
+ [DeviceContextKey.THERMAL_STATE]?: string;
// This field is deprecated in favour of locale field in culture context
language?: string;
- // This field is deprecated in favour of timezone field in culture context
- timezone?: string;
}
enum RuntimeContextKey {