diff --git a/app/products/calls/actions/calls.test.ts b/app/products/calls/actions/calls.test.ts
index b2f2c77d9..5be7c259c 100644
--- a/app/products/calls/actions/calls.test.ts
+++ b/app/products/calls/actions/calls.test.ts
@@ -115,12 +115,42 @@ const addFakeCall = (serverUrl: string, channelId: string) => {
const call: Call = {
id: 'call',
sessions: {
- a23456abcdefghijklmnopqrs: {sessionId: 'a23456abcdefghijklmnopqrs', userId: 'xohi8cki9787fgiryne716u84o', muted: false, raisedHand: 0},
- a12345667890bcdefghijklmn1: {sessionId: 'a12345667890bcdefghijklmn1', userId: 'xohi8cki9787fgiryne716u84o', muted: true, raisedHand: 0},
- a12345667890bcdefghijklmn2: {sessionId: 'a12345667890bcdefghijklmn2', userId: 'xohi8cki9787fgiryne716u84o', muted: false, raisedHand: 0},
- a12345667890bcdefghijklmn3: {sessionId: 'a12345667890bcdefghijklmn3', userId: 'xohi8cki9787fgiryne716u84o', muted: true, raisedHand: 0},
- a12345667890bcdefghijklmn4: {sessionId: 'a12345667890bcdefghijklmn4', userId: 'xohi8cki9787fgiryne716u84o', muted: false, raisedHand: 0},
- a12345667890bcdefghijklmn5: {sessionId: 'a12345667890bcdefghijklmn5', userId: 'xohi8cki9787fgiryne716u84o', muted: true, raisedHand: 0},
+ a23456abcdefghijklmnopqrs: {
+ sessionId: 'a23456abcdefghijklmnopqrs',
+ userId: 'xohi8cki9787fgiryne716u84o',
+ muted: false,
+ raisedHand: 0,
+ },
+ a12345667890bcdefghijklmn1: {
+ sessionId: 'a12345667890bcdefghijklmn1',
+ userId: 'xohi8cki9787fgiryne716u84o',
+ muted: true,
+ raisedHand: 0,
+ },
+ a12345667890bcdefghijklmn2: {
+ sessionId: 'a12345667890bcdefghijklmn2',
+ userId: 'xohi8cki9787fgiryne716u84o',
+ muted: false,
+ raisedHand: 0,
+ },
+ a12345667890bcdefghijklmn3: {
+ sessionId: 'a12345667890bcdefghijklmn3',
+ userId: 'xohi8cki9787fgiryne716u84o',
+ muted: true,
+ raisedHand: 0,
+ },
+ a12345667890bcdefghijklmn4: {
+ sessionId: 'a12345667890bcdefghijklmn4',
+ userId: 'xohi8cki9787fgiryne716u84o',
+ muted: false,
+ raisedHand: 0,
+ },
+ a12345667890bcdefghijklmn5: {
+ sessionId: 'a12345667890bcdefghijklmn5',
+ userId: 'xohi8cki9787fgiryne716u84o',
+ muted: true,
+ raisedHand: 0,
+ },
},
channelId,
startTime: (new Date()).getTime(),
@@ -198,9 +228,9 @@ describe('Actions.Calls', () => {
assert.equal(response!.data, 'channel-id');
assert.equal((result.current[1] as CurrentCall).channelId, 'channel-id');
- expect(newConnection).toBeCalled();
+ expect(newConnection).toHaveBeenCalled();
expect(newConnection.mock.calls[0][1]).toBe('channel-id');
- expect(updateThreadFollowing).toBeCalled();
+ expect(updateThreadFollowing).toHaveBeenCalled();
await act(async () => {
CallsActions.leaveCall();
@@ -229,7 +259,7 @@ describe('Actions.Calls', () => {
assert.equal(response!.data, 'channel-id');
assert.equal((result.current[1] as CurrentCall | null)?.channelId, 'channel-id');
- expect(getConnectionForTesting()!.disconnect).not.toBeCalled();
+ expect(getConnectionForTesting()!.disconnect).not.toHaveBeenCalled();
const disconnectMock = getConnectionForTesting()!.disconnect;
await act(async () => {
@@ -239,7 +269,7 @@ describe('Actions.Calls', () => {
myselfLeftCall();
});
- expect(disconnectMock).toBeCalled();
+ expect(disconnectMock).toHaveBeenCalled();
expect(getConnectionForTesting()).toBe(null);
assert.equal((result.current[1] as CurrentCall | null), null);
});
@@ -270,7 +300,7 @@ describe('Actions.Calls', () => {
CallsActions.muteMyself();
});
- expect(getConnectionForTesting()!.mute).toBeCalled();
+ expect(getConnectionForTesting()!.mute).toHaveBeenCalled();
await act(async () => {
CallsActions.leaveCall();
@@ -303,7 +333,7 @@ describe('Actions.Calls', () => {
CallsActions.unmuteMyself();
});
- expect(getConnectionForTesting()!.unmute).toBeCalled();
+ expect(getConnectionForTesting()!.unmute).toHaveBeenCalled();
await act(async () => {
CallsActions.leaveCall();
@@ -319,7 +349,7 @@ describe('Actions.Calls', () => {
await act(async () => {
await CallsActions.loadCalls('server1', 'userId1');
});
- expect(mockClient.getCalls).toBeCalled();
+ expect(mockClient.getCalls).toHaveBeenCalled();
assert.equal((result.current[0] as CallsState).calls['channel-1'].channelId, 'channel-1');
assert.equal((result.current[0] as CallsState).enabled['channel-1'], true);
assert.equal((result.current[1] as ChannelsWithCalls)['channel-1'], true);
@@ -347,7 +377,7 @@ describe('Actions.Calls', () => {
await act(async () => {
await CallsActions.loadCalls('server1', 'userId1');
});
- expect(mockClient.getCalls).toBeCalled();
+ expect(mockClient.getCalls).toHaveBeenCalled();
assert.deepEqual((result.current[0] as CallsState), expectedCallsState);
assert.deepEqual((result.current[1] as ChannelsWithCalls), {});
assert.equal((result.current[2] as CurrentCall | null), null);
@@ -362,7 +392,7 @@ describe('Actions.Calls', () => {
await act(async () => {
await CallsActions.loadConfig('server1');
});
- expect(mockClient.getCallsConfig).toBeCalledWith();
+ expect(mockClient.getCallsConfig).toHaveBeenCalledWith();
assert.equal(result.current.DefaultEnabled, true);
assert.equal(result.current.AllowEnableCalls, true);
});
@@ -374,7 +404,7 @@ describe('Actions.Calls', () => {
await act(async () => {
await CallsActions.enableChannelCalls('server1', 'channel-1', true);
});
- expect(mockClient.enableChannelCalls).toBeCalledWith('channel-1', true);
+ expect(mockClient.enableChannelCalls).toHaveBeenCalledWith('channel-1', true);
assert.equal(result.current.enabled['channel-1'], true);
});
@@ -385,13 +415,13 @@ describe('Actions.Calls', () => {
await act(async () => {
await CallsActions.enableChannelCalls('server1', 'channel-1', true);
});
- expect(mockClient.enableChannelCalls).toBeCalledWith('channel-1', true);
+ expect(mockClient.enableChannelCalls).toHaveBeenCalledWith('channel-1', true);
assert.equal(result.current.enabled['channel-1'], true);
mockClient.enableChannelCalls.mockReturnValueOnce({enabled: false});
await act(async () => {
await CallsActions.enableChannelCalls('server1', 'channel-1', false);
});
- expect(mockClient.enableChannelCalls).toBeCalledWith('channel-1', false);
+ expect(mockClient.enableChannelCalls).toHaveBeenCalledWith('channel-1', false);
assert.equal(result.current.enabled['channel-1'], false);
});
@@ -400,8 +430,8 @@ describe('Actions.Calls', () => {
await CallsActions.startCallRecording('server1', 'channel-id');
});
- expect(mockClient.startCallRecording).toBeCalledWith('channel-id');
- expect(needsRecordingErrorAlert).toBeCalled();
+ expect(mockClient.startCallRecording).toHaveBeenCalledWith('channel-id');
+ expect(needsRecordingErrorAlert).toHaveBeenCalled();
});
it('stopCallRecording', async () => {
@@ -409,9 +439,9 @@ describe('Actions.Calls', () => {
await CallsActions.stopCallRecording('server1', 'channel-id');
});
- expect(mockClient.stopCallRecording).toBeCalledWith('channel-id');
- expect(needsRecordingErrorAlert).toBeCalled();
- expect(needsRecordingWillBePostedAlert).toBeCalled();
+ expect(mockClient.stopCallRecording).toHaveBeenCalledWith('channel-id');
+ expect(needsRecordingErrorAlert).toHaveBeenCalled();
+ expect(needsRecordingWillBePostedAlert).toHaveBeenCalled();
});
it('dismissIncomingCall', async () => {
@@ -419,7 +449,7 @@ describe('Actions.Calls', () => {
await CallsActions.dismissIncomingCall('server1', 'channel-id');
});
- expect(mockClient.dismissCall).toBeCalledWith('channel-id');
+ expect(mockClient.dismissCall).toHaveBeenCalledWith('channel-id');
});
it('userLeftChannelErr', async () => {
@@ -446,20 +476,20 @@ describe('Actions.Calls', () => {
assert.equal(response!.data, 'channel-id');
assert.equal((result.current[1] as CurrentCall).channelId, 'channel-id');
- expect(newConnection).toBeCalled();
+ expect(newConnection).toHaveBeenCalled();
expect(newConnection.mock.calls[0][1]).toBe('channel-id');
- expect(updateThreadFollowing).toBeCalled();
+ expect(updateThreadFollowing).toHaveBeenCalled();
await act(async () => {
CallsActions.leaveCall(userLeftChannelErr);
});
- expect(intl.formatMessage).toBeCalledWith({
+ expect(intl.formatMessage).toHaveBeenCalledWith({
id: 'mobile.calls_user_left_channel_error_title',
defaultMessage: 'You left the channel',
});
- expect(intl.formatMessage).toBeCalledWith({
+ expect(intl.formatMessage).toHaveBeenCalledWith({
id: 'mobile.calls_user_left_channel_error_message',
defaultMessage: 'You have left the channel, and have been disconnected from the call.',
});
@@ -489,20 +519,20 @@ describe('Actions.Calls', () => {
assert.equal(response!.data, 'channel-id');
assert.equal((result.current[1] as CurrentCall).channelId, 'channel-id');
- expect(newConnection).toBeCalled();
+ expect(newConnection).toHaveBeenCalled();
expect(newConnection.mock.calls[0][1]).toBe('channel-id');
- expect(updateThreadFollowing).toBeCalled();
+ expect(updateThreadFollowing).toHaveBeenCalled();
await act(async () => {
CallsActions.leaveCall(userRemovedFromChannelErr);
});
- expect(intl.formatMessage).toBeCalledWith({
+ expect(intl.formatMessage).toHaveBeenCalledWith({
id: 'mobile.calls_user_removed_from_channel_error_title',
defaultMessage: 'You were removed from channel',
});
- expect(intl.formatMessage).toBeCalledWith({
+ expect(intl.formatMessage).toHaveBeenCalledWith({
id: 'mobile.calls_user_removed_from_channel_error_message',
defaultMessage: 'You have been removed from the channel, and have been disconnected from the call.',
});
@@ -532,14 +562,14 @@ describe('Actions.Calls', () => {
assert.equal(response!.data, 'channel-id');
assert.equal((result.current[1] as CurrentCall).channelId, 'channel-id');
- expect(newConnection).toBeCalled();
+ expect(newConnection).toHaveBeenCalled();
expect(newConnection.mock.calls[0][1]).toBe('channel-id');
- expect(updateThreadFollowing).toBeCalled();
+ expect(updateThreadFollowing).toHaveBeenCalled();
await act(async () => {
CallsActions.leaveCall(new Error('generic error'));
});
- expect(errorAlert).toBeCalled();
+ expect(errorAlert).toHaveBeenCalled();
});
});
diff --git a/app/products/calls/actions/calls.ts b/app/products/calls/actions/calls.ts
index d4d1359fa..86a0507b4 100644
--- a/app/products/calls/actions/calls.ts
+++ b/app/products/calls/actions/calls.ts
@@ -363,7 +363,7 @@ export const canEndCall = async (serverUrl: string, channelId: string) => {
return false;
}
- return isSystemAdmin(currentUser.roles) || currentUser.id === call.ownerId;
+ return isSystemAdmin(currentUser.roles) || currentUser.id === call.hostId;
};
export const getEndCallMessage = async (serverUrl: string, channelId: string, currentUserId: string, intl: IntlShape) => {
diff --git a/app/products/calls/components/calls_custom_message/calls_custom_message.tsx b/app/products/calls/components/calls_custom_message/calls_custom_message.tsx
index 7ddde2133..63df36327 100644
--- a/app/products/calls/components/calls_custom_message/calls_custom_message.tsx
+++ b/app/products/calls/components/calls_custom_message/calls_custom_message.tsx
@@ -2,10 +2,11 @@
// See LICENSE.txt for license information.
import moment from 'moment-timezone';
-import React from 'react';
+import React, {useCallback} from 'react';
import {useIntl} from 'react-intl';
import {Text, TouchableOpacity, View} from 'react-native';
+import {leaveCall} from '@calls/actions';
import {leaveAndJoinWithAlert, showLimitRestrictedAlert} from '@calls/alerts';
import CompassIcon from '@components/compass_icon';
import FormattedRelativeTime from '@components/formatted_relative_time';
@@ -15,7 +16,7 @@ import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
-import {displayUsername, getUserTimezone} from '@utils/user';
+import {getUserTimezone} from '@utils/user';
import type {LimitRestrictedInfo} from '@calls/observers';
import type PostModel from '@typings/database/models/servers/post';
@@ -24,9 +25,7 @@ import type UserModel from '@typings/database/models/servers/user';
type Props = {
post: PostModel;
currentUser?: UserModel;
- author?: UserModel;
isMilitaryTime: boolean;
- teammateNameDisplay?: string;
limitRestrictedInfo?: LimitRestrictedInfo;
ccChannelId?: string;
}
@@ -41,61 +40,74 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
flexDirection: 'row',
alignItems: 'center',
color: changeOpacity(theme.centerChannelColor, 0.6),
- paddingTop: 5,
- paddingBottom: 5,
+ padding: 12,
+ marginBottom: 2,
+ gap: 8,
+ backgroundColor: theme.centerChannelBg,
+ borderWidth: 1,
+ borderColor: changeOpacity(theme.centerChannelColor, 0.12),
+ borderRadius: 4,
+ shadowColor: theme.centerChannelColor,
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ shadowRadius: 1,
+ shadowOpacity: 0.08,
+ elevation: 1,
},
- messageText: {
+ message: {
flex: 1,
- paddingLeft: 12,
- paddingRight: 4,
+ },
+ text: {
+ color: theme.centerChannelColor,
+ ...typography('Heading', 200),
+ },
+ timeText: {
+ color: changeOpacity(theme.centerChannelColor, 0.72),
+ ...typography('Body', 75),
+ },
+ callIcon: {
+ padding: 10,
+ borderRadius: 20,
+ color: theme.buttonColor,
+ overflow: 'hidden',
},
joinCallIcon: {
- padding: 8,
backgroundColor: theme.onlineIndicator,
- borderRadius: 4,
- color: theme.buttonColor,
- overflow: 'hidden',
},
phoneHangupIcon: {
- padding: 8,
- backgroundColor: changeOpacity(theme.centerChannelColor, 0.6),
- borderRadius: 4,
- color: theme.buttonColor,
- overflow: 'hidden',
+ color: changeOpacity(theme.centerChannelColor, 0.72),
+ backgroundColor: changeOpacity(theme.centerChannelColor, 0.08),
},
- joinCallButtonText: {
- color: theme.buttonColor,
- ...typography('Body', 75, 'SemiBold'),
- },
- joinCallButtonTextRestricted: {
- color: changeOpacity(theme.centerChannelColor, 0.32),
- },
- joinCallButtonIcon: {
- color: theme.buttonColor,
- marginRight: 5,
- },
- joinCallButtonIconRestricted: {
- color: changeOpacity(theme.centerChannelColor, 0.32),
- },
- startedText: {
- color: theme.centerChannelColor,
- ...typography('Body', 100, 'SemiBold'),
- },
- joinCallButton: {
+ callButton: {
flexDirection: 'row',
- padding: 12,
- backgroundColor: theme.onlineIndicator,
+ paddingHorizontal: 16,
+ paddingVertical: 10,
+ gap: 7,
borderRadius: 4,
alignItems: 'center',
alignContent: 'center',
},
+ joinCallButton: {
+ backgroundColor: theme.onlineIndicator,
+ },
+ leaveCallButton: {
+ backgroundColor: theme.dndIndicator,
+ },
+ buttonText: {
+ color: theme.buttonColor,
+ ...typography('Body', 100, 'SemiBold'),
+ },
+ buttonRestricted: {
+ color: changeOpacity(theme.centerChannelColor, 0.32),
+ },
+ buttonIcon: {
+ color: theme.buttonColor,
+ },
joinCallButtonRestricted: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.08),
},
- timeText: {
- ...typography('Body', 75),
- color: changeOpacity(theme.centerChannelColor, 0.64),
- },
endCallInfo: {
flexDirection: 'row',
alignItems: 'center',
@@ -109,10 +121,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
};
});
-export const CallsCustomMessage = ({
- post, currentUser, author, isMilitaryTime, teammateNameDisplay,
- ccChannelId, limitRestrictedInfo,
-}: Props) => {
+export const CallsCustomMessage = ({post, currentUser, isMilitaryTime, ccChannelId, limitRestrictedInfo}: Props) => {
const intl = useIntl();
const theme = useTheme();
const style = getStyleSheet(theme);
@@ -122,18 +131,18 @@ export const CallsCustomMessage = ({
const alreadyInTheCall = Boolean(ccChannelId && ccChannelId === post.channelId);
const isLimitRestricted = Boolean(limitRestrictedInfo?.limitRestricted);
- const joinHandler = () => {
- if (alreadyInTheCall) {
- return;
- }
-
+ const joinHandler = useCallback(() => {
if (isLimitRestricted) {
showLimitRestrictedAlert(limitRestrictedInfo!, intl);
return;
}
leaveAndJoinWithAlert(intl, serverUrl, post.channelId);
- };
+ }, [limitRestrictedInfo, intl, serverUrl, post.channelId]);
+
+ const leaveHandler = useCallback(() => {
+ leaveCall();
+ }, []);
const title = post.props.title ? (
@@ -148,20 +157,20 @@ export const CallsCustomMessage = ({
-
+
{' '}
{'•'}
@@ -184,22 +193,54 @@ export const CallsCustomMessage = ({
);
}
- const joinTextStyle = [style.joinCallButtonText, isLimitRestricted && style.joinCallButtonTextRestricted];
+ const button = alreadyInTheCall ? (
+
+
+
+
+ ) : (
+
+
+
+
+ );
+
return (
<>
{title}
-
+
-
-
-
- {
- alreadyInTheCall &&
-
- }
- {
- !alreadyInTheCall &&
-
- }
-
+ {button}
>
);
diff --git a/app/products/calls/components/calls_custom_message/index.ts b/app/products/calls/components/calls_custom_message/index.ts
index 2547bf33f..6368f17b6 100644
--- a/app/products/calls/components/calls_custom_message/index.ts
+++ b/app/products/calls/components/calls_custom_message/index.ts
@@ -11,7 +11,7 @@ import {observeCurrentCall} from '@calls/state';
import {Preferences} from '@constants';
import {getDisplayNamePreferenceAsBool} from '@helpers/api/preference';
import {queryDisplayNamePreferences} from '@queries/servers/preference';
-import {observeCurrentUser, observeTeammateNameDisplay, observeUser} from '@queries/servers/user';
+import {observeCurrentUser} from '@queries/servers/user';
import type {WithDatabaseArgs} from '@typings/database/database';
import type PostModel from '@typings/database/models/servers/post';
@@ -23,7 +23,6 @@ type OwnProps = {
const enhanced = withObservables(['post'], ({serverUrl, post, database}: OwnProps & WithDatabaseArgs) => {
const currentUser = observeCurrentUser(database);
- const author = observeUser(database, post.userId);
const isMilitaryTime = queryDisplayNamePreferences(database).observeWithColumns(['value']).pipe(
switchMap(
(preferences) => of$(getDisplayNamePreferenceAsBool(preferences, Preferences.USE_MILITARY_TIME)),
@@ -34,7 +33,6 @@ const enhanced = withObservables(['post'], ({serverUrl, post, database}: OwnProp
if (post.props.end_at) {
return {
currentUser,
- author,
isMilitaryTime,
};
}
@@ -46,9 +44,7 @@ const enhanced = withObservables(['post'], ({serverUrl, post, database}: OwnProp
return {
currentUser,
- author,
isMilitaryTime,
- teammateNameDisplay: observeTeammateNameDisplay(database),
limitRestrictedInfo: observeIsCallLimitRestricted(database, serverUrl, post.channelId),
ccChannelId,
};
diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json
index dc7b077ed..b9a38edad 100644
--- a/assets/base/i18n/en.json
+++ b/assets/base/i18n/en.json
@@ -447,7 +447,6 @@
"mobile.calls_call_thread": "Call Thread",
"mobile.calls_captions": "Captions",
"mobile.calls_captions_turned_on": "Live captions turned on",
- "mobile.calls_current_call": "Current call",
"mobile.calls_disable": "Disable calls",
"mobile.calls_dismiss": "Dismiss",
"mobile.calls_enable": "Enable calls",
@@ -474,6 +473,7 @@
"mobile.calls_host_transcription_title": "Recording and transcription has started",
"mobile.calls_incoming_dm": "{name} is inviting you to a call",
"mobile.calls_incoming_gm": "{name} is inviting you to a call with {num, plural, one {# other} other {# others}}",
+ "mobile.calls_join": "Join",
"mobile.calls_join_call": "Join call",
"mobile.calls_lasted": "Lasted {duration}",
"mobile.calls_leave": "Leave",
@@ -489,7 +489,6 @@
"mobile.calls_mute_others": "Mute others",
"mobile.calls_mute_participant": "Mute participant",
"mobile.calls_name_is_talking_postfix": "is talking...",
- "mobile.calls_name_started_call": "{name} started a call",
"mobile.calls_noone_talking": "No one is talking",
"mobile.calls_not_available_msg": "Please contact your System Admin to enable the feature.",
"mobile.calls_not_available_option": "(Not available)",
@@ -529,6 +528,7 @@
"mobile.calls_speaker": "Speaker",
"mobile.calls_start_call": "Start Call",
"mobile.calls_start_call_exists": "A call is already ongoing in the channel.",
+ "mobile.calls_started_call": "Call started",
"mobile.calls_stop_recording": "Stop Recording",
"mobile.calls_stop_screenshare": "Stop screen share",
"mobile.calls_tablet": "Tablet",