import * as React from 'react'; import {fetchPlugins} from 'sentry/actionCreators/plugins'; import PluginsStore from 'sentry/stores/pluginsStore'; import {Organization, Plugin, Project} from 'sentry/types'; import {defined} from 'sentry/utils'; import getDisplayName from 'sentry/utils/getDisplayName'; import withOrganization from 'sentry/utils/withOrganization'; import withProject from 'sentry/utils/withProject'; type WithPluginProps = { organization: Organization; project?: Project; }; type State = { loading: boolean; plugins: Plugin[]; }; /** * Higher order component that fetches list of plugins and * passes PluginsStore to component as `plugins` */ function withPlugins

( WrappedComponent: React.ComponentType

) { class WithPlugins extends React.Component< Omit & WithPluginProps, State > { static displayName = `withPlugins(${getDisplayName(WrappedComponent)})`; state = {plugins: [], loading: true}; componentDidMount() { this.fetchPlugins(); } componentDidUpdate(prevProps, _prevState, prevContext) { const {organization, project} = this.props; // Only fetch plugins when a org slug or project slug has changed const prevOrg = prevProps.organization || prevContext?.organization; const prevProject = prevProps.project || prevContext?.project; // If previous org/project is undefined then it means: // the HoC has mounted, `fetchPlugins` has been called (via cDM), and // store was updated. We don't need to fetchPlugins again (or it will cause an infinite loop) // // This is for the unusual case where component is mounted and receives a new org/project prop // e.g. when switching projects via breadcrumbs in settings. if (!defined(prevProject) || !defined(prevOrg)) { return; } const isOrgSame = prevOrg.slug === organization.slug; const isProjectSame = prevProject.slug === project?.slug; // Don't do anything if org and project are the same if (isOrgSame && isProjectSame) { return; } this.fetchPlugins(); } componentWillUnmount() { this.unsubscribe(); } unsubscribe = PluginsStore.listen(({plugins, loading}: State) => { // State is destructured as store updates contain additional keys // that are not exposed by this HoC this.setState({plugins, loading}); }, undefined); fetchPlugins() { const {organization, project} = this.props; if (!project || !organization) { return; } fetchPlugins({projectId: project.slug, orgId: organization.slug}); } render() { return ( ); } } return withOrganization(withProject(WithPlugins)); } export default withPlugins;