globalSdkUpdateAlert.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import {useCallback, useEffect, useState} from 'react';
  2. import {promptsCheck, promptsUpdate} from 'sentry/actionCreators/prompts';
  3. import {Alert, AlertProps} from 'sentry/components/alert';
  4. import {Button} from 'sentry/components/button';
  5. import ButtonBar from 'sentry/components/buttonBar';
  6. import {SidebarPanelKey} from 'sentry/components/sidebar/types';
  7. import {ALL_ACCESS_PROJECTS} from 'sentry/constants/pageFilters';
  8. import {IconClose} from 'sentry/icons';
  9. import {t} from 'sentry/locale';
  10. import SidebarPanelStore from 'sentry/stores/sidebarPanelStore';
  11. import {ProjectSdkUpdates} from 'sentry/types';
  12. import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
  13. import {promptIsDismissed} from 'sentry/utils/promptIsDismissed';
  14. import useApi from 'sentry/utils/useApi';
  15. import useOrganization from 'sentry/utils/useOrganization';
  16. import usePageFilters from 'sentry/utils/usePageFilters';
  17. import withSdkUpdates from 'sentry/utils/withSdkUpdates';
  18. interface InnerGlobalSdkSuggestionsProps extends AlertProps {
  19. className?: string;
  20. sdkUpdates?: ProjectSdkUpdates[] | null;
  21. }
  22. function InnerGlobalSdkUpdateAlert(
  23. props: InnerGlobalSdkSuggestionsProps
  24. ): React.ReactElement | null {
  25. const api = useApi();
  26. const organization = useOrganization();
  27. const {selection} = usePageFilters();
  28. const [showUpdateAlert, setShowUpdateAlert] = useState<boolean>(false);
  29. const handleSnoozePrompt = useCallback(() => {
  30. promptsUpdate(api, {
  31. organizationId: organization.id,
  32. feature: 'sdk_updates',
  33. status: 'snoozed',
  34. });
  35. trackAdvancedAnalyticsEvent('sdk_updates.snoozed', {organization});
  36. setShowUpdateAlert(false);
  37. }, [api, organization]);
  38. const handleReviewUpdatesClick = useCallback(() => {
  39. SidebarPanelStore.activatePanel(SidebarPanelKey.Broadcasts);
  40. trackAdvancedAnalyticsEvent('sdk_updates.clicked', {organization});
  41. }, [organization]);
  42. useEffect(() => {
  43. trackAdvancedAnalyticsEvent('sdk_updates.seen', {organization});
  44. let isUnmounted = false;
  45. promptsCheck(api, {
  46. organizationId: organization.id,
  47. feature: 'sdk_updates',
  48. }).then(prompt => {
  49. if (isUnmounted) {
  50. return;
  51. }
  52. setShowUpdateAlert(!promptIsDismissed(prompt));
  53. });
  54. return () => {
  55. isUnmounted = true;
  56. };
  57. }, [api, organization]);
  58. if (!showUpdateAlert || !props.sdkUpdates?.length) {
  59. return null;
  60. }
  61. // withSdkUpdates explicitly only queries My Projects. This means that when
  62. // looking at any projects outside of My Projects (like All Projects), this
  63. // will only show the updates relevant to the to user.
  64. const projectSpecificUpdates =
  65. selection?.projects?.length === 0 || selection?.projects[0] === ALL_ACCESS_PROJECTS
  66. ? props.sdkUpdates
  67. : props.sdkUpdates.filter(update =>
  68. selection?.projects?.includes(parseInt(update.projectId, 10))
  69. );
  70. // Check if we have at least one suggestion out of the list of updates
  71. if (projectSpecificUpdates.every(v => v.suggestions.length === 0)) {
  72. return null;
  73. }
  74. return (
  75. <Alert
  76. type="info"
  77. showIcon
  78. className={props.className}
  79. trailingItems={
  80. <ButtonBar gap={2}>
  81. <Button priority="link" size="xs" onClick={handleReviewUpdatesClick}>
  82. {t('Review updates')}
  83. </Button>
  84. <Button
  85. aria-label={t('Remind me later')}
  86. title={t('Dismiss for the next two weeks')}
  87. priority="link"
  88. size="xs"
  89. icon={<IconClose />}
  90. onClick={handleSnoozePrompt}
  91. />
  92. </ButtonBar>
  93. }
  94. >
  95. {t(
  96. `You have outdated SDKs in your projects. Update them for important fixes and features.`
  97. )}
  98. </Alert>
  99. );
  100. }
  101. const GlobalSdkUpdateAlert = withSdkUpdates(InnerGlobalSdkUpdateAlert);
  102. export {GlobalSdkUpdateAlert, InnerGlobalSdkUpdateAlert};