|
@@ -3,13 +3,15 @@ import styled from '@emotion/styled';
|
|
|
import UserAvatar from 'sentry/components/avatar/userAvatar';
|
|
|
import {Button} from 'sentry/components/button';
|
|
|
import Collapsible from 'sentry/components/collapsible';
|
|
|
-import DeprecatedAsyncComponent from 'sentry/components/deprecatedAsyncComponent';
|
|
|
+import LoadingError from 'sentry/components/loadingError';
|
|
|
+import LoadingIndicator from 'sentry/components/loadingIndicator';
|
|
|
import * as SidebarSection from 'sentry/components/sidebarSection';
|
|
|
import {t, tn} from 'sentry/locale';
|
|
|
import {space} from 'sentry/styles/space';
|
|
|
import type {Commit, User} from 'sentry/types';
|
|
|
import {percent} from 'sentry/utils';
|
|
|
import {userDisplayName} from 'sentry/utils/formatters';
|
|
|
+import {useApiQuery} from 'sentry/utils/queryClient';
|
|
|
|
|
|
type GroupedAuthorCommits = {
|
|
|
[key: string]: {author: User | undefined; commitCount: number};
|
|
@@ -19,96 +21,91 @@ type Props = {
|
|
|
orgId: string;
|
|
|
projectSlug: string;
|
|
|
version: string;
|
|
|
-} & DeprecatedAsyncComponent['props'];
|
|
|
-
|
|
|
-type State = {
|
|
|
- commits: Commit[];
|
|
|
-} & DeprecatedAsyncComponent['state'];
|
|
|
-
|
|
|
-class CommitAuthorBreakdown extends DeprecatedAsyncComponent<Props, State> {
|
|
|
- shouldReload = true;
|
|
|
+};
|
|
|
|
|
|
- getEndpoints(): ReturnType<DeprecatedAsyncComponent['getEndpoints']> {
|
|
|
- const {orgId, projectSlug, version} = this.props;
|
|
|
+function CommitAuthorBreakdown({orgId, projectSlug, version}: Props) {
|
|
|
+ const commitsEndpoint = `/projects/${orgId}/${projectSlug}/releases/${encodeURIComponent(
|
|
|
+ version
|
|
|
+ )}/commits/`;
|
|
|
|
|
|
- const commitsEndpoint = `/projects/${orgId}/${projectSlug}/releases/${encodeURIComponent(
|
|
|
- version
|
|
|
- )}/commits/`;
|
|
|
+ const {
|
|
|
+ data: commits,
|
|
|
+ isLoading,
|
|
|
+ isError,
|
|
|
+ } = useApiQuery<Commit[]>([commitsEndpoint], {staleTime: 0});
|
|
|
|
|
|
- return [['commits', commitsEndpoint]];
|
|
|
+ if (isLoading) {
|
|
|
+ return <LoadingIndicator />;
|
|
|
}
|
|
|
|
|
|
- componentDidUpdate(prevProps: Props) {
|
|
|
- if (prevProps.version !== this.props.version) {
|
|
|
- this.remountComponent();
|
|
|
- }
|
|
|
+ if (isError) {
|
|
|
+ return <LoadingError />;
|
|
|
}
|
|
|
|
|
|
- getDisplayPercent(authorCommitCount: number): string {
|
|
|
- const {commits} = this.state;
|
|
|
-
|
|
|
- const calculatedPercent = Math.round(percent(authorCommitCount, commits.length));
|
|
|
+ function getDisplayPercent(authorCommitCount: number) {
|
|
|
+ if (commits) {
|
|
|
+ const calculatedPercent = Math.round(percent(authorCommitCount, commits.length));
|
|
|
+ return `${calculatedPercent < 1 ? '<1' : calculatedPercent}%`;
|
|
|
+ }
|
|
|
|
|
|
- return `${calculatedPercent < 1 ? '<1' : calculatedPercent}%`;
|
|
|
+ return '';
|
|
|
}
|
|
|
|
|
|
- renderBody() {
|
|
|
- // group commits by author
|
|
|
- const groupedAuthorCommits = this.state.commits?.reduce<GroupedAuthorCommits>(
|
|
|
- (authorCommitsAccumulator, commit) => {
|
|
|
- const email = commit.author?.email ?? 'unknown';
|
|
|
-
|
|
|
- if (authorCommitsAccumulator.hasOwnProperty(email)) {
|
|
|
- authorCommitsAccumulator[email].commitCount += 1;
|
|
|
- } else {
|
|
|
- authorCommitsAccumulator[email] = {
|
|
|
- commitCount: 1,
|
|
|
- author: commit.author,
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- return authorCommitsAccumulator;
|
|
|
- },
|
|
|
- {}
|
|
|
- );
|
|
|
-
|
|
|
- // sort authors by number of commits
|
|
|
- const sortedAuthorsByNumberOfCommits = Object.values(groupedAuthorCommits).sort(
|
|
|
- (a, b) => b.commitCount - a.commitCount
|
|
|
- );
|
|
|
-
|
|
|
- if (!sortedAuthorsByNumberOfCommits.length) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- return (
|
|
|
- <SidebarSection.Wrap>
|
|
|
- <SidebarSection.Title>{t('Commit Author Breakdown')}</SidebarSection.Title>
|
|
|
- <SidebarSection.Content>
|
|
|
- <Collapsible
|
|
|
- expandButton={({onExpand, numberOfHiddenItems}) => (
|
|
|
- <Button priority="link" onClick={onExpand}>
|
|
|
- {tn(
|
|
|
- 'Show %s collapsed author',
|
|
|
- 'Show %s collapsed authors',
|
|
|
- numberOfHiddenItems
|
|
|
- )}
|
|
|
- </Button>
|
|
|
- )}
|
|
|
- >
|
|
|
- {sortedAuthorsByNumberOfCommits.map(({commitCount, author}, index) => (
|
|
|
- <AuthorLine key={author?.email ?? index}>
|
|
|
- <UserAvatar user={author} size={20} hasTooltip />
|
|
|
- <AuthorName>{userDisplayName(author || {}, false)}</AuthorName>
|
|
|
- <Commits>{tn('%s commit', '%s commits', commitCount)}</Commits>
|
|
|
- <Percent>{this.getDisplayPercent(commitCount)}</Percent>
|
|
|
- </AuthorLine>
|
|
|
- ))}
|
|
|
- </Collapsible>
|
|
|
- </SidebarSection.Content>
|
|
|
- </SidebarSection.Wrap>
|
|
|
- );
|
|
|
+ // group commits by author
|
|
|
+ const groupedAuthorCommits = commits?.reduce<GroupedAuthorCommits>(
|
|
|
+ (authorCommitsAccumulator, commit) => {
|
|
|
+ const email = commit.author?.email ?? 'unknown';
|
|
|
+
|
|
|
+ if (authorCommitsAccumulator.hasOwnProperty(email)) {
|
|
|
+ authorCommitsAccumulator[email].commitCount += 1;
|
|
|
+ } else {
|
|
|
+ authorCommitsAccumulator[email] = {
|
|
|
+ commitCount: 1,
|
|
|
+ author: commit.author,
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ return authorCommitsAccumulator;
|
|
|
+ },
|
|
|
+ {}
|
|
|
+ );
|
|
|
+
|
|
|
+ // sort authors by number of commits
|
|
|
+ const sortedAuthorsByNumberOfCommits = Object.values(groupedAuthorCommits).sort(
|
|
|
+ (a, b) => b.commitCount - a.commitCount
|
|
|
+ );
|
|
|
+
|
|
|
+ if (!sortedAuthorsByNumberOfCommits.length) {
|
|
|
+ return null;
|
|
|
}
|
|
|
+
|
|
|
+ return (
|
|
|
+ <SidebarSection.Wrap>
|
|
|
+ <SidebarSection.Title>{t('Commit Author Breakdown')}</SidebarSection.Title>
|
|
|
+ <SidebarSection.Content>
|
|
|
+ <Collapsible
|
|
|
+ expandButton={({onExpand, numberOfHiddenItems}) => (
|
|
|
+ <Button priority="link" onClick={onExpand}>
|
|
|
+ {tn(
|
|
|
+ 'Show %s collapsed author',
|
|
|
+ 'Show %s collapsed authors',
|
|
|
+ numberOfHiddenItems
|
|
|
+ )}
|
|
|
+ </Button>
|
|
|
+ )}
|
|
|
+ >
|
|
|
+ {sortedAuthorsByNumberOfCommits.map(({commitCount, author}, index) => (
|
|
|
+ <AuthorLine key={author?.email ?? index}>
|
|
|
+ <UserAvatar user={author} size={20} hasTooltip />
|
|
|
+ <AuthorName>{userDisplayName(author || {}, false)}</AuthorName>
|
|
|
+ <Commits>{tn('%s commit', '%s commits', commitCount)}</Commits>
|
|
|
+ <Percent>{getDisplayPercent(commitCount)}</Percent>
|
|
|
+ </AuthorLine>
|
|
|
+ ))}
|
|
|
+ </Collapsible>
|
|
|
+ </SidebarSection.Content>
|
|
|
+ </SidebarSection.Wrap>
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
const AuthorLine = styled('div')`
|