projectProvider.tsx 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import {cloneElement, Fragment, isValidElement, useEffect, useState} from 'react';
  2. import {fetchOrgMembers} from 'sentry/actionCreators/members';
  3. import {navigateTo} from 'sentry/actionCreators/navigation';
  4. import {Alert} from 'sentry/components/alert';
  5. import LoadingIndicator from 'sentry/components/loadingIndicator';
  6. import {t} from 'sentry/locale';
  7. import type {RouteComponentProps} from 'sentry/types/legacyReactRouter';
  8. import type {Member, Organization} from 'sentry/types/organization';
  9. import useApi from 'sentry/utils/useApi';
  10. import {useIsMountedRef} from 'sentry/utils/useIsMountedRef';
  11. import useProjects from 'sentry/utils/useProjects';
  12. import useScrollToTop from 'sentry/utils/useScrollToTop';
  13. type Props = RouteComponentProps<RouteParams, {}> & {
  14. hasMetricAlerts: boolean;
  15. organization: Organization;
  16. children?: React.ReactNode;
  17. };
  18. type RouteParams = {
  19. projectId?: string;
  20. };
  21. function AlertBuilderProjectProvider(props: Props) {
  22. const api = useApi();
  23. const isMountedRef = useIsMountedRef();
  24. const [members, setMembers] = useState<Member[] | undefined>(undefined);
  25. useScrollToTop({location: props.location});
  26. const {children, params, organization, ...other} = props;
  27. const projectId = params.projectId || props.location.query.project;
  28. const useFirstProject = projectId === undefined;
  29. const {projects, initiallyLoaded, fetching, fetchError} = useProjects();
  30. const project = useFirstProject
  31. ? projects.find(p => p.isMember) ?? (projects.length && projects[0])
  32. : projects.find(({slug}) => slug === projectId);
  33. useEffect(() => {
  34. if (!project) {
  35. return;
  36. }
  37. // fetch members list for mail action fields
  38. fetchOrgMembers(api, organization.slug, [project.id]).then(mem => {
  39. if (isMountedRef.current) {
  40. setMembers(mem);
  41. }
  42. });
  43. }, [api, organization, isMountedRef, 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. members,
  70. })
  71. : children}
  72. </Fragment>
  73. );
  74. }
  75. export default AlertBuilderProjectProvider;