-
Notifications
You must be signed in to change notification settings - Fork 13.7k
Expand file tree
/
Copy pathUserStatusMenu.tsx
More file actions
106 lines (92 loc) · 3.08 KB
/
Copy pathUserStatusMenu.tsx
File metadata and controls
106 lines (92 loc) · 3.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import { UserStatus as UserStatusType } from '@rocket.chat/core-typings';
import type { OptionType } from '@rocket.chat/fuselage';
import { Button, PositionAnimated, Options, useCursor, Box } from '@rocket.chat/fuselage';
import { useSetting } from '@rocket.chat/ui-contexts';
import type { ComponentProps, ReactNode } from 'react';
import { useRef, useCallback, useState, useMemo, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { UserStatus } from './UserStatus';
type UserStatusMenuProps = {
onChange: (type: UserStatusType) => void;
initialStatus?: UserStatusType;
optionWidth?: ComponentProps<typeof Box>['width'];
placement?: ComponentProps<typeof PositionAnimated>['placement'];
};
const UserStatusMenu = ({
onChange,
initialStatus = UserStatusType.OFFLINE,
optionWidth = undefined,
placement = 'bottom-start',
}: UserStatusMenuProps) => {
const { t } = useTranslation();
const [status, setStatus] = useState(initialStatus);
const allowInvisibleStatus = useSetting('Accounts_AllowInvisibleStatusOption', true);
const options = useMemo(() => {
const renderOption = (status: UserStatusType, label: string) => (
<Box display='flex' flexDirection='row' alignItems='center'>
<Box marginInlineEnd={8}>
<UserStatus status={status} />
</Box>
{label}
</Box>
);
const statuses: Array<[value: UserStatusType, label: ReactNode]> = [
[UserStatusType.ONLINE, renderOption(UserStatusType.ONLINE, t('Online'))],
[UserStatusType.BUSY, renderOption(UserStatusType.BUSY, t('Busy'))],
];
// Away is no longer manually selectable, but surface it if the user is currently on it
// (e.g., set in a previous version or auto-applied by the server) so they can switch off.
if (status === UserStatusType.AWAY) {
statuses.push([UserStatusType.AWAY, renderOption(UserStatusType.AWAY, t('Away'))]);
}
if (allowInvisibleStatus) {
statuses.push([UserStatusType.OFFLINE, renderOption(UserStatusType.OFFLINE, t('Offline'))]);
}
return statuses;
}, [t, allowInvisibleStatus, status]);
const [cursor, handleKeyDown, handleKeyUp, reset, [visible, hide, show]] = useCursor(-1, options, ([selected], [, hide]) => {
setStatus(selected);
reset();
hide();
});
const ref = useRef<HTMLButtonElement>(null);
const onClick = useCallback(() => {
if (!ref?.current) {
return;
}
ref.current.focus();
show();
}, [show]);
const handleSelection = useCallback(
([selected]: OptionType) => {
setStatus(selected as UserStatusType);
reset();
hide();
},
[hide, reset],
);
useEffect(() => {
onChange(status);
}, [status, onChange]);
return (
<>
<Button
ref={ref}
mini
square
secondary
onClick={onClick}
onBlur={hide}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
aria-label={t('User_status_menu')}
>
<UserStatus status={status} />
</Button>
<PositionAnimated width='auto' visible={visible} anchor={ref} placement={placement}>
<Options width={optionWidth} onSelect={handleSelection} options={options} cursor={cursor} />
</PositionAnimated>
</>
);
};
export default UserStatusMenu;