Skip to content

Commit df2a23b

Browse files
authored
regression: "Voice call" action enabled when a call is in progress (#37967)
1 parent 356ad51 commit df2a23b

File tree

10 files changed

+83
-30
lines changed

10 files changed

+83
-30
lines changed

apps/meteor/client/views/mediaCallHistory/CallHistoryRowExternalUser.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { GenericMenu } from '@rocket.chat/ui-client';
2-
import { CallHistoryTableRow, useMediaCallContext } from '@rocket.chat/ui-voip';
32
import type { CallHistoryTableExternalContact, CallHistoryTableRowProps } from '@rocket.chat/ui-voip';
3+
import { CallHistoryTableRow, useMediaCallContext, isCallingBlocked } from '@rocket.chat/ui-voip';
44
import { useCallback, useMemo } from 'react';
55
import { useTranslation } from 'react-i18next';
66

@@ -11,25 +11,27 @@ type CallHistoryRowExternalUserProps = Omit<CallHistoryTableRowProps<CallHistory
1111
const CallHistoryRowExternalUser = ({ _id, contact, type, status, duration, timestamp, onClick }: CallHistoryRowExternalUserProps) => {
1212
const { t } = useTranslation();
1313

14-
const { onToggleWidget } = useMediaCallContext();
14+
const { onToggleWidget, state } = useMediaCallContext();
1515

1616
const handleClick = useCallback(() => {
1717
onClick(_id);
1818
}, [onClick, _id]);
1919

2020
const actions = useMemo(() => {
21-
if (!onToggleWidget) {
21+
if (state === 'unauthorized' || state === 'unlicensed' || !onToggleWidget) {
2222
return [];
2323
}
2424
return [
2525
{
2626
id: 'voiceCall',
2727
icon: 'phone',
2828
content: t('Voice_call'),
29+
disabled: isCallingBlocked(state),
30+
tooltip: isCallingBlocked(state) ? t('Call_in_progress') : undefined,
2931
onClick: () => onToggleWidget({ number: contact.number }),
3032
} as const,
3133
];
32-
}, [contact, onToggleWidget, t]);
34+
}, [contact, onToggleWidget, t, state]);
3335

3436
return (
3537
<CallHistoryTableRow

apps/meteor/client/views/mediaCallHistory/CallHistoryRowInternalUser.tsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Keys as IconName } from '@rocket.chat/icons';
22
import { GenericMenu } from '@rocket.chat/ui-client';
3-
import { CallHistoryTableRow } from '@rocket.chat/ui-voip';
4-
import type { CallHistoryTableRowProps, CallHistoryTableInternalContact } from '@rocket.chat/ui-voip';
3+
import type { CallHistoryTableRowProps, CallHistoryTableInternalContact, MediaCallState } from '@rocket.chat/ui-voip';
4+
import { CallHistoryTableRow, useMediaCallContext, isCallingBlocked } from '@rocket.chat/ui-voip';
55
import type { TFunction } from 'i18next';
66
import { useCallback } from 'react';
77
import { useTranslation } from 'react-i18next';
@@ -37,15 +37,20 @@ const i18nDictionary: Record<HistoryActions, string> = {
3737
userInfo: 'User_info',
3838
} as const;
3939

40-
const getItems = (actions: HistoryActionCallbacks, t: TFunction) => {
40+
const getItems = (actions: HistoryActionCallbacks, t: TFunction, state: MediaCallState) => {
4141
return (Object.entries(actions) as [HistoryActions, () => void][])
4242
.filter(([_, callback]) => callback)
43-
.map(([action, callback]) => ({
44-
id: action,
45-
icon: iconDictionary[action],
46-
content: t(i18nDictionary[action]),
47-
onClick: callback,
48-
}));
43+
.map(([action, callback]) => {
44+
const disabled = action === 'voiceCall' && isCallingBlocked(state);
45+
return {
46+
id: action,
47+
icon: iconDictionary[action],
48+
content: t(i18nDictionary[action]),
49+
disabled,
50+
tooltip: disabled ? t('Call_in_progress') : undefined,
51+
onClick: callback,
52+
};
53+
});
4954
};
5055

5156
const CallHistoryRowInternalUser = ({
@@ -61,6 +66,7 @@ const CallHistoryRowInternalUser = ({
6166
onClick,
6267
}: CallHistoryRowInternalUserProps) => {
6368
const { t } = useTranslation();
69+
const { state } = useMediaCallContext();
6470
const actions = useMediaCallInternalHistoryActions({
6571
contact: {
6672
_id: contact._id,
@@ -73,7 +79,7 @@ const CallHistoryRowInternalUser = ({
7379
openUserInfo: onClickUserInfo ? (userId) => onClickUserInfo(userId, rid) : undefined,
7480
});
7581

76-
const items = getItems(actions, t);
82+
const items = getItems(actions, t, state);
7783

7884
const handleClick = useCallback(() => {
7985
onClick(_id);

apps/meteor/client/views/mediaCallHistory/useMediaCallInternalHistoryActions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ export const useMediaCallInternalHistoryActions = ({
2929
messageRoomId,
3030
openUserInfo,
3131
}: UseMediaCallInternalHistoryActionsBaseOptions) => {
32-
const { onToggleWidget } = useMediaCallContext();
32+
const { onToggleWidget, state } = useMediaCallContext();
3333
const router = useRouter();
3434

3535
const getAvatarUrl = useUserAvatarPath();
3636

3737
const voiceCall = useEffectEvent(() => {
38-
if (!onToggleWidget) {
38+
if (state === 'unauthorized' || state === 'unlicensed' || !onToggleWidget) {
3939
return;
4040
}
4141

packages/i18n/src/locales/en.i18n.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,7 @@
976976
"Call": "Call",
977977
"Call_Already_Ended": "Call Already Ended",
978978
"Call_ID": "Call ID",
979+
"Call_in_progress": "Call in progress",
979980
"Call_info": "Call info",
980981
"Call_info_could_not_be_loaded": "Call info could not be loaded",
981982
"Call_Information": "Call Information",

packages/ui-voip/src/context/MediaCallContext.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ const MediaCallContext = createContext<MediaCallContextType | MediaCallUnauthori
119119
defaultMediaCallContextValue,
120120
);
121121

122+
export type MediaCallExternalState = State | 'unauthorized' | 'unlicensed';
123+
124+
export const isCallingBlocked = (state: MediaCallExternalState) => {
125+
return state !== 'new' && state !== 'closed';
126+
};
127+
122128
// This hook is for internal use only. It will only be available if the user has the necessary permissions and the workspace has the necessary modules.
123129
export const useMediaCallContext = (): MediaCallContextType => {
124130
const context = useContext(MediaCallContext);
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export { useMediaCallContext, useMediaCallExternalContext, default as MediaCallContext, usePeerAutocomplete } from './MediaCallContext';
2-
export type { PeerInfo, ConnectionState } from './MediaCallContext';
3-
export { isFirstPeerAutocompleteOption } from './MediaCallContext';
2+
export type { PeerInfo, ConnectionState, MediaCallExternalState as MediaCallState } from './MediaCallContext';
3+
export { isFirstPeerAutocompleteOption, isCallingBlocked } from './MediaCallContext';
44
export { default as MockedMediaCallProvider } from './MockedMediaCallProvider';

packages/ui-voip/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export { default as MediaCallProvider } from './context/MediaCallProvider';
22

3-
export { MediaCallContext, useMediaCallExternalContext as useMediaCallContext, type PeerInfo } from './context';
4-
3+
export { MediaCallContext, useMediaCallExternalContext as useMediaCallContext, isCallingBlocked } from './context';
4+
export type { PeerInfo, MediaCallState } from './context';
55
export { useMediaCallAction } from './hooks';
66

77
export { CallHistoryContextualBar } from './views';

packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryActions.stories.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { mockAppRoot } from '@rocket.chat/mock-providers';
2-
import type { Meta, StoryObj } from '@storybook/react';
2+
import type { Meta, StoryFn, StoryObj } from '@storybook/react';
33
import type { ReactElement } from 'react';
44

55
import type { HistoryActionCallbacks } from './CallHistoryActions';
66
import CallHistoryActions from './CallHistoryActions';
7+
import { MockedMediaCallProvider } from '../../context';
8+
import type { State } from '../../context/MediaCallContext';
79

810
const noop = () => undefined;
911

@@ -36,23 +38,42 @@ const getArgs = (index: number) => {
3638
return Object.fromEntries(actionList.slice(0, index).map((action) => [action, noop])) as HistoryActionCallbacks;
3739
};
3840

41+
const getDecorator = (state: State) => {
42+
return (Story: StoryFn): ReactElement => (
43+
<MockedMediaCallProvider state={state}>
44+
<Story />
45+
</MockedMediaCallProvider>
46+
);
47+
};
48+
3949
export const Default: Story = {
4050
args: {
4151
onClose: noop,
4252
actions: getArgs(5),
4353
},
54+
decorators: [getDecorator('closed')],
4455
};
4556

4657
export const WithLessActions: Story = {
4758
args: {
4859
onClose: noop,
4960
actions: getArgs(3),
5061
},
62+
decorators: [getDecorator('closed')],
5163
};
5264

5365
export const WithSingleAction: Story = {
5466
args: {
5567
onClose: noop,
5668
actions: getArgs(1),
5769
},
70+
decorators: [getDecorator('closed')],
71+
};
72+
73+
export const WithDisabledVoiceCall: Story = {
74+
args: {
75+
onClose: noop,
76+
actions: getArgs(1),
77+
},
78+
decorators: [getDecorator('ongoing')],
5879
};

packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryActions.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import { ContextualbarActions, ContextualbarClose, GenericMenu } from '@rocket.c
33
import type { TFunction } from 'i18next';
44
import { useTranslation } from 'react-i18next';
55

6+
import type { MediaCallState } from '../../context';
7+
import { isCallingBlocked, useMediaCallExternalContext } from '../../context/MediaCallContext';
8+
69
type HistoryActions = 'voiceCall' | 'videoCall' | 'jumpToMessage' | 'directMessage' | 'userInfo';
710

811
export type HistoryActionCallbacks = {
@@ -30,21 +33,27 @@ const i18nDictionary: Record<HistoryActions, string> = {
3033
userInfo: 'User_info',
3134
} as const;
3235

33-
const getItems = (actions: HistoryActionCallbacks, t: TFunction) => {
36+
const getItems = (actions: HistoryActionCallbacks, t: TFunction, state: MediaCallState) => {
3437
return (Object.entries(actions) as [HistoryActions, () => void][])
3538
.filter(([_, callback]) => callback)
36-
.map(([action, callback]) => ({
37-
id: action,
38-
icon: iconDictionary[action],
39-
content: t(i18nDictionary[action]),
40-
onClick: callback,
41-
}));
39+
.map(([action, callback]) => {
40+
const disabled = action === 'voiceCall' && isCallingBlocked(state);
41+
return {
42+
id: action,
43+
icon: iconDictionary[action],
44+
content: t(i18nDictionary[action]),
45+
disabled,
46+
onClick: callback,
47+
tooltip: disabled ? t('Call_in_progress') : undefined,
48+
};
49+
});
4250
};
4351

4452
const CallHistoryActions = ({ onClose, actions }: CallHistoryActionsProps) => {
4553
const { t } = useTranslation();
4654

47-
const items = getItems(actions, t);
55+
const { state } = useMediaCallExternalContext();
56+
const items = getItems(actions, t, state);
4857
return (
4958
<ContextualbarActions>
5059
{items.length > 0 && <GenericMenu title={t('Options')} items={items} />}

packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryContextualbar.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import type { HistoryActionCallbacks } from './CallHistoryActions';
1818
import CallHistoryActions from './CallHistoryActions';
1919
import { useFullStartDate } from './useFullStartDate';
2020
import { CallHistoryExternalUser, CallHistoryInternalUser } from '../../components';
21+
import { useMediaCallExternalContext } from '../../context';
22+
import { isCallingBlocked } from '../../context/MediaCallContext';
2123
import { getHistoryMessagePayload } from '../../ui-kit/getHistoryMessagePayload';
2224

2325
export type InternalCallHistoryContact = {
@@ -64,6 +66,7 @@ const CallHistoryContextualBar = ({ onClose, actions, contact, data }: CallHisto
6466

6567
const { voiceCall, directMessage } = actions;
6668
const { duration, callId, direction, startedAt } = data;
69+
const { state } = useMediaCallExternalContext();
6770

6871
const date = useFullStartDate(startedAt);
6972
return (
@@ -117,7 +120,12 @@ const CallHistoryContextualBar = ({ onClose, actions, contact, data }: CallHisto
117120
</Button>
118121
)}
119122
{voiceCall && (
120-
<Button success onClick={voiceCall}>
123+
<Button
124+
success
125+
onClick={voiceCall}
126+
disabled={isCallingBlocked(state)}
127+
title={isCallingBlocked(state) ? t('Call_in_progress') : undefined}
128+
>
121129
<Icon name='phone' size='x20' mie='x4' />
122130
{t('Call')}
123131
</Button>

0 commit comments

Comments
 (0)