Skip to content

Commit

Permalink
[MM-58356] Improve logic for rendering start call button in user prof…
Browse files Browse the repository at this point in the history
…ile popover (mattermost#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
  • Loading branch information
streamer45 authored Aug 12, 2024
1 parent 7dd84f6 commit aca39fb
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ function getBasePropsAndState(): [Props, DeepPartial<GlobalState>] {
// @ts-ignore
'plugins-com.mattermost.calls': {
sessions: {},
callsConfig: {
DefaultEnabled: true,
},
},
};
const props: Props = {
Expand Down Expand Up @@ -301,7 +304,7 @@ describe('components/ProfilePopover', () => {
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();
initialState.plugins!.plugins = {};

Expand All @@ -318,11 +321,47 @@ describe('components/ProfilePopover', () => {
expect(button).toBeDisabled();
});

test('should not show the start call button when callsChannelState.enabled is false', async () => {
(Client4.getCallsChannelState as jest.Mock).mockImplementationOnce(async () => ({enabled: false}));
test('should not show start call button when calls in channel have been explicitly disabled', async () => {
const [props, initialState] = getBasePropsAndState();
(initialState as any)['plugins-com.mattermost.calls'].channels = {dmChannelId: {enabled: false}};

renderWithPluginReducers(<ProfilePopover {...props}/>, initialState);
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.findByLabelText('Start Call')).not.toBeInTheDocument();
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();
});
});
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import React, {useCallback, useEffect, useState} from 'react';
import React from 'react';
import {useIntl} from 'react-intl';
import {useSelector} from 'react-redux';

import {Client4} from 'mattermost-redux/client';
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 WithTooltip from 'components/with_tooltip';
Expand All @@ -24,11 +31,6 @@ type Props = {
username: string;
}

type ChannelCallsState = {
enabled: boolean;
id: string;
};

export function isUserInCall(state: GlobalState, userId: string, channelId: string) {
const sessionsInCall = getSessionsInCalls(state)[channelId] || {};

Expand All @@ -52,35 +54,44 @@ const CallButton = ({
const isCallsEnabled = useSelector((state: GlobalState) => getIsCallsEnabled(state));
const dmChannel = useSelector((state: GlobalState) => getChannelByName(state, getDirectChannelName(currentUserId, userId)));

const hasDMCall = useSelector((state: GlobalState) => {
if (isCallsEnabled && dmChannel) {
return isUserInCall(state, currentUserId, dmChannel.id) || isUserInCall(state, userId, dmChannel.id);
const shouldRenderButton = useSelector((state: GlobalState) => {
// 1. No one should get the button if the plugin is disabled.
if (!isCallsEnabled) {
return false;
}
return false;
});

const [callsDMChannelState, setCallsDMChannelState] = useState<ChannelCallsState>();
// 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;
}

const getCallsChannelState = useCallback((channelId: string): Promise<ChannelCallsState> => {
let data: Promise<ChannelCallsState>;
try {
data = Client4.getCallsChannelState(channelId);
} catch (error) {
return error;
// 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;
}

return data;
}, []);
// 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;
}

useEffect(() => {
// 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) => {
if (isCallsEnabled && dmChannel) {
getCallsChannelState(dmChannel.id).then((data) => {
setCallsDMChannelState(data);
});
return isUserInCall(state, currentUserId, dmChannel.id) || isUserInCall(state, userId, dmChannel.id);
}
}, []);
return false;
});

if (!isCallsEnabled || callsDMChannelState?.enabled === false) {
if (!shouldRenderButton) {
return null;
}

Expand Down
28 changes: 24 additions & 4 deletions webapp/channels/src/selectors/calls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {suitePluginIds} from 'utils/constants';

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') {
return Boolean(state.plugins.plugins[suitePluginIds.calls] &&
Expand All @@ -20,17 +20,37 @@ export function isCallsEnabled(state: GlobalState, minVersion = '0.4.2') {
export function isCallsRingingEnabledOnServer(state: GlobalState) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @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>> {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return state[CALLS_PLUGIN].sessions || {};
return state[CALLS_PLUGIN]?.sessions || {};
}

export function getCallsConfig(state: GlobalState): CallsConfig {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @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;
}

0 comments on commit aca39fb

Please sign in to comment.