[MM-58356] Improve logic for rendering start call button in user profile popover (#27840)
* Improve logic for rendering start call button in user profile popover * Fix typo * Fix potential error when plugin is not enabled * Improve selector logic
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
7dd84f623a
Коммит
aca39fb1ef
@@ -148,6 +148,9 @@ function getBasePropsAndState(): [Props, DeepPartial<GlobalState>] {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
'plugins-com.mattermost.calls': {
|
'plugins-com.mattermost.calls': {
|
||||||
sessions: {},
|
sessions: {},
|
||||||
|
callsConfig: {
|
||||||
|
DefaultEnabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const props: Props = {
|
const props: Props = {
|
||||||
@@ -301,7 +304,7 @@ describe('components/ProfilePopover', () => {
|
|||||||
expect(await screen.findByLabelText('Start Call')).toBeInTheDocument();
|
expect(await screen.findByLabelText('Start Call')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not show start a call button when calls are disabled', async () => {
|
test('should not show start call button when plugin is disabled', async () => {
|
||||||
const [props, initialState] = getBasePropsAndState();
|
const [props, initialState] = getBasePropsAndState();
|
||||||
initialState.plugins!.plugins = {};
|
initialState.plugins!.plugins = {};
|
||||||
|
|
||||||
@@ -318,11 +321,47 @@ describe('components/ProfilePopover', () => {
|
|||||||
expect(button).toBeDisabled();
|
expect(button).toBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not show the start call button when callsChannelState.enabled is false', async () => {
|
test('should not show start call button when calls in channel have been explicitly disabled', async () => {
|
||||||
(Client4.getCallsChannelState as jest.Mock).mockImplementationOnce(async () => ({enabled: false}));
|
|
||||||
const [props, initialState] = getBasePropsAndState();
|
const [props, initialState] = getBasePropsAndState();
|
||||||
|
(initialState as any)['plugins-com.mattermost.calls'].channels = {dmChannelId: {enabled: false}};
|
||||||
|
|
||||||
renderWithPluginReducers(<ProfilePopover {...props}/>, initialState);
|
renderWithPluginReducers(<ProfilePopover {...props}/>, initialState);
|
||||||
expect(await screen.findByLabelText('Start Call')).not.toBeInTheDocument();
|
expect(await screen.queryByLabelText('Start Call')).not.toBeInTheDocument();
|
||||||
|
expect(await screen.queryByLabelText('Call with user is ongoing')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not show start call button for users when calls test mode is on', async () => {
|
||||||
|
const [props, initialState] = getBasePropsAndState();
|
||||||
|
(initialState as any)['plugins-com.mattermost.calls'].callsConfig = {DefaultEnabled: false};
|
||||||
|
|
||||||
|
renderWithPluginReducers(<ProfilePopover {...props}/>, initialState);
|
||||||
|
expect(await screen.queryByLabelText('Start Call')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show start call button for users when calls test mode is on if calls in channel have been explicitly enabled', async () => {
|
||||||
|
const [props, initialState] = getBasePropsAndState();
|
||||||
|
(initialState as any)['plugins-com.mattermost.calls'].callsConfig = {DefaultEnabled: false};
|
||||||
|
(initialState as any)['plugins-com.mattermost.calls'].channels = {dmChannelId: {enabled: true}};
|
||||||
|
|
||||||
|
renderWithPluginReducers(<ProfilePopover {...props}/>, initialState);
|
||||||
|
expect(await screen.queryByLabelText('Start Call')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show start call button for admin when calls test mode is on', async () => {
|
||||||
|
const [props, initialState] = getBasePropsAndState();
|
||||||
|
(initialState as any)['plugins-com.mattermost.calls'].callsConfig = {DefaultEnabled: false};
|
||||||
|
initialState.entities = {
|
||||||
|
...initialState.entities!,
|
||||||
|
users: {
|
||||||
|
...initialState.entities!.users,
|
||||||
|
profiles: {
|
||||||
|
...initialState.entities!.users!.profiles,
|
||||||
|
currentUser: TestHelper.getUserMock({id: 'currentUser', roles: General.SYSTEM_ADMIN_ROLE}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
renderWithPluginReducers(<ProfilePopover {...props}/>, initialState);
|
||||||
|
expect(await screen.findByLabelText('Start Call')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React, {useCallback, useEffect, useState} from 'react';
|
import React from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useIntl} from 'react-intl';
|
||||||
import {useSelector} from 'react-redux';
|
import {useSelector} from 'react-redux';
|
||||||
|
|
||||||
import {Client4} from 'mattermost-redux/client';
|
|
||||||
import {getChannelByName} from 'mattermost-redux/selectors/entities/channels';
|
import {getChannelByName} from 'mattermost-redux/selectors/entities/channels';
|
||||||
|
import {getUser} from 'mattermost-redux/selectors/entities/users';
|
||||||
|
import {isSystemAdmin} from 'mattermost-redux/utils/user_utils';
|
||||||
|
|
||||||
import {isCallsEnabled as getIsCallsEnabled, getSessionsInCalls} from 'selectors/calls';
|
import {
|
||||||
|
isCallsEnabled as getIsCallsEnabled,
|
||||||
|
getSessionsInCalls,
|
||||||
|
getCallsConfig,
|
||||||
|
callsChannelExplicitlyDisabled,
|
||||||
|
callsChannelExplicitlyEnabled,
|
||||||
|
} from 'selectors/calls';
|
||||||
|
|
||||||
import ProfilePopoverCallButton from 'components/profile_popover/profile_popover_calls_button';
|
import ProfilePopoverCallButton from 'components/profile_popover/profile_popover_calls_button';
|
||||||
import WithTooltip from 'components/with_tooltip';
|
import WithTooltip from 'components/with_tooltip';
|
||||||
@@ -24,11 +31,6 @@ type Props = {
|
|||||||
username: string;
|
username: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChannelCallsState = {
|
|
||||||
enabled: boolean;
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function isUserInCall(state: GlobalState, userId: string, channelId: string) {
|
export function isUserInCall(state: GlobalState, userId: string, channelId: string) {
|
||||||
const sessionsInCall = getSessionsInCalls(state)[channelId] || {};
|
const sessionsInCall = getSessionsInCalls(state)[channelId] || {};
|
||||||
|
|
||||||
@@ -52,6 +54,36 @@ const CallButton = ({
|
|||||||
const isCallsEnabled = useSelector((state: GlobalState) => getIsCallsEnabled(state));
|
const isCallsEnabled = useSelector((state: GlobalState) => getIsCallsEnabled(state));
|
||||||
const dmChannel = useSelector((state: GlobalState) => getChannelByName(state, getDirectChannelName(currentUserId, userId)));
|
const dmChannel = useSelector((state: GlobalState) => getChannelByName(state, getDirectChannelName(currentUserId, userId)));
|
||||||
|
|
||||||
|
const shouldRenderButton = useSelector((state: GlobalState) => {
|
||||||
|
// 1. No one should get the button if the plugin is disabled.
|
||||||
|
if (!isCallsEnabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. No one should get the button if calls in channel have been explicitly disabled in the DM channel.
|
||||||
|
if (callsChannelExplicitlyDisabled(state, dmChannel?.id ?? '')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Admins should get the button unless calls have been explicitly disabled in the DM channel. This
|
||||||
|
// should apply in test mode as well (DefaultEnabled = false).
|
||||||
|
if (isSystemAdmin(getUser(state, currentUserId)?.roles)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Users should only see the button if test mode is off (DefaultEnabled = true) and calls in the DM channel are not disabled.
|
||||||
|
if (getCallsConfig(state).DefaultEnabled) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Everyone should see the button if calls have been explicitly enabled in the DM channel, regardless of test mode state.
|
||||||
|
if (callsChannelExplicitlyEnabled(state, dmChannel?.id ?? '')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
const hasDMCall = useSelector((state: GlobalState) => {
|
const hasDMCall = useSelector((state: GlobalState) => {
|
||||||
if (isCallsEnabled && dmChannel) {
|
if (isCallsEnabled && dmChannel) {
|
||||||
return isUserInCall(state, currentUserId, dmChannel.id) || isUserInCall(state, userId, dmChannel.id);
|
return isUserInCall(state, currentUserId, dmChannel.id) || isUserInCall(state, userId, dmChannel.id);
|
||||||
@@ -59,28 +91,7 @@ const CallButton = ({
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
const [callsDMChannelState, setCallsDMChannelState] = useState<ChannelCallsState>();
|
if (!shouldRenderButton) {
|
||||||
|
|
||||||
const getCallsChannelState = useCallback((channelId: string): Promise<ChannelCallsState> => {
|
|
||||||
let data: Promise<ChannelCallsState>;
|
|
||||||
try {
|
|
||||||
data = Client4.getCallsChannelState(channelId);
|
|
||||||
} catch (error) {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isCallsEnabled && dmChannel) {
|
|
||||||
getCallsChannelState(dmChannel.id).then((data) => {
|
|
||||||
setCallsDMChannelState(data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (!isCallsEnabled || callsDMChannelState?.enabled === false) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {suitePluginIds} from 'utils/constants';
|
|||||||
|
|
||||||
import type {GlobalState} from 'types/store';
|
import type {GlobalState} from 'types/store';
|
||||||
|
|
||||||
const CALLS_PLUGIN = 'plugins-com.mattermost.calls';
|
const CALLS_PLUGIN = `plugins-${suitePluginIds.calls}`;
|
||||||
|
|
||||||
export function isCallsEnabled(state: GlobalState, minVersion = '0.4.2') {
|
export function isCallsEnabled(state: GlobalState, minVersion = '0.4.2') {
|
||||||
return Boolean(state.plugins.plugins[suitePluginIds.calls] &&
|
return Boolean(state.plugins.plugins[suitePluginIds.calls] &&
|
||||||
@@ -20,17 +20,37 @@ export function isCallsEnabled(state: GlobalState, minVersion = '0.4.2') {
|
|||||||
export function isCallsRingingEnabledOnServer(state: GlobalState) {
|
export function isCallsRingingEnabledOnServer(state: GlobalState) {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return Boolean(state[`plugins-${suitePluginIds.calls}`]?.callsConfig?.EnableRinging);
|
return Boolean(state[CALLS_PLUGIN]?.callsConfig?.EnableRinging);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSessionsInCalls(state: GlobalState): Record<string, Record<string, UserSessionState>> {
|
export function getSessionsInCalls(state: GlobalState): Record<string, Record<string, UserSessionState>> {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return state[CALLS_PLUGIN].sessions || {};
|
return state[CALLS_PLUGIN]?.sessions || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCallsConfig(state: GlobalState): CallsConfig {
|
export function getCallsConfig(state: GlobalState): CallsConfig {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return state[CALLS_PLUGIN].callsConfig;
|
return state[CALLS_PLUGIN]?.callsConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCallsChannelState(state: GlobalState, channelId: string): {enabled?: boolean} {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
if (!state[CALLS_PLUGIN] || !state[CALLS_PLUGIN].channels) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
return state[CALLS_PLUGIN].channels[channelId] || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callsChannelExplicitlyEnabled(state: GlobalState, channelId: string) {
|
||||||
|
return Boolean(getCallsChannelState(state, channelId).enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callsChannelExplicitlyDisabled(state: GlobalState, channelId: string) {
|
||||||
|
const enabled = getCallsChannelState(state, channelId).enabled;
|
||||||
|
return (typeof enabled !== 'undefined') && !enabled;
|
||||||
}
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user