import React from 'react'; import {browserHistory, withRouter, WithRouterProps} from 'react-router'; import * as Sentry from '@sentry/react'; import isEqual from 'lodash/isEqual'; import * as qs from 'query-string'; import {fetchOrgMembers, indexMembersByProject} from 'app/actionCreators/members'; import {Client} from 'app/api'; import EmptyStateWarning from 'app/components/emptyStateWarning'; import LoadingError from 'app/components/loadingError'; import LoadingIndicator from 'app/components/loadingIndicator'; import Pagination from 'app/components/pagination'; import {Panel, PanelBody} from 'app/components/panels'; import StreamGroup, { DEFAULT_STREAM_GROUP_STATS_PERIOD, } from 'app/components/stream/group'; import {t} from 'app/locale'; import GroupStore from 'app/stores/groupStore'; import {Group} from 'app/types'; import {callIfFunction} from 'app/utils/callIfFunction'; import StreamManager from 'app/utils/streamManager'; import withApi from 'app/utils/withApi'; import {TimePeriodType} from 'app/views/alerts/rules/details/constants'; import GroupListHeader from './groupListHeader'; const defaultProps = { canSelectGroups: true, withChart: true, withPagination: true, useFilteredStats: true, }; type Props = WithRouterProps & { api: Client; query: string; orgId: string; endpointPath: string; renderEmptyMessage?: () => React.ReactNode; queryParams?: Record; customStatsPeriod?: TimePeriodType; onFetchSuccess?: ( groupListState: State, onCursor: ( cursor: string, path: string, query: Record, pageDiff: number ) => void ) => void; } & Partial; type State = { loading: boolean; error: boolean; groups: Group[]; pageLinks: string | null; memberList?: ReturnType; }; class GroupList extends React.Component { static defaultProps = defaultProps; state: State = { loading: true, error: false, groups: [], pageLinks: null, }; componentDidMount() { this.fetchData(); } shouldComponentUpdate(nextProps: Props, nextState: State) { return ( !isEqual(this.state, nextState) || nextProps.endpointPath !== this.props.endpointPath || nextProps.query !== this.props.query || !isEqual(nextProps.queryParams, this.props.queryParams) ); } componentDidUpdate(prevProps: Props) { if ( prevProps.orgId !== this.props.orgId || prevProps.endpointPath !== this.props.endpointPath || prevProps.query !== this.props.query || !isEqual(prevProps.queryParams, this.props.queryParams) ) { this.fetchData(); } } componentWillUnmount() { GroupStore.reset(); callIfFunction(this.listener); } listener = GroupStore.listen(() => this.onGroupChange(), undefined); private _streamManager = new StreamManager(GroupStore); fetchData = () => { GroupStore.loadInitialData([]); const {api, orgId} = this.props; this.setState({loading: true, error: false}); fetchOrgMembers(api, orgId).then(members => { this.setState({memberList: indexMembersByProject(members)}); }); const endpoint = this.getGroupListEndpoint(); api.request(endpoint, { success: (data, _, jqXHR) => { this._streamManager.push(data); this.setState( { error: false, loading: false, pageLinks: jqXHR?.getResponseHeader('Link') ?? null, }, () => { this.props.onFetchSuccess?.(this.state, this.handleCursorChange); } ); }, error: err => { Sentry.captureException(err); this.setState({error: true, loading: false}); }, }); }; getGroupListEndpoint() { const {orgId, endpointPath, queryParams} = this.props; const path = endpointPath ?? `/organizations/${orgId}/issues/`; const queryParameters = queryParams ?? this.getQueryParams(); return `${path}?${qs.stringify(queryParameters)}`; } getQueryParams() { const {location, query} = this.props; const queryParams = location.query; queryParams.limit = 50; queryParams.sort = 'new'; queryParams.query = query; return queryParams; } handleCursorChange( cursor: string, path: string, query: Record, pageDiff: number ) { const queryPageInt = parseInt(query.page, 10); let nextPage: number | undefined = isNaN(queryPageInt) ? pageDiff : queryPageInt + pageDiff; let nextCursor: string | undefined = cursor; // unset cursor and page when we navigate back to the first page // also reset cursor if somehow the previous button is enabled on // first page and user attempts to go backwards if (nextPage <= 0) { nextCursor = undefined; nextPage = undefined; } browserHistory.push({ pathname: path, query: {...query, cursor: nextCursor}, }); } onGroupChange() { const groups = this._streamManager.getAllItems(); if (!isEqual(groups, this.state.groups)) { this.setState({groups}); } } render() { const { canSelectGroups, withChart, renderEmptyMessage, withPagination, useFilteredStats, customStatsPeriod, queryParams, } = this.props; const {loading, error, groups, memberList, pageLinks} = this.state; if (loading) { return ; } if (error) { return ; } if (groups.length === 0) { if (typeof renderEmptyMessage === 'function') { return renderEmptyMessage(); } return (

{t("There don't seem to be any events fitting the query.")}

); } const statsPeriod = queryParams?.groupStatsPeriod === 'auto' ? queryParams?.groupStatsPeriod : DEFAULT_STREAM_GROUP_STATS_PERIOD; return ( {groups.map(({id, project}) => { const members = memberList?.hasOwnProperty(project.slug) ? memberList[project.slug] : undefined; return ( ); })} {withPagination && ( )} ); } } export {GroupList}; export default withApi(withRouter(GroupList));