Browse Source

ref(js): Move context icons to a codesplit react component (#39080)

Evan Purkhiser 2 years ago
parent
commit
a9d4c9ca14

+ 6 - 1
static/app/components/events/attachmentViewers/imageViewer.tsx

@@ -17,7 +17,12 @@ type Props = Omit<ViewerProps, 'attachment'> & {
 function ImageViewer({className, onLoad, onError, ...props}: Props) {
   return (
     <Container className={className}>
-      <img src={getAttachmentUrl(props, true)} onLoad={onLoad} onError={onError} />
+      <img
+        data-test-id="image-viewer"
+        src={getAttachmentUrl(props, true)}
+        onLoad={onLoad}
+        onError={onError}
+      />
     </Container>
   );
 }

+ 139 - 0
static/app/components/events/contextSummary/contextIcon.tsx

@@ -0,0 +1,139 @@
+import {css} from '@emotion/react';
+import logoAmazon from 'sentry-logos/logo-amazon.svg';
+import logoAmd from 'sentry-logos/logo-amd.svg';
+import logoAndroid from 'sentry-logos/logo-android.svg';
+import logoAndroidPhone from 'sentry-logos/logo-android-phone.svg';
+import logoAndroidTablet from 'sentry-logos/logo-android-tablet.svg';
+import logoApple from 'sentry-logos/logo-apple.svg';
+import logoApplePhone from 'sentry-logos/logo-apple-phone.svg';
+import logoAppleTablet from 'sentry-logos/logo-apple-tablet.svg';
+import logoAppleTv from 'sentry-logos/logo-apple-tv.svg';
+import logoAppleWatch from 'sentry-logos/logo-apple-watch.svg';
+import logoArm from 'sentry-logos/logo-arm.svg';
+import logoChrome from 'sentry-logos/logo-chrome.svg';
+import logoChromium from 'sentry-logos/logo-chromium.svg';
+import logoCrystal from 'sentry-logos/logo-crystal.svg';
+import logoDotnet from 'sentry-logos/logo-dotnet.svg';
+import logoEdgeNew from 'sentry-logos/logo-edge-new.svg';
+import logoEdgeOld from 'sentry-logos/logo-edge-old.svg';
+import logoElectron from 'sentry-logos/logo-electron.svg';
+import logoFirefox from 'sentry-logos/logo-firefox.svg';
+import logoGoogle from 'sentry-logos/logo-google.svg';
+import logoIe from 'sentry-logos/logo-ie.svg';
+import logoLinux from 'sentry-logos/logo-linux.svg';
+import logoMonogorilla from 'sentry-logos/logo-monogorilla.svg';
+import logoMotorola from 'sentry-logos/logo-motorola.svg';
+import logoNetcore from 'sentry-logos/logo-netcore.svg';
+import logoNetframework from 'sentry-logos/logo-netframework.svg';
+import logoNvidia from 'sentry-logos/logo-nvidia.svg';
+import logoOpera from 'sentry-logos/logo-opera.svg';
+import logoPhp from 'sentry-logos/logo-php.svg';
+import logoPlaystation from 'sentry-logos/logo-playstation.svg';
+import logoPython from 'sentry-logos/logo-python.svg';
+import logoQq from 'sentry-logos/logo-qq.svg';
+import logoRuby from 'sentry-logos/logo-ruby.svg';
+import logoSafari from 'sentry-logos/logo-safari.svg';
+import logoSamsung from 'sentry-logos/logo-samsung.svg';
+import logoUbuntu from 'sentry-logos/logo-ubuntu.svg';
+import logoUnity from 'sentry-logos/logo-unity.svg';
+import logoUnknown from 'sentry-logos/logo-unknown.svg';
+import logoWindows from 'sentry-logos/logo-windows.svg';
+
+import ConfigStore from 'sentry/stores/configStore';
+import {useLegacyStore} from 'sentry/stores/useLegacyStore';
+
+const LOGO_MAPPING = {
+  'android-phone': logoAndroidPhone,
+  'android-tablet': logoAndroidTablet,
+  'chrome-mobile': logoChrome,
+  'chrome-mobile-ios': logoChrome,
+  'internet-explorer': logoIe,
+  'legacy-edge': logoEdgeOld,
+  'mac-os-x': logoApple,
+  'mobile-safari': logoSafari,
+  'net-core': logoNetcore,
+  'net-framework': logoNetframework,
+  'qq-browser': logoQq,
+  amazon: logoAmazon,
+  amd: logoAmd,
+  android: logoAndroid,
+  apple: logoApple,
+  appletv: logoAppleTv,
+  arm: logoArm,
+  chrome: logoChrome,
+  chromium: logoChromium,
+  cpython: logoPython,
+  crystal: logoCrystal,
+  darwin: logoApple,
+  edge: logoEdgeNew,
+  electron: logoElectron,
+  firefox: logoFirefox,
+  google: logoGoogle,
+  il: logoUnity,
+  ios: logoApple,
+  ipad: logoAppleTablet,
+  iphone: logoApplePhone,
+  ipod: logoApplePhone,
+  linux: logoLinux,
+  mac: logoApple,
+  macos: logoApple,
+  mono: logoMonogorilla,
+  motorola: logoMotorola,
+  net: logoDotnet,
+  nvidia: logoNvidia,
+  opera: logoOpera,
+  php: logoPhp,
+  playstation: logoPlaystation,
+  python: logoPython,
+  ruby: logoRuby,
+  safari: logoSafari,
+  samsung: logoSamsung,
+  tvos: logoApple,
+  ubuntu: logoUbuntu,
+  watch: logoAppleWatch,
+  watchos: logoApple,
+  windows: logoWindows,
+};
+
+// The icons in this list will be inverted when the theme is set to dark mode
+const INVERT_IN_DARKMODE = [
+  'darwin',
+  'ios',
+  'macos',
+  'tvos',
+  'mac-os-x',
+  'mac',
+  'apple',
+  'watchos',
+];
+
+const darkCss = css`
+  filter: invert(100%);
+  opacity: 0.8;
+`;
+
+function getLogoImage(name: string) {
+  if (name.startsWith('amd-')) {
+    return logoAmd;
+  }
+
+  if (name.startsWith('nvidia-')) {
+    return logoNvidia;
+  }
+
+  return LOGO_MAPPING[name] ?? logoUnknown;
+}
+
+type Props = {
+  name: string;
+};
+
+function ContextIcon({name}: Props) {
+  // Apply darkmode CSS to icon when in darkmode
+  const isDarkmode = useLegacyStore(ConfigStore).theme === 'dark';
+  const extraCass = isDarkmode && INVERT_IN_DARKMODE.includes(name) ? darkCss : null;
+
+  return <img height="32px" width="32px" css={extraCass} src={getLogoImage(name)} />;
+}
+
+export default ContextIcon;

+ 6 - 10
static/app/components/events/contextSummary/contextSummaryDevice.tsx

@@ -5,17 +5,13 @@ import {AnnotatedText} from 'sentry/components/events/meta/annotatedText';
 import TextOverflow from 'sentry/components/textOverflow';
 import {t} from 'sentry/locale';
 import space from 'sentry/styles/space';
-import {Event, Meta} from 'sentry/types';
+import {Meta} from 'sentry/types';
 import {defined} from 'sentry/utils';
 
 import ContextSummaryNoSummary from './contextSummaryNoSummary';
-import generateClassName from './generateClassName';
 import Item from './item';
-
-type Props = {
-  data: Data;
-  meta: NonNullable<Event['_meta']>['device'];
-};
+import {ContextItemProps} from './types';
+import {generateIconName} from './utils';
 
 type Data = {
   arch?: string;
@@ -29,6 +25,8 @@ type SubTitle = {
   meta?: Meta;
 };
 
+type Props = ContextItemProps<Data, 'device'>;
+
 export function ContextSummaryDevice({data, meta}: Props) {
   if (Object.keys(data).length === 0) {
     return <ContextSummaryNoSummary title={t('Unknown Device')} />;
@@ -73,12 +71,10 @@ export function ContextSummaryDevice({data, meta}: Props) {
     return null;
   };
 
-  // TODO(dcramer): we need a better way to parse it
-  const className = generateClassName(data.model);
   const subTitle = getSubTitle();
 
   return (
-    <Item className={className} icon={<span className="context-item-icon" />}>
+    <Item icon={generateIconName(data.model)}>
       <h3>{renderName()}</h3>
       {subTitle && (
         <TextOverflow isParagraph data-test-id="context-sub-title">

+ 6 - 11
static/app/components/events/contextSummary/contextSummaryGPU.tsx

@@ -4,17 +4,13 @@ import {AnnotatedText} from 'sentry/components/events/meta/annotatedText';
 import TextOverflow from 'sentry/components/textOverflow';
 import {t} from 'sentry/locale';
 import space from 'sentry/styles/space';
-import {Event, Meta} from 'sentry/types';
+import {Meta} from 'sentry/types';
 import {defined} from 'sentry/utils';
 
 import ContextSummaryNoSummary from './contextSummaryNoSummary';
-import generateClassName from './generateClassName';
 import Item from './item';
-
-type Props = {
-  data: Data;
-  meta: NonNullable<Event['_meta']>['gpu'];
-};
+import {ContextItemProps} from './types';
+import {generateIconName} from './utils';
 
 type Data = {
   name: string;
@@ -27,6 +23,8 @@ type VersionElement = {
   meta?: Meta;
 };
 
+type Props = ContextItemProps<Data, 'gpu'>;
+
 export function ContextSummaryGPU({data, meta}: Props) {
   if (Object.keys(data).length === 0) {
     return <ContextSummaryNoSummary title={t('Unknown GPU')} />;
@@ -50,10 +48,7 @@ export function ContextSummaryGPU({data, meta}: Props) {
   const versionElement = getVersionElement();
 
   return (
-    <Item
-      className={generateClassName(data.vendor_name ? data.vendor_name : data.name)}
-      icon={<span className="context-item-icon" />}
-    >
+    <Item icon={generateIconName(data.vendor_name ? data.vendor_name : data.name)}>
       <h3>
         <AnnotatedText value={data.name} meta={meta.name?.['']} />
       </h3>

+ 6 - 14
static/app/components/events/contextSummary/contextSummaryGeneric.tsx

@@ -4,25 +4,20 @@ import {AnnotatedText} from 'sentry/components/events/meta/annotatedText';
 import TextOverflow from 'sentry/components/textOverflow';
 import {t} from 'sentry/locale';
 import space from 'sentry/styles/space';
-import {Event} from 'sentry/types';
 import {defined} from 'sentry/utils';
 
 import ContextSummaryNoSummary from './contextSummaryNoSummary';
-import generateClassName from './generateClassName';
 import Item from './item';
-
-type Props = {
-  data: Data;
-  meta: NonNullable<Event['_meta']>[keyof Event['_meta']];
-  unknownTitle: string;
-  omitUnknownVersion?: boolean;
-};
+import {ContextItemProps} from './types';
+import {generateIconName} from './utils';
 
 type Data = {
   name: string;
   version?: string;
 };
 
+type Props = ContextItemProps<Data, any>;
+
 export function ContextSummaryGeneric({
   data,
   unknownTitle,
@@ -30,14 +25,11 @@ export function ContextSummaryGeneric({
   meta,
 }: Props) {
   if (Object.keys(data).length === 0) {
-    return <ContextSummaryNoSummary title={unknownTitle} />;
+    return <ContextSummaryNoSummary title={unknownTitle ?? t('Unknown')} />;
   }
 
   return (
-    <Item
-      className={generateClassName(data.name, data.version)}
-      icon={<span className="context-item-icon" />}
-    >
+    <Item icon={generateIconName(data.name, data.version)}>
       <h3>
         <AnnotatedText value={data.name} meta={meta.name?.['']} />
       </h3>

+ 1 - 1
static/app/components/events/contextSummary/contextSummaryNoSummary.tsx

@@ -5,7 +5,7 @@ type Props = {
 };
 
 const ContextSummaryNoSummary = ({title}: Props) => (
-  <Item icon={<span className="context-item-icon" />}>
+  <Item>
     <h3 data-test-id="no-summary-title">{title}</h3>
   </Item>
 );

+ 6 - 11
static/app/components/events/contextSummary/contextSummaryOS.tsx

@@ -4,17 +4,13 @@ import {AnnotatedText} from 'sentry/components/events/meta/annotatedText';
 import TextOverflow from 'sentry/components/textOverflow';
 import {t} from 'sentry/locale';
 import space from 'sentry/styles/space';
-import {Event, Meta} from 'sentry/types';
+import {Meta} from 'sentry/types';
 import {defined} from 'sentry/utils';
 
 import ContextSummaryNoSummary from './contextSummaryNoSummary';
-import generateClassName from './generateClassName';
 import Item from './item';
-
-type Props = {
-  data: Data;
-  meta: NonNullable<Event['_meta']>['os'] | NonNullable<Event['_meta']>['client_os'];
-};
+import {ContextItemProps} from './types';
+import {generateIconName} from './utils';
 
 type Data = {
   name: string | boolean;
@@ -28,6 +24,8 @@ type VersionElement = {
   meta?: Meta;
 };
 
+type Props = ContextItemProps<Data, 'os' | 'client_os'>;
+
 export function ContextSummaryOS({data, meta}: Props) {
   if (Object.keys(data).length === 0) {
     return <ContextSummaryNoSummary title={t('Unknown OS')} />;
@@ -59,10 +57,7 @@ export function ContextSummaryOS({data, meta}: Props) {
   const versionElement = getVersionElement();
 
   return (
-    <Item
-      className={generateClassName(data.name)}
-      icon={<span className="context-item-icon" />}
-    >
+    <Item icon={generateIconName(data.name)}>
       <h3>
         <AnnotatedText value={data.name} meta={meta.name?.['']} />
       </h3>

+ 7 - 14
static/app/components/events/contextSummary/contextSummaryUser.tsx

@@ -6,17 +6,13 @@ import {AnnotatedText} from 'sentry/components/events/meta/annotatedText';
 import TextOverflow from 'sentry/components/textOverflow';
 import {t} from 'sentry/locale';
 import space from 'sentry/styles/space';
-import {AvatarUser, Event, Meta} from 'sentry/types';
+import {AvatarUser, Meta} from 'sentry/types';
 import {EventUser} from 'sentry/types/event';
 import {defined} from 'sentry/utils';
 
 import ContextSummaryNoSummary from './contextSummaryNoSummary';
 import Item from './item';
-
-type Props = {
-  data: EventUser;
-  meta: NonNullable<Event['_meta']>['user'];
-};
+import {ContextItemProps} from './types';
 
 type UserTitle = {
   value: string;
@@ -29,6 +25,8 @@ type UserDetails = {
   value?: string;
 };
 
+type Props = ContextItemProps<EventUser, 'user'>;
+
 export function ContextSummaryUser({data, meta}: Props) {
   const user = removeFilterMaskedEntries(data);
 
@@ -96,18 +94,13 @@ export function ContextSummaryUser({data, meta}: Props) {
   }
 
   const icon = userTitle ? (
-    <UserAvatar
-      user={user as AvatarUser}
-      size={32}
-      className="context-item-icon"
-      gravatar={false}
-    />
+    <UserAvatar user={user as AvatarUser} size={32} gravatar={false} />
   ) : (
-    <span className="context-item-icon" />
+    'unknown'
   );
 
   return (
-    <Item className="user" icon={icon}>
+    <Item icon={icon}>
       {userTitle && (
         <h3 data-test-id="user-title">
           <AnnotatedText value={userTitle.value} meta={userTitle.meta} />

+ 0 - 29
static/app/components/events/contextSummary/filterContexts.tsx

@@ -1,29 +0,0 @@
-import {Event} from 'sentry/types/event';
-
-import {Context} from '.';
-
-function filterContexts(event: Event, context: Context) {
-  // if the operating system is macOS, we want to hide devices called "Mac" which don't have any additional info
-  if (context.keys.includes('device')) {
-    const {model, arch} = event.contexts?.device || {};
-    const {name: os} = event.contexts?.os || event.contexts?.client_os || {};
-
-    if (model === 'Mac' && !arch && os?.toLowerCase().includes('mac')) {
-      return false;
-    }
-  }
-
-  // do not show the context summary if only runtime raw_description is defined (without name or version)
-  if (context.keys.includes('runtime')) {
-    if (
-      event.contexts.runtime?.raw_description &&
-      !(event.contexts.runtime?.name || event.contexts.runtime?.version)
-    ) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-export default filterContexts;

+ 0 - 46
static/app/components/events/contextSummary/generateClassName.tsx

@@ -1,46 +0,0 @@
-import {defined} from 'sentry/utils';
-
-function generateClassname(name?: string | boolean | null, version?: string): string {
-  if (!defined(name) || typeof name === 'boolean') {
-    return '';
-  }
-
-  const lowerCaseName = name.toLowerCase();
-
-  // amazon fire tv device id changes with version: AFTT, AFTN, AFTS, AFTA, AFTVA (alexa), ...
-  if (lowerCaseName.startsWith('aft')) {
-    return 'amazon';
-  }
-
-  if (lowerCaseName.startsWith('sm-') || lowerCaseName.startsWith('st-')) {
-    return 'samsung';
-  }
-
-  if (lowerCaseName.startsWith('moto')) {
-    return 'motorola';
-  }
-
-  if (lowerCaseName.startsWith('pixel')) {
-    return 'google';
-  }
-
-  const formattedName = name
-    .split(/\d/)[0]
-    .toLowerCase()
-    .replace(/[^a-z0-9\-]+/g, '-')
-    .replace(/\-+$/, '')
-    .replace(/^\-+/, '');
-
-  if (formattedName === 'edge' && version) {
-    const majorVersion = version.split('.')[0];
-    const isLegacyEdge = majorVersion >= '12' && majorVersion <= '18';
-
-    if (isLegacyEdge) {
-      return 'legacy-edge';
-    }
-  }
-
-  return formattedName;
-}
-
-export default generateClassname;

Some files were not shown because too many files changed in this diff