import {Fragment} from 'react';
import {browserHistory} from 'react-router';
import styled from '@emotion/styled';
import {Location} from 'history';
import {archiveRelease, restoreRelease} from 'sentry/actionCreators/release';
import {Client} from 'sentry/api';
import ButtonBar from 'sentry/components/buttonBar';
import {openConfirmModal} from 'sentry/components/confirm';
import DropdownMenuControl from 'sentry/components/dropdownMenuControl';
import ProjectBadge from 'sentry/components/idBadge/projectBadge';
import NavigationButtonGroup from 'sentry/components/navigationButtonGroup';
import TextOverflow from 'sentry/components/textOverflow';
import Tooltip from 'sentry/components/tooltip';
import {IconEllipsis} from 'sentry/icons';
import {t, tct, tn} from 'sentry/locale';
import space from 'sentry/styles/space';
import {Organization, Release, ReleaseMeta} from 'sentry/types';
import {trackAnalyticsEvent} from 'sentry/utils/analytics';
import {formatVersion} from 'sentry/utils/formatters';
import {isReleaseArchived} from '../../utils';
type Props = {
location: Location;
organization: Organization;
projectSlug: string;
refetchData: () => void;
release: Release;
releaseMeta: ReleaseMeta;
};
function ReleaseActions({
location,
organization,
projectSlug,
release,
releaseMeta,
refetchData,
}: Props) {
async function handleArchive() {
try {
await archiveRelease(new Client(), {
orgSlug: organization.slug,
projectSlug,
releaseVersion: release.version,
});
browserHistory.push(`/organizations/${organization.slug}/releases/`);
} catch {
// do nothing, action creator is already displaying error message
}
}
async function handleRestore() {
try {
await restoreRelease(new Client(), {
orgSlug: organization.slug,
projectSlug,
releaseVersion: release.version,
});
refetchData();
} catch {
// do nothing, action creator is already displaying error message
}
}
function getProjectList() {
const maxVisibleProjects = 5;
const visibleProjects = releaseMeta.projects.slice(0, maxVisibleProjects);
const numberOfCollapsedProjects =
releaseMeta.projects.length - visibleProjects.length;
return (
{visibleProjects.map(project => (
))}
{numberOfCollapsedProjects > 0 && (
p.slug)
.join(', ')}
>
+ {tn('%s other project', '%s other projects', numberOfCollapsedProjects)}
)}
);
}
function getModalHeader(title: React.ReactNode) {
return (
{title}
);
}
function getModalMessage(message: React.ReactNode) {
return (
{message}
{getProjectList()}
{t('Are you sure you want to do this?')}
);
}
function replaceReleaseUrl(toRelease: string | null) {
return toRelease
? {
pathname: location.pathname
.replace(encodeURIComponent(release.version), toRelease)
.replace(release.version, toRelease),
query: {...location.query, activeRepo: undefined},
}
: '';
}
function handleNavigationClick(direction: string) {
trackAnalyticsEvent({
eventKey: `release_detail.pagination`,
eventName: `Release Detail: Pagination`,
organization_id: parseInt(organization.id, 10),
direction,
});
}
const menuItems = [
isReleaseArchived(release)
? {
key: 'restore',
label: t('Restore'),
onAction: () =>
openConfirmModal({
onConfirm: handleRestore,
header: getModalHeader(
tct('Restore Release [release]', {
release: formatVersion(release.version),
})
),
message: getModalMessage(
tn(
'You are restoring this release for the following project:',
'By restoring this release, you are also restoring it for the following projects:',
releaseMeta.projects.length
)
),
cancelText: t('Nevermind'),
confirmText: t('Restore'),
}),
}
: {
key: 'archive',
label: t('Archive'),
onAction: () =>
openConfirmModal({
onConfirm: handleArchive,
header: getModalHeader(
tct('Archive Release [release]', {
release: formatVersion(release.version),
})
),
message: getModalMessage(
tn(
'You are archiving this release for the following project:',
'By archiving this release, you are also archiving it for the following projects:',
releaseMeta.projects.length
)
),
cancelText: t('Nevermind'),
confirmText: t('Archive'),
}),
},
];
const {
nextReleaseVersion,
prevReleaseVersion,
firstReleaseVersion,
lastReleaseVersion,
} = release.currentProjectMeta;
return (
handleNavigationClick('oldest')}
onOlderClick={() => handleNavigationClick('older')}
onNewerClick={() => handleNavigationClick('newer')}
onNewestClick={() => handleNavigationClick('newest')}
/>
,
'aria-label': t('Actions'),
}}
position="bottom-end"
/>
);
}
const ProjectsWrapper = styled('div')`
margin: ${space(2)} 0 ${space(2)} ${space(2)};
display: grid;
gap: ${space(0.5)};
img {
border: none !important;
box-shadow: none !important;
}
`;
export default ReleaseActions;