import {Fragment} from 'react'; import styled from '@emotion/styled'; import {logout} from 'sentry/actionCreators/account'; import {Client} from 'sentry/api'; import DemoModeGate from 'sentry/components/acl/demoModeGate'; import Avatar from 'sentry/components/avatar'; import DropdownMenu from 'sentry/components/dropdownMenu'; import Hook from 'sentry/components/hook'; import IdBadge from 'sentry/components/idBadge'; import Link from 'sentry/components/links/link'; import SidebarDropdownMenu from 'sentry/components/sidebar/sidebarDropdownMenu.styled'; import SidebarMenuItem, {menuItemStyles} from 'sentry/components/sidebar/sidebarMenuItem'; import SidebarOrgSummary from 'sentry/components/sidebar/sidebarOrgSummary'; import TextOverflow from 'sentry/components/textOverflow'; import {IconChevron, IconSentry} from 'sentry/icons'; import {t} from 'sentry/locale'; import ConfigStore from 'sentry/stores/configStore'; import space from 'sentry/styles/space'; import {Config, Organization, Project, User} from 'sentry/types'; import withApi from 'sentry/utils/withApi'; import withProjects from 'sentry/utils/withProjects'; import SidebarMenuItemLink from '../sidebarMenuItemLink'; import {CommonSidebarProps} from '../types'; import Divider from './divider.styled'; import SwitchOrganization from './switchOrganization'; // TODO: make org and user optional props type Props = Pick & { api: Client; config: Config; projects: Project[]; user: User; /** * Set to true to hide links within the organization */ hideOrgLinks?: boolean; org?: Organization; }; const SidebarDropdown = ({ api, org, projects, orientation, collapsed, config, user, hideOrgLinks, }: Props) => { const handleLogout = async () => { await logout(api); window.location.assign('/auth/login/'); }; const hasOrganization = !!org; const hasUser = !!user; // It's possible we do not have an org in context (e.g. RouteNotFound) // Otherwise, we should have the full org const hasOrgRead = org?.access?.includes('org:read'); const hasMemberRead = org?.access?.includes('member:read'); const hasTeamRead = org?.access?.includes('team:read'); const canCreateOrg = ConfigStore.get('features').has('organizations:create'); // Avatar to use: Organization --> user --> Sentry const avatar = hasOrganization || hasUser ? ( ) : ( ); return ( {({isOpen, getRootProps, getActorProps, getMenuProps}) => ( {avatar} {!collapsed && orientation !== 'top' && ( {hasOrganization ? org.name : user.name}{' '} {hasOrganization ? user.name : user.email} )} {isOpen && ( {hasOrganization && ( {!hideOrgLinks && ( {hasOrgRead && ( {t('Organization settings')} )} {hasMemberRead && ( {t('Members')} )} {hasTeamRead && ( {t('Teams')} )} )} {!config.singleOrganization && ( )} )} {hasOrganization && user && } {!!user && (
{t('User settings')} {t('API keys')} {hasOrganization && ( )} {user.isSuperuser && ( {t('Admin')} )} {t('Sign out')}
)}
)}
)}
); }; export default withApi(withProjects(SidebarDropdown)); const SentryLink = styled(Link)` color: ${p => p.theme.white}; &:hover { color: ${p => p.theme.white}; } `; const UserSummary = styled(Link)< Omit, 'children'> >` ${p => menuItemStyles(p)} padding: 10px 15px; `; const UserBadgeNoOverflow = styled(IdBadge)` overflow: hidden; `; const SidebarDropdownRoot = styled('div')` position: relative; `; // So that long org names and user names do not overflow const OrgAndUserWrapper = styled('div')` overflow-x: hidden; text-align: left; `; const OrgOrUserName = styled(TextOverflow)` font-size: ${p => p.theme.fontSizeLarge}; line-height: 1.2; font-weight: bold; color: ${p => p.theme.white}; text-shadow: 0 0 6px rgba(255, 255, 255, 0); transition: 0.15s text-shadow linear; `; const UserNameOrEmail = styled(TextOverflow)` font-size: ${p => p.theme.fontSizeMedium}; line-height: 16px; transition: 0.15s color linear; `; const SidebarDropdownActor = styled('button')` display: flex; align-items: flex-start; cursor: pointer; border: none; padding: 0; background: none; width: 100%; &:hover { ${OrgOrUserName} { text-shadow: 0 0 6px rgba(255, 255, 255, 0.1); } ${UserNameOrEmail} { color: ${p => p.theme.white}; } } `; const StyledAvatar = styled(Avatar)<{collapsed: boolean}>` margin: ${space(0.25)} 0; margin-right: ${p => (p.collapsed ? '0' : space(1.5))}; box-shadow: 0 2px 0 rgba(0, 0, 0, 0.08); border-radius: 6px; /* Fixes background bleeding on corners */ `; const OrgAndUserMenu = styled('div')` ${SidebarDropdownMenu}; top: 42px; min-width: 180px; z-index: ${p => p.theme.zIndex.orgAndUserMenu}; `; const StyledIconChevron = styled(IconChevron)` margin-left: ${space(0.25)}; `;