withCommitters.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import * as React from 'react';
  2. import {getCommitters} from 'sentry/actionCreators/committers';
  3. import {Client} from 'sentry/api';
  4. import CommitterStore from 'sentry/stores/committerStore';
  5. import {AvatarProject, Committer, Group, Organization, Project} from 'sentry/types';
  6. import {Event} from 'sentry/types/event';
  7. import getDisplayName from 'sentry/utils/getDisplayName';
  8. type DependentProps = {
  9. api: Client;
  10. event: Event;
  11. organization: Organization;
  12. project: Project | AvatarProject;
  13. group?: Group;
  14. };
  15. // XXX: State does not include loading/error because components using this
  16. // HOC (suggestedOwners, eventCause) do not have loading/error states. However,
  17. // the store maintains those states if it is needed in the future.
  18. type InjectedProps = {
  19. committers?: Committer[];
  20. };
  21. const initialState: InjectedProps = {
  22. committers: [],
  23. };
  24. function withCommitters<P extends DependentProps>(
  25. WrappedComponent: React.ComponentType<P>
  26. ) {
  27. class WithCommitters extends React.Component<
  28. Omit<P, keyof InjectedProps> & Partial<InjectedProps> & DependentProps,
  29. InjectedProps
  30. > {
  31. static displayName = `withCommitters(${getDisplayName(WrappedComponent)})`;
  32. constructor(props: P, context: any) {
  33. super(props, context);
  34. const {organization, project, event} = this.props;
  35. const repoData = CommitterStore.get(organization.slug, project.slug, event.id);
  36. this.state = {...initialState, ...repoData} as InjectedProps;
  37. }
  38. componentDidMount() {
  39. const {group} = this.props;
  40. // No committers if group doesn't have any releases
  41. if (!!group?.firstRelease) {
  42. this.fetchCommitters();
  43. }
  44. }
  45. componentWillUnmount() {
  46. this.unsubscribe();
  47. }
  48. unsubscribe = CommitterStore.listen(() => this.onStoreUpdate(), undefined);
  49. fetchCommitters() {
  50. const {api, organization, project, event} = this.props;
  51. const repoData = CommitterStore.get(organization.slug, project.slug, event.id);
  52. if (
  53. (!repoData.committers && !repoData.committersLoading) ||
  54. repoData.committersError
  55. ) {
  56. getCommitters(api, {
  57. orgSlug: organization.slug,
  58. projectSlug: project.slug,
  59. eventId: event.id,
  60. });
  61. }
  62. }
  63. onStoreUpdate() {
  64. const {organization, project, event} = this.props;
  65. const repoData = CommitterStore.get(organization.slug, project.slug, event.id);
  66. this.setState({committers: repoData.committers});
  67. }
  68. render() {
  69. const {committers = []} = this.state;
  70. // XXX: We do not pass loading/error states because the components using
  71. // this HOC (suggestedOwners, eventCause) do not have loading/error states
  72. return (
  73. <WrappedComponent
  74. {...(this.props as P & DependentProps)}
  75. committers={committers}
  76. />
  77. );
  78. }
  79. }
  80. return WithCommitters;
  81. }
  82. export default withCommitters;