Skip to content

Commit dea644a

Browse files
authored
feat(preprod): Wireup app icon frontend (#102118)
Queries the new preprod `files/images/` API to get the app icon if available. If not available, falls back to a Sentry purple icon with the first character of the app's name. <img width="510" height="308" alt="Screenshot 2025-10-30 at 11 22 41 AM" src="https://github.com/user-attachments/assets/fa03494f-bac7-43c7-856b-f9ff5f4ceb17" />
1 parent 83d3884 commit dea644a

File tree

2 files changed

+28
-8
lines changed

2 files changed

+28
-8
lines changed

static/app/views/preprod/buildDetails/sidebar/buildDetailsSidebarAppInfo.tsx

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {useState} from 'react';
12
import styled from '@emotion/styled';
23
import {PlatformIcon} from 'platformicons';
34

@@ -10,6 +11,7 @@ import Feature from 'sentry/components/acl/feature';
1011
import {IconClock, IconFile, IconJson, IconLink, IconMobile} from 'sentry/icons';
1112
import {t} from 'sentry/locale';
1213
import {getFormat, getFormattedDate, getUtcToSystem} from 'sentry/utils/dates';
14+
import useOrganization from 'sentry/utils/useOrganization';
1315
import {openInstallModal} from 'sentry/views/preprod/components/installModal';
1416
import {type BuildDetailsAppInfo} from 'sentry/views/preprod/types/buildDetailsTypes';
1517
import {
@@ -27,19 +29,35 @@ interface BuildDetailsSidebarAppInfoProps {
2729
}
2830

2931
export function BuildDetailsSidebarAppInfo(props: BuildDetailsSidebarAppInfoProps) {
32+
const organization = useOrganization();
3033
const labels = getLabels(props.appInfo.platform ?? undefined);
34+
const [imageError, setImageError] = useState(false);
3135

3236
const datetimeFormat = getFormat({
3337
seconds: true,
3438
timeZone: true,
3539
});
3640

41+
let iconUrl = null;
42+
if (props.appInfo.app_icon_id) {
43+
iconUrl = `/api/0/projects/${organization.slug}/${props.projectId}/files/images/${props.appInfo.app_icon_id}/`;
44+
}
45+
3746
return (
3847
<Flex direction="column" gap="xl">
3948
<Flex align="center" gap="sm">
40-
<AppIcon>
49+
{iconUrl && !imageError && (
50+
<AppIcon
51+
src={iconUrl}
52+
alt="App Icon"
53+
width={24}
54+
height={24}
55+
onError={() => setImageError(true)}
56+
/>
57+
)}
58+
{(!iconUrl || imageError) && (
4159
<AppIconPlaceholder>{props.appInfo.name?.charAt(0) || ''}</AppIconPlaceholder>
42-
</AppIcon>
60+
)}
4361
{props.appInfo.name && <Heading as="h3">{props.appInfo.name}</Heading>}
4462
</Flex>
4563

@@ -133,19 +151,20 @@ export function BuildDetailsSidebarAppInfo(props: BuildDetailsSidebarAppInfoProp
133151
);
134152
}
135153

136-
const AppIcon = styled('div')`
154+
const AppIcon = styled('img')`
155+
border-radius: 4px;
156+
`;
157+
158+
const AppIconPlaceholder = styled('div')`
137159
width: 24px;
138160
height: 24px;
139161
border-radius: 4px;
140-
background: #ff6600;
141162
display: flex;
142163
align-items: center;
143164
justify-content: center;
144165
flex-shrink: 0;
145-
`;
146-
147-
const AppIconPlaceholder = styled('div')`
148-
color: white;
166+
background: ${p => p.theme.purple400};
167+
color: ${p => p.theme.white};
149168
font-weight: ${p => p.theme.fontWeight.bold};
150169
font-size: ${p => p.theme.fontSize.sm};
151170
`;

static/app/views/preprod/types/buildDetailsTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export interface BuildDetailsApiResponse {
1313
}
1414

1515
export interface BuildDetailsAppInfo {
16+
app_icon_id?: string | null;
1617
android_app_info?: AndroidAppInfo | null;
1718
app_id?: string | null;
1819
apple_app_info?: AppleAppInfo | null;

0 commit comments

Comments
 (0)