withLatestContext.tsx 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import * as React from 'react';
  2. import ConfigStore from 'sentry/stores/configStore';
  3. import LatestContextStore from 'sentry/stores/latestContextStore';
  4. import {Organization, OrganizationSummary, Project} from 'sentry/types';
  5. import getDisplayName from 'sentry/utils/getDisplayName';
  6. import withOrganizations from 'sentry/utils/withOrganizations';
  7. type InjectedLatestContextProps = {
  8. organization?: Organization | null;
  9. organizations?: OrganizationSummary[];
  10. project?: Project | null;
  11. };
  12. type HocProps = {
  13. organizations: OrganizationSummary[];
  14. organization?: Organization | null;
  15. };
  16. type State = {
  17. latestContext: Omit<InjectedLatestContextProps, 'organizations'>;
  18. };
  19. const fallbackContext: State['latestContext'] = {
  20. organization: null,
  21. project: null,
  22. };
  23. function withLatestContext<P extends InjectedLatestContextProps>(
  24. WrappedComponent: React.ComponentType<P>
  25. ) {
  26. class WithLatestContext extends React.Component<
  27. Omit<P, keyof InjectedLatestContextProps> & HocProps,
  28. State
  29. > {
  30. static displayName = `withLatestContext(${getDisplayName(WrappedComponent)})`;
  31. state: State = {
  32. latestContext: LatestContextStore.get(),
  33. };
  34. componentWillUmount() {
  35. this.unsubscribe();
  36. }
  37. unsubscribe = LatestContextStore.listen(
  38. (latestContext: State['latestContext']) => this.setState({latestContext}),
  39. undefined
  40. );
  41. render() {
  42. const {organizations} = this.props;
  43. const {latestContext} = this.state;
  44. const {organization, project} = latestContext || fallbackContext;
  45. // Even though org details exists in LatestContextStore,
  46. // fetch organization from OrganizationsStore so that we can
  47. // expect consistent data structure because OrganizationsStore has a list
  48. // of orgs but not full org details
  49. const latestOrganization =
  50. organization ||
  51. (organizations && organizations.length
  52. ? organizations.find(
  53. ({slug}) => slug === ConfigStore.get('lastOrganization')
  54. ) || organizations[0]
  55. : null);
  56. // TODO(billy): Below is going to be wrong if component is passed project, it will override
  57. // project from `latestContext`
  58. return (
  59. <WrappedComponent
  60. project={project as Project}
  61. {...(this.props as P)}
  62. organization={(this.props.organization || latestOrganization) as Organization}
  63. />
  64. );
  65. }
  66. }
  67. return withOrganizations(WithLatestContext);
  68. }
  69. export default withLatestContext;