Просмотр исходного кода

chore(avatars) Use avatarUrl in UI components (#59131)

Now that we have server generated URLs for avatars we can start using
them so that avatar components don't need to pass around 3 separate
properties that need to be reassembled in UI code.
Mark Story 1 год назад
Родитель
Сommit
6bc83f29d4

+ 1 - 0
fixtures/js-stubs/organization.tsx

@@ -46,6 +46,7 @@ export function Organization(
     avatar: {
       avatarType: 'default',
       avatarUuid: null,
+      avatarUrl: null,
     },
     codecovAccess: false,
     dataScrubber: false,

+ 7 - 24
static/app/components/avatar/baseAvatar.tsx

@@ -40,10 +40,6 @@ const defaultProps: DefaultProps = {
    * The type of avatar being rendered.
    */
   type: 'letter_avatar',
-  /**
-   * Path to uploaded avatar (differs based on model type)
-   */
-  uploadPath: 'avatar',
   /**
    * Should avatar be round instead of a square
    */
@@ -65,16 +61,6 @@ type DefaultProps = {
    * The type of avatar being rendered.
    */
   type?: Avatar['avatarType'];
-  /**
-   * Path to uploaded avatar (differs based on model type)
-   */
-  uploadPath?:
-    | 'avatar'
-    | 'team-avatar'
-    | 'organization-avatar'
-    | 'project-avatar'
-    | 'sentry-app-avatar'
-    | 'doc-integration-avatar';
 };
 
 type BaseProps = DefaultProps & {
@@ -102,13 +88,9 @@ type BaseProps = DefaultProps & {
    */
   tooltipOptions?: Omit<TooltipProps, 'children' | 'title'>;
   /**
-   * The region domain that organization avatars are on
+   * Full URL to the uploaded avatar's image.
    */
-  uploadDomain?: string;
-  /**
-   * The uuid for the uploaded avatar.
-   */
-  uploadId?: string | null | undefined;
+  uploadUrl?: string | null | undefined;
 };
 
 type Props = BaseProps;
@@ -145,11 +127,12 @@ class BaseAvatar extends Component<Props, State> {
   }
 
   buildUploadUrl() {
-    const {uploadDomain, uploadPath, uploadId} = this.props;
+    const {uploadUrl} = this.props;
+    if (!uploadUrl) {
+      return '';
+    }
 
-    return `${uploadDomain || ''}/${uploadPath || 'avatar'}/${uploadId}/?${qs.stringify({
-      s: DEFAULT_REMOTE_SIZE,
-    })}`;
+    return `${uploadUrl}?${qs.stringify({s: DEFAULT_REMOTE_SIZE})}`;
   }
 
   handleLoad = () => {

+ 1 - 2
static/app/components/avatar/docIntegrationAvatar.tsx

@@ -14,8 +14,7 @@ function DocIntegrationAvatar({docIntegration, ...props}: Props) {
     <BaseAvatar
       {...props}
       type="upload"
-      uploadPath="doc-integration-avatar"
-      uploadId={docIntegration.avatar.avatarUuid}
+      uploadUrl={docIntegration.avatar.avatarUrl}
       title={docIntegration.name}
     />
   );

+ 15 - 2
static/app/components/avatar/index.spec.tsx

@@ -11,6 +11,7 @@ describe('Avatar', function () {
   const avatar: Avatar = {
     avatarType: 'gravatar',
     avatarUuid: '2d641b5d-8c74-44de-9cb6-fbd54701b35e',
+    avatarUrl: 'https://sentry.io/avatar/2d641b5d-8c74-44de-9cb6-fbd54701b35e/',
   };
 
   const user = {
@@ -159,6 +160,7 @@ describe('Avatar', function () {
         avatar: {
           avatarType: 'upload',
           avatarUuid: 'abc123def',
+          avatarUrl: 'https://us.sentry.io/organization-avatar/abc123def/',
         },
       });
 
@@ -213,10 +215,16 @@ describe('Avatar', function () {
     });
 
     it('renders the correct SentryApp depending on its props', async function () {
-      const colorAvatar = {avatarUuid: 'abc', avatarType: 'upload' as const, color: true};
+      const colorAvatar = {
+        avatarUuid: 'abc',
+        avatarType: 'upload' as const,
+        avatarUrl: 'https://sentry.io/sentry-app-avatar/abc/',
+        color: true,
+      };
       const simpleAvatar = {
         avatarUuid: 'def',
         avatarType: 'upload' as const,
+        avatarUrl: 'https://sentry.io/sentry-app-avatar/def/',
         color: false,
       };
 
@@ -243,7 +251,12 @@ describe('Avatar', function () {
     });
 
     it('renders the correct fallbacks for SentryAppAvatars', async function () {
-      const colorAvatar = {avatarUuid: 'abc', avatarType: 'upload' as const, color: true};
+      const colorAvatar = {
+        avatarUuid: 'abc',
+        avatarType: 'upload' as const,
+        avatarUrl: 'https://sentry.io/sentry-app-avatar/abc/',
+        color: true,
+      };
       const sentryApp = SentryApp({avatars: []});
 
       // No existing avatars

+ 2 - 4
static/app/components/avatar/organizationAvatar.tsx

@@ -4,7 +4,7 @@ import {explodeSlug} from 'sentry/utils';
 
 type Props = {
   organization?: OrganizationSummary;
-} & Omit<BaseAvatar['props'], 'uploadPath' | 'uploadId'>;
+} & BaseAvatar['props'];
 
 function OrganizationAvatar({organization, ...props}: Props) {
   if (!organization) {
@@ -17,9 +17,7 @@ function OrganizationAvatar({organization, ...props}: Props) {
     <BaseAvatar
       {...props}
       type={(organization.avatar && organization.avatar.avatarType) || 'letter_avatar'}
-      uploadPath="organization-avatar"
-      uploadId={organization.avatar && organization.avatar.avatarUuid}
-      uploadDomain={organization.links?.regionUrl}
+      uploadUrl={organization.avatar && organization.avatar.avatarUrl}
       letterId={slug}
       tooltip={slug}
       title={title}

+ 1 - 4
static/app/components/avatar/sentryAppAvatar.tsx

@@ -21,14 +21,11 @@ function SentryAppAvatar({isColor = true, sentryApp, isDefault, ...props}: Props
   if (isDefault || !avatarDetails || avatarDetails.avatarType === 'default') {
     return defaultSentryAppAvatar;
   }
-  const {sentryUrl} = window.__initialData?.links ?? {};
   return (
     <BaseAvatar
       {...props}
       type="upload"
-      uploadPath="sentry-app-avatar"
-      uploadId={avatarDetails?.avatarUuid}
-      uploadDomain={sentryUrl}
+      uploadUrl={avatarDetails?.avatarUrl}
       title={sentryApp?.name}
       backupAvatar={defaultSentryAppAvatar}
     />

+ 2 - 4
static/app/components/avatar/teamAvatar.tsx

@@ -2,9 +2,9 @@ import BaseAvatar from 'sentry/components/avatar/baseAvatar';
 import type {Team} from 'sentry/types';
 import {explodeSlug} from 'sentry/utils';
 
-interface TeamAvatarProps extends Omit<BaseAvatar['props'], 'uploadPath' | 'uploadId'> {
+type TeamAvatarProps = {
   team: Team | null | undefined;
-}
+} & BaseAvatar['props'];
 
 function TeamAvatar({team, tooltip: tooltipProp, ...props}: TeamAvatarProps) {
   if (!team) {
@@ -19,8 +19,6 @@ function TeamAvatar({team, tooltip: tooltipProp, ...props}: TeamAvatarProps) {
     <BaseAvatar
       {...props}
       type={(team.avatar && team.avatar.avatarType) || 'letter_avatar'}
-      uploadPath="team-avatar"
-      uploadId={team.avatar && team.avatar.avatarUuid}
       letterId={slug}
       tooltip={tooltip}
       title={title}

+ 3 - 6
static/app/components/avatar/userAvatar.tsx

@@ -55,27 +55,24 @@ function UserAvatar({
 
   const avatarData = isActor(user)
     ? {
-        uploadId: '',
         gravatarId: '',
         letterId: user.name,
         title: user.name,
+        uploadUrl: '',
       }
     : {
-        uploadId: user.avatar?.avatarUuid ?? '',
+        uploadUrl: user.avatar?.avatarUrl ?? '',
         gravatarId: user.email?.toLowerCase(),
         letterId: user.email || user.username || user.id || user.ip_address,
         title: user.name || user.email || user.username || '',
       };
-  const {sentryUrl} = window.__initialData?.links ?? {};
 
   return (
     <BaseAvatar
       round
       {...props}
       type={type}
-      uploadPath="avatar"
-      uploadId={avatarData.uploadId}
-      uploadDomain={sentryUrl}
+      uploadUrl={avatarData.uploadUrl}
       gravatarId={avatarData.gravatarId}
       letterId={avatarData.letterId}
       title={avatarData.title}

+ 1 - 0
static/app/types/core.tsx

@@ -14,6 +14,7 @@ import type {API_ACCESS_SCOPES} from 'sentry/constants';
 export type Avatar = {
   avatarType: 'letter_avatar' | 'upload' | 'gravatar' | 'background' | 'default';
   avatarUuid: string | null;
+  avatarUrl?: string | null;
   color?: boolean;
 };