Files
2024-02-15 17:01:50 +00:00

192 lines
5.1 KiB
TypeScript

import {useCallback} from 'react';
import styled from '@emotion/styled';
import * as Sentry from '@sentry/react';
import {openInviteMembersModal} from 'sentry/actionCreators/modal';
import UserAvatar from 'sentry/components/avatar/userAvatar';
import {Button} from 'sentry/components/button';
import CommitLink from 'sentry/components/commitLink';
import {Hovercard} from 'sentry/components/hovercard';
import Link from 'sentry/components/links/link';
import PanelItem from 'sentry/components/panels/panelItem';
import TextOverflow from 'sentry/components/textOverflow';
import TimeSince from 'sentry/components/timeSince';
import {IconWarning} from 'sentry/icons';
import {t, tct} from 'sentry/locale';
import ConfigStore from 'sentry/stores/configStore';
import {space} from 'sentry/styles/space';
import type {Commit} from 'sentry/types';
export function formatCommitMessage(message: string | null) {
if (!message) {
return t('No message provided');
}
return message.split(/\n/)[0];
}
export interface CommitRowProps {
commit: Commit;
customAvatar?: React.ReactNode;
onCommitClick?: (commit: Commit) => void;
onPullRequestClick?: () => void;
}
function CommitRow({
commit,
customAvatar,
onPullRequestClick,
onCommitClick,
}: CommitRowProps) {
const handleInviteClick = useCallback(() => {
if (!commit.author?.email) {
Sentry.captureException(
new Error(`Commit author has no email or id, invite flow is broken.`)
);
return;
}
openInviteMembersModal({
initialData: [
{
emails: new Set([commit.author.email]),
},
],
source: 'suspect_commit',
});
}, [commit.author]);
const user = ConfigStore.get('user');
const isUser = user?.id === commit.author?.id;
return (
<StyledPanelItem key={commit.id} data-test-id="commit-row">
{customAvatar ? (
customAvatar
) : commit.author && commit.author.id === undefined ? (
<AvatarWrapper>
<Hovercard
skipWrapper
body={
<EmailWarning>
{tct(
'The email [actorEmail] is not a member of your organization. [inviteUser:Invite] them or link additional emails in [accountSettings:account settings].',
{
actorEmail: <strong>{commit.author.email}</strong>,
accountSettings: <StyledLink to="/settings/account/emails/" />,
inviteUser: <StyledLink to="" onClick={handleInviteClick} />,
}
)}
</EmailWarning>
}
>
<UserAvatar size={36} user={commit.author} />
<EmailWarningIcon data-test-id="email-warning">
<IconWarning size="xs" />
</EmailWarningIcon>
</Hovercard>
</AvatarWrapper>
) : (
<div>
<UserAvatar size={36} user={commit.author} />
</div>
)}
<CommitMessage>
<Message>{formatCommitMessage(commit.message)}</Message>
<Meta>
{tct('[author] committed [commitLink] \u2022 [date]', {
author: (
<strong>
{isUser ? t('You') : commit.author?.name ?? t('Unknown author')}
</strong>
),
commitLink: (
<CommitLink
inline
showIcon={false}
commitId={commit.id}
repository={commit.repository}
onClick={onCommitClick ? () => onCommitClick(commit) : undefined}
/>
),
date: (
<TimeSince
tooltipSuffix={commit.suspectCommitType}
date={commit.dateCreated}
/>
),
})}
</Meta>
</CommitMessage>
{commit.pullRequest?.externalUrl && (
<Button
external
href={commit.pullRequest.externalUrl}
onClick={onPullRequestClick}
>
{t('View Pull Request')}
</Button>
)}
</StyledPanelItem>
);
}
const StyledPanelItem = styled(PanelItem)`
display: flex;
align-items: center;
gap: ${space(2)};
`;
const AvatarWrapper = styled('div')`
position: relative;
`;
const EmailWarning = styled('div')`
font-size: ${p => p.theme.fontSizeSmall};
line-height: 1.4;
margin: -4px;
`;
const StyledLink = styled(Link)`
color: ${p => p.theme.textColor};
border-bottom: 1px dotted ${p => p.theme.textColor};
&:hover {
color: ${p => p.theme.textColor};
}
`;
const EmailWarningIcon = styled('span')`
position: absolute;
bottom: -6px;
right: -7px;
line-height: 12px;
border-radius: 50%;
border: 1px solid ${p => p.theme.background};
background: ${p => p.theme.yellow200};
padding: 1px 2px 3px 2px;
`;
const CommitMessage = styled('div')`
flex: 1;
flex-direction: column;
min-width: 0;
margin-right: ${space(2)};
`;
const Message = styled(TextOverflow)`
font-size: ${p => p.theme.fontSizeLarge};
line-height: 1.2;
`;
const Meta = styled(TextOverflow)`
font-size: 13px;
line-height: 1.5;
margin: 0;
color: ${p => p.theme.subText};
`;
export {CommitRow};