category ?()

in desktop/flipper-ui-core/src/NotificationsHub.tsx [214:427]


              category ? () => this.onHideCategory(category) : undefined
            }
            selectPlugin={this.props.selectPlugin}
            logger={this.props.logger}
          />
        );
      })
      .reverse();

    const invalidatedNotifications = this.props.invalidatedNotifications
      .filter(this.getFilter())
      .map((n: PluginNotification) => (
        <NotificationItem
          key={n.notification.id}
          {...n}
          plugin={this.getPlugin(n.pluginId)}
          onClear={this.props.onClear}
          inactive
        />
      ))
      .reverse();

    return (
      <ContextMenu items={this.contextMenuItems} component={Content}>
        {activeNotifications.length > 0 && (
          <Fragment>
            <Heading>Active notifications</Heading>
            <FlexColumn shrink={false}>{activeNotifications}</FlexColumn>
          </Fragment>
        )}
        {invalidatedNotifications.length > 0 && (
          <Fragment>
            <Heading>Past notifications</Heading>
            <FlexColumn shrink={false}>{invalidatedNotifications}</FlexColumn>
          </Fragment>
        )}
        {activeNotifications.length + invalidatedNotifications.length === 0 && (
          <NoContent>
            <Glyph
              name="bell-null"
              size={24}
              variant="outline"
              color={colors.light30}
            />
            No Notifications
          </NoContent>
        )}
      </ContextMenu>
    );
  }
}

export const ConnectedNotificationsTable = connect<
  StateFromProps,
  DispatchFromProps,
  OwnProps,
  StoreState
>(
  ({
    notifications: {
      activeNotifications,
      invalidatedNotifications,
      blocklistedPlugins,
      blocklistedCategories,
    },
    plugins: {devicePlugins, clientPlugins},
  }) => ({
    activeNotifications,
    invalidatedNotifications,
    blocklistedPlugins,
    blocklistedCategories,
    devicePlugins,
    clientPlugins,
  }),
  {
    updatePluginBlocklist,
    updateCategoryBlocklist,
    selectPlugin,
  },
)(Searchable(NotificationsTable));

const shadow = (
  props: {isSelected?: boolean; inactive?: boolean},
  _hover?: boolean,
) => {
  if (props.inactive) {
    return `inset 0 0 0 1px ${colors.light10}`;
  }
  const shadow = ['1px 1px 5px rgba(0,0,0,0.1)'];
  if (props.isSelected) {
    shadow.push(`inset 0 0 0 2px ${colors.macOSTitleBarIconSelected}`);
  }

  return shadow.join(',');
};

const SEVERITY_COLOR_MAP = {
  warning: colors.yellow,
  error: colors.red,
};

type NotificationBoxProps = {
  inactive?: boolean;
  isSelected?: boolean;
  severity: keyof typeof SEVERITY_COLOR_MAP;
};

const NotificationBox = styled(FlexRow)<NotificationBoxProps>((props) => ({
  backgroundColor: props.inactive ? 'transparent' : colors.white,
  opacity: props.inactive ? 0.5 : 1,
  alignItems: 'flex-start',
  borderRadius: 5,
  padding: 10,
  flexShrink: 0,
  overflow: 'hidden',
  position: 'relative',
  marginBottom: 10,
  boxShadow: shadow(props),
  '::before': {
    content: '""',
    display: !props.inactive && !props.isSelected ? 'block' : 'none',
    position: 'absolute',
    left: 0,
    top: 0,
    bottom: 0,
    width: 3,
    backgroundColor: SEVERITY_COLOR_MAP[props.severity] || colors.info,
  },
  ':hover': {
    boxShadow: shadow(props, true),
    '& > *': {
      opacity: 1,
    },
  },
}));

const Title = styled.div({
  minWidth: 150,
  color: colors.light80,
  flexShrink: 0,
  marginBottom: 6,
  fontWeight: 500,
  lineHeight: 1,
  fontSize: '1.1em',
});

const NotificationContent = styled(FlexColumn)<{isSelected?: boolean}>(
  (props) => ({
    marginLeft: 6,
    marginRight: 10,
    flexGrow: 1,
    overflow: 'hidden',
    maxHeight: props.isSelected ? 'none' : 56,
    lineHeight: 1.4,
    color: props.isSelected ? colors.light50 : colors.light30,
    userSelect: 'text',
  }),
);

const Actions = styled(FlexRow)({
  alignItems: 'center',
  justifyContent: 'space-between',
  color: colors.light20,
  marginTop: 12,
  borderTop: `1px solid ${colors.light05}`,
  paddingTop: 8,
});

const NotificationButton = styled.div({
  border: `1px solid ${colors.light20}`,
  color: colors.light50,
  borderRadius: 4,
  textAlign: 'center',
  padding: 4,
  width: 80,
  marginBottom: 4,
  opacity: 0,
  transition: '0.15s opacity',
  '[data-role="notification"]:hover &': {
    opacity: 0.5,
  },
  ':last-child': {
    marginBottom: 0,
  },
  '[data-role="notification"] &:hover': {
    opacity: 1,
  },
});

type ItemProps = {
  onHighlight?: () => any;
  onHidePlugin?: () => any;
  onHideCategory?: () => any;
  onClear?: () => any;
  isSelected?: boolean;
  inactive?: boolean;
  selectPlugin?: typeof selectPlugin;
  logger?: Logger;
  plugin: PluginDefinition | null | undefined;
};

type ItemState = {
  reportedNotHelpful: boolean;
};

class NotificationItem extends Component<
  ItemProps & PluginNotification,
  ItemState
> {
  constructor(props: ItemProps & PluginNotification) {
    super(props);
    const items: Array<ContextMenuItem> = [];
    if (props.onHidePlugin && props.plugin) {
      items.push({