projectProvider.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import {cloneElement, Fragment, isValidElement, useEffect} from 'react';
  2. import {RouteComponentProps} from 'react-router';
  3. import {fetchOrgMembers} from 'sentry/actionCreators/members';
  4. import {navigateTo} from 'sentry/actionCreators/navigation';
  5. import Alert from 'sentry/components/alert';
  6. import LoadingIndicator from 'sentry/components/loadingIndicator';
  7. import {t} from 'sentry/locale';
  8. import {Organization} from 'sentry/types';
  9. import useApi from 'sentry/utils/useApi';
  10. import useProjects from 'sentry/utils/useProjects';
  11. import useScrollToTop from 'sentry/utils/useScrollToTop';
  12. type Props = RouteComponentProps<RouteParams, {}> & {
  13. hasMetricAlerts: boolean;
  14. organization: Organization;
  15. children?: React.ReactNode;
  16. };
  17. type RouteParams = {
  18. projectId?: string;
  19. };
  20. function AlertBuilderProjectProvider(props: Props) {
  21. const api = useApi();
  22. useScrollToTop({location: props.location});
  23. const {children, params, organization, ...other} = props;
  24. const projectId = params.projectId || props.location.query.project;
  25. const useFirstProject = projectId === undefined;
  26. // calling useProjects() without args fetches all projects
  27. const {projects, initiallyLoaded, fetching, fetchError} = useProjects(
  28. useFirstProject
  29. ? undefined
  30. : {
  31. slugs: [projectId],
  32. }
  33. );
  34. const project = useFirstProject
  35. ? projects.find(p => p.isMember) ?? (projects.length && projects[0])
  36. : projects.find(({slug}) => slug === projectId);
  37. useEffect(() => {
  38. if (!project) {
  39. return;
  40. }
  41. // fetch members list for mail action fields
  42. fetchOrgMembers(api, organization.slug, [project.id]);
  43. }, [api, organization, project]);
  44. if (!initiallyLoaded || fetching) {
  45. return <LoadingIndicator />;
  46. }
  47. // If there's no project show the project selector modal
  48. if (!project && !fetchError) {
  49. navigateTo(
  50. `/organizations/${organization.slug}/alerts/wizard/?referrer=${props.location.query.referrer}&project=:projectId`,
  51. props.router
  52. );
  53. }
  54. // if loaded, but project fetching states incomplete or project can't be found, project doesn't exist
  55. if (!project || fetchError) {
  56. return (
  57. <Alert type="warning">{t('The project you were looking for was not found.')}</Alert>
  58. );
  59. }
  60. return (
  61. <Fragment>
  62. {children && isValidElement(children)
  63. ? cloneElement(children, {
  64. ...other,
  65. ...children.props,
  66. project,
  67. projectId: useFirstProject ? project.slug : projectId,
  68. organization,
  69. })
  70. : children}
  71. </Fragment>
  72. );
  73. }
  74. export default AlertBuilderProjectProvider;