import { Popover, Transition } from '@headlessui/react' import { ActionButton, ActionButtonProps } from './ActionButton' import { useLocation, useNavigate } from 'react-router-dom' import { Fragment, useMemo, useState } from 'react' import { PATHS } from 'lib/paths' import { Models } from '@kittycad/lib' import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath' import Tooltip from './Tooltip' import usePlatform from 'hooks/usePlatform' import { isDesktop } from 'lib/isDesktop' import { CustomIcon } from './CustomIcon' import { authActor } from 'machines/appMachine' type User = Models['User_type'] const UserSidebarMenu = ({ user }: { user?: User }) => { const platform = usePlatform() const location = useLocation() const filePath = useAbsoluteFilePath() const displayedName = getDisplayName(user) const [imageLoadFailed, setImageLoadFailed] = useState(false) const navigate = useNavigate() const send = authActor.send // We filter this memoized list so that no orphan "break" elements are rendered. const userMenuItems = useMemo<(ActionButtonProps | 'break')[]>( () => [ { id: 'settings', Element: 'button', children: ( <> User settings {`${platform === 'macos' ? '⌘' : 'Ctrl'}${ isDesktop() ? '' : '⬆' },`} ), 'data-testid': 'user-settings', onClick: () => { const targetPath = location.pathname.includes(PATHS.FILE) ? filePath + PATHS.SETTINGS_USER : PATHS.HOME + PATHS.SETTINGS_USER navigate(targetPath) }, }, { id: 'keybindings', Element: 'button', children: 'Keyboard shortcuts', onClick: () => { const targetPath = location.pathname.includes(PATHS.FILE) ? filePath + PATHS.SETTINGS_KEYBINDINGS : PATHS.HOME + PATHS.SETTINGS_KEYBINDINGS navigate(targetPath) }, }, { id: 'account', Element: 'externalLink', to: 'https://zoo.dev/account', children: ( <> Manage account ), }, 'break', { id: 'request-feature', Element: 'externalLink', to: 'https://github.com/KittyCAD/modeling-app/discussions', children: ( <> Request a feature ), }, { id: 'report-bug', Element: 'externalLink', to: 'https://github.com/KittyCAD/modeling-app/issues/new/choose', children: ( <> Report a bug ), }, { id: 'community', Element: 'externalLink', to: 'https://discord.gg/JQEpHR7Nt2', children: ( <> Ask the community ), }, { id: 'release-notes', Element: 'externalLink', to: 'https://github.com/KittyCAD/modeling-app/releases', children: ( <> Release notes ), }, 'break', { id: 'sign-out', Element: 'button', 'data-testid': 'user-sidebar-sign-out', children: 'Sign out', onClick: () => send({ type: 'Log out' }), className: '', // Just making TS's filter type coercion happy 😠 }, ].filter( (props) => props === 'break' || (typeof props !== 'string' && !props.className?.includes('hidden')) ) as (ActionButtonProps | 'break')[], [platform, location, filePath, navigate, send] ) // This image host goes down sometimes. We will instead rewrite the // resource to be a local one. if (user?.image === 'https://placekitten.com/200/200') { user.image = '/cat.jpg' } // Fallback logic for displaying user's "name": // 1. user.name // 2. user.first_name + ' ' + user.last_name // 3. user.first_name // 4. user.email function getDisplayName(user?: User) { if (!user) return null if (user.name) return user.name if (user.first_name) { if (user.last_name) return user.first_name + ' ' + user.last_name return user.first_name } return user.email } return (
{user?.image && !imageLoadFailed ? ( {user?.name setImageLoadFailed(true)} /> ) : ( )}
User menu
{({ close }) => ( <> {user && (

{displayedName || ''}

{displayedName !== user.email && (

{user.email}

)}
)}
    {userMenuItems.map((props, index) => { if (props === 'break') { return index !== userMenuItems.length - 1 ? (

  • ) : null } const { id, children, ...rest } = props return (
  • { close() }} > {children}
  • ) })}
)}
) } export default UserSidebarMenu