import {Fragment} from 'react'; import styled from '@emotion/styled'; import startCase from 'lodash/startCase'; import moment from 'moment-timezone'; import ContextData from 'sentry/components/contextData'; import {t} from 'sentry/locale'; import plugins from 'sentry/plugins'; import ConfigStore from 'sentry/stores/configStore'; import space from 'sentry/styles/space'; import {Event, KeyValueListData} from 'sentry/types'; import {defined} from 'sentry/utils'; const CONTEXT_TYPES = { default: require('sentry/components/events/contexts/default').default, app: require('sentry/components/events/contexts/app').AppEventContext, device: require('sentry/components/events/contexts/device').DeviceEventContext, browser: require('sentry/components/events/contexts/browser').BrowserEventContext, os: require('sentry/components/events/contexts/operatingSystem') .OperatingSystemEventContext, runtime: require('sentry/components/events/contexts/runtime').RuntimeEventContext, user: require('sentry/components/events/contexts/user').UserEventContext, gpu: require('sentry/components/events/contexts/gpu').GPUEventContext, trace: require('sentry/components/events/contexts/trace').TraceEventContext, // 'redux.state' will be replaced with more generic context called 'state' 'redux.state': require('sentry/components/events/contexts/redux').default, state: require('sentry/components/events/contexts/state').StateEventContext, }; export function getContextComponent(type: string) { return CONTEXT_TYPES[type] || plugins.contexts[type] || CONTEXT_TYPES.default; } export function getSourcePlugin(pluginContexts: Array, contextType: string) { if (CONTEXT_TYPES[contextType]) { return null; } for (const plugin of pluginContexts) { if (plugin.contexts.indexOf(contextType) >= 0) { return plugin; } } return null; } export function getRelativeTimeFromEventDateCreated( eventDateCreated: string, timestamp?: string, showTimestamp = true ) { if (!defined(timestamp)) { return timestamp; } const dateTime = moment(timestamp); if (!dateTime.isValid()) { return timestamp; } const relativeTime = `(${dateTime.from(eventDateCreated, true)} ${t( 'before this event' )})`; if (!showTimestamp) { return {relativeTime}; } return ( {timestamp} {relativeTime} ); } // Typescript doesn't have types for DisplayNames yet and that's why the type assertion "any" is needed below. // There is currently an open PR that intends to introduce the types https://github.com/microsoft/TypeScript/pull/44022 export function getFullLanguageDescription(locale: string) { const sentryAppLanguageCode = ConfigStore.get('languageCode'); const [languageAbbreviation, countryAbbreviation] = locale.includes('_') ? locale.split('_') : locale.split('-'); try { const languageNames = new (Intl as any).DisplayNames(sentryAppLanguageCode, { type: 'language', }); const languageName = languageNames.of(languageAbbreviation); if (countryAbbreviation) { const regionNames = new (Intl as any).DisplayNames(sentryAppLanguageCode, { type: 'region', }); const countryName = regionNames.of(countryAbbreviation.toUpperCase()); return `${languageName} (${countryName})`; } return languageName; } catch { return locale; } } export function geKnownData({ data, knownDataTypes, meta, raw, onGetKnownDataDetails, }: { data: Data; knownDataTypes: string[]; onGetKnownDataDetails: (props: {data: Data; type: DataType}) => | { subject: string; value?: React.ReactNode; } | undefined; meta?: Record; raw?: boolean; }): KeyValueListData { const filteredTypes = knownDataTypes.filter(knownDataType => { if ( typeof data[knownDataType] !== 'number' && typeof data[knownDataType] !== 'boolean' && !data[knownDataType] ) { return !!meta?.[knownDataType]; } return true; }); return filteredTypes .map(type => { const knownDataDetails = onGetKnownDataDetails({ data, type: type as unknown as DataType, }); if (!knownDataDetails) { return null; } return { key: type, ...knownDataDetails, value: raw ? ( knownDataDetails.value ) : ( ), }; }) .filter(defined); } export function getUnknownData({ allData, knownKeys, meta, }: { allData: Record; knownKeys: string[]; meta?: NonNullable[keyof Event['_meta']]; }): KeyValueListData { return Object.entries(allData) .filter( ([key]) => key !== 'type' && key !== 'title' && !knownKeys.includes(key) && (typeof allData[key] !== 'number' && !allData[key] ? !!meta?.[key]?.[''] : true) ) .map(([key, value]) => ({ key, value, subject: startCase(key), meta: meta?.[key]?.[''], })); } const RelativeTime = styled('span')` color: ${p => p.theme.subText}; margin-left: ${space(0.5)}; `;