diff --git a/webapp/channels/src/components/profile_popover/profile_popover_call_button_wrapper/index.test.tsx b/webapp/channels/src/components/profile_popover/profile_popover_call_button_wrapper/index.test.tsx
index 2209b9419b..a06dcdf8b2 100644
--- a/webapp/channels/src/components/profile_popover/profile_popover_call_button_wrapper/index.test.tsx
+++ b/webapp/channels/src/components/profile_popover/profile_popover_call_button_wrapper/index.test.tsx
@@ -1,7 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {isUserInCall} from './index';
+import {screen, render} from '@testing-library/react';
+import React from 'react';
+import {Provider} from 'react-redux';
+import configureStore from 'redux-mock-store';
+
+import {TestHelper} from '../../../utils/test_helper';
+
+import CallButton, {isUserInCall} from './index';
describe('isUserInCall', () => {
test('missing state', () => {
@@ -51,3 +58,385 @@ describe('isUserInCall', () => {
} as any, 'userA', 'channelID')).toBe(true);
});
});
+
+describe('CallButton', () => {
+ const mockStore = configureStore();
+ const userId = 'user1';
+ const currentUserId = 'current_user';
+ const dmChannelId = 'dm_channel_id';
+ const baseProps = {
+ userId,
+ currentUserId,
+ fullname: 'Test User',
+ username: 'testuser',
+ };
+ const PluginComponent = () => {
+ return ;
+ };
+
+ test('should not render when calls are disabled', () => {
+ const store = mockStore({
+ 'plugins-com.mattermost.calls': {
+ enabled: false,
+ },
+ entities: {
+ channels: {
+ channels: {
+ currentChannelId: dmChannelId,
+ channels: {
+ current_channel_id: TestHelper.getChannelMock({
+ id: dmChannelId,
+ name: `${currentUserId}__${userId}`,
+ display_name: `${currentUserId}__${userId}`,
+ delete_at: 0,
+ type: 'D',
+ }),
+ },
+ },
+ },
+ users: {
+ profiles: {
+ [currentUserId]: TestHelper.getUserMock({
+ id: currentUserId,
+ roles: 'system_user',
+ }),
+ },
+ },
+ },
+ plugins: {
+ plugins: {
+ 'com.mattermost.calls': {
+ id: 'com.mattermost.calls',
+ version: '1.0.0',
+ },
+ },
+ },
+ });
+
+ render(
+
+
+ ,
+ );
+
+ expect(screen.queryByTestId('startCallButton')).not.toBeInTheDocument();
+ });
+
+ test('should render for admin even in test mode', () => {
+ const store = mockStore({
+ 'plugins-com.mattermost.calls': {
+ enabled: true,
+ config: {
+ DefaultEnabled: false,
+ },
+ },
+ entities: {
+ channels: {
+ currentChannelId: dmChannelId,
+ channels: {
+ current_channel_id: TestHelper.getChannelMock({
+ id: dmChannelId,
+ name: `${currentUserId}__${userId}`,
+ display_name: `${currentUserId}__${userId}`,
+ delete_at: 0,
+ type: 'D',
+ }),
+ },
+ myMembers: {
+ [dmChannelId]: TestHelper.getChannelMembershipMock({channel_id: dmChannelId}),
+ },
+ },
+ users: {
+ currentUserId,
+ profiles: {
+ [currentUserId]: TestHelper.getUserMock({
+ id: currentUserId,
+ roles: 'system_admin',
+ }),
+ },
+ },
+ },
+ plugins: {
+ plugins: {
+ 'com.mattermost.calls': {
+ id: 'com.mattermost.calls',
+ version: '1.0.0',
+ },
+ },
+ components: {
+ CallButton: [{
+ id: 'CallButton',
+ plugin_id: 'com.mattermost.calls',
+ button: PluginComponent,
+ }],
+ },
+ },
+ views: {
+ rhs: {
+ isSidebarOpen: false,
+ },
+ },
+ });
+
+ render(
+
+
+ ,
+ );
+
+ expect(screen.getByLabelText('Start Call')).toBeInTheDocument();
+ });
+
+ test('should render when calls are enabled and not in test mode', () => {
+ const store = mockStore({
+ 'plugins-com.mattermost.calls': {
+ enabled: true,
+ config: {
+ DefaultEnabled: true,
+ },
+ channels: {},
+ sessions: {},
+ },
+ entities: {
+ channels: {
+ currentChannelId: dmChannelId,
+ channels: {
+ current_channel_id: TestHelper.getChannelMock({
+ id: dmChannelId,
+ name: `${currentUserId}__${userId}`,
+ display_name: `${currentUserId}__${userId}`,
+ delete_at: 0,
+ type: 'D',
+ }),
+ },
+ myMembers: {
+ [dmChannelId]: TestHelper.getChannelMembershipMock({channel_id: dmChannelId}),
+ },
+ },
+ users: {
+ currentUserId,
+ profiles: {
+ [currentUserId]: TestHelper.getUserMock({
+ id: currentUserId,
+ roles: 'system_admin',
+ }),
+ },
+ },
+ },
+ plugins: {
+ plugins: {
+ 'com.mattermost.calls': {
+ id: 'com.mattermost.calls',
+ version: '1.0.0',
+ },
+ },
+ components: {
+ CallButton: [{
+ id: 'CallButton',
+ plugin_id: 'com.mattermost.calls',
+ button: PluginComponent,
+ }],
+ },
+ },
+ views: {
+ rhs: {
+ isSidebarOpen: false,
+ },
+ },
+ });
+
+ render(
+
+
+ ,
+ );
+
+ expect(screen.getByLabelText('Start Call')).toBeInTheDocument();
+ });
+
+ test('should render when channel is explicitly enabled regardless of test mode', () => {
+ const store = mockStore({
+ 'plugins-com.mattermost.calls': {
+ enabled: true,
+ config: {
+ DefaultEnabled: false,
+ },
+ channels: {
+ [dmChannelId]: {enabled: true},
+ },
+ sessions: {},
+ },
+ entities: {
+ channels: {
+ currentChannelId: dmChannelId,
+ channels: {
+ current_channel_id: TestHelper.getChannelMock({
+ id: dmChannelId,
+ name: `${currentUserId}__${userId}`,
+ display_name: `${currentUserId}__${userId}`,
+ delete_at: 0,
+ type: 'D',
+ }),
+ },
+ myMembers: {
+ [dmChannelId]: TestHelper.getChannelMembershipMock({channel_id: dmChannelId}),
+ },
+ },
+ users: {
+ currentUserId,
+ profiles: {
+ [currentUserId]: TestHelper.getUserMock({
+ id: currentUserId,
+ roles: 'system_admin',
+ }),
+ },
+ },
+ },
+ plugins: {
+ plugins: {
+ 'com.mattermost.calls': {
+ id: 'com.mattermost.calls',
+ version: '1.0.0',
+ },
+ },
+ components: {
+ CallButton: [{
+ id: 'CallButton',
+ plugin_id: 'com.mattermost.calls',
+ button: PluginComponent,
+ }],
+ },
+ },
+ views: {
+ rhs: {
+ isSidebarOpen: false,
+ },
+ },
+ });
+
+ render(
+
+
+ ,
+ );
+
+ expect(screen.getByLabelText('Start Call')).toBeInTheDocument();
+ });
+
+ test('should not render when channel is explicitly disabled', () => {
+ const store = mockStore({
+ 'plugins-com.mattermost.calls': {
+ enabled: true,
+ config: {
+ DefaultEnabled: true,
+ },
+ channels: {
+ [dmChannelId]: {enabled: false},
+ },
+ sessions: {},
+ },
+ entities: {
+ channels: {
+ currentChannelId: dmChannelId,
+ channels: {
+ current_channel_id: TestHelper.getChannelMock({
+ id: dmChannelId,
+ name: `${currentUserId}__${userId}`,
+ display_name: `${currentUserId}__${userId}`,
+ delete_at: 0,
+ type: 'D',
+ }),
+ },
+ myMembers: {
+ [dmChannelId]: TestHelper.getChannelMembershipMock({channel_id: dmChannelId}),
+ },
+ },
+ users: {
+ profiles: {
+ [currentUserId]: TestHelper.getUserMock({
+ id: currentUserId,
+ roles: 'system_admin',
+ }),
+ },
+ },
+ },
+ plugins: {
+ plugins: {
+ 'com.mattermost.calls': {
+ id: 'com.mattermost.calls',
+ version: '1.0.0',
+ },
+ },
+ },
+ });
+
+ render(
+
+
+ ,
+ );
+
+ expect(screen.queryByLabelText('Start Call')).not.toBeInTheDocument();
+ });
+
+ test('should disable button when there is an ongoing call', () => {
+ const store = mockStore({
+ 'plugins-com.mattermost.calls': {
+ enabled: true,
+ config: {
+ DefaultEnabled: true,
+ },
+ sessions: {
+ [dmChannelId]: {
+ session1: {
+ user_id: currentUserId,
+ },
+ },
+ },
+ },
+ entities: {
+ channels: {
+ currentChannelId: dmChannelId,
+ channels: {
+ current_channel_id: TestHelper.getChannelMock({
+ id: dmChannelId,
+ name: `${currentUserId}__${userId}`,
+ display_name: `${currentUserId}__${userId}`,
+ delete_at: 0,
+ type: 'D',
+ }),
+ },
+ myMembers: {
+ [dmChannelId]: TestHelper.getChannelMembershipMock({channel_id: dmChannelId}),
+ },
+ },
+ users: {
+ profiles: {
+ [currentUserId]: TestHelper.getUserMock({
+ id: currentUserId,
+ roles: 'system_admin',
+ }),
+ },
+ },
+ },
+ plugins: {
+ plugins: {
+ 'com.mattermost.calls': {
+ id: 'com.mattermost.calls',
+ version: '1.0.0',
+ },
+ },
+ },
+ });
+
+ render(
+
+
+ ,
+ );
+
+ const button = screen.getByLabelText('Call with Test User is ongoing');
+ expect(button).toBeInTheDocument();
+ expect(button).toBeDisabled();
+ });
+});
diff --git a/webapp/channels/src/selectors/calls.ts b/webapp/channels/src/selectors/calls.ts
index 0d6d750d28..c380dbfde4 100644
--- a/webapp/channels/src/selectors/calls.ts
+++ b/webapp/channels/src/selectors/calls.ts
@@ -5,12 +5,18 @@ import semver from 'semver';
import type {CallsConfig, UserSessionState} from '@mattermost/calls-common/lib/types';
+import {createSelector} from 'mattermost-redux/selectors/create_selector';
+
import {suitePluginIds} from 'utils/constants';
import type {GlobalState} from 'types/store';
const CALLS_PLUGIN = `plugins-${suitePluginIds.calls}`;
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-ignore
+const pluginState = (state: GlobalState) => state[CALLS_PLUGIN];
+
export function isCallsEnabled(state: GlobalState, minVersion = '0.4.2') {
return Boolean(state.plugins.plugins[suitePluginIds.calls] &&
semver.gte(String(semver.clean(state.plugins.plugins[suitePluginIds.calls].version || '0.0.0')), minVersion));
@@ -18,32 +24,33 @@ export function isCallsEnabled(state: GlobalState, minVersion = '0.4.2') {
// isCallsRingingEnabledOnServer is the flag for the ringing/notification feature in calls
export function isCallsRingingEnabledOnServer(state: GlobalState) {
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- return Boolean(state[CALLS_PLUGIN]?.callsConfig?.EnableRinging);
+ return Boolean(pluginState(state)?.callsConfig?.EnableRinging);
}
-export function getSessionsInCalls(state: GlobalState): Record> {
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- return state[CALLS_PLUGIN]?.sessions || {};
-}
+export const getSessionsInCalls = createSelector(
+ 'getSessionsInCalls',
+ pluginState,
+ (state): Record> => {
+ return state?.sessions || {};
+ },
+);
-export function getCallsConfig(state: GlobalState): CallsConfig {
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- return state[CALLS_PLUGIN]?.callsConfig;
-}
+export const getCallsConfig = createSelector(
+ 'getCallsConfig',
+ pluginState,
+ (state): CallsConfig => {
+ return state?.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) {
+ const callsState = pluginState(state);
+
+ if (!callsState || !callsState.channels || !callsState.channels[channelId]) {
return {};
}
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- return state[CALLS_PLUGIN].channels[channelId] || {};
+
+ return callsState.channels[channelId];
}
export function callsChannelExplicitlyEnabled(state: GlobalState, channelId: string) {