Files
2024-02-15 17:01:50 +00:00

181 lines
5.3 KiB
TypeScript

import {Component, lazy, Suspense} from 'react';
import type {Client} from 'sentry/api';
import EmptyStateWarning from 'sentry/components/emptyStateWarning';
import LoadingIndicator from 'sentry/components/loadingIndicator';
import Placeholder from 'sentry/components/placeholder';
import {DEFAULT_QUERY} from 'sentry/constants';
import {t} from 'sentry/locale';
import type {Organization, Project} from 'sentry/types';
import NoIssuesMatched from 'sentry/views/issueList/noGroupsHandler/noIssuesMatched';
import {FOR_REVIEW_QUERIES} from 'sentry/views/issueList/utils';
import NoUnresolvedIssues from './noUnresolvedIssues';
type Props = {
api: Client;
groupIds: string[];
organization: Organization;
query: string;
emptyMessage?: React.ReactNode;
selectedProjectIds?: number[];
};
type State = {
fetchingSentFirstEvent: boolean;
firstEventProjects?: Project[] | null;
sentFirstEvent?: boolean;
};
/**
* Component which is rendered when no groups/issues were found. This could
* either be caused by having no first events, having resolved all issues, or
* having no issues be returned from a query. This component will conditionally
* render one of those states.
*/
class NoGroupsHandler extends Component<Props, State> {
state: State = {
fetchingSentFirstEvent: true,
sentFirstEvent: false,
firstEventProjects: null,
};
componentDidMount() {
this.fetchSentFirstEvent();
this._isMounted = true;
}
componentWillUnmount() {
this._isMounted = false;
}
/**
* This is a bit hacky, but this is causing flakiness in frontend tests
* `issueList/overview` is being unmounted during tests before the requests
* in `this.fetchSentFirstEvent` are completed and causing this React warning:
*
* Warning: Can't perform a React state update on an unmounted component.
* This is a no-op, but it indicates a memory leak in your application.
* To fix, cancel all subscriptions and asynchronous tasks in the
* componentWillUnmount method.
*
* This is something to revisit if we refactor API client
*/
private _isMounted: boolean = false;
async fetchSentFirstEvent() {
this.setState({
fetchingSentFirstEvent: true,
});
const {organization, selectedProjectIds, api} = this.props;
let sentFirstEvent = false;
let projects = [];
// If no projects are selected, then we must check every project the user is a
// member of and make sure there are no first events for all of the projects
// Set project to -1 for all projects
// Do not pass a project id for "my projects"
let firstEventQuery: {project?: number[]} = {};
const projectsQuery: {per_page: number; query?: string} = {per_page: 1};
if (selectedProjectIds?.length) {
firstEventQuery = {project: selectedProjectIds};
projectsQuery.query = selectedProjectIds.map(id => `id:${id}`).join(' ');
}
[{sentFirstEvent}, projects] = await Promise.all([
// checks to see if selection has sent a first event
api.requestPromise(`/organizations/${organization.slug}/sent-first-event/`, {
query: firstEventQuery,
}),
// retrieves a single project to feed to WaitingForEvents from renderStreamBody
api.requestPromise(`/organizations/${organization.slug}/projects/`, {
query: projectsQuery,
}),
]);
// See comment where this property is initialized
// FIXME
if (!this._isMounted) {
return;
}
this.setState({
fetchingSentFirstEvent: false,
sentFirstEvent,
firstEventProjects: projects,
});
}
renderLoading() {
return <LoadingIndicator />;
}
renderAwaitingEvents(projects: State['firstEventProjects']) {
const {organization, groupIds} = this.props;
const project = projects && projects.length > 0 ? projects[0] : undefined;
const sampleIssueId = groupIds.length > 0 ? groupIds[0] : undefined;
const WaitingForEvents = lazy(() => import('sentry/components/waitingForEvents'));
return (
<Suspense fallback={<Placeholder height="260px" />}>
<WaitingForEvents
org={organization}
project={project}
sampleIssueId={sampleIssueId}
/>
</Suspense>
);
}
renderEmpty() {
const {emptyMessage} = this.props;
if (emptyMessage) {
return (
<EmptyStateWarning>
<p>{emptyMessage}</p>
</EmptyStateWarning>
);
}
return <NoIssuesMatched />;
}
render() {
const {fetchingSentFirstEvent, sentFirstEvent, firstEventProjects} = this.state;
const {query} = this.props;
if (fetchingSentFirstEvent) {
return this.renderLoading();
}
if (!sentFirstEvent) {
return this.renderAwaitingEvents(firstEventProjects);
}
if (query === DEFAULT_QUERY) {
return (
<NoUnresolvedIssues
title={t("We couldn't find any issues that matched your filters.")}
subtitle={t('Get out there and write some broken code!')}
/>
);
}
if (FOR_REVIEW_QUERIES.includes(query || '')) {
return (
<NoUnresolvedIssues
title={t('Well, would you look at that.')}
subtitle={t(
'No more issues to review. Better get back out there and write some broken code.'
)}
/>
);
}
return this.renderEmpty();
}
}
export default NoGroupsHandler;