123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- import * as React from 'react';
- import styled from '@emotion/styled';
- import {promptsCheck, promptsUpdate} from 'sentry/actionCreators/prompts';
- import SidebarPanelActions from 'sentry/actions/sidebarPanelActions';
- import {Client} from 'sentry/api';
- import Alert from 'sentry/components/alert';
- import {ALL_ACCESS_PROJECTS} from 'sentry/constants/globalSelectionHeader';
- import {IconUpgrade} from 'sentry/icons';
- import {t} from 'sentry/locale';
- import space from 'sentry/styles/space';
- import {
- GlobalSelection,
- Organization,
- ProjectSdkUpdates,
- SDKUpdatesSuggestion,
- } from 'sentry/types';
- import {trackAnalyticsEvent} from 'sentry/utils/analytics';
- import {promptIsDismissed} from 'sentry/utils/promptIsDismissed';
- import withApi from 'sentry/utils/withApi';
- import withGlobalSelection from 'sentry/utils/withGlobalSelection';
- import withOrganization from 'sentry/utils/withOrganization';
- import withSdkUpdates from 'sentry/utils/withSdkUpdates';
- import {SidebarPanelKey} from './sidebar/types';
- import Button from './button';
- type Props = React.ComponentProps<typeof Alert> & {
- api: Client;
- organization: Organization;
- sdkUpdates?: ProjectSdkUpdates[] | null;
- selection?: GlobalSelection;
- Wrapper?: React.ComponentType;
- };
- type State = {
- isDismissed: boolean | null;
- };
- type AnalyticsOpts = {
- organization: Organization;
- };
- const recordAnalyticsSeen = ({organization}: AnalyticsOpts) =>
- trackAnalyticsEvent({
- eventKey: 'sdk_updates.seen',
- eventName: 'SDK Updates: Seen',
- organization_id: organization.id,
- });
- const recordAnalyticsSnoozed = ({organization}: AnalyticsOpts) =>
- trackAnalyticsEvent({
- eventKey: 'sdk_updates.snoozed',
- eventName: 'SDK Updates: Snoozed',
- organization_id: organization.id,
- });
- const recordAnalyticsClicked = ({organization}: AnalyticsOpts) =>
- trackAnalyticsEvent({
- eventKey: 'sdk_updates.clicked',
- eventName: 'SDK Updates: Clicked',
- organization_id: organization.id,
- });
- const flattenSuggestions = (list: ProjectSdkUpdates[]) =>
- list.reduce<SDKUpdatesSuggestion[]>(
- (suggestions, sdk) => [...suggestions, ...sdk.suggestions],
- []
- );
- class InnerGlobalSdkSuggestions extends React.Component<Props, State> {
- state: State = {
- isDismissed: null,
- };
- componentDidMount() {
- this.promptsCheck();
- recordAnalyticsSeen({organization: this.props.organization});
- }
- async promptsCheck() {
- const {api, organization} = this.props;
- const prompt = await promptsCheck(api, {
- organizationId: organization.id,
- feature: 'sdk_updates',
- });
- this.setState({
- isDismissed: promptIsDismissed(prompt),
- });
- }
- snoozePrompt = () => {
- const {api, organization} = this.props;
- promptsUpdate(api, {
- organizationId: organization.id,
- feature: 'sdk_updates',
- status: 'snoozed',
- });
- this.setState({isDismissed: true});
- recordAnalyticsSnoozed({organization: this.props.organization});
- };
- render() {
- const {
- api: _api,
- selection,
- sdkUpdates,
- organization,
- Wrapper,
- ...props
- } = this.props;
- const {isDismissed} = this.state;
- if (!sdkUpdates || isDismissed === null || isDismissed) {
- return null;
- }
- // withSdkUpdates explicitly only queries My Projects. This means that when
- // looking at any projects outside of My Projects (like All Projects), this
- // will only show the updates relevant to the to user.
- const projectSpecificUpdates =
- selection?.projects.length === 0 || selection?.projects === [ALL_ACCESS_PROJECTS]
- ? sdkUpdates
- : sdkUpdates.filter(update =>
- selection?.projects?.includes(parseInt(update.projectId, 10))
- );
- // Are there any updates?
- if (flattenSuggestions(projectSpecificUpdates).length === 0) {
- return null;
- }
- const showBroadcastsPanel = (
- <Button
- priority="link"
- onClick={() => {
- SidebarPanelActions.activatePanel(SidebarPanelKey.Broadcasts);
- recordAnalyticsClicked({organization});
- }}
- >
- {t('Review updates')}
- </Button>
- );
- const notice = (
- <Alert type="info" icon={<IconUpgrade />} {...props}>
- <Content>
- {t(
- `You have outdated SDKs in your projects. Update them for important fixes and features.`
- )}
- <Actions>
- <Button
- priority="link"
- title={t('Dismiss for the next two weeks')}
- onClick={this.snoozePrompt}
- >
- {t('Remind me later')}
- </Button>
- |{showBroadcastsPanel}
- </Actions>
- </Content>
- </Alert>
- );
- return Wrapper ? <Wrapper>{notice}</Wrapper> : notice;
- }
- }
- const Content = styled('div')`
- display: flex;
- flex-wrap: wrap;
- @media (min-width: ${p => p.theme.breakpoints[0]}) {
- justify-content: space-between;
- }
- `;
- const Actions = styled('div')`
- display: grid;
- grid-template-columns: repeat(3, max-content);
- grid-gap: ${space(1)};
- `;
- const GlobalSdkSuggestions = withOrganization(
- withSdkUpdates(withGlobalSelection(withApi(InnerGlobalSdkSuggestions)))
- );
- export default GlobalSdkSuggestions;
|