import {Component, Fragment} from 'react'; import styled from '@emotion/styled'; import {bulkUpdate} from 'sentry/actionCreators/group'; import {addLoadingMessage, clearIndicators} from 'sentry/actionCreators/indicator'; import {Client} from 'sentry/api'; import EventOrGroupTitle from 'sentry/components/eventOrGroupTitle'; import ErrorLevel from 'sentry/components/events/errorLevel'; import Link from 'sentry/components/links/link'; import {PanelItem} from 'sentry/components/panels'; import {IconChat, IconMute, IconStar} from 'sentry/icons'; import {t} from 'sentry/locale'; import GroupStore from 'sentry/stores/groupStore'; import space from 'sentry/styles/space'; import {BaseGroup, Organization} from 'sentry/types'; import {getMessage} from 'sentry/utils/events'; import {Aliases} from 'sentry/utils/theme'; import withApi from 'sentry/utils/withApi'; import withOrganization from 'sentry/utils/withOrganization'; type HeaderProps = { data: BaseGroup; organization: Organization; projectId: string; eventId?: string; }; function CompactIssueHeader({data, organization, projectId, eventId}: HeaderProps) { const basePath = `/organizations/${organization.slug}/issues/`; const issueLink = eventId ? `/organizations/${organization.slug}/projects/${projectId}/events/${eventId}/?referrer=compact-issue` : `${basePath}${data.id}/?referrer=compact-issue`; const commentColor: keyof Aliases = data.subscriptionDetails && data.subscriptionDetails.reason === 'mentioned' ? 'success' : 'textColor'; return ( {data.status === 'ignored' && } {data.isBookmarked && } {data.project.slug} {data.numComments !== 0 && ( {data.numComments} )} {getMessage(data)} ); } type GroupTypes = ReturnType; /** * Type assertion to disambiguate GroupTypes * * The GroupCollapseRelease type isn't compatible with BaseGroup */ function isGroup(maybe: GroupTypes): maybe is BaseGroup { return (maybe as BaseGroup).status !== undefined; } type Props = { api: Client; id: string; organization: Organization; data?: BaseGroup; eventId?: string; }; type State = { issue?: GroupTypes; }; class CompactIssue extends Component { state: State = { issue: this.props.data || GroupStore.get(this.props.id), }; componentWillReceiveProps(nextProps: Props) { if (nextProps.id !== this.props.id) { this.setState({ issue: GroupStore.get(this.props.id), }); } } componentWillUnmount() { this.listener(); } listener = GroupStore.listen( (itemIds: Set) => this.onGroupChange(itemIds), undefined ); onGroupChange(itemIds: Set) { if (!itemIds.has(this.props.id)) { return; } const id = this.props.id; const issue = GroupStore.get(id); this.setState({ issue, }); } onUpdate(data: Record) { const issue = this.state.issue; if (!issue) { return; } addLoadingMessage(t('Saving changes\u2026')); bulkUpdate( this.props.api, { orgId: this.props.organization.slug, projectId: issue.project.slug, itemIds: [issue.id], data, }, { complete: () => { clearIndicators(); }, } ); } render() { const issue = this.state.issue; const {organization} = this.props; if (!isGroup(issue)) { return null; } let className = 'issue'; if (issue.isBookmarked) { className += ' isBookmarked'; } if (issue.hasSeen) { className += ' hasSeen'; } if (issue.status === 'resolved') { className += ' isResolved'; } if (issue.status === 'ignored') { className += ' isIgnored'; } return ( {this.props.children} ); } } export default withApi(withOrganization(CompactIssue)); const IssueHeaderMetaWrapper = styled('div')` display: flex; align-items: center; `; const StyledErrorLevel = styled(ErrorLevel)` display: block; margin-right: ${space(1)}; `; const IconLink = styled(Link)` & > svg { margin-right: ${space(0.5)}; } `; const IssueRow = styled(PanelItem)` padding-top: ${space(1.5)}; padding-bottom: ${space(0.75)}; flex-direction: column; `;