The communications platform that puts data protection first.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
Rocket.Chat/apps/meteor/client/sidebar/header/UserDropdown.tsx

183 lines
6.3 KiB

import type { IUser, ValueOf } from '@rocket.chat/core-typings';
import { UserStatus as UserStatusEnum } from '@rocket.chat/core-typings';
import {
Box,
Margins,
Option,
OptionColumn,
OptionContent,
OptionDivider,
OptionIcon,
OptionInput,
OptionTitle,
RadioButton,
} from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import type { TranslationKey } from '@rocket.chat/ui-contexts';
import { useLayout, useRoute, useLogout, useSetting, useTranslation } from '@rocket.chat/ui-contexts';
import { useThemeMode } from '@rocket.chat/ui-theming/src/hooks/useThemeMode';
import type { ReactElement } from 'react';
import React from 'react';
import { AccountBox } from '../../../app/ui-utils/client';
import { userStatus } from '../../../app/user-status/client';
import { callbacks } from '../../../lib/callbacks';
import MarkdownText from '../../components/MarkdownText';
import { UserStatus } from '../../components/UserStatus';
import UserAvatar from '../../components/avatar/UserAvatar';
import { useUserDisplayName } from '../../hooks/useUserDisplayName';
import { imperativeModal } from '../../lib/imperativeModal';
import { useStatusDisabledModal } from '../../views/admin/customUserStatus/hooks/useStatusDisabledModal';
import EditStatusModal from './EditStatusModal';
const isDefaultStatus = (id: string): boolean => (Object.values(UserStatusEnum) as string[]).includes(id);
const isDefaultStatusName = (_name: string, id: string): _name is UserStatusEnum => isDefaultStatus(id);
const setStatus = (status: (typeof userStatus.list)['']): void => {
AccountBox.setStatus(status.statusType, !isDefaultStatus(status.id) ? status.name : '');
void callbacks.run('userStatusManuallySet', status);
};
const translateStatusName = (t: ReturnType<typeof useTranslation>, status: (typeof userStatus.list)['']): string => {
if (isDefaultStatusName(status.name, status.id)) {
return t(status.name as TranslationKey);
}
return status.name;
};
type UserDropdownProps = {
user: Pick<IUser, 'username' | 'name' | 'avatarETag' | 'status' | 'statusText'>;
onClose: () => void;
};
const UserDropdown = ({ user, onClose }: UserDropdownProps): ReactElement => {
const t = useTranslation();
const accountRoute = useRoute('account-index');
const logout = useLogout();
const { isMobile } = useLayout();
const presenceDisabled = useSetting<boolean>('Presence_broadcast_disabled');
const handleStatusDisabledModal = useStatusDisabledModal();
const [selectedTheme, setTheme] = useThemeMode();
const { username, avatarETag, status, statusText } = user;
const displayName = useUserDisplayName(user);
const filterInvisibleStatus = !useSetting('Accounts_AllowInvisibleStatusOption')
? (status: ValueOf<(typeof userStatus)['list']>): boolean => status.name !== 'invisible'
: (): boolean => true;
const handleCustomStatus = useMutableCallback((e) => {
e.preventDefault();
imperativeModal.open({
component: EditStatusModal,
props: { userStatus: status, userStatusText: statusText, onClose: imperativeModal.close },
});
onClose();
});
const handleMyAccount = useMutableCallback(() => {
accountRoute.push({});
onClose();
});
const handleLogout = useMutableCallback(() => {
logout();
onClose();
});
return (
<Box display='flex' flexDirection='column' w={!isMobile ? '244px' : undefined}>
<Box pi='x12' display='flex' flexDirection='row' alignItems='center'>
<Box mie='x4'>
<UserAvatar size='x36' username={username || ''} etag={avatarETag} />
</Box>
<Box mis='x4' display='flex' overflow='hidden' flexDirection='column' fontScale='p2' mb='neg-x4' flexGrow={1} flexShrink={1}>
<Box withTruncatedText w='full' display='flex' alignItems='center' flexDirection='row'>
<Margins inline='x4'>
<UserStatus status={presenceDisabled ? 'disabled' : status} />
<Box is='span' withTruncatedText display='inline-block' fontWeight='700'>
{displayName}
</Box>
</Margins>
</Box>
<Box color='hint'>
<MarkdownText
withTruncatedText
parseEmoji={true}
content={statusText || t(status ?? 'offline')}
variant='inlineWithoutBreaks'
/>
</Box>
</Box>
</Box>
<OptionDivider />
<OptionTitle>{t('Status')}</OptionTitle>
{presenceDisabled && (
<Box fontScale='p2' mi='x12' mb='x4'>
<Box mbe='x4'>{t('User_status_disabled')}</Box>
<Box is='a' color='info' onClick={handleStatusDisabledModal}>
{t('Learn_more')}
</Box>
</Box>
)}
{Object.values(userStatus.list)
.filter(filterInvisibleStatus)
.map((status, i) => {
const name = status.localizeName ? translateStatusName(t, status) : status.name;
const modifier = status.statusType || user.status;
return (
<Option
key={i}
disabled={presenceDisabled}
onClick={(): void => {
setStatus(status);
onClose();
}}
>
<OptionColumn>
<UserStatus status={modifier} />
</OptionColumn>
<OptionContent>
<MarkdownText content={name} parseEmoji={true} variant='inline' />
</OptionContent>
</Option>
);
})}
<Option icon='emoji' label={`${t('Custom_Status')}...`} onClick={handleCustomStatus} disabled={presenceDisabled}></Option>
<OptionDivider />
<OptionTitle>{t('Theme')}</OptionTitle>
<Option is='label' role='listitem'>
<OptionIcon name='sun' />
<OptionContent>{t('Theme_light')}</OptionContent>
<OptionInput>
<RadioButton checked={selectedTheme === 'light'} onChange={setTheme('light')} m='x4' />
</OptionInput>
</Option>
<Option is='label' role='listitem'>
<OptionIcon name='moon' />
<OptionContent>{t('Theme_dark')}</OptionContent>
<OptionInput>
<RadioButton checked={selectedTheme === 'dark'} onChange={setTheme('dark')} m='x4' />
</OptionInput>
</Option>
<Option is='label' role='listitem'>
<OptionIcon name='desktop' />
<OptionContent>{t('Theme_match_system')}</OptionContent>
<OptionInput>
<RadioButton checked={selectedTheme === 'auto'} onChange={setTheme('auto')} m='x4' />
</OptionInput>
</Option>
<OptionDivider />
<Option icon='user' label={t('My_Account')} onClick={handleMyAccount}></Option>
<Option icon='sign-out' label={t('Logout')} onClick={handleLogout}></Option>
</Box>
);
};
export default UserDropdown;