projectProvider.tsx 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import {cloneElement, Fragment, isValidElement, useEffect, useState} 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 {Member, 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. const [members, setMembers] = useState<Member[] | undefined>(undefined);
  23. useScrollToTop({location: props.location});
  24. const {children, params, organization, ...other} = props;
  25. const projectId = params.projectId || props.location.query.project;
  26. const useFirstProject = 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]).then(mem => setMembers(mem));
  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. `/organizations/${organization.slug}/alerts/wizard/?referrer=${props.location.query.referrer}&project=:projectId`,
  52. props.router
  53. );
  54. }
  55. // if loaded, but project fetching states incomplete or project can't be found, project doesn't exist
  56. if (!project || fetchError) {
  57. return (
  58. <Alert type="warning">{t('The project you were looking for was not found.')}</Alert>
  59. );
  60. }
  61. return (
  62. <Fragment>
  63. {children && isValidElement(children)
  64. ? cloneElement(children, {
  65. ...other,
  66. ...children.props,
  67. project,
  68. projectId: useFirstProject ? project.slug : projectId,
  69. organization,
  70. members,
  71. })
  72. : children}
  73. </Fragment>
  74. );
  75. }
  76. export default AlertBuilderProjectProvider;