Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 77 additions & 2 deletions static/app/views/releases/components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,36 @@
import styled from '@emotion/styled';

import {Container} from '@sentry/scraps/layout';

import {FeatureBadge} from 'sentry/components/core/badge/featureBadge';
import {TabList} from 'sentry/components/core/tabs';
import FeedbackButton from 'sentry/components/feedbackButton/feedbackButton';
import * as Layout from 'sentry/components/layouts/thirds';
import {DatePageFilter} from 'sentry/components/organizations/datePageFilter';
import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter';
import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
import {ProjectPageFilter} from 'sentry/components/organizations/projectPageFilter';
import {PageHeadingQuestionTooltip} from 'sentry/components/pageHeadingQuestionTooltip';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';

type Props = {
activeDataset: 'releases' | 'mobile-builds';
mobileBuildsDatasetQuery: Record<string, any>;
pathname: string;
releasesDatasetQuery: Record<string, any>;
shouldShowMobileBuildsTab: boolean;
};

export default function Header() {
export default function Header({
activeDataset,
mobileBuildsDatasetQuery,
pathname,
releasesDatasetQuery,
shouldShowMobileBuildsTab,
}: Props) {
return (
<Layout.Header noActionWrap unified>
<Layout.Header noActionWrap unified={!shouldShowMobileBuildsTab}>
<Layout.HeaderContent unified>
<Layout.Title>
{t('Releases')}
Expand All @@ -27,6 +52,56 @@ export default function Header() {
}}
/>
</Layout.HeaderActions>
<Container paddingTop="xl">
<ReleasesPageFilterBar condensed>
<ProjectPageFilter />
<EnvironmentPageFilter />
<DatePageFilter
disallowArbitraryRelativeRanges
menuFooterMessage={t(
'Changing this date range will recalculate the release metrics. Select a supported date range from the options above.'
)}
/>
</ReleasesPageFilterBar>
{shouldShowMobileBuildsTab ? (
<Container paddingTop="xl">
<Layout.HeaderTabs
value={activeDataset}
aria-label={t('Releases dataset selector')}
>
<TabList aria-label={t('Releases dataset selector')}>
<TabList.Item
key="releases"
to={{pathname, query: releasesDatasetQuery}}
textValue={t('Releases')}
>
{t('Releases')}
</TabList.Item>
<TabList.Item
key="mobile-builds"
to={{pathname, query: mobileBuildsDatasetQuery}}
textValue={t('Mobile Builds')}
>
<span
style={{
display: 'inline-flex',
alignItems: 'center',
gap: space(0.5),
}}
>
{t('Mobile Builds')}
<FeatureBadge type="beta" />
</span>
</TabList.Item>
</TabList>
</Layout.HeaderTabs>
</Container>
) : null}
</Container>
</Layout.Header>
);
}

const ReleasesPageFilterBar = styled(PageFilterBar)`
grid-column: 1 / -1;
`;
15 changes: 4 additions & 11 deletions static/app/views/releases/detail/header/releaseHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,14 @@ import {URL_PARAM} from 'sentry/constants/pageFilters';
import {IconOpen} from 'sentry/icons';
import {t, tct} from 'sentry/locale';
import type {Organization} from 'sentry/types/organization';
import type {PlatformKey} from 'sentry/types/project';
import type {Release, ReleaseMeta, ReleaseProject} from 'sentry/types/release';
import {formatAbbreviatedNumber} from 'sentry/utils/formatters';
import normalizeUrl from 'sentry/utils/url/normalizeUrl';
import {isMobileRelease} from 'sentry/views/releases/utils';
import {makeReleasesPathname} from 'sentry/views/releases/utils/pathnames';

import ReleaseActions from './releaseActions';

const MOBILE_PLATFORMS: PlatformKey[] = [
'android',
'apple-ios',
'flutter',
'react-native',
];

