Browse Source

feat(workflow): Add global messages (#25834)

Scott Cooper 3 years ago
parent
commit
9f4199142f

+ 2 - 0
src/sentry/conf/server.py

@@ -945,6 +945,8 @@ SENTRY_FEATURES = {
     # Prefix host with organization ID when giving users DSNs (can be
     # customized with SENTRY_ORG_SUBDOMAIN_TEMPLATE)
     "organizations:org-subdomains": False,
+    # Display a global dashboard notification for this org
+    "organizations:prompt-dashboards": False,
     # Enable views for ops breakdown
     "organizations:performance-ops-breakdown": False,
     # Enable views for tag explorer

+ 1 - 0
src/sentry/features/__init__.py

@@ -122,6 +122,7 @@ default_manager.add("organizations:trace-view-summary", OrganizationFeature)  #
 default_manager.add("organizations:transaction-comparison", OrganizationFeature)  # NOQA
 default_manager.add("organizations:unhandled-issue-flag", OrganizationFeature)  # NOQA
 default_manager.add("organizations:usage-stats-graph", OrganizationFeature)  # NOQA
+default_manager.add("organizations:prompt-dashboards", OrganizationFeature)  # NOQA
 # NOTE: Don't add features down here! Add them to their specific group and sort
 #       them alphabetically! The order features are registered is not important.
 

+ 1 - 0
static/app/stores/hookStore.tsx

@@ -16,6 +16,7 @@ const validHookNames = new Set<HookName>([
   'analytics:log-experiment',
   'component:header-date-range',
   'component:header-selector-items',
+  'component:global-notifications',
   'feature-disabled:alerts-page',
   'feature-disabled:alert-wizard-performance',
   'feature-disabled:custom-inbound-filters',

+ 2 - 0
static/app/types/hooks.tsx

@@ -49,6 +49,7 @@ export type RouteHooks = {
  */
 type DateRangeProps = React.ComponentProps<typeof DateRange>;
 type SelectorItemsProps = React.ComponentProps<typeof SelectorItems>;
+type GlobalNotificationProps = {className: string; organization?: Organization};
 
 /**
  * Component wrapping hooks
@@ -56,6 +57,7 @@ type SelectorItemsProps = React.ComponentProps<typeof SelectorItems>;
 export type ComponentHooks = {
   'component:header-date-range': () => React.ComponentType<DateRangeProps>;
   'component:header-selector-items': () => React.ComponentType<SelectorItemsProps>;
+  'component:global-notifications': () => React.ComponentType<GlobalNotificationProps>;
 };
 
 /**

+ 2 - 1
static/app/views/app/alertMessage.tsx

@@ -17,6 +17,7 @@ type AlertType = {
   message: React.ReactNode;
   type: 'success' | 'error' | 'warning' | 'info';
   url?: string;
+  onClose?: () => void;
 };
 
 type Props = {
@@ -45,7 +46,7 @@ const AlertMessage = ({alert, system}: Props) => {
       <StyledCloseButton
         icon={<IconClose size="md" isCircled />}
         aria-label={t('Close')}
-        onClick={handleCloseAlert}
+        onClick={alert.onClose ?? handleCloseAlert}
         size="zero"
         borderless
       />

+ 18 - 1
static/app/views/app/index.tsx

@@ -15,6 +15,7 @@ import AlertActions from 'app/actions/alertActions';
 import {Client, initApiClientErrorHandling} from 'app/api';
 import ErrorBoundary from 'app/components/errorBoundary';
 import GlobalModal from 'app/components/globalModal';
+import HookOrDefault from 'app/components/hookOrDefault';
 import Indicators from 'app/components/indicators';
 import LoadingIndicator from 'app/components/loadingIndicator';
 import {DEPLOY_PREVIEW_CONFIG, EXPERIMENTAL_SPA} from 'app/constants';
@@ -22,13 +23,19 @@ import {t} from 'app/locale';
 import ConfigStore from 'app/stores/configStore';
 import HookStore from 'app/stores/hookStore';
 import OrganizationsStore from 'app/stores/organizationsStore';
-import {Config} from 'app/types';
+import OrganizationStore from 'app/stores/organizationStore';
+import {Config, Organization} from 'app/types';
 import withApi from 'app/utils/withApi';
 import withConfig from 'app/utils/withConfig';
 import NewsletterConsent from 'app/views/newsletterConsent';
 
 import SystemAlerts from './systemAlerts';
 
+const GlobalNotifications = HookOrDefault({
+  hookName: 'component:global-notifications',
+  defaultComponent: () => null,
+});
+
 function getAlertTypeForProblem(problem) {
   switch (problem.severity) {
     case 'critical':
@@ -49,6 +56,7 @@ type State = {
   needsUpgrade: boolean;
   newsletterConsentPrompt: boolean;
   user?: Config['user'];
+  organization?: Organization;
 };
 
 class App extends Component<Props, State> {
@@ -137,9 +145,14 @@ class App extends Component<Props, State> {
 
   componentWillUnmount() {
     OrganizationsStore.load([]);
+    this.unlistener?.();
   }
 
   mainContainerRef = createRef<HTMLDivElement>();
+  unlistener = OrganizationStore.listen(
+    state => this.setState({organization: state.organization}),
+    undefined
+  );
 
   handleConfigStoreChange(config) {
     const newState = {} as State;
@@ -219,6 +232,10 @@ class App extends Component<Props, State> {
       <MainContainer tabIndex={-1} ref={this.mainContainerRef}>
         <GlobalModal onClose={this.handleGlobalModalClose} />
         <SystemAlerts className="messages-container" />
+        <GlobalNotifications
+          className="notifications-container messages-container"
+          organization={this.state.organization}
+        />
         <Indicators className="indicators-container" />
         <ErrorBoundary>{this.renderBody()}</ErrorBoundary>
       </MainContainer>