projectProvider.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  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 hasAlertWizardV3 = organization.features.includes('alert-wizard-v3');
  26. const useFirstProject = hasAlertWizardV3 && projectId === undefined;
  27. // calling useProjects() without args fetches all projects
  28. const {projects, initiallyLoaded, fetching, fetchError} = useProjects(
  29. useFirstProject
  30. ? undefined
  31. : {
  32. slugs: [projectId],
  33. }
  34. );
  35. const project = useFirstProject
  36. ? projects.find(p => p.isMember) ?? (projects.length && projects[0])
  37. : projects.find(({slug}) => slug === projectId);
  38. useEffect(() => {
  39. if (!project) {
  40. return;
  41. }
  42. // fetch members list for mail action fields
  43. fetchOrgMembers(api, organization.slug, [project.id]);
  44. }, [api, organization, project]);
  45. if (!initiallyLoaded || fetching) {
  46. return <LoadingIndicator />;
  47. }
  48. // If there's no project show the project selector modal
  49. if (!project && !fetchError) {
  50. navigateTo(
  51. hasAlertWizardV3
  52. ? `/organizations/${organization.slug}/alerts/wizard/?referrer=${props.location.query.referrer}&project=:projectId`
  53. : `/organizations/${organization.slug}/alerts/:projectId/wizard/?referrer=${props.location.query.referrer}`,
  54. props.router
  55. );
  56. }
  57. // if loaded, but project fetching states incomplete or project can't be found, project doesn't exist
  58. if (!project || fetchError) {
  59. return (
  60. <Alert type="warning">{t('The project you were looking for was not found.')}</Alert>
  61. );
  62. }
  63. return (
  64. <Fragment>
  65. {children && isValidElement(children)
  66. ? cloneElement(children, {
  67. ...other,
  68. ...children.props,
  69. project,
  70. projectId: useFirstProject ? project.slug : projectId,
  71. organization,
  72. })
  73. : children}
  74. </Fragment>
  75. );
  76. }
  77. export default AlertBuilderProjectProvider;