type Props = {
location: Location;
organization: Organization;
Expand Down Expand Up @@ -86,7 +79,7 @@ function ReleaseHeader({
const numberOfMobileBuilds = releaseMeta.preprodBuildCount;

const buildsTab = {
title: tct('Builds [count]', {
title: tct('Mobile Builds [count]', {
count:
numberOfMobileBuilds === 0 ? (
<BadgeWrapper>
Expand All @@ -103,13 +96,13 @@ function ReleaseHeader({
</React.Fragment>
),
}),
textValue: t('Builds %s', numberOfMobileBuilds),
textValue: t('Mobile Builds %s', numberOfMobileBuilds),
to: `builds/`,
};

if (
organization.features?.includes('preprod-frontend-routes') &&
(numberOfMobileBuilds || MOBILE_PLATFORMS.includes(project.platform))
(numberOfMobileBuilds || isMobileRelease(project.platform, false))
) {
tabs.push(buildsTab);
}
Expand Down
90 changes: 69 additions & 21 deletions static/app/views/releases/list/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@ import {useCallback, useEffect, useMemo} from 'react';
import {forceCheck} from 'react-lazyload';
import styled from '@emotion/styled';

import {addMessage} from 'sentry/actionCreators/indicator';
import {fetchTagValues} from 'sentry/actionCreators/tags';
import * as Layout from 'sentry/components/layouts/thirds';
import LoadingError from 'sentry/components/loadingError';
import NoProjectMessage from 'sentry/components/noProjectMessage';
import {DatePageFilter} from 'sentry/components/organizations/datePageFilter';
import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter';
import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container';
import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
import {ProjectPageFilter} from 'sentry/components/organizations/projectPageFilter';
import {SearchQueryBuilder} from 'sentry/components/searchQueryBuilder';
import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
import {ALL_ACCESS_PROJECTS} from 'sentry/constants/pageFilters';
import {ReleasesSortOption} from 'sentry/constants/releases';
import {t} from 'sentry/locale';
import ProjectsStore from 'sentry/stores/projectsStore';
Expand Down Expand Up @@ -201,6 +199,65 @@ export default function ReleasesList() {
return projects?.find(p => p.id === `${selectedProjectId}`);
}, [selection.projects, projects]);

const shouldShowMobileBuildsTab = useMemo(() => {
const singleProjectSelected =
selection.projects?.length === 1 && selection.projects[0] !== ALL_ACCESS_PROJECTS;

if (!singleProjectSelected || !selectedProject?.platform) {
return false;
}

return (
organization.features?.includes('preprod-frontend-routes') &&
isMobileRelease(selectedProject.platform, false)
);
}, [organization.features, selectedProject?.platform, selection.projects]);

const activeDataset = useMemo(() => {
const dataset = decodeScalar(location.query.dataset);
if (dataset === 'mobile-builds' && shouldShowMobileBuildsTab) {
return 'mobile-builds';
}
return 'releases';
}, [location.query.dataset, shouldShowMobileBuildsTab]);

const releasesDatasetQuery = useMemo(() => {
const {dataset: _removed, ...restQuery} = location.query;
return restQuery;
}, [location.query]);

const mobileBuildsDatasetQuery = useMemo(() => {
const {dataset: _removed, ...restQuery} = location.query;
return {...restQuery, dataset: 'mobile-builds'};
}, [location.query]);

useEffect(() => {
const dataset = decodeScalar(location.query.dataset);
if (dataset !== 'mobile-builds') {
return;
}

if (shouldShowMobileBuildsTab) {
return;
}

const {dataset: _removed, ...queryWithoutDataset} = location.query;

addMessage(
t('Mobile builds are only available for supported mobile projects.'),
'error'
);
navigate(
{
pathname: location.pathname,
query: {
...queryWithoutDataset,
},
},
{replace: true}
);
}, [location.pathname, location.query, navigate, shouldShowMobileBuildsTab]);

const handleSearch = useCallback(
(query: string) => {
navigate({
Expand Down Expand Up @@ -325,26 +382,21 @@ export default function ReleasesList() {
<PageFiltersContainer showAbsolute={false} defaultSelection={selection}>
<SentryDocumentTitle title={t('Releases')} orgSlug={organization.slug} />
<NoProjectMessage organization={organization}>
<Header />
<Layout.Body>
<Header
activeDataset={activeDataset}
shouldShowMobileBuildsTab={shouldShowMobileBuildsTab}
releasesDatasetQuery={releasesDatasetQuery}
mobileBuildsDatasetQuery={mobileBuildsDatasetQuery}
pathname={location.pathname}
/>
<Layout.Body data-dataset={activeDataset}>
<Layout.Main width="full">
<ReleaseHealthCTA
organization={organization}
releases={releases}
selectedProject={selectedProject}
selection={selection}
/>
<ReleasesPageFilterBar condensed>
<ProjectPageFilter />
<EnvironmentPageFilter />
<DatePageFilter
disallowArbitraryRelativeRanges
menuFooterMessage={t(
'Changing this date range will recalculate the release metrics. Select a supported date range from the options above.'
)}
/>
</ReleasesPageFilterBar>

{shouldShowQuickstart ? null : (
<SortAndFilterWrapper>
<StyledSearchQueryBuilder
Expand Down Expand Up @@ -405,10 +457,6 @@ export default function ReleasesList() {
);
}

const ReleasesPageFilterBar = styled(PageFilterBar)`
margin-bottom: ${space(2)};
`;

const SortAndFilterWrapper = styled('div')`
display: grid;
grid-template-columns: 1fr repeat(3, max-content);
Expand Down
9 changes: 7 additions & 2 deletions static/app/views/releases/utils/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,13 @@ export const ADOPTION_STAGE_LABELS: Record<
},
};

export const isMobileRelease = (releaseProjectPlatform: PlatformKey) =>
([...mobile, ...desktop] as string[]).includes(releaseProjectPlatform);
export const isMobileRelease = (
releaseProjectPlatform: PlatformKey,
includeDesktop = true
) =>
([...mobile, ...(includeDesktop ? desktop : [])] as string[]).includes(
releaseProjectPlatform
);

/**
* Helper that escapes quotes and formats release version into release search
Expand Down
Loading